/* Copyright 2014 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 "uart.h" #include "util.h" /* Console output macros */ #define CPUTS(outstr) cputs(CC_CLOCK, outstr) #define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) /* use 48Mhz USB-synchronized High-speed oscillator */ #define HSI48_CLOCK 48000000 /* use PLL at 38.4MHz as system clock. */ #define PLL_CLOCK 38400000 /* Low power idle statistics */ #ifdef CONFIG_LOW_POWER_IDLE static int idle_sleep_cnt; static int idle_dsleep_cnt; static uint64_t idle_dsleep_time_us; static int dsleep_recovery_margin_us = 1000000; /* * minimum delay to enter stop mode * * STOP_MODE_LATENCY: max time to wake up from STOP mode with regulator in low * power mode is 5 us + PLL locking time is 200us. * * 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. * For STM32F3, we are using HSE, which requires additional time to start up. * Therefore, the latency for STM32F3 is set longer. * * RESTORE_HOST_ALARM_LATENCY: max latency between the deferred routine is * called and the host alarm is actually restored. In practice, the max latency * is measured as ~600us. 1000us should be conservative enough to guarantee * we won't miss the host alarm. */ #ifdef CHIP_VARIANT_STM32F373 #define STOP_MODE_LATENCY 500 /* us */ #elif defined(CHIP_VARIANT_STM32F05X) #define STOP_MODE_LATENCY 300 /* us */ #elif (CPU_CLOCK == PLL_CLOCK) #define STOP_MODE_LATENCY 300 /* us */ #else #define STOP_MODE_LATENCY 50 /* us */ #endif #define SET_RTC_MATCH_DELAY 200 /* us */ #ifdef CONFIG_HOSTCMD_RTC #define RESTORE_HOST_ALARM_LATENCY 1000 /* us */ #endif #endif /* CONFIG_LOW_POWER_IDLE */ /* * RTC clock frequency (By default connected to LSI clock) * * The LSI on any given chip can be between 30 kHz to 60 kHz. * Without calibration, LSI frequency may be off by as much as 50%. * * Set synchronous clock freq to (RTC clock source / 2) to maximize * subsecond resolution. Set asynchronous clock to 1 Hz. */ #define RTC_PREDIV_A 1 #ifdef CONFIG_STM32_CLOCK_LSE #define RTC_FREQ (32768 / (RTC_PREDIV_A + 1)) /* Hz */ /* GCD(RTC_FREQ, 1000000) */ #define RTC_GCD 64 #else /* LSI clock, 40kHz-ish */ #define RTC_FREQ (40000 / (RTC_PREDIV_A + 1)) /* Hz */ /* GCD(RTC_FREQ, 1000000) */ #define RTC_GCD 20000 #endif #define RTC_PREDIV_S (RTC_FREQ - 1) /* * There are (1000000 / RTC_FREQ) us per RTC tick, take GCD of both terms * for conversion calculations to fit in 32 bits. */ #define US_GCD (1000000 / RTC_GCD) #define RTC_FREQ_GCD (RTC_FREQ / RTC_GCD) int32_t rtcss_to_us(uint32_t rtcss) { return ((RTC_PREDIV_S - (rtcss & 0x7fff)) * US_GCD) / RTC_FREQ_GCD; } uint32_t us_to_rtcss(int32_t us) { return RTC_PREDIV_S - us * RTC_FREQ_GCD / US_GCD; } void config_hispeed_clock(void) { #ifdef CHIP_FAMILY_STM32F3 /* Ensure that HSE is ON */ wait_for_ready(&STM32_RCC_CR, BIT(16), BIT(17)); /* * HSE = 24MHz, no prescalar, no MCO, with PLL *2 => 48MHz SYSCLK * HCLK = SYSCLK, PCLK = HCLK / 2 = 24MHz * ADCCLK = PCLK / 6 = 4MHz * USB uses SYSCLK = 48MHz */ STM32_RCC_CFGR = 0x0041a400; /* Enable the PLL */ STM32_RCC_CR |= 0x01000000; /* Wait until the PLL is ready */ while (!(STM32_RCC_CR & 0x02000000)) ; /* Switch SYSCLK to PLL */ STM32_RCC_CFGR |= 0x2; /* Wait until the PLL is the clock source */ while ((STM32_RCC_CFGR & 0xc) != 0x8) ; /* F03X and F05X and F070 don't have HSI48 */ #elif defined(CHIP_VARIANT_STM32F03X) || \ defined(CHIP_VARIANT_STM32F05X) || \ defined(CHIP_VARIANT_STM32F070) /* If PLL is the clock source, PLL has already been set up. */ if ((STM32_RCC_CFGR & 0xc) == 0x8) return; /* Ensure that HSI is ON */ wait_for_ready(&STM32_RCC_CR, BIT(0), BIT(1)); /* * HSI = 8MHz, HSI/2 with PLL *12 = ~48 MHz * therefore PCLK = FCLK = SYSCLK = 48MHz */ /* Switch the PLL source to HSI/2 */ STM32_RCC_CFGR &= ~(0x00018000); /* * Specify HSI/2 clock as input clock to PLL and set PLL (*12). */ STM32_RCC_CFGR |= 0x00280000; /* Enable the PLL. */ STM32_RCC_CR |= 0x01000000; /* Wait until PLL is ready. */ while (!(STM32_RCC_CR & 0x02000000)) ; /* Switch SYSCLK to PLL. */ STM32_RCC_CFGR |= 0x2; /* wait until the PLL is the clock source */ while ((STM32_RCC_CFGR & 0xc) != 0x8) ; #else /* Ensure that HSI48 is ON */ wait_for_ready(&STM32_RCC_CR2, BIT(16), BIT(17)); #if (CPU_CLOCK == HSI48_CLOCK) /* * HSI48 = 48MHz, no prescaler, no MCO, no PLL * therefore PCLK = FCLK = SYSCLK = 48MHz * USB uses HSI48 = 48MHz */ #ifdef CONFIG_USB /* * Configure and enable Clock Recovery System * * Since we are running from the internal RC HSI48 clock, the CSR * is needed to guarantee an accurate 48MHz clock for USB. * * The default values configure the CRS to use the periodic USB SOF * as the SYNC signal for calibrating the HSI48. * */ /* Enable Clock Recovery System */ STM32_RCC_APB1ENR |= STM32_RCC_PB1_CRS; /* Enable automatic trimming */ STM32_CRS_CR |= STM32_CRS_CR_AUTOTRIMEN; /* Enable oscillator clock for the frequency error counter */ STM32_CRS_CR |= STM32_CRS_CR_CEN; #endif /* switch SYSCLK to HSI48 */ STM32_RCC_CFGR = 0x00000003; /* wait until the HSI48 is the clock source */ while ((STM32_RCC_CFGR & 0xc) != 0xc) ; #elif (CPU_CLOCK == PLL_CLOCK) /* * HSI48 = 48MHz, no prescalar, no MCO, with PLL *4/5 => 38.4MHz SYSCLK * therefore PCLK = FCLK = SYSCLK = 38.4MHz * USB uses HSI48 = 48MHz */ /* If PLL is the clock source, PLL has already been set up. */ if ((STM32_RCC_CFGR & 0xc) == 0x8) return; /* * Specify HSI48 clock as input clock to PLL and set PLL multiplier * and divider. */ STM32_RCC_CFGR = 0x00098000; STM32_RCC_CFGR2 = 0x4; /* Enable the PLL. */ STM32_RCC_CR |= 0x01000000; /* Wait until PLL is ready. */ while (!(STM32_RCC_CR & 0x02000000)) ; /* Switch SYSCLK to PLL. */ STM32_RCC_CFGR |= 0x2; /* wait until the PLL is the clock source */ while ((STM32_RCC_CFGR & 0xc) != 0x8) ; #else #error "CPU_CLOCK must be either 48MHz or 38.4MHz" #endif #endif } #ifdef CONFIG_HIBERNATE void __enter_hibernate(uint32_t seconds, uint32_t microseconds) { struct rtc_time_reg rtc; if (seconds || microseconds) set_rtc_alarm(seconds, microseconds, &rtc, 0); /* interrupts off now */ asm volatile("cpsid i"); #ifdef CONFIG_HIBERNATE_WAKEUP_PINS /* enable the wake up pins */ STM32_PWR_CSR |= CONFIG_HIBERNATE_WAKEUP_PINS; #endif STM32_PWR_CR |= 0xe; CPU_SCB_SYSCTRL |= 0x4; /* go to Standby mode */ asm("wfi"); /* we should never reach that point */ while (1) ; } #endif #ifdef CONFIG_HOSTCMD_RTC static void restore_host_wake_alarm_deferred(void) { restore_host_wake_alarm(); } DECLARE_DEFERRED(restore_host_wake_alarm_deferred); #endif #ifdef CONFIG_LOW_POWER_IDLE 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; #ifdef CONFIG_LOW_POWER_IDLE_LIMITED if (idle_is_disabled()) goto en_int; #endif if (DEEP_SLEEP_ALLOWED && #ifdef CONFIG_HOSTCMD_RTC /* * Don't go to deep sleep mode if we might miss the * wake alarm that the host requested. Note that the * host alarm always aligns to second. Considering the * worst case, we have to ensure alarm won't go off * within RESTORE_HOST_ALARM_LATENCY + 1 second after * EC exits deep sleep mode. */ !is_host_wake_alarm_expired( (timestamp_t)(next_delay + t0.val + SECOND + RESTORE_HOST_ALARM_LATENCY)) && #endif (next_delay > (STOP_MODE_LATENCY + SET_RTC_MATCH_DELAY))) { /* Deep-sleep in STOP mode */ idle_dsleep_cnt++; uart_enable_wakeup(1); /* Set deep sleep bit */ CPU_SCB_SYSCTRL |= 0x4; set_rtc_alarm(0, next_delay - STOP_MODE_LATENCY, &rtc0, 0); asm("wfi"); CPU_SCB_SYSCTRL &= ~0x4; uart_enable_wakeup(0); /* * By default only HSI 8MHz is enabled here. Re-enable * high-speed clock if in use. */ config_hispeed_clock(); /* 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); #ifdef CONFIG_HOSTCMD_RTC hook_call_deferred( &restore_host_wake_alarm_deferred_data, 0); #endif /* 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"); } #ifdef CONFIG_LOW_POWER_IDLE_LIMITED en_int: #endif asm volatile("cpsie i"); } } #endif /* CONFIG_LOW_POWER_IDLE */ int clock_get_freq(void) { return CPU_CLOCK; } 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_DMA1_REGS->isr; } else { /* APB */ while (cycles--) unused = STM32_USART_BRR(STM32_USART1_BASE); } } void clock_enable_module(enum module_id module, int enable) { if (module == MODULE_ADC) { if (enable) STM32_RCC_APB2ENR |= STM32_RCC_APB2ENR_ADCEN; else STM32_RCC_APB2ENR &= ~STM32_RCC_APB2ENR_ADCEN; return; } else if (module == MODULE_USB) { if (enable) STM32_RCC_APB1ENR |= STM32_RCC_PB1_USB; else STM32_RCC_APB1ENR &= ~STM32_RCC_PB1_USB; } } int clock_is_module_enabled(enum module_id module) { if (module == MODULE_ADC) return !!(STM32_RCC_APB2ENR & STM32_RCC_APB2ENR_ADCEN); else if (module == MODULE_USB) return !!(STM32_RCC_APB1ENR & STM32_RCC_PB1_USB); return 0; } void rtc_init(void) { 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 */ STM32_RTC_PRER = (RTC_PREDIV_A << 16) | RTC_PREDIV_S; /* 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 #if defined(CONFIG_LOW_POWER_IDLE) && defined(CONFIG_COMMON_RUNTIME) #ifdef CONFIG_CMD_IDLE_STATS /** * 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_CMD_IDLE_STATS */ #endif