serial: xuartps: Removed unwanted checks while reading the error conditions
[cascardo/linux.git] / drivers / tty / serial / xilinx_uartps.c
index 98f7b59..39f41bf 100644 (file)
@@ -198,58 +198,43 @@ struct cdns_platform_data {
 #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
                clk_rate_change_nb);
 
-static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
+/**
+ * cdns_uart_handle_rx - Handle the received bytes along with Rx errors.
+ * @dev_id: Id of the UART port
+ * @isrstatus: The interrupt status register value as read
+ * Return: None
+ */
+static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus)
 {
+       struct uart_port *port = (struct uart_port *)dev_id;
        struct cdns_uart *cdns_uart = port->private_data;
-       bool is_rxbs_support;
+       unsigned int data;
        unsigned int rxbs_status = 0;
        unsigned int status_mask;
+       unsigned int framerrprocessed = 0;
+       char status = TTY_NORMAL;
+       bool is_rxbs_support;
 
        is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;
 
-       /*
-        * There is no hardware break detection, so we interpret framing
-        * error with all-zeros data as a break sequence. Most of the time,
-        * there's another non-zero byte at the end of the sequence.
-        */
-       if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) {
-               while (!(readl(port->membase + CDNS_UART_SR) &
-                                       CDNS_UART_SR_RXEMPTY)) {
-                       if (!readl(port->membase + CDNS_UART_FIFO)) {
-                               port->read_status_mask |= CDNS_UART_IXR_BRK;
-                               isrstatus &= ~CDNS_UART_IXR_FRAMING;
-                       }
-               }
-               writel(CDNS_UART_IXR_FRAMING, port->membase + CDNS_UART_ISR);
-       }
-
-       /* drop byte with parity error if IGNPAR specified */
-       if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY)
-               isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT);
-
-       isrstatus &= port->read_status_mask;
-       isrstatus &= ~port->ignore_status_mask;
-       status_mask = port->read_status_mask;
-       status_mask &= ~port->ignore_status_mask;
-
-       if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG)))
-               return;
-
-       while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) {
-               u32 data;
-               char status = TTY_NORMAL;
-
+       while ((readl(port->membase + CDNS_UART_SR) &
+               CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
                if (is_rxbs_support)
                        rxbs_status = readl(port->membase + CDNS_UART_RXBS);
-
                data = readl(port->membase + CDNS_UART_FIFO);
-
-               /* Non-NULL byte after BREAK is garbage (99%) */
-               if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) {
-                       port->read_status_mask &= ~CDNS_UART_IXR_BRK;
-                       port->icount.brk++;
-                       if (uart_handle_break(port))
+               port->icount.rx++;
+               /*
+                * There is no hardware break detection in Zynq, so we interpret
+                * framing error with all-zeros data as a break sequence.
+                * Most of the time, there's another non-zero byte at the
+                * end of the sequence.
+                */
+               if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) {
+                       if (!data) {
+                               port->read_status_mask |= CDNS_UART_IXR_BRK;
+                               framerrprocessed = 1;
                                continue;
+                       }
                }
                if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) {
                        port->icount.brk++;
@@ -258,19 +243,30 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
                                continue;
                }
 
+               isrstatus &= port->read_status_mask;
+               isrstatus &= ~port->ignore_status_mask;
+               status_mask = port->read_status_mask;
+               status_mask &= ~port->ignore_status_mask;
+
+               if (data &&
+                   (port->read_status_mask & CDNS_UART_IXR_BRK)) {
+                       port->read_status_mask &= ~CDNS_UART_IXR_BRK;
+                       port->icount.brk++;
+                       if (uart_handle_break(port))
+                               continue;
+               }
+
                if (uart_handle_sysrq_char(port, data))
                        continue;
 
-               port->icount.rx++;
-
                if (is_rxbs_support) {
                        if ((rxbs_status & CDNS_UART_RXBS_PARITY)
-                               && (status_mask & CDNS_UART_IXR_PARITY)) {
+                           && (status_mask & CDNS_UART_IXR_PARITY)) {
                                port->icount.parity++;
                                status = TTY_PARITY;
                        }
                        if ((rxbs_status & CDNS_UART_RXBS_FRAMING)
-                               && (status_mask & CDNS_UART_IXR_PARITY)) {
+                           && (status_mask & CDNS_UART_IXR_PARITY)) {
                                port->icount.frame++;
                                status = TTY_FRAME;
                        }
@@ -279,53 +275,67 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
                                port->icount.parity++;
                                status = TTY_PARITY;
                        }
-                       if (isrstatus & CDNS_UART_IXR_FRAMING) {
+                       if ((isrstatus & CDNS_UART_IXR_FRAMING) &&
+                           !framerrprocessed) {
                                port->icount.frame++;
                                status = TTY_FRAME;
                        }
                }
-               if (isrstatus & CDNS_UART_IXR_OVERRUN)
+               if (isrstatus & CDNS_UART_IXR_OVERRUN) {
                        port->icount.overrun++;
-
-               uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN,
-                                data, status);
+                       tty_insert_flip_char(&port->state->port, 0,
+                                            TTY_OVERRUN);
+               }
+               tty_insert_flip_char(&port->state->port, data, status);
+               isrstatus = 0;
        }
+       spin_unlock(&port->lock);
        tty_flip_buffer_push(&port->state->port);
+       spin_lock(&port->lock);
 }
 
-static void cdns_uart_handle_tx(struct uart_port *port)
+/**
+ * cdns_uart_handle_tx - Handle the bytes to be Txed.
+ * @dev_id: Id of the UART port
+ * Return: None
+ */
+static void cdns_uart_handle_tx(void *dev_id)
 {
+       struct uart_port *port = (struct uart_port *)dev_id;
        unsigned int numbytes;
 
        if (uart_circ_empty(&port->state->xmit)) {
                writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR);
-               return;
-       }
-
-       numbytes = port->fifosize;
-       while (numbytes && !uart_circ_empty(&port->state->xmit) &&
-              !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
-               /*
-                * Get the data from the UART circular buffer
-                * and write it to the cdns_uart's TX_FIFO
-                * register.
-                */
-               writel(port->state->xmit.buf[port->state->xmit.tail],
-                       port->membase + CDNS_UART_FIFO);
-               port->icount.tx++;
-
-               /*
-                * Adjust the tail of the UART buffer and wrap
-                * the buffer if it reaches limit.
-                */
-               port->state->xmit.tail =
-                       (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
+       } else {
+               numbytes = port->fifosize;
+               while (numbytes && !uart_circ_empty(&port->state->xmit) &&
+                      !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
+                       /*
+                        * Get the data from the UART circular buffer
+                        * and write it to the cdns_uart's TX_FIFO
+                        * register.
+                        */
+                       writel(
+                               port->state->xmit.buf[port->state->xmit.
+                               tail], port->membase + CDNS_UART_FIFO);
+
+                       port->icount.tx++;
+
+                       /*
+                        * Adjust the tail of the UART buffer and wrap
+                        * the buffer if it reaches limit.
+                        */
+                       port->state->xmit.tail =
+                               (port->state->xmit.tail + 1) &
+                                       (UART_XMIT_SIZE - 1);
+
+                       numbytes--;
+               }
 
-               numbytes--;
+               if (uart_circ_chars_pending(
+                               &port->state->xmit) < WAKEUP_CHARS)
+                       uart_write_wakeup(port);
        }
-
-       if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)
-               uart_write_wakeup(port);
 }
 
 /**
@@ -338,27 +348,24 @@ static void cdns_uart_handle_tx(struct uart_port *port)
 static irqreturn_t cdns_uart_isr(int irq, void *dev_id)
 {
        struct uart_port *port = (struct uart_port *)dev_id;
-       unsigned long flags;
        unsigned int isrstatus;
 
-       spin_lock_irqsave(&port->lock, flags);
+       spin_lock(&port->lock);
 
        /* Read the interrupt status register to determine which
-        * interrupt(s) is/are active.
+        * interrupt(s) is/are active and clear them.
         */
        isrstatus = readl(port->membase + CDNS_UART_ISR);
-
-       if (isrstatus & CDNS_UART_RX_IRQS)
-               cdns_uart_handle_rx(port, isrstatus);
-
-       if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY)
-               cdns_uart_handle_tx(port);
-
        writel(isrstatus, port->membase + CDNS_UART_ISR);
 
-       /* be sure to release the lock and tty before leaving */
-       spin_unlock_irqrestore(&port->lock, flags);
+       if (isrstatus & CDNS_UART_IXR_TXEMPTY) {
+               cdns_uart_handle_tx(dev_id);
+               isrstatus &= ~CDNS_UART_IXR_TXEMPTY;
+       }
+       if (isrstatus & CDNS_UART_IXR_MASK)
+               cdns_uart_handle_rx(dev_id, isrstatus);
 
+       spin_unlock(&port->lock);
        return IRQ_HANDLED;
 }