Skip to content

Commit cc4c1d0

Browse files
Tomasz Mońgregkh
authored andcommitted
sc16is7xx: Properly resume TX after stop
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ń <tomasz.mon@camlingroup.com> Link: https://lore.kernel.org/r/20220301060332.2561851-2-tomasz.mon@camlingroup.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 16b3ac9 commit cc4c1d0

1 file changed

Lines changed: 41 additions & 6 deletions

File tree

drivers/tty/serial/sc16is7xx.c

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,8 @@ struct sc16is7xx_devtype {
315315

316316
struct sc16is7xx_one_config {
317317
unsigned int flags;
318-
u8 ier_clear;
318+
u8 ier_mask;
319+
u8 ier_val;
319320
};
320321

321322
struct sc16is7xx_one {
@@ -349,6 +350,9 @@ static struct uart_driver sc16is7xx_uart = {
349350
.nr = SC16IS7XX_MAX_DEVS,
350351
};
351352

353+
static void sc16is7xx_ier_set(struct uart_port *port, u8 bit);
354+
static void sc16is7xx_stop_tx(struct uart_port *port);
355+
352356
#define to_sc16is7xx_port(p,e) ((container_of((p), struct sc16is7xx_port, e)))
353357
#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e)))
354358

@@ -651,6 +655,7 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
651655
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
652656
struct circ_buf *xmit = &port->state->xmit;
653657
unsigned int txlen, to_send, i;
658+
unsigned long flags;
654659

655660
if (unlikely(port->x_char)) {
656661
sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
@@ -659,8 +664,12 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
659664
return;
660665
}
661666

662-
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
667+
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
668+
spin_lock_irqsave(&port->lock, flags);
669+
sc16is7xx_stop_tx(port);
670+
spin_unlock_irqrestore(&port->lock, flags);
663671
return;
672+
}
664673

665674
/* Get length of data pending in circular buffer */
666675
to_send = uart_circ_chars_pending(xmit);
@@ -687,8 +696,13 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
687696
sc16is7xx_fifo_write(port, to_send);
688697
}
689698

699+
spin_lock_irqsave(&port->lock, flags);
690700
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
691701
uart_write_wakeup(port);
702+
703+
if (uart_circ_empty(xmit))
704+
sc16is7xx_stop_tx(port);
705+
spin_unlock_irqrestore(&port->lock, flags);
692706
}
693707

694708
static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
@@ -751,6 +765,7 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws)
751765
{
752766
struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
753767
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
768+
unsigned long flags;
754769

755770
if ((port->rs485.flags & SER_RS485_ENABLED) &&
756771
(port->rs485.delay_rts_before_send > 0))
@@ -759,6 +774,10 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws)
759774
mutex_lock(&s->efr_lock);
760775
sc16is7xx_handle_tx(port);
761776
mutex_unlock(&s->efr_lock);
777+
778+
spin_lock_irqsave(&port->lock, flags);
779+
sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT);
780+
spin_unlock_irqrestore(&port->lock, flags);
762781
}
763782

764783
static void sc16is7xx_reconf_rs485(struct uart_port *port)
@@ -813,7 +832,7 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws)
813832

814833
if (config.flags & SC16IS7XX_RECONF_IER)
815834
sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG,
816-
config.ier_clear, 0);
835+
config.ier_mask, config.ier_val);
817836

818837
if (config.flags & SC16IS7XX_RECONF_RS485)
819838
sc16is7xx_reconf_rs485(&one->port);
@@ -824,8 +843,24 @@ static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
824843
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
825844
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
826845

846+
lockdep_assert_held_once(&port->lock);
847+
848+
one->config.flags |= SC16IS7XX_RECONF_IER;
849+
one->config.ier_mask |= bit;
850+
one->config.ier_val &= ~bit;
851+
kthread_queue_work(&s->kworker, &one->reg_work);
852+
}
853+
854+
static void sc16is7xx_ier_set(struct uart_port *port, u8 bit)
855+
{
856+
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
857+
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
858+
859+
lockdep_assert_held_once(&port->lock);
860+
827861
one->config.flags |= SC16IS7XX_RECONF_IER;
828-
one->config.ier_clear |= bit;
862+
one->config.ier_mask |= bit;
863+
one->config.ier_val |= bit;
829864
kthread_queue_work(&s->kworker, &one->reg_work);
830865
}
831866

@@ -1067,8 +1102,8 @@ static int sc16is7xx_startup(struct uart_port *port)
10671102
SC16IS7XX_EFCR_TXDISABLE_BIT,
10681103
0);
10691104

1070-
/* Enable RX, TX interrupts */
1071-
val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT;
1105+
/* Enable RX interrupt */
1106+
val = SC16IS7XX_IER_RDI_BIT;
10721107
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
10731108

10741109
return 0;

0 commit comments

Comments
 (0)