aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2008-07-05 04:02:46 -0400
committerRobert Schwebel <r.schwebel@pengutronix.de>2008-07-05 04:02:46 -0400
commit38a41fdf94c449c165213e4665c3f8a0d30f8aba (patch)
treecde7f6c0da8c9877736a6b66f245e163fa21c2f7 /arch
parentdbff4e9ea2e83fda89143389bfb229cb29425a32 (diff)
IMX: introduce clock API
This patch introduces the clock API for i.MX and converts all in-Kernel drivers to use it. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-imx/Makefile2
-rw-r--r--arch/arm/mach-imx/clock.c205
-rw-r--r--arch/arm/mach-imx/cpufreq.c20
-rw-r--r--arch/arm/mach-imx/generic.c76
-rw-r--r--arch/arm/mach-imx/time.c23
5 files changed, 240 insertions, 86 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 88d5e61a2e13..b047c7e795a9 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -4,7 +4,7 @@
4 4
5# Object file lists. 5# Object file lists.
6 6
7obj-y += irq.o time.o dma.o generic.o 7obj-y += irq.o time.o dma.o generic.o clock.o
8 8
9obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o 9obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
10 10
diff --git a/arch/arm/mach-imx/clock.c b/arch/arm/mach-imx/clock.c
new file mode 100644
index 000000000000..6a90fe5578df
--- /dev/null
+++ b/arch/arm/mach-imx/clock.c
@@ -0,0 +1,205 @@
1/*
2 * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19#include <linux/kernel.h>
20#include <linux/device.h>
21#include <linux/list.h>
22#include <linux/math64.h>
23#include <linux/err.h>
24
25#include <asm/io.h>
26#include <asm/arch/imx-regs.h>
27
28/*
29 * Very simple approach: We can't disable clocks, so we do
30 * not need refcounting
31 */
32
33struct clk {
34 struct list_head node;
35 const char *name;
36 unsigned long (*get_rate)(void);
37};
38
39/*
40 * get the system pll clock in Hz
41 *
42 * mfi + mfn / (mfd +1)
43 * f = 2 * f_ref * --------------------
44 * pd + 1
45 */
46static unsigned long imx_decode_pll(unsigned int pll, u32 f_ref)
47{
48 unsigned long long ll;
49 unsigned long quot;
50
51 u32 mfi = (pll >> 10) & 0xf;
52 u32 mfn = pll & 0x3ff;
53 u32 mfd = (pll >> 16) & 0x3ff;
54 u32 pd = (pll >> 26) & 0xf;
55
56 mfi = mfi <= 5 ? 5 : mfi;
57
58 ll = 2 * (unsigned long long)f_ref *
59 ((mfi << 16) + (mfn << 16) / (mfd + 1));
60 quot = (pd + 1) * (1 << 16);
61 ll += quot / 2;
62 do_div(ll, quot);
63 return (unsigned long)ll;
64}
65
66static unsigned long imx_get_system_clk(void)
67{
68 u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512);
69
70 return imx_decode_pll(SPCTL0, f_ref);
71}
72
73static unsigned long imx_get_mcu_clk(void)
74{
75 return imx_decode_pll(MPCTL0, CLK32 * 512);
76}
77
78/*
79 * get peripheral clock 1 ( UART[12], Timer[12], PWM )
80 */
81static unsigned long imx_get_perclk1(void)
82{
83 return imx_get_system_clk() / (((PCDR) & 0xf)+1);
84}
85
86/*
87 * get peripheral clock 2 ( LCD, SD, SPI[12] )
88 */
89static unsigned long imx_get_perclk2(void)
90{
91 return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1);
92}
93
94/*
95 * get peripheral clock 3 ( SSI )
96 */
97static unsigned long imx_get_perclk3(void)
98{
99 return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1);
100}
101
102/*
103 * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
104 */
105static unsigned long imx_get_hclk(void)
106{
107 return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1);
108}
109
110static struct clk clk_system_clk = {
111 .name = "system_clk",
112 .get_rate = imx_get_system_clk,
113};
114
115static struct clk clk_hclk = {
116 .name = "hclk",
117 .get_rate = imx_get_hclk,
118};
119
120static struct clk clk_mcu_clk = {
121 .name = "mcu_clk",
122 .get_rate = imx_get_mcu_clk,
123};
124
125static struct clk clk_perclk1 = {
126 .name = "perclk1",
127 .get_rate = imx_get_perclk1,
128};
129
130static struct clk clk_uart_clk = {
131 .name = "uart_clk",
132 .get_rate = imx_get_perclk1,
133};
134
135static struct clk clk_perclk2 = {
136 .name = "perclk2",
137 .get_rate = imx_get_perclk2,
138};
139
140static struct clk clk_perclk3 = {
141 .name = "perclk3",
142 .get_rate = imx_get_perclk3,
143};
144
145static struct clk *clks[] = {
146 &clk_perclk1,
147 &clk_perclk2,
148 &clk_perclk3,
149 &clk_system_clk,
150 &clk_hclk,
151 &clk_mcu_clk,
152 &clk_uart_clk,
153};
154
155static LIST_HEAD(clocks);
156static DEFINE_MUTEX(clocks_mutex);
157
158struct clk *clk_get(struct device *dev, const char *id)
159{
160 struct clk *p, *clk = ERR_PTR(-ENOENT);
161
162 mutex_lock(&clocks_mutex);
163 list_for_each_entry(p, &clocks, node) {
164 if (!strcmp(p->name, id)) {
165 clk = p;
166 goto found;
167 }
168 }
169
170found:
171 mutex_unlock(&clocks_mutex);
172
173 return clk;
174}
175
176void clk_put(struct clk *clk)
177{
178}
179
180int clk_enable(struct clk *clk)
181{
182 return 0;
183}
184
185void clk_disable(struct clk *clk)
186{
187}
188
189unsigned long clk_get_rate(struct clk *clk)
190{
191 return clk->get_rate();
192}
193
194int imx_clocks_init(void)
195{
196 int i;
197
198 mutex_lock(&clocks_mutex);
199 for (i = 0; i < ARRAY_SIZE(clks); i++)
200 list_add(&clks[i]->node, &clocks);
201 mutex_unlock(&clocks_mutex);
202
203 return 0;
204}
205
diff --git a/arch/arm/mach-imx/cpufreq.c b/arch/arm/mach-imx/cpufreq.c
index e548ba74a4d2..be0809b33e08 100644
--- a/arch/arm/mach-imx/cpufreq.c
+++ b/arch/arm/mach-imx/cpufreq.c
@@ -32,6 +32,8 @@
32#include <linux/types.h> 32#include <linux/types.h>
33#include <linux/init.h> 33#include <linux/init.h>
34#include <linux/cpufreq.h> 34#include <linux/cpufreq.h>
35#include <linux/clk.h>
36#include <linux/err.h>
35#include <asm/system.h> 37#include <asm/system.h>
36 38
37#include <asm/hardware.h> 39#include <asm/hardware.h>
@@ -52,6 +54,8 @@
52static u32 mpctl0_at_boot; 54static u32 mpctl0_at_boot;
53static u32 bclk_div_at_boot; 55static u32 bclk_div_at_boot;
54 56
57static struct clk *system_clk, *mcu_clk;
58
55static void imx_set_async_mode(void) 59static void imx_set_async_mode(void)
56{ 60{
57 adjust_cr(CR_920T_CLOCK_MODE, CR_920T_ASYNC_MODE); 61 adjust_cr(CR_920T_CLOCK_MODE, CR_920T_ASYNC_MODE);
@@ -160,10 +164,10 @@ static unsigned int imx_get_speed(unsigned int cpu)
160 cr = get_cr(); 164 cr = get_cr();
161 165
162 if((cr & CR_920T_CLOCK_MODE) == CR_920T_FASTBUS_MODE) { 166 if((cr & CR_920T_CLOCK_MODE) == CR_920T_FASTBUS_MODE) {
163 freq = imx_get_system_clk(); 167 freq = clk_get_rate(system_clk);
164 freq = (freq + bclk_div/2) / bclk_div; 168 freq = (freq + bclk_div/2) / bclk_div;
165 } else { 169 } else {
166 freq = imx_get_mcu_clk(); 170 freq = clk_get_rate(mcu_clk);
167 if (cscr & CSCR_MPU_PRESC) 171 if (cscr & CSCR_MPU_PRESC)
168 freq /= 2; 172 freq /= 2;
169 } 173 }
@@ -201,7 +205,7 @@ static int imx_set_target(struct cpufreq_policy *policy,
201 pr_debug(KERN_DEBUG "imx: requested frequency %ld Hz, mpctl0 at boot 0x%08x\n", 205 pr_debug(KERN_DEBUG "imx: requested frequency %ld Hz, mpctl0 at boot 0x%08x\n",
202 freq, mpctl0_at_boot); 206 freq, mpctl0_at_boot);
203 207
204 sysclk = imx_get_system_clk(); 208 sysclk = clk_get_rate(system_clk);
205 209
206 if (freq > sysclk / bclk_div_at_boot + 1000000) { 210 if (freq > sysclk / bclk_div_at_boot + 1000000) {
207 freq = imx_compute_mpctl(&mpctl0, mpctl0_at_boot, CLK32 * 512, freq, relation); 211 freq = imx_compute_mpctl(&mpctl0, mpctl0_at_boot, CLK32 * 512, freq, relation);
@@ -290,6 +294,16 @@ static int __init imx_cpufreq_init(void)
290 bclk_div_at_boot = __mfld2val(CSCR_BCLK_DIV, CSCR) + 1; 294 bclk_div_at_boot = __mfld2val(CSCR_BCLK_DIV, CSCR) + 1;
291 mpctl0_at_boot = 0; 295 mpctl0_at_boot = 0;
292 296
297 system_clk = clk_get(NULL, "system_clk");
298 if (IS_ERR(system_clk))
299 return PTR_ERR(system_clk);
300
301 mcu_clk = clk_get(NULL, "mcu_clk");
302 if (IS_ERR(mcu_clk)) {
303 clk_put(system_clk);
304 return PTR_ERR(mcu_clk);
305 }
306
293 if((CSCR & CSCR_MPEN) && 307 if((CSCR & CSCR_MPEN) &&
294 ((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE)) 308 ((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE))
295 mpctl0_at_boot = MPCTL0; 309 mpctl0_at_boot = MPCTL0;
diff --git a/arch/arm/mach-imx/generic.c b/arch/arm/mach-imx/generic.c
index 4cfc9d3af28a..98ddd8a6d05f 100644
--- a/arch/arm/mach-imx/generic.c
+++ b/arch/arm/mach-imx/generic.c
@@ -214,82 +214,6 @@ int imx_irq_to_gpio(unsigned irq)
214 214
215EXPORT_SYMBOL(imx_irq_to_gpio); 215EXPORT_SYMBOL(imx_irq_to_gpio);
216 216
217/*
218 * get the system pll clock in Hz
219 *
220 * mfi + mfn / (mfd +1)
221 * f = 2 * f_ref * --------------------
222 * pd + 1
223 */
224static unsigned int imx_decode_pll(unsigned int pll, u32 f_ref)
225{
226 unsigned long long ll;
227 unsigned long quot;
228
229 u32 mfi = (pll >> 10) & 0xf;
230 u32 mfn = pll & 0x3ff;
231 u32 mfd = (pll >> 16) & 0x3ff;
232 u32 pd = (pll >> 26) & 0xf;
233
234 mfi = mfi <= 5 ? 5 : mfi;
235
236 ll = 2 * (unsigned long long)f_ref * ( (mfi<<16) + (mfn<<16) / (mfd+1) );
237 quot = (pd+1) * (1<<16);
238 ll += quot / 2;
239 do_div(ll, quot);
240 return (unsigned int) ll;
241}
242
243unsigned int imx_get_system_clk(void)
244{
245 u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512);
246
247 return imx_decode_pll(SPCTL0, f_ref);
248}
249EXPORT_SYMBOL(imx_get_system_clk);
250
251unsigned int imx_get_mcu_clk(void)
252{
253 return imx_decode_pll(MPCTL0, CLK32 * 512);
254}
255EXPORT_SYMBOL(imx_get_mcu_clk);
256
257/*
258 * get peripheral clock 1 ( UART[12], Timer[12], PWM )
259 */
260unsigned int imx_get_perclk1(void)
261{
262 return imx_get_system_clk() / (((PCDR) & 0xf)+1);
263}
264EXPORT_SYMBOL(imx_get_perclk1);
265
266/*
267 * get peripheral clock 2 ( LCD, SD, SPI[12] )
268 */
269unsigned int imx_get_perclk2(void)
270{
271 return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1);
272}
273EXPORT_SYMBOL(imx_get_perclk2);
274
275/*
276 * get peripheral clock 3 ( SSI )
277 */
278unsigned int imx_get_perclk3(void)
279{
280 return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1);
281}
282EXPORT_SYMBOL(imx_get_perclk3);
283
284/*
285 * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
286 */
287unsigned int imx_get_hclk(void)
288{
289 return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1);
290}
291EXPORT_SYMBOL(imx_get_hclk);
292
293static struct resource imx_mmc_resources[] = { 217static struct resource imx_mmc_resources[] = {
294 [0] = { 218 [0] = {
295 .start = 0x00214000, 219 .start = 0x00214000,
diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c
index d86d124aea22..5a41e96e8586 100644
--- a/arch/arm/mach-imx/time.c
+++ b/arch/arm/mach-imx/time.c
@@ -17,6 +17,7 @@
17#include <linux/time.h> 17#include <linux/time.h>
18#include <linux/clocksource.h> 18#include <linux/clocksource.h>
19#include <linux/clockchips.h> 19#include <linux/clockchips.h>
20#include <linux/clk.h>
20 21
21#include <asm/hardware.h> 22#include <asm/hardware.h>
22#include <asm/io.h> 23#include <asm/io.h>
@@ -86,10 +87,10 @@ static struct clocksource clocksource_imx = {
86 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 87 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
87}; 88};
88 89
89static int __init imx_clocksource_init(void) 90static int __init imx_clocksource_init(unsigned long rate)
90{ 91{
91 clocksource_imx.mult = 92 clocksource_imx.mult =
92 clocksource_hz2mult(imx_get_perclk1(), clocksource_imx.shift); 93 clocksource_hz2mult(rate, clocksource_imx.shift);
93 clocksource_register(&clocksource_imx); 94 clocksource_register(&clocksource_imx);
94 95
95 return 0; 96 return 0;
@@ -174,9 +175,9 @@ static struct clock_event_device clockevent_imx = {
174 .rating = 200, 175 .rating = 200,
175}; 176};
176 177
177static int __init imx_clockevent_init(void) 178static int __init imx_clockevent_init(unsigned long rate)
178{ 179{
179 clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC, 180 clockevent_imx.mult = div_sc(rate, NSEC_PER_SEC,
180 clockevent_imx.shift); 181 clockevent_imx.shift);
181 clockevent_imx.max_delta_ns = 182 clockevent_imx.max_delta_ns =
182 clockevent_delta2ns(0xfffffffe, &clockevent_imx); 183 clockevent_delta2ns(0xfffffffe, &clockevent_imx);
@@ -190,13 +191,23 @@ static int __init imx_clockevent_init(void)
190 return 0; 191 return 0;
191} 192}
192 193
194extern int imx_clocks_init(void);
193 195
194static void __init imx_timer_init(void) 196static void __init imx_timer_init(void)
195{ 197{
198 struct clk *clk;
199 unsigned long rate;
200
201 imx_clocks_init();
202
203 clk = clk_get(NULL, "perclk1");
204 clk_enable(clk);
205 rate = clk_get_rate(clk);
206
196 imx_timer_hardware_init(); 207 imx_timer_hardware_init();
197 imx_clocksource_init(); 208 imx_clocksource_init(rate);
198 209
199 imx_clockevent_init(); 210 imx_clockevent_init(rate);
200 211
201 /* 212 /*
202 * Make irqs happen for the system timer 213 * Make irqs happen for the system timer