summaryrefslogtreecommitdiff
path: root/chip/stm32/clock-stm32l4.c
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2016-04-20 14:49:56 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-04-25 16:49:02 -0700
commit40c02e3ff2477df1aca7657a92905816e5a13d0c (patch)
tree57f05231828259506bf8bfdd2c494e533715e01f /chip/stm32/clock-stm32l4.c
parentcb0d8108e5a5b630ec05a8d21a824cb601246bf5 (diff)
downloadchrome-ec-40c02e3ff2477df1aca7657a92905816e5a13d0c.tar.gz
Bring up STM32L476G-Eval
This patch adds initial set of files to bring up STM32L476G-Eval board. BUG=none BRANCH=tot TEST=Tested console. make buildall && make tests Change-Id: I0c0f73f31e84099746fced4214c5ed7f45468cef Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/340100 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'chip/stm32/clock-stm32l4.c')
-rw-r--r--chip/stm32/clock-stm32l4.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/chip/stm32/clock-stm32l4.c b/chip/stm32/clock-stm32l4.c
new file mode 100644
index 0000000000..7cbd098a4d
--- /dev/null
+++ b/chip/stm32/clock-stm32l4.c
@@ -0,0 +1,204 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Clocks and power management settings */
+
+#include "chipset.h"
+#include "clock.h"
+#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "hooks.h"
+#include "registers.h"
+#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)
+
+enum clock_osc {
+ OSC_INIT = 0, /* Uninitialized */
+ OSC_HSI, /* High-speed oscillator */
+ OSC_MSI, /* Med-speed oscillator @ 1 MHz */
+};
+
+static int freq;
+static int current_osc;
+
+int clock_get_freq(void)
+{
+ return freq;
+}
+
+void clock_wait_bus_cycles(enum bus_type bus, uint32_t cycles)
+{
+ volatile uint32_t dummy __attribute__((unused));
+
+ if (bus == BUS_AHB) {
+ while (cycles--)
+ dummy = STM32_DMA1_REGS->isr;
+ } else { /* APB */
+ while (cycles--)
+ dummy = STM32_USART_BRR(STM32_USART1_BASE);
+ }
+}
+
+/**
+ * Set which oscillator is used for the clock
+ *
+ * @param osc Oscillator to use
+ */
+static void clock_set_osc(enum clock_osc osc)
+{
+ if (osc == current_osc)
+ return;
+
+ if (current_osc != OSC_INIT)
+ hook_notify(HOOK_PRE_FREQ_CHANGE);
+
+ 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))
+ ;
+ }
+
+ /* 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)
+ ;
+
+ /* Disable MSI */
+ STM32_RCC_CR &= ~STM32_RCC_CR_MSION;
+
+ freq = HSI_CLOCK;
+ break;
+
+ case OSC_MSI:
+ /* Switch to MSI @ 1MHz */
+ STM32_RCC_ICSCR =
+ (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))
+ ;
+ }
+
+ /* 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)
+ ;
+
+ /* Disable HSI */
+ STM32_RCC_CR &= ~STM32_RCC_CR_HSION;
+
+ /* Enable LPSDSR */
+ STM32_PWR_CR |= STM32_PWR_CR_LPSDSR;
+
+ freq = MSI_1MHZ_CLOCK;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Notify modules of frequency change unless we're initializing */
+ if (current_osc != OSC_INIT) {
+ current_osc = osc;
+ hook_notify(HOOK_FREQ_CHANGE);
+ } else {
+ current_osc = osc;
+ }
+}
+
+void clock_enable_module(enum module_id module, int enable)
+{
+ static uint32_t clock_mask;
+ int new_mask;
+
+ if (enable)
+ new_mask = clock_mask | (1 << module);
+ else
+ new_mask = clock_mask & ~(1 << module);
+
+ /* Only change clock if needed */
+ if ((!!new_mask) != (!!clock_mask)) {
+
+ /* Flush UART before switching clock speed */
+ cflush();
+
+ clock_set_osc(new_mask ? OSC_HSI : OSC_MSI);
+ }
+
+ clock_mask = new_mask;
+}
+
+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);
+}
+
+static void clock_chipset_startup(void)
+{
+ /* Return to full speed */
+ clock_enable_module(MODULE_CHIPSET, 1);
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, clock_chipset_startup, HOOK_PRIO_DEFAULT);
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, clock_chipset_startup, HOOK_PRIO_DEFAULT);
+
+static void clock_chipset_shutdown(void)
+{
+ /* Drop to lower clock speed if no other module requires full speed */
+ clock_enable_module(MODULE_CHIPSET, 0);
+}
+DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, clock_chipset_shutdown, HOOK_PRIO_DEFAULT);
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, clock_chipset_shutdown, HOOK_PRIO_DEFAULT);
+
+static int command_clock(int argc, char **argv)
+{
+ if (argc >= 2) {
+ if (!strcasecmp(argv[1], "hsi"))
+ clock_set_osc(OSC_HSI);
+ else if (!strcasecmp(argv[1], "msi"))
+ clock_set_osc(OSC_MSI);
+ else
+ return EC_ERROR_PARAM1;
+ }
+
+ ccprintf("Clock frequency is now %d Hz\n", freq);
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(clock, command_clock,
+ "hsi | msi",
+ "Set clock frequency",
+ NULL);