sh: clkfwk: Use shared sh_clk_div_enable/disable().
authorPaul Mundt <lethal@linux-sh.org>
Fri, 25 May 2012 07:34:48 +0000 (16:34 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Fri, 25 May 2012 07:34:48 +0000 (16:34 +0900)
This introduces a new flag for clocks that need to have their divisor
ratio set back to their initial mask at disable time to prevent
interactivity problems with the clock stop bit (presently div6 only).
With this in place it's possible to handle the corner case on top of the
div4 op without any particular need for leaving things split out.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
drivers/sh/clk/cpg.c
include/linux/sh_clk.h

index 29ee5f7072a49b8a18cb4a3fb9ec4208bf8c6d92..06537f2b2fb8a18a5d0d7735ca70f60d4a475af5 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/io.h>
 #include <linux/sh_clk.h>
 
+#define CPG_CKSTP_BIT  BIT(8)
+
 static unsigned int sh_clk_read(struct clk *clk)
 {
        if (clk->flags & CLK_ENABLE_REG_8BIT)
@@ -122,6 +124,30 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
        return 0;
 }
 
+static int sh_clk_div_enable(struct clk *clk)
+{
+       sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
+       return 0;
+}
+
+static void sh_clk_div_disable(struct clk *clk)
+{
+       unsigned int val;
+
+       val = sh_clk_read(clk);
+       val |= CPG_CKSTP_BIT;
+
+       /*
+        * div6 clocks require the divisor field to be non-zero or the
+        * above CKSTP toggle silently fails. Ensure that the divisor
+        * array is reset to its initial state on disable.
+        */
+       if (clk->flags & CLK_MASK_DIV_ON_DISABLE)
+               val |= clk->div_mask;
+
+       sh_clk_write(val, clk);
+}
+
 /*
  * div6 support
  */
@@ -174,44 +200,20 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
        return 0;
 }
 
-static int sh_clk_div6_enable(struct clk *clk)
-{
-       unsigned long value;
-       int ret;
-
-       ret = sh_clk_div_set_rate(clk, clk->rate);
-       if (ret == 0) {
-               value = sh_clk_read(clk);
-               value &= ~0x100; /* clear stop bit to enable clock */
-               sh_clk_write(value, clk);
-       }
-       return ret;
-}
-
-static void sh_clk_div6_disable(struct clk *clk)
-{
-       unsigned long value;
-
-       value = sh_clk_read(clk);
-       value |= 0x100; /* stop clock */
-       value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */
-       sh_clk_write(value, clk);
-}
-
 static struct sh_clk_ops sh_clk_div6_clk_ops = {
        .recalc         = sh_clk_div_recalc,
        .round_rate     = sh_clk_div_round_rate,
        .set_rate       = sh_clk_div_set_rate,
-       .enable         = sh_clk_div6_enable,
-       .disable        = sh_clk_div6_disable,
+       .enable         = sh_clk_div_enable,
+       .disable        = sh_clk_div_disable,
 };
 
 static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = {
        .recalc         = sh_clk_div_recalc,
        .round_rate     = sh_clk_div_round_rate,
        .set_rate       = sh_clk_div_set_rate,
-       .enable         = sh_clk_div6_enable,
-       .disable        = sh_clk_div6_disable,
+       .enable         = sh_clk_div_enable,
+       .disable        = sh_clk_div_disable,
        .set_parent     = sh_clk_div6_set_parent,
 };
 
@@ -325,17 +327,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
        return 0;
 }
 
-static int sh_clk_div4_enable(struct clk *clk)
-{
-       sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk);
-       return 0;
-}
-
-static void sh_clk_div4_disable(struct clk *clk)
-{
-       sh_clk_write(sh_clk_read(clk) | (1 << 8), clk);
-}
-
 static struct sh_clk_ops sh_clk_div4_clk_ops = {
        .recalc         = sh_clk_div_recalc,
        .set_rate       = sh_clk_div_set_rate,
@@ -346,16 +337,16 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = {
        .recalc         = sh_clk_div_recalc,
        .set_rate       = sh_clk_div_set_rate,
        .round_rate     = sh_clk_div_round_rate,
-       .enable         = sh_clk_div4_enable,
-       .disable        = sh_clk_div4_disable,
+       .enable         = sh_clk_div_enable,
+       .disable        = sh_clk_div_disable,
 };
 
 static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
        .recalc         = sh_clk_div_recalc,
        .set_rate       = sh_clk_div_set_rate,
        .round_rate     = sh_clk_div_round_rate,
-       .enable         = sh_clk_div4_enable,
-       .disable        = sh_clk_div4_disable,
+       .enable         = sh_clk_div_enable,
+       .disable        = sh_clk_div_disable,
        .set_parent     = sh_clk_div4_set_parent,
 };
 
index 35a04f19fb538344a1b0461c1d01c8212bdf6a6e..50910913b2687928bf59b4053b92beb21e569918 100644 (file)
@@ -69,6 +69,8 @@ struct clk {
 #define CLK_ENABLE_REG_16BIT   BIT(2)
 #define CLK_ENABLE_REG_8BIT    BIT(3)
 
+#define CLK_MASK_DIV_ON_DISABLE        BIT(4)
+
 #define CLK_ENABLE_REG_MASK    (CLK_ENABLE_REG_32BIT | \
                                 CLK_ENABLE_REG_16BIT | \
                                 CLK_ENABLE_REG_8BIT)
@@ -173,7 +175,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr,
 {                                                              \
        .enable_reg = (void __iomem *)_reg,                     \
        .enable_bit = 0, /* unused */                           \
-       .flags = _flags,                                        \
+       .flags = _flags | CLK_MASK_DIV_ON_DISABLE,              \
        .div_mask = SH_CLK_DIV6_MSK,                            \
        .parent_table = _parents,                               \
        .parent_num = _num_parents,                             \
@@ -187,7 +189,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr,
        .enable_reg     = (void __iomem *)_reg,                 \
        .enable_bit     = 0,    /* unused */                    \
        .div_mask       = SH_CLK_DIV6_MSK,                      \
-       .flags          = _flags,                               \
+       .flags          = _flags | CLK_MASK_DIV_ON_DISABLE,     \
 }
 
 int sh_clk_div6_register(struct clk *clks, int nr);