clk: ingenic: Don't use CLK_SET_RATE_GATE for PLL
[linux-2.6-microblaze.git] / drivers / clk / ingenic / cgu.c
index d7981b6..521a40d 100644 (file)
 #include <linux/clkdev.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/math64.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/time.h>
+
 #include "cgu.h"
 
 #define MHZ (1000 * 1000)
 
+static inline const struct ingenic_cgu_clk_info *
+to_clk_info(struct ingenic_clk *clk)
+{
+       return &clk->cgu->clock_info[clk->idx];
+}
+
 /**
  * ingenic_cgu_gate_get() - get the value of clock gate register bit
  * @cgu: reference to the CGU whose registers should be read
@@ -71,14 +80,13 @@ static unsigned long
 ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        const struct ingenic_cgu_pll_info *pll_info;
        unsigned m, n, od_enc, od;
        bool bypass;
        u32 ctl;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
        BUG_ON(clk_info->type != CGU_CLK_PLL);
        pll_info = &clk_info->pll;
 
@@ -144,18 +152,6 @@ ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
                n * od);
 }
 
-static inline const struct ingenic_cgu_clk_info *to_clk_info(
-               struct ingenic_clk *ingenic_clk)
-{
-       struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
-
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-       BUG_ON(clk_info->type != CGU_CLK_PLL);
-
-       return clk_info;
-}
-
 static long
 ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate,
                       unsigned long *prate)
@@ -166,6 +162,16 @@ ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate,
        return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL);
 }
 
+static inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu,
+                                          const struct ingenic_cgu_pll_info *pll_info)
+{
+       u32 ctl;
+
+       return readl_poll_timeout(cgu->base + pll_info->reg, ctl,
+                                 ctl & BIT(pll_info->stable_bit),
+                                 0, 100 * USEC_PER_MSEC);
+}
+
 static int
 ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
                     unsigned long parent_rate)
@@ -176,6 +182,7 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
        const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
        unsigned long rate, flags;
        unsigned int m, n, od;
+       int ret = 0;
        u32 ctl;
 
        rate = ingenic_pll_calc(clk_info, req_rate, parent_rate,
@@ -197,9 +204,14 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
        ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
 
        writel(ctl, cgu->base + pll_info->reg);
+
+       /* If the PLL is enabled, verify that it's stable */
+       if (ctl & BIT(pll_info->enable_bit))
+               ret = ingenic_pll_check_stable(cgu, pll_info);
+
        spin_unlock_irqrestore(&cgu->lock, flags);
 
-       return 0;
+       return ret;
 }
 
 static int ingenic_pll_enable(struct clk_hw *hw)
@@ -208,9 +220,8 @@ static int ingenic_pll_enable(struct clk_hw *hw)
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
        const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
-       const unsigned int timeout = 100;
        unsigned long flags;
-       unsigned int i;
+       int ret;
        u32 ctl;
 
        spin_lock_irqsave(&cgu->lock, flags);
@@ -226,20 +237,10 @@ static int ingenic_pll_enable(struct clk_hw *hw)
 
        writel(ctl, cgu->base + pll_info->reg);
 
-       /* wait for the PLL to stabilise */
-       for (i = 0; i < timeout; i++) {
-               ctl = readl(cgu->base + pll_info->reg);
-               if (ctl & BIT(pll_info->stable_bit))
-                       break;
-               mdelay(1);
-       }
-
+       ret = ingenic_pll_check_stable(cgu, pll_info);
        spin_unlock_irqrestore(&cgu->lock, flags);
 
-       if (i == timeout)
-               return -EBUSY;
-
-       return 0;
+       return ret;
 }
 
 static void ingenic_pll_disable(struct clk_hw *hw)
@@ -290,13 +291,11 @@ static const struct clk_ops ingenic_pll_ops = {
 static u8 ingenic_clk_get_parent(struct clk_hw *hw)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        u32 reg;
        u8 i, hw_idx, idx = 0;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_MUX) {
                reg = readl(cgu->base + clk_info->mux.reg);
                hw_idx = (reg >> clk_info->mux.shift) &
@@ -318,14 +317,12 @@ static u8 ingenic_clk_get_parent(struct clk_hw *hw)
 static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        unsigned long flags;
        u8 curr_idx, hw_idx, num_poss;
        u32 reg, mask;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_MUX) {
                /*
                 * Convert the parent index to the hardware index by adding
@@ -368,13 +365,11 @@ static unsigned long
 ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        unsigned long rate = parent_rate;
        u32 div_reg, div;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_DIV) {
                div_reg = readl(cgu->base + clk_info->div.reg);
                div = (div_reg >> clk_info->div.shift) &
@@ -443,12 +438,9 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
                       unsigned long *parent_rate)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
-       struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        unsigned int div = 1;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_DIV)
                div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate);
        else if (clk_info->type & CGU_CLK_FIXDIV)
@@ -457,21 +449,28 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
        return DIV_ROUND_UP(*parent_rate, div);
 }
 
+static inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu,
+                                          const struct ingenic_cgu_clk_info *clk_info)
+{
+       u32 reg;
+
+       return readl_poll_timeout(cgu->base + clk_info->div.reg, reg,
+                                 !(reg & BIT(clk_info->div.busy_bit)),
+                                 0, 100 * USEC_PER_MSEC);
+}
+
 static int
 ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
                     unsigned long parent_rate)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
-       const unsigned timeout = 100;
        unsigned long rate, flags;
-       unsigned int hw_div, div, i;
+       unsigned int hw_div, div;
        u32 reg, mask;
        int ret = 0;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_DIV) {
                div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate);
                rate = DIV_ROUND_UP(parent_rate, div);
@@ -504,16 +503,8 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
                writel(reg, cgu->base + clk_info->div.reg);
 
                /* wait for the change to take effect */
-               if (clk_info->div.busy_bit != -1) {
-                       for (i = 0; i < timeout; i++) {
-                               reg = readl(cgu->base + clk_info->div.reg);
-                               if (!(reg & BIT(clk_info->div.busy_bit)))
-                                       break;
-                               mdelay(1);
-                       }
-                       if (i == timeout)
-                               ret = -EBUSY;
-               }
+               if (clk_info->div.busy_bit != -1)
+                       ret = ingenic_clk_check_stable(cgu, clk_info);
 
                spin_unlock_irqrestore(&cgu->lock, flags);
                return ret;
@@ -525,12 +516,10 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
 static int ingenic_clk_enable(struct clk_hw *hw)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        unsigned long flags;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_GATE) {
                /* ungate the clock */
                spin_lock_irqsave(&cgu->lock, flags);
@@ -547,12 +536,10 @@ static int ingenic_clk_enable(struct clk_hw *hw)
 static void ingenic_clk_disable(struct clk_hw *hw)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        unsigned long flags;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_GATE) {
                /* gate the clock */
                spin_lock_irqsave(&cgu->lock, flags);
@@ -564,12 +551,10 @@ static void ingenic_clk_disable(struct clk_hw *hw)
 static int ingenic_clk_is_enabled(struct clk_hw *hw)
 {
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
-       const struct ingenic_cgu_clk_info *clk_info;
        int enabled = 1;
 
-       clk_info = &cgu->clock_info[ingenic_clk->idx];
-
        if (clk_info->type & CGU_CLK_GATE)
                enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate);
 
@@ -683,7 +668,6 @@ static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx)
                }
        } else if (caps & CGU_CLK_PLL) {
                clk_init.ops = &ingenic_pll_ops;
-               clk_init.flags |= CLK_SET_RATE_GATE;
 
                caps &= ~CGU_CLK_PLL;