summaryrefslogtreecommitdiff
path: root/chip/stm32/clock-stm32l4.c
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2016-04-26 12:19:35 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-04-29 07:34:52 -0700
commit38c93a26f32c49f3cc6aa5ddb057f7a788633bfb (patch)
treebde43d2dcaca526f0aa6cec02876ad57d07fd2b4 /chip/stm32/clock-stm32l4.c
parent2b79492093935e3fe2aa2117216a819a3974d70b (diff)
downloadchrome-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>
Diffstat (limited to 'chip/stm32/clock-stm32l4.c')
-rw-r--r--chip/stm32/clock-stm32l4.c286
1 files changed, 236 insertions, 50 deletions
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);