diff mbox

[v2,1/1] ARM : omap3 : fix wrong container_of in clock36xx.c

Message ID 1369903827-2025-1-git-send-email-jp.francois@cynove.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jean-Philippe François May 30, 2013, 8:50 a.m. UTC
omap36xx_pwrdn_clk_enable_with_hsdiv_restore expects the parent hw of the clock
to be a clk_hw_omap. However, looking at cclock3xxx_data.c, all concerned clock
have parent defined as clk_divider. Instead of using container_of to eventually get
to the register and directly mess with the divider, change freq via clk_set_rate, 
and let the clock framework toggle the divider value.
Tested with  3.9 on dm3730.

Signed-off-by: Jean-Philippe François <jp.francois@cynove.com>

Comments

Mike Turquette May 31, 2013, 7:32 p.m. UTC | #1
Quoting Jean-Philippe Francois (2013-05-30 01:50:27)
> omap36xx_pwrdn_clk_enable_with_hsdiv_restore expects the parent hw of the clock
> to be a clk_hw_omap. However, looking at cclock3xxx_data.c, all concerned clock
> have parent defined as clk_divider. Instead of using container_of to eventually get
> to the register and directly mess with the divider, change freq via clk_set_rate, 
> and let the clock framework toggle the divider value.
> Tested with  3.9 on dm3730.
> 
> Signed-off-by: Jean-Philippe Fran??ois <jp.francois@cynove.com>

Did you use git-format-patch to create this patch?  Its a bit nicer to
use that or if you just use diff then use "diff -up" or "diff -uprN"
(taken from Documentation/SubmittingPatches.txt).

Also did you test this to make sure it works?  I don't mean a boot test,
but actually disabling/re-enabling an HSDIVIDER on 3630?  The previous
code just programmed the clksel field to 1, and this code divides the
rate by 2, then restores it.  I just used that as an example in my
previous email and it needs to be verified that it works (though it
should if I remember this errata correctly).

If that testing is done and everything looks good then please add:

Acked-by: Mike Turquette <mturquette@linaro.org>

> 
> Index: b/arch/arm/mach-omap2/clock36xx.c
> ===================================================================
> --- a/arch/arm/mach-omap2/clock36xx.c
> +++ b/arch/arm/mach-omap2/clock36xx.c
> @@ -39,30 +39,25 @@
>   */
>  int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk)
>  {
> -       struct clk_hw_omap *parent;
> -       struct clk_hw *parent_hw;
> -       u32 dummy_v, orig_v, clksel_shift;
>         int ret;
>  
>         /* Clear PWRDN bit of HSDIVIDER */
>         ret = omap2_dflt_clk_enable(clk);
>  
> -       parent_hw = __clk_get_hw(__clk_get_parent(clk->clk));
> -       parent = to_clk_hw_omap(parent_hw);
> -
> -       /* Restore the dividers */
> +       /* kick parent's clksel register after toggling PWRDN bit */
>         if (!ret) {
> -               clksel_shift = __ffs(parent->clksel_mask);
> -               orig_v = __raw_readl(parent->clksel_reg);
> -               dummy_v = orig_v;
> -
> -               /* Write any other value different from the Read value */
> -               dummy_v ^= (1 << clksel_shift);
> -               __raw_writel(dummy_v, parent->clksel_reg);
> -
> -               /* Write the original divider */
> -               __raw_writel(orig_v, parent->clksel_reg);
> +               struct clk *parent = clk_get_parent(clk->clk);
> +               unsigned long parent_rate = clk_get_rate(parent);
> +               ret = clk_set_rate(parent, parent_rate/2);
> +               if(ret)
> +                       goto badfreq;
> +               ret = clk_set_rate(parent, parent_rate);
> +               if(ret)
> +                       goto badfreq;
>         }
> +       return ret;
>  
> + badfreq :
> +       omap2_dflt_clk_disable(clk);
>         return ret;
>  }
Jean-Philippe François June 3, 2013, 7:50 a.m. UTC | #2
2013/5/31 Mike Turquette <mturquette@linaro.org>:
> Quoting Jean-Philippe Francois (2013-05-30 01:50:27)
>> omap36xx_pwrdn_clk_enable_with_hsdiv_restore expects the parent hw of the clock
>> to be a clk_hw_omap. However, looking at cclock3xxx_data.c, all concerned clock
>> have parent defined as clk_divider. Instead of using container_of to eventually get
>> to the register and directly mess with the divider, change freq via clk_set_rate,
>> and let the clock framework toggle the divider value.
>> Tested with  3.9 on dm3730.
>>
>> Signed-off-by: Jean-Philippe Fran??ois <jp.francois@cynove.com>
>
> Did you use git-format-patch to create this patch?  Its a bit nicer to
> use that or if you just use diff then use "diff -up" or "diff -uprN"
> (taken from Documentation/SubmittingPatches.txt).
>
It is easier for my build system to work with tarball + quilt patches, so
I am not working with git. I will look into the alternative you provided.

> Also did you test this to make sure it works?  I don't mean a boot test,
> but actually disabling/re-enabling an HSDIVIDER on 3630?  The previous
> code just programmed the clksel field to 1, and this code divides the
> rate by 2, then restores it.  I just used that as an example in my
> previous email and it needs to be verified that it works (though it
> should if I remember this errata correctly).
>

Yes, it is exactly what happens on my board when using the camera, because
the sensor clock is provided by the SoC. So this patch works for this
particular clock.
If the asked frequency is the lower limit of the frequency range, then
asking for half the
frequency won't change the divider, but I think it is not a problem in practice.

> If that testing is done and everything looks good then please add:
>
> Acked-by: Mike Turquette <mturquette@linaro.org>
>
How should I proceed ? Should I just add the acked by below, or should
I resend the patch ?

Jean-Philippe François

>>
>> Index: b/arch/arm/mach-omap2/clock36xx.c
>> ===================================================================
>> --- a/arch/arm/mach-omap2/clock36xx.c
>> +++ b/arch/arm/mach-omap2/clock36xx.c
>> @@ -39,30 +39,25 @@
>>   */
>>  int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk)
>>  {
>> -       struct clk_hw_omap *parent;
>> -       struct clk_hw *parent_hw;
>> -       u32 dummy_v, orig_v, clksel_shift;
>>         int ret;
>>
>>         /* Clear PWRDN bit of HSDIVIDER */
>>         ret = omap2_dflt_clk_enable(clk);
>>
>> -       parent_hw = __clk_get_hw(__clk_get_parent(clk->clk));
>> -       parent = to_clk_hw_omap(parent_hw);
>> -
>> -       /* Restore the dividers */
>> +       /* kick parent's clksel register after toggling PWRDN bit */
>>         if (!ret) {
>> -               clksel_shift = __ffs(parent->clksel_mask);
>> -               orig_v = __raw_readl(parent->clksel_reg);
>> -               dummy_v = orig_v;
>> -
>> -               /* Write any other value different from the Read value */
>> -               dummy_v ^= (1 << clksel_shift);
>> -               __raw_writel(dummy_v, parent->clksel_reg);
>> -
>> -               /* Write the original divider */
>> -               __raw_writel(orig_v, parent->clksel_reg);
>> +               struct clk *parent = clk_get_parent(clk->clk);
>> +               unsigned long parent_rate = clk_get_rate(parent);
>> +               ret = clk_set_rate(parent, parent_rate/2);
>> +               if(ret)
>> +                       goto badfreq;
>> +               ret = clk_set_rate(parent, parent_rate);
>> +               if(ret)
>> +                       goto badfreq;
>>         }
>> +       return ret;
>>
>> + badfreq :
>> +       omap2_dflt_clk_disable(clk);
>>         return ret;
>>  }
Paul Walmsley June 3, 2013, 9:37 a.m. UTC | #3
On Mon, 3 Jun 2013, jean-philippe francois wrote:

> How should I proceed ? Should I just add the acked by below, or should
> I resend the patch ?

It's been fixed up locally here.  Mike, please speak up if I shouldn't 
apply your ack.


- Paul
Paul Walmsley June 5, 2013, 6:21 p.m. UTC | #4
Hi

On Thu, 30 May 2013, Jean-Philippe Francois wrote:

> omap36xx_pwrdn_clk_enable_with_hsdiv_restore expects the parent hw of the clock
> to be a clk_hw_omap. However, looking at cclock3xxx_data.c, all concerned clock
> have parent defined as clk_divider. Instead of using container_of to eventually get
> to the register and directly mess with the divider, change freq via clk_set_rate, 
> and let the clock framework toggle the divider value.
> Tested with  3.9 on dm3730.
> 
> Signed-off-by: Jean-Philippe Fran??ois <jp.francois@cynove.com>

Tested this patch before applying, and noticed that it causes the 
retention dynamic idle power management test to fail here on the 
3730beaglexm:

http://www.pwsan.com/omap/testlogs/fixes_b_v3.10-rc/20130605113443/pm/3730beaglexm/3730beaglexm_log.txt

Not sure at this point if this is caused by the patch, or if the patch is 
just unmasking another bug.

Jean, Mike, Rajendra, care to take a look at this?


- Paul
Jean-Philippe François June 6, 2013, 7:56 a.m. UTC | #5
2013/6/5 Paul Walmsley <paul@pwsan.com>:
> Hi
>
> On Thu, 30 May 2013, Jean-Philippe Francois wrote:
>
>> omap36xx_pwrdn_clk_enable_with_hsdiv_restore expects the parent hw of the clock
>> to be a clk_hw_omap. However, looking at cclock3xxx_data.c, all concerned clock
>> have parent defined as clk_divider. Instead of using container_of to eventually get
>> to the register and directly mess with the divider, change freq via clk_set_rate,
>> and let the clock framework toggle the divider value.
>> Tested with  3.9 on dm3730.
>>
>> Signed-off-by: Jean-Philippe Fran??ois <jp.francois@cynove.com>
>
> Tested this patch before applying, and noticed that it causes the
> retention dynamic idle power management test to fail here on the
> 3730beaglexm:
>
> http://www.pwsan.com/omap/testlogs/fixes_b_v3.10-rc/20130605113443/pm/3730beaglexm/3730beaglexm_log.txt
>
> Not sure at this point if this is caused by the patch, or if the patch is
> just unmasking another bug.
>
> Jean, Mike, Rajendra, care to take a look at this?
>

I am booting with nohlt on my board, because I otherwise have memory
corruption error.
I never looked into it because power consumption is currently a non
issue for us, and the board
will always be working anyway.

However I have a beaglexm too, so I can probably help with testing.

Does the first version [1] of the patch, that only touch the MSB of
the divider also trigger the
bug ?

[1]  https://patchwork.kernel.org/patch/2609681/

Jean-Philippe
>
> - Paul
> --
> 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
Paul Walmsley June 6, 2013, 3:31 p.m. UTC | #6
On Thu, 6 Jun 2013, jean-philippe francois wrote:

> Does the first version [1] of the patch, that only touch the MSB of
> the divider also trigger the
> bug ?
> 
> [1]  https://patchwork.kernel.org/patch/2609681/

That one passes the PM test here.  Will take this one for v3.10-rc fixes 
instead, since it fixes a known DSS problem and the original code wasn't 
correct.

Jean, could you work with Mike to come up with a better approach for 
v3.11 or v3.12?


- Paul
Jean-Philippe François June 10, 2013, 8:03 a.m. UTC | #7
2013/6/6 Paul Walmsley <paul@pwsan.com>:
> On Thu, 6 Jun 2013, jean-philippe francois wrote:
>
>> Does the first version [1] of the patch, that only touch the MSB of
>> the divider also trigger the
>> bug ?
>>
>> [1]  https://patchwork.kernel.org/patch/2609681/
>
> That one passes the PM test here.  Will take this one for v3.10-rc fixes
> instead, since it fixes a known DSS problem and the original code wasn't
> correct.
>
> Jean, could you work with Mike to come up with a better approach for
> v3.11 or v3.12?
>
Hi,

I am ok to work on it, however could you explain to me what is the
expected output of your PM tests, and how to reproduce them ?
The board I used to test the clock frequency was correct suffered from
heavy handed oscilloscope probing and is currently out of order :(

Jean-Philippe François.

>
> - Paul
> --
> 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
Paul Walmsley June 10, 2013, 8:15 a.m. UTC | #8
Hello Jean-Philippe,

On Mon, 10 Jun 2013, jean-philippe francois wrote:

> I am ok to work on it, however could you explain to me what is the
> expected output of your PM tests, and how to reproduce them ?

Here is the expected output for my ancient 3730 ES1.0 Beagle-XM:

http://www.pwsan.com/omap/testlogs/test_v3.10-rc5/20130609031534/pm/3730beaglexm/3730beaglexm_log.txt

When I ran that same test on the second version of your patch, the system 
froze during the retention dynamic idle test:

----------------

%% Start retention dynamic idle UART wakeup test

echo 3000 > /sys/devices/platform/omap_uart.0/power/autosuspend_delay_ms
root@beagleboard:~# 
root@beagleboard:~# echo 3000 > 
/sys/devices/platform/omap_uart.1/power/autosuspend_delay_ms
root@beagleboard:~# 
root@beagleboard:~# echo 3000 > 
/sys/devices/platform/omap_uart.2/power/autosuspend_delay_ms
root@beagleboard:~# 
root@beagleboard:~# echo 3000 > 
/sys/devices/platform/omap_uart.3/power/autosuspend_delay_ms
root@beagleboard:~# 
root@beagleboard:~# 

-------------------

That is where the log stopped.

Sorry to hear about your board - I can certainly sympathize,

regards,

- Paul
diff mbox

Patch

Index: b/arch/arm/mach-omap2/clock36xx.c
===================================================================
--- a/arch/arm/mach-omap2/clock36xx.c
+++ b/arch/arm/mach-omap2/clock36xx.c
@@ -39,30 +39,25 @@ 
  */
 int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk)
 {
-	struct clk_hw_omap *parent;
-	struct clk_hw *parent_hw;
-	u32 dummy_v, orig_v, clksel_shift;
 	int ret;
 
 	/* Clear PWRDN bit of HSDIVIDER */
 	ret = omap2_dflt_clk_enable(clk);
 
-	parent_hw = __clk_get_hw(__clk_get_parent(clk->clk));
-	parent = to_clk_hw_omap(parent_hw);
-
-	/* Restore the dividers */
+	/* kick parent's clksel register after toggling PWRDN bit */
 	if (!ret) {
-		clksel_shift = __ffs(parent->clksel_mask);
-		orig_v = __raw_readl(parent->clksel_reg);
-		dummy_v = orig_v;
-
-		/* Write any other value different from the Read value */
-		dummy_v ^= (1 << clksel_shift);
-		__raw_writel(dummy_v, parent->clksel_reg);
-
-		/* Write the original divider */
-		__raw_writel(orig_v, parent->clksel_reg);
+		struct clk *parent = clk_get_parent(clk->clk);
+		unsigned long parent_rate = clk_get_rate(parent);
+		ret = clk_set_rate(parent, parent_rate/2);
+		if(ret)
+			goto badfreq;
+		ret = clk_set_rate(parent, parent_rate);
+		if(ret)
+			goto badfreq;
 	}
+	return ret;
 
+ badfreq :
+	omap2_dflt_clk_disable(clk);
 	return ret;
 }