mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Tim Michals <tcmichals@gmail.com>
To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org
Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, wens@kernel.org,
	jernej.skrabec@gmail.com, samuel@sholland.org,
	andersson@kernel.org, mathieu.poirier@linaro.org,
	jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, Tim Michals <tcmichals@gmail.com>
Subject: [PATCH v2 5/7] remoteproc: sunxi: add Allwinner XuanTie RISC-V remoteproc driver
Date: Sat, 26 Sep 2026 19:20:14 -0500	[thread overview]
Message-ID: <20260927002021.797069-6-tcmichals@gmail.com> (raw)
In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com>

Add remoteproc driver for the auxiliary Alibaba T-Head XuanTie E907
RISC-V co-processor found on Allwinner A523, A527, and T527 SoCs.

Features implemented:
- SRAM Space 0 and Space 1 memory window translation via Address
  Translation Table (ATT) architecture matching imx_rproc conventions.
- Dynamic DRAM carveout and CMA buffer allocation for VirtIO RPMsg.
- CCU reset and clock lifecycle management with boot address setup
  prior to core reset de-assertion.
- Dual-channel hardware Message Box integration for kick/vq signalling.
- Workqueue-deferred RPMsg notification for Linux PREEMPT_RT safety.
- Ring-buffer trace0 diagnostic endpoint via debugfs.

Signed-off-by: Tim Michals <tcmichals@gmail.com>
---
 drivers/remoteproc/Kconfig       |   8 +
 drivers/remoteproc/Makefile      |   1 +
 drivers/remoteproc/sunxi_rproc.c | 922 +++++++++++++++++++++++++++++++
 drivers/remoteproc/sunxi_rproc.h | 116 ++++
 4 files changed, 1047 insertions(+)
 create mode 100644 drivers/remoteproc/sunxi_rproc.c
 create mode 100644 drivers/remoteproc/sunxi_rproc.h

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 73500fdc227d..d35b033b9106 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -388,6 +388,14 @@ config XLNX_R5_REMOTEPROC
 
 	  It's safe to say N if not interested in using RPU r5f cores.
 
+config SUNXI_REMOTEPROC
+	tristate "Allwinner XuanTie RISC-V remoteproc support"
+	depends on (ARCH_SUNXI || COMPILE_TEST) && MAILBOX
+	help
+	  Say y or m here to support the Allwinner XuanTie E906/E907 RISC-V
+	  co-processor on Allwinner A523, A527, and T527 SoCs via the
+	  remote processor framework.
+
 endif # REMOTEPROC
 
 endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 689686de0d41..02acefa6d880 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -41,3 +41,4 @@ obj-$(CONFIG_TI_K3_DSP_REMOTEPROC)	+= ti_k3_dsp_remoteproc.o ti_k3_common.o
 obj-$(CONFIG_TI_K3_M4_REMOTEPROC)	+= ti_k3_m4_remoteproc.o ti_k3_common.o
 obj-$(CONFIG_TI_K3_R5_REMOTEPROC)	+= ti_k3_r5_remoteproc.o ti_k3_common.o
 obj-$(CONFIG_XLNX_R5_REMOTEPROC)	+= xlnx_r5_remoteproc.o
+obj-$(CONFIG_SUNXI_REMOTEPROC)		+= sunxi_rproc.o
diff --git a/drivers/remoteproc/sunxi_rproc.c b/drivers/remoteproc/sunxi_rproc.c
new file mode 100644
index 000000000000..fbb0ee8a9110
--- /dev/null
+++ b/drivers/remoteproc/sunxi_rproc.c
@@ -0,0 +1,922 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Allwinner XuanTie E906/E907 RISC-V Remote Processor Driver
+ *
+ * Copyright (C) 2024-2026 Allwinner Technology Co., Ltd.
+ * Copyright (C) 2026 Tim Michals <tcmichals@gmail.com>
+ *
+ * CCU-integrated remoteproc driver for XuanTie RISC-V co-processors on
+ * Allwinner SoCs (T527/A527/A523: E906/E907) supporting TCM, SRAM, and DRAM.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/reset.h>
+
+#include "remoteproc_internal.h"
+#include "sunxi_rproc.h"
+
+#define DRIVER_NAME "sunxi-rproc"
+
+static const struct sunxi_rproc_att sun55i_rproc_att[] = {
+	/* dev addr (remote)    , sys addr (host PA)    , size                   , flags */
+	/* Space 0 Core Aliases -> Space 0 Host PA */
+	{ E907_SRAM_SPACE0_DA,     SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM },
+	{ E907_SRAM_SPACE0_DA_ALT, SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM },
+	{ E907_SRAM_C_DA,          SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM },
+	{ SUN55I_SRAM_SPACE0_SYS,  SUN55I_SRAM_SPACE0_SYS, SUN55I_SRAM_SPACE0_SIZE, ATT_IOMEM },
+
+	/* Space 1 Core Aliases -> Space 1 Host PA */
+	{ E907_SRAM_SPACE1_DA,     SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SIZE, ATT_IOMEM },
+	{ E907_SRAM_SPACE1_DA_ALT, SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SIZE, ATT_IOMEM },
+	{ SUN55I_SRAM_SPACE1_SYS,  SUN55I_SRAM_SPACE1_SYS, SUN55I_SRAM_SPACE1_SIZE, ATT_IOMEM },
+};
+
+const struct sunxi_rproc_cfg sun55i_riscv_cfg = {
+	.name = "XuanTie E907 RISC-V",
+	.att = sun55i_rproc_att,
+	.att_size = ARRAY_SIZE(sun55i_rproc_att),
+	.has_remap_reg = true,
+	.boot_reg_offset = E906_STA_ADD_REG,
+};
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sun55i_riscv_cfg);
+#endif
+
+static void sunxi_rproc_vq_work(struct work_struct *work)
+{
+	struct sunxi_rproc *priv = container_of(work, struct sunxi_rproc, vq_work);
+
+	rproc_vq_interrupt(priv->rproc, 0);
+	rproc_vq_interrupt(priv->rproc, 1);
+}
+
+static void sunxi_rproc_mb_rx_callback(struct mbox_client *cl, void *data)
+{
+	struct sunxi_rproc *priv = container_of(cl, struct sunxi_rproc, cl);
+
+	schedule_work(&priv->vq_work);
+}
+
+static irqreturn_t sunxi_rproc_crash_handler(int irq, void *data)
+{
+	struct sunxi_rproc *priv = data;
+	struct rproc *rproc = priv->rproc;
+
+	dev_err(priv->dev, "Hardware crash event received from %s core!\n",
+		priv->cfg ? priv->cfg->name : "remote");
+	if (priv->crash_irq_enabled) {
+		disable_irq_nosync(irq);
+		priv->crash_irq_enabled = false;
+	}
+	rproc_report_crash(rproc, RPROC_FATAL_ERROR);
+
+	return IRQ_HANDLED;
+}
+
+int sunxi_rproc_prepare(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg;
+	int ret;
+
+	/* 1. Deassert configuration & SRAM bus resets */
+	if (priv->rst_cfg) {
+		ret = reset_control_deassert(priv->rst_cfg);
+		if (ret) {
+			dev_err(priv->dev, "failed to deassert cfg reset: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (priv->rst_sram) {
+		ret = reset_control_deassert(priv->rst_sram);
+		if (ret) {
+			dev_err(priv->dev, "failed to deassert sram reset: %d\n", ret);
+			goto err_assert_cfg;
+		}
+	}
+
+	if (priv->rst_msgbox) {
+		ret = reset_control_deassert(priv->rst_msgbox);
+		if (ret) {
+			dev_err(priv->dev, "failed to deassert msgbox reset: %d\n", ret);
+			goto err_assert_sram;
+		}
+	}
+
+	/* 2. Enable parent clock (PLL source) */
+	if (priv->clk_parent) {
+		ret = clk_prepare_enable(priv->clk_parent);
+		if (ret) {
+			dev_err(priv->dev, "failed to enable parent clock: %d\n", ret);
+			goto err_assert_msgbox;
+		}
+	}
+
+	/* 3. Enable interconnect bus and SRAM clocks */
+	if (priv->clk_bus) {
+		ret = clk_prepare_enable(priv->clk_bus);
+		if (ret) {
+			dev_err(priv->dev, "failed to enable bus clock: %d\n", ret);
+			goto err_disable_parent;
+		}
+	}
+
+	if (priv->clk_sram) {
+		ret = clk_prepare_enable(priv->clk_sram);
+		if (ret) {
+			dev_err(priv->dev, "failed to enable sram clock: %d\n", ret);
+			goto err_disable_bus;
+		}
+	}
+
+	if (priv->clk_msgbox) {
+		ret = clk_prepare_enable(priv->clk_msgbox);
+		if (ret) {
+			dev_err(priv->dev, "failed to enable msgbox clock: %d\n", ret);
+			goto err_disable_sram_clk;
+		}
+	}
+
+	/* 4. Enable core clock */
+	if (priv->clk_core) {
+		ret = clk_prepare_enable(priv->clk_core);
+		if (ret) {
+			dev_err(priv->dev, "failed to enable core clock: %d\n", ret);
+			goto err_disable_msgbox_clk;
+		}
+	}
+
+	/*
+	 * 4b. Enable SRAMA3_2 for MCU_SYS (RISC-V) via REMAP_CTRL_REG bit 1.
+	 */
+	if (cfg->has_remap_reg && priv->remap_va) {
+		u32 remap_val = readl(priv->remap_va);
+
+		remap_val |= SUNXI_REMAP_SRAMA3_2_BIT;
+		writel(remap_val, priv->remap_va);
+		dev_dbg(priv->dev, "REMAP_CTRL_REG set to 0x%08x (SRAMA3_2 enabled)\n",
+			readl(priv->remap_va));
+	}
+
+	/*
+	 * 5. Cleanly clear Dedicated Local SRAM and Switchable SRAM.
+	 * Only clears regions that are actually mapped from Device Tree.
+	 * Matches upstream patterns (e.g., imx_rproc / ti_k3_r5_remoteproc) to:
+	 *  - Prevent ECC/parity noise on uninitialized memory banks.
+	 *  - Ensure NOBITS / .bss sections start strictly at zero.
+	 *  - Clear stale trace0 logs/telemetry from previous runs.
+	 */
+	if (priv->r_sram_va && priv->r_sram_size)
+		memset_io(priv->r_sram_va, 0, priv->r_sram_size);
+
+	if (priv->r_sram1_va && priv->r_sram1_size)
+		memset_io(priv->r_sram1_va, 0, priv->r_sram1_size);
+
+	return 0;
+
+err_disable_msgbox_clk:
+	if (priv->clk_msgbox)
+		clk_disable_unprepare(priv->clk_msgbox);
+err_disable_sram_clk:
+	if (priv->clk_sram)
+		clk_disable_unprepare(priv->clk_sram);
+err_disable_bus:
+	if (priv->clk_bus)
+		clk_disable_unprepare(priv->clk_bus);
+err_disable_parent:
+	if (priv->clk_parent)
+		clk_disable_unprepare(priv->clk_parent);
+err_assert_msgbox:
+	if (priv->rst_msgbox)
+		reset_control_assert(priv->rst_msgbox);
+err_assert_sram:
+	if (priv->rst_sram)
+		reset_control_assert(priv->rst_sram);
+err_assert_cfg:
+	if (priv->rst_cfg)
+		reset_control_assert(priv->rst_cfg);
+	return ret;
+}
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_prepare);
+#endif
+
+int sunxi_rproc_unprepare(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg;
+
+	/* Symmetrical CCU unwinding */
+	if (cfg->has_remap_reg && priv->remap_va) {
+		u32 remap_val = readl(priv->remap_va);
+
+		remap_val &= ~SUNXI_REMAP_SRAMA3_2_BIT;
+		writel(remap_val, priv->remap_va);
+	}
+
+	if (priv->clk_core)
+		clk_disable_unprepare(priv->clk_core);
+
+	if (priv->clk_msgbox)
+		clk_disable_unprepare(priv->clk_msgbox);
+
+	if (priv->clk_sram)
+		clk_disable_unprepare(priv->clk_sram);
+
+	if (priv->clk_bus)
+		clk_disable_unprepare(priv->clk_bus);
+
+	if (priv->clk_parent)
+		clk_disable_unprepare(priv->clk_parent);
+
+	if (priv->rst_msgbox)
+		reset_control_assert(priv->rst_msgbox);
+
+	if (priv->rst_sram)
+		reset_control_assert(priv->rst_sram);
+
+	if (priv->rst_cfg)
+		reset_control_assert(priv->rst_cfg);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_unprepare);
+#endif
+
+int sunxi_rproc_start(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg;
+	int ret;
+
+	dev_info(priv->dev, "Starting %s core at entry 0x%llx\n",
+		 cfg->name ? cfg->name : "remote", (u64)rproc->bootaddr);
+
+	if (rproc->bootaddr > U32_MAX)
+		return -EINVAL;
+
+	/* Enable crash IRQ now that core is executing */
+	if (priv->crash_irq > 0 && !priv->crash_irq_enabled) {
+		enable_irq(priv->crash_irq);
+		priv->crash_irq_enabled = true;
+	}
+
+	/*
+	 * Program boot vector while the core execution reset is held.
+	 * The CFG block bus was un-gated during prepare() via rst_cfg.
+	 */
+	if (priv->cfg_va) {
+		writel((u32)rproc->bootaddr, priv->cfg_va + cfg->boot_reg_offset);
+		dev_dbg(priv->dev, "STA_ADD set to 0x%08x\n", (u32)rproc->bootaddr);
+	}
+
+	/* Release core execution reset so the core begins execution at bootaddr */
+	if (priv->rst_core) {
+		ret = reset_control_deassert(priv->rst_core);
+		if (ret) {
+			dev_err(priv->dev, "failed to release core reset: %d\n", ret);
+			return ret;
+		}
+	} else if (priv->rst_cfg) {
+		ret = reset_control_deassert(priv->rst_cfg);
+		if (ret) {
+			dev_err(priv->dev, "failed to release cfg reset: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_start);
+#endif
+
+int sunxi_rproc_stop(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg;
+
+	dev_info(priv->dev, "Halting %s core...\n",
+		 cfg->name ? cfg->name : "remote");
+
+	/*
+	 * Assert reset first so the core stops generating mailbox interrupts,
+	 * then drain any work already queued. Reversing this order leaves a
+	 * window where a late IRQ re-queues vq_work after cancel_work_sync()
+	 * returns, executing on freed resources.
+	 */
+	if (priv->rst_core)
+		reset_control_assert(priv->rst_core);
+	else if (priv->rst_cfg)
+		reset_control_assert(priv->rst_cfg);
+
+	/* Disable crash IRQ while core is stopped */
+	if (priv->crash_irq > 0 && priv->crash_irq_enabled) {
+		disable_irq(priv->crash_irq);
+		priv->crash_irq_enabled = false;
+	}
+
+	cancel_work_sync(&priv->vq_work);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_stop);
+#endif
+
+void sunxi_rproc_kick(struct rproc *rproc, int vqid)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	int ret;
+
+	if (!priv->tx_chan)
+		return;
+
+	/*
+	 * Use priv->kick_msg rather than a stack-local variable. The mailbox
+	 * controller runs with tx_block=false, so mbox_send_message() may
+	 * queue the pointer and return before the hardware reads the message.
+	 * A stack-local vqid would be a use-after-return at that point.
+	 */
+	priv->kick_msg = (u32)vqid;
+	ret = mbox_send_message(priv->tx_chan, &priv->kick_msg);
+	if (ret < 0)
+		dev_err_ratelimited(priv->dev, "failed to send mailbox kick: %d\n", ret);
+
+	mbox_client_txdone(priv->tx_chan, 0);
+}
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_kick);
+#endif
+
+static int sunxi_rproc_da_to_sys(struct sunxi_rproc *priv, u64 da,
+				 size_t len, u64 *sys, bool *is_iomem)
+{
+	const struct sunxi_rproc_cfg *cfg = priv->cfg ? priv->cfg : &sun55i_riscv_cfg;
+	size_t i;
+
+	if (len == 0 || da > U64_MAX - len)
+		return -EINVAL;
+
+	if (cfg->att) {
+		for (i = 0; i < cfg->att_size; i++) {
+			const struct sunxi_rproc_att *att = &cfg->att[i];
+
+			if (da >= att->da && (da + len) <= (att->da + att->size)) {
+				*sys = att->sa + (da - att->da);
+				if (is_iomem)
+					*is_iomem = !!(att->flags & ATT_IOMEM);
+				return 0;
+			}
+		}
+	}
+
+	return -ENOENT;
+}
+
+void *sunxi_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	u64 sys;
+
+	/*
+	 * Reject zero length and any da+len combination that overflows u64.
+	 * A crafted ELF with da near U64_MAX could wrap da+len to a small value,
+	 * bypassing upper-bound checks and allowing arbitrary kernel memory
+	 * to be mapped during firmware loading.
+	 */
+	if (len == 0 || da > U64_MAX - len)
+		return NULL;
+
+	/*
+	 * 1. Translate core-local device addresses (DA) to system bus
+	 * addresses (Host PA) using the SoC address translation table (ATT).
+	 */
+	if (sunxi_rproc_da_to_sys(priv, da, len, &sys, is_iomem) == 0) {
+		if (priv->r_sram_va && sys >= priv->r_sram_phys &&
+		    (sys + len) <= (priv->r_sram_phys + priv->r_sram_size))
+			return (__force void *)(priv->r_sram_va + (sys - priv->r_sram_phys));
+
+		if (priv->r_sram1_va && sys >= priv->r_sram1_phys &&
+		    (sys + len) <= (priv->r_sram1_phys + priv->r_sram1_size))
+			return (__force void *)(priv->r_sram1_va + (sys - priv->r_sram1_phys));
+
+		if (priv->dram_va && sys >= priv->dram_phys &&
+		    (sys + len) <= (priv->dram_phys + priv->dram_size))
+			return (__force void *)(priv->dram_va + (sys - priv->dram_phys));
+
+		if (priv->trace_va && sys >= priv->trace_phys &&
+		    (sys + len) <= (priv->trace_phys + priv->trace_size))
+			return (__force void *)(priv->trace_va + (sys - priv->trace_phys));
+	}
+
+	/*
+	 * 2. Device Tree Memory Regions (Trace buffer, DRAM carveout, or
+	 * dynamically-assigned SRAM regions whose host PA is supplied via DT).
+	 */
+	if (priv->trace_va && da >= priv->trace_phys &&
+	    (da + len) <= (priv->trace_phys + priv->trace_size)) {
+		if (is_iomem)
+			*is_iomem = false;
+		return (__force void *)(priv->trace_va + (da - priv->trace_phys));
+	}
+
+	if (priv->dram_va && da >= priv->dram_phys &&
+	    (da + len) <= (priv->dram_phys + priv->dram_size)) {
+		if (is_iomem)
+			*is_iomem = false;
+		return (__force void *)(priv->dram_va + (da - priv->dram_phys));
+	}
+
+	if (priv->r_sram_va && da >= priv->r_sram_phys &&
+	    (da + len) <= (priv->r_sram_phys + priv->r_sram_size)) {
+		if (is_iomem)
+			*is_iomem = true;
+		return (__force void *)(priv->r_sram_va + (da - priv->r_sram_phys));
+	}
+
+	if (priv->r_sram1_va && da >= priv->r_sram1_phys &&
+	    (da + len) <= (priv->r_sram1_phys + priv->r_sram1_size)) {
+		if (is_iomem)
+			*is_iomem = true;
+		return (__force void *)(priv->r_sram1_va + (da - priv->r_sram1_phys));
+	}
+
+	/*
+	 * Return NULL to delegate all DRAM carveouts (vrings, buffers,
+	 * code/data placed in DDR) directly to the remoteproc core's
+	 * internal carveout table.
+	 */
+	return NULL;
+}
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_da_to_va);
+#endif
+
+static int sunxi_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
+{
+	int ret;
+
+	ret = rproc_elf_load_rsc_table(rproc, fw);
+	if (ret == -EINVAL) {
+		dev_dbg(rproc->dev.parent, "no resource table found in ELF\n");
+		return 0;
+	}
+	return ret;
+}
+
+const struct rproc_ops sunxi_rproc_ops = {
+	.prepare        = sunxi_rproc_prepare,
+	.unprepare      = sunxi_rproc_unprepare,
+	.start          = sunxi_rproc_start,
+	.stop           = sunxi_rproc_stop,
+	.kick           = sunxi_rproc_kick,
+	.da_to_va       = sunxi_rproc_da_to_va,
+	.get_boot_addr  = rproc_elf_get_boot_addr,
+	.load           = rproc_elf_load_segments,
+	.parse_fw       = sunxi_rproc_parse_fw,
+	.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
+	.sanity_check   = rproc_elf_sanity_check,
+	.coredump       = rproc_coredump,
+};
+
+#if IS_ENABLED(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST)
+EXPORT_SYMBOL_GPL(sunxi_rproc_ops);
+#endif
+
+static int sunxi_rproc_register_mem(struct platform_device *pdev, struct rproc *rproc)
+{
+	struct device *dev = &pdev->dev;
+	struct sunxi_rproc *priv = rproc->priv;
+	struct resource *res;
+
+	/* 1. Map Optional RISC-V CFG Block ("cfg") */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+	if (res) {
+		priv->cfg_phys = res->start;
+		priv->cfg_va = devm_ioremap(dev, res->start, resource_size(res));
+		if (!priv->cfg_va)
+			dev_warn(dev, "failed to map 'cfg' registers\n");
+	}
+
+	/* 2. Map Dedicated RISC-V Local SRAM Space 0 ("r_sram" or "sram") */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "r_sram");
+	if (!res)
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+	if (res) {
+		priv->r_sram_phys = res->start;
+		priv->r_sram_size = resource_size(res);
+		priv->r_sram_va = devm_ioremap_wc(dev, res->start, resource_size(res));
+		if (!priv->r_sram_va)
+			return -ENOMEM;
+	}
+
+	/* 3. Map Switchable RISC-V Local SRAM Space 1 ("r_sram1" / SRAMA3_2) - Optional */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "r_sram1");
+	if (res) {
+		priv->r_sram1_phys = res->start;
+		priv->r_sram1_size = resource_size(res);
+		priv->r_sram1_va = devm_ioremap_wc(dev, res->start, resource_size(res));
+		if (!priv->r_sram1_va)
+			dev_warn(dev, "failed to map 'r_sram1' resource\n");
+	}
+
+	/* 4. Map Remap Control Register ("remap" or "sram-for-cpux") - Optional */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "remap");
+	if (!res)
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram-for-cpux");
+	if (res) {
+		priv->remap_phys = res->start;
+		if (resource_size(res) > SUNXI_REMAP_CTRL_OFFSET &&
+		    IS_ALIGNED(res->start, PAGE_SIZE)) {
+			void __iomem *base = devm_ioremap(dev, res->start, resource_size(res));
+
+			if (base)
+				priv->remap_va = base + SUNXI_REMAP_CTRL_OFFSET;
+		} else {
+			priv->remap_va = devm_ioremap(dev, res->start, resource_size(res));
+		}
+		if (!priv->remap_va)
+			dev_warn(dev, "failed to map 'remap' register\n");
+	}
+
+	/* 4b. Map Boot DRAM Carveout (Resource "dram" if defined in reg) */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dram");
+	if (res) {
+		priv->dram_phys = res->start;
+		priv->dram_size = resource_size(res);
+		priv->dram_va = devm_memremap(dev, res->start, resource_size(res), MEMREMAP_WB);
+		if (!priv->dram_va)
+			priv->dram_va = (__force void *)devm_ioremap_wc(dev, res->start,
+									resource_size(res));
+		if (!priv->dram_va)
+			dev_warn(dev, "failed to map 'dram' resource\n");
+	}
+
+	dev_info(dev, "Memory resources: r_sram=%s, r_sram1=%s, remap=%s, cfg=%s, dram=%s\n",
+		 priv->r_sram_va ? "yes" : "no",
+		 priv->r_sram1_va ? "yes" : "no",
+		 priv->remap_va ? "yes" : "no",
+		 priv->cfg_va ? "yes" : "no",
+		 priv->dram_va ? "yes" : "no");
+
+	/* 5. Map Trace Buffer (Resource "trace" if defined in reg) */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "trace");
+	if (res) {
+		priv->trace_phys = res->start;
+		priv->trace_size = resource_size(res);
+		priv->trace_va = devm_memremap(dev, res->start, resource_size(res), MEMREMAP_WB);
+		if (!priv->trace_va)
+			priv->trace_va = (__force void *)devm_ioremap_wc(dev, res->start,
+									 resource_size(res));
+		dev_info(dev, "mapped 'trace' mmio resource %pa+%zu\n",
+			 &priv->trace_phys, priv->trace_size);
+	}
+
+	return 0;
+}
+
+static int sunxi_rproc_parse_memory_regions(struct rproc *rproc)
+{
+	struct device *dev = rproc->dev.parent;
+	struct device_node *np = dev->of_node;
+	struct sunxi_rproc *priv = rproc->priv;
+	int num_rmems;
+	int i;
+
+	if (!np)
+		return 0;
+
+	num_rmems = of_count_phandle_with_args(np, "memory-region", NULL);
+	if (num_rmems <= 0)
+		return 0;
+
+	/* Bind default DMA pool for dynamic allocations (e.g. vdev0 vrings/buffers) */
+	if (of_reserved_mem_device_init(dev) == 0)
+		priv->has_reserved_mem = true;
+	else
+		dev_dbg(dev, "no dedicated DMA pool assigned from reserved-memory\n");
+
+	/* Register all reserved-memory regions as formal remoteproc carveouts */
+	for (i = 0; i < num_rmems; i++) {
+		struct device_node *rmem_np;
+		struct resource res;
+		const char *name = NULL;
+		struct rproc_mem_entry *mem;
+		void *va;
+
+		rmem_np = of_parse_phandle(np, "memory-region", i);
+		if (!rmem_np)
+			continue;
+
+		if (of_address_to_resource(rmem_np, 0, &res)) {
+			of_node_put(rmem_np);
+			continue;
+		}
+
+		of_property_read_string_index(np, "memory-region-names", i, &name);
+		if (!name)
+			name = rmem_np->name;
+
+		if (name && (strstr(name, "trace") || of_node_name_eq(rmem_np, "trace"))) {
+			priv->trace_phys = res.start;
+			priv->trace_size = resource_size(&res);
+			priv->trace_va = devm_memremap(dev, res.start, resource_size(&res),
+						       MEMREMAP_WB);
+			if (!priv->trace_va)
+				priv->trace_va = (__force void *)
+					devm_ioremap_wc(dev, res.start, resource_size(&res));
+			dev_info(dev, "registered trace carveout %pa+%zu (%s)\n",
+				 &priv->trace_phys, priv->trace_size, name);
+		} else if (name && (strstr(name, "dram") || strstr(name, "vram"))) {
+			priv->dram_phys = res.start;
+			priv->dram_size = resource_size(&res);
+			priv->dram_va = devm_memremap(dev, res.start, resource_size(&res),
+						      MEMREMAP_WB);
+			if (!priv->dram_va)
+				priv->dram_va = (__force void *)
+					devm_ioremap_wc(dev, res.start, resource_size(&res));
+			dev_info(dev, "registered dram carveout %pa+%zu (%s)\n",
+				 &priv->dram_phys, priv->dram_size, name);
+		}
+
+		/* Reuse existing SRAM mapping if region overlaps, else ioremap */
+		if (priv->r_sram1_va && res.start == priv->r_sram1_phys)
+			va = (__force void *)priv->r_sram1_va;
+		else if (priv->r_sram_va && res.start == priv->r_sram_phys)
+			va = (__force void *)priv->r_sram_va;
+		else
+			va = (__force void *)devm_ioremap_wc(dev, res.start, resource_size(&res));
+
+		if (va) {
+			mem = rproc_mem_entry_init(dev, va, (dma_addr_t)res.start,
+						   resource_size(&res), (u32)res.start,
+						   NULL, NULL, "%s", name);
+			if (mem) {
+				mem->is_iomem = true;
+				rproc_add_carveout(rproc, mem);
+			}
+		}
+		of_node_put(rmem_np);
+	}
+
+	return 0;
+}
+
+static int sunxi_rproc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const char *fw_name = "riscv-firmware.elf";
+	struct sunxi_rproc *priv;
+	struct rproc *rproc;
+	int crash_irq;
+	int ret;
+
+	of_property_read_string(dev->of_node, "firmware-name", &fw_name);
+
+	rproc = devm_rproc_alloc(dev, dev_name(dev), &sunxi_rproc_ops,
+				 fw_name, sizeof(*priv));
+	if (!rproc) {
+		dev_err(dev, "failed to allocate rproc context\n");
+		return -ENOMEM;
+	}
+
+	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "failed to set 32-bit DMA coherent mask: %d\n", ret);
+		return ret;
+	}
+
+	priv = rproc->priv;
+	priv->rproc = rproc;
+	priv->dev = dev;
+	priv->cfg = of_device_get_match_data(dev);
+	if (!priv->cfg)
+		priv->cfg = &sun55i_riscv_cfg;
+
+	/* 1. Common Clock Framework (CCF) Clocks */
+	priv->clk_parent = devm_clk_get_optional(dev, "parent");
+	if (IS_ERR(priv->clk_parent))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_parent),
+				     "failed to get 'parent' clock\n");
+
+	priv->clk_bus = devm_clk_get_optional(dev, "bus");
+	if (IS_ERR(priv->clk_bus))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_bus), "failed to get 'bus' clock\n");
+
+	priv->clk_core = devm_clk_get_optional(dev, "core");
+	if (IS_ERR(priv->clk_core))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_core), "failed to get 'core' clock\n");
+
+	priv->clk_sram = devm_clk_get_optional(dev, "sram");
+	if (IS_ERR(priv->clk_sram))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_sram), "failed to get 'sram' clock\n");
+
+	priv->clk_msgbox = devm_clk_get_optional(dev, "msgbox");
+	if (IS_ERR(priv->clk_msgbox))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_msgbox),
+				     "failed to get 'msgbox' clock\n");
+
+	/* 2. Resets */
+	priv->rst_core = devm_reset_control_get_optional_exclusive(dev, "core");
+	if (IS_ERR(priv->rst_core))
+		return dev_err_probe(dev, PTR_ERR(priv->rst_core), "failed to get 'core' reset\n");
+
+	priv->rst_cfg = devm_reset_control_get_optional_exclusive(dev, "cfg");
+	if (IS_ERR(priv->rst_cfg))
+		return dev_err_probe(dev, PTR_ERR(priv->rst_cfg), "failed to get 'cfg' reset\n");
+
+	priv->rst_sram = devm_reset_control_get_optional_exclusive(dev, "sram");
+	if (IS_ERR(priv->rst_sram))
+		return dev_err_probe(dev, PTR_ERR(priv->rst_sram), "failed to get 'sram' reset\n");
+
+	priv->rst_msgbox = devm_reset_control_get_optional_exclusive(dev, "msgbox");
+	if (IS_ERR(priv->rst_msgbox))
+		return dev_err_probe(dev, PTR_ERR(priv->rst_msgbox),
+				     "failed to get 'msgbox' reset\n");
+
+	/* 3. Memory Windows (TCM, SRAM, CFG) */
+	ret = sunxi_rproc_register_mem(pdev, rproc);
+	if (ret)
+		return ret;
+
+	/* 4. Dynamic Reserved Memory Carveouts & DMA Pools from Device Tree */
+	ret = sunxi_rproc_parse_memory_regions(rproc);
+	if (ret)
+		return ret;
+
+	/* 5. Optional Hardware Crash Notification IRQ */
+	crash_irq = platform_get_irq_byname_optional(pdev, "crash");
+	if (crash_irq > 0) {
+		ret = devm_request_threaded_irq(dev, crash_irq, NULL,
+						sunxi_rproc_crash_handler,
+						IRQF_ONESHOT | IRQF_NO_AUTOEN,
+						"sunxi-rproc-crash",
+						priv);
+		if (ret) {
+			dev_warn(dev, "failed to request crash IRQ %d: %d\n", crash_irq, ret);
+		} else {
+			priv->crash_irq = crash_irq;
+			priv->crash_irq_enabled = false;
+		}
+	}
+
+	/* 6. Mailbox IPC Client */
+	INIT_WORK(&priv->vq_work, sunxi_rproc_vq_work);
+
+	priv->cl.dev = dev;
+	priv->cl.rx_callback = sunxi_rproc_mb_rx_callback;
+	priv->cl.tx_block = false;
+	priv->cl.knows_txdone = true;
+
+	/*
+	 * If the hardware mailbox is assigned to userspace (generic-uio) or
+	 * lacks #mbox-cells, run RemoteProc in standalone mode.
+	 */
+	if (dev->of_node) {
+		struct device_node *mb_node = of_parse_phandle(dev->of_node, "mboxes", 0);
+
+		if (mb_node) {
+			if (of_device_is_compatible(mb_node, "generic-uio") ||
+			    !of_property_read_bool(mb_node, "#mbox-cells")) {
+				dev_info(dev, "Mailbox assigned to UIO; running standalone mode\n");
+				of_node_put(mb_node);
+				goto skip_mbox;
+			}
+			of_node_put(mb_node);
+		}
+	}
+
+	priv->tx_chan = mbox_request_channel_byname(&priv->cl, "tx");
+	if (IS_ERR(priv->tx_chan)) {
+		if (PTR_ERR(priv->tx_chan) == -EPROBE_DEFER) {
+			ret = -EPROBE_DEFER;
+			goto err_mem_release;
+		}
+		dev_info(dev, "no tx mailbox channel configured; running standalone mode\n");
+		priv->tx_chan = NULL;
+	}
+
+	if (priv->tx_chan) {
+		priv->rx_chan = mbox_request_channel_byname(&priv->cl, "rx");
+		if (IS_ERR(priv->rx_chan)) {
+			if (PTR_ERR(priv->rx_chan) == -EPROBE_DEFER) {
+				ret = -EPROBE_DEFER;
+				goto err_mbox_release;
+			}
+			dev_info(dev, "no rx mailbox channel configured\n");
+			priv->rx_chan = NULL;
+		}
+	}
+
+skip_mbox:
+	platform_set_drvdata(pdev, rproc);
+
+	ret = rproc_add(rproc);
+	if (ret) {
+		dev_err(dev, "failed to register rproc device: %d\n", ret);
+		goto err_mbox_release;
+	}
+
+	dev_info(dev, "Allwinner %s remoteproc registered (%s)\n",
+		 priv->cfg ? priv->cfg->name : "remote", fw_name);
+	return 0;
+
+err_mbox_release:
+	cancel_work_sync(&priv->vq_work);
+	/*
+	 * mbox_request_channel_byname() can return ERR_PTR on failure.
+	 * Guard with IS_ERR() to avoid calling mbox_free_channel() with
+	 * an invalid pointer, which would panic on the first dereference.
+	 */
+	if (priv->rx_chan && !IS_ERR(priv->rx_chan))
+		mbox_free_channel(priv->rx_chan);
+	if (priv->tx_chan && !IS_ERR(priv->tx_chan))
+		mbox_free_channel(priv->tx_chan);
+err_mem_release:
+	if (priv->has_reserved_mem)
+		of_reserved_mem_device_release(dev);
+	return ret;
+}
+
+static void sunxi_rproc_remove(struct platform_device *pdev)
+{
+	struct rproc *rproc = platform_get_drvdata(pdev);
+	struct sunxi_rproc *priv = rproc->priv;
+
+	/*
+	 * Teardown order is critical:
+	 * 1. Disable crash IRQ first so late hardware crash alerts cannot
+	 *    race against rproc_del() or report crashes on a deleted device.
+	 * 2. rproc_del() stops the remote core and tears down VirtIO/vring,
+	 *    which stops the hardware from generating further mailbox IRQs.
+	 * 3. cancel_work_sync() drains any in-flight vq_work. Calling this
+	 *    before rproc_del() risks a late RX IRQ re-queuing work after
+	 *    cancel_work_sync() returns, executing on freed priv->rx_chan.
+	 * 4. Free mailbox channels only after the workqueue is fully drained.
+	 */
+	if (priv->crash_irq > 0 && priv->crash_irq_enabled) {
+		disable_irq(priv->crash_irq);
+		priv->crash_irq_enabled = false;
+	}
+
+	rproc_del(rproc);
+
+	if (priv->rx_chan) {
+		mbox_free_channel(priv->rx_chan);
+		priv->rx_chan = NULL;
+	}
+	if (priv->tx_chan) {
+		mbox_free_channel(priv->tx_chan);
+		priv->tx_chan = NULL;
+	}
+
+	cancel_work_sync(&priv->vq_work);
+
+	if (priv->has_reserved_mem)
+		of_reserved_mem_device_release(&pdev->dev);
+}
+
+static const struct of_device_id sunxi_rproc_of_match[] = {
+	/*
+	 * A523, A527, and T527 are the same silicon die (sun55i family).
+	 * Use a single compatible string per upstream DT binding policy.
+	 */
+	{ .compatible = "allwinner,sun55i-a523-rproc", .data = &sun55i_riscv_cfg },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_rproc_of_match);
+
+static struct platform_driver sunxi_rproc_driver = {
+	.probe = sunxi_rproc_probe,
+	.remove = sunxi_rproc_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = sunxi_rproc_of_match,
+	},
+};
+module_platform_driver(sunxi_rproc_driver);
+
+MODULE_AUTHOR("Allwinner Technology Co., Ltd.");
+MODULE_AUTHOR("Tim Michals <tcmichals@gmail.com>");
+MODULE_DESCRIPTION("Allwinner XuanTie E906/E907 RISC-V Remoteproc Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/remoteproc/sunxi_rproc.h b/drivers/remoteproc/sunxi_rproc.h
new file mode 100644
index 000000000000..5b516b5ac956
--- /dev/null
+++ b/drivers/remoteproc/sunxi_rproc.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _SUNXI_RPROC_H_
+#define _SUNXI_RPROC_H_
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/remoteproc.h>
+#include <linux/reset.h>
+
+/* XuanTie E906/E907 core-local view of dedicated SRAM Spaces (Allwinner A523/A527/T527) */
+#define E907_SRAM_C_DA			0x00020000UL
+#define E907_SRAM_SPACE0_DA		0x3ff80000UL
+#define E907_SRAM_SPACE0_DA_ALT		0x3ffc0000UL
+#define E907_SRAM_SPACE1_DA		0x40000000UL
+#define E907_SRAM_SPACE1_DA_ALT		0x40040000UL
+
+/* Allwinner A523/A527/T527 System Bus (Host Physical) Addresses & Window Sizes */
+#define SUN55I_SRAM_SPACE0_SYS		0x07280000UL
+#define SUN55I_SRAM_SPACE0_SIZE		0x00040000UL /* 256 KB */
+#define SUN55I_SRAM_SPACE1_SYS		0x072c0000UL
+#define SUN55I_SRAM_SPACE1_SIZE		0x00040000UL /* 256 KB */
+
+/* Address Translation Table flags */
+#define ATT_IOMEM			BIT(30)
+
+struct sunxi_rproc_att {
+	u64 da;
+	u64 sa;
+	size_t size;
+	int flags;
+};
+
+/* XuanTie CFG Block Register Offsets */
+#define E906_CTRL_REG			0x0000
+#define E906_STA_ADD_REG		0x0204
+
+/* Remap Control Register (offset 0x364 in PRCM_R_CCU / MCU_CCU) */
+#define SUNXI_REMAP_CTRL_OFFSET		0x0364
+/* Bit 0: 0 = local RAM for MCU; 1 = share for system */
+#define SUNXI_REMAP_MCU_RAM_BIT		BIT(0)
+/* Bit 1: 0 = SRAMA3_2 not shared; 1 = share for MCU_SYS */
+#define SUNXI_REMAP_SRAMA3_2_BIT	BIT(1)
+
+struct sunxi_rproc_cfg {
+	const char *name;
+	const struct sunxi_rproc_att *att;
+	size_t att_size;
+	bool has_remap_reg;
+	u32 boot_reg_offset;
+};
+
+extern const struct sunxi_rproc_cfg sun55i_riscv_cfg;
+
+struct sunxi_rproc {
+	struct rproc *rproc;
+	struct device *dev;
+	const struct sunxi_rproc_cfg *cfg;
+
+	/* CCU Clocks & Resets */
+	struct clk *clk_parent;
+	struct clk *clk_bus;
+	struct clk *clk_core;
+	struct clk *clk_sram;
+	struct clk *clk_msgbox;
+	struct reset_control *rst_cfg;
+	struct reset_control *rst_core;
+	struct reset_control *rst_sram;
+	struct reset_control *rst_msgbox;
+
+	/* Hardware Memory Windows (Dedicated SRAM, Switchable SRAM, Remap) */
+	void __iomem *cfg_va;
+	phys_addr_t cfg_phys;
+
+	void __iomem *remap_va;
+	phys_addr_t remap_phys;
+
+	void __iomem *r_sram_va;
+	phys_addr_t r_sram_phys;
+	size_t r_sram_size;
+
+	void __iomem *r_sram1_va;
+	phys_addr_t r_sram1_phys;
+	size_t r_sram1_size;
+
+	void *dram_va;
+	phys_addr_t dram_phys;
+	size_t dram_size;
+
+	/* Trace / DDR Reserved Memory Window */
+	void *trace_va;
+	phys_addr_t trace_phys;
+	size_t trace_size;
+
+	/* Reserved Memory & Mailbox State */
+	bool has_reserved_mem;
+	int crash_irq;
+	bool crash_irq_enabled;
+	struct mbox_client cl;
+	struct mbox_chan *tx_chan;
+	struct mbox_chan *rx_chan;
+	struct work_struct vq_work;
+	u32 kick_msg;
+};
+
+extern const struct rproc_ops sunxi_rproc_ops;
+
+int sunxi_rproc_prepare(struct rproc *rproc);
+int sunxi_rproc_unprepare(struct rproc *rproc);
+int sunxi_rproc_start(struct rproc *rproc);
+int sunxi_rproc_stop(struct rproc *rproc);
+void sunxi_rproc_kick(struct rproc *rproc, int vqid);
+void *sunxi_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem);
+
+#endif /* _SUNXI_RPROC_H_ */
-- 
2.43.0


  parent reply	other threads:[~2026-09-27  0:21 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20260922034711.190253-1-tcmichals@gmail.com>
2026-09-27  0:20 ` [PATCH v2 0/7] remoteproc: sunxi: Add Allwinner XuanTie E907 RemoteProc and Message Box support Tim Michals
2026-09-27  0:20   ` [PATCH v2 1/7] dt-bindings: mailbox: add Allwinner sun55i msgbox schema Tim Michals
2026-09-27  0:20   ` [PATCH v2 2/7] mailbox: sun55i: add Allwinner sun55i/sun60i 4-port Message Box driver Tim Michals
2026-09-27  0:20   ` [PATCH v2 3/7] mailbox: sun55i: add KUnit test suite for sun55i msgbox driver Tim Michals
2026-09-27  0:20   ` [PATCH v2 4/7] dt-bindings: remoteproc: add Allwinner sun55i-rproc schema Tim Michals
2026-09-27  0:20   ` Tim Michals [this message]
2026-09-27  0:20   ` [PATCH v2 6/7] remoteproc: sunxi: add KUnit test suite for sunxi remoteproc driver Tim Michals
2026-09-27  0:20   ` [PATCH v2 7/7] arm64: dts: allwinner: add a523 msgbox and remoteproc nodes Tim Michals

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260927002021.797069-6-tcmichals@gmail.com \
    --to=tcmichals@gmail.com \
    --cc=andersson@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jassisinghbrar@gmail.com \
    --cc=jernej.skrabec@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-remoteproc@vger.kernel.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=mathieu.poirier@linaro.org \
    --cc=robh@kernel.org \
    --cc=samuel@sholland.org \
    --cc=wens@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®