blob: 383f79cfbaf52abfe31f5a57ad08ea1cfae603a7 [file] [log] [blame]
Claudiu Beznea923ac872020-09-07 17:46:42 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * SAM9X60's PLL clock support.
4 *
5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
6 *
7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8 *
9 * Based on drivers/clk/at91/clk-sam9x60-pll.c from Linux.
10 *
11 */
12
13#include <asm/processor.h>
Tom Riniabb9a042024-05-18 20:20:43 -060014#include <common.h>
Claudiu Beznea923ac872020-09-07 17:46:42 +030015#include <clk-uclass.h>
16#include <div64.h>
17#include <dm.h>
18#include <linux/clk-provider.h>
19#include <linux/clk/at91_pmc.h>
20#include <linux/delay.h>
21
22#include "pmc.h"
23
24#define UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL "at91-sam9x60-div-pll-clk"
25#define UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL "at91-sam9x60-frac-pll-clk"
26
27#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
28#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
29#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
30
31#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
32#define UPLL_DIV 2
33#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
34
35#define FCORE_MIN (600000000)
36#define FCORE_MAX (1200000000)
37
38#define PLL_MAX_ID 7
39
40struct sam9x60_pll {
41 void __iomem *base;
42 const struct clk_pll_characteristics *characteristics;
43 const struct clk_pll_layout *layout;
44 struct clk clk;
45 u8 id;
46};
47
48#define to_sam9x60_pll(_clk) container_of(_clk, struct sam9x60_pll, clk)
49
50static inline bool sam9x60_pll_ready(void __iomem *base, int id)
51{
52 unsigned int status;
53
54 pmc_read(base, AT91_PMC_PLL_ISR0, &status);
55
56 return !!(status & BIT(id));
57}
58
59static long sam9x60_frac_pll_compute_mul_frac(u32 *mul, u32 *frac, ulong rate,
60 ulong parent_rate)
61{
62 unsigned long tmprate, remainder;
63 unsigned long nmul = 0;
64 unsigned long nfrac = 0;
65
66 if (rate < FCORE_MIN || rate > FCORE_MAX)
67 return -ERANGE;
68
69 /*
70 * Calculate the multiplier associated with the current
71 * divider that provide the closest rate to the requested one.
72 */
73 nmul = mult_frac(rate, 1, parent_rate);
74 tmprate = mult_frac(parent_rate, nmul, 1);
75 remainder = rate - tmprate;
76
77 if (remainder) {
78 nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
79 parent_rate);
80
81 tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
82 (1 << 22));
83 }
84
85 /* Check if resulted rate is valid. */
86 if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
87 return -ERANGE;
88
89 *mul = nmul - 1;
90 *frac = nfrac;
91
92 return tmprate;
93}
94
95static ulong sam9x60_frac_pll_set_rate(struct clk *clk, ulong rate)
96{
97 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
98 void __iomem *base = pll->base;
99 ulong parent_rate = clk_get_parent_rate(clk);
100 u32 nmul, cmul, nfrac, cfrac, val;
101 bool ready = sam9x60_pll_ready(base, pll->id);
102 long ret;
103
104 if (!parent_rate)
105 return 0;
106
107 ret = sam9x60_frac_pll_compute_mul_frac(&nmul, &nfrac, rate,
108 parent_rate);
109 if (ret < 0)
110 return 0;
111
112 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
113 pll->id);
114 pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
115 cmul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
116 cfrac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
117
118 /* Check against current values. */
119 if (sam9x60_pll_ready(base, pll->id) &&
120 nmul == cmul && nfrac == cfrac)
121 return 0;
122
123 /* Update it to hardware. */
124 pmc_write(base, AT91_PMC_PLL_CTRL1,
125 (nmul << pll->layout->mul_shift) |
126 (nfrac << pll->layout->frac_shift));
127
128 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
129 AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
130 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
131
132 while (ready && !sam9x60_pll_ready(base, pll->id)) {
133 debug("waiting for pll %u...\n", pll->id);
134 cpu_relax();
135 }
136
137 return parent_rate * (nmul + 1) + ((u64)parent_rate * nfrac >> 22);
138}
139
140static ulong sam9x60_frac_pll_get_rate(struct clk *clk)
141{
142 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
143 void __iomem *base = pll->base;
144 ulong parent_rate = clk_get_parent_rate(clk);
145 u32 mul, frac, val;
146
147 if (!parent_rate)
148 return 0;
149
150 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
151 pll->id);
152 pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
153 mul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
154 frac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
155
156 return (parent_rate * (mul + 1) + ((u64)parent_rate * frac >> 22));
157}
158
159static int sam9x60_frac_pll_enable(struct clk *clk)
160{
161 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
162 void __iomem *base = pll->base;
163 unsigned int val;
164 ulong crate;
165
166 crate = sam9x60_frac_pll_get_rate(clk);
167 if (crate < FCORE_MIN || crate > FCORE_MAX)
168 return -ERANGE;
169
170 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
171 pll->id);
172 pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
173
174 if (sam9x60_pll_ready(base, pll->id))
175 return 0;
176
177 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
178 AT91_PMC_PMM_UPDT_STUPTIM_MSK |
179 AT91_PMC_PLL_UPDT_ID_MSK,
180 AT91_PMC_PLL_UPDT_STUPTIM(0x3f) | pll->id);
181
182 /* Recommended value for AT91_PMC_PLL_ACR */
183 if (pll->characteristics->upll)
184 val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
185 else
186 val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
187 pmc_write(base, AT91_PMC_PLL_ACR, val);
188
189 if (pll->characteristics->upll) {
190 /* Enable the UTMI internal bandgap */
191 val |= AT91_PMC_PLL_ACR_UTMIBG;
192 pmc_write(base, AT91_PMC_PLL_ACR, val);
193
194 udelay(10);
195
196 /* Enable the UTMI internal regulator */
197 val |= AT91_PMC_PLL_ACR_UTMIVR;
198 pmc_write(base, AT91_PMC_PLL_ACR, val);
199
200 udelay(10);
201
202 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
203 AT91_PMC_PLL_UPDT_UPDATE |
204 AT91_PMC_PLL_UPDT_ID_MSK,
205 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
206 }
207
208 pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
209 AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
210 AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
211
212 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
213 AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
214 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
215
216 while (!sam9x60_pll_ready(base, pll->id)) {
217 debug("waiting for pll %u...\n", pll->id);
218 cpu_relax();
219 }
220
221 return 0;
222}
223
224static int sam9x60_frac_pll_disable(struct clk *clk)
225{
226 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
227 void __iomem *base = pll->base;
228
229 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
230 pll->id);
231
232 pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
233 AT91_PMC_PLL_CTRL0_ENPLL, 0);
234
235 if (pll->characteristics->upll)
236 pmc_update_bits(base, AT91_PMC_PLL_ACR,
237 AT91_PMC_PLL_ACR_UTMIBG |
238 AT91_PMC_PLL_ACR_UTMIVR, 0);
239
240 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
241 AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
242 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
243
244 return 0;
245}
246
247static const struct clk_ops sam9x60_frac_pll_ops = {
248 .enable = sam9x60_frac_pll_enable,
249 .disable = sam9x60_frac_pll_disable,
250 .set_rate = sam9x60_frac_pll_set_rate,
251 .get_rate = sam9x60_frac_pll_get_rate,
252};
253
254static int sam9x60_div_pll_enable(struct clk *clk)
255{
256 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
257 void __iomem *base = pll->base;
258 unsigned int val;
259
260 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
261 pll->id);
262 pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
263
264 /* Stop if enabled. */
265 if (val & pll->layout->endiv_mask)
266 return 0;
267
268 pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
269 pll->layout->endiv_mask,
270 (1 << pll->layout->endiv_shift));
271
272 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
273 AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
274 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
275
276 while (!sam9x60_pll_ready(base, pll->id)) {
277 debug("waiting for pll %u...\n", pll->id);
278 cpu_relax();
279 }
280
281 return 0;
282}
283
284static int sam9x60_div_pll_disable(struct clk *clk)
285{
286 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
287 void __iomem *base = pll->base;
288
289 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
290 pll->id);
291
292 pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
293 pll->layout->endiv_mask, 0);
294
295 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
296 AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
297 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
298
299 return 0;
300}
301
302static ulong sam9x60_div_pll_set_rate(struct clk *clk, ulong rate)
303{
304 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
305 void __iomem *base = pll->base;
306 const struct clk_pll_characteristics *characteristics =
307 pll->characteristics;
308 ulong parent_rate = clk_get_parent_rate(clk);
309 u8 div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate) - 1;
310 ulong req_rate = parent_rate / (div + 1);
311 bool ready = sam9x60_pll_ready(base, pll->id);
312 u32 val;
313
314 if (!parent_rate || div > pll->layout->div_mask ||
315 req_rate < characteristics->output[0].min ||
316 req_rate > characteristics->output[0].max)
317 return 0;
318
319 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
320 pll->id);
321 pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
322 /* Compare against current value. */
323 if (div == ((val & pll->layout->div_mask) >> pll->layout->div_shift))
324 return 0;
325
326 /* Update it to hardware. */
327 pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
328 pll->layout->div_mask,
329 div << pll->layout->div_shift);
330
331 pmc_update_bits(base, AT91_PMC_PLL_UPDT,
332 AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
333 AT91_PMC_PLL_UPDT_UPDATE | pll->id);
334
335 while (ready && !sam9x60_pll_ready(base, pll->id)) {
336 debug("waiting for pll %u...\n", pll->id);
337 cpu_relax();
338 }
339
340 return req_rate;
341}
342
343static ulong sam9x60_div_pll_get_rate(struct clk *clk)
344{
345 struct sam9x60_pll *pll = to_sam9x60_pll(clk);
346 void __iomem *base = pll->base;
347 ulong parent_rate = clk_get_parent_rate(clk);
348 u32 val;
349 u8 div;
350
351 if (!parent_rate)
352 return 0;
353
354 pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
355 pll->id);
356
357 pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
358
359 div = (val & pll->layout->div_mask) >> pll->layout->div_shift;
360
361 return parent_rate / (div + 1);
362}
363
364static const struct clk_ops sam9x60_div_pll_ops = {
365 .enable = sam9x60_div_pll_enable,
366 .disable = sam9x60_div_pll_disable,
367 .set_rate = sam9x60_div_pll_set_rate,
368 .get_rate = sam9x60_div_pll_get_rate,
369};
370
371static struct clk *
372sam9x60_clk_register_pll(void __iomem *base, const char *type,
373 const char *name, const char *parent_name, u8 id,
374 const struct clk_pll_characteristics *characteristics,
375 const struct clk_pll_layout *layout, u32 flags)
376{
377 struct sam9x60_pll *pll;
378 struct clk *clk;
379 int ret;
380
381 if (!base || !type || !name || !parent_name || !characteristics ||
382 !layout || id > PLL_MAX_ID)
383 return ERR_PTR(-EINVAL);
384
385 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
386 if (!pll)
387 return ERR_PTR(-ENOMEM);
388
389 pll->id = id;
390 pll->characteristics = characteristics;
391 pll->layout = layout;
392 pll->base = base;
393 clk = &pll->clk;
394 clk->flags = flags;
395
396 ret = clk_register(clk, type, name, parent_name);
397 if (ret) {
398 kfree(pll);
399 clk = ERR_PTR(ret);
400 }
401
402 return clk;
403}
404
405struct clk *
406sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
407 const char *parent_name, u8 id,
408 const struct clk_pll_characteristics *characteristics,
409 const struct clk_pll_layout *layout, bool critical)
410{
411 return sam9x60_clk_register_pll(base,
412 UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL, name, parent_name, id,
413 characteristics, layout,
414 CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
415}
416
417struct clk *
418sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
419 const char *parent_name, u8 id,
420 const struct clk_pll_characteristics *characteristics,
421 const struct clk_pll_layout *layout, bool critical)
422{
423 return sam9x60_clk_register_pll(base,
424 UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL, name, parent_name, id,
425 characteristics, layout,
426 CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
427}
428
429U_BOOT_DRIVER(at91_sam9x60_div_pll_clk) = {
430 .name = UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL,
431 .id = UCLASS_CLK,
432 .ops = &sam9x60_div_pll_ops,
433 .flags = DM_FLAG_PRE_RELOC,
434};
435
436U_BOOT_DRIVER(at91_sam9x60_frac_pll_clk) = {
437 .name = UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL,
438 .id = UCLASS_CLK,
439 .ops = &sam9x60_frac_pll_ops,
440 .flags = DM_FLAG_PRE_RELOC,
441};