aboutsummaryrefslogtreecommitdiffci
path: root/patches/remoteproc
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
authorGravatar Saya Andy <saya.andy@posteo.com> 2026-09-18 21:45:43 +0700
committerGravatar Saya Andy <saya.andy@posteo.com> 2026-09-18 21:45:43 +0700
commit1f2b96ab1a13be88c57c78be08168c0bb0080088 (patch)
treecb830075f52e08698d63f0e5c4e1359a063a62b8 /patches/remoteproc
parentee8c44555b0c3d61bf1998ddf3a5823db04ae68f (diff)
downloadkernel-surface-1f2b96ab1a13be88c57c78be08168c0bb0080088.tar.gz
kernel-surface-1f2b96ab1a13be88c57c78be08168c0bb0080088.zip
fix: adapt patches for some 7.2.6 kernel updates to remoteproc and usbfedora-45-kernel-7.2.6-patchset-1
Diffstat (limited to 'patches/remoteproc')
-rw-r--r--patches/remoteproc/0002-soc-qcom-smp2p-Ensure-there-is-enough-space-for-outb.patch25
-rw-r--r--patches/remoteproc/0003-soc-qcom-smp2p-Use-length-limited-strncmp-for-compar.patch24
-rw-r--r--patches/remoteproc/0004-soc-qcom-smp2p-Drop-redundant-stack-copies-of-entry-.patch58
-rw-r--r--patches/remoteproc/0005-rpmsg-core-Call-announce_destroy-only-after-announce.patch26
-rw-r--r--patches/remoteproc/0006-rpmsg-core-Make-it-easier-to-manually-create-endpoin.patch166
-rw-r--r--patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch223
-rw-r--r--patches/remoteproc/0008-remoteproc-qcom_q6v5_pas-Add-early_boot-to-x1e80100-cdsp-adsp.patch18
-rw-r--r--patches/remoteproc/0009-remoteproc-core-Allow-restarting-detached-remoteproc.patch349
-rw-r--r--patches/remoteproc/0010-remoteproc-qcom_q6v5-Send-SMP2P-stop-signal-in-attac.patch31
-rw-r--r--patches/remoteproc/0011-remoteproc-qcom_q6v5_pas-Attach-running-remoteproc-i.patch106
-rw-r--r--patches/remoteproc/0012-remoteproc-qcom_q6v5_pas-Avoid-using-broken-reset.patch101
11 files changed, 1127 insertions, 0 deletions
diff --git a/patches/remoteproc/0002-soc-qcom-smp2p-Ensure-there-is-enough-space-for-outb.patch b/patches/remoteproc/0002-soc-qcom-smp2p-Ensure-there-is-enough-space-for-outb.patch
new file mode 100644
index 0000000..49e7de3
--- /dev/null
+++ b/patches/remoteproc/0002-soc-qcom-smp2p-Ensure-there-is-enough-space-for-outb.patch
@@ -0,0 +1,25 @@
+The SMP2P SMEM item has limited space for outbound entries, but the DT can
+specify any number of entries. Add a check to prevent out of bounds writes
+when an invalid DT specifies more entries than expected.
+
+Fixes: 50e99641413e ("soc: qcom: smp2p: Qualcomm Shared Memory Point to Point")
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/soc/qcom/smp2p.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
+index 1ea4d35c6..876648d0b 100644
+--- a/drivers/soc/qcom/smp2p.c
++++ b/drivers/soc/qcom/smp2p.c
+@@ -441,6 +441,9 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
+ struct smp2p_smem_item *out = smp2p->out;
+ char buf[SMP2P_MAX_ENTRY_NAME] = {};
+
++ if (out->valid_entries == out->total_entries)
++ return -ENOMEM;
++
+ /* Allocate an entry from the smem item */
+ strscpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
+ memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
diff --git a/patches/remoteproc/0003-soc-qcom-smp2p-Use-length-limited-strncmp-for-compar.patch b/patches/remoteproc/0003-soc-qcom-smp2p-Use-length-limited-strncmp-for-compar.patch
new file mode 100644
index 0000000..7bbb199
--- /dev/null
+++ b/patches/remoteproc/0003-soc-qcom-smp2p-Use-length-limited-strncmp-for-compar.patch
@@ -0,0 +1,24 @@
+A rogue (or broken) remoteproc might not null-terminate its entry names.
+Use strncmp() instead of strcmp() to avoid making out of bounds accesses in
+that situation.
+
+Fixes: 50e99641413e ("soc: qcom: smp2p: Qualcomm Shared Memory Point to Point")
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/soc/qcom/smp2p.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
+index 876648d0b..dcf222b19 100644
+--- a/drivers/soc/qcom/smp2p.c
++++ b/drivers/soc/qcom/smp2p.c
+@@ -238,7 +238,7 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
+ for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
+ list_for_each_entry(entry, &smp2p->inbound, node) {
+ memcpy(buf, in->entries[i].name, sizeof(buf));
+- if (!strcmp(buf, entry->name)) {
++ if (!strncmp(buf, entry->name, SMP2P_MAX_ENTRY_NAME)) {
+ entry->value = &in->entries[i].value;
+ break;
+ }
diff --git a/patches/remoteproc/0004-soc-qcom-smp2p-Drop-redundant-stack-copies-of-entry-.patch b/patches/remoteproc/0004-soc-qcom-smp2p-Drop-redundant-stack-copies-of-entry-.patch
new file mode 100644
index 0000000..a672ad3
--- /dev/null
+++ b/patches/remoteproc/0004-soc-qcom-smp2p-Drop-redundant-stack-copies-of-entry-.patch
@@ -0,0 +1,58 @@
+For the purposes of this driver, a char is always going to be the same size
+as an u8, so we can access the entry names directly instead of making a
+copy on the stack. Several other Qualcomm-related drivers use char directly
+in such binary structs as well (e.g. qcom_battmgr).
+
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/soc/qcom/smp2p.c | 10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
+index dcf222b19..176fce4cf 100644
+--- a/drivers/soc/qcom/smp2p.c
++++ b/drivers/soc/qcom/smp2p.c
+@@ -73,7 +73,7 @@ struct smp2p_smem_item {
+ u32 flags;
+
+ struct {
+- u8 name[SMP2P_MAX_ENTRY_NAME];
++ char name[SMP2P_MAX_ENTRY_NAME];
+ u32 value;
+ } entries[SMP2P_MAX_ENTRY];
+ } __packed;
+@@ -228,7 +228,6 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
+ struct smp2p_entry *entry;
+ int irq_pin;
+ u32 status;
+- char buf[SMP2P_MAX_ENTRY_NAME];
+ u32 val;
+ int i;
+
+@@ -237,8 +236,7 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
+ /* Match newly created entries */
+ for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
+ list_for_each_entry(entry, &smp2p->inbound, node) {
+- memcpy(buf, in->entries[i].name, sizeof(buf));
+- if (!strncmp(buf, entry->name, SMP2P_MAX_ENTRY_NAME)) {
++ if (!strncmp(in->entries[i].name, entry->name, SMP2P_MAX_ENTRY_NAME)) {
+ entry->value = &in->entries[i].value;
+ break;
+ }
+@@ -439,14 +437,12 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
+ struct device_node *node)
+ {
+ struct smp2p_smem_item *out = smp2p->out;
+- char buf[SMP2P_MAX_ENTRY_NAME] = {};
+
+ if (out->valid_entries == out->total_entries)
+ return -ENOMEM;
+
+ /* Allocate an entry from the smem item */
+- strscpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
+- memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
++ strscpy(out->entries[out->valid_entries].name, entry->name, SMP2P_MAX_ENTRY_NAME);
+
+ /* Make the logical entry reference the physical value */
+ entry->value = &out->entries[out->valid_entries].value;
diff --git a/patches/remoteproc/0005-rpmsg-core-Call-announce_destroy-only-after-announce.patch b/patches/remoteproc/0005-rpmsg-core-Call-announce_destroy-only-after-announce.patch
new file mode 100644
index 0000000..94bfd26
--- /dev/null
+++ b/patches/remoteproc/0005-rpmsg-core-Call-announce_destroy-only-after-announce.patch
@@ -0,0 +1,26 @@
+rpmsg_dev_probe() calls rpdev->ops->announce_create() only when the driver
+specifies a callback and an endpoint was opened, but rpmsg_dev_remove()
+calls announce_destroy() unconditionally. Fix this by adding the missing
+check.
+
+Cc: stable@vger.kernel.org
+Fixes: 7586516ca043 ("rpmsg: Only invoke announce_create for rpdev with endpoints")
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/rpmsg/rpmsg_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
+index 5d661681a..fb444dc2f 100644
+--- a/drivers/rpmsg/rpmsg_core.c
++++ b/drivers/rpmsg/rpmsg_core.c
+@@ -533,7 +533,7 @@ static void rpmsg_dev_remove(struct device *dev)
+ struct rpmsg_device *rpdev = to_rpmsg_device(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
+
+- if (rpdev->ops->announce_destroy)
++ if (rpdev->ept && rpdev->ops->announce_destroy)
+ rpdev->ops->announce_destroy(rpdev);
+
+ if (rpdrv->remove)
diff --git a/patches/remoteproc/0006-rpmsg-core-Make-it-easier-to-manually-create-endpoin.patch b/patches/remoteproc/0006-rpmsg-core-Make-it-easier-to-manually-create-endpoin.patch
new file mode 100644
index 0000000..9aac845
--- /dev/null
+++ b/patches/remoteproc/0006-rpmsg-core-Make-it-easier-to-manually-create-endpoin.patch
@@ -0,0 +1,166 @@
+Currently, the rpmsg core automatically creates an endpoint for rpmsg
+drivers that specify a receive callback. This works "somewhat" in most
+simple cases, but it is often prone to race conditions: The receive
+callback can be called as soon and as long as the endpoint is open, so
+drivers must be prepared to handle calls to the receive callback:
+
+ - Before their probe() function is called
+ (after the endpoint was created)
+ - In parallel to their probe() function
+ - In parallel to their remove() function
+ - After their remove() function is called
+ (before the endpoint is destroyed)
+
+It is difficult for drivers to handle this without being able to run code
+before endpoint creation and after endpoint destruction. Also, they may
+need to hold locks while creating/destroying the endpoint to handle edge
+cases reliably.
+
+Drivers can already create endpoints manually by omitting the receive
+callback in rpmsg_driver, but for most simple cases where there is only
+a single channel, the boilerplate required for that is a bit cumbersome.
+
+Add a rpmsg_dev_open_ept() function that can be called by drivers during
+the probe() function. It results in effectively the same that the rpmsg
+core would normally do if the receive callback is specified.
+announce_create() and announce_destroy() are still handled by the rpmsg
+core. During remove(), the drivers can directly call rpmsg_destroy_ept().
+
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/rpmsg/rpmsg_core.c | 51 +++++++++++++++++++++++++++-----------
+ include/linux/rpmsg.h | 12 +++++++++
+ 2 files changed, 49 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
+index 6783d04b591dd1..896fb8a3a4f5f4 100644
+--- a/drivers/rpmsg/rpmsg_core.c
++++ b/drivers/rpmsg/rpmsg_core.c
+@@ -95,6 +95,14 @@ EXPORT_SYMBOL(rpmsg_release_channel);
+ * equals to the src address of their rpmsg channel), the driver's handler
+ * is invoked to process it.
+ *
++ * Note that the endpoint for simple rpmsg drivers is created before calling
++ * probe() and closed after calling remove(), so special care must be taken
++ * to handle calls to the rx callback before/in parallel of probe() and
++ * after/in parallel of remove(). If more control over the endpoint creation
++ * is required to avoid race conditions, drivers can omit the callback and
++ * explicitly call rpmsg_dev_open_ept() in probe() and rpmsg_destroy_ept() in
++ * remove(), together with locks as needed.
++ *
+ * That said, more complicated drivers might need to allocate
+ * additional rpmsg addresses, and bind them to different rx callbacks.
+ * To accomplish that, those drivers need to call this function.
+@@ -451,6 +459,32 @@ static int rpmsg_uevent(const struct device *dev, struct kobj_uevent_env *env)
+ rpdev->id.name);
+ }
+
++struct rpmsg_endpoint *rpmsg_dev_open_ept(struct rpmsg_device *rpdev,
++ rpmsg_rx_cb_t cb, void *priv)
++{
++ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
++ struct rpmsg_channel_info chinfo = {
++ .src = rpdev->src,
++ .dst = RPMSG_ADDR_ANY,
++ };
++ struct rpmsg_endpoint *ept;
++
++ strscpy(chinfo.name, rpdev->id.name, sizeof(chinfo.name));
++
++ ept = rpmsg_create_ept(rpdev, cb, priv, chinfo);
++ if (!ept) {
++ dev_err(&rpdev->dev, "failed to create endpoint\n");
++ return NULL;
++ }
++
++ rpdev->ept = ept;
++ rpdev->src = ept->addr;
++
++ ept->flow_cb = rpdrv->flowcontrol;
++ return ept;
++}
++EXPORT_SYMBOL(rpmsg_dev_open_ept);
++
+ /*
+ * when an rpmsg driver is probed with a channel, we seamlessly create
+ * it an endpoint, binding its rx callback to a unique local rpmsg
+@@ -463,7 +497,6 @@ static int rpmsg_dev_probe(struct device *dev)
+ {
+ struct rpmsg_device *rpdev = to_rpmsg_device(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
+- struct rpmsg_channel_info chinfo = {};
+ struct rpmsg_endpoint *ept = NULL;
+ int err;
+
+@@ -473,21 +506,11 @@ static int rpmsg_dev_probe(struct device *dev)
+ goto out;
+
+ if (rpdrv->callback) {
+- strscpy(chinfo.name, rpdev->id.name, sizeof(chinfo.name));
+- chinfo.src = rpdev->src;
+- chinfo.dst = RPMSG_ADDR_ANY;
+-
+- ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
++ ept = rpmsg_dev_open_ept(rpdev, rpdrv->callback, NULL);
+ if (!ept) {
+- dev_err(dev, "failed to create endpoint\n");
+ err = -ENOMEM;
+ goto out;
+ }
+-
+- rpdev->ept = ept;
+- rpdev->src = ept->addr;
+-
+- ept->flow_cb = rpdrv->flowcontrol;
+ }
+
+ err = rpdrv->probe(rpdev);
+@@ -496,7 +519,7 @@ static int rpmsg_dev_probe(struct device *dev)
+ goto destroy_ept;
+ }
+
+- if (ept && rpdev->ops->announce_create) {
++ if (rpdev->ept && rpdev->ops->announce_create) {
+ err = rpdev->ops->announce_create(rpdev);
+ if (err) {
+ dev_err(dev, "failed to announce creation\n");
+@@ -527,7 +550,7 @@ static void rpmsg_dev_remove(struct device *dev)
+ if (rpdrv->remove)
+ rpdrv->remove(rpdev);
+
+- if (rpdev->ept)
++ if (rpdrv->callback && rpdev->ept)
+ rpmsg_destroy_ept(rpdev->ept);
+ }
+
+diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
+index 83266ce1464204..c3719703553c0a 100644
+--- a/include/linux/rpmsg.h
++++ b/include/linux/rpmsg.h
+@@ -181,6 +181,8 @@ void rpmsg_destroy_ept(struct rpmsg_endpoint *);
+ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *,
+ rpmsg_rx_cb_t cb, void *priv,
+ struct rpmsg_channel_info chinfo);
++struct rpmsg_endpoint *rpmsg_dev_open_ept(struct rpmsg_device *rpdev,
++ rpmsg_rx_cb_t cb, void *priv);
+
+ int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len);
+ int rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst);
+@@ -249,6 +251,16 @@ static inline struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev
+ return NULL;
+ }
+
++static inline struct rpmsg_endpoint *rpmsg_dev_open_ept(struct rpmsg_device *rpdev,
++ rpmsg_rx_cb_t cb,
++ void *priv)
++{
++ /* This shouldn't be possible */
++ WARN_ON(1);
++
++ return NULL;
++}
++
+ static inline int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len)
+ {
+ /* This shouldn't be possible */
diff --git a/patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch b/patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch
new file mode 100644
index 0000000..7e236fc
--- /dev/null
+++ b/patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch
@@ -0,0 +1,223 @@
+On some platforms (e.g. X1E), the boot firmware already starts some of the
+remoteprocs with a "lite" firmware. This firmware is left running when
+Linux gets started. In this situation, the smp2p driver currently fully
+reinitializes the outgoing SMEM item and ignores the incoming SMEM state
+until the first incoming interrupt. This has worked fine so far, but has
+also has limitations:
+
+ - The initial state of the incoming SMEM item is not captured, so we
+ might miss falling edges reported by the first incoming interrupt.
+
+ - If the SMP2P driver of the remoteproc is implemented similar to the
+ Linux driver, it might cache addresses of the incoming SMP2P entries,
+ but there is no guarantee that we allocate them in the same order as the
+ boot firmware.
+
+ - We may inadvertently send an unexpected SSR ACK, if the boot firmware
+ had the restart ack bit set before.
+
+Implement a more smoother form of handover by reusing the existing outgoing
+SMP2P item if it matches our expectation. Reuse outgoing entries if they
+already exist. Read the initial incoming state and take over the SSR state.
+
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/soc/qcom/smp2p.c | 102 +++++++++++++++++++++++++++------------
+ 1 file changed, 71 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
+index 9b1074c8c8065c..e94388f7aded6b 100644
+--- a/drivers/soc/qcom/smp2p.c
++++ b/drivers/soc/qcom/smp2p.c
+@@ -36,10 +36,6 @@
+ * The driver uses the Linux GPIO and interrupt framework to expose a virtual
+ * GPIO for each outbound entry and a virtual interrupt controller for each
+ * inbound entry.
+- *
+- * V2 of SMP2P allows remote processors to write to outbound smp2p items before
+- * the full smp2p connection is negotiated. This is important for processors
+- * started before linux runs.
+ */
+
+ #define SMP2P_MAX_ENTRY 16
+@@ -215,8 +211,6 @@ static void qcom_smp2p_do_ssr_ack(struct qcom_smp2p *smp2p)
+ if (smp2p->ssr_ack)
+ val |= BIT(SMP2P_FLAGS_RESTART_ACK_BIT);
+ out->flags = val;
+-
+- qcom_smp2p_kick(smp2p);
+ }
+
+ static void qcom_smp2p_negotiate(struct qcom_smp2p *smp2p)
+@@ -227,8 +221,10 @@ static void qcom_smp2p_negotiate(struct qcom_smp2p *smp2p)
+ if (in->version == out->version) {
+ out->features &= in->features;
+
+- if (out->features & SMP2P_FEATURE_SSR_ACK)
++ if (out->features & SMP2P_FEATURE_SSR_ACK) {
+ smp2p->ssr_ack_enabled = true;
++ smp2p->ssr_ack = !!(out->flags & BIT(SMP2P_FLAGS_RESTART_ACK_BIT));
++ }
+
+ smp2p->negotiation_done = true;
+ trace_smp2p_negotiate(smp2p->dev, out->features);
+@@ -337,6 +333,24 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
+ }
+ }
+
++static bool qcom_smp2p_scan(struct qcom_smp2p *smp2p)
++{
++ bool ack_restart = false;
++
++ if (!smp2p->negotiation_done)
++ qcom_smp2p_negotiate(smp2p);
++
++ if (smp2p->negotiation_done) {
++ ack_restart = qcom_smp2p_check_ssr(smp2p);
++ qcom_smp2p_notify_in(smp2p);
++
++ if (ack_restart)
++ qcom_smp2p_do_ssr_ack(smp2p);
++ }
++
++ return ack_restart;
++}
++
+ /**
+ * qcom_smp2p_intr() - interrupt handler for incoming notifications
+ * @irq: unused
+@@ -370,16 +384,8 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data)
+ smp2p->in = in;
+ }
+
+- if (!smp2p->negotiation_done)
+- qcom_smp2p_negotiate(smp2p);
+-
+- if (smp2p->negotiation_done) {
+- ack_restart = qcom_smp2p_check_ssr(smp2p);
+- qcom_smp2p_notify_in(smp2p);
+-
+- if (ack_restart)
+- qcom_smp2p_do_ssr_ack(smp2p);
+- }
++ if (qcom_smp2p_scan(smp2p))
++ qcom_smp2p_kick(smp2p);
+
+ out:
+ return IRQ_HANDLED;
+@@ -519,17 +525,24 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
+ struct device_node *node)
+ {
+ struct smp2p_smem_item *out = smp2p->out;
++ int i;
+
+- if (out->valid_entries == out->total_entries)
+- return -ENOMEM;
++ /* Check if we have an entry already (e.g. allocated by boot firmware) */
++ for (i = 0; i < out->valid_entries; i++)
++ if (!strncmp(out->entries[i].name, entry->name, SMP2P_MAX_ENTRY_NAME))
++ break;
+
+- /* Allocate an entry from the smem item */
+- strscpy(out->entries[out->valid_entries].name, entry->name, SMP2P_MAX_ENTRY_NAME);
++ if (i == out->valid_entries) {
++ /* Allocate an entry from the smem item */
++ if (i == out->total_entries)
++ return -ENOMEM;
+
+- /* Make the logical entry reference the physical value */
+- entry->value = &out->entries[out->valid_entries].value;
++ strscpy(out->entries[i].name, entry->name, SMP2P_MAX_ENTRY_NAME);
++ out->valid_entries++;
++ }
+
+- out->valid_entries++;
++ /* Make the logical entry reference the physical value */
++ entry->value = &out->entries[i].value;
+
+ entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry);
+ if (IS_ERR(entry->state)) {
+@@ -559,6 +572,29 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p)
+ return PTR_ERR(out);
+ }
+
++ smp2p->out = out;
++
++ if (ret == -EEXIST && smp2p->in) {
++ if (out->magic == SMP2P_MAGIC &&
++ out->version == 1 &&
++ out->local_pid == smp2p->local_pid &&
++ out->remote_pid == smp2p->remote_pid &&
++ out->total_entries >= SMP2P_MAX_ENTRY &&
++ out->valid_entries <= out->total_entries) {
++ /*
++ * Reuse existing smem item, but adjust features to
++ * what we support. This will be updated later when we
++ * negotiate with the features of the remote side.
++ */
++ out->features = SMP2P_ALL_FEATURES;
++ return 0;
++ } else {
++ dev_warn(smp2p->dev, "Unexpected local smp2p item allocated by firmware, resetting. "
++ "(magic: %#x, version: %d, local_pid: %d, remote_pid: %d, total_entries: %d, valid_entries: %d)\n",
++ out->magic, out->version, out->local_pid, out->remote_pid, out->total_entries, out->valid_entries);
++ }
++ }
++
+ memset(out, 0, sizeof(*out));
+ out->magic = SMP2P_MAGIC;
+ out->local_pid = smp2p->local_pid;
+@@ -585,8 +621,6 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p)
+
+ qcom_smp2p_kick(smp2p);
+
+- smp2p->out = out;
+-
+ return 0;
+ }
+
+@@ -626,6 +660,7 @@ static int smp2p_parse_ipc(struct qcom_smp2p *smp2p)
+
+ static int qcom_smp2p_probe(struct platform_device *pdev)
+ {
++ struct smp2p_smem_item *in;
+ struct smp2p_entry *entry;
+ struct qcom_smp2p *smp2p;
+ const char *key;
+@@ -676,6 +711,10 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
+ return ret;
+ }
+
++ in = qcom_smem_get(smp2p->remote_pid, smp2p->smem_items[SMP2P_INBOUND], NULL);
++ if (!IS_ERR(in))
++ smp2p->in = in;
++
+ ret = qcom_smp2p_alloc_outbound_item(smp2p);
+ if (ret < 0)
+ goto release_mbox;
+@@ -709,11 +748,9 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
+ }
+ }
+
+- /* Check inbound entries in the case of early boot processor */
+- qcom_smp2p_start_in(smp2p);
+-
+- /* Kick the outgoing edge after allocating entries */
+- qcom_smp2p_kick(smp2p);
++ if (smp2p->in)
++ /* Ignore return status since we kick unconditionally below */
++ qcom_smp2p_scan(smp2p);
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, qcom_smp2p_intr,
+@@ -724,6 +761,9 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
+ goto unwind_interfaces;
+ }
+
++ /* Kick the outgoing edge after allocating entries */
++ qcom_smp2p_kick(smp2p);
++
+ /*
+ * Treat smp2p interrupt as wakeup source, but keep it disabled
+ * by default. User space can decide enabling it depending on its
diff --git a/patches/remoteproc/0008-remoteproc-qcom_q6v5_pas-Add-early_boot-to-x1e80100-cdsp-adsp.patch b/patches/remoteproc/0008-remoteproc-qcom_q6v5_pas-Add-early_boot-to-x1e80100-cdsp-adsp.patch
new file mode 100644
index 0000000..950687e
--- /dev/null
+++ b/patches/remoteproc/0008-remoteproc-qcom_q6v5_pas-Add-early_boot-to-x1e80100-cdsp-adsp.patch
@@ -0,0 +1,18 @@
+--- a/drivers/remoteproc/qcom_q6v5_pas.c 2026-09-07 00:07:20.000000000 +0200
++++ b/drivers/remoteproc/qcom_q6v5_pas.c 2026-09-11 17:05:48.858145807 +0200
+@@ -1249,6 +1249,7 @@
+ .lite_dtb_pas_id = 0x29,
+ .minidump_id = 5,
+ .auto_boot = true,
++ .early_boot = true,
+ .proxy_pd_names = (char*[]){
+ "lcx",
+ "lmx",
+@@ -1268,6 +1269,7 @@
+ .dtb_pas_id = 0x25,
+ .minidump_id = 7,
+ .auto_boot = true,
++ .early_boot = true,
+ .proxy_pd_names = (char*[]){
+ "cx",
+ "mxc",
diff --git a/patches/remoteproc/0009-remoteproc-core-Allow-restarting-detached-remoteproc.patch b/patches/remoteproc/0009-remoteproc-core-Allow-restarting-detached-remoteproc.patch
new file mode 100644
index 0000000..47f81de
--- /dev/null
+++ b/patches/remoteproc/0009-remoteproc-core-Allow-restarting-detached-remoteproc.patch
@@ -0,0 +1,349 @@
+At the moment, the remoteproc core supports only one auto boot "strategy":
+A remoteproc that is already running during boot ("detached") is attached,
+a remoteproc that is offline is started after loading the firmware. This
+works if the firmware loaded during boot is the same that we would start
+later, but it could also be outdated or a reduced size version that is
+missing some functionality. In this case, the best option is to try
+restarting it with new firmware - assuming that it is available while
+booting.
+
+Add support for this alternative behavior by replacing the "auto_boot" bool
+with a more explicit enum rproc_auto_boot.
+
+For RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE, try requesting the firmware
+early and - if successful - perform a clean stop of the remoteproc so that
+it can be restarted with the new firmware afterwards. A remoteproc driver
+making use of this functionality must handle the stop() callback being
+called during the initial detached state.
+
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/remoteproc/imx_rproc.c | 8 +++--
+ drivers/remoteproc/ingenic_rproc.c | 5 ++-
+ drivers/remoteproc/pru_rproc.c | 2 +-
+ drivers/remoteproc/qcom_q6v5_adsp.c | 5 ++-
+ drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
+ drivers/remoteproc/qcom_q6v5_pas.c | 5 ++-
+ drivers/remoteproc/rcar_rproc.c | 2 +-
+ drivers/remoteproc/remoteproc_core.c | 47 ++++++++++++++++++-------
+ drivers/remoteproc/stm32_rproc.c | 8 +++--
+ drivers/remoteproc/wkup_m3_rproc.c | 2 +-
+ drivers/remoteproc/xlnx_r5_remoteproc.c | 2 +-
+ include/linux/remoteproc.h | 23 +++++++++++-
+ 12 files changed, 85 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
+index 0dd80e688b0ea3..d5758207d155b6 100644
+--- a/drivers/remoteproc/imx_rproc.c
++++ b/drivers/remoteproc/imx_rproc.c
+@@ -1288,8 +1288,12 @@ static int imx_rproc_probe(struct platform_device *pdev)
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock\n");
+ }
+
+- if (rproc->state != RPROC_DETACHED)
+- rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot");
++ if (rproc->state != RPROC_DETACHED) {
++ if (of_property_read_bool(np, "fsl,auto-boot"))
++ rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ else
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
++ }
+
+ if (dcfg->flags & IMX_RPROC_NEED_SYSTEM_OFF) {
+ /*
+diff --git a/drivers/remoteproc/ingenic_rproc.c b/drivers/remoteproc/ingenic_rproc.c
+index 1b78d8ddeacfd5..351b9529713790 100644
+--- a/drivers/remoteproc/ingenic_rproc.c
++++ b/drivers/remoteproc/ingenic_rproc.c
+@@ -177,7 +177,10 @@ static int ingenic_rproc_probe(struct platform_device *pdev)
+ if (!rproc)
+ return -ENOMEM;
+
+- rproc->auto_boot = auto_boot;
++ if (auto_boot)
++ rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ else
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+
+ vpu = rproc->priv;
+ vpu->dev = &pdev->dev;
+diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c
+index a4636c7bc6b7be..0928a75916f352 100644
+--- a/drivers/remoteproc/pru_rproc.c
++++ b/drivers/remoteproc/pru_rproc.c
+@@ -1029,7 +1029,7 @@ static int pru_rproc_probe(struct platform_device *pdev)
+ * remote-processor as part of its state machine either through the
+ * remoteproc sysfs interface or through the equivalent kernel API.
+ */
+- rproc->auto_boot = false;
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+
+ pru = rproc->priv;
+ pru->dev = dev;
+diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
+index b5c8d6d38c9cbc..7184f73be69105 100644
+--- a/drivers/remoteproc/qcom_q6v5_adsp.c
++++ b/drivers/remoteproc/qcom_q6v5_adsp.c
+@@ -673,7 +673,10 @@ static int adsp_probe(struct platform_device *pdev)
+ return -ENOMEM;
+ }
+
+- rproc->auto_boot = desc->auto_boot;
++ if (desc->auto_boot)
++ rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ else
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+ rproc->has_iommu = desc->has_iommu;
+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
+
+diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
+index ae78f5c7c1b69e..606896500ec310 100644
+--- a/drivers/remoteproc/qcom_q6v5_mss.c
++++ b/drivers/remoteproc/qcom_q6v5_mss.c
+@@ -2095,7 +2095,7 @@ static int q6v5_probe(struct platform_device *pdev)
+ return -ENOMEM;
+ }
+
+- rproc->auto_boot = false;
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
+
+ qproc = rproc->priv;
+diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
+index da27d1d3c9da64..1327e187514821 100644
+--- a/drivers/remoteproc/qcom_q6v5_pas.c
++++ b/drivers/remoteproc/qcom_q6v5_pas.c
+@@ -774,7 +774,10 @@ static int qcom_pas_probe(struct platform_device *pdev)
+ }
+
+ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
+- rproc->auto_boot = desc->auto_boot;
++ if (desc->auto_boot)
++ rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ else
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
+
+ pas = rproc->priv;
+diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c
+index 3c25625f966dcb..99a26a05165883 100644
+--- a/drivers/remoteproc/rcar_rproc.c
++++ b/drivers/remoteproc/rcar_rproc.c
+@@ -172,7 +172,7 @@ static int rcar_rproc_probe(struct platform_device *pdev)
+ dev_set_drvdata(dev, rproc);
+
+ /* Manually start the rproc */
+- rproc->auto_boot = false;
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+
+ ret = devm_rproc_add(dev, rproc);
+ if (ret) {
+diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
+index b087ed21858a8c..ec75659957033b 100644
+--- a/drivers/remoteproc/remoteproc_core.c
++++ b/drivers/remoteproc/remoteproc_core.c
+@@ -1683,7 +1683,8 @@ static int rproc_trigger_auto_boot(struct rproc *rproc)
+ {
+ int ret;
+
+- if (rproc->state == RPROC_DETACHED) {
++ if (rproc->state == RPROC_DETACHED &&
++ rproc->auto_boot != RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE) {
+ schedule_work(&rproc->attach_work);
+ return 0;
+ }
+@@ -1708,8 +1709,9 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
+ if (!rproc->ops->stop)
+ return -EINVAL;
+
+- /* Stop any subdevices for the remote processor */
+- rproc_stop_subdevices(rproc, crashed);
++ /* Stop any subdevices for the remote processor if it was attached */
++ if (rproc->state != RPROC_DETACHED)
++ rproc_stop_subdevices(rproc, crashed);
+
+ /* the installed resource table is no longer accessible */
+ ret = rproc_reset_rsc_table_on_stop(rproc);
+@@ -1726,7 +1728,8 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
+ return ret;
+ }
+
+- rproc_unprepare_subdevices(rproc);
++ if (rproc->state != RPROC_DETACHED)
++ rproc_unprepare_subdevices(rproc);
+
+ rproc->state = RPROC_OFFLINE;
+
+@@ -1903,9 +1906,9 @@ static void rproc_crash_handler_work(struct work_struct *work)
+ */
+ int rproc_boot(struct rproc *rproc)
+ {
+- const struct firmware *firmware_p;
++ const struct firmware *firmware_p = NULL;
+ struct device *dev;
+- int ret;
++ int ret, fw_ret = 1;
+
+ if (!rproc) {
+ pr_err("invalid rproc handle\n");
+@@ -1932,6 +1935,19 @@ int rproc_boot(struct rproc *rproc)
+ goto unlock_mutex;
+ }
+
++ /* Check early if we have firmware avilable if needed */
++ if (rproc->auto_boot == RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE &&
++ rproc->state == RPROC_DETACHED) {
++ fw_ret = request_firmware(&firmware_p, rproc->firmware, dev);
++ if (fw_ret == 0) {
++ dev_info(dev, "restarting %s with new firmware\n", rproc->name);
++
++ ret = rproc_stop(rproc, false);
++ if (ret)
++ goto downref_rproc;
++ }
++ }
++
+ if (rproc->state == RPROC_DETACHED) {
+ dev_info(dev, "attaching to %s\n", rproc->name);
+
+@@ -1939,19 +1955,20 @@ int rproc_boot(struct rproc *rproc)
+ } else {
+ dev_info(dev, "powering up %s\n", rproc->name);
+
+- /* load firmware */
+- ret = request_firmware(&firmware_p, rproc->firmware, dev);
+- if (ret < 0) {
+- dev_err(dev, "request_firmware failed: %d\n", ret);
++ /* load firmware (if not already happened above) */
++ if (fw_ret == 1)
++ fw_ret = request_firmware(&firmware_p, rproc->firmware, dev);
++ if (fw_ret < 0) {
++ dev_err(dev, "request_firmware failed: %d\n", fw_ret);
++ ret = fw_ret;
+ goto downref_rproc;
+ }
+
+ ret = rproc_fw_boot(rproc, firmware_p);
+-
+- release_firmware(firmware_p);
+ }
+
+ downref_rproc:
++ release_firmware(firmware_p);
+ if (ret)
+ atomic_dec(&rproc->power);
+ unlock_mutex:
+@@ -2259,6 +2276,10 @@ static int rproc_validate(struct rproc *rproc)
+ return -EINVAL;
+ }
+
++ if (rproc->auto_boot == RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE &&
++ (!rproc->ops->stop || !rproc->ops->start || !rproc->ops->attach))
++ return -EINVAL;
++
+ return 0;
+ }
+
+@@ -2470,7 +2491,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
+ return NULL;
+
+ rproc->priv = &rproc[1];
+- rproc->auto_boot = true;
++ rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
+ rproc->elf_class = ELFCLASSNONE;
+ rproc->elf_machine = EM_NONE;
+
+diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
+index 632614013dc652..891fc17f8a63eb 100644
+--- a/drivers/remoteproc/stm32_rproc.c
++++ b/drivers/remoteproc/stm32_rproc.c
+@@ -696,7 +696,8 @@ static int stm32_rproc_get_syscon(struct device_node *np, const char *prop,
+ }
+
+ static int stm32_rproc_parse_dt(struct platform_device *pdev,
+- struct stm32_rproc *ddata, bool *auto_boot)
++ struct stm32_rproc *ddata,
++ enum rproc_auto_boot *auto_boot)
+ {
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+@@ -777,7 +778,10 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev,
+ if (err)
+ dev_info(dev, "failed to get pdds\n");
+
+- *auto_boot = of_property_read_bool(np, "st,auto-boot");
++ if (of_property_read_bool(np, "st,auto-boot"))
++ *auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ else
++ *auto_boot = RPROC_AUTO_BOOT_DISABLED;
+
+ /*
+ * See if we can check the M4 status, i.e if it was started
+diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
+index 2d5bfbefcacc5b..8815648295c892 100644
+--- a/drivers/remoteproc/wkup_m3_rproc.c
++++ b/drivers/remoteproc/wkup_m3_rproc.c
+@@ -170,7 +170,7 @@ static int wkup_m3_rproc_probe(struct platform_device *pdev)
+ if (!rproc)
+ return -ENOMEM;
+
+- rproc->auto_boot = false;
++ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+ rproc->sysfs_read_only = true;
+
+ wkupm3 = rproc->priv;
+diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c
+index 50a9974f3202e6..4e230838a24abd 100644
+--- a/drivers/remoteproc/xlnx_r5_remoteproc.c
++++ b/drivers/remoteproc/xlnx_r5_remoteproc.c
+@@ -931,7 +931,7 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev)
+
+ r5_rproc->recovery_disabled = true;
+ r5_rproc->has_iommu = false;
+- r5_rproc->auto_boot = false;
++ r5_rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+
+ /* attempt to boot automatically if the firmware-name is provided */
+ if (fw_name)
+diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
+index b4795698d8c2a4..c40b18d120ab46 100644
+--- a/include/linux/remoteproc.h
++++ b/include/linux/remoteproc.h
+@@ -503,6 +503,27 @@ enum rproc_features {
+ RPROC_MAX_FEATURES,
+ };
+
++/**
++ * enum rproc_auto_boot - auto boot strategy for remoteproc during initial boot
++ *
++ * @RPROC_AUTO_BOOT_DISABLED: The remoteproc will be left offline (or detached).
++ * @RPROC_AUTO_BOOT_ATTACH_OR_START: The remoteproc will be attached (if it is
++ * already running). Otherwise, it will be
++ * started with new loaded firmware.
++ * @RPROC_FEAT_REBOOT_IF_FW_AVAILABLE: The remoteproc will be restarted if
++ * requesting new firmware succeeds. If
++ * the firmware is missing and the
++ * remoteproc is already running, it will
++ * be attached instead. A remoteproc
++ * implementing this must handle stop()
++ * being called in detached state.
++ */
++enum rproc_auto_boot {
++ RPROC_AUTO_BOOT_DISABLED,
++ RPROC_AUTO_BOOT_ATTACH_OR_START,
++ RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE,
++};
++
+ /**
+ * struct rproc - represents a physical remote processor device
+ * @node: list node of this rproc object
+@@ -577,7 +598,7 @@ struct rproc {
+ struct resource_table *cached_table;
+ size_t table_sz;
+ bool has_iommu;
+- bool auto_boot;
++ enum rproc_auto_boot auto_boot;
+ bool sysfs_read_only;
+ struct list_head dump_segments;
+ int nb_vdev;
diff --git a/patches/remoteproc/0010-remoteproc-qcom_q6v5-Send-SMP2P-stop-signal-in-attac.patch b/patches/remoteproc/0010-remoteproc-qcom_q6v5-Send-SMP2P-stop-signal-in-attac.patch
new file mode 100644
index 0000000..4f04538
--- /dev/null
+++ b/patches/remoteproc/0010-remoteproc-qcom_q6v5-Send-SMP2P-stop-signal-in-attac.patch
@@ -0,0 +1,31 @@
+If we stop the q6v5 remoteproc while it is in RPROC_ATTACHED or
+RPROC_DETACHED state, we still want to send the stop signal to shut it down
+cleanly.
+
+The main goal of the check in qcom_q6v5_request_stop() is to avoid sending
+duplicate shutdown/stop signals during crash or shutdown via sysmon, so
+check for RPROC_CRASHED to handle all of RPROC_RUNNING, RPROC_ATTACHED and
+RPROC_DETACHED. It does not make sense to call the function while in
+RPROC_OFFLINE state.
+
+Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/remoteproc/qcom_q6v5.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
+index 94c75d9cc..ed1b578a8 100644
+--- a/drivers/remoteproc/qcom_q6v5.c
++++ b/drivers/remoteproc/qcom_q6v5.c
+@@ -231,8 +231,8 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon)
+
+ q6v5->running = false;
+
+- /* A watchdog/fatal IRQ clears running; logical crashes still need a stop. */
+- if (!was_running || qcom_sysmon_shutdown_acked(sysmon))
++ /* A watchdog/fatal IRQ clears running; logical crashes still need a stop. */
++ if (q6v5->rproc->state == RPROC_CRASHED || qcom_sysmon_shutdown_acked(sysmon))
+ return 0;
+
+ qcom_smem_state_update_bits(q6v5->state,
diff --git a/patches/remoteproc/0011-remoteproc-qcom_q6v5_pas-Attach-running-remoteproc-i.patch b/patches/remoteproc/0011-remoteproc-qcom_q6v5_pas-Attach-running-remoteproc-i.patch
new file mode 100644
index 0000000..66ecc0f
--- /dev/null
+++ b/patches/remoteproc/0011-remoteproc-qcom_q6v5_pas-Attach-running-remoteproc-i.patch
@@ -0,0 +1,106 @@
+A remoteproc might be already running during boot, e.g. because it was
+already started by the boot firmware. This is the case for example on X1E,
+where the boot firmware starts a "lite" ADSP firmware that supports
+charging and USB-CC detection, but is missing audio functionality. This
+firmware uses the same interfaces as the full firmware and can be reused in
+case the device-specific firmware is missing (e.g. in generic distro
+installers).
+
+The running remoteproc is currently not modelled at all - it is just killed
+through qcom_pas_shutdown() without even using the SMP2P stop signal
+beforehand. If the firmware is present the "lite" firmware is now stopped more
+gracefully with the SMP2P stop signal.
+
+diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
+index 672af7336..3cf24f202 100644
+--- a/drivers/remoteproc/qcom_q6v5_pas.c
++++ b/drivers/remoteproc/qcom_q6v5_pas.c
+@@ -236,10 +237,13 @@
+ /* Store firmware handle to be used in qcom_pas_start() */
+ pas->firmware = fw;
+
+- if (pas->lite_pas_id)
+- qcom_scm_pas_shutdown(pas->lite_pas_id);
+- if (pas->lite_dtb_pas_id)
+- qcom_scm_pas_shutdown(pas->lite_dtb_pas_id);
++ /*
++ * We don't support loading the "lite" firmware, so we don't need to
++ * keep trying to shut it down. If it was running, it should have
++ * already been stopped by adsp_stop().
++ */
++ pas->lite_pas_id = 0;
++ pas->lite_dtb_pas_id = 0;
+
+ if (pas->dtb_pas_id) {
+ ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev);
+@@ -401,6 +405,28 @@
+ qcom_pas_pds_disable(pas, pas->proxy_pds, pas->proxy_pd_count);
+ }
+
++static int qcom_q6v5_pas_shutdown(int pas_id, int lite_pas_id)
++{
++ int ret, lite_ret = -ENODEV;
++
++ /*
++ * We don't know if the boot firmware started the "full" or "lite"
++ * firmware, so we don't know if we need to shutdown the lite_pas_id or
++ * the normal pas_id. Unfortunately, the return codes of the SCM calls
++ * are also not helpful to figure that out. Since shutting down a
++ * stopped remoteproc is a no-op, we just shutdown both and if one of
++ * the calls succeeds, we assume it's okay.
++ */
++ if (lite_pas_id)
++ lite_ret = qcom_scm_pas_shutdown(lite_pas_id);
++
++ ret = qcom_scm_pas_shutdown(pas_id);
++ if (ret && lite_ret)
++ return ret;
++
++ return 0;
++}
++
+ static int qcom_pas_stop(struct rproc *rproc)
+ {
+ struct qcom_pas *pas = rproc->priv;
+@@ -411,7 +437,7 @@
+ if (ret == -ETIMEDOUT)
+ dev_err(pas->dev, "timed out on wait\n");
+
+- ret = qcom_scm_pas_shutdown(pas->pas_id);
++ ret = qcom_q6v5_pas_shutdown(pas->pas_id, pas->lite_pas_id);
+ if (ret && pas->decrypt_shutdown)
+ ret = qcom_pas_shutdown_poll_decrypt(pas);
+
+@@ -419,7 +445,7 @@
+ dev_err(pas->dev, "failed to shutdown: %d\n", ret);
+
+ if (pas->dtb_pas_id) {
+- ret = qcom_scm_pas_shutdown(pas->dtb_pas_id);
++ ret = qcom_q6v5_pas_shutdown(pas->dtb_pas_id, pas->lite_dtb_pas_id);
+ if (ret)
+ dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret);
+
+@@ -428,9 +454,11 @@
+
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
+
+- handover = qcom_q6v5_unprepare(&pas->q6v5);
+- if (handover)
+- qcom_pas_handover(&pas->q6v5);
++ if (rproc->state != RPROC_DETACHED) {
++ 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);
+@@ -859,7 +890,7 @@
+
+ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
+ if (desc->auto_boot)
+- rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ rproc->auto_boot = RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE;
+ else
+ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
diff --git a/patches/remoteproc/0012-remoteproc-qcom_q6v5_pas-Avoid-using-broken-reset.patch b/patches/remoteproc/0012-remoteproc-qcom_q6v5_pas-Avoid-using-broken-reset.patch
new file mode 100644
index 0000000..f580ea5
--- /dev/null
+++ b/patches/remoteproc/0012-remoteproc-qcom_q6v5_pas-Avoid-using-broken-reset.patch
@@ -0,0 +1,101 @@
+Some firmware versions have a split design where firmware authentication is
+implemented in the TZ firmware, but the remoteproc reset sequence appears
+to be triggered by the hypervisor firmware. When running bare-metal without
+the hypervisor only the firmware authentication functionality is present.
+Without knowledge of the exact hardware reset register sequence, we cannot
+(re)start the remoteproc from Linux.
+
+This does not make the qcom_q6v5_pas driver useless. In case the remoteproc
+is already running during boot, we can attach to it as usual and keep using
+it until it crashes or is manually stopped. Not being able to restart it is
+not ideal, but not a big loss: A remoteproc should rarely (if ever) crash
+during normal use. Also, currently most drivers upstream cannot properly
+handle a remoteproc crash anyway (without full system restart).
+
+Detecting this case automatically is tricky since all the PAS related calls
+still succeed, it will just not release the remoteproc from reset. Also, on
+SC7180, MPSS does always work correctly, only ADSP and CDSP are broken.
+Look for a new "qcom,broken-reset" property so we can check which of the
+remoteprocs are affected.
+
+Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
+Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+---
+ drivers/remoteproc/qcom_q6v5_pas.c | 32 +++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
+index 3cf24f202..c80af9654 100644
+--- a/drivers/remoteproc/qcom_q6v5_pas.c
++++ b/drivers/remoteproc/qcom_q6v5_pas.c
+@@ -455,6 +455,13 @@ static const struct rproc_ops qcom_pas_minidump_ops = {
+ .coredump = qcom_pas_minidump,
+ };
+
++static const struct rproc_ops qcom_pas_ops_no_reset = {
++ .attach = qcom_pas_attach,
++ .da_to_va = qcom_pas_da_to_va,
++ .stop = qcom_pas_stop,
++ .panic = qcom_pas_panic,
++};
++
+ static int qcom_pas_init_clock(struct qcom_pas *pas)
+ {
+ pas->xo = devm_clk_get(pas->dev, "xo");
+@@ -664,6 +671,7 @@
+ struct rproc *rproc;
+ const char *fw_name, *dtb_fw_name = NULL;
+ const struct rproc_ops *ops = &qcom_pas_ops;
++ bool recovery_disabled = false;
+ int ret;
+
+ desc = of_device_get_match_data(&pdev->dev);
+@@ -690,6 +698,11 @@
+ if (desc->minidump_id)
+ ops = &qcom_pas_minidump_ops;
+
++ if (device_property_read_bool(&pdev->dev, "qcom,broken-reset")) {
++ ops = &qcom_pas_ops_no_reset;
++ recovery_disabled = true;
++ }
++
+ rproc = devm_rproc_alloc(&pdev->dev, desc->sysmon_name, ops, fw_name, sizeof(*pas));
+
+ if (!rproc) {
+@@ -752,10 +762,15 @@ static int qcom_pas_probe(struct platform_device *pdev)
+ }
+
+ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
+- if (desc->auto_boot)
+- rproc->auto_boot = RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE;
+- else
++ rproc->recovery_disabled = recovery_disabled;
++ if (desc->auto_boot) {
++ if (ops->start)
++ rproc->auto_boot = RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE;
++ else
++ rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START;
++ } else {
+ rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED;
++ }
+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
+
+ pas = rproc->priv;
+@@ -846,6 +868,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
+ qcom_pas_unassign_memory_region(pas);
+ free_rproc:
+ device_init_wakeup(pas->dev, false);
++ pas->rproc = NULL;
+
+ return ret;
+ }
+@@ -854,6 +877,9 @@ static void qcom_pas_remove(struct platform_device *pdev)
+ {
+ struct qcom_pas *pas = platform_get_drvdata(pdev);
+
++ if (!pas->rproc)
++ return;
++
+ rproc_del(pas->rproc);
+
+ qcom_q6v5_deinit(&pas->q6v5);