diff options
| -rw-r--r-- | drivers/rtc/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/rtc/interface.c | 54 | ||||
| -rw-r--r-- | drivers/rtc/rtc-dev.c | 51 | ||||
| -rw-r--r-- | include/linux/rtc.h | 8 |
4 files changed, 101 insertions, 18 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 123092d8a984..165a81843357 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -102,9 +102,13 @@ config RTC_INTF_DEV_UIE_EMUL | |||
| 102 | depends on RTC_INTF_DEV | 102 | depends on RTC_INTF_DEV |
| 103 | help | 103 | help |
| 104 | Provides an emulation for RTC_UIE if the underlying rtc chip | 104 | Provides an emulation for RTC_UIE if the underlying rtc chip |
| 105 | driver does not expose RTC_UIE ioctls. Those requests generate | 105 | driver does not expose RTC_UIE ioctls. Those requests generate |
| 106 | once-per-second update interrupts, used for synchronization. | 106 | once-per-second update interrupts, used for synchronization. |
| 107 | 107 | ||
| 108 | The emulation code will read the time from the hardware | ||
| 109 | clock several times per second, please enable this option | ||
| 110 | only if you know that you really need it. | ||
| 111 | |||
| 108 | config RTC_DRV_TEST | 112 | config RTC_DRV_TEST |
| 109 | tristate "Test driver/device" | 113 | tristate "Test driver/device" |
| 110 | help | 114 | help |
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index a04c1b6b1575..fd2c652504ff 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c | |||
| @@ -307,6 +307,60 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
| 307 | } | 307 | } |
| 308 | EXPORT_SYMBOL_GPL(rtc_set_alarm); | 308 | EXPORT_SYMBOL_GPL(rtc_set_alarm); |
| 309 | 309 | ||
| 310 | int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) | ||
| 311 | { | ||
| 312 | int err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 313 | if (err) | ||
| 314 | return err; | ||
| 315 | |||
| 316 | if (!rtc->ops) | ||
| 317 | err = -ENODEV; | ||
| 318 | else if (!rtc->ops->alarm_irq_enable) | ||
| 319 | err = -EINVAL; | ||
| 320 | else | ||
| 321 | err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); | ||
| 322 | |||
| 323 | mutex_unlock(&rtc->ops_lock); | ||
| 324 | return err; | ||
| 325 | } | ||
| 326 | EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); | ||
| 327 | |||
| 328 | int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) | ||
| 329 | { | ||
| 330 | int err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 331 | if (err) | ||
| 332 | return err; | ||
| 333 | |||
| 334 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 335 | if (enabled == 0 && rtc->uie_irq_active) { | ||
| 336 | mutex_unlock(&rtc->ops_lock); | ||
| 337 | return rtc_dev_update_irq_enable_emul(rtc, enabled); | ||
| 338 | } | ||
| 339 | #endif | ||
| 340 | |||
| 341 | if (!rtc->ops) | ||
| 342 | err = -ENODEV; | ||
| 343 | else if (!rtc->ops->update_irq_enable) | ||
| 344 | err = -EINVAL; | ||
| 345 | else | ||
| 346 | err = rtc->ops->update_irq_enable(rtc->dev.parent, enabled); | ||
| 347 | |||
| 348 | mutex_unlock(&rtc->ops_lock); | ||
| 349 | |||
| 350 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 351 | /* | ||
| 352 | * Enable emulation if the driver did not provide | ||
| 353 | * the update_irq_enable function pointer or if returned | ||
| 354 | * -EINVAL to signal that it has been configured without | ||
| 355 | * interrupts or that are not available at the moment. | ||
| 356 | */ | ||
| 357 | if (err == -EINVAL) | ||
| 358 | err = rtc_dev_update_irq_enable_emul(rtc, enabled); | ||
| 359 | #endif | ||
| 360 | return err; | ||
| 361 | } | ||
| 362 | EXPORT_SYMBOL_GPL(rtc_update_irq_enable); | ||
| 363 | |||
| 310 | /** | 364 | /** |
| 311 | * rtc_update_irq - report RTC periodic, alarm, and/or update irqs | 365 | * rtc_update_irq - report RTC periodic, alarm, and/or update irqs |
| 312 | * @rtc: the rtc device | 366 | * @rtc: the rtc device |
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index ecdea44ae4e5..45152f4952d6 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c | |||
| @@ -92,10 +92,10 @@ static void rtc_uie_timer(unsigned long data) | |||
| 92 | spin_unlock_irqrestore(&rtc->irq_lock, flags); | 92 | spin_unlock_irqrestore(&rtc->irq_lock, flags); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | static void clear_uie(struct rtc_device *rtc) | 95 | static int clear_uie(struct rtc_device *rtc) |
| 96 | { | 96 | { |
| 97 | spin_lock_irq(&rtc->irq_lock); | 97 | spin_lock_irq(&rtc->irq_lock); |
| 98 | if (rtc->irq_active) { | 98 | if (rtc->uie_irq_active) { |
| 99 | rtc->stop_uie_polling = 1; | 99 | rtc->stop_uie_polling = 1; |
| 100 | if (rtc->uie_timer_active) { | 100 | if (rtc->uie_timer_active) { |
| 101 | spin_unlock_irq(&rtc->irq_lock); | 101 | spin_unlock_irq(&rtc->irq_lock); |
| @@ -108,9 +108,10 @@ static void clear_uie(struct rtc_device *rtc) | |||
| 108 | flush_scheduled_work(); | 108 | flush_scheduled_work(); |
| 109 | spin_lock_irq(&rtc->irq_lock); | 109 | spin_lock_irq(&rtc->irq_lock); |
| 110 | } | 110 | } |
| 111 | rtc->irq_active = 0; | 111 | rtc->uie_irq_active = 0; |
| 112 | } | 112 | } |
| 113 | spin_unlock_irq(&rtc->irq_lock); | 113 | spin_unlock_irq(&rtc->irq_lock); |
| 114 | return 0; | ||
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | static int set_uie(struct rtc_device *rtc) | 117 | static int set_uie(struct rtc_device *rtc) |
| @@ -122,8 +123,8 @@ static int set_uie(struct rtc_device *rtc) | |||
| 122 | if (err) | 123 | if (err) |
| 123 | return err; | 124 | return err; |
| 124 | spin_lock_irq(&rtc->irq_lock); | 125 | spin_lock_irq(&rtc->irq_lock); |
| 125 | if (!rtc->irq_active) { | 126 | if (!rtc->uie_irq_active) { |
| 126 | rtc->irq_active = 1; | 127 | rtc->uie_irq_active = 1; |
| 127 | rtc->stop_uie_polling = 0; | 128 | rtc->stop_uie_polling = 0; |
| 128 | rtc->oldsecs = tm.tm_sec; | 129 | rtc->oldsecs = tm.tm_sec; |
| 129 | rtc->uie_task_active = 1; | 130 | rtc->uie_task_active = 1; |
| @@ -134,6 +135,16 @@ static int set_uie(struct rtc_device *rtc) | |||
| 134 | spin_unlock_irq(&rtc->irq_lock); | 135 | spin_unlock_irq(&rtc->irq_lock); |
| 135 | return 0; | 136 | return 0; |
| 136 | } | 137 | } |
| 138 | |||
| 139 | int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) | ||
| 140 | { | ||
| 141 | if (enabled) | ||
| 142 | return set_uie(rtc); | ||
| 143 | else | ||
| 144 | return clear_uie(rtc); | ||
| 145 | } | ||
| 146 | EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); | ||
| 147 | |||
| 137 | #endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ | 148 | #endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ |
| 138 | 149 | ||
| 139 | static ssize_t | 150 | static ssize_t |
| @@ -357,6 +368,22 @@ static long rtc_dev_ioctl(struct file *file, | |||
| 357 | err = rtc_irq_set_state(rtc, NULL, 0); | 368 | err = rtc_irq_set_state(rtc, NULL, 0); |
| 358 | break; | 369 | break; |
| 359 | 370 | ||
| 371 | case RTC_AIE_ON: | ||
| 372 | mutex_unlock(&rtc->ops_lock); | ||
| 373 | return rtc_alarm_irq_enable(rtc, 1); | ||
| 374 | |||
| 375 | case RTC_AIE_OFF: | ||
| 376 | mutex_unlock(&rtc->ops_lock); | ||
| 377 | return rtc_alarm_irq_enable(rtc, 0); | ||
| 378 | |||
| 379 | case RTC_UIE_ON: | ||
| 380 | mutex_unlock(&rtc->ops_lock); | ||
| 381 | return rtc_update_irq_enable(rtc, 1); | ||
| 382 | |||
| 383 | case RTC_UIE_OFF: | ||
| 384 | mutex_unlock(&rtc->ops_lock); | ||
| 385 | return rtc_update_irq_enable(rtc, 0); | ||
| 386 | |||
| 360 | case RTC_IRQP_SET: | 387 | case RTC_IRQP_SET: |
| 361 | err = rtc_irq_set_freq(rtc, NULL, arg); | 388 | err = rtc_irq_set_freq(rtc, NULL, arg); |
| 362 | break; | 389 | break; |
| @@ -401,17 +428,6 @@ static long rtc_dev_ioctl(struct file *file, | |||
| 401 | err = -EFAULT; | 428 | err = -EFAULT; |
| 402 | return err; | 429 | return err; |
| 403 | 430 | ||
| 404 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
| 405 | case RTC_UIE_OFF: | ||
| 406 | mutex_unlock(&rtc->ops_lock); | ||
| 407 | clear_uie(rtc); | ||
| 408 | return 0; | ||
| 409 | |||
| 410 | case RTC_UIE_ON: | ||
| 411 | mutex_unlock(&rtc->ops_lock); | ||
| 412 | err = set_uie(rtc); | ||
| 413 | return err; | ||
| 414 | #endif | ||
| 415 | default: | 431 | default: |
| 416 | err = -ENOTTY; | 432 | err = -ENOTTY; |
| 417 | break; | 433 | break; |
| @@ -440,7 +456,10 @@ static int rtc_dev_release(struct inode *inode, struct file *file) | |||
| 440 | * Leave the alarm alone; it may be set to trigger a system wakeup | 456 | * Leave the alarm alone; it may be set to trigger a system wakeup |
| 441 | * later, or be used by kernel code, and is a one-shot event anyway. | 457 | * later, or be used by kernel code, and is a one-shot event anyway. |
| 442 | */ | 458 | */ |
| 459 | |||
| 460 | /* Keep ioctl until all drivers are converted */ | ||
| 443 | rtc_dev_ioctl(file, RTC_UIE_OFF, 0); | 461 | rtc_dev_ioctl(file, RTC_UIE_OFF, 0); |
| 462 | rtc_update_irq_enable(rtc, 0); | ||
| 444 | rtc_irq_set_state(rtc, NULL, 0); | 463 | rtc_irq_set_state(rtc, NULL, 0); |
| 445 | 464 | ||
| 446 | if (rtc->ops->release) | 465 | if (rtc->ops->release) |
diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 91f597ad6acc..4046b75563c1 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h | |||
| @@ -145,6 +145,8 @@ struct rtc_class_ops { | |||
| 145 | int (*irq_set_state)(struct device *, int enabled); | 145 | int (*irq_set_state)(struct device *, int enabled); |
| 146 | int (*irq_set_freq)(struct device *, int freq); | 146 | int (*irq_set_freq)(struct device *, int freq); |
| 147 | int (*read_callback)(struct device *, int data); | 147 | int (*read_callback)(struct device *, int data); |
| 148 | int (*alarm_irq_enable)(struct device *, unsigned int enabled); | ||
| 149 | int (*update_irq_enable)(struct device *, unsigned int enabled); | ||
| 148 | }; | 150 | }; |
| 149 | 151 | ||
| 150 | #define RTC_DEVICE_NAME_SIZE 20 | 152 | #define RTC_DEVICE_NAME_SIZE 20 |
| @@ -181,7 +183,7 @@ struct rtc_device | |||
| 181 | struct timer_list uie_timer; | 183 | struct timer_list uie_timer; |
| 182 | /* Those fields are protected by rtc->irq_lock */ | 184 | /* Those fields are protected by rtc->irq_lock */ |
| 183 | unsigned int oldsecs; | 185 | unsigned int oldsecs; |
| 184 | unsigned int irq_active:1; | 186 | unsigned int uie_irq_active:1; |
| 185 | unsigned int stop_uie_polling:1; | 187 | unsigned int stop_uie_polling:1; |
| 186 | unsigned int uie_task_active:1; | 188 | unsigned int uie_task_active:1; |
| 187 | unsigned int uie_timer_active:1; | 189 | unsigned int uie_timer_active:1; |
| @@ -216,6 +218,10 @@ extern int rtc_irq_set_state(struct rtc_device *rtc, | |||
| 216 | struct rtc_task *task, int enabled); | 218 | struct rtc_task *task, int enabled); |
| 217 | extern int rtc_irq_set_freq(struct rtc_device *rtc, | 219 | extern int rtc_irq_set_freq(struct rtc_device *rtc, |
| 218 | struct rtc_task *task, int freq); | 220 | struct rtc_task *task, int freq); |
| 221 | extern int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled); | ||
| 222 | extern int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled); | ||
| 223 | extern int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, | ||
| 224 | unsigned int enabled); | ||
| 219 | 225 | ||
| 220 | typedef struct rtc_task { | 226 | typedef struct rtc_task { |
| 221 | void (*func)(void *private_data); | 227 | void (*func)(void *private_data); |
