diff --git a/include/libopencm3/stm32/h7/pwr.h b/include/libopencm3/stm32/h7/pwr.h index 64ea0e1b..091cfa5b 100644 --- a/include/libopencm3/stm32/h7/pwr.h +++ b/include/libopencm3/stm32/h7/pwr.h @@ -62,25 +62,71 @@ LGPL License Terms @ref lgpl_license #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) /** DBP[8]: Disable backup domain write protection. */ #define PWR_CR1_DBP (1 << 8) -/** PVDO: PVD output */ -#define PWR_CSR1_PVDO (1 << 4) +/** CSR1 Register Bits */ +#define PWR_CSR1_AVDO BIT16 +#define PWR_CSR1_ACTVOS_SHIFT 14 +#define PWR_CSR1_ACTVOSRDY BIT13 +#define PWR_CSR1_PVDO BIT4 + +/** CR3 Register Bits */ +#define PWR_CR3_USB33RDY BIT26 +#define PWR_CR3_USBREGEN BIT25 +#define PWR_CR3_USB33DEN BIT24 +#define PWR_CR3_VBRS BIT9 +#define PWR_CR3_VBE BIT8 +#define PWR_CR3_SCUEN 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_SCALE_3 (0x3) +#define PWR_D3CR_VOS_SCALE_2 (0x2) +#define PWR_D3CR_VOS_SCALE_1 (0x1) +#define PWR_D3CR_VOS_MASK (0x03) /* --- Function prototypes ------------------------------------------------- */ enum pwr_svos_scale { - PWR_SCALE3 = PWR_CR1_SVOS_SCALE_3 << PWR_CR1_SVOS_SHIFT, - PWR_SCALE4 = PWR_CR1_SVOS_SCALE_4 << PWR_CR1_SVOS_SHIFT, - PWR_SCALE5 = PWR_CR1_SVOS_SCALE_5 << PWR_CR1_SVOS_SHIFT, + PWR_SVOS_SCALE3 = PWR_CR1_SVOS_SCALE_3 << PWR_CR1_SVOS_SHIFT, + PWR_SVOS_SCALE4 = PWR_CR1_SVOS_SCALE_4 << PWR_CR1_SVOS_SHIFT, + PWR_SVOS_SCALE5 = PWR_CR1_SVOS_SCALE_5 << PWR_CR1_SVOS_SHIFT, +}; + +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) }; 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. + * @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. + * @param[in] scale Voltage scale value to set. + */ +void pwr_set_vos_scale(enum pwr_vos_scale scale); + + END_DECLS /**@}*/ diff --git a/include/libopencm3/stm32/h7/rcc.h b/include/libopencm3/stm32/h7/rcc.h index d9295369..1a2c1ce3 100644 --- a/include/libopencm3/stm32/h7/rcc.h +++ b/include/libopencm3/stm32/h7/rcc.h @@ -27,46 +27,38 @@ LGPL License Terms @ref lgpl_license #ifndef LIBOPENCM3_RCC_H #define LIBOPENCM3_RCC_H +#include #include /**@{*/ -/* --- RCC registers ------------------------------------------------------- */ +/** @defgroup rcc_regisers RCC Registers + * @ingroup rcc_defines +@{*/ #define RCC_CR MMIO32(RCC_BASE + 0x000) +#define RCC_ICSCR MMIO32(RCC_BASE + 0x004) /* Y-devices only */ +#define RCC_HSICFGR MMIO32(RCC_BASE + 0x004) /* V-devices only */ +#define RCC_CRRCR MMIO32(RCC_BASE + 0x008) +#define RCC_CSICFGR MMIO32(RCC_BASE + 0x00C) /* V-devices only */ #define RCC_CFGR MMIO32(RCC_BASE + 0x010) - -/** @addtogroup rcc_cr_values RCC_CR_VALUES - * @ingroup rcc_registers -@{*/ -#define RCC_CR_PLL3AIRDY (1 << 29) -#define RCC_CR_PLL3AION (1 << 28) -#define RCC_CR_PLL2RDY (1 << 27) -#define RCC_CR_PLL2ON (1 << 26) -#define RCC_CR_PLL1RDY (1 << 25) -#define RCC_CR_PLL1ON (1 << 24) -#define RCC_CR_HSECSSON (1 << 19) -#define RCC_CR_HSEBYP (1 << 18) -#define RCC_CR_HSERDY (1 << 17) -#define RCC_CR_HSEON (1 << 16) -#define RCC_CR_D2CKRDY (1 << 15) -#define RCC_CR_D1CKRDY (1 << 14) -#define RCC_CR_HSI48RDY (1 << 13) -#define RCC_CR_HSI48ON (1 << 12) -#define RCC_CR_CSIKERON (1 << 9) -#define RCC_CR_CSIRDY (1 << 8) -#define RCC_CR_CSION (1 << 7) -#define RCC_CR_HSIDIVF (1 << 5) -#define RCC_CR_HSIDIV_MASK (0x03) -#define RCC_CR_HSIDIV_SHIFT 3 -#define RCC_CR_HSIDIV(n) (((n) & RCC_CR_HSIDIV_MASK) << RCC_CR_HSIDIV_MASK) -#define RCC_CR_HSIRDY (1 << 2) -#define RCC_CR_HSIKERON (1 << 1) -#define RCC_CR_HSION (1 << 0) -/**@}*/ - -/** @addtogroup rcc_rstr_values RCC_RSTR_VALUES - * @ingroup rcc_registers -@{*/ +#define RCC_D1CFGR MMIO32(RCC_BASE + 0x018) +#define RCC_D2CFGR MMIO32(RCC_BASE + 0x01C) +#define RCC_D3CFGR MMIO32(RCC_BASE + 0x020) +#define RCC_PLLCKSELR MMIO32(RCC_BASE + 0x028) +#define RCC_PLLCFGR MMIO32(RCC_BASE + 0x02C) +/* PLLs are 1-based, so reference macros to 1..3, using index 0 will give undefined behavior. */ +#define RCC_PLLDIVR(n) MMIO32(RCC_BASE + 0x030 + (0x08 * ((n) - 1))) +#define RCC_PLLFRACR(n) MMIO32(RCC_BASE + 0x030 + (0x08 * ((n) - 1))) +#define RCC_PLL1DIVR RCC_PLLDIVR(1) +#define RCC_PLL1FRACR RCC_PLLFRACR(1) +#define RCC_PLL2DIVR RCC_PLLDIVR(2) +#define RCC_PLL2FRACR RCC_PLLFRACR(2) +#define RCC_PLL3DIVR RCC_PLLDIVR(3) +#define RCC_PLL3FRACR RCC_PLLFRACR(3) +#define RCC_D1CCIPR MMIO32(RCC_BASE + 0x04C) +#define RCC_D2CCIP1R MMIO32(RCC_BASE + 0x050) +#define RCC_D2CCIP2R MMIO32(RCC_BASE + 0x054) +#define RCC_D3CCIPR MMIO32(RCC_BASE + 0x058) #define RCC_AHB1RSTR MMIO32(RCC_BASE + 0x080) #define RCC_AHB2RSTR MMIO32(RCC_BASE + 0x084) #define RCC_AHB3RSTR MMIO32(RCC_BASE + 0x07C) @@ -102,7 +94,37 @@ LGPL License Terms @ref lgpl_license #define RCC_DCKCFGR2 MMIO32(RCC_BASE + 0x90) /**@}*/ -/** @addtogroup rcc_cfgr_values RCC_CFGR_VALUES +/** @defgroup rcc_cr_values RCC_CR Values + * @ingroup rcc_registers +@{*/ +#define RCC_CR_PLL3RDY BIT29 +#define RCC_CR_PLL3ON BIT28 +#define RCC_CR_PLL2RDY BIT27 +#define RCC_CR_PLL2ON BIT26 +#define RCC_CR_PLL1RDY BIT25 +#define RCC_CR_PLL1ON BIT24 +#define RCC_CR_HSECSSON BIT19 +#define RCC_CR_HSEBYP BIT18 +#define RCC_CR_HSERDY BIT17 +#define RCC_CR_HSEON BIT16 +#define RCC_CR_D2CKRDY BIT15 +#define RCC_CR_D1CKRDY BIT14 +#define RCC_CR_HSI48RDY BIT13 +#define RCC_CR_HSI48ON BIT12 +#define RCC_CR_CSIKERON BIT9 +#define RCC_CR_CSIRDY BIT8 +#define RCC_CR_CSION BIT7 +#define RCC_CR_HSIDIVF BIT5 +#define RCC_CR_HSIDIV_MASK (0x03) +#define RCC_CR_HSIDIV_SHIFT 3 +#define RCC_CR_HSIDIV(n) (((n) & RCC_CR_HSIDIV_MASK) << RCC_CR_HSIDIV_MASK) +#define RCC_CR_HSIRDY BIT2 +#define RCC_CR_HSIKERON BIT1 +#define RCC_CR_HSION BIT0 +/**@}*/ + + +/** @defgroup rcc_cfgr_values RCC_CFGR Values * @ingroup rcc_registers @{*/ /* MCO2: Microcontroller clock output 2 */ @@ -158,8 +180,134 @@ LGPL License Terms @ref lgpl_license #define RCC_CFGR_SW_PLL1 0x3 /**@}*/ +/** @defgroup rcc_d1cfgr_values RCC_D1CFGR Values + * @ingroup rcc_registers + * @{*/ +#define RCC_D1CFGR_D1CPRE_BYP 0x0 +#define RCC_D1CFGR_D1CPRE_DIV2 0x8 +#define RCC_D1CFGR_D1CPRE_DIV4 0x9 +#define RCC_D1CFGR_D1CPRE_DIV8 0xA +#define RCC_D1CFGR_D1CPRE_DIV16 0xB +#define RCC_D1CFGR_D1CPRE_DIV64 0xC +#define RCC_D1CFGR_D1CPRE_DIV128 0xD +#define RCC_D1CFGR_D1CPRE_DIV256 0xE +#define RCC_D1CFGR_D1CPRE_DIV512 0xF +#define RCC_D1CFGR_D1PPRE_BYP 0x0 +#define RCC_D1CFGR_D1PPRE_DIV2 0x4 +#define RCC_D1CFGR_D1PPRE_DIV4 0x5 +#define RCC_D1CFGR_D1PPRE_DIV8 0x6 +#define RCC_D1CFGR_D1PPRE_DIV16 0x7 +#define RCC_D1CFGR_D1HPRE_BYP 0x0 +#define RCC_D1CFGR_D1HPRE_DIV2 0x8 +#define RCC_D1CFGR_D1HPRE_DIV4 0x9 +#define RCC_D1CFGR_D1HPRE_DIV8 0xA +#define RCC_D1CFGR_D1HPRE_DIV16 0xB +#define RCC_D1CFGR_D1HPRE_DIV64 0xC +#define RCC_D1CFGR_D1HPRE_DIV128 0xD +#define RCC_D1CFGR_D1HPRE_DIV256 0xE +#define RCC_D1CFGR_D1HPRE_DIV512 0xF -/** @addtogroup rcc_bdcr_values RCC_BDCR_VALUES +#define RCC_D1CFGR_D1CPRE_SHIFT 8 +#define RCC_D1CFGR_D1PPRE_SHIFT 4 +#define RCC_D1CFGR_D1CPRE(cpre) (cpre << RCC_D1CFGR_D1CPRE_SHIFT) +#define RCC_D1CFGR_D1PPRE(ppre) (ppre << RCC_D1CFGR_D1PPRE_SHIFT) +#define RCC_D1CFGR_D1HPRE(hpre) (hpre) +/**@}*/ + +/** @defgroup rcc_d2cfgr_values RCC_D2CFGR Values + * @ingroup rcc_registers + * @{*/ +#define RCC_D2CFGR_D2PPRE_BYP 0x0 +#define RCC_D2CFGR_D2PPRE_DIV2 0x4 +#define RCC_D2CFGR_D2PPRE_DIV4 0x5 +#define RCC_D2CFGR_D2PPRE_DIV8 0x6 +#define RCC_D2CFGR_D2PPRE_DIV16 0x7 + +#define RCC_D2CFGR_D2PPRE2_SHIFT 8 +#define RCC_D2CFGR_D2PPRE1_SHIFT 4 +#define RCC_D2CFGR_D2PPRE2(ppre) (ppre << RCC_D2CFGR_D2PPRE2_SHIFT) +#define RCC_D2CFGR_D2PPRE1(ppre) (ppre << RCC_D2CFGR_D2PPRE1_SHIFT) +/**@}*/ + +/** @defgroup rcc_d3cfgr_values RCC_D3CFGR Values + * @ingroup rcc_registers + * @{*/ +#define RCC_D3CFGR_D3PPRE_BYP 0x0 +#define RCC_D3CFGR_D3PPRE_DIV2 0x4 +#define RCC_D3CFGR_D3PPRE_DIV4 0x5 +#define RCC_D3CFGR_D3PPRE_DIV8 0x6 +#define RCC_D3CFGR_D3PPRE_DIV16 0x7 +#define RCC_D3CFGR_D3PPRE_SHIFT 4 +#define RCC_D3CFGR_D3PPRE(ppre) (ppre << RCC_D3CFGR_D3PPRE_SHIFT) +/**@}*/ + +/** @defgroup rcc_pllckselr_values RCC_PLLCKSELR Values + * @ingroup rcc_registers + * @{*/ +#define RCC_PLLCKSELR_PLLSRC_HSI 0x0 +#define RCC_PLLCKSELR_PLLSRC_CSI 0x1 +#define RCC_PLLCKSELR_PLLSRC_HSE 0x2 +#define RCC_PLLCKSELR_PLLSRC_NONE 0x3 +#define RCC_PLLCKSELR_DIVM_DIS 0 +#define RCC_PLLCKSELR_DIVM_BYP 1 +#define RCC_PLLCKSELR_DIVM_MASK 0x3f + +#define RCC_PLLCKSELR_DIVM3_SHIFT 20 +#define RCC_PLLCKSELR_DIVM2_SHIFT 12 +#define RCC_PLLCKSELR_DIVM1_SHIFT 4 + +#define RCC_PLLCKSELR_DIVM3(n) ((n) << RCC_PLLCKSELR_DIVM3_SHIFT) +#define RCC_PLLCKSELR_DIVM2(n) ((n) << RCC_PLLCKSELR_DIVM2_SHIFT) +#define RCC_PLLCKSELR_DIVM1(n) ((n) << RCC_PLLCKSELR_DIVM1_SHIFT) +/**@}*/ + +/** @defgroup rcc_pllcfgr_values RCC_PLLCFGR Values + * @ingroup rcc_registers + * @{*/ +#define RCC_PLLCFGR_PLLRGE_1_2MHZ 0 +#define RCC_PLLCFGR_PLLRGE_2_4MHZ 1 +#define RCC_PLLCFGR_PLLRGE_4_8MHZ 2 +#define RCC_PLLCFGR_PLLRGE_8_16MHZ 3 + +#define RCC_PLLCFGR_DIVR3EN BIT24 +#define RCC_PLLCFGR_DIVQ3EN BIT23 +#define RCC_PLLCFGR_DIVP3EN BIT22 +#define RCC_PLLCFGR_DIVR2EN BIT21 +#define RCC_PLLCFGR_DIVQ2EN BIT20 +#define RCC_PLLCFGR_DIVP2EN BIT19 +#define RCC_PLLCFGR_DIVR1EN BIT18 +#define RCC_PLLCFGR_DIVQ1EN BIT17 +#define RCC_PLLCFGR_DIVP1EN BIT16 +#define RCC_PLLCFGR_PLL3RGE_SHIFT 10 +#define RCC_PLLCFGR_PLL3VCO_WIDE 0 /* 192 - 836MHz base output. */ +#define RCC_PLLCFGR_PLL3VCO_MED BIT9 /* 150 - 420MHz base output. */ +#define RCC_PLLCFGR_PLL3FRACEN BIT8 +#define RCC_PLLCFGR_PLL2RGE_SHIFT 6 +#define RCC_PLLCFGR_PLL2VCO_WIDE 0 /* 192 - 836MHz base output. */ +#define RCC_PLLCFGR_PLL2VCO_MED BIT5 /* 150 - 420MHz base output. */ +#define RCC_PLLCFGR_PLL2FRACEN BIT4 +#define RCC_PLLCFGR_PLL1RGE_SHIFT 2 +#define RCC_PLLCFGR_PLL1VCO_WIDE 0 /* 192 - 836MHz base output. */ +#define RCC_PLLCFGR_PLL1VCO_MED BIT1 /* 150 - 420MHz base output. */ +#define RCC_PLLCFGR_PLL1FRACEN BIT0 +/**@}*/ + +/** @defgroup rcc_plldivr_values RCC_PLLnDIVR Values + * @ingroup rcc_registers + * @{*/ +#define RCC_PLLNDIVR_DIVR_SHIFT 24 +#define RCC_PLLNDIVR_DIVQ_SHIFT 16 +#define RCC_PLLNDIVR_DIVP_SHIFT 9 +#define RCC_PLLNDIVR_DIVN_SHIFT 0 + +/* Need to preserve reserved bits, so give easy mask shortcut. */ +#define RCC_PLLNDIVR_DIVR(n) (((n) - 1) << RCC_PLLNDIVR_DIVR_SHIFT) +#define RCC_PLLNDIVR_DIVQ(n) (((n) - 1) << RCC_PLLNDIVR_DIVQ_SHIFT) +#define RCC_PLLNDIVR_DIVP(n) (((n) - 1) << RCC_PLLNDIVR_DIVP_SHIFT) +#define RCC_PLLNDIVR_DIVN(n) (((n) - 1) << RCC_PLLNDIVR_DIVN_SHIFT) +/**@}*/ + +/** @defgroup rcc_bdcr_values RCC_BDCR Values * @ingroup rcc_registers @{*/ #define RCC_BDCR_BDRST (1 << 16) @@ -181,16 +329,107 @@ LGPL License Terms @ref lgpl_license #define RCC_BDCR_LSEON (1 << 0) /**@}*/ -/** @addtogroup rcc_bdcr_values RCC_CSR_VALUES +/** @defgroup rcc_bdcr_values RCC_CSR Values. * @ingroup rcc_registers @{*/ #define RCC_CSR_LSIRDY (1 << 1) #define RCC_CSR_LSION (1 << 0) /**@}*/ -extern uint32_t rcc_ahb_frequency; -extern uint32_t rcc_apb1_frequency; -extern uint32_t rcc_apb2_frequency; +/** @defgroup rcc_d1ccipr_values RCC_D1CCIP1R Values + * @ingroup rcc_registers + * @{*/ +#define RCC_D1CCIPR_CKPERSEL_SHIFT 28 +#define RCC_D1CCIPR_CKPERSEL_HSI 0 +#define RCC_D1CCIPR_CKPERSEL_CSI 1 +#define RCC_D1CCIPR_CKPERSEL_HSE 2 +#define RCC_D1CCIPR_CKPERSEL_DISABLE 3 +#define RCC_D1CCIPR_CKPERSEL_MASK 3 +/**@}*/ + +/** @defgroup rcc_d2ccip1r_values RCC_D2CCIP1R Values + * @ingroup rcc_registers + * @{*/ +#define RCC_D2CCIP1R_SWPSEL_SHIFT 31 +#define RCC_D2CCIP1R_FDCANSEL_SHIFT 28 +#define RCC_D2CCIP1R_DFSDM1SEL_SHIFT 24 +#define RCC_D2CCIP1R_SPDIFSEL_SHIFT 20 +#define RCC_D2CCIP1R_SPI45SEL_SHIFT 16 +#define RCC_D2CCIP1R_SPI123SEL_SHIFT 12 +#define RCC_D2CCIP1R_SAI23SEL_SHIFT 6 + +#define RCC_D2CCIP1R_SWPSEL_PCLK 0x0 +#define RCC_D2CCIP1R_SWPSEL_HSI 0x1 +#define RCC_D2CCIP1R_FDCANSEL_HSE 0x0 +#define RCC_D2CCIP1R_FDCANSEL_PLL1Q 0x1 +#define RCC_D2CCIP1R_FDCANSEL_PLL2Q 0x2 +#define RCC_D2CCIP1R_FDCANSEL_MASK 0x3 +#define RCC_D2CCIP1R_DFSDM1SEL_PCLK2 0x0 +#define RCC_D2CCIP1R_DFSDM1SEL_SYSCLK 0x1 +#define RCC_D2CCIP1R_SPDIFSEL_PLL1Q 0x0 +#define RCC_D2CCIP1R_SPDIFSEL_PLL2R 0x1 +#define RCC_D2CCIP1R_SPDIFSEL_PLL3R 0x2 +#define RCC_D2CCIP1R_SPDIFSEL_HSI 0x3 +#define RCC_D2CCIP1R_SPI45SEL_APB4 0x0 +#define RCC_D2CCIP1R_SPI45SEL_PLL2Q 0x1 +#define RCC_D2CCIP1R_SPI45SEL_PLL3Q 0x2 +#define RCC_D2CCIP1R_SPI45SEL_HSI 0x3 +#define RCC_D2CCIP1R_SPI45SEL_CSI 0x4 +#define RCC_D2CCIP1R_SPI45SEL_HSE 0x5 +#define RCC_D2CCIP1R_SPI45SEL_MASK 0x7 +#define RCC_D2CCIP1R_SPI123SEL_PLL1Q 0x0 +#define RCC_D2CCIP1R_SPI123SEL_PLL2P 0x1 +#define RCC_D2CCIP1R_SPI123SEL_PLL3P 0x2 +#define RCC_D2CCIP1R_SPI123SEL_I2SCKIN 0x3 +#define RCC_D2CCIP1R_SPI123SEL_PERCK 0x4 +#define RCC_D2CCIP1R_SPI123SEL_MASK 0x7 +#define RCC_D2CCIP1R_SAISEL_PLL1Q 0x0 +#define RCC_D2CCIP1R_SAISEL_PLL2P 0x1 +#define RCC_D2CCIP1R_SAISEL_PLL3P 0x2 +#define RCC_D2CCIP1R_SAISEL_I2SCKIN 0x3 +#define RCC_D2CCIP1R_SAISEL_PERCK 0x4 +#define RCC_D2CCIP1R_SAISEL_MASK 0x7 +/**@}*/ + +/** @defgroup rcc_d2ccip2r_values RCC_D2CCIP2R Values + * @ingroup rcc_registers + * @{*/ +#define RCC_D2CCIP2R_LPTIM1SEL_SHIFT 28 +#define RCC_D2CCIP2R_CECSEL_SHIFT 22 +#define RCC_D2CCIP2R_USBSEL_SHIFT 20 +#define RCC_D2CCIP2R_I2C123SEL_SHIFT 12 +#define RCC_D2CCIP2R_RNGSEL_SHIFT 8 +#define RCC_D2CCIP2R_USART16SEL_SHIFT 3 +#define RCC_D2CCIP2R_USART234578SEL_SHIFT 0 + +#define RCC_D2CCIP2R_USART16SEL_PCLK2 0 +#define RCC_D2CCIP2R_USART234578SEL_PCLK1 0 +#define RCC_D2CCIP2R_USARTSEL_PLL2Q 1 +#define RCC_D2CCIP2R_USARTSEL_PLL3Q 2 +#define RCC_D2CCIP2R_USARTSEL_HSI 3 +#define RCC_D2CCIP2R_USARTSEL_CSI 4 +#define RCC_D2CCIP2R_USARTSEL_LSE 5 +#define RCC_D2CCIP2R_USARTSEL_MASK 7 +/**@}*/ + + +#define RCC_HSI_BASE_FREQUENCY 64000000UL + +/** Enumerations for clocks in the clock tree to allow user to get the current configuration of the + * clocks from the RCC module. These clock sources will each be tracked through the settings. + */ +enum rcc_clock_source { + RCC_CPUCLK, + RCC_SYSCLK, + RCC_PERCLK, + RCC_SYSTICKCLK, + RCC_HCLK3, + RCC_AHBCLK, /* AHB1,2,4 all share base HCLK. */ + RCC_APB1CLK, /* Note: APB1 and PCLK1 in manual */ + RCC_APB2CLK, /* Note: APB2 and PCLK2 in manual */ + RCC_APB3CLK, /* Note: APB3 and PCLK3 in manual */ + RCC_APB4CLK, /* Note: APB4 and PCLK4 in manual */ +}; enum rcc_osc { RCC_PLL, @@ -200,6 +439,37 @@ enum rcc_osc { RCC_LSI }; +enum rcc_sysclk_mux { + RCC_SYSCLK_PLL, + RCC_SYSCLK_HSE, + RCC_SYSCLK_HSI, +}; + +enum rcc_pll_mux { + RCC_PLL_HSI = RCC_PLLCKSELR_PLLSRC_HSI, + RCC_PLL_HSE = RCC_PLLCKSELR_PLLSRC_HSE +}; + +/** PLL Configuration structure. */ +struct rcc_pll_config { + uint32_t hse_frequency; /**< User configured external crystal frequency. */ + enum rcc_sysclk_mux sysclk_mux; /**< SYSCLK source input selection. */ + enum rcc_pll_mux pll_mux; /**< PLL source input selection. */ + struct pll_config { + uint8_t divm; /**< Pre-divider value for each PLL. 0-64 integers. */ + uint16_t divn; /**< Multiplier, 0-512 integer. */ + uint8_t divp; /**< Post divider for PLLP clock. */ + uint8_t divq; /**< Post divider for PLLQ clock. */ + uint8_t divr; /**< Post divider for PLLR clock. */ + } pll1, pll2, pll3; /**< PLL1-PLL3 configurations. */ + uint32_t d1cfg_core_prescale; /**< Core prescaler for domain 1. */ + uint32_t d1cfg_hclk3_prescale; /**< HCLK3 prescaler for domain 1. */ + uint32_t d1cfg_pclk3_prescale; /**< APB3 Peripheral prescaler for domain 1. */ + uint32_t d2cfg_pclk1_prescale; /**< APB1 Peripheral prescaler for domain 2. */ + uint32_t d2cfg_pclk2_prescale; /**< APB2 Peripheral prescaler for domain 2. */ + uint32_t d3cfg_pclk4_prescale; /**< APB4 Peripheral prescaler for domain 3. */ +}; + #define _REG_BIT(base, bit) (((base) << 5) + (bit)) enum rcc_periph_clken { @@ -429,9 +699,82 @@ enum rcc_periph_rst { }; #undef _REG_BIT +/**@}*/ + +/** @defgroup rcc_file RCC peripheral API + * + * @ingroup peripheral_apis + */ #include +BEGIN_DECLS + +/** + * Setup the base PLLs and clock domains for the STM32H7. This function will + * utilize the users input parameters to configure all 3 PLLs, as well as the + * core clock domains (namely SYSCLK, CPU, HCLK, AHB, PCLK1-4) with the + * specified dividers. Given the dividers, the RCC module will track the + * the configured frequency for each of these clock domains. + * + * Note: If clock sources, configs, divider, etc. are modified outside of + * this module, the frequency tracking may be undefined. + * Note: Clock tree is fairly complex, see RM0433 Section 7 + * for details. + * @param[in] config Input config structure defining desired setup. + */ +void rcc_clock_setup_pll(const struct rcc_pll_config *config); + +/** + * Get the clock rate (in hz) of the specified clock source. There are + * numerous clock sources and configurations on the H7, so rates for each + * configured peripheral clock are aimed to be discoverd/calculated by this + * module such that the user does not need to know how the MCU is configured + * in order to utilize a peripheral clock. + * @param[in] source Clock source desired to be fetched. + * @return Clock rate in Hz for the specified clock. 0 if undefined or error. + */ +uint32_t rcc_get_bus_clk_freq(enum rcc_clock_source source); + +/** + * Get the clock rate (in hz) of the specified peripheral. This will pull the + * proper sources out of the clock tree and calculate the clock for the + * peripheral for return to the user, based on current settings. + * @param[in] periph Peripheral base address to get the clock rate for. + * @return Clock rate in Hz for the specified peripheral. 0 if undefined or error. + */ +uint32_t rcc_get_peripheral_clk_freq(uint32_t periph); + +/** + * Set the clksel value for the specified peripheral. This code will determine + * the appropriate register, shift and mask values to apply to the selection to + * and set the values appropriately. + * @param[in] periph Base address of the peripheral to set the clock sel for. + * @param[in] Raw, unshifted selection value for the clock. + */ +void rcc_set_peripheral_clk_sel(uint32_t periph, uint32_t sel); + +/** + * Set the clock select for the FDCAN devices. + * @param[in] source Clock source to configure FDCAN kernel clock for. + * RCC_D2CCIP1R_FDCANSEL_XXX selections above. + */ +void rcc_set_fdcan_clksel(uint8_t fdcansel); +/** + * Set the clock select for the SPI 1/2/3 devices. + * @param[in] source Clock source desired to be fetched. Choose from + * RCC_D2CCIP1R_SPI123_XXX selections above. + */ +void rcc_set_spi123_clksel(uint8_t clksel); +/** + * Set the clock select for the SPI 4/5 devices. + * @param[in] source Clock source desired to be fetched. Choose from + * RCC_D2CCIP1R_SPI45_XXX selections above. + */ +void rcc_set_spi45_clksel(uint8_t clksel); + + +END_DECLS /**@}*/ #endif diff --git a/include/libopencm3/stm32/h7/syscfg.h b/include/libopencm3/stm32/h7/syscfg.h new file mode 100644 index 00000000..b6662c8e --- /dev/null +++ b/include/libopencm3/stm32/h7/syscfg.h @@ -0,0 +1,62 @@ +/** @defgroup syscfg_defines SYSCFG Defines + * + * @ingroup STM32H7xx_defines + * + * @brief Defined Constants and Types for the STM32H7xx System Configuration controller + * + * @version 1.0.0 + * + * @author @htmlonly © @endhtmlonly 2019 + * Brian Viele + * + * LGPL License Terms @ref lgpl_license + */ +/* + * This file is part of the libopencm3 project. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef LIBOPENCM3_SYSCFG_H +#define LIBOPENCM3_SYSCFG_H + +#include + +/**@{*/ +/**@defgroup syscfg_registers SYSCFG Registers + @{*/ +#define SYSCFG_PMCR MMIO32(SYSCFG_BASE + 0x04) +#define SYSCFG_EXTICR1 MMIO32(SYSCFG_BASE + 0x08) +#define SYSCFG_EXTICR2 MMIO32(SYSCFG_BASE + 0x0C) +#define SYSCFG_EXTICR3 MMIO32(SYSCFG_BASE + 0x10) +#define SYSCFG_EXTICR4 MMIO32(SYSCFG_BASE + 0x14) +#define SYSCFG_CFGR MMIO32(SYSCFG_BASE + 0x18) +#define SYSCFG_CCSR MMIO32(SYSCFG_BASE + 0x20) +#define SYSCFG_CCVR MMIO32(SYSCFG_BASE + 0x24) +#define SYSCFG_CCCR MMIO32(SYSCFG_BASE + 0x28) +#define SYSCFG_PWRCR MMIO32(SYSCFG_BASE + 0x2C) +#define SYSCFG_PKGR MMIO32(SYSCFG_BASE + 0x124) +#define SYSCFG_UR(n) MMIO32(SYSCFG_BASE + 0x300 + (4 * (n))) +/**@}*/ + +/** @defgroup syscfg_pwrcr PWRCR SYSCFG configuration register + * @ingroup syscfg_registers + * @{*/ +#define SYSCFG_PWRCR_ODEN BIT0 +/**@}*/ + + +/**@}*/ + +#endif diff --git a/include/libopencm3/stm32/syscfg.h b/include/libopencm3/stm32/syscfg.h index cbd07b27..a3c3bfdd 100644 --- a/include/libopencm3/stm32/syscfg.h +++ b/include/libopencm3/stm32/syscfg.h @@ -38,6 +38,8 @@ # include #elif defined(STM32G0) # include +#elif defined(STM32H7) +# include #else # error "stm32 family not defined." #endif diff --git a/lib/stm32/h7/pwr.c b/lib/stm32/h7/pwr.c index 4afeb909..8fd1da64 100644 --- a/lib/stm32/h7/pwr.c +++ b/lib/stm32/h7/pwr.c @@ -1,15 +1,9 @@ -/** @defgroup pwr_file PWR peripheral API - * - * @ingroup peripheral_apis - * +/** * @brief libopencm3 STM32H7xx Power Control * * @version 1.0.0 * - * @author @htmlonly © @endhtmlonly 2011 Stephen Caudle - * @author @htmlonly © @endhtmlonly 2017 Matthew Lai - * - * @date 12 March 2017 + * @date 16 December, 2019 * * This library supports the power control system for the * STM32H7 series of ARM Cortex Microcontrollers by ST Microelectronics. @@ -21,6 +15,7 @@ * * Copyright (C) 2011 Stephen Caudle * Copyright (C) 2017 Matthew Lai + * Copyright (C) 2019 Brian Viele * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -37,14 +32,34 @@ */ #include +#include +#include -/**@{*/ + +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); + while (!(PWR_CSR1 & PWR_CSR1_ACTVOSRDY)); +} void pwr_set_svos_scale(enum pwr_svos_scale scale) { uint32_t pwr_cr1_reg = PWR_CR1; - pwr_cr1_reg = (pwr_cr1_reg & ~PWR_CR1_SVOS_MASK) | scale; - PWR_CR1 = pwr_cr1_reg; + pwr_cr1_reg = (pwr_cr1_reg & ~(PWR_CR1_SVOS_MASK << PWR_CR1_SVOS_SHIFT)); + PWR_CR1 = pwr_cr1_reg | scale; } -/**@}*/ \ No newline at end of file +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); + + /* 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; + } else { + SYSCFG_PWRCR &= ~SYSCFG_PWRCR_ODEN; + PWR_D3CR = d3cr_masked | scale; + } + while (!(PWR_D3CR & PWR_D3CR_VOSRDY)); +} diff --git a/lib/stm32/h7/rcc.c b/lib/stm32/h7/rcc.c index 5069cc88..bc57c353 100644 --- a/lib/stm32/h7/rcc.c +++ b/lib/stm32/h7/rcc.c @@ -6,16 +6,401 @@ * * LGPL License Terms @ref lgpl_license */ - +#include #include #include #include #include -/**@{*/ +#define HZ_PER_MHZ 1000000UL +#define HZ_PER_KHZ 1000UL -uint32_t rcc_ahb_frequency = 64000000; -uint32_t rcc_apb1_frequency = 64000000; -uint32_t rcc_apb2_frequency = 64000000; +/* Local private copy of the clock configuration for providing user with clock tree data. */ +static struct { + uint16_t sysclk_mhz; + uint16_t cpu_mhz; + uint16_t hclk_mhz; + struct { + uint16_t pclk1_mhz; /* APB1 clock. */ + uint16_t pclk2_mhz; /* APB2 clock. */ + uint16_t pclk3_mhz; /* APB3 clock. */ + uint16_t pclk4_mhz; /* APB4 clock. */ + } per; + struct pll_clocks { /* Each PLL output set of data. */ + uint16_t p_mhz; + uint16_t q_mhz; + uint16_t r_mhz; + } pll1, pll2, pll3; + uint16_t hse_khz; /* This can't exceed 50MHz */ +} rcc_clock_tree = { + .sysclk_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ, + .cpu_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ, + .hclk_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ, + .per.pclk1_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ, + .per.pclk2_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ, + .per.pclk3_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ, + .per.pclk4_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ +}; -/**@}*/ +static void rcc_configure_pll(uint32_t clkin, const struct pll_config *config, int pll_num) { + /* Only concern ourselves with the PLL if the input clock is enabled. */ + if (config->divm == 0 || pll_num < 1 || pll_num > 3) { + return; + } + + struct pll_clocks *pll_tree_ptr; + if (pll_num == 1) { + pll_tree_ptr = &rcc_clock_tree.pll1; + } else if (pll_num == 2) { + pll_tree_ptr = &rcc_clock_tree.pll2; + } else { + pll_tree_ptr = &rcc_clock_tree.pll3; + } + + /* Let's write all of the dividers as specified. */ + RCC_PLLDIVR(pll_num) = 0; + RCC_PLLDIVR(pll_num) |= RCC_PLLNDIVR_DIVN(config->divn); + + /* Setup the PLL config values for this PLL. */ + uint8_t vco_addshift = 4 * (pll_num - 1); /* Values spaced by 4 for PLL 1/2/3 */ + + /* Set the PLL input frequency range. */ + uint32_t pll_clk_mhz = (clkin / config->divm) / HZ_PER_MHZ; + if (pll_clk_mhz > 2 && pll_clk_mhz <= 4) { + RCC_PLLCFGR |= (RCC_PLLCFGR_PLLRGE_2_4MHZ << RCC_PLLCFGR_PLL1RGE_SHIFT) << vco_addshift; + } else if (pll_clk_mhz > 4 && pll_clk_mhz <= 8) { + RCC_PLLCFGR |= (RCC_PLLCFGR_PLLRGE_4_8MHZ << RCC_PLLCFGR_PLL1RGE_SHIFT) << vco_addshift; + } else if (pll_clk_mhz > 8) { + RCC_PLLCFGR |= (RCC_PLLCFGR_PLLRGE_8_16MHZ << RCC_PLLCFGR_PLL1RGE_SHIFT) << vco_addshift; + } + + /* Set the VCO output frequency range. */ + uint32_t pll_vco_clk_mhz = (pll_clk_mhz * config->divn); + if (pll_vco_clk_mhz <= 420) { + RCC_PLLCFGR |= (RCC_PLLCFGR_PLL1VCO_MED << vco_addshift); + } + + /* Setup the enable bits for the PLL outputs. */ + uint8_t diven_addshift = 3 * (pll_num - 1); /* Values spaced by 3 for PLL1/2/3 */ + if (config->divp > 0) { + RCC_PLLDIVR(pll_num) |= RCC_PLLNDIVR_DIVP(config->divp); + RCC_PLLCFGR |= (RCC_PLLCFGR_DIVP1EN << diven_addshift); + pll_tree_ptr->p_mhz = pll_vco_clk_mhz / config->divp; + } + if (config->divq > 0) { + RCC_PLLDIVR(pll_num) |= RCC_PLLNDIVR_DIVQ(config->divq); + RCC_PLLCFGR |= (RCC_PLLCFGR_DIVQ1EN << diven_addshift); + pll_tree_ptr->q_mhz = pll_vco_clk_mhz / config->divq; + } + if (config->divr > 0) { + RCC_PLLDIVR(pll_num) |= RCC_PLLNDIVR_DIVR(config->divr); + RCC_PLLCFGR |= (RCC_PLLCFGR_DIVR1EN << diven_addshift); + pll_tree_ptr->r_mhz = pll_vco_clk_mhz / config->divr; + } + + /* Attempt to enable and lock PLL. */ + uint8_t cr_addshift = 2 * (pll_num - 1); + RCC_CR |= RCC_CR_PLL1ON << cr_addshift; + while (!(RCC_CR & (RCC_CR_PLL1RDY << cr_addshift))); +} + +static void rcc_set_and_enable_plls(const struct rcc_pll_config *config) { + /* It is assumed that this function is entered with PLLs disabled and not + * running. Setup PLL1/2/3 with configurations specified in the config. */ + RCC_PLLCKSELR = RCC_PLLCKSELR_DIVM1(config->pll1.divm) | + RCC_PLLCKSELR_DIVM2(config->pll2.divm) | + RCC_PLLCKSELR_DIVM3(config->pll3.divm) | + config->pll_mux; + + uint32_t clkin = (config->pll_mux == RCC_PLL_HSI) ? RCC_HSI_BASE_FREQUENCY + : config->hse_frequency; + + RCC_PLLCFGR = 0; + rcc_configure_pll(clkin, &config->pll1, 1); + rcc_configure_pll(clkin, &config->pll2, 2); + rcc_configure_pll(clkin, &config->pll3, 3); +} + +/* This is a helper to calculate dividers that go 2/4/8/16/64/128/256/512. + * These dividers also use the top bit as an "enable". */ +static uint16_t rcc_prediv_log_skip32_div(uint16_t clk_mhz, uint32_t div_val) { + if (div_val < 0x8) { + return clk_mhz; + } else if (div_val <= RCC_D1CFGR_D1CPRE_DIV16) { + return clk_mhz >> (div_val - 7); + } else { + return clk_mhz >> (div_val - 6); + } +} + +/* This is a helper to help calculate simple 3-bit log dividers with top bit + * used as enable bit. */ +static uint16_t rcc_prediv_3bit_log_div(uint16_t clk_mhz, uint32_t div_val) { + if (div_val < 0x4) { + return clk_mhz; + } else { + return clk_mhz >> (div_val - 3); + } +} + +static void rcc_clock_setup_domain1(const struct rcc_pll_config *config) { + RCC_D1CFGR = 0; + RCC_D1CFGR |= config->d1cfg_core_prescale | config->d1cfg_hclk3_prescale | + config->d1cfg_pclk3_prescale; + + /* Update our clock values in our tree based on the config values. */ + rcc_clock_tree.cpu_mhz = rcc_prediv_log_skip32_div(rcc_clock_tree.sysclk_mhz, + config->d1cfg_core_prescale >> RCC_D1CFGR_D1CPRE_SHIFT); + + rcc_clock_tree.hclk_mhz = rcc_prediv_log_skip32_div(rcc_clock_tree.cpu_mhz, + config->d1cfg_hclk3_prescale); + + rcc_clock_tree.per.pclk3_mhz = rcc_prediv_3bit_log_div(rcc_clock_tree.hclk_mhz, + config->d1cfg_pclk3_prescale >> RCC_D1CFGR_D1PPRE_SHIFT); +} + +static void rcc_clock_setup_domain2(const struct rcc_pll_config *config) { + RCC_D2CFGR = 0; + RCC_D2CFGR |= config->d2cfg_pclk1_prescale | config->d2cfg_pclk2_prescale; + + /* Update our clock values in our tree based on the config values. */ + rcc_clock_tree.per.pclk2_mhz = rcc_prediv_3bit_log_div(rcc_clock_tree.hclk_mhz, + config->d2cfg_pclk2_prescale >> RCC_D2CFGR_D2PPRE2_SHIFT); + rcc_clock_tree.per.pclk1_mhz = rcc_prediv_3bit_log_div(rcc_clock_tree.hclk_mhz, + config->d2cfg_pclk1_prescale >> RCC_D2CFGR_D2PPRE1_SHIFT); +} + +static void rcc_clock_setup_domain3(const struct rcc_pll_config *config) { + RCC_D3CFGR &= 0; + RCC_D3CFGR |= config->d3cfg_pclk4_prescale; + + /* Update our clock values in our tree based on the config values. */ + rcc_clock_tree.per.pclk4_mhz = rcc_prediv_3bit_log_div(rcc_clock_tree.hclk_mhz, + config->d3cfg_pclk4_prescale >> RCC_D3CFGR_D3PPRE_SHIFT); +} + +void rcc_clock_setup_pll(const struct rcc_pll_config *config) { + /* First, set system clock to utilize HSI, then disable all but HSI. */ + RCC_CR |= RCC_CR_HSION; + RCC_CFGR &= ~(RCC_CFGR_SW_MASK << RCC_CFGR_SW_SHIFT); + while (((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_HSI); + RCC_CR = RCC_CR_HSION; + + /* User has specified an external oscillator, make sure we turn it on. */ + if (config->hse_frequency > 0) { + RCC_CR |= RCC_CR_HSEON; + while (!(RCC_CR & RCC_CR_HSERDY)); + rcc_clock_tree.hse_khz = config->hse_frequency / HZ_PER_KHZ; + } + + /* Set, enable and lock all of the pll from the config. */ + rcc_set_and_enable_plls(config); + + /* Populate our base sysclk settings for use with domain clocks. */ + if (config->sysclk_mux == RCC_SYSCLK_PLL) { + rcc_clock_tree.sysclk_mhz = rcc_clock_tree.pll1.p_mhz; + } else if (config->sysclk_mux == RCC_SYSCLK_HSE) { + rcc_clock_tree.sysclk_mhz = config->hse_frequency / HZ_PER_MHZ; + } else { + rcc_clock_tree.sysclk_mhz = RCC_HSI_BASE_FREQUENCY / HZ_PER_MHZ; + } + + /* PLL's are set, now we need to get everything switched over the correct domains. */ + rcc_clock_setup_domain1(config); + rcc_clock_setup_domain2(config); + rcc_clock_setup_domain3(config); + + /* TODO: Configure custom kernel mappings. */ + + /* Domains dividers are all configured, now we can switchover to PLL. */ + RCC_CFGR |= RCC_CFGR_SW_PLL1; + uint32_t cfgr_sws = ((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK); + while(cfgr_sws != RCC_CFGR_SWS_PLL1) { + cfgr_sws = ((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK); + } +} + +uint32_t rcc_get_bus_clk_freq(enum rcc_clock_source source) { + uint32_t clksel; + switch (source) { + case RCC_SYSCLK: + return rcc_clock_tree.sysclk_mhz * HZ_PER_MHZ; + case RCC_CPUCLK: + case RCC_SYSTICKCLK: + return rcc_clock_tree.cpu_mhz * HZ_PER_MHZ; + case RCC_AHBCLK: + case RCC_HCLK3: + return rcc_clock_tree.hclk_mhz * HZ_PER_MHZ; + case RCC_APB1CLK: + return rcc_clock_tree.per.pclk1_mhz * HZ_PER_MHZ; + case RCC_APB2CLK: + return rcc_clock_tree.per.pclk2_mhz * HZ_PER_MHZ; + case RCC_APB3CLK: + return rcc_clock_tree.per.pclk3_mhz * HZ_PER_MHZ; + case RCC_APB4CLK: + return rcc_clock_tree.per.pclk4_mhz * HZ_PER_MHZ; + case RCC_PERCLK: + clksel = (RCC_D1CCIPR >> RCC_D1CCIPR_CKPERSEL_SHIFT) & RCC_D1CCIPR_CKPERSEL_MASK; + if (clksel == RCC_D1CCIPR_CKPERSEL_HSI) { + return RCC_HSI_BASE_FREQUENCY; + } else if (clksel == RCC_D1CCIPR_CKPERSEL_HSE) { + return rcc_clock_tree.hse_khz * HZ_PER_KHZ; + } else { + return 0U; + } + default: + cm3_assert_not_reached(); + return 0U; + } +} + +uint32_t rcc_get_peripheral_clk_freq(uint32_t periph) { + uint32_t clksel; + switch (periph) { + case FDCAN1_BASE: + case FDCAN2_BASE: + clksel = (RCC_D2CCIP1R >> RCC_D2CCIP1R_FDCANSEL_SHIFT) & RCC_D2CCIP1R_FDCANSEL_MASK; + if (clksel == RCC_D2CCIP1R_FDCANSEL_HSE) { + return rcc_clock_tree.hse_khz * HZ_PER_KHZ; + } else if (clksel == RCC_D2CCIP1R_FDCANSEL_PLL1Q) { + return rcc_clock_tree.pll1.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP1R_FDCANSEL_PLL2Q) { + return rcc_clock_tree.pll2.q_mhz * HZ_PER_MHZ; + } else { + return 0U; + } + case SPI1_BASE: + case SPI2_BASE: + case SPI3_BASE: + clksel = (RCC_D2CCIP1R >> RCC_D2CCIP1R_SPI123SEL_SHIFT) & RCC_D2CCIP1R_SPI123SEL_MASK; + if (clksel == RCC_D2CCIP1R_SPI123SEL_PLL1Q) { + return rcc_clock_tree.pll1.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP1R_SPI123SEL_PLL2P) { + return rcc_clock_tree.pll2.p_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP1R_SPI123SEL_PLL3P) { + return rcc_clock_tree.pll3.p_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP1R_SPI123SEL_PERCK) { + return rcc_get_bus_clk_freq(RCC_PERCLK); + } else { + return 0U; + } + case SPI4_BASE: + case SPI5_BASE: + clksel = (RCC_D2CCIP1R >> RCC_D2CCIP1R_SPI45SEL_SHIFT) & RCC_D2CCIP1R_SPI45SEL_MASK; + if (clksel == RCC_D2CCIP1R_SPI45SEL_APB4){ + return rcc_get_bus_clk_freq(RCC_APB1CLK); + } else if (clksel == RCC_D2CCIP1R_SPI45SEL_PLL2Q){ + return rcc_clock_tree.pll2.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP1R_SPI45SEL_PLL3Q){ + return rcc_clock_tree.pll3.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP1R_SPI45SEL_HSI){ + return RCC_HSI_BASE_FREQUENCY; + } else if (clksel == RCC_D2CCIP1R_SPI45SEL_HSE) { + return rcc_clock_tree.hse_khz * HZ_PER_KHZ; + } else { + return 0U; + } + case USART1_BASE: + case USART6_BASE: + clksel = (RCC_D2CCIP2R >> RCC_D2CCIP2R_USART16SEL_SHIFT) & RCC_D2CCIP2R_USARTSEL_MASK; + if (clksel == RCC_D2CCIP2R_USART16SEL_PCLK2) { + return rcc_get_bus_clk_freq(RCC_APB2CLK); + } else if (clksel == RCC_D2CCIP2R_USARTSEL_PLL2Q) { + return rcc_clock_tree.pll2.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP2R_USARTSEL_PLL3Q) { + return rcc_clock_tree.pll3.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP2R_USARTSEL_HSI) { + return RCC_HSI_BASE_FREQUENCY; + } else { + return 0U; + } + case USART2_BASE: + case USART3_BASE: + case UART4_BASE: + case UART5_BASE: + case UART7_BASE: + case UART8_BASE: + clksel = (RCC_D2CCIP2R >> RCC_D2CCIP2R_USART234578SEL_SHIFT) & RCC_D2CCIP2R_USARTSEL_MASK; + if (clksel == RCC_D2CCIP2R_USART234578SEL_PCLK1) { + return rcc_get_bus_clk_freq(RCC_APB1CLK); + } else if (clksel == RCC_D2CCIP2R_USARTSEL_PLL2Q) { + return rcc_clock_tree.pll2.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP2R_USARTSEL_PLL3Q) { + return rcc_clock_tree.pll3.q_mhz * HZ_PER_MHZ; + } else if (clksel == RCC_D2CCIP2R_USARTSEL_HSI) { + return RCC_HSI_BASE_FREQUENCY; + } else { + return 0U; + } + default: + cm3_assert_not_reached(); + return 0; + } +} + +void rcc_set_peripheral_clk_sel(uint32_t periph, uint32_t sel) { + volatile uint32_t *reg; + uint32_t mask; + uint32_t val; + + switch (periph) { + case FDCAN1_BASE: + case FDCAN2_BASE: + reg = &RCC_D2CCIP1R; + mask = RCC_D2CCIP1R_FDCANSEL_MASK << RCC_D2CCIP1R_FDCANSEL_SHIFT; + val = sel << RCC_D2CCIP1R_FDCANSEL_SHIFT; + break; + case SPI1_BASE: + case SPI2_BASE: + case SPI3_BASE: + reg = &RCC_D2CCIP2R; + mask = RCC_D2CCIP1R_SPI123SEL_MASK << RCC_D2CCIP1R_SPI123SEL_SHIFT; + val = sel << RCC_D2CCIP1R_SPI123SEL_SHIFT; + break; + case SPI4_BASE: + case SPI5_BASE: + reg = &RCC_D2CCIP1R; + mask = RCC_D2CCIP1R_SPI45SEL_MASK << RCC_D2CCIP1R_SPI45SEL_SHIFT; + val = sel << RCC_D2CCIP1R_SPI45SEL_SHIFT; + break; + case USART1_BASE: + case USART6_BASE: + reg = &RCC_D2CCIP2R; + mask = RCC_D2CCIP2R_USARTSEL_MASK << RCC_D2CCIP2R_USART16SEL_SHIFT; + val = sel << RCC_D2CCIP2R_USART16SEL_SHIFT; + break; + case USART2_BASE: + case USART3_BASE: + case UART4_BASE: + case UART5_BASE: + case UART7_BASE: + case UART8_BASE: + reg = &RCC_D2CCIP2R; + mask = RCC_D2CCIP2R_USARTSEL_MASK << RCC_D2CCIP2R_USART234578SEL_SHIFT; + val = sel << RCC_D2CCIP2R_USART234578SEL_SHIFT; + break; + + default: + cm3_assert_not_reached(); + return; + } + + // Update the register value by masking and oring in new values. + uint32_t regval = (*reg & mask) | val; + *reg = regval; +} + +void rcc_set_fdcan_clksel(uint8_t clksel) { + RCC_D2CCIP1R &= ~(RCC_D2CCIP1R_FDCANSEL_MASK << RCC_D2CCIP1R_FDCANSEL_SHIFT); + RCC_D2CCIP1R |= clksel << RCC_D2CCIP1R_FDCANSEL_SHIFT; +} + +void rcc_set_spi123_clksel(uint8_t clksel) { + RCC_D2CCIP1R &= ~(RCC_D2CCIP1R_SPI123SEL_MASK << RCC_D2CCIP1R_SPI123SEL_SHIFT); + RCC_D2CCIP1R |= clksel << RCC_D2CCIP1R_SPI123SEL_SHIFT; +} + +void rcc_set_spi45_clksel(uint8_t clksel) { + RCC_D2CCIP1R &= ~(RCC_D2CCIP1R_SPI45SEL_MASK << RCC_D2CCIP1R_SPI45SEL_SHIFT); + RCC_D2CCIP1R |= clksel << RCC_D2CCIP1R_SPI45SEL_SHIFT; +}