mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 14:52:05 +00:00
[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: Thomas Gleixner <tglx@linutronix.de> Acked-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
c2ec21c5c8
commit
91bc51d8a1
@ -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
|
||||
@ -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));
|
||||
@ -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
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user