1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014 MediaTek Inc. |
4 | * Author: James Liao <jamesjj.liao@mediatek.com> |
5 | */ |
6 | |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/mfd/syscon.h> |
9 | #include <linux/module.h> |
10 | #include <linux/printk.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/types.h> |
14 | |
15 | #include "clk-gate.h" |
16 | |
17 | struct mtk_clk_gate { |
18 | struct clk_hw hw; |
19 | struct regmap *regmap; |
20 | int set_ofs; |
21 | int clr_ofs; |
22 | int sta_ofs; |
23 | u8 bit; |
24 | }; |
25 | |
26 | static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw) |
27 | { |
28 | return container_of(hw, struct mtk_clk_gate, hw); |
29 | } |
30 | |
31 | static u32 mtk_get_clockgating(struct clk_hw *hw) |
32 | { |
33 | struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); |
34 | u32 val; |
35 | |
36 | regmap_read(map: cg->regmap, reg: cg->sta_ofs, val: &val); |
37 | |
38 | return val & BIT(cg->bit); |
39 | } |
40 | |
41 | static int mtk_cg_bit_is_cleared(struct clk_hw *hw) |
42 | { |
43 | return mtk_get_clockgating(hw) == 0; |
44 | } |
45 | |
46 | static int mtk_cg_bit_is_set(struct clk_hw *hw) |
47 | { |
48 | return mtk_get_clockgating(hw) != 0; |
49 | } |
50 | |
51 | static void mtk_cg_set_bit(struct clk_hw *hw) |
52 | { |
53 | struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); |
54 | |
55 | regmap_write(map: cg->regmap, reg: cg->set_ofs, BIT(cg->bit)); |
56 | } |
57 | |
58 | static void mtk_cg_clr_bit(struct clk_hw *hw) |
59 | { |
60 | struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); |
61 | |
62 | regmap_write(map: cg->regmap, reg: cg->clr_ofs, BIT(cg->bit)); |
63 | } |
64 | |
65 | static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw) |
66 | { |
67 | struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); |
68 | |
69 | regmap_set_bits(map: cg->regmap, reg: cg->sta_ofs, BIT(cg->bit)); |
70 | } |
71 | |
72 | static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw) |
73 | { |
74 | struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); |
75 | |
76 | regmap_clear_bits(map: cg->regmap, reg: cg->sta_ofs, BIT(cg->bit)); |
77 | } |
78 | |
79 | static int mtk_cg_enable(struct clk_hw *hw) |
80 | { |
81 | mtk_cg_clr_bit(hw); |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static void mtk_cg_disable(struct clk_hw *hw) |
87 | { |
88 | mtk_cg_set_bit(hw); |
89 | } |
90 | |
91 | static int mtk_cg_enable_inv(struct clk_hw *hw) |
92 | { |
93 | mtk_cg_set_bit(hw); |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static void mtk_cg_disable_inv(struct clk_hw *hw) |
99 | { |
100 | mtk_cg_clr_bit(hw); |
101 | } |
102 | |
103 | static int mtk_cg_enable_no_setclr(struct clk_hw *hw) |
104 | { |
105 | mtk_cg_clr_bit_no_setclr(hw); |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static void mtk_cg_disable_no_setclr(struct clk_hw *hw) |
111 | { |
112 | mtk_cg_set_bit_no_setclr(hw); |
113 | } |
114 | |
115 | static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw) |
116 | { |
117 | mtk_cg_set_bit_no_setclr(hw); |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw) |
123 | { |
124 | mtk_cg_clr_bit_no_setclr(hw); |
125 | } |
126 | |
127 | const struct clk_ops mtk_clk_gate_ops_setclr = { |
128 | .is_enabled = mtk_cg_bit_is_cleared, |
129 | .enable = mtk_cg_enable, |
130 | .disable = mtk_cg_disable, |
131 | }; |
132 | EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr); |
133 | |
134 | const struct clk_ops mtk_clk_gate_ops_setclr_inv = { |
135 | .is_enabled = mtk_cg_bit_is_set, |
136 | .enable = mtk_cg_enable_inv, |
137 | .disable = mtk_cg_disable_inv, |
138 | }; |
139 | EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv); |
140 | |
141 | const struct clk_ops mtk_clk_gate_ops_no_setclr = { |
142 | .is_enabled = mtk_cg_bit_is_cleared, |
143 | .enable = mtk_cg_enable_no_setclr, |
144 | .disable = mtk_cg_disable_no_setclr, |
145 | }; |
146 | EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr); |
147 | |
148 | const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = { |
149 | .is_enabled = mtk_cg_bit_is_set, |
150 | .enable = mtk_cg_enable_inv_no_setclr, |
151 | .disable = mtk_cg_disable_inv_no_setclr, |
152 | }; |
153 | EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv); |
154 | |
155 | static struct clk_hw *mtk_clk_register_gate(struct device *dev, const char *name, |
156 | const char *parent_name, |
157 | struct regmap *regmap, int set_ofs, |
158 | int clr_ofs, int sta_ofs, u8 bit, |
159 | const struct clk_ops *ops, |
160 | unsigned long flags) |
161 | { |
162 | struct mtk_clk_gate *cg; |
163 | int ret; |
164 | struct clk_init_data init = {}; |
165 | |
166 | cg = kzalloc(size: sizeof(*cg), GFP_KERNEL); |
167 | if (!cg) |
168 | return ERR_PTR(error: -ENOMEM); |
169 | |
170 | init.name = name; |
171 | init.flags = flags | CLK_SET_RATE_PARENT; |
172 | init.parent_names = parent_name ? &parent_name : NULL; |
173 | init.num_parents = parent_name ? 1 : 0; |
174 | init.ops = ops; |
175 | |
176 | cg->regmap = regmap; |
177 | cg->set_ofs = set_ofs; |
178 | cg->clr_ofs = clr_ofs; |
179 | cg->sta_ofs = sta_ofs; |
180 | cg->bit = bit; |
181 | |
182 | cg->hw.init = &init; |
183 | |
184 | ret = clk_hw_register(dev, hw: &cg->hw); |
185 | if (ret) { |
186 | kfree(objp: cg); |
187 | return ERR_PTR(error: ret); |
188 | } |
189 | |
190 | return &cg->hw; |
191 | } |
192 | |
193 | static void mtk_clk_unregister_gate(struct clk_hw *hw) |
194 | { |
195 | struct mtk_clk_gate *cg; |
196 | if (!hw) |
197 | return; |
198 | |
199 | cg = to_mtk_clk_gate(hw); |
200 | |
201 | clk_hw_unregister(hw); |
202 | kfree(objp: cg); |
203 | } |
204 | |
205 | int mtk_clk_register_gates(struct device *dev, struct device_node *node, |
206 | const struct mtk_gate *clks, int num, |
207 | struct clk_hw_onecell_data *clk_data) |
208 | { |
209 | int i; |
210 | struct clk_hw *hw; |
211 | struct regmap *regmap; |
212 | |
213 | if (!clk_data) |
214 | return -ENOMEM; |
215 | |
216 | regmap = device_node_to_regmap(np: node); |
217 | if (IS_ERR(ptr: regmap)) { |
218 | pr_err("Cannot find regmap for %pOF: %pe\n" , node, regmap); |
219 | return PTR_ERR(ptr: regmap); |
220 | } |
221 | |
222 | for (i = 0; i < num; i++) { |
223 | const struct mtk_gate *gate = &clks[i]; |
224 | |
225 | if (!IS_ERR_OR_NULL(ptr: clk_data->hws[gate->id])) { |
226 | pr_warn("%pOF: Trying to register duplicate clock ID: %d\n" , |
227 | node, gate->id); |
228 | continue; |
229 | } |
230 | |
231 | hw = mtk_clk_register_gate(dev, name: gate->name, parent_name: gate->parent_name, |
232 | regmap, |
233 | set_ofs: gate->regs->set_ofs, |
234 | clr_ofs: gate->regs->clr_ofs, |
235 | sta_ofs: gate->regs->sta_ofs, |
236 | bit: gate->shift, ops: gate->ops, |
237 | flags: gate->flags); |
238 | |
239 | if (IS_ERR(ptr: hw)) { |
240 | pr_err("Failed to register clk %s: %pe\n" , gate->name, |
241 | hw); |
242 | goto err; |
243 | } |
244 | |
245 | clk_data->hws[gate->id] = hw; |
246 | } |
247 | |
248 | return 0; |
249 | |
250 | err: |
251 | while (--i >= 0) { |
252 | const struct mtk_gate *gate = &clks[i]; |
253 | |
254 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[gate->id])) |
255 | continue; |
256 | |
257 | mtk_clk_unregister_gate(hw: clk_data->hws[gate->id]); |
258 | clk_data->hws[gate->id] = ERR_PTR(error: -ENOENT); |
259 | } |
260 | |
261 | return PTR_ERR(ptr: hw); |
262 | } |
263 | EXPORT_SYMBOL_GPL(mtk_clk_register_gates); |
264 | |
265 | void mtk_clk_unregister_gates(const struct mtk_gate *clks, int num, |
266 | struct clk_hw_onecell_data *clk_data) |
267 | { |
268 | int i; |
269 | |
270 | if (!clk_data) |
271 | return; |
272 | |
273 | for (i = num; i > 0; i--) { |
274 | const struct mtk_gate *gate = &clks[i - 1]; |
275 | |
276 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[gate->id])) |
277 | continue; |
278 | |
279 | mtk_clk_unregister_gate(hw: clk_data->hws[gate->id]); |
280 | clk_data->hws[gate->id] = ERR_PTR(error: -ENOENT); |
281 | } |
282 | } |
283 | EXPORT_SYMBOL_GPL(mtk_clk_unregister_gates); |
284 | |
285 | MODULE_LICENSE("GPL" ); |
286 | |