rockchip: clk: rk3368: support more clks to set and get rate

Make clock ids consistent with kernel.
Support more clks to set and get rate.
Add clk dump.

Change-Id: I348c98ce81ce76af9c492a30480fcb495da7ed79
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
This commit is contained in:
Elaine Zhang 2018-08-03 17:40:53 +08:00 committed by Kever Yang
parent 3ee2bc3c66
commit 7150785e44
3 changed files with 662 additions and 43 deletions

View File

@ -20,6 +20,12 @@ enum rk3368_pll_id {
PLL_COUNT,
};
struct rk3368_clk_info {
unsigned long id;
char *name;
bool is_cru;
};
struct rk3368_cru {
struct rk3368_pll {
unsigned int con0;
@ -79,6 +85,43 @@ enum {
PLL_RESET = 1,
PLL_RESET_MASK = GENMASK(5, 5),
/* CLKSEL1CON */
CORE_ACLK_DIV_SHIFT = 0,
CORE_ACLK_DIV_MASK = 0x1f << CORE_ACLK_DIV_SHIFT,
CORE_DBG_DIV_SHIFT = 8,
CORE_DBG_DIV_MASK = 0x1f << CORE_DBG_DIV_SHIFT,
CORE_CLK_PLL_SEL_SHIFT = 7,
CORE_CLK_PLL_SEL_MASK = 1 << CORE_CLK_PLL_SEL_SHIFT,
CORE_CLK_PLL_SEL_APLL = 0,
CORE_CLK_PLL_SEL_GPLL,
CORE_DIV_CON_SHIFT = 0,
CORE_DIV_CON_MASK = 0x1f << CORE_DIV_CON_SHIFT,
/* CLKSEL8CON */
PCLK_BUS_DIV_CON_SHIFT = 12,
PCLK_BUS_DIV_CON_MASK = 0x7 << PCLK_BUS_DIV_CON_SHIFT,
HCLK_BUS_DIV_CON_SHIFT = 8,
HCLK_BUS_DIV_CON_MASK = 0x3 << HCLK_BUS_DIV_CON_SHIFT,
CLK_BUS_PLL_SEL_CPLL = 0,
CLK_BUS_PLL_SEL_GPLL = 1,
CLK_BUS_PLL_SEL_SHIFT = 7,
CLK_BUS_PLL_SEL_MASK = 1 << CLK_BUS_PLL_SEL_SHIFT,
ACLK_BUS_DIV_CON_SHIFT = 0,
ACLK_BUS_DIV_CON_MASK = 0x1f << ACLK_BUS_DIV_CON_SHIFT,
/* CLKSEL9CON */
PCLK_PERI_DIV_CON_SHIFT = 12,
PCLK_PERI_DIV_CON_MASK = 0x3 << PCLK_PERI_DIV_CON_SHIFT,
HCLK_PERI_DIV_CON_SHIFT = 8,
HCLK_PERI_DIV_CON_MASK = 3 << HCLK_PERI_DIV_CON_SHIFT,
CLK_PERI_PLL_SEL_CPLL = 0,
CLK_PERI_PLL_SEL_GPLL,
CLK_PERI_PLL_SEL_SHIFT = 7,
CLK_PERI_PLL_SEL_MASK = 1 << CLK_PERI_PLL_SEL_SHIFT,
ACLK_PERI_DIV_CON_SHIFT = 0,
ACLK_PERI_DIV_CON_MASK = 0x1f,
/* CLKSEL12_CON */
MCU_STCLK_DIV_SHIFT = 8,
MCU_STCLK_DIV_MASK = GENMASK(10, 8),
@ -89,6 +132,23 @@ enum {
MCU_CLK_DIV_SHIFT = 0,
MCU_CLK_DIV_MASK = GENMASK(4, 0),
/* CLKSEL19_CON */
ACLK_VOP_PLL_SEL_SHIFT = 6,
ACLK_VOP_PLL_SEL_MASK = GENMASK(7, 6),
ACLK_VOP_PLL_SEL_CPLL = 0,
ACLK_VOP_PLL_SEL_GPLL = 1,
ACLK_VOP_DIV_SHIFT = 0,
ACLK_VOP_DIV_MASK = GENMASK(4, 0),
/* CLKSEL20_CON */
DCLK_VOP_PLL_SEL_SHIFT = 8,
DCLK_VOP_PLL_SEL_MASK = GENMASK(9, 8),
DCLK_VOP_PLL_SEL_CPLL = 0,
DCLK_VOP_PLL_SEL_GPLL = 1,
DCLK_VOP_PLL_SEL_NPLL = 2,
DCLK_VOP_DIV_SHIFT = 0,
DCLK_VOP_DIV_MASK = GENMASK(7, 0),
/* CLKSEL_CON25 */
CLK_SARADC_DIV_CON_SHIFT = 8,
CLK_SARADC_DIV_CON_MASK = GENMASK(15, 8),

View File

@ -42,6 +42,48 @@ struct pll_div {
#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
#if !defined(CONFIG_SPL_BUILD)
#define RK3368_CLK_DUMP(_id, _name, _iscru) \
{ \
.id = _id, \
.name = _name, \
.is_cru = _iscru, \
}
static const struct rk3368_clk_info clks_dump[] = {
RK3368_CLK_DUMP(PLL_APLLB, "apllb", true),
RK3368_CLK_DUMP(PLL_APLLL, "aplll", true),
RK3368_CLK_DUMP(PLL_DPLL, "dpll", true),
RK3368_CLK_DUMP(PLL_CPLL, "cpll", true),
RK3368_CLK_DUMP(PLL_GPLL, "gpll", true),
RK3368_CLK_DUMP(PLL_NPLL, "npll", true),
RK3368_CLK_DUMP(ARMCLKB, "armclkb", true),
RK3368_CLK_DUMP(ARMCLKL, "armclkl", true),
RK3368_CLK_DUMP(ACLK_BUS, "aclk_bus", true),
RK3368_CLK_DUMP(HCLK_BUS, "hclk_bus", true),
RK3368_CLK_DUMP(PCLK_BUS, "pclk_Bus", true),
RK3368_CLK_DUMP(ACLK_PERI, "aclk_peri", true),
RK3368_CLK_DUMP(HCLK_PERI, "hclk_peri", true),
RK3368_CLK_DUMP(PCLK_PERI, "pclk_peri", true),
};
#endif
#define RK3368_CPUCLK_RATE(_rate, _aclk_div, _pclk_div) \
{ \
.rate = _rate##U, \
.aclk_div = _aclk_div, \
.pclk_div = _pclk_div, \
}
static struct rockchip_cpu_rate_table rk3368_cpu_rates[] = {
#if !defined(CONFIG_SPL_BUILD)
RK3368_CPUCLK_RATE(1200000000, 1, 5),
RK3368_CPUCLK_RATE(1008000000, 1, 5),
#endif
RK3368_CPUCLK_RATE(816000000, 1, 3),
RK3368_CPUCLK_RATE(600000000, 1, 3),
};
#define PLL_DIVISORS(hz, _nr, _no) { \
.nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no}; \
_Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
@ -59,6 +101,83 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6);
static ulong rk3368_clk_get_rate(struct clk *clk);
#define VCO_MAX_KHZ 2200000
#define VCO_MIN_KHZ 440000
#define FREF_MAX_KHZ 2200000
#define FREF_MIN_KHZ 269
#define PLL_LIMIT_FREQ 400000000
static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div)
{
uint ref_khz = OSC_HZ / 1000, nr, nf = 0;
uint fref_khz;
uint diff_khz, best_diff_khz;
const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4;
uint vco_khz;
uint no = 1;
uint freq_khz = freq_hz / 1000;
if (!freq_hz) {
printf("%s: the frequency can not be 0 Hz\n", __func__);
return -EINVAL;
}
no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz);
if (ext_div) {
*ext_div = DIV_ROUND_UP(PLL_LIMIT_FREQ, freq_hz);
no = DIV_ROUND_UP(no, *ext_div);
}
/* only even divisors (and 1) are supported */
if (no > 1)
no = DIV_ROUND_UP(no, 2) * 2;
vco_khz = freq_khz * no;
if (ext_div)
vco_khz *= *ext_div;
if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) {
printf("%s: Cannot find out VCO for Frequency (%luHz).\n",
__func__, freq_hz);
return -1;
}
div->no = no;
best_diff_khz = vco_khz;
for (nr = 1; nr < max_nr && best_diff_khz; nr++) {
fref_khz = ref_khz / nr;
if (fref_khz < FREF_MIN_KHZ)
break;
if (fref_khz > FREF_MAX_KHZ)
continue;
nf = vco_khz / fref_khz;
if (nf >= max_nf)
continue;
diff_khz = vco_khz - nf * fref_khz;
if (nf + 1 < max_nf && diff_khz > fref_khz / 2) {
nf++;
diff_khz = fref_khz - diff_khz;
}
if (diff_khz >= best_diff_khz)
continue;
best_diff_khz = diff_khz;
div->nr = nr;
div->nf = nf;
}
if (best_diff_khz > 4 * 1000) {
printf("%s:Fail to match output freq %lu,best_is %u Hz\n",
__func__, freq_hz, best_diff_khz * 1000);
return -EINVAL;
}
return 0;
}
/* Get pll rate by id */
static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru,
enum rk3368_pll_id pll_id)
@ -86,7 +205,6 @@ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru,
}
}
#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id,
const struct pll_div *div)
{
@ -116,6 +234,7 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id,
/* return from reset */
rk_clrreg(&pll->con3, PLL_RESET_MASK);
rk_clrreg(&pll->con3, 0xf << 0);
/* waiting for pll lock */
while (!(readl(&pll->con1) & PLL_LOCK_STA))
@ -126,34 +245,6 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id,
return 0;
}
#endif
#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
static void rkclk_init(struct rk3368_cru *cru)
{
u32 apllb, aplll, dpll, cpll, gpll;
rkclk_set_pll(cru, APLLB, &apll_b_init_cfg);
rkclk_set_pll(cru, APLLL, &apll_l_init_cfg);
#if !defined(CONFIG_TPL_BUILD)
/*
* If we plan to return to the boot ROM, we can't increase the
* GPLL rate from the SPL stage.
*/
rkclk_set_pll(cru, GPLL, &gpll_init_cfg);
rkclk_set_pll(cru, CPLL, &cpll_init_cfg);
#endif
apllb = rkclk_pll_get_rate(cru, APLLB);
aplll = rkclk_pll_get_rate(cru, APLLL);
dpll = rkclk_pll_get_rate(cru, DPLL);
cpll = rkclk_pll_get_rate(cru, CPLL);
gpll = rkclk_pll_get_rate(cru, GPLL);
debug("%s apllb(%d) apll(%d) dpll(%d) cpll(%d) gpll(%d)\n",
__func__, apllb, aplll, dpll, cpll, gpll);
}
#endif
#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id)
@ -451,6 +542,301 @@ static ulong rk3368_saradc_set_clk(struct rk3368_cru *cru, uint hz)
return rk3368_saradc_get_clk(cru);
}
static ulong rk3368_bus_get_clk(struct rk3368_cru *cru, ulong clk_id)
{
u32 div, con, parent;
switch (clk_id) {
case ACLK_BUS:
con = readl(&cru->clksel_con[8]);
div = (con & ACLK_BUS_DIV_CON_MASK) >> ACLK_BUS_DIV_CON_SHIFT;
parent = rkclk_pll_get_rate(cru, GPLL);
break;
case HCLK_BUS:
con = readl(&cru->clksel_con[8]);
div = (con & HCLK_BUS_DIV_CON_MASK) >> HCLK_BUS_DIV_CON_SHIFT;
parent = rk3368_bus_get_clk(cru, ACLK_BUS);
break;
case PCLK_BUS:
case PCLK_PWM0:
case PCLK_PWM1:
case PCLK_I2C0:
case PCLK_I2C1:
con = readl(&cru->clksel_con[8]);
div = (con & PCLK_BUS_DIV_CON_MASK) >> PCLK_BUS_DIV_CON_SHIFT;
parent = rk3368_bus_get_clk(cru, ACLK_BUS);
break;
default:
return -ENOENT;
}
return DIV_TO_RATE(parent, div);
}
static ulong rk3368_bus_set_clk(struct rk3368_cru *cru,
ulong clk_id, ulong hz)
{
int src_clk_div;
/*
* select gpll as pd_bus bus clock source and
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
*/
switch (clk_id) {
case ACLK_BUS:
src_clk_div = DIV_ROUND_UP(rkclk_pll_get_rate(cru, GPLL), hz);
assert(src_clk_div - 1 < 31);
rk_clrsetreg(&cru->clksel_con[8],
CLK_BUS_PLL_SEL_MASK | ACLK_BUS_DIV_CON_MASK,
CLK_BUS_PLL_SEL_CPLL << CLK_BUS_PLL_SEL_SHIFT |
(src_clk_div - 1) << ACLK_BUS_DIV_CON_SHIFT);
break;
case HCLK_BUS:
src_clk_div = DIV_ROUND_UP(rk3368_bus_get_clk(cru,
ACLK_BUS),
hz);
assert(src_clk_div - 1 < 3);
rk_clrsetreg(&cru->clksel_con[8],
HCLK_BUS_DIV_CON_MASK,
(src_clk_div - 1) << HCLK_BUS_DIV_CON_SHIFT);
break;
case PCLK_BUS:
src_clk_div = DIV_ROUND_UP(rk3368_bus_get_clk(cru,
ACLK_BUS),
hz);
assert(src_clk_div - 1 < 3);
rk_clrsetreg(&cru->clksel_con[8],
PCLK_BUS_DIV_CON_MASK,
(src_clk_div - 1) << PCLK_BUS_DIV_CON_SHIFT);
break;
default:
printf("do not support this bus freq\n");
return -EINVAL;
}
return rk3368_bus_get_clk(cru, clk_id);
}
static ulong rk3368_peri_get_clk(struct rk3368_cru *cru, ulong clk_id)
{
u32 div, con, parent;
switch (clk_id) {
case ACLK_PERI:
con = readl(&cru->clksel_con[9]);
div = (con & ACLK_PERI_DIV_CON_MASK) >> ACLK_PERI_DIV_CON_SHIFT;
parent = rkclk_pll_get_rate(cru, GPLL);
break;
case HCLK_PERI:
con = readl(&cru->clksel_con[9]);
div = (con & HCLK_PERI_DIV_CON_MASK) >> HCLK_PERI_DIV_CON_SHIFT;
parent = rk3368_peri_get_clk(cru, ACLK_PERI);
break;
case PCLK_PERI:
case PCLK_I2C2:
case PCLK_I2C3:
case PCLK_I2C4:
case PCLK_I2C5:
con = readl(&cru->clksel_con[9]);
div = (con & PCLK_PERI_DIV_CON_MASK) >> PCLK_PERI_DIV_CON_SHIFT;
parent = rk3368_peri_get_clk(cru, ACLK_PERI);
break;
default:
return -ENOENT;
}
return DIV_TO_RATE(parent, div);
}
static ulong rk3368_peri_set_clk(struct rk3368_cru *cru,
ulong clk_id, ulong hz)
{
int src_clk_div;
/*
* select gpll as pd_bus bus clock source and
* set up dependent divisors for PCLK/HCLK and ACLK clocks.
*/
switch (clk_id) {
case ACLK_PERI:
src_clk_div = DIV_ROUND_UP(rkclk_pll_get_rate(cru, GPLL), hz);
assert(src_clk_div - 1 < 31);
rk_clrsetreg(&cru->clksel_con[9],
CLK_PERI_PLL_SEL_MASK | ACLK_PERI_DIV_CON_MASK,
CLK_PERI_PLL_SEL_CPLL << CLK_PERI_PLL_SEL_SHIFT |
(src_clk_div - 1) << ACLK_PERI_DIV_CON_SHIFT);
break;
case HCLK_PERI:
src_clk_div = DIV_ROUND_UP(rk3368_peri_get_clk(cru,
ACLK_PERI),
hz);
assert(src_clk_div - 1 < 3);
rk_clrsetreg(&cru->clksel_con[9],
HCLK_PERI_DIV_CON_MASK,
(src_clk_div - 1) << HCLK_PERI_DIV_CON_SHIFT);
break;
case PCLK_PERI:
src_clk_div = DIV_ROUND_UP(rk3368_peri_get_clk(cru,
ACLK_PERI),
hz);
assert(src_clk_div - 1 < 3);
rk_clrsetreg(&cru->clksel_con[9],
PCLK_PERI_DIV_CON_MASK,
(src_clk_div - 1) << PCLK_PERI_DIV_CON_SHIFT);
break;
default:
printf("do not support this bus freq\n");
return -EINVAL;
}
return rk3368_peri_get_clk(cru, clk_id);
}
#if !defined(CONFIG_SPL_BUILD)
static ulong rk3368_vop_get_clk(struct rk3368_cru *cru, int clk_id)
{
u32 div, con, parent, sel;
switch (clk_id) {
case DCLK_VOP:
con = readl(&cru->clksel_con[20]);
div = con & DCLK_VOP_DIV_MASK;
parent = rkclk_pll_get_rate(cru, NPLL);
break;
case ACLK_VOP:
con = readl(&cru->clksel_con[19]);
div = con & ACLK_VOP_DIV_MASK;
sel = (con & (ACLK_VOP_PLL_SEL_MASK <<
ACLK_VOP_PLL_SEL_SHIFT)) >>
ACLK_VOP_PLL_SEL_SHIFT;
if (sel == ACLK_VOP_PLL_SEL_CPLL)
parent = rkclk_pll_get_rate(cru, CPLL);
else if (ACLK_VOP_PLL_SEL_GPLL)
parent = rkclk_pll_get_rate(cru, GPLL);
else
parent = 480000000;
break;
default:
return -EINVAL;
}
return DIV_TO_RATE(parent, div);
}
static ulong rk3368_vop_set_clk(struct rk3368_cru *cru, int clk_id, uint hz)
{
struct pll_div npll_config = {0};
u32 lcdc_div;
int ret;
switch (clk_id) {
case DCLK_VOP:
ret = pll_para_config(hz, &npll_config, &lcdc_div);
if (ret)
return ret;
rkclk_set_pll(cru, NPLL, &npll_config);
/* vop dclk source clk: npll,dclk_div: 1 */
rk_clrsetreg(&cru->clksel_con[20],
(DCLK_VOP_PLL_SEL_MASK << DCLK_VOP_PLL_SEL_SHIFT) |
(DCLK_VOP_DIV_MASK << DCLK_VOP_DIV_SHIFT),
(DCLK_VOP_PLL_SEL_NPLL << DCLK_VOP_PLL_SEL_SHIFT) |
(lcdc_div - 1) << DCLK_VOP_DIV_SHIFT);
break;
case ACLK_VOP:
if ((rkclk_pll_get_rate(cru, CPLL) % hz) == 0) {
lcdc_div = rkclk_pll_get_rate(cru, CPLL) / hz;
rk_clrsetreg(&cru->clksel_con[19],
(ACLK_VOP_PLL_SEL_MASK <<
ACLK_VOP_PLL_SEL_SHIFT) |
(ACLK_VOP_DIV_MASK <<
ACLK_VOP_DIV_SHIFT),
(ACLK_VOP_PLL_SEL_CPLL <<
ACLK_VOP_PLL_SEL_SHIFT) |
(lcdc_div - 1) <<
ACLK_VOP_DIV_SHIFT);
} else {
lcdc_div = rkclk_pll_get_rate(cru, GPLL) / hz;
rk_clrsetreg(&cru->clksel_con[19],
(ACLK_VOP_PLL_SEL_MASK <<
ACLK_VOP_PLL_SEL_SHIFT) |
(ACLK_VOP_DIV_MASK <<
ACLK_VOP_DIV_SHIFT),
(ACLK_VOP_PLL_SEL_GPLL <<
ACLK_VOP_PLL_SEL_SHIFT) |
(lcdc_div - 1) <<
ACLK_VOP_DIV_SHIFT);
}
break;
default:
return -EINVAL;
}
return 0;
}
#endif
static ulong rk3368_armclk_set_clk(struct rk3368_clk_priv *priv,
int clk_id, ulong hz)
{
struct rk3368_cru *cru = priv->cru;
const struct rockchip_cpu_rate_table *rate;
struct pll_div pll_config = {0};
ulong old_rate;
u32 pll_div, pll_id, con_id;
int ret;
rate = rockchip_get_cpu_settings(rk3368_cpu_rates, hz);
if (!rate) {
printf("%s unsupported rate\n", __func__);
return -EINVAL;
}
/*
* select apll as cpu/core clock pll source and
* set up dependent divisors for PERI and ACLK clocks.
* core hz : apll = 1:1
*/
ret = pll_para_config(hz, &pll_config, &pll_div);
if (ret)
return ret;
if (clk_id == ARMCLKB) {
old_rate = rkclk_pll_get_rate(priv->cru, APLLB);
pll_id = APLLB;
con_id = 0;
} else {
old_rate = rkclk_pll_get_rate(priv->cru, APLLL);
pll_id = APLLL;
con_id = 2;
}
if (old_rate > hz) {
ret = rkclk_set_pll(priv->cru, pll_id, &pll_config);
rk_clrsetreg(&cru->clksel_con[con_id],
CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK,
CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
0 << CORE_DIV_CON_SHIFT);
rk_clrsetreg(&cru->clksel_con[con_id + 1],
CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK,
rate->aclk_div << CORE_ACLK_DIV_SHIFT |
rate->pclk_div << CORE_DBG_DIV_SHIFT);
} else if (old_rate < hz) {
rk_clrsetreg(&cru->clksel_con[con_id],
CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK,
CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
0 << CORE_DIV_CON_SHIFT);
rk_clrsetreg(&cru->clksel_con[con_id + 1],
CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK,
rate->aclk_div << CORE_ACLK_DIV_SHIFT |
rate->pclk_div << CORE_DBG_DIV_SHIFT);
ret = rkclk_set_pll(priv->cru, pll_id, &pll_config);
}
return rkclk_pll_get_rate(priv->cru, pll_id);
}
static ulong rk3368_clk_get_rate(struct clk *clk)
{
struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
@ -458,15 +844,41 @@ static ulong rk3368_clk_get_rate(struct clk *clk)
debug("%s: id %ld\n", __func__, clk->id);
switch (clk->id) {
case PLL_APLLB:
case PLL_APLLL:
case PLL_DPLL:
case PLL_CPLL:
rate = rkclk_pll_get_rate(priv->cru, CPLL);
break;
case PLL_GPLL:
rate = rkclk_pll_get_rate(priv->cru, GPLL);
case PLL_NPLL:
rate = rkclk_pll_get_rate(priv->cru, clk->id - 1);
break;
case ARMCLKB:
rate = rkclk_pll_get_rate(priv->cru, APLLB);
break;
case ARMCLKL:
rate = rkclk_pll_get_rate(priv->cru, APLLL);
break;
case SCLK_SPI0 ... SCLK_SPI2:
rate = rk3368_spi_get_clk(priv->cru, clk->id);
break;
case ACLK_BUS:
case HCLK_BUS:
case PCLK_BUS:
case PCLK_PWM0:
case PCLK_PWM1:
case PCLK_I2C0:
case PCLK_I2C1:
rate = rk3368_bus_get_clk(priv->cru, clk->id);
break;
case ACLK_PERI:
case HCLK_PERI:
case PCLK_PERI:
case PCLK_I2C2:
case PCLK_I2C3:
case PCLK_I2C4:
case PCLK_I2C5:
rate = rk3368_peri_get_clk(priv->cru, clk->id);
break;
#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
case HCLK_SDMMC:
case HCLK_EMMC:
@ -476,28 +888,59 @@ static ulong rk3368_clk_get_rate(struct clk *clk)
case SCLK_SARADC:
rate = rk3368_saradc_get_clk(priv->cru);
break;
#if !defined(CONFIG_SPL_BUILD)
case ACLK_VOP:
case DCLK_VOP:
rate = rk3368_vop_get_clk(priv->cru, clk->id);
break;
#endif
default:
return -ENOENT;
}
return rate;
}
static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
{
__maybe_unused struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
struct pll_div pll_config = {0};
u32 pll_div;
ulong ret = 0;
debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate);
switch (clk->id) {
case PLL_APLLB:
case PLL_APLLL:
case PLL_CPLL:
case PLL_GPLL:
case PLL_NPLL:
ret = pll_para_config(rate, &pll_config, &pll_div);
if (ret)
return ret;
ret = rkclk_set_pll(priv->cru, clk->id - 1, &pll_config);
break;
case ARMCLKB:
case ARMCLKL:
ret = rk3368_armclk_set_clk(priv, clk->id, rate);
break;
case SCLK_SPI0 ... SCLK_SPI2:
ret = rk3368_spi_set_clk(priv->cru, clk->id, rate);
break;
#if IS_ENABLED(CONFIG_TPL_BUILD)
case CLK_DDR:
case SCLK_DDRCLK:
ret = rk3368_ddr_set_clk(priv->cru, rate);
break;
#endif
case ACLK_BUS:
case HCLK_BUS:
case PCLK_BUS:
rate = rk3368_bus_set_clk(priv->cru, clk->id, rate);
break;
case ACLK_PERI:
case HCLK_PERI:
case PCLK_PERI:
rate = rk3368_peri_set_clk(priv->cru, clk->id, rate);
break;
#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
case HCLK_SDMMC:
case HCLK_EMMC:
@ -513,6 +956,15 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
case SCLK_SARADC:
ret = rk3368_saradc_set_clk(priv->cru, rate);
break;
#if !defined(CONFIG_SPL_BUILD)
case ACLK_VOP:
case DCLK_VOP:
ret = rk3368_vop_set_clk(priv->cru, clk->id, rate);
break;
case ACLK_CCI_PRE:
ret = 0;
break;
#endif
default:
return -ENOENT;
}
@ -576,9 +1028,37 @@ static struct clk_ops rk3368_clk_ops = {
#endif
};
#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
static void rkclk_init(struct rk3368_cru *cru)
{
u32 apllb, aplll, dpll, cpll, gpll;
rkclk_set_pll(cru, APLLB, &apll_b_init_cfg);
rkclk_set_pll(cru, APLLL, &apll_l_init_cfg);
#if !defined(CONFIG_TPL_BUILD)
/*
* If we plan to return to the boot ROM, we can't increase the
* GPLL rate from the SPL stage.
*/
rkclk_set_pll(cru, GPLL, &gpll_init_cfg);
rkclk_set_pll(cru, CPLL, &cpll_init_cfg);
#endif
rk_clrsetreg(&cru->clksel_con[37], (1 << 8), 1 << 8);
apllb = rkclk_pll_get_rate(cru, APLLB);
aplll = rkclk_pll_get_rate(cru, APLLL);
dpll = rkclk_pll_get_rate(cru, DPLL);
cpll = rkclk_pll_get_rate(cru, CPLL);
gpll = rkclk_pll_get_rate(cru, GPLL);
debug("%s apllb(%d) apll(%d) dpll(%d) cpll(%d) gpll(%d)\n",
__func__, apllb, aplll, dpll, cpll, gpll);
}
#endif
static int rk3368_clk_probe(struct udevice *dev)
{
struct rk3368_clk_priv __maybe_unused *priv = dev_get_priv(dev);
int ret;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct rk3368_clk_plat *plat = dev_get_platdata(dev);
@ -587,7 +1067,10 @@ static int rk3368_clk_probe(struct udevice *dev)
#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
rkclk_init(priv->cru);
#endif
/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
ret = clk_set_defaults(dev);
if (ret)
debug("%s clk_set_defaults failed %d\n", __func__, ret);
return 0;
}
@ -656,3 +1139,61 @@ U_BOOT_DRIVER(rockchip_rk3368_cru) = {
.bind = rk3368_clk_bind,
.probe = rk3368_clk_probe,
};
#if !defined(CONFIG_SPL_BUILD)
/**
* soc_clk_dump() - Print clock frequencies
* Returns zero on success
*
* Implementation for the clk dump command.
*/
int soc_clk_dump(void)
{
struct udevice *cru_dev;
const struct rk3368_clk_info *clk_dump;
struct clk clk;
unsigned long clk_count = ARRAY_SIZE(clks_dump);
unsigned long rate;
int i, ret;
ret = uclass_get_device_by_driver(UCLASS_CLK,
DM_GET_DRIVER(rockchip_rk3368_cru),
&cru_dev);
if (ret) {
printf("%s failed to get cru device\n", __func__);
return ret;
}
printf("CLK:");
for (i = 0; i < clk_count; i++) {
clk_dump = &clks_dump[i];
if (clk_dump->name) {
clk.id = clk_dump->id;
if (clk_dump->is_cru)
ret = clk_request(cru_dev, &clk);
if (ret < 0)
return ret;
rate = clk_get_rate(&clk);
clk_free(&clk);
if (i == 0) {
if (rate < 0)
printf("%10s%20s\n", clk_dump->name,
"unknown");
else
printf("%10s%20lu Hz\n", clk_dump->name,
rate);
} else {
if (rate < 0)
printf("%14s%20s\n", clk_dump->name,
"unknown");
else
printf("%14s%20lu Hz\n", clk_dump->name,
rate);
}
}
}
return 0;
}
#endif

View File

@ -44,13 +44,12 @@
#define SCLK_I2S_8CH 82
#define SCLK_SPDIF_8CH 83
#define SCLK_I2S_2CH 84
#define SCLK_TIMER0 85
#define SCLK_TIMER1 86
#define SCLK_TIMER2 87
#define SCLK_TIMER3 88
#define SCLK_TIMER4 89
#define SCLK_TIMER5 90
#define SCLK_TIMER6 91
#define SCLK_TIMER00 85
#define SCLK_TIMER01 86
#define SCLK_TIMER02 87
#define SCLK_TIMER03 88
#define SCLK_TIMER04 89
#define SCLK_TIMER05 90
#define SCLK_OTGPHY0 93
#define SCLK_OTG_ADP 96
#define SCLK_HSICPHY480M 97
@ -82,6 +81,19 @@
#define SCLK_SFC 126
#define SCLK_MAC 127
#define SCLK_MACREF_OUT 128
#define SCLK_MIPIDSI_24M 129
#define SCLK_CRYPTO 130
#define SCLK_VIP_SRC 131
#define SCLK_VIP_OUT 132
#define SCLK_TIMER10 133
#define SCLK_TIMER11 134
#define SCLK_TIMER12 135
#define SCLK_TIMER13 136
#define SCLK_TIMER14 137
#define SCLK_TIMER15 138
#define SCLK_DDRCLK 139
#define SCLK_TSP 140
#define SCLK_HSADC_TSP 141
#define DCLK_VOP 190
#define MCLK_CRYPTO 191
@ -105,6 +117,7 @@
#define ACLK_VIDEO 208
#define ACLK_BUS 209
#define ACLK_PERI 210
#define ACLK_CCI_PRE 211
/* pclk gates */
#define PCLK_GPIO0 320
@ -151,8 +164,13 @@
#define PCLK_ISP 366
#define PCLK_VIP 367
#define PCLK_WDT 368
#define PCLK_DPHYRX 369
#define PCLK_DPHYTX0 370
#define PCLK_EFUSE256 371
#define PCLK_EFUSE1024 372
/* hclk gates */
#define HCLK_USB_PERI 447
#define HCLK_SFC 448
#define HCLK_OTG0 449
#define HCLK_HOST0 450