diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-08-06 13:13:54 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-08-06 13:13:54 -0400 |
commit | 11e4afb49b7fa1fc8e1ffd850c1806dd86a08204 (patch) | |
tree | 9e57efcb106ae912f7bec718feb3f8ec607559bb /drivers/rtc/rtc-s3c.c | |
parent | 162500b3a3ff39d941d29db49b41a16667ae44f0 (diff) | |
parent | 9b2a606d3898fcb2eedb6faded3bb37549590ac4 (diff) |
Merge branches 'gemini' and 'misc' into devel
Diffstat (limited to 'drivers/rtc/rtc-s3c.c')
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 113 |
1 files changed, 91 insertions, 22 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index e0d7b9991505..70b68d35f969 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/bcd.h> | 21 | #include <linux/bcd.h> |
22 | #include <linux/clk.h> | 22 | #include <linux/clk.h> |
23 | #include <linux/log2.h> | 23 | #include <linux/log2.h> |
24 | #include <linux/slab.h> | ||
24 | 25 | ||
25 | #include <mach/hardware.h> | 26 | #include <mach/hardware.h> |
26 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
@@ -28,6 +29,11 @@ | |||
28 | #include <asm/irq.h> | 29 | #include <asm/irq.h> |
29 | #include <plat/regs-rtc.h> | 30 | #include <plat/regs-rtc.h> |
30 | 31 | ||
32 | enum s3c_cpu_type { | ||
33 | TYPE_S3C2410, | ||
34 | TYPE_S3C64XX, | ||
35 | }; | ||
36 | |||
31 | /* I have yet to find an S3C implementation with more than one | 37 | /* I have yet to find an S3C implementation with more than one |
32 | * of these rtc blocks in */ | 38 | * of these rtc blocks in */ |
33 | 39 | ||
@@ -36,6 +42,7 @@ static struct resource *s3c_rtc_mem; | |||
36 | static void __iomem *s3c_rtc_base; | 42 | static void __iomem *s3c_rtc_base; |
37 | static int s3c_rtc_alarmno = NO_IRQ; | 43 | static int s3c_rtc_alarmno = NO_IRQ; |
38 | static int s3c_rtc_tickno = NO_IRQ; | 44 | static int s3c_rtc_tickno = NO_IRQ; |
45 | static enum s3c_cpu_type s3c_rtc_cpu_type; | ||
39 | 46 | ||
40 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); | 47 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); |
41 | 48 | ||
@@ -79,12 +86,25 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) | |||
79 | pr_debug("%s: pie=%d\n", __func__, enabled); | 86 | pr_debug("%s: pie=%d\n", __func__, enabled); |
80 | 87 | ||
81 | spin_lock_irq(&s3c_rtc_pie_lock); | 88 | spin_lock_irq(&s3c_rtc_pie_lock); |
82 | tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; | ||
83 | 89 | ||
84 | if (enabled) | 90 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { |
85 | tmp |= S3C2410_TICNT_ENABLE; | 91 | tmp = readb(s3c_rtc_base + S3C2410_RTCCON); |
92 | tmp &= ~S3C64XX_RTCCON_TICEN; | ||
93 | |||
94 | if (enabled) | ||
95 | tmp |= S3C64XX_RTCCON_TICEN; | ||
96 | |||
97 | writeb(tmp, s3c_rtc_base + S3C2410_RTCCON); | ||
98 | } else { | ||
99 | tmp = readb(s3c_rtc_base + S3C2410_TICNT); | ||
100 | tmp &= ~S3C2410_TICNT_ENABLE; | ||
101 | |||
102 | if (enabled) | ||
103 | tmp |= S3C2410_TICNT_ENABLE; | ||
104 | |||
105 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | ||
106 | } | ||
86 | 107 | ||
87 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | ||
88 | spin_unlock_irq(&s3c_rtc_pie_lock); | 108 | spin_unlock_irq(&s3c_rtc_pie_lock); |
89 | 109 | ||
90 | return 0; | 110 | return 0; |
@@ -92,15 +112,21 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) | |||
92 | 112 | ||
93 | static int s3c_rtc_setfreq(struct device *dev, int freq) | 113 | static int s3c_rtc_setfreq(struct device *dev, int freq) |
94 | { | 114 | { |
95 | unsigned int tmp; | 115 | struct platform_device *pdev = to_platform_device(dev); |
116 | struct rtc_device *rtc_dev = platform_get_drvdata(pdev); | ||
117 | unsigned int tmp = 0; | ||
96 | 118 | ||
97 | if (!is_power_of_2(freq)) | 119 | if (!is_power_of_2(freq)) |
98 | return -EINVAL; | 120 | return -EINVAL; |
99 | 121 | ||
100 | spin_lock_irq(&s3c_rtc_pie_lock); | 122 | spin_lock_irq(&s3c_rtc_pie_lock); |
101 | 123 | ||
102 | tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; | 124 | if (s3c_rtc_cpu_type == TYPE_S3C2410) { |
103 | tmp |= (128 / freq)-1; | 125 | tmp = readb(s3c_rtc_base + S3C2410_TICNT); |
126 | tmp &= S3C2410_TICNT_ENABLE; | ||
127 | } | ||
128 | |||
129 | tmp |= (rtc_dev->max_user_freq / freq)-1; | ||
104 | 130 | ||
105 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | 131 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); |
106 | spin_unlock_irq(&s3c_rtc_pie_lock); | 132 | spin_unlock_irq(&s3c_rtc_pie_lock); |
@@ -282,10 +308,17 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
282 | 308 | ||
283 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) | 309 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) |
284 | { | 310 | { |
285 | unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT); | 311 | unsigned int ticnt; |
312 | |||
313 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | ||
314 | ticnt = readb(s3c_rtc_base + S3C2410_RTCCON); | ||
315 | ticnt &= S3C64XX_RTCCON_TICEN; | ||
316 | } else { | ||
317 | ticnt = readb(s3c_rtc_base + S3C2410_TICNT); | ||
318 | ticnt &= S3C2410_TICNT_ENABLE; | ||
319 | } | ||
286 | 320 | ||
287 | seq_printf(seq, "periodic_IRQ\t: %s\n", | 321 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); |
288 | (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); | ||
289 | return 0; | 322 | return 0; |
290 | } | 323 | } |
291 | 324 | ||
@@ -352,10 +385,16 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) | |||
352 | 385 | ||
353 | if (!en) { | 386 | if (!en) { |
354 | tmp = readb(base + S3C2410_RTCCON); | 387 | tmp = readb(base + S3C2410_RTCCON); |
355 | writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); | 388 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) |
356 | 389 | tmp &= ~S3C64XX_RTCCON_TICEN; | |
357 | tmp = readb(base + S3C2410_TICNT); | 390 | tmp &= ~S3C2410_RTCCON_RTCEN; |
358 | writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); | 391 | writeb(tmp, base + S3C2410_RTCCON); |
392 | |||
393 | if (s3c_rtc_cpu_type == TYPE_S3C2410) { | ||
394 | tmp = readb(base + S3C2410_TICNT); | ||
395 | tmp &= ~S3C2410_TICNT_ENABLE; | ||
396 | writeb(tmp, base + S3C2410_TICNT); | ||
397 | } | ||
359 | } else { | 398 | } else { |
360 | /* re-enable the device, and check it is ok */ | 399 | /* re-enable the device, and check it is ok */ |
361 | 400 | ||
@@ -456,8 +495,6 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
456 | pr_debug("s3c2410_rtc: RTCCON=%02x\n", | 495 | pr_debug("s3c2410_rtc: RTCCON=%02x\n", |
457 | readb(s3c_rtc_base + S3C2410_RTCCON)); | 496 | readb(s3c_rtc_base + S3C2410_RTCCON)); |
458 | 497 | ||
459 | s3c_rtc_setfreq(&pdev->dev, 1); | ||
460 | |||
461 | device_init_wakeup(&pdev->dev, 1); | 498 | device_init_wakeup(&pdev->dev, 1); |
462 | 499 | ||
463 | /* register RTC and exit */ | 500 | /* register RTC and exit */ |
@@ -471,9 +508,17 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
471 | goto err_nortc; | 508 | goto err_nortc; |
472 | } | 509 | } |
473 | 510 | ||
474 | rtc->max_user_freq = 128; | 511 | s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; |
512 | |||
513 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
514 | rtc->max_user_freq = 32768; | ||
515 | else | ||
516 | rtc->max_user_freq = 128; | ||
475 | 517 | ||
476 | platform_set_drvdata(pdev, rtc); | 518 | platform_set_drvdata(pdev, rtc); |
519 | |||
520 | s3c_rtc_setfreq(&pdev->dev, 1); | ||
521 | |||
477 | return 0; | 522 | return 0; |
478 | 523 | ||
479 | err_nortc: | 524 | err_nortc: |
@@ -491,20 +536,30 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
491 | 536 | ||
492 | /* RTC Power management control */ | 537 | /* RTC Power management control */ |
493 | 538 | ||
494 | static int ticnt_save; | 539 | static int ticnt_save, ticnt_en_save; |
495 | 540 | ||
496 | static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 541 | static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) |
497 | { | 542 | { |
498 | /* save TICNT for anyone using periodic interrupts */ | 543 | /* save TICNT for anyone using periodic interrupts */ |
499 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); | 544 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); |
545 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | ||
546 | ticnt_en_save = readb(s3c_rtc_base + S3C2410_RTCCON); | ||
547 | ticnt_en_save &= S3C64XX_RTCCON_TICEN; | ||
548 | } | ||
500 | s3c_rtc_enable(pdev, 0); | 549 | s3c_rtc_enable(pdev, 0); |
501 | return 0; | 550 | return 0; |
502 | } | 551 | } |
503 | 552 | ||
504 | static int s3c_rtc_resume(struct platform_device *pdev) | 553 | static int s3c_rtc_resume(struct platform_device *pdev) |
505 | { | 554 | { |
555 | unsigned int tmp; | ||
556 | |||
506 | s3c_rtc_enable(pdev, 1); | 557 | s3c_rtc_enable(pdev, 1); |
507 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); | 558 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); |
559 | if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { | ||
560 | tmp = readb(s3c_rtc_base + S3C2410_RTCCON); | ||
561 | writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); | ||
562 | } | ||
508 | return 0; | 563 | return 0; |
509 | } | 564 | } |
510 | #else | 565 | #else |
@@ -512,13 +567,27 @@ static int s3c_rtc_resume(struct platform_device *pdev) | |||
512 | #define s3c_rtc_resume NULL | 567 | #define s3c_rtc_resume NULL |
513 | #endif | 568 | #endif |
514 | 569 | ||
515 | static struct platform_driver s3c2410_rtc_driver = { | 570 | static struct platform_device_id s3c_rtc_driver_ids[] = { |
571 | { | ||
572 | .name = "s3c2410-rtc", | ||
573 | .driver_data = TYPE_S3C2410, | ||
574 | }, { | ||
575 | .name = "s3c64xx-rtc", | ||
576 | .driver_data = TYPE_S3C64XX, | ||
577 | }, | ||
578 | { } | ||
579 | }; | ||
580 | |||
581 | MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); | ||
582 | |||
583 | static struct platform_driver s3c_rtc_driver = { | ||
516 | .probe = s3c_rtc_probe, | 584 | .probe = s3c_rtc_probe, |
517 | .remove = __devexit_p(s3c_rtc_remove), | 585 | .remove = __devexit_p(s3c_rtc_remove), |
518 | .suspend = s3c_rtc_suspend, | 586 | .suspend = s3c_rtc_suspend, |
519 | .resume = s3c_rtc_resume, | 587 | .resume = s3c_rtc_resume, |
588 | .id_table = s3c_rtc_driver_ids, | ||
520 | .driver = { | 589 | .driver = { |
521 | .name = "s3c2410-rtc", | 590 | .name = "s3c-rtc", |
522 | .owner = THIS_MODULE, | 591 | .owner = THIS_MODULE, |
523 | }, | 592 | }, |
524 | }; | 593 | }; |
@@ -528,12 +597,12 @@ static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics | |||
528 | static int __init s3c_rtc_init(void) | 597 | static int __init s3c_rtc_init(void) |
529 | { | 598 | { |
530 | printk(banner); | 599 | printk(banner); |
531 | return platform_driver_register(&s3c2410_rtc_driver); | 600 | return platform_driver_register(&s3c_rtc_driver); |
532 | } | 601 | } |
533 | 602 | ||
534 | static void __exit s3c_rtc_exit(void) | 603 | static void __exit s3c_rtc_exit(void) |
535 | { | 604 | { |
536 | platform_driver_unregister(&s3c2410_rtc_driver); | 605 | platform_driver_unregister(&s3c_rtc_driver); |
537 | } | 606 | } |
538 | 607 | ||
539 | module_init(s3c_rtc_init); | 608 | module_init(s3c_rtc_init); |