diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2008-07-05 04:02:46 -0400 |
---|---|---|
committer | Robert Schwebel <r.schwebel@pengutronix.de> | 2008-07-05 04:02:46 -0400 |
commit | 38a41fdf94c449c165213e4665c3f8a0d30f8aba (patch) | |
tree | cde7f6c0da8c9877736a6b66f245e163fa21c2f7 /arch | |
parent | dbff4e9ea2e83fda89143389bfb229cb29425a32 (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/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/clock.c | 205 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpufreq.c | 20 | ||||
-rw-r--r-- | arch/arm/mach-imx/generic.c | 76 | ||||
-rw-r--r-- | arch/arm/mach-imx/time.c | 23 |
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 | ||
7 | obj-y += irq.o time.o dma.o generic.o | 7 | obj-y += irq.o time.o dma.o generic.o clock.o |
8 | 8 | ||
9 | obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o | 9 | obj-$(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 | |||
33 | struct 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 | */ | ||
46 | static 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 | |||
66 | static 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 | |||
73 | static 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 | */ | ||
81 | static 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 | */ | ||
89 | static 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 | */ | ||
97 | static 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 | */ | ||
105 | static unsigned long imx_get_hclk(void) | ||
106 | { | ||
107 | return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1); | ||
108 | } | ||
109 | |||
110 | static struct clk clk_system_clk = { | ||
111 | .name = "system_clk", | ||
112 | .get_rate = imx_get_system_clk, | ||
113 | }; | ||
114 | |||
115 | static struct clk clk_hclk = { | ||
116 | .name = "hclk", | ||
117 | .get_rate = imx_get_hclk, | ||
118 | }; | ||
119 | |||
120 | static struct clk clk_mcu_clk = { | ||
121 | .name = "mcu_clk", | ||
122 | .get_rate = imx_get_mcu_clk, | ||
123 | }; | ||
124 | |||
125 | static struct clk clk_perclk1 = { | ||
126 | .name = "perclk1", | ||
127 | .get_rate = imx_get_perclk1, | ||
128 | }; | ||
129 | |||
130 | static struct clk clk_uart_clk = { | ||
131 | .name = "uart_clk", | ||
132 | .get_rate = imx_get_perclk1, | ||
133 | }; | ||
134 | |||
135 | static struct clk clk_perclk2 = { | ||
136 | .name = "perclk2", | ||
137 | .get_rate = imx_get_perclk2, | ||
138 | }; | ||
139 | |||
140 | static struct clk clk_perclk3 = { | ||
141 | .name = "perclk3", | ||
142 | .get_rate = imx_get_perclk3, | ||
143 | }; | ||
144 | |||
145 | static 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 | |||
155 | static LIST_HEAD(clocks); | ||
156 | static DEFINE_MUTEX(clocks_mutex); | ||
157 | |||
158 | struct 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 | |||
170 | found: | ||
171 | mutex_unlock(&clocks_mutex); | ||
172 | |||
173 | return clk; | ||
174 | } | ||
175 | |||
176 | void clk_put(struct clk *clk) | ||
177 | { | ||
178 | } | ||
179 | |||
180 | int clk_enable(struct clk *clk) | ||
181 | { | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | void clk_disable(struct clk *clk) | ||
186 | { | ||
187 | } | ||
188 | |||
189 | unsigned long clk_get_rate(struct clk *clk) | ||
190 | { | ||
191 | return clk->get_rate(); | ||
192 | } | ||
193 | |||
194 | int 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 @@ | |||
52 | static u32 mpctl0_at_boot; | 54 | static u32 mpctl0_at_boot; |
53 | static u32 bclk_div_at_boot; | 55 | static u32 bclk_div_at_boot; |
54 | 56 | ||
57 | static struct clk *system_clk, *mcu_clk; | ||
58 | |||
55 | static void imx_set_async_mode(void) | 59 | static 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 | ||
215 | EXPORT_SYMBOL(imx_irq_to_gpio); | 215 | EXPORT_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 | */ | ||
224 | static 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 | |||
243 | unsigned 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 | } | ||
249 | EXPORT_SYMBOL(imx_get_system_clk); | ||
250 | |||
251 | unsigned int imx_get_mcu_clk(void) | ||
252 | { | ||
253 | return imx_decode_pll(MPCTL0, CLK32 * 512); | ||
254 | } | ||
255 | EXPORT_SYMBOL(imx_get_mcu_clk); | ||
256 | |||
257 | /* | ||
258 | * get peripheral clock 1 ( UART[12], Timer[12], PWM ) | ||
259 | */ | ||
260 | unsigned int imx_get_perclk1(void) | ||
261 | { | ||
262 | return imx_get_system_clk() / (((PCDR) & 0xf)+1); | ||
263 | } | ||
264 | EXPORT_SYMBOL(imx_get_perclk1); | ||
265 | |||
266 | /* | ||
267 | * get peripheral clock 2 ( LCD, SD, SPI[12] ) | ||
268 | */ | ||
269 | unsigned int imx_get_perclk2(void) | ||
270 | { | ||
271 | return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1); | ||
272 | } | ||
273 | EXPORT_SYMBOL(imx_get_perclk2); | ||
274 | |||
275 | /* | ||
276 | * get peripheral clock 3 ( SSI ) | ||
277 | */ | ||
278 | unsigned int imx_get_perclk3(void) | ||
279 | { | ||
280 | return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1); | ||
281 | } | ||
282 | EXPORT_SYMBOL(imx_get_perclk3); | ||
283 | |||
284 | /* | ||
285 | * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA ) | ||
286 | */ | ||
287 | unsigned int imx_get_hclk(void) | ||
288 | { | ||
289 | return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1); | ||
290 | } | ||
291 | EXPORT_SYMBOL(imx_get_hclk); | ||
292 | |||
293 | static struct resource imx_mmc_resources[] = { | 217 | static 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 | ||
89 | static int __init imx_clocksource_init(void) | 90 | static 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 | ||
177 | static int __init imx_clockevent_init(void) | 178 | static 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 | ||
194 | extern int imx_clocks_init(void); | ||
193 | 195 | ||
194 | static void __init imx_timer_init(void) | 196 | static 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 |