Skip to content

Commit 1b26cb8

Browse files
MrVanabelvesa
authored andcommitted
clk: imx: support fracn gppll
This PLL module is a Fractional-N synthesizer, supporting 30-bit numerator and denominator. Numerator is a signed number. It has feature to adjust fractional portion of feedback divider dynamically. This fracn gppll is used in i.MX93. Reviewed-by: Abel Vesa <[email protected]> Reviewed-by: Sascha Hauer <[email protected]> Signed-off-by: Peng Fan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Abel Vesa <[email protected]>
1 parent 1199419 commit 1b26cb8

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed

drivers/clk/imx/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mxc-clk-objs += clk-busy.o
55
mxc-clk-objs += clk-composite-7ulp.o
66
mxc-clk-objs += clk-composite-8m.o
77
mxc-clk-objs += clk-composite-93.o
8+
mxc-clk-objs += clk-fracn-gppll.o
89
mxc-clk-objs += clk-cpu.o
910
mxc-clk-objs += clk-divider-gate.o
1011
mxc-clk-objs += clk-fixup-div.o

drivers/clk/imx/clk-fracn-gppll.c

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright 2021 NXP
4+
*/
5+
6+
#include <linux/bitfield.h>
7+
#include <linux/clk-provider.h>
8+
#include <linux/err.h>
9+
#include <linux/export.h>
10+
#include <linux/io.h>
11+
#include <linux/iopoll.h>
12+
#include <linux/slab.h>
13+
#include <asm/div64.h>
14+
15+
#include "clk.h"
16+
17+
#define PLL_CTRL 0x0
18+
#define CLKMUX_BYPASS BIT(2)
19+
#define CLKMUX_EN BIT(1)
20+
#define POWERUP_MASK BIT(0)
21+
22+
#define PLL_ANA_PRG 0x10
23+
#define PLL_SPREAD_SPECTRUM 0x30
24+
25+
#define PLL_NUMERATOR 0x40
26+
#define PLL_MFN_MASK GENMASK(31, 2)
27+
28+
#define PLL_DENOMINATOR 0x50
29+
#define PLL_MFD_MASK GENMASK(29, 0)
30+
31+
#define PLL_DIV 0x60
32+
#define PLL_MFI_MASK GENMASK(24, 16)
33+
#define PLL_RDIV_MASK GENMASK(15, 13)
34+
#define PLL_ODIV_MASK GENMASK(7, 0)
35+
36+
#define PLL_DFS_CTRL(x) (0x70 + (x) * 0x10)
37+
38+
#define PLL_STATUS 0xF0
39+
#define LOCK_STATUS BIT(0)
40+
41+
#define DFS_STATUS 0xF4
42+
43+
#define LOCK_TIMEOUT_US 200
44+
45+
#define PLL_FRACN_GP(_rate, _mfi, _mfn, _mfd, _rdiv, _odiv) \
46+
{ \
47+
.rate = (_rate), \
48+
.mfi = (_mfi), \
49+
.mfn = (_mfn), \
50+
.mfd = (_mfd), \
51+
.rdiv = (_rdiv), \
52+
.odiv = (_odiv), \
53+
}
54+
55+
struct clk_fracn_gppll {
56+
struct clk_hw hw;
57+
void __iomem *base;
58+
const struct imx_fracn_gppll_rate_table *rate_table;
59+
int rate_count;
60+
};
61+
62+
/*
63+
* Fvco = Fref * (MFI + MFN / MFD)
64+
* Fout = Fvco / (rdiv * odiv)
65+
*/
66+
static const struct imx_fracn_gppll_rate_table fracn_tbl[] = {
67+
PLL_FRACN_GP(650000000U, 81, 0, 0, 0, 3),
68+
PLL_FRACN_GP(594000000U, 198, 0, 0, 0, 8),
69+
PLL_FRACN_GP(560000000U, 70, 0, 0, 0, 3),
70+
PLL_FRACN_GP(400000000U, 50, 0, 0, 0, 3),
71+
PLL_FRACN_GP(393216000U, 81, 92, 100, 0, 5)
72+
};
73+
74+
struct imx_fracn_gppll_clk imx_fracn_gppll = {
75+
.rate_table = fracn_tbl,
76+
.rate_count = ARRAY_SIZE(fracn_tbl),
77+
};
78+
EXPORT_SYMBOL_GPL(imx_fracn_gppll);
79+
80+
static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw)
81+
{
82+
return container_of(hw, struct clk_fracn_gppll, hw);
83+
}
84+
85+
static const struct imx_fracn_gppll_rate_table *
86+
imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate)
87+
{
88+
const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
89+
int i;
90+
91+
for (i = 0; i < pll->rate_count; i++)
92+
if (rate == rate_table[i].rate)
93+
return &rate_table[i];
94+
95+
return NULL;
96+
}
97+
98+
static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate,
99+
unsigned long *prate)
100+
{
101+
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
102+
const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
103+
int i;
104+
105+
/* Assuming rate_table is in descending order */
106+
for (i = 0; i < pll->rate_count; i++)
107+
if (rate >= rate_table[i].rate)
108+
return rate_table[i].rate;
109+
110+
/* return minimum supported value */
111+
return rate_table[pll->rate_count - 1].rate;
112+
}
113+
114+
static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
115+
{
116+
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
117+
const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
118+
u32 pll_numerator, pll_denominator, pll_div;
119+
u32 mfi, mfn, mfd, rdiv, odiv;
120+
u64 fvco = parent_rate;
121+
long rate = 0;
122+
int i;
123+
124+
pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR);
125+
mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator);
126+
127+
pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR);
128+
mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator);
129+
130+
pll_div = readl_relaxed(pll->base + PLL_DIV);
131+
mfi = FIELD_GET(PLL_MFI_MASK, pll_div);
132+
133+
rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div);
134+
rdiv = rdiv + 1;
135+
odiv = FIELD_GET(PLL_ODIV_MASK, pll_div);
136+
switch (odiv) {
137+
case 0:
138+
odiv = 2;
139+
break;
140+
case 1:
141+
odiv = 3;
142+
break;
143+
default:
144+
break;
145+
}
146+
147+
/*
148+
* Sometimes, the recalculated rate has deviation due to
149+
* the frac part. So find the accurate pll rate from the table
150+
* first, if no match rate in the table, use the rate calculated
151+
* from the equation below.
152+
*/
153+
for (i = 0; i < pll->rate_count; i++) {
154+
if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi &&
155+
rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv &&
156+
rate_table[i].odiv == odiv)
157+
rate = rate_table[i].rate;
158+
}
159+
160+
if (rate)
161+
return (unsigned long)rate;
162+
163+
/* Fvco = Fref * (MFI + MFN / MFD) */
164+
fvco = fvco * mfi * mfd + fvco * mfn;
165+
do_div(fvco, mfd * rdiv * odiv);
166+
167+
return (unsigned long)fvco;
168+
}
169+
170+
static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll)
171+
{
172+
u32 val;
173+
174+
return readl_poll_timeout(pll->base + PLL_STATUS, val,
175+
val & LOCK_STATUS, 0, LOCK_TIMEOUT_US);
176+
}
177+
178+
static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
179+
unsigned long prate)
180+
{
181+
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
182+
const struct imx_fracn_gppll_rate_table *rate;
183+
u32 tmp, pll_div, ana_mfn;
184+
int ret;
185+
186+
rate = imx_get_pll_settings(pll, drate);
187+
188+
/* Disable output */
189+
tmp = readl_relaxed(pll->base + PLL_CTRL);
190+
tmp &= ~CLKMUX_EN;
191+
writel_relaxed(tmp, pll->base + PLL_CTRL);
192+
193+
/* Power Down */
194+
tmp &= ~POWERUP_MASK;
195+
writel_relaxed(tmp, pll->base + PLL_CTRL);
196+
197+
/* Disable BYPASS */
198+
tmp &= ~CLKMUX_BYPASS;
199+
writel_relaxed(tmp, pll->base + PLL_CTRL);
200+
201+
pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv |
202+
FIELD_PREP(PLL_MFI_MASK, rate->mfi);
203+
writel_relaxed(pll_div, pll->base + PLL_DIV);
204+
writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR);
205+
writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR);
206+
207+
/* Wait for 5us according to fracn mode pll doc */
208+
udelay(5);
209+
210+
/* Enable Powerup */
211+
tmp |= POWERUP_MASK;
212+
writel_relaxed(tmp, pll->base + PLL_CTRL);
213+
214+
/* Wait Lock */
215+
ret = clk_fracn_gppll_wait_lock(pll);
216+
if (ret)
217+
return ret;
218+
219+
/* Enable output */
220+
tmp |= CLKMUX_EN;
221+
writel_relaxed(tmp, pll->base + PLL_CTRL);
222+
223+
ana_mfn = readl_relaxed(pll->base + PLL_STATUS);
224+
ana_mfn = FIELD_GET(PLL_MFN_MASK, ana_mfn);
225+
226+
WARN(ana_mfn != rate->mfn, "ana_mfn != rate->mfn\n");
227+
228+
return 0;
229+
}
230+
231+
static int clk_fracn_gppll_prepare(struct clk_hw *hw)
232+
{
233+
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
234+
u32 val;
235+
int ret;
236+
237+
val = readl_relaxed(pll->base + PLL_CTRL);
238+
if (val & POWERUP_MASK)
239+
return 0;
240+
241+
val |= CLKMUX_BYPASS;
242+
writel_relaxed(val, pll->base + PLL_CTRL);
243+
244+
val |= POWERUP_MASK;
245+
writel_relaxed(val, pll->base + PLL_CTRL);
246+
247+
val |= CLKMUX_EN;
248+
writel_relaxed(val, pll->base + PLL_CTRL);
249+
250+
ret = clk_fracn_gppll_wait_lock(pll);
251+
if (ret)
252+
return ret;
253+
254+
val &= ~CLKMUX_BYPASS;
255+
writel_relaxed(val, pll->base + PLL_CTRL);
256+
257+
return 0;
258+
}
259+
260+
static int clk_fracn_gppll_is_prepared(struct clk_hw *hw)
261+
{
262+
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
263+
u32 val;
264+
265+
val = readl_relaxed(pll->base + PLL_CTRL);
266+
267+
return (val & POWERUP_MASK) ? 1 : 0;
268+
}
269+
270+
static void clk_fracn_gppll_unprepare(struct clk_hw *hw)
271+
{
272+
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
273+
u32 val;
274+
275+
val = readl_relaxed(pll->base + PLL_CTRL);
276+
val &= ~POWERUP_MASK;
277+
writel_relaxed(val, pll->base + PLL_CTRL);
278+
}
279+
280+
static const struct clk_ops clk_fracn_gppll_ops = {
281+
.prepare = clk_fracn_gppll_prepare,
282+
.unprepare = clk_fracn_gppll_unprepare,
283+
.is_prepared = clk_fracn_gppll_is_prepared,
284+
.recalc_rate = clk_fracn_gppll_recalc_rate,
285+
.round_rate = clk_fracn_gppll_round_rate,
286+
.set_rate = clk_fracn_gppll_set_rate,
287+
};
288+
289+
struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
290+
const struct imx_fracn_gppll_clk *pll_clk)
291+
{
292+
struct clk_fracn_gppll *pll;
293+
struct clk_hw *hw;
294+
struct clk_init_data init;
295+
int ret;
296+
297+
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
298+
if (!pll)
299+
return ERR_PTR(-ENOMEM);
300+
301+
init.name = name;
302+
init.flags = pll_clk->flags;
303+
init.parent_names = &parent_name;
304+
init.num_parents = 1;
305+
init.ops = &clk_fracn_gppll_ops;
306+
307+
pll->base = base;
308+
pll->hw.init = &init;
309+
pll->rate_table = pll_clk->rate_table;
310+
pll->rate_count = pll_clk->rate_count;
311+
312+
hw = &pll->hw;
313+
314+
ret = clk_hw_register(NULL, hw);
315+
if (ret) {
316+
pr_err("%s: failed to register pll %s %d\n", __func__, name, ret);
317+
kfree(pll);
318+
return ERR_PTR(ret);
319+
}
320+
321+
return hw;
322+
}
323+
EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll);

drivers/clk/imx/clk.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ extern struct imx_pll14xx_clk imx_1416x_pll;
7272
extern struct imx_pll14xx_clk imx_1443x_pll;
7373
extern struct imx_pll14xx_clk imx_1443x_dram_pll;
7474

75+
/* NOTE: Rate table should be kept sorted in descending order. */
76+
struct imx_fracn_gppll_rate_table {
77+
unsigned int rate;
78+
unsigned int mfi;
79+
unsigned int mfn;
80+
unsigned int mfd;
81+
unsigned int rdiv;
82+
unsigned int odiv;
83+
};
84+
85+
struct imx_fracn_gppll_clk {
86+
const struct imx_fracn_gppll_rate_table *rate_table;
87+
int rate_count;
88+
int flags;
89+
};
90+
91+
struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
92+
const struct imx_fracn_gppll_clk *pll_clk);
93+
94+
extern struct imx_fracn_gppll_clk imx_fracn_gppll;
95+
7596
#define imx_clk_cpu(name, parent_name, div, mux, pll, step) \
7697
to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step))
7798

0 commit comments

Comments
 (0)