| /* 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) |
| |
| #ifdef CONFIG_STM32_CLOCK_HSE_HZ |
| #define RTC_PREDIV_A 39 |
| #define RTC_FREQ ((STM32F4_RTC_REQ) / (RTC_PREDIV_A + 1)) /* Hz */ |
| #else |
| /* LSI clock is 40kHz-ish */ |
| #define RTC_PREDIV_A 1 |
| #define RTC_FREQ (40000 / (RTC_PREDIV_A + 1)) /* Hz */ |
| #endif |
| #define RTC_PREDIV_S (RTC_FREQ - 1) |
| #define US_PER_RTC_TICK (1000000 / RTC_FREQ) |
| |
| int32_t rtcss_to_us(uint32_t rtcss) |
| { |
| return ((RTC_PREDIV_S - rtcss) * US_PER_RTC_TICK); |
| } |
| |
| uint32_t us_to_rtcss(int32_t us) |
| { |
| return (RTC_PREDIV_S - (us / US_PER_RTC_TICK)); |
| } |
| |
| static void wait_for_ready(volatile uint32_t *cr_reg, |
| uint32_t enable, uint32_t ready) |
| { |
| /* Ensure that clock source is ON */ |
| if (!(*cr_reg & ready)) { |
| /* Enable clock */ |
| *cr_reg |= enable; |
| /* Wait for ready */ |
| while (!(*cr_reg & ready)) |
| ; |
| } |
| } |
| |
| void config_hispeed_clock(void) |
| { |
| #ifdef CONFIG_STM32_CLOCK_HSE_HZ |
| int srcclock = CONFIG_STM32_CLOCK_HSE_HZ; |
| int clk_check_mask = STM32_RCC_CR_HSERDY; |
| int clk_enable_mask = STM32_RCC_CR_HSEON; |
| #else |
| int srcclock = STM32F4_HSI_CLOCK; |
| int clk_check_mask = STM32_RCC_CR_HSIRDY; |
| int clk_enable_mask = STM32_RCC_CR_HSION; |
| #endif |
| int plldiv, pllinputclock; |
| int pllmult, vcoclock; |
| int systemdivq, systemclock; |
| int usbdiv; |
| int i2sdiv; |
| |
| int ahbpre, apb1pre, apb2pre; |
| int rtcdiv = 0; |
| |
| /* If PLL is the clock source, PLL has already been set up. */ |
| if ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) == |
| STM32_RCC_CFGR_SWS_PLL) |
| return; |
| |
| /* Ensure that HSE/HSI is ON */ |
| wait_for_ready(&(STM32_RCC_CR), clk_enable_mask, clk_check_mask); |
| |
| /* 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 / 4; |
| systemdivq = 1; |
| /* 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; |
| |
| /* All IO clocks at STM32F4_IO_CLOCK |
| * For STM32F446: max 45 MHz |
| * For STM32F412: max 50 MHz |
| */ |
| /* AHB Prescalar */ |
| ahbpre = 0x8; /* AHB = system clock / 2 */ |
| /* NOTE: If apbXpre is not 0, timers are x2 clocked. RM0390 Fig. 13 */ |
| apb1pre = 0; /* APB1 = AHB */ |
| apb2pre = 0; /* APB2 = AHB */ |
| |
| #ifdef CONFIG_STM32_CLOCK_HSE_HZ |
| /* RTC clock = 1MHz */ |
| rtcdiv = (CONFIG_STM32_CLOCK_HSE_HZ + (STM32F4_RTC_REQ / 2)) |
| / STM32F4_RTC_REQ; |
| #endif |
| /* Switch SYSCLK to PLL, setup prescalars. |
| * EC codebase doesn't understand multiple clock domains |
| * so we enforce a clock config that keeps AHB = APB1 = APB2 |
| * allowing ec codebase assumptions about consistent clock |
| * rates to remain true. |
| * |
| * 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. |
| */ |
| STM32_RCC_CFGR = |
| (2 << 30) | /* MCO2 <- HSE */ |
| (0 << 27) | /* MCO2 div / 4 */ |
| (6 << 24) | /* MCO1 div / 4 */ |
| (3 << 21) | /* MCO1 <- PLL */ |
| CFGR_RTCPRE(rtcdiv) | |
| CFGR_PPRE2(apb2pre) | |
| CFGR_PPRE1(apb1pre) | |
| CFGR_HPRE(ahbpre) | |
| STM32_RCC_CFGR_SW_PLL; |
| |
| /* Set up PLL */ |
| STM32_RCC_PLLCFGR = |
| PLLCFGR_PLLM(plldiv) | |
| PLLCFGR_PLLN(pllmult) | |
| PLLCFGR_PLLP(systemdivq) | |
| #if defined(CONFIG_STM32_CLOCK_HSE_HZ) |
| PLLCFGR_PLLSRC_HSE | |
| #else |
| PLLCFGR_PLLSRC_HSI | |
| #endif |
| PLLCFGR_PLLQ(usbdiv) | |
| PLLCFGR_PLLR(i2sdiv); |
| |
| wait_for_ready(&(STM32_RCC_CR), |
| STM32_RCC_CR_PLLON, STM32_RCC_CR_PLLRDY); |
| |
| /* Wait until the PLL is the clock source */ |
| if ((STM32_RCC_CFGR & STM32_RCC_CFGR_SWS_MASK) == |
| STM32_RCC_CFGR_SWS_PLL) |
| ; |
| |
| /* Setup RTC Clock input */ |
| STM32_RCC_BDCR |= STM32_RCC_BDCR_BDRST; |
| #ifdef CONFIG_STM32_CLOCK_HSE_HZ |
| STM32_RCC_BDCR = STM32_RCC_BDCR_RTCEN | BCDR_RTCSEL(BDCR_SRC_HSE); |
| #else |
| /* Ensure that LSI is ON */ |
| wait_for_ready(&(STM32_RCC_CSR), |
| STM32_RCC_CSR_LSION, STM32_RCC_CSR_LSIRDY); |
| |
| STM32_RCC_BDCR = STM32_RCC_BDCR_RTCEN | BCDR_RTCSEL(BDCR_SRC_LSI); |
| #endif |
| } |
| |
| int clock_get_freq(void) |
| { |
| return STM32F4_IO_CLOCK; |
| } |
| |
| void clock_wait_bus_cycles(enum bus_type bus, uint32_t cycles) |
| { |
| volatile uint32_t dummy __attribute__((unused)); |
| |
| if (bus == BUS_AHB) { |
| while (cycles--) |
| dummy = STM32_DMA_GET_ISR(0); |
| } else { /* APB */ |
| while (cycles--) |
| dummy = 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; |
| } |
| |
| CPRINTS("Module %d is not supported for clock %s\n", |
| module, enable ? "enable" : "disable"); |
| } |
| |
| 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: 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(); |
| } |