diff options
Diffstat (limited to 'drivers/rtc/interface.c')
-rw-r--r-- | drivers/rtc/interface.c | 122 |
1 files changed, 118 insertions, 4 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index ad66c6ecf365..de0da545c7a1 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c | |||
@@ -12,6 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/rtc.h> | 14 | #include <linux/rtc.h> |
15 | #include <linux/log2.h> | ||
15 | 16 | ||
16 | int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) | 17 | int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) |
17 | { | 18 | { |
@@ -99,7 +100,7 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) | |||
99 | } | 100 | } |
100 | EXPORT_SYMBOL_GPL(rtc_set_mmss); | 101 | EXPORT_SYMBOL_GPL(rtc_set_mmss); |
101 | 102 | ||
102 | int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | 103 | static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm) |
103 | { | 104 | { |
104 | int err; | 105 | int err; |
105 | 106 | ||
@@ -119,6 +120,87 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
119 | mutex_unlock(&rtc->ops_lock); | 120 | mutex_unlock(&rtc->ops_lock); |
120 | return err; | 121 | return err; |
121 | } | 122 | } |
123 | |||
124 | int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | ||
125 | { | ||
126 | int err; | ||
127 | struct rtc_time before, now; | ||
128 | int first_time = 1; | ||
129 | |||
130 | /* The lower level RTC driver may not be capable of filling | ||
131 | * in all fields of the rtc_time struct (eg. rtc-cmos), | ||
132 | * and so might instead return -1 in some fields. | ||
133 | * We deal with that here by grabbing a current RTC timestamp | ||
134 | * and using values from that for any missing (-1) values. | ||
135 | * | ||
136 | * But this can be racey, because some fields of the RTC timestamp | ||
137 | * may have wrapped in the interval since we read the RTC alarm, | ||
138 | * which would lead to us inserting inconsistent values in place | ||
139 | * of the -1 fields. | ||
140 | * | ||
141 | * Reading the alarm and timestamp in the reverse sequence | ||
142 | * would have the same race condition, and not solve the issue. | ||
143 | * | ||
144 | * So, we must first read the RTC timestamp, | ||
145 | * then read the RTC alarm value, | ||
146 | * and then read a second RTC timestamp. | ||
147 | * | ||
148 | * If any fields of the second timestamp have changed | ||
149 | * when compared with the first timestamp, then we know | ||
150 | * our timestamp may be inconsistent with that used by | ||
151 | * the low-level rtc_read_alarm_internal() function. | ||
152 | * | ||
153 | * So, when the two timestamps disagree, we just loop and do | ||
154 | * the process again to get a fully consistent set of values. | ||
155 | * | ||
156 | * This could all instead be done in the lower level driver, | ||
157 | * but since more than one lower level RTC implementation needs it, | ||
158 | * then it's probably best best to do it here instead of there.. | ||
159 | */ | ||
160 | |||
161 | /* Get the "before" timestamp */ | ||
162 | err = rtc_read_time(rtc, &before); | ||
163 | if (err < 0) | ||
164 | return err; | ||
165 | do { | ||
166 | if (!first_time) | ||
167 | memcpy(&before, &now, sizeof(struct rtc_time)); | ||
168 | first_time = 0; | ||
169 | |||
170 | /* get the RTC alarm values, which may be incomplete */ | ||
171 | err = rtc_read_alarm_internal(rtc, alarm); | ||
172 | if (err) | ||
173 | return err; | ||
174 | if (!alarm->enabled) | ||
175 | return 0; | ||
176 | |||
177 | /* get the "after" timestamp, to detect wrapped fields */ | ||
178 | err = rtc_read_time(rtc, &now); | ||
179 | if (err < 0) | ||
180 | return err; | ||
181 | |||
182 | /* note that tm_sec is a "don't care" value here: */ | ||
183 | } while ( before.tm_min != now.tm_min | ||
184 | || before.tm_hour != now.tm_hour | ||
185 | || before.tm_mon != now.tm_mon | ||
186 | || before.tm_year != now.tm_year | ||
187 | || before.tm_isdst != now.tm_isdst); | ||
188 | |||
189 | /* Fill in any missing alarm fields using the timestamp */ | ||
190 | if (alarm->time.tm_sec == -1) | ||
191 | alarm->time.tm_sec = now.tm_sec; | ||
192 | if (alarm->time.tm_min == -1) | ||
193 | alarm->time.tm_min = now.tm_min; | ||
194 | if (alarm->time.tm_hour == -1) | ||
195 | alarm->time.tm_hour = now.tm_hour; | ||
196 | if (alarm->time.tm_mday == -1) | ||
197 | alarm->time.tm_mday = now.tm_mday; | ||
198 | if (alarm->time.tm_mon == -1) | ||
199 | alarm->time.tm_mon = now.tm_mon; | ||
200 | if (alarm->time.tm_year == -1) | ||
201 | alarm->time.tm_year = now.tm_year; | ||
202 | return 0; | ||
203 | } | ||
122 | EXPORT_SYMBOL_GPL(rtc_read_alarm); | 204 | EXPORT_SYMBOL_GPL(rtc_read_alarm); |
123 | 205 | ||
124 | int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | 206 | int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) |
@@ -210,6 +292,10 @@ int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task) | |||
210 | if (task == NULL || task->func == NULL) | 292 | if (task == NULL || task->func == NULL) |
211 | return -EINVAL; | 293 | return -EINVAL; |
212 | 294 | ||
295 | /* Cannot register while the char dev is in use */ | ||
296 | if (!(mutex_trylock(&rtc->char_lock))) | ||
297 | return -EBUSY; | ||
298 | |||
213 | spin_lock_irq(&rtc->irq_task_lock); | 299 | spin_lock_irq(&rtc->irq_task_lock); |
214 | if (rtc->irq_task == NULL) { | 300 | if (rtc->irq_task == NULL) { |
215 | rtc->irq_task = task; | 301 | rtc->irq_task = task; |
@@ -217,13 +303,14 @@ int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task) | |||
217 | } | 303 | } |
218 | spin_unlock_irq(&rtc->irq_task_lock); | 304 | spin_unlock_irq(&rtc->irq_task_lock); |
219 | 305 | ||
306 | mutex_unlock(&rtc->char_lock); | ||
307 | |||
220 | return retval; | 308 | return retval; |
221 | } | 309 | } |
222 | EXPORT_SYMBOL_GPL(rtc_irq_register); | 310 | EXPORT_SYMBOL_GPL(rtc_irq_register); |
223 | 311 | ||
224 | void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task) | 312 | void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task) |
225 | { | 313 | { |
226 | |||
227 | spin_lock_irq(&rtc->irq_task_lock); | 314 | spin_lock_irq(&rtc->irq_task_lock); |
228 | if (rtc->irq_task == task) | 315 | if (rtc->irq_task == task) |
229 | rtc->irq_task = NULL; | 316 | rtc->irq_task = NULL; |
@@ -231,6 +318,16 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task) | |||
231 | } | 318 | } |
232 | EXPORT_SYMBOL_GPL(rtc_irq_unregister); | 319 | EXPORT_SYMBOL_GPL(rtc_irq_unregister); |
233 | 320 | ||
321 | /** | ||
322 | * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs | ||
323 | * @rtc: the rtc device | ||
324 | * @task: currently registered with rtc_irq_register() | ||
325 | * @enabled: true to enable periodic IRQs | ||
326 | * Context: any | ||
327 | * | ||
328 | * Note that rtc_irq_set_freq() should previously have been used to | ||
329 | * specify the desired frequency of periodic IRQ task->func() callbacks. | ||
330 | */ | ||
234 | int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled) | 331 | int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled) |
235 | { | 332 | { |
236 | int err = 0; | 333 | int err = 0; |
@@ -240,8 +337,10 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled | |||
240 | return -ENXIO; | 337 | return -ENXIO; |
241 | 338 | ||
242 | spin_lock_irqsave(&rtc->irq_task_lock, flags); | 339 | spin_lock_irqsave(&rtc->irq_task_lock, flags); |
340 | if (rtc->irq_task != NULL && task == NULL) | ||
341 | err = -EBUSY; | ||
243 | if (rtc->irq_task != task) | 342 | if (rtc->irq_task != task) |
244 | err = -ENXIO; | 343 | err = -EACCES; |
245 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); | 344 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); |
246 | 345 | ||
247 | if (err == 0) | 346 | if (err == 0) |
@@ -251,6 +350,16 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled | |||
251 | } | 350 | } |
252 | EXPORT_SYMBOL_GPL(rtc_irq_set_state); | 351 | EXPORT_SYMBOL_GPL(rtc_irq_set_state); |
253 | 352 | ||
353 | /** | ||
354 | * rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ | ||
355 | * @rtc: the rtc device | ||
356 | * @task: currently registered with rtc_irq_register() | ||
357 | * @freq: positive frequency with which task->func() will be called | ||
358 | * Context: any | ||
359 | * | ||
360 | * Note that rtc_irq_set_state() is used to enable or disable the | ||
361 | * periodic IRQs. | ||
362 | */ | ||
254 | int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) | 363 | int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) |
255 | { | 364 | { |
256 | int err = 0; | 365 | int err = 0; |
@@ -259,9 +368,14 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) | |||
259 | if (rtc->ops->irq_set_freq == NULL) | 368 | if (rtc->ops->irq_set_freq == NULL) |
260 | return -ENXIO; | 369 | return -ENXIO; |
261 | 370 | ||
371 | if (!is_power_of_2(freq)) | ||
372 | return -EINVAL; | ||
373 | |||
262 | spin_lock_irqsave(&rtc->irq_task_lock, flags); | 374 | spin_lock_irqsave(&rtc->irq_task_lock, flags); |
375 | if (rtc->irq_task != NULL && task == NULL) | ||
376 | err = -EBUSY; | ||
263 | if (rtc->irq_task != task) | 377 | if (rtc->irq_task != task) |
264 | err = -ENXIO; | 378 | err = -EACCES; |
265 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); | 379 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); |
266 | 380 | ||
267 | if (err == 0) { | 381 | if (err == 0) { |