diff mbox

arm: imx: correct the hardware clock gate setting for shared nodes

Message ID 1418111885-3623-1-git-send-email-b20788@freescale.com (mailing list archive)
State New, archived
Headers show

Commit Message

Anson Huang Dec. 9, 2014, 7:58 a.m. UTC
For those clk gates which hold share count, since its is_enabled
callback is only checking the share count rather than reading
the hardware register setting, in the late phase of kernel bootup,
the clk_disable_unused action will NOT handle the scenario of
share_count is 0 but the hardware setting is enabled, actually,
uboot normally enables all clk gates, then those shared clk gates
will be always enabled until they are used by some modules.

So the problem would be: when kernel boot up, the usecount cat
from clk tree is 0, but the clk gates actually is enabled in
hardware register, it will confuse user and bring unnecessary power
consumption, take i.MX6SX for example, the ESAI clk info
is as below, the use count is 0, but the hardware register read
from CCM_CCGR1_CG8 is ungated.

cat /sys/kernel/debug/clk/clk_summary | grep esai

esai_sel            0            0   393216000          0 0
   esai_pred           0            0   196608000          0 0
      esai_podf           0            0    24576000          0 0
         esai_extal           0            0    24576000          0 0
      esai_mem           0            0   132000000          0 0
      esai_ipg           0            0   132000000          0 0

Read CCM_CCGR1:

0x020C406C:  F33FFF00

This patch disables all those clk gates which hold share count
before registering them, then the hardware status will match the
clk tree info.

Signed-off-by: Anson Huang <b20788@freescale.com>
---
 arch/arm/mach-imx/clk-imx6q.c  |    6 ++++++
 arch/arm/mach-imx/clk-imx6sl.c |    2 ++
 arch/arm/mach-imx/clk-imx6sx.c |    6 ++++++
 3 files changed, 14 insertions(+)

Comments

Uwe Kleine-König Dec. 9, 2014, 8:57 a.m. UTC | #1
Hello Anson,

[Cc += Mike Turquette]

On Tue, Dec 09, 2014 at 03:58:05PM +0800, Anson Huang wrote:
> For those clk gates which hold share count, since its is_enabled
> callback is only checking the share count rather than reading
> the hardware register setting, in the late phase of kernel bootup,
> the clk_disable_unused action will NOT handle the scenario of
> share_count is 0 but the hardware setting is enabled, actually,
> uboot normally enables all clk gates, then those shared clk gates
s/uboot/U-Boot/ IIRC

> will be always enabled until they are used by some modules.
What is the purpose of the shared clocks? Just to have different names
for a single gate?

Wouldn't it be better to fix this problem at the framework level? Then
ideally clk_disable_unused would handle these shared clocks just fine.

I guess the problem is that this shared clk implementation doesn't
fulfill the expectations of the clk framework.

This is found in arch/arm/mach-imx/clk-imx6q.c
>  	clk[IMX6QDL_CLK_ASRC]         = imx_clk_gate2_shared("asrc",         "asrc_podf",   base + 0x68, 6, &share_count_asrc);
>  	clk[IMX6QDL_CLK_ASRC_IPG]     = imx_clk_gate2_shared("asrc_ipg",     "ahb",         base + 0x68, 6, &share_count_asrc);
>  	clk[IMX6QDL_CLK_ASRC_MEM]     = imx_clk_gate2_shared("asrc_mem",     "ahb",         base + 0x68, 6, &share_count_asrc);

For these clks is_enabled returns just enable_count instead of the
status of the corresponding bit in the corresponding register. The
reason for that probably is that "asrc" must not be disabled even if
unused if "asrc_ipg" is in use. So the problem is there are three struct
clk s with a common enable count for a single register bit. Interesting
enough these clocks have different parents (the 2nd parameter to
imx_clk_gate2_shared) which makes the easy solution to just make
asrc_ipg and asrc_mem children of asrc not applicable. Any idea for a
sane solution? A multi-parent-dummy-clk would do I think.

Other than that the following might work: Ignore share_count in the
is_enabled callback (i.e. report if the register bit is set as in the
share_count == NULL case) and implement clk->ops->disable_unused that
only unsets the bit if share_count == 0? Ugly but IMHO better and more
robust than the idea implemented in this patch. Still I'd prefer to make
the usual assumptions of the clk framework just work.

> So the problem would be: when kernel boot up, the usecount cat
> from clk tree is 0, but the clk gates actually is enabled in
> hardware register, it will confuse user and bring unnecessary power
> consumption, take i.MX6SX for example, the ESAI clk info
> is as below, the use count is 0, but the hardware register read
> from CCM_CCGR1_CG8 is ungated.
> 
> cat /sys/kernel/debug/clk/clk_summary | grep esai
Just a very minor nitpick: Unnecessary usage of cat,

	grep esai /sys/kernel/debug/clk/clk_summary

just produces the same result. :-)

> 
> esai_sel            0            0   393216000          0 0
>    esai_pred           0            0   196608000          0 0
>       esai_podf           0            0    24576000          0 0
>          esai_extal           0            0    24576000          0 0
>       esai_mem           0            0   132000000          0 0
>       esai_ipg           0            0   132000000          0 0
> 
> Read CCM_CCGR1:
> 
> 0x020C406C:  F33FFF00
This is hardly useful if you don't have the reference manual at hand.
I'd believe the commit log even without the examples.
 
Best regards
Uwe
Anson.Huang@freescale.com Dec. 10, 2014, 8:49 a.m. UTC | #2
Hi, Uwe

Best regards!
Anson Huang


-----Original Message-----
From: Uwe Kleine-König [mailto:u.kleine-koenig@pengutronix.de] 
Sent: 2014-12-09 4:58 PM
To: Huang Yongcai-B20788
Cc: shawn.guo@linaro.org; kernel@pengutronix.de; linux-arm-kernel@lists.infradead.org; Mike Turquette
Subject: Re: [PATCH] arm: imx: correct the hardware clock gate setting for shared nodes

Hello Anson,

[Cc += Mike Turquette]

On Tue, Dec 09, 2014 at 03:58:05PM +0800, Anson Huang wrote:
> For those clk gates which hold share count, since its is_enabled 
> callback is only checking the share count rather than reading the 
> hardware register setting, in the late phase of kernel bootup, the 
> clk_disable_unused action will NOT handle the scenario of share_count 
> is 0 but the hardware setting is enabled, actually, uboot normally 
> enables all clk gates, then those shared clk gates
s/uboot/U-Boot/ IIRC

> will be always enabled until they are used by some modules.
What is the purpose of the shared clocks? Just to have different names for a single gate?
[Anson] The shared clocks is to meet the case that where one clk gate control multi clks path in module, for example, in esai module, there are three clk paths, esai_core, esai_ipg and esai_mem, but they share same clk gate in CCM_CCGR, and their parents are different...No very good hardware design for us.

Wouldn't it be better to fix this problem at the framework level? Then ideally clk_disable_unused would handle these shared clocks just fine.
[Anson] Agree, handle it in framework is much better than doing it in every platform, my bad:(

I guess the problem is that this shared clk implementation doesn't fulfill the expectations of the clk framework.

This is found in arch/arm/mach-imx/clk-imx6q.c
>  	clk[IMX6QDL_CLK_ASRC]         = imx_clk_gate2_shared("asrc",         "asrc_podf",   base + 0x68, 6, &share_count_asrc);
>  	clk[IMX6QDL_CLK_ASRC_IPG]     = imx_clk_gate2_shared("asrc_ipg",     "ahb",         base + 0x68, 6, &share_count_asrc);
>  	clk[IMX6QDL_CLK_ASRC_MEM]     = imx_clk_gate2_shared("asrc_mem",     "ahb",         base + 0x68, 6, &share_count_asrc);

For these clks is_enabled returns just enable_count instead of the status of the corresponding bit in the corresponding register. The reason for that probably is that "asrc" must not be disabled even if unused if "asrc_ipg" is in use. So the problem is there are three struct clk s with a common enable count for a single register bit. Interesting enough these clocks have different parents (the 2nd parameter to
imx_clk_gate2_shared) which makes the easy solution to just make asrc_ipg and asrc_mem children of asrc not applicable. Any idea for a sane solution? A multi-parent-dummy-clk would do I think.

Other than that the following might work: Ignore share_count in the is_enabled callback (i.e. report if the register bit is set as in the share_count == NULL case) and implement clk->ops->disable_unused that only unsets the bit if share_count == 0? Ugly but IMHO better and more robust than the idea implemented in this patch. Still I'd prefer to make the usual assumptions of the clk framework just work.
[Anson] I think this solution is feasible for us, as disable_unused is only used in late phase of kernel boot up, I think adding such callback is acceptable.

> So the problem would be: when kernel boot up, the usecount cat from 
> clk tree is 0, but the clk gates actually is enabled in hardware 
> register, it will confuse user and bring unnecessary power 
> consumption, take i.MX6SX for example, the ESAI clk info is as below, 
> the use count is 0, but the hardware register read from CCM_CCGR1_CG8 
> is ungated.
> 
> cat /sys/kernel/debug/clk/clk_summary | grep esai
Just a very minor nitpick: Unnecessary usage of cat,

	grep esai /sys/kernel/debug/clk/clk_summary

just produces the same result. :-)
[Anson] Thanks for sharing such knowledge:)

> 
> esai_sel            0            0   393216000          0 0
>    esai_pred           0            0   196608000          0 0
>       esai_podf           0            0    24576000          0 0
>          esai_extal           0            0    24576000          0 0
>       esai_mem           0            0   132000000          0 0
>       esai_ipg           0            0   132000000          0 0
> 
> Read CCM_CCGR1:
> 
> 0x020C406C:  F33FFF00
This is hardly useful if you don't have the reference manual at hand.
I'd believe the commit log even without the examples.
[Anson] Agreed, I will remove it in V2 patch.

Many thanks for reviewing my patch and very good advice:) Please help review V2 patch.
 
Best regards
Uwe
Uwe Kleine-König Dec. 10, 2014, 9:09 a.m. UTC | #3
Hello,

On Wed, Dec 10, 2014 at 08:49:27AM +0000, Anson.Huang@freescale.com wrote:
> > 
> > esai_sel            0            0   393216000          0 0
> >    esai_pred           0            0   196608000          0 0
> >       esai_podf           0            0    24576000          0 0
> >          esai_extal           0            0    24576000          0 0
> >       esai_mem           0            0   132000000          0 0
> >       esai_ipg           0            0   132000000          0 0
> > 
> > Read CCM_CCGR1:
> > 
> > 0x020C406C:  F33FFF00
> This is hardly useful if you don't have the reference manual at hand.
> I'd believe the commit log even without the examples.
> [Anson] Agreed, I will remove it in V2 patch.
> 
> Many thanks for reviewing my patch and very good advice:) Please help review V2 patch.
>  
> Best regards
> Uwe
> 
> -- 
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> 
If you want to do me (and most of the other readers of lakml I guess)
another favour: please fix your quoting style. The way you replied to my
mail is hard to parse in a console mail reader as you might be able to
see from the text I quoted above.

Best regards
Uwe
Uwe Kleine-König Dec. 10, 2014, 10:16 a.m. UTC | #4
Hello,

On Wed, Dec 10, 2014 at 05:11:26PM +0800, Anson Huang wrote:
> OK, very sorry for using OUTLOOK to reply such review mail, I will use console mail
> to reply it from now:)
Much better \o/. Note that Outlook can be tuned to behave better, too
(still not perfect AFAIK). Back when I was forced to use Outlook I liked
Outlook Quote Fix[1]. I hope this is not necessary any more today.

Best regards
Uwe

[1] http://home.in.tum.de/~jain/software/outlook-quotefix/
diff mbox

Patch

diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 5951660..fe53473 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -369,6 +369,8 @@  static void __init imx6q_clocks_init(struct device_node *ccm_node)
 
 	/*                                            name             parent_name          reg         shift */
 	clk[IMX6QDL_CLK_APBH_DMA]     = imx_clk_gate2("apbh_dma",      "usdhc3",            base + 0x68, 4);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x68) & (~(0x3 << 6)), base + 0x68);
 	clk[IMX6QDL_CLK_ASRC]         = imx_clk_gate2_shared("asrc",         "asrc_podf",   base + 0x68, 6, &share_count_asrc);
 	clk[IMX6QDL_CLK_ASRC_IPG]     = imx_clk_gate2_shared("asrc_ipg",     "ahb",         base + 0x68, 6, &share_count_asrc);
 	clk[IMX6QDL_CLK_ASRC_MEM]     = imx_clk_gate2_shared("asrc_mem",     "ahb",         base + 0x68, 6, &share_count_asrc);
@@ -385,6 +387,8 @@  static void __init imx6q_clocks_init(struct device_node *ccm_node)
 	else
 		clk[IMX6Q_CLK_ECSPI5] = imx_clk_gate2("ecspi5",        "ecspi_root",        base + 0x6c, 8);
 	clk[IMX6QDL_CLK_ENET]         = imx_clk_gate2("enet",          "ipg",               base + 0x6c, 10);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x6c) & (~(0x3 << 16)), base + 0x6c);
 	clk[IMX6QDL_CLK_ESAI_EXTAL]   = imx_clk_gate2_shared("esai_extal",   "esai_podf",   base + 0x6c, 16, &share_count_esai);
 	clk[IMX6QDL_CLK_ESAI_IPG]     = imx_clk_gate2_shared("esai_ipg",   "ipg",           base + 0x6c, 16, &share_count_esai);
 	clk[IMX6QDL_CLK_ESAI_MEM]     = imx_clk_gate2_shared("esai_mem", "ahb",             base + 0x6c, 16, &share_count_esai);
@@ -443,6 +447,8 @@  static void __init imx6q_clocks_init(struct device_node *ccm_node)
 	clk[IMX6QDL_CLK_SDMA]         = imx_clk_gate2("sdma",          "ahb",               base + 0x7c, 6);
 	clk[IMX6QDL_CLK_SPBA]         = imx_clk_gate2("spba",          "ipg",               base + 0x7c, 12);
 	clk[IMX6QDL_CLK_SPDIF]        = imx_clk_gate2("spdif",         "spdif_podf",        base + 0x7c, 14);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x7c) & (~(0x3f << 18)), base + 0x7c);
 	clk[IMX6QDL_CLK_SSI1_IPG]     = imx_clk_gate2_shared("ssi1_ipg",      "ipg",        base + 0x7c, 18, &share_count_ssi1);
 	clk[IMX6QDL_CLK_SSI2_IPG]     = imx_clk_gate2_shared("ssi2_ipg",      "ipg",        base + 0x7c, 20, &share_count_ssi2);
 	clk[IMX6QDL_CLK_SSI3_IPG]     = imx_clk_gate2_shared("ssi3_ipg",      "ipg",        base + 0x7c, 22, &share_count_ssi3);
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c
index e982ebe..6af14a8 100644
--- a/arch/arm/mach-imx/clk-imx6sl.c
+++ b/arch/arm/mach-imx/clk-imx6sl.c
@@ -396,6 +396,8 @@  static void __init imx6sl_clocks_init(struct device_node *ccm_node)
 	clks[IMX6SL_CLK_SDMA]         = imx_clk_gate2("sdma",         "ipg",               base + 0x7c, 6);
 	clks[IMX6SL_CLK_SPBA]         = imx_clk_gate2("spba",         "ipg",               base + 0x7c, 12);
 	clks[IMX6SL_CLK_SPDIF]        = imx_clk_gate2("spdif",        "spdif0_podf",       base + 0x7c, 14);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x7c) & (~(0x3f << 18)), base + 0x7c);
 	clks[IMX6SL_CLK_SSI1_IPG]     = imx_clk_gate2_shared("ssi1_ipg",     "ipg",        base + 0x7c, 18, &share_count_ssi1);
 	clks[IMX6SL_CLK_SSI2_IPG]     = imx_clk_gate2_shared("ssi2_ipg",     "ipg",        base + 0x7c, 20, &share_count_ssi2);
 	clks[IMX6SL_CLK_SSI3_IPG]     = imx_clk_gate2_shared("ssi3_ipg",     "ipg",        base + 0x7c, 22, &share_count_ssi3);
diff --git a/arch/arm/mach-imx/clk-imx6sx.c b/arch/arm/mach-imx/clk-imx6sx.c
index 17354a1..e42052e 100644
--- a/arch/arm/mach-imx/clk-imx6sx.c
+++ b/arch/arm/mach-imx/clk-imx6sx.c
@@ -379,6 +379,8 @@  static void __init imx6sx_clocks_init(struct device_node *ccm_node)
 	clks[IMX6SX_CLK_AIPS_TZ1]     = imx_clk_gate2("aips_tz1",      "ahb",               base + 0x68, 0);
 	clks[IMX6SX_CLK_AIPS_TZ2]     = imx_clk_gate2("aips_tz2",      "ahb",               base + 0x68, 2);
 	clks[IMX6SX_CLK_APBH_DMA]     = imx_clk_gate2("apbh_dma",      "usdhc3",            base + 0x68, 4);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x68) & (~(0x3 << 6)), base + 0x68);
 	clks[IMX6SX_CLK_ASRC_MEM]     = imx_clk_gate2_shared("asrc_mem", "ahb",             base + 0x68, 6, &share_count_asrc);
 	clks[IMX6SX_CLK_ASRC_IPG]     = imx_clk_gate2_shared("asrc_ipg", "ahb",             base + 0x68, 6, &share_count_asrc);
 	clks[IMX6SX_CLK_CAAM_MEM]     = imx_clk_gate2("caam_mem",      "ahb",               base + 0x68, 8);
@@ -400,6 +402,8 @@  static void __init imx6sx_clocks_init(struct device_node *ccm_node)
 	clks[IMX6SX_CLK_ECSPI5]       = imx_clk_gate2("ecspi5",        "ecspi_podf",        base + 0x6c, 8);
 	clks[IMX6SX_CLK_EPIT1]        = imx_clk_gate2("epit1",         "perclk",            base + 0x6c, 12);
 	clks[IMX6SX_CLK_EPIT2]        = imx_clk_gate2("epit2",         "perclk",            base + 0x6c, 14);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x6c) & (~(0x3 << 16)), base + 0x6c);
 	clks[IMX6SX_CLK_ESAI_EXTAL]   = imx_clk_gate2_shared("esai_extal", "esai_podf",     base + 0x6c, 16, &share_count_esai);
 	clks[IMX6SX_CLK_ESAI_IPG]     = imx_clk_gate2_shared("esai_ipg",   "ahb",           base + 0x6c, 16, &share_count_esai);
 	clks[IMX6SX_CLK_ESAI_MEM]     = imx_clk_gate2_shared("esai_mem",   "ahb",           base + 0x6c, 16, &share_count_esai);
@@ -455,6 +459,8 @@  static void __init imx6sx_clocks_init(struct device_node *ccm_node)
 	clks[IMX6SX_CLK_ROM]          = imx_clk_gate2("rom",           "ahb",               base + 0x7c, 0);
 	clks[IMX6SX_CLK_SDMA]         = imx_clk_gate2("sdma",          "ahb",               base + 0x7c, 6);
 	clks[IMX6SX_CLK_SPBA]         = imx_clk_gate2("spba",          "ipg",               base + 0x7c, 12);
+	/* make sure the initial clock gate setting of share_count is off */
+	writel_relaxed(readl_relaxed(base + 0x7c) & (~(0x3 << 14)) & (~(0x3f << 18)), base + 0x7c);
 	clks[IMX6SX_CLK_AUDIO]        = imx_clk_gate2_shared("audio",  "audio_podf",        base + 0x7c, 14, &share_count_audio);
 	clks[IMX6SX_CLK_SPDIF]        = imx_clk_gate2_shared("spdif",  "spdif_podf",        base + 0x7c, 14, &share_count_audio);
 	clks[IMX6SX_CLK_SSI1_IPG]     = imx_clk_gate2_shared("ssi1_ipg",      "ipg",        base + 0x7c, 18, &share_count_ssi1);