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