Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+14 −9
Original line number Original line Diff line number Diff line
@@ -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
@@ -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)
	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)
		/* 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;