diff options
-rw-r--r-- | board/stm32l476g-eval/board.h | 13 | ||||
-rw-r--r-- | chip/stm32/clock-stm32l4.c | 286 | ||||
-rw-r--r-- | chip/stm32/config_chip.h | 5 | ||||
-rw-r--r-- | chip/stm32/registers.h | 22 |
4 files changed, 275 insertions, 51 deletions
diff --git a/board/stm32l476g-eval/board.h b/board/stm32l476g-eval/board.h index d6f9eb73b2..7ce7875ac6 100644 --- a/board/stm32l476g-eval/board.h +++ b/board/stm32l476g-eval/board.h @@ -40,7 +40,18 @@ #undef CONFIG_FLASH /* Timer selection */ -#define TIM_CLOCK32 5 +#define TIM_CLOCK32 5 + +/* External clock speeds (8 MHz) */ +#define STM32_HSE_CLOCK 8000000 + +/* PLL configuration. Freq = STM32_HSE_CLOCK * n/m/r */ +#undef STM32_PLLM +#define STM32_PLLM 1 +#undef STM32_PLLN +#define STM32_PLLN 10 +#undef STM32_PLLR +#define STM32_PLLR 2 #include "gpio_signal.h" diff --git a/chip/stm32/clock-stm32l4.c b/chip/stm32/clock-stm32l4.c index 7cbd098a4d..1e825e5c39 100644 --- a/chip/stm32/clock-stm32l4.c +++ b/chip/stm32/clock-stm32l4.c @@ -15,19 +15,18 @@ #include "util.h" /* High-speed oscillator is 16 MHz */ -#define HSI_CLOCK 16000000 -/* - * MSI is 2 MHz (default) 1 MHz, depending on ICSCR setting. We use 1 MHz - * because it's the lowest clock rate we can still run 115200 baud serial - * for the debug console. - */ -#define MSI_2MHZ_CLOCK (1 << 21) -#define MSI_1MHZ_CLOCK (1 << 20) +#define STM32_HSI_CLOCK 16000000 +/* Multi-speed oscillator is 4 MHz by default */ +#define STM32_MSI_CLOCK 4000000 enum clock_osc { OSC_INIT = 0, /* Uninitialized */ - OSC_HSI, /* High-speed oscillator */ - OSC_MSI, /* Med-speed oscillator @ 1 MHz */ + OSC_HSI, /* High-speed internal oscillator */ + OSC_MSI, /* Multi-speed internal oscillator */ +#ifdef STM32_HSE_CLOCK /* Allows us to catch absence of HSE at comiple time */ + OSC_HSE, /* High-speed external oscillator */ +#endif + OSC_PLL, /* PLL */ }; static int freq; @@ -51,13 +50,174 @@ void clock_wait_bus_cycles(enum bus_type bus, uint32_t cycles) } } +static void clock_enable_osc(enum clock_osc osc) +{ + uint32_t ready; + uint32_t on; + + switch (osc) { + case OSC_HSI: + ready = STM32_RCC_CR_HSIRDY; + on = STM32_RCC_CR_HSION; + break; + case OSC_MSI: + ready = STM32_RCC_CR_MSIRDY; + on = STM32_RCC_CR_MSION; + break; +#ifdef STM32_HSE_CLOCK + case OSC_HSE: + ready = STM32_RCC_CR_HSERDY; + on = STM32_RCC_CR_HSEON; + break; +#endif + case OSC_PLL: + ready = STM32_RCC_CR_PLLRDY; + on = STM32_RCC_CR_PLLON; + break; + default: + return; + } + + if (!(STM32_RCC_CR & ready)) { + /* Enable HSI */ + STM32_RCC_CR |= on; + /* Wait for HSI to be ready */ + while (!(STM32_RCC_CR & ready)) + ; + } +} + +/* Switch system clock oscillator */ +static void clock_switch_osc(enum clock_osc osc) +{ + uint32_t sw; + uint32_t sws; + + switch (osc) { + case OSC_HSI: + sw = STM32_RCC_CFGR_SW_HSI; + sws = STM32_RCC_CFGR_SWS_HSI; + break; + case OSC_MSI: + sw = STM32_RCC_CFGR_SW_MSI; + sws = STM32_RCC_CFGR_SWS_MSI; + break; +#ifdef STM32_HSE_CLOCK + case OSC_HSE: + sw = STM32_RCC_CFGR_SW_HSE; + sws = STM32_RCC_CFGR_SWS_HSE; + break; +#endif + case OSC_PLL: + sw = STM32_RCC_CFGR_SW_PLL; + sws = STM32_RCC_CFGR_SWS_PLL; + break; + default: + return; + } + + STM32_RCC_CFGR = sw; + while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) != sws) + ; +} + +/* + * Configure PLL for HSE + * + * 1. Disable the PLL by setting PLLON to 0 in RCC_CR. + * 2. Wait until PLLRDY is cleared. The PLL is now fully stopped. + * 3. Change the desired parameter. + * 4. Enable the PLL again by setting PLLON to 1. + * 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, PLLREN + * in RCC_PLLCFGR. + */ +static int stm32_configure_pll(enum clock_osc osc, + uint8_t m, uint8_t n, uint8_t r) +{ + uint32_t val; + int f; + + /* 1 */ + STM32_RCC_CR &= ~STM32_RCC_CR_PLLON; + + /* 2 */ + while (STM32_RCC_CR & STM32_RCC_CR_PLLRDY) + ; + + /* 3 */ + val = STM32_RCC_PLLCFGR; + + val &= ~STM32_RCC_PLLCFGR_PLLSRC_MASK; + switch (osc) { + case OSC_HSI: + val |= STM32_RCC_PLLCFGR_PLLSRC_HSI; + f = STM32_HSI_CLOCK; + break; + case OSC_MSI: + val |= STM32_RCC_PLLCFGR_PLLSRC_MSI; + f = STM32_MSI_CLOCK; + break; +#ifdef STM32_HSE_CLOCK + case OSC_HSE: + val |= STM32_RCC_PLLCFGR_PLLSRC_HSE; + f = STM32_HSE_CLOCK; + break; +#endif + default: + return -1; + } + + ASSERT(m > 0 && m < 9); + val &= ~STM32_RCC_PLLCFGR_PLLM_MASK; + val |= (m - 1) << STM32_RCC_PLLCFGR_PLLM_SHIFT; + + /* Max and min values are from TRM */ + ASSERT(n > 7 && n < 87); + val &= ~STM32_RCC_PLLCFGR_PLLN_MASK; + val |= n << STM32_RCC_PLLCFGR_PLLN_SHIFT; + + val &= ~STM32_RCC_PLLCFGR_PLLR_MASK; + switch (r) { + case 2: + val |= 0 << STM32_RCC_PLLCFGR_PLLR_SHIFT; + break; + case 4: + val |= 1 << STM32_RCC_PLLCFGR_PLLR_SHIFT; + break; + case 6: + val |= 2 << STM32_RCC_PLLCFGR_PLLR_SHIFT; + break; + case 8: + val |= 3 << STM32_RCC_PLLCFGR_PLLR_SHIFT; + break; + default: + return -1; + } + + STM32_RCC_PLLCFGR = val; + + /* 4 */ + clock_enable_osc(OSC_PLL); + + /* 5 */ + val = STM32_RCC_PLLCFGR; + val |= 1 << STM32_RCC_PLLCFGR_PLLREN_SHIFT; + STM32_RCC_PLLCFGR = val; + + /* (f * n) shouldn't overflow based on their max values */ + return (f * n / m / r); +} + /** - * Set which oscillator is used for the clock + * Set system clock oscillator * * @param osc Oscillator to use + * @param pll_osc Source oscillator for PLL. Ignored if osc is not PLL. */ -static void clock_set_osc(enum clock_osc osc) +static void clock_set_osc(enum clock_osc osc, enum clock_osc pll_osc) { + uint32_t val; + if (osc == current_osc) return; @@ -67,28 +227,18 @@ static void clock_set_osc(enum clock_osc osc) switch (osc) { case OSC_HSI: /* Ensure that HSI is ON */ - if (!(STM32_RCC_CR & STM32_RCC_CR_HSIRDY)) { - /* Enable HSI */ - STM32_RCC_CR |= STM32_RCC_CR_HSION; - /* Wait for HSI to be ready */ - while (!(STM32_RCC_CR & STM32_RCC_CR_HSIRDY)) - ; - } + clock_enable_osc(osc); /* Disable LPSDSR */ STM32_PWR_CR &= ~STM32_PWR_CR_LPSDSR; /* Switch to HSI */ - STM32_RCC_CFGR = STM32_RCC_CFGR_SW_HSI; - /* RM says to check SWS bits to make sure HSI is the sysclock */ - while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) != - STM32_RCC_CFGR_SWS_HSI) - ; + clock_switch_osc(osc); /* Disable MSI */ STM32_RCC_CR &= ~STM32_RCC_CR_MSION; - freq = HSI_CLOCK; + freq = STM32_HSI_CLOCK; break; case OSC_MSI: @@ -97,20 +247,10 @@ static void clock_set_osc(enum clock_osc osc) (STM32_RCC_ICSCR & ~STM32_RCC_ICSCR_MSIRANGE_MASK) | STM32_RCC_ICSCR_MSIRANGE_1MHZ; /* Ensure that MSI is ON */ - if (!(STM32_RCC_CR & STM32_RCC_CR_MSIRDY)) { - /* Enable MSI */ - STM32_RCC_CR |= STM32_RCC_CR_MSION; - /* Wait for MSI to be ready */ - while (!(STM32_RCC_CR & STM32_RCC_CR_MSIRDY)) - ; - } + clock_enable_osc(osc); /* Switch to MSI */ - STM32_RCC_CFGR = STM32_RCC_CFGR_SW_MSI; - /* RM says to check SWS bits to make sure MSI is the sysclock */ - while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) != - STM32_RCC_CFGR_SWS_MSI) - ; + clock_switch_osc(osc); /* Disable HSI */ STM32_RCC_CR &= ~STM32_RCC_CR_HSION; @@ -118,9 +258,48 @@ static void clock_set_osc(enum clock_osc osc) /* Enable LPSDSR */ STM32_PWR_CR |= STM32_PWR_CR_LPSDSR; - freq = MSI_1MHZ_CLOCK; + freq = STM32_MSI_CLOCK; break; +#ifdef STM32_HSE_CLOCK + case OSC_HSE: + /* Ensure that HSE is stable */ + clock_enable_osc(osc); + + /* Switch to HSE */ + clock_switch_osc(osc); + + /* Disable other clock sources */ + STM32_RCC_CR &= ~(STM32_RCC_CR_MSION | STM32_RCC_CR_HSION | + STM32_RCC_CR_PLLON); + + freq = STM32_HSE_CLOCK; + + break; +#endif + case OSC_PLL: + /* Ensure that source clock is stable */ + clock_enable_osc(pll_osc); + + /* Configure PLLCFGR */ + freq = stm32_configure_pll(pll_osc, STM32_PLLM, + STM32_PLLN, STM32_PLLR); + ASSERT(freq > 0); + + /* Adjust flash latency as instructed in TRM */ + val = STM32_FLASH_ACR; + val &= ~STM32_FLASH_ACR_LATENCY_MASK; + /* Flash 4 wait state. TODO: Should depend on freq. */ + val |= 4 << STM32_FLASH_ACR_LATENCY_SHIFT; + STM32_FLASH_ACR = val; + while (STM32_FLASH_ACR != val) + ; + + /* Switch to PLL */ + clock_switch_osc(osc); + + /* TODO: Disable other sources */ + break; default: break; } @@ -150,7 +329,7 @@ void clock_enable_module(enum module_id module, int enable) /* Flush UART before switching clock speed */ cflush(); - clock_set_osc(new_mask ? OSC_HSI : OSC_MSI); + clock_set_osc(new_mask ? OSC_HSI : OSC_MSI, OSC_INIT); } clock_mask = new_mask; @@ -158,14 +337,11 @@ void clock_enable_module(enum module_id module, int enable) void clock_init(void) { - /* - * The initial state : - * SYSCLK from MSI (=2MHz), no divider on AHB, APB1, APB2 - * PLL unlocked, RTC enabled on LSE - */ - - /* Switch to high-speed oscillator */ - clock_set_osc(OSC_HSI); +#ifdef STM32_HSE_CLOCK + clock_set_osc(OSC_PLL, OSC_HSE); +#else + clock_set_osc(OSC_HSI, OSC_INIT); +#endif } static void clock_chipset_startup(void) @@ -188,9 +364,15 @@ static int command_clock(int argc, char **argv) { if (argc >= 2) { if (!strcasecmp(argv[1], "hsi")) - clock_set_osc(OSC_HSI); + clock_set_osc(OSC_HSI, OSC_INIT); else if (!strcasecmp(argv[1], "msi")) - clock_set_osc(OSC_MSI); + clock_set_osc(OSC_MSI, OSC_INIT); +#ifdef STM32_HSE_CLOCK + else if (!strcasecmp(argv[1], "hse")) + clock_set_osc(OSC_HSE, OSC_INIT); + else if (!strcasecmp(argv[1], "pll")) + clock_set_osc(OSC_PLL, OSC_HSE); +#endif else return EC_ERROR_PARAM1; } @@ -199,6 +381,10 @@ static int command_clock(int argc, char **argv) return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(clock, command_clock, - "hsi | msi", + "hsi | msi" +#ifdef STM32_HSE_CLOCK + " | hse | pll" +#endif + , "Set clock frequency", NULL); diff --git a/chip/stm32/config_chip.h b/chip/stm32/config_chip.h index 80415e413a..04041828af 100644 --- a/chip/stm32/config_chip.h +++ b/chip/stm32/config_chip.h @@ -115,4 +115,9 @@ #define GPIO_PIN(port, index) GPIO_##port, (1 << index) #define GPIO_PIN_MASK(port, mask) GPIO_##port, (mask) +/* Prescaler values for PLL. Currently used only by STM32L476. */ +#define STM32_PLLM 0 +#define STM32_PLLN 0 +#define STM32_PLLR 0 + #endif /* __CROS_EC_CONFIG_CHIP_H */ diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index 70c846a683..f74de94e37 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -575,6 +575,10 @@ typedef volatile struct timer_ctlr timer_ctlr_t; #define STM32_RCC_CR_MSIRDY (1 << 1) #define STM32_RCC_CR_HSION (1 << 8) #define STM32_RCC_CR_HSIRDY (1 << 10) +#define STM32_RCC_CR_HSEON (1 << 16) +#define STM32_RCC_CR_HSERDY (1 << 17) +#define STM32_RCC_CR_PLLON (1 << 24) +#define STM32_RCC_CR_PLLRDY (1 << 25) #define STM32_RCC_ICSCR REG32(STM32_RCC_BASE + 0x04) #define STM32_RCC_ICSCR_MSIRANGE(n) ((n) << 13) @@ -594,6 +598,22 @@ typedef volatile struct timer_ctlr timer_ctlr_t; #define STM32_RCC_CFGR_SWS_PLL (3 << 2) #define STM32_RCC_CFGR_SWS_MASK (3 << 2) +#define STM32_RCC_PLLCFGR REG32(STM32_RCC_BASE + 0x0C) +#define STM32_RCC_PLLCFGR_PLLSRC_SHIFT (0) +#define STM32_RCC_PLLCFGR_PLLSRC_NONE (0 << STM32_RCC_PLLCFGR_PLLSRC_SHIFT) +#define STM32_RCC_PLLCFGR_PLLSRC_MSI (1 << STM32_RCC_PLLCFGR_PLLSRC_SHIFT) +#define STM32_RCC_PLLCFGR_PLLSRC_HSI (2 << STM32_RCC_PLLCFGR_PLLSRC_SHIFT) +#define STM32_RCC_PLLCFGR_PLLSRC_HSE (3 << STM32_RCC_PLLCFGR_PLLSRC_SHIFT) +#define STM32_RCC_PLLCFGR_PLLSRC_MASK (3 << STM32_RCC_PLLCFGR_PLLSRC_SHIFT) +#define STM32_RCC_PLLCFGR_PLLM_SHIFT (4) +#define STM32_RCC_PLLCFGR_PLLM_MASK (0x7 << STM32_RCC_PLLCFGR_PLLM_SHIFT) +#define STM32_RCC_PLLCFGR_PLLN_SHIFT (8) +#define STM32_RCC_PLLCFGR_PLLN_MASK (0x7f << STM32_RCC_PLLCFGR_PLLN_SHIFT) +#define STM32_RCC_PLLCFGR_PLLREN_SHIFT (24) +#define STM32_RCC_PLLCFGR_PLLREN_MASK (1 << STM32_RCC_PLLCFGR_PLLREN_SHIFT) +#define STM32_RCC_PLLCFGR_PLLR_SHIFT (25) +#define STM32_RCC_PLLCFGR_PLLR_MASK (3 << STM32_RCC_PLLCFGR_PLLR_SHIFT) + #define STM32_RCC_AHB1ENR REG32(STM32_RCC_BASE + 0x48) #define STM32_RCC_AHB1ENR_DMA1EN (1 << 0) #define STM32_RCC_AHB1ENR_DMA2EN (1 << 1) @@ -869,6 +889,8 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t; #define STM32_FLASH_REGS_BASE 0x40022000 #define STM32_FLASH_ACR REG32(STM32_FLASH_REGS_BASE + 0x00) +#define STM32_FLASH_ACR_LATENCY_SHIFT (0) +#define STM32_FLASH_ACR_LATENCY_MASK (7 << STM32_FLASH_ACR_LATENCY_SHIFT) #define STM32_FLASH_ACR_LATENCY (1 << 0) #define STM32_FLASH_ACR_PRFTEN (1 << 4) #define STM32_FLASH_KEYR REG32(STM32_FLASH_REGS_BASE + 0x04) |