diff options
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; |