diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-04-06 09:33:41 -0700 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2012-04-09 10:33:35 -0700 |
commit | e9328ac4f63351b4282916034270aa86b7e74922 (patch) | |
tree | d0fde5840594c33709927a5a4d1fabfe0f858c26 /chip | |
parent | 1a9a415cf68c6e8e3b31972c072c81ed886290ab (diff) | |
download | chrome-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')
-rw-r--r-- | chip/lm4/clock.c | 164 | ||||
-rw-r--r-- | chip/lm4/hwtimer.c | 20 | ||||
-rw-r--r-- | chip/lm4/i2c.c | 37 | ||||
-rw-r--r-- | chip/lm4/peci.c | 42 | ||||
-rw-r--r-- | chip/lm4/registers.h | 10 | ||||
-rw-r--r-- | chip/lm4/watchdog.c | 24 | ||||
-rw-r--r-- | chip/stm32l/clock.c | 13 |
7 files changed, 193 insertions, 117 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 */ diff --git a/chip/stm32l/clock.c b/chip/stm32l/clock.c index 9b4c01ab73..aa243738b0 100644 --- a/chip/stm32l/clock.c +++ b/chip/stm32l/clock.c @@ -13,19 +13,6 @@ #include "registers.h" #include "util.h" -/** - * Idle task - * executed when no task are ready to be scheduled - */ -void __idle(void) -{ - while (1) { - /* wait for the irq event */ - asm("wfi"); - /* TODO more power management here */ - } -} - int clock_init(void) { uint32_t tmp_acr; |