blob: b0ccb6c8eda5d6a63bc796b6db2f57a8d2f58c26 [file] [log] [blame]
Peng Fanbbb58712019-08-19 07:53:58 +00001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2017-2019 NXP.
4 *
5 * Peng Fan <peng.fan@nxp.com>
6 */
7
8#include <common.h>
9#include <asm/io.h>
10#include <malloc.h>
11#include <clk-uclass.h>
12#include <dm/device.h>
Simon Glass61b29b82020-02-03 07:36:15 -070013#include <dm/devres.h>
Simon Glasscd93d622020-05-10 11:40:13 -060014#include <linux/bitops.h>
Peng Fanbbb58712019-08-19 07:53:58 +000015#include <linux/clk-provider.h>
Simon Glassc05ed002020-05-10 11:40:11 -060016#include <linux/delay.h>
Simon Glass61b29b82020-02-03 07:36:15 -070017#include <linux/err.h>
Peng Fanbbb58712019-08-19 07:53:58 +000018#include <linux/iopoll.h>
19#include <clk.h>
20#include <div64.h>
21
22#include "clk.h"
23
24#define UBOOT_DM_CLK_IMX_PLL1443X "imx_clk_pll1443x"
25#define UBOOT_DM_CLK_IMX_PLL1416X "imx_clk_pll1416x"
26
27#define GNRL_CTL 0x0
28#define DIV_CTL 0x4
29#define LOCK_STATUS BIT(31)
30#define LOCK_SEL_MASK BIT(29)
31#define CLKE_MASK BIT(11)
32#define RST_MASK BIT(9)
33#define BYPASS_MASK BIT(4)
34#define MDIV_SHIFT 12
35#define MDIV_MASK GENMASK(21, 12)
36#define PDIV_SHIFT 4
37#define PDIV_MASK GENMASK(9, 4)
38#define SDIV_SHIFT 0
39#define SDIV_MASK GENMASK(2, 0)
40#define KDIV_SHIFT 0
41#define KDIV_MASK GENMASK(15, 0)
42
43#define LOCK_TIMEOUT_US 10000
44
45struct clk_pll14xx {
46 struct clk clk;
47 void __iomem *base;
48 enum imx_pll14xx_type type;
49 const struct imx_pll14xx_rate_table *rate_table;
50 int rate_count;
51};
52
53#define to_clk_pll14xx(_clk) container_of(_clk, struct clk_pll14xx, clk)
54
55static const struct imx_pll14xx_rate_table *imx_get_pll_settings(
56 struct clk_pll14xx *pll, unsigned long rate)
57{
58 const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
59 int i;
60
61 for (i = 0; i < pll->rate_count; i++)
62 if (rate == rate_table[i].rate)
63 return &rate_table[i];
64
65 return NULL;
66}
67
68static unsigned long clk_pll1416x_recalc_rate(struct clk *clk)
69{
70 struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
71 u64 fvco = clk_get_parent_rate(clk);
72 u32 mdiv, pdiv, sdiv, pll_div;
73
74 pll_div = readl(pll->base + 4);
75 mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
76 pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
77 sdiv = (pll_div & SDIV_MASK) >> SDIV_SHIFT;
78
79 fvco *= mdiv;
80 do_div(fvco, pdiv << sdiv);
81
82 return fvco;
83}
84
85static unsigned long clk_pll1443x_recalc_rate(struct clk *clk)
86{
87 struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
88 u64 fvco = clk_get_parent_rate(clk);
89 u32 mdiv, pdiv, sdiv, pll_div_ctl0, pll_div_ctl1;
90 short int kdiv;
91
92 pll_div_ctl0 = readl(pll->base + 4);
93 pll_div_ctl1 = readl(pll->base + 8);
94 mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
95 pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
96 sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT;
97 kdiv = pll_div_ctl1 & KDIV_MASK;
98
99 /* fvco = (m * 65536 + k) * Fin / (p * 65536) */
100 fvco *= (mdiv * 65536 + kdiv);
101 pdiv *= 65536;
102
103 do_div(fvco, pdiv << sdiv);
104
105 return fvco;
106}
107
108static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate,
109 u32 pll_div)
110{
111 u32 old_mdiv, old_pdiv;
112
113 old_mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
114 old_pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
115
116 return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv;
117}
118
119static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate,
120 u32 pll_div_ctl0, u32 pll_div_ctl1)
121{
122 u32 old_mdiv, old_pdiv, old_kdiv;
123
124 old_mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
125 old_pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
126 old_kdiv = (pll_div_ctl1 & KDIV_MASK) >> KDIV_SHIFT;
127
128 return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
129 rate->kdiv != old_kdiv;
130}
131
132static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate,
133 u32 pll_div_ctl0, u32 pll_div_ctl1)
134{
135 u32 old_mdiv, old_pdiv, old_kdiv;
136
137 old_mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
138 old_pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
139 old_kdiv = (pll_div_ctl1 & KDIV_MASK) >> KDIV_SHIFT;
140
141 return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
142 rate->kdiv != old_kdiv;
143}
144
145static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll)
146{
147 u32 val;
148
149 return readl_poll_timeout(pll->base, val, val & LOCK_TIMEOUT_US,
150 LOCK_TIMEOUT_US);
151}
152
153static ulong clk_pll1416x_set_rate(struct clk *clk, unsigned long drate)
154{
155 struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
156 const struct imx_pll14xx_rate_table *rate;
157 u32 tmp, div_val;
158 int ret;
159
160 rate = imx_get_pll_settings(pll, drate);
161 if (!rate) {
162 pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
163 drate, "xxxx");
164 return -EINVAL;
165 }
166
167 tmp = readl(pll->base + 4);
168
169 if (!clk_pll1416x_mp_change(rate, tmp)) {
170 tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
171 tmp |= rate->sdiv << SDIV_SHIFT;
172 writel(tmp, pll->base + 4);
173
174 return clk_pll1416x_recalc_rate(clk);
175 }
176
177 /* Bypass clock and set lock to pll output lock */
178 tmp = readl(pll->base);
179 tmp |= LOCK_SEL_MASK;
180 writel(tmp, pll->base);
181
182 /* Enable RST */
183 tmp &= ~RST_MASK;
184 writel(tmp, pll->base);
185
186 /* Enable BYPASS */
187 tmp |= BYPASS_MASK;
188 writel(tmp, pll->base);
189
190
191 div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
192 (rate->sdiv << SDIV_SHIFT);
193 writel(div_val, pll->base + 0x4);
194
195 /*
196 * According to SPEC, t3 - t2 need to be greater than
197 * 1us and 1/FREF, respectively.
198 * FREF is FIN / Prediv, the prediv is [1, 63], so choose
199 * 3us.
200 */
201 udelay(3);
202
203 /* Disable RST */
204 tmp |= RST_MASK;
205 writel(tmp, pll->base);
206
207 /* Wait Lock */
208 ret = clk_pll14xx_wait_lock(pll);
209 if (ret)
210 return ret;
211
212 /* Bypass */
213 tmp &= ~BYPASS_MASK;
214 writel(tmp, pll->base);
215
216 return clk_pll1416x_recalc_rate(clk);
217}
218
219static ulong clk_pll1443x_set_rate(struct clk *clk, unsigned long drate)
220{
221 struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
222 const struct imx_pll14xx_rate_table *rate;
223 u32 tmp, div_val;
224 int ret;
225
226 rate = imx_get_pll_settings(pll, drate);
227 if (!rate) {
228 pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
229 drate, "===");
230 return -EINVAL;
231 }
232
233 tmp = readl(pll->base + 4);
234 div_val = readl(pll->base + 8);
235
236 if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) {
237 tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
238 tmp |= rate->sdiv << SDIV_SHIFT;
239 writel(tmp, pll->base + 4);
240
241 return clk_pll1443x_recalc_rate(clk);
242 }
243
244 tmp = readl(pll->base);
245
246 /* Enable RST */
247 tmp &= ~RST_MASK;
248 writel(tmp, pll->base);
249
250 /* Enable BYPASS */
251 tmp |= BYPASS_MASK;
252 writel(tmp, pll->base);
253
254 div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
255 (rate->sdiv << SDIV_SHIFT);
256 writel(div_val, pll->base + 0x4);
257 writel(rate->kdiv << KDIV_SHIFT, pll->base + 0x8);
258
259 /*
260 * According to SPEC, t3 - t2 need to be greater than
261 * 1us and 1/FREF, respectively.
262 * FREF is FIN / Prediv, the prediv is [1, 63], so choose
263 * 3us.
264 */
265 udelay(3);
266
267 /* Disable RST */
268 tmp |= RST_MASK;
269 writel(tmp, pll->base);
270
271 /* Wait Lock*/
272 ret = clk_pll14xx_wait_lock(pll);
273 if (ret)
274 return ret;
275
276 /* Bypass */
277 tmp &= ~BYPASS_MASK;
278 writel(tmp, pll->base);
279
280 return clk_pll1443x_recalc_rate(clk);
281}
282
283static int clk_pll14xx_prepare(struct clk *clk)
284{
285 struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
286 u32 val;
287
288 /*
289 * RESETB = 1 from 0, PLL starts its normal
290 * operation after lock time
291 */
292 val = readl(pll->base + GNRL_CTL);
293 val |= RST_MASK;
294 writel(val, pll->base + GNRL_CTL);
295
296 return clk_pll14xx_wait_lock(pll);
297}
298
299static int clk_pll14xx_unprepare(struct clk *clk)
300{
301 struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
302 u32 val;
303
304 /*
305 * Set RST to 0, power down mode is enabled and
306 * every digital block is reset
307 */
308 val = readl(pll->base + GNRL_CTL);
309 val &= ~RST_MASK;
310 writel(val, pll->base + GNRL_CTL);
311
312 return 0;
313}
314
315static const struct clk_ops clk_pll1416x_ops = {
316 .enable = clk_pll14xx_prepare,
317 .disable = clk_pll14xx_unprepare,
318 .set_rate = clk_pll1416x_set_rate,
319 .get_rate = clk_pll1416x_recalc_rate,
320};
321
322static const struct clk_ops clk_pll1443x_ops = {
323 .enable = clk_pll14xx_prepare,
324 .disable = clk_pll14xx_unprepare,
325 .set_rate = clk_pll1443x_set_rate,
326 .get_rate = clk_pll1443x_recalc_rate,
327};
328
329struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
330 void __iomem *base,
331 const struct imx_pll14xx_clk *pll_clk)
332{
333 struct clk_pll14xx *pll;
334 struct clk *clk;
335 char *type_name;
336 int ret;
337
338 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
339 if (!pll)
340 return ERR_PTR(-ENOMEM);
341
342 switch (pll_clk->type) {
343 case PLL_1416X:
344 type_name = UBOOT_DM_CLK_IMX_PLL1416X;
345 break;
346 case PLL_1443X:
347 type_name = UBOOT_DM_CLK_IMX_PLL1443X;
348 break;
349 default:
350 pr_err("%s: Unknown pll type for pll clk %s\n",
351 __func__, name);
352 return ERR_PTR(-EINVAL);
353 };
354
355 pll->base = base;
356 pll->type = pll_clk->type;
357 pll->rate_table = pll_clk->rate_table;
358 pll->rate_count = pll_clk->rate_count;
359
360 clk = &pll->clk;
361
362 ret = clk_register(clk, type_name, name, parent_name);
363 if (ret) {
364 pr_err("%s: failed to register pll %s %d\n",
365 __func__, name, ret);
366 kfree(pll);
367 return ERR_PTR(ret);
368 }
369
370 return clk;
371}
372
373U_BOOT_DRIVER(clk_pll1443x) = {
374 .name = UBOOT_DM_CLK_IMX_PLL1443X,
375 .id = UCLASS_CLK,
376 .ops = &clk_pll1443x_ops,
377 .flags = DM_FLAG_PRE_RELOC,
378};
379
380U_BOOT_DRIVER(clk_pll1416x) = {
381 .name = UBOOT_DM_CLK_IMX_PLL1416X,
382 .id = UCLASS_CLK,
383 .ops = &clk_pll1416x_ops,
384 .flags = DM_FLAG_PRE_RELOC,
385};