summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
authorTomasz Moń <tomasz.mon@camlingroup.com>2022-03-01 07:03:32 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-03-01 22:16:59 +0100
commit6e124e58ae2e0e3f6400dce21e942a94a67a7949 (patch)
tree3207415fc634388515ad1d48a9978010da303259 /drivers/tty
parent21144bab4f1191e01e1cf785720e6af99d86a347 (diff)
sc16is7xx: Set AUTOCTS and AUTORTS bits
Let serial core know that the chip automatically handles RTS/CTS signal. This elimines completely unnecessary I2C/SPI bus traffic. Cease reading from RX FIFO (by disabling RDI interrupt) when throttled. Eventually the FIFO will fill up and the device will drive RTS output inactive. Unthrottle by enabling back RDI interrupt. Indirectly controlling RTS via RX FIFO state seems to be the only option because RTS bit is ignored when hardware flow control is enabled. Signed-off-by: Tomasz Moń <tomasz.mon@camlingroup.com> Link: https://lore.kernel.org/r/20220301060332.2561851-4-tomasz.mon@camlingroup.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/sc16is7xx.c31
1 files changed, 30 insertions, 1 deletions
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 5c247b4a01a9..683dd3be010d 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -957,6 +957,29 @@ static void sc16is7xx_start_tx(struct uart_port *port)
kthread_queue_work(&s->kworker, &one->tx_work);
}
+static void sc16is7xx_throttle(struct uart_port *port)
+{
+ unsigned long flags;
+
+ /*
+ * Hardware flow control is enabled and thus the device ignores RTS
+ * value set in MCR register. Stop reading data from RX FIFO so the
+ * AutoRTS feature will de-activate RTS output.
+ */
+ spin_lock_irqsave(&port->lock, flags);
+ sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void sc16is7xx_unthrottle(struct uart_port *port)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ sc16is7xx_ier_set(port, SC16IS7XX_IER_RDI_BIT);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
{
unsigned int lsr;
@@ -1062,9 +1085,13 @@ static void sc16is7xx_set_termios(struct uart_port *port,
regcache_cache_bypass(s->regmap, true);
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
- if (termios->c_cflag & CRTSCTS)
+
+ port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
+ if (termios->c_cflag & CRTSCTS) {
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
SC16IS7XX_EFR_AUTORTS_BIT;
+ port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
+ }
if (termios->c_iflag & IXON)
flow |= SC16IS7XX_EFR_SWFLOW3_BIT;
if (termios->c_iflag & IXOFF)
@@ -1270,6 +1297,8 @@ static const struct uart_ops sc16is7xx_ops = {
.get_mctrl = sc16is7xx_get_mctrl,
.stop_tx = sc16is7xx_stop_tx,
.start_tx = sc16is7xx_start_tx,
+ .throttle = sc16is7xx_throttle,
+ .unthrottle = sc16is7xx_unthrottle,
.stop_rx = sc16is7xx_stop_rx,
.enable_ms = sc16is7xx_enable_ms,
.break_ctl = sc16is7xx_break_ctl,