1 // SPDX-License-Identifier: GPL-2.0+
3 * Marvell Armada 37xx SoC Peripheral clocks
5 * Marek Behun <marek.behun@nic.cz>
7 * Based on Linux driver by:
8 * Gregory CLEMENT <gregory.clement@free-electrons.com>
13 #include <clk-uclass.h>
17 #include <asm/arch/cpu.h>
26 enum a37xx_periph_parent {
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 },
49 struct a37xx_periphclk {
52 ulong parents[MAX_PARENTS];
54 const struct clk_periph *clks;
55 bool clk_has_periph_parent[16];
61 struct clk_div_table {
69 const char *parent_name;
74 const struct clk_div_table *div_table[2];
79 unsigned can_gate : 1;
81 unsigned dividers : 2;
84 static const struct clk_div_table div_table1[] = {
90 static const struct clk_div_table div_table2[] = {
96 static const struct clk_div_table div_table6[] = {
106 #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1) \
109 .disable_bit = BIT(_d), \
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, \
124 #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t) \
127 .disable_bit = BIT(_d), \
129 .div_table[0] = _t, \
130 .div_reg_off[0] = _r, \
131 .div_shift[0] = _s, \
138 #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p) \
142 .disable_bit = BIT(_d), \
143 .div_table[0] = _t, \
144 .div_reg_off[0] = _r, \
145 .div_shift[0] = _s, \
151 #define CLK_GATE(_n, _d, _p) \
155 .disable_bit = BIT(_d), \
159 #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t) \
163 .div_table[0] = _t, \
164 .div_reg_off[0] = _r, \
165 .div_shift[0] = _s, \
171 #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1) \
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, \
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),
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),
227 static inline int get_mux(struct a37xx_periphclk *priv, int shift)
229 return (readl(priv->reg + TBG_SEL) >> shift) & 3;
232 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
234 static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
236 const struct clk_periph *clk = &priv->clks[id];
240 /* parent is one of TBG clocks */
241 int tbg = get_mux(priv, clk->mux_shift);
243 res = priv->parents[tbg];
244 } else if (priv->clk_has_periph_parent[id]) {
245 /* parent is one of other periph clocks */
247 if (priv->clk_parent[id] >= priv->count)
250 res = periph_clk_get_rate(priv, priv->clk_parent[id]);
252 /* otherwise parent is one of TBGs or XTAL */
254 if (priv->clk_parent[id] >= MAX_PARENTS)
257 res = priv->parents[priv->clk_parent[id]];
263 static ulong get_div(struct a37xx_periphclk *priv,
264 const struct clk_periph *clk, int idx)
266 const struct clk_div_table *i;
269 reg = readl(priv->reg + clk->div_reg_off[idx]);
270 reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
272 /* find divisor for register value val */
273 for (i = clk->div_table[idx]; i && i->div != 0; ++i)
280 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
282 const struct clk_periph *clk = &priv->clks[id];
286 rate = get_parent_rate(priv, id);
290 /* divide the parent rate by dividers */
292 for (i = 0; i < clk->dividers; ++i)
293 div *= get_div(priv, clk, i);
298 return DIV_ROUND_UP(rate, div);
301 static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
303 struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
305 if (clk->id >= priv->count)
308 return periph_clk_get_rate(priv, clk->id);
311 static int periph_clk_enable(struct clk *clk, int enable)
313 struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
314 const struct clk_periph *periph_clk = &priv->clks[clk->id];
316 if (clk->id >= priv->count)
319 if (!periph_clk->can_gate)
323 clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
325 setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
330 static int armada_37xx_periph_clk_enable(struct clk *clk)
332 return periph_clk_enable(clk, 1);
335 static int armada_37xx_periph_clk_disable(struct clk *clk)
337 return periph_clk_enable(clk, 0);
340 int armada_37xx_periph_clk_dump(struct udevice *dev)
342 struct a37xx_periphclk *priv = dev_get_priv(dev);
343 const struct clk_periph *clks;
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));
359 static int armada_37xx_periph_clk_probe(struct udevice *dev)
361 struct a37xx_periphclk *priv = dev_get_priv(dev);
362 const struct clk_periph *clks;
365 clks = (const struct clk_periph *)dev_get_driver_data(dev);
369 priv->reg = dev_read_addr_ptr(dev);
371 dev_err(dev, "no io address\n");
375 /* count clk_periph nodes */
377 while (clks[priv->count].name)
382 /* assign parent IDs to nodes which have non-NULL parent_name */
383 for (i = 0; i < priv->count; ++i) {
386 if (!clks[i].parent_name)
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))
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;
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))
408 if (j < priv->count) {
409 priv->clk_has_periph_parent[i] = true;
410 priv->clk_parent[i] = j;
414 dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
418 for (i = 0; i < MAX_PARENTS; ++i) {
422 priv->parents[i] = get_ref_clk() * 1000000;
426 ret = clk_get_by_index(dev, i, &clk);
428 dev_err(dev, "one of parent clocks (%i) missing: %i\n",
433 priv->parents[i] = clk_get_rate(&clk);
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,
446 static const struct udevice_id armada_37xx_periph_clk_ids[] = {
448 .compatible = "marvell,armada-3700-periph-clock-nb",
449 .data = (ulong)clks_nb,
452 .compatible = "marvell,armada-3700-periph-clock-sb",
453 .data = (ulong)clks_sb,
458 U_BOOT_DRIVER(armada_37xx_periph_clk) = {
459 .name = "armada_37xx_periph_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,