clk: rockchip: add apll set_rate support
authordkl <dkl@rock-chips.com>
Mon, 20 Jan 2014 13:15:10 +0000 (21:15 +0800)
committerdkl <dkl@rock-chips.com>
Tue, 21 Jan 2014 09:06:22 +0000 (17:06 +0800)
drivers/clk/rockchip/clk-pll.c
drivers/clk/rockchip/clk-pll.h
drivers/clk/rockchip/clk.c

index 0254ac85970fe58f5cc648ae1622b8cd40189e77..8bd252ad43f8c6ca2ea43dcf9d5b56c0e324d2ae 100644 (file)
@@ -5,7 +5,7 @@
 #include "clk-pll.h"
 
 
-static unsigned long lpj_gpll;
+//static unsigned long lpj_gpll;
 
 //fixme
 extern void __iomem *reg_start;
@@ -25,6 +25,67 @@ extern void __iomem *reg_start;
 
 
 static const struct apll_clk_set apll_table[] = {
+       //            (_mhz,    nr,     nf,     no,     _periph_div,    _aclk_div)
+       _APLL_SET_CLKS(2208,    1,      92,     1,      8,      81),
+       _APLL_SET_CLKS(2184,    1,      91,     1,      8,      81),
+       _APLL_SET_CLKS(2160,    1,      90,     1,      8,      81),
+       _APLL_SET_CLKS(2136,    1,      89,     1,      8,      81),
+       _APLL_SET_CLKS(2112,    1,      88,     1,      8,      81),
+       _APLL_SET_CLKS(2088,    1,      87,     1,      8,      81),
+       _APLL_SET_CLKS(2064,    1,      86,     1,      8,      81),
+       _APLL_SET_CLKS(2040,    1,      85,     1,      8,      81),
+       _APLL_SET_CLKS(2016,    1,      84,     1,      8,      81),
+       _APLL_SET_CLKS(1992,    1,      83,     1,      8,      81),
+       _APLL_SET_CLKS(1968,    1,      82,     1,      8,      81),
+       _APLL_SET_CLKS(1944,    1,      81,     1,      8,      81),
+       _APLL_SET_CLKS(1920,    1,      80,     1,      8,      81),
+       _APLL_SET_CLKS(1896,    1,      79,     1,      8,      81),
+       _APLL_SET_CLKS(1872,    1,      78,     1,      8,      81),
+       _APLL_SET_CLKS(1848,    1,      77,     1,      8,      81),
+       _APLL_SET_CLKS(1824,    1,      76,     1,      8,      81),
+       _APLL_SET_CLKS(1800,    1,      75,     1,      8,      81),
+       _APLL_SET_CLKS(1776,    1,      74,     1,      8,      81),
+       _APLL_SET_CLKS(1752,    1,      73,     1,      8,      81),
+       _APLL_SET_CLKS(1728,    1,      72,     1,      8,      81),
+       _APLL_SET_CLKS(1704,    1,      71,     1,      8,      81),
+       _APLL_SET_CLKS(1680,    1,      70,     1,      8,      41),
+       _APLL_SET_CLKS(1656,    1,      69,     1,      8,      41),
+       _APLL_SET_CLKS(1632,    1,      68,     1,      8,      41),
+       _APLL_SET_CLKS(1608,    1,      67,     1,      8,      41),
+       _APLL_SET_CLKS(1560,    1,      65,     1,      8,      41),
+       _APLL_SET_CLKS(1512,    1,      63,     1,      8,      41),
+       _APLL_SET_CLKS(1488,    1,      62,     1,      8,      41),
+       _APLL_SET_CLKS(1464,    1,      61,     1,      8,      41),
+       _APLL_SET_CLKS(1440,    1,      60,     1,      8,      41),
+       _APLL_SET_CLKS(1416,    1,      59,     1,      8,      41),
+       _APLL_SET_CLKS(1392,    1,      58,     1,      8,      41),
+       _APLL_SET_CLKS(1368,    1,      57,     1,      8,      41),
+       _APLL_SET_CLKS(1344,    1,      56,     1,      8,      41),
+       _APLL_SET_CLKS(1320,    1,      55,     1,      8,      41),
+       _APLL_SET_CLKS(1296,    1,      54,     1,      8,      41),
+       _APLL_SET_CLKS(1272,    1,      53,     1,      8,      41),
+       _APLL_SET_CLKS(1248,    1,      52,     1,      8,      41),
+       _APLL_SET_CLKS(1224,    1,      51,     1,      8,      41),
+       _APLL_SET_CLKS(1200,    1,      50,     1,      8,      41),
+       _APLL_SET_CLKS(1176,    1,      49,     1,      8,      41),
+       _APLL_SET_CLKS(1128,    1,      47,     1,      8,      41),
+       _APLL_SET_CLKS(1104,    1,      46,     1,      8,      41),
+       _APLL_SET_CLKS(1008,    1,      84,     2,      8,      41),
+       _APLL_SET_CLKS(912,     1,      76,     2,      8,      41),
+       _APLL_SET_CLKS(888,     1,      74,     2,      8,      41),
+       _APLL_SET_CLKS(816,     1,      68,     2,      8,      41),
+       _APLL_SET_CLKS(792,     1,      66,     2,      8,      41),
+       _APLL_SET_CLKS(696,     1,      58,     2,      8,      41),
+       _APLL_SET_CLKS(600,     1,      50,     2,      4,      41),
+       _APLL_SET_CLKS(552,     1,      92,     4,      4,      41),
+       _APLL_SET_CLKS(504,     1,      84,     4,      4,      41),
+       _APLL_SET_CLKS(408,     1,      68,     4,      4,      21),
+       _APLL_SET_CLKS(312,     1,      52,     4,      2,      21),
+       _APLL_SET_CLKS(252,     1,      84,     8,      2,      21),
+       _APLL_SET_CLKS(216,     1,      72,     8,      2,      21),
+       _APLL_SET_CLKS(126,     1,      84,     16,     2,      11),
+       _APLL_SET_CLKS(48,      1,      32,     16,     2,      11),
+       _APLL_SET_CLKS(0,       1,      32,     16,     2,      11),
 };
 
 static const struct pll_clk_set pll_com_table[] = {
@@ -73,7 +134,7 @@ static void pll_wait_lock(int pll_idx)
 }
 
 
-/*recalc_rate*/
+/* recalc_rate */
 static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
                unsigned long parent_rate)
 {
@@ -101,9 +162,32 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
        return rate;
 }
 
-/*round_rate*/
-/*get rate that is most close to target*/
-static const struct pll_clk_set *pll_clk_get_best_set(unsigned long rate,
+/* round_rate */
+/* get rate that is most close to target */
+static const struct apll_clk_set *apll_get_best_set(unsigned long rate,
+               const struct apll_clk_set *table)
+{
+       const struct apll_clk_set *ps, *pt;
+
+       ps = pt = table;
+       while (pt->rate) {
+               if (pt->rate == rate) {
+                       ps = pt;
+                       break;
+               }
+
+               if ((pt->rate > rate || (rate - pt->rate < ps->rate - rate)))
+                       ps = pt;
+               if (pt->rate < rate)
+                       break;
+               pt++;
+       }
+
+       return ps;
+}
+
+/* get rate that is most close to target */
+static const struct pll_clk_set *pll_com_get_best_set(unsigned long rate,
                const struct pll_clk_set *table)
 {
        const struct pll_clk_set *ps, *pt;
@@ -128,21 +212,28 @@ static const struct pll_clk_set *pll_clk_get_best_set(unsigned long rate,
 static long clk_apll_round_rate(struct clk_hw *hw, unsigned long rate,
                unsigned long *prate)
 {
-       return rate;
+       return (apll_get_best_set(rate, apll_table)->rate);
 }
 
 static long clk_pll_com_round_rate(struct clk_hw *hw, unsigned long rate,
                unsigned long *prate)
 {
-       return (pll_clk_get_best_set(rate, pll_com_table)->rate);
+       return (pll_com_get_best_set(rate, pll_com_table)->rate);
 }
 
 static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
                unsigned long *prate)
 {
        struct clk_pll *pll = to_clk_pll(hw);
+       struct clk *parent = __clk_get_parent(hw->clk);
        long rate_out = 0;
 
+       if (parent && (rate==__clk_get_rate(parent))) {
+               clk_debug("pll id=%d round rate=%lu equal to parent rate\n",
+                               pll->id, rate);
+               return rate;
+       }
+
        switch (pll->id){
                case APLL_ID: {
                                      rate_out = clk_apll_round_rate(hw, rate, prate);
@@ -157,7 +248,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
        return rate_out;
 }
 
-/*set_rate*/
+/* set_rate */
 static int _pll_clk_set_rate(struct pll_clk_set *clk_set, u8 pll_id,
                spinlock_t *lock)
 {
@@ -210,7 +301,7 @@ static int clk_pll_com_set_rate(struct clk_hw *hw, unsigned long rate,
        int ret = 0;
 
 
-       if(rate == parent_rate) {
+       if (rate == parent_rate) {
                clk_debug("pll id=%d set rate=%lu equal to parent rate\n",
                                pll->id, rate);
                cru_writel(PLL_MODE_SLOW(pll->id), CRU_MODE_CON);
@@ -226,7 +317,7 @@ static int clk_pll_com_set_rate(struct clk_hw *hw, unsigned long rate,
                clk_set++;
        }
 
-       if(clk_set->rate == rate) {
+       if (clk_set->rate == rate) {
                ret = _pll_clk_set_rate(clk_set, pll->id, pll->lock);
                clk_debug("pll id=%d set rate=%lu OK!\n", pll->id, rate);
        } else {
@@ -238,9 +329,161 @@ static int clk_pll_com_set_rate(struct clk_hw *hw, unsigned long rate,
        return ret;
 }
 
+/* 1: use
+ * 0: no use
+ */
+#define USE_ARM_GPLL   1
+
 static int clk_apll_set_rate(struct clk_hw *hw, unsigned long rate,
                unsigned long parent_rate)
 {
+       struct clk_pll *pll = to_clk_pll(hw);
+       struct clk *clk = hw->clk;
+       struct clk *arm_gpll = __clk_lookup("clk_arm_gpll");
+       unsigned long arm_gpll_rate;
+       const struct apll_clk_set *ps;
+       u32 old_aclk_div = 0, new_aclk_div = 0;
+       u32 temp_div;
+       unsigned long flags;
+       int sel_gpll = 0;
+
+
+       if (rate == parent_rate) {
+               clk_debug("pll id=%d set rate=%lu equal to parent rate\n",
+                               pll->id, rate);
+               cru_writel(PLL_MODE_SLOW(pll->id), CRU_MODE_CON);
+               cru_writel((0x1 << (16+1)) | (0x1<<1), PLL_CONS(pll->id, 3));
+               clk_debug("pll id=%d enter slow mode, set rate OK!\n", pll->id);
+               return 0;
+       }
+
+#if !USE_ARM_GPLL
+       goto CHANGE_APLL;
+#endif
+
+       /* prepare arm_gpll before reparent clk_core to it */
+       if (!arm_gpll) {
+               clk_err("clk arm_gpll is NULL!\n");
+               goto CHANGE_APLL;
+       }
+
+       if (clk_prepare(arm_gpll)) {
+               clk_err("fail to prepare arm_gpll path\n");
+               clk_unprepare(arm_gpll);
+               goto CHANGE_APLL;
+       }
+
+       if (clk_enable(arm_gpll)) {
+               clk_err("fail to enable arm_gpll path\n");
+               clk_disable(arm_gpll);
+               clk_unprepare(arm_gpll);
+               goto CHANGE_APLL;
+       }
+
+       arm_gpll_rate = __clk_get_rate(arm_gpll);
+       temp_div = DIV_ROUND_UP(arm_gpll_rate, __clk_get_rate(clk));
+       temp_div = (temp_div == 0) ? 1 : temp_div;
+       if (temp_div > CORE_CLK_MAX_DIV) {
+               clk_debug("temp_div %d > max_div %d\n", temp_div,
+                               CORE_CLK_MAX_DIV);
+               clk_debug("can't get rate %lu from arm_gpll rate %lu\n",
+                               __clk_get_rate(clk), arm_gpll_rate);
+               clk_disable(arm_gpll);
+               clk_unprepare(arm_gpll);
+               goto CHANGE_APLL;
+       }
+
+       local_irq_save(flags);
+
+       /* firstly set div, then select arm_gpll path */
+       cru_writel(CORE_CLK_DIV_W_MSK|CORE_CLK_DIV(temp_div), CRU_CLKSELS_CON(0));
+       cru_writel(CORE_SEL_PLL_W_MSK|CORE_SEL_GPLL, CRU_CLKSELS_CON(0));
+
+       sel_gpll = 1;
+       //loops_per_jiffy = lpj_gpll / temp_div;
+       smp_wmb();
+
+       local_irq_restore(flags);
+
+       clk_debug("temp select arm_gpll path, get rate %lu\n",
+                       arm_gpll_rate/temp_div);
+       clk_debug("from arm_gpll rate %lu, temp_div %d\n", arm_gpll_rate,
+                       temp_div);
+
+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,
+                       ps->clksel0, ps->clksel1);
+
+       local_irq_save(flags);
+
+       /* If core src don't select gpll, apll need to enter slow mode
+        * before power down
+        */
+       //if(!sel_gpll)
+       cru_writel(PLL_MODE_SLOW(APLL_ID), CRU_MODE_CON);
+
+       /* PLL power down */
+       cru_writel((0x1<<(16+1))|(0x1<<1), PLL_CONS(pll->id, 3));
+       dsb();
+       dsb();
+       dsb();
+       dsb();
+       dsb();
+       dsb();
+       cru_writel(ps->pllcon0, PLL_CONS(pll->id, 0));
+       cru_writel(ps->pllcon1, PLL_CONS(pll->id, 1));
+
+       rk30_clock_udelay(1);
+
+       /* PLL power up and wait for locked */
+       cru_writel((0x1<<(16+1)), PLL_CONS(pll->id, 3));
+       pll_wait_lock(pll->id);
+
+       old_aclk_div = GET_CORE_ACLK_VAL(cru_readl(CRU_CLKSELS_CON(1)) &
+                       CORE_ACLK_MSK);
+       new_aclk_div = GET_CORE_ACLK_VAL(ps->clksel1 & CORE_ACLK_MSK);
+
+       if (new_aclk_div >= old_aclk_div) {
+               cru_writel(ps->clksel0, CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, CRU_CLKSELS_CON(1));
+       }
+
+       /* PLL return from slow mode */
+       //if (!sel_gpll)
+       cru_writel(PLL_MODE_NORM(APLL_ID), CRU_MODE_CON);
+
+       /* reparent to apll, and set div to 1 */
+       if (sel_gpll) {
+               cru_writel(CORE_SEL_PLL_W_MSK|CORE_SEL_APLL, CRU_CLKSELS_CON(0));
+               cru_writel(CORE_CLK_DIV_W_MSK|CORE_CLK_DIV(1), CRU_CLKSELS_CON(0));
+       }
+
+       if (old_aclk_div > new_aclk_div) {
+               cru_writel(ps->clksel0, CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, CRU_CLKSELS_CON(1));
+       }
+
+       //loops_per_jiffy = ps->lpj;
+       smp_wmb();
+
+       local_irq_restore(flags);
+
+       if (sel_gpll) {
+               sel_gpll = 0;
+               clk_disable(arm_gpll);
+               clk_unprepare(arm_gpll);
+       }
+
+       //clk_debug("apll set loops_per_jiffy =%lu\n", loops_per_jiffy);
+
+       clk_debug("apll set rate %lu, con(%x,%x,%x,%x), sel(%x,%x)\n",
+                       ps->rate,
+                       cru_readl(PLL_CONS(pll->id, 0)),cru_readl(PLL_CONS(pll->id, 1)),
+                       cru_readl(PLL_CONS(pll->id, 2)),cru_readl(PLL_CONS(pll->id, 3)),
+                       cru_readl(CRU_CLKSELS_CON(0)),cru_readl(CRU_CLKSELS_CON(1)));
+
        return 0;
 }
 
@@ -253,12 +496,12 @@ static int clk_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
 static int clk_gpll_set_rate(struct clk_hw *hw, unsigned long rate,
                unsigned long parent_rate)
 {
-        int ret = clk_pll_com_set_rate(hw, rate, parent_rate);
+       int ret = clk_pll_com_set_rate(hw, rate, parent_rate);
 
-        if(!ret)
-                lpj_gpll = CLK_LOOPS_RECALC(clk_pll_recalc_rate(hw, parent_rate));
+       //if(!ret)
+       //      lpj_gpll = CLK_LOOPS_RECALC(clk_pll_recalc_rate(hw, parent_rate));
 
-        return ret;
+       return ret;
 }
 
 static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
index 17fe386364d6a9078ef3382b5f64a1189b0ea048..b49ee7dcda9ca516c3503f815687df9aa0b0823d 100644 (file)
        div_u64(CLK_LOOPS_JIFFY_REF*(rate),CLK_LOOPS_RATE_REF*MHZ)
 /*******************cru reg offset***************************/
 #define CRU_MODE_CON           0x40
+#define CRU_CLKSEL_CON         0x44
+
 #define PLL_CONS(id, i)                ((id) * 0x10 + ((i) * 4))
+#define CRU_CLKSELS_CON(i)     (CRU_CLKSEL_CON + ((i) * 4))
 
 /*******************cru BITS*********************************/
 #define CRU_GET_REG_BITS_VAL(reg,bits_shift, msk)  (((reg) >> (bits_shift))&(msk))
 #define PLL_MODE_NORM(id)      ((0x1<<((id)*4))|(0x3<<(16+(id)*4)))
 #define PLL_MODE_DEEP(id)      ((0x2<<((id)*4))|(0x3<<(16+(id)*4)))
 
+/*******************CLKSEL0 BITS***************************/
+//core_preiph div
+#define CORE_PERIPH_W_MSK      (3 << 22)
+#define CORE_PERIPH_MSK                (3 << 6)
+#define CORE_PERIPH_2          (0 << 6)
+#define CORE_PERIPH_4          (1 << 6)
+#define CORE_PERIPH_8          (2 << 6)
+#define CORE_PERIPH_16         (3 << 6)
+
+//clk_core
+#define CORE_SEL_PLL_MSK       (1 << 8)
+#define CORE_SEL_PLL_W_MSK     (1 << 24)
+#define CORE_SEL_APLL          (0 << 8)
+#define CORE_SEL_GPLL          (1 << 8)
+
+#define CORE_CLK_DIV_W_MSK     (0x1F << 25)
+#define CORE_CLK_DIV_MSK       (0x1F << 9)
+#define CORE_CLK_DIV(i)                ((((i) - 1) & 0x1F) << 9)
+#define CORE_CLK_MAX_DIV       32
+
+/*******************CLKSEL1 BITS***************************/
+//aclk_core div
+#define CORE_ACLK_W_MSK                (7 << 19)
+#define CORE_ACLK_MSK          (7 << 3)
+#define CORE_ACLK_11           (0 << 3)
+#define CORE_ACLK_21           (1 << 3)
+#define CORE_ACLK_31           (2 << 3)
+#define CORE_ACLK_41           (3 << 3)
+#define CORE_ACLK_81           (4 << 3)
+#define GET_CORE_ACLK_VAL(reg) ((reg)>=4 ? 8:((reg)+1))
+
 /*******************PLL SET*********************************/
 #define _PLL_SET_CLKS(_mhz, nr, nf, no) \
 { \
        .pllcon2 = PLL_CLK_BWADJ_SET(nf >> 1),\
        .rst_dly=((nr*500)/24+1),\
 }
+
+#define _APLL_SET_CLKS(_mhz, nr, nf, no, _periph_div, _aclk_div) \
+{ \
+        .rate   = _mhz * MHZ, \
+        .pllcon0 = PLL_CLKR_SET(nr) | PLL_CLKOD_SET(no), \
+        .pllcon1 = PLL_CLKF_SET(nf),\
+        .pllcon2 = PLL_CLK_BWADJ_SET(nf >> 1),\
+        .clksel0 = CORE_PERIPH_W_MSK | CORE_PERIPH_##_periph_div,\
+        .clksel1 = CORE_ACLK_W_MSK | CORE_ACLK_##_aclk_div,\
+        .lpj= (CLK_LOOPS_JIFFY_REF*_mhz) / CLK_LOOPS_RATE_REF,\
+        .rst_dly=((nr*500)/24+1),\
+}
+
+
 /*******************OTHERS*********************************/
 #define rk30_clock_udelay(a) udelay(a)
 
index aa9a4f23b371723e2b38f2df4472e203df9c0ce7..ffa95223b40eda18228e890f0f16803d1794bb87 100755 (executable)
@@ -709,6 +709,26 @@ static int rkclk_register(struct rkclk *rkclk)
        return 0;
 }
 
+#ifdef RKCLK_DEBUG
+void rk_dump_cru(void)
+{
+       u32 i;
+
+       printk("\n");
+       printk("dump cru regs:");
+       for (i = 0; i * 4 <= 0xf4; i++) {
+               if (i % 4 == 0)
+                       printk("\n%s: \t[0x%08x]: ",
+                                       __func__, 0x20000000 + i * 4);
+               printk("%08x ", readl(reg_start + i * 4));
+       }
+       printk("\n\n");
+}
+#else
+void rk_dump_cru(void){}
+#endif
+EXPORT_SYMBOL_GPL(rk_dump_cru);
+
 #ifdef RKCLK_TEST
 struct test_table {
        const char *name;
@@ -748,7 +768,7 @@ void rk_clk_test(void)
        const char *clk_name;
        struct clk *clk;
        unsigned long rate=0, recalc_rate=0, round_rate=0, get_rate=0;
-       u32 i = 0, j = 0;
+       u32 j = 0;
        int ret;
 
        for (j = 0; j < ARRAY_SIZE(t_table); j++) {
@@ -794,26 +814,15 @@ void rk_clk_test(void)
                                        __func__, clk_name, get_rate);
                }
 
-#if 0
-               printk("\n");
-               printk("dump cru regs:");
-               for (i = 0; i * 4 <= 0xf4; i++) {
-                       if (i % 4 == 0)
-                               printk("\n%s: \t[0x%08x]: ",
-                                               __func__, 0x20000000 + i * 4);
-                       printk("%08x ", readl(reg_start + i * 4));
-               }
-               printk("\n\n");
-
-#endif
+               rk_dump_cru();
        }
 
 }
-EXPORT_SYMBOL_GPL(rk_clk_test);
 #else
-void rk_clk_test(void){};
-EXPORT_SYMBOL_GPL(rk_clk_test);
+void rk_clk_test(void){}
 #endif
+EXPORT_SYMBOL_GPL(rk_clk_test);
+
 
 void rkclk_init_clks(struct device_node *node);