summaryrefslogtreecommitdiff
path: root/chip/stm32/clock-stm32l4.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/clock-stm32l4.c')
-rw-r--r--chip/stm32/clock-stm32l4.c1110
1 files changed, 0 insertions, 1110 deletions
diff --git a/chip/stm32/clock-stm32l4.c b/chip/stm32/clock-stm32l4.c
deleted file mode 100644
index 2094751aab..0000000000
--- a/chip/stm32/clock-stm32l4.c
+++ /dev/null
@@ -1,1110 +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 for STM32L4xx as well as STM32L5xx. */
-
-#include "chipset.h"
-#include "clock.h"
-#include "clock-l4.h"
-#include "common.h"
-#include "console.h"
-#include "cpu.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "registers.h"
-#include "rtc.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)
-
-/* High-speed oscillator is 16 MHz */
-#define STM32_HSI_CLOCK 16000000
-/* Multi-speed oscillator is 4 MHz by default */
-#define STM32_MSI_CLOCK 4000000
-
-/* Real Time Clock (RTC) */
-
-#ifdef CONFIG_STM32_CLOCK_HSE_HZ
-#define RTC_PREDIV_A 39
-#define RTC_FREQ ((STM32L4_RTC_REQ) / (RTC_PREDIV_A + 1)) /* Hz */
-#else /* from LSI clock */
-#define RTC_PREDIV_A 1
-#define RTC_FREQ (STM32L4_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
-
-enum clock_osc {
- OSC_INIT = 0, /* Uninitialized */
- OSC_HSI, /* High-speed internal oscillator */
- OSC_MSI, /* Multi-speed internal oscillator */
-#ifdef STM32_HSE_CLOCK /* Allows us to catch absence of HSE at comiple time */
- OSC_HSE, /* High-speed external oscillator */
-#endif
- OSC_PLL, /* PLL */
-};
-
-static int freq = STM32_MSI_CLOCK;
-static int current_osc;
-
-int clock_get_freq(void)
-{
- return freq;
-}
-
-int clock_get_timer_freq(void)
-{
- return clock_get_freq();
-}
-
-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);
- }
-}
-
-static void clock_enable_osc(enum clock_osc osc)
-{
- uint32_t ready;
- uint32_t on;
-
- switch (osc) {
- case OSC_HSI:
- ready = STM32_RCC_CR_HSIRDY;
- on = STM32_RCC_CR_HSION;
- break;
- case OSC_MSI:
- ready = STM32_RCC_CR_MSIRDY;
- on = STM32_RCC_CR_MSION;
- break;
-#ifdef STM32_HSE_CLOCK
- case OSC_HSE:
-#ifdef STM32_HSE_BYP
- STM32_RCC_CR |= STM32_RCC_CR_HSEBYP;
-#endif
- ready = STM32_RCC_CR_HSERDY;
- on = STM32_RCC_CR_HSEON;
- break;
-#endif
- case OSC_PLL:
- ready = STM32_RCC_CR_PLLRDY;
- on = STM32_RCC_CR_PLLON;
- break;
- default:
- return;
- }
-
- /* Enable HSI and wait for HSI to be ready */
- wait_for_ready(&STM32_RCC_CR, on, ready);
-}
-
-/* Switch system clock oscillator */
-static void clock_switch_osc(enum clock_osc osc)
-{
- uint32_t sw;
- uint32_t sws;
- uint32_t val;
-
- switch (osc) {
- case OSC_HSI:
- sw = STM32_RCC_CFGR_SW_HSI;
- sws = STM32_RCC_CFGR_SWS_HSI;
- break;
- case OSC_MSI:
- sw = STM32_RCC_CFGR_SW_MSI;
- sws = STM32_RCC_CFGR_SWS_MSI;
- break;
-#ifdef STM32_HSE_CLOCK
- case OSC_HSE:
- sw = STM32_RCC_CFGR_SW_HSE;
- sws = STM32_RCC_CFGR_SWS_HSE;
- break;
-#endif
- case OSC_PLL:
- sw = STM32_RCC_CFGR_SW_PLL;
- sws = STM32_RCC_CFGR_SWS_PLL;
- break;
- default:
- return;
- }
- val = STM32_RCC_CFGR;
- val &= ~STM32_RCC_CFGR_SW;
- val |= sw;
- STM32_RCC_CFGR = val;
- while ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MSK) != sws)
- ;
-}
-
-/*
- * Configure PLL for HSE
- *
- * 1. Disable the PLL by setting PLLON to 0 in RCC_CR.
- * 2. Wait until PLLRDY is cleared. The PLL is now fully stopped.
- * 3. Change the desired parameter.
- * 4. Enable the PLL again by setting PLLON to 1.
- * 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, PLLREN
- * in RCC_PLLCFGR.
- */
-static int stm32_configure_pll(enum clock_osc osc,
- uint8_t m, uint8_t n, uint8_t r)
-{
- uint32_t val;
- bool pll_unchanged;
- int f;
-
- val = STM32_RCC_PLLCFGR;
- pll_unchanged = true;
-
- if (osc == OSC_HSI)
- if ((val & STM32_RCC_PLLCFGR_PLLSRC_MSK) !=
- STM32_RCC_PLLCFGR_PLLSRC_HSI)
- pll_unchanged = false;
-
- if (osc == OSC_MSI)
- if ((val & STM32_RCC_PLLCFGR_PLLSRC_MSK) !=
- STM32_RCC_PLLCFGR_PLLSRC_MSI)
- pll_unchanged = false;
-
-#ifdef STM32_HSE_CLOCK
- if (osc == OSC_HSE)
- if ((val & STM32_RCC_PLLCFGR_PLLSRC_MSK) !=
- STM32_RCC_PLLCFGR_PLLSRC_HSE)
- pll_unchanged = false;
-#endif
-
- if ((val & STM32_RCC_PLLCFGR_PLLM_MSK) !=
- ((m - 1) << STM32_RCC_PLLCFGR_PLLM_POS))
- pll_unchanged = false;
-
- if ((val & STM32_RCC_PLLCFGR_PLLN_MSK) !=
- (n << STM32_RCC_PLLCFGR_PLLN_POS))
- pll_unchanged = false;
-
- if ((val & STM32_RCC_PLLCFGR_PLLR_MSK) !=
- (((r >> 1) - 1) << STM32_RCC_PLLCFGR_PLLR_POS))
- pll_unchanged = false;
-
- if (pll_unchanged == true) {
- if (osc == OSC_HSI)
- f = STM32_HSI_CLOCK;
- else
- f = STM32_MSI_CLOCK;
-
- if (!(STM32_RCC_CR & STM32_RCC_CR_PLLRDY)) {
- STM32_RCC_CR |= STM32_RCC_CR_PLLON;
- STM32_RCC_PLLCFGR |= STM32_RCC_PLLCFGR_PLLREN;
-
- while ((STM32_RCC_CR & STM32_RCC_CR_PLLRDY) == 0)
- ;
- }
- /* (f * n) shouldn't overflow based on their max values */
- return (f * n / m / r);
- }
- /* 1 */
- STM32_RCC_CR &= ~STM32_RCC_CR_PLLON;
-
- /* 2 */
- while (STM32_RCC_CR & STM32_RCC_CR_PLLRDY)
- ;
-
- /* 3 */
- val = STM32_RCC_PLLCFGR;
-
- val &= ~STM32_RCC_PLLCFGR_PLLSRC_MSK;
- switch (osc) {
- case OSC_HSI:
- val |= STM32_RCC_PLLCFGR_PLLSRC_HSI;
- f = STM32_HSI_CLOCK;
- break;
- case OSC_MSI:
- val |= STM32_RCC_PLLCFGR_PLLSRC_MSI;
- f = STM32_MSI_CLOCK;
- break;
-#ifdef STM32_HSE_CLOCK
- case OSC_HSE:
- val |= STM32_RCC_PLLCFGR_PLLSRC_HSE;
- f = STM32_HSE_CLOCK;
- break;
-#endif
- default:
- return -1;
- }
-
- ASSERT(m > 0 && m < 9);
- val &= ~STM32_RCC_PLLCFGR_PLLM_MSK;
- val |= (m - 1) << STM32_RCC_PLLCFGR_PLLM_POS;
-
- /* Max and min values are from TRM */
- ASSERT(n > 7 && n < 87);
- val &= ~STM32_RCC_PLLCFGR_PLLN_MSK;
- val |= n << STM32_RCC_PLLCFGR_PLLN_POS;
-
- val &= ~STM32_RCC_PLLCFGR_PLLR_MSK;
- switch (r) {
- case 2:
- val |= 0 << STM32_RCC_PLLCFGR_PLLR_POS;
- break;
- case 4:
- val |= 1 << STM32_RCC_PLLCFGR_PLLR_POS;
- break;
- case 6:
- val |= 2 << STM32_RCC_PLLCFGR_PLLR_POS;
- break;
- case 8:
- val |= 3 << STM32_RCC_PLLCFGR_PLLR_POS;
- break;
- default:
- return -1;
- }
-
- STM32_RCC_PLLCFGR = val;
-
- /* 4 */
- clock_enable_osc(OSC_PLL);
-
- /* 5 */
- val = STM32_RCC_PLLCFGR;
- val |= 1 << STM32_RCC_PLLCFGR_PLLREN_POS;
- STM32_RCC_PLLCFGR = val;
-
- /* (f * n) shouldn't overflow based on their max values */
- return (f * n / m / r);
-}
-
-/**
- * Set system clock oscillator
- *
- * @param osc Oscillator to use
- * @param pll_osc Source oscillator for PLL. Ignored if osc is not PLL.
- */
-static void clock_set_osc(enum clock_osc osc, enum clock_osc pll_osc)
-{
- uint32_t val;
-
- if (osc == current_osc)
- return;
-
- if (current_osc != OSC_INIT)
- hook_notify(HOOK_PRE_FREQ_CHANGE);
-
- switch (osc) {
- case OSC_HSI:
- /* Ensure that HSI is ON */
- clock_enable_osc(osc);
-
- /* Set HSI as system clock after exiting stop mode */
- STM32_RCC_CFGR |= STM32_RCC_CFGR_STOPWUCK;
-
- /* Switch to HSI */
- clock_switch_osc(osc);
-
- /* Disable MSI */
- STM32_RCC_CR &= ~STM32_RCC_CR_MSION;
-
- freq = STM32_HSI_CLOCK;
- break;
-
- case OSC_MSI:
- /* Switch to MSI @ 1MHz */
- STM32_RCC_CR =
- (STM32_RCC_CR & ~STM32_RCC_ICSCR_MSIRANGE_MASK) |
- STM32_RCC_ICSCR_MSIRANGE_1MHZ;
- /* Ensure that MSI is ON */
- clock_enable_osc(osc);
-
- /*
- * Set MSI as system clock after exiting stop mode
- */
- STM32_RCC_CFGR &= ~STM32_RCC_CFGR_STOPWUCK;
-
- /* Switch to MSI */
- clock_switch_osc(osc);
-
- /* Disable HSI */
- STM32_RCC_CR &= ~STM32_RCC_CR_HSION;
-
- freq = STM32_MSI_CLOCK;
- break;
-
-#ifdef STM32_HSE_CLOCK
- case OSC_HSE:
- /* Ensure that HSE is stable */
- clock_enable_osc(osc);
-
- /* Switch to HSE */
- clock_switch_osc(osc);
-
- /* Disable other clock sources */
- STM32_RCC_CR &= ~(STM32_RCC_CR_MSION | STM32_RCC_CR_HSION |
- STM32_RCC_CR_PLLON);
-
- freq = STM32_HSE_CLOCK;
-
- break;
-#endif
- case OSC_PLL:
- /* Ensure that source clock is stable */
- if (pll_osc == OSC_INIT) {
- if ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MSK) !=
- STM32_RCC_CFGR_SWS_PLL) {
- STM32_RCC_CFGR |= STM32_RCC_CFGR_STOPWUCK;
- clock_enable_osc(OSC_HSI);
- freq = stm32_configure_pll(OSC_HSI, STM32_PLLM,
- STM32_PLLN,
- STM32_PLLR);
- } else {
- /* already set PLL, skip */
- freq = STM32_HSI_CLOCK * STM32_PLLN /
- STM32_PLLM / STM32_PLLR;
- break;
- }
- } else {
- clock_enable_osc(pll_osc);
- /* Configure PLLCFGR */
- freq = stm32_configure_pll(pll_osc, STM32_PLLM,
- STM32_PLLN, STM32_PLLR);
- }
- ASSERT(freq > 0);
-
- /* Change to Range 1 if Freq > 26MHz */
- if (freq > 26000000U) {
- /* Set VCO range 1 */
- val = STM32_RCC_CR;
- val &= ~PWR_CR1_VOS_MSK;
- val |= PWR_CR1_VOS_0;
- STM32_RCC_CR = val;
-
- /*
- * Set Flash latency according to frequency
- */
- val = STM32_FLASH_ACR;
- val &= ~STM32_FLASH_ACR_LATENCY_MASK;
- if (freq <= 16000000U) {
- val = val;
- } else if (freq <= 32000000U) {
- val |= 1;
- } else if (freq <= 48000000U) {
- val |= 2;
- } else if (freq <= 64000000U) {
- val |= 3;
- } else if (freq <= 80000000U) {
- val |= 4;
- } else {
- val |= 4;
- CPUTS("Incorrect Frequency setting in VOS1!\n");
- }
- STM32_FLASH_ACR = val;
- } else {
- val = STM32_FLASH_ACR;
- val &= ~STM32_FLASH_ACR_LATENCY_MASK;
-
- if (freq <= 6000000U) {
- val = val;
- } else if (freq <= 12000000U) {
- val |= 1;
- } else if (freq <= 18000000U) {
- val |= 2;
- } else if (freq <= 26000000U) {
- val |= 3;
- } else {
- val |= 4;
- CPUTS("Incorrect Frequency setting in VOS2!\n");
- }
- STM32_FLASH_ACR = val;
- }
-
- while (val != STM32_FLASH_ACR)
- ;
-
- /* Switch to PLL */
- clock_switch_osc(osc);
-
- /* TODO: Disable other sources */
- 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 {
- current_osc = osc;
- }
-}
-
-static uint64_t clock_mask;
-
-void clock_enable_module(enum module_id module, int enable)
-{
- uint64_t new_mask;
-
- if (enable)
- new_mask = clock_mask | BIT_ULL(module);
- else
- new_mask = clock_mask & ~BIT_ULL(module);
-
- /* Only change clock if needed */
- if (new_mask != clock_mask) {
- if (module == MODULE_ADC) {
- STM32_RCC_APB2ENR |= STM32_RCC_PB2_SYSCFGEN;
- STM32_RCC_APB1ENR1 |= STM32_RCC_PB1_PWREN;
-
- /* ADC select bit 28/29 */
- STM32_RCC_CCIPR &= ~STM32_RCC_CCIPR_ADCSEL_MSK;
- STM32_RCC_CCIPR |= (STM32_RCC_CCIPR_ADCSEL_0 |
- STM32_RCC_CCIPR_ADCSEL_1);
- /* ADC clock enable */
- if (enable)
- STM32_RCC_AHB2ENR |= STM32_RCC_HB2_ADC1;
- else
- STM32_RCC_AHB2ENR &= ~STM32_RCC_HB2_ADC1;
- } else if (module == MODULE_SPI_FLASH) {
- if (enable)
- STM32_RCC_APB1ENR1 |= STM32_RCC_PB1_SPI2;
- else
- STM32_RCC_APB1ENR1 &= ~STM32_RCC_PB1_SPI2;
- } else if (module == MODULE_SPI ||
- module == MODULE_SPI_CONTROLLER) {
- if (enable)
- STM32_RCC_APB2ENR |= STM32_RCC_APB2ENR_SPI1EN;
- else if ((new_mask & (BIT(MODULE_SPI) |
- BIT(MODULE_SPI_CONTROLLER))) == 0)
- STM32_RCC_APB2ENR &= ~STM32_RCC_APB2ENR_SPI1EN;
- } else if (module == MODULE_USB) {
-#if defined(STM32_RCC_APB1ENR2_USBFSEN)
- if (enable)
- STM32_RCC_APB1ENR2 |=
- STM32_RCC_APB1ENR2_USBFSEN;
- else
- STM32_RCC_APB1ENR2 &=
- ~STM32_RCC_APB1ENR2_USBFSEN;
-#endif
- }
- }
-
- clock_mask = new_mask;
-}
-
-int clock_is_module_enabled(enum module_id module)
-{
- return !!(clock_mask & BIT_ULL(module));
-}
-
-void rtc_init(void)
-{
- /* Enable RTC Alarm in EXTI */
- STM32_EXTI_RTSR |= EXTI_RTC_ALR_EVENT;
- task_enable_irq(STM32_IRQ_RTC_ALARM);
-
- /* RTC was initilized, avoid initialization again */
- if (STM32_RTC_ISR & STM32_RTC_ISR_INITS)
- return;
-
- 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;
-
- 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
-
-
-void clock_init(void)
-{
-#ifdef STM32_HSE_CLOCK
- clock_set_osc(OSC_PLL, OSC_HSE);
-#else
-#ifdef STM32_USE_PLL
- clock_set_osc(OSC_PLL, OSC_INIT);
-#else
- clock_set_osc(OSC_HSI, OSC_INIT);
-#endif
-#endif
-
-#ifdef CONFIG_LOW_POWER_IDLE
- low_power_init();
- rtc_init();
-#endif
-}
-
-static void clock_chipset_startup(void)
-{
- /* Return to full speed */
- clock_enable_module(MODULE_CHIPSET, 1);
-}
-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 if no other module requires full speed */
- clock_enable_module(MODULE_CHIPSET, 0);
-}
-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, OSC_INIT);
- else if (!strcasecmp(argv[1], "msi"))
- clock_set_osc(OSC_MSI, OSC_INIT);
-#ifdef STM32_HSE_CLOCK
- else if (!strcasecmp(argv[1], "hse"))
- clock_set_osc(OSC_HSE, OSC_INIT);
- else if (!strcasecmp(argv[1], "pll"))
- clock_set_osc(OSC_PLL, OSC_HSE);
-#else
- else if (!strcasecmp(argv[1], "pll"))
- clock_set_osc(OSC_PLL, OSC_HSI);
-#endif
- else
- return EC_ERROR_PARAM1;
- }
-
- ccprintf("Clock frequency is now %d Hz\n", freq);
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(clock, command_clock,
- "hsi | msi"
-#ifdef STM32_HSE_CLOCK
- " | hse | pll"
-#endif
- ,
- "Set clock frequency");
-
-uint32_t rtcss_to_us(uint32_t rtcss)
-{
- return ((RTC_PREDIV_S - (rtcss & 0x7FFF)) * (SECOND / SCALING) /
- (RTC_FREQ / SCALING));
-}
-
-uint32_t us_to_rtcss(uint32_t us)
-{
- return (RTC_PREDIV_S -
- (us * (RTC_FREQ / SCALING) / (SECOND / SCALING)));
-}
-
-
-/* Convert decimal to BCD */
-static uint8_t u8_to_bcd(uint8_t val)
-{
- /* Fast division by 10 (when lacking HW div) */
- uint32_t quot = ((uint32_t)val * 0xCCCD) >> 19;
- uint32_t rem = val - quot * 10;
-
- return rem | (quot << 4);
-}
-
-/* Convert between RTC regs in BCD and seconds */
-static uint32_t rtc_tr_to_sec(uint32_t rtc_tr)
-{
- uint32_t sec;
-
- /* convert the hours field */
- sec = (((rtc_tr & RTC_TR_HT) >> RTC_TR_HT_POS) * 10 +
- ((rtc_tr & RTC_TR_HU) >> RTC_TR_HU_POS)) * 3600;
- /* convert the minutes field */
- sec += (((rtc_tr & RTC_TR_MNT) >> RTC_TR_MNT_POS) * 10 +
- ((rtc_tr & RTC_TR_MNU) >> RTC_TR_MNU_POS)) * 60;
- /* convert the seconds field */
- sec += ((rtc_tr & RTC_TR_ST) >> RTC_TR_ST_POS) * 10 +
- (rtc_tr & RTC_TR_SU);
- return sec;
-}
-
-static uint32_t sec_to_rtc_tr(uint32_t sec)
-{
- uint32_t rtc_tr;
- uint8_t hour;
- uint8_t min;
-
- sec %= SECS_PER_DAY;
- /* convert the hours field */
- hour = sec / 3600;
- rtc_tr = u8_to_bcd(hour) << 16;
- /* convert the minutes field */
- sec -= hour * 3600;
- min = sec / 60;
- rtc_tr |= u8_to_bcd(min) << 8;
- /* convert the seconds field */
- sec -= min * 60;
- rtc_tr |= u8_to_bcd(sec);
-
- return rtc_tr;
-}
-
-/* Register setup before RTC alarm is allowed for update */
-static void pre_work_set_rtc_alarm(void)
-{
- rtc_unlock_regs();
-
- /* Make sure alarm is disabled */
- STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE;
- while (!(STM32_RTC_ISR & STM32_RTC_ISR_ALRAWF))
- ;
- STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF;
- STM32_EXTI_PR = BIT(18);
-}
-
-/* Register setup after RTC alarm is updated */
-static void post_work_set_rtc_alarm(void)
-{
- /* Enable alarm and alarm interrupt */
- STM32_EXTI_IMR |= BIT(18);
- STM32_EXTI_RTSR |= BIT(18);
- STM32_RTC_CR |= (STM32_RTC_CR_ALRAE);
-
- rtc_lock_regs();
-}
-
-#ifdef CONFIG_HOSTCMD_RTC
-static struct wake_time host_wake_time;
-
-bool is_host_wake_alarm_expired(timestamp_t ts)
-{
- return host_wake_time.ts.val &&
- timestamp_expired(host_wake_time.ts, &ts);
-}
-
-void restore_host_wake_alarm(void)
-{
- if (!host_wake_time.ts.val)
- return;
-
- pre_work_set_rtc_alarm();
-
- /* Set alarm time */
- STM32_RTC_ALRMAR = host_wake_time.rtc_alrmar;
-
- post_work_set_rtc_alarm();
-}
-
-static uint32_t rtc_dr_to_sec(uint32_t rtc_dr)
-{
- struct calendar_date time;
- uint32_t sec;
-
- time.year = (((rtc_dr & 0xf00000) >> 20) * 10 +
- ((rtc_dr & 0xf0000) >> 16));
- time.month = (((rtc_dr & 0x1000) >> 12) * 10 +
- ((rtc_dr & 0xf00) >> 8));
- time.day = ((rtc_dr & 0x30) >> 4) * 10 + (rtc_dr & 0xf);
-
- sec = date_to_sec(time);
-
- return sec;
-}
-
-static uint32_t sec_to_rtc_dr(uint32_t sec)
-{
- struct calendar_date time;
- uint32_t rtc_dr;
-
- time = sec_to_date(sec);
-
- rtc_dr = u8_to_bcd(time.year) << 16;
- rtc_dr |= u8_to_bcd(time.month) << 8;
- rtc_dr |= u8_to_bcd(time.day);
-
- return rtc_dr;
-}
-#endif
-
-uint32_t rtc_to_sec(const struct rtc_time_reg *rtc)
-{
- uint32_t sec = 0;
-
-#ifdef CONFIG_HOSTCMD_RTC
- sec = rtc_dr_to_sec(rtc->rtc_dr);
-#endif
- return sec + (rtcss_to_us(rtc->rtc_ssr) / SECOND) +
- rtc_tr_to_sec(rtc->rtc_tr);
-}
-
-void sec_to_rtc(uint32_t sec, struct rtc_time_reg *rtc)
-{
- rtc->rtc_dr = 0;
-#ifdef CONFIG_HOSTCMD_RTC
- rtc->rtc_dr = sec_to_rtc_dr(sec);
-#endif
- rtc->rtc_tr = sec_to_rtc_tr(sec);
- rtc->rtc_ssr = 0;
-}
-
-/* Return sub-10-sec time diff between two rtc readings
- *
- * Note: this function assumes rtc0 was sampled before rtc1.
- * Additionally, this function only looks at the difference mod 10
- * seconds.
- */
-uint32_t get_rtc_diff(const struct rtc_time_reg *rtc0,
- const struct rtc_time_reg *rtc1)
-{
- uint32_t rtc0_val, rtc1_val, diff;
-
- rtc0_val = (rtc0->rtc_tr & RTC_TR_SU) * SECOND +
- rtcss_to_us(rtc0->rtc_ssr);
- rtc1_val = (rtc1->rtc_tr & RTC_TR_SU) * SECOND +
- rtcss_to_us(rtc1->rtc_ssr);
- diff = rtc1_val;
- if (rtc1_val < rtc0_val) {
- /* rtc_ssr has wrapped, since we assume rtc0 < rtc1, add
- * 10 seconds to get the correct value
- */
- diff += 10 * SECOND;
- }
- diff -= rtc0_val;
- return diff;
-}
-
-void rtc_read(struct rtc_time_reg *rtc)
-{
- /*
- * Read current time synchronously. Each register must be read
- * twice with identical values because glitches may occur for reads
- * close to the RTCCLK edge.
- */
- do {
- rtc->rtc_dr = STM32_RTC_DR;
-
- do {
- rtc->rtc_tr = STM32_RTC_TR;
-
- do {
- rtc->rtc_ssr = STM32_RTC_SSR;
- } while (rtc->rtc_ssr != STM32_RTC_SSR);
-
- } while (rtc->rtc_tr != STM32_RTC_TR);
-
- } while (rtc->rtc_dr != STM32_RTC_DR);
-}
-
-void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us,
- struct rtc_time_reg *rtc, uint8_t save_alarm)
-{
- uint32_t alarm_sec = 0;
- uint32_t alarm_us = 0;
-
- if (delay_s == EC_RTC_ALARM_CLEAR && !delay_us) {
- reset_rtc_alarm(rtc);
- return;
- }
-
- /* Alarm timeout must be within 1 day (86400 seconds) */
- ASSERT((delay_s + delay_us / SECOND) < SECS_PER_DAY);
-
- pre_work_set_rtc_alarm();
- rtc_read(rtc);
-
- /* Calculate alarm time */
- alarm_sec = rtc_tr_to_sec(rtc->rtc_tr) + delay_s;
-
- if (delay_us) {
- alarm_us = rtcss_to_us(rtc->rtc_ssr) + delay_us;
- alarm_sec = alarm_sec + alarm_us / SECOND;
- alarm_us = alarm_us % SECOND;
- }
-
- /*
- * If seconds is greater than 1 day, subtract by 1 day to deal with
- * 24-hour rollover.
- */
- if (alarm_sec >= SECS_PER_DAY)
- alarm_sec -= SECS_PER_DAY;
-
- /*
- * Set alarm time in seconds and check for match on
- * hours, minutes, and seconds.
- */
- STM32_RTC_ALRMAR = sec_to_rtc_tr(alarm_sec) | 0xc0000000;
-
- /*
- * Set alarm time in subseconds and check for match on subseconds.
- * If the caller doesn't specify subsecond delay (e.g. host command),
- * just align the alarm time to second.
- */
- STM32_RTC_ALRMASSR = delay_us ?
- (us_to_rtcss(alarm_us) | 0x0f000000) : 0;
-
-#ifdef CONFIG_HOSTCMD_RTC
- /*
- * If alarm is set by the host, preserve the wake time timestamp
- * and alarm registers.
- */
- if (save_alarm) {
- host_wake_time.ts.val = delay_s * SECOND + get_time().val;
- host_wake_time.rtc_alrmar = STM32_RTC_ALRMAR;
- }
-#endif
- post_work_set_rtc_alarm();
-}
-
-uint32_t get_rtc_alarm(void)
-{
- struct rtc_time_reg now;
- uint32_t now_sec;
- uint32_t alarm_sec;
-
- if (!(STM32_RTC_CR & STM32_RTC_CR_ALRAE))
- return 0;
-
- rtc_read(&now);
-
- now_sec = rtc_tr_to_sec(now.rtc_tr);
- alarm_sec = rtc_tr_to_sec(STM32_RTC_ALRMAR & 0x3fffff);
-
- return ((alarm_sec < now_sec) ? SECS_PER_DAY : 0) +
- (alarm_sec - now_sec);
-}
-
-void reset_rtc_alarm(struct rtc_time_reg *rtc)
-{
- rtc_unlock_regs();
-
- /* Disable alarm */
- STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE;
- STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF;
-
- /* Disable RTC alarm interrupt */
- STM32_EXTI_IMR &= ~BIT(18);
- STM32_EXTI_PR = BIT(18);
-
- /* Clear the pending RTC alarm IRQ in NVIC */
- task_clear_pending_irq(STM32_IRQ_RTC_ALARM);
-
- /* Read current time */
- rtc_read(rtc);
-
- rtc_lock_regs();
-}
-
-#ifdef CONFIG_HOSTCMD_RTC
-static void set_rtc_host_event(void)
-{
- host_set_single_event(EC_HOST_EVENT_RTC);
-}
-DECLARE_DEFERRED(set_rtc_host_event);
-#endif
-
-test_mockable
-void __rtc_alarm_irq(void)
-{
- struct rtc_time_reg rtc;
-
- reset_rtc_alarm(&rtc);
-
-#ifdef CONFIG_HOSTCMD_RTC
- /* Wake up the host if there is a saved rtc wake alarm. */
- if (host_wake_time.ts.val) {
- host_wake_time.ts.val = 0;
- hook_call_deferred(&set_rtc_host_event_data, 0);
- }
-#endif
-}
-DECLARE_IRQ(STM32_IRQ_RTC_ALARM, __rtc_alarm_irq, 1);
-
-
-void print_system_rtc(enum console_channel ch)
-{
- uint32_t sec;
- struct rtc_time_reg rtc;
-
- rtc_read(&rtc);
- sec = rtc_to_sec(&rtc);
-
- cprintf(ch, "RTC: 0x%08x (%d.00 s)\n", sec, sec);
-}
-
-
-#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)
-{
- /* Enter stop1 mode */
- uint32_t val;
-
- val = STM32_PWR_CR1;
- val &= ~PWR_CR1_LPMS_MSK;
- val |= PWR_CR1_LPMS_STOP1;
- STM32_PWR_CR1 = val;
-}
-
-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++;
-
- 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);
-
-
- /* ensure outstanding memory transactions complete */
- asm volatile("dsb");
-
- asm("wfi");
-
- CPU_SCB_SYSCTRL &= ~0x4;
-
- /* turn on PLL and wait until it's ready */
- STM32_RCC_APB1ENR1 |= STM32_RCC_APB1ENR1_PWREN;
- clock_wait_bus_cycles(BUS_APB, 2);
-
- stm32_configure_pll(OSC_HSI, STM32_PLLM,
- STM32_PLLN, STM32_PLLR);
-
- /* 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");
- }
-}
-
-/*****************************************************************************/
-/* Console commands */
-/* 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: %.6llus\n",
- idle_dsleep_time_us);
- ccprintf("Total time on: %.6llus\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 */