diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-06-24 13:59:22 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-06-26 09:08:22 -0700 |
commit | 67c275f849939dfa9993175f1704cb80b11a40b3 (patch) | |
tree | 14b62b4d9f4331d4233afafcec2a75359c7704fb | |
parent | b0c8ce69480d913b09590fe12f256a64894c6aaf (diff) | |
download | chrome-ec-67c275f849939dfa9993175f1704cb80b11a40b3.tar.gz |
pit: Drop EC clock speed to 1 MHz MSI when AP is in S3/S5
Automatically drop the clock speed to 1 MHz MSI, then disable HSI and
move to lower-power flash mode. This cuts power from 2400uA to 150uA.
Note that at 1 MHz, we drop received characters on the UART if you
type too fast (or copy/paste, or use arrow keys). Clock speed can be
manually overridden back to 16 MHz via 'clock hsi'.
BUG=chrome-os-partner:20414
BRANCH=none
TEST=with AP on, 'clock' reports 16 MHz.
with AP off, 'clock' reports 1 MHz.
Change-Id: I070c80db0aa5f3a98a7bad6050890bcc2fe008d6
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/59832
-rw-r--r-- | board/pit/board.h | 5 | ||||
-rw-r--r-- | chip/stm32/clock-stm32l15x.c | 228 | ||||
-rw-r--r-- | chip/stm32/registers.h | 5 |
3 files changed, 153 insertions, 85 deletions
diff --git a/board/pit/board.h b/board/pit/board.h index 707134e08f..689146f03f 100644 --- a/board/pit/board.h +++ b/board/pit/board.h @@ -39,11 +39,6 @@ #define CONFIG_SMART_BATTERY #define CONFIG_SPI -#ifdef PORT_TO_PIT -/* TODO(rspangler): enable these features when they compile */ -#define CONFIG_LOW_POWER_IDLE -#endif - #ifndef __ASSEMBLER__ /* By default, enable all console messages except keyboard */ diff --git a/chip/stm32/clock-stm32l15x.c b/chip/stm32/clock-stm32l15x.c index 4504071fec..79b618363f 100644 --- a/chip/stm32/clock-stm32l15x.c +++ b/chip/stm32/clock-stm32l15x.c @@ -13,10 +13,24 @@ #include "registers.h" #include "util.h" -/* High-speed oscillator is 16MHz */ +/* 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 = HSI_CLOCK; +static int freq; +static int current_osc; void enable_sleep(uint32_t mask) { @@ -33,110 +47,164 @@ int clock_get_freq(void) return freq; } -void clock_init(void) +/** + * Set which oscillator is used for the clock + * + * @param osc Oscillator to use + */ +static void clock_set_osc(enum clock_osc osc) { uint32_t tmp_acr; - /* - * The initial state : - * SYSCLK from MSI (=2MHz), no divider on AHB, APB1, APB2 - * PLL unlocked, RTC enabled on LSE - */ + if (osc == current_osc) + return; - /* Ensure that HSI is ON */ - if (!(STM32_RCC_CR & (1 << 1))) { - /* Enable HSI */ - STM32_RCC_CR |= 1 << 0; - /* Wait for HSI to be ready */ - while (!(STM32_RCC_CR & (1 << 1))) - ; - } + 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)) + ; + } - /* - * Set the recommended flash settings for 16MHz clock. - * - * The 3 bits must be programmed strictly sequentially, - * but it is faster not to read-back the value of the ACR register - * in the middle of the sequence so let's use a temporary variable. - */ - tmp_acr = STM32_FLASH_ACR; - /* Enable 64-bit access */ - tmp_acr |= (1 << 2); - STM32_FLASH_ACR = tmp_acr; - /* Enable Prefetch Buffer */ - tmp_acr |= (1 << 1); - STM32_FLASH_ACR = tmp_acr; - /* Flash 1 wait state */ - tmp_acr |= (1 << 0); - STM32_FLASH_ACR = tmp_acr; + /* Disable LPSDSR */ + STM32_PWR_CR &= ~STM32_PWR_CR_LPSDSR; + + /* + * Set the recommended flash settings for 16MHz clock. + * + * The 3 bits must be programmed strictly sequentially, but it + * is faster not to read-back the value of the ACR register in + * the middle of the sequence so use a temporary variable. + */ + tmp_acr = STM32_FLASH_ACR; + /* Enable 64-bit access */ + tmp_acr |= STM32_FLASH_ACR_ACC64; + STM32_FLASH_ACR = tmp_acr; + /* Enable Prefetch Buffer */ + tmp_acr |= STM32_FLASH_ACR_PRFTEN; + STM32_FLASH_ACR = tmp_acr; + /* Flash 1 wait state */ + tmp_acr |= STM32_FLASH_ACR_LATENCY; + STM32_FLASH_ACR = tmp_acr; #ifdef CONFIG_USE_PLL - /* - * Switch to HSI, no prescaler, PLLSRC = HSI, PLLMUL = x3, PLLDIV = /3, - * no MCO => PLLVCO = 48 MHz and PLLCLK = 16 Mhz. - */ - STM32_RCC_CFGR = 0x00800001; - - /* Enable the PLL */ - STM32_RCC_CR |= 1 << 24; - /* Wait for the PLL to lock */ - while (!(STM32_RCC_CR & (1 << 25))) - ; - /* switch to SYSCLK to the PLL */ - STM32_RCC_CFGR = 0x00800003; - /* wait until the PLL is the clock source */ - while ((STM32_RCC_CFGR & 0xc) != 0xc) - ; + /* + * Switch to HSI, no prescaler, PLLSRC = HSI, PLLMUL = x3, + * PLLDIV = /3, no MCO => PLLVCO = 48 MHz and PLLCLK = 16 Mhz. + */ + STM32_RCC_CFGR = 0x00800001; + + /* Enable the PLL */ + STM32_RCC_CR |= 1 << 24; + /* Wait for the PLL to lock */ + while (!(STM32_RCC_CR & (1 << 25))) + ; + /* Switch to SYSCLK to the PLL */ + STM32_RCC_CFGR = 0x00800003; + /* Wait until the PLL is the clock source */ + while ((STM32_RCC_CFGR & 0xc) != 0xc) + ; #else - /* Switch to HSI */ - STM32_RCC_CFGR = 0x00000001; -#endif -} - -static int command_clock(int argc, char **argv) -{ - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "hsi")) { - /* Switch to 16MHz HSI */ + /* Switch to HSI */ STM32_RCC_CFGR = STM32_RCC_CFGR_SW_HSI; +#endif freq = HSI_CLOCK; - /* Disable LPSDSR */ - STM32_PWR_CR &= ~STM32_PWR_CR_LPSDSR; - - } else if (!strcasecmp(argv[1], "msi2")) { - /* Switch to 2.097MHz MSI */ - STM32_RCC_ICSCR = - (STM32_RCC_ICSCR & ~STM32_RCC_ICSCR_MSIRANGE_MASK) | - STM32_RCC_ICSCR_MSIRANGE_2MHZ; - STM32_RCC_CFGR = STM32_RCC_CFGR_SW_MSI; - freq = 1 << 21; + break; - } else if (!strcasecmp(argv[1], "msi1")) { - /* Switch to 1.049MHz MSI */ + case OSC_MSI: + /* Switch to MSI @ 1MHz */ STM32_RCC_ICSCR = (STM32_RCC_ICSCR & ~STM32_RCC_ICSCR_MSIRANGE_MASK) | STM32_RCC_ICSCR_MSIRANGE_1MHZ; STM32_RCC_CFGR = STM32_RCC_CFGR_SW_MSI; - freq = 1 << 20; + /* + * Set the recommended flash settings for <= 2MHz clock. + * + * The 3 bits must be programmed strictly sequentially, but it + * is faster not to read-back the value of the ACR register in + * the middle of the sequence so use a temporary variable. + */ + tmp_acr = STM32_FLASH_ACR; + /* Flash 0 wait state */ + tmp_acr &= ~STM32_FLASH_ACR_LATENCY; + STM32_FLASH_ACR = tmp_acr; + /* Disable prefetch Buffer */ + tmp_acr &= ~STM32_FLASH_ACR_PRFTEN; + STM32_FLASH_ACR = tmp_acr; + /* Disable 64-bit access */ + tmp_acr &= ~STM32_FLASH_ACR_ACC64; + STM32_FLASH_ACR = tmp_acr; + + /* 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 { - return EC_ERROR_PARAM1; + current_osc = osc; } +} +void clock_init(void) +{ /* - * TODO(rspangler): try enabling LPSDSR in low power modes as well: - * STM32_PWR_CR |= STM32_PWR_CR_LPSDSR; + * The initial state : + * SYSCLK from MSI (=2MHz), no divider on AHB, APB1, APB2 + * PLL unlocked, RTC enabled on LSE */ - /* Notify modules of frequency change */ - hook_notify(HOOK_FREQ_CHANGE); + /* Switch to high-speed oscillator */ + clock_set_osc(1); +} + +static void clock_chipset_startup(void) +{ + /* Return to full speed */ + clock_set_osc(OSC_HSI); +} +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 */ + clock_set_osc(OSC_MSI); +} +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 | msi2 | msi1", + "hsi | msi", "Set clock frequency", NULL); diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index 884f85cd69..774e15f06a 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -314,6 +314,8 @@ typedef volatile struct timer_ctlr timer_ctlr_t; #define STM32_RCC_BASE 0x40023800 #define STM32_RCC_CR REG32(STM32_RCC_BASE + 0x00) +#define STM32_RCC_CR_HSION (1 << 0) +#define STM32_RCC_CR_HSIRDY (1 << 1) #define STM32_RCC_ICSCR REG32(STM32_RCC_BASE + 0x04) #define STM32_RCC_ICSCR_MSIRANGE(n) ((n) << 13) #define STM32_RCC_ICSCR_MSIRANGE_1MHZ STM32_RCC_ICSCR_MSIRANGE(4) @@ -482,6 +484,9 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t; #define STM32_FLASH_REGS_BASE 0x40023c00 #define STM32_FLASH_ACR REG32(STM32_FLASH_REGS_BASE + 0x00) +#define STM32_FLASH_ACR_LATENCY (1 << 0) +#define STM32_FLASH_ACR_PRFTEN (1 << 1) +#define STM32_FLASH_ACR_ACC64 (1 << 2) #define STM32_FLASH_PECR REG32(STM32_FLASH_REGS_BASE + 0x04) #define STM32_FLASH_PECR_PE_LOCK (1 << 0) #define STM32_FLASH_PECR_PRG_LOCK (1 << 1) |