1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2015 Endless Mobile, Inc. |
4 | * Author: Carlo Caione <carlo@endlessm.com> |
5 | * |
6 | * Copyright (c) 2018 Baylibre, SAS. |
7 | * Author: Jerome Brunet <jbrunet@baylibre.com> |
8 | */ |
9 | |
10 | /* |
11 | * In the most basic form, a Meson PLL is composed as follows: |
12 | * |
13 | * PLL |
14 | * +--------------------------------+ |
15 | * | | |
16 | * | +--+ | |
17 | * in >>-----[ /N ]--->| | +-----+ | |
18 | * | | |------| DCO |---->> out |
19 | * | +--------->| | +--v--+ | |
20 | * | | +--+ | | |
21 | * | | | | |
22 | * | +--[ *(M + (F/Fmax) ]<--+ | |
23 | * | | |
24 | * +--------------------------------+ |
25 | * |
26 | * out = in * (m + frac / frac_max) / n |
27 | */ |
28 | |
29 | #include <linux/clk-provider.h> |
30 | #include <linux/delay.h> |
31 | #include <linux/err.h> |
32 | #include <linux/io.h> |
33 | #include <linux/math64.h> |
34 | #include <linux/module.h> |
35 | |
36 | #include "clk-regmap.h" |
37 | #include "clk-pll.h" |
38 | |
39 | static inline struct meson_clk_pll_data * |
40 | meson_clk_pll_data(struct clk_regmap *clk) |
41 | { |
42 | return (struct meson_clk_pll_data *)clk->data; |
43 | } |
44 | |
45 | static int __pll_round_closest_mult(struct meson_clk_pll_data *pll) |
46 | { |
47 | if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) && |
48 | !MESON_PARM_APPLICABLE(&pll->frac)) |
49 | return 1; |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static unsigned long __pll_params_to_rate(unsigned long parent_rate, |
55 | unsigned int m, unsigned int n, |
56 | unsigned int frac, |
57 | struct meson_clk_pll_data *pll) |
58 | { |
59 | u64 rate = (u64)parent_rate * m; |
60 | |
61 | if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { |
62 | u64 frac_rate = (u64)parent_rate * frac; |
63 | |
64 | rate += DIV_ROUND_UP_ULL(frac_rate, |
65 | (1 << pll->frac.width)); |
66 | } |
67 | |
68 | return DIV_ROUND_UP_ULL(rate, n); |
69 | } |
70 | |
71 | static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, |
72 | unsigned long parent_rate) |
73 | { |
74 | struct clk_regmap *clk = to_clk_regmap(hw); |
75 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
76 | unsigned int m, n, frac; |
77 | |
78 | n = meson_parm_read(map: clk->map, p: &pll->n); |
79 | |
80 | /* |
81 | * On some HW, N is set to zero on init. This value is invalid as |
82 | * it would result in a division by zero. The rate can't be |
83 | * calculated in this case |
84 | */ |
85 | if (n == 0) |
86 | return 0; |
87 | |
88 | m = meson_parm_read(map: clk->map, p: &pll->m); |
89 | |
90 | frac = MESON_PARM_APPLICABLE(&pll->frac) ? |
91 | meson_parm_read(map: clk->map, p: &pll->frac) : |
92 | 0; |
93 | |
94 | return __pll_params_to_rate(parent_rate, m, n, frac, pll); |
95 | } |
96 | |
97 | static unsigned int __pll_params_with_frac(unsigned long rate, |
98 | unsigned long parent_rate, |
99 | unsigned int m, |
100 | unsigned int n, |
101 | struct meson_clk_pll_data *pll) |
102 | { |
103 | unsigned int frac_max = (1 << pll->frac.width); |
104 | u64 val = (u64)rate * n; |
105 | |
106 | /* Bail out if we are already over the requested rate */ |
107 | if (rate < parent_rate * m / n) |
108 | return 0; |
109 | |
110 | if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) |
111 | val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); |
112 | else |
113 | val = div_u64(dividend: val * frac_max, divisor: parent_rate); |
114 | |
115 | val -= m * frac_max; |
116 | |
117 | return min((unsigned int)val, (frac_max - 1)); |
118 | } |
119 | |
120 | static bool meson_clk_pll_is_better(unsigned long rate, |
121 | unsigned long best, |
122 | unsigned long now, |
123 | struct meson_clk_pll_data *pll) |
124 | { |
125 | if (__pll_round_closest_mult(pll)) { |
126 | /* Round Closest */ |
127 | if (abs(now - rate) < abs(best - rate)) |
128 | return true; |
129 | } else { |
130 | /* Round down */ |
131 | if (now <= rate && best < now) |
132 | return true; |
133 | } |
134 | |
135 | return false; |
136 | } |
137 | |
138 | static int meson_clk_get_pll_table_index(unsigned int index, |
139 | unsigned int *m, |
140 | unsigned int *n, |
141 | struct meson_clk_pll_data *pll) |
142 | { |
143 | if (!pll->table[index].n) |
144 | return -EINVAL; |
145 | |
146 | *m = pll->table[index].m; |
147 | *n = pll->table[index].n; |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | static unsigned int meson_clk_get_pll_range_m(unsigned long rate, |
153 | unsigned long parent_rate, |
154 | unsigned int n, |
155 | struct meson_clk_pll_data *pll) |
156 | { |
157 | u64 val = (u64)rate * n; |
158 | |
159 | if (__pll_round_closest_mult(pll)) |
160 | return DIV_ROUND_CLOSEST_ULL(val, parent_rate); |
161 | |
162 | return div_u64(dividend: val, divisor: parent_rate); |
163 | } |
164 | |
165 | static int meson_clk_get_pll_range_index(unsigned long rate, |
166 | unsigned long parent_rate, |
167 | unsigned int index, |
168 | unsigned int *m, |
169 | unsigned int *n, |
170 | struct meson_clk_pll_data *pll) |
171 | { |
172 | *n = index + 1; |
173 | |
174 | /* Check the predivider range */ |
175 | if (*n >= (1 << pll->n.width)) |
176 | return -EINVAL; |
177 | |
178 | if (*n == 1) { |
179 | /* Get the boundaries out the way */ |
180 | if (rate <= pll->range->min * parent_rate) { |
181 | *m = pll->range->min; |
182 | return -ENODATA; |
183 | } else if (rate >= pll->range->max * parent_rate) { |
184 | *m = pll->range->max; |
185 | return -ENODATA; |
186 | } |
187 | } |
188 | |
189 | *m = meson_clk_get_pll_range_m(rate, parent_rate, n: *n, pll); |
190 | |
191 | /* the pre-divider gives a multiplier too big - stop */ |
192 | if (*m >= (1 << pll->m.width)) |
193 | return -EINVAL; |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static int meson_clk_get_pll_get_index(unsigned long rate, |
199 | unsigned long parent_rate, |
200 | unsigned int index, |
201 | unsigned int *m, |
202 | unsigned int *n, |
203 | struct meson_clk_pll_data *pll) |
204 | { |
205 | if (pll->range) |
206 | return meson_clk_get_pll_range_index(rate, parent_rate, |
207 | index, m, n, pll); |
208 | else if (pll->table) |
209 | return meson_clk_get_pll_table_index(index, m, n, pll); |
210 | |
211 | return -EINVAL; |
212 | } |
213 | |
214 | static int meson_clk_get_pll_settings(unsigned long rate, |
215 | unsigned long parent_rate, |
216 | unsigned int *best_m, |
217 | unsigned int *best_n, |
218 | struct meson_clk_pll_data *pll) |
219 | { |
220 | unsigned long best = 0, now = 0; |
221 | unsigned int i, m, n; |
222 | int ret; |
223 | |
224 | for (i = 0, ret = 0; !ret; i++) { |
225 | ret = meson_clk_get_pll_get_index(rate, parent_rate, |
226 | index: i, m: &m, n: &n, pll); |
227 | if (ret == -EINVAL) |
228 | break; |
229 | |
230 | now = __pll_params_to_rate(parent_rate, m, n, frac: 0, pll); |
231 | if (meson_clk_pll_is_better(rate, best, now, pll)) { |
232 | best = now; |
233 | *best_m = m; |
234 | *best_n = n; |
235 | |
236 | if (now == rate) |
237 | break; |
238 | } |
239 | } |
240 | |
241 | return best ? 0 : -EINVAL; |
242 | } |
243 | |
244 | static int meson_clk_pll_determine_rate(struct clk_hw *hw, |
245 | struct clk_rate_request *req) |
246 | { |
247 | struct clk_regmap *clk = to_clk_regmap(hw); |
248 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
249 | unsigned int m, n, frac; |
250 | unsigned long round; |
251 | int ret; |
252 | |
253 | ret = meson_clk_get_pll_settings(rate: req->rate, parent_rate: req->best_parent_rate, |
254 | best_m: &m, best_n: &n, pll); |
255 | if (ret) |
256 | return ret; |
257 | |
258 | round = __pll_params_to_rate(parent_rate: req->best_parent_rate, m, n, frac: 0, pll); |
259 | |
260 | if (!MESON_PARM_APPLICABLE(&pll->frac) || req->rate == round) { |
261 | req->rate = round; |
262 | return 0; |
263 | } |
264 | |
265 | /* |
266 | * The rate provided by the setting is not an exact match, let's |
267 | * try to improve the result using the fractional parameter |
268 | */ |
269 | frac = __pll_params_with_frac(rate: req->rate, parent_rate: req->best_parent_rate, m, n, pll); |
270 | req->rate = __pll_params_to_rate(parent_rate: req->best_parent_rate, m, n, frac, pll); |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | static int meson_clk_pll_wait_lock(struct clk_hw *hw) |
276 | { |
277 | struct clk_regmap *clk = to_clk_regmap(hw); |
278 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
279 | int delay = 5000; |
280 | |
281 | do { |
282 | /* Is the clock locked now ? Time out after 100ms. */ |
283 | if (meson_parm_read(map: clk->map, p: &pll->l)) |
284 | return 0; |
285 | |
286 | udelay(20); |
287 | } while (--delay); |
288 | |
289 | return -ETIMEDOUT; |
290 | } |
291 | |
292 | static int meson_clk_pll_init(struct clk_hw *hw) |
293 | { |
294 | struct clk_regmap *clk = to_clk_regmap(hw); |
295 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
296 | |
297 | if (pll->init_count) { |
298 | if (MESON_PARM_APPLICABLE(&pll->rst)) |
299 | meson_parm_write(map: clk->map, p: &pll->rst, val: 1); |
300 | |
301 | regmap_multi_reg_write(map: clk->map, regs: pll->init_regs, |
302 | num_regs: pll->init_count); |
303 | |
304 | if (MESON_PARM_APPLICABLE(&pll->rst)) |
305 | meson_parm_write(map: clk->map, p: &pll->rst, val: 0); |
306 | } |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static int meson_clk_pll_is_enabled(struct clk_hw *hw) |
312 | { |
313 | struct clk_regmap *clk = to_clk_regmap(hw); |
314 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
315 | |
316 | if (MESON_PARM_APPLICABLE(&pll->rst) && |
317 | meson_parm_read(map: clk->map, p: &pll->rst)) |
318 | return 0; |
319 | |
320 | if (!meson_parm_read(map: clk->map, p: &pll->en) || |
321 | !meson_parm_read(map: clk->map, p: &pll->l)) |
322 | return 0; |
323 | |
324 | return 1; |
325 | } |
326 | |
327 | static int meson_clk_pcie_pll_enable(struct clk_hw *hw) |
328 | { |
329 | int retries = 10; |
330 | |
331 | do { |
332 | meson_clk_pll_init(hw); |
333 | if (!meson_clk_pll_wait_lock(hw)) |
334 | return 0; |
335 | pr_info("Retry enabling PCIe PLL clock\n" ); |
336 | } while (--retries); |
337 | |
338 | return -EIO; |
339 | } |
340 | |
341 | static int meson_clk_pll_enable(struct clk_hw *hw) |
342 | { |
343 | struct clk_regmap *clk = to_clk_regmap(hw); |
344 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
345 | |
346 | /* do nothing if the PLL is already enabled */ |
347 | if (clk_hw_is_enabled(hw)) |
348 | return 0; |
349 | |
350 | /* Make sure the pll is in reset */ |
351 | if (MESON_PARM_APPLICABLE(&pll->rst)) |
352 | meson_parm_write(map: clk->map, p: &pll->rst, val: 1); |
353 | |
354 | /* Enable the pll */ |
355 | meson_parm_write(map: clk->map, p: &pll->en, val: 1); |
356 | |
357 | /* Take the pll out reset */ |
358 | if (MESON_PARM_APPLICABLE(&pll->rst)) |
359 | meson_parm_write(map: clk->map, p: &pll->rst, val: 0); |
360 | |
361 | /* |
362 | * Compared with the previous SoCs, self-adaption current module |
363 | * is newly added for A1, keep the new power-on sequence to enable the |
364 | * PLL. The sequence is: |
365 | * 1. enable the pll, delay for 10us |
366 | * 2. enable the pll self-adaption current module, delay for 40us |
367 | * 3. enable the lock detect module |
368 | */ |
369 | if (MESON_PARM_APPLICABLE(&pll->current_en)) { |
370 | udelay(10); |
371 | meson_parm_write(map: clk->map, p: &pll->current_en, val: 1); |
372 | udelay(40); |
373 | } |
374 | |
375 | if (MESON_PARM_APPLICABLE(&pll->l_detect)) { |
376 | meson_parm_write(map: clk->map, p: &pll->l_detect, val: 1); |
377 | meson_parm_write(map: clk->map, p: &pll->l_detect, val: 0); |
378 | } |
379 | |
380 | if (meson_clk_pll_wait_lock(hw)) |
381 | return -EIO; |
382 | |
383 | return 0; |
384 | } |
385 | |
386 | static void meson_clk_pll_disable(struct clk_hw *hw) |
387 | { |
388 | struct clk_regmap *clk = to_clk_regmap(hw); |
389 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
390 | |
391 | /* Put the pll is in reset */ |
392 | if (MESON_PARM_APPLICABLE(&pll->rst)) |
393 | meson_parm_write(map: clk->map, p: &pll->rst, val: 1); |
394 | |
395 | /* Disable the pll */ |
396 | meson_parm_write(map: clk->map, p: &pll->en, val: 0); |
397 | |
398 | /* Disable PLL internal self-adaption current module */ |
399 | if (MESON_PARM_APPLICABLE(&pll->current_en)) |
400 | meson_parm_write(map: clk->map, p: &pll->current_en, val: 0); |
401 | } |
402 | |
403 | static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, |
404 | unsigned long parent_rate) |
405 | { |
406 | struct clk_regmap *clk = to_clk_regmap(hw); |
407 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
408 | unsigned int enabled, m, n, frac = 0; |
409 | unsigned long old_rate; |
410 | int ret; |
411 | |
412 | if (parent_rate == 0 || rate == 0) |
413 | return -EINVAL; |
414 | |
415 | old_rate = clk_hw_get_rate(hw); |
416 | |
417 | ret = meson_clk_get_pll_settings(rate, parent_rate, best_m: &m, best_n: &n, pll); |
418 | if (ret) |
419 | return ret; |
420 | |
421 | enabled = meson_parm_read(map: clk->map, p: &pll->en); |
422 | if (enabled) |
423 | meson_clk_pll_disable(hw); |
424 | |
425 | meson_parm_write(map: clk->map, p: &pll->n, val: n); |
426 | meson_parm_write(map: clk->map, p: &pll->m, val: m); |
427 | |
428 | if (MESON_PARM_APPLICABLE(&pll->frac)) { |
429 | frac = __pll_params_with_frac(rate, parent_rate, m, n, pll); |
430 | meson_parm_write(map: clk->map, p: &pll->frac, val: frac); |
431 | } |
432 | |
433 | /* If the pll is stopped, bail out now */ |
434 | if (!enabled) |
435 | return 0; |
436 | |
437 | ret = meson_clk_pll_enable(hw); |
438 | if (ret) { |
439 | pr_warn("%s: pll did not lock, trying to restore old rate %lu\n" , |
440 | __func__, old_rate); |
441 | /* |
442 | * FIXME: Do we really need/want this HACK ? |
443 | * It looks unsafe. what happens if the clock gets into a |
444 | * broken state and we can't lock back on the old_rate ? Looks |
445 | * like an infinite recursion is possible |
446 | */ |
447 | meson_clk_pll_set_rate(hw, rate: old_rate, parent_rate); |
448 | } |
449 | |
450 | return ret; |
451 | } |
452 | |
453 | /* |
454 | * The Meson G12A PCIE PLL is fined tuned to deliver a very precise |
455 | * 100MHz reference clock for the PCIe Analog PHY, and thus requires |
456 | * a strict register sequence to enable the PLL. |
457 | * To simplify, re-use the _init() op to enable the PLL and keep |
458 | * the other ops except set_rate since the rate is fixed. |
459 | */ |
460 | const struct clk_ops meson_clk_pcie_pll_ops = { |
461 | .recalc_rate = meson_clk_pll_recalc_rate, |
462 | .determine_rate = meson_clk_pll_determine_rate, |
463 | .is_enabled = meson_clk_pll_is_enabled, |
464 | .enable = meson_clk_pcie_pll_enable, |
465 | .disable = meson_clk_pll_disable |
466 | }; |
467 | EXPORT_SYMBOL_GPL(meson_clk_pcie_pll_ops); |
468 | |
469 | const struct clk_ops meson_clk_pll_ops = { |
470 | .init = meson_clk_pll_init, |
471 | .recalc_rate = meson_clk_pll_recalc_rate, |
472 | .determine_rate = meson_clk_pll_determine_rate, |
473 | .set_rate = meson_clk_pll_set_rate, |
474 | .is_enabled = meson_clk_pll_is_enabled, |
475 | .enable = meson_clk_pll_enable, |
476 | .disable = meson_clk_pll_disable |
477 | }; |
478 | EXPORT_SYMBOL_GPL(meson_clk_pll_ops); |
479 | |
480 | const struct clk_ops meson_clk_pll_ro_ops = { |
481 | .recalc_rate = meson_clk_pll_recalc_rate, |
482 | .is_enabled = meson_clk_pll_is_enabled, |
483 | }; |
484 | EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); |
485 | |
486 | MODULE_DESCRIPTION("Amlogic PLL driver" ); |
487 | MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>" ); |
488 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>" ); |
489 | MODULE_LICENSE("GPL v2" ); |
490 | |