diff options
author | Daisuke Nojiri <dnojiri@chromium.org> | 2016-04-26 12:19:35 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-04-29 07:34:52 -0700 |
commit | 38c93a26f32c49f3cc6aa5ddb057f7a788633bfb (patch) | |
tree | bde43d2dcaca526f0aa6cec02876ad57d07fd2b4 | |
parent | 2b79492093935e3fe2aa2117216a819a3974d70b (diff) | |
download | chrome-ec-38c93a26f32c49f3cc6aa5ddb057f7a788633bfb.tar.gz |
STM32: Add HSE and PLL to clock source selection
This patch adds HSE and PLL as a system clock oscillator for STM32L4.
This allows us to drive the chip at a higher frequency (up to 80 MHz),
which is necessary to big-bang GPIO ports accurately.
BUG=none
BRANCH=tot
TEST=make buildall. Verified console works on STM32L476G-Eval using HSE,
PLL-HSE, PLL-HSI, PLL-MSI as an oscillator. Verified console runs soundly
with different frequencies from 20 Mhz to 80 Mhz. Verified frequencies
using oscilloscope on MCO (Microcontroller Clock Output) port up to 50 MHz.
Change-Id: I493cdb6c323eb4e6a1560f6d030935c1950b1a2a
Reviewed-on: https://chromium-review.googlesource.com/341275
Commit-Ready: Daisuke Nojiri <dnojiri@chromium.org>
Tested-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-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) |