From 33e34a55dca5e9ffbfeaa3c6705b8d33550f8891 Mon Sep 17 00:00:00 2001
From: Thomas Chou <thomas@wytron.com.tw>
Date: Tue, 15 Jan 2008 11:36:39 +0800
Subject: [PATCH] nios2: recheck pending irq

Wally reported, interrupts was being serviced more times than it was
actually happening. The original codes service multiple interrupts that
are detected at the same time.  However, the code assumes that
interrupts will not be enabled by the "call process_int" function.
Unfortunately Linux enables interrupts when tasklets are run, and the
tasklets are run before "call process_int" returns. For example, two
interrupts (a high priority interrupt that schedules a tasklet, and our
interrupt) are detected at the same time.  The high priority interrupt
is serviced first by "call process_int", but before returning the
tasklet is run with interrupts enabled.  As soon as interrupts are
enabled, our interrupt occurs.  It is serviced and returns to the
tasklet code.  When the tasklet completes the "call process_int"
returns, then the original loop processes our interrupt a second time
(because our interrupt bit is still set in register r12).

This patch checks pending interrupt requests again before serving.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
 linux-2.6.x/arch/nios2nommu/kernel/entry.S |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/linux-2.6.x/arch/nios2nommu/kernel/entry.S b/linux-2.6.x/arch/nios2nommu/kernel/entry.S
index 7f71a01..8b4ea34 100644
--- a/linux-2.6.x/arch/nios2nommu/kernel/entry.S
+++ b/linux-2.6.x/arch/nios2nommu/kernel/entry.S
@@ -169,7 +169,7 @@ ENTRY(inthandler)
 
 	addi	ea,ea,-4		/* re-issue the interrupted instruction */
 	stw	ea,PT_EA(sp)
-	rdctl	r9,ienable		/* Isolate possible interrupts */
+2:	rdctl	r9,ienable		/* Isolate possible interrupts */
 	and	r12,r12,r9
 	beq	r12,r0,ret_from_interrupt /* No one to service done */
 	movi	r4,%lo(-1)		/* Start from bit position 0, highest priority */
@@ -184,9 +184,9 @@ ENTRY(inthandler)
 	PUSH	r4			/* Save state for return */
 	PUSH	r12
 	call	process_int
-	POP	r12
-	POP	r4
-	br	1b			/* Check for other interrupts while here */
+	addi	sp,sp,8			/* pop r12,r4 */
+	rdctl	r12,ipending		/* check again if irq still pending */
+	bne	r12,r0,2b
 
 ENTRY(ret_from_interrupt)
 	ldw	r4,PT_STATUS_EXTENSION(sp)
-- 
1.5.3.3

