1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * drivers/clk/at91/sckc.c |
4 | * |
5 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> |
6 | */ |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/clkdev.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/io.h> |
14 | |
15 | #define SLOW_CLOCK_FREQ 32768 |
16 | #define SLOWCK_SW_CYCLES 5 |
17 | #define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ |
18 | SLOW_CLOCK_FREQ) |
19 | |
20 | #define AT91_SCKC_CR 0x00 |
21 | |
22 | struct clk_slow_bits { |
23 | u32 cr_rcen; |
24 | u32 cr_osc32en; |
25 | u32 cr_osc32byp; |
26 | u32 cr_oscsel; |
27 | }; |
28 | |
29 | struct clk_slow_osc { |
30 | struct clk_hw hw; |
31 | void __iomem *sckcr; |
32 | const struct clk_slow_bits *bits; |
33 | unsigned long startup_usec; |
34 | }; |
35 | |
36 | #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) |
37 | |
38 | struct clk_sama5d4_slow_osc { |
39 | struct clk_hw hw; |
40 | void __iomem *sckcr; |
41 | const struct clk_slow_bits *bits; |
42 | unsigned long startup_usec; |
43 | bool prepared; |
44 | }; |
45 | |
46 | #define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw) |
47 | |
48 | struct clk_slow_rc_osc { |
49 | struct clk_hw hw; |
50 | void __iomem *sckcr; |
51 | const struct clk_slow_bits *bits; |
52 | unsigned long frequency; |
53 | unsigned long accuracy; |
54 | unsigned long startup_usec; |
55 | }; |
56 | |
57 | #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) |
58 | |
59 | struct clk_sam9x5_slow { |
60 | struct clk_hw hw; |
61 | void __iomem *sckcr; |
62 | const struct clk_slow_bits *bits; |
63 | u8 parent; |
64 | }; |
65 | |
66 | #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) |
67 | |
68 | static int clk_slow_osc_prepare(struct clk_hw *hw) |
69 | { |
70 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); |
71 | void __iomem *sckcr = osc->sckcr; |
72 | u32 tmp = readl(addr: sckcr); |
73 | |
74 | if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en)) |
75 | return 0; |
76 | |
77 | writel(val: tmp | osc->bits->cr_osc32en, addr: sckcr); |
78 | |
79 | if (system_state < SYSTEM_RUNNING) |
80 | udelay(osc->startup_usec); |
81 | else |
82 | usleep_range(min: osc->startup_usec, max: osc->startup_usec + 1); |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static void clk_slow_osc_unprepare(struct clk_hw *hw) |
88 | { |
89 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); |
90 | void __iomem *sckcr = osc->sckcr; |
91 | u32 tmp = readl(addr: sckcr); |
92 | |
93 | if (tmp & osc->bits->cr_osc32byp) |
94 | return; |
95 | |
96 | writel(val: tmp & ~osc->bits->cr_osc32en, addr: sckcr); |
97 | } |
98 | |
99 | static int clk_slow_osc_is_prepared(struct clk_hw *hw) |
100 | { |
101 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); |
102 | void __iomem *sckcr = osc->sckcr; |
103 | u32 tmp = readl(addr: sckcr); |
104 | |
105 | if (tmp & osc->bits->cr_osc32byp) |
106 | return 1; |
107 | |
108 | return !!(tmp & osc->bits->cr_osc32en); |
109 | } |
110 | |
111 | static const struct clk_ops slow_osc_ops = { |
112 | .prepare = clk_slow_osc_prepare, |
113 | .unprepare = clk_slow_osc_unprepare, |
114 | .is_prepared = clk_slow_osc_is_prepared, |
115 | }; |
116 | |
117 | static struct clk_hw * __init |
118 | at91_clk_register_slow_osc(void __iomem *sckcr, |
119 | const char *name, |
120 | const struct clk_parent_data *parent_data, |
121 | unsigned long startup, |
122 | bool bypass, |
123 | const struct clk_slow_bits *bits) |
124 | { |
125 | struct clk_slow_osc *osc; |
126 | struct clk_hw *hw; |
127 | struct clk_init_data init = {}; |
128 | int ret; |
129 | |
130 | if (!sckcr || !name || !parent_data) |
131 | return ERR_PTR(error: -EINVAL); |
132 | |
133 | osc = kzalloc(size: sizeof(*osc), GFP_KERNEL); |
134 | if (!osc) |
135 | return ERR_PTR(error: -ENOMEM); |
136 | |
137 | init.name = name; |
138 | init.ops = &slow_osc_ops; |
139 | init.parent_data = parent_data; |
140 | init.num_parents = 1; |
141 | init.flags = CLK_IGNORE_UNUSED; |
142 | |
143 | osc->hw.init = &init; |
144 | osc->sckcr = sckcr; |
145 | osc->startup_usec = startup; |
146 | osc->bits = bits; |
147 | |
148 | if (bypass) |
149 | writel(val: (readl(addr: sckcr) & ~osc->bits->cr_osc32en) | |
150 | osc->bits->cr_osc32byp, addr: sckcr); |
151 | |
152 | hw = &osc->hw; |
153 | ret = clk_hw_register(NULL, hw: &osc->hw); |
154 | if (ret) { |
155 | kfree(objp: osc); |
156 | hw = ERR_PTR(error: ret); |
157 | } |
158 | |
159 | return hw; |
160 | } |
161 | |
162 | static void at91_clk_unregister_slow_osc(struct clk_hw *hw) |
163 | { |
164 | struct clk_slow_osc *osc = to_clk_slow_osc(hw); |
165 | |
166 | clk_hw_unregister(hw); |
167 | kfree(objp: osc); |
168 | } |
169 | |
170 | static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, |
171 | unsigned long parent_rate) |
172 | { |
173 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); |
174 | |
175 | return osc->frequency; |
176 | } |
177 | |
178 | static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, |
179 | unsigned long parent_acc) |
180 | { |
181 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); |
182 | |
183 | return osc->accuracy; |
184 | } |
185 | |
186 | static int clk_slow_rc_osc_prepare(struct clk_hw *hw) |
187 | { |
188 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); |
189 | void __iomem *sckcr = osc->sckcr; |
190 | |
191 | writel(readl(addr: sckcr) | osc->bits->cr_rcen, addr: sckcr); |
192 | |
193 | if (system_state < SYSTEM_RUNNING) |
194 | udelay(osc->startup_usec); |
195 | else |
196 | usleep_range(min: osc->startup_usec, max: osc->startup_usec + 1); |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) |
202 | { |
203 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); |
204 | void __iomem *sckcr = osc->sckcr; |
205 | |
206 | writel(readl(addr: sckcr) & ~osc->bits->cr_rcen, addr: sckcr); |
207 | } |
208 | |
209 | static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) |
210 | { |
211 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); |
212 | |
213 | return !!(readl(addr: osc->sckcr) & osc->bits->cr_rcen); |
214 | } |
215 | |
216 | static const struct clk_ops slow_rc_osc_ops = { |
217 | .prepare = clk_slow_rc_osc_prepare, |
218 | .unprepare = clk_slow_rc_osc_unprepare, |
219 | .is_prepared = clk_slow_rc_osc_is_prepared, |
220 | .recalc_rate = clk_slow_rc_osc_recalc_rate, |
221 | .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, |
222 | }; |
223 | |
224 | static struct clk_hw * __init |
225 | at91_clk_register_slow_rc_osc(void __iomem *sckcr, |
226 | const char *name, |
227 | unsigned long frequency, |
228 | unsigned long accuracy, |
229 | unsigned long startup, |
230 | const struct clk_slow_bits *bits) |
231 | { |
232 | struct clk_slow_rc_osc *osc; |
233 | struct clk_hw *hw; |
234 | struct clk_init_data init; |
235 | int ret; |
236 | |
237 | if (!sckcr || !name) |
238 | return ERR_PTR(error: -EINVAL); |
239 | |
240 | osc = kzalloc(size: sizeof(*osc), GFP_KERNEL); |
241 | if (!osc) |
242 | return ERR_PTR(error: -ENOMEM); |
243 | |
244 | init.name = name; |
245 | init.ops = &slow_rc_osc_ops; |
246 | init.parent_names = NULL; |
247 | init.num_parents = 0; |
248 | init.flags = CLK_IGNORE_UNUSED; |
249 | |
250 | osc->hw.init = &init; |
251 | osc->sckcr = sckcr; |
252 | osc->bits = bits; |
253 | osc->frequency = frequency; |
254 | osc->accuracy = accuracy; |
255 | osc->startup_usec = startup; |
256 | |
257 | hw = &osc->hw; |
258 | ret = clk_hw_register(NULL, hw: &osc->hw); |
259 | if (ret) { |
260 | kfree(objp: osc); |
261 | hw = ERR_PTR(error: ret); |
262 | } |
263 | |
264 | return hw; |
265 | } |
266 | |
267 | static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw) |
268 | { |
269 | struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); |
270 | |
271 | clk_hw_unregister(hw); |
272 | kfree(objp: osc); |
273 | } |
274 | |
275 | static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) |
276 | { |
277 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); |
278 | void __iomem *sckcr = slowck->sckcr; |
279 | u32 tmp; |
280 | |
281 | if (index > 1) |
282 | return -EINVAL; |
283 | |
284 | tmp = readl(addr: sckcr); |
285 | |
286 | if ((!index && !(tmp & slowck->bits->cr_oscsel)) || |
287 | (index && (tmp & slowck->bits->cr_oscsel))) |
288 | return 0; |
289 | |
290 | if (index) |
291 | tmp |= slowck->bits->cr_oscsel; |
292 | else |
293 | tmp &= ~slowck->bits->cr_oscsel; |
294 | |
295 | writel(val: tmp, addr: sckcr); |
296 | |
297 | if (system_state < SYSTEM_RUNNING) |
298 | udelay(SLOWCK_SW_TIME_USEC); |
299 | else |
300 | usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) |
306 | { |
307 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); |
308 | |
309 | return !!(readl(addr: slowck->sckcr) & slowck->bits->cr_oscsel); |
310 | } |
311 | |
312 | static const struct clk_ops sam9x5_slow_ops = { |
313 | .determine_rate = clk_hw_determine_rate_no_reparent, |
314 | .set_parent = clk_sam9x5_slow_set_parent, |
315 | .get_parent = clk_sam9x5_slow_get_parent, |
316 | }; |
317 | |
318 | static struct clk_hw * __init |
319 | at91_clk_register_sam9x5_slow(void __iomem *sckcr, |
320 | const char *name, |
321 | const struct clk_hw **parent_hws, |
322 | int num_parents, |
323 | const struct clk_slow_bits *bits) |
324 | { |
325 | struct clk_sam9x5_slow *slowck; |
326 | struct clk_hw *hw; |
327 | struct clk_init_data init = {}; |
328 | int ret; |
329 | |
330 | if (!sckcr || !name || !parent_hws || !num_parents) |
331 | return ERR_PTR(error: -EINVAL); |
332 | |
333 | slowck = kzalloc(size: sizeof(*slowck), GFP_KERNEL); |
334 | if (!slowck) |
335 | return ERR_PTR(error: -ENOMEM); |
336 | |
337 | init.name = name; |
338 | init.ops = &sam9x5_slow_ops; |
339 | init.parent_hws = parent_hws; |
340 | init.num_parents = num_parents; |
341 | init.flags = 0; |
342 | |
343 | slowck->hw.init = &init; |
344 | slowck->sckcr = sckcr; |
345 | slowck->bits = bits; |
346 | slowck->parent = !!(readl(addr: sckcr) & slowck->bits->cr_oscsel); |
347 | |
348 | hw = &slowck->hw; |
349 | ret = clk_hw_register(NULL, hw: &slowck->hw); |
350 | if (ret) { |
351 | kfree(objp: slowck); |
352 | hw = ERR_PTR(error: ret); |
353 | } |
354 | |
355 | return hw; |
356 | } |
357 | |
358 | static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw) |
359 | { |
360 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); |
361 | |
362 | clk_hw_unregister(hw); |
363 | kfree(objp: slowck); |
364 | } |
365 | |
366 | static void __init at91sam9x5_sckc_register(struct device_node *np, |
367 | unsigned int rc_osc_startup_us, |
368 | const struct clk_slow_bits *bits) |
369 | { |
370 | void __iomem *regbase = of_iomap(node: np, index: 0); |
371 | struct device_node *child = NULL; |
372 | const char *xtal_name; |
373 | struct clk_hw *slow_rc, *slow_osc, *slowck; |
374 | static struct clk_parent_data parent_data = { |
375 | .name = "slow_xtal" , |
376 | }; |
377 | const struct clk_hw *parent_hws[2]; |
378 | bool bypass; |
379 | int ret; |
380 | |
381 | if (!regbase) |
382 | return; |
383 | |
384 | slow_rc = at91_clk_register_slow_rc_osc(sckcr: regbase, name: "slow_rc_osc" , |
385 | frequency: 32768, accuracy: 50000000, |
386 | startup: rc_osc_startup_us, bits); |
387 | if (IS_ERR(ptr: slow_rc)) |
388 | return; |
389 | |
390 | xtal_name = of_clk_get_parent_name(np, index: 0); |
391 | if (!xtal_name) { |
392 | /* DT backward compatibility */ |
393 | child = of_get_compatible_child(parent: np, compatible: "atmel,at91sam9x5-clk-slow-osc" ); |
394 | if (!child) |
395 | goto unregister_slow_rc; |
396 | |
397 | xtal_name = of_clk_get_parent_name(np: child, index: 0); |
398 | bypass = of_property_read_bool(np: child, propname: "atmel,osc-bypass" ); |
399 | |
400 | child = of_get_compatible_child(parent: np, compatible: "atmel,at91sam9x5-clk-slow" ); |
401 | } else { |
402 | bypass = of_property_read_bool(np, propname: "atmel,osc-bypass" ); |
403 | } |
404 | |
405 | if (!xtal_name) |
406 | goto unregister_slow_rc; |
407 | |
408 | parent_data.fw_name = xtal_name; |
409 | |
410 | slow_osc = at91_clk_register_slow_osc(sckcr: regbase, name: "slow_osc" , |
411 | parent_data: &parent_data, startup: 1200000, bypass, bits); |
412 | if (IS_ERR(ptr: slow_osc)) |
413 | goto unregister_slow_rc; |
414 | |
415 | parent_hws[0] = slow_rc; |
416 | parent_hws[1] = slow_osc; |
417 | slowck = at91_clk_register_sam9x5_slow(sckcr: regbase, name: "slowck" , parent_hws, |
418 | num_parents: 2, bits); |
419 | if (IS_ERR(ptr: slowck)) |
420 | goto unregister_slow_osc; |
421 | |
422 | /* DT backward compatibility */ |
423 | if (child) |
424 | ret = of_clk_add_hw_provider(np: child, get: of_clk_hw_simple_get, |
425 | data: slowck); |
426 | else |
427 | ret = of_clk_add_hw_provider(np, get: of_clk_hw_simple_get, data: slowck); |
428 | |
429 | if (WARN_ON(ret)) |
430 | goto unregister_slowck; |
431 | |
432 | return; |
433 | |
434 | unregister_slowck: |
435 | at91_clk_unregister_sam9x5_slow(hw: slowck); |
436 | unregister_slow_osc: |
437 | at91_clk_unregister_slow_osc(hw: slow_osc); |
438 | unregister_slow_rc: |
439 | at91_clk_unregister_slow_rc_osc(hw: slow_rc); |
440 | } |
441 | |
442 | static const struct clk_slow_bits at91sam9x5_bits = { |
443 | .cr_rcen = BIT(0), |
444 | .cr_osc32en = BIT(1), |
445 | .cr_osc32byp = BIT(2), |
446 | .cr_oscsel = BIT(3), |
447 | }; |
448 | |
449 | static void __init of_at91sam9x5_sckc_setup(struct device_node *np) |
450 | { |
451 | at91sam9x5_sckc_register(np, rc_osc_startup_us: 75, bits: &at91sam9x5_bits); |
452 | } |
453 | CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc" , |
454 | of_at91sam9x5_sckc_setup); |
455 | |
456 | static void __init of_sama5d3_sckc_setup(struct device_node *np) |
457 | { |
458 | at91sam9x5_sckc_register(np, rc_osc_startup_us: 500, bits: &at91sam9x5_bits); |
459 | } |
460 | CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc" , |
461 | of_sama5d3_sckc_setup); |
462 | |
463 | static const struct clk_slow_bits at91sam9x60_bits = { |
464 | .cr_osc32en = BIT(1), |
465 | .cr_osc32byp = BIT(2), |
466 | .cr_oscsel = BIT(24), |
467 | }; |
468 | |
469 | static void __init of_sam9x60_sckc_setup(struct device_node *np) |
470 | { |
471 | void __iomem *regbase = of_iomap(node: np, index: 0); |
472 | struct clk_hw_onecell_data *clk_data; |
473 | struct clk_hw *slow_rc, *slow_osc; |
474 | const char *xtal_name; |
475 | const struct clk_hw *parent_hws[2]; |
476 | static struct clk_parent_data parent_data = { |
477 | .name = "slow_xtal" , |
478 | }; |
479 | bool bypass; |
480 | int ret; |
481 | |
482 | if (!regbase) |
483 | return; |
484 | |
485 | slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc" , |
486 | NULL, 0, 32768, |
487 | 93750000); |
488 | if (IS_ERR(ptr: slow_rc)) |
489 | return; |
490 | |
491 | xtal_name = of_clk_get_parent_name(np, index: 0); |
492 | if (!xtal_name) |
493 | goto unregister_slow_rc; |
494 | |
495 | parent_data.fw_name = xtal_name; |
496 | bypass = of_property_read_bool(np, propname: "atmel,osc-bypass" ); |
497 | slow_osc = at91_clk_register_slow_osc(sckcr: regbase, name: "slow_osc" , |
498 | parent_data: &parent_data, startup: 5000000, bypass, |
499 | bits: &at91sam9x60_bits); |
500 | if (IS_ERR(ptr: slow_osc)) |
501 | goto unregister_slow_rc; |
502 | |
503 | clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL); |
504 | if (!clk_data) |
505 | goto unregister_slow_osc; |
506 | |
507 | /* MD_SLCK and TD_SLCK. */ |
508 | clk_data->num = 2; |
509 | clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck" , |
510 | slow_rc, |
511 | 0, 32768); |
512 | if (IS_ERR(ptr: clk_data->hws[0])) |
513 | goto clk_data_free; |
514 | |
515 | parent_hws[0] = slow_rc; |
516 | parent_hws[1] = slow_osc; |
517 | clk_data->hws[1] = at91_clk_register_sam9x5_slow(sckcr: regbase, name: "td_slck" , |
518 | parent_hws, num_parents: 2, |
519 | bits: &at91sam9x60_bits); |
520 | if (IS_ERR(ptr: clk_data->hws[1])) |
521 | goto unregister_md_slck; |
522 | |
523 | ret = of_clk_add_hw_provider(np, get: of_clk_hw_onecell_get, data: clk_data); |
524 | if (WARN_ON(ret)) |
525 | goto unregister_td_slck; |
526 | |
527 | return; |
528 | |
529 | unregister_td_slck: |
530 | at91_clk_unregister_sam9x5_slow(hw: clk_data->hws[1]); |
531 | unregister_md_slck: |
532 | clk_hw_unregister(hw: clk_data->hws[0]); |
533 | clk_data_free: |
534 | kfree(objp: clk_data); |
535 | unregister_slow_osc: |
536 | at91_clk_unregister_slow_osc(hw: slow_osc); |
537 | unregister_slow_rc: |
538 | clk_hw_unregister(hw: slow_rc); |
539 | } |
540 | CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc" , |
541 | of_sam9x60_sckc_setup); |
542 | |
543 | static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) |
544 | { |
545 | struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); |
546 | |
547 | if (osc->prepared) |
548 | return 0; |
549 | |
550 | /* |
551 | * Assume that if it has already been selected (for example by the |
552 | * bootloader), enough time has already passed. |
553 | */ |
554 | if ((readl(addr: osc->sckcr) & osc->bits->cr_oscsel)) { |
555 | osc->prepared = true; |
556 | return 0; |
557 | } |
558 | |
559 | if (system_state < SYSTEM_RUNNING) |
560 | udelay(osc->startup_usec); |
561 | else |
562 | usleep_range(min: osc->startup_usec, max: osc->startup_usec + 1); |
563 | osc->prepared = true; |
564 | |
565 | return 0; |
566 | } |
567 | |
568 | static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw) |
569 | { |
570 | struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); |
571 | |
572 | return osc->prepared; |
573 | } |
574 | |
575 | static const struct clk_ops sama5d4_slow_osc_ops = { |
576 | .prepare = clk_sama5d4_slow_osc_prepare, |
577 | .is_prepared = clk_sama5d4_slow_osc_is_prepared, |
578 | }; |
579 | |
580 | static const struct clk_slow_bits at91sama5d4_bits = { |
581 | .cr_oscsel = BIT(3), |
582 | }; |
583 | |
584 | static void __init of_sama5d4_sckc_setup(struct device_node *np) |
585 | { |
586 | void __iomem *regbase = of_iomap(node: np, index: 0); |
587 | struct clk_hw *slow_rc, *slowck; |
588 | struct clk_sama5d4_slow_osc *osc; |
589 | struct clk_init_data init = {}; |
590 | const char *xtal_name; |
591 | const struct clk_hw *parent_hws[2]; |
592 | static struct clk_parent_data parent_data = { |
593 | .name = "slow_xtal" , |
594 | }; |
595 | int ret; |
596 | |
597 | if (!regbase) |
598 | return; |
599 | |
600 | slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, |
601 | "slow_rc_osc" , |
602 | NULL, 0, 32768, |
603 | 250000000); |
604 | if (IS_ERR(ptr: slow_rc)) |
605 | return; |
606 | |
607 | xtal_name = of_clk_get_parent_name(np, index: 0); |
608 | if (!xtal_name) |
609 | goto unregister_slow_rc; |
610 | parent_data.fw_name = xtal_name; |
611 | |
612 | osc = kzalloc(size: sizeof(*osc), GFP_KERNEL); |
613 | if (!osc) |
614 | goto unregister_slow_rc; |
615 | |
616 | init.name = "slow_osc" ; |
617 | init.ops = &sama5d4_slow_osc_ops; |
618 | init.parent_data = &parent_data; |
619 | init.num_parents = 1; |
620 | init.flags = CLK_IGNORE_UNUSED; |
621 | |
622 | osc->hw.init = &init; |
623 | osc->sckcr = regbase; |
624 | osc->startup_usec = 1200000; |
625 | osc->bits = &at91sama5d4_bits; |
626 | |
627 | ret = clk_hw_register(NULL, hw: &osc->hw); |
628 | if (ret) |
629 | goto free_slow_osc_data; |
630 | |
631 | parent_hws[0] = slow_rc; |
632 | parent_hws[1] = &osc->hw; |
633 | slowck = at91_clk_register_sam9x5_slow(sckcr: regbase, name: "slowck" , |
634 | parent_hws, num_parents: 2, |
635 | bits: &at91sama5d4_bits); |
636 | if (IS_ERR(ptr: slowck)) |
637 | goto unregister_slow_osc; |
638 | |
639 | ret = of_clk_add_hw_provider(np, get: of_clk_hw_simple_get, data: slowck); |
640 | if (WARN_ON(ret)) |
641 | goto unregister_slowck; |
642 | |
643 | return; |
644 | |
645 | unregister_slowck: |
646 | at91_clk_unregister_sam9x5_slow(hw: slowck); |
647 | unregister_slow_osc: |
648 | clk_hw_unregister(hw: &osc->hw); |
649 | free_slow_osc_data: |
650 | kfree(objp: osc); |
651 | unregister_slow_rc: |
652 | clk_hw_unregister(hw: slow_rc); |
653 | } |
654 | CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc" , |
655 | of_sama5d4_sckc_setup); |
656 | |