diff options
Diffstat (limited to 'arch/arm/mach-tegra/clock.c')
-rw-r--r-- | arch/arm/mach-tegra/clock.c | 267 |
1 files changed, 244 insertions, 23 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 03ad578349b9..ae19f95585be 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c | |||
@@ -24,13 +24,80 @@ | |||
24 | #include <linux/debugfs.h> | 24 | #include <linux/debugfs.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/seq_file.h> | 26 | #include <linux/seq_file.h> |
27 | #include <linux/regulator/consumer.h> | ||
27 | #include <asm/clkdev.h> | 28 | #include <asm/clkdev.h> |
28 | 29 | ||
29 | #include "clock.h" | 30 | #include "clock.h" |
31 | #include "board.h" | ||
32 | #include "fuse.h" | ||
30 | 33 | ||
31 | static LIST_HEAD(clocks); | 34 | static LIST_HEAD(clocks); |
32 | 35 | ||
33 | static DEFINE_SPINLOCK(clock_lock); | 36 | static DEFINE_SPINLOCK(clock_lock); |
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 | } | ||
34 | 101 | ||
35 | struct clk *tegra_get_clock_by_name(const char *name) | 102 | struct clk *tegra_get_clock_by_name(const char *name) |
36 | { | 103 | { |
@@ -48,14 +115,31 @@ struct clk *tegra_get_clock_by_name(const char *name) | |||
48 | return ret; | 115 | return ret; |
49 | } | 116 | } |
50 | 117 | ||
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 | |||
51 | int clk_reparent(struct clk *c, struct clk *parent) | 139 | int clk_reparent(struct clk *c, struct clk *parent) |
52 | { | 140 | { |
53 | pr_debug("%s: %s\n", __func__, c->name); | 141 | pr_debug("%s: %s\n", __func__, c->name); |
54 | if (c->refcnt && c->parent) | ||
55 | clk_disable_locked(c->parent); | ||
56 | c->parent = parent; | 142 | c->parent = parent; |
57 | if (c->refcnt && c->parent) | ||
58 | clk_enable_locked(c->parent); | ||
59 | list_del(&c->sibling); | 143 | list_del(&c->sibling); |
60 | list_add_tail(&c->sibling, &parent->children); | 144 | list_add_tail(&c->sibling, &parent->children); |
61 | return 0; | 145 | return 0; |
@@ -67,8 +151,7 @@ static void propagate_rate(struct clk *c) | |||
67 | pr_debug("%s: %s\n", __func__, c->name); | 151 | pr_debug("%s: %s\n", __func__, c->name); |
68 | list_for_each_entry(clkp, &c->children, sibling) { | 152 | list_for_each_entry(clkp, &c->children, sibling) { |
69 | pr_debug(" %s\n", clkp->name); | 153 | pr_debug(" %s\n", clkp->name); |
70 | if (clkp->ops->recalculate_rate) | 154 | clk_recalculate_rate(clkp); |
71 | clkp->ops->recalculate_rate(clkp); | ||
72 | propagate_rate(clkp); | 155 | propagate_rate(clkp); |
73 | } | 156 | } |
74 | } | 157 | } |
@@ -77,6 +160,8 @@ void clk_init(struct clk *c) | |||
77 | { | 160 | { |
78 | unsigned long flags; | 161 | unsigned long flags; |
79 | 162 | ||
163 | pr_debug("%s: %s\n", __func__, c->name); | ||
164 | |||
80 | spin_lock_irqsave(&clock_lock, flags); | 165 | spin_lock_irqsave(&clock_lock, flags); |
81 | 166 | ||
82 | INIT_LIST_HEAD(&c->children); | 167 | INIT_LIST_HEAD(&c->children); |
@@ -85,6 +170,8 @@ void clk_init(struct clk *c) | |||
85 | if (c->ops && c->ops->init) | 170 | if (c->ops && c->ops->init) |
86 | c->ops->init(c); | 171 | c->ops->init(c); |
87 | 172 | ||
173 | clk_recalculate_rate(c); | ||
174 | |||
88 | list_add(&c->node, &clocks); | 175 | list_add(&c->node, &clocks); |
89 | 176 | ||
90 | if (c->parent) | 177 | if (c->parent) |
@@ -122,13 +209,38 @@ int clk_enable_locked(struct clk *c) | |||
122 | return 0; | 209 | return 0; |
123 | } | 210 | } |
124 | 211 | ||
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 | |||
125 | int clk_enable(struct clk *c) | 232 | int clk_enable(struct clk *c) |
126 | { | 233 | { |
127 | int ret; | 234 | int ret; |
128 | unsigned long flags; | 235 | unsigned long flags; |
236 | |||
237 | if (clk_is_dvfs(c)) | ||
238 | BUG(); | ||
239 | |||
129 | spin_lock_irqsave(&clock_lock, flags); | 240 | spin_lock_irqsave(&clock_lock, flags); |
130 | ret = clk_enable_locked(c); | 241 | ret = clk_enable_locked(c); |
131 | spin_unlock_irqrestore(&clock_lock, flags); | 242 | spin_unlock_irqrestore(&clock_lock, flags); |
243 | |||
132 | return ret; | 244 | return ret; |
133 | } | 245 | } |
134 | EXPORT_SYMBOL(clk_enable); | 246 | EXPORT_SYMBOL(clk_enable); |
@@ -152,9 +264,30 @@ void clk_disable_locked(struct clk *c) | |||
152 | c->refcnt--; | 264 | c->refcnt--; |
153 | } | 265 | } |
154 | 266 | ||
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 | |||
155 | void clk_disable(struct clk *c) | 284 | void clk_disable(struct clk *c) |
156 | { | 285 | { |
157 | unsigned long flags; | 286 | unsigned long flags; |
287 | |||
288 | if (clk_is_dvfs(c)) | ||
289 | BUG(); | ||
290 | |||
158 | spin_lock_irqsave(&clock_lock, flags); | 291 | spin_lock_irqsave(&clock_lock, flags); |
159 | clk_disable_locked(c); | 292 | clk_disable_locked(c); |
160 | spin_unlock_irqrestore(&clock_lock, flags); | 293 | spin_unlock_irqrestore(&clock_lock, flags); |
@@ -175,6 +308,8 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent) | |||
175 | if (ret) | 308 | if (ret) |
176 | return ret; | 309 | return ret; |
177 | 310 | ||
311 | clk_recalculate_rate(c); | ||
312 | |||
178 | propagate_rate(c); | 313 | propagate_rate(c); |
179 | 314 | ||
180 | return 0; | 315 | return 0; |
@@ -197,22 +332,69 @@ struct clk *clk_get_parent(struct clk *c) | |||
197 | } | 332 | } |
198 | EXPORT_SYMBOL(clk_get_parent); | 333 | EXPORT_SYMBOL(clk_get_parent); |
199 | 334 | ||
200 | int clk_set_rate(struct clk *c, unsigned long rate) | 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) | ||
201 | { | 358 | { |
202 | int ret = 0; | 359 | int ret = 0; |
203 | unsigned long flags; | 360 | unsigned long flags; |
204 | 361 | ||
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 | |||
205 | spin_lock_irqsave(&clock_lock, flags); | 371 | spin_lock_irqsave(&clock_lock, flags); |
372 | ret = clk_set_rate_locked(c, rate); | ||
373 | spin_unlock_irqrestore(&clock_lock, flags); | ||
206 | 374 | ||
207 | pr_debug("%s: %s\n", __func__, c->name); | 375 | if (ret) |
376 | goto out; | ||
208 | 377 | ||
209 | if (c->ops && c->ops->set_rate) | 378 | ret = dvfs_set_rate(c->dvfs, rate); |
210 | ret = c->ops->set_rate(c, rate); | ||
211 | else | ||
212 | ret = -ENOSYS; | ||
213 | 379 | ||
214 | propagate_rate(c); | 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(); | ||
215 | 395 | ||
396 | spin_lock_irqsave(&clock_lock, flags); | ||
397 | ret = clk_set_rate_locked(c, rate); | ||
216 | spin_unlock_irqrestore(&clock_lock, flags); | 398 | spin_unlock_irqrestore(&clock_lock, flags); |
217 | 399 | ||
218 | return ret; | 400 | return ret; |
@@ -235,6 +417,20 @@ unsigned long clk_get_rate(struct clk *c) | |||
235 | } | 417 | } |
236 | EXPORT_SYMBOL(clk_get_rate); | 418 | EXPORT_SYMBOL(clk_get_rate); |
237 | 419 | ||
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 | |||
238 | static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) | 434 | static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) |
239 | { | 435 | { |
240 | struct clk *c; | 436 | struct clk *c; |
@@ -308,13 +504,28 @@ void tegra_periph_reset_assert(struct clk *c) | |||
308 | } | 504 | } |
309 | EXPORT_SYMBOL(tegra_periph_reset_assert); | 505 | EXPORT_SYMBOL(tegra_periph_reset_assert); |
310 | 506 | ||
311 | int __init tegra_init_clock(void) | 507 | void __init tegra_init_clock(void) |
312 | { | 508 | { |
313 | tegra2_init_clocks(); | 509 | tegra2_init_clocks(); |
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); | ||
314 | 523 | ||
315 | return 0; | 524 | return 0; |
316 | } | 525 | } |
317 | 526 | ||
527 | late_initcall(tegra_init_dvfs); | ||
528 | |||
318 | #ifdef CONFIG_DEBUG_FS | 529 | #ifdef CONFIG_DEBUG_FS |
319 | static struct dentry *clk_debugfs_root; | 530 | static struct dentry *clk_debugfs_root; |
320 | 531 | ||
@@ -324,7 +535,7 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) | |||
324 | struct clk *child; | 535 | struct clk *child; |
325 | struct clk *safe; | 536 | struct clk *safe; |
326 | const char *state = "uninit"; | 537 | const char *state = "uninit"; |
327 | char div[5] = {0}; | 538 | char div[8] = {0}; |
328 | 539 | ||
329 | if (c->state == ON) | 540 | if (c->state == ON) |
330 | state = "on"; | 541 | state = "on"; |
@@ -332,16 +543,26 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) | |||
332 | state = "off"; | 543 | state = "off"; |
333 | 544 | ||
334 | if (c->mul != 0 && c->div != 0) { | 545 | if (c->mul != 0 && c->div != 0) { |
335 | BUG_ON(c->mul > 2); | 546 | if (c->mul > c->div) { |
336 | if (c->mul > c->div) | 547 | int mul = c->mul / c->div; |
337 | snprintf(div, sizeof(div), "x%d", c->mul / c->div); | 548 | int mul2 = (c->mul * 10 / c->div) % 10; |
338 | else | 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 { | ||
339 | snprintf(div, sizeof(div), "%d%s", c->div / c->mul, | 557 | snprintf(div, sizeof(div), "%d%s", c->div / c->mul, |
340 | (c->div % c->mul) ? ".5" : ""); | 558 | (c->div % c->mul) ? ".5" : ""); |
559 | } | ||
341 | } | 560 | } |
342 | 561 | ||
343 | seq_printf(s, "%*s%-*s %-6s %-3d %-5s %-10lu\n", | 562 | seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n", |
344 | level * 3 + 1, c->set ? "" : "*", | 563 | level * 3 + 1, "", |
564 | c->rate > c->max_rate ? '!' : ' ', | ||
565 | !c->set ? '*' : ' ', | ||
345 | 30 - level * 3, c->name, | 566 | 30 - level * 3, c->name, |
346 | state, c->refcnt, div, c->rate); | 567 | state, c->refcnt, div, c->rate); |
347 | list_for_each_entry_safe(child, safe, &c->children, sibling) { | 568 | list_for_each_entry_safe(child, safe, &c->children, sibling) { |
@@ -353,8 +574,8 @@ static int clock_tree_show(struct seq_file *s, void *data) | |||
353 | { | 574 | { |
354 | struct clk *c; | 575 | struct clk *c; |
355 | unsigned long flags; | 576 | unsigned long flags; |
356 | seq_printf(s, " clock state ref div rate \n"); | 577 | seq_printf(s, " clock state ref div rate\n"); |
357 | seq_printf(s, "-----------------------------------------------------------\n"); | 578 | seq_printf(s, "--------------------------------------------------------------\n"); |
358 | spin_lock_irqsave(&clock_lock, flags); | 579 | spin_lock_irqsave(&clock_lock, flags); |
359 | list_for_each_entry(c, &clocks, node) | 580 | list_for_each_entry(c, &clocks, node) |
360 | if (c->parent == NULL) | 581 | if (c->parent == NULL) |