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 */