From a713b86ca3b50354f475bb79f9fd293ef03a1465 Mon Sep 17 00:00:00 2001 From: dkl Date: Mon, 20 Jan 2014 21:15:10 +0800 Subject: [PATCH] clk: rockchip: add apll set_rate support --- drivers/clk/rockchip/clk-pll.c | 271 +++++++++++++++++++++++++++++++-- drivers/clk/rockchip/clk-pll.h | 48 ++++++ drivers/clk/rockchip/clk.c | 41 +++-- 3 files changed, 330 insertions(+), 30 deletions(-) diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 0254ac85970f..8bd252ad43f8 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -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, diff --git a/drivers/clk/rockchip/clk-pll.h b/drivers/clk/rockchip/clk-pll.h index 17fe386364d6..b49ee7dcda9c 100644 --- a/drivers/clk/rockchip/clk-pll.h +++ b/drivers/clk/rockchip/clk-pll.h @@ -11,7 +11,10 @@ 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)) @@ -94,6 +97,37 @@ #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) \ { \ @@ -103,6 +137,20 @@ .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) diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index aa9a4f23b371..ffa95223b40e 100755 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -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); -- 2.34.1