]>
Commit | Line | Data |
---|---|---|
82a248df MB |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Marvell Armada 37xx SoC Peripheral clocks | |
4 | * | |
5 | * Marek Behun <marek.behun@nic.cz> | |
6 | * | |
7 | * Based on Linux driver by: | |
8 | * Gregory CLEMENT <gregory.clement@free-electrons.com> | |
9 | */ | |
10 | ||
11 | #include <common.h> | |
12 | #include <malloc.h> | |
13 | #include <clk-uclass.h> | |
14 | #include <clk.h> | |
15 | #include <dm.h> | |
16 | #include <asm/io.h> | |
17 | #include <asm/arch/cpu.h> | |
336d4615 | 18 | #include <dm/device_compat.h> |
82a248df MB |
19 | |
20 | #define TBG_SEL 0x0 | |
21 | #define DIV_SEL0 0x4 | |
22 | #define DIV_SEL1 0x8 | |
23 | #define DIV_SEL2 0xC | |
24 | #define CLK_SEL 0x10 | |
25 | #define CLK_DIS 0x14 | |
26 | ||
27 | enum a37xx_periph_parent { | |
28 | TBG_A_P = 0, | |
29 | TBG_B_P = 1, | |
30 | TBG_A_S = 2, | |
31 | TBG_B_S = 3, | |
32 | MAX_TBG_PARENTS = 4, | |
33 | XTAL = 4, | |
34 | MAX_PARENTS = 5, | |
35 | }; | |
36 | ||
37 | static const struct { | |
38 | const char *name; | |
39 | enum a37xx_periph_parent parent; | |
40 | } a37xx_periph_parent_names[] = { | |
41 | { "TBG-A-P", TBG_A_P }, | |
42 | { "TBG-B-P", TBG_B_P }, | |
43 | { "TBG-A-S", TBG_A_S }, | |
44 | { "TBG-B-S", TBG_B_S }, | |
45 | { "xtal", XTAL }, | |
46 | }; | |
47 | ||
48 | struct clk_periph; | |
49 | ||
50 | struct a37xx_periphclk { | |
51 | void __iomem *reg; | |
52 | ||
53 | ulong parents[MAX_PARENTS]; | |
54 | ||
55 | const struct clk_periph *clks; | |
56 | bool clk_has_periph_parent[16]; | |
57 | int clk_parent[16]; | |
58 | ||
59 | int count; | |
60 | }; | |
61 | ||
62 | struct clk_div_table { | |
63 | u32 div; | |
64 | u32 val; | |
65 | }; | |
66 | ||
67 | struct clk_periph { | |
68 | const char *name; | |
69 | ||
70 | const char *parent_name; | |
71 | ||
72 | u32 disable_bit; | |
73 | int mux_shift; | |
74 | ||
75 | const struct clk_div_table *div_table[2]; | |
76 | s32 div_reg_off[2]; | |
77 | u32 div_mask[2]; | |
78 | int div_shift[2]; | |
79 | ||
80 | unsigned can_gate : 1; | |
81 | unsigned can_mux : 1; | |
82 | unsigned dividers : 2; | |
83 | }; | |
84 | ||
85 | static const struct clk_div_table div_table1[] = { | |
86 | { 1, 1 }, | |
87 | { 2, 2 }, | |
88 | { 0, 0 }, | |
89 | }; | |
90 | ||
91 | static const struct clk_div_table div_table2[] = { | |
92 | { 2, 1 }, | |
93 | { 4, 2 }, | |
94 | { 0, 0 }, | |
95 | }; | |
96 | ||
97 | static const struct clk_div_table div_table6[] = { | |
98 | { 1, 1 }, | |
99 | { 2, 2 }, | |
100 | { 3, 3 }, | |
101 | { 4, 4 }, | |
102 | { 5, 5 }, | |
103 | { 6, 6 }, | |
104 | { 0, 0 }, | |
105 | }; | |
106 | ||
107 | #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1) \ | |
108 | { \ | |
109 | .name = #_n, \ | |
110 | .disable_bit = BIT(_d), \ | |
111 | .mux_shift = _mux, \ | |
112 | .div_table[0] = div_table6, \ | |
113 | .div_table[1] = div_table6, \ | |
114 | .div_reg_off[0] = _r0, \ | |
115 | .div_reg_off[1] = _r1, \ | |
116 | .div_shift[0] = _s0, \ | |
117 | .div_shift[1] = _s1, \ | |
118 | .div_mask[0] = 7, \ | |
119 | .div_mask[1] = 7, \ | |
120 | .can_gate = 1, \ | |
121 | .can_mux = 1, \ | |
122 | .dividers = 2, \ | |
123 | } | |
124 | ||
125 | #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t) \ | |
126 | { \ | |
127 | .name = #_n, \ | |
128 | .disable_bit = BIT(_d), \ | |
129 | .mux_shift = _mux, \ | |
130 | .div_table[0] = _t, \ | |
131 | .div_reg_off[0] = _r, \ | |
132 | .div_shift[0] = _s, \ | |
133 | .div_mask[0] = _m, \ | |
134 | .can_gate = 1, \ | |
135 | .can_mux = 1, \ | |
136 | .dividers = 1, \ | |
137 | } | |
138 | ||
139 | #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p) \ | |
140 | { \ | |
141 | .name = #_n, \ | |
142 | .parent_name = _p, \ | |
143 | .disable_bit = BIT(_d), \ | |
144 | .div_table[0] = _t, \ | |
145 | .div_reg_off[0] = _r, \ | |
146 | .div_shift[0] = _s, \ | |
147 | .div_mask[0] = _m, \ | |
148 | .can_gate = 1, \ | |
149 | .dividers = 1, \ | |
150 | } | |
151 | ||
152 | #define CLK_GATE(_n, _d, _p) \ | |
153 | { \ | |
154 | .name = #_n, \ | |
155 | .parent_name = _p, \ | |
156 | .disable_bit = BIT(_d), \ | |
157 | .can_gate = 1, \ | |
158 | } | |
159 | ||
160 | #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t) \ | |
161 | { \ | |
162 | .name = #_n, \ | |
163 | .mux_shift = _mux, \ | |
164 | .div_table[0] = _t, \ | |
165 | .div_reg_off[0] = _r, \ | |
166 | .div_shift[0] = _s, \ | |
167 | .div_mask[0] = _m, \ | |
168 | .can_mux = 1, \ | |
169 | .dividers = 1, \ | |
170 | } | |
171 | ||
172 | #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1) \ | |
173 | { \ | |
174 | .name = #_n, \ | |
175 | .mux_shift = _mux, \ | |
176 | .div_table[0] = div_table6, \ | |
177 | .div_table[1] = div_table6, \ | |
178 | .div_reg_off[0] = _r0, \ | |
179 | .div_reg_off[1] = _r1, \ | |
180 | .div_shift[0] = _s0, \ | |
181 | .div_shift[1] = _s1, \ | |
182 | .div_mask[0] = 7, \ | |
183 | .div_mask[1] = 7, \ | |
184 | .can_mux = 1, \ | |
185 | .dividers = 2, \ | |
186 | } | |
187 | ||
188 | /* NB periph clocks */ | |
189 | static const struct clk_periph clks_nb[] = { | |
190 | CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13), | |
191 | CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7), | |
192 | CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0), | |
193 | CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6), | |
194 | CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12), | |
195 | CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6), | |
196 | CLK_GATE(avs, 11, "xtal"), | |
197 | CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24), | |
198 | CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0), | |
199 | CLK_GATE(i2c_2, 16, "xtal"), | |
200 | CLK_GATE(i2c_1, 17, "xtal"), | |
201 | CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"), | |
202 | CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12), | |
203 | CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6), | |
204 | CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6), | |
205 | CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19), | |
206 | CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6), | |
207 | { }, | |
208 | }; | |
209 | ||
210 | /* SB periph clocks */ | |
211 | static const struct clk_periph clks_sb[] = { | |
212 | CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9), | |
213 | CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21), | |
214 | CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9), | |
215 | CLK_GATE(gbe1_50, 0, "gbe_50"), | |
216 | CLK_GATE(gbe0_50, 1, "gbe_50"), | |
217 | CLK_GATE(gbe1_125, 2, "gbe_125"), | |
218 | CLK_GATE(gbe0_125, 3, "gbe_125"), | |
219 | CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"), | |
220 | CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"), | |
221 | CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"), | |
222 | CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6), | |
223 | CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12), | |
224 | CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18), | |
225 | { }, | |
226 | }; | |
227 | ||
de75fb09 | 228 | static int get_mux(struct a37xx_periphclk *priv, int shift) |
82a248df MB |
229 | { |
230 | return (readl(priv->reg + TBG_SEL) >> shift) & 3; | |
231 | } | |
232 | ||
de75fb09 MB |
233 | static void set_mux(struct a37xx_periphclk *priv, int shift, int val) |
234 | { | |
235 | u32 reg; | |
236 | ||
237 | reg = readl(priv->reg + TBG_SEL); | |
238 | reg &= ~(3 << shift); | |
239 | reg |= (val & 3) << shift; | |
240 | writel(reg, priv->reg + TBG_SEL); | |
241 | } | |
242 | ||
82a248df MB |
243 | static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id); |
244 | ||
245 | static ulong get_parent_rate(struct a37xx_periphclk *priv, int id) | |
246 | { | |
247 | const struct clk_periph *clk = &priv->clks[id]; | |
248 | ulong res; | |
249 | ||
250 | if (clk->can_mux) { | |
251 | /* parent is one of TBG clocks */ | |
252 | int tbg = get_mux(priv, clk->mux_shift); | |
253 | ||
254 | res = priv->parents[tbg]; | |
255 | } else if (priv->clk_has_periph_parent[id]) { | |
256 | /* parent is one of other periph clocks */ | |
257 | ||
258 | if (priv->clk_parent[id] >= priv->count) | |
259 | return -EINVAL; | |
260 | ||
261 | res = periph_clk_get_rate(priv, priv->clk_parent[id]); | |
262 | } else { | |
263 | /* otherwise parent is one of TBGs or XTAL */ | |
264 | ||
265 | if (priv->clk_parent[id] >= MAX_PARENTS) | |
266 | return -EINVAL; | |
267 | ||
268 | res = priv->parents[priv->clk_parent[id]]; | |
269 | } | |
270 | ||
271 | return res; | |
272 | } | |
273 | ||
274 | static ulong get_div(struct a37xx_periphclk *priv, | |
275 | const struct clk_periph *clk, int idx) | |
276 | { | |
277 | const struct clk_div_table *i; | |
278 | u32 reg; | |
279 | ||
280 | reg = readl(priv->reg + clk->div_reg_off[idx]); | |
281 | reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx]; | |
282 | ||
283 | /* find divisor for register value val */ | |
284 | for (i = clk->div_table[idx]; i && i->div != 0; ++i) | |
285 | if (i->val == reg) | |
286 | return i->div; | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
de75fb09 MB |
291 | static void set_div_val(struct a37xx_periphclk *priv, |
292 | const struct clk_periph *clk, int idx, int val) | |
293 | { | |
294 | u32 reg; | |
295 | ||
296 | reg = readl(priv->reg + clk->div_reg_off[idx]); | |
297 | reg &= ~(clk->div_mask[idx] << clk->div_shift[idx]); | |
298 | reg |= (val & clk->div_mask[idx]) << clk->div_shift[idx]; | |
299 | writel(reg, priv->reg + clk->div_reg_off[idx]); | |
300 | } | |
301 | ||
82a248df MB |
302 | static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id) |
303 | { | |
304 | const struct clk_periph *clk = &priv->clks[id]; | |
305 | ulong rate, div; | |
306 | int i; | |
307 | ||
308 | rate = get_parent_rate(priv, id); | |
309 | if (rate == -EINVAL) | |
310 | return -EINVAL; | |
311 | ||
312 | /* divide the parent rate by dividers */ | |
313 | div = 1; | |
314 | for (i = 0; i < clk->dividers; ++i) | |
315 | div *= get_div(priv, clk, i); | |
316 | ||
317 | if (!div) | |
318 | return 0; | |
319 | ||
320 | return DIV_ROUND_UP(rate, div); | |
321 | } | |
322 | ||
323 | static ulong armada_37xx_periph_clk_get_rate(struct clk *clk) | |
324 | { | |
325 | struct a37xx_periphclk *priv = dev_get_priv(clk->dev); | |
326 | ||
327 | if (clk->id >= priv->count) | |
328 | return -EINVAL; | |
329 | ||
330 | return periph_clk_get_rate(priv, clk->id); | |
331 | } | |
332 | ||
333 | static int periph_clk_enable(struct clk *clk, int enable) | |
334 | { | |
335 | struct a37xx_periphclk *priv = dev_get_priv(clk->dev); | |
336 | const struct clk_periph *periph_clk = &priv->clks[clk->id]; | |
337 | ||
338 | if (clk->id >= priv->count) | |
339 | return -EINVAL; | |
340 | ||
341 | if (!periph_clk->can_gate) | |
342 | return -ENOTSUPP; | |
343 | ||
344 | if (enable) | |
345 | clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit); | |
346 | else | |
347 | setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit); | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
352 | static int armada_37xx_periph_clk_enable(struct clk *clk) | |
353 | { | |
354 | return periph_clk_enable(clk, 1); | |
355 | } | |
356 | ||
357 | static int armada_37xx_periph_clk_disable(struct clk *clk) | |
358 | { | |
359 | return periph_clk_enable(clk, 0); | |
360 | } | |
361 | ||
de75fb09 MB |
362 | #define diff(a, b) abs((long)(a) - (long)(b)) |
363 | ||
364 | static ulong find_best_div(const struct clk_div_table *t0, | |
365 | const struct clk_div_table *t1, ulong parent_rate, | |
366 | ulong req_rate, int *v0, int *v1) | |
367 | { | |
368 | const struct clk_div_table *i, *j; | |
369 | ulong rate, best_rate = 0; | |
370 | ||
371 | for (i = t0; i && i->div; ++i) { | |
372 | for (j = t1; j && j->div; ++j) { | |
373 | rate = DIV_ROUND_UP(parent_rate, i->div * j->div); | |
374 | ||
375 | if (!best_rate || | |
376 | diff(rate, req_rate) < diff(best_rate, req_rate)) { | |
377 | best_rate = rate; | |
378 | *v0 = i->val; | |
379 | *v1 = j->val; | |
380 | } | |
381 | } | |
382 | } | |
383 | ||
384 | return best_rate; | |
385 | } | |
386 | ||
387 | static ulong armada_37xx_periph_clk_set_rate(struct clk *clk, ulong req_rate) | |
388 | { | |
389 | struct a37xx_periphclk *priv = dev_get_priv(clk->dev); | |
390 | const struct clk_periph *periph_clk = &priv->clks[clk->id]; | |
391 | ulong rate, old_rate, parent_rate; | |
392 | int div_val0 = 0, div_val1 = 0; | |
393 | const struct clk_div_table *t1; | |
394 | static const struct clk_div_table empty_table[2] = { | |
395 | { 1, 0 }, | |
396 | { 0, 0 } | |
397 | }; | |
398 | ||
399 | if (clk->id > priv->count) | |
400 | return -EINVAL; | |
401 | ||
402 | old_rate = periph_clk_get_rate(priv, clk->id); | |
403 | if (old_rate == -EINVAL) | |
404 | return -EINVAL; | |
405 | ||
406 | if (old_rate == req_rate) | |
407 | return old_rate; | |
408 | ||
409 | if (!periph_clk->can_gate || !periph_clk->dividers) | |
410 | return -ENOTSUPP; | |
411 | ||
412 | parent_rate = get_parent_rate(priv, clk->id); | |
413 | if (parent_rate == -EINVAL) | |
414 | return -EINVAL; | |
415 | ||
416 | t1 = empty_table; | |
417 | if (periph_clk->dividers > 1) | |
418 | t1 = periph_clk->div_table[1]; | |
419 | ||
420 | rate = find_best_div(periph_clk->div_table[0], t1, parent_rate, | |
421 | req_rate, &div_val0, &div_val1); | |
422 | ||
423 | periph_clk_enable(clk, 0); | |
424 | ||
425 | set_div_val(priv, periph_clk, 0, div_val0); | |
426 | if (periph_clk->dividers > 1) | |
427 | set_div_val(priv, periph_clk, 1, div_val1); | |
428 | ||
429 | periph_clk_enable(clk, 1); | |
430 | ||
431 | return rate; | |
432 | } | |
433 | ||
434 | static int armada_37xx_periph_clk_set_parent(struct clk *clk, | |
435 | struct clk *parent) | |
436 | { | |
437 | struct a37xx_periphclk *priv = dev_get_priv(clk->dev); | |
438 | const struct clk_periph *periph_clk = &priv->clks[clk->id]; | |
439 | struct clk check_parent; | |
440 | int ret; | |
441 | ||
442 | /* We also check if parent is our TBG clock */ | |
443 | if (clk->id > priv->count || parent->id >= MAX_TBG_PARENTS) | |
444 | return -EINVAL; | |
445 | ||
446 | if (!periph_clk->can_mux || !periph_clk->can_gate) | |
447 | return -ENOTSUPP; | |
448 | ||
449 | ret = clk_get_by_index(clk->dev, 0, &check_parent); | |
450 | if (ret < 0) | |
451 | return ret; | |
452 | ||
453 | if (parent->dev != check_parent.dev) | |
454 | ret = -EINVAL; | |
455 | ||
456 | clk_free(&check_parent); | |
457 | if (ret < 0) | |
458 | return ret; | |
459 | ||
460 | periph_clk_enable(clk, 0); | |
461 | set_mux(priv, periph_clk->mux_shift, parent->id); | |
462 | periph_clk_enable(clk, 1); | |
463 | ||
464 | return 0; | |
465 | } | |
466 | ||
dd77690c MB |
467 | #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720) |
468 | static int armada_37xx_periph_clk_dump(struct udevice *dev) | |
82a248df MB |
469 | { |
470 | struct a37xx_periphclk *priv = dev_get_priv(dev); | |
471 | const struct clk_periph *clks; | |
472 | int i; | |
473 | ||
474 | if (!priv) | |
475 | return -ENODEV; | |
476 | ||
477 | clks = priv->clks; | |
478 | ||
479 | for (i = 0; i < priv->count; ++i) | |
480 | printf(" %s at %lu Hz\n", clks[i].name, | |
481 | periph_clk_get_rate(priv, i)); | |
482 | printf("\n"); | |
483 | ||
484 | return 0; | |
485 | } | |
486 | ||
dd77690c MB |
487 | static int clk_dump(const char *name, int (*func)(struct udevice *)) |
488 | { | |
489 | struct udevice *dev; | |
490 | ||
491 | if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) { | |
492 | printf("Cannot find device %s\n", name); | |
493 | return -ENODEV; | |
494 | } | |
495 | ||
496 | return func(dev); | |
497 | } | |
498 | ||
499 | int armada_37xx_tbg_clk_dump(struct udevice *); | |
500 | ||
501 | int soc_clk_dump(void) | |
502 | { | |
503 | printf(" xtal at %u000000 Hz\n\n", get_ref_clk()); | |
504 | ||
505 | if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump)) | |
506 | return 1; | |
507 | ||
508 | if (clk_dump("nb-periph-clk@13000", | |
509 | armada_37xx_periph_clk_dump)) | |
510 | return 1; | |
511 | ||
512 | if (clk_dump("sb-periph-clk@18000", | |
513 | armada_37xx_periph_clk_dump)) | |
514 | return 1; | |
515 | ||
516 | return 0; | |
517 | } | |
518 | #endif | |
519 | ||
82a248df MB |
520 | static int armada_37xx_periph_clk_probe(struct udevice *dev) |
521 | { | |
522 | struct a37xx_periphclk *priv = dev_get_priv(dev); | |
523 | const struct clk_periph *clks; | |
524 | int ret, i; | |
525 | ||
526 | clks = (const struct clk_periph *)dev_get_driver_data(dev); | |
527 | if (!clks) | |
528 | return -ENODEV; | |
529 | ||
530 | priv->reg = dev_read_addr_ptr(dev); | |
531 | if (!priv->reg) { | |
532 | dev_err(dev, "no io address\n"); | |
533 | return -ENODEV; | |
534 | } | |
535 | ||
536 | /* count clk_periph nodes */ | |
537 | priv->count = 0; | |
538 | while (clks[priv->count].name) | |
539 | priv->count++; | |
540 | ||
541 | priv->clks = clks; | |
542 | ||
543 | /* assign parent IDs to nodes which have non-NULL parent_name */ | |
544 | for (i = 0; i < priv->count; ++i) { | |
545 | int j; | |
546 | ||
547 | if (!clks[i].parent_name) | |
548 | continue; | |
549 | ||
550 | /* first try if parent_name is one of TBGs or XTAL */ | |
551 | for (j = 0; j < MAX_PARENTS; ++j) | |
552 | if (!strcmp(clks[i].parent_name, | |
553 | a37xx_periph_parent_names[j].name)) | |
554 | break; | |
555 | ||
556 | if (j < MAX_PARENTS) { | |
557 | priv->clk_has_periph_parent[i] = false; | |
558 | priv->clk_parent[i] = | |
559 | a37xx_periph_parent_names[j].parent; | |
560 | continue; | |
561 | } | |
562 | ||
563 | /* else parent_name should be one of other periph clocks */ | |
564 | for (j = 0; j < priv->count; ++j) { | |
565 | if (!strcmp(clks[i].parent_name, clks[j].name)) | |
566 | break; | |
567 | } | |
568 | ||
569 | if (j < priv->count) { | |
570 | priv->clk_has_periph_parent[i] = true; | |
571 | priv->clk_parent[i] = j; | |
572 | continue; | |
573 | } | |
574 | ||
575 | dev_err(dev, "undefined parent %s\n", clks[i].parent_name); | |
576 | return -EINVAL; | |
577 | } | |
578 | ||
579 | for (i = 0; i < MAX_PARENTS; ++i) { | |
580 | struct clk clk; | |
581 | ||
582 | if (i == XTAL) { | |
583 | priv->parents[i] = get_ref_clk() * 1000000; | |
584 | continue; | |
585 | } | |
586 | ||
587 | ret = clk_get_by_index(dev, i, &clk); | |
588 | if (ret) { | |
589 | dev_err(dev, "one of parent clocks (%i) missing: %i\n", | |
590 | i, ret); | |
591 | return -ENODEV; | |
592 | } | |
593 | ||
594 | priv->parents[i] = clk_get_rate(&clk); | |
595 | clk_free(&clk); | |
596 | } | |
597 | ||
598 | return 0; | |
599 | } | |
600 | ||
601 | static const struct clk_ops armada_37xx_periph_clk_ops = { | |
602 | .get_rate = armada_37xx_periph_clk_get_rate, | |
de75fb09 MB |
603 | .set_rate = armada_37xx_periph_clk_set_rate, |
604 | .set_parent = armada_37xx_periph_clk_set_parent, | |
82a248df MB |
605 | .enable = armada_37xx_periph_clk_enable, |
606 | .disable = armada_37xx_periph_clk_disable, | |
607 | }; | |
608 | ||
609 | static const struct udevice_id armada_37xx_periph_clk_ids[] = { | |
610 | { | |
611 | .compatible = "marvell,armada-3700-periph-clock-nb", | |
612 | .data = (ulong)clks_nb, | |
613 | }, | |
614 | { | |
615 | .compatible = "marvell,armada-3700-periph-clock-sb", | |
616 | .data = (ulong)clks_sb, | |
617 | }, | |
618 | {} | |
619 | }; | |
620 | ||
621 | U_BOOT_DRIVER(armada_37xx_periph_clk) = { | |
622 | .name = "armada_37xx_periph_clk", | |
623 | .id = UCLASS_CLK, | |
624 | .of_match = armada_37xx_periph_clk_ids, | |
625 | .ops = &armada_37xx_periph_clk_ops, | |
626 | .priv_auto_alloc_size = sizeof(struct a37xx_periphclk), | |
627 | .probe = armada_37xx_periph_clk_probe, | |
628 | }; |