Skip to content

Commit b09c68d

Browse files
saschahauerabelvesa
authored andcommitted
clk: imx: pll14xx: Support dynamic rates
The pll1443x PLL so far only supports rates from a rate table passed during initialization. Calculating PLL settings dynamically helps audio applications to get their desired rates, so support for this is added in this patch. The strategy to get to the PLL setting for a rate is: - First try to only adjust kdiv which specifies the fractional part of the PLL. This setting can be changed without glitches on the output and is therefore preferred - When that isn't possible then the rate table is searched for suitable rates, so for standard rates the same settings are used as without this patch - As a last resort the best settings are calculated dynamically The code in this patch is based on patches from Adrian Alonso <adrian.alonso@nxp.com> and Mads Bligaard Nielsen <bli@bang-olufsen.dk> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Reviewed-by: Abel Vesa <abel.vesa@nxp.com> Link: https://lore.kernel.org/r/20220304125256.2125023-9-s.hauer@pengutronix.de Signed-off-by: Abel Vesa <abel.vesa@nxp.com>
1 parent 80cbc80 commit b09c68d

File tree

1 file changed

+126
-17
lines changed

1 file changed

+126
-17
lines changed

drivers/clk/imx/clk-pll14xx.c

Lines changed: 126 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#define PDIV_MASK GENMASK(9, 4)
3030
#define SDIV_MASK GENMASK(2, 0)
3131
#define KDIV_MASK GENMASK(15, 0)
32+
#define KDIV_MIN SHRT_MIN
33+
#define KDIV_MAX SHRT_MAX
3234

3335
#define LOCK_TIMEOUT_US 10000
3436

@@ -113,7 +115,106 @@ static long pll14xx_calc_rate(struct clk_pll14xx *pll, int mdiv, int pdiv,
113115
return fvco;
114116
}
115117

116-
static long clk_pll14xx_round_rate(struct clk_hw *hw, unsigned long rate,
118+
static long pll1443x_calc_kdiv(int mdiv, int pdiv, int sdiv,
119+
unsigned long rate, unsigned long prate)
120+
{
121+
long kdiv;
122+
123+
/* calc kdiv = round(rate * pdiv * 65536 * 2^sdiv / prate) - (mdiv * 65536) */
124+
kdiv = ((rate * ((pdiv * 65536) << sdiv) + prate / 2) / prate) - (mdiv * 65536);
125+
126+
return clamp_t(short, kdiv, KDIV_MIN, KDIV_MAX);
127+
}
128+
129+
static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rate,
130+
unsigned long prate, struct imx_pll14xx_rate_table *t)
131+
{
132+
u32 pll_div_ctl0, pll_div_ctl1;
133+
int mdiv, pdiv, sdiv, kdiv;
134+
long fvco, rate_min, rate_max, dist, best = LONG_MAX;
135+
const struct imx_pll14xx_rate_table *tt;
136+
137+
/*
138+
* Fractional PLL constrains:
139+
*
140+
* a) 6MHz <= prate <= 25MHz
141+
* b) 1 <= p <= 63 (1 <= p <= 4 prate = 24MHz)
142+
* c) 64 <= m <= 1023
143+
* d) 0 <= s <= 6
144+
* e) -32768 <= k <= 32767
145+
*
146+
* fvco = (m * 65536 + k) * prate / (p * 65536)
147+
*/
148+
149+
/* First try if we can get the desired rate from one of the static entries */
150+
tt = imx_get_pll_settings(pll, rate);
151+
if (tt) {
152+
pr_debug("%s: in=%ld, want=%ld, Using PLL setting from table\n",
153+
clk_hw_get_name(&pll->hw), prate, rate);
154+
t->rate = tt->rate;
155+
t->mdiv = tt->mdiv;
156+
t->pdiv = tt->pdiv;
157+
t->sdiv = tt->sdiv;
158+
t->kdiv = tt->kdiv;
159+
return;
160+
}
161+
162+
pll_div_ctl0 = readl_relaxed(pll->base + DIV_CTL0);
163+
mdiv = FIELD_GET(MDIV_MASK, pll_div_ctl0);
164+
pdiv = FIELD_GET(PDIV_MASK, pll_div_ctl0);
165+
sdiv = FIELD_GET(SDIV_MASK, pll_div_ctl0);
166+
pll_div_ctl1 = readl_relaxed(pll->base + DIV_CTL1);
167+
168+
/* Then see if we can get the desired rate by only adjusting kdiv (glitch free) */
169+
rate_min = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, KDIV_MIN, prate);
170+
rate_max = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, KDIV_MAX, prate);
171+
172+
if (rate >= rate_min && rate <= rate_max) {
173+
kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate);
174+
pr_debug("%s: in=%ld, want=%ld Only adjust kdiv %ld -> %d\n",
175+
clk_hw_get_name(&pll->hw), prate, rate,
176+
FIELD_GET(KDIV_MASK, pll_div_ctl1), kdiv);
177+
fvco = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
178+
t->rate = (unsigned int)fvco;
179+
t->mdiv = mdiv;
180+
t->pdiv = pdiv;
181+
t->sdiv = sdiv;
182+
t->kdiv = kdiv;
183+
return;
184+
}
185+
186+
/* Finally calculate best values */
187+
for (pdiv = 1; pdiv <= 7; pdiv++) {
188+
for (sdiv = 0; sdiv <= 6; sdiv++) {
189+
/* calc mdiv = round(rate * pdiv * 2^sdiv) / prate) */
190+
mdiv = DIV_ROUND_CLOSEST(rate * (pdiv << sdiv), prate);
191+
mdiv = clamp(mdiv, 64, 1023);
192+
193+
kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate);
194+
fvco = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
195+
196+
/* best match */
197+
dist = abs((long)rate - (long)fvco);
198+
if (dist < best) {
199+
best = dist;
200+
t->rate = (unsigned int)fvco;
201+
t->mdiv = mdiv;
202+
t->pdiv = pdiv;
203+
t->sdiv = sdiv;
204+
t->kdiv = kdiv;
205+
206+
if (!dist)
207+
goto found;
208+
}
209+
}
210+
}
211+
found:
212+
pr_debug("%s: in=%ld, want=%ld got=%d (pdiv=%d sdiv=%d mdiv=%d kdiv=%d)\n",
213+
clk_hw_get_name(&pll->hw), prate, rate, t->rate, t->pdiv, t->sdiv,
214+
t->mdiv, t->kdiv);
215+
}
216+
217+
static long clk_pll1416x_round_rate(struct clk_hw *hw, unsigned long rate,
117218
unsigned long *prate)
118219
{
119220
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
@@ -129,6 +230,17 @@ static long clk_pll14xx_round_rate(struct clk_hw *hw, unsigned long rate,
129230
return rate_table[pll->rate_count - 1].rate;
130231
}
131232

233+
static long clk_pll1443x_round_rate(struct clk_hw *hw, unsigned long rate,
234+
unsigned long *prate)
235+
{
236+
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
237+
struct imx_pll14xx_rate_table t;
238+
239+
imx_pll14xx_calc_settings(pll, rate, *prate, &t);
240+
241+
return t.rate;
242+
}
243+
132244
static unsigned long clk_pll14xx_recalc_rate(struct clk_hw *hw,
133245
unsigned long parent_rate)
134246
{
@@ -239,25 +351,21 @@ static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate,
239351
unsigned long prate)
240352
{
241353
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
242-
const struct imx_pll14xx_rate_table *rate;
354+
struct imx_pll14xx_rate_table rate;
243355
u32 gnrl_ctl, div_ctl0;
244356
int ret;
245357

246-
rate = imx_get_pll_settings(pll, drate);
247-
if (!rate) {
248-
pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
249-
drate, clk_hw_get_name(hw));
250-
return -EINVAL;
251-
}
358+
imx_pll14xx_calc_settings(pll, drate, prate, &rate);
252359

253360
div_ctl0 = readl_relaxed(pll->base + DIV_CTL0);
254361

255-
if (!clk_pll14xx_mp_change(rate, div_ctl0)) {
362+
if (!clk_pll14xx_mp_change(&rate, div_ctl0)) {
363+
/* only sdiv and/or kdiv changed - no need to RESET PLL */
256364
div_ctl0 &= ~SDIV_MASK;
257-
div_ctl0 |= FIELD_PREP(SDIV_MASK, rate->sdiv);
365+
div_ctl0 |= FIELD_PREP(SDIV_MASK, rate.sdiv);
258366
writel_relaxed(div_ctl0, pll->base + DIV_CTL0);
259367

260-
writel_relaxed(FIELD_PREP(KDIV_MASK, rate->kdiv),
368+
writel_relaxed(FIELD_PREP(KDIV_MASK, rate.kdiv),
261369
pll->base + DIV_CTL1);
262370

263371
return 0;
@@ -272,11 +380,12 @@ static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate,
272380
gnrl_ctl |= BYPASS_MASK;
273381
writel_relaxed(gnrl_ctl, pll->base + GNRL_CTL);
274382

275-
div_ctl0 = FIELD_PREP(MDIV_MASK, rate->mdiv) |
276-
FIELD_PREP(PDIV_MASK, rate->pdiv) |
277-
FIELD_PREP(SDIV_MASK, rate->sdiv);
383+
div_ctl0 = FIELD_PREP(MDIV_MASK, rate.mdiv) |
384+
FIELD_PREP(PDIV_MASK, rate.pdiv) |
385+
FIELD_PREP(SDIV_MASK, rate.sdiv);
278386
writel_relaxed(div_ctl0, pll->base + DIV_CTL0);
279-
writel_relaxed(FIELD_PREP(KDIV_MASK, rate->kdiv), pll->base + DIV_CTL1);
387+
388+
writel_relaxed(FIELD_PREP(KDIV_MASK, rate.kdiv), pll->base + DIV_CTL1);
280389

281390
/*
282391
* According to SPEC, t3 - t2 need to be greater than
@@ -359,7 +468,7 @@ static const struct clk_ops clk_pll1416x_ops = {
359468
.unprepare = clk_pll14xx_unprepare,
360469
.is_prepared = clk_pll14xx_is_prepared,
361470
.recalc_rate = clk_pll14xx_recalc_rate,
362-
.round_rate = clk_pll14xx_round_rate,
471+
.round_rate = clk_pll1416x_round_rate,
363472
.set_rate = clk_pll1416x_set_rate,
364473
};
365474

@@ -372,7 +481,7 @@ static const struct clk_ops clk_pll1443x_ops = {
372481
.unprepare = clk_pll14xx_unprepare,
373482
.is_prepared = clk_pll14xx_is_prepared,
374483
.recalc_rate = clk_pll14xx_recalc_rate,
375-
.round_rate = clk_pll14xx_round_rate,
484+
.round_rate = clk_pll1443x_round_rate,
376485
.set_rate = clk_pll1443x_set_rate,
377486
};
378487

0 commit comments

Comments
 (0)