aboutsummaryrefslogtreecommitdiffci
path: root/patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
Diffstat (limited to 'patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch')
-rw-r--r--patches/remoteproc/0007-soc-qcom-smp2p-Take-over-outgoing-SMEM-items-from-bo.patch223
1 files changed, 223 insertions, 0 deletions
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