blob: 295e671e9cfdbcb397560cac97d881960077c07c [file] [log] [blame]
Tony Lindgren046d6b22005-11-10 14:26:52 +00001/*
2 * linux/arch/arm/mach-omap2/clock.c
3 *
Tony Lindgrena16e9702008-03-18 11:56:39 +02004 * Copyright (C) 2005-2008 Texas Instruments, Inc.
5 * Copyright (C) 2004-2008 Nokia Corporation
6 *
7 * Contacts:
Tony Lindgren046d6b22005-11-10 14:26:52 +00008 * Richard Woodruff <r-woodruff2@ti.com>
Tony Lindgrena16e9702008-03-18 11:56:39 +02009 * Paul Walmsley
Tony Lindgren046d6b22005-11-10 14:26:52 +000010 *
Tony Lindgrena16e9702008-03-18 11:56:39 +020011 * Based on earlier work by Tuukka Tikkanen, Tony Lindgren,
12 * Gordon McNutt and RidgeRun, Inc.
Tony Lindgren046d6b22005-11-10 14:26:52 +000013 *
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 */
Paul Walmsley6b8858a2008-03-18 10:35:15 +020018#undef DEBUG
19
Tony Lindgren046d6b22005-11-10 14:26:52 +000020#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/device.h>
23#include <linux/list.h>
24#include <linux/errno.h>
25#include <linux/delay.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000026#include <linux/clk.h>
Tony Lindgren046d6b22005-11-10 14:26:52 +000027
Paul Walmsley6b8858a2008-03-18 10:35:15 +020028#include <linux/io.h>
29#include <linux/cpufreq.h>
Tony Lindgren046d6b22005-11-10 14:26:52 +000030
Russell Kinga09e64f2008-08-05 16:14:15 +010031#include <mach/clock.h>
32#include <mach/sram.h>
Tony Lindgren76631482006-12-12 23:02:43 -080033#include <asm/div64.h>
Paul Walmsley6b8858a2008-03-18 10:35:15 +020034#include <asm/bitops.h>
Tony Lindgren046d6b22005-11-10 14:26:52 +000035
Tony Lindgrenb824efa2006-04-02 17:46:20 +010036#include "memory.h"
Paul Walmsley6b8858a2008-03-18 10:35:15 +020037#include "clock.h"
Paul Walmsley32ab2cb2008-03-18 10:15:28 +020038#include "clock24xx.h"
Paul Walmsley6b8858a2008-03-18 10:35:15 +020039#include "prm.h"
40#include "prm-regbits-24xx.h"
41#include "cm.h"
42#include "cm-regbits-24xx.h"
Tony Lindgren046d6b22005-11-10 14:26:52 +000043
Paul Walmsley6b8858a2008-03-18 10:35:15 +020044/* CM_CLKEN_PLL.EN_{54,96}M_PLL options (24XX) */
45#define EN_APLL_STOPPED 0
46#define EN_APLL_LOCKED 3
Juha Yrjoladdc32a82006-09-25 12:41:50 +030047
Paul Walmsley6b8858a2008-03-18 10:35:15 +020048/* CM_CLKSEL1_PLL.APLLS_CLKIN options (24XX) */
49#define APLLS_CLKIN_19_2MHZ 0
50#define APLLS_CLKIN_13MHZ 2
51#define APLLS_CLKIN_12MHZ 3
52
53/* #define DOWN_VARIABLE_DPLL 1 */ /* Experimental */
Tony Lindgren046d6b22005-11-10 14:26:52 +000054
55static struct prcm_config *curr_prcm_set;
Tony Lindgrenae78dcf2006-09-25 12:41:20 +030056static struct clk *vclk;
57static struct clk *sclk;
Tony Lindgren046d6b22005-11-10 14:26:52 +000058
59/*-------------------------------------------------------------------------
Paul Walmsley6b8858a2008-03-18 10:35:15 +020060 * Omap24xx specific clock functions
Tony Lindgren046d6b22005-11-10 14:26:52 +000061 *-------------------------------------------------------------------------*/
62
Tony Lindgrena16e9702008-03-18 11:56:39 +020063/* This actually returns the rate of core_ck, not dpll_ck. */
64static u32 omap2_get_dpll_rate_24xx(struct clk *tclk)
65{
66 long long dpll_clk;
67 u8 amult;
68
69 dpll_clk = omap2_get_dpll_rate(tclk);
70
71 amult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
72 amult &= OMAP24XX_CORE_CLK_SRC_MASK;
73 dpll_clk *= amult;
74
75 return dpll_clk;
76}
77
Paul Walmsley6b8858a2008-03-18 10:35:15 +020078static int omap2_enable_osc_ck(struct clk *clk)
79{
80 u32 pcc;
81
82 pcc = __raw_readl(OMAP24XX_PRCM_CLKSRC_CTRL);
83
84 __raw_writel(pcc & ~OMAP_AUTOEXTCLKMODE_MASK,
85 OMAP24XX_PRCM_CLKSRC_CTRL);
86
87 return 0;
88}
89
90static void omap2_disable_osc_ck(struct clk *clk)
91{
92 u32 pcc;
93
94 pcc = __raw_readl(OMAP24XX_PRCM_CLKSRC_CTRL);
95
96 __raw_writel(pcc | OMAP_AUTOEXTCLKMODE_MASK,
97 OMAP24XX_PRCM_CLKSRC_CTRL);
98}
99
100#ifdef OLD_CK
Tony Lindgren046d6b22005-11-10 14:26:52 +0000101/* Recalculate SYST_CLK */
102static void omap2_sys_clk_recalc(struct clk * clk)
103{
104 u32 div = PRCM_CLKSRC_CTRL;
105 div &= (1 << 7) | (1 << 6); /* Test if ext clk divided by 1 or 2 */
106 div >>= clk->rate_offset;
107 clk->rate = (clk->parent->rate / div);
108 propagate_rate(clk);
109}
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200110#endif /* OLD_CK */
Tony Lindgren046d6b22005-11-10 14:26:52 +0000111
Tony Lindgren046d6b22005-11-10 14:26:52 +0000112/* Enable an APLL if off */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200113static int omap2_clk_fixed_enable(struct clk *clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000114{
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200115 u32 cval, apll_mask;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000116
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200117 apll_mask = EN_APLL_LOCKED << clk->enable_bit;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000118
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200119 cval = cm_read_mod_reg(PLL_MOD, CM_CLKEN);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000120
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200121 if ((cval & apll_mask) == apll_mask)
122 return 0; /* apll already enabled */
Tony Lindgren046d6b22005-11-10 14:26:52 +0000123
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200124 cval &= ~apll_mask;
125 cval |= apll_mask;
126 cm_write_mod_reg(cval, PLL_MOD, CM_CLKEN);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000127
128 if (clk == &apll96_ck)
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200129 cval = OMAP24XX_ST_96M_APLL;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000130 else if (clk == &apll54_ck)
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200131 cval = OMAP24XX_ST_54M_APLL;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000132
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200133 omap2_wait_clock_ready(OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST), cval,
134 clk->name);
135
136 /*
137 * REVISIT: Should we return an error code if omap2_wait_clock_ready()
138 * fails?
139 */
140 return 0;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000141}
142
Tony Lindgren046d6b22005-11-10 14:26:52 +0000143/* Stop APLL */
144static void omap2_clk_fixed_disable(struct clk *clk)
145{
146 u32 cval;
147
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200148 cval = cm_read_mod_reg(PLL_MOD, CM_CLKEN);
149 cval &= ~(EN_APLL_LOCKED << clk->enable_bit);
150 cm_write_mod_reg(cval, PLL_MOD, CM_CLKEN);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000151}
152
Tony Lindgren046d6b22005-11-10 14:26:52 +0000153/*
154 * Uses the current prcm set to tell if a rate is valid.
155 * You can go slower, but not faster within a given rate set.
156 */
Paul Walmsley88b8ba92008-07-03 12:24:46 +0300157long omap2_dpllcore_round_rate(unsigned long target_rate)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000158{
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200159 u32 high, low, core_clk_src;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000160
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200161 core_clk_src = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
162 core_clk_src &= OMAP24XX_CORE_CLK_SRC_MASK;
163
164 if (core_clk_src == CORE_CLK_SRC_DPLL) { /* DPLL clockout */
Tony Lindgren046d6b22005-11-10 14:26:52 +0000165 high = curr_prcm_set->dpll_speed * 2;
166 low = curr_prcm_set->dpll_speed;
167 } else { /* DPLL clockout x 2 */
168 high = curr_prcm_set->dpll_speed;
169 low = curr_prcm_set->dpll_speed / 2;
170 }
171
172#ifdef DOWN_VARIABLE_DPLL
173 if (target_rate > high)
174 return high;
175 else
176 return target_rate;
177#else
178 if (target_rate > low)
179 return high;
180 else
181 return low;
182#endif
183
184}
185
Paul Walmsley88b8ba92008-07-03 12:24:46 +0300186static void omap2_dpllcore_recalc(struct clk *clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000187{
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200188 clk->rate = omap2_get_dpll_rate_24xx(clk);
189
190 propagate_rate(clk);
191}
192
Paul Walmsley88b8ba92008-07-03 12:24:46 +0300193static int omap2_reprogram_dpllcore(struct clk *clk, unsigned long rate)
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200194{
195 u32 cur_rate, low, mult, div, valid_rate, done_rate;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000196 u32 bypass = 0;
197 struct prcm_config tmpset;
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200198 const struct dpll_data *dd;
199 unsigned long flags;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000200 int ret = -EINVAL;
201
202 local_irq_save(flags);
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200203 cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck);
204 mult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
205 mult &= OMAP24XX_CORE_CLK_SRC_MASK;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000206
207 if ((rate == (cur_rate / 2)) && (mult == 2)) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200208 omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL, 1);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000209 } else if ((rate == (cur_rate * 2)) && (mult == 1)) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200210 omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000211 } else if (rate != cur_rate) {
Paul Walmsley88b8ba92008-07-03 12:24:46 +0300212 valid_rate = omap2_dpllcore_round_rate(rate);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000213 if (valid_rate != rate)
214 goto dpll_exit;
215
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200216 if (mult == 1)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000217 low = curr_prcm_set->dpll_speed;
218 else
219 low = curr_prcm_set->dpll_speed / 2;
220
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200221 dd = clk->dpll_data;
222 if (!dd)
223 goto dpll_exit;
224
225 tmpset.cm_clksel1_pll = __raw_readl(dd->mult_div1_reg);
226 tmpset.cm_clksel1_pll &= ~(dd->mult_mask |
227 dd->div1_mask);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000228 div = ((curr_prcm_set->xtal_speed / 1000000) - 1);
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200229 tmpset.cm_clksel2_pll = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
230 tmpset.cm_clksel2_pll &= ~OMAP24XX_CORE_CLK_SRC_MASK;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000231 if (rate > low) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200232 tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL_X2;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000233 mult = ((rate / 2) / 1000000);
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200234 done_rate = CORE_CLK_SRC_DPLL_X2;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000235 } else {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200236 tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000237 mult = (rate / 1000000);
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200238 done_rate = CORE_CLK_SRC_DPLL;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000239 }
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200240 tmpset.cm_clksel1_pll |= (div << __ffs(dd->mult_mask));
241 tmpset.cm_clksel1_pll |= (mult << __ffs(dd->div1_mask));
Tony Lindgren046d6b22005-11-10 14:26:52 +0000242
243 /* Worst case */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200244 tmpset.base_sdrc_rfr = SDRC_RFR_CTRL_BYPASS;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000245
246 if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */
247 bypass = 1;
248
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200249 omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1); /* For init_mem */
Tony Lindgren046d6b22005-11-10 14:26:52 +0000250
251 /* Force dll lock mode */
252 omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr,
253 bypass);
254
255 /* Errata: ret dll entry state */
256 omap2_init_memory_params(omap2_dll_force_needed());
257 omap2_reprogram_sdrc(done_rate, 0);
258 }
Paul Walmsley88b8ba92008-07-03 12:24:46 +0300259 omap2_dpllcore_recalc(&dpll_ck);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000260 ret = 0;
261
262dpll_exit:
263 local_irq_restore(flags);
264 return(ret);
265}
266
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200267/**
268 * omap2_table_mpu_recalc - just return the MPU speed
269 * @clk: virt_prcm_set struct clk
270 *
271 * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set.
272 */
273static void omap2_table_mpu_recalc(struct clk *clk)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000274{
275 clk->rate = curr_prcm_set->mpu_speed;
276}
277
278/*
279 * Look for a rate equal or less than the target rate given a configuration set.
280 *
281 * What's not entirely clear is "which" field represents the key field.
282 * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
283 * just uses the ARM rates.
284 */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200285static long omap2_round_to_table_rate(struct clk *clk, unsigned long rate)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000286{
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200287 struct prcm_config *ptr;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000288 long highest_rate;
289
290 if (clk != &virt_prcm_set)
291 return -EINVAL;
292
293 highest_rate = -EINVAL;
294
295 for (ptr = rate_table; ptr->mpu_speed; ptr++) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200296 if (!(ptr->flags & cpu_mask))
297 continue;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000298 if (ptr->xtal_speed != sys_ck.rate)
299 continue;
300
301 highest_rate = ptr->mpu_speed;
302
303 /* Can check only after xtal frequency check */
304 if (ptr->mpu_speed <= rate)
305 break;
306 }
307 return highest_rate;
308}
309
Tony Lindgren046d6b22005-11-10 14:26:52 +0000310/* Sets basic clocks based on the specified rate */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200311static int omap2_select_table_rate(struct clk *clk, unsigned long rate)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000312{
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200313 u32 cur_rate, done_rate, bypass = 0, tmp;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000314 struct prcm_config *prcm;
315 unsigned long found_speed = 0;
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200316 unsigned long flags;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000317
318 if (clk != &virt_prcm_set)
319 return -EINVAL;
320
Tony Lindgren046d6b22005-11-10 14:26:52 +0000321 for (prcm = rate_table; prcm->mpu_speed; prcm++) {
322 if (!(prcm->flags & cpu_mask))
323 continue;
324
325 if (prcm->xtal_speed != sys_ck.rate)
326 continue;
327
328 if (prcm->mpu_speed <= rate) {
329 found_speed = prcm->mpu_speed;
330 break;
331 }
332 }
333
334 if (!found_speed) {
335 printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
Tony Lindgrena16e9702008-03-18 11:56:39 +0200336 rate / 1000000);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000337 return -EINVAL;
338 }
339
340 curr_prcm_set = prcm;
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200341 cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000342
343 if (prcm->dpll_speed == cur_rate / 2) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200344 omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL, 1);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000345 } else if (prcm->dpll_speed == cur_rate * 2) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200346 omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000347 } else if (prcm->dpll_speed != cur_rate) {
348 local_irq_save(flags);
349
350 if (prcm->dpll_speed == prcm->xtal_speed)
351 bypass = 1;
352
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200353 if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
354 CORE_CLK_SRC_DPLL_X2)
355 done_rate = CORE_CLK_SRC_DPLL_X2;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000356 else
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200357 done_rate = CORE_CLK_SRC_DPLL;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000358
359 /* MPU divider */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200360 cm_write_mod_reg(prcm->cm_clksel_mpu, MPU_MOD, CM_CLKSEL);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000361
362 /* dsp + iva1 div(2420), iva2.1(2430) */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200363 cm_write_mod_reg(prcm->cm_clksel_dsp,
364 OMAP24XX_DSP_MOD, CM_CLKSEL);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000365
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200366 cm_write_mod_reg(prcm->cm_clksel_gfx, GFX_MOD, CM_CLKSEL);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000367
368 /* Major subsystem dividers */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200369 tmp = cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & OMAP24XX_CLKSEL_DSS2_MASK;
370 cm_write_mod_reg(prcm->cm_clksel1_core | tmp, CORE_MOD, CM_CLKSEL1);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000371 if (cpu_is_omap2430())
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200372 cm_write_mod_reg(prcm->cm_clksel_mdm,
373 OMAP2430_MDM_MOD, CM_CLKSEL);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000374
375 /* x2 to enter init_mem */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200376 omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000377
378 omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
379 bypass);
380
381 omap2_init_memory_params(omap2_dll_force_needed());
382 omap2_reprogram_sdrc(done_rate, 0);
383
384 local_irq_restore(flags);
385 }
Paul Walmsley88b8ba92008-07-03 12:24:46 +0300386 omap2_dpllcore_recalc(&dpll_ck);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000387
388 return 0;
389}
390
Tony Lindgren046d6b22005-11-10 14:26:52 +0000391static struct clk_functions omap2_clk_functions = {
392 .clk_enable = omap2_clk_enable,
393 .clk_disable = omap2_clk_disable,
Tony Lindgren046d6b22005-11-10 14:26:52 +0000394 .clk_round_rate = omap2_clk_round_rate,
395 .clk_set_rate = omap2_clk_set_rate,
396 .clk_set_parent = omap2_clk_set_parent,
Tony Lindgren90afd5c2006-09-25 13:27:20 +0300397 .clk_disable_unused = omap2_clk_disable_unused,
Tony Lindgren046d6b22005-11-10 14:26:52 +0000398};
399
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200400static u32 omap2_get_apll_clkin(void)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000401{
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200402 u32 aplls, sclk = 0;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000403
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200404 aplls = cm_read_mod_reg(PLL_MOD, CM_CLKSEL1);
405 aplls &= OMAP24XX_APLLS_CLKIN_MASK;
406 aplls >>= OMAP24XX_APLLS_CLKIN_SHIFT;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000407
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200408 if (aplls == APLLS_CLKIN_19_2MHZ)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000409 sclk = 19200000;
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200410 else if (aplls == APLLS_CLKIN_13MHZ)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000411 sclk = 13000000;
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200412 else if (aplls == APLLS_CLKIN_12MHZ)
Tony Lindgren046d6b22005-11-10 14:26:52 +0000413 sclk = 12000000;
414
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200415 return sclk;
416}
Tony Lindgren046d6b22005-11-10 14:26:52 +0000417
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200418static u32 omap2_get_sysclkdiv(void)
419{
420 u32 div;
421
422 div = __raw_readl(OMAP24XX_PRCM_CLKSRC_CTRL);
423 div &= OMAP_SYSCLKDIV_MASK;
424 div >>= OMAP_SYSCLKDIV_SHIFT;
425
426 return div;
427}
428
429static void omap2_osc_clk_recalc(struct clk *clk)
430{
431 clk->rate = omap2_get_apll_clkin() * omap2_get_sysclkdiv();
432 propagate_rate(clk);
433}
434
435static void omap2_sys_clk_recalc(struct clk *clk)
436{
437 clk->rate = clk->parent->rate / omap2_get_sysclkdiv();
438 propagate_rate(clk);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000439}
440
Tony Lindgrenae78dcf2006-09-25 12:41:20 +0300441/*
442 * Set clocks for bypass mode for reboot to work.
443 */
444void omap2_clk_prepare_for_reboot(void)
445{
446 u32 rate;
447
448 if (vclk == NULL || sclk == NULL)
449 return;
450
451 rate = clk_get_rate(sclk);
452 clk_set_rate(vclk, rate);
453}
454
Tony Lindgren046d6b22005-11-10 14:26:52 +0000455/*
456 * Switch the MPU rate if specified on cmdline.
457 * We cannot do this early until cmdline is parsed.
458 */
459static int __init omap2_clk_arch_init(void)
460{
461 if (!mpurate)
462 return -EINVAL;
463
464 if (omap2_select_table_rate(&virt_prcm_set, mpurate))
465 printk(KERN_ERR "Could not find matching MPU rate\n");
466
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200467 recalculate_root_clocks();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000468
469 printk(KERN_INFO "Switched to new clocking rate (Crystal/DPLL/MPU): "
470 "%ld.%01ld/%ld/%ld MHz\n",
471 (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10,
472 (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ;
473
474 return 0;
475}
476arch_initcall(omap2_clk_arch_init);
477
478int __init omap2_clk_init(void)
479{
480 struct prcm_config *prcm;
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200481 struct clk **clkp;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000482 u32 clkrate;
483
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200484 if (cpu_is_omap242x())
485 cpu_mask = RATE_IN_242X;
486 else if (cpu_is_omap2430())
487 cpu_mask = RATE_IN_243X;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000488
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200489 clk_init(&omap2_clk_functions);
490
491 omap2_osc_clk_recalc(&osc_ck);
492 omap2_sys_clk_recalc(&sys_ck);
493
494 for (clkp = onchip_24xx_clks;
495 clkp < onchip_24xx_clks + ARRAY_SIZE(onchip_24xx_clks);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000496 clkp++) {
497
498 if ((*clkp)->flags & CLOCK_IN_OMAP242X && cpu_is_omap2420()) {
499 clk_register(*clkp);
500 continue;
501 }
502
503 if ((*clkp)->flags & CLOCK_IN_OMAP243X && cpu_is_omap2430()) {
504 clk_register(*clkp);
505 continue;
506 }
507 }
508
509 /* Check the MPU rate set by bootloader */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200510 clkrate = omap2_get_dpll_rate_24xx(&dpll_ck);
Tony Lindgren046d6b22005-11-10 14:26:52 +0000511 for (prcm = rate_table; prcm->mpu_speed; prcm++) {
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200512 if (!(prcm->flags & cpu_mask))
513 continue;
Tony Lindgren046d6b22005-11-10 14:26:52 +0000514 if (prcm->xtal_speed != sys_ck.rate)
515 continue;
516 if (prcm->dpll_speed <= clkrate)
517 break;
518 }
519 curr_prcm_set = prcm;
520
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200521 recalculate_root_clocks();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000522
523 printk(KERN_INFO "Clocking rate (Crystal/DPLL/MPU): "
524 "%ld.%01ld/%ld/%ld MHz\n",
525 (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10,
526 (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ;
527
528 /*
529 * Only enable those clocks we will need, let the drivers
530 * enable other clocks as necessary
531 */
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200532 clk_enable_init_clocks();
Tony Lindgren046d6b22005-11-10 14:26:52 +0000533
Tony Lindgrenae78dcf2006-09-25 12:41:20 +0300534 /* Avoid sleeping sleeping during omap2_clk_prepare_for_reboot() */
535 vclk = clk_get(NULL, "virt_prcm_set");
536 sclk = clk_get(NULL, "sys_ck");
537
Tony Lindgren046d6b22005-11-10 14:26:52 +0000538 return 0;
539}