]> git.sur5r.net Git - u-boot/blob - drivers/clk/mvebu/armada-37xx-periph.c
driver: clk: Add support for clocks on Armada 37xx
[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 int armada_37xx_periph_clk_dump(struct udevice *dev)
341 {
342         struct a37xx_periphclk *priv = dev_get_priv(dev);
343         const struct clk_periph *clks;
344         int i;
345
346         if (!priv)
347                 return -ENODEV;
348
349         clks = priv->clks;
350
351         for (i = 0; i < priv->count; ++i)
352                 printf("  %s at %lu Hz\n", clks[i].name,
353                        periph_clk_get_rate(priv, i));
354         printf("\n");
355
356         return 0;
357 }
358
359 static int armada_37xx_periph_clk_probe(struct udevice *dev)
360 {
361         struct a37xx_periphclk *priv = dev_get_priv(dev);
362         const struct clk_periph *clks;
363         int ret, i;
364
365         clks = (const struct clk_periph *)dev_get_driver_data(dev);
366         if (!clks)
367                 return -ENODEV;
368
369         priv->reg = dev_read_addr_ptr(dev);
370         if (!priv->reg) {
371                 dev_err(dev, "no io address\n");
372                 return -ENODEV;
373         }
374
375         /* count clk_periph nodes */
376         priv->count = 0;
377         while (clks[priv->count].name)
378                 priv->count++;
379
380         priv->clks = clks;
381
382         /* assign parent IDs to nodes which have non-NULL parent_name */
383         for (i = 0; i < priv->count; ++i) {
384                 int j;
385
386                 if (!clks[i].parent_name)
387                         continue;
388
389                 /* first try if parent_name is one of TBGs or XTAL */
390                 for (j = 0; j < MAX_PARENTS; ++j)
391                         if (!strcmp(clks[i].parent_name,
392                                     a37xx_periph_parent_names[j].name))
393                                 break;
394
395                 if (j < MAX_PARENTS) {
396                         priv->clk_has_periph_parent[i] = false;
397                         priv->clk_parent[i] =
398                                 a37xx_periph_parent_names[j].parent;
399                         continue;
400                 }
401
402                 /* else parent_name should be one of other periph clocks */
403                 for (j = 0; j < priv->count; ++j) {
404                         if (!strcmp(clks[i].parent_name, clks[j].name))
405                                 break;
406                 }
407
408                 if (j < priv->count) {
409                         priv->clk_has_periph_parent[i] = true;
410                         priv->clk_parent[i] = j;
411                         continue;
412                 }
413
414                 dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
415                 return -EINVAL;
416         }
417
418         for (i = 0; i < MAX_PARENTS; ++i) {
419                 struct clk clk;
420
421                 if (i == XTAL) {
422                         priv->parents[i] = get_ref_clk() * 1000000;
423                         continue;
424                 }
425
426                 ret = clk_get_by_index(dev, i, &clk);
427                 if (ret) {
428                         dev_err(dev, "one of parent clocks (%i) missing: %i\n",
429                                 i, ret);
430                         return -ENODEV;
431                 }
432
433                 priv->parents[i] = clk_get_rate(&clk);
434                 clk_free(&clk);
435         }
436
437         return 0;
438 }
439
440 static const struct clk_ops armada_37xx_periph_clk_ops = {
441         .get_rate = armada_37xx_periph_clk_get_rate,
442         .enable = armada_37xx_periph_clk_enable,
443         .disable = armada_37xx_periph_clk_disable,
444 };
445
446 static const struct udevice_id armada_37xx_periph_clk_ids[] = {
447         {
448                 .compatible = "marvell,armada-3700-periph-clock-nb",
449                 .data = (ulong)clks_nb,
450         },
451         {
452                 .compatible = "marvell,armada-3700-periph-clock-sb",
453                 .data = (ulong)clks_sb,
454         },
455         {}
456 };
457
458 U_BOOT_DRIVER(armada_37xx_periph_clk) = {
459         .name           = "armada_37xx_periph_clk",
460         .id             = UCLASS_CLK,
461         .of_match       = armada_37xx_periph_clk_ids,
462         .ops            = &armada_37xx_periph_clk_ops,
463         .priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
464         .probe          = armada_37xx_periph_clk_probe,
465 };