Skip to contentSkip to conversation

clk: shmobile: rcar-gen2: Use kick bit to allow Z clock frequency change

PATCH Superseded Benoit Cousson 4 participants 9 messages Feb 25, 2014 – Feb 28, 2014

Subsystems (5)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 clk: shmobile: rcar-gen2: Use kick bit to allow Z clock frequency change

The Z clock frequency change is effective only after setting the kick bit located in the FRQCRB register.
Without that, the CA15 CPUs clock rate will never change.

Fix that by checking if the kick bit is cleared and enable it to make the clock rate change effective. The bit is cleared automatically upon completion.

Note: The kick bit is used as well to change 3 other emulation clocks:
Debug Trace port clock (ZTR), Debug Trace bus clock (ZT), and Debug clock (ZTRD2). It is not clear from the spec [1] if there
is any relation between the CPU clock rate and these emulation clocks. Moreover, these clocks are probably supposed to be controled by an external debugger / tracer like Lauterbach PowerTrace.
For all these reasons, the current implementation does not expose these clock nodes and thus does not have to consider the multiple accesses to the kick bit.

Cc Mike Turquette <[email protected]>
Cc Laurent Pinchart <[email protected]>

[1] R-Car-H2-v0.6-en.pdf - page 186
---

Salut Laurent,

If you have any information about these emulation clocks, please let me know.

Moreover, the CCF clock driver is shared with the r8a7791, so a test on that platform will be good.

Regards,
Benoit

  drivers/clk/shmobile/clk-rcar-gen2.c | 26 ++++++++++++++++++++++++--
  1 file changed, 24 insertions(+), 2 deletions(-)
(quoted diff) +0 -0

b/drivers/clk/shmobile/clk-rcar-gen2.c
index a59ec21..9f12746 100644

drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
@@ -26,6 +26,8 @@ struct rcar_gen2_cpg {
  	void __iomem *reg;
  };
 
+#define CPG_FRQCRB			0x00000004
+#define CPG_FRQCRB_KICK			BIT(31)
  #define CPG_SDCKCR			0x00000074
  #define CPG_PLL0CR			0x000000d8
  #define CPG_FRQCRC			0x000000e0
@@ -45,6 +47,7 @@ struct rcar_gen2_cpg {
  struct cpg_z_clk {
  	struct clk_hw hw;
  	void __iomem *reg;
+	void __iomem *kick_reg;
  };
 
  #define to_z_clk(_hw)	container_of(_hw, struct cpg_z_clk, hw)
@@ -83,17 +86,35 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, 

unsigned long rate,
{
struct cpg_z_clk *zclk = to_z_clk(hw);
unsigned int mult;
- u32 val;
+ u32 val, kick;
+ int i;

  	mult = div_u64((u64)rate * 32, parent_rate);
  	mult = clamp(mult, 1U, 32U);

+ if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+ return -EBUSY;
+
val = clk_readl(zclk->reg);
val &= ~CPG_FRQCRC_ZFC_MASK;
val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT; clk_writel(val, zclk->reg);

- return 0;
+ /*
+ * Set KICK bit in FRQCRB to update hardware setting and
+ * wait for completion.
+ */
+ kick = clk_readl(zclk->kick_reg);
+ kick |= CPG_FRQCRB_KICK;
+ clk_writel(kick, zclk->kick_reg);
+
+ for (i = 1000; i; i--)
+ if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+ cpu_relax();
+ else
+ return 0;
+
+ return -ETIMEDOUT;
}

static const struct clk_ops cpg_z_clk_ops = {
@@ -120,6 +141,7 @@ static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg)
init.num_parents = 1;

zclk->reg = cpg->reg + CPG_FRQCRC;
+ zclk->kick_reg = cpg->reg + CPG_FRQCRB;
zclk->hw.init = &init;

clk = clk_register(NULL, &zclk->hw);

Signature · 1 line
1.8.3.2
#1 lore raw

Hi Benoit,

Thank you for the patch.

Quoted 22 lines · On Tuesday 25 February 2014 14:39:54 Benoit Cousson wrote:

The Z clock frequency change is effective only after setting the kick bit located in the FRQCRB register.
Without that, the CA15 CPUs clock rate will never change.

Fix that by checking if the kick bit is cleared and enable it to make the clock rate change effective. The bit is cleared automatically upon completion.

Note: The kick bit is used as well to change 3 other emulation clocks:
Debug Trace port clock (ZTR), Debug Trace bus clock (ZT), and Debug clock (ZTRD2). It is not clear from the spec [1] if there
is any relation between the CPU clock rate and these emulation clocks. Moreover, these clocks are probably supposed to be controled by an external debugger / tracer like Lauterbach PowerTrace.
For all these reasons, the current implementation does not expose these clock nodes and thus does not have to consider the multiple accesses to the kick bit.

Cc Mike Turquette <[email protected]>
Cc Laurent Pinchart <[email protected]>
Quoted 7 lines

[1] R-Car-H2-v0.6-en.pdf - page 186
---

Salut Laurent,

If you have any information about these emulation clocks, please let me know.

I don't I'm afraid. All I know is that we don't use them for now, so I wouldn't care :-)

Quoted 2 lines

Moreover, the CCF clock driver is shared with the r8a7791, so a test on that platform will be good.

I can do that. I assume you're working on Lager then ?

How can I exercise the code ? Using cpufreq to change the CA15 clock frequency ? A brief test procedure would be appreciated.

Quoted 2 lines
  drivers/clk/shmobile/clk-rcar-gen2.c | 26 ++++++++++++++++++++++++--
  1 file changed, 24 insertions(+), 2 deletions(-)

By the way the patch was corrupted with line wraps and additional spaces.

Quoted 23 lines
drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
index a59ec21..9f12746 100644
@@ -26,6 +26,8 @@ struct rcar_gen2_cpg {
  	void __iomem *reg;
  };
 
+#define CPG_FRQCRB			0x00000004
+#define CPG_FRQCRB_KICK			BIT(31)
  #define CPG_SDCKCR			0x00000074
  #define CPG_PLL0CR			0x000000d8
  #define CPG_FRQCRC			0x000000e0
@@ -45,6 +47,7 @@ struct rcar_gen2_cpg {
  struct cpg_z_clk {
  	struct clk_hw hw;
  	void __iomem *reg;
+	void __iomem *kick_reg;
  };
 
  #define to_z_clk(_hw)	container_of(_hw, struct cpg_z_clk, hw)

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 91

Quoted 7 lines
drivers/clk/shmobile/clk-rcar-gen2.c +2 -1
@@ -83,17 +86,35 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw,
 unsigned long rate,
  {
  	struct cpg_z_clk *zclk = to_z_clk(hw);
  	unsigned int mult;
-	u32 val;
+	u32 val, kick;
+	int i;

The loop counter will always be positive, please use an unsigned int.

Quoted 10 lines
drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
  	mult = div_u64((u64)rate * 32, parent_rate);
  	mult = clamp(mult, 1U, 32U);
 
+	if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+		return -EBUSY;
+
  	val = clk_readl(zclk->reg);
  	val &= ~CPG_FRQCRC_ZFC_MASK;
  	val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 109

Quoted 10 lines
drivers/clk/shmobile/clk-rcar-gen2.c +7 -1
  	clk_writel(val, zclk->reg);
 
-	return 0;
+	/*
+	 * Set KICK bit in FRQCRB to update hardware setting and
+	 * wait for completion.
+	 */
+	kick = clk_readl(zclk->kick_reg);
+	kick |= CPG_FRQCRB_KICK;
+	clk_writel(kick, zclk->kick_reg);

Does CCF guarantee that two set_rate calls for different clocks will not occur concurrently ? If so a brief comment would be nice. Otherwise we need a lock around this.

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 115

Quoted 6 lines
drivers/clk/shmobile/clk-rcar-gen2.c +6 -0
+
+	for (i = 1000; i; i--)
+		if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+			cpu_relax();
+		else
+			return 0;

Please put braces around the for() content.

How many iterations does this need in practice ? I also wonder if we really need to wait or if we could just return and assume the clock frequency will change soon enough.

Quoted 14 lines
drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
+
+	return -ETIMEDOUT;
  }
 
  static const struct clk_ops cpg_z_clk_ops = {
@@ -120,6 +141,7 @@ static struct clk * __init cpg_z_clk_register(struct
 rcar_gen2_cpg *cpg)
  	init.num_parents = 1;
 
  	zclk->reg = cpg->reg + CPG_FRQCRC;
+	zclk->kick_reg = cpg->reg + CPG_FRQCRB;
  	zclk->hw.init = &init;
 
  	clk = clk_register(NULL, &zclk->hw);
Signature · 3 lines
Regards,

Laurent Pinchart
#2 lore raw
In reply to Benoit Cousson

Hi Laurent,

Quoted 3 lines · Laurent Pinchart

Hi Benoit,

Thank you for the patch.

You're welcome. Thanks for your comments.

Quoted 31 lines · On Tuesday 25 February 2014 14:39:54 Benoit Cousson wrote:
Quoted 22 lines

The Z clock frequency change is effective only after setting the kick bit located in the FRQCRB register.
Without that, the CA15 CPUs clock rate will never change.

Fix that by checking if the kick bit is cleared and enable it to make the clock rate change effective. The bit is cleared automatically upon completion.

Note: The kick bit is used as well to change 3 other emulation clocks:
Debug Trace port clock (ZTR), Debug Trace bus clock (ZT), and Debug clock (ZTRD2). It is not clear from the spec [1] if there
is any relation between the CPU clock rate and these emulation clocks. Moreover, these clocks are probably supposed to be controled by an external debugger / tracer like Lauterbach PowerTrace.
For all these reasons, the current implementation does not expose these clock nodes and thus does not have to consider the multiple accesses to the kick bit.

Cc Mike Turquette <[email protected]>
Cc Laurent Pinchart <[email protected]>
Quoted 7 lines

[1] R-Car-H2-v0.6-en.pdf - page 186
---

Salut Laurent,

If you have any information about these emulation clocks, please let me know.

Quoted 7 lines

I don't I'm afraid. All I know is that we don't use them for now, so I wouldn't care :-)

Quoted 2 lines

Moreover, the CCF clock driver is shared with the r8a7791, so a test on that platform will be good.

I can do that. I assume you're working on Lager then ?

Yes I am. I don't have any Koelsch board.

Quoted 2 lines

How can I exercise the code ? Using cpufreq to change the CA15 clock frequency ? A brief test procedure would be appreciated.

Sure. First you can use dhrystone to check that the CPU frequency is changing or not.

So far, changing the clock rate will not affect the dhrystone value at all becasue the magic kick bit is not set.

debugfs does not seems to allow write access to the clk_rate, so using cpufreq is propably the best way to test it.

I have a small series to add DVFS for Lager (Multiplatform boot only), I can share you that code if you want.

Naive question: How did you test that z-clock code originally ?

Quoted 4 lines
Quoted 2 lines
   drivers/clk/shmobile/clk-rcar-gen2.c | 26 ++++++++++++++++++++++++--
   1 file changed, 24 insertions(+), 2 deletions(-)

By the way the patch was corrupted with line wraps and additional spaces.

Gosh! I edited it slightly by hand with thunderbird before submitting... I probably messed it up. Sorry :-(

Quoted 22 lines
Quoted 22 lines
drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
index a59ec21..9f12746 100644
@@ -26,6 +26,8 @@ struct rcar_gen2_cpg {
   	void __iomem *reg;
   };
 
+#define CPG_FRQCRB			0x00000004
+#define CPG_FRQCRB_KICK			BIT(31)
   #define CPG_SDCKCR			0x00000074
   #define CPG_PLL0CR			0x000000d8
   #define CPG_FRQCRC			0x000000e0
@@ -45,6 +47,7 @@ struct rcar_gen2_cpg {
   struct cpg_z_clk {
   	struct clk_hw hw;
   	void __iomem *reg;
+	void __iomem *kick_reg;
   };
 
Quoted 11 lines

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 91

Quoted 9 lines
drivers/clk/shmobile/clk-rcar-gen2.c +2 -1
   #define to_z_clk(_hw)	container_of(_hw, struct cpg_z_clk, hw)
@@ -83,17 +86,35 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw,
 unsigned long rate,
   {
   	struct cpg_z_clk *zclk = to_z_clk(hw);
   	unsigned int mult;
-	u32 val;
+	u32 val, kick;
+	int i;

The loop counter will always be positive, please use an unsigned int.

OK.

Quoted 11 lines
Quoted 11 lines
drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
   	mult = div_u64((u64)rate * 32, parent_rate);
   	mult = clamp(mult, 1U, 32U);
 
+	if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+		return -EBUSY;
+
   	val = clk_readl(zclk->reg);
   	val &= ~CPG_FRQCRC_ZFC_MASK;
   	val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
   	clk_writel(val, zclk->reg);
 
Quoted 12 lines

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 109

Quoted 8 lines
drivers/clk/shmobile/clk-rcar-gen2.c +7 -1
-	return 0;ir: cannot create directory `/home/ubuntu/projects/ti-glsdk_omap5-uevm_6_03_00_01/targetfs': Permission denied
+	/*
+	 * Set KICK bit in FRQCRB to update hardware setting and
+	 * wait for completion.
+	 */
+	kick = clk_readl(zclk->kick_reg);
+	kick |= CPG_FRQCRB_KICK;
+	clk_writel(kick, zclk->kick_reg);

Does CCF guarantee that two set_rate calls for different clocks will not occur concurrently ? If so a brief comment would be nice. Otherwise we need a lock around this.

So far, this is the only user of the register. That's why there is no lock. It is explained in the changelog. But if you want I can re-use the same comment here.

Quoted 9 lines

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 115

Quoted 7 lines
drivers/clk/shmobile/clk-rcar-gen2.c +6 -0
+
+	for (i = 1000; i; i--)
+		if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+			cpu_relax();
+		else
+			return 0;

Please put braces around the for() content.

OK, no problem, if you prefer that.

Quoted 1 line

How many iterations does this need in practice ?

I don't have any clue :-)

I don't like these kind of magic numbers as well, but without further information, it is hard to know.

Based on TI experience, there is no way to get any accurate information about this kind of transition time even from HW enginners. Most of the time the best you can get is a tens of micro-sec or a hundred of clock cycles.

FWIW, this "value" is based on what was already done and merged for the zlock inside the clock-r8a73a4.c code (non-CCF).

Quoted 3 lines

I also wonder if we really
need to wait or if we could just return and assume the clock frequency will change soon enough.

We do need to wait, that the recommended procedure from the spec, and this is the only way to ensure the frequency is properly changed.

In the case of DVFS sequence, you do not want to start reducing the voltage before you are 100% sure the frequency was reduced before. So that's the only safe way to do that.

Regards,
Benoit

Signature · 4 lines
Benoît Cousson
BayLibre
Embedded Linux Technology Lab
www.baylibre.com
#3 lore raw
In reply to Benoit Cousson

Hi Benoit,

Quoted 46 lines · On Tuesday 25 February 2014 18:07:54 Benoit Cousson wrote:
Quoted 3 lines · Laurent Pinchart

Hi Benoit,

Thank you for the patch.

You're welcome. Thanks for your comments.

Quoted 31 lines · On Tuesday 25 February 2014 14:39:54 Benoit Cousson wrote:
Quoted 22 lines

The Z clock frequency change is effective only after setting the kick bit located in the FRQCRB register.
Without that, the CA15 CPUs clock rate will never change.

Fix that by checking if the kick bit is cleared and enable it to make the clock rate change effective. The bit is cleared automatically upon completion.

Note: The kick bit is used as well to change 3 other emulation clocks:
Debug Trace port clock (ZTR), Debug Trace bus clock (ZT), and Debug clock (ZTRD2). It is not clear from the spec [1] if there
is any relation between the CPU clock rate and these emulation clocks. Moreover, these clocks are probably supposed to be controled by an external debugger / tracer like Lauterbach PowerTrace.
For all these reasons, the current implementation does not expose these clock nodes and thus does not have to consider the multiple accesses to the kick bit.

Cc Mike Turquette <[email protected]>
Cc Laurent Pinchart <[email protected]>
Quoted 7 lines

[1] R-Car-H2-v0.6-en.pdf - page 186
---

Salut Laurent,

If you have any information about these emulation clocks, please let me know.

Quoted 7 lines

I don't I'm afraid. All I know is that we don't use them for now, so I wouldn't care :-)

Quoted 2 lines

Moreover, the CCF clock driver is shared with the r8a7791, so a test on that platform will be good.

I can do that. I assume you're working on Lager then ?

Quoted 7 lines

Yes I am. I don't have any Koelsch board.

Quoted 2 lines

How can I exercise the code ? Using cpufreq to change the CA15 clock frequency ? A brief test procedure would be appreciated.

Sure. First you can use dhrystone to check that the CPU frequency is changing or not.

OK.

Quoted 8 lines

So far, changing the clock rate will not affect the dhrystone value at all becasue the magic kick bit is not set.

debugfs does not seems to allow write access to the clk_rate, so using cpufreq is propably the best way to test it.

I have a small series to add DVFS for Lager (Multiplatform boot only), I can share you that code if you want.

That would be nice, thanks.

Quoted 1 line

Naive question: How did you test that z-clock code originally ?

Simple answer: I haven't :-)

Quoted 13 lines
Quoted 4 lines
Quoted 2 lines
   drivers/clk/shmobile/clk-rcar-gen2.c | 26 ++++++++++++++++++++++++--
   1 file changed, 24 insertions(+), 2 deletions(-)

By the way the patch was corrupted with line wraps and additional spaces.

Gosh! I edited it slightly by hand with thunderbird before submitting... I probably messed it up. Sorry :-(

Quoted 5 lines
Quoted 5 lines
drivers/clk/shmobile/clk-rcar-gen2.c +0 -0
index a59ec21..9f12746 100644

[snip]

Quoted 39 lines
Quoted 10 lines

drivers/clk/shmobile/clk-rcar-gen2.c static int cpg_z_clk_set_rate(struct clk_hw *hw, · line 91

Quoted 8 lines
drivers/clk/shmobile/clk-rcar-gen2.c +2 -1
@@ -83,17 +86,35 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw,
 unsigned long rate,
   {
   	struct cpg_z_clk *zclk = to_z_clk(hw);
   	unsigned int mult;
-	u32 val;
+	u32 val, kick;
+	int i;

The loop counter will always be positive, please use an unsigned int.

OK.

Quoted 11 lines
Quoted 11 lines
drivers/clk/shmobile/clk-rcar-gen2.c +3 -0
   	mult = div_u64((u64)rate * 32, parent_rate);
   	mult = clamp(mult, 1U, 32U);
 
+	if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+		return -EBUSY;
+
   	val = clk_readl(zclk->reg);
   	val &= ~CPG_FRQCRC_ZFC_MASK;
   	val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
   	clk_writel(val, zclk->reg);
 
Quoted 14 lines

drivers/clk/shmobile/clk-rcar-gen2.c

Quoted 10 lines
drivers/clk/shmobile/clk-rcar-gen2.c +0 -1
-	return 0;ir: cannot create directory

`/home/ubuntu/projects/ti-glsdk_omap5-uevm_6_03_00_01/targetfs':
Permission denied

drivers/clk/shmobile/clk-rcar-gen2.c +7 -0
+	/*
+	 * Set KICK bit in FRQCRB to update hardware setting and
+	 * wait for completion.
+	 */
+	kick = clk_readl(zclk->kick_reg);
+	kick |= CPG_FRQCRB_KICK;
+	clk_writel(kick, zclk->kick_reg);

Does CCF guarantee that two set_rate calls for different clocks will not occur concurrently ? If so a brief comment would be nice. Otherwise we need a lock around this.

Quoted 3 lines

So far, this is the only user of the register. That's why there is no lock. It is explained in the changelog. But if you want I can re-use the same comment here.

A comment would be nice, yes. You can make it shorter than the commit message.

Quoted 9 lines
Quoted 8 lines

drivers/clk/shmobile/clk-rcar-gen2.c

Quoted 6 lines
drivers/clk/shmobile/clk-rcar-gen2.c +6 -0
+
+	for (i = 1000; i; i--)
+		if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+			cpu_relax();
+		else
+			return 0;

Please put braces around the for() content.

Quoted 5 lines

OK, no problem, if you prefer that.

Quoted 1 line

How many iterations does this need in practice ?

I don't have any clue :-)

Could you test it ?

Quoted 14 lines

I don't like these kind of magic numbers as well, but without further information, it is hard to know.

Based on TI experience, there is no way to get any accurate information about this kind of transition time even from HW enginners. Most of the time the best you can get is a tens of micro-sec or a hundred of clock cycles.

FWIW, this "value" is based on what was already done and merged for the zlock inside the clock-r8a73a4.c code (non-CCF).

Quoted 2 lines

I also wonder if we really need to wait or if we could just return and assume the clock frequency will change soon enough.

Quoted 6 lines

We do need to wait, that the recommended procedure from the spec, and this is the only way to ensure the frequency is properly changed.

In the case of DVFS sequence, you do not want to start reducing the voltage before you are 100% sure the frequency was reduced before. So that's the only safe way to do that.

Good point.

Signature · 3 lines
Regards,

Laurent Pinchart
#4 lore raw
In reply to Benoit Cousson

Quoting Laurent Pinchart (2014-02-25 09:48:38)

Quoted 15 lines · On Tuesday 25 February 2014 18:07:54 Benoit Cousson wrote:
Quoted 14 lines · Laurent Pinchart
Quoted 12 lines · On Tuesday 25 February 2014 14:39:54 Benoit Cousson wrote:

Review

Quoted 7 lines
(quoted diff) +7 -0
+  /*
+   * Set KICK bit in FRQCRB to update hardware setting and
+   * wait for completion.
+   */
+  kick = clk_readl(zclk->kick_reg);
+  kick |= CPG_FRQCRB_KICK;
+  clk_writel(kick, zclk->kick_reg);

Does CCF guarantee that two set_rate calls for different clocks will not occur concurrently ? If so a brief comment would be nice. Otherwise we need a lock around this.

Quoted 5 lines
Quoted 3 lines

So far, this is the only user of the register. That's why there is no lock. It is explained in the changelog. But if you want I can re-use the same comment here.

A comment would be nice, yes. You can make it shorter than the commit message.

CCF holds a global mutex during a call to clk_set_rate. So clk_prepare, clk_unprepare, clk_set_parent or a competing clk_set_rate will not touch this register during the critical section.

However it is possible to reenter the framework, but usually you control that code flow.

The main two reasons to introduce your own more granular register-level locking are:

1) clk_enable & clk_disable hold a separate global spinlock (not the global mutex), so if this register is used for set_rate operations AND enable/disable operations then you'll need a spinlock.

2) Other stuff outside of the clk framework touches this register (sounds like it is not the case here).

Regards,
Mike

#5 lore raw
In reply to Benoit Cousson

Hi Mike,

Quoted 33 lines · On Wednesday 26 February 2014 12:54:55 Mike Turquette wrote:

Quoting Laurent Pinchart (2014-02-25 09:48:38)

Quoted 15 lines · On Tuesday 25 February 2014 18:07:54 Benoit Cousson wrote:
Quoted 14 lines · Laurent Pinchart
Quoted 12 lines · On Tuesday 25 February 2014 14:39:54 Benoit Cousson wrote:

Review

Quoted 7 lines
(quoted diff) +7 -0
+  /*
+   * Set KICK bit in FRQCRB to update hardware setting and
+   * wait for completion.
+   */
+  kick = clk_readl(zclk->kick_reg);
+  kick |= CPG_FRQCRB_KICK;
+  clk_writel(kick, zclk->kick_reg);

Does CCF guarantee that two set_rate calls for different clocks will not occur concurrently ? If so a brief comment would be nice. Otherwise we need a lock around this.

Quoted 6 lines
Quoted 3 lines

So far, this is the only user of the register. That's why there is no lock. It is explained in the changelog. But if you want I can re-use the same comment here.

A comment would be nice, yes. You can make it shorter than the commit message.

CCF holds a global mutex during a call to clk_set_rate. So clk_prepare, clk_unprepare, clk_set_parent or a competing clk_set_rate will not touch this register during the critical section.

However it is possible to reenter the framework, but usually you control that code flow.

The main two reasons to introduce your own more granular register-level locking are:

Quoted 6 lines

1) clk_enable & clk_disable hold a separate global spinlock (not the global mutex), so if this register is used for set_rate operations AND enable/disable operations then you'll need a spinlock.

2) Other stuff outside of the clk framework touches this register (sounds like it is not the case here).

Thanks a lot for the explanation. I've cooked up a small documentation patch to avoid the need to repeat this over and over, I'll send it separately.

Looking at the implementation I found something that looked like a locking bug in the Qualcomm clock drivers at first sight.

clk-rpg.c calls __clk_is_enabled() from within its configure_bank() function. That function ends up being called from within the .set_rate, .set_parent and .set_rate_and_parent operations. This leads to __clk_is_enabled() being called without the enable spinlock held.

Now, clk-rpg.c provides an .is_enabled handler (clk_is_enabled_regmap) so we're at least not accessing the clock enable_count counter without the proper lock being held. I don't know whether clk_is_enabled_regmap() will handle locking properly though.

Exporting __clk_is_enabled() looks a bit dangerous to me. It might make sense to replace the __clk_is_enabled() call in clk-rpg.c with a direct call to clk_is_enabled_regmap(), but we still have two other users (namely drivers/cpufreq/kirkwood-cpufreq.c and arch/arm/mach-omap2/pm24xx.c) in the mainline kernel.

Signature · 3 lines
Regards,

Laurent Pinchart
#6 lore raw
In reply to Benoit Cousson
Quoted 60 lines · Mike Turquette

Quoting Laurent Pinchart (2014-02-26 13:53:03)

Quoted 59 lines

Hi Mike,

Quoted 29 lines · On Wednesday 26 February 2014 12:54:55 Mike Turquette wrote:

Quoting Laurent Pinchart (2014-02-25 09:48:38)

Quoted 10 lines · On Tuesday 25 February 2014 18:07:54 Benoit Cousson wrote:
Quoted 9 lines · Laurent Pinchart
Quoted 8 lines · On Tuesday 25 February 2014 14:39:54 Benoit Cousson wrote:
Quoted 7 lines
(quoted diff) +7 -0
+  /*
+   * Set KICK bit in FRQCRB to update hardware setting and
+   * wait for completion.
+   */
+  kick = clk_readl(zclk->kick_reg);
+  kick |= CPG_FRQCRB_KICK;
+  clk_writel(kick, zclk->kick_reg);
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

Does CCF guarantee that two set_rate calls for different clocks will not occur concurrently ? If so a brief comment would be nice. Otherwise we need a lock around this.

So far, this is the only user of the register. That's why there is no lock. It is explained in the changelog. But if you want I can re-use the same comment here.

A comment would be nice, yes. You can make it shorter than the commit message.

CCF holds a global mutex during a call to clk_set_rate. So clk_prepare, clk_unprepare, clk_set_parent or a competing clk_set_rate will not touch this register during the critical section.

However it is possible to reenter the framework, but usually you control that code flow.

The main two reasons to introduce your own more granular register-level locking are:

Quoted 6 lines

1) clk_enable & clk_disable hold a separate global spinlock (not the global mutex), so if this register is used for set_rate operations AND enable/disable operations then you'll need a spinlock.

2) Other stuff outside of the clk framework touches this register (sounds like it is not the case here).

Thanks a lot for the explanation. I've cooked up a small documentation patch to avoid the need to repeat this over and over, I'll send it separately.

Looking at the implementation I found something that looked like a locking bug in the Qualcomm clock drivers at first sight.

clk-rpg.c calls __clk_is_enabled() from within its configure_bank() function. That function ends up being called from within the .set_rate, .set_parent and .set_rate_and_parent operations. This leads to __clk_is_enabled() being called without the enable spinlock held.

Now, clk-rpg.c provides an .is_enabled handler (clk_is_enabled_regmap) so we're at least not accessing the clock enable_count counter without the proper lock being held. I don't know whether clk_is_enabled_regmap() will handle locking properly though.

Exporting __clk_is_enabled() looks a bit dangerous to me. It might make sense to replace the __clk_is_enabled() call in clk-rpg.c with a direct call to clk_is_enabled_regmap(), but we still have two other users (namely drivers/cpufreq/kirkwood-cpufreq.c and arch/arm/mach-omap2/pm24xx.c) in the mainline kernel.

(I'm guessing clk-rpg.c is actually clk-rcg.c?)

It's safe for the set_parent and set_parent_and_rate case because of the way we do the parent switch (see __clk_set_parent_before() in particular). The enable/prepare state of the clock cannot change while these ops are called. If the clock is prepared before configure_bank() is called then __clk_set_parent_before() has enabled the clock for us. If the clock isn't prepared before configure_bank() is called then we're holding the prepare lock and nobody can enable the clock without first preparing the clock (which means they would need to grab the prepare lock and wait for us to be done).

It looks like the only case where it actually is racy is a plain set_rate, in which case we're not switching parents and a clk_enable()/disable() could happen in the middle of configure_bank(). Sad. I doubt anyone will ever actually do that in practice, but sure theoretical problems are still problems. This patch should fix it.

---8<---
From: Stephen Boyd <[email protected]>
Subject: [PATCH] clk: qcom: Synchronize configure_bank() with enable/disable

Calling __clk_is_enabled() in configure_bank() isn't safe without synchronizing with enable/disable of this clock. Introduce a global spinlock that we grab in enable, disable and across this bank configuration function so that the clock can't be enabled or disabled while configure_bank() is running.

---
 drivers/clk/qcom/clk-rcg.c | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)
drivers/clk/qcom/clk-rcg.c +29 -2
index abfc2b675aea..51c08bc22378 100644
@@ -22,6 +22,9 @@
 
 #include "clk-rcg.h"
 
+/* Synchronize .enable/.disable with plain .set_rate */
+static DEFINE_SPINLOCK(rcg_enable_lock);
+
 static u32 ns_to_src(struct src_sel *s, u32 ns)
 {
 	ns >>= s->src_sel_shift;
@@ -202,7 +205,9 @@ static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
 	u32 bank_reg;
 	bool banked_mn = !!rcg->mn[1].width;
 	struct clk_hw *hw = &rcg->clkr.hw;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rcg_enable_lock, flags);
 	enabled = __clk_is_enabled(hw->clk);
 
 	regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
@@ -251,6 +256,7 @@ static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
 		*regp ^= BIT(rcg->mux_sel_bit);
 		regmap_write(rcg->clkr.regmap, bank_reg, *regp);
 	}
+	spin_unlock_irqrestore(&rcg_enable_lock, flags);
 }
 
 static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
@@ -492,6 +498,27 @@ static int clk_dyn_rcg_set_rate_and_parent(struct clk_hw *hw,
 	return __clk_dyn_rcg_set_rate(hw, rate);
 }
 
+static int clk_dyn_rcg_enable(struct clk_hw *hw)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rcg_enable_lock, flags);
+	ret = clk_enable_regmap(hw);
+	spin_unlock_irqrestore(&rcg_enable_lock, flags);
+
+	return ret;
+}
+
+static void clk_dyn_rcg_disable(struct clk_hw *hw)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&rcg_enable_lock, flags);
+	clk_disable_regmap(hw);
+	spin_unlock_irqrestore(&rcg_enable_lock, flags);
+}
+
 const struct clk_ops clk_rcg_ops = {
 	.enable = clk_enable_regmap,
 	.disable = clk_disable_regmap,
@@ -504,9 +531,9 @@ const struct clk_ops clk_rcg_ops = {
 EXPORT_SYMBOL_GPL(clk_rcg_ops);
 
 const struct clk_ops clk_dyn_rcg_ops = {
-	.enable = clk_enable_regmap,
+	.enable = clk_dyn_rcg_enable,
 	.is_enabled = clk_is_enabled_regmap,
-	.disable = clk_disable_regmap,
+	.disable = clk_dyn_rcg_disable,
 	.get_parent = clk_dyn_rcg_get_parent,
 	.set_parent = clk_dyn_rcg_set_parent,
 	.recalc_rate = clk_dyn_rcg_recalc_rate,
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#7 lore raw
In reply to Benoit Cousson

Hi Stephen,

Quoted 3 lines · On Wednesday 26 February 2014 16:50:29 Stephen Boyd wrote:
Quoted 2 lines · Mike Turquette

Quoting Laurent Pinchart (2014-02-26 13:53:03)

Quoted 1 line · On Wednesday 26 February 2014 12:54:55 Mike Turquette wrote:

[snip]

Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
Quoted 10 lines

CCF holds a global mutex during a call to clk_set_rate. So clk_prepare, clk_unprepare, clk_set_parent or a competing clk_set_rate will not touch this register during the critical section.

However it is possible to reenter the framework, but usually you control that code flow.

The main two reasons to introduce your own more granular register-level locking are:

Quoted 6 lines

1) clk_enable & clk_disable hold a separate global spinlock (not the global mutex), so if this register is used for set_rate operations AND enable/disable operations then you'll need a spinlock.

2) Other stuff outside of the clk framework touches this register (sounds like it is not the case here).

Thanks a lot for the explanation. I've cooked up a small documentation patch to avoid the need to repeat this over and over, I'll send it separately.

Looking at the implementation I found something that looked like a locking bug in the Qualcomm clock drivers at first sight.

clk-rpg.c calls __clk_is_enabled() from within its configure_bank() function. That function ends up being called from within the .set_rate, .set_parent and .set_rate_and_parent operations. This leads to __clk_is_enabled() being called without the enable spinlock held.

Now, clk-rpg.c provides an .is_enabled handler (clk_is_enabled_regmap) so we're at least not accessing the clock enable_count counter without the proper lock being held. I don't know whether clk_is_enabled_regmap() will handle locking properly though.

Quoted 7 lines
Quoted 5 lines
Quoted 5 lines

Exporting __clk_is_enabled() looks a bit dangerous to me. It might make sense to replace the __clk_is_enabled() call in clk-rpg.c with a direct call to clk_is_enabled_regmap(), but we still have two other users (namely drivers/cpufreq/kirkwood-cpufreq.c and arch/arm/mach-omap2/pm24xx.c) in the mainline kernel.

(I'm guessing clk-rpg.c is actually clk-rcg.c?)

Yes, sorry.

Quoted 10 lines

It's safe for the set_parent and set_parent_and_rate case because of the way we do the parent switch (see __clk_set_parent_before() in particular). The enable/prepare state of the clock cannot change while these ops are called. If the clock is prepared before configure_bank() is called then __clk_set_parent_before() has enabled the clock for us. If the clock isn't prepared before configure_bank() is called then we're holding the prepare lock and nobody can enable the clock without first preparing the clock (which means they would need to grab the prepare lock and wait for us to be done).

Quoted 5 lines

It looks like the only case where it actually is racy is a plain set_rate, in which case we're not switching parents and a clk_enable()/disable() could happen in the middle of configure_bank(). Sad. I doubt anyone will ever actually do that in practice, but sure theoretical problems are still problems. This patch should fix it.

I'm not familiar with the hardware, but the patch below looks good to me. While you're at it, what would you think about replacing the __clk_is_enabled() call with a direct call to clk_is_enabled_regmap() ? Any user of __clk_is_enabled() outside the CCF core is suspect from a locking point of view, and exporting that function seems like a dangerous idea. On the other hand, if you had done so already I wouldn't have found the race condition.

Quoted 88 lines

---8<---
From: Stephen Boyd <[email protected]>
Subject: [PATCH] clk: qcom: Synchronize configure_bank() with enable/disable

Calling __clk_is_enabled() in configure_bank() isn't safe without synchronizing with enable/disable of this clock. Introduce a global spinlock that we grab in enable, disable and across this bank configuration function so that the clock can't be enabled or disabled while configure_bank() is running.

---
 drivers/clk/qcom/clk-rcg.c | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)
drivers/clk/qcom/clk-rcg.c +29 -2
index abfc2b675aea..51c08bc22378 100644
@@ -22,6 +22,9 @@
 
 #include "clk-rcg.h"
 
+/* Synchronize .enable/.disable with plain .set_rate */
+static DEFINE_SPINLOCK(rcg_enable_lock);
+
 static u32 ns_to_src(struct src_sel *s, u32 ns)
 {
 	ns >>= s->src_sel_shift;
@@ -202,7 +205,9 @@ static void configure_bank(struct clk_dyn_rcg *rcg,
 const struct freq_tbl *f) u32 bank_reg;
 	bool banked_mn = !!rcg->mn[1].width;
 	struct clk_hw *hw = &rcg->clkr.hw;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rcg_enable_lock, flags);
 	enabled = __clk_is_enabled(hw->clk);
 
 	regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
@@ -251,6 +256,7 @@ static void configure_bank(struct clk_dyn_rcg *rcg,
 const struct freq_tbl *f) *regp ^= BIT(rcg->mux_sel_bit);
 		regmap_write(rcg->clkr.regmap, bank_reg, *regp);
 	}
+	spin_unlock_irqrestore(&rcg_enable_lock, flags);
 }
 
 static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
@@ -492,6 +498,27 @@ static int clk_dyn_rcg_set_rate_and_parent(struct
 clk_hw *hw, return __clk_dyn_rcg_set_rate(hw, rate);
 }
 
+static int clk_dyn_rcg_enable(struct clk_hw *hw)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rcg_enable_lock, flags);
+	ret = clk_enable_regmap(hw);
+	spin_unlock_irqrestore(&rcg_enable_lock, flags);
+
+	return ret;
+}
+
+static void clk_dyn_rcg_disable(struct clk_hw *hw)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&rcg_enable_lock, flags);
+	clk_disable_regmap(hw);
+	spin_unlock_irqrestore(&rcg_enable_lock, flags);
+}
+
 const struct clk_ops clk_rcg_ops = {
 	.enable = clk_enable_regmap,
 	.disable = clk_disable_regmap,
@@ -504,9 +531,9 @@ const struct clk_ops clk_rcg_ops = {
 EXPORT_SYMBOL_GPL(clk_rcg_ops);
 
 const struct clk_ops clk_dyn_rcg_ops = {
-	.enable = clk_enable_regmap,
+	.enable = clk_dyn_rcg_enable,
 	.is_enabled = clk_is_enabled_regmap,
-	.disable = clk_disable_regmap,
+	.disable = clk_dyn_rcg_disable,
 	.get_parent = clk_dyn_rcg_get_parent,
 	.set_parent = clk_dyn_rcg_set_parent,
 	.recalc_rate = clk_dyn_rcg_recalc_rate,
Signature · 3 lines
Regards,

Laurent Pinchart
#8 lore raw
In reply to Benoit Cousson
Quoted 7 lines · Laurent Pinchart

I'm not familiar with the hardware, but the patch below looks good to me. While you're at it, what would you think about replacing the __clk_is_enabled() call with a direct call to clk_is_enabled_regmap() ? Any user of __clk_is_enabled() outside the CCF core is suspect from a locking point of view, and exporting that function seems like a dangerous idea. On the other hand, if you had done so already I wouldn't have found the race condition.

Hm.. I guess that's ok. I like not having to care about what op is assigned to the .is_enabled op though and we can't easily access the .ops structure because it's hidden by the clk framework.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation