rk3188 plus: add clock support

This commit is contained in:
chenxing
2013-05-02 09:16:54 +08:00
parent 913cbaf8c2
commit 47f58b4895
2 changed files with 221 additions and 11 deletions

View File

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

View File

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