diff options
-rw-r--r-- | drivers/clk/tegra/Kconfig | 4 | ||||
-rw-r--r-- | drivers/clk/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-bpmp.c | 620 |
3 files changed, 625 insertions, 0 deletions
diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig index 1ba30d1e14f2..7ddacae5d0b1 100644 --- a/drivers/clk/tegra/Kconfig +++ b/drivers/clk/tegra/Kconfig | |||
@@ -1,3 +1,7 @@ | |||
1 | config TEGRA_CLK_EMC | 1 | config TEGRA_CLK_EMC |
2 | def_bool y | 2 | def_bool y |
3 | depends on TEGRA124_EMC | 3 | depends on TEGRA124_EMC |
4 | |||
5 | config CLK_TEGRA_BPMP | ||
6 | def_bool y | ||
7 | depends on TEGRA_BPMP | ||
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 33fd0938d79e..4be8af28ee61 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile | |||
@@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o | |||
22 | obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o | 22 | obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o |
23 | obj-y += cvb.o | 23 | obj-y += cvb.o |
24 | obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o | 24 | obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o |
25 | obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o | ||
diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c new file mode 100644 index 000000000000..638ace64033b --- /dev/null +++ b/drivers/clk/tegra/clk-bpmp.c | |||
@@ -0,0 +1,620 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 NVIDIA Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/clk-provider.h> | ||
10 | #include <linux/device.h> | ||
11 | #include <linux/seq_buf.h> | ||
12 | #include <linux/slab.h> | ||
13 | |||
14 | #include <soc/tegra/bpmp.h> | ||
15 | #include <soc/tegra/bpmp-abi.h> | ||
16 | |||
17 | #define TEGRA_BPMP_DUMP_CLOCK_INFO 0 | ||
18 | |||
19 | #define TEGRA_BPMP_CLK_HAS_MUX BIT(0) | ||
20 | #define TEGRA_BPMP_CLK_HAS_SET_RATE BIT(1) | ||
21 | #define TEGRA_BPMP_CLK_IS_ROOT BIT(2) | ||
22 | |||
23 | struct tegra_bpmp_clk_info { | ||
24 | unsigned int id; | ||
25 | char name[MRQ_CLK_NAME_MAXLEN]; | ||
26 | unsigned int parents[MRQ_CLK_MAX_PARENTS]; | ||
27 | unsigned int num_parents; | ||
28 | unsigned long flags; | ||
29 | }; | ||
30 | |||
31 | struct tegra_bpmp_clk { | ||
32 | struct clk_hw hw; | ||
33 | |||
34 | struct tegra_bpmp *bpmp; | ||
35 | unsigned int id; | ||
36 | |||
37 | unsigned int num_parents; | ||
38 | unsigned int *parents; | ||
39 | }; | ||
40 | |||
41 | static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw) | ||
42 | { | ||
43 | return container_of(hw, struct tegra_bpmp_clk, hw); | ||
44 | } | ||
45 | |||
46 | struct tegra_bpmp_clk_message { | ||
47 | unsigned int cmd; | ||
48 | unsigned int id; | ||
49 | |||
50 | struct { | ||
51 | const void *data; | ||
52 | size_t size; | ||
53 | } tx; | ||
54 | |||
55 | struct { | ||
56 | void *data; | ||
57 | size_t size; | ||
58 | } rx; | ||
59 | }; | ||
60 | |||
61 | static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp, | ||
62 | const struct tegra_bpmp_clk_message *clk) | ||
63 | { | ||
64 | struct mrq_clk_request request; | ||
65 | struct tegra_bpmp_message msg; | ||
66 | void *req = &request; | ||
67 | |||
68 | memset(&request, 0, sizeof(request)); | ||
69 | request.cmd_and_id = (clk->cmd << 24) | clk->id; | ||
70 | |||
71 | /* | ||
72 | * The mrq_clk_request structure has an anonymous union at offset 4 | ||
73 | * that contains all possible sub-command structures. Copy the data | ||
74 | * to that union. Ideally we'd be able to refer to it by name, but | ||
75 | * doing so would require changing the ABI header and increase the | ||
76 | * maintenance burden. | ||
77 | */ | ||
78 | memcpy(req + 4, clk->tx.data, clk->tx.size); | ||
79 | |||
80 | memset(&msg, 0, sizeof(msg)); | ||
81 | msg.mrq = MRQ_CLK; | ||
82 | msg.tx.data = &request; | ||
83 | msg.tx.size = sizeof(request); | ||
84 | msg.rx.data = clk->rx.data; | ||
85 | msg.rx.size = clk->rx.size; | ||
86 | |||
87 | return tegra_bpmp_transfer(bpmp, &msg); | ||
88 | } | ||
89 | |||
90 | static int tegra_bpmp_clk_prepare(struct clk_hw *hw) | ||
91 | { | ||
92 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
93 | struct tegra_bpmp_clk_message msg; | ||
94 | |||
95 | memset(&msg, 0, sizeof(msg)); | ||
96 | msg.cmd = CMD_CLK_ENABLE; | ||
97 | msg.id = clk->id; | ||
98 | |||
99 | return tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
100 | } | ||
101 | |||
102 | static void tegra_bpmp_clk_unprepare(struct clk_hw *hw) | ||
103 | { | ||
104 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
105 | struct tegra_bpmp_clk_message msg; | ||
106 | int err; | ||
107 | |||
108 | memset(&msg, 0, sizeof(msg)); | ||
109 | msg.cmd = CMD_CLK_DISABLE; | ||
110 | msg.id = clk->id; | ||
111 | |||
112 | err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
113 | if (err < 0) | ||
114 | dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n", | ||
115 | clk_hw_get_name(hw), err); | ||
116 | } | ||
117 | |||
118 | static int tegra_bpmp_clk_is_prepared(struct clk_hw *hw) | ||
119 | { | ||
120 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
121 | struct cmd_clk_is_enabled_response response; | ||
122 | struct tegra_bpmp_clk_message msg; | ||
123 | int err; | ||
124 | |||
125 | memset(&msg, 0, sizeof(msg)); | ||
126 | msg.cmd = CMD_CLK_IS_ENABLED; | ||
127 | msg.id = clk->id; | ||
128 | msg.rx.data = &response; | ||
129 | msg.rx.size = sizeof(response); | ||
130 | |||
131 | err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
132 | if (err < 0) | ||
133 | return err; | ||
134 | |||
135 | return response.state; | ||
136 | } | ||
137 | |||
138 | static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw, | ||
139 | unsigned long parent_rate) | ||
140 | { | ||
141 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
142 | struct cmd_clk_get_rate_response response; | ||
143 | struct cmd_clk_get_rate_request request; | ||
144 | struct tegra_bpmp_clk_message msg; | ||
145 | int err; | ||
146 | |||
147 | memset(&msg, 0, sizeof(msg)); | ||
148 | msg.cmd = CMD_CLK_GET_RATE; | ||
149 | msg.id = clk->id; | ||
150 | msg.tx.data = &request; | ||
151 | msg.tx.size = sizeof(request); | ||
152 | msg.rx.data = &response; | ||
153 | msg.rx.size = sizeof(response); | ||
154 | |||
155 | err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
156 | if (err < 0) | ||
157 | return err; | ||
158 | |||
159 | return response.rate; | ||
160 | } | ||
161 | |||
162 | static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate, | ||
163 | unsigned long *parent_rate) | ||
164 | { | ||
165 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
166 | struct cmd_clk_round_rate_response response; | ||
167 | struct cmd_clk_round_rate_request request; | ||
168 | struct tegra_bpmp_clk_message msg; | ||
169 | int err; | ||
170 | |||
171 | memset(&request, 0, sizeof(request)); | ||
172 | request.rate = rate; | ||
173 | |||
174 | memset(&msg, 0, sizeof(msg)); | ||
175 | msg.cmd = CMD_CLK_ROUND_RATE; | ||
176 | msg.id = clk->id; | ||
177 | msg.tx.data = &request; | ||
178 | msg.tx.size = sizeof(request); | ||
179 | msg.rx.data = &response; | ||
180 | msg.rx.size = sizeof(response); | ||
181 | |||
182 | err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
183 | if (err < 0) | ||
184 | return err; | ||
185 | |||
186 | return response.rate; | ||
187 | } | ||
188 | |||
189 | static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index) | ||
190 | { | ||
191 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
192 | struct cmd_clk_set_parent_response response; | ||
193 | struct cmd_clk_set_parent_request request; | ||
194 | struct tegra_bpmp_clk_message msg; | ||
195 | int err; | ||
196 | |||
197 | memset(&request, 0, sizeof(request)); | ||
198 | request.parent_id = clk->parents[index]; | ||
199 | |||
200 | memset(&msg, 0, sizeof(msg)); | ||
201 | msg.cmd = CMD_CLK_SET_PARENT; | ||
202 | msg.id = clk->id; | ||
203 | msg.tx.data = &request; | ||
204 | msg.tx.size = sizeof(request); | ||
205 | msg.rx.data = &response; | ||
206 | msg.rx.size = sizeof(response); | ||
207 | |||
208 | err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
209 | if (err < 0) | ||
210 | return err; | ||
211 | |||
212 | /* XXX check parent ID in response */ | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw) | ||
218 | { | ||
219 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
220 | struct cmd_clk_get_parent_response response; | ||
221 | struct tegra_bpmp_clk_message msg; | ||
222 | unsigned int i; | ||
223 | int err; | ||
224 | |||
225 | memset(&msg, 0, sizeof(msg)); | ||
226 | msg.cmd = CMD_CLK_GET_PARENT; | ||
227 | msg.id = clk->id; | ||
228 | msg.rx.data = &response; | ||
229 | msg.rx.size = sizeof(response); | ||
230 | |||
231 | err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
232 | if (err < 0) { | ||
233 | dev_err(clk->bpmp->dev, "failed to get parent for %s: %d\n", | ||
234 | clk_hw_get_name(hw), err); | ||
235 | return U8_MAX; | ||
236 | } | ||
237 | |||
238 | for (i = 0; i < clk->num_parents; i++) | ||
239 | if (clk->parents[i] == response.parent_id) | ||
240 | return i; | ||
241 | |||
242 | return U8_MAX; | ||
243 | } | ||
244 | |||
245 | static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate, | ||
246 | unsigned long parent_rate) | ||
247 | { | ||
248 | struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); | ||
249 | struct cmd_clk_set_rate_response response; | ||
250 | struct cmd_clk_set_rate_request request; | ||
251 | struct tegra_bpmp_clk_message msg; | ||
252 | |||
253 | memset(&request, 0, sizeof(request)); | ||
254 | request.rate = rate; | ||
255 | |||
256 | memset(&msg, 0, sizeof(msg)); | ||
257 | msg.cmd = CMD_CLK_SET_RATE; | ||
258 | msg.id = clk->id; | ||
259 | msg.tx.data = &request; | ||
260 | msg.tx.size = sizeof(request); | ||
261 | msg.rx.data = &response; | ||
262 | msg.rx.size = sizeof(response); | ||
263 | |||
264 | return tegra_bpmp_clk_transfer(clk->bpmp, &msg); | ||
265 | } | ||
266 | |||
267 | static const struct clk_ops tegra_bpmp_clk_gate_ops = { | ||
268 | .prepare = tegra_bpmp_clk_prepare, | ||
269 | .unprepare = tegra_bpmp_clk_unprepare, | ||
270 | .is_prepared = tegra_bpmp_clk_is_prepared, | ||
271 | .recalc_rate = tegra_bpmp_clk_recalc_rate, | ||
272 | }; | ||
273 | |||
274 | static const struct clk_ops tegra_bpmp_clk_mux_ops = { | ||
275 | .prepare = tegra_bpmp_clk_prepare, | ||
276 | .unprepare = tegra_bpmp_clk_unprepare, | ||
277 | .is_prepared = tegra_bpmp_clk_is_prepared, | ||
278 | .recalc_rate = tegra_bpmp_clk_recalc_rate, | ||
279 | .set_parent = tegra_bpmp_clk_set_parent, | ||
280 | .get_parent = tegra_bpmp_clk_get_parent, | ||
281 | }; | ||
282 | |||
283 | static const struct clk_ops tegra_bpmp_clk_rate_ops = { | ||
284 | .prepare = tegra_bpmp_clk_prepare, | ||
285 | .unprepare = tegra_bpmp_clk_unprepare, | ||
286 | .is_prepared = tegra_bpmp_clk_is_prepared, | ||
287 | .recalc_rate = tegra_bpmp_clk_recalc_rate, | ||
288 | .round_rate = tegra_bpmp_clk_round_rate, | ||
289 | .set_rate = tegra_bpmp_clk_set_rate, | ||
290 | }; | ||
291 | |||
292 | static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = { | ||
293 | .prepare = tegra_bpmp_clk_prepare, | ||
294 | .unprepare = tegra_bpmp_clk_unprepare, | ||
295 | .is_prepared = tegra_bpmp_clk_is_prepared, | ||
296 | .recalc_rate = tegra_bpmp_clk_recalc_rate, | ||
297 | .round_rate = tegra_bpmp_clk_round_rate, | ||
298 | .set_parent = tegra_bpmp_clk_set_parent, | ||
299 | .get_parent = tegra_bpmp_clk_get_parent, | ||
300 | .set_rate = tegra_bpmp_clk_set_rate, | ||
301 | }; | ||
302 | |||
303 | static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp) | ||
304 | { | ||
305 | struct cmd_clk_get_max_clk_id_response response; | ||
306 | struct tegra_bpmp_clk_message msg; | ||
307 | int err; | ||
308 | |||
309 | memset(&msg, 0, sizeof(msg)); | ||
310 | msg.cmd = CMD_CLK_GET_MAX_CLK_ID; | ||
311 | msg.rx.data = &response; | ||
312 | msg.rx.size = sizeof(response); | ||
313 | |||
314 | err = tegra_bpmp_clk_transfer(bpmp, &msg); | ||
315 | if (err < 0) | ||
316 | return err; | ||
317 | |||
318 | if (response.max_id > INT_MAX) | ||
319 | return -E2BIG; | ||
320 | |||
321 | return response.max_id; | ||
322 | } | ||
323 | |||
324 | static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id, | ||
325 | struct tegra_bpmp_clk_info *info) | ||
326 | { | ||
327 | struct cmd_clk_get_all_info_response response; | ||
328 | struct tegra_bpmp_clk_message msg; | ||
329 | unsigned int i; | ||
330 | int err; | ||
331 | |||
332 | memset(&msg, 0, sizeof(msg)); | ||
333 | msg.cmd = CMD_CLK_GET_ALL_INFO; | ||
334 | msg.id = id; | ||
335 | msg.rx.data = &response; | ||
336 | msg.rx.size = sizeof(response); | ||
337 | |||
338 | err = tegra_bpmp_clk_transfer(bpmp, &msg); | ||
339 | if (err < 0) | ||
340 | return err; | ||
341 | |||
342 | strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN); | ||
343 | info->num_parents = response.num_parents; | ||
344 | |||
345 | for (i = 0; i < info->num_parents; i++) | ||
346 | info->parents[i] = response.parents[i]; | ||
347 | |||
348 | info->flags = response.flags; | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static void tegra_bpmp_clk_info_dump(struct tegra_bpmp *bpmp, | ||
354 | const char *level, | ||
355 | const struct tegra_bpmp_clk_info *info) | ||
356 | { | ||
357 | const char *prefix = ""; | ||
358 | struct seq_buf buf; | ||
359 | unsigned int i; | ||
360 | char flags[64]; | ||
361 | |||
362 | seq_buf_init(&buf, flags, sizeof(flags)); | ||
363 | |||
364 | if (info->flags) | ||
365 | seq_buf_printf(&buf, "("); | ||
366 | |||
367 | if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) { | ||
368 | seq_buf_printf(&buf, "%smux", prefix); | ||
369 | prefix = ", "; | ||
370 | } | ||
371 | |||
372 | if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) { | ||
373 | seq_buf_printf(&buf, "%sfixed", prefix); | ||
374 | prefix = ", "; | ||
375 | } | ||
376 | |||
377 | if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) { | ||
378 | seq_buf_printf(&buf, "%sroot", prefix); | ||
379 | prefix = ", "; | ||
380 | } | ||
381 | |||
382 | if (info->flags) | ||
383 | seq_buf_printf(&buf, ")"); | ||
384 | |||
385 | dev_printk(level, bpmp->dev, "%03u: %s\n", info->id, info->name); | ||
386 | dev_printk(level, bpmp->dev, " flags: %lx %s\n", info->flags, flags); | ||
387 | dev_printk(level, bpmp->dev, " parents: %u\n", info->num_parents); | ||
388 | |||
389 | for (i = 0; i < info->num_parents; i++) | ||
390 | dev_printk(level, bpmp->dev, " %03u\n", info->parents[i]); | ||
391 | } | ||
392 | |||
393 | static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp, | ||
394 | struct tegra_bpmp_clk_info **clocksp) | ||
395 | { | ||
396 | struct tegra_bpmp_clk_info *clocks; | ||
397 | unsigned int max_id, id, count = 0; | ||
398 | unsigned int holes = 0; | ||
399 | int err; | ||
400 | |||
401 | err = tegra_bpmp_clk_get_max_id(bpmp); | ||
402 | if (err < 0) | ||
403 | return err; | ||
404 | |||
405 | max_id = err; | ||
406 | |||
407 | dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id); | ||
408 | |||
409 | clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL); | ||
410 | if (!clocks) | ||
411 | return -ENOMEM; | ||
412 | |||
413 | for (id = 0; id <= max_id; id++) { | ||
414 | struct tegra_bpmp_clk_info *info = &clocks[count]; | ||
415 | |||
416 | err = tegra_bpmp_clk_get_info(bpmp, id, info); | ||
417 | if (err < 0) { | ||
418 | dev_err(bpmp->dev, "failed to query clock %u: %d\n", | ||
419 | id, err); | ||
420 | continue; | ||
421 | } | ||
422 | |||
423 | if (info->num_parents >= U8_MAX) { | ||
424 | dev_err(bpmp->dev, | ||
425 | "clock %u has too many parents (%u, max: %u)\n", | ||
426 | id, info->num_parents, U8_MAX); | ||
427 | continue; | ||
428 | } | ||
429 | |||
430 | /* clock not exposed by BPMP */ | ||
431 | if (info->name[0] == '\0') { | ||
432 | holes++; | ||
433 | continue; | ||
434 | } | ||
435 | |||
436 | info->id = id; | ||
437 | count++; | ||
438 | |||
439 | if (TEGRA_BPMP_DUMP_CLOCK_INFO) | ||
440 | tegra_bpmp_clk_info_dump(bpmp, KERN_DEBUG, info); | ||
441 | } | ||
442 | |||
443 | dev_dbg(bpmp->dev, "holes: %u\n", holes); | ||
444 | *clocksp = clocks; | ||
445 | |||
446 | return count; | ||
447 | } | ||
448 | |||
449 | static const struct tegra_bpmp_clk_info * | ||
450 | tegra_bpmp_clk_find(const struct tegra_bpmp_clk_info *clocks, | ||
451 | unsigned int num_clocks, unsigned int id) | ||
452 | { | ||
453 | unsigned int i; | ||
454 | |||
455 | for (i = 0; i < num_clocks; i++) | ||
456 | if (clocks[i].id == id) | ||
457 | return &clocks[i]; | ||
458 | |||
459 | return NULL; | ||
460 | } | ||
461 | |||
462 | static struct tegra_bpmp_clk * | ||
463 | tegra_bpmp_clk_register(struct tegra_bpmp *bpmp, | ||
464 | const struct tegra_bpmp_clk_info *info, | ||
465 | const struct tegra_bpmp_clk_info *clocks, | ||
466 | unsigned int num_clocks) | ||
467 | { | ||
468 | struct tegra_bpmp_clk *clk; | ||
469 | struct clk_init_data init; | ||
470 | const char **parents; | ||
471 | unsigned int i; | ||
472 | int err; | ||
473 | |||
474 | clk = devm_kzalloc(bpmp->dev, sizeof(*clk), GFP_KERNEL); | ||
475 | if (!clk) | ||
476 | return ERR_PTR(-ENOMEM); | ||
477 | |||
478 | clk->id = info->id; | ||
479 | clk->bpmp = bpmp; | ||
480 | |||
481 | clk->parents = devm_kcalloc(bpmp->dev, info->num_parents, | ||
482 | sizeof(*clk->parents), GFP_KERNEL); | ||
483 | if (!clk->parents) | ||
484 | return ERR_PTR(-ENOMEM); | ||
485 | |||
486 | clk->num_parents = info->num_parents; | ||
487 | |||
488 | /* hardware clock initialization */ | ||
489 | memset(&init, 0, sizeof(init)); | ||
490 | init.name = info->name; | ||
491 | clk->hw.init = &init; | ||
492 | |||
493 | if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) { | ||
494 | if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) | ||
495 | init.ops = &tegra_bpmp_clk_mux_rate_ops; | ||
496 | else | ||
497 | init.ops = &tegra_bpmp_clk_mux_ops; | ||
498 | } else { | ||
499 | if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) | ||
500 | init.ops = &tegra_bpmp_clk_rate_ops; | ||
501 | else | ||
502 | init.ops = &tegra_bpmp_clk_gate_ops; | ||
503 | } | ||
504 | |||
505 | init.num_parents = info->num_parents; | ||
506 | |||
507 | parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL); | ||
508 | if (!parents) | ||
509 | return ERR_PTR(-ENOMEM); | ||
510 | |||
511 | for (i = 0; i < info->num_parents; i++) { | ||
512 | const struct tegra_bpmp_clk_info *parent; | ||
513 | |||
514 | /* keep a private copy of the ID to parent index map */ | ||
515 | clk->parents[i] = info->parents[i]; | ||
516 | |||
517 | parent = tegra_bpmp_clk_find(clocks, num_clocks, | ||
518 | info->parents[i]); | ||
519 | if (!parent) { | ||
520 | dev_err(bpmp->dev, "no parent %u found for %u\n", | ||
521 | info->parents[i], info->id); | ||
522 | continue; | ||
523 | } | ||
524 | |||
525 | parents[i] = parent->name; | ||
526 | } | ||
527 | |||
528 | init.parent_names = parents; | ||
529 | |||
530 | err = devm_clk_hw_register(bpmp->dev, &clk->hw); | ||
531 | |||
532 | kfree(parents); | ||
533 | |||
534 | if (err < 0) | ||
535 | return ERR_PTR(err); | ||
536 | |||
537 | return clk; | ||
538 | } | ||
539 | |||
540 | static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp, | ||
541 | struct tegra_bpmp_clk_info *infos, | ||
542 | unsigned int count) | ||
543 | { | ||
544 | struct tegra_bpmp_clk *clk; | ||
545 | unsigned int i; | ||
546 | |||
547 | bpmp->num_clocks = count; | ||
548 | |||
549 | bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(clk), GFP_KERNEL); | ||
550 | if (!bpmp->clocks) | ||
551 | return -ENOMEM; | ||
552 | |||
553 | for (i = 0; i < count; i++) { | ||
554 | struct tegra_bpmp_clk_info *info = &infos[i]; | ||
555 | |||
556 | clk = tegra_bpmp_clk_register(bpmp, info, infos, count); | ||
557 | if (IS_ERR(clk)) { | ||
558 | dev_err(bpmp->dev, | ||
559 | "failed to register clock %u (%s): %ld\n", | ||
560 | info->id, info->name, PTR_ERR(clk)); | ||
561 | continue; | ||
562 | } | ||
563 | |||
564 | bpmp->clocks[i] = clk; | ||
565 | } | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | static void tegra_bpmp_unregister_clocks(struct tegra_bpmp *bpmp) | ||
571 | { | ||
572 | unsigned int i; | ||
573 | |||
574 | for (i = 0; i < bpmp->num_clocks; i++) | ||
575 | clk_hw_unregister(&bpmp->clocks[i]->hw); | ||
576 | } | ||
577 | |||
578 | static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec, | ||
579 | void *data) | ||
580 | { | ||
581 | unsigned int id = clkspec->args[0], i; | ||
582 | struct tegra_bpmp *bpmp = data; | ||
583 | |||
584 | for (i = 0; i < bpmp->num_clocks; i++) | ||
585 | if (bpmp->clocks[i]->id == id) | ||
586 | return &bpmp->clocks[i]->hw; | ||
587 | |||
588 | return NULL; | ||
589 | } | ||
590 | |||
591 | int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp) | ||
592 | { | ||
593 | struct tegra_bpmp_clk_info *clocks; | ||
594 | unsigned int count; | ||
595 | int err; | ||
596 | |||
597 | err = tegra_bpmp_probe_clocks(bpmp, &clocks); | ||
598 | if (err < 0) | ||
599 | return err; | ||
600 | |||
601 | count = err; | ||
602 | |||
603 | dev_dbg(bpmp->dev, "%u clocks probed\n", count); | ||
604 | |||
605 | err = tegra_bpmp_register_clocks(bpmp, clocks, count); | ||
606 | if (err < 0) | ||
607 | goto free; | ||
608 | |||
609 | err = of_clk_add_hw_provider(bpmp->dev->of_node, | ||
610 | tegra_bpmp_clk_of_xlate, | ||
611 | bpmp); | ||
612 | if (err < 0) { | ||
613 | tegra_bpmp_unregister_clocks(bpmp); | ||
614 | goto free; | ||
615 | } | ||
616 | |||
617 | free: | ||
618 | kfree(clocks); | ||
619 | return err; | ||
620 | } | ||