Diffstat (limited to 'patches/remoteproc')
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); |