tty: serial: 8250_core: read only RX if there is something in the FIFO
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Wed, 10 Sep 2014 19:29:58 +0000 (21:29 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 26 Sep 2014 16:01:56 +0000 (18:01 +0200)
The serial8250_do_startup() function unconditionally clears the
interrupts and for that it reads from the RX-FIFO without checking if
there is a byte in the FIFO or not. This works fine on OMAP4+ HW like
AM335x or DRA7.
OMAP3630 ES1.1 (which means probably all OMAP3 and earlier) does not like
this:

|Unhandled fault: external abort on non-linefetch (0x1028) at 0xfb020000
|Internal error: : 1028 [#1] ARM
|Modules linked in:
|CPU: 0 PID: 1 Comm: swapper Not tainted 3.16.0-00022-g7edcb57-dirty #1213
|task: de0572c0 ti: de058000 task.ti: de058000
|PC is at mem32_serial_in+0xc/0x1c
|LR is at serial8250_do_startup+0x220/0x85c
|Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
|Control: 10c5387d  Table: 80004019  DAC: 00000015
|[<c03051d4>] (mem32_serial_in) from [<c0307fe8>] (serial8250_do_startup+0x220/0x85c)
|[<c0307fe8>] (serial8250_do_startup) from [<c0309e00>] (omap_8250_startup+0x5c/0xe0)
|[<c0309e00>] (omap_8250_startup) from [<c030863c>] (serial8250_startup+0x18/0x2c)
|[<c030863c>] (serial8250_startup) from [<c030394c>] (uart_startup+0x78/0x1d8)
|[<c030394c>] (uart_startup) from [<c0304678>] (uart_open+0xe8/0x114)
|[<c0304678>] (uart_open) from [<c02e9e10>] (tty_open+0x1a8/0x5a4)

Reviewed-by: Tony Lindgren <tony@atomide.com>
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_core.c

index 3cf5c98013e4e90e16ac6cae6556426980ce9e82..547afde9fddaf017e310dd9425f0248d9143c990 100644 (file)
@@ -2088,8 +2088,8 @@ int serial8250_do_startup(struct uart_port *port)
        /*
         * Clear the interrupt registers.
         */
-       serial_port_in(port, UART_LSR);
-       serial_port_in(port, UART_RX);
+       if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
+               serial_port_in(port, UART_RX);
        serial_port_in(port, UART_IIR);
        serial_port_in(port, UART_MSR);
 
@@ -2250,8 +2250,8 @@ dont_test_tx_en:
         * saved flags to avoid getting false values from polling
         * routines or the previous session.
         */
-       serial_port_in(port, UART_LSR);
-       serial_port_in(port, UART_RX);
+       if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
+               serial_port_in(port, UART_RX);
        serial_port_in(port, UART_IIR);
        serial_port_in(port, UART_MSR);
        up->lsr_saved_flags = 0;
@@ -2344,7 +2344,8 @@ void serial8250_do_shutdown(struct uart_port *port)
         * Read data port to reset things, and then unlink from
         * the IRQ chain.
         */
-       serial_port_in(port, UART_RX);
+       if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
+               serial_port_in(port, UART_RX);
        serial8250_rpm_put(up);
 
        del_timer_sync(&up->timer);