summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/serial/xilinx_uartps.c219
1 files changed, 114 insertions, 105 deletions
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 98f7b590ec55..511999755f5c 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -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,74 +243,101 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
continue;
}
- if (uart_handle_sysrq_char(port, data))
- continue;
+ 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) ||
+ (isrstatus & CDNS_UART_IXR_RXTRIG)) {
+ 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;
+ }
- port->icount.rx++;
+ if (uart_handle_sysrq_char(port, data))
+ continue;
- if (is_rxbs_support) {
- if ((rxbs_status & CDNS_UART_RXBS_PARITY)
- && (status_mask & CDNS_UART_IXR_PARITY)) {
- port->icount.parity++;
- status = TTY_PARITY;
+ if (is_rxbs_support) {
+ if ((rxbs_status & CDNS_UART_RXBS_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)) {
+ port->icount.frame++;
+ status = TTY_FRAME;
+ }
+ } else {
+ if (isrstatus & CDNS_UART_IXR_PARITY) {
+ port->icount.parity++;
+ status = TTY_PARITY;
+ }
+ if ((isrstatus & CDNS_UART_IXR_FRAMING) &&
+ !framerrprocessed) {
+ port->icount.frame++;
+ status = TTY_FRAME;
+ }
}
- if ((rxbs_status & CDNS_UART_RXBS_FRAMING)
- && (status_mask & CDNS_UART_IXR_PARITY)) {
- port->icount.frame++;
- status = TTY_FRAME;
- }
- } else {
- if (isrstatus & CDNS_UART_IXR_PARITY) {
- port->icount.parity++;
- status = TTY_PARITY;
- }
- if (isrstatus & CDNS_UART_IXR_FRAMING) {
- port->icount.frame++;
- status = TTY_FRAME;
+ if (isrstatus & CDNS_UART_IXR_OVERRUN) {
+ port->icount.overrun++;
+ tty_insert_flip_char(&port->state->port, 0,
+ TTY_OVERRUN);
}
+ tty_insert_flip_char(&port->state->port, data, status);
}
- if (isrstatus & CDNS_UART_IXR_OVERRUN)
- port->icount.overrun++;
-
- uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN,
- data, status);
}
+ 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 +350,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;
}