diff options
author | David Brownell <david-b@pacbell.net> | 2007-05-08 03:33:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:18 -0400 |
commit | 7ca1d488ffe4817adaba61cc05b972782f7d3f91 (patch) | |
tree | 97fee4d2ddbc5be5265d99f5825e902f7a9262c1 /drivers/rtc/class.c | |
parent | cd9662094edf4173e87f0452e57e4eacc228f8ff (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/rtc/class.c')
-rw-r--r-- | drivers/rtc/class.c | 74 |
1 files changed, 74 insertions, 0 deletions
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 | |||
42 | static struct timespec delta; | ||
43 | static time_t oldtime; | ||
44 | |||
45 | static 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 | |||
66 | static 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; |