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> | |
27 | #include <asm/clkdev.h> | |
28 | ||
29 | #include "clock.h" | |
30 | ||
31 | static LIST_HEAD(clocks); | |
32 | ||
33 | static DEFINE_SPINLOCK(clock_lock); | |
34 | ||
35 | struct clk *tegra_get_clock_by_name(const char *name) | |
36 | { | |
37 | struct clk *c; | |
38 | struct clk *ret = NULL; | |
39 | unsigned long flags; | |
40 | spin_lock_irqsave(&clock_lock, flags); | |
41 | list_for_each_entry(c, &clocks, node) { | |
42 | if (strcmp(c->name, name) == 0) { | |
43 | ret = c; | |
44 | break; | |
45 | } | |
46 | } | |
47 | spin_unlock_irqrestore(&clock_lock, flags); | |
48 | return ret; | |
49 | } | |
50 | ||
51 | int clk_reparent(struct clk *c, struct clk *parent) | |
52 | { | |
53 | pr_debug("%s: %s\n", __func__, c->name); | |
54 | if (c->refcnt && c->parent) | |
55 | clk_disable_locked(c->parent); | |
56 | c->parent = parent; | |
57 | if (c->refcnt && c->parent) | |
58 | clk_enable_locked(c->parent); | |
59 | list_del(&c->sibling); | |
60 | list_add_tail(&c->sibling, &parent->children); | |
61 | return 0; | |
62 | } | |
63 | ||
64 | static void propagate_rate(struct clk *c) | |
65 | { | |
66 | struct clk *clkp; | |
67 | pr_debug("%s: %s\n", __func__, c->name); | |
68 | list_for_each_entry(clkp, &c->children, sibling) { | |
69 | pr_debug(" %s\n", clkp->name); | |
70 | if (clkp->ops->recalculate_rate) | |
71 | clkp->ops->recalculate_rate(clkp); | |
72 | propagate_rate(clkp); | |
73 | } | |
74 | } | |
75 | ||
76 | void clk_init(struct clk *c) | |
77 | { | |
78 | unsigned long flags; | |
79 | ||
80 | spin_lock_irqsave(&clock_lock, flags); | |
81 | ||
82 | INIT_LIST_HEAD(&c->children); | |
83 | INIT_LIST_HEAD(&c->sibling); | |
84 | ||
85 | if (c->ops && c->ops->init) | |
86 | c->ops->init(c); | |
87 | ||
88 | list_add(&c->node, &clocks); | |
89 | ||
90 | if (c->parent) | |
91 | list_add_tail(&c->sibling, &c->parent->children); | |
92 | ||
93 | spin_unlock_irqrestore(&clock_lock, flags); | |
94 | } | |
95 | ||
96 | int clk_enable_locked(struct clk *c) | |
97 | { | |
98 | int ret; | |
99 | pr_debug("%s: %s\n", __func__, c->name); | |
100 | if (c->refcnt == 0) { | |
101 | if (c->parent) { | |
102 | ret = clk_enable_locked(c->parent); | |
103 | if (ret) | |
104 | return ret; | |
105 | } | |
106 | ||
107 | if (c->ops && c->ops->enable) { | |
108 | ret = c->ops->enable(c); | |
109 | if (ret) { | |
110 | if (c->parent) | |
111 | clk_disable_locked(c->parent); | |
112 | return ret; | |
113 | } | |
114 | c->state = ON; | |
115 | #ifdef CONFIG_DEBUG_FS | |
116 | c->set = 1; | |
117 | #endif | |
118 | } | |
119 | } | |
120 | c->refcnt++; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | int clk_enable(struct clk *c) | |
126 | { | |
127 | int ret; | |
128 | unsigned long flags; | |
129 | spin_lock_irqsave(&clock_lock, flags); | |
130 | ret = clk_enable_locked(c); | |
131 | spin_unlock_irqrestore(&clock_lock, flags); | |
132 | return ret; | |
133 | } | |
134 | EXPORT_SYMBOL(clk_enable); | |
135 | ||
136 | void clk_disable_locked(struct clk *c) | |
137 | { | |
138 | pr_debug("%s: %s\n", __func__, c->name); | |
139 | if (c->refcnt == 0) { | |
140 | WARN(1, "Attempting to disable clock %s with refcnt 0", c->name); | |
141 | return; | |
142 | } | |
143 | if (c->refcnt == 1) { | |
144 | if (c->ops && c->ops->disable) | |
145 | c->ops->disable(c); | |
146 | ||
147 | if (c->parent) | |
148 | clk_disable_locked(c->parent); | |
149 | ||
150 | c->state = OFF; | |
151 | } | |
152 | c->refcnt--; | |
153 | } | |
154 | ||
155 | void clk_disable(struct clk *c) | |
156 | { | |
157 | unsigned long flags; | |
158 | spin_lock_irqsave(&clock_lock, flags); | |
159 | clk_disable_locked(c); | |
160 | spin_unlock_irqrestore(&clock_lock, flags); | |
161 | } | |
162 | EXPORT_SYMBOL(clk_disable); | |
163 | ||
164 | int clk_set_parent_locked(struct clk *c, struct clk *parent) | |
165 | { | |
166 | int ret; | |
167 | ||
168 | pr_debug("%s: %s\n", __func__, c->name); | |
169 | ||
170 | if (!c->ops || !c->ops->set_parent) | |
171 | return -ENOSYS; | |
172 | ||
173 | ret = c->ops->set_parent(c, parent); | |
174 | ||
175 | if (ret) | |
176 | return ret; | |
177 | ||
178 | propagate_rate(c); | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | int clk_set_parent(struct clk *c, struct clk *parent) | |
184 | { | |
185 | int ret; | |
186 | unsigned long flags; | |
187 | spin_lock_irqsave(&clock_lock, flags); | |
188 | ret = clk_set_parent_locked(c, parent); | |
189 | spin_unlock_irqrestore(&clock_lock, flags); | |
190 | return ret; | |
191 | } | |
192 | EXPORT_SYMBOL(clk_set_parent); | |
193 | ||
194 | struct clk *clk_get_parent(struct clk *c) | |
195 | { | |
196 | return c->parent; | |
197 | } | |
198 | EXPORT_SYMBOL(clk_get_parent); | |
199 | ||
200 | int clk_set_rate(struct clk *c, unsigned long rate) | |
201 | { | |
202 | int ret = 0; | |
203 | unsigned long flags; | |
204 | ||
205 | spin_lock_irqsave(&clock_lock, flags); | |
206 | ||
207 | pr_debug("%s: %s\n", __func__, c->name); | |
208 | ||
209 | if (c->ops && c->ops->set_rate) | |
210 | ret = c->ops->set_rate(c, rate); | |
211 | else | |
212 | ret = -ENOSYS; | |
213 | ||
214 | propagate_rate(c); | |
215 | ||
216 | spin_unlock_irqrestore(&clock_lock, flags); | |
217 | ||
218 | return ret; | |
219 | } | |
220 | EXPORT_SYMBOL(clk_set_rate); | |
221 | ||
222 | unsigned long clk_get_rate(struct clk *c) | |
223 | { | |
224 | unsigned long flags; | |
225 | unsigned long ret; | |
226 | ||
227 | spin_lock_irqsave(&clock_lock, flags); | |
228 | ||
229 | pr_debug("%s: %s\n", __func__, c->name); | |
230 | ||
231 | ret = c->rate; | |
232 | ||
233 | spin_unlock_irqrestore(&clock_lock, flags); | |
234 | return ret; | |
235 | } | |
236 | EXPORT_SYMBOL(clk_get_rate); | |
237 | ||
238 | static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) | |
239 | { | |
240 | struct clk *c; | |
241 | struct clk *p; | |
242 | ||
243 | int ret = 0; | |
244 | ||
245 | c = tegra_get_clock_by_name(table->name); | |
246 | ||
247 | if (!c) { | |
248 | pr_warning("Unable to initialize clock %s\n", | |
249 | table->name); | |
250 | return -ENODEV; | |
251 | } | |
252 | ||
253 | if (table->parent) { | |
254 | p = tegra_get_clock_by_name(table->parent); | |
255 | if (!p) { | |
256 | pr_warning("Unable to find parent %s of clock %s\n", | |
257 | table->parent, table->name); | |
258 | return -ENODEV; | |
259 | } | |
260 | ||
261 | if (c->parent != p) { | |
262 | ret = clk_set_parent(c, p); | |
263 | if (ret) { | |
264 | pr_warning("Unable to set parent %s of clock %s: %d\n", | |
265 | table->parent, table->name, ret); | |
266 | return -EINVAL; | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | if (table->rate && table->rate != clk_get_rate(c)) { | |
272 | ret = clk_set_rate(c, table->rate); | |
273 | if (ret) { | |
274 | pr_warning("Unable to set clock %s to rate %lu: %d\n", | |
275 | table->name, table->rate, ret); | |
276 | return -EINVAL; | |
277 | } | |
278 | } | |
279 | ||
280 | if (table->enabled) { | |
281 | ret = clk_enable(c); | |
282 | if (ret) { | |
283 | pr_warning("Unable to enable clock %s: %d\n", | |
284 | table->name, ret); | |
285 | return -EINVAL; | |
286 | } | |
287 | } | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | void tegra_clk_init_from_table(struct tegra_clk_init_table *table) | |
293 | { | |
294 | for (; table->name; table++) | |
295 | tegra_clk_init_one_from_table(table); | |
296 | } | |
297 | EXPORT_SYMBOL(tegra_clk_init_from_table); | |
298 | ||
299 | void tegra_periph_reset_deassert(struct clk *c) | |
300 | { | |
301 | tegra2_periph_reset_deassert(c); | |
302 | } | |
303 | EXPORT_SYMBOL(tegra_periph_reset_deassert); | |
304 | ||
305 | void tegra_periph_reset_assert(struct clk *c) | |
306 | { | |
307 | tegra2_periph_reset_assert(c); | |
308 | } | |
309 | EXPORT_SYMBOL(tegra_periph_reset_assert); | |
310 | ||
311 | int __init tegra_init_clock(void) | |
312 | { | |
313 | tegra2_init_clocks(); | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | #ifdef CONFIG_DEBUG_FS | |
319 | static struct dentry *clk_debugfs_root; | |
320 | ||
321 | ||
322 | static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) | |
323 | { | |
324 | struct clk *child; | |
325 | struct clk *safe; | |
326 | const char *state = "uninit"; | |
327 | char div[5] = {0}; | |
328 | ||
329 | if (c->state == ON) | |
330 | state = "on"; | |
331 | else if (c->state == OFF) | |
332 | state = "off"; | |
333 | ||
334 | if (c->mul != 0 && c->div != 0) { | |
335 | BUG_ON(c->mul > 2); | |
336 | if (c->mul > c->div) | |
337 | snprintf(div, sizeof(div), "x%d", c->mul / c->div); | |
338 | else | |
339 | snprintf(div, sizeof(div), "%d%s", c->div / c->mul, | |
340 | (c->div % c->mul) ? ".5" : ""); | |
341 | } | |
342 | ||
343 | seq_printf(s, "%*s%-*s %-6s %-3d %-5s %-10lu\n", | |
344 | level * 3 + 1, c->set ? "" : "*", | |
345 | 30 - level * 3, c->name, | |
346 | state, c->refcnt, div, c->rate); | |
347 | list_for_each_entry_safe(child, safe, &c->children, sibling) { | |
348 | clock_tree_show_one(s, child, level + 1); | |
349 | } | |
350 | } | |
351 | ||
352 | static int clock_tree_show(struct seq_file *s, void *data) | |
353 | { | |
354 | struct clk *c; | |
355 | unsigned long flags; | |
356 | seq_printf(s, " clock state ref div rate \n"); | |
357 | seq_printf(s, "-----------------------------------------------------------\n"); | |
358 | spin_lock_irqsave(&clock_lock, flags); | |
359 | list_for_each_entry(c, &clocks, node) | |
360 | if (c->parent == NULL) | |
361 | clock_tree_show_one(s, c, 0); | |
362 | spin_unlock_irqrestore(&clock_lock, flags); | |
363 | return 0; | |
364 | } | |
365 | ||
366 | static int clock_tree_open(struct inode *inode, struct file *file) | |
367 | { | |
368 | return single_open(file, clock_tree_show, inode->i_private); | |
369 | } | |
370 | ||
371 | static const struct file_operations clock_tree_fops = { | |
372 | .open = clock_tree_open, | |
373 | .read = seq_read, | |
374 | .llseek = seq_lseek, | |
375 | .release = single_release, | |
376 | }; | |
377 | ||
378 | static int possible_parents_show(struct seq_file *s, void *data) | |
379 | { | |
380 | struct clk *c = s->private; | |
381 | int i; | |
382 | ||
383 | for (i = 0; c->inputs[i].input; i++) { | |
384 | char *first = (i == 0) ? "" : " "; | |
385 | seq_printf(s, "%s%s", first, c->inputs[i].input->name); | |
386 | } | |
387 | seq_printf(s, "\n"); | |
388 | return 0; | |
389 | } | |
390 | ||
391 | static int possible_parents_open(struct inode *inode, struct file *file) | |
392 | { | |
393 | return single_open(file, possible_parents_show, inode->i_private); | |
394 | } | |
395 | ||
396 | static const struct file_operations possible_parents_fops = { | |
397 | .open = possible_parents_open, | |
398 | .read = seq_read, | |
399 | .llseek = seq_lseek, | |
400 | .release = single_release, | |
401 | }; | |
402 | ||
403 | static int clk_debugfs_register_one(struct clk *c) | |
404 | { | |
405 | struct dentry *d, *child, *child_tmp; | |
406 | ||
407 | d = debugfs_create_dir(c->name, clk_debugfs_root); | |
408 | if (!d) | |
409 | return -ENOMEM; | |
410 | c->dent = d; | |
411 | ||
412 | d = debugfs_create_u8("refcnt", S_IRUGO, c->dent, (u8 *)&c->refcnt); | |
413 | if (!d) | |
414 | goto err_out; | |
415 | ||
416 | d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); | |
417 | if (!d) | |
418 | goto err_out; | |
419 | ||
420 | d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); | |
421 | if (!d) | |
422 | goto err_out; | |
423 | ||
424 | if (c->inputs) { | |
425 | d = debugfs_create_file("possible_parents", S_IRUGO, c->dent, | |
426 | c, &possible_parents_fops); | |
427 | if (!d) | |
428 | goto err_out; | |
429 | } | |
430 | ||
431 | return 0; | |
432 | ||
433 | err_out: | |
434 | d = c->dent; | |
435 | list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) | |
436 | debugfs_remove(child); | |
437 | debugfs_remove(c->dent); | |
438 | return -ENOMEM; | |
439 | } | |
440 | ||
441 | static int clk_debugfs_register(struct clk *c) | |
442 | { | |
443 | int err; | |
444 | struct clk *pa = c->parent; | |
445 | ||
446 | if (pa && !pa->dent) { | |
447 | err = clk_debugfs_register(pa); | |
448 | if (err) | |
449 | return err; | |
450 | } | |
451 | ||
452 | if (!c->dent) { | |
453 | err = clk_debugfs_register_one(c); | |
454 | if (err) | |
455 | return err; | |
456 | } | |
457 | return 0; | |
458 | } | |
459 | ||
460 | static int __init clk_debugfs_init(void) | |
461 | { | |
462 | struct clk *c; | |
463 | struct dentry *d; | |
464 | int err = -ENOMEM; | |
465 | ||
466 | d = debugfs_create_dir("clock", NULL); | |
467 | if (!d) | |
468 | return -ENOMEM; | |
469 | clk_debugfs_root = d; | |
470 | ||
471 | d = debugfs_create_file("clock_tree", S_IRUGO, clk_debugfs_root, NULL, | |
472 | &clock_tree_fops); | |
473 | if (!d) | |
474 | goto err_out; | |
475 | ||
476 | list_for_each_entry(c, &clocks, node) { | |
477 | err = clk_debugfs_register(c); | |
478 | if (err) | |
479 | goto err_out; | |
480 | } | |
481 | return 0; | |
482 | err_out: | |
483 | debugfs_remove_recursive(clk_debugfs_root); | |
484 | return err; | |
485 | } | |
486 | ||
487 | late_initcall(clk_debugfs_init); | |
488 | #endif |