diff options
Diffstat (limited to 'kernel/time/alarmtimer.c')
| -rw-r--r-- | kernel/time/alarmtimer.c | 158 |
1 files changed, 88 insertions, 70 deletions
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 2d966244ea60..59f369f98a04 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c | |||
| @@ -42,15 +42,75 @@ static struct alarm_base { | |||
| 42 | clockid_t base_clockid; | 42 | clockid_t base_clockid; |
| 43 | } alarm_bases[ALARM_NUMTYPE]; | 43 | } alarm_bases[ALARM_NUMTYPE]; |
| 44 | 44 | ||
| 45 | /* freezer delta & lock used to handle clock_nanosleep triggered wakeups */ | ||
| 46 | static ktime_t freezer_delta; | ||
| 47 | static DEFINE_SPINLOCK(freezer_delta_lock); | ||
| 48 | |||
| 45 | #ifdef CONFIG_RTC_CLASS | 49 | #ifdef CONFIG_RTC_CLASS |
| 46 | /* rtc timer and device for setting alarm wakeups at suspend */ | 50 | /* rtc timer and device for setting alarm wakeups at suspend */ |
| 47 | static struct rtc_timer rtctimer; | 51 | static struct rtc_timer rtctimer; |
| 48 | static struct rtc_device *rtcdev; | 52 | static struct rtc_device *rtcdev; |
| 49 | #endif | 53 | static DEFINE_SPINLOCK(rtcdev_lock); |
| 50 | 54 | ||
| 51 | /* freezer delta & lock used to handle clock_nanosleep triggered wakeups */ | 55 | /** |
| 52 | static ktime_t freezer_delta; | 56 | * has_wakealarm - check rtc device has wakealarm ability |
| 53 | static DEFINE_SPINLOCK(freezer_delta_lock); | 57 | * @dev: current device |
| 58 | * @name_ptr: name to be returned | ||
| 59 | * | ||
| 60 | * This helper function checks to see if the rtc device can wake | ||
| 61 | * from suspend. | ||
| 62 | */ | ||
| 63 | static int has_wakealarm(struct device *dev, void *name_ptr) | ||
| 64 | { | ||
| 65 | struct rtc_device *candidate = to_rtc_device(dev); | ||
| 66 | |||
| 67 | if (!candidate->ops->set_alarm) | ||
| 68 | return 0; | ||
| 69 | if (!device_may_wakeup(candidate->dev.parent)) | ||
| 70 | return 0; | ||
| 71 | |||
| 72 | *(const char **)name_ptr = dev_name(dev); | ||
| 73 | return 1; | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * alarmtimer_get_rtcdev - Return selected rtcdevice | ||
| 78 | * | ||
| 79 | * This function returns the rtc device to use for wakealarms. | ||
| 80 | * If one has not already been chosen, it checks to see if a | ||
| 81 | * functional rtc device is available. | ||
| 82 | */ | ||
| 83 | static struct rtc_device *alarmtimer_get_rtcdev(void) | ||
| 84 | { | ||
| 85 | struct device *dev; | ||
| 86 | char *str; | ||
| 87 | unsigned long flags; | ||
| 88 | struct rtc_device *ret; | ||
| 89 | |||
| 90 | spin_lock_irqsave(&rtcdev_lock, flags); | ||
| 91 | if (!rtcdev) { | ||
| 92 | /* Find an rtc device and init the rtc_timer */ | ||
| 93 | dev = class_find_device(rtc_class, NULL, &str, has_wakealarm); | ||
| 94 | /* If we have a device then str is valid. See has_wakealarm() */ | ||
| 95 | if (dev) { | ||
| 96 | rtcdev = rtc_class_open(str); | ||
| 97 | /* | ||
| 98 | * Drop the reference we got in class_find_device, | ||
| 99 | * rtc_open takes its own. | ||
| 100 | */ | ||
| 101 | put_device(dev); | ||
| 102 | rtc_timer_init(&rtctimer, NULL, NULL); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | ret = rtcdev; | ||
| 106 | spin_unlock_irqrestore(&rtcdev_lock, flags); | ||
| 107 | |||
| 108 | return ret; | ||
| 109 | } | ||
| 110 | #else | ||
| 111 | #define alarmtimer_get_rtcdev() (0) | ||
| 112 | #define rtcdev (0) | ||
| 113 | #endif | ||
| 54 | 114 | ||
| 55 | 115 | ||
| 56 | /** | 116 | /** |
| @@ -166,6 +226,7 @@ static int alarmtimer_suspend(struct device *dev) | |||
| 166 | struct rtc_time tm; | 226 | struct rtc_time tm; |
| 167 | ktime_t min, now; | 227 | ktime_t min, now; |
| 168 | unsigned long flags; | 228 | unsigned long flags; |
| 229 | struct rtc_device *rtc; | ||
| 169 | int i; | 230 | int i; |
| 170 | 231 | ||
| 171 | spin_lock_irqsave(&freezer_delta_lock, flags); | 232 | spin_lock_irqsave(&freezer_delta_lock, flags); |
| @@ -173,8 +234,9 @@ static int alarmtimer_suspend(struct device *dev) | |||
| 173 | freezer_delta = ktime_set(0, 0); | 234 | freezer_delta = ktime_set(0, 0); |
| 174 | spin_unlock_irqrestore(&freezer_delta_lock, flags); | 235 | spin_unlock_irqrestore(&freezer_delta_lock, flags); |
| 175 | 236 | ||
| 237 | rtc = rtcdev; | ||
| 176 | /* If we have no rtcdev, just return */ | 238 | /* If we have no rtcdev, just return */ |
| 177 | if (!rtcdev) | 239 | if (!rtc) |
| 178 | return 0; | 240 | return 0; |
| 179 | 241 | ||
| 180 | /* Find the soonest timer to expire*/ | 242 | /* Find the soonest timer to expire*/ |
| @@ -199,12 +261,12 @@ static int alarmtimer_suspend(struct device *dev) | |||
| 199 | WARN_ON(min.tv64 < NSEC_PER_SEC); | 261 | WARN_ON(min.tv64 < NSEC_PER_SEC); |
| 200 | 262 | ||
| 201 | /* Setup an rtc timer to fire that far in the future */ | 263 | /* Setup an rtc timer to fire that far in the future */ |
| 202 | rtc_timer_cancel(rtcdev, &rtctimer); | 264 | rtc_timer_cancel(rtc, &rtctimer); |
| 203 | rtc_read_time(rtcdev, &tm); | 265 | rtc_read_time(rtc, &tm); |
| 204 | now = rtc_tm_to_ktime(tm); | 266 | now = rtc_tm_to_ktime(tm); |
| 205 | now = ktime_add(now, min); | 267 | now = ktime_add(now, min); |
| 206 | 268 | ||
| 207 | rtc_timer_start(rtcdev, &rtctimer, now, ktime_set(0, 0)); | 269 | rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0)); |
| 208 | 270 | ||
| 209 | return 0; | 271 | return 0; |
| 210 | } | 272 | } |
| @@ -322,6 +384,9 @@ static int alarm_clock_getres(const clockid_t which_clock, struct timespec *tp) | |||
| 322 | { | 384 | { |
| 323 | clockid_t baseid = alarm_bases[clock2alarm(which_clock)].base_clockid; | 385 | clockid_t baseid = alarm_bases[clock2alarm(which_clock)].base_clockid; |
| 324 | 386 | ||
| 387 | if (!alarmtimer_get_rtcdev()) | ||
| 388 | return -ENOTSUPP; | ||
| 389 | |||
| 325 | return hrtimer_get_res(baseid, tp); | 390 | return hrtimer_get_res(baseid, tp); |
| 326 | } | 391 | } |
| 327 | 392 | ||
| @@ -336,6 +401,9 @@ static int alarm_clock_get(clockid_t which_clock, struct timespec *tp) | |||
| 336 | { | 401 | { |
| 337 | struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)]; | 402 | struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)]; |
| 338 | 403 | ||
| 404 | if (!alarmtimer_get_rtcdev()) | ||
| 405 | return -ENOTSUPP; | ||
| 406 | |||
| 339 | *tp = ktime_to_timespec(base->gettime()); | 407 | *tp = ktime_to_timespec(base->gettime()); |
| 340 | return 0; | 408 | return 0; |
| 341 | } | 409 | } |
| @@ -351,6 +419,9 @@ static int alarm_timer_create(struct k_itimer *new_timer) | |||
| 351 | enum alarmtimer_type type; | 419 | enum alarmtimer_type type; |
| 352 | struct alarm_base *base; | 420 | struct alarm_base *base; |
| 353 | 421 | ||
| 422 | if (!alarmtimer_get_rtcdev()) | ||
| 423 | return -ENOTSUPP; | ||
| 424 | |||
| 354 | if (!capable(CAP_WAKE_ALARM)) | 425 | if (!capable(CAP_WAKE_ALARM)) |
| 355 | return -EPERM; | 426 | return -EPERM; |
| 356 | 427 | ||
| @@ -385,6 +456,9 @@ static void alarm_timer_get(struct k_itimer *timr, | |||
| 385 | */ | 456 | */ |
| 386 | static int alarm_timer_del(struct k_itimer *timr) | 457 | static int alarm_timer_del(struct k_itimer *timr) |
| 387 | { | 458 | { |
| 459 | if (!rtcdev) | ||
| 460 | return -ENOTSUPP; | ||
| 461 | |||
| 388 | alarm_cancel(&timr->it.alarmtimer); | 462 | alarm_cancel(&timr->it.alarmtimer); |
| 389 | return 0; | 463 | return 0; |
| 390 | } | 464 | } |
| @@ -402,6 +476,9 @@ static int alarm_timer_set(struct k_itimer *timr, int flags, | |||
| 402 | struct itimerspec *new_setting, | 476 | struct itimerspec *new_setting, |
| 403 | struct itimerspec *old_setting) | 477 | struct itimerspec *old_setting) |
| 404 | { | 478 | { |
| 479 | if (!rtcdev) | ||
| 480 | return -ENOTSUPP; | ||
| 481 | |||
| 405 | /* Save old values */ | 482 | /* Save old values */ |
| 406 | old_setting->it_interval = | 483 | old_setting->it_interval = |
| 407 | ktime_to_timespec(timr->it.alarmtimer.period); | 484 | ktime_to_timespec(timr->it.alarmtimer.period); |
| @@ -541,6 +618,9 @@ static int alarm_timer_nsleep(const clockid_t which_clock, int flags, | |||
| 541 | int ret = 0; | 618 | int ret = 0; |
| 542 | struct restart_block *restart; | 619 | struct restart_block *restart; |
| 543 | 620 | ||
| 621 | if (!alarmtimer_get_rtcdev()) | ||
| 622 | return -ENOTSUPP; | ||
| 623 | |||
| 544 | if (!capable(CAP_WAKE_ALARM)) | 624 | if (!capable(CAP_WAKE_ALARM)) |
| 545 | return -EPERM; | 625 | return -EPERM; |
| 546 | 626 | ||
| @@ -638,65 +718,3 @@ static int __init alarmtimer_init(void) | |||
| 638 | } | 718 | } |
| 639 | device_initcall(alarmtimer_init); | 719 | device_initcall(alarmtimer_init); |
| 640 | 720 | ||
| 641 | #ifdef CONFIG_RTC_CLASS | ||
| 642 | /** | ||
| 643 | * has_wakealarm - check rtc device has wakealarm ability | ||
| 644 | * @dev: current device | ||
| 645 | * @name_ptr: name to be returned | ||
| 646 | * | ||
| 647 | * This helper function checks to see if the rtc device can wake | ||
| 648 | * from suspend. | ||
| 649 | */ | ||
| 650 | static int __init has_wakealarm(struct device *dev, void *name_ptr) | ||
| 651 | { | ||
| 652 | struct rtc_device *candidate = to_rtc_device(dev); | ||
| 653 | |||
| 654 | if (!candidate->ops->set_alarm) | ||
| 655 | return 0; | ||
| 656 | if (!device_may_wakeup(candidate->dev.parent)) | ||
| 657 | return 0; | ||
| 658 | |||
| 659 | *(const char **)name_ptr = dev_name(dev); | ||
| 660 | return 1; | ||
| 661 | } | ||
| 662 | |||
| 663 | /** | ||
| 664 | * alarmtimer_init_late - Late initializing of alarmtimer code | ||
| 665 | * | ||
| 666 | * This function locates a rtc device to use for wakealarms. | ||
| 667 | * Run as late_initcall to make sure rtc devices have been | ||
| 668 | * registered. | ||
| 669 | */ | ||
| 670 | static int __init alarmtimer_init_late(void) | ||
| 671 | { | ||
| 672 | struct device *dev; | ||
| 673 | char *str; | ||
| 674 | |||
| 675 | /* Find an rtc device and init the rtc_timer */ | ||
| 676 | dev = class_find_device(rtc_class, NULL, &str, has_wakealarm); | ||
| 677 | /* If we have a device then str is valid. See has_wakealarm() */ | ||
| 678 | if (dev) { | ||
| 679 | rtcdev = rtc_class_open(str); | ||
| 680 | /* | ||
| 681 | * Drop the reference we got in class_find_device, | ||
| 682 | * rtc_open takes its own. | ||
| 683 | */ | ||
| 684 | put_device(dev); | ||
| 685 | } | ||
| 686 | if (!rtcdev) { | ||
| 687 | printk(KERN_WARNING "No RTC device found, ALARM timers will" | ||
| 688 | " not wake from suspend"); | ||
| 689 | } | ||
| 690 | rtc_timer_init(&rtctimer, NULL, NULL); | ||
| 691 | |||
| 692 | return 0; | ||
| 693 | } | ||
| 694 | #else | ||
| 695 | static int __init alarmtimer_init_late(void) | ||
| 696 | { | ||
| 697 | printk(KERN_WARNING "Kernel not built with RTC support, ALARM timers" | ||
| 698 | " will not wake from suspend"); | ||
| 699 | return 0; | ||
| 700 | } | ||
| 701 | #endif | ||
| 702 | late_initcall(alarmtimer_init_late); | ||
