rk3188 plus: add clock support
authorchenxing <chenxing@rock-chips.com>
Thu, 2 May 2013 01:16:54 +0000 (09:16 +0800)
committerchenxing <chenxing@rock-chips.com>
Thu, 2 May 2013 01:16:54 +0000 (09:16 +0800)
arch/arm/mach-rk3188/clock_data.c
arch/arm/mach-rk3188/include/mach/cru-rk3188.h

index 7e3ef5ed95504b330db5d6ebd3e0a364050c7cd1..913cbab16a92c082ad9d7c7780aac619bc4c2b5e 100755 (executable)
@@ -27,6 +27,7 @@
 #include <mach/pmu.h>
 #include <mach/dvfs.h>
 #include <mach/ddr.h>
+#include <mach/cpu.h>
 
 #define MHZ                    (1000UL * 1000UL)
 #define KHZ                    (1000UL)
@@ -78,6 +79,7 @@ struct pll_clk_set {
        .rate   = (_mhz) * KHZ, \
        .pllcon0 = PLL_CLKR_SET(nr)|PLL_CLKOD_SET(no), \
        .pllcon1 = PLL_CLKF_SET(nf),\
+       .pllcon2 = PLL_CLK_BWADJ_SET(nf >> 1),\
        .rst_dly=((nr*500)/24+1),\
 }
 
@@ -92,6 +94,7 @@ struct pll_clk_set {
        .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_##_axi_core_div,\
        _APLL_SET_LPJ(_mhz),\
@@ -657,6 +660,31 @@ static unsigned long plls_clk_recalc(struct clk *clk)
 {
        return pll_clk_recalc(clk->pll->id, clk->parent->rate);
 }
+static unsigned long plus_pll_clk_recalc(u32 pll_id, unsigned long parent_rate)
+{
+       unsigned long rate;
+
+       if (PLLS_IN_NORM(pll_id)) {
+               u32 pll_con0 = cru_readl(PLL_CONS(pll_id, 0));
+               u32 pll_con1 = cru_readl(PLL_CONS(pll_id, 1));
+
+               u64 rate64 = (u64)parent_rate * PLUS_PLL_NF(pll_con1);
+
+               do_div(rate64, PLUS_PLL_NR(pll_con0));
+               do_div(rate64, PLUS_PLL_NO(pll_con0));
+
+               rate = rate64;
+       } else {
+               rate = parent_rate;
+               CLKDATA_DBG("pll_clk_recalc id=%d rate=%lu by pass mode\n", pll_id, rate);
+       }
+       return rate;
+}
+static unsigned long plus_plls_clk_recalc(struct clk *clk)
+{
+       DVFS_DBG("%s: for rk3188 plus\n", __func__);
+       return plus_pll_clk_recalc(clk->pll->id, clk->parent->rate);
+}
 
 static int pll_clk_set_rate(struct pll_clk_set *clk_set, u8 pll_id)
 {
@@ -687,6 +715,32 @@ static int pll_clk_set_rate(struct pll_clk_set *clk_set, u8 pll_id)
 
        return 0;
 }
+
+static int plus_pll_clk_set_rate(struct pll_clk_set *clk_set, u8 pll_id)
+{
+       //enter slowmode
+       cru_writel(PLL_MODE_SLOW(pll_id), CRU_MODE_CON);
+
+       //enter rest
+       cru_writel(PLL_RESET_W_MSK | PLL_RESET, PLL_CONS(pll_id, 3));
+       cru_writel(clk_set->pllcon0, PLL_CONS(pll_id, 0));
+       cru_writel(clk_set->pllcon1, PLL_CONS(pll_id, 1));
+       cru_writel(clk_set->pllcon2, PLL_CONS(pll_id, 2));
+       rk30_clock_udelay(5);
+       
+       //return form rest
+       cru_writel(PLL_RESET_W_MSK | PLL_RESET_RESUME, PLL_CONS(pll_id, 3));
+       
+       //wating lock state
+       rk30_clock_udelay(clk_set->rst_dly);
+       pll_wait_lock(pll_id);
+
+       //return form slow
+       cru_writel(PLL_MODE_NORM(pll_id), CRU_MODE_CON);
+       
+       return 0;
+}
+
 static int gpll_clk_set_rate(struct clk *c, unsigned long rate)
 {
        struct _pll_data *pll_data = c->pll;
@@ -708,6 +762,27 @@ static int gpll_clk_set_rate(struct clk *c, unsigned long rate)
        return 0;
 }
 
+static int plus_gpll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       struct _pll_data *pll_data = c->pll;
+       struct pll_clk_set *clk_set = (struct pll_clk_set *)pll_data->table;
+       DVFS_DBG("%s: for rk3188 plus\n", __func__);
+
+       while(clk_set->rate) {
+               if (clk_set->rate == rate) {
+                       break;
+               }
+               clk_set++;
+       }
+       if(clk_set->rate == rate) {
+               plus_pll_clk_set_rate(clk_set, pll_data->id);
+               lpj_gpll = CLK_LOOPS_RECALC(rate);
+       } else {
+               CLKDATA_ERR("gpll is no corresponding rate=%lu\n", rate);
+               return -1;
+       }
+       return 0;
+}
 #define PLL_FREF_MIN (183*KHZ)
 #define PLL_FREF_MAX (1500*MHZ)
 
@@ -825,6 +900,39 @@ static int cpll_clk_set_rate(struct clk *c, unsigned long rate)
        }
        return 0;
 }
+static int plus_cpll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       struct _pll_data *pll_data = c->pll;
+       struct pll_clk_set *clk_set = (struct pll_clk_set *)pll_data->table;
+       struct pll_clk_set temp_clk_set;
+       u32 clk_nr, clk_nf, clk_no;
+       DVFS_DBG("%s: for rk3188 plus\n", __func__);
+
+       while(clk_set->rate) {
+               if (clk_set->rate == rate) {
+                       break;
+               }
+               clk_set++;
+       }
+       if(clk_set->rate == rate) {
+               CLKDATA_DBG("cpll get a rate\n");
+               plus_pll_clk_set_rate(clk_set, pll_data->id);
+
+       } else {
+               CLKDATA_DBG("cpll get auto calc a rate\n");
+               if(pll_clk_get_set(c->parent->rate, rate, &clk_nr, &clk_nf, &clk_no) == 0) {
+                       pr_err("cpll auto set rate error\n");
+                       return -ENOENT;
+               }
+               CLKDATA_DBG("cpll auto ger rate set nr=%d,nf=%d,no=%d\n", clk_nr, clk_nf, clk_no);
+               temp_clk_set.pllcon0 = PLL_CLKR_SET(clk_nr) | PLL_CLKOD_SET(clk_no);
+               temp_clk_set.pllcon1 = PLL_CLKF_SET(clk_nf);
+               temp_clk_set.rst_dly = (clk_nr * 500) / 24 + 1;
+               plus_pll_clk_set_rate(&temp_clk_set, pll_data->id);
+
+       }
+       return 0;
+}
 
 
 /* ******************fixed input clk ***********************************************/
@@ -993,6 +1101,90 @@ static int arm_pll_clk_set_rate(struct clk *clk, unsigned long rate)
        return 0;
 }
 
+static int plus_arm_pll_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long flags;
+       const struct apll_clk_set *ps;
+       u32 pll_id = clk->pll->id;
+       u32 temp_div;
+       u32 old_aclk_div = 0, new_aclk_div;
+       DVFS_DBG("%s: for rk3188 plus\n", __func__);
+
+       ps = arm_pll_clk_get_best_pll_set(rate, (struct apll_clk_set *)clk->pll->table);
+
+       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);
+
+       CLKDATA_LOG("apll will set rate(%lu) tlb con(%x,%x,%x),sel(%x,%x)\n",
+                       ps->rate, ps->pllcon0, ps->pllcon1, ps->pllcon2, ps->clksel0, ps->clksel1);
+
+       if(general_pll_clk.rate > clk->rate) {
+               temp_div = clk_get_freediv(clk->rate, general_pll_clk.rate, 10);
+       } else {
+               temp_div = 1;
+       }
+
+       // ungating cpu gpll path
+       //cru_writel(CLK_GATE_W_MSK(CLK_GATE_CPU_GPLL_PATH) | CLK_UN_GATE(CLK_GATE_CPU_GPLL_PATH), 
+       //      CLK_GATE_CLKID_CONS(CLK_GATE_CPU_GPLL_PATH));
+
+       local_irq_save(flags);
+       //div arm clk for gpll
+
+       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));
+
+       loops_per_jiffy = lpj_gpll / temp_div;
+       smp_wmb();
+
+       /*if core src don't select gpll ,apll neet to enter slow mode */
+       //cru_writel(PLL_MODE_SLOW(APLL_ID), CRU_MODE_CON);
+       
+       //enter rest
+       cru_writel(PLL_RESET_W_MSK | PLL_RESET, PLL_CONS(pll_id, 3));
+       cru_writel(ps->pllcon0, PLL_CONS(pll_id, 0));
+       cru_writel(ps->pllcon1, PLL_CONS(pll_id, 1));
+       cru_writel(ps->pllcon2, PLL_CONS(pll_id, 2));
+       rk30_clock_udelay(5);
+       
+       //return form rest
+       cru_writel(PLL_RESET_W_MSK | PLL_RESET_RESUME, PLL_CONS(pll_id, 3));
+       
+       //wating lock state
+       rk30_clock_udelay(ps->rst_dly);
+       pll_wait_lock(pll_id);
+
+       if(new_aclk_div>=old_aclk_div) {
+               cru_writel(ps->clksel0, CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, CRU_CLKSELS_CON(1));
+       }
+
+       cru_writel(CORE_SEL_PLL_W_MSK | CORE_SEL_APLL, 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));
+       }
+
+       cru_writel(CORE_CLK_DIV_W_MSK|CORE_CLK_DIV(1), CRU_CLKSELS_CON(0));
+
+       loops_per_jiffy = ps->lpj;
+       smp_wmb();
+
+       //CLKDATA_DBG("apll set loops_per_jiffy =%lu,rate(%lu)\n",loops_per_jiffy,ps->rate);
+
+       local_irq_restore(flags);
+
+       //gate gpll path
+       // FIXME
+       //cru_writel(CLK_GATE_W_MSK(CLK_GATE_CPU_GPLL_PATH) | CLK_GATE(CLK_GATE_CPU_GPLL_PATH)
+       //              , CLK_GATE_CLKID_CONS(CLK_GATE_CPU_GPLL_PATH));
+
+       CLKDATA_LOG("apll set over con(%x,%x,%x,%x),sel(%x,%x)\n", 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;
+}
 
 /************************************pll clocks***************************/
 
@@ -3223,11 +3415,20 @@ static struct clk def_ops_clk = {
 struct clk_dump_ops dump_ops;
 #endif
 void rk_dump_clock_info(void);
-
 void __init _rk30_clock_data_init(unsigned long gpll, unsigned long cpll, int flags)
 {
        struct clk_lookup *lk;
 
+       if (soc_is_rk3188plus()) {
+               arm_pll_clk.recalc = plus_plls_clk_recalc;
+               ddr_pll_clk.recalc = plus_plls_clk_recalc;
+               codec_pll_clk.recalc = plus_plls_clk_recalc;
+               general_pll_clk.recalc = plus_plls_clk_recalc;
+
+               arm_pll_clk.set_rate = plus_arm_pll_clk_set_rate;
+               codec_pll_clk.set_rate = plus_cpll_clk_set_rate;
+               general_pll_clk.set_rate = plus_gpll_clk_set_rate;
+       }
        clk_register_dump_ops(&dump_ops);
        clk_register_default_ops_clk(&def_ops_clk);
        rk30_clock_flags = flags;
@@ -3272,7 +3473,8 @@ extern int rk3188_dvfs_init(void);
 
 void __init rk30_clock_data_init(unsigned long gpll, unsigned long cpll, u32 flags)
 {
-       printk("clock: gpll %lu cpll %lu flags 0x%x con2 0x%x/0x%x\n", gpll, cpll, flags, cru_readl(PLL_CONS(DPLL_ID, 2)), cru_readl(PLL_CONS(CPLL_ID, 2)));
+       DVFS_DBG("clock: gpll %lu cpll %lu flags 0x%x con2 0x%x/0x%x\n", 
+                       gpll, cpll, flags, cru_readl(PLL_CONS(DPLL_ID, 2)), cru_readl(PLL_CONS(CPLL_ID, 2)));
        _rk30_clock_data_init(gpll, cpll, flags);
        rk3188_dvfs_init();
 }
index 291190c1f130edaf1609694974ee0959419fead8..56172d0906e0a018326b5376c0c89529c0422815 100755 (executable)
@@ -61,29 +61,37 @@ enum rk_plls_id {
 
 #define PLL_CLKR_SET(val)      (PLL_CLKR(val) | CRU_W_MSK(PLL_NR_SHIFT, PLL_NR_MSK))
 
+#define PLUS_PLL_OD_MSK                (0xf)
+#define PLUS_PLL_NO(reg)       (PLL_NO(reg) & PLUS_PLL_OD_MSK)
+
+#define PLUS_PLL_NR_MSK                (0x3f)
+#define PLUS_PLL_NR(reg)       (PLL_NO(reg) & PLUS_PLL_NR_MSK)
+
+#define PLUS_PLL_CLKR_SET(val) PLL_CLKR_SET(val & PLUS_PLL_NR_MSK)
+#define PLUS_PLL_CLKOD_SET(val)        PLL_CLKOD_SET(val & PLUS_PLL_OD_MSK)
 /*******************PLL CON1 BITS***************************/
 
 #define PLL_NF_MSK             (0xffff)
 #define PLL_NF_SHIFT           (0)
 #define PLL_CLKF(val)          PLL_CLKFACTOR_SET(val, PLL_NF_SHIFT, PLL_NF_MSK)
 #define PLL_NF(reg)            PLL_CLKFACTOR_GET(reg, PLL_NF_SHIFT, PLL_NF_MSK)
-
 #define PLL_CLKF_SET(val)      (PLL_CLKF(val) | CRU_W_MSK(PLL_NF_SHIFT, PLL_NF_MSK))
 
+#define PLUS_PLL_NF_MSK                (0x1ff)
+#define PLUS_PLL_NF(reg)       (PLL_NF(reg) & PLUS_PLL_NF_MSK)
+#define PLUS_PLL_CLKF_SET(val) PLL_CLKF_SET(val & PLUS_PLL_NF_MSK)
 /*******************PLL CON2 BITS***************************/
-#if 0
+
 #define PLL_BWADJ_MSK          (0xfff)
 #define PLL_BWADJ_SHIFT                (0)
 #define PLL_CLK_BWADJ_SET(val) ((val) | CRU_W_MSK(PLL_BWADJ_SHIFT, PLL_BWADJ_MSK))
-#endif
+
 /*******************PLL CON3 BITS***************************/
 
-#if 0
-#define PLL_REST_MSK           (1 << 5)
-#define PLL_REST_W_MSK         (PLL_REST_MSK << 16)
-#define PLL_REST               (1 << 5)
-#define PLL_REST_RESM          (0 << 5)
-#endif
+#define PLL_RESET_MSK          (1 << 5)
+#define PLL_RESET_W_MSK                (PLL_RESET_MSK << 16)
+#define PLL_RESET              (1 << 5)
+#define PLL_RESET_RESUME       (0 << 5)
 
 #define PLL_BYPASS_MSK         (1 << 0)
 #define PLL_BYPASS             (1 << 0)