aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2011-02-22 01:58:51 -0500
committerJohn Stultz <john.stultz@linaro.org>2011-03-09 14:22:50 -0500
commitf44f7f96a20af16f6f12e1c995576d6becf5f57b (patch)
treebbe0b8bec5defed39fade13c79a1cb19b74fb6e3
parenta5abba989deceb731047425812d268daf7536575 (diff)
RTC: Initialize kernel state from RTC
Mark Brown pointed out a corner case: that RTC alarms should be allowed to be persistent across reboots if the hardware supported it. The rework of the generic layer to virtualize the RTC alarm virtualized much of the alarm handling, and removed the code used to read the alarm time from the hardware. Mark noted if we want the alarm to be persistent across reboots, we need to re-read the alarm value into the virtualized generic layer at boot up, so that the generic layer properly exposes that value. This patch restores much of the earlier removed rtc_read_alarm code and wires it in so that we set the kernel's alarm value to what we find in the hardware at boot time. NOTE: Not all hardware supports persistent RTC alarm state across system reset. rtc-cmos for example will keep the alarm time, but disables the AIE mode irq. Applications should not expect the RTC alarm to be valid after a system reset. We will preserve what we can, to represent the hardware state at boot, but its not guarenteed. Further, in the future, with multiplexed RTC alarms, the soonest alarm to fire may not be the one set via the /dev/rt ioctls. So an application may set the alarm with RTC_ALM_SET, but after a reset find that RTC_ALM_READ returns an earlier time. Again, we preserve what we can, but applications should not expect the RTC alarm state to persist across a system reset. Big thanks to Mark for pointing out the issue! Thanks also to Marcelo for helping think through the solution. CC: Mark Brown <broonie@opensource.wolfsonmicro.com> CC: Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br> CC: Thomas Gleixner <tglx@linutronix.de> CC: Alessandro Zummo <a.zummo@towertech.it> CC: rtc-linux@googlegroups.com Reported-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: John Stultz <john.stultz@linaro.org>
-rw-r--r--drivers/rtc/class.c7
-rw-r--r--drivers/rtc/interface.c180
-rw-r--r--include/linux/rtc.h1
3 files changed, 188 insertions, 0 deletions
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index c404b61386bf..09b4437b3e61 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -117,6 +117,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
117 struct module *owner) 117 struct module *owner)
118{ 118{
119 struct rtc_device *rtc; 119 struct rtc_device *rtc;
120 struct rtc_wkalrm alrm;
120 int id, err; 121 int id, err;
121 122
122 if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { 123 if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
@@ -166,6 +167,12 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
166 rtc->pie_timer.function = rtc_pie_update_irq; 167 rtc->pie_timer.function = rtc_pie_update_irq;
167 rtc->pie_enabled = 0; 168 rtc->pie_enabled = 0;
168 169
170 /* Check to see if there is an ALARM already set in hw */
171 err = __rtc_read_alarm(rtc, &alrm);
172
173 if (!err && !rtc_valid_tm(&alrm.time))
174 rtc_set_alarm(rtc, &alrm);
175
169 strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); 176 strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
170 dev_set_name(&rtc->dev, "rtc%d", id); 177 dev_set_name(&rtc->dev, "rtc%d", id);
171 178
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index cb2f0728fd70..8ec6b069a7f5 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -116,6 +116,186 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
116} 116}
117EXPORT_SYMBOL_GPL(rtc_set_mmss); 117EXPORT_SYMBOL_GPL(rtc_set_mmss);
118 118
119static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
120{
121 int err;
122
123 err = mutex_lock_interruptible(&rtc->ops_lock);
124 if (err)
125 return err;
126
127 if (rtc->ops == NULL)
128 err = -ENODEV;
129 else if (!rtc->ops->read_alarm)
130 err = -EINVAL;
131 else {
132 memset(alarm, 0, sizeof(struct rtc_wkalrm));
133 err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
134 }
135
136 mutex_unlock(&rtc->ops_lock);
137 return err;
138}
139
140int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
141{
142 int err;
143 struct rtc_time before, now;
144 int first_time = 1;
145 unsigned long t_now, t_alm;
146 enum { none, day, month, year } missing = none;
147 unsigned days;
148
149 /* The lower level RTC driver may return -1 in some fields,
150 * creating invalid alarm->time values, for reasons like:
151 *
152 * - The hardware may not be capable of filling them in;
153 * many alarms match only on time-of-day fields, not
154 * day/month/year calendar data.
155 *
156 * - Some hardware uses illegal values as "wildcard" match
157 * values, which non-Linux firmware (like a BIOS) may try
158 * to set up as e.g. "alarm 15 minutes after each hour".
159 * Linux uses only oneshot alarms.
160 *
161 * When we see that here, we deal with it by using values from
162 * a current RTC timestamp for any missing (-1) values. The
163 * RTC driver prevents "periodic alarm" modes.
164 *
165 * But this can be racey, because some fields of the RTC timestamp
166 * may have wrapped in the interval since we read the RTC alarm,
167 * which would lead to us inserting inconsistent values in place
168 * of the -1 fields.
169 *
170 * Reading the alarm and timestamp in the reverse sequence
171 * would have the same race condition, and not solve the issue.
172 *
173 * So, we must first read the RTC timestamp,
174 * then read the RTC alarm value,
175 * and then read a second RTC timestamp.
176 *
177 * If any fields of the second timestamp have changed
178 * when compared with the first timestamp, then we know
179 * our timestamp may be inconsistent with that used by
180 * the low-level rtc_read_alarm_internal() function.
181 *
182 * So, when the two timestamps disagree, we just loop and do
183 * the process again to get a fully consistent set of values.
184 *
185 * This could all instead be done in the lower level driver,
186 * but since more than one lower level RTC implementation needs it,
187 * then it's probably best best to do it here instead of there..
188 */
189
190 /* Get the "before" timestamp */
191 err = rtc_read_time(rtc, &before);
192 if (err < 0)
193 return err;
194 do {
195 if (!first_time)
196 memcpy(&before, &now, sizeof(struct rtc_time));
197 first_time = 0;
198
199 /* get the RTC alarm values, which may be incomplete */
200 err = rtc_read_alarm_internal(rtc, alarm);
201 if (err)
202 return err;
203
204 /* full-function RTCs won't have such missing fields */
205 if (rtc_valid_tm(&alarm->time) == 0)
206 return 0;
207
208 /* get the "after" timestamp, to detect wrapped fields */
209 err = rtc_read_time(rtc, &now);
210 if (err < 0)
211 return err;
212
213 /* note that tm_sec is a "don't care" value here: */
214 } while ( before.tm_min != now.tm_min
215 || before.tm_hour != now.tm_hour
216 || before.tm_mon != now.tm_mon
217 || before.tm_year != now.tm_year);
218
219 /* Fill in the missing alarm fields using the timestamp; we
220 * know there's at least one since alarm->time is invalid.
221 */
222 if (alarm->time.tm_sec == -1)
223 alarm->time.tm_sec = now.tm_sec;
224 if (alarm->time.tm_min == -1)
225 alarm->time.tm_min = now.tm_min;
226 if (alarm->time.tm_hour == -1)
227 alarm->time.tm_hour = now.tm_hour;
228
229 /* For simplicity, only support date rollover for now */
230 if (alarm->time.tm_mday == -1) {
231 alarm->time.tm_mday = now.tm_mday;
232 missing = day;
233 }
234 if (alarm->time.tm_mon == -1) {
235 alarm->time.tm_mon = now.tm_mon;
236 if (missing == none)
237 missing = month;
238 }
239 if (alarm->time.tm_year == -1) {
240 alarm->time.tm_year = now.tm_year;
241 if (missing == none)
242 missing = year;
243 }
244
245 /* with luck, no rollover is needed */
246 rtc_tm_to_time(&now, &t_now);
247 rtc_tm_to_time(&alarm->time, &t_alm);
248 if (t_now < t_alm)
249 goto done;
250
251 switch (missing) {
252
253 /* 24 hour rollover ... if it's now 10am Monday, an alarm that
254 * that will trigger at 5am will do so at 5am Tuesday, which
255 * could also be in the next month or year. This is a common
256 * case, especially for PCs.
257 */
258 case day:
259 dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day");
260 t_alm += 24 * 60 * 60;
261 rtc_time_to_tm(t_alm, &alarm->time);
262 break;
263
264 /* Month rollover ... if it's the 31th, an alarm on the 3rd will
265 * be next month. An alarm matching on the 30th, 29th, or 28th
266 * may end up in the month after that! Many newer PCs support
267 * this type of alarm.
268 */
269 case month:
270 dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month");
271 do {
272 if (alarm->time.tm_mon < 11)
273 alarm->time.tm_mon++;
274 else {
275 alarm->time.tm_mon = 0;
276 alarm->time.tm_year++;
277 }
278 days = rtc_month_days(alarm->time.tm_mon,
279 alarm->time.tm_year);
280 } while (days < alarm->time.tm_mday);
281 break;
282
283 /* Year rollover ... easy except for leap years! */
284 case year:
285 dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year");
286 do {
287 alarm->time.tm_year++;
288 } while (rtc_valid_tm(&alarm->time) != 0);
289 break;
290
291 default:
292 dev_warn(&rtc->dev, "alarm rollover not handled\n");
293 }
294
295done:
296 return 0;
297}
298
119int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) 299int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
120{ 300{
121 int err; 301 int err;
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 89c3e5182991..db3832d5f280 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -227,6 +227,7 @@ extern void rtc_device_unregister(struct rtc_device *rtc);
227extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm); 227extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);
228extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); 228extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);
229extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs); 229extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs);
230int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);
230extern int rtc_read_alarm(struct rtc_device *rtc, 231extern int rtc_read_alarm(struct rtc_device *rtc,
231 struct rtc_wkalrm *alrm); 232 struct rtc_wkalrm *alrm);
232extern int rtc_set_alarm(struct rtc_device *rtc, 233extern int rtc_set_alarm(struct rtc_device *rtc,