diff mbox

[RFC,9/9] OMAP4460: dpll: Support MPU frequencies > 1 Ghz

Message ID 1306375016-707-10-git-send-email-nm@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Nishanth Menon May 26, 2011, 1:56 a.m. UTC
From: Rajendra Nayak <rnayak@ti.com>

The OMAP4460 platform needs DCC (Duty cycle correction)
enabled for frequencies above 1GHz from the MPU DPLL.

Further, on OMAP4460 when the MPU Frequency is above 748Mhz,
the programmable divider for the Async bridge to ABE must be
set to MPU-Freq/8. For lower frequency, it should be MPU-Freq/4.

Similarly for MPU Frequency above 920Mhz, the programmable divider
for the async bridge to L3 and Memory Adapter interfaces of EMIF
must be MPU-Freq/4. For lower frequency, they should be MPU-Freq/2.

Also on 4460, the MPU clk for frequencies higher than 1Ghz
is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while
value of M3 is fixed to 1. Hence for frequencies higher
than 1 Ghz, lock the DPLL at half the rate so the CLKOUTX2_M3
then matches the requested rate.

Do all this as part of the DPLL control api.

Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Vishwanath BS <vishwanath.bs@ti.com>
---
 arch/arm/mach-omap2/dpll3xxx.c |   58 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 58 insertions(+), 0 deletions(-)

Comments

Todd Poynor May 26, 2011, 3:16 a.m. UTC | #1
On Wed, May 25, 2011 at 06:56:56PM -0700, Nishanth Menon wrote:
...
> @@ -427,6 +465,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
>  	u16 freqsel = 0;
>  	struct dpll_data *dd;
>  	int ret;
> +	unsigned long orig_rate = 0;
>  
>  	if (!clk || !rate)
>  		return -EINVAL;
> @@ -454,6 +493,19 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
>  		if (!ret)
>  			new_parent = dd->clk_bypass;
>  	} else {
> +		/*
> +		 * On 4460, the MPU clk for frequencies higher than 1Ghz
> +		 * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while
> +		 * value of M3 is fixed to 1. Hence for frequencies higher
> +		 * than 1 Ghz, lock the DPLL at half the rate so the
> +		 * CLKOUTX2_M3 then matches the requested rate.
> +		 */
> +		if (cpu_is_omap446x() && !strcmp(clk->name, "dpll_mpu_ck")
> +					&& (rate > 1000000000)) {
> +			orig_rate = rate;
> +			rate = rate/2;
> +		}
> +
>  		if (dd->last_rounded_rate != rate)
>  			omap2_dpll_round_rate(clk, rate);
>  
> @@ -468,6 +520,12 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
>  				WARN_ON(1);
>  		}
>  
> +		/* Set the rate back to original for book keeping*/
> +		if (orig_rate) {
> +			rate = orig_rate;
> +			dd->last_rounded_rate = dd->last_rounded_rate * 2;

Not sure why dd->last_rounded_rate is being adjusted here.  Its
value was computed based on orig_rate/2, and this adjustment will 
force the code above to call omap2_dpll_round_rate() every time
(because the * 2 value will never equal the / 2 value).  I haven't
seen the value reported anywhere, so it doesn't seem necessary?

...


Todd
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rajendra Nayak May 26, 2011, 4:13 a.m. UTC | #2
On 5/26/2011 8:46 AM, Todd Poynor wrote:
> On Wed, May 25, 2011 at 06:56:56PM -0700, Nishanth Menon wrote:
> ...
>> @@ -427,6 +465,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
>>   	u16 freqsel = 0;
>>   	struct dpll_data *dd;
>>   	int ret;
>> +	unsigned long orig_rate = 0;
>>
>>   	if (!clk || !rate)
>>   		return -EINVAL;
>> @@ -454,6 +493,19 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
>>   		if (!ret)
>>   			new_parent = dd->clk_bypass;
>>   	} else {
>> +		/*
>> +		 * On 4460, the MPU clk for frequencies higher than 1Ghz
>> +		 * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while
>> +		 * value of M3 is fixed to 1. Hence for frequencies higher
>> +		 * than 1 Ghz, lock the DPLL at half the rate so the
>> +		 * CLKOUTX2_M3 then matches the requested rate.
>> +		 */
>> +		if (cpu_is_omap446x()&&  !strcmp(clk->name, "dpll_mpu_ck")
>> +					&&  (rate>  1000000000)) {
>> +			orig_rate = rate;
>> +			rate = rate/2;
>> +		}
>> +
>>   		if (dd->last_rounded_rate != rate)
>>   			omap2_dpll_round_rate(clk, rate);
>>
>> @@ -468,6 +520,12 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
>>   				WARN_ON(1);
>>   		}
>>
>> +		/* Set the rate back to original for book keeping*/
>> +		if (orig_rate) {
>> +			rate = orig_rate;
>> +			dd->last_rounded_rate = dd->last_rounded_rate * 2;
>
> Not sure why dd->last_rounded_rate is being adjusted here.  Its
> value was computed based on orig_rate/2, and this adjustment will
> force the code above to call omap2_dpll_round_rate() every time
> (because the * 2 value will never equal the / 2 value).  I haven't
> seen the value reported anywhere, so it doesn't seem necessary?

Todd, I have to admit I have'nt even tested this patch myself on a 4460
(I don't even have one) and I did mention this to Nishanth when I sent
this out to him.
You are right that playing with the last_rounded_rate is not a good
idea, that was done thinking the omap3_noncore_dpll_program then uses
it and it needs the orig_rate and not the /2. But that certainly
causes the omap2_dpll_round_rate to get called every time.
I need to work some more on this patch and certainly *test* it to
work on a 4460.

>
> ...
>
>
> Todd

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nishanth Menon May 26, 2011, 4:53 a.m. UTC | #3
On Wed, May 25, 2011 at 21:13, Rajendra Nayak <rnayak@ti.com> wrote:
> On 5/26/2011 8:46 AM, Todd Poynor wrote:
>>
>> On Wed, May 25, 2011 at 06:56:56PM -0700, Nishanth Menon wrote:
>> ...
>>>
>>> @@ -427,6 +465,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk,
>>> unsigned long rate)
>>>        u16 freqsel = 0;
>>>        struct dpll_data *dd;
>>>        int ret;
>>> +       unsigned long orig_rate = 0;
>>>
>>>        if (!clk || !rate)
>>>                return -EINVAL;
>>> @@ -454,6 +493,19 @@ int omap3_noncore_dpll_set_rate(struct clk *clk,
>>> unsigned long rate)
>>>                if (!ret)
>>>                        new_parent = dd->clk_bypass;
>>>        } else {
>>> +               /*
>>> +                * On 4460, the MPU clk for frequencies higher than 1Ghz
>>> +                * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2,
>>> while
>>> +                * value of M3 is fixed to 1. Hence for frequencies
>>> higher
>>> +                * than 1 Ghz, lock the DPLL at half the rate so the
>>> +                * CLKOUTX2_M3 then matches the requested rate.
>>> +                */
>>> +               if (cpu_is_omap446x()&&  !strcmp(clk->name,
>>> "dpll_mpu_ck")
>>> +                                       &&  (rate>  1000000000)) {
>>> +                       orig_rate = rate;
>>> +                       rate = rate/2;
>>> +               }
>>> +
>>>                if (dd->last_rounded_rate != rate)
>>>                        omap2_dpll_round_rate(clk, rate);
>>>
>>> @@ -468,6 +520,12 @@ int omap3_noncore_dpll_set_rate(struct clk *clk,
>>> unsigned long rate)
>>>                                WARN_ON(1);
>>>                }
>>>
>>> +               /* Set the rate back to original for book keeping*/
>>> +               if (orig_rate) {
>>> +                       rate = orig_rate;
>>> +                       dd->last_rounded_rate = dd->last_rounded_rate *
>>> 2;
>>
>> Not sure why dd->last_rounded_rate is being adjusted here.  Its
>> value was computed based on orig_rate/2, and this adjustment will
>> force the code above to call omap2_dpll_round_rate() every time
>> (because the * 2 value will never equal the / 2 value).  I haven't
>> seen the value reported anywhere, so it doesn't seem necessary?
>
> Todd, I have to admit I have'nt even tested this patch myself on a 4460
> (I don't even have one) and I did mention this to Nishanth when I sent
> this out to him.
> You are right that playing with the last_rounded_rate is not a good
> idea, that was done thinking the omap3_noncore_dpll_program then uses
> it and it needs the orig_rate and not the /2. But that certainly
> causes the omap2_dpll_round_rate to get called every time.
> I need to work some more on this patch and certainly *test* it to
> work on a 4460.
Hmm.. I apologize, I had expected the bootloader I was using was
supposed to boot at highest frequency - I might have been
mistaken(expectation was to test without a DVFS framework). However,
that said, the interest here in RFC itself (beyond the point that it
is done wrongly - thanks for confirming), is to know if this is the
right place to handle it. Looping in Paul and Benoit as well for their
views on the approach taken.

Regards,
Nishanth Menon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c
index f77022b..81d2f9f 100644
--- a/arch/arm/mach-omap2/dpll3xxx.c
+++ b/arch/arm/mach-omap2/dpll3xxx.c
@@ -34,6 +34,8 @@ 
 #include "clock.h"
 #include "cm2xxx_3xxx.h"
 #include "cm-regbits-34xx.h"
+#include "cm-regbits-44xx.h"
+#include "cm1_44xx.h"
 
 /* CM_AUTOIDLE_PLL*.AUTO_* bit values */
 #define DPLL_AUTOIDLE_DISABLE			0x0
@@ -311,6 +313,42 @@  static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
 		__raw_writel(v, dd->control_reg);
 	}
 
+	/*
+	 * On OMAP4460, to obtain MPU DPLL frequency higher
+	 * than 1GHz, DCC (Duty Cycle Correction) needs to
+	 * be enabled.
+	 * Also the interconnect frequency to EMIF should
+	 * be switched between MPU clk divide by 4 (for
+	 * frequencies higher than 920Mhz) and MPU clk divide
+	 * by 2 (for frequencies lower than or equal to 920Mhz)
+	 * Lastly the async bridge to ABE must be MPU clk divide
+	 * by 8 for MPU clk > 748Mhz and MPU clk divide by 4
+	 * for lower frequencies.
+	 * TODO: For now use a strcmp, but need to find a
+	 * better way to identify the MPU dpll.
+	 */
+	if (cpu_is_omap446x() && !strcmp(clk->name, "dpll_mpu_ck")) {
+		/* DCC control */
+		v = __raw_readl(dd->mult_div1_reg);
+		if (dd->last_rounded_rate > 1000000000)
+			v |= OMAP4460_DCC_EN_MASK; /* Enable DCC */
+		else
+			v &= ~OMAP4460_DCC_EN_MASK; /* Disable DCC */
+		__raw_writel(v, dd->mult_div1_reg);
+
+		/* EMIF/ABE clock rate control */
+		v = __raw_readl(OMAP4430_CM_MPU_MPU_CLKCTRL);
+		if (dd->last_rounded_rate > 920000000)
+			v |= OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK;
+		else
+			v &= ~OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK;
+		if (dd->last_rounded_rate > 748000000)
+			v |= OMAP4460_CLKSEL_ABE_DIV_MODE_MASK;
+		else
+			v &= ~OMAP4460_CLKSEL_ABE_DIV_MODE_MASK;
+		__raw_writel(v, OMAP4430_CM_MPU_MPU_CLKCTRL);
+	}
+
 	/* Set DPLL multiplier, divider */
 	v = __raw_readl(dd->mult_div1_reg);
 	v &= ~(dd->mult_mask | dd->div1_mask);
@@ -427,6 +465,7 @@  int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
 	u16 freqsel = 0;
 	struct dpll_data *dd;
 	int ret;
+	unsigned long orig_rate = 0;
 
 	if (!clk || !rate)
 		return -EINVAL;
@@ -454,6 +493,19 @@  int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
 		if (!ret)
 			new_parent = dd->clk_bypass;
 	} else {
+		/*
+		 * On 4460, the MPU clk for frequencies higher than 1Ghz
+		 * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while
+		 * value of M3 is fixed to 1. Hence for frequencies higher
+		 * than 1 Ghz, lock the DPLL at half the rate so the
+		 * CLKOUTX2_M3 then matches the requested rate.
+		 */
+		if (cpu_is_omap446x() && !strcmp(clk->name, "dpll_mpu_ck")
+					&& (rate > 1000000000)) {
+			orig_rate = rate;
+			rate = rate/2;
+		}
+
 		if (dd->last_rounded_rate != rate)
 			omap2_dpll_round_rate(clk, rate);
 
@@ -468,6 +520,12 @@  int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
 				WARN_ON(1);
 		}
 
+		/* Set the rate back to original for book keeping*/
+		if (orig_rate) {
+			rate = orig_rate;
+			dd->last_rounded_rate = dd->last_rounded_rate * 2;
+		}
+
 		pr_debug("clock: %s: set rate: locking rate to %lu.\n",
 			 clk->name, rate);