blob: 353718f5648db9aaeb5755521f6b3fb376817b32 [file] [log] [blame]
/* 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();
}