diff options
Diffstat (limited to 'arch/arm/mach-omap2/clock.c')
| -rw-r--r-- | arch/arm/mach-omap2/clock.c | 499 | 
1 files changed, 283 insertions, 216 deletions
| diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index ce4d46a4a838..4247a1534411 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -26,11 +26,10 @@  #include <mach/clock.h>  #include <mach/clockdomain.h> -#include <mach/sram.h>  #include <mach/cpu.h>  #include <asm/div64.h> -#include "memory.h" +#include <mach/sdrc.h>  #include "sdrc.h"  #include "clock.h"  #include "prm.h" @@ -46,7 +45,7 @@  #define DPLL_MIN_DIVIDER		1  /* Possible error results from _dpll_test_mult */ -#define DPLL_MULT_UNDERFLOW		(1 << 0) +#define DPLL_MULT_UNDERFLOW		-1  /*   * Scale factor to mitigate roundoff errors in DPLL rate rounding. @@ -59,6 +58,16 @@  #define DPLL_ROUNDING_VAL		((DPLL_SCALE_BASE / 2) * \  					 (DPLL_SCALE_FACTOR / DPLL_SCALE_BASE)) +/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */ +#define DPLL_FINT_BAND1_MIN		750000 +#define DPLL_FINT_BAND1_MAX		2100000 +#define DPLL_FINT_BAND2_MIN		7500000 +#define DPLL_FINT_BAND2_MAX		21000000 + +/* _dpll_test_fint() return codes */ +#define DPLL_FINT_UNDERFLOW		-1 +#define DPLL_FINT_INVALID		-2 +  u8 cpu_mask;  /*------------------------------------------------------------------------- @@ -66,6 +75,74 @@ u8 cpu_mask;   *-------------------------------------------------------------------------*/  /** + * _omap2xxx_clk_commit - commit clock parent/rate changes in hardware + * @clk: struct clk * + * + * If @clk has the DELAYED_APP flag set, meaning that parent/rate changes + * don't take effect until the VALID_CONFIG bit is written, write the + * VALID_CONFIG bit and wait for the write to complete.  No return value. + */ +static void _omap2xxx_clk_commit(struct clk *clk) +{ +	if (!cpu_is_omap24xx()) +		return; + +	if (!(clk->flags & DELAYED_APP)) +		return; + +	prm_write_mod_reg(OMAP24XX_VALID_CONFIG, OMAP24XX_GR_MOD, +		OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET); +	/* OCP barrier */ +	prm_read_mod_reg(OMAP24XX_GR_MOD, OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET); +} + +/* + * _dpll_test_fint - test whether an Fint value is valid for the DPLL + * @clk: DPLL struct clk to test + * @n: divider value (N) to test + * + * Tests whether a particular divider @n will result in a valid DPLL + * internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter + * Correction".  Returns 0 if OK, -1 if the enclosing loop can terminate + * (assuming that it is counting N upwards), or -2 if the enclosing loop + * should skip to the next iteration (again assuming N is increasing). + */ +static int _dpll_test_fint(struct clk *clk, u8 n) +{ +	struct dpll_data *dd; +	long fint; +	int ret = 0; + +	dd = clk->dpll_data; + +	/* DPLL divider must result in a valid jitter correction val */ +	fint = clk->parent->rate / (n + 1); +	if (fint < DPLL_FINT_BAND1_MIN) { + +		pr_debug("rejecting n=%d due to Fint failure, " +			 "lowering max_divider\n", n); +		dd->max_divider = n; +		ret = DPLL_FINT_UNDERFLOW; + +	} else if (fint > DPLL_FINT_BAND1_MAX && +		   fint < DPLL_FINT_BAND2_MIN) { + +		pr_debug("rejecting n=%d due to Fint failure\n", n); +		ret = DPLL_FINT_INVALID; + +	} else if (fint > DPLL_FINT_BAND2_MAX) { + +		pr_debug("rejecting n=%d due to Fint failure, " +			 "boosting min_divider\n", n); +		dd->min_divider = n; +		ret = DPLL_FINT_INVALID; + +	} + +	return ret; +} + +/**   * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk   * @clk: OMAP clock struct ptr to use   * @@ -120,7 +197,7 @@ void omap2_init_clksel_parent(struct clk *clk)  						 clk->name, clks->parent->name,  						 ((clk->parent) ?  						  clk->parent->name : "NULL")); -					clk->parent = clks->parent; +					clk_reparent(clk, clks->parent);  				};  				found = 1;  			} @@ -134,25 +211,52 @@ void omap2_init_clksel_parent(struct clk *clk)  	return;  } -/* Returns the DPLL rate */ +/** + * omap2_get_dpll_rate - returns the current DPLL CLKOUT rate + * @clk: struct clk * of a DPLL + * + * DPLLs can be locked or bypassed - basically, enabled or disabled. + * When locked, the DPLL output depends on the M and N values.  When + * bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock + * or sys_clk.  Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and + * 2 are bypassed with dpll1_fclk and dpll2_fclk respectively + * (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk. + * Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is + * locked, or the appropriate bypass rate if the DPLL is bypassed, or 0 + * if the clock @clk is not a DPLL. + */  u32 omap2_get_dpll_rate(struct clk *clk)  {  	long long dpll_clk; -	u32 dpll_mult, dpll_div, dpll; +	u32 dpll_mult, dpll_div, v;  	struct dpll_data *dd;  	dd = clk->dpll_data; -	/* REVISIT: What do we return on error? */  	if (!dd)  		return 0; -	dpll = __raw_readl(dd->mult_div1_reg); -	dpll_mult = dpll & dd->mult_mask; +	/* Return bypass rate if DPLL is bypassed */ +	v = __raw_readl(dd->control_reg); +	v &= dd->enable_mask; +	v >>= __ffs(dd->enable_mask); + +	if (cpu_is_omap24xx()) { +		if (v == OMAP2XXX_EN_DPLL_LPBYPASS || +		    v == OMAP2XXX_EN_DPLL_FRBYPASS) +			return dd->clk_bypass->rate; +	} else if (cpu_is_omap34xx()) { +		if (v == OMAP3XXX_EN_DPLL_LPBYPASS || +		    v == OMAP3XXX_EN_DPLL_FRBYPASS) +			return dd->clk_bypass->rate; +	} + +	v = __raw_readl(dd->mult_div1_reg); +	dpll_mult = v & dd->mult_mask;  	dpll_mult >>= __ffs(dd->mult_mask); -	dpll_div = dpll & dd->div1_mask; +	dpll_div = v & dd->div1_mask;  	dpll_div >>= __ffs(dd->div1_mask); -	dpll_clk = (long long)clk->parent->rate * dpll_mult; +	dpll_clk = (long long)dd->clk_ref->rate * dpll_mult;  	do_div(dpll_clk, dpll_div + 1);  	return dpll_clk; @@ -162,14 +266,11 @@ u32 omap2_get_dpll_rate(struct clk *clk)   * Used for clocks that have the same value as the parent clock,   * divided by some factor   */ -void omap2_fixed_divisor_recalc(struct clk *clk) +unsigned long omap2_fixed_divisor_recalc(struct clk *clk)  {  	WARN_ON(!clk->fixed_div); -	clk->rate = clk->parent->rate / clk->fixed_div; - -	if (clk->flags & RATE_PROPAGATES) -		propagate_rate(clk); +	return clk->parent->rate / clk->fixed_div;  }  /** @@ -190,11 +291,10 @@ int omap2_wait_clock_ready(void __iomem *reg, u32 mask, const char *name)  	 * 24xx uses 0 to indicate not ready, and 1 to indicate ready.  	 * 34xx reverses this, just to keep us on our toes  	 */ -	if (cpu_mask & (RATE_IN_242X | RATE_IN_243X)) { +	if (cpu_mask & (RATE_IN_242X | RATE_IN_243X))  		ena = mask; -	} else if (cpu_mask & RATE_IN_343X) { +	else if (cpu_mask & RATE_IN_343X)  		ena = 0; -	}  	/* Wait for lock */  	while (((__raw_readl(reg) & mask) != ena) && @@ -228,31 +328,12 @@ static void omap2_clk_wait_ready(struct clk *clk)  	 * it and pull it into struct clk itself somehow.  	 */  	reg = clk->enable_reg; -	if ((((u32)reg & 0xff) >= CM_FCLKEN1) && -	    (((u32)reg & 0xff) <= OMAP24XX_CM_FCLKEN2)) -		other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x10); /* CM_ICLKEN* */ -	else if ((((u32)reg & 0xff) >= CM_ICLKEN1) && -		 (((u32)reg & 0xff) <= OMAP24XX_CM_ICLKEN4)) -		other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x00); /* CM_FCLKEN* */ -	else -		return; -	/* REVISIT: What are the appropriate exclusions for 34XX? */ -	/* No check for DSS or cam clocks */ -	if (cpu_is_omap24xx() && ((u32)reg & 0x0f) == 0) { /* CM_{F,I}CLKEN1 */ -		if (clk->enable_bit == OMAP24XX_EN_DSS2_SHIFT || -		    clk->enable_bit == OMAP24XX_EN_DSS1_SHIFT || -		    clk->enable_bit == OMAP24XX_EN_CAM_SHIFT) -			return; -	} - -	/* REVISIT: What are the appropriate exclusions for 34XX? */ -	/* OMAP3: ignore DSS-mod clocks */ -	if (cpu_is_omap34xx() && -	    (((u32)reg & ~0xff) == (u32)OMAP_CM_REGADDR(OMAP3430_DSS_MOD, 0) || -	     ((((u32)reg & ~0xff) == (u32)OMAP_CM_REGADDR(CORE_MOD, 0)) && -	     clk->enable_bit == OMAP3430_EN_SSI_SHIFT))) -		return; +	/* +	 * Convert CM_ICLKEN* <-> CM_FCLKEN*.  This conversion assumes +	 * it's just a matter of XORing the bits. +	 */ +	other_reg = (void __iomem *)((u32)reg ^ (CM_FCLKEN ^ CM_ICLKEN));  	/* Check if both functional and interface clocks  	 * are running. */ @@ -264,18 +345,9 @@ static void omap2_clk_wait_ready(struct clk *clk)  	omap2_wait_clock_ready(st_reg, bit, clk->name);  } -/* Enables clock without considering parent dependencies or use count - * REVISIT: Maybe change this to use clk->enable like on omap1? - */ -int _omap2_clk_enable(struct clk *clk) +static int omap2_dflt_clk_enable(struct clk *clk)  { -	u32 regval32; - -	if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)) -		return 0; - -	if (clk->enable) -		return clk->enable(clk); +	u32 v;  	if (unlikely(clk->enable_reg == NULL)) {  		printk(KERN_ERR "clock.c: Enable for %s without enable code\n", @@ -283,33 +355,38 @@ int _omap2_clk_enable(struct clk *clk)  		return 0; /* REVISIT: -EINVAL */  	} -	regval32 = __raw_readl(clk->enable_reg); +	v = __raw_readl(clk->enable_reg);  	if (clk->flags & INVERT_ENABLE) -		regval32 &= ~(1 << clk->enable_bit); +		v &= ~(1 << clk->enable_bit);  	else -		regval32 |= (1 << clk->enable_bit); -	__raw_writel(regval32, clk->enable_reg); -	wmb(); - -	omap2_clk_wait_ready(clk); +		v |= (1 << clk->enable_bit); +	__raw_writel(v, clk->enable_reg); +	v = __raw_readl(clk->enable_reg); /* OCP barrier */  	return 0;  } -/* Disables clock without considering parent dependencies or use count */ -void _omap2_clk_disable(struct clk *clk) +static int omap2_dflt_clk_enable_wait(struct clk *clk)  { -	u32 regval32; - -	if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)) -		return; +	int ret; -	if (clk->disable) { -		clk->disable(clk); -		return; +	if (!clk->enable_reg) { +		printk(KERN_ERR "clock.c: Enable for %s without enable code\n", +		       clk->name); +		return 0; /* REVISIT: -EINVAL */  	} -	if (clk->enable_reg == NULL) { +	ret = omap2_dflt_clk_enable(clk); +	if (ret == 0) +		omap2_clk_wait_ready(clk); +	return ret; +} + +static void omap2_dflt_clk_disable(struct clk *clk) +{ +	u32 v; + +	if (!clk->enable_reg) {  		/*  		 * 'Independent' here refers to a clock which is not  		 * controlled by its parent. @@ -319,20 +396,44 @@ void _omap2_clk_disable(struct clk *clk)  		return;  	} -	regval32 = __raw_readl(clk->enable_reg); +	v = __raw_readl(clk->enable_reg);  	if (clk->flags & INVERT_ENABLE) -		regval32 |= (1 << clk->enable_bit); +		v |= (1 << clk->enable_bit);  	else -		regval32 &= ~(1 << clk->enable_bit); -	__raw_writel(regval32, clk->enable_reg); -	wmb(); +		v &= ~(1 << clk->enable_bit); +	__raw_writel(v, clk->enable_reg); +	/* No OCP barrier needed here since it is a disable operation */ +} + +const struct clkops clkops_omap2_dflt_wait = { +	.enable		= omap2_dflt_clk_enable_wait, +	.disable	= omap2_dflt_clk_disable, +}; + +const struct clkops clkops_omap2_dflt = { +	.enable		= omap2_dflt_clk_enable, +	.disable	= omap2_dflt_clk_disable, +}; + +/* 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) +{ +	return clk->ops->enable(clk); +} + +/* Disables clock without considering parent dependencies or use count */ +static void _omap2_clk_disable(struct clk *clk) +{ +	clk->ops->disable(clk);  }  void omap2_clk_disable(struct clk *clk)  {  	if (clk->usecount > 0 && !(--clk->usecount)) {  		_omap2_clk_disable(clk); -		if (likely((u32)clk->parent)) +		if (clk->parent)  			omap2_clk_disable(clk->parent);  		if (clk->clkdm)  			omap2_clkdm_clk_disable(clk->clkdm, clk); @@ -345,30 +446,29 @@ int omap2_clk_enable(struct clk *clk)  	int ret = 0;  	if (clk->usecount++ == 0) { -		if (likely((u32)clk->parent)) -			ret = omap2_clk_enable(clk->parent); - -		if (unlikely(ret != 0)) { -			clk->usecount--; -			return ret; -		} -  		if (clk->clkdm)  			omap2_clkdm_clk_enable(clk->clkdm, clk); -		ret = _omap2_clk_enable(clk); - -		if (unlikely(ret != 0)) { -			if (clk->clkdm) -				omap2_clkdm_clk_disable(clk->clkdm, clk); +		if (clk->parent) { +			ret = omap2_clk_enable(clk->parent); +			if (ret) +				goto err; +		} -			if (clk->parent) { +		ret = _omap2_clk_enable(clk); +		if (ret) { +			if (clk->parent)  				omap2_clk_disable(clk->parent); -				clk->usecount--; -			} + +			goto err;  		}  	} +	return ret; +err: +	if (clk->clkdm) +		omap2_clkdm_clk_disable(clk->clkdm, clk); +	clk->usecount--;  	return ret;  } @@ -376,24 +476,22 @@ int omap2_clk_enable(struct clk *clk)   * Used for clocks that are part of CLKSEL_xyz governed clocks.   * REVISIT: Maybe change to use clk->enable() functions like on omap1?   */ -void omap2_clksel_recalc(struct clk *clk) +unsigned long omap2_clksel_recalc(struct clk *clk)  { +	unsigned long rate;  	u32 div = 0;  	pr_debug("clock: recalc'ing clksel clk %s\n", clk->name);  	div = omap2_clksel_get_divisor(clk);  	if (div == 0) -		return; +		return clk->rate; -	if (unlikely(clk->rate == clk->parent->rate / div)) -		return; -	clk->rate = clk->parent->rate / div; +	rate = clk->parent->rate / div; -	pr_debug("clock: new clock rate is %ld (div %d)\n", clk->rate, div); +	pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div); -	if (unlikely(clk->flags & RATE_PROPAGATES)) -		propagate_rate(clk); +	return rate;  }  /** @@ -405,8 +503,8 @@ void omap2_clksel_recalc(struct clk *clk)   * the element associated with the supplied parent clock address.   * Returns a pointer to the struct clksel on success or NULL on error.   */ -const struct clksel *omap2_get_clksel_by_parent(struct clk *clk, -						struct clk *src_clk) +static const struct clksel *omap2_get_clksel_by_parent(struct clk *clk, +						       struct clk *src_clk)  {  	const struct clksel *clks; @@ -455,7 +553,7 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,  	*new_div = 1;  	clks = omap2_get_clksel_by_parent(clk, clk->parent); -	if (clks == NULL) +	if (!clks)  		return ~0;  	for (clkr = clks->rates; clkr->div; clkr++) { @@ -514,7 +612,7 @@ long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)  /* Given a clock and a rate apply a clock specific rounding function */  long omap2_clk_round_rate(struct clk *clk, unsigned long rate)  { -	if (clk->round_rate != NULL) +	if (clk->round_rate)  		return clk->round_rate(clk, rate);  	if (clk->flags & RATE_FIXED) @@ -540,7 +638,7 @@ u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val)  	const struct clksel_rate *clkr;  	clks = omap2_get_clksel_by_parent(clk, clk->parent); -	if (clks == NULL) +	if (!clks)  		return 0;  	for (clkr = clks->rates; clkr->div; clkr++) { @@ -576,7 +674,7 @@ u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)  	WARN_ON(div == 0);  	clks = omap2_get_clksel_by_parent(clk, clk->parent); -	if (clks == NULL) +	if (!clks)  		return ~0;  	for (clkr = clks->rates; clkr->div; clkr++) { @@ -595,23 +693,6 @@ u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)  }  /** - * omap2_get_clksel - find clksel register addr & field mask for a clk - * @clk: struct clk to use - * @field_mask: ptr to u32 to store the register field mask - * - * Returns the address of the clksel register upon success or NULL on error. - */ -void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask) -{ -	if (unlikely((clk->clksel_reg == NULL) || (clk->clksel_mask == NULL))) -		return NULL; - -	*field_mask = clk->clksel_mask; - -	return clk->clksel_reg; -} - -/**   * omap2_clksel_get_divisor - get current divider applied to parent clock.   * @clk: OMAP struct clk to use.   * @@ -619,49 +700,41 @@ void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)   */  u32 omap2_clksel_get_divisor(struct clk *clk)  { -	u32 field_mask, field_val; -	void __iomem *div_addr; +	u32 v; -	div_addr = omap2_get_clksel(clk, &field_mask); -	if (div_addr == NULL) +	if (!clk->clksel_mask)  		return 0; -	field_val = __raw_readl(div_addr) & field_mask; -	field_val >>= __ffs(field_mask); +	v = __raw_readl(clk->clksel_reg) & clk->clksel_mask; +	v >>= __ffs(clk->clksel_mask); -	return omap2_clksel_to_divisor(clk, field_val); +	return omap2_clksel_to_divisor(clk, v);  }  int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)  { -	u32 field_mask, field_val, reg_val, validrate, new_div = 0; -	void __iomem *div_addr; +	u32 v, field_val, validrate, new_div = 0; -	validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); -	if (validrate != rate) +	if (!clk->clksel_mask)  		return -EINVAL; -	div_addr = omap2_get_clksel(clk, &field_mask); -	if (div_addr == NULL) +	validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); +	if (validrate != rate)  		return -EINVAL;  	field_val = omap2_divisor_to_clksel(clk, new_div);  	if (field_val == ~0)  		return -EINVAL; -	reg_val = __raw_readl(div_addr); -	reg_val &= ~field_mask; -	reg_val |= (field_val << __ffs(field_mask)); -	__raw_writel(reg_val, div_addr); -	wmb(); +	v = __raw_readl(clk->clksel_reg); +	v &= ~clk->clksel_mask; +	v |= field_val << __ffs(clk->clksel_mask); +	__raw_writel(v, clk->clksel_reg); +	v = __raw_readl(clk->clksel_reg); /* OCP barrier */  	clk->rate = clk->parent->rate / new_div; -	if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) { -		prm_write_mod_reg(OMAP24XX_VALID_CONFIG, -			OMAP24XX_GR_MOD, OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET); -		wmb(); -	} +	_omap2xxx_clk_commit(clk);  	return 0;  } @@ -680,31 +753,24 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)  		return -EINVAL;  	/* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */ -	if (clk->set_rate != NULL) +	if (clk->set_rate)  		ret = clk->set_rate(clk, rate); -	if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) -		propagate_rate(clk); -  	return ret;  }  /*   * Converts encoded control register address into a full address - * On error, *src_addr will be returned as 0. + * On error, the return value (parent_div) will be 0.   */ -static u32 omap2_clksel_get_src_field(void __iomem **src_addr, -				      struct clk *src_clk, u32 *field_mask, -				      struct clk *clk, u32 *parent_div) +static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, +				       u32 *field_val)  {  	const struct clksel *clks;  	const struct clksel_rate *clkr; -	*parent_div = 0; -	*src_addr = NULL; -  	clks = omap2_get_clksel_by_parent(clk, src_clk); -	if (clks == NULL) +	if (!clks)  		return 0;  	for (clkr = clks->rates; clkr->div; clkr++) { @@ -722,47 +788,35 @@ static u32 omap2_clksel_get_src_field(void __iomem **src_addr,  	/* Should never happen.  Add a clksel mask to the struct clk. */  	WARN_ON(clk->clksel_mask == 0); -	*field_mask = clk->clksel_mask; -	*src_addr = clk->clksel_reg; -	*parent_div = clkr->div; +	*field_val = clkr->val; -	return clkr->val; +	return clkr->div;  }  int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)  { -	void __iomem *src_addr; -	u32 field_val, field_mask, reg_val, parent_div; +	u32 field_val, v, parent_div; -	if (unlikely(clk->flags & CONFIG_PARTICIPANT)) +	if (clk->flags & CONFIG_PARTICIPANT)  		return -EINVAL;  	if (!clk->clksel)  		return -EINVAL; -	field_val = omap2_clksel_get_src_field(&src_addr, new_parent, -					       &field_mask, clk, &parent_div); -	if (src_addr == NULL) +	parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); +	if (!parent_div)  		return -EINVAL; -	if (clk->usecount > 0) -		omap2_clk_disable(clk); -  	/* Set new source value (previous dividers if any in effect) */ -	reg_val = __raw_readl(src_addr) & ~field_mask; -	reg_val |= (field_val << __ffs(field_mask)); -	__raw_writel(reg_val, src_addr); -	wmb(); - -	if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) { -		__raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL); -		wmb(); -	} +	v = __raw_readl(clk->clksel_reg); +	v &= ~clk->clksel_mask; +	v |= field_val << __ffs(clk->clksel_mask); +	__raw_writel(v, clk->clksel_reg); +	v = __raw_readl(clk->clksel_reg);    /* OCP barrier */ -	clk->parent = new_parent; +	_omap2xxx_clk_commit(clk); -	if (clk->usecount > 0) -		omap2_clk_enable(clk); +	clk_reparent(clk, new_parent);  	/* CLKSEL clocks follow their parents' rates, divided by a divisor */  	clk->rate = new_parent->rate; @@ -773,9 +827,6 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)  	pr_debug("clock: set parent of %s to %s (new rate %ld)\n",  		 clk->name, clk->parent->name, clk->rate); -	if (unlikely(clk->flags & RATE_PROPAGATES)) -		propagate_rate(clk); -  	return 0;  } @@ -805,7 +856,8 @@ int omap2_dpll_set_rate_tolerance(struct clk *clk, unsigned int tolerance)  	return 0;  } -static unsigned long _dpll_compute_new_rate(unsigned long parent_rate, unsigned int m, unsigned int n) +static unsigned long _dpll_compute_new_rate(unsigned long parent_rate, +					    unsigned int m, unsigned int n)  {  	unsigned long long num; @@ -838,7 +890,7 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,  			   unsigned long target_rate,  			   unsigned long parent_rate)  { -	int flags = 0, carry = 0; +	int r = 0, carry = 0;  	/* Unscale m and round if necessary */  	if (*m % DPLL_SCALE_FACTOR >= DPLL_ROUNDING_VAL) @@ -859,13 +911,13 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,  	if (*m < DPLL_MIN_MULTIPLIER) {  		*m = DPLL_MIN_MULTIPLIER;  		*new_rate = 0; -		flags = DPLL_MULT_UNDERFLOW; +		r = DPLL_MULT_UNDERFLOW;  	}  	if (*new_rate == 0)  		*new_rate = _dpll_compute_new_rate(parent_rate, *m, n); -	return flags; +	return r;  }  /** @@ -889,54 +941,65 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)  	int m, n, r, e, scaled_max_m;  	unsigned long scaled_rt_rp, new_rate;  	int min_e = -1, min_e_m = -1, min_e_n = -1; +	struct dpll_data *dd;  	if (!clk || !clk->dpll_data)  		return ~0; +	dd = clk->dpll_data; +  	pr_debug("clock: starting DPLL round_rate for clock %s, target rate "  		 "%ld\n", clk->name, target_rate); -	scaled_rt_rp = target_rate / (clk->parent->rate / DPLL_SCALE_FACTOR); -	scaled_max_m = clk->dpll_data->max_multiplier * DPLL_SCALE_FACTOR; +	scaled_rt_rp = target_rate / (dd->clk_ref->rate / DPLL_SCALE_FACTOR); +	scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR; -	clk->dpll_data->last_rounded_rate = 0; +	dd->last_rounded_rate = 0; -	for (n = clk->dpll_data->max_divider; n >= DPLL_MIN_DIVIDER; n--) { +	for (n = dd->min_divider; n <= dd->max_divider; n++) { + +		/* Is the (input clk, divider) pair valid for the DPLL? */ +		r = _dpll_test_fint(clk, n); +		if (r == DPLL_FINT_UNDERFLOW) +			break; +		else if (r == DPLL_FINT_INVALID) +			continue;  		/* Compute the scaled DPLL multiplier, based on the divider */  		m = scaled_rt_rp * n;  		/* -		 * Since we're counting n down, a m overflow means we can -		 * can immediately skip to the next n +		 * Since we're counting n up, a m overflow means we +		 * can bail out completely (since as n increases in +		 * the next iteration, there's no way that m can +		 * increase beyond the current m)  		 */  		if (m > scaled_max_m) -			continue; +			break;  		r = _dpll_test_mult(&m, n, &new_rate, target_rate, -				    clk->parent->rate); +				    dd->clk_ref->rate); + +		/* m can't be set low enough for this n - try with a larger n */ +		if (r == DPLL_MULT_UNDERFLOW) +			continue;  		e = target_rate - new_rate;  		pr_debug("clock: n = %d: m = %d: rate error is %d "  			 "(new_rate = %ld)\n", n, m, e, new_rate);  		if (min_e == -1 || -		    min_e >= (int)(abs(e) - clk->dpll_data->rate_tolerance)) { +		    min_e >= (int)(abs(e) - dd->rate_tolerance)) {  			min_e = e;  			min_e_m = m;  			min_e_n = n;  			pr_debug("clock: found new least error %d\n", min_e); -		} -		/* -		 * Since we're counting n down, a m underflow means we -		 * can bail out completely (since as n decreases in -		 * the next iteration, there's no way that m can -		 * increase beyond the current m) -		 */ -		if (r & DPLL_MULT_UNDERFLOW) -			break; +			/* We found good settings -- bail out now */ +			if (min_e <= dd->rate_tolerance) +				break; +		}  	}  	if (min_e < 0) { @@ -944,17 +1007,17 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)  		return ~0;  	} -	clk->dpll_data->last_rounded_m = min_e_m; -	clk->dpll_data->last_rounded_n = min_e_n; -	clk->dpll_data->last_rounded_rate = -		_dpll_compute_new_rate(clk->parent->rate, min_e_m,  min_e_n); +	dd->last_rounded_m = min_e_m; +	dd->last_rounded_n = min_e_n; +	dd->last_rounded_rate = _dpll_compute_new_rate(dd->clk_ref->rate, +						       min_e_m,  min_e_n);  	pr_debug("clock: final least error: e = %d, m = %d, n = %d\n",  		 min_e, min_e_m, min_e_n);  	pr_debug("clock: final rate: %ld  (target rate: %ld)\n", -		 clk->dpll_data->last_rounded_rate, target_rate); +		 dd->last_rounded_rate, target_rate); -	return clk->dpll_data->last_rounded_rate; +	return dd->last_rounded_rate;  }  /*------------------------------------------------------------------------- @@ -973,6 +1036,10 @@ void omap2_clk_disable_unused(struct clk *clk)  		return;  	printk(KERN_INFO "Disabling unused clock \"%s\"\n", clk->name); -	_omap2_clk_disable(clk); +	if (cpu_is_omap34xx()) { +		omap2_clk_enable(clk); +		omap2_clk_disable(clk); +	} else +		_omap2_clk_disable(clk);  }  #endif |