diff mbox

[27/27] ARM: shmobile: sh73a0: div4 clocks must check the kick bit before changing rate

Message ID 1370591517-20239-28-git-send-email-horms+renesas@verge.net.au (mailing list archive)
State Accepted
Commit 413bfd0e67894c930242482cd15ac09a800e2ab8
Headers show

Commit Message

Simon Horman June 7, 2013, 7:51 a.m. UTC
From: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

According to the datasheet, it is not allowed to change div4 clock rates
if an earlier rate change operation is still in progress, as indicated by
a set kick bit.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski+renesas@gmail.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/clock-sh73a0.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index d05cf90..d9fd033 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -257,7 +257,7 @@  static struct clk twd_clk = {
 	.ops = &twd_clk_ops,
 };
 
-static struct sh_clk_ops zclk_ops;
+static struct sh_clk_ops zclk_ops, kicker_ops;
 static const struct sh_clk_ops *div4_clk_ops;
 
 static int zclk_set_rate(struct clk *clk, unsigned long rate)
@@ -324,18 +324,32 @@  static unsigned long zclk_recalc(struct clk *clk)
 	return clk_get_rate(clk->parent);
 }
 
-static void zclk_extend(void)
+static int kicker_set_rate(struct clk *clk, unsigned long rate)
 {
-	div4_clk_ops = div4_clks[DIV4_Z].ops;
+	if (__raw_readl(FRQCRB) & (1 << 31))
+		return -EBUSY;
+
+	return div4_clk_ops->set_rate(clk, rate);
+}
+
+static void div4_clk_extend(void)
+{
+	int i;
+
+	div4_clk_ops = div4_clks[0].ops;
 
+	/* Add a kicker-busy check before changing the rate */
+	kicker_ops = *div4_clk_ops;
 	/* We extend the DIV4 clock with a 1:1 pass-through case */
 	zclk_ops = *div4_clk_ops;
 
+	kicker_ops.set_rate = kicker_set_rate;
 	zclk_ops.set_rate = zclk_set_rate;
 	zclk_ops.round_rate = zclk_round_rate;
 	zclk_ops.recalc = zclk_recalc;
 
-	div4_clks[DIV4_Z].ops = &zclk_ops;
+	for (i = 0; i < DIV4_NR; i++)
+		div4_clks[i].ops = i == DIV4_Z ? &zclk_ops : &kicker_ops;
 }
 
 enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_ZB1,
@@ -697,7 +711,7 @@  void __init sh73a0_clock_init(void)
 	if (!ret) {
 		ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
 		if (!ret)
-			zclk_extend();
+			div4_clk_extend();
 	}
 
 	if (!ret)