⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 clock.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/arch/arm/mach-omap2/clock.c * *  Copyright (C) 2005 Texas Instruments Inc. *  Richard Woodruff <r-woodruff2@ti.com> *  Created for OMAP2. * *  Cleaned up and modified to use omap shared clock framework by *  Tony Lindgren <tony@atomide.com> * *  Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/device.h>#include <linux/list.h>#include <linux/errno.h>#include <linux/delay.h>#include <asm/io.h>#include <asm/hardware/clock.h>#include <asm/arch/clock.h>#include <asm/arch/sram.h>#include <asm/arch/prcm.h>#include "clock.h"//#define DOWN_VARIABLE_DPLL 1			/* Experimental */static struct prcm_config *curr_prcm_set;static struct memory_timings mem_timings;static u32 curr_perf_level = PRCM_FULL_SPEED;/*------------------------------------------------------------------------- * Omap2 specific clock functions *-------------------------------------------------------------------------*//* Recalculate SYST_CLK */static void omap2_sys_clk_recalc(struct clk * clk){	u32 div = PRCM_CLKSRC_CTRL;	div &= (1 << 7) | (1 << 6);	/* Test if ext clk divided by 1 or 2 */	div >>= clk->rate_offset;	clk->rate = (clk->parent->rate / div);	propagate_rate(clk);}static u32 omap2_get_dpll_rate(struct clk * tclk){	int dpll_clk, dpll_mult, dpll_div, amult;	dpll_mult = (CM_CLKSEL1_PLL >> 12) & 0x03ff;	/* 10 bits */	dpll_div = (CM_CLKSEL1_PLL >> 8) & 0x0f;	/* 4 bits */	dpll_clk = (tclk->parent->rate * dpll_mult) / (dpll_div + 1);	amult = CM_CLKSEL2_PLL & 0x3;	dpll_clk *= amult;	return dpll_clk;}static void omap2_followparent_recalc(struct clk *clk){	followparent_recalc(clk);}static void omap2_propagate_rate(struct clk * clk){	if (!(clk->flags & RATE_FIXED))		clk->rate = clk->parent->rate;	propagate_rate(clk);}/* Enable an APLL if off */static void omap2_clk_fixed_enable(struct clk *clk){	u32 cval, i=0;	if (clk->enable_bit == 0xff)			/* Parent will do it */		return;	cval = CM_CLKEN_PLL;	if ((cval & (0x3 << clk->enable_bit)) == (0x3 << clk->enable_bit))		return;	cval &= ~(0x3 << clk->enable_bit);	cval |= (0x3 << clk->enable_bit);	CM_CLKEN_PLL = cval;	if (clk == &apll96_ck)		cval = (1 << 8);	else if (clk == &apll54_ck)		cval = (1 << 6);	while (!CM_IDLEST_CKGEN & cval) {		/* Wait for lock */		++i;		udelay(1);		if (i == 100000)			break;	}}/* Enables clock without considering parent dependencies or use count * REVISIT: Maybe change this to use clk->enable like on omap1? */static int omap2_clk_enable(struct clk * clk){	u32 regval32;	if (clk->flags & ALWAYS_ENABLED)		return 0;	if (unlikely(clk->enable_reg == 0)) {		printk(KERN_ERR "clock.c: Enable for %s without enable code\n",		       clk->name);		return 0;	}	if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) {		omap2_clk_fixed_enable(clk);		return 0;	}	regval32 = __raw_readl(clk->enable_reg);	regval32 |= (1 << clk->enable_bit);	__raw_writel(regval32, clk->enable_reg);	return 0;}/* Stop APLL */static void omap2_clk_fixed_disable(struct clk *clk){	u32 cval;	if(clk->enable_bit == 0xff)		/* let parent off do it */		return;	cval = CM_CLKEN_PLL;	cval &= ~(0x3 << clk->enable_bit);	CM_CLKEN_PLL = cval;}/* Disables clock without considering parent dependencies or use count */static void omap2_clk_disable(struct clk *clk){	u32 regval32;	if (clk->enable_reg == 0)		return;	if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) {		omap2_clk_fixed_disable(clk);		return;	}	regval32 = __raw_readl(clk->enable_reg);	regval32 &= ~(1 << clk->enable_bit);	__raw_writel(regval32, clk->enable_reg);}static int omap2_clk_use(struct clk *clk){	int ret = 0;	if (clk->usecount++ == 0) {		if (likely((u32)clk->parent))			ret = omap2_clk_use(clk->parent);		if (unlikely(ret != 0)) {			clk->usecount--;			return ret;		}		ret = omap2_clk_enable(clk);		if (unlikely(ret != 0) && clk->parent) {			omap2_clk_unuse(clk->parent);			clk->usecount--;		}	}	return ret;}static void omap2_clk_unuse(struct clk *clk){	if (clk->usecount > 0 && !(--clk->usecount)) {		omap2_clk_disable(clk);		if (likely((u32)clk->parent))			omap2_clk_unuse(clk->parent);	}}/* * Uses the current prcm set to tell if a rate is valid. * You can go slower, but not faster within a given rate set. */static u32 omap2_dpll_round_rate(unsigned long target_rate){	u32 high, low;	if ((CM_CLKSEL2_PLL & 0x3) == 1) {	/* DPLL clockout */		high = curr_prcm_set->dpll_speed * 2;		low = curr_prcm_set->dpll_speed;	} else {				/* DPLL clockout x 2 */		high = curr_prcm_set->dpll_speed;		low = curr_prcm_set->dpll_speed / 2;	}#ifdef DOWN_VARIABLE_DPLL	if (target_rate > high)		return high;	else		return target_rate;#else	if (target_rate > low)		return high;	else		return low;#endif}/* * Used for clocks that are part of CLKSEL_xyz governed clocks. * REVISIT: Maybe change to use clk->enable() functions like on omap1? */static void omap2_clksel_recalc(struct clk * clk){	u32 fixed = 0, div = 0;	if (clk == &dpll_ck) {		clk->rate = omap2_get_dpll_rate(clk);		fixed = 1;		div = 0;	}	if (clk == &iva1_mpu_int_ifck) {		div = 2;		fixed = 1;	}	if ((clk == &dss1_fck) && ((CM_CLKSEL1_CORE & (0x1f << 8)) == 0)) {		clk->rate = sys_ck.rate;		return;	}	if (!fixed) {		div = omap2_clksel_get_divisor(clk);		if (div == 0)			return;	}	if (div != 0) {		if (unlikely(clk->rate == clk->parent->rate / div))			return;		clk->rate = clk->parent->rate / div;	}	if (unlikely(clk->flags & RATE_PROPAGATES))		propagate_rate(clk);}/* * Finds best divider value in an array based on the source and target * rates. The divider array must be sorted with smallest divider first. */static inline u32 omap2_divider_from_table(u32 size, u32 *div_array,					   u32 src_rate, u32 tgt_rate){	int i, test_rate;	if (div_array == NULL)		return ~1;	for (i=0; i < size; i++) {		test_rate = src_rate / *div_array;		if (test_rate <= tgt_rate)			return *div_array;		++div_array;	}	return ~0;	/* No acceptable divider */}/* * Find divisor for the given clock and target rate. * * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, * they are only settable as part of virtual_prcm set. */static u32 omap2_clksel_round_rate(struct clk *tclk, u32 target_rate,	u32 *new_div){	u32 gfx_div[] = {2, 3, 4};	u32 sysclkout_div[] = {1, 2, 4, 8, 16};	u32 dss1_div[] = {1, 2, 3, 4, 5, 6, 8, 9, 12, 16};	u32 vylnq_div[] = {1, 2, 3, 4, 6, 8, 9, 12, 16, 18};	u32 best_div = ~0, asize = 0;	u32 *div_array = NULL;	switch (tclk->flags & SRC_RATE_SEL_MASK) {	case CM_GFX_SEL1:		asize = 3;		div_array = gfx_div;		break;	case CM_PLL_SEL1:		return omap2_dpll_round_rate(target_rate);	case CM_SYSCLKOUT_SEL1:		asize = 5;		div_array = sysclkout_div;		break;	case CM_CORE_SEL1:		if(tclk == &dss1_fck){			if(tclk->parent == &core_ck){				asize = 10;				div_array = dss1_div;			} else {				*new_div = 0; /* fixed clk */				return(tclk->parent->rate);			}		} else if((tclk == &vlynq_fck) && cpu_is_omap2420()){			if(tclk->parent == &core_ck){				asize = 10;				div_array = vylnq_div;			} else {				*new_div = 0; /* fixed clk */				return(tclk->parent->rate);			}		}		break;	}	best_div = omap2_divider_from_table(asize, div_array,	 tclk->parent->rate, target_rate);	if (best_div == ~0){		*new_div = 1;		return best_div; /* signal error */	}	*new_div = best_div;	return (tclk->parent->rate / best_div);}/* Given a clock and a rate apply a clock specific rounding function */static long omap2_clk_round_rate(struct clk *clk, unsigned long rate){	u32 new_div = 0;	int valid_rate;	if (clk->flags & RATE_FIXED)		return clk->rate;	if (clk->flags & RATE_CKCTL) {		valid_rate = omap2_clksel_round_rate(clk, rate, &new_div);		return valid_rate;	}	if (clk->round_rate != 0)		return clk->round_rate(clk, rate);	return clk->rate;}/* * Check the DLL lock state, and return tue if running in unlock mode. * This is needed to compenste for the shifted DLL value in unlock mode. */static u32 omap2_dll_force_needed(void){	u32 dll_state = SDRC_DLLA_CTRL;		/* dlla and dllb are a set */	if ((dll_state & (1 << 2)) == (1 << 2))		return 1;	else		return 0;}static void omap2_init_memory_params(u32 force_lock_to_unlock_mode){	unsigned long dll_cnt;	u32 fast_dll = 0;	mem_timings.m_type = !((SDRC_MR_0 & 0x3) == 0x1); /* DDR = 1, SDR = 0 */	/* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others.	 * In the case of 2422, its ok to use CS1 instead of CS0.	 */#if 0	/* FIXME: Enable after 24xx cpu detection works */	ctype = get_cpu_type();	if (cpu_is_omap2422())		mem_timings.base_cs = 1;	else#endif		mem_timings.base_cs = 0;	if (mem_timings.m_type != M_DDR)		return;	/* With DDR we need to determine the low frequency DLL value */	if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL))		mem_timings.dll_mode = M_UNLOCK;	else		mem_timings.dll_mode = M_LOCK;	if (mem_timings.base_cs == 0) {		fast_dll = SDRC_DLLA_CTRL;		dll_cnt = SDRC_DLLA_STATUS & 0xff00;	} else {		fast_dll = SDRC_DLLB_CTRL;		dll_cnt = SDRC_DLLB_STATUS & 0xff00;	}	if (force_lock_to_unlock_mode) {		fast_dll &= ~0xff00;		fast_dll |= dll_cnt;		/* Current lock mode */	}	mem_timings.fast_dll_ctrl = fast_dll;	/* No disruptions, DDR will be offline & C-ABI not followed */	omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl,			    mem_timings.fast_dll_ctrl,			    mem_timings.base_cs,			    force_lock_to_unlock_mode);	mem_timings.slow_dll_ctrl &= 0xff00;	/* Keep lock value */	/* Turn status into unlock ctrl */	mem_timings.slow_dll_ctrl |=		((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2));	/* 90 degree phase for anything below 133Mhz */	mem_timings.slow_dll_ctrl |= (1 << 1);}static u32 omap2_reprogram_sdrc(u32 level, u32 force){	u32 prev = curr_perf_level, flags;	if ((curr_perf_level == level) && !force)		return prev;	if (level == PRCM_HALF_SPEED) {		local_irq_save(flags);		PRCM_VOLTSETUP = 0xffff;		omap2_sram_reprogram_sdrc(PRCM_HALF_SPEED,					  mem_timings.slow_dll_ctrl,					  mem_timings.m_type);		curr_perf_level = PRCM_HALF_SPEED;		local_irq_restore(flags);	}	if (level == PRCM_FULL_SPEED) {		local_irq_save(flags);		PRCM_VOLTSETUP = 0xffff;		omap2_sram_reprogram_sdrc(PRCM_FULL_SPEED,					  mem_timings.fast_dll_ctrl,					  mem_timings.m_type);		curr_perf_level = PRCM_FULL_SPEED;		local_irq_restore(flags);	}	return prev;}static int omap2_reprogram_dpll(struct clk * clk, unsigned long rate){	u32 flags, cur_rate, low, mult, div, valid_rate, done_rate;	u32 bypass = 0;	struct prcm_config tmpset;	int ret = -EINVAL;	local_irq_save(flags);	cur_rate = omap2_get_dpll_rate(&dpll_ck);	mult = CM_CLKSEL2_PLL & 0x3;	if ((rate == (cur_rate / 2)) && (mult == 2)) {		omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1);	} else if ((rate == (cur_rate * 2)) && (mult == 1)) {		omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1);	} else if (rate != cur_rate) {		valid_rate = omap2_dpll_round_rate(rate);		if (valid_rate != rate)			goto dpll_exit;		if ((CM_CLKSEL2_PLL & 0x3) == 1)			low = curr_prcm_set->dpll_speed;		else			low = curr_prcm_set->dpll_speed / 2;		tmpset.cm_clksel1_pll = CM_CLKSEL1_PLL;		tmpset.cm_clksel1_pll &= ~(0x3FFF << 8);		div = ((curr_prcm_set->xtal_speed / 1000000) - 1);		tmpset.cm_clksel2_pll = CM_CLKSEL2_PLL;		tmpset.cm_clksel2_pll &= ~0x3;		if (rate > low) {			tmpset.cm_clksel2_pll |= 0x2;			mult = ((rate / 2) / 1000000);			done_rate = PRCM_FULL_SPEED;		} else {			tmpset.cm_clksel2_pll |= 0x1;			mult = (rate / 1000000);			done_rate = PRCM_HALF_SPEED;		}		tmpset.cm_clksel1_pll |= ((div << 8) | (mult << 12));		/* Worst case */		tmpset.base_sdrc_rfr = V24XX_SDRC_RFR_CTRL_BYPASS;		if (rate == curr_prcm_set->xtal_speed)	/* If asking for 1-1 */			bypass = 1;		omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); /* For init_mem */		/* Force dll lock mode */		omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr,			       bypass);		/* Errata: ret dll entry state */		omap2_init_memory_params(omap2_dll_force_needed());		omap2_reprogram_sdrc(done_rate, 0);	}	omap2_clksel_recalc(&dpll_ck);	ret = 0;dpll_exit:	local_irq_restore(flags);	return(ret);}/* Just return the MPU speed */static void omap2_mpu_recalc(struct clk * clk){	clk->rate = curr_prcm_set->mpu_speed;}/* * Look for a rate equal or less than the target rate given a configuration set. * * What's not entirely clear is "which" field represents the key field. * Some might argue L3-DDR, others ARM, others IVA. This code is simple and * just uses the ARM rates. */static long omap2_round_to_table_rate(struct clk * clk, unsigned long rate){	struct prcm_config * ptr;	long highest_rate;	if (clk != &virt_prcm_set)		return -EINVAL;	highest_rate = -EINVAL;	for (ptr = rate_table; ptr->mpu_speed; ptr++) {		if (ptr->xtal_speed != sys_ck.rate)			continue;		highest_rate = ptr->mpu_speed;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -