The SMEM and RPM transports request their receive interrupt with
IRQF_NO_AUTOEN and enable it only once qcom_glink_native_probe() has
returned. But native_probe() sends the version command, so on a fast
remote the version ACK can land while the interrupt is still masked. The
ACK is dropped and the handshake never completes.

Move the version command and the chrdev registration into a new
qcom_glink_native_start(), leaving native_probe() to set up the glink
instance. Both transports enable their interrupt before calling
native_start().

Signed-off-by: Chunkai Deng <[email protected]>
Assisted-by: Claude:claude-opus-5
---
 drivers/rpmsg/qcom_glink_native.c | 32 ++++++++++++++++++++++++++++----
 drivers/rpmsg/qcom_glink_native.h |  1 +
 drivers/rpmsg/qcom_glink_rpm.c    |  8 ++++++++
 drivers/rpmsg/qcom_glink_smem.c   |  8 ++++++++
 4 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/drivers/rpmsg/qcom_glink_native.c 
b/drivers/rpmsg/qcom_glink_native.c
index d9d4468e4cbd..2a284b22a037 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -1928,17 +1928,41 @@ struct qcom_glink *qcom_glink_native_probe(struct 
device *dev,
        if (ret)
                dev_err(dev, "failed to add groups\n");
 
+       return glink;
+}
+EXPORT_SYMBOL_GPL(qcom_glink_native_probe);
+
+/**
+ * qcom_glink_native_start() - start the GLINK protocol handshake
+ * @glink:     glink handle returned by qcom_glink_native_probe()
+ *
+ * Send the initial version command and register the chrdev. This is split
+ * out from qcom_glink_native_probe() so that a transport can enable its
+ * receive interrupt before the version handshake is initiated, ensuring the
+ * version ACK from the remote is not missed.
+ *
+ * Failure to register the chrdev is not fatal and only logged, matching the
+ * previous behaviour of qcom_glink_native_probe().
+ *
+ * Return: 0 on success, negative errno if sending the version command failed.
+ */
+int qcom_glink_native_start(struct qcom_glink *glink)
+{
+       int ret;
+
        ret = qcom_glink_send_version(glink);
-       if (ret)
-               return ERR_PTR(ret);
+       if (ret) {
+               dev_err(glink->dev, "failed to send version: %d\n", ret);
+               return ret;
+       }
 
        ret = qcom_glink_create_chrdev(glink);
        if (ret)
                dev_err(glink->dev, "failed to register chrdev\n");
 
-       return glink;
+       return 0;
 }
-EXPORT_SYMBOL_GPL(qcom_glink_native_probe);
+EXPORT_SYMBOL_GPL(qcom_glink_native_start);
 
 static int qcom_glink_remove_device(struct device *dev, void *data)
 {
diff --git a/drivers/rpmsg/qcom_glink_native.h 
b/drivers/rpmsg/qcom_glink_native.h
index 8dbec24de23e..783209980c3a 100644
--- a/drivers/rpmsg/qcom_glink_native.h
+++ b/drivers/rpmsg/qcom_glink_native.h
@@ -35,6 +35,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
                                           struct qcom_glink_pipe *rx,
                                           struct qcom_glink_pipe *tx,
                                           bool intentless);
+int qcom_glink_native_start(struct qcom_glink *glink);
 void qcom_glink_native_remove(struct qcom_glink *glink);
 void qcom_glink_native_rx(struct qcom_glink *glink);
 
diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c
index e3ba2c63a5fc..34f18c3e58c8 100644
--- a/drivers/rpmsg/qcom_glink_rpm.c
+++ b/drivers/rpmsg/qcom_glink_rpm.c
@@ -358,6 +358,14 @@ static int glink_rpm_probe(struct platform_device *pdev)
 
        enable_irq(rpm->irq);
 
+       ret = qcom_glink_native_start(glink);
+       if (ret) {
+               disable_irq(rpm->irq);
+               qcom_glink_native_remove(glink);
+               mbox_free_channel(rpm->mbox_chan);
+               return ret;
+       }
+
        return 0;
 }
 
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 62adc4db2317..28f6cfda6352 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -348,8 +348,16 @@ struct qcom_glink_smem *qcom_glink_smem_register(struct 
device *parent,
 
        enable_irq(smem->irq);
 
+       ret = qcom_glink_native_start(glink);
+       if (ret)
+               goto err_disable_irq;
+
        return smem;
 
+err_disable_irq:
+       disable_irq(smem->irq);
+       qcom_glink_native_remove(glink);
+
 err_free_mbox:
        mbox_free_channel(smem->mbox_chan);
 

-- 
2.43.0


Reply via email to