Commit 91bc51d8 authored by Russell King's avatar Russell King Committed by Russell King

[ARM] pxa: fix one-shot timer mode

One-shot timer mode on PXA has various bugs which prevent kernels
build with NO_HZ enabled booting.  They end up spinning on a
permanently asserted timer interrupt because we don't properly
clear it down - clearing the OIER bit does not stop the pending
interrupt status.  Fix this in the set_mode handler as well.

Moreover, the code which sets the next expiry point may race with
the hardware, and we might not set the match register sufficiently
in the future.  If we encounter that situation, return -ETIME so
the generic time code retries.
Acked-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarNicolas Pitre <nico@cam.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent c2ec21c5
...@@ -68,6 +68,7 @@ pxa_ost0_interrupt(int irq, void *dev_id) ...@@ -68,6 +68,7 @@ pxa_ost0_interrupt(int irq, void *dev_id)
if (c->mode == CLOCK_EVT_MODE_ONESHOT) { if (c->mode == CLOCK_EVT_MODE_ONESHOT) {
/* Disarm the compare/match, signal the event. */ /* Disarm the compare/match, signal the event. */
OIER &= ~OIER_E0; OIER &= ~OIER_E0;
OSSR = OSSR_M0;
c->event_handler(c); c->event_handler(c);
} else if (c->mode == CLOCK_EVT_MODE_PERIODIC) { } else if (c->mode == CLOCK_EVT_MODE_PERIODIC) {
/* Call the event handler as many times as necessary /* Call the event handler as many times as necessary
...@@ -100,9 +101,9 @@ pxa_ost0_interrupt(int irq, void *dev_id) ...@@ -100,9 +101,9 @@ pxa_ost0_interrupt(int irq, void *dev_id)
* anything that might put us "very close". * anything that might put us "very close".
*/ */
#define MIN_OSCR_DELTA 16 #define MIN_OSCR_DELTA 16
do { do {
OSSR = OSSR_M0; OSSR = OSSR_M0;
next_match = (OSMR0 += LATCH); next_match = (OSMR0 += LATCH);
c->event_handler(c); c->event_handler(c);
} while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA) } while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA)
&& (c->mode == CLOCK_EVT_MODE_PERIODIC)); && (c->mode == CLOCK_EVT_MODE_PERIODIC));
...@@ -114,14 +115,16 @@ pxa_ost0_interrupt(int irq, void *dev_id) ...@@ -114,14 +115,16 @@ pxa_ost0_interrupt(int irq, void *dev_id)
static int static int
pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev) pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
{ {
unsigned long irqflags; unsigned long flags, next, oscr;
raw_local_irq_save(irqflags); raw_local_irq_save(flags);
OSMR0 = OSCR + delta;
OSSR = OSSR_M0;
OIER |= OIER_E0; OIER |= OIER_E0;
raw_local_irq_restore(irqflags); next = OSCR + delta;
return 0; OSMR0 = next;
oscr = OSCR;
raw_local_irq_restore(flags);
return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
} }
static void static void
...@@ -132,15 +135,16 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) ...@@ -132,15 +135,16 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
switch (mode) { switch (mode) {
case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_PERIODIC:
raw_local_irq_save(irqflags); raw_local_irq_save(irqflags);
OSMR0 = OSCR + LATCH;
OSSR = OSSR_M0; OSSR = OSSR_M0;
OIER |= OIER_E0; OIER |= OIER_E0;
OSMR0 = OSCR + LATCH;
raw_local_irq_restore(irqflags); raw_local_irq_restore(irqflags);
break; break;
case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_ONESHOT:
raw_local_irq_save(irqflags); raw_local_irq_save(irqflags);
OIER &= ~OIER_E0; OIER &= ~OIER_E0;
OSSR = OSSR_M0;
raw_local_irq_restore(irqflags); raw_local_irq_restore(irqflags);
break; break;
...@@ -149,6 +153,7 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) ...@@ -149,6 +153,7 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
/* initializing, released, or preparing for suspend */ /* initializing, released, or preparing for suspend */
raw_local_irq_save(irqflags); raw_local_irq_save(irqflags);
OIER &= ~OIER_E0; OIER &= ~OIER_E0;
OSSR = OSSR_M0;
raw_local_irq_restore(irqflags); raw_local_irq_restore(irqflags);
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment