clk: rockchip: rk3588: fix up the frac pll calculation

rk3588 frac pll:
FFVCO = ((m + k / 65536) * FFIN) / p
FFOUT = ((m + k / 65536) * FFIN) / (p * 2s)
k is the original code, but the K[15:0] is complement code
(6'b1000_0000_0000_0000 <= K[15:0] <= 16'b0111_1111_1111_1111),
need to be converted.

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Change-Id: I107d31d910d260c83891d5b6e927f119761d6fba
This commit is contained in:
Elaine Zhang
2022-11-03 17:49:30 +08:00
committed by Tao Huang
parent d0e6f8a073
commit 7a72bc05dc
2 changed files with 141 additions and 37 deletions

View File

@@ -332,6 +332,64 @@ rockchip_rk3066_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
return rate_table;
}
static u32
rockchip_rk3588_pll_frac_get(u32 m, u32 p, u32 s, u64 fin_hz, u64 fvco)
{
u64 fref, fout, ffrac;
u32 k = 0;
fref = fin_hz / p;
ffrac = fvco - (m * fref);
fout = ffrac * 65536;
k = fout / fref;
if (k > 32767) {
fref = fin_hz / p;
ffrac = ((m + 1) * fref) - fvco;
fout = ffrac * 65536;
k = ((fout * 10 / fref) + 7) / 10;
if (k > 32767)
k = 0;
else
k = ~k + 1;
}
return k;
}
static struct rockchip_pll_rate_table *
rockchip_rk3588_pll_frac_by_auto(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;
u32 p, m, s, k;
u64 fvco;
for (s = 0; s <= 6; s++) {
fvco = (u64)fout_hz << 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) && (fvco < (m + 1) * fin_hz / p)) {
k = rockchip_rk3588_pll_frac_get(m, p, s,
(u64)fin_hz,
fvco);
if (!k)
continue;
rate_table->p = p;
rate_table->s = s;
rate_table->k = k;
if (k > 32767)
rate_table->m = m + 1;
else
rate_table->m = m;
return rate_table;
}
}
}
}
return NULL;
}
static struct rockchip_pll_rate_table *
rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
unsigned long fin_hz,
@@ -341,7 +399,7 @@ rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
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;
u64 fvco;
if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
return NULL;
@@ -368,26 +426,11 @@ rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
}
pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
} else {
for (s = 0; s <= 6; s++) {
fvco = (u64)fout_hz << 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) && (fvco < (m + 1) * fin_hz / p)) {
rate_table->p = p;
rate_table->m = m;
rate_table->s = s;
fref = fin_hz / p;
ffrac = fvco - (m * fref);
fout = ffrac * 65536;
rate_table->k = fout / fref;
return rate_table;
}
}
}
}
pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
rate_table = rockchip_rk3588_pll_frac_by_auto(fin_hz, fout_hz);
if (!rate_table)
pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
else
return rate_table;
}
return NULL;
}
@@ -1345,7 +1388,17 @@ static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw,
rate64 *= cur.m;
do_div(rate64, cur.p);
if (cur.k) {
if (cur.k & BIT(15)) {
/* fractional mode */
u64 frac_rate64;
cur.k = (~(cur.k - 1)) & RK3588_PLLCON2_K_MASK;
frac_rate64 = prate * cur.k;
postdiv = cur.p;
postdiv *= 65536;
do_div(frac_rate64, postdiv);
rate64 -= frac_rate64;
} else {
/* fractional mode */
u64 frac_rate64 = prate * cur.k;
@@ -1516,7 +1569,7 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
{
struct clk *parent = clk_get_parent(clk);
struct rockchip_clk_pll *pll;
static u32 frac, fbdiv;
static u32 frac, fbdiv, s, p;
bool negative;
u32 pllcon, pllcon0, pllcon2, fbdiv_mask, frac_mask, frac_shift;
u64 fracdiv, m, n;
@@ -1567,11 +1620,6 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
negative = !!(ppm & BIT(31));
ppm = negative ? ~ppm + 1 : ppm;
if (!frac) {
frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
}
switch (pll->type) {
case pll_rk3036:
case pll_rk3328:
@@ -1583,6 +1631,10 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
* 1 << 24 1 << 24 1000000
*
*/
if (!frac) {
frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
}
m = div64_u64((uint64_t)frac * ppm, 1000000);
n = div64_u64((uint64_t)ppm << 24, 1000000) * fbdiv;
@@ -1597,13 +1649,65 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
writel_relaxed(pllcon, pll->reg_base + pllcon2);
break;
case pll_rk3588:
m = div64_u64((uint64_t)frac * ppm, 100000);
n = div64_u64((uint64_t)ppm * 65535 * fbdiv, 100000);
if (!fbdiv) {
frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
}
if (!frac) {
pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
s = ((pllcon >> RK3588_PLLCON1_S_SHIFT)
& RK3588_PLLCON1_S_MASK);
p = ((pllcon >> RK3588_PLLCON1_P_SHIFT)
& RK3588_PLLCON1_P_MASK);
m = div64_u64((uint64_t)clk_get_rate(clk) * ppm, 24000000);
n = div64_u64((uint64_t)m * 65536 * p * (1 << s), 1000000);
fracdiv = negative ? frac - (div64_u64(m + n, 10)) : frac + (div64_u64(m + n, 10));
if (!frac || fracdiv > frac_mask)
return -EINVAL;
if (n > 32767)
return -EINVAL;
fracdiv = negative ? ~n + 1 : n;
} else if (frac & BIT(15)) {
frac = (~(frac - 1)) & RK3588_PLLCON2_K_MASK;
m = div64_u64((uint64_t)frac * ppm, 100000);
n = div64_u64((uint64_t)ppm * 65536 * fbdiv, 100000);
if (negative) {
fracdiv = frac + (div64_u64(m + n, 10));
if (fracdiv > 32767)
return -EINVAL;
fracdiv = ~fracdiv + 1;
} else {
s = div64_u64(m + n, 10);
if (frac >= s) {
fracdiv = frac - s;
if (fracdiv > 32767)
return -EINVAL;
fracdiv = ~fracdiv + 1;
} else {
fracdiv = s - frac;
if (fracdiv > 32767)
return -EINVAL;
}
}
} else {
m = div64_u64((uint64_t)frac * ppm, 100000);
n = div64_u64((uint64_t)ppm * 65536 * fbdiv, 100000);
if (!negative) {
fracdiv = frac + (div64_u64(m + n, 10));
if (fracdiv > 32767)
return -EINVAL;
} else {
s = div64_u64(m + n, 10);
if (frac >= s) {
fracdiv = frac - s;
if (fracdiv > 32767)
return -EINVAL;
} else {
fracdiv = s - frac;
if (fracdiv > 32767)
return -EINVAL;
fracdiv = ~fracdiv + 1;
}
}
}
writel_relaxed(HIWORD_UPDATE(fracdiv, frac_mask, frac_shift),
pll->reg_base + pllcon2);

View File

@@ -79,16 +79,16 @@ static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
RK3588_PLL_RATE(983040000, 4, 655, 2, 23592),
RK3588_PLL_RATE(955520000, 3, 477, 2, 49806),
RK3588_PLL_RATE(955520000, 3, 478, 2, 49807),
RK3588_PLL_RATE(903168000, 6, 903, 2, 11009),
RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
RK3588_PLL_RATE(785560000, 3, 392, 2, 51117),
RK3588_PLL_RATE(785560000, 3, 393, 2, 51119),
RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
RK3588_PLL_RATE(594000000, 1, 99, 2, 0),
RK3588_PLL_RATE(408000000, 2, 272, 3, 0),
RK3588_PLL_RATE(312000000, 2, 208, 3, 0),
RK3588_PLL_RATE(216000000, 2, 288, 4, 0),