blob: 855f979b4f92bf7d8a7ed764eff9fa3e344fbb0d [file] [log] [blame]
Marek Behún82a248d2018-04-24 17:21:25 +02001// 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>
Simon Glass336d4612020-02-03 07:36:16 -070018#include <dm/device_compat.h>
Marek Behún82a248d2018-04-24 17:21:25 +020019
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
27enum 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
37static 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
48struct clk_periph;
49
50struct 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
62struct clk_div_table {
63 u32 div;
64 u32 val;
65};
66
67struct 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
85static const struct clk_div_table div_table1[] = {
86 { 1, 1 },
87 { 2, 2 },
88 { 0, 0 },
89};
90
91static const struct clk_div_table div_table2[] = {
Marek Behún239f4242020-04-15 00:59:18 +020092 { 2, 0 },
93 { 4, 1 },
Marek Behún82a248d2018-04-24 17:21:25 +020094 { 0, 0 },
95};
96
97static 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 */
189static 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 */
211static 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
Marek Behúnde75fb02018-08-17 12:58:52 +0200228static int get_mux(struct a37xx_periphclk *priv, int shift)
Marek Behún82a248d2018-04-24 17:21:25 +0200229{
230 return (readl(priv->reg + TBG_SEL) >> shift) & 3;
231}
232
Marek Behúnde75fb02018-08-17 12:58:52 +0200233static 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
Marek Behún82a248d2018-04-24 17:21:25 +0200243static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
244
245static 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
274static 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
Marek Behúnde75fb02018-08-17 12:58:52 +0200291static 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
Marek Behún82a248d2018-04-24 17:21:25 +0200302static 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
323static 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
333static 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
352static int armada_37xx_periph_clk_enable(struct clk *clk)
353{
354 return periph_clk_enable(clk, 1);
355}
356
357static int armada_37xx_periph_clk_disable(struct clk *clk)
358{
359 return periph_clk_enable(clk, 0);
360}
361
Marek Behúnde75fb02018-08-17 12:58:52 +0200362#define diff(a, b) abs((long)(a) - (long)(b))
363
364static 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
387static 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
434static 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
Marek Behúndd776902018-04-24 17:21:27 +0200467#if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
468static int armada_37xx_periph_clk_dump(struct udevice *dev)
Marek Behún82a248d2018-04-24 17:21:25 +0200469{
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
Marek Behúndd776902018-04-24 17:21:27 +0200487static 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
499int armada_37xx_tbg_clk_dump(struct udevice *);
500
501int 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
Marek Behún82a248d2018-04-24 17:21:25 +0200520static 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
601static const struct clk_ops armada_37xx_periph_clk_ops = {
602 .get_rate = armada_37xx_periph_clk_get_rate,
Marek Behúnde75fb02018-08-17 12:58:52 +0200603 .set_rate = armada_37xx_periph_clk_set_rate,
604 .set_parent = armada_37xx_periph_clk_set_parent,
Marek Behún82a248d2018-04-24 17:21:25 +0200605 .enable = armada_37xx_periph_clk_enable,
606 .disable = armada_37xx_periph_clk_disable,
607};
608
609static 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
621U_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};