summary refs log tree commit diff
path: root/arch/powerpc/platforms/powermac/low_i2c.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-04-18 22:16:48 +0000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-04-23 11:04:28 +1000
commit3027691e58bfb21f6ea2e9f1d225d11b4e2b20e2 (patch)
tree4ff705ce7b3b9b51563777b584d90cf4d0b1a348 /arch/powerpc/platforms/powermac/low_i2c.c
parent2ef822c55371b20548d4f58193c580407a5d738d (diff)
downloadlinux-3027691e58bfb21f6ea2e9f1d225d11b4e2b20e2.tar.gz
powerpc/pmac: Don't add_timer() twice
If the interrupt and the timeout happen roughly at the same
time, we can get into a situation where the timer function
is run while the interrupt has already been processed. In
this case, the timer function might end up doing an add_timer
on an already pending timer, causing a BUG_ON() to trigger.

Instead, just skip the whole timeout operation if we see that
the timer is pending. The spinlock ensures that the only way
that happens is if we already started a new operation and thus
the timeout can be ignored.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/powermac/low_i2c.c')
-rw-r--r--arch/powerpc/platforms/powermac/low_i2c.c9
1 files changed, 9 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index 996c5ff7824b..03685a329d7d 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -366,11 +366,20 @@ static void kw_i2c_timeout(unsigned long data)
 	unsigned long flags;
 
 	spin_lock_irqsave(&host->lock, flags);
+
+	/*
+	 * If the timer is pending, that means we raced with the
+	 * irq, in which case we just return
+	 */
+	if (timer_pending(&host->timeout_timer))
+		goto skip;
+
 	kw_i2c_handle_interrupt(host, kw_read_reg(reg_isr));
 	if (host->state != state_idle) {
 		host->timeout_timer.expires = jiffies + KW_POLL_TIMEOUT;
 		add_timer(&host->timeout_timer);
 	}
+ skip:
 	spin_unlock_irqrestore(&host->lock, flags);
 }