xref: /linux/drivers/clk/st/clkgen-mux.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
294885fafSGabriel FERNANDEZ /*
394885fafSGabriel FERNANDEZ  * clkgen-mux.c: ST GEN-MUX Clock driver
494885fafSGabriel FERNANDEZ  *
594885fafSGabriel FERNANDEZ  * Copyright (C) 2014 STMicroelectronics (R&D) Limited
694885fafSGabriel FERNANDEZ  *
794885fafSGabriel FERNANDEZ  * Authors: Stephen Gallimore <stephen.gallimore@st.com>
894885fafSGabriel FERNANDEZ  *	    Pankaj Dev <pankaj.dev@st.com>
994885fafSGabriel FERNANDEZ  */
1094885fafSGabriel FERNANDEZ 
1194885fafSGabriel FERNANDEZ #include <linux/slab.h>
1262e59c4eSStephen Boyd #include <linux/io.h>
1394885fafSGabriel FERNANDEZ #include <linux/of_address.h>
14d5f728acSStephen Boyd #include <linux/clk.h>
1594885fafSGabriel FERNANDEZ #include <linux/clk-provider.h>
1646a57afdSGabriel Fernandez #include "clkgen.h"
1794885fafSGabriel FERNANDEZ 
clkgen_mux_get_parents(struct device_node * np,int * num_parents)1894885fafSGabriel FERNANDEZ static const char ** __init clkgen_mux_get_parents(struct device_node *np,
1994885fafSGabriel FERNANDEZ 						       int *num_parents)
2094885fafSGabriel FERNANDEZ {
2194885fafSGabriel FERNANDEZ 	const char **parents;
22caeb057cSStephen Boyd 	unsigned int nparents;
2394885fafSGabriel FERNANDEZ 
240a65239cSGeert Uytterhoeven 	nparents = of_clk_get_parent_count(np);
25caeb057cSStephen Boyd 	if (WARN_ON(!nparents))
2694885fafSGabriel FERNANDEZ 		return ERR_PTR(-EINVAL);
2794885fafSGabriel FERNANDEZ 
2886665d28SStephen Boyd 	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
2994885fafSGabriel FERNANDEZ 	if (!parents)
3094885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
3194885fafSGabriel FERNANDEZ 
320b4e7f08SDinh Nguyen 	*num_parents = of_clk_parent_fill(np, parents, nparents);
3394885fafSGabriel FERNANDEZ 	return parents;
3494885fafSGabriel FERNANDEZ }
3594885fafSGabriel FERNANDEZ 
3644993d38SGabriel FERNANDEZ struct clkgen_mux_data {
3744993d38SGabriel FERNANDEZ 	u32 offset;
3844993d38SGabriel FERNANDEZ 	u8 shift;
3944993d38SGabriel FERNANDEZ 	u8 width;
4044993d38SGabriel FERNANDEZ 	spinlock_t *lock;
4144993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
4244993d38SGabriel FERNANDEZ 	u8 mux_flags;
4344993d38SGabriel FERNANDEZ };
4444993d38SGabriel FERNANDEZ 
4513e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = {
4613e6f2daSGabriel FERNANDEZ 	.offset = 0x1a4,
473be6d8ceSGabriel Fernandez 	.shift = 0,
4813e6f2daSGabriel FERNANDEZ 	.width = 2,
4946a57afdSGabriel Fernandez 	.lock = &clkgen_a9_lock,
5013e6f2daSGabriel FERNANDEZ };
51ab35dc13SGabriel FERNANDEZ 
st_of_clkgen_mux_setup(struct device_node * np,struct clkgen_mux_data * data)52880d54ffSGabriel Fernandez static void __init st_of_clkgen_mux_setup(struct device_node *np,
53880d54ffSGabriel Fernandez 		struct clkgen_mux_data *data)
5444993d38SGabriel FERNANDEZ {
5544993d38SGabriel FERNANDEZ 	struct clk *clk;
5644993d38SGabriel FERNANDEZ 	void __iomem *reg;
5744993d38SGabriel FERNANDEZ 	const char **parents;
587df404c9SGabriel Fernandez 	int num_parents = 0;
59*42997330SLiang He 	struct device_node *parent_np;
6044993d38SGabriel FERNANDEZ 
61810251b0SAlain Volmat 	/*
62810251b0SAlain Volmat 	 * First check for reg property within the node to keep backward
63810251b0SAlain Volmat 	 * compatibility, then if reg doesn't exist look at the parent node
64810251b0SAlain Volmat 	 */
6544993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
6644993d38SGabriel FERNANDEZ 	if (!reg) {
67*42997330SLiang He 		parent_np = of_get_parent(np);
68*42997330SLiang He 		reg = of_iomap(parent_np, 0);
69*42997330SLiang He 		of_node_put(parent_np);
70810251b0SAlain Volmat 		if (!reg) {
7144993d38SGabriel FERNANDEZ 			pr_err("%s: Failed to get base address\n", __func__);
7244993d38SGabriel FERNANDEZ 			return;
7344993d38SGabriel FERNANDEZ 		}
74810251b0SAlain Volmat 	}
7544993d38SGabriel FERNANDEZ 
7644993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
7744993d38SGabriel FERNANDEZ 	if (IS_ERR(parents)) {
7844993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get parents (%ld)\n",
7944993d38SGabriel FERNANDEZ 				__func__, PTR_ERR(parents));
8086665d28SStephen Boyd 		goto err_parents;
8144993d38SGabriel FERNANDEZ 	}
8244993d38SGabriel FERNANDEZ 
8344993d38SGabriel FERNANDEZ 	clk = clk_register_mux(NULL, np->name, parents, num_parents,
8444993d38SGabriel FERNANDEZ 				data->clk_flags | CLK_SET_RATE_PARENT,
8544993d38SGabriel FERNANDEZ 				reg + data->offset,
8644993d38SGabriel FERNANDEZ 				data->shift, data->width, data->mux_flags,
8744993d38SGabriel FERNANDEZ 				data->lock);
8844993d38SGabriel FERNANDEZ 	if (IS_ERR(clk))
8944993d38SGabriel FERNANDEZ 		goto err;
9044993d38SGabriel FERNANDEZ 
9144993d38SGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %u\n",
9244993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
9344993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
9444993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
9544993d38SGabriel FERNANDEZ 
9686665d28SStephen Boyd 	kfree(parents);
9744993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
9886665d28SStephen Boyd 	return;
9944993d38SGabriel FERNANDEZ 
10044993d38SGabriel FERNANDEZ err:
10144993d38SGabriel FERNANDEZ 	kfree(parents);
10286665d28SStephen Boyd err_parents:
10386665d28SStephen Boyd 	iounmap(reg);
10444993d38SGabriel FERNANDEZ }
105880d54ffSGabriel Fernandez 
st_of_clkgen_a9_mux_setup(struct device_node * np)106880d54ffSGabriel Fernandez static void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
107880d54ffSGabriel Fernandez {
108880d54ffSGabriel Fernandez 	st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
109880d54ffSGabriel Fernandez }
110880d54ffSGabriel Fernandez CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
111880d54ffSGabriel Fernandez 		st_of_clkgen_a9_mux_setup);
112