diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre7/arch/i386/kernel/setup.c linux-2.4.18-pre7-elan/arch/i386/kernel/setup.c --- linux-2.4.18-pre7/arch/i386/kernel/setup.c Thu Jan 24 07:36:14 2002 +++ linux-2.4.18-pre7-elan/arch/i386/kernel/setup.c Thu Jan 24 08:51:01 2002 @@ -329,7 +329,7 @@ { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, { "fpu", 0xf0, 0xff, IORESOURCE_BUSY } }; -#ifdef CONFIG_ELAN +#ifdef CONFIG_MELAN standard_io_resources[1] = { "pic1", 0x20, 0x21, IORESOURCE_BUSY }; standard_io_resources[5] = { "pic2", 0xa0, 0xa1, IORESOURCE_BUSY }; #endif diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre7/drivers/char/serial.c linux-2.4.18-pre7-elan/drivers/char/serial.c --- linux-2.4.18-pre7/drivers/char/serial.c Thu Jan 24 07:36:20 2002 +++ linux-2.4.18-pre7-elan/drivers/char/serial.c Thu Jan 24 08:50:33 2002 @@ -57,6 +57,10 @@ * 10/00: add in optional software flow control for serial console. * Kanoj Sarcar (Modified by Theodore Ts'o) * + * 12/01: Fix for AMD Elan bug in transmit irq routine, by + * Christer Weinigel , + * Robert Schwebel + * Juergen Beisert */ static char *serial_version = "5.05c"; @@ -802,6 +806,7 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) { int status; + int iir; struct async_struct * info; int pass_counter = 0; struct async_struct *end_mark = 0; @@ -825,12 +830,24 @@ #endif do { - if (!info->tty || - (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { - if (!end_mark) - end_mark = info; + + /* + * It seems to be important in a SC520 systems to read + * the UART_IIR register only once per loop. But I think + * this small correction does not disturb the normal + * implementation. + */ + + /* Something to test? */ + /* Or is this UART the source of the interrupt? */ + + if (!info->tty || + ((iir=serial_inp(info, UART_IIR)) & 0x01)) { + if (!end_mark) /* last reached? */ + end_mark = info; /* ..yes, leave loop */ goto next; - } + } + #ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info, UART_IIR)); #endif @@ -839,6 +856,24 @@ info->last_active = jiffies; status = serial_inp(info, UART_LSR); + +#ifdef CONFIG_MELAN + + /* + * There is a bug (misfeature?) in the UART on the AMD Elan + * SC4x0 and SC520 embedded processor series; the THRE bit of + * the line status register seems to be delayed one bit + * clock after the interrupt is generated, so kludge this + * if the IIR indicates a Transmit Holding Register Interrupt + */ + if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + status |= UART_LSR_THRE; +#ifdef SERIAL_DEBUG_INTR + printk("|%x", status); +#endif + } +#endif /* CONFIG_MELAN */ + #ifdef SERIAL_DEBUG_INTR printk("status = %x...", status); #endif @@ -853,7 +888,7 @@ if (!info) { info = IRQ_ports[irq]; if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 +#ifdef SERIAL_DEBUG_INTR printk("rs loop break\n"); #endif break; /* Prevent infinite loops */ @@ -886,6 +921,9 @@ int first_multi = 0; struct rs_multiport_struct *multi; #endif +#ifdef CONFIG_MELAN + int iir; +#endif #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq); @@ -900,7 +938,9 @@ if (multi->port_monitor) first_multi = inb(multi->port_monitor); #endif - +#ifdef CONFIG_MELAN + iir = serial_in(info, UART_IIR); +#endif do { status = serial_inp(info, UART_LSR); #ifdef SERIAL_DEBUG_INTR @@ -909,10 +949,25 @@ if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); + +#ifdef CONFIG_MELAN + + /* + * There is a bug (misfeature?) in the UART on the AMD Elan + * SC4x0 and SC520 embedded processor series; the THRE bit of + * the line status register seems to be delayed one bit + * clock after the interrupt is generated, so kludge this + * if the IIR indicates a Transmit Holding Register Interrupt + */ + + if ((iir & UART_IIR_ID) == UART_IIR_THRI) + status |= UART_LSR_THRE; +#endif if (status & UART_LSR_THRE) transmit_chars(info, 0); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 +#ifdef SERIAL_DEBUG_INTR printk("rs_single loop break.\n"); #endif break; @@ -920,7 +975,7 @@ #ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info, UART_IIR)); #endif - } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + } while (!((iir = serial_in(info, UART_IIR)) & UART_IIR_NO_INT)); info->last_active = jiffies; #ifdef CONFIG_SERIAL_MULTIPORT if (multi->port_monitor)