diff mbox

[3/3] 8250: add workaround for MPC8[356]xx UART break IRQ storm

Message ID 1322783258-20443-4-git-send-email-paul.gortmaker@windriver.com (mailing list archive)
State Superseded, archived
Delegated to: Benjamin Herrenschmidt
Headers show

Commit Message

Paul Gortmaker Dec. 1, 2011, 11:47 p.m. UTC
Sending a break on the SOC UARTs found in some MPC83xx/85xx/86xx
chips seems to cause a short lived IRQ storm (/proc/interrupts
typically shows somewhere between 300 and 1500 events).  Unfortunately
this renders SysRQ over the serial console completely inoperable.

The suggested workaround in the errata is to read the Rx register,
wait one character period, and then read the Rx register again.
We achieve this by tracking the old LSR value, and on the subsequent
interrupt event after a break, we don't read LSR, instead we just
read the RBR again and return immediately.

The "fsl,ns16550" is used in the compatible field of the serial
device to mark UARTs known to have this issue.

Thanks to Scott Wood for providing the errata data which led to
a much cleaner fix.

Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
---
 arch/powerpc/kernel/legacy_serial.c |   11 +++++++++++
 drivers/tty/serial/8250.c           |   11 ++++++++++-
 include/linux/serial_8250.h         |    5 +++++
 3 files changed, 26 insertions(+), 1 deletions(-)

Comments

Scott Wood Dec. 1, 2011, 11:51 p.m. UTC | #1
On 12/01/2011 05:47 PM, Paul Gortmaker wrote:
> diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
> index 8c660af..b0f4042 100644
> --- a/include/linux/serial_8250.h
> +++ b/include/linux/serial_8250.h
> @@ -18,6 +18,11 @@
>  #define UART_BUG_TXEN	(1 << 1)	/* buggy TX IIR status */
>  #define UART_BUG_NOMSR	(1 << 2)	/* buggy MSR status bits (Au1x00) */
>  #define UART_BUG_THRE	(1 << 3)	/* buggy THRE reassertion */
> +#ifdef CONFIG_PPC32
> +#define UART_BUG_FSLBK	(1 << 4)	/* buggy FSL break IRQ storm */
> +#else	/* help GCC optimize away IRQ handler errata code for ARCH != PPC32 */
> +#define UART_BUG_FSLBK	0
> +#endif

I believe this bug still exists on our 64-bit chips.

-Scott
Paul Gortmaker Dec. 2, 2011, 12:05 a.m. UTC | #2
On 11-12-01 06:51 PM, Scott Wood wrote:
> On 12/01/2011 05:47 PM, Paul Gortmaker wrote:
>> diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
>> index 8c660af..b0f4042 100644
>> --- a/include/linux/serial_8250.h
>> +++ b/include/linux/serial_8250.h
>> @@ -18,6 +18,11 @@
>>  #define UART_BUG_TXEN	(1 << 1)	/* buggy TX IIR status */
>>  #define UART_BUG_NOMSR	(1 << 2)	/* buggy MSR status bits (Au1x00) */
>>  #define UART_BUG_THRE	(1 << 3)	/* buggy THRE reassertion */
>> +#ifdef CONFIG_PPC32
>> +#define UART_BUG_FSLBK	(1 << 4)	/* buggy FSL break IRQ storm */
>> +#else	/* help GCC optimize away IRQ handler errata code for ARCH != PPC32 */
>> +#define UART_BUG_FSLBK	0
>> +#endif
> 
> I believe this bug still exists on our 64-bit chips.

OK, I'll simply change the above to CONFIG_PPC then.

Thanks,
Paul.

> 
> -Scott
>
Kumar Gala Dec. 2, 2011, 12:17 a.m. UTC | #3
On Dec 1, 2011, at 6:05 PM, Paul Gortmaker wrote:

> On 11-12-01 06:51 PM, Scott Wood wrote:
>> On 12/01/2011 05:47 PM, Paul Gortmaker wrote:
>>> diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
>>> index 8c660af..b0f4042 100644
>>> --- a/include/linux/serial_8250.h
>>> +++ b/include/linux/serial_8250.h
>>> @@ -18,6 +18,11 @@
>>> #define UART_BUG_TXEN	(1 << 1)	/* buggy TX IIR status */
>>> #define UART_BUG_NOMSR	(1 << 2)	/* buggy MSR status bits (Au1x00) */
>>> #define UART_BUG_THRE	(1 << 3)	/* buggy THRE reassertion */
>>> +#ifdef CONFIG_PPC32
>>> +#define UART_BUG_FSLBK	(1 << 4)	/* buggy FSL break IRQ storm */
>>> +#else	/* help GCC optimize away IRQ handler errata code for ARCH != PPC32 */
>>> +#define UART_BUG_FSLBK	0
>>> +#endif
>> 
>> I believe this bug still exists on our 64-bit chips.
> 
> OK, I'll simply change the above to CONFIG_PPC then.

It does, the bug is in the uart IP which I don't think we ever plan on fixing, so 32 or 64-bit parts will have it for ever and ever ;)

- k
Alan Cox Dec. 2, 2011, 12:57 a.m. UTC | #4
> @@ -1553,7 +1554,15 @@ static void serial8250_handle_port(struct
> uart_8250_port *up) 
>  	spin_lock_irqsave(&up->port.lock, flags);
>  
> -	status = serial_inp(up, UART_LSR);
> +	/* Workaround for IRQ storm errata on break with Freescale
> 16550 */
> +	if (UART_BUG_FSLBK & up->port.bugs && up->lsr_last &
> UART_LSR_BI) {
> +		up->lsr_last &= ~UART_LSR_BI;
> +		serial_inp(up, UART_RX);
> +		spin_unlock_irqrestore(&up->port.lock, flags);
> +		return;
> +	}
> +
> +	status = up->lsr_last = serial_inp(up, UART_LSR);

We've now had a recent pile of IRQ function changes adding more quirk
bits and special casing. This doesn't scale. We either need to make
handle_port a method the specific drivers can override or you could
hide the mess in your serial_inp implementation and not touch any core
code.

I really don't mind which but I suspect dealing with it in your
serial_inp handler so that when you read UART_LSR you do the fixup
might be simplest providing you can just do that.

Sorting out a ->handle_port override is probably ultimately the right
thing to do and then we can push some of the other funnies out further.

At this point it's becoming increasingly clear that 8250 UART cloners
are both very bad at cloning the things accurately, and very busy adding
extra useful features so we need to start to treat 8250.c as a library
you can wrap.

Alan
Paul Gortmaker Dec. 2, 2011, 1:42 a.m. UTC | #5
On Thu, Dec 1, 2011 at 7:57 PM, Alan Cox <alan@linux.intel.com> wrote:
>
>> @@ -1553,7 +1554,15 @@ static void serial8250_handle_port(struct
>> uart_8250_port *up)
>>       spin_lock_irqsave(&up->port.lock, flags);
>>
>> -     status = serial_inp(up, UART_LSR);
>> +     /* Workaround for IRQ storm errata on break with Freescale
>> 16550 */
>> +     if (UART_BUG_FSLBK & up->port.bugs && up->lsr_last &
>> UART_LSR_BI) {
>> +             up->lsr_last &= ~UART_LSR_BI;
>> +             serial_inp(up, UART_RX);
>> +             spin_unlock_irqrestore(&up->port.lock, flags);
>> +             return;
>> +     }
>> +
>> +     status = up->lsr_last = serial_inp(up, UART_LSR);
>
> We've now had a recent pile of IRQ function changes adding more quirk
> bits and special casing. This doesn't scale. We either need to make
> handle_port a method the specific drivers can override or you could
> hide the mess in your serial_inp implementation and not touch any core
> code.

To be fair, this one is zero cost for !PPC, but I understand your point,
and the idea of hiding it somehow in serial_inp was something that
never crossed my mind.  I'll look into seeing if I can abuse that.

>
> I really don't mind which but I suspect dealing with it in your
> serial_inp handler so that when you read UART_LSR you do the fixup
> might be simplest providing you can just do that.
>
> Sorting out a ->handle_port override is probably ultimately the right
> thing to do and then we can push some of the other funnies out further.
>
> At this point it's becoming increasingly clear that 8250 UART cloners
> are both very bad at cloning the things accurately, and very busy adding
> extra useful features so we need to start to treat 8250.c as a library
> you can wrap.

Sad but true.  It is like a time warp back to lib8390.c  --- whee.

P.

>
> Alan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Cox Dec. 2, 2011, 11:30 a.m. UTC | #6
> > OK, I'll simply change the above to CONFIG_PPC then.
> 
> It does, the bug is in the uart IP which I don't think we ever plan on fixing, so 32 or 64-bit parts will have it for ever and ever ;)

It should be runtime selected, there should be no ifdefs here.
Paul Gortmaker Dec. 2, 2011, 4:34 p.m. UTC | #7
On 11-12-02 06:30 AM, Alan Cox wrote:
>>> OK, I'll simply change the above to CONFIG_PPC then.
>>
>> It does, the bug is in the uart IP which I don't think we ever plan on fixing, so 32 or 64-bit parts will have it for ever and ever ;)
> 
> It should be runtime selected, there should be no ifdefs here.

The ifdef wasn't strictly required; it just made it so gcc would
toss the errata code out of the irq handler for !PPC.  Anyway it
will be a moot point if I can somehow hide all the mess by snooping
serial_inp() traffic and deploying the errata fix from there....

P.

>
Scott Wood Dec. 2, 2011, 5:27 p.m. UTC | #8
On 12/02/2011 10:34 AM, Paul Gortmaker wrote:
> On 11-12-02 06:30 AM, Alan Cox wrote:
>>>> OK, I'll simply change the above to CONFIG_PPC then.
>>>
>>> It does, the bug is in the uart IP which I don't think we ever plan on fixing, so 32 or 64-bit parts will have it for ever and ever ;)
>>
>> It should be runtime selected, there should be no ifdefs here.
> 
> The ifdef wasn't strictly required; it just made it so gcc would
> toss the errata code out of the irq handler for !PPC.  Anyway it
> will be a moot point if I can somehow hide all the mess by snooping
> serial_inp() traffic and deploying the errata fix from there....

Eww.

If it's not to be allowed in the main 8250 code (even for ppc builds
only), a custom handle_port sounds like a saner option.

-Scott
diff mbox

Patch

diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index c7b5afe..dd232ca 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -476,6 +476,15 @@  static void __init fixup_port_mmio(int index,
 	port->membase = ioremap(port->mapbase, 0x100);
 }
 
+static void __init fixup_port_bugs(int index,
+				   struct device_node *np,
+				   struct plat_serial8250_port *port)
+{
+	DBG("fixup_port_bugs(%d)\n", index);
+
+	if (of_device_is_compatible(np, "fsl,ns16550"))
+		port->bugs = UART_BUG_FSLBK;
+}
 /*
  * This is called as an arch initcall, hopefully before the PCI bus is
  * probed and/or the 8250 driver loaded since we need to register our
@@ -512,6 +521,8 @@  static int __init serial_dev_init(void)
 			fixup_port_pio(i, np, port);
 		if ((port->iotype == UPIO_MEM) || (port->iotype == UPIO_TSI))
 			fixup_port_mmio(i, np, port);
+
+		fixup_port_bugs(i, np, port);
 	}
 
 	DBG("Registering platform serial ports\n");
diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c
index f99f27c..32e9821 100644
--- a/drivers/tty/serial/8250.c
+++ b/drivers/tty/serial/8250.c
@@ -142,6 +142,7 @@  struct uart_8250_port {
 	unsigned char		mcr_mask;	/* mask of user bits */
 	unsigned char		mcr_force;	/* mask of forced bits */
 	unsigned char		cur_iotype;	/* Running I/O type */
+	unsigned char		lsr_last;	/* LSR of last IRQ event */
 
 	/*
 	 * Some bits in registers are cleared on a read, so they must
@@ -1553,7 +1554,15 @@  static void serial8250_handle_port(struct uart_8250_port *up)
 
 	spin_lock_irqsave(&up->port.lock, flags);
 
-	status = serial_inp(up, UART_LSR);
+	/* Workaround for IRQ storm errata on break with Freescale 16550 */
+	if (UART_BUG_FSLBK & up->port.bugs && up->lsr_last & UART_LSR_BI) {
+		up->lsr_last &= ~UART_LSR_BI;
+		serial_inp(up, UART_RX);
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return;
+	}
+
+	status = up->lsr_last = serial_inp(up, UART_LSR);
 
 	DEBUG_INTR("status = %x...", status);
 
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 8c660af..b0f4042 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -18,6 +18,11 @@ 
 #define UART_BUG_TXEN	(1 << 1)	/* buggy TX IIR status */
 #define UART_BUG_NOMSR	(1 << 2)	/* buggy MSR status bits (Au1x00) */
 #define UART_BUG_THRE	(1 << 3)	/* buggy THRE reassertion */
+#ifdef CONFIG_PPC32
+#define UART_BUG_FSLBK	(1 << 4)	/* buggy FSL break IRQ storm */
+#else	/* help GCC optimize away IRQ handler errata code for ARCH != PPC32 */
+#define UART_BUG_FSLBK	0
+#endif
 
 /*
  * This is the platform device platform_data structure