diff options
Diffstat (limited to 'drivers/rtc/rtc-s3c.c')
| -rw-r--r-- | drivers/rtc/rtc-s3c.c | 107 |
1 files changed, 87 insertions, 20 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4969b6059c89..e5972b2c17b7 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c | |||
| @@ -29,6 +29,11 @@ | |||
| 29 | #include <asm/irq.h> | 29 | #include <asm/irq.h> |
| 30 | #include <plat/regs-rtc.h> | 30 | #include <plat/regs-rtc.h> |
| 31 | 31 | ||
| 32 | enum s3c_cpu_type { | ||
| 33 | TYPE_S3C2410, | ||
| 34 | TYPE_S3C64XX, | ||
| 35 | }; | ||
| 36 | |||
| 32 | /* 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 |
| 33 | * of these rtc blocks in */ | 38 | * of these rtc blocks in */ |
| 34 | 39 | ||
| @@ -37,6 +42,7 @@ static struct resource *s3c_rtc_mem; | |||
| 37 | static void __iomem *s3c_rtc_base; | 42 | static void __iomem *s3c_rtc_base; |
| 38 | static int s3c_rtc_alarmno = NO_IRQ; | 43 | static int s3c_rtc_alarmno = NO_IRQ; |
| 39 | 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; | ||
| 40 | 46 | ||
| 41 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); | 47 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); |
| 42 | 48 | ||
| @@ -80,12 +86,25 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) | |||
| 80 | pr_debug("%s: pie=%d\n", __func__, enabled); | 86 | pr_debug("%s: pie=%d\n", __func__, enabled); |
| 81 | 87 | ||
| 82 | spin_lock_irq(&s3c_rtc_pie_lock); | 88 | spin_lock_irq(&s3c_rtc_pie_lock); |
| 83 | tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; | ||
| 84 | 89 | ||
| 85 | if (enabled) | 90 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { |
| 86 | 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 | } | ||
| 87 | 107 | ||
| 88 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | ||
| 89 | spin_unlock_irq(&s3c_rtc_pie_lock); | 108 | spin_unlock_irq(&s3c_rtc_pie_lock); |
| 90 | 109 | ||
| 91 | return 0; | 110 | return 0; |
| @@ -93,15 +112,21 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) | |||
| 93 | 112 | ||
| 94 | static int s3c_rtc_setfreq(struct device *dev, int freq) | 113 | static int s3c_rtc_setfreq(struct device *dev, int freq) |
| 95 | { | 114 | { |
| 96 | 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; | ||
| 97 | 118 | ||
| 98 | if (!is_power_of_2(freq)) | 119 | if (!is_power_of_2(freq)) |
| 99 | return -EINVAL; | 120 | return -EINVAL; |
| 100 | 121 | ||
| 101 | spin_lock_irq(&s3c_rtc_pie_lock); | 122 | spin_lock_irq(&s3c_rtc_pie_lock); |
| 102 | 123 | ||
| 103 | tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; | 124 | if (s3c_rtc_cpu_type == TYPE_S3C2410) { |
| 104 | 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; | ||
| 105 | 130 | ||
| 106 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | 131 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); |
| 107 | spin_unlock_irq(&s3c_rtc_pie_lock); | 132 | spin_unlock_irq(&s3c_rtc_pie_lock); |
| @@ -283,10 +308,17 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
| 283 | 308 | ||
| 284 | 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) |
| 285 | { | 310 | { |
| 286 | unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT); | 311 | unsigned int ticnt; |
| 287 | 312 | ||
| 288 | seq_printf(seq, "periodic_IRQ\t: %s\n", | 313 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { |
| 289 | (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); | 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 | } | ||
| 320 | |||
| 321 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
| 290 | return 0; | 322 | return 0; |
| 291 | } | 323 | } |
| 292 | 324 | ||
| @@ -353,10 +385,16 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) | |||
| 353 | 385 | ||
| 354 | if (!en) { | 386 | if (!en) { |
| 355 | tmp = readb(base + S3C2410_RTCCON); | 387 | tmp = readb(base + S3C2410_RTCCON); |
| 356 | writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); | 388 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) |
| 357 | 389 | tmp &= ~S3C64XX_RTCCON_TICEN; | |
| 358 | tmp = readb(base + S3C2410_TICNT); | 390 | tmp &= ~S3C2410_RTCCON_RTCEN; |
| 359 | 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 | } | ||
| 360 | } else { | 398 | } else { |
| 361 | /* re-enable the device, and check it is ok */ | 399 | /* re-enable the device, and check it is ok */ |
| 362 | 400 | ||
| @@ -472,7 +510,12 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
| 472 | goto err_nortc; | 510 | goto err_nortc; |
| 473 | } | 511 | } |
| 474 | 512 | ||
| 475 | rtc->max_user_freq = 128; | 513 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) |
| 514 | rtc->max_user_freq = 32768; | ||
| 515 | else | ||
| 516 | rtc->max_user_freq = 128; | ||
| 517 | |||
| 518 | s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; | ||
| 476 | 519 | ||
| 477 | platform_set_drvdata(pdev, rtc); | 520 | platform_set_drvdata(pdev, rtc); |
| 478 | return 0; | 521 | return 0; |
| @@ -492,20 +535,30 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
| 492 | 535 | ||
| 493 | /* RTC Power management control */ | 536 | /* RTC Power management control */ |
| 494 | 537 | ||
| 495 | static int ticnt_save; | 538 | static int ticnt_save, ticnt_en_save; |
| 496 | 539 | ||
| 497 | static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 540 | static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) |
| 498 | { | 541 | { |
| 499 | /* save TICNT for anyone using periodic interrupts */ | 542 | /* save TICNT for anyone using periodic interrupts */ |
| 500 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); | 543 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); |
| 544 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | ||
| 545 | ticnt_en_save = readb(s3c_rtc_base + S3C2410_RTCCON); | ||
| 546 | ticnt_en_save &= S3C64XX_RTCCON_TICEN; | ||
| 547 | } | ||
| 501 | s3c_rtc_enable(pdev, 0); | 548 | s3c_rtc_enable(pdev, 0); |
| 502 | return 0; | 549 | return 0; |
| 503 | } | 550 | } |
| 504 | 551 | ||
| 505 | static int s3c_rtc_resume(struct platform_device *pdev) | 552 | static int s3c_rtc_resume(struct platform_device *pdev) |
| 506 | { | 553 | { |
| 554 | unsigned int tmp; | ||
| 555 | |||
| 507 | s3c_rtc_enable(pdev, 1); | 556 | s3c_rtc_enable(pdev, 1); |
| 508 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); | 557 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); |
| 558 | if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { | ||
| 559 | tmp = readb(s3c_rtc_base + S3C2410_RTCCON); | ||
| 560 | writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); | ||
| 561 | } | ||
| 509 | return 0; | 562 | return 0; |
| 510 | } | 563 | } |
| 511 | #else | 564 | #else |
| @@ -513,13 +566,27 @@ static int s3c_rtc_resume(struct platform_device *pdev) | |||
| 513 | #define s3c_rtc_resume NULL | 566 | #define s3c_rtc_resume NULL |
| 514 | #endif | 567 | #endif |
| 515 | 568 | ||
| 516 | static struct platform_driver s3c2410_rtc_driver = { | 569 | static struct platform_device_id s3c_rtc_driver_ids[] = { |
| 570 | { | ||
| 571 | .name = "s3c2410-rtc", | ||
| 572 | .driver_data = TYPE_S3C2410, | ||
| 573 | }, { | ||
| 574 | .name = "s3c64xx-rtc", | ||
| 575 | .driver_data = TYPE_S3C64XX, | ||
| 576 | }, | ||
| 577 | { } | ||
| 578 | }; | ||
| 579 | |||
| 580 | MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); | ||
| 581 | |||
| 582 | static struct platform_driver s3c_rtc_driver = { | ||
| 517 | .probe = s3c_rtc_probe, | 583 | .probe = s3c_rtc_probe, |
| 518 | .remove = __devexit_p(s3c_rtc_remove), | 584 | .remove = __devexit_p(s3c_rtc_remove), |
| 519 | .suspend = s3c_rtc_suspend, | 585 | .suspend = s3c_rtc_suspend, |
| 520 | .resume = s3c_rtc_resume, | 586 | .resume = s3c_rtc_resume, |
| 587 | .id_table = s3c_rtc_driver_ids, | ||
| 521 | .driver = { | 588 | .driver = { |
| 522 | .name = "s3c2410-rtc", | 589 | .name = "s3c-rtc", |
| 523 | .owner = THIS_MODULE, | 590 | .owner = THIS_MODULE, |
| 524 | }, | 591 | }, |
| 525 | }; | 592 | }; |
| @@ -529,12 +596,12 @@ static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics | |||
| 529 | static int __init s3c_rtc_init(void) | 596 | static int __init s3c_rtc_init(void) |
| 530 | { | 597 | { |
| 531 | printk(banner); | 598 | printk(banner); |
| 532 | return platform_driver_register(&s3c2410_rtc_driver); | 599 | return platform_driver_register(&s3c_rtc_driver); |
| 533 | } | 600 | } |
| 534 | 601 | ||
| 535 | static void __exit s3c_rtc_exit(void) | 602 | static void __exit s3c_rtc_exit(void) |
| 536 | { | 603 | { |
| 537 | platform_driver_unregister(&s3c2410_rtc_driver); | 604 | platform_driver_unregister(&s3c_rtc_driver); |
| 538 | } | 605 | } |
| 539 | 606 | ||
| 540 | module_init(s3c_rtc_init); | 607 | module_init(s3c_rtc_init); |
