diff options
Diffstat (limited to 'arch/arm/mach-s3c2410/clock.c')
-rw-r--r-- | arch/arm/mach-s3c2410/clock.c | 178 |
1 files changed, 174 insertions, 4 deletions
diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index 08489efdaf06..fec02c92f95f 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c | |||
@@ -38,12 +38,14 @@ | |||
38 | #include <linux/ioport.h> | 38 | #include <linux/ioport.h> |
39 | #include <linux/clk.h> | 39 | #include <linux/clk.h> |
40 | #include <linux/mutex.h> | 40 | #include <linux/mutex.h> |
41 | #include <linux/delay.h> | ||
41 | 42 | ||
42 | #include <asm/hardware.h> | 43 | #include <asm/hardware.h> |
43 | #include <asm/irq.h> | 44 | #include <asm/irq.h> |
44 | #include <asm/io.h> | 45 | #include <asm/io.h> |
45 | 46 | ||
46 | #include <asm/arch/regs-clock.h> | 47 | #include <asm/arch/regs-clock.h> |
48 | #include <asm/arch/regs-gpio.h> | ||
47 | 49 | ||
48 | #include "clock.h" | 50 | #include "clock.h" |
49 | #include "cpu.h" | 51 | #include "cpu.h" |
@@ -51,7 +53,8 @@ | |||
51 | /* clock information */ | 53 | /* clock information */ |
52 | 54 | ||
53 | static LIST_HEAD(clocks); | 55 | static LIST_HEAD(clocks); |
54 | static DEFINE_MUTEX(clocks_mutex); | 56 | |
57 | DEFINE_MUTEX(clocks_mutex); | ||
55 | 58 | ||
56 | /* old functions */ | 59 | /* old functions */ |
57 | 60 | ||
@@ -178,12 +181,24 @@ unsigned long clk_get_rate(struct clk *clk) | |||
178 | 181 | ||
179 | long clk_round_rate(struct clk *clk, unsigned long rate) | 182 | long clk_round_rate(struct clk *clk, unsigned long rate) |
180 | { | 183 | { |
184 | if (!IS_ERR(clk) && clk->round_rate) | ||
185 | return (clk->round_rate)(clk, rate); | ||
186 | |||
181 | return rate; | 187 | return rate; |
182 | } | 188 | } |
183 | 189 | ||
184 | int clk_set_rate(struct clk *clk, unsigned long rate) | 190 | int clk_set_rate(struct clk *clk, unsigned long rate) |
185 | { | 191 | { |
186 | return -EINVAL; | 192 | int ret; |
193 | |||
194 | if (IS_ERR(clk)) | ||
195 | return -EINVAL; | ||
196 | |||
197 | mutex_lock(&clocks_mutex); | ||
198 | ret = (clk->set_rate)(clk, rate); | ||
199 | mutex_unlock(&clocks_mutex); | ||
200 | |||
201 | return ret; | ||
187 | } | 202 | } |
188 | 203 | ||
189 | struct clk *clk_get_parent(struct clk *clk) | 204 | struct clk *clk_get_parent(struct clk *clk) |
@@ -191,6 +206,23 @@ struct clk *clk_get_parent(struct clk *clk) | |||
191 | return clk->parent; | 206 | return clk->parent; |
192 | } | 207 | } |
193 | 208 | ||
209 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
210 | { | ||
211 | int ret = 0; | ||
212 | |||
213 | if (IS_ERR(clk)) | ||
214 | return -EINVAL; | ||
215 | |||
216 | mutex_lock(&clocks_mutex); | ||
217 | |||
218 | if (clk->set_parent) | ||
219 | ret = (clk->set_parent)(clk, parent); | ||
220 | |||
221 | mutex_unlock(&clocks_mutex); | ||
222 | |||
223 | return ret; | ||
224 | } | ||
225 | |||
194 | EXPORT_SYMBOL(clk_get); | 226 | EXPORT_SYMBOL(clk_get); |
195 | EXPORT_SYMBOL(clk_put); | 227 | EXPORT_SYMBOL(clk_put); |
196 | EXPORT_SYMBOL(clk_enable); | 228 | EXPORT_SYMBOL(clk_enable); |
@@ -199,6 +231,29 @@ EXPORT_SYMBOL(clk_get_rate); | |||
199 | EXPORT_SYMBOL(clk_round_rate); | 231 | EXPORT_SYMBOL(clk_round_rate); |
200 | EXPORT_SYMBOL(clk_set_rate); | 232 | EXPORT_SYMBOL(clk_set_rate); |
201 | EXPORT_SYMBOL(clk_get_parent); | 233 | EXPORT_SYMBOL(clk_get_parent); |
234 | EXPORT_SYMBOL(clk_set_parent); | ||
235 | |||
236 | /* base clock enable */ | ||
237 | |||
238 | static int s3c24xx_upll_enable(struct clk *clk, int enable) | ||
239 | { | ||
240 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); | ||
241 | unsigned long orig = clkslow; | ||
242 | |||
243 | if (enable) | ||
244 | clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; | ||
245 | else | ||
246 | clkslow |= S3C2410_CLKSLOW_UCLK_OFF; | ||
247 | |||
248 | __raw_writel(clkslow, S3C2410_CLKSLOW); | ||
249 | |||
250 | /* if we started the UPLL, then allow to settle */ | ||
251 | |||
252 | if (enable && !(orig & S3C2410_CLKSLOW_UCLK_OFF)) | ||
253 | udelay(200); | ||
254 | |||
255 | return 0; | ||
256 | } | ||
202 | 257 | ||
203 | /* base clocks */ | 258 | /* base clocks */ |
204 | 259 | ||
@@ -210,6 +265,14 @@ static struct clk clk_xtal = { | |||
210 | .ctrlbit = 0, | 265 | .ctrlbit = 0, |
211 | }; | 266 | }; |
212 | 267 | ||
268 | static struct clk clk_upll = { | ||
269 | .name = "upll", | ||
270 | .id = -1, | ||
271 | .parent = NULL, | ||
272 | .enable = s3c24xx_upll_enable, | ||
273 | .ctrlbit = 0, | ||
274 | }; | ||
275 | |||
213 | static struct clk clk_f = { | 276 | static struct clk clk_f = { |
214 | .name = "fclk", | 277 | .name = "fclk", |
215 | .id = -1, | 278 | .id = -1, |
@@ -234,26 +297,124 @@ static struct clk clk_p = { | |||
234 | .ctrlbit = 0, | 297 | .ctrlbit = 0, |
235 | }; | 298 | }; |
236 | 299 | ||
300 | struct clk clk_usb_bus = { | ||
301 | .name = "usb-bus", | ||
302 | .id = -1, | ||
303 | .rate = 0, | ||
304 | .parent = &clk_upll, | ||
305 | }; | ||
306 | |||
237 | /* clocks that could be registered by external code */ | 307 | /* clocks that could be registered by external code */ |
238 | 308 | ||
309 | static int s3c24xx_dclk_enable(struct clk *clk, int enable) | ||
310 | { | ||
311 | unsigned long dclkcon = __raw_readl(S3C2410_DCLKCON); | ||
312 | |||
313 | if (enable) | ||
314 | dclkcon |= clk->ctrlbit; | ||
315 | else | ||
316 | dclkcon &= ~clk->ctrlbit; | ||
317 | |||
318 | __raw_writel(dclkcon, S3C2410_DCLKCON); | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) | ||
324 | { | ||
325 | unsigned long dclkcon; | ||
326 | unsigned int uclk; | ||
327 | |||
328 | if (parent == &clk_upll) | ||
329 | uclk = 1; | ||
330 | else if (parent == &clk_p) | ||
331 | uclk = 0; | ||
332 | else | ||
333 | return -EINVAL; | ||
334 | |||
335 | clk->parent = parent; | ||
336 | |||
337 | dclkcon = __raw_readl(S3C2410_DCLKCON); | ||
338 | |||
339 | if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { | ||
340 | if (uclk) | ||
341 | dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; | ||
342 | else | ||
343 | dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; | ||
344 | } else { | ||
345 | if (uclk) | ||
346 | dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; | ||
347 | else | ||
348 | dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; | ||
349 | } | ||
350 | |||
351 | __raw_writel(dclkcon, S3C2410_DCLKCON); | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | |||
357 | static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) | ||
358 | { | ||
359 | unsigned long mask; | ||
360 | unsigned long source; | ||
361 | |||
362 | /* calculate the MISCCR setting for the clock */ | ||
363 | |||
364 | if (parent == &clk_xtal) | ||
365 | source = S3C2410_MISCCR_CLK0_MPLL; | ||
366 | else if (parent == &clk_upll) | ||
367 | source = S3C2410_MISCCR_CLK0_UPLL; | ||
368 | else if (parent == &clk_f) | ||
369 | source = S3C2410_MISCCR_CLK0_FCLK; | ||
370 | else if (parent == &clk_p) | ||
371 | source = S3C2410_MISCCR_CLK0_PCLK; | ||
372 | else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) | ||
373 | source = S3C2410_MISCCR_CLK0_DCLK0; | ||
374 | else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) | ||
375 | source = S3C2410_MISCCR_CLK0_DCLK0; | ||
376 | else | ||
377 | return -EINVAL; | ||
378 | |||
379 | if (clk == &s3c24xx_dclk0) | ||
380 | mask = S3C2410_MISCCR_CLK0_MASK; | ||
381 | else { | ||
382 | source <<= 4; | ||
383 | mask = S3C2410_MISCCR_CLK1_MASK; | ||
384 | } | ||
385 | |||
386 | s3c2410_modify_misccr(mask, source); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | /* external clock definitions */ | ||
391 | |||
239 | struct clk s3c24xx_dclk0 = { | 392 | struct clk s3c24xx_dclk0 = { |
240 | .name = "dclk0", | 393 | .name = "dclk0", |
241 | .id = -1, | 394 | .id = -1, |
395 | .ctrlbit = S3C2410_DCLKCON_DCLK0EN, | ||
396 | .enable = s3c24xx_dclk_enable, | ||
397 | .set_parent = s3c24xx_dclk_setparent, | ||
242 | }; | 398 | }; |
243 | 399 | ||
244 | struct clk s3c24xx_dclk1 = { | 400 | struct clk s3c24xx_dclk1 = { |
245 | .name = "dclk1", | 401 | .name = "dclk1", |
246 | .id = -1, | 402 | .id = -1, |
403 | .ctrlbit = S3C2410_DCLKCON_DCLK0EN, | ||
404 | .enable = s3c24xx_dclk_enable, | ||
405 | .set_parent = s3c24xx_dclk_setparent, | ||
247 | }; | 406 | }; |
248 | 407 | ||
249 | struct clk s3c24xx_clkout0 = { | 408 | struct clk s3c24xx_clkout0 = { |
250 | .name = "clkout0", | 409 | .name = "clkout0", |
251 | .id = -1, | 410 | .id = -1, |
411 | .set_parent = s3c24xx_clkout_setparent, | ||
252 | }; | 412 | }; |
253 | 413 | ||
254 | struct clk s3c24xx_clkout1 = { | 414 | struct clk s3c24xx_clkout1 = { |
255 | .name = "clkout1", | 415 | .name = "clkout1", |
256 | .id = -1, | 416 | .id = -1, |
417 | .set_parent = s3c24xx_clkout_setparent, | ||
257 | }; | 418 | }; |
258 | 419 | ||
259 | struct clk s3c24xx_uclk = { | 420 | struct clk s3c24xx_uclk = { |
@@ -262,7 +423,7 @@ struct clk s3c24xx_uclk = { | |||
262 | }; | 423 | }; |
263 | 424 | ||
264 | 425 | ||
265 | /* clock definitions */ | 426 | /* standard clock definitions */ |
266 | 427 | ||
267 | static struct clk init_clocks[] = { | 428 | static struct clk init_clocks[] = { |
268 | { | 429 | { |
@@ -374,7 +535,7 @@ int s3c24xx_register_clock(struct clk *clk) | |||
374 | 535 | ||
375 | /* if this is a standard clock, set the usage state */ | 536 | /* if this is a standard clock, set the usage state */ |
376 | 537 | ||
377 | if (clk->ctrlbit) { | 538 | if (clk->ctrlbit && clk->enable == s3c24xx_clkcon_enable) { |
378 | unsigned long clkcon = __raw_readl(S3C2410_CLKCON); | 539 | unsigned long clkcon = __raw_readl(S3C2410_CLKCON); |
379 | 540 | ||
380 | clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0; | 541 | clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0; |
@@ -396,6 +557,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
396 | unsigned long hclk, | 557 | unsigned long hclk, |
397 | unsigned long pclk) | 558 | unsigned long pclk) |
398 | { | 559 | { |
560 | unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); | ||
399 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); | 561 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); |
400 | struct clk *clkp = init_clocks; | 562 | struct clk *clkp = init_clocks; |
401 | int ptr; | 563 | int ptr; |
@@ -406,6 +568,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
406 | /* initialise the main system clocks */ | 568 | /* initialise the main system clocks */ |
407 | 569 | ||
408 | clk_xtal.rate = xtal; | 570 | clk_xtal.rate = xtal; |
571 | clk_upll.rate = s3c2410_get_pll(upllcon, xtal); | ||
409 | 572 | ||
410 | clk_h.rate = hclk; | 573 | clk_h.rate = hclk; |
411 | clk_p.rate = pclk; | 574 | clk_p.rate = pclk; |
@@ -439,6 +602,9 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
439 | if (s3c24xx_register_clock(&clk_xtal) < 0) | 602 | if (s3c24xx_register_clock(&clk_xtal) < 0) |
440 | printk(KERN_ERR "failed to register master xtal\n"); | 603 | printk(KERN_ERR "failed to register master xtal\n"); |
441 | 604 | ||
605 | if (s3c24xx_register_clock(&clk_upll) < 0) | ||
606 | printk(KERN_ERR "failed to register upll clock\n"); | ||
607 | |||
442 | if (s3c24xx_register_clock(&clk_f) < 0) | 608 | if (s3c24xx_register_clock(&clk_f) < 0) |
443 | printk(KERN_ERR "failed to register cpu fclk\n"); | 609 | printk(KERN_ERR "failed to register cpu fclk\n"); |
444 | 610 | ||
@@ -448,6 +614,10 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
448 | if (s3c24xx_register_clock(&clk_p) < 0) | 614 | if (s3c24xx_register_clock(&clk_p) < 0) |
449 | printk(KERN_ERR "failed to register cpu pclk\n"); | 615 | printk(KERN_ERR "failed to register cpu pclk\n"); |
450 | 616 | ||
617 | |||
618 | if (s3c24xx_register_clock(&clk_usb_bus) < 0) | ||
619 | printk(KERN_ERR "failed to register usb bus clock\n"); | ||
620 | |||
451 | /* register clocks from clock array */ | 621 | /* register clocks from clock array */ |
452 | 622 | ||
453 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { | 623 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { |