When the TX FIFO drops below WAKEUP_CHARS, mxpcie used to call
uart_write_wakeup() directly from the interrupt path. Move this into a
per-port work item so we avoid doing TTY wakeups in interrupt context and
reduce IRQ-side work.
Changes:
- Add per-port state (event_flags, work, cached uport pointer).
- In tx_chars(), set a TXLOW event and schedule the per-port work instead
of calling uart_write_wakeup() directly.
- The work handler test-and-clear the TXLOW bit and calls
uart_write_wakeup().
This keeps IRQ handlers lightweight and avoids potential locking or RT
latency issues while preserving existing behavior.
Note: removal path must cancel pending works before unregistering ports.
Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
drivers/tty/serial/8250/8250_mxpcie.c | 32 +++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 04b9c9ff5cbf..a0deb464a318 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -93,9 +93,14 @@
#define MOXA_EVEN_RS_MASK GENMASK(3, 0)
#define MOXA_ODD_RS_MASK GENMASK(7, 4)
+#define MOXA_EVENT_TXLOW BIT(0)
+
struct mxpcie8250_port {
int line;
+ unsigned long event_flags;
u8 rx_trig_level;
+ struct uart_port *uport;
+ struct work_struct work;
};
struct mxpcie8250 {
@@ -332,6 +337,8 @@ static void mxpcie8250_tx_chars(struct uart_8250_port *up)
{
struct uart_port *port = &up->port;
struct tty_port *tport = &port->state->port;
+ struct pci_dev *pdev = to_pci_dev(port->dev);
+ struct mxpcie8250 *priv = pci_get_drvdata(pdev);
unsigned int count, i;
unsigned char c;
@@ -352,9 +359,10 @@ static void mxpcie8250_tx_chars(struct uart_8250_port *up)
*(port->membase + MOXA_PUART_TX_FIFO_MEM + i) = c;
}
- if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) {
+ if (!test_and_set_bit(MOXA_EVENT_TXLOW, &priv->port[port->port_id].event_flags))
+ schedule_work(&priv->port[port->port_id].work);
+ }
if (kfifo_is_empty(&tport->xmit_fifo) && !(up->capabilities & UART_CAP_RPM))
port->ops->stop_tx(port);
}
@@ -473,6 +481,14 @@ static int mxpcie8250_get_rxtrig(struct uart_port *port)
return rx_trig_byte;
}
+static void mxpcie8250_work_handler(struct work_struct *work)
+{
+ struct mxpcie8250_port *priv_port = container_of(work, struct mxpcie8250_port, work);
+
+ if (test_and_clear_bit(MOXA_EVENT_TXLOW, &priv_port->event_flags))
+ uart_write_wakeup(priv_port->uport);
+}
+
static int mxpcie8250_init(struct pci_dev *pdev)
{
resource_size_t iobar_addr = pci_resource_start(pdev, 2);
@@ -533,6 +549,7 @@ static int mxpcie8250_setup(struct pci_dev *pdev,
static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct uart_8250_port up;
+ struct uart_8250_port *new_port;
struct mxpcie8250 *priv;
unsigned int i, num_ports;
int ret;
@@ -590,7 +607,12 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
up.port.iotype, priv->port[i].line);
break;
}
+ new_port = serial8250_get_port(priv->port[i].line);
+
priv->port[i].rx_trig_level = 96;
+ priv->port[i].uport = &new_port->port;
+
+ INIT_WORK(&priv->port[i].work, mxpcie8250_work_handler);
}
pci_set_drvdata(pdev, priv);
@@ -602,8 +624,10 @@ static void mxpcie8250_remove(struct pci_dev *pdev)
struct mxpcie8250 *priv = pci_get_drvdata(pdev);
unsigned int i;
- for (i = 0; i < priv->num_ports; i++)
+ for (i = 0; i < priv->num_ports; i++) {
+ cancel_work_sync(&priv->port[i].work);
serial8250_unregister_port(priv->port[i].line);
+ }
}
static const struct pci_device_id mxpcie8250_pci_ids[] = {
--
2.45.2