summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-06-21 10:16:28 -0700
committerChromeBot <chrome-bot@google.com>2013-06-25 23:24:23 -0700
commitb0c8ce69480d913b09590fe12f256a64894c6aaf (patch)
tree76975f199c06913f3fe91522a67fba75b197a231 /chip
parent6957684d304825cc6fd926c8ee7a2ccb1edb6839 (diff)
downloadchrome-ec-b0c8ce69480d913b09590fe12f256a64894c6aaf.tar.gz
pit: Support changing EC clock frequency
Modules which care about system clock frequency now hook HOOK_FREQ_CHANGE. - hwtimer - i2c - uart (which is now also smart enough to use x8 oversampling instead of x16 when the system clock is too slow to support x16) Added 'clock' debug command to set system clock frequency. STM32F platforms don't change clock frequency; on those platforms, clock_get_freq() simply returns CPU_CLOCK, so behavior of those platforms is unchanged. BUG=chrome-os-partner:20414 BRANCH=none TEST=from EC console: - reboot ap-off -> to make sure AP is off during testing - clock msi2 -> reports 2MHz clock - battery -> reports battery info - clock msi1 -> reports 1MHz clock - battery -> reports battery info - clock hsi -> reports 16MHz clock - battery -> reports battery info - power on -> AP powers on and host commands succeed Change-Id: Ib8276bf124727e4fb502297ca8b3d6d4b6170241 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/59645
Diffstat (limited to 'chip')
-rw-r--r--chip/stm32/clock-stm32f100.c5
-rw-r--r--chip/stm32/clock-stm32l15x.c61
-rw-r--r--chip/stm32/flash-stm32l15x.c19
-rw-r--r--chip/stm32/hwtimer.c32
-rw-r--r--chip/stm32/i2c-stm32l15x.c38
-rw-r--r--chip/stm32/power_led.c22
-rw-r--r--chip/stm32/registers.h10
-rw-r--r--chip/stm32/uart.c55
-rw-r--r--chip/stm32/watchdog.c22
9 files changed, 196 insertions, 68 deletions
diff --git a/chip/stm32/clock-stm32f100.c b/chip/stm32/clock-stm32f100.c
index 58c51e0a94..e878d7856d 100644
--- a/chip/stm32/clock-stm32f100.c
+++ b/chip/stm32/clock-stm32f100.c
@@ -261,6 +261,11 @@ void __idle(void)
}
#endif /* CONFIG_LOW_POWER_IDLE */
+int clock_get_freq(void)
+{
+ return CPU_CLOCK;
+}
+
void clock_init(void)
{
/*
diff --git a/chip/stm32/clock-stm32l15x.c b/chip/stm32/clock-stm32l15x.c
index 59f53c6400..4504071fec 100644
--- a/chip/stm32/clock-stm32l15x.c
+++ b/chip/stm32/clock-stm32l15x.c
@@ -7,10 +7,16 @@
#include "clock.h"
#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "hooks.h"
#include "registers.h"
#include "util.h"
-BUILD_ASSERT(CPU_CLOCK == 16000000);
+/* High-speed oscillator is 16MHz */
+#define HSI_CLOCK 16000000
+
+static int freq = HSI_CLOCK;
void enable_sleep(uint32_t mask)
{
@@ -22,6 +28,11 @@ void disable_sleep(uint32_t mask)
/* low power mode not implemented */
}
+int clock_get_freq(void)
+{
+ return freq;
+}
+
void clock_init(void)
{
uint32_t tmp_acr;
@@ -81,3 +92,51 @@ void clock_init(void)
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 */
+ STM32_RCC_CFGR = STM32_RCC_CFGR_SW_HSI;
+ 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;
+
+ } else if (!strcasecmp(argv[1], "msi1")) {
+ /* Switch to 1.049MHz MSI */
+ 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;
+
+ } else {
+ return EC_ERROR_PARAM1;
+ }
+
+ /*
+ * TODO(rspangler): try enabling LPSDSR in low power modes as well:
+ * STM32_PWR_CR |= STM32_PWR_CR_LPSDSR;
+ */
+
+ /* Notify modules of frequency change */
+ hook_notify(HOOK_FREQ_CHANGE);
+
+ ccprintf("Clock frequency is now %d Hz\n", freq);
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(clock, command_clock,
+ "hsi | msi2 | msi1",
+ "Set clock frequency",
+ NULL);
diff --git a/chip/stm32/flash-stm32l15x.c b/chip/stm32/flash-stm32l15x.c
index a5394497d1..afb614b921 100644
--- a/chip/stm32/flash-stm32l15x.c
+++ b/chip/stm32/flash-stm32l15x.c
@@ -5,6 +5,7 @@
/* Flash memory module for Chrome EC */
+#include "clock.h"
#include "console.h"
#include "flash.h"
#include "registers.h"
@@ -21,9 +22,9 @@
#define CYCLE_PER_FLASH_LOOP 10
/* Flash page programming timeout. This is 2x the datasheet max. */
-#define FLASH_TIMEOUT_US 16000
-#define FLASH_TIMEOUT_LOOP \
- (FLASH_TIMEOUT_US * (CPU_CLOCK / SECOND) / CYCLE_PER_FLASH_LOOP)
+#define FLASH_TIMEOUT_MS 16
+
+static int flash_timeout_loop;
/**
* Lock all the locks.
@@ -140,7 +141,7 @@ void __attribute__((section(".iram.text")))
interrupt_disable();
/* Wait for ready */
- for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP) ;
+ for (i = 0; (STM32_FLASH_SR & 1) && (i < flash_timeout_loop) ;
i++)
;
@@ -152,7 +153,7 @@ void __attribute__((section(".iram.text")))
*addr++ = *data++;
/* Wait for writes to complete */
- for (i = 0; ((STM32_FLASH_SR & 9) != 8) && (i < FLASH_TIMEOUT_LOOP) ;
+ for (i = 0; ((STM32_FLASH_SR & 9) != 8) && (i < flash_timeout_loop) ;
i++)
;
@@ -191,6 +192,10 @@ int flash_physical_write(int offset, int size, const char *data)
if ((offset | size) & (CONFIG_FLASH_WRITE_SIZE - 1))
word_mode = 1;
+ /* Update flash timeout based on current clock speed */
+ flash_timeout_loop = FLASH_TIMEOUT_MS * (clock_get_freq() / MSEC) /
+ CYCLE_PER_FLASH_LOOP;
+
while (size > 0) {
/*
* Reload the watchdog timer to avoid watchdog reset when doing
@@ -204,7 +209,7 @@ int flash_physical_write(int offset, int size, const char *data)
/* Wait for writes to complete */
for (i = 0; ((STM32_FLASH_SR & 9) != 8) &&
- (i < FLASH_TIMEOUT_LOOP) ; i++)
+ (i < flash_timeout_loop) ; i++)
;
size -= sizeof(uint32_t);
@@ -273,7 +278,7 @@ int flash_physical_erase(int offset, int size)
*/
watchdog_reload();
- deadline.val = get_time().val + FLASH_TIMEOUT_US;
+ deadline.val = get_time().val + FLASH_TIMEOUT_MS * MSEC;
/* Wait for erase to complete */
while ((STM32_FLASH_SR & 1) &&
(get_time().val < deadline.val)) {
diff --git a/chip/stm32/hwtimer.c b/chip/stm32/hwtimer.c
index bb036619dc..059814de8a 100644
--- a/chip/stm32/hwtimer.c
+++ b/chip/stm32/hwtimer.c
@@ -5,7 +5,9 @@
/* Hardware timers driver */
+#include "clock.h"
#include "common.h"
+#include "hooks.h"
#include "hwtimer.h"
#include "panic.h"
#include "registers.h"
@@ -13,9 +15,6 @@
#include "timer.h"
#include "watchdog.h"
-/* Divider to get microsecond for the clock */
-#define CLOCKSOURCE_DIVIDER (CPU_CLOCK / SECOND)
-
/*
* Trigger select mapping for slave timer from master timer. This is
* unfortunately not very straightforward; there's no tidy way to do this
@@ -171,6 +170,21 @@ void __hw_timer_enable_clock(int n)
STM32_RCC_APB2ENR |= 1 << (n - 7);
}
+static void update_prescaler(void)
+{
+ /*
+ * Pre-scaler value :
+ * TIM_CLOCK_LSB is counting microseconds;
+ * TIM_CLOCK_MSB is counting every TIM_CLOCK_LSB overflow.
+ *
+ * This will take effect at the next update event (when the current
+ * prescaler counter ticks down, or if forced via EGR).
+ */
+ STM32_TIM_PSC(TIM_CLOCK_MSB) = 0;
+ STM32_TIM_PSC(TIM_CLOCK_LSB) = (clock_get_freq() / SECOND) - 1;
+}
+DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT);
+
int __hw_clock_source_init(uint32_t start_t)
{
/*
@@ -203,19 +217,15 @@ int __hw_clock_source_init(uint32_t start_t)
/* Auto-reload value : 16-bit free-running counters */
STM32_TIM_ARR(TIM_CLOCK_MSB) = 0xffff;
STM32_TIM_ARR(TIM_CLOCK_LSB) = 0xffff;
- /*
- * Pre-scaler value :
- * TIM_CLOCK_LSB is counting microseconds, TIM_CLOCK_MSB is counting
- * every TIM_CLOCK_LSB overflow.
- */
- STM32_TIM_PSC(TIM_CLOCK_MSB) = 0;
- STM32_TIM_PSC(TIM_CLOCK_LSB) = CLOCKSOURCE_DIVIDER - 1;
+
+ /* Update prescaler */
+ update_prescaler();
/* Reload the pre-scaler */
STM32_TIM_EGR(TIM_CLOCK_MSB) = 0x0001;
STM32_TIM_EGR(TIM_CLOCK_LSB) = 0x0001;
- /* setup the overflow interrupt on TIM_CLOCK_MSB */
+ /* Set up the overflow interrupt on TIM_CLOCK_MSB */
STM32_TIM_DIER(TIM_CLOCK_MSB) = 0x0001;
STM32_TIM_DIER(TIM_CLOCK_LSB) = 0x0000;
diff --git a/chip/stm32/i2c-stm32l15x.c b/chip/stm32/i2c-stm32l15x.c
index 8434db02f2..36f6817815 100644
--- a/chip/stm32/i2c-stm32l15x.c
+++ b/chip/stm32/i2c-stm32l15x.c
@@ -316,34 +316,50 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data,
/*****************************************************************************/
/* Hooks */
-static void i2c_init(void)
+/* Handle CPU clock changing frequency */
+static void i2c_freq_change(void)
{
const struct i2c_port_t *p = i2c_ports;
+ int freq = clock_get_freq();
int i;
for (i = 0; i < I2C_PORTS_USED; i++, p++) {
int port = p->port;
- /* Enable clock if necessary */
- if (!(STM32_RCC_APB1ENR & (1 << (21 + port)))) {
- /* TODO: unwedge bus if necessary */
- STM32_RCC_APB1ENR |= 1 << (21 + port);
- }
-
/* Force peripheral reset and disable port */
STM32_I2C_CR1(port) = STM32_I2C_CR1_SWRST;
STM32_I2C_CR1(port) = 0;
/* Set clock frequency */
- STM32_I2C_CCR(port) = CPU_CLOCK / (2 * 1000 * p->kbps);
- STM32_I2C_CR2(port) = CPU_CLOCK / 1000000;
- STM32_I2C_TRISE(port) = CPU_CLOCK / 1000000 + 1;
+ STM32_I2C_CCR(port) = freq / (2 * MSEC * p->kbps);
+ STM32_I2C_CR2(port) = freq / SECOND;
+ STM32_I2C_TRISE(port) = freq / SECOND + 1;
/* Enable port */
STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE;
+ }
+}
+DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_change, HOOK_PRIO_DEFAULT);
+
+static void i2c_init(void)
+{
+ const struct i2c_port_t *p = i2c_ports;
+ int i;
+
+ for (i = 0; i < I2C_PORTS_USED; i++, p++) {
+ int port = p->port;
- /* TODO: enable interrupts using I2C_CR2 bits 8,9 */
+ /* Enable clocks to I2C modules if necessary */
+ if (!(STM32_RCC_APB1ENR & (1 << (21 + port)))) {
+ /* TODO: unwedge bus if necessary */
+ STM32_RCC_APB1ENR |= 1 << (21 + port);
+ }
}
+
+ /* Set up initial bus frequencies */
+ i2c_freq_change();
+
+ /* TODO: enable interrupts using I2C_CR2 bits 8,9 */
}
DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT);
diff --git a/chip/stm32/power_led.c b/chip/stm32/power_led.c
index d2d9382a08..c4773d37b8 100644
--- a/chip/stm32/power_led.c
+++ b/chip/stm32/power_led.c
@@ -15,8 +15,10 @@
* results in a breathing effect. It takes about 2sec for a full cycle.
*/
+#include "clock.h"
#include "console.h"
#include "gpio.h"
+#include "hooks.h"
#include "power_led.h"
#include "registers.h"
#include "task.h"
@@ -73,14 +75,13 @@ static void power_led_use_pwm(void)
STM32_TIM_CR1(2) = 0x0000;
/*
- * CPU_CLOCK / PSC determines how fast the counter operates.
+ * CPU clock / PSC determines how fast the counter operates.
* ARR determines the wave period, CCRn determines duty cycle.
- * Thus, frequency = CPU_CLOCK / PSC / ARR.
+ * Thus, frequency = cpu_freq / PSC / ARR. so:
*
- * Assuming 16MHz clock, the following yields:
- * 16MHz / 1600 / 100 = 100Hz.
+ * frequency = cpu_freq / (cpu_freq/10000) / 100 = 100 Hz.
*/
- STM32_TIM_PSC(2) = CPU_CLOCK / 10000; /* pre-scaler */
+ STM32_TIM_PSC(2) = clock_get_freq() / 10000; /* pre-scaler */
STM32_TIM_ARR(2) = 100; /* auto-reload value */
power_led_set_duty(100);
@@ -161,6 +162,17 @@ static int power_led_step(void)
return state_timeout;
}
+/**
+ * Handle clock frequency change
+ */
+static void power_led_freq_change(void)
+{
+ /* If we're using PWM, re-initialize to adjust timer divisor */
+ if (using_pwm)
+ power_led_use_pwm();
+}
+DECLARE_HOOK(HOOK_FREQ_CHANGE, power_led_freq_change, HOOK_PRIO_DEFAULT);
+
void power_led_task(void)
{
while (1) {
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index adc1ed1b70..884f85cd69 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -307,14 +307,22 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_PWR_BASE 0x40007000
#define STM32_PWR_CR REG32(STM32_PWR_BASE + 0x00)
+#define STM32_PWR_CR_LPSDSR (1 << 0)
#define STM32_PWR_CSR REG32(STM32_PWR_BASE + 0x04)
#if defined(CHIP_VARIANT_stm32l15x)
#define STM32_RCC_BASE 0x40023800
#define STM32_RCC_CR REG32(STM32_RCC_BASE + 0x00)
-#define STM32_RCC_ICSR REG32(STM32_RCC_BASE + 0x04)
+#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)
+#define STM32_RCC_ICSCR_MSIRANGE_2MHZ STM32_RCC_ICSCR_MSIRANGE(5)
+#define STM32_RCC_ICSCR_MSIRANGE_MASK STM32_RCC_ICSCR_MSIRANGE(7)
#define STM32_RCC_CFGR REG32(STM32_RCC_BASE + 0x08)
+#define STM32_RCC_CFGR_SW_MSI (0 << 0)
+#define STM32_RCC_CFGR_SW_HSI (1 << 0)
+#define STM32_RCC_CFGR_SW_MASK (3 << 0)
#define STM32_RCC_CIR REG32(STM32_RCC_BASE + 0x0C)
#define STM32_RCC_AHBRSTR REG32(STM32_RCC_BASE + 0x10)
#define STM32_RCC_APB2RSTR REG32(STM32_RCC_BASE + 0x14)
diff --git a/chip/stm32/uart.c b/chip/stm32/uart.c
index 2cd661d9e4..6754834f99 100644
--- a/chip/stm32/uart.c
+++ b/chip/stm32/uart.c
@@ -5,18 +5,18 @@
/* USART driver for Chrome EC */
-#include <stdarg.h>
-
-#include "board.h"
-#include "config.h"
+#include "common.h"
#include "clock.h"
+#include "hooks.h"
#include "registers.h"
#include "task.h"
#include "uart.h"
#include "util.h"
/* Baud rate for UARTs */
-#define BAUD_RATE 115200
+#ifndef CONFIG_UART_BAUD_RATE
+#define CONFIG_UART_BAUD_RATE 115200
+#endif
/* Console USART index */
#define UARTN CONFIG_CONSOLE_UART
@@ -112,6 +112,37 @@ static void uart_interrupt(void)
}
DECLARE_IRQ(STM32_IRQ_USART(UARTN), uart_interrupt, 2);
+/**
+ * Handle clock frequency changes
+ */
+static void uart_freq_change(void)
+{
+ int div = DIV_ROUND_NEAREST(clock_get_freq(), CONFIG_UART_BAUD_RATE);
+
+#ifdef CHIP_VARIANT_stm32l15x
+ if (div / 16 > 0) {
+ /*
+ * CPU clock is high enough to support x16 oversampling.
+ * BRR = (div mantissa)<<4 | (4-bit div fraction)
+ */
+ STM32_USART_CR1(UARTN) &= ~(1 << 15); /* OVER8 = 0 */
+ STM32_USART_BRR(UARTN) = div;
+ } else {
+ /*
+ * CPU clock is low; use x8 oversampling.
+ * BRR = (div mantissa)<<4 | (3-bit div fraction)
+ */
+ STM32_USART_BRR(UARTN) = ((div / 8) << 4) | (div & 7);
+ STM32_USART_CR1(UARTN) |= (1 << 15); /* OVER8 = 1 */
+ }
+#else
+ /* STM32F only supports x16 oversampling */
+ STM32_USART_BRR(UARTN) = div;
+#endif
+
+}
+DECLARE_HOOK(HOOK_FREQ_CHANGE, uart_freq_change, HOOK_PRIO_DEFAULT);
+
void uart_init(void)
{
/* Enable USART clock */
@@ -126,7 +157,8 @@ void uart_init(void)
else if (UARTN == 5)
STM32_RCC_APB1ENR |= 1 << 20; /* USART5 */
- /* UART enabled, 8 Data bits, oversampling x16, no parity,
+ /*
+ * UART enabled, 8 Data bits, oversampling x16, no parity,
* RXNE interrupt, TX and RX enabled.
*/
STM32_USART_CR1(UARTN) = 0x202C;
@@ -137,10 +169,13 @@ void uart_init(void)
/* DMA disabled, special modes disabled, error interrupt disabled */
STM32_USART_CR3(UARTN) = 0x0000;
- /* Select the baud rate
- * using x16 oversampling (OVER8 == 0)
- */
- STM32_USART_BRR(UARTN) = DIV_ROUND_NEAREST(CPU_CLOCK, BAUD_RATE);
+#ifdef CHIP_VARIANT_stm32l15x
+ /* Use single-bit sampling */
+ STM32_USART_CR3(UARTN) |= (1 << 11);
+#endif
+
+ /* Set initial baud rate */
+ uart_freq_change();
/* Enable interrupts */
task_enable_irq(STM32_IRQ_USART(UARTN));
diff --git a/chip/stm32/watchdog.c b/chip/stm32/watchdog.c
index 774768ad36..6a7c0d3983 100644
--- a/chip/stm32/watchdog.c
+++ b/chip/stm32/watchdog.c
@@ -26,31 +26,11 @@
#define IWDG_PRESCALER 6
#define IWDG_PRESCALER_DIV (1 << ((IWDG_PRESCALER) + 2))
-/*
- * We use the WWDG as an early warning for the real watchdog, which just
- * resets. Since it has a very short period, we need to allow several cycles
- * of this to make up one IWDG cycle. The WWDG's early warning kicks in
- * half way through the cycle, with a maximum time of 65.54ms at 32 MHz.
- */
-#define WATCHDOG_CYCLES_BEFORE_RESET \
- (WATCHDOG_PERIOD_MS / (65540 * 32000 / CPU_CLOCK))
-
-/* Keep a track of how many WWDG cycles we have had */
-static unsigned int watchdog_count;
-
-
-static void watchdog_reset_count(void)
-{
- watchdog_count = WATCHDOG_CYCLES_BEFORE_RESET;
-}
-
-
void watchdog_reload(void)
{
/* Reload the watchdog */
STM32_IWDG_KR = 0xaaaa;
- watchdog_reset_count();
#ifdef CONFIG_WATCHDOG_HELP
hwtimer_reset_watchdog();
#endif
@@ -76,8 +56,6 @@ int watchdog_init(void)
/* Start the watchdog (and re-lock registers) */
STM32_IWDG_KR = 0xcccc;
- watchdog_reset_count();
-
#ifdef CONFIG_WATCHDOG_HELP
/* Use a harder timer to warn about an impending watchdog reset */
hwtimer_setup_watchdog();