diff options
Diffstat (limited to 'arch/arm/plat-s3c24xx/s3c2443-clock.c')
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c2443-clock.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c24xx/s3c2443-clock.c b/arch/arm/plat-s3c24xx/s3c2443-clock.c new file mode 100644 index 00000000000..59552c0ea5f --- /dev/null +++ b/arch/arm/plat-s3c24xx/s3c2443-clock.c | |||
@@ -0,0 +1,441 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/s3c2443-clock.c | ||
2 | * | ||
3 | * Copyright (c) 2007, 2010 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2443 Clock control suport - common code | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/clk.h> | ||
11 | #include <linux/io.h> | ||
12 | |||
13 | #include <mach/regs-s3c2443-clock.h> | ||
14 | |||
15 | #include <plat/s3c2443.h> | ||
16 | #include <plat/clock.h> | ||
17 | #include <plat/clock-clksrc.h> | ||
18 | #include <plat/cpu.h> | ||
19 | |||
20 | #include <plat/cpu-freq.h> | ||
21 | |||
22 | |||
23 | static int s3c2443_gate(void __iomem *reg, struct clk *clk, int enable) | ||
24 | { | ||
25 | u32 ctrlbit = clk->ctrlbit; | ||
26 | u32 con = __raw_readl(reg); | ||
27 | |||
28 | if (enable) | ||
29 | con |= ctrlbit; | ||
30 | else | ||
31 | con &= ~ctrlbit; | ||
32 | |||
33 | __raw_writel(con, reg); | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | int s3c2443_clkcon_enable_h(struct clk *clk, int enable) | ||
38 | { | ||
39 | return s3c2443_gate(S3C2443_HCLKCON, clk, enable); | ||
40 | } | ||
41 | |||
42 | int s3c2443_clkcon_enable_p(struct clk *clk, int enable) | ||
43 | { | ||
44 | return s3c2443_gate(S3C2443_PCLKCON, clk, enable); | ||
45 | } | ||
46 | |||
47 | int s3c2443_clkcon_enable_s(struct clk *clk, int enable) | ||
48 | { | ||
49 | return s3c2443_gate(S3C2443_SCLKCON, clk, enable); | ||
50 | } | ||
51 | |||
52 | /* mpllref is a direct descendant of clk_xtal by default, but it is not | ||
53 | * elided as the EPLL can be either sourced by the XTAL or EXTCLK and as | ||
54 | * such directly equating the two source clocks is impossible. | ||
55 | */ | ||
56 | struct clk clk_mpllref = { | ||
57 | .name = "mpllref", | ||
58 | .parent = &clk_xtal, | ||
59 | }; | ||
60 | |||
61 | static struct clk *clk_epllref_sources[] = { | ||
62 | [0] = &clk_mpllref, | ||
63 | [1] = &clk_mpllref, | ||
64 | [2] = &clk_xtal, | ||
65 | [3] = &clk_ext, | ||
66 | }; | ||
67 | |||
68 | struct clksrc_clk clk_epllref = { | ||
69 | .clk = { | ||
70 | .name = "epllref", | ||
71 | }, | ||
72 | .sources = &(struct clksrc_sources) { | ||
73 | .sources = clk_epllref_sources, | ||
74 | .nr_sources = ARRAY_SIZE(clk_epllref_sources), | ||
75 | }, | ||
76 | .reg_src = { .reg = S3C2443_CLKSRC, .size = 2, .shift = 7 }, | ||
77 | }; | ||
78 | |||
79 | /* esysclk | ||
80 | * | ||
81 | * this is sourced from either the EPLL or the EPLLref clock | ||
82 | */ | ||
83 | |||
84 | static struct clk *clk_sysclk_sources[] = { | ||
85 | [0] = &clk_epllref.clk, | ||
86 | [1] = &clk_epll, | ||
87 | }; | ||
88 | |||
89 | struct clksrc_clk clk_esysclk = { | ||
90 | .clk = { | ||
91 | .name = "esysclk", | ||
92 | .parent = &clk_epll, | ||
93 | }, | ||
94 | .sources = &(struct clksrc_sources) { | ||
95 | .sources = clk_sysclk_sources, | ||
96 | .nr_sources = ARRAY_SIZE(clk_sysclk_sources), | ||
97 | }, | ||
98 | .reg_src = { .reg = S3C2443_CLKSRC, .size = 1, .shift = 6 }, | ||
99 | }; | ||
100 | |||
101 | static unsigned long s3c2443_getrate_mdivclk(struct clk *clk) | ||
102 | { | ||
103 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
104 | unsigned long div = __raw_readl(S3C2443_CLKDIV0); | ||
105 | |||
106 | div &= S3C2443_CLKDIV0_EXTDIV_MASK; | ||
107 | div >>= (S3C2443_CLKDIV0_EXTDIV_SHIFT-1); /* x2 */ | ||
108 | |||
109 | return parent_rate / (div + 1); | ||
110 | } | ||
111 | |||
112 | static struct clk clk_mdivclk = { | ||
113 | .name = "mdivclk", | ||
114 | .parent = &clk_mpllref, | ||
115 | .ops = &(struct clk_ops) { | ||
116 | .get_rate = s3c2443_getrate_mdivclk, | ||
117 | }, | ||
118 | }; | ||
119 | |||
120 | static struct clk *clk_msysclk_sources[] = { | ||
121 | [0] = &clk_mpllref, | ||
122 | [1] = &clk_mpll, | ||
123 | [2] = &clk_mdivclk, | ||
124 | [3] = &clk_mpllref, | ||
125 | }; | ||
126 | |||
127 | struct clksrc_clk clk_msysclk = { | ||
128 | .clk = { | ||
129 | .name = "msysclk", | ||
130 | .parent = &clk_xtal, | ||
131 | }, | ||
132 | .sources = &(struct clksrc_sources) { | ||
133 | .sources = clk_msysclk_sources, | ||
134 | .nr_sources = ARRAY_SIZE(clk_msysclk_sources), | ||
135 | }, | ||
136 | .reg_src = { .reg = S3C2443_CLKSRC, .size = 2, .shift = 3 }, | ||
137 | }; | ||
138 | |||
139 | /* prediv | ||
140 | * | ||
141 | * this divides the msysclk down to pass to h/p/etc. | ||
142 | */ | ||
143 | |||
144 | static unsigned long s3c2443_prediv_getrate(struct clk *clk) | ||
145 | { | ||
146 | unsigned long rate = clk_get_rate(clk->parent); | ||
147 | unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0); | ||
148 | |||
149 | clkdiv0 &= S3C2443_CLKDIV0_PREDIV_MASK; | ||
150 | clkdiv0 >>= S3C2443_CLKDIV0_PREDIV_SHIFT; | ||
151 | |||
152 | return rate / (clkdiv0 + 1); | ||
153 | } | ||
154 | |||
155 | static struct clk clk_prediv = { | ||
156 | .name = "prediv", | ||
157 | .parent = &clk_msysclk.clk, | ||
158 | .ops = &(struct clk_ops) { | ||
159 | .get_rate = s3c2443_prediv_getrate, | ||
160 | }, | ||
161 | }; | ||
162 | |||
163 | /* usbhost | ||
164 | * | ||
165 | * usb host bus-clock, usually 48MHz to provide USB bus clock timing | ||
166 | */ | ||
167 | |||
168 | static struct clksrc_clk clk_usb_bus_host = { | ||
169 | .clk = { | ||
170 | .name = "usb-bus-host-parent", | ||
171 | .parent = &clk_esysclk.clk, | ||
172 | .ctrlbit = S3C2443_SCLKCON_USBHOST, | ||
173 | .enable = s3c2443_clkcon_enable_s, | ||
174 | }, | ||
175 | .reg_div = { .reg = S3C2443_CLKDIV1, .size = 2, .shift = 4 }, | ||
176 | }; | ||
177 | |||
178 | /* common clksrc clocks */ | ||
179 | |||
180 | static struct clksrc_clk clksrc_clks[] = { | ||
181 | { | ||
182 | /* ART baud-rate clock sourced from esysclk via a divisor */ | ||
183 | .clk = { | ||
184 | .name = "uartclk", | ||
185 | .parent = &clk_esysclk.clk, | ||
186 | }, | ||
187 | .reg_div = { .reg = S3C2443_CLKDIV1, .size = 4, .shift = 8 }, | ||
188 | }, { | ||
189 | /* camera interface bus-clock, divided down from esysclk */ | ||
190 | .clk = { | ||
191 | .name = "camif-upll", /* same as 2440 name */ | ||
192 | .parent = &clk_esysclk.clk, | ||
193 | .ctrlbit = S3C2443_SCLKCON_CAMCLK, | ||
194 | .enable = s3c2443_clkcon_enable_s, | ||
195 | }, | ||
196 | .reg_div = { .reg = S3C2443_CLKDIV1, .size = 4, .shift = 26 }, | ||
197 | }, { | ||
198 | .clk = { | ||
199 | .name = "display-if", | ||
200 | .parent = &clk_esysclk.clk, | ||
201 | .ctrlbit = S3C2443_SCLKCON_DISPCLK, | ||
202 | .enable = s3c2443_clkcon_enable_s, | ||
203 | }, | ||
204 | .reg_div = { .reg = S3C2443_CLKDIV1, .size = 8, .shift = 16 }, | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | |||
209 | static struct clk init_clocks_off[] = { | ||
210 | { | ||
211 | .name = "adc", | ||
212 | .parent = &clk_p, | ||
213 | .enable = s3c2443_clkcon_enable_p, | ||
214 | .ctrlbit = S3C2443_PCLKCON_ADC, | ||
215 | }, { | ||
216 | .name = "i2c", | ||
217 | .parent = &clk_p, | ||
218 | .enable = s3c2443_clkcon_enable_p, | ||
219 | .ctrlbit = S3C2443_PCLKCON_IIC, | ||
220 | } | ||
221 | }; | ||
222 | |||
223 | static struct clk init_clocks[] = { | ||
224 | { | ||
225 | .name = "dma", | ||
226 | .parent = &clk_h, | ||
227 | .enable = s3c2443_clkcon_enable_h, | ||
228 | .ctrlbit = S3C2443_HCLKCON_DMA0, | ||
229 | }, { | ||
230 | .name = "dma", | ||
231 | .parent = &clk_h, | ||
232 | .enable = s3c2443_clkcon_enable_h, | ||
233 | .ctrlbit = S3C2443_HCLKCON_DMA1, | ||
234 | }, { | ||
235 | .name = "dma", | ||
236 | .parent = &clk_h, | ||
237 | .enable = s3c2443_clkcon_enable_h, | ||
238 | .ctrlbit = S3C2443_HCLKCON_DMA2, | ||
239 | }, { | ||
240 | .name = "dma", | ||
241 | .parent = &clk_h, | ||
242 | .enable = s3c2443_clkcon_enable_h, | ||
243 | .ctrlbit = S3C2443_HCLKCON_DMA3, | ||
244 | }, { | ||
245 | .name = "dma", | ||
246 | .parent = &clk_h, | ||
247 | .enable = s3c2443_clkcon_enable_h, | ||
248 | .ctrlbit = S3C2443_HCLKCON_DMA4, | ||
249 | }, { | ||
250 | .name = "dma", | ||
251 | .parent = &clk_h, | ||
252 | .enable = s3c2443_clkcon_enable_h, | ||
253 | .ctrlbit = S3C2443_HCLKCON_DMA5, | ||
254 | }, { | ||
255 | .name = "hsmmc", | ||
256 | .parent = &clk_h, | ||
257 | .enable = s3c2443_clkcon_enable_h, | ||
258 | .ctrlbit = S3C2443_HCLKCON_HSMMC, | ||
259 | }, { | ||
260 | .name = "gpio", | ||
261 | .parent = &clk_p, | ||
262 | .enable = s3c2443_clkcon_enable_p, | ||
263 | .ctrlbit = S3C2443_PCLKCON_GPIO, | ||
264 | }, { | ||
265 | .name = "usb-host", | ||
266 | .parent = &clk_h, | ||
267 | .enable = s3c2443_clkcon_enable_h, | ||
268 | .ctrlbit = S3C2443_HCLKCON_USBH, | ||
269 | }, { | ||
270 | .name = "usb-device", | ||
271 | .parent = &clk_h, | ||
272 | .enable = s3c2443_clkcon_enable_h, | ||
273 | .ctrlbit = S3C2443_HCLKCON_USBD, | ||
274 | }, { | ||
275 | .name = "lcd", | ||
276 | .parent = &clk_h, | ||
277 | .enable = s3c2443_clkcon_enable_h, | ||
278 | .ctrlbit = S3C2443_HCLKCON_LCDC, | ||
279 | |||
280 | }, { | ||
281 | .name = "timers", | ||
282 | .parent = &clk_p, | ||
283 | .enable = s3c2443_clkcon_enable_p, | ||
284 | .ctrlbit = S3C2443_PCLKCON_PWMT, | ||
285 | }, { | ||
286 | .name = "cfc", | ||
287 | .parent = &clk_h, | ||
288 | .enable = s3c2443_clkcon_enable_h, | ||
289 | .ctrlbit = S3C2443_HCLKCON_CFC, | ||
290 | }, { | ||
291 | .name = "ssmc", | ||
292 | .parent = &clk_h, | ||
293 | .enable = s3c2443_clkcon_enable_h, | ||
294 | .ctrlbit = S3C2443_HCLKCON_SSMC, | ||
295 | }, { | ||
296 | .name = "uart", | ||
297 | .devname = "s3c2440-uart.0", | ||
298 | .parent = &clk_p, | ||
299 | .enable = s3c2443_clkcon_enable_p, | ||
300 | .ctrlbit = S3C2443_PCLKCON_UART0, | ||
301 | }, { | ||
302 | .name = "uart", | ||
303 | .devname = "s3c2440-uart.1", | ||
304 | .parent = &clk_p, | ||
305 | .enable = s3c2443_clkcon_enable_p, | ||
306 | .ctrlbit = S3C2443_PCLKCON_UART1, | ||
307 | }, { | ||
308 | .name = "uart", | ||
309 | .devname = "s3c2440-uart.2", | ||
310 | .parent = &clk_p, | ||
311 | .enable = s3c2443_clkcon_enable_p, | ||
312 | .ctrlbit = S3C2443_PCLKCON_UART2, | ||
313 | }, { | ||
314 | .name = "uart", | ||
315 | .devname = "s3c2440-uart.3", | ||
316 | .parent = &clk_p, | ||
317 | .enable = s3c2443_clkcon_enable_p, | ||
318 | .ctrlbit = S3C2443_PCLKCON_UART3, | ||
319 | }, { | ||
320 | .name = "rtc", | ||
321 | .parent = &clk_p, | ||
322 | .enable = s3c2443_clkcon_enable_p, | ||
323 | .ctrlbit = S3C2443_PCLKCON_RTC, | ||
324 | }, { | ||
325 | .name = "watchdog", | ||
326 | .parent = &clk_p, | ||
327 | .ctrlbit = S3C2443_PCLKCON_WDT, | ||
328 | }, { | ||
329 | .name = "ac97", | ||
330 | .parent = &clk_p, | ||
331 | .ctrlbit = S3C2443_PCLKCON_AC97, | ||
332 | }, { | ||
333 | .name = "nand", | ||
334 | .parent = &clk_h, | ||
335 | }, { | ||
336 | .name = "usb-bus-host", | ||
337 | .parent = &clk_usb_bus_host.clk, | ||
338 | } | ||
339 | }; | ||
340 | |||
341 | static inline unsigned long s3c2443_get_hdiv(unsigned long clkcon0) | ||
342 | { | ||
343 | clkcon0 &= S3C2443_CLKDIV0_HCLKDIV_MASK; | ||
344 | |||
345 | return clkcon0 + 1; | ||
346 | } | ||
347 | |||
348 | /* EPLLCON compatible enough to get on/off information */ | ||
349 | |||
350 | void __init_or_cpufreq s3c2443_common_setup_clocks(pll_fn get_mpll, | ||
351 | fdiv_fn get_fdiv) | ||
352 | { | ||
353 | unsigned long epllcon = __raw_readl(S3C2443_EPLLCON); | ||
354 | unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON); | ||
355 | unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0); | ||
356 | struct clk *xtal_clk; | ||
357 | unsigned long xtal; | ||
358 | unsigned long pll; | ||
359 | unsigned long fclk; | ||
360 | unsigned long hclk; | ||
361 | unsigned long pclk; | ||
362 | int ptr; | ||
363 | |||
364 | xtal_clk = clk_get(NULL, "xtal"); | ||
365 | xtal = clk_get_rate(xtal_clk); | ||
366 | clk_put(xtal_clk); | ||
367 | |||
368 | pll = get_mpll(mpllcon, xtal); | ||
369 | clk_msysclk.clk.rate = pll; | ||
370 | |||
371 | fclk = pll / get_fdiv(clkdiv0); | ||
372 | hclk = s3c2443_prediv_getrate(&clk_prediv); | ||
373 | hclk /= s3c2443_get_hdiv(clkdiv0); | ||
374 | pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1); | ||
375 | |||
376 | s3c24xx_setup_clocks(fclk, hclk, pclk); | ||
377 | |||
378 | printk("CPU: MPLL %s %ld.%03ld MHz, cpu %ld.%03ld MHz, mem %ld.%03ld MHz, pclk %ld.%03ld MHz\n", | ||
379 | (mpllcon & S3C2443_PLLCON_OFF) ? "off":"on", | ||
380 | print_mhz(pll), print_mhz(fclk), | ||
381 | print_mhz(hclk), print_mhz(pclk)); | ||
382 | |||
383 | for (ptr = 0; ptr < ARRAY_SIZE(clksrc_clks); ptr++) | ||
384 | s3c_set_clksrc(&clksrc_clks[ptr], true); | ||
385 | |||
386 | /* ensure usb bus clock is within correct rate of 48MHz */ | ||
387 | |||
388 | if (clk_get_rate(&clk_usb_bus_host.clk) != (48 * 1000 * 1000)) { | ||
389 | printk(KERN_INFO "Warning: USB host bus not at 48MHz\n"); | ||
390 | clk_set_rate(&clk_usb_bus_host.clk, 48*1000*1000); | ||
391 | } | ||
392 | |||
393 | printk("CPU: EPLL %s %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n", | ||
394 | (epllcon & S3C2443_PLLCON_OFF) ? "off":"on", | ||
395 | print_mhz(clk_get_rate(&clk_epll)), | ||
396 | print_mhz(clk_get_rate(&clk_usb_bus))); | ||
397 | } | ||
398 | |||
399 | static struct clk *clks[] __initdata = { | ||
400 | &clk_prediv, | ||
401 | &clk_mpllref, | ||
402 | &clk_mdivclk, | ||
403 | &clk_ext, | ||
404 | &clk_epll, | ||
405 | &clk_usb_bus, | ||
406 | }; | ||
407 | |||
408 | static struct clksrc_clk *clksrcs[] __initdata = { | ||
409 | &clk_usb_bus_host, | ||
410 | &clk_epllref, | ||
411 | &clk_esysclk, | ||
412 | &clk_msysclk, | ||
413 | }; | ||
414 | |||
415 | void __init s3c2443_common_init_clocks(int xtal, pll_fn get_mpll, | ||
416 | fdiv_fn get_fdiv) | ||
417 | { | ||
418 | int ptr; | ||
419 | |||
420 | /* s3c2443 parents h and p clocks from prediv */ | ||
421 | clk_h.parent = &clk_prediv; | ||
422 | clk_p.parent = &clk_prediv; | ||
423 | |||
424 | clk_usb_bus.parent = &clk_usb_bus_host.clk; | ||
425 | clk_epll.parent = &clk_epllref.clk; | ||
426 | |||
427 | s3c24xx_register_baseclocks(xtal); | ||
428 | s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); | ||
429 | |||
430 | for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) | ||
431 | s3c_register_clksrc(clksrcs[ptr], 1); | ||
432 | |||
433 | s3c_register_clksrc(clksrc_clks, ARRAY_SIZE(clksrc_clks)); | ||
434 | s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); | ||
435 | |||
436 | /* See s3c2443/etc notes on disabling clocks at init time */ | ||
437 | s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); | ||
438 | s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); | ||
439 | |||
440 | s3c2443_common_setup_clocks(get_mpll, get_fdiv); | ||
441 | } | ||