blob: 54d9fdc93599079112070a24cd730c60d7121b73 [file] [log] [blame]
Thomas Gleixner16216332019-05-19 15:51:31 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Haojian Zhuang0aa0c952013-11-13 08:51:23 +08002/*
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>
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080010 */
11
12#include <linux/kernel.h>
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080013#include <linux/clkdev.h>
Stephen Boyd593438e2015-06-19 15:00:46 -070014#include <linux/clk-provider.h>
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080015#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>
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080021
22#include "clk.h"
23
24static DEFINE_SPINLOCK(hisi_clk_lock);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080025
Jiancheng Xue322269162016-06-15 14:26:35 +080026struct 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);
Wei Yongjunc744b632018-03-20 14:19:34 +000038 if (!res)
39 return NULL;
Jiancheng Xue322269162016-06-15 14:26:35 +080040 clk_data->base = devm_ioremap(&pdev->dev,
41 res->start, resource_size(res));
42 if (!clk_data->base)
43 return NULL;
44
Markus Elfring8d9bdc42017-04-18 10:15:19 +020045 clk_table = devm_kmalloc_array(&pdev->dev, nr_clks,
46 sizeof(*clk_table),
47 GFP_KERNEL);
Jiancheng Xue322269162016-06-15 14:26:35 +080048 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}
56EXPORT_SYMBOL_GPL(hisi_clk_alloc);
57
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +080058struct hisi_clock_data *hisi_clk_init(struct device_node *np,
Haojian Zhuang75af25f2013-12-24 21:38:26 +080059 int nr_clks)
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080060{
Haojian Zhuang75af25f2013-12-24 21:38:26 +080061 struct hisi_clock_data *clk_data;
62 struct clk **clk_table;
63 void __iomem *base;
64
Leo Yan1fb6dd92015-08-03 09:13:34 +080065 base = of_iomap(np, 0);
66 if (!base) {
67 pr_err("%s: failed to map clock registers\n", __func__);
Haojian Zhuang75af25f2013-12-24 21:38:26 +080068 goto err;
69 }
70
71 clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
Markus Elfring840e5632017-04-18 10:12:32 +020072 if (!clk_data)
Haojian Zhuang75af25f2013-12-24 21:38:26 +080073 goto err;
Markus Elfring840e5632017-04-18 10:12:32 +020074
Haojian Zhuang75af25f2013-12-24 21:38:26 +080075 clk_data->base = base;
Markus Elfring7b9bae12017-04-18 09:25:47 +020076 clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL);
Markus Elfring840e5632017-04-18 10:12:32 +020077 if (!clk_table)
Haojian Zhuang75af25f2013-12-24 21:38:26 +080078 goto err_data;
Markus Elfring840e5632017-04-18 10:12:32 +020079
Haojian Zhuang75af25f2013-12-24 21:38:26 +080080 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;
84err_data:
85 kfree(clk_data);
86err:
87 return NULL;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080088}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +080089EXPORT_SYMBOL_GPL(hisi_clk_init);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080090
Jiancheng Xue5497f662016-06-15 14:26:36 +080091int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks,
Haojian Zhuang75af25f2013-12-24 21:38:26 +080092 int nums, struct hisi_clock_data *data)
Haojian Zhuang0aa0c952013-11-13 08:51:23 +080093{
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);
Jiancheng Xue5497f662016-06-15 14:26:36 +0800105 goto err;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800106 }
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800107 data->clk_data.clks[clks[i].id] = clk;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800108 }
Jiancheng Xue5497f662016-06-15 14:26:36 +0800109
110 return 0;
111
112err:
113 while (i--)
114 clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]);
115
116 return PTR_ERR(clk);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800117}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800118EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800119
Jiancheng Xue5497f662016-06-15 14:26:36 +0800120int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks,
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800121 int nums,
122 struct hisi_clock_data *data)
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800123{
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);
Jiancheng Xue5497f662016-06-15 14:26:36 +0800135 goto err;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800136 }
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800137 data->clk_data.clks[clks[i].id] = clk;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800138 }
Jiancheng Xue5497f662016-06-15 14:26:36 +0800139
140 return 0;
141
142err:
143 while (i--)
144 clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]);
145
146 return PTR_ERR(clk);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800147}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800148EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800149
Jiancheng Xue5497f662016-06-15 14:26:36 +0800150int hisi_clk_register_mux(const struct hisi_mux_clock *clks,
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800151 int nums, struct hisi_clock_data *data)
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800152{
153 struct clk *clk;
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800154 void __iomem *base = data->base;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800155 int i;
156
157 for (i = 0; i < nums; i++) {
Zhangfei Gao156342a2014-04-21 11:35:21 +0800158 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 clks[i].table, &hisi_clk_lock);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800166 if (IS_ERR(clk)) {
167 pr_err("%s: failed to register clock %s\n",
168 __func__, clks[i].name);
Jiancheng Xue5497f662016-06-15 14:26:36 +0800169 goto err;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800170 }
171
172 if (clks[i].alias)
173 clk_register_clkdev(clk, clks[i].alias, NULL);
174
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800175 data->clk_data.clks[clks[i].id] = clk;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800176 }
Jiancheng Xue5497f662016-06-15 14:26:36 +0800177
178 return 0;
179
180err:
181 while (i--)
182 clk_unregister_mux(data->clk_data.clks[clks[i].id]);
183
184 return PTR_ERR(clk);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800185}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800186EXPORT_SYMBOL_GPL(hisi_clk_register_mux);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800187
tianshuliang811f67c2018-03-05 15:01:31 +0800188int 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}
210EXPORT_SYMBOL_GPL(hisi_clk_register_phase);
211
Jiancheng Xue5497f662016-06-15 14:26:36 +0800212int hisi_clk_register_divider(const struct hisi_divider_clock *clks,
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800213 int nums, struct hisi_clock_data *data)
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800214{
215 struct clk *clk;
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800216 void __iomem *base = data->base;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800217 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);
Jiancheng Xue5497f662016-06-15 14:26:36 +0800231 goto err;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800232 }
233
234 if (clks[i].alias)
235 clk_register_clkdev(clk, clks[i].alias, NULL);
236
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800237 data->clk_data.clks[clks[i].id] = clk;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800238 }
Jiancheng Xue5497f662016-06-15 14:26:36 +0800239
240 return 0;
241
242err:
243 while (i--)
244 clk_unregister_divider(data->clk_data.clks[clks[i].id]);
245
246 return PTR_ERR(clk);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800247}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800248EXPORT_SYMBOL_GPL(hisi_clk_register_divider);
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800249
Jiancheng Xue5497f662016-06-15 14:26:36 +0800250int hisi_clk_register_gate(const struct hisi_gate_clock *clks,
Zhangfei Gao8b9dcb6c2014-04-22 15:42:47 +0800251 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);
Jiancheng Xue5497f662016-06-15 14:26:36 +0800268 goto err;
Zhangfei Gao8b9dcb6c2014-04-22 15:42:47 +0800269 }
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 }
Jiancheng Xue5497f662016-06-15 14:26:36 +0800276
277 return 0;
278
279err:
280 while (i--)
281 clk_unregister_gate(data->clk_data.clks[clks[i].id]);
282
283 return PTR_ERR(clk);
Zhangfei Gao8b9dcb6c2014-04-22 15:42:47 +0800284}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800285EXPORT_SYMBOL_GPL(hisi_clk_register_gate);
Zhangfei Gao8b9dcb6c2014-04-22 15:42:47 +0800286
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800287void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks,
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800288 int nums, struct hisi_clock_data *data)
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800289{
290 struct clk *clk;
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800291 void __iomem *base = data->base;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800292 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
Haojian Zhuang75af25f2013-12-24 21:38:26 +0800311 data->clk_data.clks[clks[i].id] = clk;
Haojian Zhuang0aa0c952013-11-13 08:51:23 +0800312 }
313}
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800314EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep);
Bintian Wang72ea4862015-05-29 10:08:38 +0800315
Jiancheng Xuef6ff57c2016-04-23 15:40:29 +0800316void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks,
Bintian Wang72ea4862015-05-29 10:08:38 +0800317 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}