summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/serial/max310x.c126
1 files changed, 86 insertions, 40 deletions
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 2a2c4ea16306..dc0e1ad351f9 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -609,57 +609,103 @@ static void max310x_batch_write(struct uart_port *port, u8 *txbuf, unsigned int
spi_sync_transfer(to_spi_device(port->dev), xfer, ARRAY_SIZE(xfer));
}
-static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
+static void max310x_batch_read(struct uart_port *port, u8 *rxbuf, unsigned int len)
{
- unsigned int sts, ch, flag;
+ u8 header[] = { port->iobase + MAX310X_RHR_REG };
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &header,
+ .len = sizeof(header),
+ }, {
+ .rx_buf = rxbuf,
+ .len = len,
+ }
+ };
+ spi_sync_transfer(to_spi_device(port->dev), xfer, ARRAY_SIZE(xfer));
+}
- if (unlikely(rxlen >= port->fifosize)) {
- dev_warn_ratelimited(port->dev, "Possible RX FIFO overrun\n");
- port->icount.buf_overrun++;
- /* Ensure sanity of RX level */
- rxlen = port->fifosize;
- }
+static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
+{
+ unsigned int sts, ch, flag, i;
+ u8 buf[MAX310X_FIFO_SIZE];
+
+ if (port->read_status_mask == MAX310X_LSR_RXOVR_BIT) {
+ /* We are just reading, happily ignoring any error conditions.
+ * Break condition, parity checking, framing errors -- they
+ * are all ignored. That means that we can do a batch-read.
+ *
+ * There is a small opportunity for race if the RX FIFO
+ * overruns while we're reading the buffer; the datasheets says
+ * that the LSR register applies to the "current" character.
+ * That's also the reason why we cannot do batched reads when
+ * asked to check the individual statuses.
+ * */
- while (rxlen--) {
- ch = max310x_port_read(port, MAX310X_RHR_REG);
sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
+ max310x_batch_read(port, buf, rxlen);
- sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
- MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
-
- port->icount.rx++;
+ port->icount.rx += rxlen;
flag = TTY_NORMAL;
+ sts &= port->read_status_mask;
- if (unlikely(sts)) {
- if (sts & MAX310X_LSR_RXBRK_BIT) {
- port->icount.brk++;
- if (uart_handle_break(port))
- continue;
- } else if (sts & MAX310X_LSR_RXPAR_BIT)
- port->icount.parity++;
- else if (sts & MAX310X_LSR_FRERR_BIT)
- port->icount.frame++;
- else if (sts & MAX310X_LSR_RXOVR_BIT)
- port->icount.overrun++;
-
- sts &= port->read_status_mask;
- if (sts & MAX310X_LSR_RXBRK_BIT)
- flag = TTY_BREAK;
- else if (sts & MAX310X_LSR_RXPAR_BIT)
- flag = TTY_PARITY;
- else if (sts & MAX310X_LSR_FRERR_BIT)
- flag = TTY_FRAME;
- else if (sts & MAX310X_LSR_RXOVR_BIT)
- flag = TTY_OVERRUN;
+ if (sts & MAX310X_LSR_RXOVR_BIT) {
+ dev_warn_ratelimited(port->dev, "Hardware RX FIFO overrun\n");
+ port->icount.overrun++;
}
- if (uart_handle_sysrq_char(port, ch))
- continue;
+ for (i = 0; i < rxlen; ++i) {
+ uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, buf[i], flag);
+ }
- if (sts & port->ignore_status_mask)
- continue;
+ } else {
+ if (unlikely(rxlen >= port->fifosize)) {
+ dev_warn_ratelimited(port->dev, "Possible RX FIFO overrun\n");
+ port->icount.buf_overrun++;
+ /* Ensure sanity of RX level */
+ rxlen = port->fifosize;
+ }
- uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag);
+ while (rxlen--) {
+ ch = max310x_port_read(port, MAX310X_RHR_REG);
+ sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
+
+ sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
+ MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
+
+ port->icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (unlikely(sts)) {
+ if (sts & MAX310X_LSR_RXBRK_BIT) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ } else if (sts & MAX310X_LSR_RXPAR_BIT)
+ port->icount.parity++;
+ else if (sts & MAX310X_LSR_FRERR_BIT)
+ port->icount.frame++;
+ else if (sts & MAX310X_LSR_RXOVR_BIT)
+ port->icount.overrun++;
+
+ sts &= port->read_status_mask;
+ if (sts & MAX310X_LSR_RXBRK_BIT)
+ flag = TTY_BREAK;
+ else if (sts & MAX310X_LSR_RXPAR_BIT)
+ flag = TTY_PARITY;
+ else if (sts & MAX310X_LSR_FRERR_BIT)
+ flag = TTY_FRAME;
+ else if (sts & MAX310X_LSR_RXOVR_BIT)
+ flag = TTY_OVERRUN;
+ }
+
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+
+ if (sts & port->ignore_status_mask)
+ continue;
+
+ uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag);
+ }
}
tty_flip_buffer_push(&port->state->port);