clk: rockchip: add apll set_rate support

This commit is contained in:
dkl
2014-01-20 21:15:10 +08:00
parent c278ce337a
commit a713b86ca3
3 changed files with 330 additions and 30 deletions

View 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,

View File

@@ -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)

View File

@@ -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);