clk: rockchip: add pll type for RK3588

add pll_rk3588 and pll_rk3588_core type for RK3588 Soc.

Change-Id: Ie84adcb1ff8fe59efc212feee3ed872bb318fc8b
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
This commit is contained in:
Elaine Zhang
2021-05-07 17:36:44 +08:00
committed by Tao Huang
parent 1d4d01d6ed
commit 58c1fa2ef2
2 changed files with 318 additions and 1 deletions

View File

@@ -326,6 +326,67 @@ rockchip_rk3066_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
return rate_table; return rate_table;
} }
static struct rockchip_pll_rate_table *
rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
unsigned long fin_hz,
unsigned long fout_hz)
{
struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
u64 fvco_min = 2250 * MHZ, fvco_max = 4500 * MHZ;
u64 fout_min = 37 * MHZ, fout_max = 4500 * MHZ;
u32 p, m, s;
u64 fvco, fref, fout, ffrac;
if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
return NULL;
if (fout_hz > fout_max || fout_hz < fout_min)
return NULL;
if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
for (s = 0; s <= 6; s++) {
fvco = fout_hz << s;
if (fvco < fvco_min || fvco > fvco_max)
continue;
for (p = 2; p <= 4; p++) {
for (m = 64; m <= 1023; m++) {
if (fvco == m * fin_hz / p) {
rate_table->p = p;
rate_table->m = m;
rate_table->s = s;
rate_table->k = 0;
return rate_table;
}
}
}
}
pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
} else {
fout = (fout_hz / MHZ) * MHZ;
ffrac = (fout_hz % MHZ);
for (s = 0; s <= 6; s++) {
fvco = fout << s;
if (fvco < fvco_min || fvco > fvco_max)
continue;
for (p = 1; p <= 4; p++) {
for (m = 64; m <= 1023; m++) {
if (fvco == m * fin_hz / p) {
rate_table->p = p;
rate_table->m = m;
rate_table->s = s;
fref = fin_hz / p;
fout = (ffrac << s) * 65535;
rate_table->k = fout / fref;
return rate_table;
}
}
}
}
pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
}
return NULL;
}
static const struct rockchip_pll_rate_table *rockchip_get_pll_settings( static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
struct rockchip_clk_pll *pll, unsigned long rate) struct rockchip_clk_pll *pll, unsigned long rate)
{ {
@@ -346,6 +407,8 @@ static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
if (pll->type == pll_rk3066) if (pll->type == pll_rk3066)
return rockchip_rk3066_pll_clk_set_by_auto(pll, 24 * MHZ, rate); return rockchip_rk3066_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
else if (pll->type == pll_rk3588 || pll->type == pll_rk3588_core)
return rockchip_rk3588_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
else else
return rockchip_pll_clk_set_by_auto(pll, 24 * MHZ, rate); return rockchip_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
} }
@@ -1184,6 +1247,234 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
.init = rockchip_rk3399_pll_init, .init = rockchip_rk3399_pll_init,
}; };
/**
* PLL used in RK3588
*/
#define RK3588_PLLCON(i) (i * 0x4)
#define RK3588_PLLCON0_M_MASK 0x3ff
#define RK3588_PLLCON0_M_SHIFT 0
#define RK3588_PLLCON1_P_MASK 0x3f
#define RK3588_PLLCON1_P_SHIFT 0
#define RK3588_PLLCON1_S_MASK 0x7
#define RK3588_PLLCON1_S_SHIFT 6
#define RK3588_PLLCON2_K_MASK 0xffff
#define RK3588_PLLCON2_K_SHIFT 0
#define RK3588_PLLCON1_PWRDOWN BIT(13)
#define RK3588_PLLCON6_LOCK_STATUS BIT(15)
static int rockchip_rk3588_pll_wait_lock(struct rockchip_clk_pll *pll)
{
u32 pllcon;
int ret;
/*
* Lock time typical 250, max 500 input clock cycles @24MHz
* So define a very safe maximum of 1000us, meaning 24000 cycles.
*/
ret = readl_relaxed_poll_timeout(pll->reg_base + RK3588_PLLCON(6),
pllcon,
pllcon & RK3588_PLLCON6_LOCK_STATUS,
0, 1000);
if (ret)
pr_err("%s: timeout waiting for pll to lock\n", __func__);
return ret;
}
static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll,
struct rockchip_pll_rate_table *rate)
{
u32 pllcon;
pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(0));
rate->m = ((pllcon >> RK3588_PLLCON0_M_SHIFT)
& RK3588_PLLCON0_M_MASK);
pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
rate->p = ((pllcon >> RK3588_PLLCON1_P_SHIFT)
& RK3588_PLLCON1_P_MASK);
rate->s = ((pllcon >> RK3588_PLLCON1_S_SHIFT)
& RK3588_PLLCON1_S_MASK);
pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(2));
rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT)
& RK3588_PLLCON2_K_MASK);
}
static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw,
unsigned long prate)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
struct rockchip_pll_rate_table cur;
u64 rate64 = prate, postdiv;
if (pll->sel && pll->scaling)
return pll->scaling;
rockchip_rk3588_pll_get_params(pll, &cur);
rate64 *= cur.m;
do_div(rate64, cur.p);
if (cur.k) {
/* fractional mode */
u64 frac_rate64 = prate * cur.k;
postdiv = cur.p * 65535;
do_div(frac_rate64, postdiv);
rate64 += frac_rate64;
}
rate64 = rate64 >> cur.s;
return (unsigned long)rate64;
}
static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll,
const struct rockchip_pll_rate_table *rate)
{
const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
struct clk_mux *pll_mux = &pll->pll_mux;
struct rockchip_pll_rate_table cur;
int rate_change_remuxed = 0;
int cur_parent;
int ret;
pr_debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
__func__, rate->rate, rate->p, rate->m, rate->s, rate->k);
rockchip_rk3588_pll_get_params(pll, &cur);
cur.rate = 0;
if (pll->type == pll_rk3588) {
cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
if (cur_parent == PLL_MODE_NORM) {
pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
rate_change_remuxed = 1;
}
}
/* set pll power down */
writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
RK3588_PLLCON1_PWRDOWN, 0),
pll->reg_base + RK3399_PLLCON(1));
/* update pll values */
writel_relaxed(HIWORD_UPDATE(rate->m, RK3588_PLLCON0_M_MASK,
RK3588_PLLCON0_M_SHIFT),
pll->reg_base + RK3399_PLLCON(0));
writel_relaxed(HIWORD_UPDATE(rate->p, RK3588_PLLCON1_P_MASK,
RK3588_PLLCON1_P_SHIFT) |
HIWORD_UPDATE(rate->s, RK3588_PLLCON1_S_MASK,
RK3588_PLLCON1_S_SHIFT),
pll->reg_base + RK3399_PLLCON(1));
if (rate->k)
writel_relaxed(HIWORD_UPDATE(rate->k, RK3588_PLLCON2_K_MASK,
RK3588_PLLCON2_K_SHIFT),
pll->reg_base + RK3399_PLLCON(2));
/* set pll power up */
writel(HIWORD_UPDATE(0,
RK3588_PLLCON1_PWRDOWN, 0),
pll->reg_base + RK3588_PLLCON(1));
/* wait for the pll to lock */
ret = rockchip_rk3588_pll_wait_lock(pll);
if (ret) {
pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
__func__);
rockchip_rk3588_pll_set_params(pll, &cur);
}
if ((pll->type == pll_rk3588) && rate_change_remuxed)
pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
return ret;
}
static int rockchip_rk3588_pll_set_rate(struct clk_hw *hw, unsigned long drate,
unsigned long prate)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
const struct rockchip_pll_rate_table *rate;
unsigned long old_rate = rockchip_rk3588_pll_recalc_rate(hw, prate);
int ret;
pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n",
__func__, __clk_get_name(hw->clk), old_rate, drate, prate);
/* Get required rate settings from table */
rate = rockchip_get_pll_settings(pll, drate);
if (!rate) {
pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
drate, __clk_get_name(hw->clk));
return -EINVAL;
}
ret = rockchip_rk3588_pll_set_params(pll, rate);
if (ret)
pll->scaling = 0;
return ret;
}
static int rockchip_rk3588_pll_enable(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
pll->reg_base + RK3588_PLLCON(1));
rockchip_rk3588_pll_wait_lock(pll);
return 0;
}
static void rockchip_rk3588_pll_disable(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
RK3588_PLLCON1_PWRDOWN, 0),
pll->reg_base + RK3588_PLLCON(1));
}
static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
u32 pllcon = readl(pll->reg_base + RK3588_PLLCON(1));
return !(pllcon & RK3588_PLLCON1_PWRDOWN);
}
static int rockchip_rk3588_pll_init(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
return 0;
return 0;
}
static const struct clk_ops rockchip_rk3588_pll_clk_norate_ops = {
.recalc_rate = rockchip_rk3588_pll_recalc_rate,
.enable = rockchip_rk3588_pll_enable,
.disable = rockchip_rk3588_pll_disable,
.is_enabled = rockchip_rk3588_pll_is_enabled,
};
static const struct clk_ops rockchip_rk3588_pll_clk_ops = {
.recalc_rate = rockchip_rk3588_pll_recalc_rate,
.round_rate = rockchip_pll_round_rate,
.set_rate = rockchip_rk3588_pll_set_rate,
.enable = rockchip_rk3588_pll_enable,
.disable = rockchip_rk3588_pll_disable,
.is_enabled = rockchip_rk3588_pll_is_enabled,
.init = rockchip_rk3588_pll_init,
};
#ifdef CONFIG_ROCKCHIP_CLK_COMPENSATION #ifdef CONFIG_ROCKCHIP_CLK_COMPENSATION
int rockchip_pll_clk_compensation(struct clk *clk, int ppm) int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
{ {
@@ -1306,7 +1597,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
if (pll_type == pll_rk3036 || if (pll_type == pll_rk3036 ||
pll_type == pll_rk3066 || pll_type == pll_rk3066 ||
pll_type == pll_rk3328 || pll_type == pll_rk3328 ||
pll_type == pll_rk3399) pll_type == pll_rk3399 ||
pll_type == pll_rk3588)
pll_mux->flags |= CLK_MUX_HIWORD_MASK; pll_mux->flags |= CLK_MUX_HIWORD_MASK;
/* the actual muxing is xin24m, pll-output, xin32k */ /* the actual muxing is xin24m, pll-output, xin32k */
@@ -1377,6 +1669,13 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
else else
init.ops = &rockchip_rk3399_pll_clk_ops; init.ops = &rockchip_rk3399_pll_clk_ops;
break; break;
case pll_rk3588:
case pll_rk3588_core:
if (!pll->rate_table)
init.ops = &rockchip_rk3588_pll_clk_norate_ops;
else
init.ops = &rockchip_rk3588_pll_clk_ops;
break;
default: default:
pr_warn("%s: Unknown pll type for pll clk %s\n", pr_warn("%s: Unknown pll type for pll clk %s\n",
__func__, name); __func__, name);

View File

@@ -279,6 +279,8 @@ enum rockchip_pll_type {
pll_rk3066, pll_rk3066,
pll_rk3328, pll_rk3328,
pll_rk3399, pll_rk3399,
pll_rk3588,
pll_rk3588_core,
}; };
#define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \
@@ -311,6 +313,15 @@ enum rockchip_pll_type {
.nb = _nb, \ .nb = _nb, \
} }
#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k) \
{ \
.rate = _rate##U, \
.p = _p, \
.m = _m, \
.s = _s, \
.k = _k, \
}
/** /**
* struct rockchip_clk_provider - information about clock provider * struct rockchip_clk_provider - information about clock provider
* @reg_base: virtual address for the register base. * @reg_base: virtual address for the register base.
@@ -347,6 +358,13 @@ struct rockchip_pll_rate_table {
unsigned int dsmpd; unsigned int dsmpd;
unsigned int frac; unsigned int frac;
}; };
struct {
/* for RK3588 */
unsigned int m;
unsigned int p;
unsigned int s;
unsigned int k;
};
}; };
}; };