Merge drm/drm-next into drm-misc-next
[linux-2.6-microblaze.git] / drivers / clk / mediatek / clk-mux.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 MediaTek Inc.
4  * Author: Owen Chen <owen.chen@mediatek.com>
5  */
6
7 #include <linux/clk-provider.h>
8 #include <linux/compiler_types.h>
9 #include <linux/container_of.h>
10 #include <linux/err.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/module.h>
13 #include <linux/regmap.h>
14 #include <linux/spinlock.h>
15 #include <linux/slab.h>
16
17 #include "clk-mux.h"
18
19 struct mtk_clk_mux {
20         struct clk_hw hw;
21         struct regmap *regmap;
22         const struct mtk_mux *data;
23         spinlock_t *lock;
24         bool reparent;
25 };
26
27 static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
28 {
29         return container_of(hw, struct mtk_clk_mux, hw);
30 }
31
32 static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
33 {
34         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
35         unsigned long flags = 0;
36
37         if (mux->lock)
38                 spin_lock_irqsave(mux->lock, flags);
39         else
40                 __acquire(mux->lock);
41
42         regmap_write(mux->regmap, mux->data->clr_ofs,
43                      BIT(mux->data->gate_shift));
44
45         /*
46          * If the parent has been changed when the clock was disabled, it will
47          * not be effective yet. Set the update bit to ensure the mux gets
48          * updated.
49          */
50         if (mux->reparent && mux->data->upd_shift >= 0) {
51                 regmap_write(mux->regmap, mux->data->upd_ofs,
52                              BIT(mux->data->upd_shift));
53                 mux->reparent = false;
54         }
55
56         if (mux->lock)
57                 spin_unlock_irqrestore(mux->lock, flags);
58         else
59                 __release(mux->lock);
60
61         return 0;
62 }
63
64 static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
65 {
66         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
67
68         regmap_write(mux->regmap, mux->data->set_ofs,
69                         BIT(mux->data->gate_shift));
70 }
71
72 static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
73 {
74         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
75         u32 val;
76
77         regmap_read(mux->regmap, mux->data->mux_ofs, &val);
78
79         return (val & BIT(mux->data->gate_shift)) == 0;
80 }
81
82 static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
83 {
84         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
85         u32 mask = GENMASK(mux->data->mux_width - 1, 0);
86         u32 val;
87
88         regmap_read(mux->regmap, mux->data->mux_ofs, &val);
89         val = (val >> mux->data->mux_shift) & mask;
90
91         return val;
92 }
93
94 static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
95 {
96         struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
97         u32 mask = GENMASK(mux->data->mux_width - 1, 0);
98         u32 val, orig;
99         unsigned long flags = 0;
100
101         if (mux->lock)
102                 spin_lock_irqsave(mux->lock, flags);
103         else
104                 __acquire(mux->lock);
105
106         regmap_read(mux->regmap, mux->data->mux_ofs, &orig);
107         val = (orig & ~(mask << mux->data->mux_shift))
108                         | (index << mux->data->mux_shift);
109
110         if (val != orig) {
111                 regmap_write(mux->regmap, mux->data->clr_ofs,
112                                 mask << mux->data->mux_shift);
113                 regmap_write(mux->regmap, mux->data->set_ofs,
114                                 index << mux->data->mux_shift);
115
116                 if (mux->data->upd_shift >= 0) {
117                         regmap_write(mux->regmap, mux->data->upd_ofs,
118                                         BIT(mux->data->upd_shift));
119                         mux->reparent = true;
120                 }
121         }
122
123         if (mux->lock)
124                 spin_unlock_irqrestore(mux->lock, flags);
125         else
126                 __release(mux->lock);
127
128         return 0;
129 }
130
131 const struct clk_ops mtk_mux_clr_set_upd_ops = {
132         .get_parent = mtk_clk_mux_get_parent,
133         .set_parent = mtk_clk_mux_set_parent_setclr_lock,
134 };
135 EXPORT_SYMBOL_GPL(mtk_mux_clr_set_upd_ops);
136
137 const struct clk_ops mtk_mux_gate_clr_set_upd_ops  = {
138         .enable = mtk_clk_mux_enable_setclr,
139         .disable = mtk_clk_mux_disable_setclr,
140         .is_enabled = mtk_clk_mux_is_enabled,
141         .get_parent = mtk_clk_mux_get_parent,
142         .set_parent = mtk_clk_mux_set_parent_setclr_lock,
143 };
144 EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);
145
146 static struct clk *mtk_clk_register_mux(const struct mtk_mux *mux,
147                                  struct regmap *regmap,
148                                  spinlock_t *lock)
149 {
150         struct mtk_clk_mux *clk_mux;
151         struct clk_init_data init = {};
152         struct clk *clk;
153
154         clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL);
155         if (!clk_mux)
156                 return ERR_PTR(-ENOMEM);
157
158         init.name = mux->name;
159         init.flags = mux->flags | CLK_SET_RATE_PARENT;
160         init.parent_names = mux->parent_names;
161         init.num_parents = mux->num_parents;
162         init.ops = mux->ops;
163
164         clk_mux->regmap = regmap;
165         clk_mux->data = mux;
166         clk_mux->lock = lock;
167         clk_mux->hw.init = &init;
168
169         clk = clk_register(NULL, &clk_mux->hw);
170         if (IS_ERR(clk)) {
171                 kfree(clk_mux);
172                 return clk;
173         }
174
175         return clk;
176 }
177
178 static void mtk_clk_unregister_mux(struct clk *clk)
179 {
180         struct mtk_clk_mux *mux;
181         struct clk_hw *hw;
182
183         hw = __clk_get_hw(clk);
184         if (!hw)
185                 return;
186
187         mux = to_mtk_clk_mux(hw);
188
189         clk_unregister(clk);
190         kfree(mux);
191 }
192
193 int mtk_clk_register_muxes(const struct mtk_mux *muxes,
194                            int num, struct device_node *node,
195                            spinlock_t *lock,
196                            struct clk_onecell_data *clk_data)
197 {
198         struct regmap *regmap;
199         struct clk *clk;
200         int i;
201
202         regmap = device_node_to_regmap(node);
203         if (IS_ERR(regmap)) {
204                 pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
205                 return PTR_ERR(regmap);
206         }
207
208         for (i = 0; i < num; i++) {
209                 const struct mtk_mux *mux = &muxes[i];
210
211                 if (!IS_ERR_OR_NULL(clk_data->clks[mux->id])) {
212                         pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
213                                 node, mux->id);
214                         continue;
215                 }
216
217                 clk = mtk_clk_register_mux(mux, regmap, lock);
218
219                 if (IS_ERR(clk)) {
220                         pr_err("Failed to register clk %s: %pe\n", mux->name, clk);
221                         goto err;
222                 }
223
224                 clk_data->clks[mux->id] = clk;
225         }
226
227         return 0;
228
229 err:
230         while (--i >= 0) {
231                 const struct mtk_mux *mux = &muxes[i];
232
233                 if (IS_ERR_OR_NULL(clk_data->clks[mux->id]))
234                         continue;
235
236                 mtk_clk_unregister_mux(clk_data->clks[mux->id]);
237                 clk_data->clks[mux->id] = ERR_PTR(-ENOENT);
238         }
239
240         return PTR_ERR(clk);
241 }
242 EXPORT_SYMBOL_GPL(mtk_clk_register_muxes);
243
244 void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num,
245                               struct clk_onecell_data *clk_data)
246 {
247         int i;
248
249         if (!clk_data)
250                 return;
251
252         for (i = num; i > 0; i--) {
253                 const struct mtk_mux *mux = &muxes[i - 1];
254
255                 if (IS_ERR_OR_NULL(clk_data->clks[mux->id]))
256                         continue;
257
258                 mtk_clk_unregister_mux(clk_data->clks[mux->id]);
259                 clk_data->clks[mux->id] = ERR_PTR(-ENOENT);
260         }
261 }
262 EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes);
263
264 MODULE_LICENSE("GPL");