mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	Up till this commit passing NULL as input parameter was allowed, but not handled properly. When a NULL parameter was passed to the function a data abort was raised. Signed-off-by: Dario Binacchi <dariobin@libero.it> Reviewed-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			261 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * clock.c
 | 
						|
 *
 | 
						|
 * Clock initialization for AM33XX boards.
 | 
						|
 * Derived from OMAP4 boards
 | 
						|
 *
 | 
						|
 * Copyright (C) 2013, Texas Instruments, Incorporated - http://www.ti.com/
 | 
						|
 */
 | 
						|
#include <common.h>
 | 
						|
#include <hang.h>
 | 
						|
#include <init.h>
 | 
						|
#include <log.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 && 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 &&
 | 
						|
	     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 && 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 && 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);
 | 
						|
}
 |