blob: 302d5a796340c2ab9acf4fa32b19aee3bbc4d8a1 [file] [log] [blame]
Tony Lindgren046d6b22005-11-10 14:26:52 +00001/*
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 * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation
12 * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
17 */
Tony Lindgren046d6b22005-11-10 14:26:52 +000018#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/device.h>
21#include <linux/list.h>
22#include <linux/errno.h>
23#include <linux/delay.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000024#include <linux/clk.h>
Tony Lindgren046d6b22005-11-10 14:26:52 +000025
26#include <asm/io.h>
27
Tony Lindgren046d6b22005-11-10 14:26:52 +000028#include <asm/arch/clock.h>
29#include <asm/arch/sram.h>
Tony Lindgren046d6b22005-11-10 14:26:52 +000030
Tony Lindgrenb824efa2006-04-02 17:46:20 +010031#include "prcm-regs.h"
32#include "memory.h"
Tony Lindgren046d6b22005-11-10 14:26:52 +000033#include "clock.h"
34
35//#define DOWN_VARIABLE_DPLL 1 /* Experimental */
36
37static struct prcm_config *curr_prcm_set;
Tony Lindgren046d6b22005-11-10 14:26:52 +000038static u32 curr_perf_level = PRCM_FULL_SPEED;
Tony Lindgrenae78dcf2006-09-25 12:41:20 +030039static struct clk *vclk;
40static struct clk *sclk;
Tony Lindgren046d6b22005-11-10 14:26:52 +000041
42/*-------------------------------------------------------------------------
43 * Omap2 specific clock functions
44 *-------------------------------------------------------------------------*/
45
46/* Recalculate SYST_CLK */
47static void omap2_sys_clk_recalc(struct clk * clk)
48{
49 u32 div = PRCM_CLKSRC_CTRL;
50 div &= (1 << 7) | (1 << 6); /* Test if ext clk divided by 1 or 2 */
51 div >>= clk->rate_offset;
52 clk->rate = (clk->parent->rate / div);
53 propagate_rate(clk);
54}
55
56static u32 omap2_get_dpll_rate(struct clk * tclk)
57{
Tony Lindgrenb824efa2006-04-02 17:46:20 +010058 long long dpll_clk;
59 int dpll_mult, dpll_div, amult;
Tony Lindgren046d6b22005-11-10 14:26:52 +000060
61 dpll_mult = (CM_CLKSEL1_PLL >> 12) & 0x03ff; /* 10 bits */
62 dpll_div = (CM_CLKSEL1_PLL >> 8) & 0x0f; /* 4 bits */
Tony Lindgrenb824efa2006-04-02 17:46:20 +010063 dpll_clk = (long long)tclk->parent->rate * dpll_mult;
64 do_div(dpll_clk, dpll_div + 1);
Tony Lindgren046d6b22005-11-10 14:26:52 +000065 amult = CM_CLKSEL2_PLL & 0x3;
66 dpll_clk *= amult;
67
68 return dpll_clk;
69}
70
71static void omap2_followparent_recalc(struct clk *clk)
72{
73 followparent_recalc(clk);
74}
75
76static void omap2_propagate_rate(struct clk * clk)
77{
78 if (!(clk->flags & RATE_FIXED))
79 clk->rate = clk->parent->rate;
80
81 propagate_rate(clk);
82}
83
84/* Enable an APLL if off */
85static void omap2_clk_fixed_enable(struct clk *clk)
86{
87 u32 cval, i=0;
88
89 if (clk->enable_bit == 0xff) /* Parent will do it */
90 return;
91
92 cval = CM_CLKEN_PLL;
93
94 if ((cval & (0x3 << clk->enable_bit)) == (0x3 << clk->enable_bit))
95 return;
96
97 cval &= ~(0x3 << clk->enable_bit);
98 cval |= (0x3 << clk->enable_bit);
99 CM_CLKEN_PLL = cval;
100
101 if (clk == &apll96_ck)
102 cval = (1 << 8);
103 else if (clk == &apll54_ck)
104 cval = (1 << 6);
105
Samuel Ortizdbab2882006-09-25 12:41:36 +0300106 while (!(CM_IDLEST_CKGEN & cval)) { /* Wait for lock */
Tony Lindgren046d6b22005-11-10 14:26:52 +0000107 ++i;
108 udelay(1);
109 if (i == 100000)
110 break;
111 }
112}
113
114/* Enables clock without considering parent dependencies or use count
115 * REVISIT: Maybe change this to use clk->enable like on omap1?
116 */
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800117static int _omap2_clk_enable(struct clk * clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000118{
119 u32 regval32;
120
121 if (clk->flags & ALWAYS_ENABLED)
122 return 0;
123
124 if (unlikely(clk->enable_reg == 0)) {
125 printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
126 clk->name);
127 return 0;
128 }
129
130 if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) {
131 omap2_clk_fixed_enable(clk);
132 return 0;
133 }
134
135 regval32 = __raw_readl(clk->enable_reg);
136 regval32 |= (1 << clk->enable_bit);
137 __raw_writel(regval32, clk->enable_reg);
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300138 wmb();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000139
140 return 0;
141}
142
143/* Stop APLL */
144static void omap2_clk_fixed_disable(struct clk *clk)
145{
146 u32 cval;
147
148 if(clk->enable_bit == 0xff) /* let parent off do it */
149 return;
150
151 cval = CM_CLKEN_PLL;
152 cval &= ~(0x3 << clk->enable_bit);
153 CM_CLKEN_PLL = cval;
154}
155
156/* Disables clock without considering parent dependencies or use count */
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800157static void _omap2_clk_disable(struct clk *clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000158{
159 u32 regval32;
160
161 if (clk->enable_reg == 0)
162 return;
163
164 if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) {
165 omap2_clk_fixed_disable(clk);
166 return;
167 }
168
169 regval32 = __raw_readl(clk->enable_reg);
170 regval32 &= ~(1 << clk->enable_bit);
171 __raw_writel(regval32, clk->enable_reg);
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300172 wmb();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000173}
174
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800175static int omap2_clk_enable(struct clk *clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000176{
177 int ret = 0;
178
179 if (clk->usecount++ == 0) {
180 if (likely((u32)clk->parent))
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800181 ret = omap2_clk_enable(clk->parent);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000182
183 if (unlikely(ret != 0)) {
184 clk->usecount--;
185 return ret;
186 }
187
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800188 ret = _omap2_clk_enable(clk);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000189
190 if (unlikely(ret != 0) && clk->parent) {
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800191 omap2_clk_disable(clk->parent);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000192 clk->usecount--;
193 }
194 }
195
196 return ret;
197}
198
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800199static void omap2_clk_disable(struct clk *clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000200{
201 if (clk->usecount > 0 && !(--clk->usecount)) {
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800202 _omap2_clk_disable(clk);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000203 if (likely((u32)clk->parent))
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800204 omap2_clk_disable(clk->parent);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000205 }
206}
207
208/*
209 * Uses the current prcm set to tell if a rate is valid.
210 * You can go slower, but not faster within a given rate set.
211 */
212static u32 omap2_dpll_round_rate(unsigned long target_rate)
213{
214 u32 high, low;
215
216 if ((CM_CLKSEL2_PLL & 0x3) == 1) { /* DPLL clockout */
217 high = curr_prcm_set->dpll_speed * 2;
218 low = curr_prcm_set->dpll_speed;
219 } else { /* DPLL clockout x 2 */
220 high = curr_prcm_set->dpll_speed;
221 low = curr_prcm_set->dpll_speed / 2;
222 }
223
224#ifdef DOWN_VARIABLE_DPLL
225 if (target_rate > high)
226 return high;
227 else
228 return target_rate;
229#else
230 if (target_rate > low)
231 return high;
232 else
233 return low;
234#endif
235
236}
237
238/*
239 * Used for clocks that are part of CLKSEL_xyz governed clocks.
240 * REVISIT: Maybe change to use clk->enable() functions like on omap1?
241 */
242static void omap2_clksel_recalc(struct clk * clk)
243{
244 u32 fixed = 0, div = 0;
245
246 if (clk == &dpll_ck) {
247 clk->rate = omap2_get_dpll_rate(clk);
248 fixed = 1;
249 div = 0;
250 }
251
252 if (clk == &iva1_mpu_int_ifck) {
253 div = 2;
254 fixed = 1;
255 }
256
257 if ((clk == &dss1_fck) && ((CM_CLKSEL1_CORE & (0x1f << 8)) == 0)) {
258 clk->rate = sys_ck.rate;
259 return;
260 }
261
262 if (!fixed) {
263 div = omap2_clksel_get_divisor(clk);
264 if (div == 0)
265 return;
266 }
267
268 if (div != 0) {
269 if (unlikely(clk->rate == clk->parent->rate / div))
270 return;
271 clk->rate = clk->parent->rate / div;
272 }
273
274 if (unlikely(clk->flags & RATE_PROPAGATES))
275 propagate_rate(clk);
276}
277
278/*
279 * Finds best divider value in an array based on the source and target
280 * rates. The divider array must be sorted with smallest divider first.
281 */
282static inline u32 omap2_divider_from_table(u32 size, u32 *div_array,
283 u32 src_rate, u32 tgt_rate)
284{
285 int i, test_rate;
286
287 if (div_array == NULL)
288 return ~1;
289
290 for (i=0; i < size; i++) {
291 test_rate = src_rate / *div_array;
292 if (test_rate <= tgt_rate)
293 return *div_array;
294 ++div_array;
295 }
296
297 return ~0; /* No acceptable divider */
298}
299
300/*
301 * Find divisor for the given clock and target rate.
302 *
303 * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
304 * they are only settable as part of virtual_prcm set.
305 */
306static u32 omap2_clksel_round_rate(struct clk *tclk, u32 target_rate,
307 u32 *new_div)
308{
309 u32 gfx_div[] = {2, 3, 4};
310 u32 sysclkout_div[] = {1, 2, 4, 8, 16};
311 u32 dss1_div[] = {1, 2, 3, 4, 5, 6, 8, 9, 12, 16};
312 u32 vylnq_div[] = {1, 2, 3, 4, 6, 8, 9, 12, 16, 18};
313 u32 best_div = ~0, asize = 0;
314 u32 *div_array = NULL;
315
316 switch (tclk->flags & SRC_RATE_SEL_MASK) {
317 case CM_GFX_SEL1:
318 asize = 3;
319 div_array = gfx_div;
320 break;
321 case CM_PLL_SEL1:
322 return omap2_dpll_round_rate(target_rate);
323 case CM_SYSCLKOUT_SEL1:
324 asize = 5;
325 div_array = sysclkout_div;
326 break;
327 case CM_CORE_SEL1:
328 if(tclk == &dss1_fck){
329 if(tclk->parent == &core_ck){
330 asize = 10;
331 div_array = dss1_div;
332 } else {
333 *new_div = 0; /* fixed clk */
334 return(tclk->parent->rate);
335 }
336 } else if((tclk == &vlynq_fck) && cpu_is_omap2420()){
337 if(tclk->parent == &core_ck){
338 asize = 10;
339 div_array = vylnq_div;
340 } else {
341 *new_div = 0; /* fixed clk */
342 return(tclk->parent->rate);
343 }
344 }
345 break;
346 }
347
348 best_div = omap2_divider_from_table(asize, div_array,
349 tclk->parent->rate, target_rate);
350 if (best_div == ~0){
351 *new_div = 1;
352 return best_div; /* signal error */
353 }
354
355 *new_div = best_div;
356 return (tclk->parent->rate / best_div);
357}
358
359/* Given a clock and a rate apply a clock specific rounding function */
360static long omap2_clk_round_rate(struct clk *clk, unsigned long rate)
361{
362 u32 new_div = 0;
363 int valid_rate;
364
365 if (clk->flags & RATE_FIXED)
366 return clk->rate;
367
368 if (clk->flags & RATE_CKCTL) {
369 valid_rate = omap2_clksel_round_rate(clk, rate, &new_div);
370 return valid_rate;
371 }
372
373 if (clk->round_rate != 0)
374 return clk->round_rate(clk, rate);
375
376 return clk->rate;
377}
378
379/*
380 * Check the DLL lock state, and return tue if running in unlock mode.
381 * This is needed to compenste for the shifted DLL value in unlock mode.
382 */
383static u32 omap2_dll_force_needed(void)
384{
385 u32 dll_state = SDRC_DLLA_CTRL; /* dlla and dllb are a set */
386
387 if ((dll_state & (1 << 2)) == (1 << 2))
388 return 1;
389 else
390 return 0;
391}
392
Tony Lindgren046d6b22005-11-10 14:26:52 +0000393static u32 omap2_reprogram_sdrc(u32 level, u32 force)
394{
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100395 u32 slow_dll_ctrl, fast_dll_ctrl, m_type;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000396 u32 prev = curr_perf_level, flags;
397
398 if ((curr_perf_level == level) && !force)
399 return prev;
400
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100401 m_type = omap2_memory_get_type();
402 slow_dll_ctrl = omap2_memory_get_slow_dll_ctrl();
403 fast_dll_ctrl = omap2_memory_get_fast_dll_ctrl();
404
Tony Lindgren046d6b22005-11-10 14:26:52 +0000405 if (level == PRCM_HALF_SPEED) {
406 local_irq_save(flags);
407 PRCM_VOLTSETUP = 0xffff;
408 omap2_sram_reprogram_sdrc(PRCM_HALF_SPEED,
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100409 slow_dll_ctrl, m_type);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000410 curr_perf_level = PRCM_HALF_SPEED;
411 local_irq_restore(flags);
412 }
413 if (level == PRCM_FULL_SPEED) {
414 local_irq_save(flags);
415 PRCM_VOLTSETUP = 0xffff;
416 omap2_sram_reprogram_sdrc(PRCM_FULL_SPEED,
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100417 fast_dll_ctrl, m_type);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000418 curr_perf_level = PRCM_FULL_SPEED;
419 local_irq_restore(flags);
420 }
421
422 return prev;
423}
424
425static int omap2_reprogram_dpll(struct clk * clk, unsigned long rate)
426{
427 u32 flags, cur_rate, low, mult, div, valid_rate, done_rate;
428 u32 bypass = 0;
429 struct prcm_config tmpset;
430 int ret = -EINVAL;
431
432 local_irq_save(flags);
433 cur_rate = omap2_get_dpll_rate(&dpll_ck);
434 mult = CM_CLKSEL2_PLL & 0x3;
435
436 if ((rate == (cur_rate / 2)) && (mult == 2)) {
437 omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1);
438 } else if ((rate == (cur_rate * 2)) && (mult == 1)) {
439 omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1);
440 } else if (rate != cur_rate) {
441 valid_rate = omap2_dpll_round_rate(rate);
442 if (valid_rate != rate)
443 goto dpll_exit;
444
445 if ((CM_CLKSEL2_PLL & 0x3) == 1)
446 low = curr_prcm_set->dpll_speed;
447 else
448 low = curr_prcm_set->dpll_speed / 2;
449
450 tmpset.cm_clksel1_pll = CM_CLKSEL1_PLL;
451 tmpset.cm_clksel1_pll &= ~(0x3FFF << 8);
452 div = ((curr_prcm_set->xtal_speed / 1000000) - 1);
453 tmpset.cm_clksel2_pll = CM_CLKSEL2_PLL;
454 tmpset.cm_clksel2_pll &= ~0x3;
455 if (rate > low) {
456 tmpset.cm_clksel2_pll |= 0x2;
457 mult = ((rate / 2) / 1000000);
458 done_rate = PRCM_FULL_SPEED;
459 } else {
460 tmpset.cm_clksel2_pll |= 0x1;
461 mult = (rate / 1000000);
462 done_rate = PRCM_HALF_SPEED;
463 }
464 tmpset.cm_clksel1_pll |= ((div << 8) | (mult << 12));
465
466 /* Worst case */
467 tmpset.base_sdrc_rfr = V24XX_SDRC_RFR_CTRL_BYPASS;
468
469 if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */
470 bypass = 1;
471
472 omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); /* For init_mem */
473
474 /* Force dll lock mode */
475 omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr,
476 bypass);
477
478 /* Errata: ret dll entry state */
479 omap2_init_memory_params(omap2_dll_force_needed());
480 omap2_reprogram_sdrc(done_rate, 0);
481 }
482 omap2_clksel_recalc(&dpll_ck);
483 ret = 0;
484
485dpll_exit:
486 local_irq_restore(flags);
487 return(ret);
488}
489
490/* Just return the MPU speed */
491static void omap2_mpu_recalc(struct clk * clk)
492{
493 clk->rate = curr_prcm_set->mpu_speed;
494}
495
496/*
497 * Look for a rate equal or less than the target rate given a configuration set.
498 *
499 * What's not entirely clear is "which" field represents the key field.
500 * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
501 * just uses the ARM rates.
502 */
503static long omap2_round_to_table_rate(struct clk * clk, unsigned long rate)
504{
505 struct prcm_config * ptr;
506 long highest_rate;
507
508 if (clk != &virt_prcm_set)
509 return -EINVAL;
510
511 highest_rate = -EINVAL;
512
513 for (ptr = rate_table; ptr->mpu_speed; ptr++) {
514 if (ptr->xtal_speed != sys_ck.rate)
515 continue;
516
517 highest_rate = ptr->mpu_speed;
518
519 /* Can check only after xtal frequency check */
520 if (ptr->mpu_speed <= rate)
521 break;
522 }
523 return highest_rate;
524}
525
526/*
527 * omap2_convert_field_to_div() - turn field value into integer divider
528 */
529static u32 omap2_clksel_to_divisor(u32 div_sel, u32 field_val)
530{
531 u32 i;
532 u32 clkout_array[] = {1, 2, 4, 8, 16};
533
534 if ((div_sel & SRC_RATE_SEL_MASK) == CM_SYSCLKOUT_SEL1) {
535 for (i = 0; i < 5; i++) {
536 if (field_val == i)
537 return clkout_array[i];
538 }
539 return ~0;
540 } else
541 return field_val;
542}
543
544/*
545 * Returns the CLKSEL divider register value
546 * REVISIT: This should be cleaned up to work nicely with void __iomem *
547 */
548static u32 omap2_get_clksel(u32 *div_sel, u32 *field_mask,
549 struct clk *clk)
550{
551 int ret = ~0;
552 u32 reg_val, div_off;
553 u32 div_addr = 0;
554 u32 mask = ~0;
555
556 div_off = clk->rate_offset;
557
558 switch ((*div_sel & SRC_RATE_SEL_MASK)) {
559 case CM_MPU_SEL1:
560 div_addr = (u32)&CM_CLKSEL_MPU;
561 mask = 0x1f;
562 break;
563 case CM_DSP_SEL1:
564 div_addr = (u32)&CM_CLKSEL_DSP;
565 if (cpu_is_omap2420()) {
566 if ((div_off == 0) || (div_off == 8))
567 mask = 0x1f;
568 else if (div_off == 5)
569 mask = 0x3;
570 } else if (cpu_is_omap2430()) {
571 if (div_off == 0)
572 mask = 0x1f;
573 else if (div_off == 5)
574 mask = 0x3;
575 }
576 break;
577 case CM_GFX_SEL1:
578 div_addr = (u32)&CM_CLKSEL_GFX;
579 if (div_off == 0)
580 mask = 0x7;
581 break;
582 case CM_MODEM_SEL1:
583 div_addr = (u32)&CM_CLKSEL_MDM;
584 if (div_off == 0)
585 mask = 0xf;
586 break;
587 case CM_SYSCLKOUT_SEL1:
588 div_addr = (u32)&PRCM_CLKOUT_CTRL;
589 if ((div_off == 3) || (div_off = 11))
590 mask= 0x3;
591 break;
592 case CM_CORE_SEL1:
593 div_addr = (u32)&CM_CLKSEL1_CORE;
594 switch (div_off) {
595 case 0: /* l3 */
596 case 8: /* dss1 */
597 case 15: /* vylnc-2420 */
598 case 20: /* ssi */
599 mask = 0x1f; break;
600 case 5: /* l4 */
601 mask = 0x3; break;
602 case 13: /* dss2 */
603 mask = 0x1; break;
604 case 25: /* usb */
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100605 mask = 0x7; break;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000606 }
607 }
608
609 *field_mask = mask;
610
611 if (unlikely(mask == ~0))
612 div_addr = 0;
613
614 *div_sel = div_addr;
615
616 if (unlikely(div_addr == 0))
617 return ret;
618
619 /* Isolate field */
620 reg_val = __raw_readl((void __iomem *)div_addr) & (mask << div_off);
621
622 /* Normalize back to divider value */
623 reg_val >>= div_off;
624
625 return reg_val;
626}
627
628/*
629 * Return divider to be applied to parent clock.
630 * Return 0 on error.
631 */
632static u32 omap2_clksel_get_divisor(struct clk *clk)
633{
634 int ret = 0;
635 u32 div, div_sel, div_off, field_mask, field_val;
636
637 /* isolate control register */
638 div_sel = (SRC_RATE_SEL_MASK & clk->flags);
639
640 div_off = clk->rate_offset;
641 field_val = omap2_get_clksel(&div_sel, &field_mask, clk);
642 if (div_sel == 0)
643 return ret;
644
645 div_sel = (SRC_RATE_SEL_MASK & clk->flags);
646 div = omap2_clksel_to_divisor(div_sel, field_val);
647
648 return div;
649}
650
651/* Set the clock rate for a clock source */
652static int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
653
654{
655 int ret = -EINVAL;
656 void __iomem * reg;
657 u32 div_sel, div_off, field_mask, field_val, reg_val, validrate;
658 u32 new_div = 0;
659
660 if (!(clk->flags & CONFIG_PARTICIPANT) && (clk->flags & RATE_CKCTL)) {
661 if (clk == &dpll_ck)
662 return omap2_reprogram_dpll(clk, rate);
663
664 /* Isolate control register */
665 div_sel = (SRC_RATE_SEL_MASK & clk->flags);
Jarkko Nikula6e711ec2006-06-26 16:16:11 -0700666 div_off = clk->rate_offset;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000667
668 validrate = omap2_clksel_round_rate(clk, rate, &new_div);
Jarkko Nikula6e711ec2006-06-26 16:16:11 -0700669 if (validrate != rate)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000670 return(ret);
671
672 field_val = omap2_get_clksel(&div_sel, &field_mask, clk);
673 if (div_sel == 0)
674 return ret;
675
Jarkko Nikula6e711ec2006-06-26 16:16:11 -0700676 if (clk->flags & CM_SYSCLKOUT_SEL1) {
677 switch (new_div) {
678 case 16:
679 field_val = 4;
680 break;
681 case 8:
682 field_val = 3;
683 break;
684 case 4:
685 field_val = 2;
686 break;
687 case 2:
688 field_val = 1;
689 break;
690 case 1:
691 field_val = 0;
692 break;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000693 }
Jarkko Nikula6e711ec2006-06-26 16:16:11 -0700694 } else
Tony Lindgren046d6b22005-11-10 14:26:52 +0000695 field_val = new_div;
696
697 reg = (void __iomem *)div_sel;
698
699 reg_val = __raw_readl(reg);
700 reg_val &= ~(field_mask << div_off);
701 reg_val |= (field_val << div_off);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000702 __raw_writel(reg_val, reg);
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300703 wmb();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000704 clk->rate = clk->parent->rate / field_val;
705
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300706 if (clk->flags & DELAYED_APP) {
Tony Lindgren046d6b22005-11-10 14:26:52 +0000707 __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL);
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300708 wmb();
709 }
Tony Lindgren046d6b22005-11-10 14:26:52 +0000710 ret = 0;
711 } else if (clk->set_rate != 0)
712 ret = clk->set_rate(clk, rate);
713
714 if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES)))
715 propagate_rate(clk);
716
717 return ret;
718}
719
720/* Converts encoded control register address into a full address */
721static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset,
722 struct clk *src_clk, u32 *field_mask)
723{
724 u32 val = ~0, src_reg_addr = 0, mask = 0;
725
726 /* Find target control register.*/
727 switch ((*type_to_addr & SRC_RATE_SEL_MASK)) {
728 case CM_CORE_SEL1:
729 src_reg_addr = (u32)&CM_CLKSEL1_CORE;
730 if (reg_offset == 13) { /* DSS2_fclk */
731 mask = 0x1;
732 if (src_clk == &sys_ck)
733 val = 0;
734 if (src_clk == &func_48m_ck)
735 val = 1;
736 } else if (reg_offset == 8) { /* DSS1_fclk */
737 mask = 0x1f;
738 if (src_clk == &sys_ck)
739 val = 0;
740 else if (src_clk == &core_ck) /* divided clock */
741 val = 0x10; /* rate needs fixing */
742 } else if ((reg_offset == 15) && cpu_is_omap2420()){ /*vlnyq*/
743 mask = 0x1F;
744 if(src_clk == &func_96m_ck)
745 val = 0;
746 else if (src_clk == &core_ck)
747 val = 0x10;
748 }
749 break;
750 case CM_CORE_SEL2:
751 src_reg_addr = (u32)&CM_CLKSEL2_CORE;
752 mask = 0x3;
753 if (src_clk == &func_32k_ck)
754 val = 0x0;
755 if (src_clk == &sys_ck)
756 val = 0x1;
757 if (src_clk == &alt_ck)
758 val = 0x2;
759 break;
760 case CM_WKUP_SEL1:
Timo Terase32f7ec2006-06-26 16:16:13 -0700761 src_reg_addr = (u32)&CM_CLKSEL_WKUP;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000762 mask = 0x3;
763 if (src_clk == &func_32k_ck)
764 val = 0x0;
765 if (src_clk == &sys_ck)
766 val = 0x1;
767 if (src_clk == &alt_ck)
768 val = 0x2;
769 break;
770 case CM_PLL_SEL1:
771 src_reg_addr = (u32)&CM_CLKSEL1_PLL;
772 mask = 0x1;
773 if (reg_offset == 0x3) {
774 if (src_clk == &apll96_ck)
775 val = 0;
776 if (src_clk == &alt_ck)
777 val = 1;
778 }
779 else if (reg_offset == 0x5) {
780 if (src_clk == &apll54_ck)
781 val = 0;
782 if (src_clk == &alt_ck)
783 val = 1;
784 }
785 break;
786 case CM_PLL_SEL2:
787 src_reg_addr = (u32)&CM_CLKSEL2_PLL;
788 mask = 0x3;
789 if (src_clk == &func_32k_ck)
790 val = 0x0;
791 if (src_clk == &dpll_ck)
792 val = 0x2;
793 break;
794 case CM_SYSCLKOUT_SEL1:
795 src_reg_addr = (u32)&PRCM_CLKOUT_CTRL;
796 mask = 0x3;
797 if (src_clk == &dpll_ck)
798 val = 0;
799 if (src_clk == &sys_ck)
800 val = 1;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000801 if (src_clk == &func_96m_ck)
Jarkko Nikula6e711ec2006-06-26 16:16:11 -0700802 val = 2;
803 if (src_clk == &func_54m_ck)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000804 val = 3;
805 break;
806 }
807
808 if (val == ~0) /* Catch errors in offset */
809 *type_to_addr = 0;
810 else
811 *type_to_addr = src_reg_addr;
812 *field_mask = mask;
813
814 return val;
815}
816
817static int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
818{
819 void __iomem * reg;
820 u32 src_sel, src_off, field_val, field_mask, reg_val, rate;
821 int ret = -EINVAL;
822
823 if (unlikely(clk->flags & CONFIG_PARTICIPANT))
824 return ret;
825
826 if (clk->flags & SRC_SEL_MASK) { /* On-chip SEL collection */
827 src_sel = (SRC_RATE_SEL_MASK & clk->flags);
828 src_off = clk->src_offset;
829
830 if (src_sel == 0)
831 goto set_parent_error;
832
833 field_val = omap2_get_src_field(&src_sel, src_off, new_parent,
834 &field_mask);
835
836 reg = (void __iomem *)src_sel;
837
838 if (clk->usecount > 0)
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800839 _omap2_clk_disable(clk);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000840
841 /* Set new source value (previous dividers if any in effect) */
842 reg_val = __raw_readl(reg) & ~(field_mask << src_off);
843 reg_val |= (field_val << src_off);
844 __raw_writel(reg_val, reg);
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300845 wmb();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000846
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300847 if (clk->flags & DELAYED_APP) {
Tony Lindgren046d6b22005-11-10 14:26:52 +0000848 __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL);
Juha Yrjolaeaca33d2006-09-25 12:41:37 +0300849 wmb();
850 }
Tony Lindgren046d6b22005-11-10 14:26:52 +0000851 if (clk->usecount > 0)
Tony Lindgrenfde0fd42006-01-17 15:31:18 -0800852 _omap2_clk_enable(clk);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000853
854 clk->parent = new_parent;
855
856 /* SRC_RATE_SEL_MASK clocks follow their parents rates.*/
857 if ((new_parent == &core_ck) && (clk == &dss1_fck))
858 clk->rate = new_parent->rate / 0x10;
859 else
860 clk->rate = new_parent->rate;
861
862 if (unlikely(clk->flags & RATE_PROPAGATES))
863 propagate_rate(clk);
864
865 return 0;
866 } else {
867 clk->parent = new_parent;
868 rate = new_parent->rate;
869 omap2_clk_set_rate(clk, rate);
870 ret = 0;
871 }
872
873 set_parent_error:
874 return ret;
875}
876
877/* Sets basic clocks based on the specified rate */
878static int omap2_select_table_rate(struct clk * clk, unsigned long rate)
879{
880 u32 flags, cur_rate, done_rate, bypass = 0;
881 u8 cpu_mask = 0;
882 struct prcm_config *prcm;
883 unsigned long found_speed = 0;
884
885 if (clk != &virt_prcm_set)
886 return -EINVAL;
887
888 /* FIXME: Change cpu_is_omap2420() to cpu_is_omap242x() */
889 if (cpu_is_omap2420())
890 cpu_mask = RATE_IN_242X;
891 else if (cpu_is_omap2430())
892 cpu_mask = RATE_IN_243X;
893
894 for (prcm = rate_table; prcm->mpu_speed; prcm++) {
895 if (!(prcm->flags & cpu_mask))
896 continue;
897
898 if (prcm->xtal_speed != sys_ck.rate)
899 continue;
900
901 if (prcm->mpu_speed <= rate) {
902 found_speed = prcm->mpu_speed;
903 break;
904 }
905 }
906
907 if (!found_speed) {
908 printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
909 rate / 1000000);
910 return -EINVAL;
911 }
912
913 curr_prcm_set = prcm;
914 cur_rate = omap2_get_dpll_rate(&dpll_ck);
915
916 if (prcm->dpll_speed == cur_rate / 2) {
917 omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1);
918 } else if (prcm->dpll_speed == cur_rate * 2) {
919 omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1);
920 } else if (prcm->dpll_speed != cur_rate) {
921 local_irq_save(flags);
922
923 if (prcm->dpll_speed == prcm->xtal_speed)
924 bypass = 1;
925
926 if ((prcm->cm_clksel2_pll & 0x3) == 2)
927 done_rate = PRCM_FULL_SPEED;
928 else
929 done_rate = PRCM_HALF_SPEED;
930
931 /* MPU divider */
932 CM_CLKSEL_MPU = prcm->cm_clksel_mpu;
933
934 /* dsp + iva1 div(2420), iva2.1(2430) */
935 CM_CLKSEL_DSP = prcm->cm_clksel_dsp;
936
937 CM_CLKSEL_GFX = prcm->cm_clksel_gfx;
938
939 /* Major subsystem dividers */
940 CM_CLKSEL1_CORE = prcm->cm_clksel1_core;
941 if (cpu_is_omap2430())
942 CM_CLKSEL_MDM = prcm->cm_clksel_mdm;
943
944 /* x2 to enter init_mem */
945 omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1);
946
947 omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
948 bypass);
949
950 omap2_init_memory_params(omap2_dll_force_needed());
951 omap2_reprogram_sdrc(done_rate, 0);
952
953 local_irq_restore(flags);
954 }
955 omap2_clksel_recalc(&dpll_ck);
956
957 return 0;
958}
959
960/*-------------------------------------------------------------------------
961 * Omap2 clock reset and init functions
962 *-------------------------------------------------------------------------*/
963
964static struct clk_functions omap2_clk_functions = {
965 .clk_enable = omap2_clk_enable,
966 .clk_disable = omap2_clk_disable,
Tony Lindgren046d6b22005-11-10 14:26:52 +0000967 .clk_round_rate = omap2_clk_round_rate,
968 .clk_set_rate = omap2_clk_set_rate,
969 .clk_set_parent = omap2_clk_set_parent,
970};
971
972static void __init omap2_get_crystal_rate(struct clk *osc, struct clk *sys)
973{
974 u32 div, aplls, sclk = 13000000;
975
976 aplls = CM_CLKSEL1_PLL;
977 aplls &= ((1 << 23) | (1 << 24) | (1 << 25));
978 aplls >>= 23; /* Isolate field, 0,2,3 */
979
980 if (aplls == 0)
981 sclk = 19200000;
982 else if (aplls == 2)
983 sclk = 13000000;
984 else if (aplls == 3)
985 sclk = 12000000;
986
987 div = PRCM_CLKSRC_CTRL;
988 div &= ((1 << 7) | (1 << 6));
989 div >>= sys->rate_offset;
990
991 osc->rate = sclk * div;
992 sys->rate = sclk;
993}
994
Tony Lindgrenae78dcf2006-09-25 12:41:20 +0300995/*
996 * Set clocks for bypass mode for reboot to work.
997 */
998void omap2_clk_prepare_for_reboot(void)
999{
1000 u32 rate;
1001
1002 if (vclk == NULL || sclk == NULL)
1003 return;
1004
1005 rate = clk_get_rate(sclk);
1006 clk_set_rate(vclk, rate);
1007}
1008
Tony Lindgren046d6b22005-11-10 14:26:52 +00001009#ifdef CONFIG_OMAP_RESET_CLOCKS
1010static void __init omap2_disable_unused_clocks(void)
1011{
1012 struct clk *ck;
1013 u32 regval32;
1014
1015 list_for_each_entry(ck, &clocks, node) {
1016 if (ck->usecount > 0 || (ck->flags & ALWAYS_ENABLED) ||
1017 ck->enable_reg == 0)
1018 continue;
1019
1020 regval32 = __raw_readl(ck->enable_reg);
1021 if ((regval32 & (1 << ck->enable_bit)) == 0)
1022 continue;
1023
1024 printk(KERN_INFO "Disabling unused clock \"%s\"\n", ck->name);
Tony Lindgrenfde0fd42006-01-17 15:31:18 -08001025 _omap2_clk_disable(ck);
Tony Lindgren046d6b22005-11-10 14:26:52 +00001026 }
1027}
1028late_initcall(omap2_disable_unused_clocks);
1029#endif
1030
1031/*
1032 * Switch the MPU rate if specified on cmdline.
1033 * We cannot do this early until cmdline is parsed.
1034 */
1035static int __init omap2_clk_arch_init(void)
1036{
1037 if (!mpurate)
1038 return -EINVAL;
1039
1040 if (omap2_select_table_rate(&virt_prcm_set, mpurate))
1041 printk(KERN_ERR "Could not find matching MPU rate\n");
1042
1043 propagate_rate(&osc_ck); /* update main root fast */
1044 propagate_rate(&func_32k_ck); /* update main root slow */
1045
1046 printk(KERN_INFO "Switched to new clocking rate (Crystal/DPLL/MPU): "
1047 "%ld.%01ld/%ld/%ld MHz\n",
1048 (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10,
1049 (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ;
1050
1051 return 0;
1052}
1053arch_initcall(omap2_clk_arch_init);
1054
1055int __init omap2_clk_init(void)
1056{
1057 struct prcm_config *prcm;
1058 struct clk ** clkp;
1059 u32 clkrate;
1060
1061 clk_init(&omap2_clk_functions);
1062 omap2_get_crystal_rate(&osc_ck, &sys_ck);
1063
1064 for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks);
1065 clkp++) {
1066
1067 if ((*clkp)->flags & CLOCK_IN_OMAP242X && cpu_is_omap2420()) {
1068 clk_register(*clkp);
1069 continue;
1070 }
1071
1072 if ((*clkp)->flags & CLOCK_IN_OMAP243X && cpu_is_omap2430()) {
1073 clk_register(*clkp);
1074 continue;
1075 }
1076 }
1077
1078 /* Check the MPU rate set by bootloader */
1079 clkrate = omap2_get_dpll_rate(&dpll_ck);
1080 for (prcm = rate_table; prcm->mpu_speed; prcm++) {
1081 if (prcm->xtal_speed != sys_ck.rate)
1082 continue;
1083 if (prcm->dpll_speed <= clkrate)
1084 break;
1085 }
1086 curr_prcm_set = prcm;
1087
1088 propagate_rate(&osc_ck); /* update main root fast */
1089 propagate_rate(&func_32k_ck); /* update main root slow */
1090
1091 printk(KERN_INFO "Clocking rate (Crystal/DPLL/MPU): "
1092 "%ld.%01ld/%ld/%ld MHz\n",
1093 (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10,
1094 (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ;
1095
1096 /*
1097 * Only enable those clocks we will need, let the drivers
1098 * enable other clocks as necessary
1099 */
Tony Lindgrenfde0fd42006-01-17 15:31:18 -08001100 clk_enable(&sync_32k_ick);
1101 clk_enable(&omapctrl_ick);
Tony Lindgren046d6b22005-11-10 14:26:52 +00001102 if (cpu_is_omap2430())
Tony Lindgrenfde0fd42006-01-17 15:31:18 -08001103 clk_enable(&sdrc_ick);
Tony Lindgren046d6b22005-11-10 14:26:52 +00001104
Tony Lindgrenae78dcf2006-09-25 12:41:20 +03001105 /* Avoid sleeping sleeping during omap2_clk_prepare_for_reboot() */
1106 vclk = clk_get(NULL, "virt_prcm_set");
1107 sclk = clk_get(NULL, "sys_ck");
1108
Tony Lindgren046d6b22005-11-10 14:26:52 +00001109 return 0;
1110}