From cc4c1d05eb10c3ad4c6315f1897bc56b1e7429aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 1 Mar 2022 07:03:30 +0100 Subject: [PATCH] sc16is7xx: Properly resume TX after stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sc16is7xx_stop_tx() clears THRI bit and thus disables THRI interrupt. This makes it possible for transmission to cease indefinitely when more than 64 characters are being sent. The sc16is7xx_handle_tx() call executed by sc16is7xx_tx_proc() can send up to FIFO length (64) characters. If more characters are written to the output buffer, then the THRI interrupt is needed. Solve the issue by enabling THRI interrupt in sc16is7xx_tx_proc(). Signed-off-by: Tomasz Moń Link: https://lore.kernel.org/r/20220301060332.2561851-2-tomasz.mon@camlingroup.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 47 +++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 21ae2c0b7bbe..17e79af36604 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -315,7 +315,8 @@ struct sc16is7xx_devtype { struct sc16is7xx_one_config { unsigned int flags; - u8 ier_clear; + u8 ier_mask; + u8 ier_val; }; struct sc16is7xx_one { @@ -349,6 +350,9 @@ static struct uart_driver sc16is7xx_uart = { .nr = SC16IS7XX_MAX_DEVS, }; +static void sc16is7xx_ier_set(struct uart_port *port, u8 bit); +static void sc16is7xx_stop_tx(struct uart_port *port); + #define to_sc16is7xx_port(p,e) ((container_of((p), struct sc16is7xx_port, e))) #define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e))) @@ -651,6 +655,7 @@ static void sc16is7xx_handle_tx(struct uart_port *port) struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; unsigned int txlen, to_send, i; + unsigned long flags; if (unlikely(port->x_char)) { sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); @@ -659,8 +664,12 @@ static void sc16is7xx_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + spin_lock_irqsave(&port->lock, flags); + sc16is7xx_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); return; + } /* Get length of data pending in circular buffer */ to_send = uart_circ_chars_pending(xmit); @@ -687,8 +696,13 @@ static void sc16is7xx_handle_tx(struct uart_port *port) sc16is7xx_fifo_write(port, to_send); } + spin_lock_irqsave(&port->lock, flags); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sc16is7xx_stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); } static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) @@ -751,6 +765,7 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) { struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned long flags; if ((port->rs485.flags & SER_RS485_ENABLED) && (port->rs485.delay_rts_before_send > 0)) @@ -759,6 +774,10 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) mutex_lock(&s->efr_lock); sc16is7xx_handle_tx(port); mutex_unlock(&s->efr_lock); + + spin_lock_irqsave(&port->lock, flags); + sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); + spin_unlock_irqrestore(&port->lock, flags); } static void sc16is7xx_reconf_rs485(struct uart_port *port) @@ -813,7 +832,7 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws) if (config.flags & SC16IS7XX_RECONF_IER) sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG, - config.ier_clear, 0); + config.ier_mask, config.ier_val); if (config.flags & SC16IS7XX_RECONF_RS485) sc16is7xx_reconf_rs485(&one->port); @@ -824,8 +843,24 @@ static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit) struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + lockdep_assert_held_once(&port->lock); + one->config.flags |= SC16IS7XX_RECONF_IER; - one->config.ier_clear |= bit; + one->config.ier_mask |= bit; + one->config.ier_val &= ~bit; + kthread_queue_work(&s->kworker, &one->reg_work); +} + +static void sc16is7xx_ier_set(struct uart_port *port, u8 bit) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + lockdep_assert_held_once(&port->lock); + + one->config.flags |= SC16IS7XX_RECONF_IER; + one->config.ier_mask |= bit; + one->config.ier_val |= bit; kthread_queue_work(&s->kworker, &one->reg_work); } @@ -1067,8 +1102,8 @@ static int sc16is7xx_startup(struct uart_port *port) SC16IS7XX_EFCR_TXDISABLE_BIT, 0); - /* Enable RX, TX interrupts */ - val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT; + /* Enable RX interrupt */ + val = SC16IS7XX_IER_RDI_BIT; sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); return 0;