From: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
Subsystems can be brought out of reset by entities such as bootloaders.
As the irq enablement could be later than subsystem bring up, the state
of subsystem should be checked by reading SMP2P bits and performing ping
test.
A new qcom_pas_attach() function is introduced. if a crash state is
detected for the subsystem, rproc_report_crash() is called. If the
subsystem is ready either at the first check or within a 5-second timeout
and the ping is successful, it will be marked as "attached". The ready
state could be set by either ready interrupt or handover interrupt.
If "early_boot" is set by kernel but "subsys_booted" is not completed
within the timeout, It could be the early boot feature is not supported
by other entities. In this case, the state will be marked as RPROC_OFFLINE
so that the PAS driver can load the firmware and start the remoteproc. As
the running state is set once attach function is called, the watchdog or
fatal interrupt received can be handled correctly.
Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
Co-developed-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
---
drivers/remoteproc/qcom_q6v5.c | 88 +++++++++++++++++++++++++++++-
drivers/remoteproc/qcom_q6v5.h | 17 +++++-
drivers/remoteproc/qcom_q6v5_adsp.c | 2 +-
drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
drivers/remoteproc/qcom_q6v5_pas.c | 103 ++++++++++++++++++++++++++++++++++--
drivers/remoteproc/qcom_q6v5_wcss.c | 2 +-
6 files changed, 204 insertions(+), 10 deletions(-)
diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
index 58d5b85e58cd..abfe3aa71042 100644
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -20,6 +20,7 @@
#define Q6V5_LOAD_STATE_MSG_LEN 64
#define Q6V5_PANIC_DELAY_MS 200
+#define Q6V5_PING_TIMEOUT_MS 500
static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable)
{
@@ -94,6 +95,9 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *data)
size_t len;
char *msg;
+ if (q6v5->early_boot)
+ complete(&q6v5->subsys_booted);
+
/* Sometimes the stop triggers a watchdog rather than a stop-ack */
if (!q6v5->running) {
complete(&q6v5->stop_done);
@@ -118,6 +122,9 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
size_t len;
char *msg;
+ if (q6v5->early_boot)
+ complete(&q6v5->subsys_booted);
+
if (!q6v5->running)
return IRQ_HANDLED;
@@ -139,6 +146,9 @@ static irqreturn_t q6v5_ready_interrupt(int irq, void *data)
complete(&q6v5->start_done);
+ if (q6v5->early_boot)
+ complete(&q6v5->subsys_booted);
+
return IRQ_HANDLED;
}
@@ -172,6 +182,9 @@ static irqreturn_t q6v5_handover_interrupt(int irq, void *data)
if (q6v5->handover)
q6v5->handover(q6v5);
+ if (q6v5->early_boot)
+ complete(&q6v5->subsys_booted);
+
icc_set_bw(q6v5->path, 0, 0);
q6v5->handover_issued = true;
@@ -234,6 +247,74 @@ unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5)
}
EXPORT_SYMBOL_GPL(qcom_q6v5_panic);
+static irqreturn_t q6v5_pong_interrupt(int irq, void *data)
+{
+ struct qcom_q6v5 *q6v5 = data;
+
+ complete(&q6v5->ping_done);
+
+ return IRQ_HANDLED;
+}
+
+int qcom_q6v5_ping_subsystem(struct qcom_q6v5 *q6v5)
+{
+ int ret;
+ int ping_failed = 0;
+
+ reinit_completion(&q6v5->ping_done);
+
+ /* Set master kernel Ping bit */
+ ret = qcom_smem_state_update_bits(q6v5->ping_state,
+ BIT(q6v5->ping_bit), BIT(q6v5->ping_bit));
+ if (ret) {
+ dev_err(q6v5->dev, "Failed to update ping bits\n");
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&q6v5->ping_done, msecs_to_jiffies(Q6V5_PING_TIMEOUT_MS));
+ if (!ret) {
+ ping_failed = -ETIMEDOUT;
+ dev_err(q6v5->dev, "Failed to get back pong\n");
+ }
+
+ /* Clear ping bit master kernel */
+ ret = qcom_smem_state_update_bits(q6v5->ping_state, BIT(q6v5->ping_bit), 0);
+ if (ret) {
+ dev_err(q6v5->dev, "Failed to clear master kernel bits\n");
+ return ret;
+ }
+
+ return ping_failed;
+}
+EXPORT_SYMBOL_GPL(qcom_q6v5_ping_subsystem);
+
+int qcom_q6v5_ping_subsystem_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev)
+{
+ int ret = -ENODEV;
+
+ q6v5->ping_state = devm_qcom_smem_state_get(&pdev->dev, "ping", &q6v5->ping_bit);
+ if (IS_ERR(q6v5->ping_state)) {
+ dev_err(&pdev->dev, "Failed to acquire smem state %ld\n",
+ PTR_ERR(q6v5->ping_state));
+ return PTR_ERR(q6v5->ping_state);
+ }
+
+ init_completion(&q6v5->ping_done);
+
+ q6v5->pong_irq = platform_get_irq_byname(pdev, "pong");
+ if (q6v5->pong_irq < 0)
+ return q6v5->pong_irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, q6v5->pong_irq, NULL,
+ q6v5_pong_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "q6v5 pong", q6v5);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to acquire pong IRQ\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_q6v5_ping_subsystem_init);
+
/**
* qcom_q6v5_init() - initializer of the q6v5 common struct
* @q6v5: handle to be initialized
@@ -242,12 +323,13 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_panic);
* @crash_reason: SMEM id for crash reason string, or 0 if none
* @load_state: load state resource string
* @handover: function to be called when proxy resources should be released
+ * @early_boot: true if the subsystem should be brought up by the bootloader
*
* Return: 0 on success, negative errno on failure
*/
int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
struct rproc *rproc, int crash_reason, const char *load_state,
- void (*handover)(struct qcom_q6v5 *q6v5))
+ bool early_boot, void (*handover)(struct qcom_q6v5 *q6v5))
{
int ret;
@@ -255,10 +337,14 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
q6v5->dev = &pdev->dev;
q6v5->crash_reason = crash_reason;
q6v5->handover = handover;
+ q6v5->early_boot = early_boot;
init_completion(&q6v5->start_done);
init_completion(&q6v5->stop_done);
+ if (early_boot)
+ init_completion(&q6v5->subsys_booted);
+
q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog");
if (q6v5->wdog_irq < 0)
return q6v5->wdog_irq;
diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h
index 5a859c41896e..69574a211710 100644
--- a/drivers/remoteproc/qcom_q6v5.h
+++ b/drivers/remoteproc/qcom_q6v5.h
@@ -17,22 +17,27 @@ struct qcom_q6v5 {
struct rproc *rproc;
struct qcom_smem_state *state;
+ struct qcom_smem_state *ping_state;
struct qmp *qmp;
struct icc_path *path;
unsigned stop_bit;
+ unsigned int ping_bit;
int wdog_irq;
int fatal_irq;
int ready_irq;
int handover_irq;
int stop_irq;
+ int pong_irq;
bool handover_issued;
struct completion start_done;
struct completion stop_done;
+ struct completion subsys_booted;
+ struct completion ping_done;
int crash_reason;
@@ -40,10 +45,16 @@ struct qcom_q6v5 {
const char *load_state;
void (*handover)(struct qcom_q6v5 *q6v5);
+
+ bool early_boot;
};
-int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
- struct rproc *rproc, int crash_reason, const char *load_state,
+int qcom_q6v5_init(struct qcom_q6v5 *q6v5,
+ struct platform_device *pdev,
+ struct rproc *rproc,
+ int crash_reason,
+ const char *load_state,
+ bool early_boot,
void (*handover)(struct qcom_q6v5 *q6v5));
void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5);
@@ -52,5 +63,7 @@ int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon);
int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
+int qcom_q6v5_ping_subsystem(struct qcom_q6v5 *q6v5);
+int qcom_q6v5_ping_subsystem_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev);
#endif
diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
index b5c8d6d38c9c..15b3cdf8c157 100644
--- a/drivers/remoteproc/qcom_q6v5_adsp.c
+++ b/drivers/remoteproc/qcom_q6v5_adsp.c
@@ -712,7 +712,7 @@ static int adsp_probe(struct platform_device *pdev)
goto disable_pm;
ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem,
- desc->load_state, qcom_adsp_pil_handover);
+ desc->load_state, false, qcom_adsp_pil_handover);
if (ret)
goto disable_pm;
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index 4e9eb5bd11fa..99d48d48c37f 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -2181,7 +2181,7 @@ static int q6v5_probe(struct platform_device *pdev)
qproc->has_mba_logs = desc->has_mba_logs;
ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, "modem",
- qcom_msa_handover);
+ false, qcom_msa_handover);
if (ret)
goto detach_proxy_pds;
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index 46204da046fa..4700d111e058 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -36,6 +36,8 @@
#define MAX_ASSIGN_COUNT 3
+#define EARLY_ATTACH_TIMEOUT_MS 5000
+
struct qcom_pas_data {
int crash_reason_smem;
const char *firmware_name;
@@ -60,6 +62,7 @@ struct qcom_pas_data {
int region_assign_count;
bool region_assign_shared;
int region_assign_vmid;
+ bool early_boot;
};
struct qcom_pas {
@@ -423,13 +426,21 @@ static int qcom_pas_stop(struct rproc *rproc)
qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
- handover = qcom_q6v5_unprepare(&pas->q6v5);
- if (handover)
- qcom_pas_handover(&pas->q6v5);
+ /*
+ * qcom_q6v5_prepare is not called in qcom_pas_attach, skip unprepare to
+ * avoid mismatch.
+ */
+ if (pas->rproc->state != RPROC_ATTACHED) {
+ handover = qcom_q6v5_unprepare(&pas->q6v5);
+ if (handover)
+ qcom_pas_handover(&pas->q6v5);
+ }
if (pas->smem_host_id)
ret = qcom_smem_bust_hwspin_lock_by_host(pas->smem_host_id);
+ pas->q6v5.early_boot = false;
+
return ret;
}
@@ -510,6 +521,80 @@ static unsigned long qcom_pas_panic(struct rproc *rproc)
return qcom_q6v5_panic(&pas->q6v5);
}
+static int qcom_pas_attach(struct rproc *rproc)
+{
+ int ret;
+ struct qcom_pas *pas = rproc->priv;
+ bool ready_state;
+ bool crash_state;
+
+ pas->q6v5.running = true;
+ ret = irq_get_irqchip_state(pas->q6v5.fatal_irq,
+ IRQCHIP_STATE_LINE_LEVEL, &crash_state);
+
+ if (!ret && crash_state) {
+ dev_err(pas->dev, "Sub system has crashed before driver probe\n");
+ rproc_report_crash(rproc, RPROC_FATAL_ERROR);
+ ret = -EINVAL;
+ goto disable_running;
+ }
+
+ if (!ret)
+ ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
+ IRQCHIP_STATE_LINE_LEVEL, &ready_state);
+
+ /*
+ * smp2p allocate irq entry can be delayed, irq_get_irqchip_state will get -ENODEV,
+ * the 5 seconds timeout is set to wait for this, after the entry is allocated, smp2p
+ * will call the qcom_smp2p_intr and complete the timeout in the ISR.
+ */
+ if (unlikely(ret == -ENODEV) || unlikely(!ready_state)) {
+ ret = wait_for_completion_timeout(&pas->q6v5.subsys_booted,
+ msecs_to_jiffies(EARLY_ATTACH_TIMEOUT_MS));
+
+ /*
+ * The bootloader may not support early boot, mark the state as
+ * RPROC_OFFLINE so that the PAS driver can load the firmware and
+ * start the remoteproc.
+ */
+ if (!ret) {
+ dev_err(pas->dev, "Timeout on waiting for subsystem interrupt\n");
+ pas->rproc->state = RPROC_OFFLINE;
+ ret = -ETIMEDOUT;
+ goto disable_running;
+ }
+
+ /* Only ping the subsystem if ready_state is set */
+ ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
+ IRQCHIP_STATE_LINE_LEVEL, &ready_state);
+
+ if (ret)
+ goto disable_running;
+
+ if (!ready_state) {
+ ret = -EINVAL;
+ goto disable_running;
+ }
+ }
+
+ ret = qcom_q6v5_ping_subsystem(&pas->q6v5);
+
+ if (ret) {
+ dev_err(pas->dev, "Failed to ping subsystem, assuming device crashed\n");
+ rproc_report_crash(rproc, RPROC_FATAL_ERROR);
+ goto disable_running;
+ }
+
+ pas->q6v5.handover_issued = true;
+
+ return 0;
+
+disable_running:
+ pas->q6v5.running = false;
+
+ return ret;
+}
+
static const struct rproc_ops qcom_pas_ops = {
.unprepare = qcom_pas_unprepare,
.start = qcom_pas_start,
@@ -518,6 +603,7 @@ static const struct rproc_ops qcom_pas_ops = {
.parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
+ .attach = qcom_pas_attach,
};
static const struct rproc_ops qcom_pas_minidump_ops = {
@@ -823,7 +909,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
pas->proxy_pd_count = ret;
ret = qcom_q6v5_init(&pas->q6v5, pdev, rproc, desc->crash_reason_smem,
- desc->load_state, qcom_pas_handover);
+ desc->load_state, desc->early_boot, qcom_pas_handover);
if (ret)
goto detach_proxy_pds;
@@ -855,6 +941,15 @@ static int qcom_pas_probe(struct platform_device *pdev)
pas->pas_ctx->use_tzmem = rproc->has_iommu;
pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
+
+ if (pas->q6v5.early_boot) {
+ ret = qcom_q6v5_ping_subsystem_init(&pas->q6v5, pdev);
+ if (ret)
+ dev_warn(&pdev->dev, "Falling back to firmware load\n");
+ else
+ pas->rproc->state = RPROC_DETACHED;
+ }
+
ret = rproc_add(rproc);
if (ret)
goto remove_ssr_sysmon;
diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c
index c27200159a88..859141589ed7 100644
--- a/drivers/remoteproc/qcom_q6v5_wcss.c
+++ b/drivers/remoteproc/qcom_q6v5_wcss.c
@@ -1011,7 +1011,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);
+ ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, false, NULL);
if (ret)
return ret;
--
2.25.1
On Tue, Mar 10, 2026 at 03:03:22AM -0700, Jingyi Wang wrote:
> From: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
>
> Subsystems can be brought out of reset by entities such as bootloaders.
> As the irq enablement could be later than subsystem bring up, the state
> of subsystem should be checked by reading SMP2P bits and performing ping
> test.
>
> A new qcom_pas_attach() function is introduced. if a crash state is
> detected for the subsystem, rproc_report_crash() is called. If the
> subsystem is ready either at the first check or within a 5-second timeout
> and the ping is successful, it will be marked as "attached". The ready
> state could be set by either ready interrupt or handover interrupt.
>
The whole use case of early booting SoCCP is to get the charger and USB
Type-C running early - so that charging and USB Type-C works in UEFI.
If SMP2P indicates that it was booted, but it's still not there...then
there's no reason to wait another 5 seconds - it's not there.
> If "early_boot" is set by kernel but "subsys_booted" is not completed
> within the timeout, It could be the early boot feature is not supported
> by other entities. In this case, the state will be marked as RPROC_OFFLINE
> so that the PAS driver can load the firmware and start the remoteproc. As
> the running state is set once attach function is called, the watchdog or
> fatal interrupt received can be handled correctly.
>
> Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
> Co-developed-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
[..]
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
[..]
> +static int qcom_pas_attach(struct rproc *rproc)
[..]
> + if (!ret)
> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
> +
> + /*
> + * smp2p allocate irq entry can be delayed, irq_get_irqchip_state will get -ENODEV,
This on the other hand, sounds like a bug in the smp2p driver. If we can
acquire the interrupt without getting EPROBE_DEFER, then we should not
get -ENODEV when reading the irq state.
> + * the 5 seconds timeout is set to wait for this, after the entry is allocated, smp2p
> + * will call the qcom_smp2p_intr and complete the timeout in the ISR.
If this indeed is the problem you're working around with the 5 second
delay - then stop. Fix the issue instead!
Also, this comment conflicts with the reasoning for the ping and the 5
second thing in the commit message.
Regards,
Bjorn
> + */
> + if (unlikely(ret == -ENODEV) || unlikely(!ready_state)) {
> + ret = wait_for_completion_timeout(&pas->q6v5.subsys_booted,
> + msecs_to_jiffies(EARLY_ATTACH_TIMEOUT_MS));
> +
> + /*
> + * The bootloader may not support early boot, mark the state as
> + * RPROC_OFFLINE so that the PAS driver can load the firmware and
> + * start the remoteproc.
> + */
> + if (!ret) {
> + dev_err(pas->dev, "Timeout on waiting for subsystem interrupt\n");
> + pas->rproc->state = RPROC_OFFLINE;
> + ret = -ETIMEDOUT;
> + goto disable_running;
> + }
> +
> + /* Only ping the subsystem if ready_state is set */
> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
> +
> + if (ret)
> + goto disable_running;
> +
> + if (!ready_state) {
> + ret = -EINVAL;
> + goto disable_running;
> + }
> + }
> +
> + ret = qcom_q6v5_ping_subsystem(&pas->q6v5);
> +
> + if (ret) {
> + dev_err(pas->dev, "Failed to ping subsystem, assuming device crashed\n");
> + rproc_report_crash(rproc, RPROC_FATAL_ERROR);
> + goto disable_running;
> + }
> +
> + pas->q6v5.handover_issued = true;
> +
> + return 0;
> +
> +disable_running:
> + pas->q6v5.running = false;
> +
> + return ret;
> +}
> +
> static const struct rproc_ops qcom_pas_ops = {
> .unprepare = qcom_pas_unprepare,
> .start = qcom_pas_start,
> @@ -518,6 +603,7 @@ static const struct rproc_ops qcom_pas_ops = {
> .parse_fw = qcom_pas_parse_firmware,
> .load = qcom_pas_load,
> .panic = qcom_pas_panic,
> + .attach = qcom_pas_attach,
> };
>
> static const struct rproc_ops qcom_pas_minidump_ops = {
> @@ -823,7 +909,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
> pas->proxy_pd_count = ret;
>
> ret = qcom_q6v5_init(&pas->q6v5, pdev, rproc, desc->crash_reason_smem,
> - desc->load_state, qcom_pas_handover);
> + desc->load_state, desc->early_boot, qcom_pas_handover);
> if (ret)
> goto detach_proxy_pds;
>
> @@ -855,6 +941,15 @@ static int qcom_pas_probe(struct platform_device *pdev)
>
> pas->pas_ctx->use_tzmem = rproc->has_iommu;
> pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
> +
> + if (pas->q6v5.early_boot) {
> + ret = qcom_q6v5_ping_subsystem_init(&pas->q6v5, pdev);
> + if (ret)
> + dev_warn(&pdev->dev, "Falling back to firmware load\n");
> + else
> + pas->rproc->state = RPROC_DETACHED;
> + }
> +
> ret = rproc_add(rproc);
> if (ret)
> goto remove_ssr_sysmon;
> diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c
> index c27200159a88..859141589ed7 100644
> --- a/drivers/remoteproc/qcom_q6v5_wcss.c
> +++ b/drivers/remoteproc/qcom_q6v5_wcss.c
> @@ -1011,7 +1011,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> - ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);
> + ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, false, NULL);
> if (ret)
> return ret;
>
>
> --
> 2.25.1
>
On 4/6/2026 10:59 PM, Bjorn Andersson wrote:
> On Tue, Mar 10, 2026 at 03:03:22AM -0700, Jingyi Wang wrote:
>> From: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
>>
>> Subsystems can be brought out of reset by entities such as bootloaders.
>> As the irq enablement could be later than subsystem bring up, the state
>> of subsystem should be checked by reading SMP2P bits and performing ping
>> test.
>>
>> A new qcom_pas_attach() function is introduced. if a crash state is
>> detected for the subsystem, rproc_report_crash() is called. If the
>> subsystem is ready either at the first check or within a 5-second timeout
>> and the ping is successful, it will be marked as "attached". The ready
>> state could be set by either ready interrupt or handover interrupt.
>>
>
> The whole use case of early booting SoCCP is to get the charger and USB
> Type-C running early - so that charging and USB Type-C works in UEFI.
>
> If SMP2P indicates that it was booted, but it's still not there...then
> there's no reason to wait another 5 seconds - it's not there.
>
>> If "early_boot" is set by kernel but "subsys_booted" is not completed
>> within the timeout, It could be the early boot feature is not supported
>> by other entities. In this case, the state will be marked as RPROC_OFFLINE
>> so that the PAS driver can load the firmware and start the remoteproc. As
>> the running state is set once attach function is called, the watchdog or
>> fatal interrupt received can be handled correctly.
>>
>> Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
>> Co-developed-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
>> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> [..]
>> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> [..]
>> +static int qcom_pas_attach(struct rproc *rproc)
> [..]
>> + if (!ret)
>> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
>> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
>> +
>> + /*
>> + * smp2p allocate irq entry can be delayed, irq_get_irqchip_state will get -ENODEV,
>
> This on the other hand, sounds like a bug in the smp2p driver. If we can
> acquire the interrupt without getting EPROBE_DEFER, then we should not
> get -ENODEV when reading the irq state.
>
>> + * the 5 seconds timeout is set to wait for this, after the entry is allocated, smp2p
>> + * will call the qcom_smp2p_intr and complete the timeout in the ISR.
>
> If this indeed is the problem you're working around with the 5 second
> delay - then stop. Fix the issue instead!
>
> Also, this comment conflicts with the reasoning for the ping and the 5
> second thing in the commit message.
>
> Regards,
> Bjorn
>
It is a design in downstream code to avoid the irq is not received when
we check the irq state, but indeed it seems like a redundant design and
I didn't see the delay in the test for the soccp attach. So we will remove
this 5 seconds wait in next series.
Thanks,
Jingyi
>> + */
>> + if (unlikely(ret == -ENODEV) || unlikely(!ready_state)) {
>> + ret = wait_for_completion_timeout(&pas->q6v5.subsys_booted,
>> + msecs_to_jiffies(EARLY_ATTACH_TIMEOUT_MS));
>> +
>> + /*
>> + * The bootloader may not support early boot, mark the state as
>> + * RPROC_OFFLINE so that the PAS driver can load the firmware and
>> + * start the remoteproc.
>> + */
>> + if (!ret) {
>> + dev_err(pas->dev, "Timeout on waiting for subsystem interrupt\n");
>> + pas->rproc->state = RPROC_OFFLINE;
>> + ret = -ETIMEDOUT;
>> + goto disable_running;
>> + }
>> +
>> + /* Only ping the subsystem if ready_state is set */
>> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
>> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
>> +
>> + if (ret)
>> + goto disable_running;
>> +
>> + if (!ready_state) {
>> + ret = -EINVAL;
>> + goto disable_running;
>> + }
>> + }
>> +
>> + ret = qcom_q6v5_ping_subsystem(&pas->q6v5);
>> +
>> + if (ret) {
>> + dev_err(pas->dev, "Failed to ping subsystem, assuming device crashed\n");
>> + rproc_report_crash(rproc, RPROC_FATAL_ERROR);
>> + goto disable_running;
>> + }
>> +
>> + pas->q6v5.handover_issued = true;
>> +
>> + return 0;
>> +
>> +disable_running:
>> + pas->q6v5.running = false;
>> +
>> + return ret;
>> +}
>> +
>> static const struct rproc_ops qcom_pas_ops = {
>> .unprepare = qcom_pas_unprepare,
>> .start = qcom_pas_start,
>> @@ -518,6 +603,7 @@ static const struct rproc_ops qcom_pas_ops = {
>> .parse_fw = qcom_pas_parse_firmware,
>> .load = qcom_pas_load,
>> .panic = qcom_pas_panic,
>> + .attach = qcom_pas_attach,
>> };
>>
>> static const struct rproc_ops qcom_pas_minidump_ops = {
>> @@ -823,7 +909,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
>> pas->proxy_pd_count = ret;
>>
>> ret = qcom_q6v5_init(&pas->q6v5, pdev, rproc, desc->crash_reason_smem,
>> - desc->load_state, qcom_pas_handover);
>> + desc->load_state, desc->early_boot, qcom_pas_handover);
>> if (ret)
>> goto detach_proxy_pds;
>>
>> @@ -855,6 +941,15 @@ static int qcom_pas_probe(struct platform_device *pdev)
>>
>> pas->pas_ctx->use_tzmem = rproc->has_iommu;
>> pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
>> +
>> + if (pas->q6v5.early_boot) {
>> + ret = qcom_q6v5_ping_subsystem_init(&pas->q6v5, pdev);
>> + if (ret)
>> + dev_warn(&pdev->dev, "Falling back to firmware load\n");
>> + else
>> + pas->rproc->state = RPROC_DETACHED;
>> + }
>> +
>> ret = rproc_add(rproc);
>> if (ret)
>> goto remove_ssr_sysmon;
>> diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c
>> index c27200159a88..859141589ed7 100644
>> --- a/drivers/remoteproc/qcom_q6v5_wcss.c
>> +++ b/drivers/remoteproc/qcom_q6v5_wcss.c
>> @@ -1011,7 +1011,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev)
>> if (ret)
>> return ret;
>>
>> - ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);
>> + ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, false, NULL);
>> if (ret)
>> return ret;
>>
>>
>> --
>> 2.25.1
>>
On Tue, Mar 10, 2026 at 03:03:22AM -0700, Jingyi Wang wrote:
> From: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
>
> Subsystems can be brought out of reset by entities such as bootloaders.
> As the irq enablement could be later than subsystem bring up, the state
> of subsystem should be checked by reading SMP2P bits and performing ping
> test.
>
> A new qcom_pas_attach() function is introduced. if a crash state is
> detected for the subsystem, rproc_report_crash() is called. If the
> subsystem is ready either at the first check or within a 5-second timeout
> and the ping is successful, it will be marked as "attached". The ready
> state could be set by either ready interrupt or handover interrupt.
>
> If "early_boot" is set by kernel but "subsys_booted" is not completed
> within the timeout, It could be the early boot feature is not supported
> by other entities. In this case, the state will be marked as RPROC_OFFLINE
> so that the PAS driver can load the firmware and start the remoteproc. As
> the running state is set once attach function is called, the watchdog or
> fatal interrupt received can be handled correctly.
>
> Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
> Co-developed-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> ---
> drivers/remoteproc/qcom_q6v5.c | 88 +++++++++++++++++++++++++++++-
> drivers/remoteproc/qcom_q6v5.h | 17 +++++-
> drivers/remoteproc/qcom_q6v5_adsp.c | 2 +-
> drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
> drivers/remoteproc/qcom_q6v5_pas.c | 103 ++++++++++++++++++++++++++++++++++--
> drivers/remoteproc/qcom_q6v5_wcss.c | 2 +-
> 6 files changed, 204 insertions(+), 10 deletions(-)
>
> [...]
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> index 46204da046fa..4700d111e058 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -36,6 +36,8 @@
>
> #define MAX_ASSIGN_COUNT 3
>
> +#define EARLY_ATTACH_TIMEOUT_MS 5000
> +
> struct qcom_pas_data {
> int crash_reason_smem;
> const char *firmware_name;
> [...]
> @@ -510,6 +521,80 @@ static unsigned long qcom_pas_panic(struct rproc *rproc)
> return qcom_q6v5_panic(&pas->q6v5);
> }
>
> +static int qcom_pas_attach(struct rproc *rproc)
> +{
> + int ret;
> + struct qcom_pas *pas = rproc->priv;
> + bool ready_state;
> + bool crash_state;
> +
> + pas->q6v5.running = true;
> + ret = irq_get_irqchip_state(pas->q6v5.fatal_irq,
> + IRQCHIP_STATE_LINE_LEVEL, &crash_state);
> +
> + if (!ret && crash_state) {
> + dev_err(pas->dev, "Sub system has crashed before driver probe\n");
> + rproc_report_crash(rproc, RPROC_FATAL_ERROR);
> + ret = -EINVAL;
> + goto disable_running;
> + }
> +
> + if (!ret)
> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
> +
> + /*
> + * smp2p allocate irq entry can be delayed, irq_get_irqchip_state will get -ENODEV,
> + * the 5 seconds timeout is set to wait for this, after the entry is allocated, smp2p
> + * will call the qcom_smp2p_intr and complete the timeout in the ISR.
> + */
> + if (unlikely(ret == -ENODEV) || unlikely(!ready_state)) {
> + ret = wait_for_completion_timeout(&pas->q6v5.subsys_booted,
> + msecs_to_jiffies(EARLY_ATTACH_TIMEOUT_MS));
I have asked this back in October for v2 [1] and again in December for
v3 [2], but you still haven't really answered it. Please answer all
of the following questions:
1. What is the use case for this timeout?
2. In which situations will the start of the remoteproc be delayed?
3. Why does the boot firmware not wait until the remoteproc is fully
started before it continues booting?
4. If the boot firmware gives up control before the remoteproc is fully
started, how do you ensure that the handover resources are
maintained until the remoteproc signals handover?
v4 looks a bit less dangerous now since you don't enable the handover
IRQ anymore. Still, I don't understand how this would work in practice.
Removing this timeout would be preferable because then we could actually
support firmware versions that do not automatically start the remoteproc
without having to delay the boot process for 5s.
Thanks,
Stephan
[1]: https://lore.kernel.org/r/aQHmanEiWmEac7aV@linaro.org/
[2]: https://lore.kernel.org/r/aUsUhX8Km275qonq@linaro.org/
On 3/11/2026 4:28 PM, Stephan Gerhold wrote:
> On Tue, Mar 10, 2026 at 03:03:22AM -0700, Jingyi Wang wrote:
>> From: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
>>
>> Subsystems can be brought out of reset by entities such as bootloaders.
>> As the irq enablement could be later than subsystem bring up, the state
>> of subsystem should be checked by reading SMP2P bits and performing ping
>> test.
>>
>> A new qcom_pas_attach() function is introduced. if a crash state is
>> detected for the subsystem, rproc_report_crash() is called. If the
>> subsystem is ready either at the first check or within a 5-second timeout
>> and the ping is successful, it will be marked as "attached". The ready
>> state could be set by either ready interrupt or handover interrupt.
>>
>> If "early_boot" is set by kernel but "subsys_booted" is not completed
>> within the timeout, It could be the early boot feature is not supported
>> by other entities. In this case, the state will be marked as RPROC_OFFLINE
>> so that the PAS driver can load the firmware and start the remoteproc. As
>> the running state is set once attach function is called, the watchdog or
>> fatal interrupt received can be handled correctly.
>>
>> Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
>> Co-developed-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
>> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
>> ---
>> drivers/remoteproc/qcom_q6v5.c | 88 +++++++++++++++++++++++++++++-
>> drivers/remoteproc/qcom_q6v5.h | 17 +++++-
>> drivers/remoteproc/qcom_q6v5_adsp.c | 2 +-
>> drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
>> drivers/remoteproc/qcom_q6v5_pas.c | 103 ++++++++++++++++++++++++++++++++++--
>> drivers/remoteproc/qcom_q6v5_wcss.c | 2 +-
>> 6 files changed, 204 insertions(+), 10 deletions(-)
>>
>> [...]
>> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
>> index 46204da046fa..4700d111e058 100644
>> --- a/drivers/remoteproc/qcom_q6v5_pas.c
>> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
>> @@ -36,6 +36,8 @@
>>
>> #define MAX_ASSIGN_COUNT 3
>>
>> +#define EARLY_ATTACH_TIMEOUT_MS 5000
>> +
>> struct qcom_pas_data {
>> int crash_reason_smem;
>> const char *firmware_name;
>> [...]
>> @@ -510,6 +521,80 @@ static unsigned long qcom_pas_panic(struct rproc *rproc)
>> return qcom_q6v5_panic(&pas->q6v5);
>> }
>>
>> +static int qcom_pas_attach(struct rproc *rproc)
>> +{
>> + int ret;
>> + struct qcom_pas *pas = rproc->priv;
>> + bool ready_state;
>> + bool crash_state;
>> +
>> + pas->q6v5.running = true;
>> + ret = irq_get_irqchip_state(pas->q6v5.fatal_irq,
>> + IRQCHIP_STATE_LINE_LEVEL, &crash_state);
>> +
>> + if (!ret && crash_state) {
>> + dev_err(pas->dev, "Sub system has crashed before driver probe\n");
>> + rproc_report_crash(rproc, RPROC_FATAL_ERROR);
>> + ret = -EINVAL;
>> + goto disable_running;
>> + }
>> +
>> + if (!ret)
>> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
>> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
>> +
>> + /*
>> + * smp2p allocate irq entry can be delayed, irq_get_irqchip_state will get -ENODEV,
>> + * the 5 seconds timeout is set to wait for this, after the entry is allocated, smp2p
>> + * will call the qcom_smp2p_intr and complete the timeout in the ISR.
>> + */
>> + if (unlikely(ret == -ENODEV) || unlikely(!ready_state)) {
>> + ret = wait_for_completion_timeout(&pas->q6v5.subsys_booted,
>> + msecs_to_jiffies(EARLY_ATTACH_TIMEOUT_MS));
>
> I have asked this back in October for v2 [1] and again in December for
> v3 [2], but you still haven't really answered it. Please answer all
> of the following questions:
>
> 1. What is the use case for this timeout?
> 2. In which situations will the start of the remoteproc be delayed?
> 3. Why does the boot firmware not wait until the remoteproc is fully
> started before it continues booting?
> 4. If the boot firmware gives up control before the remoteproc is fully
> started, how do you ensure that the handover resources are
> maintained until the remoteproc signals handover?
>
> v4 looks a bit less dangerous now since you don't enable the handover
> IRQ anymore. Still, I don't understand how this would work in practice.
> Removing this timeout would be preferable because then we could actually
> support firmware versions that do not automatically start the remoteproc
> without having to delay the boot process for 5s.
>
> Thanks,
> Stephan
>
> [1]: https://lore.kernel.org/r/aQHmanEiWmEac7aV@linaro.org/
> [2]: https://lore.kernel.org/r/aUsUhX8Km275qonq@linaro.org/
Hi Stephan,
For question [1] and [2],
We tried to answer the reason why the timeout is added in the comments,
but seems it is still not clear enough, it is a design in downstream
code to avoid the irq is not received when we check the irq state, but
indeed it seems like a redundant design and may block firmware start.
So we will remove this timeout in next series.
For question [3] and [4],
I think it related to how to deal with the "start" if "attach" fail?
As we will remove the timeout, we have 2 proposals for this:
a. attach fail -> keep the state "RPROC_DETACHED" -> if user echo "start" -> call attach() function again
-> if user echo "stop" to set RPROC_OFFLINE -> user echo "start" to do firmware load
If attach fail, user need to do "stop" first to set the RPROC_OFFLINE
and then "start" the remoteproc for firmware loading.
b. attach fail -> change state to RPROC_OFFLINE -> user echo "start" to do firmware load
this is what current code do, if attach fail, RPROC_OFFLINE will be set,
next time user echo start will go to the firmware load path.
Could you please share your comments on which one is better?
And qcom_pas_attach() is called by rproc_add() with auto_boot enabled.
if qcom_pas_attach() return false, rproc_add() will teardown all the
resources, so in rproc_trigger_auto_boot(), instead of calling rproc_boot
directly:
if (rproc->state == RPROC_DETACHED)
return rproc_boot(rproc);
we will schedule work to call rproc_boot asynchronously, like what firmware
loading is doing now, to make sure rproc_add() can success even if attach
failed, and we can do the firmware loading in the next step.
Do you think it is okay to do this change?
Thanks,
Jingyi
© 2016 - 2026 Red Hat, Inc.