182a248dfSMarek Behún // SPDX-License-Identifier: GPL-2.0+
282a248dfSMarek Behún /*
382a248dfSMarek Behún  * Marvell Armada 37xx SoC Peripheral clocks
482a248dfSMarek Behún  *
582a248dfSMarek Behún  * Marek Behun <marek.behun@nic.cz>
682a248dfSMarek Behún  *
782a248dfSMarek Behún  * Based on Linux driver by:
882a248dfSMarek Behún  *   Gregory CLEMENT <gregory.clement@free-electrons.com>
982a248dfSMarek Behún  */
1082a248dfSMarek Behún 
1182a248dfSMarek Behún #include <common.h>
1282a248dfSMarek Behún #include <malloc.h>
1382a248dfSMarek Behún #include <clk-uclass.h>
1482a248dfSMarek Behún #include <clk.h>
1582a248dfSMarek Behún #include <dm.h>
1682a248dfSMarek Behún #include <asm/io.h>
1782a248dfSMarek Behún #include <asm/arch/cpu.h>
1882a248dfSMarek Behún 
1982a248dfSMarek Behún #define TBG_SEL		0x0
2082a248dfSMarek Behún #define DIV_SEL0	0x4
2182a248dfSMarek Behún #define DIV_SEL1	0x8
2282a248dfSMarek Behún #define DIV_SEL2	0xC
2382a248dfSMarek Behún #define CLK_SEL		0x10
2482a248dfSMarek Behún #define CLK_DIS		0x14
2582a248dfSMarek Behún 
2682a248dfSMarek Behún enum a37xx_periph_parent {
2782a248dfSMarek Behún 	TBG_A_P		= 0,
2882a248dfSMarek Behún 	TBG_B_P		= 1,
2982a248dfSMarek Behún 	TBG_A_S		= 2,
3082a248dfSMarek Behún 	TBG_B_S		= 3,
3182a248dfSMarek Behún 	MAX_TBG_PARENTS	= 4,
3282a248dfSMarek Behún 	XTAL		= 4,
3382a248dfSMarek Behún 	MAX_PARENTS	= 5,
3482a248dfSMarek Behún };
3582a248dfSMarek Behún 
3682a248dfSMarek Behún static const struct {
3782a248dfSMarek Behún 	const char *name;
3882a248dfSMarek Behún 	enum a37xx_periph_parent parent;
3982a248dfSMarek Behún } a37xx_periph_parent_names[] = {
4082a248dfSMarek Behún 	{ "TBG-A-P", TBG_A_P },
4182a248dfSMarek Behún 	{ "TBG-B-P", TBG_B_P },
4282a248dfSMarek Behún 	{ "TBG-A-S", TBG_A_S },
4382a248dfSMarek Behún 	{ "TBG-B-S", TBG_B_S },
4482a248dfSMarek Behún 	{ "xtal",    XTAL    },
4582a248dfSMarek Behún };
4682a248dfSMarek Behún 
4782a248dfSMarek Behún struct clk_periph;
4882a248dfSMarek Behún 
4982a248dfSMarek Behún struct a37xx_periphclk {
5082a248dfSMarek Behún 	void __iomem *reg;
5182a248dfSMarek Behún 
5282a248dfSMarek Behún 	ulong parents[MAX_PARENTS];
5382a248dfSMarek Behún 
5482a248dfSMarek Behún 	const struct clk_periph *clks;
5582a248dfSMarek Behún 	bool clk_has_periph_parent[16];
5682a248dfSMarek Behún 	int clk_parent[16];
5782a248dfSMarek Behún 
5882a248dfSMarek Behún 	int count;
5982a248dfSMarek Behún };
6082a248dfSMarek Behún 
6182a248dfSMarek Behún struct clk_div_table {
6282a248dfSMarek Behún 	u32 div;
6382a248dfSMarek Behún 	u32 val;
6482a248dfSMarek Behún };
6582a248dfSMarek Behún 
6682a248dfSMarek Behún struct clk_periph {
6782a248dfSMarek Behún 	const char *name;
6882a248dfSMarek Behún 
6982a248dfSMarek Behún 	const char *parent_name;
7082a248dfSMarek Behún 
7182a248dfSMarek Behún 	u32 disable_bit;
7282a248dfSMarek Behún 	int mux_shift;
7382a248dfSMarek Behún 
7482a248dfSMarek Behún 	const struct clk_div_table *div_table[2];
7582a248dfSMarek Behún 	s32 div_reg_off[2];
7682a248dfSMarek Behún 	u32 div_mask[2];
7782a248dfSMarek Behún 	int div_shift[2];
7882a248dfSMarek Behún 
7982a248dfSMarek Behún 	unsigned can_gate : 1;
8082a248dfSMarek Behún 	unsigned can_mux : 1;
8182a248dfSMarek Behún 	unsigned dividers : 2;
8282a248dfSMarek Behún };
8382a248dfSMarek Behún 
8482a248dfSMarek Behún static const struct clk_div_table div_table1[] = {
8582a248dfSMarek Behún 	{ 1, 1 },
8682a248dfSMarek Behún 	{ 2, 2 },
8782a248dfSMarek Behún 	{ 0, 0 },
8882a248dfSMarek Behún };
8982a248dfSMarek Behún 
9082a248dfSMarek Behún static const struct clk_div_table div_table2[] = {
9182a248dfSMarek Behún 	{ 2, 1 },
9282a248dfSMarek Behún 	{ 4, 2 },
9382a248dfSMarek Behún 	{ 0, 0 },
9482a248dfSMarek Behún };
9582a248dfSMarek Behún 
9682a248dfSMarek Behún static const struct clk_div_table div_table6[] = {
9782a248dfSMarek Behún 	{ 1, 1 },
9882a248dfSMarek Behún 	{ 2, 2 },
9982a248dfSMarek Behún 	{ 3, 3 },
10082a248dfSMarek Behún 	{ 4, 4 },
10182a248dfSMarek Behún 	{ 5, 5 },
10282a248dfSMarek Behún 	{ 6, 6 },
10382a248dfSMarek Behún 	{ 0, 0 },
10482a248dfSMarek Behún };
10582a248dfSMarek Behún 
10682a248dfSMarek Behún #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1)	\
10782a248dfSMarek Behún 	{						\
10882a248dfSMarek Behún 		.name = #_n,				\
10982a248dfSMarek Behún 		.disable_bit = BIT(_d),			\
11082a248dfSMarek Behún 		.mux_shift = _mux,			\
11182a248dfSMarek Behún 		.div_table[0] = div_table6,		\
11282a248dfSMarek Behún 		.div_table[1] = div_table6,		\
11382a248dfSMarek Behún 		.div_reg_off[0] = _r0,			\
11482a248dfSMarek Behún 		.div_reg_off[1] = _r1,			\
11582a248dfSMarek Behún 		.div_shift[0] = _s0,			\
11682a248dfSMarek Behún 		.div_shift[1] = _s1,			\
11782a248dfSMarek Behún 		.div_mask[0] = 7,			\
11882a248dfSMarek Behún 		.div_mask[1] = 7,			\
11982a248dfSMarek Behún 		.can_gate = 1,				\
12082a248dfSMarek Behún 		.can_mux = 1,				\
12182a248dfSMarek Behún 		.dividers = 2,				\
12282a248dfSMarek Behún 	}
12382a248dfSMarek Behún 
12482a248dfSMarek Behún #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t)	\
12582a248dfSMarek Behún 	{					\
12682a248dfSMarek Behún 		.name = #_n,			\
12782a248dfSMarek Behún 		.disable_bit = BIT(_d),		\
12882a248dfSMarek Behún 		.mux_shift = _mux,		\
12982a248dfSMarek Behún 		.div_table[0] = _t,		\
13082a248dfSMarek Behún 		.div_reg_off[0] = _r,		\
13182a248dfSMarek Behún 		.div_shift[0] = _s,		\
13282a248dfSMarek Behún 		.div_mask[0] = _m,		\
13382a248dfSMarek Behún 		.can_gate = 1,			\
13482a248dfSMarek Behún 		.can_mux = 1,			\
13582a248dfSMarek Behún 		.dividers = 1,			\
13682a248dfSMarek Behún 	}
13782a248dfSMarek Behún 
13882a248dfSMarek Behún #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p)	\
13982a248dfSMarek Behún 	{						\
14082a248dfSMarek Behún 		.name = #_n,				\
14182a248dfSMarek Behún 		.parent_name = _p,			\
14282a248dfSMarek Behún 		.disable_bit = BIT(_d),			\
14382a248dfSMarek Behún 		.div_table[0] = _t,			\
14482a248dfSMarek Behún 		.div_reg_off[0] = _r,			\
14582a248dfSMarek Behún 		.div_shift[0] = _s,			\
14682a248dfSMarek Behún 		.div_mask[0] = _m,			\
14782a248dfSMarek Behún 		.can_gate = 1,				\
14882a248dfSMarek Behún 		.dividers = 1,				\
14982a248dfSMarek Behún 	}
15082a248dfSMarek Behún 
15182a248dfSMarek Behún #define CLK_GATE(_n, _d, _p)		\
15282a248dfSMarek Behún 	{				\
15382a248dfSMarek Behún 		.name = #_n,		\
15482a248dfSMarek Behún 		.parent_name = _p,	\
15582a248dfSMarek Behún 		.disable_bit = BIT(_d),	\
15682a248dfSMarek Behún 		.can_gate = 1,		\
15782a248dfSMarek Behún 	}
15882a248dfSMarek Behún 
15982a248dfSMarek Behún #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t)	\
16082a248dfSMarek Behún 	{					\
16182a248dfSMarek Behún 		.name = #_n,			\
16282a248dfSMarek Behún 		.mux_shift = _mux,		\
16382a248dfSMarek Behún 		.div_table[0] = _t,		\
16482a248dfSMarek Behún 		.div_reg_off[0] = _r,		\
16582a248dfSMarek Behún 		.div_shift[0] = _s,		\
16682a248dfSMarek Behún 		.div_mask[0] = _m,		\
16782a248dfSMarek Behún 		.can_mux = 1,			\
16882a248dfSMarek Behún 		.dividers = 1,			\
16982a248dfSMarek Behún 	}
17082a248dfSMarek Behún 
17182a248dfSMarek Behún #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1)	\
17282a248dfSMarek Behún 	{						\
17382a248dfSMarek Behún 		.name = #_n,				\
17482a248dfSMarek Behún 		.mux_shift = _mux,			\
17582a248dfSMarek Behún 		.div_table[0] = div_table6,		\
17682a248dfSMarek Behún 		.div_table[1] = div_table6,		\
17782a248dfSMarek Behún 		.div_reg_off[0] = _r0,			\
17882a248dfSMarek Behún 		.div_reg_off[1] = _r1,			\
17982a248dfSMarek Behún 		.div_shift[0] = _s0,			\
18082a248dfSMarek Behún 		.div_shift[1] = _s1,			\
18182a248dfSMarek Behún 		.div_mask[0] = 7,			\
18282a248dfSMarek Behún 		.div_mask[1] = 7,			\
18382a248dfSMarek Behún 		.can_mux = 1,				\
18482a248dfSMarek Behún 		.dividers = 2,				\
18582a248dfSMarek Behún 	}
18682a248dfSMarek Behún 
18782a248dfSMarek Behún /* NB periph clocks */
18882a248dfSMarek Behún static const struct clk_periph clks_nb[] = {
18982a248dfSMarek Behún 	CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13),
19082a248dfSMarek Behún 	CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7),
19182a248dfSMarek Behún 	CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0),
19282a248dfSMarek Behún 	CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6),
19382a248dfSMarek Behún 	CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12),
19482a248dfSMarek Behún 	CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6),
19582a248dfSMarek Behún 	CLK_GATE(avs, 11, "xtal"),
19682a248dfSMarek Behún 	CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24),
19782a248dfSMarek Behún 	CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0),
19882a248dfSMarek Behún 	CLK_GATE(i2c_2, 16, "xtal"),
19982a248dfSMarek Behún 	CLK_GATE(i2c_1, 17, "xtal"),
20082a248dfSMarek Behún 	CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"),
20182a248dfSMarek Behún 	CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12),
20282a248dfSMarek Behún 	CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6),
20382a248dfSMarek Behún 	CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6),
20482a248dfSMarek Behún 	CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19),
20582a248dfSMarek Behún 	CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6),
20682a248dfSMarek Behún 	{ },
20782a248dfSMarek Behún };
20882a248dfSMarek Behún 
20982a248dfSMarek Behún /* SB periph clocks */
21082a248dfSMarek Behún static const struct clk_periph clks_sb[] = {
21182a248dfSMarek Behún 	CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9),
21282a248dfSMarek Behún 	CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21),
21382a248dfSMarek Behún 	CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9),
21482a248dfSMarek Behún 	CLK_GATE(gbe1_50, 0, "gbe_50"),
21582a248dfSMarek Behún 	CLK_GATE(gbe0_50, 1, "gbe_50"),
21682a248dfSMarek Behún 	CLK_GATE(gbe1_125, 2, "gbe_125"),
21782a248dfSMarek Behún 	CLK_GATE(gbe0_125, 3, "gbe_125"),
21882a248dfSMarek Behún 	CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"),
21982a248dfSMarek Behún 	CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"),
22082a248dfSMarek Behún 	CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"),
22182a248dfSMarek Behún 	CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6),
22282a248dfSMarek Behún 	CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12),
22382a248dfSMarek Behún 	CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18),
22482a248dfSMarek Behún 	{ },
22582a248dfSMarek Behún };
22682a248dfSMarek Behún 
get_mux(struct a37xx_periphclk * priv,int shift)227*de75fb09SMarek Behún static int get_mux(struct a37xx_periphclk *priv, int shift)
22882a248dfSMarek Behún {
22982a248dfSMarek Behún 	return (readl(priv->reg + TBG_SEL) >> shift) & 3;
23082a248dfSMarek Behún }
23182a248dfSMarek Behún 
set_mux(struct a37xx_periphclk * priv,int shift,int val)232*de75fb09SMarek Behún static void set_mux(struct a37xx_periphclk *priv, int shift, int val)
233*de75fb09SMarek Behún {
234*de75fb09SMarek Behún 	u32 reg;
235*de75fb09SMarek Behún 
236*de75fb09SMarek Behún 	reg = readl(priv->reg + TBG_SEL);
237*de75fb09SMarek Behún 	reg &= ~(3 << shift);
238*de75fb09SMarek Behún 	reg |= (val & 3) << shift;
239*de75fb09SMarek Behún 	writel(reg, priv->reg + TBG_SEL);
240*de75fb09SMarek Behún }
241*de75fb09SMarek Behún 
24282a248dfSMarek Behún static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
24382a248dfSMarek Behún 
get_parent_rate(struct a37xx_periphclk * priv,int id)24482a248dfSMarek Behún static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
24582a248dfSMarek Behún {
24682a248dfSMarek Behún 	const struct clk_periph *clk = &priv->clks[id];
24782a248dfSMarek Behún 	ulong res;
24882a248dfSMarek Behún 
24982a248dfSMarek Behún 	if (clk->can_mux) {
25082a248dfSMarek Behún 		/* parent is one of TBG clocks */
25182a248dfSMarek Behún 		int tbg = get_mux(priv, clk->mux_shift);
25282a248dfSMarek Behún 
25382a248dfSMarek Behún 		res = priv->parents[tbg];
25482a248dfSMarek Behún 	} else if (priv->clk_has_periph_parent[id]) {
25582a248dfSMarek Behún 		/* parent is one of other periph clocks */
25682a248dfSMarek Behún 
25782a248dfSMarek Behún 		if (priv->clk_parent[id] >= priv->count)
25882a248dfSMarek Behún 			return -EINVAL;
25982a248dfSMarek Behún 
26082a248dfSMarek Behún 		res = periph_clk_get_rate(priv, priv->clk_parent[id]);
26182a248dfSMarek Behún 	} else {
26282a248dfSMarek Behún 		/* otherwise parent is one of TBGs or XTAL */
26382a248dfSMarek Behún 
26482a248dfSMarek Behún 		if (priv->clk_parent[id] >= MAX_PARENTS)
26582a248dfSMarek Behún 			return -EINVAL;
26682a248dfSMarek Behún 
26782a248dfSMarek Behún 		res = priv->parents[priv->clk_parent[id]];
26882a248dfSMarek Behún 	}
26982a248dfSMarek Behún 
27082a248dfSMarek Behún 	return res;
27182a248dfSMarek Behún }
27282a248dfSMarek Behún 
get_div(struct a37xx_periphclk * priv,const struct clk_periph * clk,int idx)27382a248dfSMarek Behún static ulong get_div(struct a37xx_periphclk *priv,
27482a248dfSMarek Behún 		     const struct clk_periph *clk, int idx)
27582a248dfSMarek Behún {
27682a248dfSMarek Behún 	const struct clk_div_table *i;
27782a248dfSMarek Behún 	u32 reg;
27882a248dfSMarek Behún 
27982a248dfSMarek Behún 	reg = readl(priv->reg + clk->div_reg_off[idx]);
28082a248dfSMarek Behún 	reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
28182a248dfSMarek Behún 
28282a248dfSMarek Behún 	/* find divisor for register value val */
28382a248dfSMarek Behún 	for (i = clk->div_table[idx]; i && i->div != 0; ++i)
28482a248dfSMarek Behún 		if (i->val == reg)
28582a248dfSMarek Behún 			return i->div;
28682a248dfSMarek Behún 
28782a248dfSMarek Behún 	return 0;
28882a248dfSMarek Behún }
28982a248dfSMarek Behún 
set_div_val(struct a37xx_periphclk * priv,const struct clk_periph * clk,int idx,int val)290*de75fb09SMarek Behún static void set_div_val(struct a37xx_periphclk *priv,
291*de75fb09SMarek Behún 			const struct clk_periph *clk, int idx, int val)
292*de75fb09SMarek Behún {
293*de75fb09SMarek Behún 	u32 reg;
294*de75fb09SMarek Behún 
295*de75fb09SMarek Behún 	reg = readl(priv->reg + clk->div_reg_off[idx]);
296*de75fb09SMarek Behún 	reg &= ~(clk->div_mask[idx] << clk->div_shift[idx]);
297*de75fb09SMarek Behún 	reg |= (val & clk->div_mask[idx]) << clk->div_shift[idx];
298*de75fb09SMarek Behún 	writel(reg, priv->reg + clk->div_reg_off[idx]);
299*de75fb09SMarek Behún }
300*de75fb09SMarek Behún 
periph_clk_get_rate(struct a37xx_periphclk * priv,int id)30182a248dfSMarek Behún static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
30282a248dfSMarek Behún {
30382a248dfSMarek Behún 	const struct clk_periph *clk = &priv->clks[id];
30482a248dfSMarek Behún 	ulong rate, div;
30582a248dfSMarek Behún 	int i;
30682a248dfSMarek Behún 
30782a248dfSMarek Behún 	rate = get_parent_rate(priv, id);
30882a248dfSMarek Behún 	if (rate == -EINVAL)
30982a248dfSMarek Behún 		return -EINVAL;
31082a248dfSMarek Behún 
31182a248dfSMarek Behún 	/* divide the parent rate by dividers */
31282a248dfSMarek Behún 	div = 1;
31382a248dfSMarek Behún 	for (i = 0; i < clk->dividers; ++i)
31482a248dfSMarek Behún 		div *= get_div(priv, clk, i);
31582a248dfSMarek Behún 
31682a248dfSMarek Behún 	if (!div)
31782a248dfSMarek Behún 		return 0;
31882a248dfSMarek Behún 
31982a248dfSMarek Behún 	return DIV_ROUND_UP(rate, div);
32082a248dfSMarek Behún }
32182a248dfSMarek Behún 
armada_37xx_periph_clk_get_rate(struct clk * clk)32282a248dfSMarek Behún static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
32382a248dfSMarek Behún {
32482a248dfSMarek Behún 	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
32582a248dfSMarek Behún 
32682a248dfSMarek Behún 	if (clk->id >= priv->count)
32782a248dfSMarek Behún 		return -EINVAL;
32882a248dfSMarek Behún 
32982a248dfSMarek Behún 	return periph_clk_get_rate(priv, clk->id);
33082a248dfSMarek Behún }
33182a248dfSMarek Behún 
periph_clk_enable(struct clk * clk,int enable)33282a248dfSMarek Behún static int periph_clk_enable(struct clk *clk, int enable)
33382a248dfSMarek Behún {
33482a248dfSMarek Behún 	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
33582a248dfSMarek Behún 	const struct clk_periph *periph_clk = &priv->clks[clk->id];
33682a248dfSMarek Behún 
33782a248dfSMarek Behún 	if (clk->id >= priv->count)
33882a248dfSMarek Behún 		return -EINVAL;
33982a248dfSMarek Behún 
34082a248dfSMarek Behún 	if (!periph_clk->can_gate)
34182a248dfSMarek Behún 		return -ENOTSUPP;
34282a248dfSMarek Behún 
34382a248dfSMarek Behún 	if (enable)
34482a248dfSMarek Behún 		clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
34582a248dfSMarek Behún 	else
34682a248dfSMarek Behún 		setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
34782a248dfSMarek Behún 
34882a248dfSMarek Behún 	return 0;
34982a248dfSMarek Behún }
35082a248dfSMarek Behún 
armada_37xx_periph_clk_enable(struct clk * clk)35182a248dfSMarek Behún static int armada_37xx_periph_clk_enable(struct clk *clk)
35282a248dfSMarek Behún {
35382a248dfSMarek Behún 	return periph_clk_enable(clk, 1);
35482a248dfSMarek Behún }
35582a248dfSMarek Behún 
armada_37xx_periph_clk_disable(struct clk * clk)35682a248dfSMarek Behún static int armada_37xx_periph_clk_disable(struct clk *clk)
35782a248dfSMarek Behún {
35882a248dfSMarek Behún 	return periph_clk_enable(clk, 0);
35982a248dfSMarek Behún }
36082a248dfSMarek Behún 
361*de75fb09SMarek Behún #define diff(a, b) abs((long)(a) - (long)(b))
362*de75fb09SMarek Behún 
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*de75fb09SMarek Behún static ulong find_best_div(const struct clk_div_table *t0,
364*de75fb09SMarek Behún 			   const struct clk_div_table *t1, ulong parent_rate,
365*de75fb09SMarek Behún 			   ulong req_rate, int *v0, int *v1)
366*de75fb09SMarek Behún {
367*de75fb09SMarek Behún 	const struct clk_div_table *i, *j;
368*de75fb09SMarek Behún 	ulong rate, best_rate = 0;
369*de75fb09SMarek Behún 
370*de75fb09SMarek Behún 	for (i = t0; i && i->div; ++i) {
371*de75fb09SMarek Behún 		for (j = t1; j && j->div; ++j) {
372*de75fb09SMarek Behún 			rate = DIV_ROUND_UP(parent_rate, i->div * j->div);
373*de75fb09SMarek Behún 
374*de75fb09SMarek Behún 			if (!best_rate ||
375*de75fb09SMarek Behún 			    diff(rate, req_rate) < diff(best_rate, req_rate)) {
376*de75fb09SMarek Behún 				best_rate = rate;
377*de75fb09SMarek Behún 				*v0 = i->val;
378*de75fb09SMarek Behún 				*v1 = j->val;
379*de75fb09SMarek Behún 			}
380*de75fb09SMarek Behún 		}
381*de75fb09SMarek Behún 	}
382*de75fb09SMarek Behún 
383*de75fb09SMarek Behún 	return best_rate;
384*de75fb09SMarek Behún }
385*de75fb09SMarek Behún 
armada_37xx_periph_clk_set_rate(struct clk * clk,ulong req_rate)386*de75fb09SMarek Behún static ulong armada_37xx_periph_clk_set_rate(struct clk *clk, ulong req_rate)
387*de75fb09SMarek Behún {
388*de75fb09SMarek Behún 	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
389*de75fb09SMarek Behún 	const struct clk_periph *periph_clk = &priv->clks[clk->id];
390*de75fb09SMarek Behún 	ulong rate, old_rate, parent_rate;
391*de75fb09SMarek Behún 	int div_val0 = 0, div_val1 = 0;
392*de75fb09SMarek Behún 	const struct clk_div_table *t1;
393*de75fb09SMarek Behún 	static const struct clk_div_table empty_table[2] = {
394*de75fb09SMarek Behún 		{ 1, 0 },
395*de75fb09SMarek Behún 		{ 0, 0 }
396*de75fb09SMarek Behún 	};
397*de75fb09SMarek Behún 
398*de75fb09SMarek Behún 	if (clk->id > priv->count)
399*de75fb09SMarek Behún 		return -EINVAL;
400*de75fb09SMarek Behún 
401*de75fb09SMarek Behún 	old_rate = periph_clk_get_rate(priv, clk->id);
402*de75fb09SMarek Behún 	if (old_rate == -EINVAL)
403*de75fb09SMarek Behún 		return -EINVAL;
404*de75fb09SMarek Behún 
405*de75fb09SMarek Behún 	if (old_rate == req_rate)
406*de75fb09SMarek Behún 		return old_rate;
407*de75fb09SMarek Behún 
408*de75fb09SMarek Behún 	if (!periph_clk->can_gate || !periph_clk->dividers)
409*de75fb09SMarek Behún 		return -ENOTSUPP;
410*de75fb09SMarek Behún 
411*de75fb09SMarek Behún 	parent_rate = get_parent_rate(priv, clk->id);
412*de75fb09SMarek Behún 	if (parent_rate == -EINVAL)
413*de75fb09SMarek Behún 		return -EINVAL;
414*de75fb09SMarek Behún 
415*de75fb09SMarek Behún 	t1 = empty_table;
416*de75fb09SMarek Behún 	if (periph_clk->dividers > 1)
417*de75fb09SMarek Behún 		t1 = periph_clk->div_table[1];
418*de75fb09SMarek Behún 
419*de75fb09SMarek Behún 	rate = find_best_div(periph_clk->div_table[0], t1, parent_rate,
420*de75fb09SMarek Behún 			     req_rate, &div_val0, &div_val1);
421*de75fb09SMarek Behún 
422*de75fb09SMarek Behún 	periph_clk_enable(clk, 0);
423*de75fb09SMarek Behún 
424*de75fb09SMarek Behún 	set_div_val(priv, periph_clk, 0, div_val0);
425*de75fb09SMarek Behún 	if (periph_clk->dividers > 1)
426*de75fb09SMarek Behún 		set_div_val(priv, periph_clk, 1, div_val1);
427*de75fb09SMarek Behún 
428*de75fb09SMarek Behún 	periph_clk_enable(clk, 1);
429*de75fb09SMarek Behún 
430*de75fb09SMarek Behún 	return rate;
431*de75fb09SMarek Behún }
432*de75fb09SMarek Behún 
armada_37xx_periph_clk_set_parent(struct clk * clk,struct clk * parent)433*de75fb09SMarek Behún static int armada_37xx_periph_clk_set_parent(struct clk *clk,
434*de75fb09SMarek Behún 					     struct clk *parent)
435*de75fb09SMarek Behún {
436*de75fb09SMarek Behún 	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
437*de75fb09SMarek Behún 	const struct clk_periph *periph_clk = &priv->clks[clk->id];
438*de75fb09SMarek Behún 	struct clk check_parent;
439*de75fb09SMarek Behún 	int ret;
440*de75fb09SMarek Behún 
441*de75fb09SMarek Behún 	/* We also check if parent is our TBG clock */
442*de75fb09SMarek Behún 	if (clk->id > priv->count || parent->id >= MAX_TBG_PARENTS)
443*de75fb09SMarek Behún 		return -EINVAL;
444*de75fb09SMarek Behún 
445*de75fb09SMarek Behún 	if (!periph_clk->can_mux || !periph_clk->can_gate)
446*de75fb09SMarek Behún 		return -ENOTSUPP;
447*de75fb09SMarek Behún 
448*de75fb09SMarek Behún 	ret = clk_get_by_index(clk->dev, 0, &check_parent);
449*de75fb09SMarek Behún 	if (ret < 0)
450*de75fb09SMarek Behún 		return ret;
451*de75fb09SMarek Behún 
452*de75fb09SMarek Behún 	if (parent->dev != check_parent.dev)
453*de75fb09SMarek Behún 		ret = -EINVAL;
454*de75fb09SMarek Behún 
455*de75fb09SMarek Behún 	clk_free(&check_parent);
456*de75fb09SMarek Behún 	if (ret < 0)
457*de75fb09SMarek Behún 		return ret;
458*de75fb09SMarek Behún 
459*de75fb09SMarek Behún 	periph_clk_enable(clk, 0);
460*de75fb09SMarek Behún 	set_mux(priv, periph_clk->mux_shift, parent->id);
461*de75fb09SMarek Behún 	periph_clk_enable(clk, 1);
462*de75fb09SMarek Behún 
463*de75fb09SMarek Behún 	return 0;
464*de75fb09SMarek Behún }
465*de75fb09SMarek Behún 
466dd77690cSMarek Behún #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
armada_37xx_periph_clk_dump(struct udevice * dev)467dd77690cSMarek Behún static int armada_37xx_periph_clk_dump(struct udevice *dev)
46882a248dfSMarek Behún {
46982a248dfSMarek Behún 	struct a37xx_periphclk *priv = dev_get_priv(dev);
47082a248dfSMarek Behún 	const struct clk_periph *clks;
47182a248dfSMarek Behún 	int i;
47282a248dfSMarek Behún 
47382a248dfSMarek Behún 	if (!priv)
47482a248dfSMarek Behún 		return -ENODEV;
47582a248dfSMarek Behún 
47682a248dfSMarek Behún 	clks = priv->clks;
47782a248dfSMarek Behún 
47882a248dfSMarek Behún 	for (i = 0; i < priv->count; ++i)
47982a248dfSMarek Behún 		printf("  %s at %lu Hz\n", clks[i].name,
48082a248dfSMarek Behún 		       periph_clk_get_rate(priv, i));
48182a248dfSMarek Behún 	printf("\n");
48282a248dfSMarek Behún 
48382a248dfSMarek Behún 	return 0;
48482a248dfSMarek Behún }
48582a248dfSMarek Behún 
clk_dump(const char * name,int (* func)(struct udevice *))486dd77690cSMarek Behún static int clk_dump(const char *name, int (*func)(struct udevice *))
487dd77690cSMarek Behún {
488dd77690cSMarek Behún 	struct udevice *dev;
489dd77690cSMarek Behún 
490dd77690cSMarek Behún 	if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
491dd77690cSMarek Behún 		printf("Cannot find device %s\n", name);
492dd77690cSMarek Behún 		return -ENODEV;
493dd77690cSMarek Behún 	}
494dd77690cSMarek Behún 
495dd77690cSMarek Behún 	return func(dev);
496dd77690cSMarek Behún }
497dd77690cSMarek Behún 
498dd77690cSMarek Behún int armada_37xx_tbg_clk_dump(struct udevice *);
499dd77690cSMarek Behún 
soc_clk_dump(void)500dd77690cSMarek Behún int soc_clk_dump(void)
501dd77690cSMarek Behún {
502dd77690cSMarek Behún 	printf("  xtal at %u000000 Hz\n\n", get_ref_clk());
503dd77690cSMarek Behún 
504dd77690cSMarek Behún 	if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
505dd77690cSMarek Behún 		return 1;
506dd77690cSMarek Behún 
507dd77690cSMarek Behún 	if (clk_dump("nb-periph-clk@13000",
508dd77690cSMarek Behún 		     armada_37xx_periph_clk_dump))
509dd77690cSMarek Behún 		return 1;
510dd77690cSMarek Behún 
511dd77690cSMarek Behún 	if (clk_dump("sb-periph-clk@18000",
512dd77690cSMarek Behún 		     armada_37xx_periph_clk_dump))
513dd77690cSMarek Behún 		return 1;
514dd77690cSMarek Behún 
515dd77690cSMarek Behún 	return 0;
516dd77690cSMarek Behún }
517dd77690cSMarek Behún #endif
518dd77690cSMarek Behún 
armada_37xx_periph_clk_probe(struct udevice * dev)51982a248dfSMarek Behún static int armada_37xx_periph_clk_probe(struct udevice *dev)
52082a248dfSMarek Behún {
52182a248dfSMarek Behún 	struct a37xx_periphclk *priv = dev_get_priv(dev);
52282a248dfSMarek Behún 	const struct clk_periph *clks;
52382a248dfSMarek Behún 	int ret, i;
52482a248dfSMarek Behún 
52582a248dfSMarek Behún 	clks = (const struct clk_periph *)dev_get_driver_data(dev);
52682a248dfSMarek Behún 	if (!clks)
52782a248dfSMarek Behún 		return -ENODEV;
52882a248dfSMarek Behún 
52982a248dfSMarek Behún 	priv->reg = dev_read_addr_ptr(dev);
53082a248dfSMarek Behún 	if (!priv->reg) {
53182a248dfSMarek Behún 		dev_err(dev, "no io address\n");
53282a248dfSMarek Behún 		return -ENODEV;
53382a248dfSMarek Behún 	}
53482a248dfSMarek Behún 
53582a248dfSMarek Behún 	/* count clk_periph nodes */
53682a248dfSMarek Behún 	priv->count = 0;
53782a248dfSMarek Behún 	while (clks[priv->count].name)
53882a248dfSMarek Behún 		priv->count++;
53982a248dfSMarek Behún 
54082a248dfSMarek Behún 	priv->clks = clks;
54182a248dfSMarek Behún 
54282a248dfSMarek Behún 	/* assign parent IDs to nodes which have non-NULL parent_name */
54382a248dfSMarek Behún 	for (i = 0; i < priv->count; ++i) {
54482a248dfSMarek Behún 		int j;
54582a248dfSMarek Behún 
54682a248dfSMarek Behún 		if (!clks[i].parent_name)
54782a248dfSMarek Behún 			continue;
54882a248dfSMarek Behún 
54982a248dfSMarek Behún 		/* first try if parent_name is one of TBGs or XTAL */
55082a248dfSMarek Behún 		for (j = 0; j < MAX_PARENTS; ++j)
55182a248dfSMarek Behún 			if (!strcmp(clks[i].parent_name,
55282a248dfSMarek Behún 				    a37xx_periph_parent_names[j].name))
55382a248dfSMarek Behún 				break;
55482a248dfSMarek Behún 
55582a248dfSMarek Behún 		if (j < MAX_PARENTS) {
55682a248dfSMarek Behún 			priv->clk_has_periph_parent[i] = false;
55782a248dfSMarek Behún 			priv->clk_parent[i] =
55882a248dfSMarek Behún 				a37xx_periph_parent_names[j].parent;
55982a248dfSMarek Behún 			continue;
56082a248dfSMarek Behún 		}
56182a248dfSMarek Behún 
56282a248dfSMarek Behún 		/* else parent_name should be one of other periph clocks */
56382a248dfSMarek Behún 		for (j = 0; j < priv->count; ++j) {
56482a248dfSMarek Behún 			if (!strcmp(clks[i].parent_name, clks[j].name))
56582a248dfSMarek Behún 				break;
56682a248dfSMarek Behún 		}
56782a248dfSMarek Behún 
56882a248dfSMarek Behún 		if (j < priv->count) {
56982a248dfSMarek Behún 			priv->clk_has_periph_parent[i] = true;
57082a248dfSMarek Behún 			priv->clk_parent[i] = j;
57182a248dfSMarek Behún 			continue;
57282a248dfSMarek Behún 		}
57382a248dfSMarek Behún 
57482a248dfSMarek Behún 		dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
57582a248dfSMarek Behún 		return -EINVAL;
57682a248dfSMarek Behún 	}
57782a248dfSMarek Behún 
57882a248dfSMarek Behún 	for (i = 0; i < MAX_PARENTS; ++i) {
57982a248dfSMarek Behún 		struct clk clk;
58082a248dfSMarek Behún 
58182a248dfSMarek Behún 		if (i == XTAL) {
58282a248dfSMarek Behún 			priv->parents[i] = get_ref_clk() * 1000000;
58382a248dfSMarek Behún 			continue;
58482a248dfSMarek Behún 		}
58582a248dfSMarek Behún 
58682a248dfSMarek Behún 		ret = clk_get_by_index(dev, i, &clk);
58782a248dfSMarek Behún 		if (ret) {
58882a248dfSMarek Behún 			dev_err(dev, "one of parent clocks (%i) missing: %i\n",
58982a248dfSMarek Behún 				i, ret);
59082a248dfSMarek Behún 			return -ENODEV;
59182a248dfSMarek Behún 		}
59282a248dfSMarek Behún 
59382a248dfSMarek Behún 		priv->parents[i] = clk_get_rate(&clk);
59482a248dfSMarek Behún 		clk_free(&clk);
59582a248dfSMarek Behún 	}
59682a248dfSMarek Behún 
59782a248dfSMarek Behún 	return 0;
59882a248dfSMarek Behún }
59982a248dfSMarek Behún 
60082a248dfSMarek Behún static const struct clk_ops armada_37xx_periph_clk_ops = {
60182a248dfSMarek Behún 	.get_rate = armada_37xx_periph_clk_get_rate,
602*de75fb09SMarek Behún 	.set_rate = armada_37xx_periph_clk_set_rate,
603*de75fb09SMarek Behún 	.set_parent = armada_37xx_periph_clk_set_parent,
60482a248dfSMarek Behún 	.enable = armada_37xx_periph_clk_enable,
60582a248dfSMarek Behún 	.disable = armada_37xx_periph_clk_disable,
60682a248dfSMarek Behún };
60782a248dfSMarek Behún 
60882a248dfSMarek Behún static const struct udevice_id armada_37xx_periph_clk_ids[] = {
60982a248dfSMarek Behún 	{
61082a248dfSMarek Behún 		.compatible = "marvell,armada-3700-periph-clock-nb",
61182a248dfSMarek Behún 		.data = (ulong)clks_nb,
61282a248dfSMarek Behún 	},
61382a248dfSMarek Behún 	{
61482a248dfSMarek Behún 		.compatible = "marvell,armada-3700-periph-clock-sb",
61582a248dfSMarek Behún 		.data = (ulong)clks_sb,
61682a248dfSMarek Behún 	},
61782a248dfSMarek Behún 	{}
61882a248dfSMarek Behún };
61982a248dfSMarek Behún 
62082a248dfSMarek Behún U_BOOT_DRIVER(armada_37xx_periph_clk) = {
62182a248dfSMarek Behún 	.name		= "armada_37xx_periph_clk",
62282a248dfSMarek Behún 	.id		= UCLASS_CLK,
62382a248dfSMarek Behún 	.of_match	= armada_37xx_periph_clk_ids,
62482a248dfSMarek Behún 	.ops		= &armada_37xx_periph_clk_ops,
62582a248dfSMarek Behún 	.priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
62682a248dfSMarek Behún 	.probe		= armada_37xx_periph_clk_probe,
62782a248dfSMarek Behún };
628