xref: /openbmc/linux/drivers/clk/st/clkgen-mux.c (revision 880d54ff564181b4d9fff31f7f81807cf60cd85d)
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 const char ** __init clkgen_mux_get_parents(struct device_node *np,
2394885fafSGabriel FERNANDEZ 						       int *num_parents)
2494885fafSGabriel FERNANDEZ {
2594885fafSGabriel FERNANDEZ 	const char **parents;
26caeb057cSStephen Boyd 	unsigned int nparents;
2794885fafSGabriel FERNANDEZ 
280a65239cSGeert Uytterhoeven 	nparents = of_clk_get_parent_count(np);
29caeb057cSStephen Boyd 	if (WARN_ON(!nparents))
3094885fafSGabriel FERNANDEZ 		return ERR_PTR(-EINVAL);
3194885fafSGabriel FERNANDEZ 
3286665d28SStephen Boyd 	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
3394885fafSGabriel FERNANDEZ 	if (!parents)
3494885fafSGabriel FERNANDEZ 		return ERR_PTR(-ENOMEM);
3594885fafSGabriel FERNANDEZ 
360b4e7f08SDinh Nguyen 	*num_parents = of_clk_parent_fill(np, parents, nparents);
3794885fafSGabriel FERNANDEZ 	return parents;
3894885fafSGabriel FERNANDEZ }
3994885fafSGabriel FERNANDEZ 
4044993d38SGabriel FERNANDEZ struct clkgen_mux_data {
4144993d38SGabriel FERNANDEZ 	u32 offset;
4244993d38SGabriel FERNANDEZ 	u8 shift;
4344993d38SGabriel FERNANDEZ 	u8 width;
4444993d38SGabriel FERNANDEZ 	spinlock_t *lock;
4544993d38SGabriel FERNANDEZ 	unsigned long clk_flags;
4644993d38SGabriel FERNANDEZ 	u8 mux_flags;
4744993d38SGabriel FERNANDEZ };
4844993d38SGabriel FERNANDEZ 
4913e6f2daSGabriel FERNANDEZ static struct clkgen_mux_data stih407_a9_mux_data = {
5013e6f2daSGabriel FERNANDEZ 	.offset = 0x1a4,
513be6d8ceSGabriel Fernandez 	.shift = 0,
5213e6f2daSGabriel FERNANDEZ 	.width = 2,
5346a57afdSGabriel Fernandez 	.lock = &clkgen_a9_lock,
5413e6f2daSGabriel FERNANDEZ };
55ab35dc13SGabriel FERNANDEZ 
56*880d54ffSGabriel Fernandez static void __init st_of_clkgen_mux_setup(struct device_node *np,
57*880d54ffSGabriel Fernandez 		struct clkgen_mux_data *data)
5844993d38SGabriel FERNANDEZ {
5944993d38SGabriel FERNANDEZ 	struct clk *clk;
6044993d38SGabriel FERNANDEZ 	void __iomem *reg;
6144993d38SGabriel FERNANDEZ 	const char **parents;
627df404c9SGabriel Fernandez 	int num_parents = 0;
6344993d38SGabriel FERNANDEZ 
6444993d38SGabriel FERNANDEZ 	reg = of_iomap(np, 0);
6544993d38SGabriel FERNANDEZ 	if (!reg) {
6644993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get base address\n", __func__);
6744993d38SGabriel FERNANDEZ 		return;
6844993d38SGabriel FERNANDEZ 	}
6944993d38SGabriel FERNANDEZ 
7044993d38SGabriel FERNANDEZ 	parents = clkgen_mux_get_parents(np, &num_parents);
7144993d38SGabriel FERNANDEZ 	if (IS_ERR(parents)) {
7244993d38SGabriel FERNANDEZ 		pr_err("%s: Failed to get parents (%ld)\n",
7344993d38SGabriel FERNANDEZ 				__func__, PTR_ERR(parents));
7486665d28SStephen Boyd 		goto err_parents;
7544993d38SGabriel FERNANDEZ 	}
7644993d38SGabriel FERNANDEZ 
7744993d38SGabriel FERNANDEZ 	clk = clk_register_mux(NULL, np->name, parents, num_parents,
7844993d38SGabriel FERNANDEZ 				data->clk_flags | CLK_SET_RATE_PARENT,
7944993d38SGabriel FERNANDEZ 				reg + data->offset,
8044993d38SGabriel FERNANDEZ 				data->shift, data->width, data->mux_flags,
8144993d38SGabriel FERNANDEZ 				data->lock);
8244993d38SGabriel FERNANDEZ 	if (IS_ERR(clk))
8344993d38SGabriel FERNANDEZ 		goto err;
8444993d38SGabriel FERNANDEZ 
8544993d38SGabriel FERNANDEZ 	pr_debug("%s: parent %s rate %u\n",
8644993d38SGabriel FERNANDEZ 			__clk_get_name(clk),
8744993d38SGabriel FERNANDEZ 			__clk_get_name(clk_get_parent(clk)),
8844993d38SGabriel FERNANDEZ 			(unsigned int)clk_get_rate(clk));
8944993d38SGabriel FERNANDEZ 
9086665d28SStephen Boyd 	kfree(parents);
9144993d38SGabriel FERNANDEZ 	of_clk_add_provider(np, of_clk_src_simple_get, clk);
9286665d28SStephen Boyd 	return;
9344993d38SGabriel FERNANDEZ 
9444993d38SGabriel FERNANDEZ err:
9544993d38SGabriel FERNANDEZ 	kfree(parents);
9686665d28SStephen Boyd err_parents:
9786665d28SStephen Boyd 	iounmap(reg);
9844993d38SGabriel FERNANDEZ }
99*880d54ffSGabriel Fernandez 
100*880d54ffSGabriel Fernandez static void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
101*880d54ffSGabriel Fernandez {
102*880d54ffSGabriel Fernandez 	st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
103*880d54ffSGabriel Fernandez }
104*880d54ffSGabriel Fernandez CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
105*880d54ffSGabriel Fernandez 		st_of_clkgen_a9_mux_setup);
106