mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-07 16:11:43 +00:00
On stm32f412, AHB prescaler must be 1 in order for stm32f412 to be clocked at greater than 50MHz. APBX prescaler must be 2 so the clocks can be in the right range. When APBX prescaler != 1, it results in 2x timer clocks on both APB1 and APB2. We added a new clock_get_timer_freq() function for stm32 to get timer specific clock frequency so we can return 2x timer clocks when APBX != 1. Flash latencies also need to be changed when we clock at 96MHz, the FLASH_ACR_LATENCY defines are moved into the variant-specific switches so each board can defined latency when setting CPU clocks. BUG=b:38077127 TEST=`make BOARD=rose -j`, touch performance improved by 2x. Change-Id: Ieb211ad80c168d3f57e72a8d16b954b703ee1444 Reviewed-on: https://chromium-review.googlesource.com/539375 Commit-Ready: Wei-Ning Huang <wnhuang@chromium.org> Tested-by: Wei-Ning Huang <wnhuang@chromium.org> Reviewed-by: Rong Chang <rongchang@chromium.org>
265 lines
6.8 KiB
C
265 lines
6.8 KiB
C
/* 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 = STM32F4_AHB_PRE;
|
|
/* NOTE: If apbXpre is not 0, timers are x2 clocked. RM0390 Fig. 13
|
|
* One should define STM32F4_TIMER_CLOCK when apbXpre is not 0.
|
|
* STM32F4_TIMER_CLOCK is used for hwtimer in EC. */
|
|
apb1pre = STM32F4_APB1_PRE;
|
|
apb2pre = STM32F4_APB2_PRE;
|
|
|
|
#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_timer_freq(void)
|
|
{
|
|
return STM32F4_TIMER_CLOCK;
|
|
}
|
|
|
|
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();
|
|
}
|