Skip to content

Commit

Permalink
[ARM] pxa: fix one-shot timer mode
Browse files Browse the repository at this point in the history
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: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Russell King authored and Russell King committed Nov 8, 2007
1 parent c2ec21c commit 91bc51d
Showing 1 changed file with 14 additions and 9 deletions.
23 changes: 14 additions & 9 deletions arch/arm/mach-pxa/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pxa_ost0_interrupt(int irq, void *dev_id)
if (c->mode == CLOCK_EVT_MODE_ONESHOT) {
/* Disarm the compare/match, signal the event. */
OIER &= ~OIER_E0;
OSSR = OSSR_M0;
c->event_handler(c);
} else if (c->mode == CLOCK_EVT_MODE_PERIODIC) {
/* Call the event handler as many times as necessary
Expand Down Expand Up @@ -100,9 +101,9 @@ pxa_ost0_interrupt(int irq, void *dev_id)
* anything that might put us "very close".
*/
#define MIN_OSCR_DELTA 16
do {
do {
OSSR = OSSR_M0;
next_match = (OSMR0 += LATCH);
next_match = (OSMR0 += LATCH);
c->event_handler(c);
} while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA)
&& (c->mode == CLOCK_EVT_MODE_PERIODIC));
Expand All @@ -114,14 +115,16 @@ pxa_ost0_interrupt(int irq, void *dev_id)
static int
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);
OSMR0 = OSCR + delta;
OSSR = OSSR_M0;
raw_local_irq_save(flags);
OIER |= OIER_E0;
raw_local_irq_restore(irqflags);
return 0;
next = OSCR + delta;
OSMR0 = next;
oscr = OSCR;
raw_local_irq_restore(flags);

return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
}

static void
Expand All @@ -132,15 +135,16 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
raw_local_irq_save(irqflags);
OSMR0 = OSCR + LATCH;
OSSR = OSSR_M0;
OIER |= OIER_E0;
OSMR0 = OSCR + LATCH;
raw_local_irq_restore(irqflags);
break;

case CLOCK_EVT_MODE_ONESHOT:
raw_local_irq_save(irqflags);
OIER &= ~OIER_E0;
OSSR = OSSR_M0;
raw_local_irq_restore(irqflags);
break;

Expand All @@ -149,6 +153,7 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
/* initializing, released, or preparing for suspend */
raw_local_irq_save(irqflags);
OIER &= ~OIER_E0;
OSSR = OSSR_M0;
raw_local_irq_restore(irqflags);
break;

Expand Down

0 comments on commit 91bc51d

Please sign in to comment.