[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