clk: rockchip: add clk_core ops support
authordkl <dkl@rock-chips.com>
Tue, 21 Jan 2014 08:55:26 +0000 (16:55 +0800)
committerdkl <dkl@rock-chips.com>
Tue, 21 Jan 2014 09:06:22 +0000 (17:06 +0800)
arch/arm/boot/dts/rk3188-clocks.dtsi
drivers/clk/rockchip/clk-ops.c
drivers/clk/rockchip/clk-pll.c
drivers/clk/rockchip/clkops-dtsi.h

index 0097acce16f77d9f69cf43a1706d79e3bcdabaf2..807503d41ae79cb309508dbb8222193bc6e3c67c 100755 (executable)
                                                rockchip,bits = <5 1>;
                                                clocks = <&clk_apll>, <&clk_gpll>;
                                                clock-output-names = "aclk_cpu";
-                                               #clock-cells = <0>;     
+                                               #clock-cells = <0>;
                                                #clock-init-cells = <1>;
                                        };
 
                                                clocks = <&clk_core>;
                                                clock-output-names = "clk_core_peri";
                                                rockchip,div-type = <CLK_DIVIDER_USER_DEFINE>;
+                                               rockchip,clkops-idx = <CLKOPS_RATE_CORE_PERI>;
                                                #clock-cells = <0>;
                                                rockchip,div-relations = <0x0 2
                                                                 0x1 4
                                                clocks = <&clk_apll>,
                                                       <&clk_gates0 1>;
                                                clock-output-names = "clk_core";
+                                               rockchip,flags = <(CLK_GET_RATE_NOCACHE |
+                                                                       CLK_SET_RATE_NO_REPARENT)>;
                                                #clock-cells = <0>;
                                        };
 
                                                rockchip,bits = <9 5>;
                                                clocks = <&clk_core>;
                                                rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx = <CLKOPS_RATE_CORE>;
                                        };
 
                                        /* reg[15:14]: reserved */
                                                clock-output-names = "aclk_core";
                                                #clock-cells = <0>;
                                                rockchip,div-type = <CLK_DIVIDER_USER_DEFINE>;
+                                               rockchip,clkops-idx = <CLKOPS_RATE_CORE_PERI>;
                                                rockchip,div-relations = <0x0 1
                                                                 0x1 2
                                                                 0x2 3
                                                rockchip,bits = <15 1>;
                                                clocks = <&clk_cpll>, <&clk_gpll>;
                                                clock-output-names = "aclk_peri_mux";
-                                               #clock-cells = <0>;                     
+                                               #clock-cells = <0>;
                                                #clock-init-cells = <1>;
                                        };
                                };
index 8527f1aa61303dc2f12fca8d8e936ecbaa87e597..e895f55fda7c2a430441b409a8c62e70670c9b67 100644 (file)
@@ -310,12 +310,81 @@ const struct clk_ops clkops_rate_i2s_frac = {
        .set_rate       = clk_fracdiv_set_rate,
 };
 
+static unsigned long clk_core_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       /* As parent rate could be changed in clk_core.set_rate
+        * ops, the passing_in parent_rate may not be the newest
+        * and we should use the parent->rate instead. As a side
+        * effect, we should NOT directly set clk_core's parent
+        * (apll) rate, otherwise we will get a wrong recalc rate
+        * with clk_core_recalc_rate.
+        */
+       struct clk *parent = __clk_get_parent(hw->clk);
+
+       return clk_divider_recalc_rate(hw, __clk_get_rate(parent));
+}
+
+static long clk_core_determine_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *best_parent_rate,
+               struct clk **best_parent_p)
+{
+       struct clk *parent = __clk_get_parent(hw->clk);
+
+       if (IS_ERR_OR_NULL(parent)) {
+               clk_err("fail to get parent!\n");
+               return 0;
+       }
+
+       return clk_round_rate(parent, rate);
+}
+
+static long clk_core_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return clk_core_determine_rate(hw, rate, prate, NULL);
+}
+
+static int clk_core_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct clk *parent = __clk_get_parent(hw->clk);
+       struct clk *grand_p = __clk_get_parent(parent);
+       int ret;
+
+       if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) {
+               clk_err("fail to get parent or grand_parent!\n");
+               return -EINVAL;
+       }
+
+       ret = parent->ops->set_rate(parent->hw, rate, __clk_get_rate(grand_p));
+       parent->rate = parent->ops->recalc_rate(parent->hw,
+                       __clk_get_rate(grand_p));
+
+       return ret;
+}
+
+const struct clk_ops clkops_rate_core = {
+       .recalc_rate    = clk_core_recalc_rate,
+       .round_rate     = clk_core_round_rate,
+       .set_rate       = clk_core_set_rate,
+       .determine_rate = clk_core_determine_rate,
+};
+
+const struct clk_ops clkops_rate_core_peri = {
+       .recalc_rate    = clk_divider_recalc_rate,
+       .round_rate     = clk_divider_round_rate,
+       .set_rate       = NULL,
+};
+
 struct clk_ops_table rk_clkops_rate_table[] = {
        {.index = CLKOPS_RATE_MUX_DIV,          .clk_ops = &clkops_rate_auto_parent},
        {.index = CLKOPS_RATE_EVENDIV,          .clk_ops = &clkops_rate_evendiv},
        {.index = CLKOPS_RATE_DCLK_LCDC,        .clk_ops = &clkops_rate_dclk_lcdc},
        {.index = CLKOPS_RATE_I2S_FRAC,         .clk_ops = &clkops_rate_i2s_frac},
        {.index = CLKOPS_RATE_FRAC,             .clk_ops = &clkops_rate_frac},
+       {.index = CLKOPS_RATE_CORE,             .clk_ops = &clkops_rate_core},
+       {.index = CLKOPS_RATE_CORE_PERI,        .clk_ops = &clkops_rate_core_peri},
        {.index = CLKOPS_RATE_I2S,              .clk_ops = NULL},
        {.index = CLKOPS_RATE_CIFOUT,           .clk_ops = NULL},
        {.index = CLKOPS_RATE_UART,             .clk_ops = NULL},
index 8bd252ad43f8c6ca2ea43dcf9d5b56c0e324d2ae..eafc7cec0d28b6b3edf3731561628d5a13f3b047 100644 (file)
@@ -154,7 +154,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
                rate = rate64;
        } else {
                rate = parent_rate;
-               clk_debug("pll id=%d  by pass mode\n", pll_id);
+               clk_debug("pll id=%d  slow mode\n", pll_id);
        }
 
        clk_debug("pll id=%d, recalc rate =%lu\n", pll->id, rate);
@@ -284,8 +284,10 @@ static int _pll_clk_set_rate(struct pll_clk_set *clk_set, u8 pll_id,
        if (lock)
                spin_unlock_irqrestore(lock, flags);
 
-       clk_debug("pll id=%d, dump reg:\n con0=%x,con1=%x,mode=%x\n", pll_id,
-                       cru_readl(PLL_CONS(pll_id,0)),(PLL_CONS(pll_id,1)),
+       clk_debug("pll id=%d, dump reg: con0=0x%08x, con1=0x%08x, mode=0x%08x\n",
+                       pll_id,
+                       cru_readl(PLL_CONS(pll_id,0)),
+                       cru_readl(PLL_CONS(pll_id,1)),
                        cru_readl(CRU_MODE_CON));
 
        clk_debug("_pll_clk_set_rate end!\n");
@@ -412,8 +414,9 @@ static int clk_apll_set_rate(struct clk_hw *hw, unsigned long rate,
 
 CHANGE_APLL:
        ps = apll_get_best_set(rate, apll_table);
-       clk_debug("apll will set rate(%lu) table con(%x,%x,%x),sel(%x,%x)\n",
-                       ps->rate, ps->pllcon0, ps->pllcon1, ps->pllcon2,
+       clk_debug("apll will set rate %lu\n", ps->rate);
+       clk_debug("table con:%08x,%08x,%08x, sel:%08x,%08x\n",
+                       ps->pllcon0, ps->pllcon1, ps->pllcon2,
                        ps->clksel0, ps->clksel1);
 
        local_irq_save(flags);
@@ -421,8 +424,9 @@ CHANGE_APLL:
        /* If core src don't select gpll, apll need to enter slow mode
         * before power down
         */
+       //FIXME
        //if(!sel_gpll)
-       cru_writel(PLL_MODE_SLOW(APLL_ID), CRU_MODE_CON);
+       cru_writel(PLL_MODE_SLOW(pll->id), CRU_MODE_CON);
 
        /* PLL power down */
        cru_writel((0x1<<(16+1))|(0x1<<1), PLL_CONS(pll->id, 3));
@@ -451,8 +455,9 @@ CHANGE_APLL:
        }
 
        /* PLL return from slow mode */
-       //if (!sel_gpll)
-       cru_writel(PLL_MODE_NORM(APLL_ID), CRU_MODE_CON);
+       //FIXME
+       //if(!sel_gpll)
+       cru_writel(PLL_MODE_NORM(pll->id), CRU_MODE_CON);
 
        /* reparent to apll, and set div to 1 */
        if (sel_gpll) {
index 578e5bd8189631b70895c2f984b81c0f4ccf234e..07af92b375703a9e6e1123e8136586e009781bea 100644 (file)
@@ -20,6 +20,8 @@
 #define CLKOPS_RATE_UART               7
 #define CLKOPS_RATE_HSADC              8
 #define CLKOPS_RATE_MAC_REF            9
+#define CLKOPS_RATE_CORE               10
+#define CLKOPS_RATE_CORE_PERI          11
 #define CLKOPS_TABLE_END               (~0)