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 };
500