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_mxupci.c | 33 +++++++++++++++++++++++----
1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_mxupci.c b/drivers/tty/serial/8250/8250_mxupci.c
index 15565729b7e4..a19880b99236 100644
--- a/drivers/tty/serial/8250/8250_mxupci.c
+++ b/drivers/tty/serial/8250/8250_mxupci.c
@@ -86,9 +86,14 @@
#define MOXA_UART_RBRTI 0x06 /* Rx Interrupt Trigger Level */
#define MOXA_UART_THRTL 0x07 /* Tx Interrupt Trigger Level */
+#define MOXA_EVENT_TXLOW BIT(0)
+
struct mxupci8250_port {
int line;
+ unsigned long event_flags;
u8 rx_trig_level;
+ struct uart_port *uport;
+ struct work_struct work;
};
struct mxupci8250 {
@@ -251,6 +256,8 @@ static void mxupci8250_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 mxupci8250 *priv = pci_get_drvdata(pdev);
unsigned int count, i;
unsigned char c;
@@ -271,9 +278,10 @@ static void mxupci8250_tx_chars(struct uart_8250_port *up)
serial_out(up, UART_TX, 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);
}
@@ -353,9 +361,19 @@ static int mxupci8250_get_rxtrig(struct uart_port *port)
return priv->port[port->port_id].rx_trig_level;
}
+static void mxupci8250_work_handler(struct work_struct *work)
+{
+ struct mxupci8250_port *priv_port = container_of(work, struct mxupci8250_port, work);
+
+ if (test_and_clear_bit(MOXA_EVENT_TXLOW, &priv_port->event_flags))
+ uart_write_wakeup(priv_port->uport);
+
+}
+
static int mxupci8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct uart_8250_port up;
+ struct uart_8250_port *new_port;
struct mxupci8250 *priv;
unsigned int i, num_ports;
int ret;
@@ -409,7 +427,12 @@ static int mxupci8250_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, mxupci8250_work_handler);
}
pci_set_drvdata(pdev, priv);
@@ -421,8 +444,10 @@ static void mxupci8250_remove(struct pci_dev *pdev)
struct mxupci8250 *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 mxupci8250_pci_ids[] = {
--
2.45.2