Commit | Line | Data |
---|---|---|
d8611961 CC |
1 | /* |
2 | * | |
3 | * Copyright (C) 2010 Google, Inc. | |
4 | * | |
5 | * Author: | |
6 | * Colin Cross <ccross@google.com> | |
7 | * | |
8 | * This software is licensed under the terms of the GNU General Public | |
9 | * License version 2, as published by the Free Software Foundation, and | |
10 | * may be copied, distributed, and modified under those terms. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/clk.h> | |
21 | #include <linux/list.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/debugfs.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/seq_file.h> | |
71fc84cc | 27 | #include <linux/regulator/consumer.h> |
6d803ba7 | 28 | #include <linux/clkdev.h> |
d8611961 CC |
29 | |
30 | #include "clock.h" | |
71fc84cc CC |
31 | #include "board.h" |
32 | #include "fuse.h" | |
d8611961 CC |
33 | |
34 | static LIST_HEAD(clocks); | |
35 | ||
36 | static DEFINE_SPINLOCK(clock_lock); | |
71fc84cc CC |
37 | static DEFINE_MUTEX(dvfs_lock); |
38 | ||
39 | static int clk_is_dvfs(struct clk *c) | |
40 | { | |
41 | return (c->dvfs != NULL); | |
42 | }; | |
43 | ||
44 | static int dvfs_set_rate(struct dvfs *d, unsigned long rate) | |
45 | { | |
46 | struct dvfs_table *t; | |
47 | ||
48 | if (d->table == NULL) | |
49 | return -ENODEV; | |
50 | ||
51 | for (t = d->table; t->rate != 0; t++) { | |
52 | if (rate <= t->rate) { | |
53 | if (!d->reg) | |
54 | return 0; | |
55 | ||
56 | return regulator_set_voltage(d->reg, | |
57 | t->millivolts * 1000, | |
58 | d->max_millivolts * 1000); | |
59 | } | |
60 | } | |
61 | ||
62 | return -EINVAL; | |
63 | } | |
64 | ||
65 | static void dvfs_init(struct clk *c) | |
66 | { | |
67 | int process_id; | |
68 | int i; | |
69 | struct dvfs_table *table; | |
70 | ||
71 | process_id = c->dvfs->cpu ? tegra_core_process_id() : | |
72 | tegra_cpu_process_id(); | |
73 | ||
74 | for (i = 0; i < c->dvfs->process_id_table_length; i++) | |
75 | if (process_id == c->dvfs->process_id_table[i].process_id) | |
76 | c->dvfs->table = c->dvfs->process_id_table[i].table; | |
77 | ||
78 | if (c->dvfs->table == NULL) { | |
79 | pr_err("Failed to find dvfs table for clock %s process %d\n", | |
80 | c->name, process_id); | |
81 | return; | |
82 | } | |
83 | ||
84 | c->dvfs->max_millivolts = 0; | |
85 | for (table = c->dvfs->table; table->rate != 0; table++) | |
86 | if (c->dvfs->max_millivolts < table->millivolts) | |
87 | c->dvfs->max_millivolts = table->millivolts; | |
88 | ||
89 | c->dvfs->reg = regulator_get(NULL, c->dvfs->reg_id); | |
90 | ||
91 | if (IS_ERR(c->dvfs->reg)) { | |
92 | pr_err("Failed to get regulator %s for clock %s\n", | |
93 | c->dvfs->reg_id, c->name); | |
94 | c->dvfs->reg = NULL; | |
95 | return; | |
96 | } | |
97 | ||
98 | if (c->refcnt > 0) | |
99 | dvfs_set_rate(c->dvfs, c->rate); | |
100 | } | |
d8611961 CC |
101 | |
102 | struct clk *tegra_get_clock_by_name(const char *name) | |
103 | { | |
104 | struct clk *c; | |
105 | struct clk *ret = NULL; | |
106 | unsigned long flags; | |
107 | spin_lock_irqsave(&clock_lock, flags); | |
108 | list_for_each_entry(c, &clocks, node) { | |
109 | if (strcmp(c->name, name) == 0) { | |
110 | ret = c; | |
111 | break; | |
112 | } | |
113 | } | |
114 | spin_unlock_irqrestore(&clock_lock, flags); | |
115 | return ret; | |
116 | } | |
117 | ||
71fc84cc CC |
118 | static void clk_recalculate_rate(struct clk *c) |
119 | { | |
120 | u64 rate; | |
121 | ||
122 | if (!c->parent) | |
123 | return; | |
124 | ||
125 | rate = c->parent->rate; | |
126 | ||
127 | if (c->mul != 0 && c->div != 0) { | |
128 | rate = rate * c->mul; | |
129 | do_div(rate, c->div); | |
130 | } | |
131 | ||
132 | if (rate > c->max_rate) | |
133 | pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n", | |
134 | c->name, rate, c->max_rate); | |
135 | ||
136 | c->rate = rate; | |
137 | } | |
138 | ||
d8611961 CC |
139 | int clk_reparent(struct clk *c, struct clk *parent) |
140 | { | |
141 | pr_debug("%s: %s\n", __func__, c->name); | |
d8611961 | 142 | c->parent = parent; |
d8611961 CC |
143 | list_del(&c->sibling); |
144 | list_add_tail(&c->sibling, &parent->children); | |
145 | return 0; | |
146 | } | |
147 | ||
148 | static void propagate_rate(struct clk *c) | |
149 | { | |
150 | struct clk *clkp; | |
151 | pr_debug("%s: %s\n", __func__, c->name); | |
152 | list_for_each_entry(clkp, &c->children, sibling) { | |
153 | pr_debug(" %s\n", clkp->name); | |
71fc84cc | 154 | clk_recalculate_rate(clkp); |
d8611961 CC |
155 | propagate_rate(clkp); |
156 | } | |
157 | } | |
158 | ||
159 | void clk_init(struct clk *c) | |
160 | { | |
161 | unsigned long flags; | |
162 | ||
71fc84cc CC |
163 | pr_debug("%s: %s\n", __func__, c->name); |
164 | ||
d8611961 CC |
165 | spin_lock_irqsave(&clock_lock, flags); |
166 | ||
167 | INIT_LIST_HEAD(&c->children); | |
168 | INIT_LIST_HEAD(&c->sibling); | |
169 | ||
170 | if (c->ops && c->ops->init) | |
171 | c->ops->init(c); | |
172 | ||
71fc84cc CC |
173 | clk_recalculate_rate(c); |
174 | ||
d8611961 CC |
175 | list_add(&c->node, &clocks); |
176 | ||
177 | if (c->parent) | |
178 | list_add_tail(&c->sibling, &c->parent->children); | |
179 | ||
180 | spin_unlock_irqrestore(&clock_lock, flags); | |
181 | } | |
182 | ||
183 | int clk_enable_locked(struct clk *c) | |
184 | { | |
185 | int ret; | |
186 | pr_debug("%s: %s\n", __func__, c->name); | |
187 | if (c->refcnt == 0) { | |
188 | if (c->parent) { | |
189 | ret = clk_enable_locked(c->parent); | |
190 | if (ret) | |
191 | return ret; | |
192 | } | |
193 | ||
194 | if (c->ops && c->ops->enable) { | |
195 | ret = c->ops->enable(c); | |
196 | if (ret) { | |
197 | if (c->parent) | |
198 | clk_disable_locked(c->parent); | |
199 | return ret; | |
200 | } | |
201 | c->state = ON; | |
202 | #ifdef CONFIG_DEBUG_FS | |
203 | c->set = 1; | |
204 | #endif | |
205 | } | |
206 | } | |
207 | c->refcnt++; | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
71fc84cc CC |
212 | int clk_enable_cansleep(struct clk *c) |
213 | { | |
214 | int ret; | |
215 | unsigned long flags; | |
216 | ||
217 | mutex_lock(&dvfs_lock); | |
218 | ||
219 | if (clk_is_dvfs(c) && c->refcnt > 0) | |
220 | dvfs_set_rate(c->dvfs, c->rate); | |
221 | ||
222 | spin_lock_irqsave(&clock_lock, flags); | |
223 | ret = clk_enable_locked(c); | |
224 | spin_unlock_irqrestore(&clock_lock, flags); | |
225 | ||
226 | mutex_unlock(&dvfs_lock); | |
227 | ||
228 | return ret; | |
229 | } | |
230 | EXPORT_SYMBOL(clk_enable_cansleep); | |
231 | ||
d8611961 CC |
232 | int clk_enable(struct clk *c) |
233 | { | |
234 | int ret; | |
235 | unsigned long flags; | |
71fc84cc CC |
236 | |
237 | if (clk_is_dvfs(c)) | |
238 | BUG(); | |
239 | ||
d8611961 CC |
240 | spin_lock_irqsave(&clock_lock, flags); |
241 | ret = clk_enable_locked(c); | |
242 | spin_unlock_irqrestore(&clock_lock, flags); | |
71fc84cc | 243 | |
d8611961 CC |
244 | return ret; |
245 | } | |
246 | EXPORT_SYMBOL(clk_enable); | |
247 | ||
248 | void clk_disable_locked(struct clk *c) | |
249 | { | |
250 | pr_debug("%s: %s\n", __func__, c->name); | |
251 | if (c->refcnt == 0) { | |
252 | WARN(1, "Attempting to disable clock %s with refcnt 0", c->name); | |
253 | return; | |
254 | } | |
255 | if (c->refcnt == 1) { | |
256 | if (c->ops && c->ops->disable) | |
257 | c->ops->disable(c); | |
258 | ||
259 | if (c->parent) | |
260 | clk_disable_locked(c->parent); | |
261 | ||
262 | c->state = OFF; | |
263 | } | |
264 | c->refcnt--; | |
265 | } | |
266 | ||
71fc84cc CC |
267 | void clk_disable_cansleep(struct clk *c) |
268 | { | |
269 | unsigned long flags; | |
270 | ||
271 | mutex_lock(&dvfs_lock); | |
272 | ||
273 | spin_lock_irqsave(&clock_lock, flags); | |
274 | clk_disable_locked(c); | |
275 | spin_unlock_irqrestore(&clock_lock, flags); | |
276 | ||
277 | if (clk_is_dvfs(c) && c->refcnt == 0) | |
278 | dvfs_set_rate(c->dvfs, c->rate); | |
279 | ||
280 | mutex_unlock(&dvfs_lock); | |
281 | } | |
282 | EXPORT_SYMBOL(clk_disable_cansleep); | |
283 | ||
d8611961 CC |
284 | void clk_disable(struct clk *c) |
285 | { | |
286 | unsigned long flags; | |
71fc84cc CC |
287 | |
288 | if (clk_is_dvfs(c)) | |
289 | BUG(); | |
290 | ||
d8611961 CC |
291 | spin_lock_irqsave(&clock_lock, flags); |
292 | clk_disable_locked(c); | |
293 | spin_unlock_irqrestore(&clock_lock, flags); | |
294 | } | |
295 | EXPORT_SYMBOL(clk_disable); | |
296 | ||
297 | int clk_set_parent_locked(struct clk *c, struct clk *parent) | |
298 | { | |
299 | int ret; | |
300 | ||
301 | pr_debug("%s: %s\n", __func__, c->name); | |
302 | ||
303 | if (!c->ops || !c->ops->set_parent) | |
304 | return -ENOSYS; | |
305 | ||
306 | ret = c->ops->set_parent(c, parent); | |
307 | ||
308 | if (ret) | |
309 | return ret; | |
310 | ||
71fc84cc CC |
311 | clk_recalculate_rate(c); |
312 | ||
d8611961 CC |
313 | propagate_rate(c); |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | int clk_set_parent(struct clk *c, struct clk *parent) | |
319 | { | |
320 | int ret; | |
321 | unsigned long flags; | |
322 | spin_lock_irqsave(&clock_lock, flags); | |
323 | ret = clk_set_parent_locked(c, parent); | |
324 | spin_unlock_irqrestore(&clock_lock, flags); | |
325 | return ret; | |
326 | } | |
327 | EXPORT_SYMBOL(clk_set_parent); | |
328 | ||
329 | struct clk *clk_get_parent(struct clk *c) | |
330 | { | |
331 | return c->parent; | |
332 | } | |
333 | EXPORT_SYMBOL(clk_get_parent); | |
334 | ||
71fc84cc CC |
335 | int clk_set_rate_locked(struct clk *c, unsigned long rate) |
336 | { | |
337 | int ret; | |
338 | ||
339 | if (rate > c->max_rate) | |
340 | rate = c->max_rate; | |
341 | ||
342 | if (!c->ops || !c->ops->set_rate) | |
343 | return -ENOSYS; | |
344 | ||
345 | ret = c->ops->set_rate(c, rate); | |
346 | ||
347 | if (ret) | |
348 | return ret; | |
349 | ||
350 | clk_recalculate_rate(c); | |
351 | ||
352 | propagate_rate(c); | |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
357 | int clk_set_rate_cansleep(struct clk *c, unsigned long rate) | |
d8611961 CC |
358 | { |
359 | int ret = 0; | |
360 | unsigned long flags; | |
361 | ||
71fc84cc CC |
362 | pr_debug("%s: %s\n", __func__, c->name); |
363 | ||
364 | mutex_lock(&dvfs_lock); | |
365 | ||
366 | if (rate > c->rate) | |
367 | ret = dvfs_set_rate(c->dvfs, rate); | |
368 | if (ret) | |
369 | goto out; | |
370 | ||
d8611961 | 371 | spin_lock_irqsave(&clock_lock, flags); |
71fc84cc CC |
372 | ret = clk_set_rate_locked(c, rate); |
373 | spin_unlock_irqrestore(&clock_lock, flags); | |
d8611961 | 374 | |
71fc84cc CC |
375 | if (ret) |
376 | goto out; | |
d8611961 | 377 | |
71fc84cc | 378 | ret = dvfs_set_rate(c->dvfs, rate); |
d8611961 | 379 | |
71fc84cc CC |
380 | out: |
381 | mutex_unlock(&dvfs_lock); | |
382 | return ret; | |
383 | } | |
384 | EXPORT_SYMBOL(clk_set_rate_cansleep); | |
385 | ||
386 | int clk_set_rate(struct clk *c, unsigned long rate) | |
387 | { | |
388 | int ret = 0; | |
389 | unsigned long flags; | |
390 | ||
391 | pr_debug("%s: %s\n", __func__, c->name); | |
392 | ||
393 | if (clk_is_dvfs(c)) | |
394 | BUG(); | |
d8611961 | 395 | |
71fc84cc CC |
396 | spin_lock_irqsave(&clock_lock, flags); |
397 | ret = clk_set_rate_locked(c, rate); | |
d8611961 CC |
398 | spin_unlock_irqrestore(&clock_lock, flags); |
399 | ||
400 | return ret; | |
401 | } | |
402 | EXPORT_SYMBOL(clk_set_rate); | |
403 | ||
404 | unsigned long clk_get_rate(struct clk *c) | |
405 | { | |
406 | unsigned long flags; | |
407 | unsigned long ret; | |
408 | ||
409 | spin_lock_irqsave(&clock_lock, flags); | |
410 | ||
411 | pr_debug("%s: %s\n", __func__, c->name); | |
412 | ||
413 | ret = c->rate; | |
414 | ||
415 | spin_unlock_irqrestore(&clock_lock, flags); | |
416 | return ret; | |
417 | } | |
418 | EXPORT_SYMBOL(clk_get_rate); | |
419 | ||
71fc84cc CC |
420 | long clk_round_rate(struct clk *c, unsigned long rate) |
421 | { | |
422 | pr_debug("%s: %s\n", __func__, c->name); | |
423 | ||
424 | if (!c->ops || !c->ops->round_rate) | |
425 | return -ENOSYS; | |
426 | ||
427 | if (rate > c->max_rate) | |
428 | rate = c->max_rate; | |
429 | ||
430 | return c->ops->round_rate(c, rate); | |
431 | } | |
432 | EXPORT_SYMBOL(clk_round_rate); | |
433 | ||
d8611961 CC |
434 | static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) |
435 | { | |
436 | struct clk *c; | |
437 | struct clk *p; | |
438 | ||
439 | int ret = 0; | |
440 | ||
441 | c = tegra_get_clock_by_name(table->name); | |
442 | ||
443 | if (!c) { | |
444 | pr_warning("Unable to initialize clock %s\n", | |
445 | table->name); | |
446 | return -ENODEV; | |
447 | } | |
448 | ||
449 | if (table->parent) { | |
450 | p = tegra_get_clock_by_name(table->parent); | |
451 | if (!p) { | |
452 | pr_warning("Unable to find parent %s of clock %s\n", | |
453 | table->parent, table->name); | |
454 | return -ENODEV; | |
455 | } | |
456 | ||
457 | if (c->parent != p) { | |
458 | ret = clk_set_parent(c, p); | |
459 | if (ret) { | |
460 | pr_warning("Unable to set parent %s of clock %s: %d\n", | |
461 | table->parent, table->name, ret); | |
462 | return -EINVAL; | |
463 | } | |
464 | } | |
465 | } | |
466 | ||
467 | if (table->rate && table->rate != clk_get_rate(c)) { | |
468 | ret = clk_set_rate(c, table->rate); | |
469 | if (ret) { | |
470 | pr_warning("Unable to set clock %s to rate %lu: %d\n", | |
471 | table->name, table->rate, ret); | |
472 | return -EINVAL; | |
473 | } | |
474 | } | |
475 | ||
476 | if (table->enabled) { | |
477 | ret = clk_enable(c); | |
478 | if (ret) { | |
479 | pr_warning("Unable to enable clock %s: %d\n", | |
480 | table->name, ret); | |
481 | return -EINVAL; | |
482 | } | |
483 | } | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | void tegra_clk_init_from_table(struct tegra_clk_init_table *table) | |
489 | { | |
490 | for (; table->name; table++) | |
491 | tegra_clk_init_one_from_table(table); | |
492 | } | |
493 | EXPORT_SYMBOL(tegra_clk_init_from_table); | |
494 | ||
495 | void tegra_periph_reset_deassert(struct clk *c) | |
496 | { | |
497 | tegra2_periph_reset_deassert(c); | |
498 | } | |
499 | EXPORT_SYMBOL(tegra_periph_reset_deassert); | |
500 | ||
501 | void tegra_periph_reset_assert(struct clk *c) | |
502 | { | |
503 | tegra2_periph_reset_assert(c); | |
504 | } | |
505 | EXPORT_SYMBOL(tegra_periph_reset_assert); | |
506 | ||
71fc84cc | 507 | void __init tegra_init_clock(void) |
d8611961 CC |
508 | { |
509 | tegra2_init_clocks(); | |
71fc84cc CC |
510 | } |
511 | ||
512 | int __init tegra_init_dvfs(void) | |
513 | { | |
514 | struct clk *c, *safe; | |
515 | ||
516 | mutex_lock(&dvfs_lock); | |
517 | ||
518 | list_for_each_entry_safe(c, safe, &clocks, node) | |
519 | if (c->dvfs) | |
520 | dvfs_init(c); | |
521 | ||
522 | mutex_unlock(&dvfs_lock); | |
d8611961 CC |
523 | |
524 | return 0; | |
525 | } | |
526 | ||
71fc84cc CC |
527 | late_initcall(tegra_init_dvfs); |
528 | ||
d8611961 CC |
529 | #ifdef CONFIG_DEBUG_FS |
530 | static struct dentry *clk_debugfs_root; | |
531 | ||
532 | ||
533 | static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) | |
534 | { | |
535 | struct clk *child; | |
536 | struct clk *safe; | |
537 | const char *state = "uninit"; | |
71fc84cc | 538 | char div[8] = {0}; |
d8611961 CC |
539 | |
540 | if (c->state == ON) | |
541 | state = "on"; | |
542 | else if (c->state == OFF) | |
543 | state = "off"; | |
544 | ||
545 | if (c->mul != 0 && c->div != 0) { | |
71fc84cc CC |
546 | if (c->mul > c->div) { |
547 | int mul = c->mul / c->div; | |
548 | int mul2 = (c->mul * 10 / c->div) % 10; | |
549 | int mul3 = (c->mul * 10) % c->div; | |
550 | if (mul2 == 0 && mul3 == 0) | |
551 | snprintf(div, sizeof(div), "x%d", mul); | |
552 | else if (mul3 == 0) | |
553 | snprintf(div, sizeof(div), "x%d.%d", mul, mul2); | |
554 | else | |
555 | snprintf(div, sizeof(div), "x%d.%d..", mul, mul2); | |
556 | } else { | |
d8611961 CC |
557 | snprintf(div, sizeof(div), "%d%s", c->div / c->mul, |
558 | (c->div % c->mul) ? ".5" : ""); | |
71fc84cc | 559 | } |
d8611961 CC |
560 | } |
561 | ||
71fc84cc CC |
562 | seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n", |
563 | level * 3 + 1, "", | |
564 | c->rate > c->max_rate ? '!' : ' ', | |
565 | !c->set ? '*' : ' ', | |
d8611961 CC |
566 | 30 - level * 3, c->name, |
567 | state, c->refcnt, div, c->rate); | |
568 | list_for_each_entry_safe(child, safe, &c->children, sibling) { | |
569 | clock_tree_show_one(s, child, level + 1); | |
570 | } | |
571 | } | |
572 | ||
573 | static int clock_tree_show(struct seq_file *s, void *data) | |
574 | { | |
575 | struct clk *c; | |
576 | unsigned long flags; | |
71fc84cc CC |
577 | seq_printf(s, " clock state ref div rate\n"); |
578 | seq_printf(s, "--------------------------------------------------------------\n"); | |
d8611961 CC |
579 | spin_lock_irqsave(&clock_lock, flags); |
580 | list_for_each_entry(c, &clocks, node) | |
581 | if (c->parent == NULL) | |
582 | clock_tree_show_one(s, c, 0); | |
583 | spin_unlock_irqrestore(&clock_lock, flags); | |
584 | return 0; | |
585 | } | |
586 | ||
587 | static int clock_tree_open(struct inode *inode, struct file *file) | |
588 | { | |
589 | return single_open(file, clock_tree_show, inode->i_private); | |
590 | } | |
591 | ||
592 | static const struct file_operations clock_tree_fops = { | |
593 | .open = clock_tree_open, | |
594 | .read = seq_read, | |
595 | .llseek = seq_lseek, | |
596 | .release = single_release, | |
597 | }; | |
598 | ||
599 | static int possible_parents_show(struct seq_file *s, void *data) | |
600 | { | |
601 | struct clk *c = s->private; | |
602 | int i; | |
603 | ||
604 | for (i = 0; c->inputs[i].input; i++) { | |
605 | char *first = (i == 0) ? "" : " "; | |
606 | seq_printf(s, "%s%s", first, c->inputs[i].input->name); | |
607 | } | |
608 | seq_printf(s, "\n"); | |
609 | return 0; | |
610 | } | |
611 | ||
612 | static int possible_parents_open(struct inode *inode, struct file *file) | |
613 | { | |
614 | return single_open(file, possible_parents_show, inode->i_private); | |
615 | } | |
616 | ||
617 | static const struct file_operations possible_parents_fops = { | |
618 | .open = possible_parents_open, | |
619 | .read = seq_read, | |
620 | .llseek = seq_lseek, | |
621 | .release = single_release, | |
622 | }; | |
623 | ||
624 | static int clk_debugfs_register_one(struct clk *c) | |
625 | { | |
626 | struct dentry *d, *child, *child_tmp; | |
627 | ||
628 | d = debugfs_create_dir(c->name, clk_debugfs_root); | |
629 | if (!d) | |
630 | return -ENOMEM; | |
631 | c->dent = d; | |
632 | ||
633 | d = debugfs_create_u8("refcnt", S_IRUGO, c->dent, (u8 *)&c->refcnt); | |
634 | if (!d) | |
635 | goto err_out; | |
636 | ||
637 | d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); | |
638 | if (!d) | |
639 | goto err_out; | |
640 | ||
641 | d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); | |
642 | if (!d) | |
643 | goto err_out; | |
644 | ||
645 | if (c->inputs) { | |
646 | d = debugfs_create_file("possible_parents", S_IRUGO, c->dent, | |
647 | c, &possible_parents_fops); | |
648 | if (!d) | |
649 | goto err_out; | |
650 | } | |
651 | ||
652 | return 0; | |
653 | ||
654 | err_out: | |
655 | d = c->dent; | |
656 | list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) | |
657 | debugfs_remove(child); | |
658 | debugfs_remove(c->dent); | |
659 | return -ENOMEM; | |
660 | } | |
661 | ||
662 | static int clk_debugfs_register(struct clk *c) | |
663 | { | |
664 | int err; | |
665 | struct clk *pa = c->parent; | |
666 | ||
667 | if (pa && !pa->dent) { | |
668 | err = clk_debugfs_register(pa); | |
669 | if (err) | |
670 | return err; | |
671 | } | |
672 | ||
673 | if (!c->dent) { | |
674 | err = clk_debugfs_register_one(c); | |
675 | if (err) | |
676 | return err; | |
677 | } | |
678 | return 0; | |
679 | } | |
680 | ||
681 | static int __init clk_debugfs_init(void) | |
682 | { | |
683 | struct clk *c; | |
684 | struct dentry *d; | |
685 | int err = -ENOMEM; | |
686 | ||
687 | d = debugfs_create_dir("clock", NULL); | |
688 | if (!d) | |
689 | return -ENOMEM; | |
690 | clk_debugfs_root = d; | |
691 | ||
692 | d = debugfs_create_file("clock_tree", S_IRUGO, clk_debugfs_root, NULL, | |
693 | &clock_tree_fops); | |
694 | if (!d) | |
695 | goto err_out; | |
696 | ||
697 | list_for_each_entry(c, &clocks, node) { | |
698 | err = clk_debugfs_register(c); | |
699 | if (err) | |
700 | goto err_out; | |
701 | } | |
702 | return 0; | |
703 | err_out: | |
704 | debugfs_remove_recursive(clk_debugfs_root); | |
705 | return err; | |
706 | } | |
707 | ||
708 | late_initcall(clk_debugfs_init); | |
709 | #endif |