mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-12 07:57:21 +00:00
7619badb9c
Kernel stores information to the RTC_SCRATCH0 and RTC_SCRATCH1 registers for wakeup from RTC-only mode with DDR in self-refresh. Parse these registers during SPL boot and jump to the kernel resume vector if the device is waking up from RTC-only modewith DDR in Self-refresh. The RTC scratch register layout used is: SCRATCH0 : bits00-31 : kernel resume address SCRATCH1 : bits00-15 : RTC magic value used to detect valid config SCRATCH1 : bits16-31 : board type information populated by bootloader During the normal boot path the SCRATCH1 : bits16-31 are updated with the eeprom read board type data. In the rtc_only boot path the rtc scratchpad register is read and the board type is determined and correspondingly ddr dpll parameters are set. This is done so as to avoid costly i2c read to eeprom. RTC-only +DRR in self-refresh mode support is currently only enabled for am43xx_evm_rtconly_config. This is not to be used with epos evm builds. Signed-off-by: Tero Kristo <t-kristo@ti.com> [j-keerthy@ti.com Rebased to latest u-boot master branch] Signed-off-by: Keerthy <j-keerthy@ti.com> Reviewed-by: Tom Rini <trini@konsulko.com>
256 lines
6.5 KiB
C
256 lines
6.5 KiB
C
/*
|
|
* clock.c
|
|
*
|
|
* Clock initialization for AM33XX boards.
|
|
* Derived from OMAP4 boards
|
|
*
|
|
* Copyright (C) 2013, Texas Instruments, Incorporated - http://www.ti.com/
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#include <common.h>
|
|
#include <asm/arch/cpu.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/arch/sys_proto.h>
|
|
#include <asm/io.h>
|
|
|
|
static void setup_post_dividers(const struct dpll_regs *dpll_regs,
|
|
const struct dpll_params *params)
|
|
{
|
|
/* Setup post-dividers */
|
|
if (params->m2 >= 0)
|
|
writel(params->m2, dpll_regs->cm_div_m2_dpll);
|
|
if (params->m3 >= 0)
|
|
writel(params->m3, dpll_regs->cm_div_m3_dpll);
|
|
if (params->m4 >= 0)
|
|
writel(params->m4, dpll_regs->cm_div_m4_dpll);
|
|
if (params->m5 >= 0)
|
|
writel(params->m5, dpll_regs->cm_div_m5_dpll);
|
|
if (params->m6 >= 0)
|
|
writel(params->m6, dpll_regs->cm_div_m6_dpll);
|
|
}
|
|
|
|
static inline void do_lock_dpll(const struct dpll_regs *dpll_regs)
|
|
{
|
|
clrsetbits_le32(dpll_regs->cm_clkmode_dpll,
|
|
CM_CLKMODE_DPLL_DPLL_EN_MASK,
|
|
DPLL_EN_LOCK << CM_CLKMODE_DPLL_EN_SHIFT);
|
|
}
|
|
|
|
static inline void wait_for_lock(const struct dpll_regs *dpll_regs)
|
|
{
|
|
if (!wait_on_value(ST_DPLL_CLK_MASK, ST_DPLL_CLK_MASK,
|
|
(void *)dpll_regs->cm_idlest_dpll, LDELAY)) {
|
|
printf("DPLL locking failed for 0x%x\n",
|
|
dpll_regs->cm_clkmode_dpll);
|
|
hang();
|
|
}
|
|
}
|
|
|
|
static inline void do_bypass_dpll(const struct dpll_regs *dpll_regs)
|
|
{
|
|
clrsetbits_le32(dpll_regs->cm_clkmode_dpll,
|
|
CM_CLKMODE_DPLL_DPLL_EN_MASK,
|
|
DPLL_EN_MN_BYPASS << CM_CLKMODE_DPLL_EN_SHIFT);
|
|
}
|
|
|
|
static inline void wait_for_bypass(const struct dpll_regs *dpll_regs)
|
|
{
|
|
if (!wait_on_value(ST_DPLL_CLK_MASK, 0,
|
|
(void *)dpll_regs->cm_idlest_dpll, LDELAY)) {
|
|
printf("Bypassing DPLL failed 0x%x\n",
|
|
dpll_regs->cm_clkmode_dpll);
|
|
}
|
|
}
|
|
|
|
static void bypass_dpll(const struct dpll_regs *dpll_regs)
|
|
{
|
|
do_bypass_dpll(dpll_regs);
|
|
wait_for_bypass(dpll_regs);
|
|
}
|
|
|
|
void do_setup_dpll(const struct dpll_regs *dpll_regs,
|
|
const struct dpll_params *params)
|
|
{
|
|
u32 temp;
|
|
|
|
if (!params)
|
|
return;
|
|
|
|
temp = readl(dpll_regs->cm_clksel_dpll);
|
|
|
|
bypass_dpll(dpll_regs);
|
|
|
|
/* Set M & N */
|
|
temp &= ~CM_CLKSEL_DPLL_M_MASK;
|
|
temp |= (params->m << CM_CLKSEL_DPLL_M_SHIFT) & CM_CLKSEL_DPLL_M_MASK;
|
|
|
|
temp &= ~CM_CLKSEL_DPLL_N_MASK;
|
|
temp |= (params->n << CM_CLKSEL_DPLL_N_SHIFT) & CM_CLKSEL_DPLL_N_MASK;
|
|
|
|
writel(temp, dpll_regs->cm_clksel_dpll);
|
|
|
|
setup_post_dividers(dpll_regs, params);
|
|
|
|
/* Wait till the DPLL locks */
|
|
do_lock_dpll(dpll_regs);
|
|
wait_for_lock(dpll_regs);
|
|
}
|
|
|
|
static void setup_dplls(void)
|
|
{
|
|
const struct dpll_params *params;
|
|
|
|
params = get_dpll_core_params();
|
|
do_setup_dpll(&dpll_core_regs, params);
|
|
|
|
params = get_dpll_mpu_params();
|
|
do_setup_dpll(&dpll_mpu_regs, params);
|
|
|
|
params = get_dpll_per_params();
|
|
do_setup_dpll(&dpll_per_regs, params);
|
|
writel(0x300, &cmwkup->clkdcoldodpllper);
|
|
|
|
params = get_dpll_ddr_params();
|
|
do_setup_dpll(&dpll_ddr_regs, params);
|
|
}
|
|
|
|
static inline void wait_for_clk_enable(u32 *clkctrl_addr)
|
|
{
|
|
u32 clkctrl, idlest = MODULE_CLKCTRL_IDLEST_DISABLED;
|
|
u32 bound = LDELAY;
|
|
|
|
while ((idlest == MODULE_CLKCTRL_IDLEST_DISABLED) ||
|
|
(idlest == MODULE_CLKCTRL_IDLEST_TRANSITIONING)) {
|
|
clkctrl = readl(clkctrl_addr);
|
|
idlest = (clkctrl & MODULE_CLKCTRL_IDLEST_MASK) >>
|
|
MODULE_CLKCTRL_IDLEST_SHIFT;
|
|
if (--bound == 0) {
|
|
printf("Clock enable failed for 0x%p idlest 0x%x\n",
|
|
clkctrl_addr, clkctrl);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void enable_clock_module(u32 *const clkctrl_addr, u32 enable_mode,
|
|
u32 wait_for_enable)
|
|
{
|
|
clrsetbits_le32(clkctrl_addr, MODULE_CLKCTRL_MODULEMODE_MASK,
|
|
enable_mode << MODULE_CLKCTRL_MODULEMODE_SHIFT);
|
|
debug("Enable clock module - %p\n", clkctrl_addr);
|
|
if (wait_for_enable)
|
|
wait_for_clk_enable(clkctrl_addr);
|
|
}
|
|
|
|
static inline void wait_for_clk_disable(u32 *clkctrl_addr)
|
|
{
|
|
u32 clkctrl, idlest = MODULE_CLKCTRL_IDLEST_FULLY_FUNCTIONAL;
|
|
u32 bound = LDELAY;
|
|
|
|
while ((idlest != MODULE_CLKCTRL_IDLEST_DISABLED)) {
|
|
clkctrl = readl(clkctrl_addr);
|
|
idlest = (clkctrl & MODULE_CLKCTRL_IDLEST_MASK) >>
|
|
MODULE_CLKCTRL_IDLEST_SHIFT;
|
|
if (--bound == 0) {
|
|
printf("Clock disable failed for 0x%p idlest 0x%x\n",
|
|
clkctrl_addr, clkctrl);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
static inline void disable_clock_module(u32 *const clkctrl_addr,
|
|
u32 wait_for_disable)
|
|
{
|
|
clrsetbits_le32(clkctrl_addr, MODULE_CLKCTRL_MODULEMODE_MASK,
|
|
MODULE_CLKCTRL_MODULEMODE_SW_DISABLE <<
|
|
MODULE_CLKCTRL_MODULEMODE_SHIFT);
|
|
debug("Disable clock module - %p\n", clkctrl_addr);
|
|
if (wait_for_disable)
|
|
wait_for_clk_disable(clkctrl_addr);
|
|
}
|
|
|
|
static inline void enable_clock_domain(u32 *const clkctrl_reg, u32 enable_mode)
|
|
{
|
|
clrsetbits_le32(clkctrl_reg, CD_CLKCTRL_CLKTRCTRL_MASK,
|
|
enable_mode << CD_CLKCTRL_CLKTRCTRL_SHIFT);
|
|
debug("Enable clock domain - %p\n", clkctrl_reg);
|
|
}
|
|
|
|
static inline void disable_clock_domain(u32 *const clkctrl_reg)
|
|
{
|
|
clrsetbits_le32(clkctrl_reg, CD_CLKCTRL_CLKTRCTRL_MASK,
|
|
CD_CLKCTRL_CLKTRCTRL_SW_SLEEP <<
|
|
CD_CLKCTRL_CLKTRCTRL_SHIFT);
|
|
debug("Disable clock domain - %p\n", clkctrl_reg);
|
|
}
|
|
|
|
void do_enable_clocks(u32 *const *clk_domains,
|
|
u32 *const *clk_modules_explicit_en, u8 wait_for_enable)
|
|
{
|
|
u32 i, max = 100;
|
|
|
|
/* Put the clock domains in SW_WKUP mode */
|
|
for (i = 0; (i < max) && clk_domains[i]; i++) {
|
|
enable_clock_domain(clk_domains[i],
|
|
CD_CLKCTRL_CLKTRCTRL_SW_WKUP);
|
|
}
|
|
|
|
/* Clock modules that need to be put in SW_EXPLICIT_EN mode */
|
|
for (i = 0; (i < max) && clk_modules_explicit_en[i]; i++) {
|
|
enable_clock_module(clk_modules_explicit_en[i],
|
|
MODULE_CLKCTRL_MODULEMODE_SW_EXPLICIT_EN,
|
|
wait_for_enable);
|
|
};
|
|
}
|
|
|
|
void do_disable_clocks(u32 *const *clk_domains,
|
|
u32 *const *clk_modules_disable,
|
|
u8 wait_for_disable)
|
|
{
|
|
u32 i, max = 100;
|
|
|
|
|
|
/* Clock modules that need to be put in SW_DISABLE */
|
|
for (i = 0; (i < max) && clk_modules_disable[i]; i++)
|
|
disable_clock_module(clk_modules_disable[i],
|
|
wait_for_disable);
|
|
|
|
/* Put the clock domains in SW_SLEEP mode */
|
|
for (i = 0; (i < max) && clk_domains[i]; i++)
|
|
disable_clock_domain(clk_domains[i]);
|
|
}
|
|
|
|
/*
|
|
* Before scaling up the clocks we need to have the PMIC scale up the
|
|
* voltages first. This will be dependent on which PMIC is in use
|
|
* and in some cases we may not be scaling things up at all and thus not
|
|
* need to do anything here.
|
|
*/
|
|
__weak void scale_vcores(void)
|
|
{
|
|
}
|
|
|
|
void setup_early_clocks(void)
|
|
{
|
|
setup_clocks_for_console();
|
|
enable_basic_clocks();
|
|
timer_init();
|
|
}
|
|
|
|
void prcm_init(void)
|
|
{
|
|
scale_vcores();
|
|
setup_dplls();
|
|
}
|
|
|
|
void rtc_only_prcm_init(void)
|
|
{
|
|
const struct dpll_params *params;
|
|
|
|
rtc_only_enable_basic_clocks();
|
|
|
|
params = get_dpll_ddr_params();
|
|
do_setup_dpll(&dpll_ddr_regs, params);
|
|
}
|