2002-02-28 09:34:28

by Robert Schwebel

[permalink] [raw]
Subject: [PATCH] Serial driver patch for AMD Elans


Below you'll find the part of the AMD Elan patch which deals with a bug in
the processor-integrated serial interface.

The code was discussed with Theodore Ts'o and Russell King - tytso wanted
to send it to Marcelo afterwards, but nothing happened so far, so here's
the patch, against 2.4.18.

Thx,
Robert
--
+--------------------------------------------------------+
| Dipl.-Ing. Robert Schwebel | http://www.pengutronix.de |
| Pengutronix - Linux Solutions for Science and Industry |
| Braunschweiger Str. 79, 31134 Hildesheim, Germany |
| Phone: +49-5121-28619-0 | Fax: +49-5121-28619-4 |
+--------------------------------------------------------+

diff -urN -X kernel-patches/dontdiff linux-2.4.18/drivers/char/serial.c linux-2.4.18-elan/drivers/char/serial.c
--- linux-2.4.18/drivers/char/serial.c Mon Feb 25 20:37:57 2002
+++ linux-2.4.18-elan/drivers/char/serial.c Tue Feb 26 09:58:39 2002
@@ -57,6 +57,11 @@
* 10/00: add in optional software flow control for serial console.
* Kanoj Sarcar <[email protected]> (Modified by Theodore Ts'o)
*
+ * 02/02: Fix for AMD Elan bug in transmit irq routine, by
+ * Christer Weinigel <[email protected]>,
+ * Robert Schwebel <[email protected]>,
+ * Juergen Beisert <[email protected]>,
+ * Theodore Ts'o <[email protected]>
*/

static char *serial_version = "5.05c";
@@ -801,7 +806,7 @@
*/
static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- int status;
+ int status, iir;
struct async_struct * info;
int pass_counter = 0;
struct async_struct *end_mark = 0;
@@ -826,7 +831,7 @@

do {
if (!info->tty ||
- (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
+ ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) {
if (!end_mark)
end_mark = info;
goto next;
@@ -845,7 +850,9 @@
if (status & UART_LSR_DR)
receive_chars(info, &status, regs);
check_modem_status(info);
- if (status & UART_LSR_THRE)
+ if ((status & UART_LSR_THRE) ||
+ /* for buggy ELAN processors */
+ ((iir & UART_IIR_ID) == UART_IIR_THRI))
transmit_chars(info, 0);

next:
@@ -879,7 +886,7 @@
*/
static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
{
- int status;
+ int status, iir;
int pass_counter = 0;
struct async_struct * info;
#ifdef CONFIG_SERIAL_MULTIPORT
@@ -901,6 +908,7 @@
first_multi = inb(multi->port_monitor);
#endif

+ iir = serial_in(info, UART_IIR);
do {
status = serial_inp(info, UART_LSR);
#ifdef SERIAL_DEBUG_INTR
@@ -909,18 +917,21 @@
if (status & UART_LSR_DR)
receive_chars(info, &status, regs);
check_modem_status(info);
- if (status & UART_LSR_THRE)
+ if ((status & UART_LSR_THRE) ||
+ /* For buggy ELAN processors */
+ ((iir & UART_IIR_ID) == UART_IIR_THRI))
transmit_chars(info, 0);
if (pass_counter++ > RS_ISR_PASS_LIMIT) {
-#if 0
+#if SERIAL_DEBUG_INTR
printk("rs_single loop break.\n");
#endif
break;
}
+ iir = serial_in(info, UART_IIR);
#ifdef SERIAL_DEBUG_INTR
- printk("IIR = %x...", serial_in(info, UART_IIR));
+ printk("IIR = %x...", iir);
#endif
- } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
+ } while ((iir & UART_IIR_NO_INT) == 0);
info->last_active = jiffies;
#ifdef CONFIG_SERIAL_MULTIPORT
if (multi->port_monitor)