serial: 8250_exar: Clear buffer before shutdown
authorRobert Middleton <robert.middleton@rm5248.com>
Thu, 1 Aug 2019 14:56:40 +0000 (10:56 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Sep 2019 10:43:50 +0000 (12:43 +0200)
When closing and shutting down the exar serial port, if the chip
has not finished sending all of the data in its buffer, the
remaining bytes will be lost.  Hold off on the shutdown until the
bytes have all been sent.

Signed-off-by: Robert Middleton <robert.middleton@rm5248.com>
Link: https://lore.kernel.org/r/20190801145640.26080-1-robert.middleton@rm5248.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_exar.c

index 3a39368e6e4736a3a0bee875a3195923c167f2a8..357e20a6566fcabf81073a9820437688fd2749ca 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/tty.h>
 #include <linux/8250_pci.h>
+#include <linux/delay.h>
 
 #include <asm/byteorder.h>
 
@@ -515,6 +516,27 @@ static irqreturn_t exar_misc_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static void
+exar_shutdown(struct uart_port *port)
+{
+       unsigned char lsr;
+       bool tx_complete = 0;
+       struct uart_8250_port *up = up_to_u8250p(port);
+       struct circ_buf *xmit = &port->state->xmit;
+       int i = 0;
+
+       do {
+               lsr = serial_in(up, UART_LSR);
+               if (lsr & (UART_LSR_TEMT | UART_LSR_THRE))
+                       tx_complete = 1;
+               else
+                       tx_complete = 0;
+               msleep(1);
+       } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
+
+       serial8250_do_shutdown(port);
+}
+
 static int
 exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
 {
@@ -555,6 +577,7 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
        uart.port.flags = UPF_SHARE_IRQ | UPF_EXAR_EFR | UPF_FIXED_TYPE | UPF_FIXED_PORT;
        uart.port.irq = pci_irq_vector(pcidev, 0);
        uart.port.dev = &pcidev->dev;
+       uart.port.shutdown = exar_shutdown;
 
        rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler,
                         IRQF_SHARED, "exar_uart", priv);