xref: /openbmc/linux/drivers/clk/st/clkgen-mux.c (revision 0b4e7f0842fe5c8bd19654999f6c41c4119e7c90)
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;
27*0b4e7f08SDinh Nguyen 	int nparents;
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 
3386665d28SStephen Boyd 	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
3494885fafSGabriel FERNANDEZ 	if (!parents)
3594885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
3694885fafSGabriel FERNANDEZ 
37*0b4e7f08SDinh Nguyen 	*num_parents = of_clk_parent_fill(np, parents, nparents);
3894885fafSGabriel FERNANDEZ 	return parents;
3994885fafSGabriel FERNANDEZ }
4094885fafSGabriel FERNANDEZ 
4194885fafSGabriel FERNANDEZ /**
4294885fafSGabriel FERNANDEZ  * DOC: Clock mux with a programmable divider on each of its three inputs.
4394885fafSGabriel FERNANDEZ  *      The mux has an input setting which effectively gates its output.
4494885fafSGabriel FERNANDEZ  *
4594885fafSGabriel FERNANDEZ  * Traits of this clock:
4694885fafSGabriel FERNANDEZ  * prepare - clk_(un)prepare only ensures parent is (un)prepared
4794885fafSGabriel FERNANDEZ  * enable - clk_enable and clk_disable are functional & control gating
4894885fafSGabriel FERNANDEZ  * rate - set rate is supported
4994885fafSGabriel FERNANDEZ  * parent - set/get parent
5094885fafSGabriel FERNANDEZ  */
5194885fafSGabriel FERNANDEZ 
5294885fafSGabriel FERNANDEZ #define NUM_INPUTS 3
5394885fafSGabriel FERNANDEZ 
5494885fafSGabriel FERNANDEZ struct clkgena_divmux {
5594885fafSGabriel FERNANDEZ 	struct clk_hw hw;
5694885fafSGabriel FERNANDEZ 	/* Subclassed mux and divider structures */
5794885fafSGabriel FERNANDEZ 	struct clk_mux mux;
5894885fafSGabriel FERNANDEZ 	struct clk_divider div[NUM_INPUTS];
5994885fafSGabriel FERNANDEZ 	/* Enable/running feedback register bits for each input */
6094885fafSGabriel FERNANDEZ 	void __iomem *feedback_reg[NUM_INPUTS];
6194885fafSGabriel FERNANDEZ 	int feedback_bit_idx;
6294885fafSGabriel FERNANDEZ 
6394885fafSGabriel FERNANDEZ 	u8              muxsel;
6494885fafSGabriel FERNANDEZ };
6594885fafSGabriel FERNANDEZ 
6694885fafSGabriel FERNANDEZ #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
6794885fafSGabriel FERNANDEZ 
6894885fafSGabriel FERNANDEZ struct clkgena_divmux_data {
6994885fafSGabriel FERNANDEZ 	int num_outputs;
7094885fafSGabriel FERNANDEZ 	int mux_offset;
7194885fafSGabriel FERNANDEZ 	int mux_offset2;
7294885fafSGabriel FERNANDEZ 	int mux_start_bit;
7394885fafSGabriel FERNANDEZ 	int div_offsets[NUM_INPUTS];
7494885fafSGabriel FERNANDEZ 	int fb_offsets[NUM_INPUTS];
7594885fafSGabriel FERNANDEZ 	int fb_start_bit_idx;
7694885fafSGabriel FERNANDEZ };
7794885fafSGabriel FERNANDEZ 
7894885fafSGabriel FERNANDEZ #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
7994885fafSGabriel FERNANDEZ 
8094885fafSGabriel FERNANDEZ static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
8194885fafSGabriel FERNANDEZ {
8294885fafSGabriel FERNANDEZ 	u32 regval = readl(mux->feedback_reg[mux->muxsel]);
8394885fafSGabriel FERNANDEZ 	u32 running = regval & BIT(mux->feedback_bit_idx);
8494885fafSGabriel FERNANDEZ 	return !!running;
8594885fafSGabriel FERNANDEZ }
8694885fafSGabriel FERNANDEZ 
8794885fafSGabriel FERNANDEZ static int clkgena_divmux_enable(struct clk_hw *hw)
8894885fafSGabriel FERNANDEZ {
8994885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
9094885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
9194885fafSGabriel FERNANDEZ 	unsigned long timeout;
9294885fafSGabriel FERNANDEZ 	int ret = 0;
9394885fafSGabriel FERNANDEZ 
944e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
9594885fafSGabriel FERNANDEZ 
9694885fafSGabriel FERNANDEZ 	ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
9794885fafSGabriel FERNANDEZ 	if (ret)
9894885fafSGabriel FERNANDEZ 		return ret;
9994885fafSGabriel FERNANDEZ 
10094885fafSGabriel FERNANDEZ 	timeout = jiffies + msecs_to_jiffies(10);
10194885fafSGabriel FERNANDEZ 
10294885fafSGabriel FERNANDEZ 	while (!clkgena_divmux_is_running(genamux)) {
10394885fafSGabriel FERNANDEZ 		if (time_after(jiffies, timeout))
10494885fafSGabriel FERNANDEZ 			return -ETIMEDOUT;
10594885fafSGabriel FERNANDEZ 		cpu_relax();
10694885fafSGabriel FERNANDEZ 	}
10794885fafSGabriel FERNANDEZ 
10894885fafSGabriel FERNANDEZ 	return 0;
10994885fafSGabriel FERNANDEZ }
11094885fafSGabriel FERNANDEZ 
11194885fafSGabriel FERNANDEZ static void clkgena_divmux_disable(struct clk_hw *hw)
11294885fafSGabriel FERNANDEZ {
11394885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
11494885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
11594885fafSGabriel FERNANDEZ 
1164e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
11794885fafSGabriel FERNANDEZ 
11894885fafSGabriel FERNANDEZ 	clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
11994885fafSGabriel FERNANDEZ }
12094885fafSGabriel FERNANDEZ 
12194885fafSGabriel FERNANDEZ static int clkgena_divmux_is_enabled(struct clk_hw *hw)
12294885fafSGabriel FERNANDEZ {
12394885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
12494885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
12594885fafSGabriel FERNANDEZ 
1264e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
12794885fafSGabriel FERNANDEZ 
12894885fafSGabriel FERNANDEZ 	return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
12994885fafSGabriel FERNANDEZ }
13094885fafSGabriel FERNANDEZ 
1318e6dd77cSStephen Boyd static u8 clkgena_divmux_get_parent(struct clk_hw *hw)
13294885fafSGabriel FERNANDEZ {
13394885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
13494885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
13594885fafSGabriel FERNANDEZ 
1364e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
13794885fafSGabriel FERNANDEZ 
13894885fafSGabriel FERNANDEZ 	genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
13994885fafSGabriel FERNANDEZ 	if ((s8)genamux->muxsel < 0) {
14094885fafSGabriel FERNANDEZ 		pr_debug("%s: %s: Invalid parent, setting to default.\n",
14194885fafSGabriel FERNANDEZ 		      __func__, __clk_get_name(hw->clk));
14294885fafSGabriel FERNANDEZ 		genamux->muxsel = 0;
14394885fafSGabriel FERNANDEZ 	}
14494885fafSGabriel FERNANDEZ 
14594885fafSGabriel FERNANDEZ 	return genamux->muxsel;
14694885fafSGabriel FERNANDEZ }
14794885fafSGabriel FERNANDEZ 
14894885fafSGabriel FERNANDEZ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
14994885fafSGabriel FERNANDEZ {
15094885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
15194885fafSGabriel FERNANDEZ 
15294885fafSGabriel FERNANDEZ 	if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
15394885fafSGabriel FERNANDEZ 		return -EINVAL;
15494885fafSGabriel FERNANDEZ 
15594885fafSGabriel FERNANDEZ 	genamux->muxsel = index;
15694885fafSGabriel FERNANDEZ 
15794885fafSGabriel FERNANDEZ 	/*
15894885fafSGabriel FERNANDEZ 	 * If the mux is already enabled, call enable directly to set the
15994885fafSGabriel FERNANDEZ 	 * new mux position and wait for it to start running again. Otherwise
16094885fafSGabriel FERNANDEZ 	 * do nothing.
16194885fafSGabriel FERNANDEZ 	 */
16294885fafSGabriel FERNANDEZ 	if (clkgena_divmux_is_enabled(hw))
16394885fafSGabriel FERNANDEZ 		clkgena_divmux_enable(hw);
16494885fafSGabriel FERNANDEZ 
16594885fafSGabriel FERNANDEZ 	return 0;
16694885fafSGabriel FERNANDEZ }
16794885fafSGabriel FERNANDEZ 
1688e6dd77cSStephen Boyd static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
16994885fafSGabriel FERNANDEZ 		unsigned long parent_rate)
17094885fafSGabriel FERNANDEZ {
17194885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
17294885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
17394885fafSGabriel FERNANDEZ 
1744e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
17594885fafSGabriel FERNANDEZ 
17694885fafSGabriel FERNANDEZ 	return clk_divider_ops.recalc_rate(div_hw, parent_rate);
17794885fafSGabriel FERNANDEZ }
17894885fafSGabriel FERNANDEZ 
17994885fafSGabriel FERNANDEZ static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
18094885fafSGabriel FERNANDEZ 				unsigned long parent_rate)
18194885fafSGabriel FERNANDEZ {
18294885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
18394885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
18494885fafSGabriel FERNANDEZ 
1854e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
18694885fafSGabriel FERNANDEZ 
18794885fafSGabriel FERNANDEZ 	return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
18894885fafSGabriel FERNANDEZ }
18994885fafSGabriel FERNANDEZ 
19094885fafSGabriel FERNANDEZ static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
19194885fafSGabriel FERNANDEZ 				   unsigned long *prate)
19294885fafSGabriel FERNANDEZ {
19394885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
19494885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
19594885fafSGabriel FERNANDEZ 
1964e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
19794885fafSGabriel FERNANDEZ 
19894885fafSGabriel FERNANDEZ 	return clk_divider_ops.round_rate(div_hw, rate, prate);
19994885fafSGabriel FERNANDEZ }
20094885fafSGabriel FERNANDEZ 
20194885fafSGabriel FERNANDEZ static const struct clk_ops clkgena_divmux_ops = {
20294885fafSGabriel FERNANDEZ 	.enable = clkgena_divmux_enable,
20394885fafSGabriel FERNANDEZ 	.disable = clkgena_divmux_disable,
20494885fafSGabriel FERNANDEZ 	.is_enabled = clkgena_divmux_is_enabled,
20594885fafSGabriel FERNANDEZ 	.get_parent = clkgena_divmux_get_parent,
20694885fafSGabriel FERNANDEZ 	.set_parent = clkgena_divmux_set_parent,
20794885fafSGabriel FERNANDEZ 	.round_rate = clkgena_divmux_round_rate,
20894885fafSGabriel FERNANDEZ 	.recalc_rate = clkgena_divmux_recalc_rate,
20994885fafSGabriel FERNANDEZ 	.set_rate = clkgena_divmux_set_rate,
21094885fafSGabriel FERNANDEZ };
21194885fafSGabriel FERNANDEZ 
21294885fafSGabriel FERNANDEZ /**
21394885fafSGabriel FERNANDEZ  * clk_register_genamux - register a genamux clock with the clock framework
21494885fafSGabriel FERNANDEZ  */
21586665d28SStephen Boyd static struct clk * __init clk_register_genamux(const char *name,
21694885fafSGabriel FERNANDEZ 				const char **parent_names, u8 num_parents,
21794885fafSGabriel FERNANDEZ 				void __iomem *reg,
21894885fafSGabriel FERNANDEZ 				const struct clkgena_divmux_data *muxdata,
21994885fafSGabriel FERNANDEZ 				u32 idx)
22094885fafSGabriel FERNANDEZ {
22194885fafSGabriel FERNANDEZ 	/*
22294885fafSGabriel FERNANDEZ 	 * Fixed constants across all ClockgenA variants
22394885fafSGabriel FERNANDEZ 	 */
22494885fafSGabriel FERNANDEZ 	const int mux_width = 2;
22594885fafSGabriel FERNANDEZ 	const int divider_width = 5;
22694885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux;
22794885fafSGabriel FERNANDEZ 	struct clk *clk;
22894885fafSGabriel FERNANDEZ 	struct clk_init_data init;
22994885fafSGabriel FERNANDEZ 	int i;
23094885fafSGabriel FERNANDEZ 
23194885fafSGabriel FERNANDEZ 	genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
23294885fafSGabriel FERNANDEZ 	if (!genamux)
23394885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
23494885fafSGabriel FERNANDEZ 
23594885fafSGabriel FERNANDEZ 	init.name = name;
23694885fafSGabriel FERNANDEZ 	init.ops = &clkgena_divmux_ops;
23718fee453SPankaj Dev 	init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE;
23894885fafSGabriel FERNANDEZ 	init.parent_names = parent_names;
23994885fafSGabriel FERNANDEZ 	init.num_parents = num_parents;
24094885fafSGabriel FERNANDEZ 
24194885fafSGabriel FERNANDEZ 	genamux->mux.lock  = &clkgena_divmux_lock;
24294885fafSGabriel FERNANDEZ 	genamux->mux.mask = BIT(mux_width) - 1;
24394885fafSGabriel FERNANDEZ 	genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
24494885fafSGabriel FERNANDEZ 	if (genamux->mux.shift > 31) {
24594885fafSGabriel FERNANDEZ 		/*
24694885fafSGabriel FERNANDEZ 		 * We have spilled into the second mux register so
24794885fafSGabriel FERNANDEZ 		 * adjust the register address and the bit shift accordingly
24894885fafSGabriel FERNANDEZ 		 */
24994885fafSGabriel FERNANDEZ 		genamux->mux.reg = reg + muxdata->mux_offset2;
25094885fafSGabriel FERNANDEZ 		genamux->mux.shift -= 32;
25194885fafSGabriel FERNANDEZ 	} else {
25294885fafSGabriel FERNANDEZ 		genamux->mux.reg   = reg + muxdata->mux_offset;
25394885fafSGabriel FERNANDEZ 	}
25494885fafSGabriel FERNANDEZ 
25594885fafSGabriel FERNANDEZ 	for (i = 0; i < NUM_INPUTS; i++) {
25694885fafSGabriel FERNANDEZ 		/*
25794885fafSGabriel FERNANDEZ 		 * Divider config for each input
25894885fafSGabriel FERNANDEZ 		 */
25994885fafSGabriel FERNANDEZ 		void __iomem *divbase = reg + muxdata->div_offsets[i];
26094885fafSGabriel FERNANDEZ 		genamux->div[i].width = divider_width;
26194885fafSGabriel FERNANDEZ 		genamux->div[i].reg = divbase + (idx * sizeof(u32));
26294885fafSGabriel FERNANDEZ 
26394885fafSGabriel FERNANDEZ 		/*
26494885fafSGabriel FERNANDEZ 		 * Mux enabled/running feedback register for each input.
26594885fafSGabriel FERNANDEZ 		 */
26694885fafSGabriel FERNANDEZ 		genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
26794885fafSGabriel FERNANDEZ 	}
26894885fafSGabriel FERNANDEZ 
26994885fafSGabriel FERNANDEZ 	genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
27094885fafSGabriel FERNANDEZ 	genamux->hw.init = &init;
27194885fafSGabriel FERNANDEZ 
27294885fafSGabriel FERNANDEZ 	clk = clk_register(NULL, &genamux->hw);
27394885fafSGabriel FERNANDEZ 	if (IS_ERR(clk)) {
27494885fafSGabriel FERNANDEZ 		kfree(genamux);
27594885fafSGabriel FERNANDEZ 		goto err;
27694885fafSGabriel FERNANDEZ 	}
27794885fafSGabriel FERNANDEZ 
27894885fafSGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %lu\n",
27994885fafSGabriel FERNANDEZ 			__clk_get_name(clk),
28094885fafSGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
28194885fafSGabriel FERNANDEZ 			clk_get_rate(clk));
28294885fafSGabriel FERNANDEZ err:
28394885fafSGabriel FERNANDEZ 	return clk;
28494885fafSGabriel FERNANDEZ }
28594885fafSGabriel FERNANDEZ 
28694885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65hs = {
28794885fafSGabriel FERNANDEZ 	.num_outputs = 4,
28894885fafSGabriel FERNANDEZ 	.mux_offset = 0x14,
28994885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
29094885fafSGabriel FERNANDEZ 	.div_offsets = { 0x800, 0x900, 0xb00 },
29194885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x18, 0x1c, 0x20 },
29294885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 0,
29394885fafSGabriel FERNANDEZ };
29494885fafSGabriel FERNANDEZ 
29594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65ls = {
29694885fafSGabriel FERNANDEZ 	.num_outputs = 14,
29794885fafSGabriel FERNANDEZ 	.mux_offset = 0x14,
29894885fafSGabriel FERNANDEZ 	.mux_offset2 = 0x24,
29994885fafSGabriel FERNANDEZ 	.mux_start_bit = 8,
30094885fafSGabriel FERNANDEZ 	.div_offsets = { 0x810, 0xa10, 0xb10 },
30194885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x18, 0x1c, 0x20 },
30294885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 4,
30394885fafSGabriel FERNANDEZ };
30494885fafSGabriel FERNANDEZ 
30594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf0 = {
30694885fafSGabriel FERNANDEZ 	.num_outputs = 8,
30794885fafSGabriel FERNANDEZ 	.mux_offset = 0x1c,
30894885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
30994885fafSGabriel FERNANDEZ 	.div_offsets = { 0x800, 0x900, 0xa60 },
31094885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
31194885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 0,
31294885fafSGabriel FERNANDEZ };
31394885fafSGabriel FERNANDEZ 
31494885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf1 = {
31594885fafSGabriel FERNANDEZ 	.num_outputs = 8,
31694885fafSGabriel FERNANDEZ 	.mux_offset = 0x1c,
31794885fafSGabriel FERNANDEZ 	.mux_start_bit = 16,
31894885fafSGabriel FERNANDEZ 	.div_offsets = { 0x820, 0x980, 0xa80 },
31994885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
32094885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 8,
32194885fafSGabriel FERNANDEZ };
32294885fafSGabriel FERNANDEZ 
32394885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf2 = {
32494885fafSGabriel FERNANDEZ 	.num_outputs = 8,
32594885fafSGabriel FERNANDEZ 	.mux_offset = 0x20,
32694885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
32794885fafSGabriel FERNANDEZ 	.div_offsets = { 0x840, 0xa20, 0xb10 },
32894885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
32994885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 16,
33094885fafSGabriel FERNANDEZ };
33194885fafSGabriel FERNANDEZ 
33294885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf3 = {
33394885fafSGabriel FERNANDEZ 	.num_outputs = 8,
33494885fafSGabriel FERNANDEZ 	.mux_offset = 0x20,
33594885fafSGabriel FERNANDEZ 	.mux_start_bit = 16,
33694885fafSGabriel FERNANDEZ 	.div_offsets = { 0x860, 0xa40, 0xb30 },
33794885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
33894885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 24,
33994885fafSGabriel FERNANDEZ };
34094885fafSGabriel FERNANDEZ 
341f375573cSFabian Frederick static const struct of_device_id clkgena_divmux_of_match[] = {
34294885fafSGabriel FERNANDEZ 	{
34394885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c65-hs",
34494885fafSGabriel FERNANDEZ 		.data = &st_divmux_c65hs,
34594885fafSGabriel FERNANDEZ 	},
34694885fafSGabriel FERNANDEZ 	{
34794885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c65-ls",
34894885fafSGabriel FERNANDEZ 		.data = &st_divmux_c65ls,
34994885fafSGabriel FERNANDEZ 	},
35094885fafSGabriel FERNANDEZ 	{
35194885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf0",
35294885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf0,
35394885fafSGabriel FERNANDEZ 	},
35494885fafSGabriel FERNANDEZ 	{
35594885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf1",
35694885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf1,
35794885fafSGabriel FERNANDEZ 	},
35894885fafSGabriel FERNANDEZ 	{
35994885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf2",
36094885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf2,
36194885fafSGabriel FERNANDEZ 	},
36294885fafSGabriel FERNANDEZ 	{
36394885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf3",
36494885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf3,
36594885fafSGabriel FERNANDEZ 	},
36694885fafSGabriel FERNANDEZ 	{}
36794885fafSGabriel FERNANDEZ };
36894885fafSGabriel FERNANDEZ 
36986665d28SStephen Boyd static void __iomem * __init clkgen_get_register_base(struct device_node *np)
37094885fafSGabriel FERNANDEZ {
37194885fafSGabriel FERNANDEZ 	struct device_node *pnode;
37286665d28SStephen Boyd 	void __iomem *reg;
37394885fafSGabriel FERNANDEZ 
37494885fafSGabriel FERNANDEZ 	pnode = of_get_parent(np);
37594885fafSGabriel FERNANDEZ 	if (!pnode)
37694885fafSGabriel FERNANDEZ 		return NULL;
37794885fafSGabriel FERNANDEZ 
37894885fafSGabriel FERNANDEZ 	reg = of_iomap(pnode, 0);
37994885fafSGabriel FERNANDEZ 
38094885fafSGabriel FERNANDEZ 	of_node_put(pnode);
38194885fafSGabriel FERNANDEZ 	return reg;
38294885fafSGabriel FERNANDEZ }
38394885fafSGabriel FERNANDEZ 
3848e6dd77cSStephen Boyd static void __init st_of_clkgena_divmux_setup(struct device_node *np)
38594885fafSGabriel FERNANDEZ {
38694885fafSGabriel FERNANDEZ 	const struct of_device_id *match;
38794885fafSGabriel FERNANDEZ 	const struct clkgena_divmux_data *data;
38894885fafSGabriel FERNANDEZ 	struct clk_onecell_data *clk_data;
38994885fafSGabriel FERNANDEZ 	void __iomem *reg;
39094885fafSGabriel FERNANDEZ 	const char **parents;
39194885fafSGabriel FERNANDEZ 	int num_parents = 0, i;
39294885fafSGabriel FERNANDEZ 
39394885fafSGabriel FERNANDEZ 	match = of_match_node(clkgena_divmux_of_match, np);
39494885fafSGabriel FERNANDEZ 	if (WARN_ON(!match))
39594885fafSGabriel FERNANDEZ 		return;
39694885fafSGabriel FERNANDEZ 
39786665d28SStephen Boyd 	data = match->data;
39894885fafSGabriel FERNANDEZ 
39994885fafSGabriel FERNANDEZ 	reg = clkgen_get_register_base(np);
40094885fafSGabriel FERNANDEZ 	if (!reg)
40194885fafSGabriel FERNANDEZ 		return;
40294885fafSGabriel FERNANDEZ 
40394885fafSGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
40494885fafSGabriel FERNANDEZ 	if (IS_ERR(parents))
40586665d28SStephen Boyd 		goto err_parents;
40694885fafSGabriel FERNANDEZ 
40794885fafSGabriel FERNANDEZ 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
40894885fafSGabriel FERNANDEZ 	if (!clk_data)
40986665d28SStephen Boyd 		goto err_alloc;
41094885fafSGabriel FERNANDEZ 
41194885fafSGabriel FERNANDEZ 	clk_data->clk_num = data->num_outputs;
41286665d28SStephen Boyd 	clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
41394885fafSGabriel FERNANDEZ 				 GFP_KERNEL);
41494885fafSGabriel FERNANDEZ 
41594885fafSGabriel FERNANDEZ 	if (!clk_data->clks)
41686665d28SStephen Boyd 		goto err_alloc_clks;
41794885fafSGabriel FERNANDEZ 
41894885fafSGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
41994885fafSGabriel FERNANDEZ 		struct clk *clk;
42094885fafSGabriel FERNANDEZ 		const char *clk_name;
42194885fafSGabriel FERNANDEZ 
42294885fafSGabriel FERNANDEZ 		if (of_property_read_string_index(np, "clock-output-names",
42394885fafSGabriel FERNANDEZ 						  i, &clk_name))
42494885fafSGabriel FERNANDEZ 			break;
42594885fafSGabriel FERNANDEZ 
42694885fafSGabriel FERNANDEZ 		/*
42794885fafSGabriel FERNANDEZ 		 * If we read an empty clock name then the output is unused
42894885fafSGabriel FERNANDEZ 		 */
42994885fafSGabriel FERNANDEZ 		if (*clk_name == '\0')
43094885fafSGabriel FERNANDEZ 			continue;
43194885fafSGabriel FERNANDEZ 
43294885fafSGabriel FERNANDEZ 		clk = clk_register_genamux(clk_name, parents, num_parents,
43394885fafSGabriel FERNANDEZ 					   reg, data, i);
43494885fafSGabriel FERNANDEZ 
43594885fafSGabriel FERNANDEZ 		if (IS_ERR(clk))
43694885fafSGabriel FERNANDEZ 			goto err;
43794885fafSGabriel FERNANDEZ 
43894885fafSGabriel FERNANDEZ 		clk_data->clks[i] = clk;
43994885fafSGabriel FERNANDEZ 	}
44094885fafSGabriel FERNANDEZ 
44194885fafSGabriel FERNANDEZ 	kfree(parents);
44294885fafSGabriel FERNANDEZ 
44394885fafSGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
44494885fafSGabriel FERNANDEZ 	return;
44594885fafSGabriel FERNANDEZ err:
44694885fafSGabriel FERNANDEZ 	kfree(clk_data->clks);
44786665d28SStephen Boyd err_alloc_clks:
44894885fafSGabriel FERNANDEZ 	kfree(clk_data);
44986665d28SStephen Boyd err_alloc:
45094885fafSGabriel FERNANDEZ 	kfree(parents);
45186665d28SStephen Boyd err_parents:
45286665d28SStephen Boyd 	iounmap(reg);
45394885fafSGabriel FERNANDEZ }
45494885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
45594885fafSGabriel FERNANDEZ 
45694885fafSGabriel FERNANDEZ struct clkgena_prediv_data {
45794885fafSGabriel FERNANDEZ 	u32 offset;
45894885fafSGabriel FERNANDEZ 	u8 shift;
45994885fafSGabriel FERNANDEZ 	struct clk_div_table *table;
46094885fafSGabriel FERNANDEZ };
46194885fafSGabriel FERNANDEZ 
46294885fafSGabriel FERNANDEZ static struct clk_div_table prediv_table16[] = {
46394885fafSGabriel FERNANDEZ 	{ .val = 0, .div = 1 },
46494885fafSGabriel FERNANDEZ 	{ .val = 1, .div = 16 },
46594885fafSGabriel FERNANDEZ 	{ .div = 0 },
46694885fafSGabriel FERNANDEZ };
46794885fafSGabriel FERNANDEZ 
46894885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c65_data = {
46994885fafSGabriel FERNANDEZ 	.offset = 0x4c,
47094885fafSGabriel FERNANDEZ 	.shift = 31,
47194885fafSGabriel FERNANDEZ 	.table = prediv_table16,
47294885fafSGabriel FERNANDEZ };
47394885fafSGabriel FERNANDEZ 
47494885fafSGabriel FERNANDEZ static struct clkgena_prediv_data prediv_c32_data = {
47594885fafSGabriel FERNANDEZ 	.offset = 0x50,
47694885fafSGabriel FERNANDEZ 	.shift = 1,
47794885fafSGabriel FERNANDEZ 	.table = prediv_table16,
47894885fafSGabriel FERNANDEZ };
47994885fafSGabriel FERNANDEZ 
480f375573cSFabian Frederick static const struct of_device_id clkgena_prediv_of_match[] = {
48194885fafSGabriel FERNANDEZ 	{ .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
48294885fafSGabriel FERNANDEZ 	{ .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
48394885fafSGabriel FERNANDEZ 	{}
48494885fafSGabriel FERNANDEZ };
48594885fafSGabriel FERNANDEZ 
4868e6dd77cSStephen Boyd static void __init st_of_clkgena_prediv_setup(struct device_node *np)
48794885fafSGabriel FERNANDEZ {
48894885fafSGabriel FERNANDEZ 	const struct of_device_id *match;
48994885fafSGabriel FERNANDEZ 	void __iomem *reg;
49094885fafSGabriel FERNANDEZ 	const char *parent_name, *clk_name;
49194885fafSGabriel FERNANDEZ 	struct clk *clk;
49286665d28SStephen Boyd 	const struct clkgena_prediv_data *data;
49394885fafSGabriel FERNANDEZ 
49494885fafSGabriel FERNANDEZ 	match = of_match_node(clkgena_prediv_of_match, np);
49594885fafSGabriel FERNANDEZ 	if (!match) {
49694885fafSGabriel FERNANDEZ 		pr_err("%s: No matching data\n", __func__);
49794885fafSGabriel FERNANDEZ 		return;
49894885fafSGabriel FERNANDEZ 	}
49994885fafSGabriel FERNANDEZ 
50086665d28SStephen Boyd 	data = match->data;
50194885fafSGabriel FERNANDEZ 
50294885fafSGabriel FERNANDEZ 	reg = clkgen_get_register_base(np);
50394885fafSGabriel FERNANDEZ 	if (!reg)
50494885fafSGabriel FERNANDEZ 		return;
50594885fafSGabriel FERNANDEZ 
50694885fafSGabriel FERNANDEZ 	parent_name = of_clk_get_parent_name(np, 0);
50794885fafSGabriel FERNANDEZ 	if (!parent_name)
50886665d28SStephen Boyd 		goto err;
50994885fafSGabriel FERNANDEZ 
51094885fafSGabriel FERNANDEZ 	if (of_property_read_string_index(np, "clock-output-names",
51194885fafSGabriel FERNANDEZ 					  0, &clk_name))
51286665d28SStephen Boyd 		goto err;
51394885fafSGabriel FERNANDEZ 
51418fee453SPankaj Dev 	clk = clk_register_divider_table(NULL, clk_name, parent_name,
51518fee453SPankaj Dev 					 CLK_GET_RATE_NOCACHE,
51694885fafSGabriel FERNANDEZ 					 reg + data->offset, data->shift, 1,
51794885fafSGabriel FERNANDEZ 					 0, data->table, NULL);
51894885fafSGabriel FERNANDEZ 	if (IS_ERR(clk))
51986665d28SStephen Boyd 		goto err;
52094885fafSGabriel FERNANDEZ 
52194885fafSGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
52294885fafSGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %u\n",
52394885fafSGabriel FERNANDEZ 		__clk_get_name(clk),
52494885fafSGabriel FERNANDEZ 		__clk_get_name(clk_get_parent(clk)),
52594885fafSGabriel FERNANDEZ 		(unsigned int)clk_get_rate(clk));
52694885fafSGabriel FERNANDEZ 
52794885fafSGabriel FERNANDEZ 	return;
52886665d28SStephen Boyd err:
52986665d28SStephen Boyd 	iounmap(reg);
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,
5863be6d8ceSGabriel 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;
63386665d28SStephen Boyd 	const 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 
64186665d28SStephen Boyd 	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));
65386665d28SStephen Boyd 		goto err_parents;
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 
66986665d28SStephen Boyd 	kfree(parents);
67044993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
67186665d28SStephen Boyd 	return;
67244993d38SGabriel FERNANDEZ 
67344993d38SGabriel FERNANDEZ err:
67444993d38SGabriel FERNANDEZ 	kfree(parents);
67586665d28SStephen Boyd err_parents:
67686665d28SStephen Boyd 	iounmap(reg);
67744993d38SGabriel FERNANDEZ }
67844993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
67944993d38SGabriel FERNANDEZ 
68044993d38SGabriel FERNANDEZ #define VCC_MAX_CHANNELS 16
68144993d38SGabriel FERNANDEZ 
68244993d38SGabriel FERNANDEZ #define VCC_GATE_OFFSET 0x0
68344993d38SGabriel FERNANDEZ #define VCC_MUX_OFFSET 0x4
68444993d38SGabriel FERNANDEZ #define VCC_DIV_OFFSET 0x8
68544993d38SGabriel FERNANDEZ 
68644993d38SGabriel FERNANDEZ struct clkgen_vcc_data {
68744993d38SGabriel FERNANDEZ 	spinlock_t *lock;
68844993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
68944993d38SGabriel FERNANDEZ };
69044993d38SGabriel FERNANDEZ 
69144993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
69244993d38SGabriel FERNANDEZ 	.clk_flags = CLK_SET_RATE_PARENT,
69344993d38SGabriel FERNANDEZ };
69444993d38SGabriel FERNANDEZ 
69544993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
69644993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
69744993d38SGabriel FERNANDEZ };
69844993d38SGabriel FERNANDEZ 
699f375573cSFabian Frederick static const struct of_device_id vcc_of_match[] = {
70044993d38SGabriel FERNANDEZ 	{ .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
70144993d38SGabriel FERNANDEZ 	{ .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
70244993d38SGabriel FERNANDEZ 	{}
70344993d38SGabriel FERNANDEZ };
70444993d38SGabriel FERNANDEZ 
7058e6dd77cSStephen Boyd static void __init st_of_clkgen_vcc_setup(struct device_node *np)
70644993d38SGabriel FERNANDEZ {
70744993d38SGabriel FERNANDEZ 	const struct of_device_id *match;
70844993d38SGabriel FERNANDEZ 	void __iomem *reg;
70944993d38SGabriel FERNANDEZ 	const char **parents;
71044993d38SGabriel FERNANDEZ 	int num_parents, i;
71144993d38SGabriel FERNANDEZ 	struct clk_onecell_data *clk_data;
71286665d28SStephen Boyd 	const struct clkgen_vcc_data *data;
71344993d38SGabriel FERNANDEZ 
71444993d38SGabriel FERNANDEZ 	match = of_match_node(vcc_of_match, np);
71544993d38SGabriel FERNANDEZ 	if (WARN_ON(!match))
71644993d38SGabriel FERNANDEZ 		return;
71786665d28SStephen Boyd 	data = match->data;
71844993d38SGabriel FERNANDEZ 
71944993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
72044993d38SGabriel FERNANDEZ 	if (!reg)
72144993d38SGabriel FERNANDEZ 		return;
72244993d38SGabriel FERNANDEZ 
72344993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
72444993d38SGabriel FERNANDEZ 	if (IS_ERR(parents))
72586665d28SStephen Boyd 		goto err_parents;
72644993d38SGabriel FERNANDEZ 
72744993d38SGabriel FERNANDEZ 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
72844993d38SGabriel FERNANDEZ 	if (!clk_data)
72986665d28SStephen Boyd 		goto err_alloc;
73044993d38SGabriel FERNANDEZ 
73144993d38SGabriel FERNANDEZ 	clk_data->clk_num = VCC_MAX_CHANNELS;
73286665d28SStephen Boyd 	clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
73344993d38SGabriel FERNANDEZ 				 GFP_KERNEL);
73444993d38SGabriel FERNANDEZ 
73544993d38SGabriel FERNANDEZ 	if (!clk_data->clks)
73686665d28SStephen Boyd 		goto err_alloc_clks;
73744993d38SGabriel FERNANDEZ 
73844993d38SGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
73944993d38SGabriel FERNANDEZ 		struct clk *clk;
74044993d38SGabriel FERNANDEZ 		const char *clk_name;
74144993d38SGabriel FERNANDEZ 		struct clk_gate *gate;
74244993d38SGabriel FERNANDEZ 		struct clk_divider *div;
74344993d38SGabriel FERNANDEZ 		struct clk_mux *mux;
74444993d38SGabriel FERNANDEZ 
74544993d38SGabriel FERNANDEZ 		if (of_property_read_string_index(np, "clock-output-names",
74644993d38SGabriel FERNANDEZ 						  i, &clk_name))
74744993d38SGabriel FERNANDEZ 			break;
74844993d38SGabriel FERNANDEZ 
74944993d38SGabriel FERNANDEZ 		/*
75044993d38SGabriel FERNANDEZ 		 * If we read an empty clock name then the output is unused
75144993d38SGabriel FERNANDEZ 		 */
75244993d38SGabriel FERNANDEZ 		if (*clk_name == '\0')
75344993d38SGabriel FERNANDEZ 			continue;
75444993d38SGabriel FERNANDEZ 
75586665d28SStephen Boyd 		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
75644993d38SGabriel FERNANDEZ 		if (!gate)
75786665d28SStephen Boyd 			goto err;
75844993d38SGabriel FERNANDEZ 
75986665d28SStephen Boyd 		div = kzalloc(sizeof(*div), GFP_KERNEL);
76044993d38SGabriel FERNANDEZ 		if (!div) {
76144993d38SGabriel FERNANDEZ 			kfree(gate);
76286665d28SStephen Boyd 			goto err;
76344993d38SGabriel FERNANDEZ 		}
76444993d38SGabriel FERNANDEZ 
76586665d28SStephen Boyd 		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
76644993d38SGabriel FERNANDEZ 		if (!mux) {
76744993d38SGabriel FERNANDEZ 			kfree(gate);
76844993d38SGabriel FERNANDEZ 			kfree(div);
76986665d28SStephen Boyd 			goto err;
77044993d38SGabriel FERNANDEZ 		}
77144993d38SGabriel FERNANDEZ 
77244993d38SGabriel FERNANDEZ 		gate->reg = reg + VCC_GATE_OFFSET;
77344993d38SGabriel FERNANDEZ 		gate->bit_idx = i;
77444993d38SGabriel FERNANDEZ 		gate->flags = CLK_GATE_SET_TO_DISABLE;
77544993d38SGabriel FERNANDEZ 		gate->lock = data->lock;
77644993d38SGabriel FERNANDEZ 
77744993d38SGabriel FERNANDEZ 		div->reg = reg + VCC_DIV_OFFSET;
77844993d38SGabriel FERNANDEZ 		div->shift = 2 * i;
77944993d38SGabriel FERNANDEZ 		div->width = 2;
780eee40bb4SGabriel FERNANDEZ 		div->flags = CLK_DIVIDER_POWER_OF_TWO |
781eee40bb4SGabriel FERNANDEZ 			CLK_DIVIDER_ROUND_CLOSEST;
78244993d38SGabriel FERNANDEZ 
78344993d38SGabriel FERNANDEZ 		mux->reg = reg + VCC_MUX_OFFSET;
78444993d38SGabriel FERNANDEZ 		mux->shift = 2 * i;
78544993d38SGabriel FERNANDEZ 		mux->mask = 0x3;
78644993d38SGabriel FERNANDEZ 
78744993d38SGabriel FERNANDEZ 		clk = clk_register_composite(NULL, clk_name, parents,
78844993d38SGabriel FERNANDEZ 					     num_parents,
78944993d38SGabriel FERNANDEZ 					     &mux->hw, &clk_mux_ops,
79044993d38SGabriel FERNANDEZ 					     &div->hw, &clk_divider_ops,
79144993d38SGabriel FERNANDEZ 					     &gate->hw, &clk_gate_ops,
79218fee453SPankaj Dev 					     data->clk_flags |
79318fee453SPankaj Dev 					     CLK_GET_RATE_NOCACHE);
79444993d38SGabriel FERNANDEZ 		if (IS_ERR(clk)) {
79544993d38SGabriel FERNANDEZ 			kfree(gate);
79644993d38SGabriel FERNANDEZ 			kfree(div);
79744993d38SGabriel FERNANDEZ 			kfree(mux);
79844993d38SGabriel FERNANDEZ 			goto err;
79944993d38SGabriel FERNANDEZ 		}
80044993d38SGabriel FERNANDEZ 
80144993d38SGabriel FERNANDEZ 		pr_debug("%s: parent %s rate %u\n",
80244993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
80344993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
80444993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
80544993d38SGabriel FERNANDEZ 
80644993d38SGabriel FERNANDEZ 		clk_data->clks[i] = clk;
80744993d38SGabriel FERNANDEZ 	}
80844993d38SGabriel FERNANDEZ 
80944993d38SGabriel FERNANDEZ 	kfree(parents);
81044993d38SGabriel FERNANDEZ 
81144993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
81244993d38SGabriel FERNANDEZ 	return;
81344993d38SGabriel FERNANDEZ 
81444993d38SGabriel FERNANDEZ err:
81544993d38SGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
81644993d38SGabriel FERNANDEZ 		struct clk_composite *composite;
81744993d38SGabriel FERNANDEZ 
81844993d38SGabriel FERNANDEZ 		if (!clk_data->clks[i])
81944993d38SGabriel FERNANDEZ 			continue;
82044993d38SGabriel FERNANDEZ 
82144993d38SGabriel FERNANDEZ 		composite = container_of(__clk_get_hw(clk_data->clks[i]),
82244993d38SGabriel FERNANDEZ 					 struct clk_composite, hw);
82344993d38SGabriel FERNANDEZ 		kfree(container_of(composite->gate_hw, struct clk_gate, hw));
82444993d38SGabriel FERNANDEZ 		kfree(container_of(composite->rate_hw, struct clk_divider, hw));
82544993d38SGabriel FERNANDEZ 		kfree(container_of(composite->mux_hw, struct clk_mux, hw));
82644993d38SGabriel FERNANDEZ 	}
82744993d38SGabriel FERNANDEZ 
82844993d38SGabriel FERNANDEZ 	kfree(clk_data->clks);
82986665d28SStephen Boyd err_alloc_clks:
83044993d38SGabriel FERNANDEZ 	kfree(clk_data);
83186665d28SStephen Boyd err_alloc:
83244993d38SGabriel FERNANDEZ 	kfree(parents);
83386665d28SStephen Boyd err_parents:
83486665d28SStephen Boyd 	iounmap(reg);
83544993d38SGabriel FERNANDEZ }
83644993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
837