stm32l1: rcc: reliably clock up/down

When changing the system clock, you must take care to not exceed the
legal ranges based on voltage and flash wait states.

Existing code made it possible to provide a valid clock structure, that
would run out of bounds temporarily.  Some boards would crash with
various Usage faults / Bus errors due to this.
This commit is contained in:
Karl Palsson 2016-08-19 11:26:18 +00:00
parent 0869e15718
commit 095ed8511a

View File

@ -497,31 +497,46 @@ void rcc_clock_setup_msi(const struct rcc_clock_scale *clock)
rcc_apb2_frequency = clock->apb2_frequency;
}
/**
* Switch sysclock to HSI with the given parameters.
* This should be usable from any point in time, but only if you have used
* library functions to manage clocks. It relies on the global
* @ref rcc_ahb_frequency to ensure that it reliably scales voltage up or down
* as appropriate.
* @param clock full struct with desired parameters
*/
void rcc_clock_setup_hsi(const struct rcc_clock_scale *clock)
{
/* Enable internal high-speed oscillator. */
rcc_osc_on(RCC_HSI);
rcc_wait_for_osc_ready(RCC_HSI);
/* Select HSI as SYSCLK source. */
rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
/*
* Set prescalers for AHB, ADC, ABP1, ABP2.
* Do this before touching the PLL (TODO: why?).
*/
rcc_set_hpre(clock->hpre);
rcc_set_ppre1(clock->ppre1);
rcc_set_ppre2(clock->ppre2);
rcc_periph_clock_enable(RCC_PWR);
pwr_set_vos_scale(clock->voltage_scale);
/* I guess this should be in the settings? */
flash_64bit_enable();
flash_prefetch_enable();
/* Configure flash settings. */
/* Don't try and go to fast for a voltage range! */
if (clock->ahb_frequency > rcc_ahb_frequency) {
/* Going up, power up first */
pwr_set_vos_scale(clock->voltage_scale);
rcc_set_hpre(clock->hpre);
rcc_set_ppre1(clock->ppre1);
rcc_set_ppre2(clock->ppre2);
flash_set_ws(clock->flash_config);
} else {
/* going down, slow down before cutting power */
rcc_set_hpre(clock->hpre);
rcc_set_ppre1(clock->ppre1);
rcc_set_ppre2(clock->ppre2);
flash_set_ws(clock->flash_config);
pwr_set_vos_scale(clock->voltage_scale);
}
rcc_wait_for_osc_ready(RCC_HSI);
while (PWR_CSR & PWR_CSR_VOSF)
;
rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
/* Set the peripheral clock frequencies used. */
rcc_ahb_frequency = clock->ahb_frequency;