1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Hisilicon clock driver |
4 | * |
5 | * Copyright (c) 2012-2013 Hisilicon Limited. |
6 | * Copyright (c) 2012-2013 Linaro Limited. |
7 | * |
8 | * Author: Haojian Zhuang <haojian.zhuang@linaro.org> |
9 | * Xin Li <li.xin@linaro.org> |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/clkdev.h> |
14 | #include <linux/clk-provider.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/io.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include "clk.h" |
23 | |
24 | static DEFINE_SPINLOCK(hisi_clk_lock); |
25 | |
26 | struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev, |
27 | int nr_clks) |
28 | { |
29 | struct hisi_clock_data *clk_data; |
30 | struct resource *res; |
31 | struct clk **clk_table; |
32 | |
33 | clk_data = devm_kmalloc(dev: &pdev->dev, size: sizeof(*clk_data), GFP_KERNEL); |
34 | if (!clk_data) |
35 | return NULL; |
36 | |
37 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
38 | if (!res) |
39 | return NULL; |
40 | clk_data->base = devm_ioremap(dev: &pdev->dev, |
41 | offset: res->start, size: resource_size(res)); |
42 | if (!clk_data->base) |
43 | return NULL; |
44 | |
45 | clk_table = devm_kmalloc_array(dev: &pdev->dev, n: nr_clks, |
46 | size: sizeof(*clk_table), |
47 | GFP_KERNEL); |
48 | if (!clk_table) |
49 | return NULL; |
50 | |
51 | clk_data->clk_data.clks = clk_table; |
52 | clk_data->clk_data.clk_num = nr_clks; |
53 | |
54 | return clk_data; |
55 | } |
56 | EXPORT_SYMBOL_GPL(hisi_clk_alloc); |
57 | |
58 | struct hisi_clock_data *hisi_clk_init(struct device_node *np, |
59 | int nr_clks) |
60 | { |
61 | struct hisi_clock_data *clk_data; |
62 | struct clk **clk_table; |
63 | void __iomem *base; |
64 | |
65 | base = of_iomap(node: np, index: 0); |
66 | if (!base) { |
67 | pr_err("%s: failed to map clock registers\n" , __func__); |
68 | goto err; |
69 | } |
70 | |
71 | clk_data = kzalloc(size: sizeof(*clk_data), GFP_KERNEL); |
72 | if (!clk_data) |
73 | goto err; |
74 | |
75 | clk_data->base = base; |
76 | clk_table = kcalloc(n: nr_clks, size: sizeof(*clk_table), GFP_KERNEL); |
77 | if (!clk_table) |
78 | goto err_data; |
79 | |
80 | clk_data->clk_data.clks = clk_table; |
81 | clk_data->clk_data.clk_num = nr_clks; |
82 | of_clk_add_provider(np, clk_src_get: of_clk_src_onecell_get, data: &clk_data->clk_data); |
83 | return clk_data; |
84 | err_data: |
85 | kfree(objp: clk_data); |
86 | err: |
87 | return NULL; |
88 | } |
89 | EXPORT_SYMBOL_GPL(hisi_clk_init); |
90 | |
91 | int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks, |
92 | int nums, struct hisi_clock_data *data) |
93 | { |
94 | struct clk *clk; |
95 | int i; |
96 | |
97 | for (i = 0; i < nums; i++) { |
98 | clk = clk_register_fixed_rate(NULL, name: clks[i].name, |
99 | parent_name: clks[i].parent_name, |
100 | flags: clks[i].flags, |
101 | fixed_rate: clks[i].fixed_rate); |
102 | if (IS_ERR(ptr: clk)) { |
103 | pr_err("%s: failed to register clock %s\n" , |
104 | __func__, clks[i].name); |
105 | goto err; |
106 | } |
107 | data->clk_data.clks[clks[i].id] = clk; |
108 | } |
109 | |
110 | return 0; |
111 | |
112 | err: |
113 | while (i--) |
114 | clk_unregister_fixed_rate(clk: data->clk_data.clks[clks[i].id]); |
115 | |
116 | return PTR_ERR(ptr: clk); |
117 | } |
118 | EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate); |
119 | |
120 | int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks, |
121 | int nums, |
122 | struct hisi_clock_data *data) |
123 | { |
124 | struct clk *clk; |
125 | int i; |
126 | |
127 | for (i = 0; i < nums; i++) { |
128 | clk = clk_register_fixed_factor(NULL, name: clks[i].name, |
129 | parent_name: clks[i].parent_name, |
130 | flags: clks[i].flags, mult: clks[i].mult, |
131 | div: clks[i].div); |
132 | if (IS_ERR(ptr: clk)) { |
133 | pr_err("%s: failed to register clock %s\n" , |
134 | __func__, clks[i].name); |
135 | goto err; |
136 | } |
137 | data->clk_data.clks[clks[i].id] = clk; |
138 | } |
139 | |
140 | return 0; |
141 | |
142 | err: |
143 | while (i--) |
144 | clk_unregister_fixed_factor(clk: data->clk_data.clks[clks[i].id]); |
145 | |
146 | return PTR_ERR(ptr: clk); |
147 | } |
148 | EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor); |
149 | |
150 | int hisi_clk_register_mux(const struct hisi_mux_clock *clks, |
151 | int nums, struct hisi_clock_data *data) |
152 | { |
153 | struct clk *clk; |
154 | void __iomem *base = data->base; |
155 | int i; |
156 | |
157 | for (i = 0; i < nums; i++) { |
158 | u32 mask = BIT(clks[i].width) - 1; |
159 | |
160 | clk = clk_register_mux_table(NULL, name: clks[i].name, |
161 | parent_names: clks[i].parent_names, |
162 | num_parents: clks[i].num_parents, flags: clks[i].flags, |
163 | reg: base + clks[i].offset, shift: clks[i].shift, |
164 | mask, clk_mux_flags: clks[i].mux_flags, |
165 | table: clks[i].table, lock: &hisi_clk_lock); |
166 | if (IS_ERR(ptr: clk)) { |
167 | pr_err("%s: failed to register clock %s\n" , |
168 | __func__, clks[i].name); |
169 | goto err; |
170 | } |
171 | |
172 | if (clks[i].alias) |
173 | clk_register_clkdev(clk, clks[i].alias, NULL); |
174 | |
175 | data->clk_data.clks[clks[i].id] = clk; |
176 | } |
177 | |
178 | return 0; |
179 | |
180 | err: |
181 | while (i--) |
182 | clk_unregister_mux(clk: data->clk_data.clks[clks[i].id]); |
183 | |
184 | return PTR_ERR(ptr: clk); |
185 | } |
186 | EXPORT_SYMBOL_GPL(hisi_clk_register_mux); |
187 | |
188 | int hisi_clk_register_phase(struct device *dev, |
189 | const struct hisi_phase_clock *clks, |
190 | int nums, struct hisi_clock_data *data) |
191 | { |
192 | void __iomem *base = data->base; |
193 | struct clk *clk; |
194 | int i; |
195 | |
196 | for (i = 0; i < nums; i++) { |
197 | clk = clk_register_hisi_phase(dev, clks: &clks[i], base, |
198 | lock: &hisi_clk_lock); |
199 | if (IS_ERR(ptr: clk)) { |
200 | pr_err("%s: failed to register clock %s\n" , __func__, |
201 | clks[i].name); |
202 | return PTR_ERR(ptr: clk); |
203 | } |
204 | |
205 | data->clk_data.clks[clks[i].id] = clk; |
206 | } |
207 | |
208 | return 0; |
209 | } |
210 | EXPORT_SYMBOL_GPL(hisi_clk_register_phase); |
211 | |
212 | int hisi_clk_register_divider(const struct hisi_divider_clock *clks, |
213 | int nums, struct hisi_clock_data *data) |
214 | { |
215 | struct clk *clk; |
216 | void __iomem *base = data->base; |
217 | int i; |
218 | |
219 | for (i = 0; i < nums; i++) { |
220 | clk = clk_register_divider_table(NULL, name: clks[i].name, |
221 | parent_name: clks[i].parent_name, |
222 | flags: clks[i].flags, |
223 | reg: base + clks[i].offset, |
224 | shift: clks[i].shift, width: clks[i].width, |
225 | clk_divider_flags: clks[i].div_flags, |
226 | table: clks[i].table, |
227 | lock: &hisi_clk_lock); |
228 | if (IS_ERR(ptr: clk)) { |
229 | pr_err("%s: failed to register clock %s\n" , |
230 | __func__, clks[i].name); |
231 | goto err; |
232 | } |
233 | |
234 | if (clks[i].alias) |
235 | clk_register_clkdev(clk, clks[i].alias, NULL); |
236 | |
237 | data->clk_data.clks[clks[i].id] = clk; |
238 | } |
239 | |
240 | return 0; |
241 | |
242 | err: |
243 | while (i--) |
244 | clk_unregister_divider(clk: data->clk_data.clks[clks[i].id]); |
245 | |
246 | return PTR_ERR(ptr: clk); |
247 | } |
248 | EXPORT_SYMBOL_GPL(hisi_clk_register_divider); |
249 | |
250 | int hisi_clk_register_gate(const struct hisi_gate_clock *clks, |
251 | int nums, struct hisi_clock_data *data) |
252 | { |
253 | struct clk *clk; |
254 | void __iomem *base = data->base; |
255 | int i; |
256 | |
257 | for (i = 0; i < nums; i++) { |
258 | clk = clk_register_gate(NULL, name: clks[i].name, |
259 | parent_name: clks[i].parent_name, |
260 | flags: clks[i].flags, |
261 | reg: base + clks[i].offset, |
262 | bit_idx: clks[i].bit_idx, |
263 | clk_gate_flags: clks[i].gate_flags, |
264 | lock: &hisi_clk_lock); |
265 | if (IS_ERR(ptr: clk)) { |
266 | pr_err("%s: failed to register clock %s\n" , |
267 | __func__, clks[i].name); |
268 | goto err; |
269 | } |
270 | |
271 | if (clks[i].alias) |
272 | clk_register_clkdev(clk, clks[i].alias, NULL); |
273 | |
274 | data->clk_data.clks[clks[i].id] = clk; |
275 | } |
276 | |
277 | return 0; |
278 | |
279 | err: |
280 | while (i--) |
281 | clk_unregister_gate(clk: data->clk_data.clks[clks[i].id]); |
282 | |
283 | return PTR_ERR(ptr: clk); |
284 | } |
285 | EXPORT_SYMBOL_GPL(hisi_clk_register_gate); |
286 | |
287 | void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, |
288 | int nums, struct hisi_clock_data *data) |
289 | { |
290 | struct clk *clk; |
291 | void __iomem *base = data->base; |
292 | int i; |
293 | |
294 | for (i = 0; i < nums; i++) { |
295 | clk = hisi_register_clkgate_sep(NULL, clks[i].name, |
296 | clks[i].parent_name, |
297 | clks[i].flags, |
298 | base + clks[i].offset, |
299 | clks[i].bit_idx, |
300 | clks[i].gate_flags, |
301 | &hisi_clk_lock); |
302 | if (IS_ERR(ptr: clk)) { |
303 | pr_err("%s: failed to register clock %s\n" , |
304 | __func__, clks[i].name); |
305 | continue; |
306 | } |
307 | |
308 | if (clks[i].alias) |
309 | clk_register_clkdev(clk, clks[i].alias, NULL); |
310 | |
311 | data->clk_data.clks[clks[i].id] = clk; |
312 | } |
313 | } |
314 | EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep); |
315 | |
316 | void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, |
317 | int nums, struct hisi_clock_data *data) |
318 | { |
319 | struct clk *clk; |
320 | void __iomem *base = data->base; |
321 | int i; |
322 | |
323 | for (i = 0; i < nums; i++) { |
324 | clk = hi6220_register_clkdiv(NULL, name: clks[i].name, |
325 | parent_name: clks[i].parent_name, |
326 | flags: clks[i].flags, |
327 | reg: base + clks[i].offset, |
328 | shift: clks[i].shift, |
329 | width: clks[i].width, |
330 | mask_bit: clks[i].mask_bit, |
331 | lock: &hisi_clk_lock); |
332 | if (IS_ERR(ptr: clk)) { |
333 | pr_err("%s: failed to register clock %s\n" , |
334 | __func__, clks[i].name); |
335 | continue; |
336 | } |
337 | |
338 | if (clks[i].alias) |
339 | clk_register_clkdev(clk, clks[i].alias, NULL); |
340 | |
341 | data->clk_data.clks[clks[i].id] = clk; |
342 | } |
343 | } |
344 | |