diff --git a/include/libopencm3/stm32/h7/memorymap.h b/include/libopencm3/stm32/h7/memorymap.h index ec892d21..875b28e3 100644 --- a/include/libopencm3/stm32/h7/memorymap.h +++ b/include/libopencm3/stm32/h7/memorymap.h @@ -154,4 +154,7 @@ #define TIM3_BASE 0x40000400U #define TIM2_BASE 0x40000000U +/* Debug/Trace Peripherals */ +#define DBGMCU_BASE 0x5C001000U + #endif diff --git a/include/libopencm3/stm32/h7/pwr.h b/include/libopencm3/stm32/h7/pwr.h index 714d6436..70c6d8fa 100644 --- a/include/libopencm3/stm32/h7/pwr.h +++ b/include/libopencm3/stm32/h7/pwr.h @@ -47,8 +47,12 @@ LGPL License Terms @ref lgpl_license /** CPU Power control register 3. */ #define PWR_CPUCR MMIO32(POWER_CONTROL_BASE + 0x10) -/** D3 Domain Power Control register. */ +/** D3 Domain Power Control register. + * Note: Referred to as PWR_SRDCR (SmartRun Domain Control) on LP devices. + * The VOS bitfield differs between the two implementations (unfortunately). + */ #define PWR_D3CR MMIO32(POWER_CONTROL_BASE + 0x18) +#define PWR_SRDCR MMIO32(POWER_CONTROL_BASE + 0x18) /** Wakeup Domain Power Control register. */ #define PWR_WKUPCR MMIO32(POWER_CONTROL_BASE + 0x20) @@ -57,39 +61,133 @@ LGPL License Terms @ref lgpl_license /** VOS[15:14]: Regulator voltage scaling output selection */ #define PWR_CR1_SVOS_SHIFT 14 +#define PWR_CR1_SVOS_MASK (0x3) #define PWR_CR1_SVOS_SCALE_3 (0x3) #define PWR_CR1_SVOS_SCALE_4 (0x2) #define PWR_CR1_SVOS_SCALE_5 (0x1) -#define PWR_CR1_SVOS_MASK (0x3) - +/** SmartRun domain AHB memory shut-off in DStop/DStop2 low-power mode */ +#define PWR_CR1_SRDRAMSO BIT27 +/** high-speed interfaces USB and FDCAN memory shut-off in DStop/DStop2 mode */ +#define PWR_CR1_HSITFSO BIT26 +/** GFXMMU and JPEG memory shut-off in DStop/DStop2 mode */ +#define PWR_CR1_GFXSO BIT25 +/** instruction TCM and ETM memory shut-off in DStop/DStop2 mode */ +#define PWR_CR1_ITCMSO BIT24 +/** AHB SRAM2 shut-off in DStop/DStop2 mode */ +#define PWR_CR1_AHBRAM2SO BIT23 +/** AHB SRAM1 shut-off in DStop/DStop2 mode */ +#define PWR_CR1_AHBRAM1SO BIT22 +/** AXI SRAM3 shut-off in DStop/DStop2 mode */ +#define PWR_CR1_AXIRAM3SO BIT21 +/** AXI SRAM2 shut-off in DStop/DStop2 mode */ +#define PWR_CR1_AXIRAM2SO BIT20 +/** AXI SRAM1 shut-off in DStop/DStop2 mode */ +#define PWR_CR1_AXIRAM1SO BIT19 +/** voltage threshold detected by the AVD. */ +#define PWR_CR1_ALS_SHIFT 17 +#define PWR_CR1_ALS_MASK 0x3 +#define PWR_CR1_ALS_1P7V 0x0 +#define PWR_CR1_ALS_2P1V 0x1 +#define PWR_CR1_ALS_2P5V 0x2 +#define PWR_CR1_ALS_2P8V 0x3 +/** peripheral voltage monitor on V DDA enable */ +#define PWR_CR1_AVDEN BIT16 +/** analog voltage ready + * This bit is only used when the analog switch boost needs to be enabled (see BOOSTE bit). + * It must be set by software when the expected V DDA analog supply level is available. + * The correct analog supply level is indicated by the AVDO bit (PWR_CSR1 register) after + * setting the AVDEN bit and selecting the supply level to be monitored (ALS bits). + */ +#define PWR_CR1_AVD_READY BIT13 +/** analog switch VBoost control + * This bit enables the booster to guarantee the analog switch AC performance when the V DD + * supply voltage is below 2.7 V (reduction of the total harmonic distortion to have the same + * switch performance over the full supply voltage range) + * The V DD supply voltage can be monitored through the PVD and the PLS bits. + */ +#define PWR_CR1_BOOSTE BIT12 /** DBP[8]: Disable backup domain write protection. */ -#define PWR_CR1_DBP (1 << 8) +#define PWR_CR1_DBP BIT8 /** CSR1 Register Bits */ +#define PWR_CSR1_MMCVDO BIT17 #define PWR_CSR1_AVDO BIT16 #define PWR_CSR1_ACTVOS_SHIFT 14 #define PWR_CSR1_ACTVOSRDY BIT13 #define PWR_CSR1_PVDO BIT4 +/** CR2 Register Bits */ +/** temperature level monitoring versus high threshold */ +#define PWR_CR2_TEMPH BIT23 +/** temperature level monitoring versus low threshold */ +#define PWR_CR2_TEMPL BIT22 +/** backup regulator ready */ +#define PWR_CR2_BRRDY BIT16 +/** V BAT and temperature monitoring enable */ +#define PWR_CR2_MONEN BIT4 +/** backup regulator enable */ +#define PWR_CR2_BREN BIT0 + /** CR3 Register Bits */ #define PWR_CR3_USB33RDY BIT26 #define PWR_CR3_USBREGEN BIT25 #define PWR_CR3_USB33DEN BIT24 +/** SMPS step-down converter external supply ready */ +#define PWR_CR3_SMPSEXTRDY BIT16 +/** V BAT charging resistor selection */ #define PWR_CR3_VBRS BIT9 +/** V BAT charging enable */ #define PWR_CR3_VBE BIT8 + +/** SMPS step-down converter voltage output level selection + * This setting is used when both the LDO and SMPS step-down converter are enabled with SMPSEN and + * LDOEN enabled or when SMPSEXTHP is enabled. In this case SMPSLEVEL must be written with + * a value different than 00 to reach the appropriate voltage, based on VOS or external supply. + */ +#define PWR_CR3_SMPSLEVEL_SHIFT 4 +#define PWR_CR3_SMPSLEVEL_MASK 0x3 +#define PWR_CR3_SMPSLEVEL_VOS 0x0 +#define PWR_CR3_SMPSLEVEL_1P8V 0x1 +#define PWR_CR3_SMPSLEVEL_2P5V 0x2 + +/** SMPS step-down converter external power delivery selection */ +#define PWR_CR3_SMPSEXTHP BIT3 #define PWR_CR3_SCUEN BIT2 +/* BIT2 Is overloaded on devices with SMPS as the SMPSEN bit. */ +#define PWR_CR3_SMPSEN BIT2 #define PWR_CR3_LDOEN BIT1 #define PWR_CR3_BYPASS BIT0 /** D3CR Register Bits */ -#define PWR_D3CR_VOS_SHIFT 14 #define PWR_D3CR_VOSRDY BIT13 - +#define PWR_D3CR_VOS_SHIFT 14 +#define PWR_D3CR_VOS_MASK (0x03) +/** VOS0 is implemented on STM32H72x/3x with simple VOS setting. + * STM32H742/43/45/47/50/53/55/57 SCALE0 this as SCALE1 + SYSCFG.ODEN */ +#define PWR_D3CR_VOS_SCALE_0 (0x0) #define PWR_D3CR_VOS_SCALE_3 (0x1) #define PWR_D3CR_VOS_SCALE_2 (0x2) #define PWR_D3CR_VOS_SCALE_1 (0x3) -#define PWR_D3CR_VOS_MASK (0x03) +/** SRDCR Register Bits */ +#define PWR_SRDCR_VOSRDY BIT13 +#define PWR_SRDCR_VOS_SHIFT 14 +#define PWR_SRDCR_VOS_MASK (0x03) +#define PWR_SRDCR_VOS_SCALE_3 (0x0) +#define PWR_SRDCR_VOS_SCALE_2 (0x1) +#define PWR_SRDCR_VOS_SCALE_1 (0x2) +#define PWR_SRDCR_VOS_SCALE_0 (0x3) + +enum pwr_sys_mode { + PWR_SYS_SCU_LDO = 0, /**< STM32H742/43/50/53 has special SCUEN handling, use for LDO. */ + PWR_SYS_SCU_BYPASS, /**< STM32H742/43/50/53 has special SCUEN handling, use for bypass. */ + PWR_SYS_LDO, /**< Devices with SMPS use this to run from LDO only. */ + PWR_SYS_SMPS_DIRECT, /**< Disable LDO, apply SMPS direct to CPU using VOS. */ + PWR_SYS_SMPS_LDO, /**< SMPS supplies internal LDO. */ + PWR_SYS_EXT_SMPS_LDO, /**< SMPS supplies external power, and CPU through LDO. */ + PWR_SYS_EXT_SMPS_LDO_BYP, /**< SMPS supplies external power, bypasses LDO (e.g. external LDO) */ + PWR_SYS_BYPASS /**< Disable all internal power supplies. */ +}; enum pwr_svos_scale { PWR_SVOS_SCALE3 = PWR_CR1_SVOS_SCALE_3 << PWR_CR1_SVOS_SHIFT, @@ -98,10 +196,11 @@ enum pwr_svos_scale { }; enum pwr_vos_scale { - PWR_VOS_SCALE_0 = 0, /* Note: This state requires SYSCFG ODEN set. */ - PWR_VOS_SCALE_1 = (PWR_D3CR_VOS_SCALE_1 << PWR_D3CR_VOS_SHIFT), - PWR_VOS_SCALE_2 = (PWR_D3CR_VOS_SCALE_2 << PWR_D3CR_VOS_SHIFT), - PWR_VOS_SCALE_3 = (PWR_D3CR_VOS_SCALE_3 << PWR_D3CR_VOS_SHIFT) + PWR_VOS_SCALE_UNDEFINED = 0, + PWR_VOS_SCALE_0, + PWR_VOS_SCALE_1, + PWR_VOS_SCALE_2, + PWR_VOS_SCALE_3, }; BEGIN_DECLS @@ -109,16 +208,37 @@ BEGIN_DECLS /** @defgroup pwr_peripheral_api PWR Peripheral API * @ingroup peripheral_apis @{*/ - /** Set power subsystem to utilize the LDO for CPU. */ void pwr_set_mode_ldo(void); -/** Set the voltage scaling/strength for the internal LDO when in Stop mode. +/** Specific STM32H742/43/50/53 LDO mode setting.. */ +void pwr_set_mode_scu_ldo(void); + +/** Set power subsystem to utilize the SMPS run through the LDO for CPU. + * @param[in] supply_external Supply is powering external circuits, enable high power mode. + * @param[in] smps_level Voltage level from available PWR_CR3_SMPSLEVEL_XXX settings (1.8V/2.5V) + * @param[in] use_ldo Set this value to true if the internal LDO should be enabled. + */ +void pwr_set_mode_smps_ldo(bool supply_external, uint32_t smps_level, bool use_ldo); + +/** Set power system based on "System Supply Configurations" table in reference manual. + * @param[in] mode Mode mapping to a mode in the system configuration. Note special SCU modes. + * @param[in] smps_level Optional, only needed if using an EXT_SMPS or SMPS_LDO mode. + */ +void pwr_set_mode(enum pwr_sys_mode mode, uint8_t smps_level); + +/** Set power subsystem to bypass all internal supplies. */ +void pwr_set_mode_bypass(void); + +/** Specific STM32H742/43/50/53 Bypsass mode setting.. */ +void pwr_set_mode_scu_bypass(void); + +/** Set the voltage scaling/strength for the internal SMPS/LDO when in Stop mode. * @param[in] scale Voltage scale value to set. */ void pwr_set_svos_scale(enum pwr_svos_scale scale); -/** Set the voltage scaling/strength for the internal LDO while running. +/** Set the voltage scaling/strength for the internal SMPS/LDO while running. * @param[in] scale Voltage scale value to set. */ void pwr_set_vos_scale(enum pwr_vos_scale scale); diff --git a/include/libopencm3/stm32/h7/rcc.h b/include/libopencm3/stm32/h7/rcc.h index 32a1058e..e9ca5be2 100644 --- a/include/libopencm3/stm32/h7/rcc.h +++ b/include/libopencm3/stm32/h7/rcc.h @@ -462,7 +462,9 @@ struct rcc_pll_config { uint8_t ppre3; /**< APB3 Peripheral prescaler note: domain 1. */ uint8_t ppre4; /**< APB4 Peripheral prescaler note: domain 3. */ uint8_t flash_waitstates; /**< Latency Value to set for flahs. */ - enum pwr_vos_scale voltage_scale; /**< LDO Voltage scale used for this frequency. */ + enum pwr_vos_scale voltage_scale; /**< LDO/SMPS Voltage scale used for this frequency. */ + enum pwr_sys_mode power_mode; /**< LDO/SMPS configuration for device. */ + uint8_t smps_level; /**< If using SMPS, voltage level to set. */ }; #define _REG_BIT(base, bit) (((base) << 5) + (bit)) diff --git a/lib/stm32/h7/pwr.c b/lib/stm32/h7/pwr.c index 8fd1da64..ce6e4baf 100644 --- a/lib/stm32/h7/pwr.c +++ b/lib/stm32/h7/pwr.c @@ -30,15 +30,80 @@ * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ - +#include +#include #include #include #include +/* 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. */ } diff --git a/lib/stm32/h7/rcc.c b/lib/stm32/h7/rcc.c index 71f7fae9..30f3b4bb 100644 --- a/lib/stm32/h7/rcc.c +++ b/lib/stm32/h7/rcc.c @@ -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 */