| From dd927617ab0fb585bb466cf7660db0bf3f89c522 Mon Sep 17 00:00:00 2001 |
| From: Guennadi Liakhovetski <g.liakhovetski@gmx.de> |
| Date: Thu, 23 May 2013 00:10:00 +0200 |
| Subject: ARM: shmobile: sh73a0: div4 clocks must check the kick bit before |
| changing rate |
| |
| 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> |
| (cherry picked from commit 413bfd0e67894c930242482cd15ac09a800e2ab8) |
| 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 --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c |
| index d05cf903..d9fd0336 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) |
| -- |
| 1.8.4.3.gca3854a |
| |