From 3122c0b33ff4304d0323127911687751ddb8481f Mon Sep 17 00:00:00 2001 From: Vlad Logyin Date: Sun, 28 Nov 2021 11:08:57 +0000 Subject: [PATCH] stm32l4: rcc: Implement PLL helper as seen in other MCU families Reviewed-by: Karl Palsson --- include/libopencm3/stm32/l4/rcc.h | 104 +++++++++++++++++++++--------- lib/stm32/l4/rcc.c | 102 ++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 34 deletions(-) diff --git a/include/libopencm3/stm32/l4/rcc.h b/include/libopencm3/stm32/l4/rcc.h index af430107..f85791c3 100644 --- a/include/libopencm3/stm32/l4/rcc.h +++ b/include/libopencm3/stm32/l4/rcc.h @@ -39,16 +39,25 @@ #ifndef LIBOPENCM3_RCC_H #define LIBOPENCM3_RCC_H -/* --- RCC registers ------------------------------------------------------- */ +#include +/** @defgroup rcc_registers RCC Registers + * @brief Reset / Clock Control Registers + @{*/ + /** Clock control register */ #define RCC_CR MMIO32(RCC_BASE + 0x00) #define RCC_ICSCR MMIO32(RCC_BASE + 0x04) + /** Clock Configuration register */ #define RCC_CFGR MMIO32(RCC_BASE + 0x08) +/** PLL Configuration register */ #define RCC_PLLCFGR MMIO32(RCC_BASE + 0x0c) #define RCC_PLLSAI1_CFGR MMIO32(RCC_BASE + 0x10) #define RCC_PLLSAI2_CFGR MMIO32(RCC_BASE + 0x14) +/** Clock interrupt enable register */ #define RCC_CIER MMIO32(RCC_BASE + 0x18) +/** Clock interrupt flag resiger */ #define RCC_CIFR MMIO32(RCC_BASE + 0x1c) +/** Clock interrupt clear register */ #define RCC_CICR MMIO32(RCC_BASE + 0x20) #define RCC_AHB1RSTR_OFFSET 0x28 #define RCC_AHB1RSTR MMIO32(RCC_BASE + RCC_AHB1RSTR_OFFSET) @@ -87,13 +96,18 @@ #define RCC_APB2SMENR_OFFSET 0x80 #define RCC_APB2SMENR MMIO32(RCC_BASE + RCC_APB2SMENR_OFFSET) #define RCC_CCIPR MMIO32(RCC_BASE + 0x88) +/** Backup Domain control register */ #define RCC_BDCR MMIO32(RCC_BASE + 0x90) +/** Clock control and status register */ #define RCC_CSR MMIO32(RCC_BASE + 0x94) #define RCC_CRRCR MMIO32(RCC_BASE + 0x98) #define RCC_CCIPR2 MMIO32(RCC_BASE + 0x9C) +/** @}*/ -/* --- RCC_CR values ------------------------------------------------------- */ - +/** @defgroup rcc_cr_values RCC_CR values + * @ingroup rcc_registers + * @brief Clock Control register values + @{*/ #define RCC_CR_PLLSAI2RDY (1 << 29) #define RCC_CR_PLLSAI2ON (1 << 28) #define RCC_CR_PLLSAI1RDY (1 << 27) @@ -108,6 +122,8 @@ #define RCC_CR_HSIRDY (1 << 10) #define RCC_CR_HSIKERON (1 << 9) #define RCC_CR_HSION (1 << 8) +/** @}*/ + /** @defgroup rcc_cr_msirange MSI Range * @ingroup STM32L4xx_rcc_defines * @brief Range of the MSI oscillator @@ -180,36 +196,35 @@ Twelve frequency ranges are available: 100 kHz, 200 kHz, 400 kHz, 800 kHz, #define RCC_CFGR_STOPWUCK_MSI (0 << 15) #define RCC_CFGR_STOPWUCK_HSI16 (1 << 15) -/* PPRE2: APB high-speed prescaler (APB2) */ -#define RCC_CFGR_PPRE2_NODIV 0x0 -#define RCC_CFGR_PPRE2_DIV2 0x4 -#define RCC_CFGR_PPRE2_DIV4 0x5 -#define RCC_CFGR_PPRE2_DIV8 0x6 -#define RCC_CFGR_PPRE2_DIV16 0x7 -#define RCC_CFGR_PPRE2_MASK 0x7 -#define RCC_CFGR_PPRE2_SHIFT 11 +#define RCC_CFGR_PPRE1_SHIFT 8 +#define RCC_CFGR_PPRE1_MASK 0x7 +#define RCC_CFGR_PPRE2_SHIFT 11 +#define RCC_CFGR_PPRE2_MASK 0x7 +/** @defgroup rcc_cfgr_apbxpre RCC_CFGR APBx prescale factors + * These can be used for both APB1 and APB2 prescaling + * @{ + */ +#define RCC_CFGR_PPRE_NODIV 0x0 +#define RCC_CFGR_PPRE_DIV2 0x4 +#define RCC_CFGR_PPRE_DIV4 0x5 +#define RCC_CFGR_PPRE_DIV8 0x6 +#define RCC_CFGR_PPRE_DIV16 0x7 +/**@}*/ -/* PPRE1: APB low-speed prescaler (APB1) */ -#define RCC_CFGR_PPRE1_NODIV 0x0 -#define RCC_CFGR_PPRE1_DIV2 0x4 -#define RCC_CFGR_PPRE1_DIV4 0x5 -#define RCC_CFGR_PPRE1_DIV8 0x6 -#define RCC_CFGR_PPRE1_DIV16 0x7 -#define RCC_CFGR_PPRE1_MASK 0x7 -#define RCC_CFGR_PPRE1_SHIFT 8 - -/* HPRE: AHB prescaler */ -#define RCC_CFGR_HPRE_NODIV 0x0 -#define RCC_CFGR_HPRE_DIV2 0x8 -#define RCC_CFGR_HPRE_DIV4 0x9 -#define RCC_CFGR_HPRE_DIV8 0xa -#define RCC_CFGR_HPRE_DIV16 0xb -#define RCC_CFGR_HPRE_DIV64 0xc -#define RCC_CFGR_HPRE_DIV128 0xd -#define RCC_CFGR_HPRE_DIV256 0xe -#define RCC_CFGR_HPRE_DIV512 0xf -#define RCC_CFGR_HPRE_MASK 0xf -#define RCC_CFGR_HPRE_SHIFT 4 +#define RCC_CFGR_HPRE_SHIFT 4 +#define RCC_CFGR_HPRE_MASK 0xf +/** @defgroup rcc_cfgr_ahbpre RCC_CFGR AHB prescale factors +@{*/ +#define RCC_CFGR_HPRE_NODIV 0x0 +#define RCC_CFGR_HPRE_DIV2 (0x8 + 0) +#define RCC_CFGR_HPRE_DIV4 (0x8 + 1) +#define RCC_CFGR_HPRE_DIV8 (0x8 + 2) +#define RCC_CFGR_HPRE_DIV16 (0x8 + 3) +#define RCC_CFGR_HPRE_DIV64 (0x8 + 4) +#define RCC_CFGR_HPRE_DIV128 (0x8 + 5) +#define RCC_CFGR_HPRE_DIV256 (0x8 + 6) +#define RCC_CFGR_HPRE_DIV512 (0x8 + 7) +/**@}*/ /* SWS: System clock switch status */ #define RCC_CFGR_SWS_MSI 0x0 @@ -725,6 +740,30 @@ Twelve frequency ranges are available: 100 kHz, 200 kHz, 400 kHz, 800 kHz, #define RCC_CSR_LSIRDY (1 << 1) #define RCC_CSR_LSION (1 << 0) +struct rcc_clock_scale { + uint8_t pllm; + uint16_t plln; + uint8_t pllp; + uint8_t pllq; + uint8_t pllr; + uint8_t pll_source; + uint32_t flash_config; + uint8_t hpre; + uint8_t ppre1; + uint8_t ppre2; + enum pwr_vos_scale voltage_scale; + uint32_t ahb_frequency; + uint32_t apb1_frequency; + uint32_t apb2_frequency; +}; + +enum rcc_clock_config_entry { + RCC_CLOCK_VRANGE1_80MHZ, + RCC_CLOCK_CONFIG_END +}; + +extern const struct rcc_clock_scale rcc_hsi16_configs[RCC_CLOCK_CONFIG_END]; + /* --- Variable definitions ------------------------------------------------ */ extern uint32_t rcc_ahb_frequency; @@ -981,6 +1020,7 @@ void rcc_set_ppre1(uint32_t ppre1); void rcc_set_hpre(uint32_t hpre); void rcc_set_main_pll(uint32_t source, uint32_t pllm, uint32_t plln, uint32_t pllp, uint32_t pllq, uint32_t pllr); uint32_t rcc_system_clock_source(void); +void rcc_clock_setup_pll(const struct rcc_clock_scale *clock); void rcc_set_msi_range(uint32_t msi_range); void rcc_set_msi_range_standby(uint32_t msi_range); void rcc_pll_output_enable(uint32_t pllout); diff --git a/lib/stm32/l4/rcc.c b/lib/stm32/l4/rcc.c index d90db8b2..c35a1896 100644 --- a/lib/stm32/l4/rcc.c +++ b/lib/stm32/l4/rcc.c @@ -38,12 +38,34 @@ /**@{*/ #include #include +#include +#include /* Set the default clock frequencies after reset. */ uint32_t rcc_ahb_frequency = 4000000; uint32_t rcc_apb1_frequency = 4000000; uint32_t rcc_apb2_frequency = 4000000; +const struct rcc_clock_scale rcc_hsi16_configs[RCC_CLOCK_CONFIG_END] = { + { /* 80MHz PLL from HSI16 VR1 */ + .pllm = 4, + .plln = 40, + .pllp = RCC_PLLCFGR_PLLP_DIV7, + .pllq = RCC_PLLCFGR_PLLQ_DIV6, + .pllr = RCC_PLLCFGR_PLLR_DIV2, + .pll_source = RCC_PLLCFGR_PLLSRC_HSI16, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_NODIV, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .voltage_scale = PWR_SCALE1, + .flash_config = FLASH_ACR_DCEN | FLASH_ACR_ICEN | + FLASH_ACR_LATENCY_4WS, + .ahb_frequency = 80000000, + .apb1_frequency = 80000000, + .apb2_frequency = 80000000, + }, +}; + void rcc_osc_ready_int_clear(enum rcc_osc osc) { switch (osc) { @@ -341,6 +363,82 @@ uint32_t rcc_system_clock_source(void) return (RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK; } +/** + * Setup clocks to run from PLL. + * + * The arguments provide the pll source, multipliers, dividers, all that's + * needed to establish a system clock. + * + * @param clock clock information structure. + */ +void rcc_clock_setup_pll(const struct rcc_clock_scale *clock) +{ + /* Enable internal high-speed oscillator (HSI16). */ + rcc_osc_on(RCC_HSI16); + rcc_wait_for_osc_ready(RCC_HSI16); + + /* Select HSI16 as SYSCLK source. */ + rcc_set_sysclk_source(RCC_PLLCFGR_PLLSRC_HSI16); + + /* Enable external high-speed oscillator (HSE). */ + if (clock->pll_source == RCC_PLLCFGR_PLLSRC_HSE) { + rcc_osc_on(RCC_HSE); + rcc_wait_for_osc_ready(RCC_HSE); + } + + /* Set the VOS scale mode */ + rcc_periph_clock_enable(RCC_PWR); + pwr_set_vos_scale(clock->voltage_scale); + + /* + * Set prescalers for AHB, ADC, APB1, APB2. + * Do this before touching the PLL (TODO: why?). + */ + rcc_set_hpre(clock->hpre); + rcc_set_ppre1(clock->ppre1); + rcc_set_ppre2(clock->ppre2); + + /* Disable PLL oscillator before changing its configuration. */ + rcc_osc_off(RCC_PLL); + + /* Configure the PLL oscillator. */ + rcc_set_main_pll(clock->pll_source, clock->pllm, clock->plln, + clock->pllp, clock->pllq, clock->pllr); + + /* Enable PLL oscillator and wait for it to stabilize. */ + rcc_osc_on(RCC_PLL); + rcc_wait_for_osc_ready(RCC_PLL); + + /* Configure flash settings. */ + if (clock->flash_config & FLASH_ACR_DCEN) { + flash_dcache_enable(); + } else { + flash_dcache_disable(); + } + if (clock->flash_config & FLASH_ACR_ICEN) { + flash_icache_enable(); + } else { + flash_icache_disable(); + } + flash_set_ws(clock->flash_config); + + /* Select PLL as SYSCLK source. */ + rcc_set_sysclk_source(RCC_CFGR_SW_PLL); + + /* Wait for PLL clock to be selected. */ + rcc_wait_for_sysclk_status(RCC_PLL); + + /* Set the peripheral clock frequencies used. */ + rcc_ahb_frequency = clock->ahb_frequency; + rcc_apb1_frequency = clock->apb1_frequency; + rcc_apb2_frequency = clock->apb2_frequency; + + /* Disable internal high-speed oscillator. */ + if (clock->pll_source == RCC_PLLCFGR_PLLSRC_HSE) { + rcc_osc_off(RCC_HSI16); + } +} + /** * Set the msi run time range. * Can only be called when MSI is either OFF, or when MSI is on _and_ @@ -497,11 +595,11 @@ uint32_t rcc_get_timer_clk_freq(uint32_t timer) } } else if (timer >= TIM2_BASE && timer <= TIM7_BASE) { uint8_t ppre1 = (RCC_CFGR >> RCC_CFGR_PPRE1_SHIFT) & RCC_CFGR_PPRE1_MASK; - return (ppre1 == RCC_CFGR_PPRE1_NODIV) ? rcc_apb1_frequency + return (ppre1 == RCC_CFGR_PPRE_NODIV) ? rcc_apb1_frequency : 2 * rcc_apb1_frequency; } else { uint8_t ppre2 = (RCC_CFGR >> RCC_CFGR_PPRE2_SHIFT) & RCC_CFGR_PPRE2_MASK; - return (ppre2 == RCC_CFGR_PPRE2_NODIV) ? rcc_apb2_frequency + return (ppre2 == RCC_CFGR_PPRE_NODIV) ? rcc_apb2_frequency : 2 * rcc_apb2_frequency; } cm3_assert_not_reached();