summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-06-24 13:59:22 -0700
committerChromeBot <chrome-bot@google.com>2013-06-26 09:08:22 -0700
commit67c275f849939dfa9993175f1704cb80b11a40b3 (patch)
tree14b62b4d9f4331d4233afafcec2a75359c7704fb
parentb0c8ce69480d913b09590fe12f256a64894c6aaf (diff)
downloadchrome-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.h5
-rw-r--r--chip/stm32/clock-stm32l15x.c228
-rw-r--r--chip/stm32/registers.h5
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)