[PATCH v2 1/2] serial: msm_geni: fix RX FIFO init with proper cancel sequence

Balaji Selvanathan balaji.selvanathan at oss.qualcomm.com
Tue Jul 7 11:52:42 CEST 2026


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.

Taken from 679aac5ead2f ("tty: serial: qcom_geni_serial: Fix RX cancel command failure")

Signed-off-by: Balaji Selvanathan <balaji.selvanathan at oss.qualcomm.com>
---
Changes in v2:
- Replace the abort-only RX initialization with a proper stop sequence
  that matches the Linux driver approach.
---
 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 3dca581f68f..a580dee8c8a 100644
--- a/drivers/serial/serial_msm_geni.c
+++ b/drivers/serial/serial_msm_geni.c
@@ -362,11 +362,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);
@@ -494,7 +553,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);

-- 
2.34.1



More information about the U-Boot mailing list