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