|
@@ -37,7 +37,6 @@
|
|
static struct alarm_base {
|
|
static struct alarm_base {
|
|
spinlock_t lock;
|
|
spinlock_t lock;
|
|
struct timerqueue_head timerqueue;
|
|
struct timerqueue_head timerqueue;
|
|
- struct hrtimer timer;
|
|
|
|
ktime_t (*gettime)(void);
|
|
ktime_t (*gettime)(void);
|
|
clockid_t base_clockid;
|
|
clockid_t base_clockid;
|
|
} alarm_bases[ALARM_NUMTYPE];
|
|
} alarm_bases[ALARM_NUMTYPE];
|
|
@@ -46,6 +45,8 @@ static struct alarm_base {
|
|
static ktime_t freezer_delta;
|
|
static ktime_t freezer_delta;
|
|
static DEFINE_SPINLOCK(freezer_delta_lock);
|
|
static DEFINE_SPINLOCK(freezer_delta_lock);
|
|
|
|
|
|
|
|
+static struct wakeup_source *ws;
|
|
|
|
+
|
|
#ifdef CONFIG_RTC_CLASS
|
|
#ifdef CONFIG_RTC_CLASS
|
|
/* rtc timer and device for setting alarm wakeups at suspend */
|
|
/* rtc timer and device for setting alarm wakeups at suspend */
|
|
static struct rtc_timer rtctimer;
|
|
static struct rtc_timer rtctimer;
|
|
@@ -130,50 +131,35 @@ static inline void alarmtimer_rtc_timer_init(void) { }
|
|
* @base: pointer to the base where the timer is being run
|
|
* @base: pointer to the base where the timer is being run
|
|
* @alarm: pointer to alarm being enqueued.
|
|
* @alarm: pointer to alarm being enqueued.
|
|
*
|
|
*
|
|
- * Adds alarm to a alarm_base timerqueue and if necessary sets
|
|
|
|
- * an hrtimer to run.
|
|
|
|
|
|
+ * Adds alarm to a alarm_base timerqueue
|
|
*
|
|
*
|
|
* Must hold base->lock when calling.
|
|
* Must hold base->lock when calling.
|
|
*/
|
|
*/
|
|
static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
|
|
static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
|
|
{
|
|
{
|
|
|
|
+ if (alarm->state & ALARMTIMER_STATE_ENQUEUED)
|
|
|
|
+ timerqueue_del(&base->timerqueue, &alarm->node);
|
|
|
|
+
|
|
timerqueue_add(&base->timerqueue, &alarm->node);
|
|
timerqueue_add(&base->timerqueue, &alarm->node);
|
|
alarm->state |= ALARMTIMER_STATE_ENQUEUED;
|
|
alarm->state |= ALARMTIMER_STATE_ENQUEUED;
|
|
-
|
|
|
|
- if (&alarm->node == timerqueue_getnext(&base->timerqueue)) {
|
|
|
|
- hrtimer_try_to_cancel(&base->timer);
|
|
|
|
- hrtimer_start(&base->timer, alarm->node.expires,
|
|
|
|
- HRTIMER_MODE_ABS);
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * alarmtimer_remove - Removes an alarm timer from an alarm_base timerqueue
|
|
|
|
|
|
+ * alarmtimer_dequeue - Removes an alarm timer from an alarm_base timerqueue
|
|
* @base: pointer to the base where the timer is running
|
|
* @base: pointer to the base where the timer is running
|
|
* @alarm: pointer to alarm being removed
|
|
* @alarm: pointer to alarm being removed
|
|
*
|
|
*
|
|
- * Removes alarm to a alarm_base timerqueue and if necessary sets
|
|
|
|
- * a new timer to run.
|
|
|
|
|
|
+ * Removes alarm to a alarm_base timerqueue
|
|
*
|
|
*
|
|
* Must hold base->lock when calling.
|
|
* Must hold base->lock when calling.
|
|
*/
|
|
*/
|
|
-static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
|
|
|
|
|
|
+static void alarmtimer_dequeue(struct alarm_base *base, struct alarm *alarm)
|
|
{
|
|
{
|
|
- struct timerqueue_node *next = timerqueue_getnext(&base->timerqueue);
|
|
|
|
-
|
|
|
|
if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED))
|
|
if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED))
|
|
return;
|
|
return;
|
|
|
|
|
|
timerqueue_del(&base->timerqueue, &alarm->node);
|
|
timerqueue_del(&base->timerqueue, &alarm->node);
|
|
alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
|
|
alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
|
|
-
|
|
|
|
- if (next == &alarm->node) {
|
|
|
|
- hrtimer_try_to_cancel(&base->timer);
|
|
|
|
- next = timerqueue_getnext(&base->timerqueue);
|
|
|
|
- if (!next)
|
|
|
|
- return;
|
|
|
|
- hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS);
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -188,42 +174,23 @@ static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
|
|
*/
|
|
*/
|
|
static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
|
|
static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
|
|
{
|
|
{
|
|
- struct alarm_base *base = container_of(timer, struct alarm_base, timer);
|
|
|
|
- struct timerqueue_node *next;
|
|
|
|
|
|
+ struct alarm *alarm = container_of(timer, struct alarm, timer);
|
|
|
|
+ struct alarm_base *base = &alarm_bases[alarm->type];
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- ktime_t now;
|
|
|
|
int ret = HRTIMER_NORESTART;
|
|
int ret = HRTIMER_NORESTART;
|
|
int restart = ALARMTIMER_NORESTART;
|
|
int restart = ALARMTIMER_NORESTART;
|
|
|
|
|
|
spin_lock_irqsave(&base->lock, flags);
|
|
spin_lock_irqsave(&base->lock, flags);
|
|
- now = base->gettime();
|
|
|
|
- while ((next = timerqueue_getnext(&base->timerqueue))) {
|
|
|
|
- struct alarm *alarm;
|
|
|
|
- ktime_t expired = next->expires;
|
|
|
|
-
|
|
|
|
- if (expired.tv64 > now.tv64)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- alarm = container_of(next, struct alarm, node);
|
|
|
|
-
|
|
|
|
- timerqueue_del(&base->timerqueue, &alarm->node);
|
|
|
|
- alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
|
|
|
|
-
|
|
|
|
- alarm->state |= ALARMTIMER_STATE_CALLBACK;
|
|
|
|
- spin_unlock_irqrestore(&base->lock, flags);
|
|
|
|
- if (alarm->function)
|
|
|
|
- restart = alarm->function(alarm, now);
|
|
|
|
- spin_lock_irqsave(&base->lock, flags);
|
|
|
|
- alarm->state &= ~ALARMTIMER_STATE_CALLBACK;
|
|
|
|
|
|
+ alarmtimer_dequeue(base, alarm);
|
|
|
|
+ spin_unlock_irqrestore(&base->lock, flags);
|
|
|
|
|
|
- if (restart != ALARMTIMER_NORESTART) {
|
|
|
|
- timerqueue_add(&base->timerqueue, &alarm->node);
|
|
|
|
- alarm->state |= ALARMTIMER_STATE_ENQUEUED;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ if (alarm->function)
|
|
|
|
+ restart = alarm->function(alarm, base->gettime());
|
|
|
|
|
|
- if (next) {
|
|
|
|
- hrtimer_set_expires(&base->timer, next->expires);
|
|
|
|
|
|
+ spin_lock_irqsave(&base->lock, flags);
|
|
|
|
+ if (restart != ALARMTIMER_NORESTART) {
|
|
|
|
+ hrtimer_set_expires(&alarm->timer, alarm->node.expires);
|
|
|
|
+ alarmtimer_enqueue(base, alarm);
|
|
ret = HRTIMER_RESTART;
|
|
ret = HRTIMER_RESTART;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&base->lock, flags);
|
|
spin_unlock_irqrestore(&base->lock, flags);
|
|
@@ -250,6 +217,7 @@ static int alarmtimer_suspend(struct device *dev)
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
struct rtc_device *rtc;
|
|
struct rtc_device *rtc;
|
|
int i;
|
|
int i;
|
|
|
|
+ int ret;
|
|
|
|
|
|
spin_lock_irqsave(&freezer_delta_lock, flags);
|
|
spin_lock_irqsave(&freezer_delta_lock, flags);
|
|
min = freezer_delta;
|
|
min = freezer_delta;
|
|
@@ -279,8 +247,10 @@ static int alarmtimer_suspend(struct device *dev)
|
|
if (min.tv64 == 0)
|
|
if (min.tv64 == 0)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- /* XXX - Should we enforce a minimum sleep time? */
|
|
|
|
- WARN_ON(min.tv64 < NSEC_PER_SEC);
|
|
|
|
|
|
+ if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {
|
|
|
|
+ __pm_wakeup_event(ws, 2 * MSEC_PER_SEC);
|
|
|
|
+ return -EBUSY;
|
|
|
|
+ }
|
|
|
|
|
|
/* Setup an rtc timer to fire that far in the future */
|
|
/* Setup an rtc timer to fire that far in the future */
|
|
rtc_timer_cancel(rtc, &rtctimer);
|
|
rtc_timer_cancel(rtc, &rtctimer);
|
|
@@ -288,9 +258,11 @@ static int alarmtimer_suspend(struct device *dev)
|
|
now = rtc_tm_to_ktime(tm);
|
|
now = rtc_tm_to_ktime(tm);
|
|
now = ktime_add(now, min);
|
|
now = ktime_add(now, min);
|
|
|
|
|
|
- rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* Set alarm, if in the past reject suspend briefly to handle */
|
|
|
|
+ ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ __pm_wakeup_event(ws, MSEC_PER_SEC);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
#else
|
|
#else
|
|
static int alarmtimer_suspend(struct device *dev)
|
|
static int alarmtimer_suspend(struct device *dev)
|
|
@@ -324,6 +296,9 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
|
|
enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
|
|
enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
|
|
{
|
|
{
|
|
timerqueue_init(&alarm->node);
|
|
timerqueue_init(&alarm->node);
|
|
|
|
+ hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid,
|
|
|
|
+ HRTIMER_MODE_ABS);
|
|
|
|
+ alarm->timer.function = alarmtimer_fired;
|
|
alarm->function = function;
|
|
alarm->function = function;
|
|
alarm->type = type;
|
|
alarm->type = type;
|
|
alarm->state = ALARMTIMER_STATE_INACTIVE;
|
|
alarm->state = ALARMTIMER_STATE_INACTIVE;
|
|
@@ -334,17 +309,19 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
|
|
* @alarm: ptr to alarm to set
|
|
* @alarm: ptr to alarm to set
|
|
* @start: time to run the alarm
|
|
* @start: time to run the alarm
|
|
*/
|
|
*/
|
|
-void alarm_start(struct alarm *alarm, ktime_t start)
|
|
|
|
|
|
+int alarm_start(struct alarm *alarm, ktime_t start)
|
|
{
|
|
{
|
|
struct alarm_base *base = &alarm_bases[alarm->type];
|
|
struct alarm_base *base = &alarm_bases[alarm->type];
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
+ int ret;
|
|
|
|
|
|
spin_lock_irqsave(&base->lock, flags);
|
|
spin_lock_irqsave(&base->lock, flags);
|
|
- if (alarmtimer_active(alarm))
|
|
|
|
- alarmtimer_remove(base, alarm);
|
|
|
|
alarm->node.expires = start;
|
|
alarm->node.expires = start;
|
|
alarmtimer_enqueue(base, alarm);
|
|
alarmtimer_enqueue(base, alarm);
|
|
|
|
+ ret = hrtimer_start(&alarm->timer, alarm->node.expires,
|
|
|
|
+ HRTIMER_MODE_ABS);
|
|
spin_unlock_irqrestore(&base->lock, flags);
|
|
spin_unlock_irqrestore(&base->lock, flags);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -358,18 +335,12 @@ int alarm_try_to_cancel(struct alarm *alarm)
|
|
{
|
|
{
|
|
struct alarm_base *base = &alarm_bases[alarm->type];
|
|
struct alarm_base *base = &alarm_bases[alarm->type];
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- int ret = -1;
|
|
|
|
- spin_lock_irqsave(&base->lock, flags);
|
|
|
|
-
|
|
|
|
- if (alarmtimer_callback_running(alarm))
|
|
|
|
- goto out;
|
|
|
|
|
|
+ int ret;
|
|
|
|
|
|
- if (alarmtimer_is_queued(alarm)) {
|
|
|
|
- alarmtimer_remove(base, alarm);
|
|
|
|
- ret = 1;
|
|
|
|
- } else
|
|
|
|
- ret = 0;
|
|
|
|
-out:
|
|
|
|
|
|
+ spin_lock_irqsave(&base->lock, flags);
|
|
|
|
+ ret = hrtimer_try_to_cancel(&alarm->timer);
|
|
|
|
+ if (ret >= 0)
|
|
|
|
+ alarmtimer_dequeue(base, alarm);
|
|
spin_unlock_irqrestore(&base->lock, flags);
|
|
spin_unlock_irqrestore(&base->lock, flags);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -802,10 +773,6 @@ static int __init alarmtimer_init(void)
|
|
for (i = 0; i < ALARM_NUMTYPE; i++) {
|
|
for (i = 0; i < ALARM_NUMTYPE; i++) {
|
|
timerqueue_init_head(&alarm_bases[i].timerqueue);
|
|
timerqueue_init_head(&alarm_bases[i].timerqueue);
|
|
spin_lock_init(&alarm_bases[i].lock);
|
|
spin_lock_init(&alarm_bases[i].lock);
|
|
- hrtimer_init(&alarm_bases[i].timer,
|
|
|
|
- alarm_bases[i].base_clockid,
|
|
|
|
- HRTIMER_MODE_ABS);
|
|
|
|
- alarm_bases[i].timer.function = alarmtimer_fired;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
error = alarmtimer_rtc_interface_setup();
|
|
error = alarmtimer_rtc_interface_setup();
|
|
@@ -821,6 +788,7 @@ static int __init alarmtimer_init(void)
|
|
error = PTR_ERR(pdev);
|
|
error = PTR_ERR(pdev);
|
|
goto out_drv;
|
|
goto out_drv;
|
|
}
|
|
}
|
|
|
|
+ ws = wakeup_source_register("alarmtimer");
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
out_drv:
|
|
out_drv:
|