summaryrefslogtreecommitdiff
path: root/chip/lm4
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
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')
-rw-r--r--chip/lm4/clock.c164
-rw-r--r--chip/lm4/hwtimer.c20
-rw-r--r--chip/lm4/i2c.c37
-rw-r--r--chip/lm4/peci.c42
-rw-r--r--chip/lm4/registers.h10
-rw-r--r--chip/lm4/watchdog.c24
6 files changed, 193 insertions, 104 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;
}
diff --git a/chip/lm4/hwtimer.c b/chip/lm4/hwtimer.c
index 9b221010c8..a45f74705d 100644
--- a/chip/lm4/hwtimer.c
+++ b/chip/lm4/hwtimer.c
@@ -5,18 +5,14 @@
/* Hardware timers driver */
-#include <stdint.h>
-
#include "board.h"
+#include "clock.h"
#include "hwtimer.h"
#include "registers.h"
#include "task.h"
#define US_PER_SECOND 1000000
-/* Divider to get microsecond for the clock */
-#define CLOCKSOURCE_DIVIDER (CPU_CLOCK/US_PER_SECOND)
-
void __hw_clock_event_set(uint32_t deadline)
{
/* set the match on the deadline */
@@ -57,6 +53,14 @@ static void __hw_clock_source_irq(void)
DECLARE_IRQ(LM4_IRQ_TIMERW0A, __hw_clock_source_irq, 1);
+void hwtimer_clock_changed(int freq)
+{
+ /* Set the prescaler to increment every microsecond. This takes
+ * effect immediately, because the TAILD bit in TAMR is clear. */
+ LM4_TIMER_TAPR(6) = freq / US_PER_SECOND;
+}
+
+
int __hw_clock_source_init(void)
{
volatile uint32_t scratch __attribute__((unused));
@@ -75,8 +79,10 @@ int __hw_clock_source_init(void)
LM4_TIMER_IMR(6) = 0x1;
/* 32-bit timer mode */
LM4_TIMER_CFG(6) = 4;
- /* set the prescaler to increment every microsecond */
- LM4_TIMER_TAPR(6) = CLOCKSOURCE_DIVIDER;
+
+ /* Set initial clock frequency */
+ hwtimer_clock_changed(clock_get_freq());
+
/* Periodic mode, counting down */
LM4_TIMER_TAMR(6) = 0x22;
/* use the full 32-bits of the timer */
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c
index a6e8a5c77c..131a84a584 100644
--- a/chip/lm4/i2c.c
+++ b/chip/lm4/i2c.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 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.
*/
@@ -6,6 +6,7 @@
/* I2C port module for Chrome EC */
#include "board.h"
+#include "clock.h"
#include "console.h"
#include "gpio.h"
#include "i2c.h"
@@ -58,6 +59,7 @@ static int wait_idle(int port)
return EC_SUCCESS;
}
+
/* Transmit one block of raw data, then receive one block of raw data.
* <start> flag indicates this smbus session start from idle state.
* <stop> flag means this session can be termicate with smbus stop bit
@@ -142,7 +144,6 @@ static int i2c_transmit_receive(int port, int slave_addr,
}
-
int i2c_read16(int port, int slave_addr, int offset, int *data)
{
int rv;
@@ -192,6 +193,7 @@ int i2c_write16(int port, int slave_addr, int offset, int data)
return rv;
}
+
int i2c_read8(int port, int slave_addr, int offset, int* data)
{
int rv;
@@ -210,6 +212,7 @@ int i2c_read8(int port, int slave_addr, int offset, int* data)
return rv;
}
+
int i2c_write8(int port, int slave_addr, int offset, int data)
{
int rv;
@@ -226,9 +229,7 @@ int i2c_write8(int port, int slave_addr, int offset, int data)
return rv;
}
-/* Read ascii string using smbus read block protocol.
- * The return data <data> will be null terminated.
- */
+
int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data,
int len)
{
@@ -259,6 +260,18 @@ exit:
}
+void i2c_clock_changed(int freq)
+{
+ LM4_I2C_MTPR(I2C_PORT_THERMAL) =
+ (freq / (I2C_SPEED_THERMAL * 10 * 2)) - 1;
+ LM4_I2C_MTPR(I2C_PORT_BATTERY) =
+ (freq / (I2C_SPEED_BATTERY * 10 * 2)) - 1;
+ LM4_I2C_MTPR(I2C_PORT_CHARGER) =
+ (freq / (I2C_SPEED_CHARGER * 10 * 2)) - 1;
+ LM4_I2C_MTPR(I2C_PORT_LIGHTBAR) =
+ (freq / (I2C_SPEED_LIGHTBAR * 10 * 2)) - 1;
+}
+
/*****************************************************************************/
/* Interrupt handlers */
@@ -439,20 +452,12 @@ int i2c_init(void)
/* Initialize ports as master, with interrupts enabled */
LM4_I2C_MCR(I2C_PORT_THERMAL) = 0x10;
- LM4_I2C_MTPR(I2C_PORT_THERMAL) =
- (CPU_CLOCK / (I2C_SPEED_THERMAL * 10 * 2)) - 1;
-
LM4_I2C_MCR(I2C_PORT_BATTERY) = 0x10;
- LM4_I2C_MTPR(I2C_PORT_BATTERY) =
- (CPU_CLOCK / (I2C_SPEED_BATTERY * 10 * 2)) - 1;
-
LM4_I2C_MCR(I2C_PORT_CHARGER) = 0x10;
- LM4_I2C_MTPR(I2C_PORT_CHARGER) =
- (CPU_CLOCK / (I2C_SPEED_CHARGER * 10 * 2)) - 1;
-
LM4_I2C_MCR(I2C_PORT_LIGHTBAR) = 0x10;
- LM4_I2C_MTPR(I2C_PORT_LIGHTBAR) =
- (CPU_CLOCK / (I2C_SPEED_LIGHTBAR * 10 * 2)) - 1;
+
+ /* Set initial clock frequency */
+ i2c_clock_changed(clock_get_freq());
/* Enable irqs */
task_enable_irq(LM4_IRQ_I2C0);
diff --git a/chip/lm4/peci.c b/chip/lm4/peci.c
index 75946a4e61..2dbbd914b3 100644
--- a/chip/lm4/peci.c
+++ b/chip/lm4/peci.c
@@ -6,6 +6,7 @@
/* PECI interface for Chrome EC */
#include "board.h"
+#include "clock.h"
#include "console.h"
#include "gpio.h"
#include "peci.h"
@@ -51,6 +52,7 @@ int peci_get_cpu_temp(void)
return v >> 6;
}
+
int peci_temp_sensor_poll(void)
{
last_temp_val = peci_get_cpu_temp();
@@ -61,11 +63,33 @@ int peci_temp_sensor_poll(void)
return EC_ERROR_UNKNOWN;
}
+
int peci_temp_sensor_get_val(int idx)
{
return last_temp_val;
}
+
+void peci_clock_changed(int freq)
+{
+ int baud;
+
+ /* Disable polling while reconfiguring */
+ LM4_PECI_CTL = 0;
+
+ /* Calculate baud setting from desired rate, compensating for internal
+ * and external delays. */
+ baud = freq / (4 * PECI_BAUD_RATE) - 2;
+ baud -= (freq / 1000000) * (PECI_TD_FET_NS + PECI_TD_INT_NS) / 1000;
+
+ /* Set baud rate and polling rate */
+ LM4_PECI_DIV = (baud << 16) |
+ (PECI_POLL_INTERVAL_MS * (freq / 1000 / 4096));
+
+ /* Set up temperature monitoring to report in degrees K */
+ LM4_PECI_CTL = ((PECI_TJMAX + 273) << 22) | 0x2001;
+}
+
/*****************************************************************************/
/* Console commands */
@@ -88,7 +112,6 @@ DECLARE_CONSOLE_COMMAND(pecitemp, command_peci_temp);
int peci_init(void)
{
volatile uint32_t scratch __attribute__((unused));
- int baud;
/* Enable the PECI module and delay a few clocks */
LM4_SYSTEM_RCGCPECI = 1;
@@ -97,21 +120,8 @@ int peci_init(void)
/* Configure GPIOs */
configure_gpios();
- /* Disable polling while reconfiguring */
- LM4_PECI_CTL = 0;
-
- /* Calculate baud setting from desired rate, compensating for internal
- * and external delays. */
- baud = CPU_CLOCK / (4 * PECI_BAUD_RATE) - 2;
- baud -= (CPU_CLOCK / 1000000) * (PECI_TD_FET_NS + PECI_TD_INT_NS)
- / 1000;
-
- /* Set baud rate and polling rate */
- LM4_PECI_DIV = (baud << 16) |
- (PECI_POLL_INTERVAL_MS * (CPU_CLOCK / 1000 / 4096));
-
- /* Set up temperature monitoring to report in degrees K */
- LM4_PECI_CTL = ((PECI_TJMAX + 273) << 22) | 0x2001;
+ /* Set initial clock frequency */
+ peci_clock_changed(clock_get_freq());
return EC_SUCCESS;
}
diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h
index a572e763ed..1617e97cd4 100644
--- a/chip/lm4/registers.h
+++ b/chip/lm4/registers.h
@@ -191,7 +191,17 @@ static inline int lm4_fan_addr(int ch, int offset)
#define LM4_SYSTEM_MISC LM4REG(0x400fe058)
#define LM4_SYSTEM_RESC LM4REG(0x400fe05c)
#define LM4_SYSTEM_RCC LM4REG(0x400fe060)
+#define LM4_SYSTEM_RCC_SYSDIV(x) (((x) & 0xf) << 23)
+#define LM4_SYSTEM_RCC_USESYSDIV (1 << 22)
+#define LM4_SYSTEM_RCC_PWRDN (1 << 13)
+#define LM4_SYSTEM_RCC_BYPASS (1 << 11)
+#define LM4_SYSTEM_RCC_XTAL(x) (((x) & 0x1f) << 6)
+#define LM4_SYSTEM_RCC_OSCSRC(x) (((x) & 0x3) << 4)
+#define LM4_SYSTEM_RCC_IOSCDIS (1 << 1)
+#define LM4_SYSTEM_RCC_MOSCDIS (1 << 0)
#define LM4_SYSTEM_RCC2 LM4REG(0x400fe070)
+#define LM4_SYSTEM_RCC2_USERCC2 (1 << 31)
+#define LM4_SYSTEM_MOSCCTL LM4REG(0x400fe07c)
#define LM4_SYSTEM_PIOSCCAL LM4REG(0x400fe150)
#define LM4_SYSTEM_PIOSCSTAT LM4REG(0x400fe154)
#define LM4_SYSTEM_PLLSTAT LM4REG(0x400fe168)
diff --git a/chip/lm4/watchdog.c b/chip/lm4/watchdog.c
index ca76da2c24..248d895369 100644
--- a/chip/lm4/watchdog.c
+++ b/chip/lm4/watchdog.c
@@ -1,13 +1,12 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 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.
*/
/* Watchdog driver */
-#include <stdint.h>
-
#include "board.h"
+#include "clock.h"
#include "common.h"
#include "config.h"
#include "registers.h"
@@ -25,8 +24,9 @@
/* magic value to unlock the watchdog registers */
#define LM4_WATCHDOG_MAGIC_WORD 0x1ACCE551
-/* watchdog counter initial value */
-static uint32_t watchdog_period;
+#define WATCHDOG_PERIOD_MS 1100 /* Watchdog period in ms */
+
+static uint32_t watchdog_period; /* Watchdog counter initial value */
/* console debug command prototypes */
int command_task_info(int argc, char **argv);
@@ -110,7 +110,15 @@ void watchdog_reload(void)
LM4_WATCHDOG_LOCK(0) = 0xdeaddead;
}
-int watchdog_init(int period_ms)
+
+void watchdog_clock_changed(int freq)
+{
+ /* Set the timeout period */
+ watchdog_period = WATCHDOG_PERIOD_MS * (freq / 1000);
+}
+
+
+int watchdog_init(void)
{
volatile uint32_t scratch __attribute__((unused));
@@ -122,8 +130,8 @@ int watchdog_init(int period_ms)
/* Unlock watchdog registers */
LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;
- /* Set the time-out period */
- watchdog_period = period_ms * (CPU_CLOCK / 1000);
+ /* Set initial timeout period */
+ watchdog_clock_changed(clock_get_freq());
LM4_WATCHDOG_LOAD(0) = watchdog_period;
/* de-activate the watchdog when the JTAG stops the CPU */