]> Git Repo - linux.git/blob - drivers/clk/mediatek/clk-mux.c
Merge tag 'audit-pr-20221003' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoor...
[linux.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 <[email protected]>
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_hw *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         int ret;
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         ret = clk_hw_register(NULL, &clk_mux->hw);
170         if (ret) {
171                 kfree(clk_mux);
172                 return ERR_PTR(ret);
173         }
174
175         return &clk_mux->hw;
176 }
177
178 static void mtk_clk_unregister_mux(struct clk_hw *hw)
179 {
180         struct mtk_clk_mux *mux;
181         if (!hw)
182                 return;
183
184         mux = to_mtk_clk_mux(hw);
185
186         clk_hw_unregister(hw);
187         kfree(mux);
188 }
189
190 int mtk_clk_register_muxes(const struct mtk_mux *muxes,
191                            int num, struct device_node *node,
192                            spinlock_t *lock,
193                            struct clk_hw_onecell_data *clk_data)
194 {
195         struct regmap *regmap;
196         struct clk_hw *hw;
197         int i;
198
199         regmap = device_node_to_regmap(node);
200         if (IS_ERR(regmap)) {
201                 pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
202                 return PTR_ERR(regmap);
203         }
204
205         for (i = 0; i < num; i++) {
206                 const struct mtk_mux *mux = &muxes[i];
207
208                 if (!IS_ERR_OR_NULL(clk_data->hws[mux->id])) {
209                         pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
210                                 node, mux->id);
211                         continue;
212                 }
213
214                 hw = mtk_clk_register_mux(mux, regmap, lock);
215
216                 if (IS_ERR(hw)) {
217                         pr_err("Failed to register clk %s: %pe\n", mux->name,
218                                hw);
219                         goto err;
220                 }
221
222                 clk_data->hws[mux->id] = hw;
223         }
224
225         return 0;
226
227 err:
228         while (--i >= 0) {
229                 const struct mtk_mux *mux = &muxes[i];
230
231                 if (IS_ERR_OR_NULL(clk_data->hws[mux->id]))
232                         continue;
233
234                 mtk_clk_unregister_mux(clk_data->hws[mux->id]);
235                 clk_data->hws[mux->id] = ERR_PTR(-ENOENT);
236         }
237
238         return PTR_ERR(hw);
239 }
240 EXPORT_SYMBOL_GPL(mtk_clk_register_muxes);
241
242 void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num,
243                               struct clk_hw_onecell_data *clk_data)
244 {
245         int i;
246
247         if (!clk_data)
248                 return;
249
250         for (i = num; i > 0; i--) {
251                 const struct mtk_mux *mux = &muxes[i - 1];
252
253                 if (IS_ERR_OR_NULL(clk_data->hws[mux->id]))
254                         continue;
255
256                 mtk_clk_unregister_mux(clk_data->hws[mux->id]);
257                 clk_data->hws[mux->id] = ERR_PTR(-ENOENT);
258         }
259 }
260 EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes);
261
262 MODULE_LICENSE("GPL");
This page took 0.048548 seconds and 4 git commands to generate.