aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2008-03-18 04:22:06 -0400
committerTony Lindgren <tony@atomide.com>2008-04-14 13:29:38 -0400
commit543d93781a3c744017594d0721c4c1814a26bcce (patch)
treead0058328bb8a0c1746e451093f77691dee2980b /arch/arm
parent3d876e59c819efcc4810a5945f9d780d05e28d93 (diff)
ARM: OMAP2: Add common clock framework for 24xx and 34xx
This patch adds a common clock framework for 24xx and 34xx. Note that this patch does not add it to Makefile until in next patch. Some functions are modified from earlier 24xx clock framework code. Signed-off-by: Paul Walmsley <paul@pwsan.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-omap2/clock.c757
-rw-r--r--arch/arm/mach-omap2/clock.h73
2 files changed, 830 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
new file mode 100644
index 000000000000..a921efd43a70
--- /dev/null
+++ b/arch/arm/mach-omap2/clock.c
@@ -0,0 +1,757 @@
1/*
2 * linux/arch/arm/mach-omap2/clock.c
3 *
4 * Copyright (C) 2005 Texas Instruments Inc.
5 * Richard Woodruff <r-woodruff2@ti.com>
6 * Created for OMAP2.
7 *
8 * Cleaned up and modified to use omap shared clock framework by
9 * Tony Lindgren <tony@atomide.com>
10 *
11 * Copyright (C) 2007 Texas Instruments, Inc.
12 * Copyright (C) 2007 Nokia Corporation
13 * Paul Walmsley
14 *
15 * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation
16 * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License version 2 as
20 * published by the Free Software Foundation.
21 */
22#undef DEBUG
23
24#include <linux/module.h>
25#include <linux/kernel.h>
26#include <linux/device.h>
27#include <linux/list.h>
28#include <linux/errno.h>
29#include <linux/delay.h>
30#include <linux/clk.h>
31#include <asm/bitops.h>
32
33#include <asm/io.h>
34
35#include <asm/arch/clock.h>
36#include <asm/arch/sram.h>
37#include <asm/arch/cpu.h>
38#include <asm/div64.h>
39
40#include "memory.h"
41#include "sdrc.h"
42#include "clock.h"
43#include "prm.h"
44#include "prm-regbits-24xx.h"
45#include "cm.h"
46#include "cm-regbits-24xx.h"
47#include "cm-regbits-34xx.h"
48
49#define MAX_CLOCK_ENABLE_WAIT 100000
50
51u8 cpu_mask;
52
53/*-------------------------------------------------------------------------
54 * Omap2 specific clock functions
55 *-------------------------------------------------------------------------*/
56
57/**
58 * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware
59 * @clk: OMAP clock struct ptr to use
60 *
61 * Given a pointer to a source-selectable struct clk, read the hardware
62 * register and determine what its parent is currently set to. Update the
63 * clk->parent field with the appropriate clk ptr.
64 */
65void omap2_init_clksel_parent(struct clk *clk)
66{
67 const struct clksel *clks;
68 const struct clksel_rate *clkr;
69 u32 r, found = 0;
70
71 if (!clk->clksel)
72 return;
73
74 r = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
75 r >>= __ffs(clk->clksel_mask);
76
77 for (clks = clk->clksel; clks->parent && !found; clks++) {
78 for (clkr = clks->rates; clkr->div && !found; clkr++) {
79 if ((clkr->flags & cpu_mask) && (clkr->val == r)) {
80 if (clk->parent != clks->parent) {
81 pr_debug("clock: inited %s parent "
82 "to %s (was %s)\n",
83 clk->name, clks->parent->name,
84 ((clk->parent) ?
85 clk->parent->name : "NULL"));
86 clk->parent = clks->parent;
87 };
88 found = 1;
89 }
90 }
91 }
92
93 if (!found)
94 printk(KERN_ERR "clock: init parent: could not find "
95 "regval %0x for clock %s\n", r, clk->name);
96
97 return;
98}
99
100/* Returns the DPLL rate */
101u32 omap2_get_dpll_rate(struct clk *clk)
102{
103 long long dpll_clk;
104 u32 dpll_mult, dpll_div, dpll;
105 const struct dpll_data *dd;
106
107 dd = clk->dpll_data;
108 /* REVISIT: What do we return on error? */
109 if (!dd)
110 return 0;
111
112 dpll = __raw_readl(dd->mult_div1_reg);
113 dpll_mult = dpll & dd->mult_mask;
114 dpll_mult >>= __ffs(dd->mult_mask);
115 dpll_div = dpll & dd->div1_mask;
116 dpll_div >>= __ffs(dd->div1_mask);
117
118 dpll_clk = (long long)clk->parent->rate * dpll_mult;
119 do_div(dpll_clk, dpll_div + 1);
120
121 /* 34XX only */
122 if (dd->div2_reg) {
123 dpll = __raw_readl(dd->div2_reg);
124 dpll_div = dpll & dd->div2_mask;
125 dpll_div >>= __fss(dd->div2_mask);
126 do_div(dpll_clk, dpll_div + 1);
127 }
128
129 return dpll_clk;
130}
131
132/*
133 * Used for clocks that have the same value as the parent clock,
134 * divided by some factor
135 */
136void omap2_fixed_divisor_recalc(struct clk *clk)
137{
138 WARN_ON(!clk->fixed_div);
139
140 clk->rate = clk->parent->rate / clk->fixed_div;
141
142 if (clk->flags & RATE_PROPAGATES)
143 propagate_rate(clk);
144}
145
146/**
147 * omap2_wait_clock_ready - wait for clock to enable
148 * @reg: physical address of clock IDLEST register
149 * @mask: value to mask against to determine if the clock is active
150 * @name: name of the clock (for printk)
151 *
152 * Returns 1 if the clock enabled in time, or 0 if it failed to enable
153 * in roughly MAX_CLOCK_ENABLE_WAIT microseconds.
154 */
155int omap2_wait_clock_ready(void __iomem *reg, u32 mask, const char *name)
156{
157 int i = 0;
158 int ena = 0;
159
160 /*
161 * 24xx uses 0 to indicate not ready, and 1 to indicate ready.
162 * 34xx reverses this, just to keep us on our toes
163 */
164 if (cpu_mask & (RATE_IN_242X | RATE_IN_243X)) {
165 ena = mask;
166 } else if (cpu_mask & RATE_IN_343X) {
167 ena = 0;
168 }
169
170 /* Wait for lock */
171 while (((__raw_readl(reg) & mask) != ena) &&
172 (i++ < MAX_CLOCK_ENABLE_WAIT)) {
173 udelay(1);
174 }
175
176 if (i < MAX_CLOCK_ENABLE_WAIT)
177 pr_debug("Clock %s stable after %d loops\n", name, i);
178 else
179 printk(KERN_ERR "Clock %s didn't enable in %d tries\n",
180 name, MAX_CLOCK_ENABLE_WAIT);
181
182
183 return (i < MAX_CLOCK_ENABLE_WAIT) ? 1 : 0;
184};
185
186
187/*
188 * Note: We don't need special code here for INVERT_ENABLE
189 * for the time being since INVERT_ENABLE only applies to clocks enabled by
190 * CM_CLKEN_PLL
191 */
192static void omap2_clk_wait_ready(struct clk *clk)
193{
194 void __iomem *reg, *other_reg, *st_reg;
195 u32 bit;
196
197 /*
198 * REVISIT: This code is pretty ugly. It would be nice to generalize
199 * it and pull it into struct clk itself somehow.
200 */
201 reg = clk->enable_reg;
202 if ((((u32)reg & 0xff) >= CM_FCLKEN1) &&
203 (((u32)reg & 0xff) <= OMAP24XX_CM_FCLKEN2))
204 other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x10); /* CM_ICLKEN* */
205 else if ((((u32)reg & 0xff) >= CM_ICLKEN1) &&
206 (((u32)reg & 0xff) <= OMAP24XX_CM_ICLKEN4))
207 other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x00); /* CM_FCLKEN* */
208 else
209 return;
210
211 /* REVISIT: What are the appropriate exclusions for 34XX? */
212 /* No check for DSS or cam clocks */
213 if (cpu_is_omap24xx() && ((u32)reg & 0x0f) == 0) { /* CM_{F,I}CLKEN1 */
214 if (clk->enable_bit == OMAP24XX_EN_DSS2_SHIFT ||
215 clk->enable_bit == OMAP24XX_EN_DSS1_SHIFT ||
216 clk->enable_bit == OMAP24XX_EN_CAM_SHIFT)
217 return;
218 }
219
220 /* REVISIT: What are the appropriate exclusions for 34XX? */
221 /* OMAP3: ignore DSS-mod clocks */
222 if (cpu_is_omap34xx() &&
223 (((u32)reg & ~0xff) == (u32)OMAP_CM_REGADDR(OMAP3430_DSS_MOD, 0)))
224 return;
225
226 /* Check if both functional and interface clocks
227 * are running. */
228 bit = 1 << clk->enable_bit;
229 if (!(__raw_readl(other_reg) & bit))
230 return;
231 st_reg = (void __iomem *)(((u32)other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */
232
233 omap2_wait_clock_ready(st_reg, bit, clk->name);
234}
235
236/* Enables clock without considering parent dependencies or use count
237 * REVISIT: Maybe change this to use clk->enable like on omap1?
238 */
239int _omap2_clk_enable(struct clk *clk)
240{
241 u32 regval32;
242
243 if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK))
244 return 0;
245
246 if (clk->enable)
247 return clk->enable(clk);
248
249 if (unlikely(clk->enable_reg == 0)) {
250 printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
251 clk->name);
252 return 0; /* REVISIT: -EINVAL */
253 }
254
255 regval32 = __raw_readl(clk->enable_reg);
256 if (clk->flags & INVERT_ENABLE)
257 regval32 &= ~(1 << clk->enable_bit);
258 else
259 regval32 |= (1 << clk->enable_bit);
260 __raw_writel(regval32, clk->enable_reg);
261 wmb();
262
263 omap2_clk_wait_ready(clk);
264
265 return 0;
266}
267
268/* Disables clock without considering parent dependencies or use count */
269void _omap2_clk_disable(struct clk *clk)
270{
271 u32 regval32;
272
273 if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK))
274 return;
275
276 if (clk->disable) {
277 clk->disable(clk);
278 return;
279 }
280
281 if (clk->enable_reg == 0) {
282 /*
283 * 'Independent' here refers to a clock which is not
284 * controlled by its parent.
285 */
286 printk(KERN_ERR "clock: clk_disable called on independent "
287 "clock %s which has no enable_reg\n", clk->name);
288 return;
289 }
290
291 regval32 = __raw_readl(clk->enable_reg);
292 if (clk->flags & INVERT_ENABLE)
293 regval32 |= (1 << clk->enable_bit);
294 else
295 regval32 &= ~(1 << clk->enable_bit);
296 __raw_writel(regval32, clk->enable_reg);
297 wmb();
298}
299
300void omap2_clk_disable(struct clk *clk)
301{
302 if (clk->usecount > 0 && !(--clk->usecount)) {
303 _omap2_clk_disable(clk);
304 if (likely((u32)clk->parent))
305 omap2_clk_disable(clk->parent);
306 }
307}
308
309int omap2_clk_enable(struct clk *clk)
310{
311 int ret = 0;
312
313 if (clk->usecount++ == 0) {
314 if (likely((u32)clk->parent))
315 ret = omap2_clk_enable(clk->parent);
316
317 if (unlikely(ret != 0)) {
318 clk->usecount--;
319 return ret;
320 }
321
322 ret = _omap2_clk_enable(clk);
323
324 if (unlikely(ret != 0) && clk->parent) {
325 omap2_clk_disable(clk->parent);
326 clk->usecount--;
327 }
328 }
329
330 return ret;
331}
332
333/*
334 * Used for clocks that are part of CLKSEL_xyz governed clocks.
335 * REVISIT: Maybe change to use clk->enable() functions like on omap1?
336 */
337void omap2_clksel_recalc(struct clk *clk)
338{
339 u32 div = 0;
340
341 pr_debug("clock: recalc'ing clksel clk %s\n", clk->name);
342
343 div = omap2_clksel_get_divisor(clk);
344 if (div == 0)
345 return;
346
347 if (unlikely(clk->rate == clk->parent->rate / div))
348 return;
349 clk->rate = clk->parent->rate / div;
350
351 pr_debug("clock: new clock rate is %ld (div %d)\n", clk->rate, div);
352
353 if (unlikely(clk->flags & RATE_PROPAGATES))
354 propagate_rate(clk);
355}
356
357/**
358 * omap2_get_clksel_by_parent - return clksel struct for a given clk & parent
359 * @clk: OMAP struct clk ptr to inspect
360 * @src_clk: OMAP struct clk ptr of the parent clk to search for
361 *
362 * Scan the struct clksel array associated with the clock to find
363 * the element associated with the supplied parent clock address.
364 * Returns a pointer to the struct clksel on success or NULL on error.
365 */
366const struct clksel *omap2_get_clksel_by_parent(struct clk *clk,
367 struct clk *src_clk)
368{
369 const struct clksel *clks;
370
371 if (!clk->clksel)
372 return NULL;
373
374 for (clks = clk->clksel; clks->parent; clks++) {
375 if (clks->parent == src_clk)
376 break; /* Found the requested parent */
377 }
378
379 if (!clks->parent) {
380 printk(KERN_ERR "clock: Could not find parent clock %s in "
381 "clksel array of clock %s\n", src_clk->name,
382 clk->name);
383 return NULL;
384 }
385
386 return clks;
387}
388
389/**
390 * omap2_clksel_round_rate_div - find divisor for the given clock and rate
391 * @clk: OMAP struct clk to use
392 * @target_rate: desired clock rate
393 * @new_div: ptr to where we should store the divisor
394 *
395 * Finds 'best' divider value in an array based on the source and target
396 * rates. The divider array must be sorted with smallest divider first.
397 * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
398 * they are only settable as part of virtual_prcm set.
399 *
400 * Returns the rounded clock rate or returns 0xffffffff on error.
401 */
402u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
403 u32 *new_div)
404{
405 unsigned long test_rate;
406 const struct clksel *clks;
407 const struct clksel_rate *clkr;
408 u32 last_div = 0;
409
410 printk(KERN_INFO "clock: clksel_round_rate_div: %s target_rate %ld\n",
411 clk->name, target_rate);
412
413 *new_div = 1;
414
415 clks = omap2_get_clksel_by_parent(clk, clk->parent);
416 if (clks == NULL)
417 return ~0;
418
419 for (clkr = clks->rates; clkr->div; clkr++) {
420 if (!(clkr->flags & cpu_mask))
421 continue;
422
423 /* Sanity check */
424 if (clkr->div <= last_div)
425 printk(KERN_ERR "clock: clksel_rate table not sorted "
426 "for clock %s", clk->name);
427
428 last_div = clkr->div;
429
430 test_rate = clk->parent->rate / clkr->div;
431
432 if (test_rate <= target_rate)
433 break; /* found it */
434 }
435
436 if (!clkr->div) {
437 printk(KERN_ERR "clock: Could not find divisor for target "
438 "rate %ld for clock %s parent %s\n", target_rate,
439 clk->name, clk->parent->name);
440 return ~0;
441 }
442
443 *new_div = clkr->div;
444
445 printk(KERN_INFO "clock: new_div = %d, new_rate = %ld\n", *new_div,
446 (clk->parent->rate / clkr->div));
447
448 return (clk->parent->rate / clkr->div);
449}
450
451/**
452 * omap2_clksel_round_rate - find rounded rate for the given clock and rate
453 * @clk: OMAP struct clk to use
454 * @target_rate: desired clock rate
455 *
456 * Compatibility wrapper for OMAP clock framework
457 * Finds best target rate based on the source clock and possible dividers.
458 * rates. The divider array must be sorted with smallest divider first.
459 * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
460 * they are only settable as part of virtual_prcm set.
461 *
462 * Returns the rounded clock rate or returns 0xffffffff on error.
463 */
464long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)
465{
466 u32 new_div;
467
468 return omap2_clksel_round_rate_div(clk, target_rate, &new_div);
469}
470
471
472/* Given a clock and a rate apply a clock specific rounding function */
473long omap2_clk_round_rate(struct clk *clk, unsigned long rate)
474{
475 if (clk->round_rate != 0)
476 return clk->round_rate(clk, rate);
477
478 if (clk->flags & RATE_FIXED)
479 printk(KERN_ERR "clock: generic omap2_clk_round_rate called "
480 "on fixed-rate clock %s\n", clk->name);
481
482 return clk->rate;
483}
484
485/**
486 * omap2_clksel_to_divisor() - turn clksel field value into integer divider
487 * @clk: OMAP struct clk to use
488 * @field_val: register field value to find
489 *
490 * Given a struct clk of a rate-selectable clksel clock, and a register field
491 * value to search for, find the corresponding clock divisor. The register
492 * field value should be pre-masked and shifted down so the LSB is at bit 0
493 * before calling. Returns 0 on error
494 */
495u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val)
496{
497 const struct clksel *clks;
498 const struct clksel_rate *clkr;
499
500 clks = omap2_get_clksel_by_parent(clk, clk->parent);
501 if (clks == NULL)
502 return 0;
503
504 for (clkr = clks->rates; clkr->div; clkr++) {
505 if ((clkr->flags & cpu_mask) && (clkr->val == field_val))
506 break;
507 }
508
509 if (!clkr->div) {
510 printk(KERN_ERR "clock: Could not find fieldval %d for "
511 "clock %s parent %s\n", field_val, clk->name,
512 clk->parent->name);
513 return 0;
514 }
515
516 return clkr->div;
517}
518
519/**
520 * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value
521 * @clk: OMAP struct clk to use
522 * @div: integer divisor to search for
523 *
524 * Given a struct clk of a rate-selectable clksel clock, and a clock divisor,
525 * find the corresponding register field value. The return register value is
526 * the value before left-shifting. Returns 0xffffffff on error
527 */
528u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)
529{
530 const struct clksel *clks;
531 const struct clksel_rate *clkr;
532
533 /* should never happen */
534 WARN_ON(div == 0);
535
536 clks = omap2_get_clksel_by_parent(clk, clk->parent);
537 if (clks == NULL)
538 return 0;
539
540 for (clkr = clks->rates; clkr->div; clkr++) {
541 if ((clkr->flags & cpu_mask) && (clkr->div == div))
542 break;
543 }
544
545 if (!clkr->div) {
546 printk(KERN_ERR "clock: Could not find divisor %d for "
547 "clock %s parent %s\n", div, clk->name,
548 clk->parent->name);
549 return 0;
550 }
551
552 return clkr->val;
553}
554
555/**
556 * omap2_get_clksel - find clksel register addr & field mask for a clk
557 * @clk: struct clk to use
558 * @field_mask: ptr to u32 to store the register field mask
559 *
560 * Returns the address of the clksel register upon success or NULL on error.
561 */
562void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)
563{
564 if (unlikely((clk->clksel_reg == 0) || (clk->clksel_mask == 0)))
565 return NULL;
566
567 *field_mask = clk->clksel_mask;
568
569 return clk->clksel_reg;
570}
571
572/**
573 * omap2_clksel_get_divisor - get current divider applied to parent clock.
574 * @clk: OMAP struct clk to use.
575 *
576 * Returns the integer divisor upon success or 0 on error.
577 */
578u32 omap2_clksel_get_divisor(struct clk *clk)
579{
580 u32 field_mask, field_val;
581 void __iomem *div_addr;
582
583 div_addr = omap2_get_clksel(clk, &field_mask);
584 if (div_addr == 0)
585 return 0;
586
587 field_val = __raw_readl(div_addr) & field_mask;
588 field_val >>= __ffs(field_mask);
589
590 return omap2_clksel_to_divisor(clk, field_val);
591}
592
593int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
594{
595 u32 field_mask, field_val, reg_val, validrate, new_div = 0;
596 void __iomem *div_addr;
597
598 validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
599 if (validrate != rate)
600 return -EINVAL;
601
602 div_addr = omap2_get_clksel(clk, &field_mask);
603 if (div_addr == 0)
604 return -EINVAL;
605
606 field_val = omap2_divisor_to_clksel(clk, new_div);
607 if (field_val == ~0)
608 return -EINVAL;
609
610 reg_val = __raw_readl(div_addr);
611 reg_val &= ~field_mask;
612 reg_val |= (field_val << __ffs(field_mask));
613 __raw_writel(reg_val, div_addr);
614 wmb();
615
616 clk->rate = clk->parent->rate / new_div;
617
618 if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) {
619 __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL);
620 wmb();
621 }
622
623 return 0;
624}
625
626
627/* Set the clock rate for a clock source */
628int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
629{
630 int ret = -EINVAL;
631
632 pr_debug("clock: set_rate for clock %s to rate %ld\n", clk->name, rate);
633
634 /* CONFIG_PARTICIPANT clocks are changed only in sets via the
635 rate table mechanism, driven by mpu_speed */
636 if (clk->flags & CONFIG_PARTICIPANT)
637 return -EINVAL;
638
639 /* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */
640 if (clk->set_rate != 0)
641 ret = clk->set_rate(clk, rate);
642
643 if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES)))
644 propagate_rate(clk);
645
646 return ret;
647}
648
649/*
650 * Converts encoded control register address into a full address
651 * On error, *src_addr will be returned as 0.
652 */
653static u32 omap2_clksel_get_src_field(void __iomem **src_addr,
654 struct clk *src_clk, u32 *field_mask,
655 struct clk *clk, u32 *parent_div)
656{
657 const struct clksel *clks;
658 const struct clksel_rate *clkr;
659
660 *parent_div = 0;
661 *src_addr = 0;
662
663 clks = omap2_get_clksel_by_parent(clk, src_clk);
664 if (clks == NULL)
665 return 0;
666
667 for (clkr = clks->rates; clkr->div; clkr++) {
668 if (clkr->flags & (cpu_mask | DEFAULT_RATE))
669 break; /* Found the default rate for this platform */
670 }
671
672 if (!clkr->div) {
673 printk(KERN_ERR "clock: Could not find default rate for "
674 "clock %s parent %s\n", clk->name,
675 src_clk->parent->name);
676 return 0;
677 }
678
679 /* Should never happen. Add a clksel mask to the struct clk. */
680 WARN_ON(clk->clksel_mask == 0);
681
682 *field_mask = clk->clksel_mask;
683 *src_addr = clk->clksel_reg;
684 *parent_div = clkr->div;
685
686 return clkr->val;
687}
688
689int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
690{
691 void __iomem *src_addr;
692 u32 field_val, field_mask, reg_val, parent_div;
693
694 if (unlikely(clk->flags & CONFIG_PARTICIPANT))
695 return -EINVAL;
696
697 if (!clk->clksel)
698 return -EINVAL;
699
700 field_val = omap2_clksel_get_src_field(&src_addr, new_parent,
701 &field_mask, clk, &parent_div);
702 if (src_addr == 0)
703 return -EINVAL;
704
705 if (clk->usecount > 0)
706 _omap2_clk_disable(clk);
707
708 /* Set new source value (previous dividers if any in effect) */
709 reg_val = __raw_readl(src_addr) & ~field_mask;
710 reg_val |= (field_val << __ffs(field_mask));
711 __raw_writel(reg_val, src_addr);
712 wmb();
713
714 if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) {
715 __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL);
716 wmb();
717 }
718
719 if (clk->usecount > 0)
720 _omap2_clk_enable(clk);
721
722 clk->parent = new_parent;
723
724 /* CLKSEL clocks follow their parents' rates, divided by a divisor */
725 clk->rate = new_parent->rate;
726
727 if (parent_div > 0)
728 clk->rate /= parent_div;
729
730 pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
731 clk->name, clk->parent->name, clk->rate);
732
733 if (unlikely(clk->flags & RATE_PROPAGATES))
734 propagate_rate(clk);
735
736 return 0;
737}
738
739/*-------------------------------------------------------------------------
740 * Omap2 clock reset and init functions
741 *-------------------------------------------------------------------------*/
742
743#ifdef CONFIG_OMAP_RESET_CLOCKS
744void omap2_clk_disable_unused(struct clk *clk)
745{
746 u32 regval32, v;
747
748 v = (clk->flags & INVERT_ENABLE) ? (1 << clk->enable_bit) : 0;
749
750 regval32 = __raw_readl(clk->enable_reg);
751 if ((regval32 & (1 << clk->enable_bit)) == v)
752 return;
753
754 printk(KERN_INFO "Disabling unused clock \"%s\"\n", clk->name);
755 _omap2_clk_disable(clk);
756}
757#endif
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
new file mode 100644
index 000000000000..d9cc99c466da
--- /dev/null
+++ b/arch/arm/mach-omap2/clock.h
@@ -0,0 +1,73 @@
1/*
2 * linux/arch/arm/mach-omap2/clock.h
3 *
4 * Copyright (C) 2005 Texas Instruments Inc.
5 * Richard Woodruff <r-woodruff2@ti.com>
6 * Created for OMAP2.
7 *
8 * Copyright (C) 2004 Nokia corporation
9 * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
10 * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc
11 *
12 * Copyright (C) 2007 Texas Instruments, Inc.
13 * Copyright (C) 2007 Nokia Corporation
14 * Paul Walmsley
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License version 2 as
18 * published by the Free Software Foundation.
19 */
20
21#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H
22#define __ARCH_ARM_MACH_OMAP2_CLOCK_H
23
24#include <asm/arch/clock.h>
25
26int omap2_clk_enable(struct clk *clk);
27void omap2_clk_disable(struct clk *clk);
28long omap2_clk_round_rate(struct clk *clk, unsigned long rate);
29int omap2_clk_set_rate(struct clk *clk, unsigned long rate);
30int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent);
31
32#ifdef CONFIG_OMAP_RESET_CLOCKS
33void omap2_clk_disable_unused(struct clk *clk);
34#else
35#define omap2_clk_disable_unused NULL
36#endif
37
38void omap2_clksel_recalc(struct clk *clk);
39void omap2_init_clksel_parent(struct clk *clk);
40u32 omap2_clksel_get_divisor(struct clk *clk);
41u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
42 u32 *new_div);
43u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val);
44u32 omap2_divisor_to_clksel(struct clk *clk, u32 div);
45void omap2_fixed_divisor_recalc(struct clk *clk);
46long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate);
47int omap2_clksel_set_rate(struct clk *clk, unsigned long rate);
48u32 omap2_get_dpll_rate(struct clk *clk);
49int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name);
50
51extern u8 cpu_mask;
52
53/* clksel_rate data common to 24xx/343x */
54static const struct clksel_rate gpt_32k_rates[] = {
55 { .div = 1, .val = 0, .flags = RATE_IN_24XX | RATE_IN_343X | DEFAULT_RATE },
56 { .div = 0 }
57};
58
59static const struct clksel_rate gpt_sys_rates[] = {
60 { .div = 1, .val = 1, .flags = RATE_IN_24XX | RATE_IN_343X | DEFAULT_RATE },
61 { .div = 0 }
62};
63
64static const struct clksel_rate gfx_l3_rates[] = {
65 { .div = 1, .val = 1, .flags = RATE_IN_24XX | RATE_IN_343X },
66 { .div = 2, .val = 2, .flags = RATE_IN_24XX | RATE_IN_343X | DEFAULT_RATE },
67 { .div = 3, .val = 3, .flags = RATE_IN_243X | RATE_IN_343X },
68 { .div = 4, .val = 4, .flags = RATE_IN_243X | RATE_IN_343X },
69 { .div = 0 }
70};
71
72
73#endif