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