Pierre Peiffer wrote:
> 
> Anders Larsen wrote:
> 
> > I have had the same problem on the built-in serial interface of an AMD �lan
> > SC410 CPU.
> > It turned out to be a bug in AMD's 8250 emulation, which caused the THRE bit in
> > the LSR to be set one bit-time *after* the interrupt was generated, instead of
> > simultaneously.
> > This, in turn, causes the interrupt service routine in serial.c to ignore the Tx
> > interrupt.
> > I have patches ready for the AMD SC4x0 bug - please let me know if you are using
> > the same (or a similar AMD) CPU and, if yes, which kernel version you are using.
> >
> 
> Hi,
> 
>     We are using an SC520 CPU in our project and have noticed the same problem. So,
> I'm very interested by your patch. It will be very kind of you if you can send to
> me the patch. We are using Linux kernel v2.2.13 (with RT-Linux 2.0).
> 
>     Thank you very much in advance !
> 
>     Pierre PEIFFER

Hi,

I'm surprised to learn that AMD has ported the bug to the SC5x0 series, as it
has
actually been known for years now (albeit only to some insiders; I found out the
hard way only about 4 weeks ago).

I've attached a verbatim description of the problem as well as a patch against
the 2.2.15 kernel (you should be able to apply it to 2.2.13 without too much
work,
the line numbers haven't changed that much).

--
cheers
  Anders Larsen

Q: What does the CE in Windows CE stand for?
A: Caveat Emptor
From: Christer Weinigel <[EMAIL PROTECTED]>
Subject: Re: Why does /dev/cua3 respond so slowly?
Date: 26 Nov 1998 00:00:00 GMT
Message-ID: <[EMAIL PROTECTED]>
Original-Date: 25 Nov 1998 15:21:33 -0000
Sender: [EMAIL PROTECTED]
Original-Message-ID: <[EMAIL PROTECTED]>
References: <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
X-Orcpt: rfc822;[EMAIL PROTECTED]
Organization: Internet mailing list
Newsgroups: fa.linux.serial
X-Loop: [EMAIL PROTECTED]

Theodore Y. Ts'o quoted LD Landis who wrote:

>     When running RedHat 5.0 (kernel 2.0.34), /dev/cua3 works fine.
>     When running RedHat 5.2 (kernel 2.0.36), /dev/cua3 is *SLOW*.
>
>     When I say slow, we are talking several seconds to echo a single
>     character.  It's as if each character is sent after there is some sort of
>     long time out... like it's not interrupt driven...

That reminds me.  There is a bug in the AMD Elan SC400 CPU (an
embedded 486, AMD has some specifications on their site) which causes
symptoms like that.

The problem is that the UART_LSR_THRE bit is delayed one bit clock
after the interrupt line is asserted, i.e. if the serial port is
running at 1200 bps, and the tranmitter becomes empty and causes an
interupt, the interrupt is signalled about a millisecond (1/1200
second) _before_ the THRE bit is set.

This means that when the interrupt handler is entered after the
interrupt, the THRE bit is still clear, the handler believes that
there is nothing to be done and returns.  When the THRE bit finally is
set, it is too late, the interrupt cause has been cleared and there
won't be any more transmit interrupts.  The transmitter will start
again if there is an interrupt for some other reason or after a
timeout.  This gives the same symptoms as you described, the driver
transmits some data and then hangs for a long time before transmitting
some data again.

The bug is very dependent on timing, when I was trying to find out
what was happening and added a few printk:s to the source, the bug
disappeared; I was running the serial port at 38400 bps and my guess
is that the printk delayed the check for the THRE bit more than a bit
clock (about 30 microseconds).  If your CPU has a similar bug there
might be some slight changes in the interrupt timing that makes the
problem show up with 2.0.36 but not with 2.0.34.

Anyway, the patch below works around the problem by reading the IIR
register instead (which works correctly on the AMD).  Theodore, would
it be a good idea to change the serial driver to use the IIR register
instead of the THRE bit, or will this break with other chips?

  /Christer

ps.  I do hope that I remembered to report this bug to AMD, I'd better
check again, when doing rapid prototyping and approaching deadlines,
things like this have a tendency to be forgotten :-)

--- serial.c.orig       Sun Jun  7 18:41:20 1998
+++ serial.c    Wed Nov 25 16:02:29 1998
@@ -610,6 +610,7 @@
        int first_multi = 0;
        struct async_struct * info;
        struct rs_multiport_struct *multi;
+       int iir;
        
 #ifdef SERIAL_DEBUG_INTR
        printk("rs_interrupt_single(%d)...", irq);
@@ -623,11 +624,21 @@
        if (multi->port_monitor)
                first_multi = inb(multi->port_monitor);
 
+       iir = serial_in(info, UART_IIR);
        do {
                status = serial_inp(info, UART_LSR) & info->read_status_mask;
 #ifdef SERIAL_DEBUG_INTR
                printk("status = %x...", status);
 #endif
+#ifdef AMD_ELAN_BUGFIX
+               /* There is a bug (misfeature?) in the UART on the
+                  Elan SC400, 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_DR)
                        receive_chars(info, &status);
                check_modem_status(info);
@@ -639,7 +650,7 @@
 #endif
                        break;
                }
-       } 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;
        if (multi->port_monitor)
                printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",

-
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to [EMAIL PROTECTED]
<img src="http://www.dejanews.com/ndc2.xp?04.61596.4.1.1270.58520">
diff -u --recursive v2.2.15/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.2.15/drivers/char/serial.c       Thu May  4 11:28:36 2000
+++ linux/drivers/char/serial.c Thu May  4 11:38:33 2000
@@ -622,7 +622,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 */
@@ -651,6 +651,7 @@
        int status;
        int pass_counter = 0;
        struct async_struct * info;
+       int iir;
 #ifdef CONFIG_SERIAL_MULTIPORT 
        int first_multi = 0;
        struct rs_multiport_struct *multi;
@@ -670,6 +671,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
@@ -678,15 +680,25 @@
                if (status & UART_LSR_DR)
                        receive_chars(info, &status);
                check_modem_status(info);
-               if (status & UART_LSR_THRE)
-                       transmit_chars(info, 0);
+               /* There is a bug in the UART on the AMD Elan SC4x0
+                 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 (status & UART_LSR_THRE || (iir & UART_IIR_ID) == UART_IIR_THRI)
+                       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;
                }
-       } 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)

Reply via email to