xref: /linux/drivers/clk/imx/clk-gpr-mux.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
1ee394f63SOleksij Rempel // SPDX-License-Identifier: GPL-2.0
2ee394f63SOleksij Rempel /*
3ee394f63SOleksij Rempel  */
4ee394f63SOleksij Rempel 
5ee394f63SOleksij Rempel #define pr_fmt(fmt) "imx:clk-gpr-mux: " fmt
6ee394f63SOleksij Rempel 
7ee394f63SOleksij Rempel #include <linux/module.h>
8ee394f63SOleksij Rempel 
9ee394f63SOleksij Rempel #include <linux/clk-provider.h>
10ee394f63SOleksij Rempel #include <linux/errno.h>
11ee394f63SOleksij Rempel #include <linux/export.h>
12ee394f63SOleksij Rempel #include <linux/io.h>
13ee394f63SOleksij Rempel #include <linux/slab.h>
14ee394f63SOleksij Rempel #include <linux/regmap.h>
15ee394f63SOleksij Rempel #include <linux/mfd/syscon.h>
16ee394f63SOleksij Rempel 
17ee394f63SOleksij Rempel #include "clk.h"
18ee394f63SOleksij Rempel 
19ee394f63SOleksij Rempel struct imx_clk_gpr {
20ee394f63SOleksij Rempel 	struct clk_hw hw;
21ee394f63SOleksij Rempel 	struct regmap *regmap;
22ee394f63SOleksij Rempel 	u32 mask;
23ee394f63SOleksij Rempel 	u32 reg;
24ee394f63SOleksij Rempel 	const u32 *mux_table;
25ee394f63SOleksij Rempel };
26ee394f63SOleksij Rempel 
to_imx_clk_gpr(struct clk_hw * hw)27ee394f63SOleksij Rempel static struct imx_clk_gpr *to_imx_clk_gpr(struct clk_hw *hw)
28ee394f63SOleksij Rempel {
29ee394f63SOleksij Rempel 	return container_of(hw, struct imx_clk_gpr, hw);
30ee394f63SOleksij Rempel }
31ee394f63SOleksij Rempel 
imx_clk_gpr_mux_get_parent(struct clk_hw * hw)32ee394f63SOleksij Rempel static u8 imx_clk_gpr_mux_get_parent(struct clk_hw *hw)
33ee394f63SOleksij Rempel {
34ee394f63SOleksij Rempel 	struct imx_clk_gpr *priv = to_imx_clk_gpr(hw);
35ee394f63SOleksij Rempel 	unsigned int val;
36ee394f63SOleksij Rempel 	int ret;
37ee394f63SOleksij Rempel 
38ee394f63SOleksij Rempel 	ret = regmap_read(priv->regmap, priv->reg, &val);
39ee394f63SOleksij Rempel 	if (ret)
40ee394f63SOleksij Rempel 		goto get_parent_err;
41ee394f63SOleksij Rempel 
42ee394f63SOleksij Rempel 	val &= priv->mask;
43ee394f63SOleksij Rempel 
44ee394f63SOleksij Rempel 	ret = clk_mux_val_to_index(hw, priv->mux_table, 0, val);
45ee394f63SOleksij Rempel 	if (ret < 0)
46ee394f63SOleksij Rempel 		goto get_parent_err;
47ee394f63SOleksij Rempel 
48ee394f63SOleksij Rempel 	return ret;
49ee394f63SOleksij Rempel 
50ee394f63SOleksij Rempel get_parent_err:
51f47a669fSStefan Wahren 	pr_err("%s: failed to get parent (%pe)\n",
52f47a669fSStefan Wahren 	       clk_hw_get_name(hw), ERR_PTR(ret));
53ee394f63SOleksij Rempel 
54ee394f63SOleksij Rempel 	/* return some realistic non negative value. Potentially we could
55ee394f63SOleksij Rempel 	 * give index to some dummy error parent.
56ee394f63SOleksij Rempel 	 */
57ee394f63SOleksij Rempel 	return 0;
58ee394f63SOleksij Rempel }
59ee394f63SOleksij Rempel 
imx_clk_gpr_mux_set_parent(struct clk_hw * hw,u8 index)60ee394f63SOleksij Rempel static int imx_clk_gpr_mux_set_parent(struct clk_hw *hw, u8 index)
61ee394f63SOleksij Rempel {
62ee394f63SOleksij Rempel 	struct imx_clk_gpr *priv = to_imx_clk_gpr(hw);
63ee394f63SOleksij Rempel 	unsigned int val = clk_mux_index_to_val(priv->mux_table, 0, index);
64ee394f63SOleksij Rempel 
65ee394f63SOleksij Rempel 	return regmap_update_bits(priv->regmap, priv->reg, priv->mask, val);
66ee394f63SOleksij Rempel }
67ee394f63SOleksij Rempel 
68f89ea8f9STom Rix static const struct clk_ops imx_clk_gpr_mux_ops = {
69ee394f63SOleksij Rempel 	.get_parent = imx_clk_gpr_mux_get_parent,
70ee394f63SOleksij Rempel 	.set_parent = imx_clk_gpr_mux_set_parent,
71*2deed4cdSChristophe JAILLET 	.determine_rate = __clk_mux_determine_rate,
72ee394f63SOleksij Rempel };
73ee394f63SOleksij Rempel 
imx_clk_gpr_mux(const char * name,const char * compatible,u32 reg,const char ** parent_names,u8 num_parents,const u32 * mux_table,u32 mask)74ee394f63SOleksij Rempel struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible,
75ee394f63SOleksij Rempel 			       u32 reg, const char **parent_names,
76ee394f63SOleksij Rempel 			       u8 num_parents, const u32 *mux_table, u32 mask)
77ee394f63SOleksij Rempel {
78ee394f63SOleksij Rempel 	struct clk_init_data init  = { };
79ee394f63SOleksij Rempel 	struct imx_clk_gpr *priv;
80ee394f63SOleksij Rempel 	struct regmap *regmap;
81ee394f63SOleksij Rempel 	struct clk_hw *hw;
82ee394f63SOleksij Rempel 	int ret;
83ee394f63SOleksij Rempel 
84ee394f63SOleksij Rempel 	regmap = syscon_regmap_lookup_by_compatible(compatible);
85ee394f63SOleksij Rempel 	if (IS_ERR(regmap)) {
86ee394f63SOleksij Rempel 		pr_err("failed to find %s regmap\n", compatible);
87ee394f63SOleksij Rempel 		return ERR_CAST(regmap);
88ee394f63SOleksij Rempel 	}
89ee394f63SOleksij Rempel 
90ee394f63SOleksij Rempel 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
91ee394f63SOleksij Rempel 	if (!priv)
92ee394f63SOleksij Rempel 		return ERR_PTR(-ENOMEM);
93ee394f63SOleksij Rempel 
94ee394f63SOleksij Rempel 	init.name = name;
95ee394f63SOleksij Rempel 	init.ops = &imx_clk_gpr_mux_ops;
96ee394f63SOleksij Rempel 	init.parent_names = parent_names;
97ee394f63SOleksij Rempel 	init.num_parents = num_parents;
98ee394f63SOleksij Rempel 	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
99ee394f63SOleksij Rempel 
100ee394f63SOleksij Rempel 	priv->hw.init = &init;
101ee394f63SOleksij Rempel 	priv->regmap = regmap;
102ee394f63SOleksij Rempel 	priv->mux_table = mux_table;
103ee394f63SOleksij Rempel 	priv->reg = reg;
104ee394f63SOleksij Rempel 	priv->mask = mask;
105ee394f63SOleksij Rempel 
106ee394f63SOleksij Rempel 	hw = &priv->hw;
107ee394f63SOleksij Rempel 	ret = clk_hw_register(NULL, &priv->hw);
108ee394f63SOleksij Rempel 	if (ret) {
109ee394f63SOleksij Rempel 		kfree(priv);
110ee394f63SOleksij Rempel 		hw = ERR_PTR(ret);
111ee394f63SOleksij Rempel 	}
112ee394f63SOleksij Rempel 
113ee394f63SOleksij Rempel 	return hw;
114ee394f63SOleksij Rempel }
115