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
24static DEFINE_SPINLOCK(hisi_clk_lock);
25
26struct 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}
56EXPORT_SYMBOL_GPL(hisi_clk_alloc);
57
58struct 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;
84err_data:
85	kfree(clk_data);
86err:
87	return NULL;
88}
89EXPORT_SYMBOL_GPL(hisi_clk_init);
90
91int 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
112err:
113	while (i--)
114		clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]);
115
116	return PTR_ERR(clk);
117}
118EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate);
119
120int 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
142err:
143	while (i--)
144		clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]);
145
146	return PTR_ERR(clk);
147}
148EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor);
149
150int 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					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
180err:
181	while (i--)
182		clk_unregister_mux(data->clk_data.clks[clks[i].id]);
183
184	return PTR_ERR(clk);
185}
186EXPORT_SYMBOL_GPL(hisi_clk_register_mux);
187
188int 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
212int 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
242err:
243	while (i--)
244		clk_unregister_divider(data->clk_data.clks[clks[i].id]);
245
246	return PTR_ERR(clk);
247}
248EXPORT_SYMBOL_GPL(hisi_clk_register_divider);
249
250int 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
279err:
280	while (i--)
281		clk_unregister_gate(data->clk_data.clks[clks[i].id]);
282
283	return PTR_ERR(clk);
284}
285EXPORT_SYMBOL_GPL(hisi_clk_register_gate);
286
287void 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}
314EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep);
315
316void __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