summaryrefslogtreecommitdiff
path: root/chip/lm4/clock.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-04-06 09:33:41 -0700
committerRandall Spangler <rspangler@chromium.org>2012-04-09 10:33:35 -0700
commite9328ac4f63351b4282916034270aa86b7e74922 (patch)
treed0fde5840594c33709927a5a4d1fabfe0f858c26 /chip/lm4/clock.c
parent1a9a415cf68c6e8e3b31972c072c81ed886290ab (diff)
downloadchrome-ec-e9328ac4f63351b4282916034270aa86b7e74922.tar.gz
Support dynamically changing the system clock
Add nopll command to turn off the PLL, reducing the system clock to 16Mhz. Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=chrome-os-partner:8798 TEST=manual boot system press power button to boot x86 temps // should print all temperatures timerinfo timerinfo timerinfo // convince yourself this is counting up at about 1MHz nopll // this drops the system clock to 16MHz temps // should still print all temperatures timerinfo timerinfo timerinfo // should still be counting up at about 1MHz Change-Id: Ie29ceb17af348148bffadf63d60c1b731f4c3f6d
Diffstat (limited to 'chip/lm4/clock.c')
-rw-r--r--chip/lm4/clock.c164
1 files changed, 107 insertions, 57 deletions
diff --git a/chip/lm4/clock.c b/chip/lm4/clock.c
index c80f36ecc1..694f1bc1b9 100644
--- a/chip/lm4/clock.c
+++ b/chip/lm4/clock.c
@@ -5,41 +5,74 @@
/* Clocks and power management settings */
-#include <stdint.h>
-
#include "board.h"
#include "clock.h"
#include "config.h"
#include "console.h"
#include "gpio.h"
+#include "registers.h"
+#include "system.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
-#include "registers.h"
#include "util.h"
-/**
- * Idle task
- * executed when no task are ready to be scheduled
- */
-void __idle(void)
+#define PLL_CLOCK 66666667 /* System clock = 200MHz PLL/3 = 66.667MHz */
+
+/* Disable the PLL; run off internal oscillator. */
+static void clock_disable_pll(void)
{
- while (1) {
- /* wait for the irq event */
- asm("wfi");
- /* TODO more power management here */
- }
+ /* Switch to 16MHz internal oscillator and power down the PLL */
+ LM4_SYSTEM_RCC = LM4_SYSTEM_RCC_SYSDIV(0) |
+ LM4_SYSTEM_RCC_BYPASS |
+ LM4_SYSTEM_RCC_PWRDN |
+ LM4_SYSTEM_RCC_OSCSRC(1) |
+ LM4_SYSTEM_RCC_MOSCDIS;
+ LM4_SYSTEM_RCC2 &= ~LM4_SYSTEM_RCC2_USERCC2;
+}
+
+
+/* Enable the PLL to run at full clock speed */
+static void clock_enable_pll(void)
+{
+ /* Disable the PLL so we can reconfigure it */
+ clock_disable_pll();
+
+ /* Enable the PLL (PWRDN is no longer set) and set divider. PLL is
+ * still bypassed, since it hasn't locked yet. */
+ LM4_SYSTEM_RCC = LM4_SYSTEM_RCC_SYSDIV(2) |
+ LM4_SYSTEM_RCC_USESYSDIV |
+ LM4_SYSTEM_RCC_BYPASS |
+ LM4_SYSTEM_RCC_OSCSRC(1) |
+ LM4_SYSTEM_RCC_MOSCDIS;
+
+ /* Wait for the PLL to lock */
+ clock_wait_cycles(1024);
+ while (!(LM4_SYSTEM_PLLSTAT & 1))
+ ;
+
+ /* Remove bypass on PLL */
+ LM4_SYSTEM_RCC &= ~LM4_SYSTEM_RCC_BYPASS;
}
-/* simple busy waiting before clocks are initialized */
-static void wait_cycles(uint32_t cycles)
+
+void clock_wait_cycles(uint32_t cycles)
{
asm("1: subs %0, #1\n"
" bne 1b\n" :: "r"(cycles));
}
-/**
- * Function to measure baseline for power consumption.
+
+int clock_get_freq(void)
+{
+ return (LM4_SYSTEM_PLLSTAT & 1) ? PLL_CLOCK : INTERNAL_CLOCK;
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+/* Function to measure baseline for power consumption.
*
* Levels :
* 0 : CPU running in tight loop
@@ -47,8 +80,7 @@ static void wait_cycles(uint32_t cycles)
* 2 : CPU in sleep mode
* 3 : CPU in sleep mode and peripherals gated
* 4 : CPU in deep sleep mode
- * 5 : CPU in deep sleep mode and peripherals gated
- */
+ * 5 : CPU in deep sleep mode and peripherals gated */
static int command_sleep(int argc, char **argv)
{
int level = 0;
@@ -146,53 +178,71 @@ static int command_sleep(int argc, char **argv)
DECLARE_CONSOLE_COMMAND(sleep, command_sleep);
-static void clock_init_pll(uint32_t value)
+/* TODO: temporary holding place for notifying modules of clock change. Should
+ * be moved to main.c after we finish measuring the power savings, so the clock
+ * frequency is automatically dropped after verified boot. */
+#include "i2c.h"
+#include "hwtimer.h"
+#include "peci.h"
+#include "watchdog.h"
+
+static int command_disable_pll(int argc, char **argv)
{
- /**
- * at startup, OSCSRC is PIOSC (precision internal oscillator)
- * PLL and PLL2 are in power-down
- */
+ int freq;
- /* PLL already setup */
- if (LM4_SYSTEM_PLLSTAT & 1)
- return;
+ clock_disable_pll();
- /* Put a bypass on the system clock PLLs, no divider */
- LM4_SYSTEM_RCC = (LM4_SYSTEM_RCC | 0x800) & ~0x400000;
- LM4_SYSTEM_RCC2 = (LM4_SYSTEM_RCC2 | 0x800) & ~0x80000000;
+ /* Notify modules of frequency change */
+ freq = clock_get_freq();
+ hwtimer_clock_changed(freq);
+#ifdef CONFIG_TASK_WATCHDOG
+ watchdog_clock_changed(freq);
+#endif
+#ifdef CONFIG_I2C
+ i2c_clock_changed(freq);
+#endif
+#ifdef CONFIG_PECI
+ peci_clock_changed(freq);
+#endif
- /* Enable main and precision internal oscillators */
- LM4_SYSTEM_RCC &= ~0x3;
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(nopll, command_disable_pll);
- /* Perform an auto calibration of the internal oscillator, using the
- * 32.768KHz hibernate clock. */
- /* TODO: (crosbug.com/p/7693) This is only needed on early chips which
- * aren't factory trimmed. */
- LM4_SYSTEM_PIOSCCAL = 0x80000000;
- LM4_SYSTEM_PIOSCCAL = 0x80000200;
+/*****************************************************************************/
+/* Initialization */
- /* wait 1 million CPU cycles */
- wait_cycles(512 * 1024);
+int clock_init(void)
+{
- /* clear PLL lock flag (aka PLLLMIS) */
- LM4_SYSTEM_MISC = 0x40;
- /* clear powerdown / set XTAL frequency, divider, and source */
- LM4_SYSTEM_RCC = (LM4_SYSTEM_RCC & ~0x07c027f0) | (value & 0x07c007f0);
- /* wait 32 CPU cycles */
- wait_cycles(16);
- /* wait for PLL to lock */
- while (!(LM4_SYSTEM_RIS & 0x40));
+#ifndef BOARD_bds
+ /* Only BDS has an external crystal; other boards don't have one, and
+ * can disable main oscillator control to reduce power consumption. */
+ LM4_SYSTEM_MOSCCTL = 0x04;
+#endif
- /* Remove bypass on PLL */
- LM4_SYSTEM_RCC = LM4_SYSTEM_RCC & ~0x800;
-}
+ /* Perform an auto calibration of the internal oscillator using the
+ * 32.768KHz hibernate clock, unless we've already done so. */
+ /* TODO: (crosbug.com/p/7693) This is only needed on early chips which
+ * aren't factory trimmed. */
+ if ((LM4_SYSTEM_PIOSCSTAT & 0x300) != 0x100) {
+ /* Start calibration */
+ LM4_SYSTEM_PIOSCCAL = 0x80000000;
+ LM4_SYSTEM_PIOSCCAL = 0x80000200;
+ /* Wait for result */
+ clock_wait_cycles(16);
+ while (!(LM4_SYSTEM_PIOSCSTAT & 0x300))
+ ;
+ }
-int clock_init(void)
-{
- /* CPU clock = PLL/3 = 66.667MHz; System clock = PLL */
- BUILD_ASSERT(CPU_CLOCK == 66666667);
- /* Osc source = internal 16MHz oscillator */
- clock_init_pll(0x01400550);
+ /* TODO: UART seems to glitch unless we wait 500k cycles before
+ * enabling the PLL, but only if this is a cold boot. Why? UART
+ * doesn't even use the PLL'd system clock. I've heard rumors the
+ * Stellaris ROM library does this too, but why? */
+ if (!system_jumped_to_this_image())
+ clock_wait_cycles(500000);
+
+ clock_enable_pll();
return EC_SUCCESS;
}