From 2ca56f4c21094fd1da022efac9e3a0694d6b482b Mon Sep 17 00:00:00 2001 From: Brian Viele Date: Tue, 17 Dec 2019 02:07:55 -0500 Subject: [PATCH] stm32h7: updates to PWR and RCC to support PLL configuration. PLL configuration on the H7 is pretty involved, and takes a number of configurations to make it work. In order to make peripheral drivers a bit easier to implement, working with a soft clock tree in the rcc module which stores the clock settings for each clock as they are setup such that users can request the clock value from the RCC module for configuration. Added getter for the clock which allows the user to pass the base address of the peripheral, and get the peripheral clock value for convenience. Clock configuration is still missing values for setting up all of the kernel clocks for the peripherals, but this is in work, and there is a framework to do so. Have tested to 400MHz without issue. Peripherals that are explicitly supported are working and the clock tree values appear to follow correctly. Added LDO settings to allow setting the scaling to support high frequencies. --- include/libopencm3/stm32/h7/pwr.h | 56 +++- include/libopencm3/stm32/h7/rcc.h | 423 ++++++++++++++++++++++++--- include/libopencm3/stm32/h7/syscfg.h | 62 ++++ include/libopencm3/stm32/syscfg.h | 2 + lib/stm32/h7/pwr.c | 39 ++- lib/stm32/h7/rcc.c | 397 ++++++++++++++++++++++++- 6 files changed, 916 insertions(+), 63 deletions(-) create mode 100644 include/libopencm3/stm32/h7/syscfg.h 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 + +#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) +/**@}*/ -/** @addtogroup rcc_bdcr_values RCC_BDCR_VALUES +/** @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 + +/* 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; +} -uint32_t rcc_ahb_frequency = 64000000; -uint32_t rcc_apb1_frequency = 64000000; -uint32_t rcc_apb2_frequency = 64000000; +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; +}