diff --git a/include/libopencm3/stm32/f1/rcc.h b/include/libopencm3/stm32/f1/rcc.h index c3efced0..f0634641 100644 --- a/include/libopencm3/stm32/f1/rcc.h +++ b/include/libopencm3/stm32/f1/rcc.h @@ -704,6 +704,45 @@ enum rcc_periph_rst { #include +enum rcc_clock_hsi { + RCC_CLOCK_HSI_24MHZ, + RCC_CLOCK_HSI_48MHZ, + RCC_CLOCK_HSI_64MHZ, + RCC_CLOCK_HSI_END +}; + +enum rcc_clock_hse { + RCC_CLOCK_HSE12_72MHZ, + RCC_CLOCK_HSE16_72MHZ, + RCC_CLOCK_HSE25_72MHZ, + RCC_CLOCK_HSE8_24MHZ, + RCC_CLOCK_HSE8_72MHZ, + RCC_CLOCK_HSE_END +}; + +/* Union of all options for f100 through to f107 */ +struct rcc_clock_scale { + uint8_t pll_mul; + uint8_t pll_source; + uint8_t hpre; + uint8_t ppre1; + uint8_t ppre2; + uint8_t adcpre; + uint8_t flash_waitstates; + uint8_t prediv1; /* aka xtpre, only one bit on smaller parts */ + uint8_t prediv1_source; + uint8_t prediv2; + uint8_t pll2_mul; + uint8_t pll3_mul; + uint8_t usbpre; + uint32_t ahb_frequency; + uint32_t apb1_frequency; + uint32_t apb2_frequency; +}; + +extern const struct rcc_clock_scale rcc_hsi_configs[RCC_CLOCK_HSI_END]; +extern const struct rcc_clock_scale rcc_hse_configs[RCC_CLOCK_HSE_END]; + BEGIN_DECLS void rcc_osc_ready_int_clear(enum rcc_osc osc); @@ -742,6 +781,14 @@ void rcc_clock_setup_in_hse_8mhz_out_72mhz(void); void rcc_clock_setup_in_hse_12mhz_out_72mhz(void); void rcc_clock_setup_in_hse_16mhz_out_72mhz(void); void rcc_clock_setup_in_hse_25mhz_out_72mhz(void); + +/** + * Switch sysclock to PLL with the given parameters. + * This should be usable from any point in time, but only if you have used + * library functions to manage clocks. + * @param clock full struct with desired parameters + */ +void rcc_clock_setup_pll(const struct rcc_clock_scale *clock); void rcc_backupdomain_reset(void); END_DECLS diff --git a/lib/stm32/f1/rcc.c b/lib/stm32/f1/rcc.c index ddd24e6a..78c4f840 100644 --- a/lib/stm32/f1/rcc.c +++ b/lib/stm32/f1/rcc.c @@ -58,6 +58,129 @@ uint32_t rcc_apb1_frequency = 8000000; uint32_t rcc_apb2_frequency = 8000000; uint32_t rcc_ahb_frequency = 8000000; +const struct rcc_clock_scale rcc_hse_configs[RCC_CLOCK_HSE_END] = { + { + /* hse-12, pll to 72 */ + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL6, + .prediv1 = RCC_CFGR2_PREDIV_NODIV, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_DIV2, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV6, + .flash_waitstates = 2, + .ahb_frequency = 72000000, + .apb1_frequency = 36000000, + .apb2_frequency = 72000000, + }, + { + /* hse16, pll to 72 */ + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL9, + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_DIV2, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV6, + .flash_waitstates = 2, + .prediv1 = RCC_CFGR2_PREDIV_DIV2, + .ahb_frequency = 72e6, + .apb1_frequency = 36e6, + .apb2_frequency = 72e6, + }, + { + /* hse25 to 72, this was a f105 config originally! intention preserved */ + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL9, + .pll_source = RCC_CFGR_PLLSRC_PREDIV1_CLK, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_DIV2, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV6, + .flash_waitstates = 2, + .prediv1 = RCC_CFGR2_PREDIV_DIV5, + .prediv1_source = RCC_CFGR2_PREDIV1SRC_PLL2_CLK, + .pll2_mul = RCC_CFGR2_PLL2MUL_PLL2_CLK_MUL8, + .prediv2 = RCC_CFGR2_PREDIV2_DIV5, + .usbpre = RCC_CFGR_USBPRE_PLL_VCO_CLK_DIV3, + .ahb_frequency = 72e6, + .apb1_frequency = 36e6, + .apb2_frequency = 72e6, + }, + { + /* hse8, pll to 24 (f100 value line max) */ + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL3, + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_NODIV, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV2, + .flash_waitstates = 0, + .prediv1 = RCC_CFGR2_PREDIV_NODIV, + .ahb_frequency = 24e6, + .apb1_frequency = 24e6, + .apb2_frequency = 24e6, + }, + { + /* hse8, pll to 72 */ + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL9, + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_DIV2, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV8, + .flash_waitstates = 2, + .prediv1 = RCC_CFGR2_PREDIV_NODIV, + .ahb_frequency = 72e6, + .apb1_frequency = 36e6, + .apb2_frequency = 72e6, + }, +}; + +const struct rcc_clock_scale rcc_hsi_configs[RCC_CLOCK_HSI_END] = { + { + /* hsi to 24Mhz, max for f100 */ + .pll_source = RCC_CFGR_PLLSRC_HSI_CLK_DIV2, + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL6, + .prediv1 = RCC_CFGR2_PREDIV_NODIV, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_NODIV, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV2, + .flash_waitstates = 0, + .ahb_frequency = 24e6, + .apb1_frequency = 24e6, + .apb2_frequency = 24e6, + }, + { + /* hsi to 48Mhz, allows usb, but out of spec */ + .pll_source = RCC_CFGR_PLLSRC_HSI_CLK_DIV2, + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL12, + .prediv1 = RCC_CFGR2_PREDIV_NODIV, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_DIV2, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV8, + .usbpre = RCC_CFGR_USBPRE_PLL_CLK_NODIV, + .flash_waitstates = 1, + .ahb_frequency = 48e6, + .apb1_frequency = 24e6, + .apb2_frequency = 48e6, + }, + { + /* hsi to 64Mhz, max possible from hsi */ + .pll_source = RCC_CFGR_PLLSRC_HSI_CLK_DIV2, + .pll_mul = RCC_CFGR_PLLMUL_PLL_CLK_MUL16, + .prediv1 = RCC_CFGR2_PREDIV_NODIV, + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_DIV2, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .adcpre = RCC_CFGR_ADCPRE_DIV8, + .flash_waitstates = 2, + .ahb_frequency = 64e6, + .apb1_frequency = 32e6, + .apb2_frequency = 64e6, + }, +}; + /*---------------------------------------------------------------------------*/ /** @brief RCC Clear the Oscillator Ready Interrupt Flag @@ -1087,6 +1210,69 @@ void rcc_clock_setup_in_hse_25mhz_out_72mhz(void) rcc_apb2_frequency = 72000000; } +void rcc_clock_setup_pll(const struct rcc_clock_scale *clock) +{ + if (clock->pll_source == RCC_CFGR_PLLSRC_HSE_CLK) { + rcc_osc_on(RCC_HSE); + rcc_wait_for_osc_ready(RCC_HSE); + } else { + rcc_osc_on(RCC_HSI); + rcc_wait_for_osc_ready(RCC_HSI); + } + rcc_set_hpre(clock->hpre); + rcc_set_ppre1(clock->ppre1); + rcc_set_ppre2(clock->ppre2); + rcc_set_adcpre(clock->adcpre); + rcc_set_usbpre(clock->usbpre); + flash_set_ws(clock->flash_waitstates); + + rcc_set_pll_multiplication_factor(clock->pll_mul); + rcc_set_pll_source(clock->pll_source); + + /* + * Magically handle F105/7 parts too. + * xtpre == prediv1 bit 0. + */ + if (clock->prediv1 > RCC_CFGR2_PREDIV_DIV2) { + rcc_set_prediv1(clock->prediv1); + } else { + rcc_set_pllxtpre(clock->prediv1); + } + if (clock->prediv1_source) { + rcc_set_prediv1_source(clock->prediv1_source); + } + + /* + * Magically handle other plls/prescalers on other parts + */ + if (clock->prediv2) { + rcc_set_prediv2(clock->prediv2); + } + if (clock->pll2_mul) { + rcc_set_pll2_multiplication_factor(clock->pll2_mul); + rcc_osc_on(RCC_PLL2); + rcc_wait_for_osc_ready(RCC_PLL2); + } + if (clock->pll3_mul) { + rcc_set_pll3_multiplication_factor(clock->pll3_mul); + rcc_osc_on(RCC_PLL3); + rcc_wait_for_osc_ready(RCC_PLL3); + } + + /* Enable PLL oscillator and wait for it to stabilize. */ + rcc_osc_on(RCC_PLL); + rcc_wait_for_osc_ready(RCC_PLL); + + /* Select PLL as SYSCLK source. */ + rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK); + + /* Set the peripheral clock frequencies used. */ + rcc_ahb_frequency = clock->ahb_frequency; + rcc_apb1_frequency = clock->apb1_frequency; + rcc_apb2_frequency = clock->apb2_frequency; +} + + /*---------------------------------------------------------------------------*/ /** @brief RCC Reset the Backup Domain