diff options
Diffstat (limited to 'arch/arm/mach-omap2')
| -rw-r--r-- | arch/arm/mach-omap2/cclock44xx_data.c | 2 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/clock.h | 10 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 46 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/dpll44xx.c | 64 | 
4 files changed, 99 insertions, 23 deletions
diff --git a/arch/arm/mach-omap2/cclock44xx_data.c b/arch/arm/mach-omap2/cclock44xx_data.c index 7c1ffe61ec1..44ef54fa118 100644 --- a/arch/arm/mach-omap2/cclock44xx_data.c +++ b/arch/arm/mach-omap2/cclock44xx_data.c @@ -124,6 +124,8 @@ static struct dpll_data dpll_abe_dd = {  	.enable_mask	= OMAP4430_DPLL_EN_MASK,  	.autoidle_mask	= OMAP4430_AUTO_DPLL_MODE_MASK,  	.idlest_mask	= OMAP4430_ST_DPLL_CLK_MASK, +	.m4xen_mask	= OMAP4430_DPLL_REGM4XEN_MASK, +	.lpmode_mask	= OMAP4430_DPLL_LPMODE_EN_MASK,  	.max_multiplier	= 2047,  	.max_divider	= 128,  	.min_divider	= 1, diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 9917f793c3b..b40204837bd 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -195,6 +195,10 @@ struct clksel {   * @enable_mask: mask of the DPLL mode bitfield in @control_reg   * @last_rounded_rate: cache of the last rate result of omap2_dpll_round_rate()   * @last_rounded_m: cache of the last M result of omap2_dpll_round_rate() + * @last_rounded_m4xen: cache of the last M4X result of + *			omap4_dpll_regm4xen_round_rate() + * @last_rounded_lpmode: cache of the last lpmode result of + *			 omap4_dpll_lpmode_recalc()   * @max_multiplier: maximum valid non-bypass multiplier value (actual)   * @last_rounded_n: cache of the last N result of omap2_dpll_round_rate()   * @min_divider: minimum valid non-bypass divider value (actual) @@ -205,6 +209,8 @@ struct clksel {   * @autoidle_mask: mask of the DPLL autoidle mode bitfield in @autoidle_reg   * @freqsel_mask: mask of the DPLL jitter correction bitfield in @control_reg   * @idlest_mask: mask of the DPLL idle status bitfield in @idlest_reg + * @lpmode_mask: mask of the DPLL low-power mode bitfield in @control_reg + * @m4xen_mask: mask of the DPLL M4X multiplier bitfield in @control_reg   * @auto_recal_bit: bitshift of the driftguard enable bit in @control_reg   * @recal_en_bit: bitshift of the PRM_IRQENABLE_* bit for recalibration IRQs   * @recal_st_bit: bitshift of the PRM_IRQSTATUS_* bit for recalibration IRQs @@ -233,6 +239,8 @@ struct dpll_data {  	u32			enable_mask;  	unsigned long		last_rounded_rate;  	u16			last_rounded_m; +	u8			last_rounded_m4xen; +	u8			last_rounded_lpmode;  	u16			max_multiplier;  	u8			last_rounded_n;  	u8			min_divider; @@ -245,6 +253,8 @@ struct dpll_data {  	u32			idlest_mask;  	u32			dco_mask;  	u32			sddiv_mask; +	u32			lpmode_mask; +	u32			m4xen_mask;  	u8			auto_recal_bit;  	u8			recal_en_bit;  	u8			recal_st_bit; diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index fafb28c0dcb..2bb18838cba 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c @@ -291,16 +291,13 @@ static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n)  /*   * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly - * @clk: struct clk * of DPLL to set - * @m: DPLL multiplier to set - * @n: DPLL divider to set - * @freqsel: FREQSEL value to set + * @clk:	struct clk * of DPLL to set + * @freqsel:	FREQSEL value to set   * - * Program the DPLL with the supplied M, N values, and wait for the DPLL to - * lock..  Returns -EINVAL upon error, or 0 upon success. + * Program the DPLL with the last M, N values calculated, and wait for + * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success.   */ -static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, -				      u16 freqsel) +static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)  {  	struct dpll_data *dd = clk->dpll_data;  	u8 dco, sd_div; @@ -323,23 +320,45 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n,  	/* Set DPLL multiplier, divider */  	v = __raw_readl(dd->mult_div1_reg);  	v &= ~(dd->mult_mask | dd->div1_mask); -	v |= m << __ffs(dd->mult_mask); -	v |= (n - 1) << __ffs(dd->div1_mask); +	v |= dd->last_rounded_m << __ffs(dd->mult_mask); +	v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask);  	/* Configure dco and sd_div for dplls that have these fields */  	if (dd->dco_mask) { -		_lookup_dco(clk, &dco, m, n); +		_lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n);  		v &= ~(dd->dco_mask);  		v |= dco << __ffs(dd->dco_mask);  	}  	if (dd->sddiv_mask) { -		_lookup_sddiv(clk, &sd_div, m, n); +		_lookup_sddiv(clk, &sd_div, dd->last_rounded_m, +			      dd->last_rounded_n);  		v &= ~(dd->sddiv_mask);  		v |= sd_div << __ffs(dd->sddiv_mask);  	}  	__raw_writel(v, dd->mult_div1_reg); +	/* Set 4X multiplier and low-power mode */ +	if (dd->m4xen_mask || dd->lpmode_mask) { +		v = __raw_readl(dd->control_reg); + +		if (dd->m4xen_mask) { +			if (dd->last_rounded_m4xen) +				v |= dd->m4xen_mask; +			else +				v &= ~dd->m4xen_mask; +		} + +		if (dd->lpmode_mask) { +			if (dd->last_rounded_lpmode) +				v |= dd->lpmode_mask; +			else +				v &= ~dd->lpmode_mask; +		} + +		__raw_writel(v, dd->control_reg); +	} +  	/* We let the clock framework set the other output dividers later */  	/* REVISIT: Set ramp-up delay? */ @@ -492,8 +511,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,  		pr_debug("%s: %s: set rate: locking rate to %lu.\n",  			 __func__, __clk_get_name(hw->clk), rate); -		ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, -						dd->last_rounded_n, freqsel); +		ret = omap3_noncore_dpll_program(clk, freqsel);  		if (!ret)  			new_parent = dd->clk_ref;  	} diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c index d3326c474fd..d28b0f72671 100644 --- a/arch/arm/mach-omap2/dpll44xx.c +++ b/arch/arm/mach-omap2/dpll44xx.c @@ -20,6 +20,15 @@  #include "clock44xx.h"  #include "cm-regbits-44xx.h" +/* + * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that + * can supported when using the DPLL low-power mode. Frequencies are + * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control, + * Status, and Low-Power Operation Mode". + */ +#define OMAP4_DPLL_LP_FINT_MAX	1000000 +#define OMAP4_DPLL_LP_FOUT_MAX	100000000 +  /* Supported only on OMAP4 */  int omap4_dpllmx_gatectrl_read(struct clk_hw_omap *clk)  { @@ -82,6 +91,31 @@ const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {  };  /** + * omap4_dpll_lpmode_recalc - compute DPLL low-power setting + * @dd: pointer to the dpll data structure + * + * Calculates if low-power mode can be enabled based upon the last + * multiplier and divider values calculated. If low-power mode can be + * enabled, then the bit to enable low-power mode is stored in the + * last_rounded_lpmode variable. This implementation is based upon the + * criteria for enabling low-power mode as described in the OMAP4430/60 + * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power + * Operation Mode". + */ +static void omap4_dpll_lpmode_recalc(struct dpll_data *dd) +{ +	long fint, fout; + +	fint = __clk_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1); +	fout = fint * dd->last_rounded_m; + +	if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX)) +		dd->last_rounded_lpmode = 1; +	else +		dd->last_rounded_lpmode = 0; +} + +/**   * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit   * @clk: struct clk * of the DPLL to compute the rate for   * @@ -130,7 +164,6 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,  				    unsigned long *parent_rate)  {  	struct clk_hw_omap *clk = to_clk_hw_omap(hw); -	u32 v;  	struct dpll_data *dd;  	long r; @@ -139,18 +172,31 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,  	dd = clk->dpll_data; -	/* regm4xen adds a multiplier of 4 to DPLL calculations */ -	v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK; - -	if (v) -		target_rate = target_rate / OMAP4430_REGM4XEN_MULT; +	dd->last_rounded_m4xen = 0; +	/* +	 * First try to compute the DPLL configuration for +	 * target rate without using the 4X multiplier. +	 */  	r = omap2_dpll_round_rate(hw, target_rate, NULL); +	if (r != ~0) +		goto out; + +	/* +	 * If we did not find a valid DPLL configuration, try again, but +	 * this time see if using the 4X multiplier can help. Enabling the +	 * 4X multiplier is equivalent to dividing the target rate by 4. +	 */ +	r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, +				  NULL);  	if (r == ~0)  		return r; -	if (v) -		clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; +	dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; +	dd->last_rounded_m4xen = 1; + +out: +	omap4_dpll_lpmode_recalc(dd); -	return clk->dpll_data->last_rounded_rate; +	return dd->last_rounded_rate;  }  |