Commit | Line | Data |
---|---|---|
0aa0c95f HZ |
1 | /* |
2 | * Hisilicon clock driver | |
3 | * | |
4 | * Copyright (c) 2012-2013 Hisilicon Limited. | |
5 | * Copyright (c) 2012-2013 Linaro Limited. | |
6 | * | |
7 | * Author: Haojian Zhuang <haojian.zhuang@linaro.org> | |
8 | * Xin Li <li.xin@linaro.org> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along | |
21 | * with this program; if not, write to the Free Software Foundation, Inc., | |
22 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
23 | * | |
24 | */ | |
25 | ||
26 | #include <linux/kernel.h> | |
0aa0c95f | 27 | #include <linux/clkdev.h> |
593438e4 | 28 | #include <linux/clk-provider.h> |
0aa0c95f HZ |
29 | #include <linux/delay.h> |
30 | #include <linux/io.h> | |
31 | #include <linux/of.h> | |
32 | #include <linux/of_address.h> | |
33 | #include <linux/of_device.h> | |
34 | #include <linux/slab.h> | |
0aa0c95f HZ |
35 | |
36 | #include "clk.h" | |
37 | ||
38 | static DEFINE_SPINLOCK(hisi_clk_lock); | |
0aa0c95f | 39 | |
32226916 JX |
40 | struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev, |
41 | int nr_clks) | |
42 | { | |
43 | struct hisi_clock_data *clk_data; | |
44 | struct resource *res; | |
45 | struct clk **clk_table; | |
46 | ||
47 | clk_data = devm_kmalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); | |
48 | if (!clk_data) | |
49 | return NULL; | |
50 | ||
51 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
c744b63b WY |
52 | if (!res) |
53 | return NULL; | |
32226916 JX |
54 | clk_data->base = devm_ioremap(&pdev->dev, |
55 | res->start, resource_size(res)); | |
56 | if (!clk_data->base) | |
57 | return NULL; | |
58 | ||
8d9bdc46 ME |
59 | clk_table = devm_kmalloc_array(&pdev->dev, nr_clks, |
60 | sizeof(*clk_table), | |
61 | GFP_KERNEL); | |
32226916 JX |
62 | if (!clk_table) |
63 | return NULL; | |
64 | ||
65 | clk_data->clk_data.clks = clk_table; | |
66 | clk_data->clk_data.clk_num = nr_clks; | |
67 | ||
68 | return clk_data; | |
69 | } | |
70 | EXPORT_SYMBOL_GPL(hisi_clk_alloc); | |
71 | ||
f6ff57c8 | 72 | struct hisi_clock_data *hisi_clk_init(struct device_node *np, |
75af25f5 | 73 | int nr_clks) |
0aa0c95f | 74 | { |
75af25f5 HZ |
75 | struct hisi_clock_data *clk_data; |
76 | struct clk **clk_table; | |
77 | void __iomem *base; | |
78 | ||
1fb6dd9d LY |
79 | base = of_iomap(np, 0); |
80 | if (!base) { | |
81 | pr_err("%s: failed to map clock registers\n", __func__); | |
75af25f5 HZ |
82 | goto err; |
83 | } | |
84 | ||
85 | clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); | |
840e5632 | 86 | if (!clk_data) |
75af25f5 | 87 | goto err; |
840e5632 | 88 | |
75af25f5 | 89 | clk_data->base = base; |
7b9bae17 | 90 | clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL); |
840e5632 | 91 | if (!clk_table) |
75af25f5 | 92 | goto err_data; |
840e5632 | 93 | |
75af25f5 HZ |
94 | clk_data->clk_data.clks = clk_table; |
95 | clk_data->clk_data.clk_num = nr_clks; | |
96 | of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data); | |
97 | return clk_data; | |
98 | err_data: | |
99 | kfree(clk_data); | |
100 | err: | |
101 | return NULL; | |
0aa0c95f | 102 | } |
f6ff57c8 | 103 | EXPORT_SYMBOL_GPL(hisi_clk_init); |
0aa0c95f | 104 | |
5497f668 | 105 | int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks, |
75af25f5 | 106 | int nums, struct hisi_clock_data *data) |
0aa0c95f HZ |
107 | { |
108 | struct clk *clk; | |
109 | int i; | |
110 | ||
111 | for (i = 0; i < nums; i++) { | |
112 | clk = clk_register_fixed_rate(NULL, clks[i].name, | |
113 | clks[i].parent_name, | |
114 | clks[i].flags, | |
115 | clks[i].fixed_rate); | |
116 | if (IS_ERR(clk)) { | |
117 | pr_err("%s: failed to register clock %s\n", | |
118 | __func__, clks[i].name); | |
5497f668 | 119 | goto err; |
0aa0c95f | 120 | } |
75af25f5 | 121 | data->clk_data.clks[clks[i].id] = clk; |
0aa0c95f | 122 | } |
5497f668 JX |
123 | |
124 | return 0; | |
125 | ||
126 | err: | |
127 | while (i--) | |
128 | clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]); | |
129 | ||
130 | return PTR_ERR(clk); | |
0aa0c95f | 131 | } |
f6ff57c8 | 132 | EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate); |
0aa0c95f | 133 | |
5497f668 | 134 | int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks, |
75af25f5 HZ |
135 | int nums, |
136 | struct hisi_clock_data *data) | |
0aa0c95f HZ |
137 | { |
138 | struct clk *clk; | |
139 | int i; | |
140 | ||
141 | for (i = 0; i < nums; i++) { | |
142 | clk = clk_register_fixed_factor(NULL, clks[i].name, | |
143 | clks[i].parent_name, | |
144 | clks[i].flags, clks[i].mult, | |
145 | clks[i].div); | |
146 | if (IS_ERR(clk)) { | |
147 | pr_err("%s: failed to register clock %s\n", | |
148 | __func__, clks[i].name); | |
5497f668 | 149 | goto err; |
0aa0c95f | 150 | } |
75af25f5 | 151 | data->clk_data.clks[clks[i].id] = clk; |
0aa0c95f | 152 | } |
5497f668 JX |
153 | |
154 | return 0; | |
155 | ||
156 | err: | |
157 | while (i--) | |
158 | clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]); | |
159 | ||
160 | return PTR_ERR(clk); | |
0aa0c95f | 161 | } |
f6ff57c8 | 162 | EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor); |
0aa0c95f | 163 | |
5497f668 | 164 | int hisi_clk_register_mux(const struct hisi_mux_clock *clks, |
75af25f5 | 165 | int nums, struct hisi_clock_data *data) |
0aa0c95f HZ |
166 | { |
167 | struct clk *clk; | |
75af25f5 | 168 | void __iomem *base = data->base; |
0aa0c95f HZ |
169 | int i; |
170 | ||
171 | for (i = 0; i < nums; i++) { | |
156342a1 ZG |
172 | u32 mask = BIT(clks[i].width) - 1; |
173 | ||
174 | clk = clk_register_mux_table(NULL, clks[i].name, | |
175 | clks[i].parent_names, | |
176 | clks[i].num_parents, clks[i].flags, | |
177 | base + clks[i].offset, clks[i].shift, | |
178 | mask, clks[i].mux_flags, | |
179 | clks[i].table, &hisi_clk_lock); | |
0aa0c95f HZ |
180 | if (IS_ERR(clk)) { |
181 | pr_err("%s: failed to register clock %s\n", | |
182 | __func__, clks[i].name); | |
5497f668 | 183 | goto err; |
0aa0c95f HZ |
184 | } |
185 | ||
186 | if (clks[i].alias) | |
187 | clk_register_clkdev(clk, clks[i].alias, NULL); | |
188 | ||
75af25f5 | 189 | data->clk_data.clks[clks[i].id] = clk; |
0aa0c95f | 190 | } |
5497f668 JX |
191 | |
192 | return 0; | |
193 | ||
194 | err: | |
195 | while (i--) | |
196 | clk_unregister_mux(data->clk_data.clks[clks[i].id]); | |
197 | ||
198 | return PTR_ERR(clk); | |
0aa0c95f | 199 | } |
f6ff57c8 | 200 | EXPORT_SYMBOL_GPL(hisi_clk_register_mux); |
0aa0c95f | 201 | |
811f67cc | 202 | int hisi_clk_register_phase(struct device *dev, |
203 | const struct hisi_phase_clock *clks, | |
204 | int nums, struct hisi_clock_data *data) | |
205 | { | |
206 | void __iomem *base = data->base; | |
207 | struct clk *clk; | |
208 | int i; | |
209 | ||
210 | for (i = 0; i < nums; i++) { | |
211 | clk = clk_register_hisi_phase(dev, &clks[i], base, | |
212 | &hisi_clk_lock); | |
213 | if (IS_ERR(clk)) { | |
214 | pr_err("%s: failed to register clock %s\n", __func__, | |
215 | clks[i].name); | |
216 | return PTR_ERR(clk); | |
217 | } | |
218 | ||
219 | data->clk_data.clks[clks[i].id] = clk; | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | EXPORT_SYMBOL_GPL(hisi_clk_register_phase); | |
225 | ||
5497f668 | 226 | int hisi_clk_register_divider(const struct hisi_divider_clock *clks, |
75af25f5 | 227 | int nums, struct hisi_clock_data *data) |
0aa0c95f HZ |
228 | { |
229 | struct clk *clk; | |
75af25f5 | 230 | void __iomem *base = data->base; |
0aa0c95f HZ |
231 | int i; |
232 | ||
233 | for (i = 0; i < nums; i++) { | |
234 | clk = clk_register_divider_table(NULL, clks[i].name, | |
235 | clks[i].parent_name, | |
236 | clks[i].flags, | |
237 | base + clks[i].offset, | |
238 | clks[i].shift, clks[i].width, | |
239 | clks[i].div_flags, | |
240 | clks[i].table, | |
241 | &hisi_clk_lock); | |
242 | if (IS_ERR(clk)) { | |
243 | pr_err("%s: failed to register clock %s\n", | |
244 | __func__, clks[i].name); | |
5497f668 | 245 | goto err; |
0aa0c95f HZ |
246 | } |
247 | ||
248 | if (clks[i].alias) | |
249 | clk_register_clkdev(clk, clks[i].alias, NULL); | |
250 | ||
75af25f5 | 251 | data->clk_data.clks[clks[i].id] = clk; |
0aa0c95f | 252 | } |
5497f668 JX |
253 | |
254 | return 0; | |
255 | ||
256 | err: | |
257 | while (i--) | |
258 | clk_unregister_divider(data->clk_data.clks[clks[i].id]); | |
259 | ||
260 | return PTR_ERR(clk); | |
0aa0c95f | 261 | } |
f6ff57c8 | 262 | EXPORT_SYMBOL_GPL(hisi_clk_register_divider); |
0aa0c95f | 263 | |
5497f668 | 264 | int hisi_clk_register_gate(const struct hisi_gate_clock *clks, |
8b9dcb6c ZG |
265 | int nums, struct hisi_clock_data *data) |
266 | { | |
267 | struct clk *clk; | |
268 | void __iomem *base = data->base; | |
269 | int i; | |
270 | ||
271 | for (i = 0; i < nums; i++) { | |
272 | clk = clk_register_gate(NULL, clks[i].name, | |
273 | clks[i].parent_name, | |
274 | clks[i].flags, | |
275 | base + clks[i].offset, | |
276 | clks[i].bit_idx, | |
277 | clks[i].gate_flags, | |
278 | &hisi_clk_lock); | |
279 | if (IS_ERR(clk)) { | |
280 | pr_err("%s: failed to register clock %s\n", | |
281 | __func__, clks[i].name); | |
5497f668 | 282 | goto err; |
8b9dcb6c ZG |
283 | } |
284 | ||
285 | if (clks[i].alias) | |
286 | clk_register_clkdev(clk, clks[i].alias, NULL); | |
287 | ||
288 | data->clk_data.clks[clks[i].id] = clk; | |
289 | } | |
5497f668 JX |
290 | |
291 | return 0; | |
292 | ||
293 | err: | |
294 | while (i--) | |
295 | clk_unregister_gate(data->clk_data.clks[clks[i].id]); | |
296 | ||
297 | return PTR_ERR(clk); | |
8b9dcb6c | 298 | } |
f6ff57c8 | 299 | EXPORT_SYMBOL_GPL(hisi_clk_register_gate); |
8b9dcb6c | 300 | |
f6ff57c8 | 301 | void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, |
75af25f5 | 302 | int nums, struct hisi_clock_data *data) |
0aa0c95f HZ |
303 | { |
304 | struct clk *clk; | |
75af25f5 | 305 | void __iomem *base = data->base; |
0aa0c95f HZ |
306 | int i; |
307 | ||
308 | for (i = 0; i < nums; i++) { | |
309 | clk = hisi_register_clkgate_sep(NULL, clks[i].name, | |
310 | clks[i].parent_name, | |
311 | clks[i].flags, | |
312 | base + clks[i].offset, | |
313 | clks[i].bit_idx, | |
314 | clks[i].gate_flags, | |
315 | &hisi_clk_lock); | |
316 | if (IS_ERR(clk)) { | |
317 | pr_err("%s: failed to register clock %s\n", | |
318 | __func__, clks[i].name); | |
319 | continue; | |
320 | } | |
321 | ||
322 | if (clks[i].alias) | |
323 | clk_register_clkdev(clk, clks[i].alias, NULL); | |
324 | ||
75af25f5 | 325 | data->clk_data.clks[clks[i].id] = clk; |
0aa0c95f HZ |
326 | } |
327 | } | |
f6ff57c8 | 328 | EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep); |
72ea4861 | 329 | |
f6ff57c8 | 330 | void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, |
72ea4861 BW |
331 | int nums, struct hisi_clock_data *data) |
332 | { | |
333 | struct clk *clk; | |
334 | void __iomem *base = data->base; | |
335 | int i; | |
336 | ||
337 | for (i = 0; i < nums; i++) { | |
338 | clk = hi6220_register_clkdiv(NULL, clks[i].name, | |
339 | clks[i].parent_name, | |
340 | clks[i].flags, | |
341 | base + clks[i].offset, | |
342 | clks[i].shift, | |
343 | clks[i].width, | |
344 | clks[i].mask_bit, | |
345 | &hisi_clk_lock); | |
346 | if (IS_ERR(clk)) { | |
347 | pr_err("%s: failed to register clock %s\n", | |
348 | __func__, clks[i].name); | |
349 | continue; | |
350 | } | |
351 | ||
352 | if (clks[i].alias) | |
353 | clk_register_clkdev(clk, clks[i].alias, NULL); | |
354 | ||
355 | data->clk_data.clks[clks[i].id] = clk; | |
356 | } | |
357 | } |