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/of_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(sizeof(*clk_data), GFP_KERNEL);
72 	if (!clk_data)
73 		goto err;
74 
75 	clk_data->base = base;
76 	clk_table = kcalloc(nr_clks, 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, of_clk_src_onecell_get, &clk_data->clk_data);
83 	return clk_data;
84 err_data:
85 	kfree(clk_data);
86 err:
87 	return NULL;
88 }
89 EXPORT_SYMBOL_GPL(hisi_clk_init);
90 
hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock * clks,int nums,struct hisi_clock_data * data)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, clks[i].name,
99 					      clks[i].parent_name,
100 					      clks[i].flags,
101 					      clks[i].fixed_rate);
102 		if (IS_ERR(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(data->clk_data.clks[clks[i].id]);
115 
116 	return PTR_ERR(clk);
117 }
118 EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate);
119 
hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock * clks,int nums,struct hisi_clock_data * data)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, clks[i].name,
129 						clks[i].parent_name,
130 						clks[i].flags, clks[i].mult,
131 						clks[i].div);
132 		if (IS_ERR(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(data->clk_data.clks[clks[i].id]);
145 
146 	return PTR_ERR(clk);
147 }
148 EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor);
149 
hisi_clk_register_mux(const struct hisi_mux_clock * clks,int nums,struct hisi_clock_data * data)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, clks[i].name,
161 					clks[i].parent_names,
162 					clks[i].num_parents, clks[i].flags,
163 					base + clks[i].offset, clks[i].shift,
164 					mask, clks[i].mux_flags,
165 					(u32 *)clks[i].table, &hisi_clk_lock);
166 		if (IS_ERR(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(data->clk_data.clks[clks[i].id]);
183 
184 	return PTR_ERR(clk);
185 }
186 EXPORT_SYMBOL_GPL(hisi_clk_register_mux);
187 
hisi_clk_register_phase(struct device * dev,const struct hisi_phase_clock * clks,int nums,struct hisi_clock_data * data)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[i], base,
198 					      &hisi_clk_lock);
199 		if (IS_ERR(clk)) {
200 			pr_err("%s: failed to register clock %s\n", __func__,
201 			       clks[i].name);
202 			return PTR_ERR(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 
hisi_clk_register_divider(const struct hisi_divider_clock * clks,int nums,struct hisi_clock_data * data)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, clks[i].name,
221 						 clks[i].parent_name,
222 						 clks[i].flags,
223 						 base + clks[i].offset,
224 						 clks[i].shift, clks[i].width,
225 						 clks[i].div_flags,
226 						 clks[i].table,
227 						 &hisi_clk_lock);
228 		if (IS_ERR(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(data->clk_data.clks[clks[i].id]);
245 
246 	return PTR_ERR(clk);
247 }
248 EXPORT_SYMBOL_GPL(hisi_clk_register_divider);
249 
hisi_clk_register_gate(const struct hisi_gate_clock * clks,int nums,struct hisi_clock_data * data)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, clks[i].name,
259 						clks[i].parent_name,
260 						clks[i].flags,
261 						base + clks[i].offset,
262 						clks[i].bit_idx,
263 						clks[i].gate_flags,
264 						&hisi_clk_lock);
265 		if (IS_ERR(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(data->clk_data.clks[clks[i].id]);
282 
283 	return PTR_ERR(clk);
284 }
285 EXPORT_SYMBOL_GPL(hisi_clk_register_gate);
286 
hisi_clk_register_gate_sep(const struct hisi_gate_clock * clks,int nums,struct hisi_clock_data * data)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(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 
hi6220_clk_register_divider(const struct hi6220_divider_clock * clks,int nums,struct hisi_clock_data * data)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, clks[i].name,
325 						clks[i].parent_name,
326 						clks[i].flags,
327 						base + clks[i].offset,
328 						clks[i].shift,
329 						clks[i].width,
330 						clks[i].mask_bit,
331 						&hisi_clk_lock);
332 		if (IS_ERR(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