mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
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:
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user