]> git.sur5r.net Git - u-boot/blob - drivers/clk/mvebu/armada-37xx-periph.c
clk: armada-37xx: Support soc_clk_dump
[u-boot] / drivers / clk / mvebu / armada-37xx-periph.c
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>
18
19 #define TBG_SEL         0x0
20 #define DIV_SEL0        0x4
21 #define DIV_SEL1        0x8
22 #define DIV_SEL2        0xC
23 #define CLK_SEL         0x10
24 #define CLK_DIS         0x14
25
26 enum a37xx_periph_parent {
27         TBG_A_P         = 0,
28         TBG_B_P         = 1,
29         TBG_A_S         = 2,
30         TBG_B_S         = 3,
31         MAX_TBG_PARENTS = 4,
32         XTAL            = 4,
33         MAX_PARENTS     = 5,
34 };
35
36 static const struct {
37         const char *name;
38         enum a37xx_periph_parent parent;
39 } a37xx_periph_parent_names[] = {
40         { "TBG-A-P", TBG_A_P },
41         { "TBG-B-P", TBG_B_P },
42         { "TBG-A-S", TBG_A_S },
43         { "TBG-B-S", TBG_B_S },
44         { "xtal",    XTAL    },
45 };
46
47 struct clk_periph;
48
49 struct a37xx_periphclk {
50         void __iomem *reg;
51
52         ulong parents[MAX_PARENTS];
53
54         const struct clk_periph *clks;
55         bool clk_has_periph_parent[16];
56         int clk_parent[16];
57
58         int count;
59 };
60
61 struct clk_div_table {
62         u32 div;
63         u32 val;
64 };
65
66 struct clk_periph {
67         const char *name;
68
69         const char *parent_name;
70
71         u32 disable_bit;
72         int mux_shift;
73
74         const struct clk_div_table *div_table[2];
75         s32 div_reg_off[2];
76         u32 div_mask[2];
77         int div_shift[2];
78
79         unsigned can_gate : 1;
80         unsigned can_mux : 1;
81         unsigned dividers : 2;
82 };
83
84 static const struct clk_div_table div_table1[] = {
85         { 1, 1 },
86         { 2, 2 },
87         { 0, 0 },
88 };
89
90 static const struct clk_div_table div_table2[] = {
91         { 2, 1 },
92         { 4, 2 },
93         { 0, 0 },
94 };
95
96 static const struct clk_div_table div_table6[] = {
97         { 1, 1 },
98         { 2, 2 },
99         { 3, 3 },
100         { 4, 4 },
101         { 5, 5 },
102         { 6, 6 },
103         { 0, 0 },
104 };
105
106 #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1)   \
107         {                                               \
108                 .name = #_n,                            \
109                 .disable_bit = BIT(_d),                 \
110                 .mux_shift = _mux,                      \
111                 .div_table[0] = div_table6,             \
112                 .div_table[1] = div_table6,             \
113                 .div_reg_off[0] = _r0,                  \
114                 .div_reg_off[1] = _r1,                  \
115                 .div_shift[0] = _s0,                    \
116                 .div_shift[1] = _s1,                    \
117                 .div_mask[0] = 7,                       \
118                 .div_mask[1] = 7,                       \
119                 .can_gate = 1,                          \
120                 .can_mux = 1,                           \
121                 .dividers = 2,                          \
122         }
123
124 #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t)  \
125         {                                       \
126                 .name = #_n,                    \
127                 .disable_bit = BIT(_d),         \
128                 .mux_shift = _mux,              \
129                 .div_table[0] = _t,             \
130                 .div_reg_off[0] = _r,           \
131                 .div_shift[0] = _s,             \
132                 .div_mask[0] = _m,              \
133                 .can_gate = 1,                  \
134                 .can_mux = 1,                   \
135                 .dividers = 1,                  \
136         }
137
138 #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p)        \
139         {                                               \
140                 .name = #_n,                            \
141                 .parent_name = _p,                      \
142                 .disable_bit = BIT(_d),                 \
143                 .div_table[0] = _t,                     \
144                 .div_reg_off[0] = _r,                   \
145                 .div_shift[0] = _s,                     \
146                 .div_mask[0] = _m,                      \
147                 .can_gate = 1,                          \
148                 .dividers = 1,                          \
149         }
150
151 #define CLK_GATE(_n, _d, _p)            \
152         {                               \
153                 .name = #_n,            \
154                 .parent_name = _p,      \
155                 .disable_bit = BIT(_d), \
156                 .can_gate = 1,          \
157         }
158
159 #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t)   \
160         {                                       \
161                 .name = #_n,                    \
162                 .mux_shift = _mux,              \
163                 .div_table[0] = _t,             \
164                 .div_reg_off[0] = _r,           \
165                 .div_shift[0] = _s,             \
166                 .div_mask[0] = _m,              \
167                 .can_mux = 1,                   \
168                 .dividers = 1,                  \
169         }
170
171 #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1)        \
172         {                                               \
173                 .name = #_n,                            \
174                 .mux_shift = _mux,                      \
175                 .div_table[0] = div_table6,             \
176                 .div_table[1] = div_table6,             \
177                 .div_reg_off[0] = _r0,                  \
178                 .div_reg_off[1] = _r1,                  \
179                 .div_shift[0] = _s0,                    \
180                 .div_shift[1] = _s1,                    \
181                 .div_mask[0] = 7,                       \
182                 .div_mask[1] = 7,                       \
183                 .can_mux = 1,                           \
184                 .dividers = 2,                          \
185         }
186
187 /* NB periph clocks */
188 static const struct clk_periph clks_nb[] = {
189         CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13),
190         CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7),
191         CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0),
192         CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6),
193         CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12),
194         CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6),
195         CLK_GATE(avs, 11, "xtal"),
196         CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24),
197         CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0),
198         CLK_GATE(i2c_2, 16, "xtal"),
199         CLK_GATE(i2c_1, 17, "xtal"),
200         CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"),
201         CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12),
202         CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6),
203         CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6),
204         CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19),
205         CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6),
206         { },
207 };
208
209 /* SB periph clocks */
210 static const struct clk_periph clks_sb[] = {
211         CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9),
212         CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21),
213         CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9),
214         CLK_GATE(gbe1_50, 0, "gbe_50"),
215         CLK_GATE(gbe0_50, 1, "gbe_50"),
216         CLK_GATE(gbe1_125, 2, "gbe_125"),
217         CLK_GATE(gbe0_125, 3, "gbe_125"),
218         CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"),
219         CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"),
220         CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"),
221         CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6),
222         CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12),
223         CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18),
224         { },
225 };
226
227 static inline int get_mux(struct a37xx_periphclk *priv, int shift)
228 {
229         return (readl(priv->reg + TBG_SEL) >> shift) & 3;
230 }
231
232 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
233
234 static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
235 {
236         const struct clk_periph *clk = &priv->clks[id];
237         ulong res;
238
239         if (clk->can_mux) {
240                 /* parent is one of TBG clocks */
241                 int tbg = get_mux(priv, clk->mux_shift);
242
243                 res = priv->parents[tbg];
244         } else if (priv->clk_has_periph_parent[id]) {
245                 /* parent is one of other periph clocks */
246
247                 if (priv->clk_parent[id] >= priv->count)
248                         return -EINVAL;
249
250                 res = periph_clk_get_rate(priv, priv->clk_parent[id]);
251         } else {
252                 /* otherwise parent is one of TBGs or XTAL */
253
254                 if (priv->clk_parent[id] >= MAX_PARENTS)
255                         return -EINVAL;
256
257                 res = priv->parents[priv->clk_parent[id]];
258         }
259
260         return res;
261 }
262
263 static ulong get_div(struct a37xx_periphclk *priv,
264                      const struct clk_periph *clk, int idx)
265 {
266         const struct clk_div_table *i;
267         u32 reg;
268
269         reg = readl(priv->reg + clk->div_reg_off[idx]);
270         reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
271
272         /* find divisor for register value val */
273         for (i = clk->div_table[idx]; i && i->div != 0; ++i)
274                 if (i->val == reg)
275                         return i->div;
276
277         return 0;
278 }
279
280 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
281 {
282         const struct clk_periph *clk = &priv->clks[id];
283         ulong rate, div;
284         int i;
285
286         rate = get_parent_rate(priv, id);
287         if (rate == -EINVAL)
288                 return -EINVAL;
289
290         /* divide the parent rate by dividers */
291         div = 1;
292         for (i = 0; i < clk->dividers; ++i)
293                 div *= get_div(priv, clk, i);
294
295         if (!div)
296                 return 0;
297
298         return DIV_ROUND_UP(rate, div);
299 }
300
301 static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
302 {
303         struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
304
305         if (clk->id >= priv->count)
306                 return -EINVAL;
307
308         return periph_clk_get_rate(priv, clk->id);
309 }
310
311 static int periph_clk_enable(struct clk *clk, int enable)
312 {
313         struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
314         const struct clk_periph *periph_clk = &priv->clks[clk->id];
315
316         if (clk->id >= priv->count)
317                 return -EINVAL;
318
319         if (!periph_clk->can_gate)
320                 return -ENOTSUPP;
321
322         if (enable)
323                 clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
324         else
325                 setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
326
327         return 0;
328 }
329
330 static int armada_37xx_periph_clk_enable(struct clk *clk)
331 {
332         return periph_clk_enable(clk, 1);
333 }
334
335 static int armada_37xx_periph_clk_disable(struct clk *clk)
336 {
337         return periph_clk_enable(clk, 0);
338 }
339
340 #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
341 static int armada_37xx_periph_clk_dump(struct udevice *dev)
342 {
343         struct a37xx_periphclk *priv = dev_get_priv(dev);
344         const struct clk_periph *clks;
345         int i;
346
347         if (!priv)
348                 return -ENODEV;
349
350         clks = priv->clks;
351
352         for (i = 0; i < priv->count; ++i)
353                 printf("  %s at %lu Hz\n", clks[i].name,
354                        periph_clk_get_rate(priv, i));
355         printf("\n");
356
357         return 0;
358 }
359
360 static int clk_dump(const char *name, int (*func)(struct udevice *))
361 {
362         struct udevice *dev;
363
364         if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
365                 printf("Cannot find device %s\n", name);
366                 return -ENODEV;
367         }
368
369         return func(dev);
370 }
371
372 int armada_37xx_tbg_clk_dump(struct udevice *);
373
374 int soc_clk_dump(void)
375 {
376         printf("  xtal at %u000000 Hz\n\n", get_ref_clk());
377
378         if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
379                 return 1;
380
381         if (clk_dump("nb-periph-clk@13000",
382                      armada_37xx_periph_clk_dump))
383                 return 1;
384
385         if (clk_dump("sb-periph-clk@18000",
386                      armada_37xx_periph_clk_dump))
387                 return 1;
388
389         return 0;
390 }
391 #endif
392
393 static int armada_37xx_periph_clk_probe(struct udevice *dev)
394 {
395         struct a37xx_periphclk *priv = dev_get_priv(dev);
396         const struct clk_periph *clks;
397         int ret, i;
398
399         clks = (const struct clk_periph *)dev_get_driver_data(dev);
400         if (!clks)
401                 return -ENODEV;
402
403         priv->reg = dev_read_addr_ptr(dev);
404         if (!priv->reg) {
405                 dev_err(dev, "no io address\n");
406                 return -ENODEV;
407         }
408
409         /* count clk_periph nodes */
410         priv->count = 0;
411         while (clks[priv->count].name)
412                 priv->count++;
413
414         priv->clks = clks;
415
416         /* assign parent IDs to nodes which have non-NULL parent_name */
417         for (i = 0; i < priv->count; ++i) {
418                 int j;
419
420                 if (!clks[i].parent_name)
421                         continue;
422
423                 /* first try if parent_name is one of TBGs or XTAL */
424                 for (j = 0; j < MAX_PARENTS; ++j)
425                         if (!strcmp(clks[i].parent_name,
426                                     a37xx_periph_parent_names[j].name))
427                                 break;
428
429                 if (j < MAX_PARENTS) {
430                         priv->clk_has_periph_parent[i] = false;
431                         priv->clk_parent[i] =
432                                 a37xx_periph_parent_names[j].parent;
433                         continue;
434                 }
435
436                 /* else parent_name should be one of other periph clocks */
437                 for (j = 0; j < priv->count; ++j) {
438                         if (!strcmp(clks[i].parent_name, clks[j].name))
439                                 break;
440                 }
441
442                 if (j < priv->count) {
443                         priv->clk_has_periph_parent[i] = true;
444                         priv->clk_parent[i] = j;
445                         continue;
446                 }
447
448                 dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
449                 return -EINVAL;
450         }
451
452         for (i = 0; i < MAX_PARENTS; ++i) {
453                 struct clk clk;
454
455                 if (i == XTAL) {
456                         priv->parents[i] = get_ref_clk() * 1000000;
457                         continue;
458                 }
459
460                 ret = clk_get_by_index(dev, i, &clk);
461                 if (ret) {
462                         dev_err(dev, "one of parent clocks (%i) missing: %i\n",
463                                 i, ret);
464                         return -ENODEV;
465                 }
466
467                 priv->parents[i] = clk_get_rate(&clk);
468                 clk_free(&clk);
469         }
470
471         return 0;
472 }
473
474 static const struct clk_ops armada_37xx_periph_clk_ops = {
475         .get_rate = armada_37xx_periph_clk_get_rate,
476         .enable = armada_37xx_periph_clk_enable,
477         .disable = armada_37xx_periph_clk_disable,
478 };
479
480 static const struct udevice_id armada_37xx_periph_clk_ids[] = {
481         {
482                 .compatible = "marvell,armada-3700-periph-clock-nb",
483                 .data = (ulong)clks_nb,
484         },
485         {
486                 .compatible = "marvell,armada-3700-periph-clock-sb",
487                 .data = (ulong)clks_sb,
488         },
489         {}
490 };
491
492 U_BOOT_DRIVER(armada_37xx_periph_clk) = {
493         .name           = "armada_37xx_periph_clk",
494         .id             = UCLASS_CLK,
495         .of_match       = armada_37xx_periph_clk_ids,
496         .ops            = &armada_37xx_periph_clk_ops,
497         .priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
498         .probe          = armada_37xx_periph_clk_probe,
499 };