aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-05-08 03:33:42 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:18 -0400
commit7ca1d488ffe4817adaba61cc05b972782f7d3f91 (patch)
tree97fee4d2ddbc5be5265d99f5825e902f7a9262c1 /drivers
parentcd9662094edf4173e87f0452e57e4eacc228f8ff (diff)
rtc: suspend()/resume() restores system clock
RTC class suspend/resume support, re-initializing the system clock on resume from the clock used to initialize it at boot time. - The reinit-on-resume is hooked to the existing RTC_HCTOSYS config option, on the grounds that a clock good enough for init must also be good enough for re-init. - Inlining a version of the code used by ARM, to save and restore the delta between a selected RTC and the current system wall-clock time. - Removes calls to that ARM code from AT91, OMAP1, and S3C RTCs. This means that systems using those RTCs across suspend/resume will likely want to change their kernel configs to enable RTC_HCTOSYS. If HCTOSYS isn't using a second RTC (with battery?), this changes the system's initial date from Jan 1970 to the epoch this hardware uses: 1998 for AT91, 2000 for OMAP1 (assuming no split power mode), etc. This goes on top of the patch series removing "struct class_device" usage from the RTC framework. That's all needed for class suspend()/resume(). Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Greg Kroah-Hartman <gregkh@suse.de> Acked-By: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/rtc/Kconfig24
-rw-r--r--drivers/rtc/class.c74
-rw-r--r--drivers/rtc/rtc-at91rm9200.c30
-rw-r--r--drivers/rtc/rtc-omap.c17
-rw-r--r--drivers/rtc/rtc-s3c.c22
5 files changed, 91 insertions, 76 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ec33ee87735e..a53ef4d670cc 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -21,21 +21,31 @@ config RTC_CLASS
21 will be called rtc-class. 21 will be called rtc-class.
22 22
23config RTC_HCTOSYS 23config RTC_HCTOSYS
24 bool "Set system time from RTC on startup" 24 bool "Set system time from RTC on startup and resume"
25 depends on RTC_CLASS = y 25 depends on RTC_CLASS = y
26 default y 26 default y
27 help 27 help
28 If you say yes here, the system time will be set using 28 If you say yes here, the system time (wall clock) will be set using
29 the value read from the specified RTC device. This is useful 29 the value read from a specified RTC device. This is useful to avoid
30 in order to avoid unnecessary fsck runs. 30 unnecessary fsck runs at boot time, and to network better.
31 31
32config RTC_HCTOSYS_DEVICE 32config RTC_HCTOSYS_DEVICE
33 string "The RTC to read the time from" 33 string "RTC used to set the system time"
34 depends on RTC_HCTOSYS = y 34 depends on RTC_HCTOSYS = y
35 default "rtc0" 35 default "rtc0"
36 help 36 help
37 The RTC device that will be used as the source for 37 The RTC device that will be used to (re)initialize the system
38 the system time, usually rtc0. 38 clock, usually rtc0. Initialization is done when the system
39 starts up, and when it resumes from a low power state.
40
41 This clock should be battery-backed, so that it reads the correct
42 time when the system boots from a power-off state. Otherwise, your
43 system will need an external clock source (like an NTP server).
44
45 If the clock you specify here is not battery backed, it may still
46 be useful to reinitialize system time when resuming from system
47 sleep states. Do not specify an RTC here unless it stays powered
48 during all this system's supported sleep states.
39 49
40config RTC_DEBUG 50config RTC_DEBUG
41 bool "RTC debug support" 51 bool "RTC debug support"
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index d58d74cf570e..587d251be454 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -32,6 +32,78 @@ static void rtc_device_release(struct device *dev)
32 kfree(rtc); 32 kfree(rtc);
33} 33}
34 34
35#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
36
37/*
38 * On suspend(), measure the delta between one RTC and the
39 * system's wall clock; restore it on resume().
40 */
41
42static struct timespec delta;
43static time_t oldtime;
44
45static int rtc_suspend(struct device *dev, pm_message_t mesg)
46{
47 struct rtc_device *rtc = to_rtc_device(dev);
48 struct rtc_time tm;
49
50 if (strncmp(rtc->dev.bus_id,
51 CONFIG_RTC_HCTOSYS_DEVICE,
52 BUS_ID_SIZE) != 0)
53 return 0;
54
55 rtc_read_time(rtc, &tm);
56 rtc_tm_to_time(&tm, &oldtime);
57
58 /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */
59 set_normalized_timespec(&delta,
60 xtime.tv_sec - oldtime,
61 xtime.tv_nsec - (NSEC_PER_SEC >> 1));
62
63 return 0;
64}
65
66static int rtc_resume(struct device *dev)
67{
68 struct rtc_device *rtc = to_rtc_device(dev);
69 struct rtc_time tm;
70 time_t newtime;
71 struct timespec time;
72
73 if (strncmp(rtc->dev.bus_id,
74 CONFIG_RTC_HCTOSYS_DEVICE,
75 BUS_ID_SIZE) != 0)
76 return 0;
77
78 rtc_read_time(rtc, &tm);
79 if (rtc_valid_tm(&tm) != 0) {
80 pr_debug("%s: bogus resume time\n", rtc->dev.bus_id);
81 return 0;
82 }
83 rtc_tm_to_time(&tm, &newtime);
84 if (newtime <= oldtime) {
85 if (newtime < oldtime)
86 pr_debug("%s: time travel!\n", rtc->dev.bus_id);
87 return 0;
88 }
89
90 /* restore wall clock using delta against this RTC;
91 * adjust again for avg 1/2 second RTC sampling error
92 */
93 set_normalized_timespec(&time,
94 newtime + delta.tv_sec,
95 (NSEC_PER_SEC >> 1) + delta.tv_nsec);
96 do_settimeofday(&time);
97
98 return 0;
99}
100
101#else
102#define rtc_suspend NULL
103#define rtc_resume NULL
104#endif
105
106
35/** 107/**
36 * rtc_device_register - register w/ RTC class 108 * rtc_device_register - register w/ RTC class
37 * @dev: the device to register 109 * @dev: the device to register
@@ -143,6 +215,8 @@ static int __init rtc_init(void)
143 printk(KERN_ERR "%s: couldn't create class\n", __FILE__); 215 printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
144 return PTR_ERR(rtc_class); 216 return PTR_ERR(rtc_class);
145 } 217 }
218 rtc_class->suspend = rtc_suspend;
219 rtc_class->resume = rtc_resume;
146 rtc_dev_init(); 220 rtc_dev_init();
147 rtc_sysfs_init(rtc_class); 221 rtc_sysfs_init(rtc_class);
148 return 0; 222 return 0;
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index 2f06b5f9fb7b..33795e5a5595 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -348,21 +348,10 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)
348 348
349/* AT91RM9200 RTC Power management control */ 349/* AT91RM9200 RTC Power management control */
350 350
351static struct timespec at91_rtc_delta;
352static u32 at91_rtc_imr; 351static u32 at91_rtc_imr;
353 352
354static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) 353static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
355{ 354{
356 struct rtc_time tm;
357 struct timespec time;
358
359 time.tv_nsec = 0;
360
361 /* calculate time delta for suspend */
362 at91_rtc_readtime(&pdev->dev, &tm);
363 rtc_tm_to_time(&tm, &time.tv_sec);
364 save_time_delta(&at91_rtc_delta, &time);
365
366 /* this IRQ is shared with DBGU and other hardware which isn't 355 /* this IRQ is shared with DBGU and other hardware which isn't
367 * necessarily doing PM like we are... 356 * necessarily doing PM like we are...
368 */ 357 */
@@ -374,36 +363,17 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
374 else 363 else
375 at91_sys_write(AT91_RTC_IDR, at91_rtc_imr); 364 at91_sys_write(AT91_RTC_IDR, at91_rtc_imr);
376 } 365 }
377
378 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
379 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
380 tm.tm_hour, tm.tm_min, tm.tm_sec);
381
382 return 0; 366 return 0;
383} 367}
384 368
385static int at91_rtc_resume(struct platform_device *pdev) 369static int at91_rtc_resume(struct platform_device *pdev)
386{ 370{
387 struct rtc_time tm;
388 struct timespec time;
389
390 time.tv_nsec = 0;
391
392 at91_rtc_readtime(&pdev->dev, &tm);
393 rtc_tm_to_time(&tm, &time.tv_sec);
394 restore_time_delta(&at91_rtc_delta, &time);
395
396 if (at91_rtc_imr) { 371 if (at91_rtc_imr) {
397 if (device_may_wakeup(&pdev->dev)) 372 if (device_may_wakeup(&pdev->dev))
398 disable_irq_wake(AT91_ID_SYS); 373 disable_irq_wake(AT91_ID_SYS);
399 else 374 else
400 at91_sys_write(AT91_RTC_IER, at91_rtc_imr); 375 at91_sys_write(AT91_RTC_IER, at91_rtc_imr);
401 } 376 }
402
403 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
404 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
405 tm.tm_hour, tm.tm_min, tm.tm_sec);
406
407 return 0; 377 return 0;
408} 378}
409#else 379#else
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index ded35fd13ba0..e6c7b0149f27 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -488,19 +488,10 @@ static int __devexit omap_rtc_remove(struct platform_device *pdev)
488 488
489#ifdef CONFIG_PM 489#ifdef CONFIG_PM
490 490
491static struct timespec rtc_delta;
492static u8 irqstat; 491static u8 irqstat;
493 492
494static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state) 493static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
495{ 494{
496 struct rtc_time rtc_tm;
497 struct timespec time;
498
499 time.tv_nsec = 0;
500 omap_rtc_read_time(NULL, &rtc_tm);
501 rtc_tm_to_time(&rtc_tm, &time.tv_sec);
502
503 save_time_delta(&rtc_delta, &time);
504 irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG); 495 irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG);
505 496
506 /* FIXME the RTC alarm is not currently acting as a wakeup event 497 /* FIXME the RTC alarm is not currently acting as a wakeup event
@@ -517,14 +508,6 @@ static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
517 508
518static int omap_rtc_resume(struct platform_device *pdev) 509static int omap_rtc_resume(struct platform_device *pdev)
519{ 510{
520 struct rtc_time rtc_tm;
521 struct timespec time;
522
523 time.tv_nsec = 0;
524 omap_rtc_read_time(NULL, &rtc_tm);
525 rtc_tm_to_time(&rtc_tm, &time.tv_sec);
526
527 restore_time_delta(&rtc_delta, &time);
528 if (device_may_wakeup(&pdev->dev)) 511 if (device_may_wakeup(&pdev->dev))
529 disable_irq_wake(omap_rtc_alarm); 512 disable_irq_wake(omap_rtc_alarm);
530 else 513 else
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 3617c970caaa..54b613053468 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -548,37 +548,15 @@ static int ticnt_save;
548 548
549static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) 549static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
550{ 550{
551 struct rtc_time tm;
552 struct timespec time;
553
554 time.tv_nsec = 0;
555
556 /* save TICNT for anyone using periodic interrupts */ 551 /* save TICNT for anyone using periodic interrupts */
557
558 ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); 552 ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
559
560 /* calculate time delta for suspend */
561
562 s3c_rtc_gettime(&pdev->dev, &tm);
563 rtc_tm_to_time(&tm, &time.tv_sec);
564 save_time_delta(&s3c_rtc_delta, &time);
565 s3c_rtc_enable(pdev, 0); 553 s3c_rtc_enable(pdev, 0);
566
567 return 0; 554 return 0;
568} 555}
569 556
570static int s3c_rtc_resume(struct platform_device *pdev) 557static int s3c_rtc_resume(struct platform_device *pdev)
571{ 558{
572 struct rtc_time tm;
573 struct timespec time;
574
575 time.tv_nsec = 0;
576
577 s3c_rtc_enable(pdev, 1); 559 s3c_rtc_enable(pdev, 1);
578 s3c_rtc_gettime(&pdev->dev, &tm);
579 rtc_tm_to_time(&tm, &time.tv_sec);
580 restore_time_delta(&s3c_rtc_delta, &time);
581
582 writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); 560 writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
583 return 0; 561 return 0;
584} 562}