248 lines
7.2 KiB
Diff
248 lines
7.2 KiB
Diff
From 21dd2b6821d171463407a77536ea8044dc64bbeb Mon Sep 17 00:00:00 2001
|
|
From: Allen Martin <armartin@gmail.com>
|
|
Date: Fri, 20 Jan 2023 19:42:33 -0800
|
|
Subject: [PATCH 20/21] tty: serial: bflb_uart: fix leaked ISR registration
|
|
|
|
-call devm_free_irq() in shutdown to release ISR registered in
|
|
startup.
|
|
-change many functions to take driver private bflb_uart_port *
|
|
instead of serial_core uart_port *
|
|
-add to_bflb_uart_port() to assist with above
|
|
-switch to using uart_port_tx_limited() serial_core helper
|
|
which is the preferred way to do transmission instead of
|
|
accessing serial_core xmit buffer directly
|
|
---
|
|
drivers/tty/serial/bflb_uart.c | 133 ++++++++++++++++++++-------------
|
|
1 file changed, 79 insertions(+), 54 deletions(-)
|
|
|
|
diff --git a/drivers/tty/serial/bflb_uart.c b/drivers/tty/serial/bflb_uart.c
|
|
index b091ad7a891a..5911f489959c 100644
|
|
--- a/drivers/tty/serial/bflb_uart.c
|
|
+++ b/drivers/tty/serial/bflb_uart.c
|
|
@@ -82,12 +82,17 @@
|
|
#define BFLB_UART_MAXPORTS 8
|
|
#define BFLB_UART_BAUD 2000000
|
|
#define BFLB_UART_RX_FIFO_TH 7
|
|
+#define BFLB_UART_TX_FIFO_DEPTH 32
|
|
|
|
struct bflb_uart_port {
|
|
struct uart_port port;
|
|
struct clk *clk;
|
|
};
|
|
|
|
+#define to_bflb_uart_port(p) (container_of((p), \
|
|
+ struct bflb_uart_port, \
|
|
+ port))
|
|
+
|
|
static struct bflb_uart_port *bflb_uart_ports[BFLB_UART_MAXPORTS];
|
|
|
|
static inline u32 rdl(struct uart_port *port, u32 reg)
|
|
@@ -227,91 +232,104 @@ static void bflb_uart_set_termios(struct uart_port *port,
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
}
|
|
|
|
-static void bflb_uart_rx_chars(struct uart_port *port)
|
|
+static void bflb_uart_rx_chars(struct bflb_uart_port *bp)
|
|
{
|
|
unsigned char ch, flag;
|
|
unsigned long status;
|
|
|
|
- while ((status = rdl(port, UART_FIFO_CONFIG_1)) & UART_RX_FIFO_CNT_MSK) {
|
|
- ch = rdl(port, UART_FIFO_RDATA) & UART_FIFO_RDATA_MSK;
|
|
+ while ((status = rdl(&bp->port, UART_FIFO_CONFIG_1)) & UART_RX_FIFO_CNT_MSK) {
|
|
+ ch = rdl(&bp->port, UART_FIFO_RDATA) & UART_FIFO_RDATA_MSK;
|
|
flag = TTY_NORMAL;
|
|
- port->icount.rx++;
|
|
+ bp->port.icount.rx++;
|
|
|
|
- if (uart_handle_sysrq_char(port, ch))
|
|
+ if (uart_handle_sysrq_char(&bp->port, ch))
|
|
continue;
|
|
- uart_insert_char(port, 0, 0, ch, flag);
|
|
+ uart_insert_char(&bp->port, 0, 0, ch, flag);
|
|
}
|
|
|
|
- spin_unlock(&port->lock);
|
|
- tty_flip_buffer_push(&port->state->port);
|
|
- spin_lock(&port->lock);
|
|
+ spin_unlock(&bp->port.lock);
|
|
+ tty_flip_buffer_push(&bp->port.state->port);
|
|
+ spin_lock(&bp->port.lock);
|
|
}
|
|
|
|
-static void bflb_uart_tx_chars(struct uart_port *port)
|
|
+/**
|
|
+ * bflb_uart_txfifo_space() - How much space is left int the TX FIFO?
|
|
+ * @bp: pointer to a struct bflb_uart_port
|
|
+ *
|
|
+ * Read the transmit FIFO count to find out how much space is left
|
|
+ *
|
|
+ * Returns: UART_TX_FIFO_CNT - count of space left in the TX FIFO
|
|
+ */
|
|
+static int bflb_uart_txfifo_space(struct bflb_uart_port *bp)
|
|
{
|
|
- struct circ_buf *xmit = &port->state->xmit;
|
|
- unsigned int pending, count;
|
|
+ return (rdl(&bp->port, UART_FIFO_CONFIG_1)
|
|
+ & UART_TX_FIFO_CNT_MSK) >> UART_TX_FIFO_CNT_SFT;
|
|
+}
|
|
|
|
- if (port->x_char) {
|
|
- /* Send special char - probably flow control */
|
|
- wrl(port, UART_FIFO_WDATA, port->x_char);
|
|
- port->x_char = 0;
|
|
- port->icount.tx++;
|
|
- return;
|
|
- }
|
|
+/**
|
|
+ * bflb_uart_tx_char() - enqueue a byte to transmit onto the TX FIFO
|
|
+ * @bp: pointer to a struct bflb_uart_port
|
|
+ * @ch: character to transmit
|
|
+ *
|
|
+ * Enqueue a byte @ch onto the transmit FIFO, given a pointer @bp to the
|
|
+ * struct bflb_uart_port * to transmit on.
|
|
+ *
|
|
+ * Context: Any context.
|
|
+ */
|
|
+static void bflb_uart_tx_char(struct bflb_uart_port *bp, int ch)
|
|
+{
|
|
+ wrl(&bp->port, UART_FIFO_WDATA, ch);
|
|
+}
|
|
|
|
- pending = uart_circ_chars_pending(xmit);
|
|
- if (pending > 0) {
|
|
- count = (rdl(port, UART_FIFO_CONFIG_1) &
|
|
- UART_TX_FIFO_CNT_MSK) >> UART_TX_FIFO_CNT_SFT;
|
|
- if (count > pending)
|
|
- count = pending;
|
|
- if (count > 0) {
|
|
- pending -= count;
|
|
- while (count--) {
|
|
- wrl(port, UART_FIFO_WDATA, xmit->buf[xmit->tail]);
|
|
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
|
- port->icount.tx++;
|
|
- }
|
|
- if (pending < WAKEUP_CHARS)
|
|
- uart_write_wakeup(port);
|
|
- }
|
|
- }
|
|
+/**
|
|
+ * bflb_uart_tx_chars() - enqueue multiple bytes onto the TX FIFO
|
|
+ * @bp: pointer to a struct bflb_uart_port
|
|
+ *
|
|
+ * Transfer up to a TX FIFO size's worth of characters from the Linux serial
|
|
+ * transmit buffer to the BFLB UART TX FIFO.
|
|
+ *
|
|
+ * Context: Any context. Expects @bp->port.lock to be held by caller.
|
|
+ */
|
|
+static void bflb_uart_tx_chars(struct bflb_uart_port *bp)
|
|
+{
|
|
+ u8 ch;
|
|
|
|
- if (pending == 0)
|
|
- bflb_uart_stop_tx(port);
|
|
+ uart_port_tx_limited(&bp->port, ch, BFLB_UART_TX_FIFO_DEPTH,
|
|
+ bflb_uart_txfifo_space(bp),
|
|
+ bflb_uart_tx_char(bp, ch),
|
|
+ ({}));
|
|
}
|
|
|
|
static irqreturn_t bflb_uart_interrupt(int irq, void *data)
|
|
{
|
|
- struct uart_port *port = data;
|
|
+ struct bflb_uart_port *bp = data;
|
|
u32 isr, val;
|
|
|
|
- isr = rdl(port, UART_INT_STS);
|
|
- wrl(port, UART_INT_CLEAR, isr);
|
|
+ isr = rdl(&bp->port, UART_INT_STS);
|
|
+ wrl(&bp->port, UART_INT_CLEAR, isr);
|
|
|
|
- isr &= ~rdl(port, UART_INT_MASK);
|
|
+ isr &= ~rdl(&bp->port, UART_INT_MASK);
|
|
|
|
- spin_lock(&port->lock);
|
|
+ spin_lock(&bp->port.lock);
|
|
|
|
if (isr & UART_URX_FER_INT) {
|
|
/* RX FIFO error interrupt */
|
|
- val = rdl(port, UART_FIFO_CONFIG_0);
|
|
+ val = rdl(&bp->port, UART_FIFO_CONFIG_0);
|
|
if (val & UART_RX_FIFO_OVERFLOW)
|
|
- port->icount.overrun++;
|
|
+ bp->port.icount.overrun++;
|
|
|
|
val |= UART_RX_FIFO_CLR;
|
|
- wrl(port, UART_FIFO_CONFIG_0, val);
|
|
+ wrl(&bp->port, UART_FIFO_CONFIG_0, val);
|
|
}
|
|
|
|
if (isr & (UART_URX_FIFO_INT | UART_URX_RTO_INT)) {
|
|
- bflb_uart_rx_chars(port);
|
|
+ bflb_uart_rx_chars(bp);
|
|
}
|
|
if (isr & (UART_UTX_FIFO_INT | UART_UTX_END_INT)) {
|
|
- bflb_uart_tx_chars(port);
|
|
+ bflb_uart_tx_chars(bp);
|
|
}
|
|
|
|
- spin_unlock(&port->lock);
|
|
+ spin_unlock(&bp->port.lock);
|
|
|
|
return IRQ_RETVAL(isr);
|
|
}
|
|
@@ -335,19 +353,22 @@ static void bflb_uart_config_port(struct uart_port *port, int flags)
|
|
static int bflb_uart_startup(struct uart_port *port)
|
|
{
|
|
unsigned long flags;
|
|
- int ret;
|
|
u32 val;
|
|
+ struct bflb_uart_port *bp = to_bflb_uart_port(port);
|
|
+ int ret;
|
|
+
|
|
+ dev_dbg(port->dev, "startup %s\n", port->name);
|
|
+
|
|
+ spin_lock_irqsave(&port->lock, flags);
|
|
|
|
ret = devm_request_irq(port->dev, port->irq, bflb_uart_interrupt,
|
|
- IRQF_SHARED, port->name, port);
|
|
+ IRQF_SHARED, port->name, bp);
|
|
if (ret) {
|
|
dev_err(port->dev, "fail to request serial irq %d, ret=%d\n",
|
|
port->irq, ret);
|
|
return ret;
|
|
}
|
|
|
|
- spin_lock_irqsave(&port->lock, flags);
|
|
-
|
|
val = rdl(port, UART_INT_MASK);
|
|
val |= 0xfff;
|
|
wrl(port, UART_INT_MASK, val);
|
|
@@ -383,10 +404,14 @@ static int bflb_uart_startup(struct uart_port *port)
|
|
static void bflb_uart_shutdown(struct uart_port *port)
|
|
{
|
|
unsigned long flags;
|
|
+ struct bflb_uart_port *bp = to_bflb_uart_port(port);
|
|
+
|
|
+ dev_dbg(port->dev, "shutdown %s\n", port->name);
|
|
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
/* mask all interrupts now */
|
|
wrl(port, UART_INT_MASK, UART_UTX_END_INT | UART_URX_END_INT);
|
|
+ devm_free_irq(port->dev, port->irq, bp);
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
}
|
|
|
|
@@ -589,7 +614,7 @@ static int bflb_uart_probe(struct platform_device *pdev)
|
|
port->line = index;
|
|
port->type = PORT_BFLB;
|
|
port->iotype = UPIO_MEM;
|
|
- port->fifosize = 32;
|
|
+ port->fifosize = BFLB_UART_TX_FIFO_DEPTH;
|
|
port->ops = &bflb_uart_ops;
|
|
port->flags = UPF_BOOT_AUTOCONF;
|
|
port->dev = &pdev->dev;
|
|
--
|
|
2.25.1
|
|
|