stm32h7: Updated pwr and rcc configs to support devices with SMPS.
Worked in nuances for differences between versions of STM32H7 devices, such as handling of ODEN, explicit SCUEN bit, and different VOS mappings. This has been validated on the STM32H7A3 and STM32H743 MCUs.
This commit is contained in:
committed by
Karl Palsson
parent
7b88c2d9d2
commit
c2dbea012b
@@ -30,15 +30,80 @@
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libopencm3/cm3/assert.h>
|
||||
#include <libopencm3/stm32/dbgmcu.h>
|
||||
#include <libopencm3/stm32/pwr.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/syscfg.h>
|
||||
|
||||
/* DBGMCU_IDC DEV ID values needed to account for variations between part types. */
|
||||
#define DBGMCU_IDCODE_DEV_ID_STM32H74X_5X 0x450
|
||||
#define DBGMCU_IDCODE_DEV_ID_STM32H7A3_B3_B0 0x480
|
||||
|
||||
void pwr_set_mode_ldo(void) {
|
||||
const uint32_t ldo_mask = (PWR_CR3_SCUEN | PWR_CR3_LDOEN | PWR_CR3_BYPASS);
|
||||
PWR_CR3 = (PWR_CR3 & ~ldo_mask) | (PWR_CR3_SCUEN | PWR_CR3_LDOEN);
|
||||
/* Per table in manual for SMPS, mask and set SMPSEN=0 : LDOEN=1 : BYPASS=0. */
|
||||
const uint32_t cr3_mask = (PWR_CR3_SMPSEN | PWR_CR3_LDOEN | PWR_CR3_BYPASS);
|
||||
PWR_CR3 = (PWR_CR3 & ~cr3_mask) | (PWR_CR3_LDOEN);
|
||||
}
|
||||
|
||||
void pwr_set_mode_scu_ldo(void) {
|
||||
const uint32_t cr3_mask = (PWR_CR3_SCUEN | PWR_CR3_LDOEN | PWR_CR3_BYPASS);
|
||||
PWR_CR3 = (PWR_CR3 & ~cr3_mask) | (PWR_CR3_SCUEN | PWR_CR3_LDOEN);
|
||||
}
|
||||
|
||||
void pwr_set_mode_smps_ldo(bool supply_external, uint32_t smps_level, bool use_ldo) {
|
||||
uint32_t cr3_mask, cr3_set;
|
||||
cr3_mask = (PWR_CR3_SMPSEXTHP | PWR_CR3_SMPSEN | PWR_CR3_LDOEN | PWR_CR3_BYPASS);
|
||||
cr3_mask |= PWR_CR3_SMPSLEVEL_MASK << PWR_CR3_SMPSLEVEL_SHIFT;
|
||||
|
||||
/* Default, take in unconditional settings, will OR in the rest. */
|
||||
cr3_set = PWR_CR3_SMPSEN | (smps_level << PWR_CR3_SMPSLEVEL_SHIFT);
|
||||
if (supply_external) {
|
||||
cm3_assert(smps_level != PWR_CR3_SMPSLEVEL_VOS); /* Unsupported setting! */
|
||||
cr3_set |= PWR_CR3_SMPSEXTHP;
|
||||
}
|
||||
|
||||
if (use_ldo) {
|
||||
cr3_set |= PWR_CR3_LDOEN;
|
||||
}
|
||||
PWR_CR3 = (PWR_CR3 & ~cr3_mask) | cr3_set;
|
||||
}
|
||||
|
||||
void pwr_set_mode_bypass(void) {
|
||||
const uint32_t cr3_mask = (PWR_CR3_SMPSEN | PWR_CR3_LDOEN | PWR_CR3_BYPASS);
|
||||
PWR_CR3 = (PWR_CR3 & ~cr3_mask) | PWR_CR3_BYPASS;
|
||||
}
|
||||
|
||||
void pwr_set_mode_scu_bypass(void) {
|
||||
const uint32_t cr3_mask = (PWR_CR3_SCUEN | PWR_CR3_LDOEN | PWR_CR3_BYPASS);
|
||||
PWR_CR3 = (PWR_CR3 & ~cr3_mask) | (PWR_CR3_SCUEN | PWR_CR3_BYPASS);
|
||||
}
|
||||
|
||||
|
||||
void pwr_set_mode(enum pwr_sys_mode mode, uint8_t smps_level) {
|
||||
switch (mode) {
|
||||
case PWR_SYS_SCU_LDO:
|
||||
pwr_set_mode_scu_ldo();
|
||||
break;
|
||||
case PWR_SYS_SCU_BYPASS:
|
||||
pwr_set_mode_scu_bypass();
|
||||
break;
|
||||
case PWR_SYS_LDO:
|
||||
pwr_set_mode_ldo();
|
||||
break;
|
||||
case PWR_SYS_SMPS_DIRECT:
|
||||
case PWR_SYS_SMPS_LDO:
|
||||
pwr_set_mode_smps_ldo(false, PWR_CR3_SMPSLEVEL_VOS, mode == PWR_SYS_SMPS_LDO);
|
||||
break;
|
||||
case PWR_SYS_EXT_SMPS_LDO:
|
||||
case PWR_SYS_EXT_SMPS_LDO_BYP:
|
||||
pwr_set_mode_smps_ldo(false, smps_level, mode == PWR_SYS_EXT_SMPS_LDO);
|
||||
break;
|
||||
case PWR_SYS_BYPASS:
|
||||
pwr_set_mode_bypass();
|
||||
break;
|
||||
}
|
||||
/* Wait for power supply status to state ready. */
|
||||
while (!(PWR_CSR1 & PWR_CSR1_ACTVOSRDY));
|
||||
}
|
||||
|
||||
@@ -50,16 +115,49 @@ void pwr_set_svos_scale(enum pwr_svos_scale scale)
|
||||
}
|
||||
|
||||
void pwr_set_vos_scale(enum pwr_vos_scale scale) {
|
||||
rcc_periph_clock_enable(RCC_SYSCFG); /* Ensure we can access ODEN. */
|
||||
uint32_t d3cr_masked = PWR_D3CR & ~(PWR_D3CR_VOS_MASK << PWR_D3CR_VOS_SHIFT);
|
||||
static const uint8_t srdcr_vos_values[] = {
|
||||
PWR_SRDCR_VOS_SCALE_0,
|
||||
PWR_SRDCR_VOS_SCALE_1,
|
||||
PWR_SRDCR_VOS_SCALE_2,
|
||||
PWR_SRDCR_VOS_SCALE_3,
|
||||
};
|
||||
static const uint8_t d3cr_vos_values[] = {
|
||||
PWR_D3CR_VOS_SCALE_0,
|
||||
PWR_D3CR_VOS_SCALE_1,
|
||||
PWR_D3CR_VOS_SCALE_2,
|
||||
PWR_D3CR_VOS_SCALE_3,
|
||||
};
|
||||
cm3_assert(scale != PWR_VOS_SCALE_UNDEFINED); /* Make sure this has been set. */
|
||||
|
||||
/* Per the manual, VOS0 is implemented as VOS1 + ODEN. Handle this case. */
|
||||
if (scale == PWR_VOS_SCALE_0) {
|
||||
PWR_D3CR = d3cr_masked | PWR_VOS_SCALE_1;
|
||||
SYSCFG_PWRCR |= SYSCFG_PWRCR_ODEN;
|
||||
/* "SmartRun Domain" devices (presently only know of A3/B3/B0) have different mapping.
|
||||
* Note: DBGMCU_IDCODE_DEV_ID_STM32H7A3 covers all three of these models.
|
||||
*/
|
||||
uint32_t devid = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK;
|
||||
if (devid == DBGMCU_IDCODE_DEV_ID_STM32H7A3_B3_B0) {
|
||||
const uint32_t srdcr_vos_mask = (PWR_SRDCR_VOS_MASK << PWR_SRDCR_VOS_SHIFT);
|
||||
const uint32_t vos_value = srdcr_vos_values[scale - 1] << PWR_SRDCR_VOS_SHIFT;
|
||||
PWR_SRDCR = (PWR_SRDCR & ~srdcr_vos_mask) | vos_value;
|
||||
} else {
|
||||
SYSCFG_PWRCR &= ~SYSCFG_PWRCR_ODEN;
|
||||
PWR_D3CR = d3cr_masked | scale;
|
||||
/* Get the VOS value for the non-smart domain types. */
|
||||
uint32_t d3cr_vos = (uint32_t)d3cr_vos_values[scale - 1] << PWR_D3CR_VOS_SHIFT;
|
||||
uint32_t d3cr_masked = PWR_D3CR & ~(PWR_D3CR_VOS_MASK << PWR_D3CR_VOS_SHIFT);
|
||||
/* STM32H742/43/45/47/50/53/55/57 have special handling of VOS0, which is to set
|
||||
* VOS1, and also enable the ODEN in the SYSCFG_PWRCR.
|
||||
* Note: Conveniently, all devices with this setup share a devid, so pick one.
|
||||
*/
|
||||
if (devid == DBGMCU_IDCODE_DEV_ID_STM32H74X_5X) {
|
||||
rcc_periph_clock_enable(RCC_SYSCFG); /* Ensure we can access ODEN. */
|
||||
/* Per the manual, VOS0 is implemented as VOS1 + ODEN. Handle this case. */
|
||||
if (scale == PWR_VOS_SCALE_0) {
|
||||
PWR_D3CR = d3cr_masked | (PWR_D3CR_VOS_SCALE_1 << PWR_SRDCR_VOS_SHIFT);
|
||||
SYSCFG_PWRCR |= SYSCFG_PWRCR_ODEN;
|
||||
} else {
|
||||
SYSCFG_PWRCR &= ~SYSCFG_PWRCR_ODEN;
|
||||
PWR_D3CR = d3cr_masked | d3cr_vos;
|
||||
}
|
||||
} else {
|
||||
PWR_D3CR = d3cr_masked | d3cr_vos;
|
||||
}
|
||||
}
|
||||
while (!(PWR_D3CR & PWR_D3CR_VOSRDY));
|
||||
while (!(PWR_D3CR & PWR_D3CR_VOSRDY)); /* VOSRDY bit is same between D3CR and SRDCR. */
|
||||
}
|
||||
|
||||
@@ -185,8 +185,8 @@ void rcc_clock_setup_pll(const struct rcc_pll_config *config) {
|
||||
while (((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_HSI);
|
||||
RCC_CR = RCC_CR_HSION;
|
||||
|
||||
/* Now that we're safely running on HSI, let's setup the LDO. */
|
||||
pwr_set_mode_ldo();
|
||||
/* Now that we're safely running on HSI, let's setup the power system for scaling. */
|
||||
pwr_set_mode(config->power_mode, config->smps_level);
|
||||
pwr_set_vos_scale(config->voltage_scale);
|
||||
|
||||
/* Set flash waitstates. Enable flash prefetch if we have at least 1WS */
|
||||
|
||||
Reference in New Issue
Block a user