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