diff options
Diffstat (limited to 'chip/stm32/clock-stm32f4.c')
-rw-r--r-- | chip/stm32/clock-stm32f4.c | 553 |
1 files changed, 0 insertions, 553 deletions
diff --git a/chip/stm32/clock-stm32f4.c b/chip/stm32/clock-stm32f4.c deleted file mode 100644 index a50f5f51dd..0000000000 --- a/chip/stm32/clock-stm32f4.c +++ /dev/null @@ -1,553 +0,0 @@ -/* Copyright 2016 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. - */ - -/* Clocks and power management settings */ - -#include "chipset.h" -#include "clock.h" -#include "clock-f.h" -#include "common.h" -#include "console.h" -#include "cpu.h" -#include "hooks.h" -#include "hwtimer.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CLOCK, outstr) -#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) - -enum clock_osc { - OSC_HSI = 0, /* High-speed internal oscillator */ - OSC_HSE, /* High-speed external oscillator */ - OSC_PLL, /* PLL */ -}; - -/* - * NOTE: Sweetberry requires MCO2 <- HSE @ 24MHz - * MCO outputs are selected here but are not changeable later. - * A CONFIG may be needed if other boards have different MCO - * requirements. - */ -#define RCC_CFGR_MCO_CONFIG ((2 << 30) | /* MCO2 <- HSE */ \ - (0 << 27) | /* MCO2 div / 4 */ \ - (6 << 24) | /* MCO1 div / 4 */ \ - (3 << 21)) /* MCO1 <- PLL */ - -#ifdef CONFIG_STM32_CLOCK_HSE_HZ -/* RTC clock must 1 Mhz when derived from HSE */ -#define RTC_DIV DIV_ROUND_NEAREST(CONFIG_STM32_CLOCK_HSE_HZ, STM32F4_RTC_REQ) -#else /* !CONFIG_STM32_CLOCK_HSE_HZ */ -/* RTC clock not derived from HSE, turn it off */ -#define RTC_DIV 0 -#endif /* CONFIG_STM32_CLOCK_HSE_HZ */ - - -/* Bus clocks dividers depending on the configuration */ -/* - * max speed configuration with the PLL ON - * as defined in the registers file. - * For STM32F446: max 45 MHz - * For STM32F412: max AHB 100 MHz / APB2 100 Mhz / APB1 50 Mhz - */ -#define RCC_CFGR_DIVIDERS_WITH_PLL (RCC_CFGR_MCO_CONFIG | \ - CFGR_RTCPRE(RTC_DIV) | \ - CFGR_PPRE2(STM32F4_APB2_PRE) | \ - CFGR_PPRE1(STM32F4_APB1_PRE) | \ - CFGR_HPRE(STM32F4_AHB_PRE)) -/* - * lower power configuration without the PLL - * the frequency will be low (8-24Mhz), we don't want dividers to the - * peripheral clocks, put /1 everywhere. - */ -#define RCC_CFGR_DIVIDERS_NO_PLL (RCC_CFGR_MCO_CONFIG | CFGR_RTCPRE(0) | \ - CFGR_PPRE2(0) | CFGR_PPRE1(0) | CFGR_HPRE(0)) - -/* PLL output frequency */ -#define STM32F4_PLL_CLOCK (STM32F4_VCO_CLOCK / STM32F4_PLLP_DIV) - -/* current clock settings (PLL is initialized at startup) */ -static int current_osc = OSC_PLL; -static int current_io_freq = STM32F4_IO_CLOCK; -static int current_timer_freq = STM32F4_TIMER_CLOCK; - -/* the EC code expects to get the USART/I2C clock frequency here (APB clock) */ -int clock_get_freq(void) -{ - return current_io_freq; -} - -int clock_get_timer_freq(void) -{ - return current_timer_freq; -} - -static void clock_enable_osc(enum clock_osc osc, bool enabled) -{ - uint32_t ready; - uint32_t on; - - switch (osc) { - case OSC_HSI: - ready = STM32_RCC_CR_HSIRDY; - on = STM32_RCC_CR_HSION; - break; - case OSC_HSE: - ready = STM32_RCC_CR_HSERDY; - on = STM32_RCC_CR_HSEON; - break; - case OSC_PLL: - ready = STM32_RCC_CR_PLLRDY; - on = STM32_RCC_CR_PLLON; - break; - default: - ASSERT(0); - return; - } - - /* Turn off the oscillator, but don't wait for shutdown */ - if (!enabled) { - STM32_RCC_CR &= ~on; - return; - } - - /* Turn on the oscillator if not already on */ - wait_for_ready(&STM32_RCC_CR, on, ready); -} - -static void clock_switch_osc(enum clock_osc osc) -{ - uint32_t sw; - uint32_t sws; - - switch (osc) { - case OSC_HSI: - sw = STM32_RCC_CFGR_SW_HSI | RCC_CFGR_DIVIDERS_NO_PLL; - sws = STM32_RCC_CFGR_SWS_HSI; - break; - case OSC_HSE: - sw = STM32_RCC_CFGR_SW_HSE | RCC_CFGR_DIVIDERS_NO_PLL; - sws = STM32_RCC_CFGR_SWS_HSE; - break; - case OSC_PLL: - sw = STM32_RCC_CFGR_SW_PLL | RCC_CFGR_DIVIDERS_WITH_PLL; - sws = STM32_RCC_CFGR_SWS_PLL; - break; - default: - return; - } - - STM32_RCC_CFGR = sw; - while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) != sws) - ; -} - -void clock_set_osc(enum clock_osc osc) -{ - volatile uint32_t unused __attribute__((unused)); - - if (osc == current_osc) - return; - - hook_notify(HOOK_PRE_FREQ_CHANGE); - - switch (osc) { - default: - case OSC_HSI: - /* new clock settings: no dividers */ - current_io_freq = STM32F4_HSI_CLOCK; - current_timer_freq = STM32F4_HSI_CLOCK; - /* Switch to HSI */ - clock_switch_osc(OSC_HSI); - /* optimized flash latency settings for <30Mhz clock (0-WS) */ - STM32_FLASH_ACR = (STM32_FLASH_ACR & ~STM32_FLASH_ACR_LAT_MASK) - | STM32_FLASH_ACR_LATENCY_SLOW; - /* read-back the latency as advised by the Reference Manual */ - unused = STM32_FLASH_ACR; - /* Turn off the PLL1 to save power */ - clock_enable_osc(OSC_PLL, false); - break; - -#ifdef CONFIG_STM32_CLOCK_HSE_HZ - case OSC_HSE: - /* new clock settings: no dividers */ - current_io_freq = CONFIG_STM32_CLOCK_HSE_HZ; - current_timer_freq = CONFIG_STM32_CLOCK_HSE_HZ; - /* Switch to HSE */ - clock_switch_osc(OSC_HSE); - /* optimized flash latency settings for <30Mhz clock (0-WS) */ - STM32_FLASH_ACR = (STM32_FLASH_ACR & ~STM32_FLASH_ACR_LAT_MASK) - | STM32_FLASH_ACR_LATENCY_SLOW; - /* read-back the latency as advised by the Reference Manual */ - unused = STM32_FLASH_ACR; - /* Turn off the PLL1 to save power */ - clock_enable_osc(OSC_PLL, false); - break; -#endif /* CONFIG_STM32_CLOCK_HSE_HZ */ - - case OSC_PLL: - /* new clock settings */ - current_io_freq = STM32F4_IO_CLOCK; - current_timer_freq = STM32F4_TIMER_CLOCK; - /* turn on PLL and wait until it's ready */ - clock_enable_osc(OSC_PLL, true); - /* - * Increase flash latency before transition the clock - * Use the minimum Wait States value optimized for the platform. - */ - STM32_FLASH_ACR = (STM32_FLASH_ACR & ~STM32_FLASH_ACR_LAT_MASK) - | STM32_FLASH_ACR_LATENCY; - /* read-back the latency as advised by the Reference Manual */ - unused = STM32_FLASH_ACR; - /* Switch to PLL */ - clock_switch_osc(OSC_PLL); - - break; - } - - current_osc = osc; - hook_notify(HOOK_FREQ_CHANGE); -} - -static void clock_pll_configure(void) -{ -#ifdef CONFIG_STM32_CLOCK_HSE_HZ - int srcclock = CONFIG_STM32_CLOCK_HSE_HZ; -#else - int srcclock = STM32F4_HSI_CLOCK; -#endif - int plldiv, pllinputclock; - int pllmult, vcoclock; - int systemclock; - int usbdiv; - int i2sdiv; - - /* PLL input must be between 1-2MHz, near 2 */ - /* Valid values 2-63 */ - plldiv = (srcclock + STM32F4_PLL_REQ - 1) / STM32F4_PLL_REQ; - pllinputclock = srcclock / plldiv; - - /* PLL output clock: Must be 100-432MHz */ - pllmult = (STM32F4_VCO_CLOCK + (pllinputclock / 2)) / pllinputclock; - vcoclock = pllinputclock * pllmult; - - /* CPU/System clock */ - systemclock = vcoclock / STM32F4_PLLP_DIV; - /* USB clock = 48MHz exactly */ - usbdiv = (vcoclock + (STM32F4_USB_REQ / 2)) / STM32F4_USB_REQ; - assert(vcoclock / usbdiv == STM32F4_USB_REQ); - - /* SYSTEM/I2S: same system clock */ - i2sdiv = (vcoclock + (systemclock / 2)) / systemclock; - - /* Set up PLL */ - STM32_RCC_PLLCFGR = - PLLCFGR_PLLM(plldiv) | - PLLCFGR_PLLN(pllmult) | - PLLCFGR_PLLP(STM32F4_PLLP_DIV / 2 - 1) | -#if defined(CONFIG_STM32_CLOCK_HSE_HZ) - PLLCFGR_PLLSRC_HSE | -#else - PLLCFGR_PLLSRC_HSI | -#endif - PLLCFGR_PLLQ(usbdiv) | - PLLCFGR_PLLR(i2sdiv); -} - -void low_power_init(void); - -void config_hispeed_clock(void) -{ -#ifdef CONFIG_STM32_CLOCK_HSE_HZ - /* Ensure that HSE is ON */ - clock_enable_osc(OSC_HSE, true); -#endif - - /* Put the PLL settings, they are never changing */ - clock_pll_configure(); - clock_enable_osc(OSC_PLL, true); - - /* Switch SYSCLK to PLL, setup bus prescalers. */ - clock_switch_osc(OSC_PLL); - -#ifdef CONFIG_LOW_POWER_IDLE - low_power_init(); -#endif -} - -void clock_wait_bus_cycles(enum bus_type bus, uint32_t cycles) -{ - volatile uint32_t unused __attribute__((unused)); - - if (bus == BUS_AHB) { - while (cycles--) - unused = STM32_DMA_GET_ISR(0); - } else { /* APB */ - while (cycles--) - unused = STM32_USART_BRR(STM32_USART1_BASE); - } -} - -void clock_enable_module(enum module_id module, int enable) -{ - if (module == MODULE_USB) { - if (enable) { - STM32_RCC_AHB2ENR |= STM32_RCC_AHB2ENR_OTGFSEN; - STM32_RCC_AHB1ENR |= STM32_RCC_AHB1ENR_OTGHSEN | - STM32_RCC_AHB1ENR_OTGHSULPIEN; - } else { - STM32_RCC_AHB2ENR &= ~STM32_RCC_AHB2ENR_OTGFSEN; - STM32_RCC_AHB1ENR &= ~STM32_RCC_AHB1ENR_OTGHSEN & - ~STM32_RCC_AHB1ENR_OTGHSULPIEN; - } - return; - } else if (module == MODULE_I2C) { - if (enable) { - /* Enable clocks to I2C modules if necessary */ - STM32_RCC_APB1ENR |= - STM32_RCC_I2C1EN | STM32_RCC_I2C2EN - | STM32_RCC_I2C3EN | STM32_RCC_FMPI2C4EN; - STM32_RCC_DCKCFGR2 = - (STM32_RCC_DCKCFGR2 & ~DCKCFGR2_FMPI2C1SEL_MASK) - | DCKCFGR2_FMPI2C1SEL(FMPI2C1SEL_APB); - } else { - STM32_RCC_APB1ENR &= - ~(STM32_RCC_I2C1EN | STM32_RCC_I2C2EN | - STM32_RCC_I2C3EN | STM32_RCC_FMPI2C4EN); - } - return; - } else if (module == MODULE_ADC) { - if (enable) - STM32_RCC_APB2ENR |= STM32_RCC_APB2ENR_ADC1EN; - else - STM32_RCC_APB2ENR &= ~STM32_RCC_APB2ENR_ADC1EN; - return; - } -} - -/* Real Time Clock (RTC) */ - -#ifdef CONFIG_STM32_CLOCK_HSE_HZ -#define RTC_PREDIV_A 39 -#define RTC_FREQ ((STM32F4_RTC_REQ) / (RTC_PREDIV_A + 1)) /* Hz */ -#else /* from LSI clock */ -#define RTC_PREDIV_A 1 -#define RTC_FREQ (STM32F4_LSI_CLOCK / (RTC_PREDIV_A + 1)) /* Hz */ -#endif -#define RTC_PREDIV_S (RTC_FREQ - 1) -/* - * Scaling factor to ensure that the intermediate values computed from/to the - * RTC frequency are fitting in a 32-bit integer. - */ -#define SCALING 1000 - -int32_t rtcss_to_us(uint32_t rtcss) -{ - return ((RTC_PREDIV_S - rtcss) * (SECOND/SCALING) / (RTC_FREQ/SCALING)); -} - -uint32_t us_to_rtcss(int32_t us) -{ - return (RTC_PREDIV_S - (us * (RTC_FREQ/SCALING) / (SECOND/SCALING))); -} - -void rtc_init(void) -{ - /* Setup RTC Clock input */ -#ifdef CONFIG_STM32_CLOCK_HSE_HZ - /* RTC clocked from the HSE */ - STM32_RCC_BDCR = STM32_RCC_BDCR_RTCEN | BDCR_RTCSEL(BDCR_SRC_HSE); -#else - /* RTC clocked from the LSI, ensure first it is ON */ - wait_for_ready(&(STM32_RCC_CSR), - STM32_RCC_CSR_LSION, STM32_RCC_CSR_LSIRDY); - - STM32_RCC_BDCR = STM32_RCC_BDCR_RTCEN | BDCR_RTCSEL(BDCR_SRC_LSI); -#endif - - rtc_unlock_regs(); - - /* Enter RTC initialize mode */ - STM32_RTC_ISR |= STM32_RTC_ISR_INIT; - while (!(STM32_RTC_ISR & STM32_RTC_ISR_INITF)) - ; - - /* Set clock prescalars: Needs two separate writes. */ - STM32_RTC_PRER = - (STM32_RTC_PRER & ~STM32_RTC_PRER_S_MASK) | RTC_PREDIV_S; - STM32_RTC_PRER = - (STM32_RTC_PRER & ~STM32_RTC_PRER_A_MASK) - | (RTC_PREDIV_A << 16); - - /* Start RTC timer */ - STM32_RTC_ISR &= ~STM32_RTC_ISR_INIT; - while (STM32_RTC_ISR & STM32_RTC_ISR_INITF) - ; - - /* Enable RTC alarm interrupt */ - STM32_RTC_CR |= STM32_RTC_CR_ALRAIE | STM32_RTC_CR_BYPSHAD; - STM32_EXTI_RTSR |= EXTI_RTC_ALR_EVENT; - task_enable_irq(STM32_IRQ_RTC_ALARM); - - rtc_lock_regs(); -} - -#if defined(CONFIG_CMD_RTC) || defined(CONFIG_HOSTCMD_RTC) -void rtc_set(uint32_t sec) -{ - struct rtc_time_reg rtc; - - sec_to_rtc(sec, &rtc); - rtc_unlock_regs(); - - /* Disable alarm */ - STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE; - - /* Enter RTC initialize mode */ - STM32_RTC_ISR |= STM32_RTC_ISR_INIT; - while (!(STM32_RTC_ISR & STM32_RTC_ISR_INITF)) - ; - - /* Set clock prescalars */ - STM32_RTC_PRER = (RTC_PREDIV_A << 16) | RTC_PREDIV_S; - - STM32_RTC_TR = rtc.rtc_tr; - STM32_RTC_DR = rtc.rtc_dr; - /* Start RTC timer */ - STM32_RTC_ISR &= ~STM32_RTC_ISR_INIT; - - rtc_lock_regs(); -} -#endif - -#ifdef CONFIG_LOW_POWER_IDLE -/* Low power idle statistics */ -static int idle_sleep_cnt; -static int idle_dsleep_cnt; -static uint64_t idle_dsleep_time_us; -static int dsleep_recovery_margin_us = 1000000; - -/* STOP_MODE_LATENCY: delay to wake up from STOP mode with main regulator off */ -#define STOP_MODE_LATENCY 50 /* us */ -/* PLL_LOCK_LATENCY: delay to switch from HSI to PLL */ -#define PLL_LOCK_LATENCY 150 /* us */ -/* - * SET_RTC_MATCH_DELAY: max time to set RTC match alarm. If we set the alarm - * in the past, it will never wake up and cause a watchdog. - */ -#define SET_RTC_MATCH_DELAY 120 /* us */ - - -void low_power_init(void) -{ - /* Turn off the main regulator during stop mode */ - STM32_PWR_CR |= STM32_PWR_CR_LPSDSR /* aka LPDS */; -} - -void clock_refresh_console_in_use(void) -{ -} - -void __idle(void) -{ - timestamp_t t0; - uint32_t rtc_diff; - int next_delay, margin_us; - struct rtc_time_reg rtc0, rtc1; - - while (1) { - asm volatile("cpsid i"); - - t0 = get_time(); - next_delay = __hw_clock_event_get() - t0.le.lo; - - if (DEEP_SLEEP_ALLOWED && - (next_delay > (STOP_MODE_LATENCY + PLL_LOCK_LATENCY + - SET_RTC_MATCH_DELAY))) { - /* Deep-sleep in STOP mode */ - idle_dsleep_cnt++; - - /* - * TODO(b/174337385) no support for wake-up on USART - * uart_enable_wakeup(1); - */ - - /* Set deep sleep bit */ - CPU_SCB_SYSCTRL |= 0x4; - - set_rtc_alarm(0, next_delay - STOP_MODE_LATENCY - - PLL_LOCK_LATENCY, - &rtc0, 0); - - /* Switch to HSI */ - clock_switch_osc(OSC_HSI); - /* Turn off the PLL1 to save power */ - clock_enable_osc(OSC_PLL, false); - - /* ensure outstanding memory transactions complete */ - asm volatile("dsb"); - - asm("wfi"); - - CPU_SCB_SYSCTRL &= ~0x4; - - /* turn on PLL and wait until it's ready */ - clock_enable_osc(OSC_PLL, true); - /* Switch to PLL */ - clock_switch_osc(OSC_PLL); - - /*uart_enable_wakeup(0);*/ - - /* Fast forward timer according to RTC counter */ - reset_rtc_alarm(&rtc1); - rtc_diff = get_rtc_diff(&rtc0, &rtc1); - t0.val = t0.val + rtc_diff; - force_time(t0); - - /* Record time spent in deep sleep. */ - idle_dsleep_time_us += rtc_diff; - - /* Calculate how close we were to missing deadline */ - margin_us = next_delay - rtc_diff; - if (margin_us < 0) - /* Use CPUTS to save stack space */ - CPUTS("Idle overslept!\n"); - - /* Record the closest to missing a deadline. */ - if (margin_us < dsleep_recovery_margin_us) - dsleep_recovery_margin_us = margin_us; - } else { - idle_sleep_cnt++; - - /* Normal idle : only CPU clock stopped */ - asm("wfi"); - } - asm volatile("cpsie i"); - } -} - -/* Print low power idle statistics. */ -static int command_idle_stats(int argc, char **argv) -{ - timestamp_t ts = get_time(); - - ccprintf("Num idle calls that sleep: %d\n", idle_sleep_cnt); - ccprintf("Num idle calls that deep-sleep: %d\n", idle_dsleep_cnt); - ccprintf("Time spent in deep-sleep: %.6llds\n", - idle_dsleep_time_us); - ccprintf("Total time on: %.6llds\n", ts.val); - ccprintf("Deep-sleep closest to wake deadline: %dus\n", - dsleep_recovery_margin_us); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats, - "", - "Print last idle stats"); -#endif /* CONFIG_LOW_POWER_IDLE */ |