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
hisi_clk_alloc(struct platform_device * pdev,int nr_clks)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(&pdev->dev, 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(&pdev->dev,
41 res->start, resource_size(res));
42 if (!clk_data->base)
43 return NULL;
44
45 clk_table = devm_kmalloc_array(&pdev->dev, nr_clks,
46 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
hisi_clk_init(struct device_node * np,int nr_clks)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(np, 0);
66 if (!base) {
67 pr_err("%s: failed to map clock registers\n", __func__);
68 goto err;
69 }
70
71 clk_data = kzalloc_obj(*clk_data);
72 if (!clk_data)
73 goto err_base;
74
75 clk_data->base = base;
76 clk_table = kzalloc_objs(*clk_table, nr_clks);
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, of_clk_src_onecell_get, &clk_data->clk_data);
83 return clk_data;
84 err_data:
85 kfree(clk_data);
86 err_base:
87 iounmap(base);
88 err:
89 return NULL;
90 }
91 EXPORT_SYMBOL_GPL(hisi_clk_init);
92
hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock * clks,int nums,struct hisi_clock_data * data)93 int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks,
94 int nums, struct hisi_clock_data *data)
95 {
96 struct clk *clk;
97 int i;
98
99 for (i = 0; i < nums; i++) {
100 clk = clk_register_fixed_rate(NULL, clks[i].name,
101 clks[i].parent_name,
102 clks[i].flags,
103 clks[i].fixed_rate);
104 if (IS_ERR(clk)) {
105 pr_err("%s: failed to register clock %s\n",
106 __func__, clks[i].name);
107 goto err;
108 }
109 data->clk_data.clks[clks[i].id] = clk;
110 }
111
112 return 0;
113
114 err:
115 while (i--)
116 clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]);
117
118 return PTR_ERR(clk);
119 }
120 EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate);
121
hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock * clks,int nums,struct hisi_clock_data * data)122 int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks,
123 int nums,
124 struct hisi_clock_data *data)
125 {
126 struct clk *clk;
127 int i;
128
129 for (i = 0; i < nums; i++) {
130 clk = clk_register_fixed_factor(NULL, clks[i].name,
131 clks[i].parent_name,
132 clks[i].flags, clks[i].mult,
133 clks[i].div);
134 if (IS_ERR(clk)) {
135 pr_err("%s: failed to register clock %s\n",
136 __func__, clks[i].name);
137 goto err;
138 }
139 data->clk_data.clks[clks[i].id] = clk;
140 }
141
142 return 0;
143
144 err:
145 while (i--)
146 clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]);
147
148 return PTR_ERR(clk);
149 }
150 EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor);
151
hisi_clk_register_mux(const struct hisi_mux_clock * clks,int nums,struct hisi_clock_data * data)152 int hisi_clk_register_mux(const struct hisi_mux_clock *clks,
153 int nums, struct hisi_clock_data *data)
154 {
155 struct clk *clk;
156 void __iomem *base = data->base;
157 int i;
158
159 for (i = 0; i < nums; i++) {
160 u32 mask = BIT(clks[i].width) - 1;
161
162 clk = clk_register_mux_table(NULL, clks[i].name,
163 clks[i].parent_names,
164 clks[i].num_parents, clks[i].flags,
165 base + clks[i].offset, clks[i].shift,
166 mask, clks[i].mux_flags,
167 clks[i].table, &hisi_clk_lock);
168 if (IS_ERR(clk)) {
169 pr_err("%s: failed to register clock %s\n",
170 __func__, clks[i].name);
171 goto err;
172 }
173
174 if (clks[i].alias)
175 clk_register_clkdev(clk, clks[i].alias, NULL);
176
177 data->clk_data.clks[clks[i].id] = clk;
178 }
179
180 return 0;
181
182 err:
183 while (i--)
184 clk_unregister_mux(data->clk_data.clks[clks[i].id]);
185
186 return PTR_ERR(clk);
187 }
188 EXPORT_SYMBOL_GPL(hisi_clk_register_mux);
189
hisi_clk_register_phase(struct device * dev,const struct hisi_phase_clock * clks,int nums,struct hisi_clock_data * data)190 int hisi_clk_register_phase(struct device *dev,
191 const struct hisi_phase_clock *clks,
192 int nums, struct hisi_clock_data *data)
193 {
194 void __iomem *base = data->base;
195 struct clk *clk;
196 int i;
197
198 for (i = 0; i < nums; i++) {
199 clk = clk_register_hisi_phase(dev, &clks[i], base,
200 &hisi_clk_lock);
201 if (IS_ERR(clk)) {
202 pr_err("%s: failed to register clock %s\n", __func__,
203 clks[i].name);
204 return PTR_ERR(clk);
205 }
206
207 data->clk_data.clks[clks[i].id] = clk;
208 }
209
210 return 0;
211 }
212 EXPORT_SYMBOL_GPL(hisi_clk_register_phase);
213
hisi_clk_register_divider(const struct hisi_divider_clock * clks,int nums,struct hisi_clock_data * data)214 int hisi_clk_register_divider(const struct hisi_divider_clock *clks,
215 int nums, struct hisi_clock_data *data)
216 {
217 struct clk *clk;
218 void __iomem *base = data->base;
219 int i;
220
221 for (i = 0; i < nums; i++) {
222 clk = clk_register_divider_table(NULL, clks[i].name,
223 clks[i].parent_name,
224 clks[i].flags,
225 base + clks[i].offset,
226 clks[i].shift, clks[i].width,
227 clks[i].div_flags,
228 clks[i].table,
229 &hisi_clk_lock);
230 if (IS_ERR(clk)) {
231 pr_err("%s: failed to register clock %s\n",
232 __func__, clks[i].name);
233 goto err;
234 }
235
236 if (clks[i].alias)
237 clk_register_clkdev(clk, clks[i].alias, NULL);
238
239 data->clk_data.clks[clks[i].id] = clk;
240 }
241
242 return 0;
243
244 err:
245 while (i--)
246 clk_unregister_divider(data->clk_data.clks[clks[i].id]);
247
248 return PTR_ERR(clk);
249 }
250 EXPORT_SYMBOL_GPL(hisi_clk_register_divider);
251
hisi_clk_register_gate(const struct hisi_gate_clock * clks,int nums,struct hisi_clock_data * data)252 int hisi_clk_register_gate(const struct hisi_gate_clock *clks,
253 int nums, struct hisi_clock_data *data)
254 {
255 struct clk *clk;
256 void __iomem *base = data->base;
257 int i;
258
259 for (i = 0; i < nums; i++) {
260 clk = clk_register_gate(NULL, clks[i].name,
261 clks[i].parent_name,
262 clks[i].flags,
263 base + clks[i].offset,
264 clks[i].bit_idx,
265 clks[i].gate_flags,
266 &hisi_clk_lock);
267 if (IS_ERR(clk)) {
268 pr_err("%s: failed to register clock %s\n",
269 __func__, clks[i].name);
270 goto err;
271 }
272
273 if (clks[i].alias)
274 clk_register_clkdev(clk, clks[i].alias, NULL);
275
276 data->clk_data.clks[clks[i].id] = clk;
277 }
278
279 return 0;
280
281 err:
282 while (i--)
283 clk_unregister_gate(data->clk_data.clks[clks[i].id]);
284
285 return PTR_ERR(clk);
286 }
287 EXPORT_SYMBOL_GPL(hisi_clk_register_gate);
288
hisi_clk_register_gate_sep(const struct hisi_gate_clock * clks,int nums,struct hisi_clock_data * data)289 void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks,
290 int nums, struct hisi_clock_data *data)
291 {
292 struct clk *clk;
293 void __iomem *base = data->base;
294 int i;
295
296 for (i = 0; i < nums; i++) {
297 clk = hisi_register_clkgate_sep(NULL, clks[i].name,
298 clks[i].parent_name,
299 clks[i].flags,
300 base + clks[i].offset,
301 clks[i].bit_idx,
302 clks[i].gate_flags,
303 &hisi_clk_lock);
304 if (IS_ERR(clk)) {
305 pr_err("%s: failed to register clock %s\n",
306 __func__, clks[i].name);
307 continue;
308 }
309
310 if (clks[i].alias)
311 clk_register_clkdev(clk, clks[i].alias, NULL);
312
313 data->clk_data.clks[clks[i].id] = clk;
314 }
315 }
316 EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep);
317
hi6220_clk_register_divider(const struct hi6220_divider_clock * clks,int nums,struct hisi_clock_data * data)318 void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks,
319 int nums, struct hisi_clock_data *data)
320 {
321 struct clk *clk;
322 void __iomem *base = data->base;
323 int i;
324
325 for (i = 0; i < nums; i++) {
326 clk = hi6220_register_clkdiv(NULL, clks[i].name,
327 clks[i].parent_name,
328 clks[i].flags,
329 base + clks[i].offset,
330 clks[i].shift,
331 clks[i].width,
332 clks[i].mask_bit,
333 &hisi_clk_lock);
334 if (IS_ERR(clk)) {
335 pr_err("%s: failed to register clock %s\n",
336 __func__, clks[i].name);
337 continue;
338 }
339
340 if (clks[i].alias)
341 clk_register_clkdev(clk, clks[i].alias, NULL);
342
343 data->clk_data.clks[clks[i].id] = clk;
344 }
345 }
346