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.h> |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/compiler_types.h> |
10 | #include <linux/container_of.h> |
11 | #include <linux/err.h> |
12 | #include <linux/mfd/syscon.h> |
13 | #include <linux/module.h> |
14 | #include <linux/regmap.h> |
15 | #include <linux/spinlock.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #include "clk-mux.h" |
19 | |
20 | struct mtk_clk_mux { |
21 | struct clk_hw hw; |
22 | struct regmap *regmap; |
23 | const struct mtk_mux *data; |
24 | spinlock_t *lock; |
25 | bool reparent; |
26 | }; |
27 | |
28 | static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw) |
29 | { |
30 | return container_of(hw, struct mtk_clk_mux, hw); |
31 | } |
32 | |
33 | static int mtk_clk_mux_enable_setclr(struct clk_hw *hw) |
34 | { |
35 | struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); |
36 | unsigned long flags = 0; |
37 | |
38 | if (mux->lock) |
39 | spin_lock_irqsave(mux->lock, flags); |
40 | else |
41 | __acquire(mux->lock); |
42 | |
43 | regmap_write(map: mux->regmap, reg: mux->data->clr_ofs, |
44 | BIT(mux->data->gate_shift)); |
45 | |
46 | /* |
47 | * If the parent has been changed when the clock was disabled, it will |
48 | * not be effective yet. Set the update bit to ensure the mux gets |
49 | * updated. |
50 | */ |
51 | if (mux->reparent && mux->data->upd_shift >= 0) { |
52 | regmap_write(map: mux->regmap, reg: mux->data->upd_ofs, |
53 | BIT(mux->data->upd_shift)); |
54 | mux->reparent = false; |
55 | } |
56 | |
57 | if (mux->lock) |
58 | spin_unlock_irqrestore(lock: mux->lock, flags); |
59 | else |
60 | __release(mux->lock); |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static void mtk_clk_mux_disable_setclr(struct clk_hw *hw) |
66 | { |
67 | struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); |
68 | |
69 | regmap_write(map: mux->regmap, reg: mux->data->set_ofs, |
70 | BIT(mux->data->gate_shift)); |
71 | } |
72 | |
73 | static int mtk_clk_mux_is_enabled(struct clk_hw *hw) |
74 | { |
75 | struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); |
76 | u32 val; |
77 | |
78 | regmap_read(map: mux->regmap, reg: mux->data->mux_ofs, val: &val); |
79 | |
80 | return (val & BIT(mux->data->gate_shift)) == 0; |
81 | } |
82 | |
83 | static u8 mtk_clk_mux_get_parent(struct clk_hw *hw) |
84 | { |
85 | struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); |
86 | u32 mask = GENMASK(mux->data->mux_width - 1, 0); |
87 | u32 val; |
88 | |
89 | regmap_read(map: mux->regmap, reg: mux->data->mux_ofs, val: &val); |
90 | val = (val >> mux->data->mux_shift) & mask; |
91 | |
92 | if (mux->data->parent_index) { |
93 | int i; |
94 | |
95 | for (i = 0; i < mux->data->num_parents; i++) |
96 | if (mux->data->parent_index[i] == val) |
97 | return i; |
98 | |
99 | /* Not found: return an impossible index to generate error */ |
100 | return mux->data->num_parents + 1; |
101 | } |
102 | |
103 | return val; |
104 | } |
105 | |
106 | static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index) |
107 | { |
108 | struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); |
109 | u32 mask = GENMASK(mux->data->mux_width - 1, 0); |
110 | u32 val, orig; |
111 | unsigned long flags = 0; |
112 | |
113 | if (mux->lock) |
114 | spin_lock_irqsave(mux->lock, flags); |
115 | else |
116 | __acquire(mux->lock); |
117 | |
118 | if (mux->data->parent_index) |
119 | index = mux->data->parent_index[index]; |
120 | |
121 | regmap_read(map: mux->regmap, reg: mux->data->mux_ofs, val: &orig); |
122 | val = (orig & ~(mask << mux->data->mux_shift)) |
123 | | (index << mux->data->mux_shift); |
124 | |
125 | if (val != orig) { |
126 | regmap_write(map: mux->regmap, reg: mux->data->clr_ofs, |
127 | val: mask << mux->data->mux_shift); |
128 | regmap_write(map: mux->regmap, reg: mux->data->set_ofs, |
129 | val: index << mux->data->mux_shift); |
130 | |
131 | if (mux->data->upd_shift >= 0) { |
132 | regmap_write(map: mux->regmap, reg: mux->data->upd_ofs, |
133 | BIT(mux->data->upd_shift)); |
134 | mux->reparent = true; |
135 | } |
136 | } |
137 | |
138 | if (mux->lock) |
139 | spin_unlock_irqrestore(lock: mux->lock, flags); |
140 | else |
141 | __release(mux->lock); |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | static int mtk_clk_mux_determine_rate(struct clk_hw *hw, |
147 | struct clk_rate_request *req) |
148 | { |
149 | struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); |
150 | |
151 | return clk_mux_determine_rate_flags(hw, req, flags: mux->data->flags); |
152 | } |
153 | |
154 | const struct clk_ops mtk_mux_clr_set_upd_ops = { |
155 | .get_parent = mtk_clk_mux_get_parent, |
156 | .set_parent = mtk_clk_mux_set_parent_setclr_lock, |
157 | .determine_rate = mtk_clk_mux_determine_rate, |
158 | }; |
159 | EXPORT_SYMBOL_GPL(mtk_mux_clr_set_upd_ops); |
160 | |
161 | const struct clk_ops mtk_mux_gate_clr_set_upd_ops = { |
162 | .enable = mtk_clk_mux_enable_setclr, |
163 | .disable = mtk_clk_mux_disable_setclr, |
164 | .is_enabled = mtk_clk_mux_is_enabled, |
165 | .get_parent = mtk_clk_mux_get_parent, |
166 | .set_parent = mtk_clk_mux_set_parent_setclr_lock, |
167 | .determine_rate = mtk_clk_mux_determine_rate, |
168 | }; |
169 | EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops); |
170 | |
171 | static struct clk_hw *mtk_clk_register_mux(struct device *dev, |
172 | const struct mtk_mux *mux, |
173 | struct regmap *regmap, |
174 | spinlock_t *lock) |
175 | { |
176 | struct mtk_clk_mux *clk_mux; |
177 | struct clk_init_data init = {}; |
178 | int ret; |
179 | |
180 | clk_mux = kzalloc(size: sizeof(*clk_mux), GFP_KERNEL); |
181 | if (!clk_mux) |
182 | return ERR_PTR(error: -ENOMEM); |
183 | |
184 | init.name = mux->name; |
185 | init.flags = mux->flags; |
186 | init.parent_names = mux->parent_names; |
187 | init.num_parents = mux->num_parents; |
188 | init.ops = mux->ops; |
189 | |
190 | clk_mux->regmap = regmap; |
191 | clk_mux->data = mux; |
192 | clk_mux->lock = lock; |
193 | clk_mux->hw.init = &init; |
194 | |
195 | ret = clk_hw_register(dev, hw: &clk_mux->hw); |
196 | if (ret) { |
197 | kfree(objp: clk_mux); |
198 | return ERR_PTR(error: ret); |
199 | } |
200 | |
201 | return &clk_mux->hw; |
202 | } |
203 | |
204 | static void mtk_clk_unregister_mux(struct clk_hw *hw) |
205 | { |
206 | struct mtk_clk_mux *mux; |
207 | if (!hw) |
208 | return; |
209 | |
210 | mux = to_mtk_clk_mux(hw); |
211 | |
212 | clk_hw_unregister(hw); |
213 | kfree(objp: mux); |
214 | } |
215 | |
216 | int mtk_clk_register_muxes(struct device *dev, |
217 | const struct mtk_mux *muxes, |
218 | int num, struct device_node *node, |
219 | spinlock_t *lock, |
220 | struct clk_hw_onecell_data *clk_data) |
221 | { |
222 | struct regmap *regmap; |
223 | struct clk_hw *hw; |
224 | int i; |
225 | |
226 | regmap = device_node_to_regmap(np: node); |
227 | if (IS_ERR(ptr: regmap)) { |
228 | pr_err("Cannot find regmap for %pOF: %pe\n" , node, regmap); |
229 | return PTR_ERR(ptr: regmap); |
230 | } |
231 | |
232 | for (i = 0; i < num; i++) { |
233 | const struct mtk_mux *mux = &muxes[i]; |
234 | |
235 | if (!IS_ERR_OR_NULL(ptr: clk_data->hws[mux->id])) { |
236 | pr_warn("%pOF: Trying to register duplicate clock ID: %d\n" , |
237 | node, mux->id); |
238 | continue; |
239 | } |
240 | |
241 | hw = mtk_clk_register_mux(dev, mux, regmap, lock); |
242 | |
243 | if (IS_ERR(ptr: hw)) { |
244 | pr_err("Failed to register clk %s: %pe\n" , mux->name, |
245 | hw); |
246 | goto err; |
247 | } |
248 | |
249 | clk_data->hws[mux->id] = hw; |
250 | } |
251 | |
252 | return 0; |
253 | |
254 | err: |
255 | while (--i >= 0) { |
256 | const struct mtk_mux *mux = &muxes[i]; |
257 | |
258 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[mux->id])) |
259 | continue; |
260 | |
261 | mtk_clk_unregister_mux(hw: clk_data->hws[mux->id]); |
262 | clk_data->hws[mux->id] = ERR_PTR(error: -ENOENT); |
263 | } |
264 | |
265 | return PTR_ERR(ptr: hw); |
266 | } |
267 | EXPORT_SYMBOL_GPL(mtk_clk_register_muxes); |
268 | |
269 | void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num, |
270 | struct clk_hw_onecell_data *clk_data) |
271 | { |
272 | int i; |
273 | |
274 | if (!clk_data) |
275 | return; |
276 | |
277 | for (i = num; i > 0; i--) { |
278 | const struct mtk_mux *mux = &muxes[i - 1]; |
279 | |
280 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[mux->id])) |
281 | continue; |
282 | |
283 | mtk_clk_unregister_mux(hw: clk_data->hws[mux->id]); |
284 | clk_data->hws[mux->id] = ERR_PTR(error: -ENOENT); |
285 | } |
286 | } |
287 | EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes); |
288 | |
289 | /* |
290 | * This clock notifier is called when the frequency of the parent |
291 | * PLL clock is to be changed. The idea is to switch the parent to a |
292 | * stable clock, such as the main oscillator, while the PLL frequency |
293 | * stabilizes. |
294 | */ |
295 | static int mtk_clk_mux_notifier_cb(struct notifier_block *nb, |
296 | unsigned long event, void *_data) |
297 | { |
298 | struct clk_notifier_data *data = _data; |
299 | struct clk_hw *hw = __clk_get_hw(clk: data->clk); |
300 | struct mtk_mux_nb *mux_nb = to_mtk_mux_nb(nb); |
301 | int ret = 0; |
302 | |
303 | switch (event) { |
304 | case PRE_RATE_CHANGE: |
305 | mux_nb->original_index = mux_nb->ops->get_parent(hw); |
306 | ret = mux_nb->ops->set_parent(hw, mux_nb->bypass_index); |
307 | break; |
308 | case POST_RATE_CHANGE: |
309 | case ABORT_RATE_CHANGE: |
310 | ret = mux_nb->ops->set_parent(hw, mux_nb->original_index); |
311 | break; |
312 | } |
313 | |
314 | return notifier_from_errno(err: ret); |
315 | } |
316 | |
317 | int devm_mtk_clk_mux_notifier_register(struct device *dev, struct clk *clk, |
318 | struct mtk_mux_nb *mux_nb) |
319 | { |
320 | mux_nb->nb.notifier_call = mtk_clk_mux_notifier_cb; |
321 | |
322 | return devm_clk_notifier_register(dev, clk, nb: &mux_nb->nb); |
323 | } |
324 | EXPORT_SYMBOL_GPL(devm_mtk_clk_mux_notifier_register); |
325 | |
326 | MODULE_LICENSE("GPL" ); |
327 | |