From 127d3a90fb5fbebb16f8e50c709381dd02e230cb Mon Sep 17 00:00:00 2001 From: Balaji Selvanathan Date: Tue, 7 Jul 2026 10:39:15 +0530 Subject: [PATCH 1/2] serial: msm_geni: fix RX FIFO init with proper cancel sequence Replace the abort-only RX initialization with a proper stop sequence that matches the Linux driver approach. The new qcom_geni_serial_stop_rx_fifo function sends a CANCEL command instead of immediately aborting, waits for completion, drains any remaining FIFO data if S_RX_FIFO_LAST_EN is set, and only sends ABORT as a fallback if the secondary sequencer remains active after cancellation. Signed-off-by: Balaji Selvanathan --- drivers/serial/serial_msm_geni.c | 63 +++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/serial/serial_msm_geni.c b/drivers/serial/serial_msm_geni.c index 7db7b8de185d..a5fd78122088 100644 --- a/drivers/serial/serial_msm_geni.c +++ b/drivers/serial/serial_msm_geni.c @@ -387,11 +387,70 @@ static void qcom_geni_serial_abort_rx(struct udevice *dev) writel(FORCE_DEFAULT, priv->base + GENI_FORCE_DEFAULT_REG); } +static void qcom_geni_serial_stop_rx_fifo(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + u32 irq_en; + u32 s_irq_status; + u32 status; + u32 rx_fifo_status; + + /* Disable RX interrupts */ + irq_en = readl(priv->base + SE_GENI_S_IRQ_EN); + irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); + writel(irq_en, priv->base + SE_GENI_S_IRQ_EN); + + irq_en = readl(priv->base + SE_GENI_M_IRQ_EN); + irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); + writel(irq_en, priv->base + SE_GENI_M_IRQ_EN); + + /* Check if secondary sequencer is active */ + status = readl(priv->base + SE_GENI_STATUS); + if (!(status & S_GENI_CMD_ACTIVE)) + return; + + /* Send CANCEL command */ + writel(S_GENI_CMD_CANCEL, priv->base + SE_GENI_S_CMD_CTRL_REG); + + /* Poll for CANCEL completion */ + qcom_geni_serial_poll_bit(dev, SE_GENI_S_CMD_CTRL_REG, + S_GENI_CMD_CANCEL, false); + + /* Read IRQ status and check if S_RX_FIFO_LAST_EN is set */ + s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS); + + /* Flush/drain the RX FIFO if needed */ + if (s_irq_status & S_RX_FIFO_LAST_EN) { + while ((rx_fifo_status = readl(priv->base + SE_GENI_RX_FIFO_STATUS)) & + RX_FIFO_WC_MSK) { + readl(priv->base + SE_GENI_RX_FIFOn); + } + } + + /* Clear all pending IRQ status bits */ + writel(s_irq_status, priv->base + SE_GENI_S_IRQ_CLEAR); + + /* Check if still active after CANCEL */ + status = readl(priv->base + SE_GENI_STATUS); + if (status & S_GENI_CMD_ACTIVE) { + /* Send ABORT as fallback */ + writel(S_GENI_CMD_ABORT, priv->base + SE_GENI_S_CMD_CTRL_REG); + qcom_geni_serial_poll_bit(dev, SE_GENI_S_CMD_CTRL_REG, + S_GENI_CMD_ABORT, false); + + /* Clear ABORT IRQ status */ + s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS); + writel(s_irq_status, priv->base + SE_GENI_S_IRQ_CLEAR); + } + + writel(FORCE_DEFAULT, priv->base + GENI_FORCE_DEFAULT_REG); +} + static void msm_geni_serial_setup_rx(struct udevice *dev) { struct msm_serial_data *priv = dev_get_priv(dev); - qcom_geni_serial_abort_rx(dev); + qcom_geni_serial_stop_rx_fifo(dev); writel(UART_PACKING_CFG0, priv->base + SE_GENI_RX_PACKING_CFG0); writel(UART_PACKING_CFG1, priv->base + SE_GENI_RX_PACKING_CFG1); @@ -524,7 +583,7 @@ static inline void geni_serial_init(struct udevice *dev) * it else we could end up in data loss scenarios. */ qcom_geni_serial_poll_tx_done(dev); - qcom_geni_serial_abort_rx(dev); + qcom_geni_serial_stop_rx_fifo(dev); writel(UART_PACKING_CFG0, base_address + SE_GENI_TX_PACKING_CFG0); writel(UART_PACKING_CFG1, base_address + SE_GENI_TX_PACKING_CFG1); From f3656e2872d54fa52d96ac56ac931b4fc43c5ed9 Mon Sep 17 00:00:00 2001 From: Balaji Selvanathan Date: Tue, 7 Jul 2026 10:39:30 +0530 Subject: [PATCH 2/2] WORKAROUND: serial: msm_geni: validate RX FIFO data with IRQ status flags Add IRQ status validation to distinguish legitimate UART data from electrical noise in the RX FIFO. When the UART cable is disconnected, the floating RX line can pick up noise that the GENI hardware interprets as valid data and places in the FIFO. So, check the RX IRQ status register for RX watermark or last flags before reporting pending data. Signed-off-by: Balaji Selvanathan --- drivers/serial/serial_msm_geni.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/serial/serial_msm_geni.c b/drivers/serial/serial_msm_geni.c index a5fd78122088..9352aa85a282 100644 --- a/drivers/serial/serial_msm_geni.c +++ b/drivers/serial/serial_msm_geni.c @@ -513,15 +513,29 @@ static int msm_serial_getc(struct udevice *dev) static int msm_serial_pending(struct udevice *dev, bool input) { struct msm_serial_data *priv = dev_get_priv(dev); + u32 fifo_status; + u32 s_irq_status; + int word_count; - if (input) - return readl(priv->base + SE_GENI_RX_FIFO_STATUS) & - RX_FIFO_WC_MSK; - else - return readl(priv->base + SE_GENI_TX_FIFO_STATUS) & - TX_FIFO_WC_MSK; + if (input) { + fifo_status = readl(priv->base + SE_GENI_RX_FIFO_STATUS); + word_count = fifo_status & RX_FIFO_WC_MSK; - return 0; + if (word_count > 0) { + /* Validate RX FIFO data with IRQ status */ + s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS); + + if (!(s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN))) + return 0; + } + + return word_count; + } else { + fifo_status = readl(priv->base + SE_GENI_TX_FIFO_STATUS); + word_count = fifo_status & TX_FIFO_WC_MSK; + + return word_count; + } } static const struct dm_serial_ops msm_serial_ops = {