xref: /openbmc/linux/drivers/clk/st/clkgen-mux.c (revision 3be6d8ce639d92e60d144fb99dd74a53fe3799bb)
194885fafSGabriel FERNANDEZ /*
294885fafSGabriel FERNANDEZ  * clkgen-mux.c: ST GEN-MUX Clock driver
394885fafSGabriel FERNANDEZ  *
494885fafSGabriel FERNANDEZ  * Copyright (C) 2014 STMicroelectronics (R&D) Limited
594885fafSGabriel FERNANDEZ  *
694885fafSGabriel FERNANDEZ  * Authors: Stephen Gallimore <stephen.gallimore@st.com>
794885fafSGabriel FERNANDEZ  *	    Pankaj Dev <pankaj.dev@st.com>
894885fafSGabriel FERNANDEZ  *
994885fafSGabriel FERNANDEZ  * This program is free software; you can redistribute it and/or modify
1094885fafSGabriel FERNANDEZ  * it under the terms of the GNU General Public License as published by
1194885fafSGabriel FERNANDEZ  * the Free Software Foundation; either version 2 of the License, or
1294885fafSGabriel FERNANDEZ  * (at your option) any later version.
1394885fafSGabriel FERNANDEZ  *
1494885fafSGabriel FERNANDEZ  */
1594885fafSGabriel FERNANDEZ 
1694885fafSGabriel FERNANDEZ #include <linux/slab.h>
1794885fafSGabriel FERNANDEZ #include <linux/of_address.h>
1894885fafSGabriel FERNANDEZ #include <linux/clk-provider.h>
1994885fafSGabriel FERNANDEZ 
2094885fafSGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgena_divmux_lock);
2144993d38SGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgenf_lock);
2294885fafSGabriel FERNANDEZ 
2394885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np,
2494885fafSGabriel FERNANDEZ 						       int *num_parents)
2594885fafSGabriel FERNANDEZ {
2694885fafSGabriel FERNANDEZ 	const char **parents;
2794885fafSGabriel FERNANDEZ 	int nparents, i;
2894885fafSGabriel FERNANDEZ 
290a65239cSGeert Uytterhoeven 	nparents = of_clk_get_parent_count(np);
3094885fafSGabriel FERNANDEZ 	if (WARN_ON(nparents <= 0))
3194885fafSGabriel FERNANDEZ 		return ERR_PTR(-EINVAL);
3294885fafSGabriel FERNANDEZ 
3394885fafSGabriel FERNANDEZ 	parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL);
3494885fafSGabriel FERNANDEZ 	if (!parents)
3594885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
3694885fafSGabriel FERNANDEZ 
3794885fafSGabriel FERNANDEZ 	for (i = 0; i < nparents; i++)
3894885fafSGabriel FERNANDEZ 		parents[i] = of_clk_get_parent_name(np, i);
3994885fafSGabriel FERNANDEZ 
4094885fafSGabriel FERNANDEZ 	*num_parents = nparents;
4194885fafSGabriel FERNANDEZ 	return parents;
4294885fafSGabriel FERNANDEZ }
4394885fafSGabriel FERNANDEZ 
4494885fafSGabriel FERNANDEZ /**
4594885fafSGabriel FERNANDEZ  * DOC: Clock mux with a programmable divider on each of its three inputs.
4694885fafSGabriel FERNANDEZ  *      The mux has an input setting which effectively gates its output.
4794885fafSGabriel FERNANDEZ  *
4894885fafSGabriel FERNANDEZ  * Traits of this clock:
4994885fafSGabriel FERNANDEZ  * prepare - clk_(un)prepare only ensures parent is (un)prepared
5094885fafSGabriel FERNANDEZ  * enable - clk_enable and clk_disable are functional & control gating
5194885fafSGabriel FERNANDEZ  * rate - set rate is supported
5294885fafSGabriel FERNANDEZ  * parent - set/get parent
5394885fafSGabriel FERNANDEZ  */
5494885fafSGabriel FERNANDEZ 
5594885fafSGabriel FERNANDEZ #define NUM_INPUTS 3
5694885fafSGabriel FERNANDEZ 
5794885fafSGabriel FERNANDEZ struct clkgena_divmux {
5894885fafSGabriel FERNANDEZ 	struct clk_hw hw;
5994885fafSGabriel FERNANDEZ 	/* Subclassed mux and divider structures */
6094885fafSGabriel FERNANDEZ 	struct clk_mux mux;
6194885fafSGabriel FERNANDEZ 	struct clk_divider div[NUM_INPUTS];
6294885fafSGabriel FERNANDEZ 	/* Enable/running feedback register bits for each input */
6394885fafSGabriel FERNANDEZ 	void __iomem *feedback_reg[NUM_INPUTS];
6494885fafSGabriel FERNANDEZ 	int feedback_bit_idx;
6594885fafSGabriel FERNANDEZ 
6694885fafSGabriel FERNANDEZ 	u8              muxsel;
6794885fafSGabriel FERNANDEZ };
6894885fafSGabriel FERNANDEZ 
6994885fafSGabriel FERNANDEZ #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
7094885fafSGabriel FERNANDEZ 
7194885fafSGabriel FERNANDEZ struct clkgena_divmux_data {
7294885fafSGabriel FERNANDEZ 	int num_outputs;
7394885fafSGabriel FERNANDEZ 	int mux_offset;
7494885fafSGabriel FERNANDEZ 	int mux_offset2;
7594885fafSGabriel FERNANDEZ 	int mux_start_bit;
7694885fafSGabriel FERNANDEZ 	int div_offsets[NUM_INPUTS];
7794885fafSGabriel FERNANDEZ 	int fb_offsets[NUM_INPUTS];
7894885fafSGabriel FERNANDEZ 	int fb_start_bit_idx;
7994885fafSGabriel FERNANDEZ };
8094885fafSGabriel FERNANDEZ 
8194885fafSGabriel FERNANDEZ #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
8294885fafSGabriel FERNANDEZ 
8394885fafSGabriel FERNANDEZ static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
8494885fafSGabriel FERNANDEZ {
8594885fafSGabriel FERNANDEZ 	u32 regval = readl(mux->feedback_reg[mux->muxsel]);
8694885fafSGabriel FERNANDEZ 	u32 running = regval & BIT(mux->feedback_bit_idx);
8794885fafSGabriel FERNANDEZ 	return !!running;
8894885fafSGabriel FERNANDEZ }
8994885fafSGabriel FERNANDEZ 
9094885fafSGabriel FERNANDEZ static int clkgena_divmux_enable(struct clk_hw *hw)
9194885fafSGabriel FERNANDEZ {
9294885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
9394885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
9494885fafSGabriel FERNANDEZ 	unsigned long timeout;
9594885fafSGabriel FERNANDEZ 	int ret = 0;
9694885fafSGabriel FERNANDEZ 
974e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
9894885fafSGabriel FERNANDEZ 
9994885fafSGabriel FERNANDEZ 	ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
10094885fafSGabriel FERNANDEZ 	if (ret)
10194885fafSGabriel FERNANDEZ 		return ret;
10294885fafSGabriel FERNANDEZ 
10394885fafSGabriel FERNANDEZ 	timeout = jiffies + msecs_to_jiffies(10);
10494885fafSGabriel FERNANDEZ 
10594885fafSGabriel FERNANDEZ 	while (!clkgena_divmux_is_running(genamux)) {
10694885fafSGabriel FERNANDEZ 		if (time_after(jiffies, timeout))
10794885fafSGabriel FERNANDEZ 			return -ETIMEDOUT;
10894885fafSGabriel FERNANDEZ 		cpu_relax();
10994885fafSGabriel FERNANDEZ 	}
11094885fafSGabriel FERNANDEZ 
11194885fafSGabriel FERNANDEZ 	return 0;
11294885fafSGabriel FERNANDEZ }
11394885fafSGabriel FERNANDEZ 
11494885fafSGabriel FERNANDEZ static void clkgena_divmux_disable(struct clk_hw *hw)
11594885fafSGabriel FERNANDEZ {
11694885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
11794885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
11894885fafSGabriel FERNANDEZ 
1194e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
12094885fafSGabriel FERNANDEZ 
12194885fafSGabriel FERNANDEZ 	clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
12294885fafSGabriel FERNANDEZ }
12394885fafSGabriel FERNANDEZ 
12494885fafSGabriel FERNANDEZ static int clkgena_divmux_is_enabled(struct clk_hw *hw)
12594885fafSGabriel FERNANDEZ {
12694885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
12794885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
12894885fafSGabriel FERNANDEZ 
1294e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
13094885fafSGabriel FERNANDEZ 
13194885fafSGabriel FERNANDEZ 	return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
13294885fafSGabriel FERNANDEZ }
13394885fafSGabriel FERNANDEZ 
1348e6dd77cSStephen Boyd static u8 clkgena_divmux_get_parent(struct clk_hw *hw)
13594885fafSGabriel FERNANDEZ {
13694885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
13794885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
13894885fafSGabriel FERNANDEZ 
1394e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
14094885fafSGabriel FERNANDEZ 
14194885fafSGabriel FERNANDEZ 	genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
14294885fafSGabriel FERNANDEZ 	if ((s8)genamux->muxsel < 0) {
14394885fafSGabriel FERNANDEZ 		pr_debug("%s: %s: Invalid parent, setting to default.\n",
14494885fafSGabriel FERNANDEZ 		      __func__, __clk_get_name(hw->clk));
14594885fafSGabriel FERNANDEZ 		genamux->muxsel = 0;
14694885fafSGabriel FERNANDEZ 	}
14794885fafSGabriel FERNANDEZ 
14894885fafSGabriel FERNANDEZ 	return genamux->muxsel;
14994885fafSGabriel FERNANDEZ }
15094885fafSGabriel FERNANDEZ 
15194885fafSGabriel FERNANDEZ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
15294885fafSGabriel FERNANDEZ {
15394885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
15494885fafSGabriel FERNANDEZ 
15594885fafSGabriel FERNANDEZ 	if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
15694885fafSGabriel FERNANDEZ 		return -EINVAL;
15794885fafSGabriel FERNANDEZ 
15894885fafSGabriel FERNANDEZ 	genamux->muxsel = index;
15994885fafSGabriel FERNANDEZ 
16094885fafSGabriel FERNANDEZ 	/*
16194885fafSGabriel FERNANDEZ 	 * If the mux is already enabled, call enable directly to set the
16294885fafSGabriel FERNANDEZ 	 * new mux position and wait for it to start running again. Otherwise
16394885fafSGabriel FERNANDEZ 	 * do nothing.
16494885fafSGabriel FERNANDEZ 	 */
16594885fafSGabriel FERNANDEZ 	if (clkgena_divmux_is_enabled(hw))
16694885fafSGabriel FERNANDEZ 		clkgena_divmux_enable(hw);
16794885fafSGabriel FERNANDEZ 
16894885fafSGabriel FERNANDEZ 	return 0;
16994885fafSGabriel FERNANDEZ }
17094885fafSGabriel FERNANDEZ 
1718e6dd77cSStephen Boyd static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
17294885fafSGabriel FERNANDEZ 		unsigned long parent_rate)
17394885fafSGabriel FERNANDEZ {
17494885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
17594885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
17694885fafSGabriel FERNANDEZ 
1774e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
17894885fafSGabriel FERNANDEZ 
17994885fafSGabriel FERNANDEZ 	return clk_divider_ops.recalc_rate(div_hw, parent_rate);
18094885fafSGabriel FERNANDEZ }
18194885fafSGabriel FERNANDEZ 
18294885fafSGabriel FERNANDEZ static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
18394885fafSGabriel FERNANDEZ 				unsigned long parent_rate)
18494885fafSGabriel FERNANDEZ {
18594885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
18694885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
18794885fafSGabriel FERNANDEZ 
1884e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
18994885fafSGabriel FERNANDEZ 
19094885fafSGabriel FERNANDEZ 	return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
19194885fafSGabriel FERNANDEZ }
19294885fafSGabriel FERNANDEZ 
19394885fafSGabriel FERNANDEZ static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
19494885fafSGabriel FERNANDEZ 				   unsigned long *prate)
19594885fafSGabriel FERNANDEZ {
19694885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
19794885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
19894885fafSGabriel FERNANDEZ 
1994e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
20094885fafSGabriel FERNANDEZ 
20194885fafSGabriel FERNANDEZ 	return clk_divider_ops.round_rate(div_hw, rate, prate);
20294885fafSGabriel FERNANDEZ }
20394885fafSGabriel FERNANDEZ 
20494885fafSGabriel FERNANDEZ static const struct clk_ops clkgena_divmux_ops = {
20594885fafSGabriel FERNANDEZ 	.enable = clkgena_divmux_enable,
20694885fafSGabriel FERNANDEZ 	.disable = clkgena_divmux_disable,
20794885fafSGabriel FERNANDEZ 	.is_enabled = clkgena_divmux_is_enabled,
20894885fafSGabriel FERNANDEZ 	.get_parent = clkgena_divmux_get_parent,
20994885fafSGabriel FERNANDEZ 	.set_parent = clkgena_divmux_set_parent,
21094885fafSGabriel FERNANDEZ 	.round_rate = clkgena_divmux_round_rate,
21194885fafSGabriel FERNANDEZ 	.recalc_rate = clkgena_divmux_recalc_rate,
21294885fafSGabriel FERNANDEZ 	.set_rate = clkgena_divmux_set_rate,
21394885fafSGabriel FERNANDEZ };
21494885fafSGabriel FERNANDEZ 
21594885fafSGabriel FERNANDEZ /**
21694885fafSGabriel FERNANDEZ  * clk_register_genamux - register a genamux clock with the clock framework
21794885fafSGabriel FERNANDEZ  */
2188e6dd77cSStephen Boyd static struct clk *clk_register_genamux(const char *name,
21994885fafSGabriel FERNANDEZ 				const char **parent_names, u8 num_parents,
22094885fafSGabriel FERNANDEZ 				void __iomem *reg,
22194885fafSGabriel FERNANDEZ 				const struct clkgena_divmux_data *muxdata,
22294885fafSGabriel FERNANDEZ 				u32 idx)
22394885fafSGabriel FERNANDEZ {
22494885fafSGabriel FERNANDEZ 	/*
22594885fafSGabriel FERNANDEZ 	 * Fixed constants across all ClockgenA variants
22694885fafSGabriel FERNANDEZ 	 */
22794885fafSGabriel FERNANDEZ 	const int mux_width = 2;
22894885fafSGabriel FERNANDEZ 	const int divider_width = 5;
22994885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux;
23094885fafSGabriel FERNANDEZ 	struct clk *clk;
23194885fafSGabriel FERNANDEZ 	struct clk_init_data init;
23294885fafSGabriel FERNANDEZ 	int i;
23394885fafSGabriel FERNANDEZ 
23494885fafSGabriel FERNANDEZ 	genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
23594885fafSGabriel FERNANDEZ 	if (!genamux)
23694885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
23794885fafSGabriel FERNANDEZ 
23894885fafSGabriel FERNANDEZ 	init.name = name;
23994885fafSGabriel FERNANDEZ 	init.ops = &clkgena_divmux_ops;
24018fee453SPankaj Dev 	init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE;
24194885fafSGabriel FERNANDEZ 	init.parent_names = parent_names;
24294885fafSGabriel FERNANDEZ 	init.num_parents = num_parents;
24394885fafSGabriel FERNANDEZ 
24494885fafSGabriel FERNANDEZ 	genamux->mux.lock  = &clkgena_divmux_lock;
24594885fafSGabriel FERNANDEZ 	genamux->mux.mask = BIT(mux_width) - 1;
24694885fafSGabriel FERNANDEZ 	genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
24794885fafSGabriel FERNANDEZ 	if (genamux->mux.shift > 31) {
24894885fafSGabriel FERNANDEZ 		/*
24994885fafSGabriel FERNANDEZ 		 * We have spilled into the second mux register so
25094885fafSGabriel FERNANDEZ 		 * adjust the register address and the bit shift accordingly
25194885fafSGabriel FERNANDEZ 		 */
25294885fafSGabriel FERNANDEZ 		genamux->mux.reg = reg + muxdata->mux_offset2;
25394885fafSGabriel FERNANDEZ 		genamux->mux.shift -= 32;
25494885fafSGabriel FERNANDEZ 	} else {
25594885fafSGabriel FERNANDEZ 		genamux->mux.reg   = reg + muxdata->mux_offset;
25694885fafSGabriel FERNANDEZ 	}
25794885fafSGabriel FERNANDEZ 
25894885fafSGabriel FERNANDEZ 	for (i = 0; i < NUM_INPUTS; i++) {
25994885fafSGabriel FERNANDEZ 		/*
26094885fafSGabriel FERNANDEZ 		 * Divider config for each input
26194885fafSGabriel FERNANDEZ 		 */
26294885fafSGabriel FERNANDEZ 		void __iomem *divbase = reg + muxdata->div_offsets[i];
26394885fafSGabriel FERNANDEZ 		genamux->div[i].width = divider_width;
26494885fafSGabriel FERNANDEZ 		genamux->div[i].reg = divbase + (idx * sizeof(u32));
26594885fafSGabriel FERNANDEZ 
26694885fafSGabriel FERNANDEZ 		/*
26794885fafSGabriel FERNANDEZ 		 * Mux enabled/running feedback register for each input.
26894885fafSGabriel FERNANDEZ 		 */
26994885fafSGabriel FERNANDEZ 		genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
27094885fafSGabriel FERNANDEZ 	}
27194885fafSGabriel FERNANDEZ 
27294885fafSGabriel FERNANDEZ 	genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
27394885fafSGabriel FERNANDEZ 	genamux->hw.init = &init;
27494885fafSGabriel FERNANDEZ 
27594885fafSGabriel FERNANDEZ 	clk = clk_register(NULL, &genamux->hw);
27694885fafSGabriel FERNANDEZ 	if (IS_ERR(clk)) {
27794885fafSGabriel FERNANDEZ 		kfree(genamux);
27894885fafSGabriel FERNANDEZ 		goto err;
27994885fafSGabriel FERNANDEZ 	}
28094885fafSGabriel FERNANDEZ 
28194885fafSGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %lu\n",
28294885fafSGabriel FERNANDEZ 			__clk_get_name(clk),
28394885fafSGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
28494885fafSGabriel FERNANDEZ 			clk_get_rate(clk));
28594885fafSGabriel FERNANDEZ err:
28694885fafSGabriel FERNANDEZ 	return clk;
28794885fafSGabriel FERNANDEZ }
28894885fafSGabriel FERNANDEZ 
28994885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65hs = {
29094885fafSGabriel FERNANDEZ 	.num_outputs = 4,
29194885fafSGabriel FERNANDEZ 	.mux_offset = 0x14,
29294885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
29394885fafSGabriel FERNANDEZ 	.div_offsets = { 0x800, 0x900, 0xb00 },
29494885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x18, 0x1c, 0x20 },
29594885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 0,
29694885fafSGabriel FERNANDEZ };
29794885fafSGabriel FERNANDEZ 
29894885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65ls = {
29994885fafSGabriel FERNANDEZ 	.num_outputs = 14,
30094885fafSGabriel FERNANDEZ 	.mux_offset = 0x14,
30194885fafSGabriel FERNANDEZ 	.mux_offset2 = 0x24,
30294885fafSGabriel FERNANDEZ 	.mux_start_bit = 8,
30394885fafSGabriel FERNANDEZ 	.div_offsets = { 0x810, 0xa10, 0xb10 },
30494885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x18, 0x1c, 0x20 },
30594885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 4,
30694885fafSGabriel FERNANDEZ };
30794885fafSGabriel FERNANDEZ 
30894885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf0 = {
30994885fafSGabriel FERNANDEZ 	.num_outputs = 8,
31094885fafSGabriel FERNANDEZ 	.mux_offset = 0x1c,
31194885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
31294885fafSGabriel FERNANDEZ 	.div_offsets = { 0x800, 0x900, 0xa60 },
31394885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
31494885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 0,
31594885fafSGabriel FERNANDEZ };
31694885fafSGabriel FERNANDEZ 
31794885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf1 = {
31894885fafSGabriel FERNANDEZ 	.num_outputs = 8,
31994885fafSGabriel FERNANDEZ 	.mux_offset = 0x1c,
32094885fafSGabriel FERNANDEZ 	.mux_start_bit = 16,
32194885fafSGabriel FERNANDEZ 	.div_offsets = { 0x820, 0x980, 0xa80 },
32294885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
32394885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 8,
32494885fafSGabriel FERNANDEZ };
32594885fafSGabriel FERNANDEZ 
32694885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf2 = {
32794885fafSGabriel FERNANDEZ 	.num_outputs = 8,
32894885fafSGabriel FERNANDEZ 	.mux_offset = 0x20,
32994885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
33094885fafSGabriel FERNANDEZ 	.div_offsets = { 0x840, 0xa20, 0xb10 },
33194885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
33294885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 16,
33394885fafSGabriel FERNANDEZ };
33494885fafSGabriel FERNANDEZ 
33594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf3 = {
33694885fafSGabriel FERNANDEZ 	.num_outputs = 8,
33794885fafSGabriel FERNANDEZ 	.mux_offset = 0x20,
33894885fafSGabriel FERNANDEZ 	.mux_start_bit = 16,
33994885fafSGabriel FERNANDEZ 	.div_offsets = { 0x860, 0xa40, 0xb30 },
34094885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
34194885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 24,
34294885fafSGabriel FERNANDEZ };
34394885fafSGabriel FERNANDEZ 
344f375573cSFabian Frederick static const struct of_device_id clkgena_divmux_of_match[] = {
34594885fafSGabriel FERNANDEZ 	{
34694885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c65-hs",
34794885fafSGabriel FERNANDEZ 		.data = &st_divmux_c65hs,
34894885fafSGabriel FERNANDEZ 	},
34994885fafSGabriel FERNANDEZ 	{
35094885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c65-ls",
35194885fafSGabriel FERNANDEZ 		.data = &st_divmux_c65ls,
35294885fafSGabriel FERNANDEZ 	},
35394885fafSGabriel FERNANDEZ 	{
35494885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf0",
35594885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf0,
35694885fafSGabriel FERNANDEZ 	},
35794885fafSGabriel FERNANDEZ 	{
35894885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf1",
35994885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf1,
36094885fafSGabriel FERNANDEZ 	},
36194885fafSGabriel FERNANDEZ 	{
36294885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf2",
36394885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf2,
36494885fafSGabriel FERNANDEZ 	},
36594885fafSGabriel FERNANDEZ 	{
36694885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf3",
36794885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf3,
36894885fafSGabriel FERNANDEZ 	},
36994885fafSGabriel FERNANDEZ 	{}
37094885fafSGabriel FERNANDEZ };
37194885fafSGabriel FERNANDEZ 
37294885fafSGabriel FERNANDEZ static void __iomem * __init clkgen_get_register_base(
37394885fafSGabriel FERNANDEZ 				struct device_node *np)
37494885fafSGabriel FERNANDEZ {
37594885fafSGabriel FERNANDEZ 	struct device_node *pnode;
37694885fafSGabriel FERNANDEZ 	void __iomem *reg = NULL;
37794885fafSGabriel FERNANDEZ 
37894885fafSGabriel FERNANDEZ 	pnode = of_get_parent(np);
37994885fafSGabriel FERNANDEZ 	if (!pnode)
38094885fafSGabriel FERNANDEZ 		return NULL;
38194885fafSGabriel FERNANDEZ 
38294885fafSGabriel FERNANDEZ 	reg = of_iomap(pnode, 0);
38394885fafSGabriel FERNANDEZ 
38494885fafSGabriel FERNANDEZ 	of_node_put(pnode);
38594885fafSGabriel FERNANDEZ 	return reg;
38694885fafSGabriel FERNANDEZ }
38794885fafSGabriel FERNANDEZ 
3888e6dd77cSStephen Boyd static void __init st_of_clkgena_divmux_setup(struct device_node *np)
38994885fafSGabriel FERNANDEZ {
39094885fafSGabriel FERNANDEZ 	const struct of_device_id *match;
39194885fafSGabriel FERNANDEZ 	const struct clkgena_divmux_data *data;
39294885fafSGabriel FERNANDEZ 	struct clk_onecell_data *clk_data;
39394885fafSGabriel FERNANDEZ 	void __iomem *reg;
39494885fafSGabriel FERNANDEZ 	const char **parents;
39594885fafSGabriel FERNANDEZ 	int num_parents = 0, i;
39694885fafSGabriel FERNANDEZ 
39794885fafSGabriel FERNANDEZ 	match = of_match_node(clkgena_divmux_of_match, np);
39894885fafSGabriel FERNANDEZ 	if (WARN_ON(!match))
39994885fafSGabriel FERNANDEZ 		return;
40094885fafSGabriel FERNANDEZ 
40194885fafSGabriel FERNANDEZ 	data = (struct clkgena_divmux_data *)match->data;
40294885fafSGabriel FERNANDEZ 
40394885fafSGabriel FERNANDEZ 	reg = clkgen_get_register_base(np);
40494885fafSGabriel FERNANDEZ 	if (!reg)
40594885fafSGabriel FERNANDEZ 		return;
40694885fafSGabriel FERNANDEZ 
40794885fafSGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
40894885fafSGabriel FERNANDEZ 	if (IS_ERR(parents))
40994885fafSGabriel FERNANDEZ 		return;
41094885fafSGabriel FERNANDEZ 
41194885fafSGabriel FERNANDEZ 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
41294885fafSGabriel FERNANDEZ 	if (!clk_data)
41394885fafSGabriel FERNANDEZ 		goto err;
41494885fafSGabriel FERNANDEZ 
41594885fafSGabriel FERNANDEZ 	clk_data->clk_num = data->num_outputs;
41694885fafSGabriel FERNANDEZ 	clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
41794885fafSGabriel FERNANDEZ 				 GFP_KERNEL);
41894885fafSGabriel FERNANDEZ 
41994885fafSGabriel FERNANDEZ 	if (!clk_data->clks)
42094885fafSGabriel FERNANDEZ 		goto err;
42194885fafSGabriel FERNANDEZ 
42294885fafSGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
42394885fafSGabriel FERNANDEZ 		struct clk *clk;
42494885fafSGabriel FERNANDEZ 		const char *clk_name;
42594885fafSGabriel FERNANDEZ 
42694885fafSGabriel FERNANDEZ 		if (of_property_read_string_index(np, "clock-output-names",
42794885fafSGabriel FERNANDEZ 						  i, &clk_name))
42894885fafSGabriel FERNANDEZ 			break;
42994885fafSGabriel FERNANDEZ 
43094885fafSGabriel FERNANDEZ 		/*
43194885fafSGabriel FERNANDEZ 		 * If we read an empty clock name then the output is unused
43294885fafSGabriel FERNANDEZ 		 */
43394885fafSGabriel FERNANDEZ 		if (*clk_name == '\0')
43494885fafSGabriel FERNANDEZ 			continue;
43594885fafSGabriel FERNANDEZ 
43694885fafSGabriel FERNANDEZ 		clk = clk_register_genamux(clk_name, parents, num_parents,
43794885fafSGabriel FERNANDEZ 					   reg, data, i);
43894885fafSGabriel FERNANDEZ 
43994885fafSGabriel FERNANDEZ 		if (IS_ERR(clk))
44094885fafSGabriel FERNANDEZ 			goto err;
44194885fafSGabriel FERNANDEZ 
44294885fafSGabriel FERNANDEZ 		clk_data->clks[i] = clk;
44394885fafSGabriel FERNANDEZ 	}
44494885fafSGabriel FERNANDEZ 
44594885fafSGabriel FERNANDEZ 	kfree(parents);
44694885fafSGabriel FERNANDEZ 
44794885fafSGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
44894885fafSGabriel FERNANDEZ 	return;
44994885fafSGabriel FERNANDEZ err:
45094885fafSGabriel FERNANDEZ 	if (clk_data)
45194885fafSGabriel FERNANDEZ 		kfree(clk_data->clks);
45294885fafSGabriel FERNANDEZ 
45394885fafSGabriel FERNANDEZ 	kfree(clk_data);
45494885fafSGabriel FERNANDEZ 	kfree(parents);
45594885fafSGabriel FERNANDEZ }
45694885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
45794885fafSGabriel FERNANDEZ 
45894885fafSGabriel FERNANDEZ struct clkgena_prediv_data {
45994885fafSGabriel FERNANDEZ 	u32 offset;
46094885fafSGabriel FERNANDEZ 	u8 shift;
46194885fafSGabriel FERNANDEZ 	struct clk_div_table *table;
46294885fafSGabriel FERNANDEZ };
46394885fafSGabriel FERNANDEZ 
46494885fafSGabriel FERNANDEZ static struct clk_div_table prediv_table16[] = {
46594885fafSGabriel FERNANDEZ 	{ .val = 0, .div = 1 },
46694885fafSGabriel FERNANDEZ 	{ .val = 1, .div = 16 },
46794885fafSGabriel FERNANDEZ 	{ .div = 0 },
46894885fafSGabriel FERNANDEZ };
46994885fafSGabriel FERNANDEZ 
47094885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c65_data = {
47194885fafSGabriel FERNANDEZ 	.offset = 0x4c,
47294885fafSGabriel FERNANDEZ 	.shift = 31,
47394885fafSGabriel FERNANDEZ 	.table = prediv_table16,
47494885fafSGabriel FERNANDEZ };
47594885fafSGabriel FERNANDEZ 
47694885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c32_data = {
47794885fafSGabriel FERNANDEZ 	.offset = 0x50,
47894885fafSGabriel FERNANDEZ 	.shift = 1,
47994885fafSGabriel FERNANDEZ 	.table = prediv_table16,
48094885fafSGabriel FERNANDEZ };
48194885fafSGabriel FERNANDEZ 
482f375573cSFabian Frederick static const struct of_device_id clkgena_prediv_of_match[] = {
48394885fafSGabriel FERNANDEZ 	{ .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
48494885fafSGabriel FERNANDEZ 	{ .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
48594885fafSGabriel FERNANDEZ 	{}
48694885fafSGabriel FERNANDEZ };
48794885fafSGabriel FERNANDEZ 
4888e6dd77cSStephen Boyd static void __init st_of_clkgena_prediv_setup(struct device_node *np)
48994885fafSGabriel FERNANDEZ {
49094885fafSGabriel FERNANDEZ 	const struct of_device_id *match;
49194885fafSGabriel FERNANDEZ 	void __iomem *reg;
49294885fafSGabriel FERNANDEZ 	const char *parent_name, *clk_name;
49394885fafSGabriel FERNANDEZ 	struct clk *clk;
49494885fafSGabriel FERNANDEZ 	struct clkgena_prediv_data *data;
49594885fafSGabriel FERNANDEZ 
49694885fafSGabriel FERNANDEZ 	match = of_match_node(clkgena_prediv_of_match, np);
49794885fafSGabriel FERNANDEZ 	if (!match) {
49894885fafSGabriel FERNANDEZ 		pr_err("%s: No matching data\n", __func__);
49994885fafSGabriel FERNANDEZ 		return;
50094885fafSGabriel FERNANDEZ 	}
50194885fafSGabriel FERNANDEZ 
50294885fafSGabriel FERNANDEZ 	data = (struct clkgena_prediv_data *)match->data;
50394885fafSGabriel FERNANDEZ 
50494885fafSGabriel FERNANDEZ 	reg = clkgen_get_register_base(np);
50594885fafSGabriel FERNANDEZ 	if (!reg)
50694885fafSGabriel FERNANDEZ 		return;
50794885fafSGabriel FERNANDEZ 
50894885fafSGabriel FERNANDEZ 	parent_name = of_clk_get_parent_name(np, 0);
50994885fafSGabriel FERNANDEZ 	if (!parent_name)
51094885fafSGabriel FERNANDEZ 		return;
51194885fafSGabriel FERNANDEZ 
51294885fafSGabriel FERNANDEZ 	if (of_property_read_string_index(np, "clock-output-names",
51394885fafSGabriel FERNANDEZ 					  0, &clk_name))
51494885fafSGabriel FERNANDEZ 		return;
51594885fafSGabriel FERNANDEZ 
51618fee453SPankaj Dev 	clk = clk_register_divider_table(NULL, clk_name, parent_name,
51718fee453SPankaj Dev 					 CLK_GET_RATE_NOCACHE,
51894885fafSGabriel FERNANDEZ 					 reg + data->offset, data->shift, 1,
51994885fafSGabriel FERNANDEZ 					 0, data->table, NULL);
52094885fafSGabriel FERNANDEZ 	if (IS_ERR(clk))
52194885fafSGabriel FERNANDEZ 		return;
52294885fafSGabriel FERNANDEZ 
52394885fafSGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
52494885fafSGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %u\n",
52594885fafSGabriel FERNANDEZ 		__clk_get_name(clk),
52694885fafSGabriel FERNANDEZ 		__clk_get_name(clk_get_parent(clk)),
52794885fafSGabriel FERNANDEZ 		(unsigned int)clk_get_rate(clk));
52894885fafSGabriel FERNANDEZ 
52994885fafSGabriel FERNANDEZ 	return;
53094885fafSGabriel FERNANDEZ }
53194885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
53244993d38SGabriel FERNANDEZ 
53344993d38SGabriel FERNANDEZ struct clkgen_mux_data {
53444993d38SGabriel FERNANDEZ 	u32 offset;
53544993d38SGabriel FERNANDEZ 	u8 shift;
53644993d38SGabriel FERNANDEZ 	u8 width;
53744993d38SGabriel FERNANDEZ 	spinlock_t *lock;
53844993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
53944993d38SGabriel FERNANDEZ 	u8 mux_flags;
54044993d38SGabriel FERNANDEZ };
54144993d38SGabriel FERNANDEZ 
54244993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
54344993d38SGabriel FERNANDEZ 	.offset = 0,
54444993d38SGabriel FERNANDEZ 	.shift = 0,
54544993d38SGabriel FERNANDEZ 	.width = 1,
54644993d38SGabriel FERNANDEZ };
54744993d38SGabriel FERNANDEZ 
54844993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
54944993d38SGabriel FERNANDEZ 	.offset = 0,
55044993d38SGabriel FERNANDEZ 	.shift = 0,
55144993d38SGabriel FERNANDEZ 	.width = 1,
55244993d38SGabriel FERNANDEZ };
55344993d38SGabriel FERNANDEZ 
55444993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
55544993d38SGabriel FERNANDEZ 	.offset = 0,
55644993d38SGabriel FERNANDEZ 	.shift = 0,
55744993d38SGabriel FERNANDEZ 	.width = 1,
55844993d38SGabriel FERNANDEZ };
55944993d38SGabriel FERNANDEZ 
56044993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
56144993d38SGabriel FERNANDEZ 	.offset = 0,
56244993d38SGabriel FERNANDEZ 	.shift = 16,
56344993d38SGabriel FERNANDEZ 	.width = 1,
56444993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
56544993d38SGabriel FERNANDEZ };
56644993d38SGabriel FERNANDEZ 
56744993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
56844993d38SGabriel FERNANDEZ 	.offset = 0,
56944993d38SGabriel FERNANDEZ 	.shift = 17,
57044993d38SGabriel FERNANDEZ 	.width = 1,
57144993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
57244993d38SGabriel FERNANDEZ };
57344993d38SGabriel FERNANDEZ 
574ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih415_a9_mux_data = {
575ab35dc13SGabriel FERNANDEZ 	.offset = 0,
576ab35dc13SGabriel FERNANDEZ 	.shift = 1,
577ab35dc13SGabriel FERNANDEZ 	.width = 2,
578ab35dc13SGabriel FERNANDEZ };
579ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih416_a9_mux_data = {
580ab35dc13SGabriel FERNANDEZ 	.offset = 0,
581ab35dc13SGabriel FERNANDEZ 	.shift = 0,
582ab35dc13SGabriel FERNANDEZ 	.width = 2,
583ab35dc13SGabriel FERNANDEZ };
58413e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = {
58513e6f2daSGabriel FERNANDEZ 	.offset = 0x1a4,
586*3be6d8ceSGabriel Fernandez 	.shift = 0,
58713e6f2daSGabriel FERNANDEZ 	.width = 2,
58813e6f2daSGabriel FERNANDEZ };
589ab35dc13SGabriel FERNANDEZ 
590f375573cSFabian Frederick static const struct of_device_id mux_of_match[] = {
59144993d38SGabriel FERNANDEZ 	{
59244993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenc-vcc-hd",
59344993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_c_vcc_hd_416,
59444993d38SGabriel FERNANDEZ 	},
59544993d38SGabriel FERNANDEZ 	{
59644993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-fvdp",
59744993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_f_vcc_fvdp_416,
59844993d38SGabriel FERNANDEZ 	},
59944993d38SGabriel FERNANDEZ 	{
60044993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-hva",
60144993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_f_vcc_hva_416,
60244993d38SGabriel FERNANDEZ 	},
60344993d38SGabriel FERNANDEZ 	{
60444993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-hd",
60544993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_f_vcc_hd_416,
60644993d38SGabriel FERNANDEZ 	},
60744993d38SGabriel FERNANDEZ 	{
60844993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-sd",
60944993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_c_vcc_sd_416,
61044993d38SGabriel FERNANDEZ 	},
611ab35dc13SGabriel FERNANDEZ 	{
612ab35dc13SGabriel FERNANDEZ 		.compatible = "st,stih415-clkgen-a9-mux",
613ab35dc13SGabriel FERNANDEZ 		.data = &stih415_a9_mux_data,
614ab35dc13SGabriel FERNANDEZ 	},
615ab35dc13SGabriel FERNANDEZ 	{
616ab35dc13SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgen-a9-mux",
617ab35dc13SGabriel FERNANDEZ 		.data = &stih416_a9_mux_data,
618ab35dc13SGabriel FERNANDEZ 	},
61913e6f2daSGabriel FERNANDEZ 	{
62013e6f2daSGabriel FERNANDEZ 		.compatible = "st,stih407-clkgen-a9-mux",
62113e6f2daSGabriel FERNANDEZ 		.data = &stih407_a9_mux_data,
62213e6f2daSGabriel FERNANDEZ 	},
62344993d38SGabriel FERNANDEZ 	{}
62444993d38SGabriel FERNANDEZ };
62544993d38SGabriel FERNANDEZ 
6268e6dd77cSStephen Boyd static void __init st_of_clkgen_mux_setup(struct device_node *np)
62744993d38SGabriel FERNANDEZ {
62844993d38SGabriel FERNANDEZ 	const struct of_device_id *match;
62944993d38SGabriel FERNANDEZ 	struct clk *clk;
63044993d38SGabriel FERNANDEZ 	void __iomem *reg;
63144993d38SGabriel FERNANDEZ 	const char **parents;
63244993d38SGabriel FERNANDEZ 	int num_parents;
63344993d38SGabriel FERNANDEZ 	struct clkgen_mux_data *data;
63444993d38SGabriel FERNANDEZ 
63544993d38SGabriel FERNANDEZ 	match = of_match_node(mux_of_match, np);
63644993d38SGabriel FERNANDEZ 	if (!match) {
63744993d38SGabriel FERNANDEZ 		pr_err("%s: No matching data\n", __func__);
63844993d38SGabriel FERNANDEZ 		return;
63944993d38SGabriel FERNANDEZ 	}
64044993d38SGabriel FERNANDEZ 
64144993d38SGabriel FERNANDEZ 	data = (struct clkgen_mux_data *)match->data;
64244993d38SGabriel FERNANDEZ 
64344993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
64444993d38SGabriel FERNANDEZ 	if (!reg) {
64544993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get base address\n", __func__);
64644993d38SGabriel FERNANDEZ 		return;
64744993d38SGabriel FERNANDEZ 	}
64844993d38SGabriel FERNANDEZ 
64944993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
65044993d38SGabriel FERNANDEZ 	if (IS_ERR(parents)) {
65144993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get parents (%ld)\n",
65244993d38SGabriel FERNANDEZ 				__func__, PTR_ERR(parents));
65344993d38SGabriel FERNANDEZ 		return;
65444993d38SGabriel FERNANDEZ 	}
65544993d38SGabriel FERNANDEZ 
65644993d38SGabriel FERNANDEZ 	clk = clk_register_mux(NULL, np->name, parents, num_parents,
65744993d38SGabriel FERNANDEZ 				data->clk_flags | CLK_SET_RATE_PARENT,
65844993d38SGabriel FERNANDEZ 				reg + data->offset,
65944993d38SGabriel FERNANDEZ 				data->shift, data->width, data->mux_flags,
66044993d38SGabriel FERNANDEZ 				data->lock);
66144993d38SGabriel FERNANDEZ 	if (IS_ERR(clk))
66244993d38SGabriel FERNANDEZ 		goto err;
66344993d38SGabriel FERNANDEZ 
66444993d38SGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %u\n",
66544993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
66644993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
66744993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
66844993d38SGabriel FERNANDEZ 
66944993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
67044993d38SGabriel FERNANDEZ 
67144993d38SGabriel FERNANDEZ err:
67244993d38SGabriel FERNANDEZ 	kfree(parents);
67344993d38SGabriel FERNANDEZ 
67444993d38SGabriel FERNANDEZ 	return;
67544993d38SGabriel FERNANDEZ }
67644993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
67744993d38SGabriel FERNANDEZ 
67844993d38SGabriel FERNANDEZ #define VCC_MAX_CHANNELS 16
67944993d38SGabriel FERNANDEZ 
68044993d38SGabriel FERNANDEZ #define VCC_GATE_OFFSET 0x0
68144993d38SGabriel FERNANDEZ #define VCC_MUX_OFFSET 0x4
68244993d38SGabriel FERNANDEZ #define VCC_DIV_OFFSET 0x8
68344993d38SGabriel FERNANDEZ 
68444993d38SGabriel FERNANDEZ struct clkgen_vcc_data {
68544993d38SGabriel FERNANDEZ 	spinlock_t *lock;
68644993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
68744993d38SGabriel FERNANDEZ };
68844993d38SGabriel FERNANDEZ 
68944993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
69044993d38SGabriel FERNANDEZ 	.clk_flags = CLK_SET_RATE_PARENT,
69144993d38SGabriel FERNANDEZ };
69244993d38SGabriel FERNANDEZ 
69344993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
69444993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
69544993d38SGabriel FERNANDEZ };
69644993d38SGabriel FERNANDEZ 
697f375573cSFabian Frederick static const struct of_device_id vcc_of_match[] = {
69844993d38SGabriel FERNANDEZ 	{ .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
69944993d38SGabriel FERNANDEZ 	{ .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
70044993d38SGabriel FERNANDEZ 	{}
70144993d38SGabriel FERNANDEZ };
70244993d38SGabriel FERNANDEZ 
7038e6dd77cSStephen Boyd static void __init st_of_clkgen_vcc_setup(struct device_node *np)
70444993d38SGabriel FERNANDEZ {
70544993d38SGabriel FERNANDEZ 	const struct of_device_id *match;
70644993d38SGabriel FERNANDEZ 	void __iomem *reg;
70744993d38SGabriel FERNANDEZ 	const char **parents;
70844993d38SGabriel FERNANDEZ 	int num_parents, i;
70944993d38SGabriel FERNANDEZ 	struct clk_onecell_data *clk_data;
71044993d38SGabriel FERNANDEZ 	struct clkgen_vcc_data *data;
71144993d38SGabriel FERNANDEZ 
71244993d38SGabriel FERNANDEZ 	match = of_match_node(vcc_of_match, np);
71344993d38SGabriel FERNANDEZ 	if (WARN_ON(!match))
71444993d38SGabriel FERNANDEZ 		return;
71544993d38SGabriel FERNANDEZ 	data = (struct clkgen_vcc_data *)match->data;
71644993d38SGabriel FERNANDEZ 
71744993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
71844993d38SGabriel FERNANDEZ 	if (!reg)
71944993d38SGabriel FERNANDEZ 		return;
72044993d38SGabriel FERNANDEZ 
72144993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
72244993d38SGabriel FERNANDEZ 	if (IS_ERR(parents))
72344993d38SGabriel FERNANDEZ 		return;
72444993d38SGabriel FERNANDEZ 
72544993d38SGabriel FERNANDEZ 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
72644993d38SGabriel FERNANDEZ 	if (!clk_data)
72744993d38SGabriel FERNANDEZ 		goto err;
72844993d38SGabriel FERNANDEZ 
72944993d38SGabriel FERNANDEZ 	clk_data->clk_num = VCC_MAX_CHANNELS;
73044993d38SGabriel FERNANDEZ 	clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
73144993d38SGabriel FERNANDEZ 				 GFP_KERNEL);
73244993d38SGabriel FERNANDEZ 
73344993d38SGabriel FERNANDEZ 	if (!clk_data->clks)
73444993d38SGabriel FERNANDEZ 		goto err;
73544993d38SGabriel FERNANDEZ 
73644993d38SGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
73744993d38SGabriel FERNANDEZ 		struct clk *clk;
73844993d38SGabriel FERNANDEZ 		const char *clk_name;
73944993d38SGabriel FERNANDEZ 		struct clk_gate *gate;
74044993d38SGabriel FERNANDEZ 		struct clk_divider *div;
74144993d38SGabriel FERNANDEZ 		struct clk_mux *mux;
74244993d38SGabriel FERNANDEZ 
74344993d38SGabriel FERNANDEZ 		if (of_property_read_string_index(np, "clock-output-names",
74444993d38SGabriel FERNANDEZ 						  i, &clk_name))
74544993d38SGabriel FERNANDEZ 			break;
74644993d38SGabriel FERNANDEZ 
74744993d38SGabriel FERNANDEZ 		/*
74844993d38SGabriel FERNANDEZ 		 * If we read an empty clock name then the output is unused
74944993d38SGabriel FERNANDEZ 		 */
75044993d38SGabriel FERNANDEZ 		if (*clk_name == '\0')
75144993d38SGabriel FERNANDEZ 			continue;
75244993d38SGabriel FERNANDEZ 
75344993d38SGabriel FERNANDEZ 		gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
75444993d38SGabriel FERNANDEZ 		if (!gate)
75544993d38SGabriel FERNANDEZ 			break;
75644993d38SGabriel FERNANDEZ 
75744993d38SGabriel FERNANDEZ 		div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
75844993d38SGabriel FERNANDEZ 		if (!div) {
75944993d38SGabriel FERNANDEZ 			kfree(gate);
76044993d38SGabriel FERNANDEZ 			break;
76144993d38SGabriel FERNANDEZ 		}
76244993d38SGabriel FERNANDEZ 
76344993d38SGabriel FERNANDEZ 		mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
76444993d38SGabriel FERNANDEZ 		if (!mux) {
76544993d38SGabriel FERNANDEZ 			kfree(gate);
76644993d38SGabriel FERNANDEZ 			kfree(div);
76744993d38SGabriel FERNANDEZ 			break;
76844993d38SGabriel FERNANDEZ 		}
76944993d38SGabriel FERNANDEZ 
77044993d38SGabriel FERNANDEZ 		gate->reg = reg + VCC_GATE_OFFSET;
77144993d38SGabriel FERNANDEZ 		gate->bit_idx = i;
77244993d38SGabriel FERNANDEZ 		gate->flags = CLK_GATE_SET_TO_DISABLE;
77344993d38SGabriel FERNANDEZ 		gate->lock = data->lock;
77444993d38SGabriel FERNANDEZ 
77544993d38SGabriel FERNANDEZ 		div->reg = reg + VCC_DIV_OFFSET;
77644993d38SGabriel FERNANDEZ 		div->shift = 2 * i;
77744993d38SGabriel FERNANDEZ 		div->width = 2;
778eee40bb4SGabriel FERNANDEZ 		div->flags = CLK_DIVIDER_POWER_OF_TWO |
779eee40bb4SGabriel FERNANDEZ 			CLK_DIVIDER_ROUND_CLOSEST;
78044993d38SGabriel FERNANDEZ 
78144993d38SGabriel FERNANDEZ 		mux->reg = reg + VCC_MUX_OFFSET;
78244993d38SGabriel FERNANDEZ 		mux->shift = 2 * i;
78344993d38SGabriel FERNANDEZ 		mux->mask = 0x3;
78444993d38SGabriel FERNANDEZ 
78544993d38SGabriel FERNANDEZ 		clk = clk_register_composite(NULL, clk_name, parents,
78644993d38SGabriel FERNANDEZ 					     num_parents,
78744993d38SGabriel FERNANDEZ 					     &mux->hw, &clk_mux_ops,
78844993d38SGabriel FERNANDEZ 					     &div->hw, &clk_divider_ops,
78944993d38SGabriel FERNANDEZ 					     &gate->hw, &clk_gate_ops,
79018fee453SPankaj Dev 					     data->clk_flags |
79118fee453SPankaj Dev 					     CLK_GET_RATE_NOCACHE);
79244993d38SGabriel FERNANDEZ 		if (IS_ERR(clk)) {
79344993d38SGabriel FERNANDEZ 			kfree(gate);
79444993d38SGabriel FERNANDEZ 			kfree(div);
79544993d38SGabriel FERNANDEZ 			kfree(mux);
79644993d38SGabriel FERNANDEZ 			goto err;
79744993d38SGabriel FERNANDEZ 		}
79844993d38SGabriel FERNANDEZ 
79944993d38SGabriel FERNANDEZ 		pr_debug("%s: parent %s rate %u\n",
80044993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
80144993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
80244993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
80344993d38SGabriel FERNANDEZ 
80444993d38SGabriel FERNANDEZ 		clk_data->clks[i] = clk;
80544993d38SGabriel FERNANDEZ 	}
80644993d38SGabriel FERNANDEZ 
80744993d38SGabriel FERNANDEZ 	kfree(parents);
80844993d38SGabriel FERNANDEZ 
80944993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
81044993d38SGabriel FERNANDEZ 	return;
81144993d38SGabriel FERNANDEZ 
81244993d38SGabriel FERNANDEZ err:
81344993d38SGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
81444993d38SGabriel FERNANDEZ 		struct clk_composite *composite;
81544993d38SGabriel FERNANDEZ 
81644993d38SGabriel FERNANDEZ 		if (!clk_data->clks[i])
81744993d38SGabriel FERNANDEZ 			continue;
81844993d38SGabriel FERNANDEZ 
81944993d38SGabriel FERNANDEZ 		composite = container_of(__clk_get_hw(clk_data->clks[i]),
82044993d38SGabriel FERNANDEZ 					 struct clk_composite, hw);
82144993d38SGabriel FERNANDEZ 		kfree(container_of(composite->gate_hw, struct clk_gate, hw));
82244993d38SGabriel FERNANDEZ 		kfree(container_of(composite->rate_hw, struct clk_divider, hw));
82344993d38SGabriel FERNANDEZ 		kfree(container_of(composite->mux_hw, struct clk_mux, hw));
82444993d38SGabriel FERNANDEZ 	}
82544993d38SGabriel FERNANDEZ 
82644993d38SGabriel FERNANDEZ 	if (clk_data)
82744993d38SGabriel FERNANDEZ 		kfree(clk_data->clks);
82844993d38SGabriel FERNANDEZ 
82944993d38SGabriel FERNANDEZ 	kfree(clk_data);
83044993d38SGabriel FERNANDEZ 	kfree(parents);
83144993d38SGabriel FERNANDEZ }
83244993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
833