Subject: Re: [PATCH] ppc32: fix cpm_uart_int() missing interrupts

From: Guillaume Autran <[email-masked]>
Date: Thu, 19 May 2005 13:59:40 -0400

This is a patch to fix a problem that occurs at high baud rates in the 
cpm uart interrupt handling.

In cpm_uart_int(), the existing code reads the event register, processes 
the events, and then clears bits in the event register before returning. 
The problem here is that sometimes event processing generates new events 
quite quickly (i.e. at higher baud rates, the transmit interrupt handler 
puts another character into an SCC's transmit buffer and the SCC clears 
the READY bit almost immediately). In this case, the second interrupt 
can be missed because the scc_scce event register gets cleared after 
processing the first. The port can get hung.

The fix adds a while loop. It reads the event register saving its value 
in the local variable 'events' (as before) then clears the event 
register in the device immediately. It processes the events and tests 
the event register again handling new events that might get generated 
during handling.

Any comment ?

Guillaume.

-- 

=======================================
Guillaume Autran
Senior Software Engineer
MRV Communications, Inc.
Tel: (978) 952-4932 office
E-mail: [email-masked]
======================================= 

diff -Nru linux-2.6.12-rc4.org/drivers/serial/cpm_uart/cpm_uart_core.c linux-2.6.12-rc4.new/drivers/serial/cpm_uart/cpm_uart_core.c
--- linux-2.6.12-rc4.org/drivers/serial/cpm_uart/cpm_uart_core.c	2005-05-07 01:20:31.000000000 -0400
+++ linux-2.6.12-rc4.new/drivers/serial/cpm_uart/cpm_uart_core.c	2005-05-19 13:48:58.000000000 -0400
@@ -335,23 +335,25 @@
 	pr_debug("CPM uart[%d]:IRQ\n", port->line);
 
 	if (IS_SMC(pinfo)) {
-		events = smcp->smc_smce;
+	    while ((events = smcp->smc_smce) & (SMCM_BRKE | SMCM_RX | SMCM_TX)) {
+		smcp->smc_smce = events;
 		if (events & SMCM_BRKE)
-			uart_handle_break(port);
+		    uart_handle_break(port);
 		if (events & SMCM_RX)
-			cpm_uart_int_rx(port, regs);
+		    cpm_uart_int_rx(port, regs);
 		if (events & SMCM_TX)
-			cpm_uart_int_tx(port, regs);
-		smcp->smc_smce = events;
+		    cpm_uart_int_tx(port, regs);
+	    }
 	} else {
-		events = sccp->scc_scce;
+	    while ((events = sccp->scc_scce) & (UART_SCCM_BRKE | UART_SCCM_RX | UART_SCCM_TX)) {
+		sccp->scc_scce = events;
 		if (events & UART_SCCM_BRKE)
-			uart_handle_break(port);
+		    uart_handle_break(port);
 		if (events & UART_SCCM_RX)
-			cpm_uart_int_rx(port, regs);
+		    cpm_uart_int_rx(port, regs);
 		if (events & UART_SCCM_TX)
-			cpm_uart_int_tx(port, regs);
-		sccp->scc_scce = events;
+		    cpm_uart_int_tx(port, regs);
+	    }
 	}
 	return (events) ? IRQ_HANDLED : IRQ_NONE;
 }