xref: /openbmc/u-boot/drivers/clk/mvebu/armada-37xx-periph.c (revision 333279af23ac08ebc8d8056c677c98964dd013b6)
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  
get_mux(struct a37xx_periphclk * priv,int shift)227  static int get_mux(struct a37xx_periphclk *priv, int shift)
228  {
229  	return (readl(priv->reg + TBG_SEL) >> shift) & 3;
230  }
231  
set_mux(struct a37xx_periphclk * priv,int shift,int val)232  static void set_mux(struct a37xx_periphclk *priv, int shift, int val)
233  {
234  	u32 reg;
235  
236  	reg = readl(priv->reg + TBG_SEL);
237  	reg &= ~(3 << shift);
238  	reg |= (val & 3) << shift;
239  	writel(reg, priv->reg + TBG_SEL);
240  }
241  
242  static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
243  
get_parent_rate(struct a37xx_periphclk * priv,int id)244  static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
245  {
246  	const struct clk_periph *clk = &priv->clks[id];
247  	ulong res;
248  
249  	if (clk->can_mux) {
250  		/* parent is one of TBG clocks */
251  		int tbg = get_mux(priv, clk->mux_shift);
252  
253  		res = priv->parents[tbg];
254  	} else if (priv->clk_has_periph_parent[id]) {
255  		/* parent is one of other periph clocks */
256  
257  		if (priv->clk_parent[id] >= priv->count)
258  			return -EINVAL;
259  
260  		res = periph_clk_get_rate(priv, priv->clk_parent[id]);
261  	} else {
262  		/* otherwise parent is one of TBGs or XTAL */
263  
264  		if (priv->clk_parent[id] >= MAX_PARENTS)
265  			return -EINVAL;
266  
267  		res = priv->parents[priv->clk_parent[id]];
268  	}
269  
270  	return res;
271  }
272  
get_div(struct a37xx_periphclk * priv,const struct clk_periph * clk,int idx)273  static ulong get_div(struct a37xx_periphclk *priv,
274  		     const struct clk_periph *clk, int idx)
275  {
276  	const struct clk_div_table *i;
277  	u32 reg;
278  
279  	reg = readl(priv->reg + clk->div_reg_off[idx]);
280  	reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
281  
282  	/* find divisor for register value val */
283  	for (i = clk->div_table[idx]; i && i->div != 0; ++i)
284  		if (i->val == reg)
285  			return i->div;
286  
287  	return 0;
288  }
289  
set_div_val(struct a37xx_periphclk * priv,const struct clk_periph * clk,int idx,int val)290  static void set_div_val(struct a37xx_periphclk *priv,
291  			const struct clk_periph *clk, int idx, int val)
292  {
293  	u32 reg;
294  
295  	reg = readl(priv->reg + clk->div_reg_off[idx]);
296  	reg &= ~(clk->div_mask[idx] << clk->div_shift[idx]);
297  	reg |= (val & clk->div_mask[idx]) << clk->div_shift[idx];
298  	writel(reg, priv->reg + clk->div_reg_off[idx]);
299  }
300  
periph_clk_get_rate(struct a37xx_periphclk * priv,int id)301  static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
302  {
303  	const struct clk_periph *clk = &priv->clks[id];
304  	ulong rate, div;
305  	int i;
306  
307  	rate = get_parent_rate(priv, id);
308  	if (rate == -EINVAL)
309  		return -EINVAL;
310  
311  	/* divide the parent rate by dividers */
312  	div = 1;
313  	for (i = 0; i < clk->dividers; ++i)
314  		div *= get_div(priv, clk, i);
315  
316  	if (!div)
317  		return 0;
318  
319  	return DIV_ROUND_UP(rate, div);
320  }
321  
armada_37xx_periph_clk_get_rate(struct clk * clk)322  static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
323  {
324  	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
325  
326  	if (clk->id >= priv->count)
327  		return -EINVAL;
328  
329  	return periph_clk_get_rate(priv, clk->id);
330  }
331  
periph_clk_enable(struct clk * clk,int enable)332  static int periph_clk_enable(struct clk *clk, int enable)
333  {
334  	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
335  	const struct clk_periph *periph_clk = &priv->clks[clk->id];
336  
337  	if (clk->id >= priv->count)
338  		return -EINVAL;
339  
340  	if (!periph_clk->can_gate)
341  		return -ENOTSUPP;
342  
343  	if (enable)
344  		clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
345  	else
346  		setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
347  
348  	return 0;
349  }
350  
armada_37xx_periph_clk_enable(struct clk * clk)351  static int armada_37xx_periph_clk_enable(struct clk *clk)
352  {
353  	return periph_clk_enable(clk, 1);
354  }
355  
armada_37xx_periph_clk_disable(struct clk * clk)356  static int armada_37xx_periph_clk_disable(struct clk *clk)
357  {
358  	return periph_clk_enable(clk, 0);
359  }
360  
361  #define diff(a, b) abs((long)(a) - (long)(b))
362  
find_best_div(const struct clk_div_table * t0,const struct clk_div_table * t1,ulong parent_rate,ulong req_rate,int * v0,int * v1)363  static ulong find_best_div(const struct clk_div_table *t0,
364  			   const struct clk_div_table *t1, ulong parent_rate,
365  			   ulong req_rate, int *v0, int *v1)
366  {
367  	const struct clk_div_table *i, *j;
368  	ulong rate, best_rate = 0;
369  
370  	for (i = t0; i && i->div; ++i) {
371  		for (j = t1; j && j->div; ++j) {
372  			rate = DIV_ROUND_UP(parent_rate, i->div * j->div);
373  
374  			if (!best_rate ||
375  			    diff(rate, req_rate) < diff(best_rate, req_rate)) {
376  				best_rate = rate;
377  				*v0 = i->val;
378  				*v1 = j->val;
379  			}
380  		}
381  	}
382  
383  	return best_rate;
384  }
385  
armada_37xx_periph_clk_set_rate(struct clk * clk,ulong req_rate)386  static ulong armada_37xx_periph_clk_set_rate(struct clk *clk, ulong req_rate)
387  {
388  	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
389  	const struct clk_periph *periph_clk = &priv->clks[clk->id];
390  	ulong rate, old_rate, parent_rate;
391  	int div_val0 = 0, div_val1 = 0;
392  	const struct clk_div_table *t1;
393  	static const struct clk_div_table empty_table[2] = {
394  		{ 1, 0 },
395  		{ 0, 0 }
396  	};
397  
398  	if (clk->id > priv->count)
399  		return -EINVAL;
400  
401  	old_rate = periph_clk_get_rate(priv, clk->id);
402  	if (old_rate == -EINVAL)
403  		return -EINVAL;
404  
405  	if (old_rate == req_rate)
406  		return old_rate;
407  
408  	if (!periph_clk->can_gate || !periph_clk->dividers)
409  		return -ENOTSUPP;
410  
411  	parent_rate = get_parent_rate(priv, clk->id);
412  	if (parent_rate == -EINVAL)
413  		return -EINVAL;
414  
415  	t1 = empty_table;
416  	if (periph_clk->dividers > 1)
417  		t1 = periph_clk->div_table[1];
418  
419  	rate = find_best_div(periph_clk->div_table[0], t1, parent_rate,
420  			     req_rate, &div_val0, &div_val1);
421  
422  	periph_clk_enable(clk, 0);
423  
424  	set_div_val(priv, periph_clk, 0, div_val0);
425  	if (periph_clk->dividers > 1)
426  		set_div_val(priv, periph_clk, 1, div_val1);
427  
428  	periph_clk_enable(clk, 1);
429  
430  	return rate;
431  }
432  
armada_37xx_periph_clk_set_parent(struct clk * clk,struct clk * parent)433  static int armada_37xx_periph_clk_set_parent(struct clk *clk,
434  					     struct clk *parent)
435  {
436  	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
437  	const struct clk_periph *periph_clk = &priv->clks[clk->id];
438  	struct clk check_parent;
439  	int ret;
440  
441  	/* We also check if parent is our TBG clock */
442  	if (clk->id > priv->count || parent->id >= MAX_TBG_PARENTS)
443  		return -EINVAL;
444  
445  	if (!periph_clk->can_mux || !periph_clk->can_gate)
446  		return -ENOTSUPP;
447  
448  	ret = clk_get_by_index(clk->dev, 0, &check_parent);
449  	if (ret < 0)
450  		return ret;
451  
452  	if (parent->dev != check_parent.dev)
453  		ret = -EINVAL;
454  
455  	clk_free(&check_parent);
456  	if (ret < 0)
457  		return ret;
458  
459  	periph_clk_enable(clk, 0);
460  	set_mux(priv, periph_clk->mux_shift, parent->id);
461  	periph_clk_enable(clk, 1);
462  
463  	return 0;
464  }
465  
466  #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
armada_37xx_periph_clk_dump(struct udevice * dev)467  static int armada_37xx_periph_clk_dump(struct udevice *dev)
468  {
469  	struct a37xx_periphclk *priv = dev_get_priv(dev);
470  	const struct clk_periph *clks;
471  	int i;
472  
473  	if (!priv)
474  		return -ENODEV;
475  
476  	clks = priv->clks;
477  
478  	for (i = 0; i < priv->count; ++i)
479  		printf("  %s at %lu Hz\n", clks[i].name,
480  		       periph_clk_get_rate(priv, i));
481  	printf("\n");
482  
483  	return 0;
484  }
485  
clk_dump(const char * name,int (* func)(struct udevice *))486  static int clk_dump(const char *name, int (*func)(struct udevice *))
487  {
488  	struct udevice *dev;
489  
490  	if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
491  		printf("Cannot find device %s\n", name);
492  		return -ENODEV;
493  	}
494  
495  	return func(dev);
496  }
497  
498  int armada_37xx_tbg_clk_dump(struct udevice *);
499  
soc_clk_dump(void)500  int soc_clk_dump(void)
501  {
502  	printf("  xtal at %u000000 Hz\n\n", get_ref_clk());
503  
504  	if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
505  		return 1;
506  
507  	if (clk_dump("nb-periph-clk@13000",
508  		     armada_37xx_periph_clk_dump))
509  		return 1;
510  
511  	if (clk_dump("sb-periph-clk@18000",
512  		     armada_37xx_periph_clk_dump))
513  		return 1;
514  
515  	return 0;
516  }
517  #endif
518  
armada_37xx_periph_clk_probe(struct udevice * dev)519  static int armada_37xx_periph_clk_probe(struct udevice *dev)
520  {
521  	struct a37xx_periphclk *priv = dev_get_priv(dev);
522  	const struct clk_periph *clks;
523  	int ret, i;
524  
525  	clks = (const struct clk_periph *)dev_get_driver_data(dev);
526  	if (!clks)
527  		return -ENODEV;
528  
529  	priv->reg = dev_read_addr_ptr(dev);
530  	if (!priv->reg) {
531  		dev_err(dev, "no io address\n");
532  		return -ENODEV;
533  	}
534  
535  	/* count clk_periph nodes */
536  	priv->count = 0;
537  	while (clks[priv->count].name)
538  		priv->count++;
539  
540  	priv->clks = clks;
541  
542  	/* assign parent IDs to nodes which have non-NULL parent_name */
543  	for (i = 0; i < priv->count; ++i) {
544  		int j;
545  
546  		if (!clks[i].parent_name)
547  			continue;
548  
549  		/* first try if parent_name is one of TBGs or XTAL */
550  		for (j = 0; j < MAX_PARENTS; ++j)
551  			if (!strcmp(clks[i].parent_name,
552  				    a37xx_periph_parent_names[j].name))
553  				break;
554  
555  		if (j < MAX_PARENTS) {
556  			priv->clk_has_periph_parent[i] = false;
557  			priv->clk_parent[i] =
558  				a37xx_periph_parent_names[j].parent;
559  			continue;
560  		}
561  
562  		/* else parent_name should be one of other periph clocks */
563  		for (j = 0; j < priv->count; ++j) {
564  			if (!strcmp(clks[i].parent_name, clks[j].name))
565  				break;
566  		}
567  
568  		if (j < priv->count) {
569  			priv->clk_has_periph_parent[i] = true;
570  			priv->clk_parent[i] = j;
571  			continue;
572  		}
573  
574  		dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
575  		return -EINVAL;
576  	}
577  
578  	for (i = 0; i < MAX_PARENTS; ++i) {
579  		struct clk clk;
580  
581  		if (i == XTAL) {
582  			priv->parents[i] = get_ref_clk() * 1000000;
583  			continue;
584  		}
585  
586  		ret = clk_get_by_index(dev, i, &clk);
587  		if (ret) {
588  			dev_err(dev, "one of parent clocks (%i) missing: %i\n",
589  				i, ret);
590  			return -ENODEV;
591  		}
592  
593  		priv->parents[i] = clk_get_rate(&clk);
594  		clk_free(&clk);
595  	}
596  
597  	return 0;
598  }
599  
600  static const struct clk_ops armada_37xx_periph_clk_ops = {
601  	.get_rate = armada_37xx_periph_clk_get_rate,
602  	.set_rate = armada_37xx_periph_clk_set_rate,
603  	.set_parent = armada_37xx_periph_clk_set_parent,
604  	.enable = armada_37xx_periph_clk_enable,
605  	.disable = armada_37xx_periph_clk_disable,
606  };
607  
608  static const struct udevice_id armada_37xx_periph_clk_ids[] = {
609  	{
610  		.compatible = "marvell,armada-3700-periph-clock-nb",
611  		.data = (ulong)clks_nb,
612  	},
613  	{
614  		.compatible = "marvell,armada-3700-periph-clock-sb",
615  		.data = (ulong)clks_sb,
616  	},
617  	{}
618  };
619  
620  U_BOOT_DRIVER(armada_37xx_periph_clk) = {
621  	.name		= "armada_37xx_periph_clk",
622  	.id		= UCLASS_CLK,
623  	.of_match	= armada_37xx_periph_clk_ids,
624  	.ops		= &armada_37xx_periph_clk_ops,
625  	.priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
626  	.probe		= armada_37xx_periph_clk_probe,
627  };
628