diff --git a/board/pit/board.h b/board/pit/board.h index a076abf882..707134e08f 100644 --- a/board/pit/board.h +++ b/board/pit/board.h @@ -8,9 +8,6 @@ #ifndef __BOARD_H #define __BOARD_H -/* 16 MHz SYSCLK clock frequency */ -#define CPU_CLOCK 16000000 - /* Use USART1 as console serial port */ #define CONFIG_CONSOLE_UART 1 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,6 +316,31 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, /*****************************************************************************/ /* Hooks */ +/* 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; + + /* 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) = 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; @@ -324,26 +349,17 @@ static void i2c_init(void) for (i = 0; i < I2C_PORTS_USED; i++, p++) { int port = p->port; - /* Enable clock if necessary */ + /* 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); } - - /* 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; - - /* Enable port */ - STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE; - - /* TODO: enable interrupts using I2C_CR2 bits 8,9 */ } + + /* 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 - -#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();