]>
Commit | Line | Data |
---|---|---|
653bcce4 CB |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Compatible code for non CCF AT91 platforms. | |
4 | * | |
5 | * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries | |
6 | * | |
7 | * Author: Claudiu Beznea <[email protected]> | |
8 | */ | |
9 | #include <common.h> | |
10 | #include <clk-uclass.h> | |
11 | #include <dm.h> | |
68963979 | 12 | #include <dm/device_compat.h> |
653bcce4 CB |
13 | #include <dm/lists.h> |
14 | #include <dm/util.h> | |
15 | #include <mach/at91_pmc.h> | |
16 | #include <mach/at91_sfr.h> | |
17 | #include <regmap.h> | |
18 | #include <syscon.h> | |
19 | ||
20 | #include "pmc.h" | |
21 | ||
22 | DECLARE_GLOBAL_DATA_PTR; | |
23 | ||
24 | struct pmc_platdata { | |
25 | struct at91_pmc *reg_base; | |
26 | struct regmap *regmap_sfr; | |
27 | }; | |
28 | ||
29 | static const struct udevice_id at91_pmc_match[] = { | |
30 | { .compatible = "atmel,at91rm9200-pmc" }, | |
31 | { .compatible = "atmel,at91sam9260-pmc" }, | |
32 | { .compatible = "atmel,at91sam9g45-pmc" }, | |
33 | { .compatible = "atmel,at91sam9n12-pmc" }, | |
34 | { .compatible = "atmel,at91sam9x5-pmc" }, | |
35 | { .compatible = "atmel,sama5d3-pmc" }, | |
36 | { .compatible = "atmel,sama5d2-pmc" }, | |
37 | {} | |
38 | }; | |
39 | ||
40 | U_BOOT_DRIVER(at91_pmc) = { | |
41 | .name = "at91-pmc", | |
42 | .id = UCLASS_SIMPLE_BUS, | |
43 | .of_match = at91_pmc_match, | |
44 | }; | |
45 | ||
46 | static int at91_pmc_core_probe(struct udevice *dev) | |
47 | { | |
48 | struct pmc_platdata *plat = dev_get_platdata(dev); | |
49 | ||
50 | dev = dev_get_parent(dev); | |
51 | ||
52 | plat->reg_base = dev_read_addr_ptr(dev); | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | /** | |
58 | * at91_clk_sub_device_bind() - for the at91 clock driver | |
59 | * Recursively bind its children as clk devices. | |
60 | * | |
61 | * @return: 0 on success, or negative error code on failure | |
62 | */ | |
63 | int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name) | |
64 | { | |
a2703ce1 SG |
65 | ofnode parent = dev_ofnode(dev); |
66 | ofnode node; | |
653bcce4 CB |
67 | bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC); |
68 | const char *name; | |
69 | int ret; | |
70 | ||
a2703ce1 SG |
71 | ofnode_for_each_subnode(node, parent) { |
72 | if (pre_reloc_only && !ofnode_pre_reloc(node)) | |
653bcce4 CB |
73 | continue; |
74 | /* | |
75 | * If this node has "compatible" property, this is not | |
76 | * a clock sub-node, but a normal device. skip. | |
77 | */ | |
a2703ce1 | 78 | if (ofnode_read_prop(node, "compatible", NULL)) |
653bcce4 CB |
79 | continue; |
80 | ||
81 | if (ret != -FDT_ERR_NOTFOUND) | |
82 | return ret; | |
83 | ||
a2703ce1 | 84 | name = ofnode_get_name(node); |
653bcce4 CB |
85 | if (!name) |
86 | return -EINVAL; | |
a2703ce1 SG |
87 | ret = device_bind_driver_to_node(dev, drv_name, name, node, |
88 | NULL); | |
653bcce4 CB |
89 | if (ret) |
90 | return ret; | |
91 | } | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) | |
97 | { | |
98 | int periph; | |
99 | ||
100 | if (args->args_count) { | |
101 | debug("Invalid args_count: %d\n", args->args_count); | |
102 | return -EINVAL; | |
103 | } | |
104 | ||
105 | periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg", | |
106 | -1); | |
107 | if (periph < 0) | |
108 | return -EINVAL; | |
109 | ||
110 | clk->id = periph; | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | int at91_clk_probe(struct udevice *dev) | |
116 | { | |
117 | struct udevice *dev_periph_container, *dev_pmc; | |
118 | struct pmc_platdata *plat = dev_get_platdata(dev); | |
119 | ||
120 | dev_periph_container = dev_get_parent(dev); | |
121 | dev_pmc = dev_get_parent(dev_periph_container); | |
122 | ||
123 | plat->reg_base = dev_read_addr_ptr(dev_pmc); | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | /* SCKC specific code. */ | |
129 | static const struct udevice_id at91_sckc_match[] = { | |
130 | { .compatible = "atmel,at91sam9x5-sckc" }, | |
131 | {} | |
132 | }; | |
133 | ||
134 | U_BOOT_DRIVER(at91_sckc) = { | |
135 | .name = "at91-sckc", | |
136 | .id = UCLASS_SIMPLE_BUS, | |
137 | .of_match = at91_sckc_match, | |
138 | }; | |
139 | ||
140 | /* Slow clock specific code. */ | |
141 | static int at91_slow_clk_enable(struct clk *clk) | |
142 | { | |
143 | return 0; | |
144 | } | |
145 | ||
146 | static ulong at91_slow_clk_get_rate(struct clk *clk) | |
147 | { | |
148 | return CONFIG_SYS_AT91_SLOW_CLOCK; | |
149 | } | |
150 | ||
151 | static struct clk_ops at91_slow_clk_ops = { | |
152 | .enable = at91_slow_clk_enable, | |
153 | .get_rate = at91_slow_clk_get_rate, | |
154 | }; | |
155 | ||
156 | static const struct udevice_id at91_slow_clk_match[] = { | |
157 | { .compatible = "atmel,at91sam9x5-clk-slow" }, | |
158 | {} | |
159 | }; | |
160 | ||
161 | U_BOOT_DRIVER(at91_slow_clk) = { | |
162 | .name = "at91-slow-clk", | |
163 | .id = UCLASS_CLK, | |
164 | .of_match = at91_slow_clk_match, | |
165 | .ops = &at91_slow_clk_ops, | |
166 | }; | |
167 | ||
168 | /* Master clock specific code. */ | |
169 | static ulong at91_master_clk_get_rate(struct clk *clk) | |
170 | { | |
171 | return gd->arch.mck_rate_hz; | |
172 | } | |
173 | ||
174 | static struct clk_ops at91_master_clk_ops = { | |
175 | .get_rate = at91_master_clk_get_rate, | |
176 | }; | |
177 | ||
178 | static const struct udevice_id at91_master_clk_match[] = { | |
179 | { .compatible = "atmel,at91rm9200-clk-master" }, | |
180 | { .compatible = "atmel,at91sam9x5-clk-master" }, | |
181 | {} | |
182 | }; | |
183 | ||
184 | U_BOOT_DRIVER(at91_master_clk) = { | |
185 | .name = "at91-master-clk", | |
186 | .id = UCLASS_CLK, | |
187 | .of_match = at91_master_clk_match, | |
188 | .ops = &at91_master_clk_ops, | |
189 | }; | |
190 | ||
191 | /* Main osc clock specific code. */ | |
192 | static int main_osc_clk_enable(struct clk *clk) | |
193 | { | |
194 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
195 | struct at91_pmc *pmc = plat->reg_base; | |
196 | ||
197 | if (readl(&pmc->sr) & AT91_PMC_MOSCSELS) | |
198 | return 0; | |
199 | ||
200 | return -EINVAL; | |
201 | } | |
202 | ||
203 | static ulong main_osc_clk_get_rate(struct clk *clk) | |
204 | { | |
205 | return gd->arch.main_clk_rate_hz; | |
206 | } | |
207 | ||
208 | static struct clk_ops main_osc_clk_ops = { | |
209 | .enable = main_osc_clk_enable, | |
210 | .get_rate = main_osc_clk_get_rate, | |
211 | }; | |
212 | ||
213 | static int main_osc_clk_probe(struct udevice *dev) | |
214 | { | |
215 | return at91_pmc_core_probe(dev); | |
216 | } | |
217 | ||
218 | static const struct udevice_id main_osc_clk_match[] = { | |
219 | { .compatible = "atmel,at91sam9x5-clk-main" }, | |
220 | {} | |
221 | }; | |
222 | ||
223 | U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = { | |
224 | .name = "at91sam9x5-main-osc-clk", | |
225 | .id = UCLASS_CLK, | |
226 | .of_match = main_osc_clk_match, | |
227 | .probe = main_osc_clk_probe, | |
41575d8e | 228 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
229 | .ops = &main_osc_clk_ops, |
230 | }; | |
231 | ||
232 | /* PLLA clock specific code. */ | |
233 | static int plla_clk_enable(struct clk *clk) | |
234 | { | |
235 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
236 | struct at91_pmc *pmc = plat->reg_base; | |
237 | ||
238 | if (readl(&pmc->sr) & AT91_PMC_LOCKA) | |
239 | return 0; | |
240 | ||
241 | return -EINVAL; | |
242 | } | |
243 | ||
244 | static ulong plla_clk_get_rate(struct clk *clk) | |
245 | { | |
246 | return gd->arch.plla_rate_hz; | |
247 | } | |
248 | ||
249 | static struct clk_ops plla_clk_ops = { | |
250 | .enable = plla_clk_enable, | |
251 | .get_rate = plla_clk_get_rate, | |
252 | }; | |
253 | ||
254 | static int plla_clk_probe(struct udevice *dev) | |
255 | { | |
256 | return at91_pmc_core_probe(dev); | |
257 | } | |
258 | ||
259 | static const struct udevice_id plla_clk_match[] = { | |
260 | { .compatible = "atmel,sama5d3-clk-pll" }, | |
261 | {} | |
262 | }; | |
263 | ||
264 | U_BOOT_DRIVER(at91_plla_clk) = { | |
265 | .name = "at91-plla-clk", | |
266 | .id = UCLASS_CLK, | |
267 | .of_match = plla_clk_match, | |
268 | .probe = plla_clk_probe, | |
41575d8e | 269 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
270 | .ops = &plla_clk_ops, |
271 | }; | |
272 | ||
273 | /* PLLA DIV clock specific code. */ | |
274 | static int at91_plladiv_clk_enable(struct clk *clk) | |
275 | { | |
276 | return 0; | |
277 | } | |
278 | ||
279 | static ulong at91_plladiv_clk_get_rate(struct clk *clk) | |
280 | { | |
281 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
282 | struct at91_pmc *pmc = plat->reg_base; | |
283 | struct clk source; | |
284 | ulong clk_rate; | |
285 | int ret; | |
286 | ||
287 | ret = clk_get_by_index(clk->dev, 0, &source); | |
288 | if (ret) | |
289 | return -EINVAL; | |
290 | ||
291 | clk_rate = clk_get_rate(&source); | |
292 | if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2) | |
293 | clk_rate /= 2; | |
294 | ||
295 | return clk_rate; | |
296 | } | |
297 | ||
298 | static ulong at91_plladiv_clk_set_rate(struct clk *clk, ulong rate) | |
299 | { | |
300 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
301 | struct at91_pmc *pmc = plat->reg_base; | |
302 | struct clk source; | |
303 | ulong parent_rate; | |
304 | int ret; | |
305 | ||
306 | ret = clk_get_by_index(clk->dev, 0, &source); | |
307 | if (ret) | |
308 | return -EINVAL; | |
309 | ||
310 | parent_rate = clk_get_rate(&source); | |
311 | if ((parent_rate != rate) && ((parent_rate) / 2 != rate)) | |
312 | return -EINVAL; | |
313 | ||
314 | if (parent_rate != rate) { | |
315 | writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2), | |
316 | &pmc->mckr); | |
317 | } | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
322 | static struct clk_ops at91_plladiv_clk_ops = { | |
323 | .enable = at91_plladiv_clk_enable, | |
324 | .get_rate = at91_plladiv_clk_get_rate, | |
325 | .set_rate = at91_plladiv_clk_set_rate, | |
326 | }; | |
327 | ||
328 | static int at91_plladiv_clk_probe(struct udevice *dev) | |
329 | { | |
330 | return at91_pmc_core_probe(dev); | |
331 | } | |
332 | ||
333 | static const struct udevice_id at91_plladiv_clk_match[] = { | |
334 | { .compatible = "atmel,at91sam9x5-clk-plldiv" }, | |
335 | {} | |
336 | }; | |
337 | ||
338 | U_BOOT_DRIVER(at91_plladiv_clk) = { | |
339 | .name = "at91-plladiv-clk", | |
340 | .id = UCLASS_CLK, | |
341 | .of_match = at91_plladiv_clk_match, | |
342 | .probe = at91_plladiv_clk_probe, | |
41575d8e | 343 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
344 | .ops = &at91_plladiv_clk_ops, |
345 | }; | |
346 | ||
347 | /* System clock specific code. */ | |
348 | #define SYSTEM_MAX_ID 31 | |
349 | ||
350 | /** | |
351 | * at91_system_clk_bind() - for the system clock driver | |
352 | * Recursively bind its children as clk devices. | |
353 | * | |
354 | * @return: 0 on success, or negative error code on failure | |
355 | */ | |
356 | static int at91_system_clk_bind(struct udevice *dev) | |
357 | { | |
358 | return at91_clk_sub_device_bind(dev, "system-clk"); | |
359 | } | |
360 | ||
361 | static const struct udevice_id at91_system_clk_match[] = { | |
362 | { .compatible = "atmel,at91rm9200-clk-system" }, | |
363 | {} | |
364 | }; | |
365 | ||
366 | U_BOOT_DRIVER(at91_system_clk) = { | |
367 | .name = "at91-system-clk", | |
368 | .id = UCLASS_MISC, | |
369 | .of_match = at91_system_clk_match, | |
370 | .bind = at91_system_clk_bind, | |
371 | }; | |
372 | ||
373 | static inline int is_pck(int id) | |
374 | { | |
375 | return (id >= 8) && (id <= 15); | |
376 | } | |
377 | ||
378 | static ulong system_clk_get_rate(struct clk *clk) | |
379 | { | |
380 | struct clk clk_dev; | |
381 | int ret; | |
382 | ||
383 | ret = clk_get_by_index(clk->dev, 0, &clk_dev); | |
384 | if (ret) | |
385 | return -EINVAL; | |
386 | ||
387 | return clk_get_rate(&clk_dev); | |
388 | } | |
389 | ||
390 | static ulong system_clk_set_rate(struct clk *clk, ulong rate) | |
391 | { | |
392 | struct clk clk_dev; | |
393 | int ret; | |
394 | ||
395 | ret = clk_get_by_index(clk->dev, 0, &clk_dev); | |
396 | if (ret) | |
397 | return -EINVAL; | |
398 | ||
399 | return clk_set_rate(&clk_dev, rate); | |
400 | } | |
401 | ||
402 | static int system_clk_enable(struct clk *clk) | |
403 | { | |
404 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
405 | struct at91_pmc *pmc = plat->reg_base; | |
406 | u32 mask; | |
407 | ||
408 | if (clk->id > SYSTEM_MAX_ID) | |
409 | return -EINVAL; | |
410 | ||
411 | mask = BIT(clk->id); | |
412 | ||
413 | writel(mask, &pmc->scer); | |
414 | ||
415 | /** | |
416 | * For the programmable clocks the Ready status in the PMC | |
417 | * status register should be checked after enabling. | |
418 | * For other clocks this is unnecessary. | |
419 | */ | |
420 | if (!is_pck(clk->id)) | |
421 | return 0; | |
422 | ||
423 | while (!(readl(&pmc->sr) & mask)) | |
424 | ; | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | static struct clk_ops system_clk_ops = { | |
430 | .of_xlate = at91_clk_of_xlate, | |
431 | .get_rate = system_clk_get_rate, | |
432 | .set_rate = system_clk_set_rate, | |
433 | .enable = system_clk_enable, | |
434 | }; | |
435 | ||
436 | U_BOOT_DRIVER(system_clk) = { | |
437 | .name = "system-clk", | |
438 | .id = UCLASS_CLK, | |
439 | .probe = at91_clk_probe, | |
41575d8e | 440 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
441 | .ops = &system_clk_ops, |
442 | }; | |
443 | ||
444 | /* Peripheral clock specific code. */ | |
445 | #define PERIPHERAL_ID_MIN 2 | |
446 | #define PERIPHERAL_ID_MAX 31 | |
447 | #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) | |
448 | ||
449 | enum periph_clk_type { | |
450 | CLK_PERIPH_AT91RM9200 = 0, | |
451 | CLK_PERIPH_AT91SAM9X5, | |
452 | }; | |
453 | ||
454 | /** | |
455 | * sam9x5_periph_clk_bind() - for the periph clock driver | |
456 | * Recursively bind its children as clk devices. | |
457 | * | |
458 | * @return: 0 on success, or negative error code on failure | |
459 | */ | |
460 | static int sam9x5_periph_clk_bind(struct udevice *dev) | |
461 | { | |
462 | return at91_clk_sub_device_bind(dev, "periph-clk"); | |
463 | } | |
464 | ||
465 | static const struct udevice_id sam9x5_periph_clk_match[] = { | |
466 | { | |
467 | .compatible = "atmel,at91rm9200-clk-peripheral", | |
468 | .data = CLK_PERIPH_AT91RM9200, | |
469 | }, | |
470 | { | |
471 | .compatible = "atmel,at91sam9x5-clk-peripheral", | |
472 | .data = CLK_PERIPH_AT91SAM9X5, | |
473 | }, | |
474 | {} | |
475 | }; | |
476 | ||
477 | U_BOOT_DRIVER(sam9x5_periph_clk) = { | |
478 | .name = "sam9x5-periph-clk", | |
479 | .id = UCLASS_MISC, | |
480 | .of_match = sam9x5_periph_clk_match, | |
481 | .bind = sam9x5_periph_clk_bind, | |
482 | }; | |
483 | ||
484 | static int periph_clk_enable(struct clk *clk) | |
485 | { | |
486 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
487 | struct at91_pmc *pmc = plat->reg_base; | |
488 | enum periph_clk_type clk_type; | |
489 | void *addr; | |
490 | ||
491 | if (clk->id < PERIPHERAL_ID_MIN) | |
492 | return -1; | |
493 | ||
494 | clk_type = dev_get_driver_data(dev_get_parent(clk->dev)); | |
495 | if (clk_type == CLK_PERIPH_AT91RM9200) { | |
496 | addr = &pmc->pcer; | |
497 | if (clk->id > PERIPHERAL_ID_MAX) | |
498 | addr = &pmc->pcer1; | |
499 | ||
500 | setbits_le32(addr, PERIPHERAL_MASK(clk->id)); | |
501 | } else { | |
502 | writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); | |
503 | setbits_le32(&pmc->pcr, | |
504 | AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN); | |
505 | } | |
506 | ||
507 | return 0; | |
508 | } | |
509 | ||
510 | static ulong periph_get_rate(struct clk *clk) | |
511 | { | |
512 | struct udevice *dev; | |
513 | struct clk clk_dev; | |
514 | ulong clk_rate; | |
515 | int ret; | |
516 | ||
517 | dev = dev_get_parent(clk->dev); | |
518 | ||
519 | ret = clk_get_by_index(dev, 0, &clk_dev); | |
520 | if (ret) | |
521 | return ret; | |
522 | ||
523 | clk_rate = clk_get_rate(&clk_dev); | |
524 | ||
525 | clk_free(&clk_dev); | |
526 | ||
527 | return clk_rate; | |
528 | } | |
529 | ||
530 | static struct clk_ops periph_clk_ops = { | |
531 | .of_xlate = at91_clk_of_xlate, | |
532 | .enable = periph_clk_enable, | |
533 | .get_rate = periph_get_rate, | |
534 | }; | |
535 | ||
536 | U_BOOT_DRIVER(clk_periph) = { | |
537 | .name = "periph-clk", | |
538 | .id = UCLASS_CLK, | |
41575d8e | 539 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
540 | .probe = at91_clk_probe, |
541 | .ops = &periph_clk_ops, | |
542 | }; | |
543 | ||
544 | /* UTMI clock specific code. */ | |
545 | #ifdef CONFIG_AT91_UTMI | |
546 | ||
547 | /* | |
548 | * The purpose of this clock is to generate a 480 MHz signal. A different | |
549 | * rate can't be configured. | |
550 | */ | |
551 | #define UTMI_RATE 480000000 | |
552 | ||
553 | static int utmi_clk_enable(struct clk *clk) | |
554 | { | |
555 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
556 | struct at91_pmc *pmc = plat->reg_base; | |
557 | struct clk clk_dev; | |
558 | ulong clk_rate; | |
559 | u32 utmi_ref_clk_freq; | |
560 | u32 tmp; | |
561 | int err; | |
562 | int timeout = 2000000; | |
563 | ||
564 | if (readl(&pmc->sr) & AT91_PMC_LOCKU) | |
565 | return 0; | |
566 | ||
567 | /* | |
568 | * If mainck rate is different from 12 MHz, we have to configure the | |
569 | * FREQ field of the SFR_UTMICKTRIM register to generate properly | |
570 | * the utmi clock. | |
571 | */ | |
572 | err = clk_get_by_index(clk->dev, 0, &clk_dev); | |
573 | if (err) | |
574 | return -EINVAL; | |
575 | ||
576 | clk_rate = clk_get_rate(&clk_dev); | |
577 | switch (clk_rate) { | |
578 | case 12000000: | |
579 | utmi_ref_clk_freq = 0; | |
580 | break; | |
581 | case 16000000: | |
582 | utmi_ref_clk_freq = 1; | |
583 | break; | |
584 | case 24000000: | |
585 | utmi_ref_clk_freq = 2; | |
586 | break; | |
587 | /* | |
588 | * Not supported on SAMA5D2 but it's not an issue since MAINCK | |
589 | * maximum value is 24 MHz. | |
590 | */ | |
591 | case 48000000: | |
592 | utmi_ref_clk_freq = 3; | |
593 | break; | |
594 | default: | |
595 | printf("UTMICK: unsupported mainck rate\n"); | |
596 | return -EINVAL; | |
597 | } | |
598 | ||
599 | if (plat->regmap_sfr) { | |
600 | err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp); | |
601 | if (err) | |
602 | return -EINVAL; | |
603 | ||
604 | tmp &= ~AT91_UTMICKTRIM_FREQ; | |
605 | tmp |= utmi_ref_clk_freq; | |
606 | err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp); | |
607 | if (err) | |
608 | return -EINVAL; | |
609 | } else if (utmi_ref_clk_freq) { | |
610 | printf("UTMICK: sfr node required\n"); | |
611 | return -EINVAL; | |
612 | } | |
613 | ||
614 | tmp = readl(&pmc->uckr); | |
615 | tmp |= AT91_PMC_UPLLEN | | |
616 | AT91_PMC_UPLLCOUNT | | |
617 | AT91_PMC_BIASEN; | |
618 | writel(tmp, &pmc->uckr); | |
619 | ||
620 | while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU)) | |
621 | ; | |
622 | if (!timeout) { | |
623 | printf("UTMICK: timeout waiting for UPLL lock\n"); | |
624 | return -ETIMEDOUT; | |
625 | } | |
626 | ||
627 | return 0; | |
628 | } | |
629 | ||
630 | static ulong utmi_clk_get_rate(struct clk *clk) | |
631 | { | |
632 | /* UTMI clk rate is fixed. */ | |
633 | return UTMI_RATE; | |
634 | } | |
635 | ||
636 | static struct clk_ops utmi_clk_ops = { | |
637 | .enable = utmi_clk_enable, | |
638 | .get_rate = utmi_clk_get_rate, | |
639 | }; | |
640 | ||
641 | static int utmi_clk_ofdata_to_platdata(struct udevice *dev) | |
642 | { | |
643 | struct pmc_platdata *plat = dev_get_platdata(dev); | |
644 | struct udevice *syscon; | |
645 | ||
646 | uclass_get_device_by_phandle(UCLASS_SYSCON, dev, | |
647 | "regmap-sfr", &syscon); | |
648 | ||
649 | if (syscon) | |
650 | plat->regmap_sfr = syscon_get_regmap(syscon); | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
655 | static int utmi_clk_probe(struct udevice *dev) | |
656 | { | |
657 | return at91_pmc_core_probe(dev); | |
658 | } | |
659 | ||
660 | static const struct udevice_id utmi_clk_match[] = { | |
661 | { .compatible = "atmel,at91sam9x5-clk-utmi" }, | |
662 | {} | |
663 | }; | |
664 | ||
665 | U_BOOT_DRIVER(at91sam9x5_utmi_clk) = { | |
666 | .name = "at91sam9x5-utmi-clk", | |
667 | .id = UCLASS_CLK, | |
668 | .of_match = utmi_clk_match, | |
669 | .probe = utmi_clk_probe, | |
670 | .ofdata_to_platdata = utmi_clk_ofdata_to_platdata, | |
41575d8e | 671 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
672 | .ops = &utmi_clk_ops, |
673 | }; | |
674 | ||
675 | #endif /* CONFIG_AT91_UTMI */ | |
676 | ||
677 | /* H32MX clock specific code. */ | |
678 | #ifdef CONFIG_AT91_H32MX | |
679 | ||
680 | #define H32MX_MAX_FREQ 90000000 | |
681 | ||
682 | static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk) | |
683 | { | |
684 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
685 | struct at91_pmc *pmc = plat->reg_base; | |
686 | ulong rate = gd->arch.mck_rate_hz; | |
687 | ||
688 | if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV) | |
689 | rate /= 2; | |
690 | ||
691 | if (rate > H32MX_MAX_FREQ) | |
692 | dev_dbg(clk->dev, "H32MX clock is too fast\n"); | |
693 | ||
694 | return rate; | |
695 | } | |
696 | ||
697 | static struct clk_ops sama5d4_h32mx_clk_ops = { | |
698 | .get_rate = sama5d4_h32mx_clk_get_rate, | |
699 | }; | |
700 | ||
701 | static int sama5d4_h32mx_clk_probe(struct udevice *dev) | |
702 | { | |
703 | return at91_pmc_core_probe(dev); | |
704 | } | |
705 | ||
706 | static const struct udevice_id sama5d4_h32mx_clk_match[] = { | |
707 | { .compatible = "atmel,sama5d4-clk-h32mx" }, | |
708 | {} | |
709 | }; | |
710 | ||
711 | U_BOOT_DRIVER(sama5d4_h32mx_clk) = { | |
712 | .name = "sama5d4-h32mx-clk", | |
713 | .id = UCLASS_CLK, | |
714 | .of_match = sama5d4_h32mx_clk_match, | |
715 | .probe = sama5d4_h32mx_clk_probe, | |
41575d8e | 716 | .platdata_auto = sizeof(struct pmc_platdata), |
653bcce4 CB |
717 | .ops = &sama5d4_h32mx_clk_ops, |
718 | }; | |
719 | ||
720 | #endif /* CONFIG_AT91_H32MX */ | |
721 | ||
722 | /* Generic clock specific code. */ | |
723 | #ifdef CONFIG_AT91_GENERIC_CLK | |
724 | ||
725 | #define GENERATED_SOURCE_MAX 6 | |
726 | #define GENERATED_MAX_DIV 255 | |
727 | ||
728 | /** | |
729 | * generated_clk_bind() - for the generated clock driver | |
730 | * Recursively bind its children as clk devices. | |
731 | * | |
732 | * @return: 0 on success, or negative error code on failure | |
733 | */ | |
734 | static int generated_clk_bind(struct udevice *dev) | |
735 | { | |
736 | return at91_clk_sub_device_bind(dev, "generic-clk"); | |
737 | } | |
738 | ||
739 | static const struct udevice_id generated_clk_match[] = { | |
740 | { .compatible = "atmel,sama5d2-clk-generated" }, | |
741 | {} | |
742 | }; | |
743 | ||
744 | U_BOOT_DRIVER(generated_clk) = { | |
745 | .name = "generated-clk", | |
746 | .id = UCLASS_MISC, | |
747 | .of_match = generated_clk_match, | |
748 | .bind = generated_clk_bind, | |
749 | }; | |
750 | ||
751 | struct generic_clk_priv { | |
752 | u32 num_parents; | |
753 | }; | |
754 | ||
755 | static ulong generic_clk_get_rate(struct clk *clk) | |
756 | { | |
757 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
758 | struct at91_pmc *pmc = plat->reg_base; | |
759 | struct clk parent; | |
760 | ulong clk_rate; | |
761 | u32 tmp, gckdiv; | |
762 | u8 clock_source, parent_index; | |
763 | int ret; | |
764 | ||
765 | writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); | |
766 | tmp = readl(&pmc->pcr); | |
767 | clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) & | |
768 | AT91_PMC_PCR_GCKCSS_MASK; | |
769 | gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; | |
770 | ||
771 | parent_index = clock_source - 1; | |
772 | ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent); | |
773 | if (ret) | |
774 | return 0; | |
775 | ||
776 | clk_rate = clk_get_rate(&parent) / (gckdiv + 1); | |
777 | ||
778 | clk_free(&parent); | |
779 | ||
780 | return clk_rate; | |
781 | } | |
782 | ||
783 | static ulong generic_clk_set_rate(struct clk *clk, ulong rate) | |
784 | { | |
785 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
786 | struct at91_pmc *pmc = plat->reg_base; | |
787 | struct generic_clk_priv *priv = dev_get_priv(clk->dev); | |
788 | struct clk parent, best_parent; | |
789 | ulong tmp_rate, best_rate = rate, parent_rate; | |
790 | int tmp_diff, best_diff = -1; | |
791 | u32 div, best_div = 0; | |
792 | u8 best_parent_index, best_clock_source = 0; | |
793 | u8 i; | |
794 | u32 tmp; | |
795 | int ret; | |
796 | ||
797 | for (i = 0; i < priv->num_parents; i++) { | |
798 | ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent); | |
799 | if (ret) | |
800 | return ret; | |
801 | ||
802 | parent_rate = clk_get_rate(&parent); | |
803 | if (IS_ERR_VALUE(parent_rate)) | |
804 | return parent_rate; | |
805 | ||
806 | for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { | |
807 | tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); | |
808 | tmp_diff = abs(rate - tmp_rate); | |
809 | ||
810 | if (best_diff < 0 || best_diff > tmp_diff) { | |
811 | best_rate = tmp_rate; | |
812 | best_diff = tmp_diff; | |
813 | ||
814 | best_div = div - 1; | |
815 | best_parent = parent; | |
816 | best_parent_index = i; | |
817 | best_clock_source = best_parent_index + 1; | |
818 | } | |
819 | ||
820 | if (!best_diff || tmp_rate < rate) | |
821 | break; | |
822 | } | |
823 | ||
824 | if (!best_diff) | |
825 | break; | |
826 | } | |
827 | ||
828 | debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n", | |
829 | best_parent.dev->name, best_rate, best_div); | |
830 | ||
831 | ret = clk_enable(&best_parent); | |
832 | if (ret) | |
833 | return ret; | |
834 | ||
835 | writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); | |
836 | tmp = readl(&pmc->pcr); | |
837 | tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS); | |
838 | tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) | | |
839 | AT91_PMC_PCR_CMD_WRITE | | |
840 | AT91_PMC_PCR_GCKDIV_(best_div) | | |
841 | AT91_PMC_PCR_GCKEN; | |
842 | writel(tmp, &pmc->pcr); | |
843 | ||
844 | while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY)) | |
845 | ; | |
846 | ||
847 | return 0; | |
848 | } | |
849 | ||
850 | static struct clk_ops generic_clk_ops = { | |
851 | .of_xlate = at91_clk_of_xlate, | |
852 | .get_rate = generic_clk_get_rate, | |
853 | .set_rate = generic_clk_set_rate, | |
854 | }; | |
855 | ||
856 | static int generic_clk_ofdata_to_platdata(struct udevice *dev) | |
857 | { | |
858 | struct generic_clk_priv *priv = dev_get_priv(dev); | |
859 | u32 cells[GENERATED_SOURCE_MAX]; | |
860 | u32 num_parents; | |
861 | ||
862 | num_parents = fdtdec_get_int_array_count(gd->fdt_blob, | |
863 | dev_of_offset(dev_get_parent(dev)), "clocks", cells, | |
864 | GENERATED_SOURCE_MAX); | |
865 | ||
866 | if (!num_parents) | |
867 | return -1; | |
868 | ||
869 | priv->num_parents = num_parents; | |
870 | ||
871 | return 0; | |
872 | } | |
873 | ||
874 | U_BOOT_DRIVER(generic_clk) = { | |
875 | .name = "generic-clk", | |
876 | .id = UCLASS_CLK, | |
877 | .probe = at91_clk_probe, | |
878 | .ofdata_to_platdata = generic_clk_ofdata_to_platdata, | |
41575d8e SG |
879 | .priv_auto = sizeof(struct generic_clk_priv), |
880 | .platdata_auto = sizeof(struct pmc_platdata), | |
653bcce4 CB |
881 | .ops = &generic_clk_ops, |
882 | }; | |
883 | ||
884 | #endif /* CONFIG_AT91_GENERIC_CLK */ | |
885 | ||
886 | /* USB clock specific code. */ | |
887 | #ifdef CONFIG_AT91_USB_CLK | |
888 | ||
889 | #define AT91_USB_CLK_SOURCE_MAX 2 | |
890 | #define AT91_USB_CLK_MAX_DIV 15 | |
891 | ||
892 | struct at91_usb_clk_priv { | |
893 | u32 num_clksource; | |
894 | }; | |
895 | ||
896 | static ulong at91_usb_clk_get_rate(struct clk *clk) | |
897 | { | |
898 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
899 | struct at91_pmc *pmc = plat->reg_base; | |
900 | struct clk source; | |
901 | u32 tmp, usbdiv; | |
902 | u8 source_index; | |
903 | int ret; | |
904 | ||
905 | tmp = readl(&pmc->pcr); | |
906 | source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) & | |
907 | AT91_PMC_USB_USBS_MASK; | |
908 | usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK; | |
909 | ||
910 | ret = clk_get_by_index(clk->dev, source_index, &source); | |
911 | if (ret) | |
912 | return 0; | |
913 | ||
914 | return clk_get_rate(&source) / (usbdiv + 1); | |
915 | } | |
916 | ||
917 | static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate) | |
918 | { | |
919 | struct pmc_platdata *plat = dev_get_platdata(clk->dev); | |
920 | struct at91_pmc *pmc = plat->reg_base; | |
921 | struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev); | |
922 | struct clk source, best_source; | |
923 | ulong tmp_rate, best_rate = rate, source_rate; | |
924 | int tmp_diff, best_diff = -1; | |
925 | u32 div, best_div = 0; | |
926 | u8 best_source_index = 0; | |
927 | u8 i; | |
928 | u32 tmp; | |
929 | int ret; | |
930 | ||
931 | for (i = 0; i < priv->num_clksource; i++) { | |
932 | ret = clk_get_by_index(clk->dev, i, &source); | |
933 | if (ret) | |
934 | return ret; | |
935 | ||
936 | source_rate = clk_get_rate(&source); | |
937 | if (IS_ERR_VALUE(source_rate)) | |
938 | return source_rate; | |
939 | ||
940 | for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) { | |
941 | tmp_rate = DIV_ROUND_CLOSEST(source_rate, div); | |
942 | tmp_diff = abs(rate - tmp_rate); | |
943 | ||
944 | if (best_diff < 0 || best_diff > tmp_diff) { | |
945 | best_rate = tmp_rate; | |
946 | best_diff = tmp_diff; | |
947 | ||
948 | best_div = div - 1; | |
949 | best_source = source; | |
950 | best_source_index = i; | |
951 | } | |
952 | ||
953 | if (!best_diff || tmp_rate < rate) | |
954 | break; | |
955 | } | |
956 | ||
957 | if (!best_diff) | |
958 | break; | |
959 | } | |
960 | ||
961 | debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n", | |
962 | best_source.dev->name, best_rate, best_div); | |
963 | ||
964 | ret = clk_enable(&best_source); | |
965 | if (ret) | |
966 | return ret; | |
967 | ||
968 | tmp = AT91_PMC_USB_USBS_(best_source_index) | | |
969 | AT91_PMC_USB_DIV_(best_div); | |
970 | writel(tmp, &pmc->usb); | |
971 | ||
972 | return 0; | |
973 | } | |
974 | ||
975 | static struct clk_ops at91_usb_clk_ops = { | |
976 | .get_rate = at91_usb_clk_get_rate, | |
977 | .set_rate = at91_usb_clk_set_rate, | |
978 | }; | |
979 | ||
980 | static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev) | |
981 | { | |
982 | struct at91_usb_clk_priv *priv = dev_get_priv(dev); | |
983 | u32 cells[AT91_USB_CLK_SOURCE_MAX]; | |
984 | u32 num_clksource; | |
985 | ||
986 | num_clksource = fdtdec_get_int_array_count(gd->fdt_blob, | |
987 | dev_of_offset(dev), | |
988 | "clocks", cells, | |
989 | AT91_USB_CLK_SOURCE_MAX); | |
990 | ||
991 | if (!num_clksource) | |
992 | return -1; | |
993 | ||
994 | priv->num_clksource = num_clksource; | |
995 | ||
996 | return 0; | |
997 | } | |
998 | ||
999 | static int at91_usb_clk_probe(struct udevice *dev) | |
1000 | { | |
1001 | return at91_pmc_core_probe(dev); | |
1002 | } | |
1003 | ||
1004 | static const struct udevice_id at91_usb_clk_match[] = { | |
1005 | { .compatible = "atmel,at91sam9x5-clk-usb" }, | |
1006 | {} | |
1007 | }; | |
1008 | ||
1009 | U_BOOT_DRIVER(at91_usb_clk) = { | |
1010 | .name = "at91-usb-clk", | |
1011 | .id = UCLASS_CLK, | |
1012 | .of_match = at91_usb_clk_match, | |
1013 | .probe = at91_usb_clk_probe, | |
1014 | .ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata, | |
41575d8e SG |
1015 | .priv_auto = sizeof(struct at91_usb_clk_priv), |
1016 | .platdata_auto = sizeof(struct pmc_platdata), | |
653bcce4 CB |
1017 | .ops = &at91_usb_clk_ops, |
1018 | }; | |
1019 | ||
1020 | #endif /* CONFIG_AT91_USB_CLK */ |