1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> |
4 | */ |
5 | |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/clkdev.h> |
8 | #include <linux/clk/at91_pmc.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/mfd/syscon.h> |
11 | #include <linux/regmap.h> |
12 | |
13 | #include "pmc.h" |
14 | |
15 | #define SLOW_CLOCK_FREQ 32768 |
16 | #define MAINF_DIV 16 |
17 | #define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \ |
18 | SLOW_CLOCK_FREQ) |
19 | #define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ) |
20 | #define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT |
21 | |
22 | #define MOR_KEY_MASK (0xff << 16) |
23 | |
24 | #define clk_main_parent_select(s) (((s) & \ |
25 | (AT91_PMC_MOSCEN | \ |
26 | AT91_PMC_OSCBYPASS)) ? 1 : 0) |
27 | |
28 | struct clk_main_osc { |
29 | struct clk_hw hw; |
30 | struct regmap *regmap; |
31 | struct at91_clk_pms pms; |
32 | }; |
33 | |
34 | #define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw) |
35 | |
36 | struct clk_main_rc_osc { |
37 | struct clk_hw hw; |
38 | struct regmap *regmap; |
39 | unsigned long frequency; |
40 | unsigned long accuracy; |
41 | struct at91_clk_pms pms; |
42 | }; |
43 | |
44 | #define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw) |
45 | |
46 | struct clk_rm9200_main { |
47 | struct clk_hw hw; |
48 | struct regmap *regmap; |
49 | }; |
50 | |
51 | #define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw) |
52 | |
53 | struct clk_sam9x5_main { |
54 | struct clk_hw hw; |
55 | struct regmap *regmap; |
56 | struct at91_clk_pms pms; |
57 | u8 parent; |
58 | }; |
59 | |
60 | #define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw) |
61 | |
62 | static inline bool clk_main_osc_ready(struct regmap *regmap) |
63 | { |
64 | unsigned int status; |
65 | |
66 | regmap_read(map: regmap, AT91_PMC_SR, val: &status); |
67 | |
68 | return status & AT91_PMC_MOSCS; |
69 | } |
70 | |
71 | static int clk_main_osc_prepare(struct clk_hw *hw) |
72 | { |
73 | struct clk_main_osc *osc = to_clk_main_osc(hw); |
74 | struct regmap *regmap = osc->regmap; |
75 | u32 tmp; |
76 | |
77 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &tmp); |
78 | tmp &= ~MOR_KEY_MASK; |
79 | |
80 | if (tmp & AT91_PMC_OSCBYPASS) |
81 | return 0; |
82 | |
83 | if (!(tmp & AT91_PMC_MOSCEN)) { |
84 | tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY; |
85 | regmap_write(map: regmap, AT91_CKGR_MOR, val: tmp); |
86 | } |
87 | |
88 | while (!clk_main_osc_ready(regmap)) |
89 | cpu_relax(); |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | static void clk_main_osc_unprepare(struct clk_hw *hw) |
95 | { |
96 | struct clk_main_osc *osc = to_clk_main_osc(hw); |
97 | struct regmap *regmap = osc->regmap; |
98 | u32 tmp; |
99 | |
100 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &tmp); |
101 | if (tmp & AT91_PMC_OSCBYPASS) |
102 | return; |
103 | |
104 | if (!(tmp & AT91_PMC_MOSCEN)) |
105 | return; |
106 | |
107 | tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN); |
108 | regmap_write(map: regmap, AT91_CKGR_MOR, val: tmp | AT91_PMC_KEY); |
109 | } |
110 | |
111 | static int clk_main_osc_is_prepared(struct clk_hw *hw) |
112 | { |
113 | struct clk_main_osc *osc = to_clk_main_osc(hw); |
114 | struct regmap *regmap = osc->regmap; |
115 | u32 tmp, status; |
116 | |
117 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &tmp); |
118 | if (tmp & AT91_PMC_OSCBYPASS) |
119 | return 1; |
120 | |
121 | regmap_read(map: regmap, AT91_PMC_SR, val: &status); |
122 | |
123 | return (status & AT91_PMC_MOSCS) && clk_main_parent_select(tmp); |
124 | } |
125 | |
126 | static int clk_main_osc_save_context(struct clk_hw *hw) |
127 | { |
128 | struct clk_main_osc *osc = to_clk_main_osc(hw); |
129 | |
130 | osc->pms.status = clk_main_osc_is_prepared(hw); |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static void clk_main_osc_restore_context(struct clk_hw *hw) |
136 | { |
137 | struct clk_main_osc *osc = to_clk_main_osc(hw); |
138 | |
139 | if (osc->pms.status) |
140 | clk_main_osc_prepare(hw); |
141 | } |
142 | |
143 | static const struct clk_ops main_osc_ops = { |
144 | .prepare = clk_main_osc_prepare, |
145 | .unprepare = clk_main_osc_unprepare, |
146 | .is_prepared = clk_main_osc_is_prepared, |
147 | .save_context = clk_main_osc_save_context, |
148 | .restore_context = clk_main_osc_restore_context, |
149 | }; |
150 | |
151 | struct clk_hw * __init |
152 | at91_clk_register_main_osc(struct regmap *regmap, |
153 | const char *name, |
154 | const char *parent_name, |
155 | struct clk_parent_data *parent_data, |
156 | bool bypass) |
157 | { |
158 | struct clk_main_osc *osc; |
159 | struct clk_init_data init = {}; |
160 | struct clk_hw *hw; |
161 | int ret; |
162 | |
163 | if (!name || !(parent_name || parent_data)) |
164 | return ERR_PTR(error: -EINVAL); |
165 | |
166 | osc = kzalloc(size: sizeof(*osc), GFP_KERNEL); |
167 | if (!osc) |
168 | return ERR_PTR(error: -ENOMEM); |
169 | |
170 | init.name = name; |
171 | init.ops = &main_osc_ops; |
172 | if (parent_data) |
173 | init.parent_data = (const struct clk_parent_data *)parent_data; |
174 | else |
175 | init.parent_names = &parent_name; |
176 | init.num_parents = 1; |
177 | init.flags = CLK_IGNORE_UNUSED; |
178 | |
179 | osc->hw.init = &init; |
180 | osc->regmap = regmap; |
181 | |
182 | if (bypass) |
183 | regmap_update_bits(map: regmap, |
184 | AT91_CKGR_MOR, MOR_KEY_MASK | |
185 | AT91_PMC_OSCBYPASS, |
186 | AT91_PMC_OSCBYPASS | AT91_PMC_KEY); |
187 | |
188 | hw = &osc->hw; |
189 | ret = clk_hw_register(NULL, hw: &osc->hw); |
190 | if (ret) { |
191 | kfree(objp: osc); |
192 | hw = ERR_PTR(error: ret); |
193 | } |
194 | |
195 | return hw; |
196 | } |
197 | |
198 | static bool clk_main_rc_osc_ready(struct regmap *regmap) |
199 | { |
200 | unsigned int status; |
201 | |
202 | regmap_read(map: regmap, AT91_PMC_SR, val: &status); |
203 | |
204 | return !!(status & AT91_PMC_MOSCRCS); |
205 | } |
206 | |
207 | static int clk_main_rc_osc_prepare(struct clk_hw *hw) |
208 | { |
209 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
210 | struct regmap *regmap = osc->regmap; |
211 | unsigned int mor; |
212 | |
213 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &mor); |
214 | |
215 | if (!(mor & AT91_PMC_MOSCRCEN)) |
216 | regmap_update_bits(map: regmap, AT91_CKGR_MOR, |
217 | MOR_KEY_MASK | AT91_PMC_MOSCRCEN, |
218 | AT91_PMC_MOSCRCEN | AT91_PMC_KEY); |
219 | |
220 | while (!clk_main_rc_osc_ready(regmap)) |
221 | cpu_relax(); |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | static void clk_main_rc_osc_unprepare(struct clk_hw *hw) |
227 | { |
228 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
229 | struct regmap *regmap = osc->regmap; |
230 | unsigned int mor; |
231 | |
232 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &mor); |
233 | |
234 | if (!(mor & AT91_PMC_MOSCRCEN)) |
235 | return; |
236 | |
237 | regmap_update_bits(map: regmap, AT91_CKGR_MOR, |
238 | MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); |
239 | } |
240 | |
241 | static int clk_main_rc_osc_is_prepared(struct clk_hw *hw) |
242 | { |
243 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
244 | struct regmap *regmap = osc->regmap; |
245 | unsigned int mor, status; |
246 | |
247 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &mor); |
248 | regmap_read(map: regmap, AT91_PMC_SR, val: &status); |
249 | |
250 | return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS); |
251 | } |
252 | |
253 | static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw, |
254 | unsigned long parent_rate) |
255 | { |
256 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
257 | |
258 | return osc->frequency; |
259 | } |
260 | |
261 | static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw, |
262 | unsigned long parent_acc) |
263 | { |
264 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
265 | |
266 | return osc->accuracy; |
267 | } |
268 | |
269 | static int clk_main_rc_osc_save_context(struct clk_hw *hw) |
270 | { |
271 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
272 | |
273 | osc->pms.status = clk_main_rc_osc_is_prepared(hw); |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | static void clk_main_rc_osc_restore_context(struct clk_hw *hw) |
279 | { |
280 | struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); |
281 | |
282 | if (osc->pms.status) |
283 | clk_main_rc_osc_prepare(hw); |
284 | } |
285 | |
286 | static const struct clk_ops main_rc_osc_ops = { |
287 | .prepare = clk_main_rc_osc_prepare, |
288 | .unprepare = clk_main_rc_osc_unprepare, |
289 | .is_prepared = clk_main_rc_osc_is_prepared, |
290 | .recalc_rate = clk_main_rc_osc_recalc_rate, |
291 | .recalc_accuracy = clk_main_rc_osc_recalc_accuracy, |
292 | .save_context = clk_main_rc_osc_save_context, |
293 | .restore_context = clk_main_rc_osc_restore_context, |
294 | }; |
295 | |
296 | struct clk_hw * __init |
297 | at91_clk_register_main_rc_osc(struct regmap *regmap, |
298 | const char *name, |
299 | u32 frequency, u32 accuracy) |
300 | { |
301 | struct clk_main_rc_osc *osc; |
302 | struct clk_init_data init; |
303 | struct clk_hw *hw; |
304 | int ret; |
305 | |
306 | if (!name || !frequency) |
307 | return ERR_PTR(error: -EINVAL); |
308 | |
309 | osc = kzalloc(size: sizeof(*osc), GFP_KERNEL); |
310 | if (!osc) |
311 | return ERR_PTR(error: -ENOMEM); |
312 | |
313 | init.name = name; |
314 | init.ops = &main_rc_osc_ops; |
315 | init.parent_names = NULL; |
316 | init.num_parents = 0; |
317 | init.flags = CLK_IGNORE_UNUSED; |
318 | |
319 | osc->hw.init = &init; |
320 | osc->regmap = regmap; |
321 | osc->frequency = frequency; |
322 | osc->accuracy = accuracy; |
323 | |
324 | hw = &osc->hw; |
325 | ret = clk_hw_register(NULL, hw); |
326 | if (ret) { |
327 | kfree(objp: osc); |
328 | hw = ERR_PTR(error: ret); |
329 | } |
330 | |
331 | return hw; |
332 | } |
333 | |
334 | static int clk_main_probe_frequency(struct regmap *regmap) |
335 | { |
336 | unsigned long prep_time, timeout; |
337 | unsigned int mcfr; |
338 | |
339 | timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT); |
340 | do { |
341 | prep_time = jiffies; |
342 | regmap_read(map: regmap, AT91_CKGR_MCFR, val: &mcfr); |
343 | if (mcfr & AT91_PMC_MAINRDY) |
344 | return 0; |
345 | if (system_state < SYSTEM_RUNNING) |
346 | udelay(MAINF_LOOP_MIN_WAIT); |
347 | else |
348 | usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT); |
349 | } while (time_before(prep_time, timeout)); |
350 | |
351 | return -ETIMEDOUT; |
352 | } |
353 | |
354 | static unsigned long clk_main_recalc_rate(struct regmap *regmap, |
355 | unsigned long parent_rate) |
356 | { |
357 | unsigned int mcfr; |
358 | |
359 | if (parent_rate) |
360 | return parent_rate; |
361 | |
362 | pr_warn("Main crystal frequency not set, using approximate value\n" ); |
363 | regmap_read(map: regmap, AT91_CKGR_MCFR, val: &mcfr); |
364 | if (!(mcfr & AT91_PMC_MAINRDY)) |
365 | return 0; |
366 | |
367 | return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV; |
368 | } |
369 | |
370 | static int clk_rm9200_main_prepare(struct clk_hw *hw) |
371 | { |
372 | struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); |
373 | |
374 | return clk_main_probe_frequency(regmap: clkmain->regmap); |
375 | } |
376 | |
377 | static int clk_rm9200_main_is_prepared(struct clk_hw *hw) |
378 | { |
379 | struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); |
380 | unsigned int status; |
381 | |
382 | regmap_read(map: clkmain->regmap, AT91_CKGR_MCFR, val: &status); |
383 | |
384 | return !!(status & AT91_PMC_MAINRDY); |
385 | } |
386 | |
387 | static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw, |
388 | unsigned long parent_rate) |
389 | { |
390 | struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); |
391 | |
392 | return clk_main_recalc_rate(regmap: clkmain->regmap, parent_rate); |
393 | } |
394 | |
395 | static const struct clk_ops rm9200_main_ops = { |
396 | .prepare = clk_rm9200_main_prepare, |
397 | .is_prepared = clk_rm9200_main_is_prepared, |
398 | .recalc_rate = clk_rm9200_main_recalc_rate, |
399 | }; |
400 | |
401 | struct clk_hw * __init |
402 | at91_clk_register_rm9200_main(struct regmap *regmap, |
403 | const char *name, |
404 | const char *parent_name, |
405 | struct clk_hw *parent_hw) |
406 | { |
407 | struct clk_rm9200_main *clkmain; |
408 | struct clk_init_data init = {}; |
409 | struct clk_hw *hw; |
410 | int ret; |
411 | |
412 | if (!name) |
413 | return ERR_PTR(error: -EINVAL); |
414 | |
415 | if (!(parent_name || parent_hw)) |
416 | return ERR_PTR(error: -EINVAL); |
417 | |
418 | clkmain = kzalloc(size: sizeof(*clkmain), GFP_KERNEL); |
419 | if (!clkmain) |
420 | return ERR_PTR(error: -ENOMEM); |
421 | |
422 | init.name = name; |
423 | init.ops = &rm9200_main_ops; |
424 | if (parent_hw) |
425 | init.parent_hws = (const struct clk_hw **)&parent_hw; |
426 | else |
427 | init.parent_names = &parent_name; |
428 | init.num_parents = 1; |
429 | init.flags = 0; |
430 | |
431 | clkmain->hw.init = &init; |
432 | clkmain->regmap = regmap; |
433 | |
434 | hw = &clkmain->hw; |
435 | ret = clk_hw_register(NULL, hw: &clkmain->hw); |
436 | if (ret) { |
437 | kfree(objp: clkmain); |
438 | hw = ERR_PTR(error: ret); |
439 | } |
440 | |
441 | return hw; |
442 | } |
443 | |
444 | static inline bool clk_sam9x5_main_ready(struct regmap *regmap) |
445 | { |
446 | unsigned int status; |
447 | |
448 | regmap_read(map: regmap, AT91_PMC_SR, val: &status); |
449 | |
450 | return !!(status & AT91_PMC_MOSCSELS); |
451 | } |
452 | |
453 | static int clk_sam9x5_main_prepare(struct clk_hw *hw) |
454 | { |
455 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
456 | struct regmap *regmap = clkmain->regmap; |
457 | |
458 | while (!clk_sam9x5_main_ready(regmap)) |
459 | cpu_relax(); |
460 | |
461 | return clk_main_probe_frequency(regmap); |
462 | } |
463 | |
464 | static int clk_sam9x5_main_is_prepared(struct clk_hw *hw) |
465 | { |
466 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
467 | |
468 | return clk_sam9x5_main_ready(regmap: clkmain->regmap); |
469 | } |
470 | |
471 | static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw, |
472 | unsigned long parent_rate) |
473 | { |
474 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
475 | |
476 | return clk_main_recalc_rate(regmap: clkmain->regmap, parent_rate); |
477 | } |
478 | |
479 | static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index) |
480 | { |
481 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
482 | struct regmap *regmap = clkmain->regmap; |
483 | unsigned int tmp; |
484 | |
485 | if (index > 1) |
486 | return -EINVAL; |
487 | |
488 | regmap_read(map: regmap, AT91_CKGR_MOR, val: &tmp); |
489 | |
490 | if (index && !(tmp & AT91_PMC_MOSCSEL)) |
491 | tmp = AT91_PMC_MOSCSEL; |
492 | else if (!index && (tmp & AT91_PMC_MOSCSEL)) |
493 | tmp = 0; |
494 | else |
495 | return 0; |
496 | |
497 | regmap_update_bits(map: regmap, AT91_CKGR_MOR, |
498 | AT91_PMC_MOSCSEL | MOR_KEY_MASK, |
499 | val: tmp | AT91_PMC_KEY); |
500 | |
501 | while (!clk_sam9x5_main_ready(regmap)) |
502 | cpu_relax(); |
503 | |
504 | return 0; |
505 | } |
506 | |
507 | static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw) |
508 | { |
509 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
510 | unsigned int status; |
511 | |
512 | regmap_read(map: clkmain->regmap, AT91_CKGR_MOR, val: &status); |
513 | |
514 | return clk_main_parent_select(status); |
515 | } |
516 | |
517 | static int clk_sam9x5_main_save_context(struct clk_hw *hw) |
518 | { |
519 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
520 | |
521 | clkmain->pms.status = clk_main_rc_osc_is_prepared(hw: &clkmain->hw); |
522 | clkmain->pms.parent = clk_sam9x5_main_get_parent(hw: &clkmain->hw); |
523 | |
524 | return 0; |
525 | } |
526 | |
527 | static void clk_sam9x5_main_restore_context(struct clk_hw *hw) |
528 | { |
529 | struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); |
530 | int ret; |
531 | |
532 | ret = clk_sam9x5_main_set_parent(hw, index: clkmain->pms.parent); |
533 | if (ret) |
534 | return; |
535 | |
536 | if (clkmain->pms.status) |
537 | clk_sam9x5_main_prepare(hw); |
538 | } |
539 | |
540 | static const struct clk_ops sam9x5_main_ops = { |
541 | .prepare = clk_sam9x5_main_prepare, |
542 | .is_prepared = clk_sam9x5_main_is_prepared, |
543 | .recalc_rate = clk_sam9x5_main_recalc_rate, |
544 | .determine_rate = clk_hw_determine_rate_no_reparent, |
545 | .set_parent = clk_sam9x5_main_set_parent, |
546 | .get_parent = clk_sam9x5_main_get_parent, |
547 | .save_context = clk_sam9x5_main_save_context, |
548 | .restore_context = clk_sam9x5_main_restore_context, |
549 | }; |
550 | |
551 | struct clk_hw * __init |
552 | at91_clk_register_sam9x5_main(struct regmap *regmap, |
553 | const char *name, |
554 | const char **parent_names, |
555 | struct clk_hw **parent_hws, |
556 | int num_parents) |
557 | { |
558 | struct clk_sam9x5_main *clkmain; |
559 | struct clk_init_data init = {}; |
560 | unsigned int status; |
561 | struct clk_hw *hw; |
562 | int ret; |
563 | |
564 | if (!name) |
565 | return ERR_PTR(error: -EINVAL); |
566 | |
567 | if (!(parent_hws || parent_names) || !num_parents) |
568 | return ERR_PTR(error: -EINVAL); |
569 | |
570 | clkmain = kzalloc(size: sizeof(*clkmain), GFP_KERNEL); |
571 | if (!clkmain) |
572 | return ERR_PTR(error: -ENOMEM); |
573 | |
574 | init.name = name; |
575 | init.ops = &sam9x5_main_ops; |
576 | if (parent_hws) |
577 | init.parent_hws = (const struct clk_hw **)parent_hws; |
578 | else |
579 | init.parent_names = parent_names; |
580 | init.num_parents = num_parents; |
581 | init.flags = CLK_SET_PARENT_GATE; |
582 | |
583 | clkmain->hw.init = &init; |
584 | clkmain->regmap = regmap; |
585 | regmap_read(map: clkmain->regmap, AT91_CKGR_MOR, val: &status); |
586 | clkmain->parent = clk_main_parent_select(status); |
587 | |
588 | hw = &clkmain->hw; |
589 | ret = clk_hw_register(NULL, hw: &clkmain->hw); |
590 | if (ret) { |
591 | kfree(objp: clkmain); |
592 | hw = ERR_PTR(error: ret); |
593 | } |
594 | |
595 | return hw; |
596 | } |
597 | |