xref: /openbmc/linux/drivers/clk/st/clkgen-mux.c (revision caeb057cd30412b85e69f663a43f55a29666c79b)
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>
18d5f728acSStephen Boyd #include <linux/clk.h>
1994885fafSGabriel FERNANDEZ #include <linux/clk-provider.h>
2046a57afdSGabriel Fernandez #include "clkgen.h"
2194885fafSGabriel FERNANDEZ 
2294885fafSGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgena_divmux_lock);
2344993d38SGabriel FERNANDEZ static DEFINE_SPINLOCK(clkgenf_lock);
2494885fafSGabriel FERNANDEZ 
2594885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np,
2694885fafSGabriel FERNANDEZ 						       int *num_parents)
2794885fafSGabriel FERNANDEZ {
2894885fafSGabriel FERNANDEZ 	const char **parents;
29*caeb057cSStephen Boyd 	unsigned int nparents;
3094885fafSGabriel FERNANDEZ 
310a65239cSGeert Uytterhoeven 	nparents = of_clk_get_parent_count(np);
32*caeb057cSStephen Boyd 	if (WARN_ON(!nparents))
3394885fafSGabriel FERNANDEZ 		return ERR_PTR(-EINVAL);
3494885fafSGabriel FERNANDEZ 
3586665d28SStephen Boyd 	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
3694885fafSGabriel FERNANDEZ 	if (!parents)
3794885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
3894885fafSGabriel FERNANDEZ 
390b4e7f08SDinh Nguyen 	*num_parents = of_clk_parent_fill(np, parents, nparents);
4094885fafSGabriel FERNANDEZ 	return parents;
4194885fafSGabriel FERNANDEZ }
4294885fafSGabriel FERNANDEZ 
4394885fafSGabriel FERNANDEZ /**
4494885fafSGabriel FERNANDEZ  * DOC: Clock mux with a programmable divider on each of its three inputs.
4594885fafSGabriel FERNANDEZ  *      The mux has an input setting which effectively gates its output.
4694885fafSGabriel FERNANDEZ  *
4794885fafSGabriel FERNANDEZ  * Traits of this clock:
4894885fafSGabriel FERNANDEZ  * prepare - clk_(un)prepare only ensures parent is (un)prepared
4994885fafSGabriel FERNANDEZ  * enable - clk_enable and clk_disable are functional & control gating
5094885fafSGabriel FERNANDEZ  * rate - set rate is supported
5194885fafSGabriel FERNANDEZ  * parent - set/get parent
5294885fafSGabriel FERNANDEZ  */
5394885fafSGabriel FERNANDEZ 
5494885fafSGabriel FERNANDEZ #define NUM_INPUTS 3
5594885fafSGabriel FERNANDEZ 
5694885fafSGabriel FERNANDEZ struct clkgena_divmux {
5794885fafSGabriel FERNANDEZ 	struct clk_hw hw;
5894885fafSGabriel FERNANDEZ 	/* Subclassed mux and divider structures */
5994885fafSGabriel FERNANDEZ 	struct clk_mux mux;
6094885fafSGabriel FERNANDEZ 	struct clk_divider div[NUM_INPUTS];
6194885fafSGabriel FERNANDEZ 	/* Enable/running feedback register bits for each input */
6294885fafSGabriel FERNANDEZ 	void __iomem *feedback_reg[NUM_INPUTS];
6394885fafSGabriel FERNANDEZ 	int feedback_bit_idx;
6494885fafSGabriel FERNANDEZ 
6594885fafSGabriel FERNANDEZ 	u8              muxsel;
6694885fafSGabriel FERNANDEZ };
6794885fafSGabriel FERNANDEZ 
6894885fafSGabriel FERNANDEZ #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
6994885fafSGabriel FERNANDEZ 
7094885fafSGabriel FERNANDEZ struct clkgena_divmux_data {
7194885fafSGabriel FERNANDEZ 	int num_outputs;
7294885fafSGabriel FERNANDEZ 	int mux_offset;
7394885fafSGabriel FERNANDEZ 	int mux_offset2;
7494885fafSGabriel FERNANDEZ 	int mux_start_bit;
7594885fafSGabriel FERNANDEZ 	int div_offsets[NUM_INPUTS];
7694885fafSGabriel FERNANDEZ 	int fb_offsets[NUM_INPUTS];
7794885fafSGabriel FERNANDEZ 	int fb_start_bit_idx;
7894885fafSGabriel FERNANDEZ };
7994885fafSGabriel FERNANDEZ 
8094885fafSGabriel FERNANDEZ #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
8194885fafSGabriel FERNANDEZ 
8294885fafSGabriel FERNANDEZ static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
8394885fafSGabriel FERNANDEZ {
8494885fafSGabriel FERNANDEZ 	u32 regval = readl(mux->feedback_reg[mux->muxsel]);
8594885fafSGabriel FERNANDEZ 	u32 running = regval & BIT(mux->feedback_bit_idx);
8694885fafSGabriel FERNANDEZ 	return !!running;
8794885fafSGabriel FERNANDEZ }
8894885fafSGabriel FERNANDEZ 
8994885fafSGabriel FERNANDEZ static int clkgena_divmux_enable(struct clk_hw *hw)
9094885fafSGabriel FERNANDEZ {
9194885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
9294885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
9394885fafSGabriel FERNANDEZ 	unsigned long timeout;
9494885fafSGabriel FERNANDEZ 	int ret = 0;
9594885fafSGabriel FERNANDEZ 
964e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
9794885fafSGabriel FERNANDEZ 
9894885fafSGabriel FERNANDEZ 	ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
9994885fafSGabriel FERNANDEZ 	if (ret)
10094885fafSGabriel FERNANDEZ 		return ret;
10194885fafSGabriel FERNANDEZ 
10294885fafSGabriel FERNANDEZ 	timeout = jiffies + msecs_to_jiffies(10);
10394885fafSGabriel FERNANDEZ 
10494885fafSGabriel FERNANDEZ 	while (!clkgena_divmux_is_running(genamux)) {
10594885fafSGabriel FERNANDEZ 		if (time_after(jiffies, timeout))
10694885fafSGabriel FERNANDEZ 			return -ETIMEDOUT;
10794885fafSGabriel FERNANDEZ 		cpu_relax();
10894885fafSGabriel FERNANDEZ 	}
10994885fafSGabriel FERNANDEZ 
11094885fafSGabriel FERNANDEZ 	return 0;
11194885fafSGabriel FERNANDEZ }
11294885fafSGabriel FERNANDEZ 
11394885fafSGabriel FERNANDEZ static void clkgena_divmux_disable(struct clk_hw *hw)
11494885fafSGabriel FERNANDEZ {
11594885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
11694885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
11794885fafSGabriel FERNANDEZ 
1184e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
11994885fafSGabriel FERNANDEZ 
12094885fafSGabriel FERNANDEZ 	clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
12194885fafSGabriel FERNANDEZ }
12294885fafSGabriel FERNANDEZ 
12394885fafSGabriel FERNANDEZ static int clkgena_divmux_is_enabled(struct clk_hw *hw)
12494885fafSGabriel FERNANDEZ {
12594885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
12694885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
12794885fafSGabriel FERNANDEZ 
1284e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
12994885fafSGabriel FERNANDEZ 
13094885fafSGabriel FERNANDEZ 	return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
13194885fafSGabriel FERNANDEZ }
13294885fafSGabriel FERNANDEZ 
1338e6dd77cSStephen Boyd static u8 clkgena_divmux_get_parent(struct clk_hw *hw)
13494885fafSGabriel FERNANDEZ {
13594885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
13694885fafSGabriel FERNANDEZ 	struct clk_hw *mux_hw = &genamux->mux.hw;
13794885fafSGabriel FERNANDEZ 
1384e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
13994885fafSGabriel FERNANDEZ 
14094885fafSGabriel FERNANDEZ 	genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
14194885fafSGabriel FERNANDEZ 	if ((s8)genamux->muxsel < 0) {
14294885fafSGabriel FERNANDEZ 		pr_debug("%s: %s: Invalid parent, setting to default.\n",
143836ee0f7SStephen Boyd 		      __func__, clk_hw_get_name(hw));
14494885fafSGabriel FERNANDEZ 		genamux->muxsel = 0;
14594885fafSGabriel FERNANDEZ 	}
14694885fafSGabriel FERNANDEZ 
14794885fafSGabriel FERNANDEZ 	return genamux->muxsel;
14894885fafSGabriel FERNANDEZ }
14994885fafSGabriel FERNANDEZ 
15094885fafSGabriel FERNANDEZ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
15194885fafSGabriel FERNANDEZ {
15294885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
15394885fafSGabriel FERNANDEZ 
15494885fafSGabriel FERNANDEZ 	if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
15594885fafSGabriel FERNANDEZ 		return -EINVAL;
15694885fafSGabriel FERNANDEZ 
15794885fafSGabriel FERNANDEZ 	genamux->muxsel = index;
15894885fafSGabriel FERNANDEZ 
15994885fafSGabriel FERNANDEZ 	/*
16094885fafSGabriel FERNANDEZ 	 * If the mux is already enabled, call enable directly to set the
16194885fafSGabriel FERNANDEZ 	 * new mux position and wait for it to start running again. Otherwise
16294885fafSGabriel FERNANDEZ 	 * do nothing.
16394885fafSGabriel FERNANDEZ 	 */
16494885fafSGabriel FERNANDEZ 	if (clkgena_divmux_is_enabled(hw))
16594885fafSGabriel FERNANDEZ 		clkgena_divmux_enable(hw);
16694885fafSGabriel FERNANDEZ 
16794885fafSGabriel FERNANDEZ 	return 0;
16894885fafSGabriel FERNANDEZ }
16994885fafSGabriel FERNANDEZ 
1708e6dd77cSStephen Boyd static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
17194885fafSGabriel FERNANDEZ 		unsigned long parent_rate)
17294885fafSGabriel FERNANDEZ {
17394885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
17494885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
17594885fafSGabriel FERNANDEZ 
1764e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
17794885fafSGabriel FERNANDEZ 
17894885fafSGabriel FERNANDEZ 	return clk_divider_ops.recalc_rate(div_hw, parent_rate);
17994885fafSGabriel FERNANDEZ }
18094885fafSGabriel FERNANDEZ 
18194885fafSGabriel FERNANDEZ static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
18294885fafSGabriel FERNANDEZ 				unsigned long parent_rate)
18394885fafSGabriel FERNANDEZ {
18494885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
18594885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
18694885fafSGabriel FERNANDEZ 
1874e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
18894885fafSGabriel FERNANDEZ 
18994885fafSGabriel FERNANDEZ 	return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
19094885fafSGabriel FERNANDEZ }
19194885fafSGabriel FERNANDEZ 
19294885fafSGabriel FERNANDEZ static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
19394885fafSGabriel FERNANDEZ 				   unsigned long *prate)
19494885fafSGabriel FERNANDEZ {
19594885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
19694885fafSGabriel FERNANDEZ 	struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
19794885fafSGabriel FERNANDEZ 
1984e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
19994885fafSGabriel FERNANDEZ 
20094885fafSGabriel FERNANDEZ 	return clk_divider_ops.round_rate(div_hw, rate, prate);
20194885fafSGabriel FERNANDEZ }
20294885fafSGabriel FERNANDEZ 
20394885fafSGabriel FERNANDEZ static const struct clk_ops clkgena_divmux_ops = {
20494885fafSGabriel FERNANDEZ 	.enable = clkgena_divmux_enable,
20594885fafSGabriel FERNANDEZ 	.disable = clkgena_divmux_disable,
20694885fafSGabriel FERNANDEZ 	.is_enabled = clkgena_divmux_is_enabled,
20794885fafSGabriel FERNANDEZ 	.get_parent = clkgena_divmux_get_parent,
20894885fafSGabriel FERNANDEZ 	.set_parent = clkgena_divmux_set_parent,
20994885fafSGabriel FERNANDEZ 	.round_rate = clkgena_divmux_round_rate,
21094885fafSGabriel FERNANDEZ 	.recalc_rate = clkgena_divmux_recalc_rate,
21194885fafSGabriel FERNANDEZ 	.set_rate = clkgena_divmux_set_rate,
21294885fafSGabriel FERNANDEZ };
21394885fafSGabriel FERNANDEZ 
21494885fafSGabriel FERNANDEZ /**
21594885fafSGabriel FERNANDEZ  * clk_register_genamux - register a genamux clock with the clock framework
21694885fafSGabriel FERNANDEZ  */
21786665d28SStephen Boyd static struct clk * __init clk_register_genamux(const char *name,
21894885fafSGabriel FERNANDEZ 				const char **parent_names, u8 num_parents,
21994885fafSGabriel FERNANDEZ 				void __iomem *reg,
22094885fafSGabriel FERNANDEZ 				const struct clkgena_divmux_data *muxdata,
22194885fafSGabriel FERNANDEZ 				u32 idx)
22294885fafSGabriel FERNANDEZ {
22394885fafSGabriel FERNANDEZ 	/*
22494885fafSGabriel FERNANDEZ 	 * Fixed constants across all ClockgenA variants
22594885fafSGabriel FERNANDEZ 	 */
22694885fafSGabriel FERNANDEZ 	const int mux_width = 2;
22794885fafSGabriel FERNANDEZ 	const int divider_width = 5;
22894885fafSGabriel FERNANDEZ 	struct clkgena_divmux *genamux;
22994885fafSGabriel FERNANDEZ 	struct clk *clk;
23094885fafSGabriel FERNANDEZ 	struct clk_init_data init;
23194885fafSGabriel FERNANDEZ 	int i;
23294885fafSGabriel FERNANDEZ 
23394885fafSGabriel FERNANDEZ 	genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
23494885fafSGabriel FERNANDEZ 	if (!genamux)
23594885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
23694885fafSGabriel FERNANDEZ 
23794885fafSGabriel FERNANDEZ 	init.name = name;
23894885fafSGabriel FERNANDEZ 	init.ops = &clkgena_divmux_ops;
23918fee453SPankaj Dev 	init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE;
24094885fafSGabriel FERNANDEZ 	init.parent_names = parent_names;
24194885fafSGabriel FERNANDEZ 	init.num_parents = num_parents;
24294885fafSGabriel FERNANDEZ 
24394885fafSGabriel FERNANDEZ 	genamux->mux.lock  = &clkgena_divmux_lock;
24494885fafSGabriel FERNANDEZ 	genamux->mux.mask = BIT(mux_width) - 1;
24594885fafSGabriel FERNANDEZ 	genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
24694885fafSGabriel FERNANDEZ 	if (genamux->mux.shift > 31) {
24794885fafSGabriel FERNANDEZ 		/*
24894885fafSGabriel FERNANDEZ 		 * We have spilled into the second mux register so
24994885fafSGabriel FERNANDEZ 		 * adjust the register address and the bit shift accordingly
25094885fafSGabriel FERNANDEZ 		 */
25194885fafSGabriel FERNANDEZ 		genamux->mux.reg = reg + muxdata->mux_offset2;
25294885fafSGabriel FERNANDEZ 		genamux->mux.shift -= 32;
25394885fafSGabriel FERNANDEZ 	} else {
25494885fafSGabriel FERNANDEZ 		genamux->mux.reg   = reg + muxdata->mux_offset;
25594885fafSGabriel FERNANDEZ 	}
25694885fafSGabriel FERNANDEZ 
25794885fafSGabriel FERNANDEZ 	for (i = 0; i < NUM_INPUTS; i++) {
25894885fafSGabriel FERNANDEZ 		/*
25994885fafSGabriel FERNANDEZ 		 * Divider config for each input
26094885fafSGabriel FERNANDEZ 		 */
26194885fafSGabriel FERNANDEZ 		void __iomem *divbase = reg + muxdata->div_offsets[i];
26294885fafSGabriel FERNANDEZ 		genamux->div[i].width = divider_width;
26394885fafSGabriel FERNANDEZ 		genamux->div[i].reg = divbase + (idx * sizeof(u32));
26494885fafSGabriel FERNANDEZ 
26594885fafSGabriel FERNANDEZ 		/*
26694885fafSGabriel FERNANDEZ 		 * Mux enabled/running feedback register for each input.
26794885fafSGabriel FERNANDEZ 		 */
26894885fafSGabriel FERNANDEZ 		genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
26994885fafSGabriel FERNANDEZ 	}
27094885fafSGabriel FERNANDEZ 
27194885fafSGabriel FERNANDEZ 	genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
27294885fafSGabriel FERNANDEZ 	genamux->hw.init = &init;
27394885fafSGabriel FERNANDEZ 
27494885fafSGabriel FERNANDEZ 	clk = clk_register(NULL, &genamux->hw);
27594885fafSGabriel FERNANDEZ 	if (IS_ERR(clk)) {
27694885fafSGabriel FERNANDEZ 		kfree(genamux);
27794885fafSGabriel FERNANDEZ 		goto err;
27894885fafSGabriel FERNANDEZ 	}
27994885fafSGabriel FERNANDEZ 
28094885fafSGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %lu\n",
28194885fafSGabriel FERNANDEZ 			__clk_get_name(clk),
28294885fafSGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
28394885fafSGabriel FERNANDEZ 			clk_get_rate(clk));
28494885fafSGabriel FERNANDEZ err:
28594885fafSGabriel FERNANDEZ 	return clk;
28694885fafSGabriel FERNANDEZ }
28794885fafSGabriel FERNANDEZ 
28894885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65hs = {
28994885fafSGabriel FERNANDEZ 	.num_outputs = 4,
29094885fafSGabriel FERNANDEZ 	.mux_offset = 0x14,
29194885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
29294885fafSGabriel FERNANDEZ 	.div_offsets = { 0x800, 0x900, 0xb00 },
29394885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x18, 0x1c, 0x20 },
29494885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 0,
29594885fafSGabriel FERNANDEZ };
29694885fafSGabriel FERNANDEZ 
29794885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c65ls = {
29894885fafSGabriel FERNANDEZ 	.num_outputs = 14,
29994885fafSGabriel FERNANDEZ 	.mux_offset = 0x14,
30094885fafSGabriel FERNANDEZ 	.mux_offset2 = 0x24,
30194885fafSGabriel FERNANDEZ 	.mux_start_bit = 8,
30294885fafSGabriel FERNANDEZ 	.div_offsets = { 0x810, 0xa10, 0xb10 },
30394885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x18, 0x1c, 0x20 },
30494885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 4,
30594885fafSGabriel FERNANDEZ };
30694885fafSGabriel FERNANDEZ 
30794885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf0 = {
30894885fafSGabriel FERNANDEZ 	.num_outputs = 8,
30994885fafSGabriel FERNANDEZ 	.mux_offset = 0x1c,
31094885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
31194885fafSGabriel FERNANDEZ 	.div_offsets = { 0x800, 0x900, 0xa60 },
31294885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
31394885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 0,
31494885fafSGabriel FERNANDEZ };
31594885fafSGabriel FERNANDEZ 
31694885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf1 = {
31794885fafSGabriel FERNANDEZ 	.num_outputs = 8,
31894885fafSGabriel FERNANDEZ 	.mux_offset = 0x1c,
31994885fafSGabriel FERNANDEZ 	.mux_start_bit = 16,
32094885fafSGabriel FERNANDEZ 	.div_offsets = { 0x820, 0x980, 0xa80 },
32194885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
32294885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 8,
32394885fafSGabriel FERNANDEZ };
32494885fafSGabriel FERNANDEZ 
32594885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf2 = {
32694885fafSGabriel FERNANDEZ 	.num_outputs = 8,
32794885fafSGabriel FERNANDEZ 	.mux_offset = 0x20,
32894885fafSGabriel FERNANDEZ 	.mux_start_bit = 0,
32994885fafSGabriel FERNANDEZ 	.div_offsets = { 0x840, 0xa20, 0xb10 },
33094885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
33194885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 16,
33294885fafSGabriel FERNANDEZ };
33394885fafSGabriel FERNANDEZ 
33494885fafSGabriel FERNANDEZ static struct clkgena_divmux_data st_divmux_c32odf3 = {
33594885fafSGabriel FERNANDEZ 	.num_outputs = 8,
33694885fafSGabriel FERNANDEZ 	.mux_offset = 0x20,
33794885fafSGabriel FERNANDEZ 	.mux_start_bit = 16,
33894885fafSGabriel FERNANDEZ 	.div_offsets = { 0x860, 0xa40, 0xb30 },
33994885fafSGabriel FERNANDEZ 	.fb_offsets = { 0x2c, 0x24, 0x28 },
34094885fafSGabriel FERNANDEZ 	.fb_start_bit_idx = 24,
34194885fafSGabriel FERNANDEZ };
34294885fafSGabriel FERNANDEZ 
343f375573cSFabian Frederick static const struct of_device_id clkgena_divmux_of_match[] = {
34494885fafSGabriel FERNANDEZ 	{
34594885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c65-hs",
34694885fafSGabriel FERNANDEZ 		.data = &st_divmux_c65hs,
34794885fafSGabriel FERNANDEZ 	},
34894885fafSGabriel FERNANDEZ 	{
34994885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c65-ls",
35094885fafSGabriel FERNANDEZ 		.data = &st_divmux_c65ls,
35194885fafSGabriel FERNANDEZ 	},
35294885fafSGabriel FERNANDEZ 	{
35394885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf0",
35494885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf0,
35594885fafSGabriel FERNANDEZ 	},
35694885fafSGabriel FERNANDEZ 	{
35794885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf1",
35894885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf1,
35994885fafSGabriel FERNANDEZ 	},
36094885fafSGabriel FERNANDEZ 	{
36194885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf2",
36294885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf2,
36394885fafSGabriel FERNANDEZ 	},
36494885fafSGabriel FERNANDEZ 	{
36594885fafSGabriel FERNANDEZ 		.compatible = "st,clkgena-divmux-c32-odf3",
36694885fafSGabriel FERNANDEZ 		.data = &st_divmux_c32odf3,
36794885fafSGabriel FERNANDEZ 	},
36894885fafSGabriel FERNANDEZ 	{}
36994885fafSGabriel FERNANDEZ };
37094885fafSGabriel FERNANDEZ 
37186665d28SStephen Boyd static void __iomem * __init clkgen_get_register_base(struct device_node *np)
37294885fafSGabriel FERNANDEZ {
37394885fafSGabriel FERNANDEZ 	struct device_node *pnode;
37486665d28SStephen Boyd 	void __iomem *reg;
37594885fafSGabriel FERNANDEZ 
37694885fafSGabriel FERNANDEZ 	pnode = of_get_parent(np);
37794885fafSGabriel FERNANDEZ 	if (!pnode)
37894885fafSGabriel FERNANDEZ 		return NULL;
37994885fafSGabriel FERNANDEZ 
38094885fafSGabriel FERNANDEZ 	reg = of_iomap(pnode, 0);
38194885fafSGabriel FERNANDEZ 
38294885fafSGabriel FERNANDEZ 	of_node_put(pnode);
38394885fafSGabriel FERNANDEZ 	return reg;
38494885fafSGabriel FERNANDEZ }
38594885fafSGabriel FERNANDEZ 
3868e6dd77cSStephen Boyd static void __init st_of_clkgena_divmux_setup(struct device_node *np)
38794885fafSGabriel FERNANDEZ {
38894885fafSGabriel FERNANDEZ 	const struct of_device_id *match;
38994885fafSGabriel FERNANDEZ 	const struct clkgena_divmux_data *data;
39094885fafSGabriel FERNANDEZ 	struct clk_onecell_data *clk_data;
39194885fafSGabriel FERNANDEZ 	void __iomem *reg;
39294885fafSGabriel FERNANDEZ 	const char **parents;
39394885fafSGabriel FERNANDEZ 	int num_parents = 0, i;
39494885fafSGabriel FERNANDEZ 
39594885fafSGabriel FERNANDEZ 	match = of_match_node(clkgena_divmux_of_match, np);
39694885fafSGabriel FERNANDEZ 	if (WARN_ON(!match))
39794885fafSGabriel FERNANDEZ 		return;
39894885fafSGabriel FERNANDEZ 
39986665d28SStephen Boyd 	data = match->data;
40094885fafSGabriel FERNANDEZ 
40194885fafSGabriel FERNANDEZ 	reg = clkgen_get_register_base(np);
40294885fafSGabriel FERNANDEZ 	if (!reg)
40394885fafSGabriel FERNANDEZ 		return;
40494885fafSGabriel FERNANDEZ 
40594885fafSGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
40694885fafSGabriel FERNANDEZ 	if (IS_ERR(parents))
40786665d28SStephen Boyd 		goto err_parents;
40894885fafSGabriel FERNANDEZ 
40994885fafSGabriel FERNANDEZ 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
41094885fafSGabriel FERNANDEZ 	if (!clk_data)
41186665d28SStephen Boyd 		goto err_alloc;
41294885fafSGabriel FERNANDEZ 
41394885fafSGabriel FERNANDEZ 	clk_data->clk_num = data->num_outputs;
41486665d28SStephen Boyd 	clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
41594885fafSGabriel FERNANDEZ 				 GFP_KERNEL);
41694885fafSGabriel FERNANDEZ 
41794885fafSGabriel FERNANDEZ 	if (!clk_data->clks)
41886665d28SStephen Boyd 		goto err_alloc_clks;
41994885fafSGabriel FERNANDEZ 
42094885fafSGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
42194885fafSGabriel FERNANDEZ 		struct clk *clk;
42294885fafSGabriel FERNANDEZ 		const char *clk_name;
42394885fafSGabriel FERNANDEZ 
42494885fafSGabriel FERNANDEZ 		if (of_property_read_string_index(np, "clock-output-names",
42594885fafSGabriel FERNANDEZ 						  i, &clk_name))
42694885fafSGabriel FERNANDEZ 			break;
42794885fafSGabriel FERNANDEZ 
42894885fafSGabriel FERNANDEZ 		/*
42994885fafSGabriel FERNANDEZ 		 * If we read an empty clock name then the output is unused
43094885fafSGabriel FERNANDEZ 		 */
43194885fafSGabriel FERNANDEZ 		if (*clk_name == '\0')
43294885fafSGabriel FERNANDEZ 			continue;
43394885fafSGabriel FERNANDEZ 
43494885fafSGabriel FERNANDEZ 		clk = clk_register_genamux(clk_name, parents, num_parents,
43594885fafSGabriel FERNANDEZ 					   reg, data, i);
43694885fafSGabriel FERNANDEZ 
43794885fafSGabriel FERNANDEZ 		if (IS_ERR(clk))
43894885fafSGabriel FERNANDEZ 			goto err;
43994885fafSGabriel FERNANDEZ 
44094885fafSGabriel FERNANDEZ 		clk_data->clks[i] = clk;
44194885fafSGabriel FERNANDEZ 	}
44294885fafSGabriel FERNANDEZ 
44394885fafSGabriel FERNANDEZ 	kfree(parents);
44494885fafSGabriel FERNANDEZ 
44594885fafSGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
44694885fafSGabriel FERNANDEZ 	return;
44794885fafSGabriel FERNANDEZ err:
44894885fafSGabriel FERNANDEZ 	kfree(clk_data->clks);
44986665d28SStephen Boyd err_alloc_clks:
45094885fafSGabriel FERNANDEZ 	kfree(clk_data);
45186665d28SStephen Boyd err_alloc:
45294885fafSGabriel FERNANDEZ 	kfree(parents);
45386665d28SStephen Boyd err_parents:
45486665d28SStephen Boyd 	iounmap(reg);
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;
49486665d28SStephen Boyd 	const 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 
50286665d28SStephen Boyd 	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)
51086665d28SStephen Boyd 		goto err;
51194885fafSGabriel FERNANDEZ 
51294885fafSGabriel FERNANDEZ 	if (of_property_read_string_index(np, "clock-output-names",
51394885fafSGabriel FERNANDEZ 					  0, &clk_name))
51486665d28SStephen Boyd 		goto err;
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))
52186665d28SStephen Boyd 		goto err;
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;
53086665d28SStephen Boyd err:
53186665d28SStephen Boyd 	iounmap(reg);
53294885fafSGabriel FERNANDEZ }
53394885fafSGabriel FERNANDEZ CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
53444993d38SGabriel FERNANDEZ 
53544993d38SGabriel FERNANDEZ struct clkgen_mux_data {
53644993d38SGabriel FERNANDEZ 	u32 offset;
53744993d38SGabriel FERNANDEZ 	u8 shift;
53844993d38SGabriel FERNANDEZ 	u8 width;
53944993d38SGabriel FERNANDEZ 	spinlock_t *lock;
54044993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
54144993d38SGabriel FERNANDEZ 	u8 mux_flags;
54244993d38SGabriel FERNANDEZ };
54344993d38SGabriel FERNANDEZ 
54444993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
54544993d38SGabriel FERNANDEZ 	.offset = 0,
54644993d38SGabriel FERNANDEZ 	.shift = 0,
54744993d38SGabriel FERNANDEZ 	.width = 1,
54844993d38SGabriel FERNANDEZ };
54944993d38SGabriel FERNANDEZ 
55044993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
55144993d38SGabriel FERNANDEZ 	.offset = 0,
55244993d38SGabriel FERNANDEZ 	.shift = 0,
55344993d38SGabriel FERNANDEZ 	.width = 1,
55444993d38SGabriel FERNANDEZ };
55544993d38SGabriel FERNANDEZ 
55644993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
55744993d38SGabriel FERNANDEZ 	.offset = 0,
55844993d38SGabriel FERNANDEZ 	.shift = 0,
55944993d38SGabriel FERNANDEZ 	.width = 1,
56044993d38SGabriel FERNANDEZ };
56144993d38SGabriel FERNANDEZ 
56244993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
56344993d38SGabriel FERNANDEZ 	.offset = 0,
56444993d38SGabriel FERNANDEZ 	.shift = 16,
56544993d38SGabriel FERNANDEZ 	.width = 1,
56644993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
56744993d38SGabriel FERNANDEZ };
56844993d38SGabriel FERNANDEZ 
56944993d38SGabriel FERNANDEZ static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
57044993d38SGabriel FERNANDEZ 	.offset = 0,
57144993d38SGabriel FERNANDEZ 	.shift = 17,
57244993d38SGabriel FERNANDEZ 	.width = 1,
57344993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
57444993d38SGabriel FERNANDEZ };
57544993d38SGabriel FERNANDEZ 
576ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih415_a9_mux_data = {
577ab35dc13SGabriel FERNANDEZ 	.offset = 0,
578ab35dc13SGabriel FERNANDEZ 	.shift = 1,
579ab35dc13SGabriel FERNANDEZ 	.width = 2,
58046a57afdSGabriel Fernandez 	.lock = &clkgen_a9_lock,
581ab35dc13SGabriel FERNANDEZ };
582ab35dc13SGabriel FERNANDEZ static struct clkgen_mux_data stih416_a9_mux_data = {
583ab35dc13SGabriel FERNANDEZ 	.offset = 0,
584ab35dc13SGabriel FERNANDEZ 	.shift = 0,
585ab35dc13SGabriel FERNANDEZ 	.width = 2,
586ab35dc13SGabriel FERNANDEZ };
58713e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = {
58813e6f2daSGabriel FERNANDEZ 	.offset = 0x1a4,
5893be6d8ceSGabriel Fernandez 	.shift = 0,
59013e6f2daSGabriel FERNANDEZ 	.width = 2,
59146a57afdSGabriel Fernandez 	.lock = &clkgen_a9_lock,
59213e6f2daSGabriel FERNANDEZ };
593ab35dc13SGabriel FERNANDEZ 
594f375573cSFabian Frederick static const struct of_device_id mux_of_match[] = {
59544993d38SGabriel FERNANDEZ 	{
59644993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenc-vcc-hd",
59744993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_c_vcc_hd_416,
59844993d38SGabriel FERNANDEZ 	},
59944993d38SGabriel FERNANDEZ 	{
60044993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-fvdp",
60144993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_f_vcc_fvdp_416,
60244993d38SGabriel FERNANDEZ 	},
60344993d38SGabriel FERNANDEZ 	{
60444993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-hva",
60544993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_f_vcc_hva_416,
60644993d38SGabriel FERNANDEZ 	},
60744993d38SGabriel FERNANDEZ 	{
60844993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-hd",
60944993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_f_vcc_hd_416,
61044993d38SGabriel FERNANDEZ 	},
61144993d38SGabriel FERNANDEZ 	{
61244993d38SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgenf-vcc-sd",
61344993d38SGabriel FERNANDEZ 		.data = &clkgen_mux_c_vcc_sd_416,
61444993d38SGabriel FERNANDEZ 	},
615ab35dc13SGabriel FERNANDEZ 	{
616ab35dc13SGabriel FERNANDEZ 		.compatible = "st,stih415-clkgen-a9-mux",
617ab35dc13SGabriel FERNANDEZ 		.data = &stih415_a9_mux_data,
618ab35dc13SGabriel FERNANDEZ 	},
619ab35dc13SGabriel FERNANDEZ 	{
620ab35dc13SGabriel FERNANDEZ 		.compatible = "st,stih416-clkgen-a9-mux",
621ab35dc13SGabriel FERNANDEZ 		.data = &stih416_a9_mux_data,
622ab35dc13SGabriel FERNANDEZ 	},
62313e6f2daSGabriel FERNANDEZ 	{
62413e6f2daSGabriel FERNANDEZ 		.compatible = "st,stih407-clkgen-a9-mux",
62513e6f2daSGabriel FERNANDEZ 		.data = &stih407_a9_mux_data,
62613e6f2daSGabriel FERNANDEZ 	},
62744993d38SGabriel FERNANDEZ 	{}
62844993d38SGabriel FERNANDEZ };
62944993d38SGabriel FERNANDEZ 
6308e6dd77cSStephen Boyd static void __init st_of_clkgen_mux_setup(struct device_node *np)
63144993d38SGabriel FERNANDEZ {
63244993d38SGabriel FERNANDEZ 	const struct of_device_id *match;
63344993d38SGabriel FERNANDEZ 	struct clk *clk;
63444993d38SGabriel FERNANDEZ 	void __iomem *reg;
63544993d38SGabriel FERNANDEZ 	const char **parents;
63644993d38SGabriel FERNANDEZ 	int num_parents;
63786665d28SStephen Boyd 	const struct clkgen_mux_data *data;
63844993d38SGabriel FERNANDEZ 
63944993d38SGabriel FERNANDEZ 	match = of_match_node(mux_of_match, np);
64044993d38SGabriel FERNANDEZ 	if (!match) {
64144993d38SGabriel FERNANDEZ 		pr_err("%s: No matching data\n", __func__);
64244993d38SGabriel FERNANDEZ 		return;
64344993d38SGabriel FERNANDEZ 	}
64444993d38SGabriel FERNANDEZ 
64586665d28SStephen Boyd 	data = match->data;
64644993d38SGabriel FERNANDEZ 
64744993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
64844993d38SGabriel FERNANDEZ 	if (!reg) {
64944993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get base address\n", __func__);
65044993d38SGabriel FERNANDEZ 		return;
65144993d38SGabriel FERNANDEZ 	}
65244993d38SGabriel FERNANDEZ 
65344993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
65444993d38SGabriel FERNANDEZ 	if (IS_ERR(parents)) {
65544993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get parents (%ld)\n",
65644993d38SGabriel FERNANDEZ 				__func__, PTR_ERR(parents));
65786665d28SStephen Boyd 		goto err_parents;
65844993d38SGabriel FERNANDEZ 	}
65944993d38SGabriel FERNANDEZ 
66044993d38SGabriel FERNANDEZ 	clk = clk_register_mux(NULL, np->name, parents, num_parents,
66144993d38SGabriel FERNANDEZ 				data->clk_flags | CLK_SET_RATE_PARENT,
66244993d38SGabriel FERNANDEZ 				reg + data->offset,
66344993d38SGabriel FERNANDEZ 				data->shift, data->width, data->mux_flags,
66444993d38SGabriel FERNANDEZ 				data->lock);
66544993d38SGabriel FERNANDEZ 	if (IS_ERR(clk))
66644993d38SGabriel FERNANDEZ 		goto err;
66744993d38SGabriel FERNANDEZ 
66844993d38SGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %u\n",
66944993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
67044993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
67144993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
67244993d38SGabriel FERNANDEZ 
67386665d28SStephen Boyd 	kfree(parents);
67444993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
67586665d28SStephen Boyd 	return;
67644993d38SGabriel FERNANDEZ 
67744993d38SGabriel FERNANDEZ err:
67844993d38SGabriel FERNANDEZ 	kfree(parents);
67986665d28SStephen Boyd err_parents:
68086665d28SStephen Boyd 	iounmap(reg);
68144993d38SGabriel FERNANDEZ }
68244993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
68344993d38SGabriel FERNANDEZ 
68444993d38SGabriel FERNANDEZ #define VCC_MAX_CHANNELS 16
68544993d38SGabriel FERNANDEZ 
68644993d38SGabriel FERNANDEZ #define VCC_GATE_OFFSET 0x0
68744993d38SGabriel FERNANDEZ #define VCC_MUX_OFFSET 0x4
68844993d38SGabriel FERNANDEZ #define VCC_DIV_OFFSET 0x8
68944993d38SGabriel FERNANDEZ 
69044993d38SGabriel FERNANDEZ struct clkgen_vcc_data {
69144993d38SGabriel FERNANDEZ 	spinlock_t *lock;
69244993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
69344993d38SGabriel FERNANDEZ };
69444993d38SGabriel FERNANDEZ 
69544993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
69644993d38SGabriel FERNANDEZ 	.clk_flags = CLK_SET_RATE_PARENT,
69744993d38SGabriel FERNANDEZ };
69844993d38SGabriel FERNANDEZ 
69944993d38SGabriel FERNANDEZ static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
70044993d38SGabriel FERNANDEZ 	.lock = &clkgenf_lock,
70144993d38SGabriel FERNANDEZ };
70244993d38SGabriel FERNANDEZ 
703f375573cSFabian Frederick static const struct of_device_id vcc_of_match[] = {
70444993d38SGabriel FERNANDEZ 	{ .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
70544993d38SGabriel FERNANDEZ 	{ .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
70644993d38SGabriel FERNANDEZ 	{}
70744993d38SGabriel FERNANDEZ };
70844993d38SGabriel FERNANDEZ 
7098e6dd77cSStephen Boyd static void __init st_of_clkgen_vcc_setup(struct device_node *np)
71044993d38SGabriel FERNANDEZ {
71144993d38SGabriel FERNANDEZ 	const struct of_device_id *match;
71244993d38SGabriel FERNANDEZ 	void __iomem *reg;
71344993d38SGabriel FERNANDEZ 	const char **parents;
71444993d38SGabriel FERNANDEZ 	int num_parents, i;
71544993d38SGabriel FERNANDEZ 	struct clk_onecell_data *clk_data;
71686665d28SStephen Boyd 	const struct clkgen_vcc_data *data;
71744993d38SGabriel FERNANDEZ 
71844993d38SGabriel FERNANDEZ 	match = of_match_node(vcc_of_match, np);
71944993d38SGabriel FERNANDEZ 	if (WARN_ON(!match))
72044993d38SGabriel FERNANDEZ 		return;
72186665d28SStephen Boyd 	data = match->data;
72244993d38SGabriel FERNANDEZ 
72344993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
72444993d38SGabriel FERNANDEZ 	if (!reg)
72544993d38SGabriel FERNANDEZ 		return;
72644993d38SGabriel FERNANDEZ 
72744993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
72844993d38SGabriel FERNANDEZ 	if (IS_ERR(parents))
72986665d28SStephen Boyd 		goto err_parents;
73044993d38SGabriel FERNANDEZ 
73144993d38SGabriel FERNANDEZ 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
73244993d38SGabriel FERNANDEZ 	if (!clk_data)
73386665d28SStephen Boyd 		goto err_alloc;
73444993d38SGabriel FERNANDEZ 
73544993d38SGabriel FERNANDEZ 	clk_data->clk_num = VCC_MAX_CHANNELS;
73686665d28SStephen Boyd 	clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
73744993d38SGabriel FERNANDEZ 				 GFP_KERNEL);
73844993d38SGabriel FERNANDEZ 
73944993d38SGabriel FERNANDEZ 	if (!clk_data->clks)
74086665d28SStephen Boyd 		goto err_alloc_clks;
74144993d38SGabriel FERNANDEZ 
74244993d38SGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
74344993d38SGabriel FERNANDEZ 		struct clk *clk;
74444993d38SGabriel FERNANDEZ 		const char *clk_name;
74544993d38SGabriel FERNANDEZ 		struct clk_gate *gate;
74644993d38SGabriel FERNANDEZ 		struct clk_divider *div;
74744993d38SGabriel FERNANDEZ 		struct clk_mux *mux;
74844993d38SGabriel FERNANDEZ 
74944993d38SGabriel FERNANDEZ 		if (of_property_read_string_index(np, "clock-output-names",
75044993d38SGabriel FERNANDEZ 						  i, &clk_name))
75144993d38SGabriel FERNANDEZ 			break;
75244993d38SGabriel FERNANDEZ 
75344993d38SGabriel FERNANDEZ 		/*
75444993d38SGabriel FERNANDEZ 		 * If we read an empty clock name then the output is unused
75544993d38SGabriel FERNANDEZ 		 */
75644993d38SGabriel FERNANDEZ 		if (*clk_name == '\0')
75744993d38SGabriel FERNANDEZ 			continue;
75844993d38SGabriel FERNANDEZ 
75986665d28SStephen Boyd 		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
76044993d38SGabriel FERNANDEZ 		if (!gate)
76186665d28SStephen Boyd 			goto err;
76244993d38SGabriel FERNANDEZ 
76386665d28SStephen Boyd 		div = kzalloc(sizeof(*div), GFP_KERNEL);
76444993d38SGabriel FERNANDEZ 		if (!div) {
76544993d38SGabriel FERNANDEZ 			kfree(gate);
76686665d28SStephen Boyd 			goto err;
76744993d38SGabriel FERNANDEZ 		}
76844993d38SGabriel FERNANDEZ 
76986665d28SStephen Boyd 		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
77044993d38SGabriel FERNANDEZ 		if (!mux) {
77144993d38SGabriel FERNANDEZ 			kfree(gate);
77244993d38SGabriel FERNANDEZ 			kfree(div);
77386665d28SStephen Boyd 			goto err;
77444993d38SGabriel FERNANDEZ 		}
77544993d38SGabriel FERNANDEZ 
77644993d38SGabriel FERNANDEZ 		gate->reg = reg + VCC_GATE_OFFSET;
77744993d38SGabriel FERNANDEZ 		gate->bit_idx = i;
77844993d38SGabriel FERNANDEZ 		gate->flags = CLK_GATE_SET_TO_DISABLE;
77944993d38SGabriel FERNANDEZ 		gate->lock = data->lock;
78044993d38SGabriel FERNANDEZ 
78144993d38SGabriel FERNANDEZ 		div->reg = reg + VCC_DIV_OFFSET;
78244993d38SGabriel FERNANDEZ 		div->shift = 2 * i;
78344993d38SGabriel FERNANDEZ 		div->width = 2;
784eee40bb4SGabriel FERNANDEZ 		div->flags = CLK_DIVIDER_POWER_OF_TWO |
785eee40bb4SGabriel FERNANDEZ 			CLK_DIVIDER_ROUND_CLOSEST;
78644993d38SGabriel FERNANDEZ 
78744993d38SGabriel FERNANDEZ 		mux->reg = reg + VCC_MUX_OFFSET;
78844993d38SGabriel FERNANDEZ 		mux->shift = 2 * i;
78944993d38SGabriel FERNANDEZ 		mux->mask = 0x3;
79044993d38SGabriel FERNANDEZ 
79144993d38SGabriel FERNANDEZ 		clk = clk_register_composite(NULL, clk_name, parents,
79244993d38SGabriel FERNANDEZ 					     num_parents,
79344993d38SGabriel FERNANDEZ 					     &mux->hw, &clk_mux_ops,
79444993d38SGabriel FERNANDEZ 					     &div->hw, &clk_divider_ops,
79544993d38SGabriel FERNANDEZ 					     &gate->hw, &clk_gate_ops,
79618fee453SPankaj Dev 					     data->clk_flags |
79718fee453SPankaj Dev 					     CLK_GET_RATE_NOCACHE);
79844993d38SGabriel FERNANDEZ 		if (IS_ERR(clk)) {
79944993d38SGabriel FERNANDEZ 			kfree(gate);
80044993d38SGabriel FERNANDEZ 			kfree(div);
80144993d38SGabriel FERNANDEZ 			kfree(mux);
80244993d38SGabriel FERNANDEZ 			goto err;
80344993d38SGabriel FERNANDEZ 		}
80444993d38SGabriel FERNANDEZ 
80544993d38SGabriel FERNANDEZ 		pr_debug("%s: parent %s rate %u\n",
80644993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
80744993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
80844993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
80944993d38SGabriel FERNANDEZ 
81044993d38SGabriel FERNANDEZ 		clk_data->clks[i] = clk;
81144993d38SGabriel FERNANDEZ 	}
81244993d38SGabriel FERNANDEZ 
81344993d38SGabriel FERNANDEZ 	kfree(parents);
81444993d38SGabriel FERNANDEZ 
81544993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
81644993d38SGabriel FERNANDEZ 	return;
81744993d38SGabriel FERNANDEZ 
81844993d38SGabriel FERNANDEZ err:
81944993d38SGabriel FERNANDEZ 	for (i = 0; i < clk_data->clk_num; i++) {
82044993d38SGabriel FERNANDEZ 		struct clk_composite *composite;
82144993d38SGabriel FERNANDEZ 
82244993d38SGabriel FERNANDEZ 		if (!clk_data->clks[i])
82344993d38SGabriel FERNANDEZ 			continue;
82444993d38SGabriel FERNANDEZ 
8255fd9c05cSGeliang Tang 		composite = to_clk_composite(__clk_get_hw(clk_data->clks[i]));
8265fd9c05cSGeliang Tang 		kfree(to_clk_gate(composite->gate_hw));
8275fd9c05cSGeliang Tang 		kfree(to_clk_divider(composite->rate_hw));
8285fd9c05cSGeliang Tang 		kfree(to_clk_mux(composite->mux_hw));
82944993d38SGabriel FERNANDEZ 	}
83044993d38SGabriel FERNANDEZ 
83144993d38SGabriel FERNANDEZ 	kfree(clk_data->clks);
83286665d28SStephen Boyd err_alloc_clks:
83344993d38SGabriel FERNANDEZ 	kfree(clk_data);
83486665d28SStephen Boyd err_alloc:
83544993d38SGabriel FERNANDEZ 	kfree(parents);
83686665d28SStephen Boyd err_parents:
83786665d28SStephen Boyd 	iounmap(reg);
83844993d38SGabriel FERNANDEZ }
83944993d38SGabriel FERNANDEZ CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
840