All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tim Michals <tcmichals@gmail.com>
To: linux-sunxi@lists.linux.dev
Cc: wens@kernel.org, jernej.skrabec@gmail.com, samuel@sholland.org,
	Tim Michals <tcmichals@gmail.com>,
	Bjorn Andersson <andersson@kernel.org>,
	Mathieu Poirier <mathieu.poirier@linaro.org>,
	linux-remoteproc@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: [PATCH 5/7] remoteproc: sunxi: add allwinner riscv remoteproc driver
Date: Mon, 21 Sep 2026 22:47:09 -0500	[thread overview]
Message-ID: <20260922034711.190253-6-tcmichals@gmail.com> (raw)
In-Reply-To: <20260922034711.190253-1-tcmichals@gmail.com>

Add remoteproc driver for the XuanTie E906/E907 RISC-V remote processor
found on Allwinner A523, A527, and T527 SoCs.

The driver integrates with the Common Clock Framework (CCF) and Reset
Framework to manage co-processor clocks (bus, core, sram) and resets
(cfg, core, sram), programs the core boot vector (STA_ADD_REG),
maps internal TCM and dedicated SRAM windows, and connects with hardware
mailboxes (CPUX_MSGBOX) for standard VirtIO RPMsg IPC.

Cc: Bjorn Andersson <andersson@kernel.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Jernej Skrabec <jernej.skrabec@gmail.com>
Cc: Samuel Holland <samuel@sholland.org>
Cc: linux-remoteproc@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-sunxi@lists.linux.dev
Signed-off-by: Tim Michals <tcmichals@gmail.com>
---
 drivers/remoteproc/Kconfig       |   8 +
 drivers/remoteproc/Makefile      |   1 +
 drivers/remoteproc/sunxi_rproc.c | 905 +++++++++++++++++++++++++++++++
 3 files changed, 914 insertions(+)
 create mode 100644 drivers/remoteproc/sunxi_rproc.c

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..4cbffc7b8277
--- /dev/null
+++ b/drivers/remoteproc/sunxi_rproc.c
@@ -0,0 +1,905 @@
+// 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"
+
+#define DRIVER_NAME "sunxi-rproc"
+
+/* 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)
+
+/* XuanTie core-local view of dedicated SRAM Spaces (E906/E907) */
+#define E907_SRAM_C_DA			0x00020000
+#define E907_SRAM_SPACE0_DA		0x3ff80000
+#define E907_SRAM_SPACE0_DA_ALT		0x3ffc0000
+#define E907_SRAM_SPACE1_DA		0x40000000
+
+/* XuanTie E906/E907 Control & Boot Address Registers (when CFG block is present) */
+#define E906_CTRL_REG			0x0000
+#define E906_STA_ADD_REG		0x0204
+
+struct sunxi_rproc_cfg {
+	const char *name;
+};
+
+static const struct sunxi_rproc_cfg sun55i_riscv_cfg = {
+	.name = "XuanTie E907 RISC-V",
+};
+
+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;
+	struct mbox_client cl;
+	struct mbox_chan *tx_chan;
+	struct mbox_chan *rx_chan;
+	struct work_struct vq_work;
+};
+
+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 rproc *rproc = data;
+	struct sunxi_rproc *priv = rproc->priv;
+
+	dev_err(priv->dev, "Hardware crash event received from %s core!\n",
+		priv->cfg ? priv->cfg->name : "remote");
+	rproc_report_crash(rproc, RPROC_FATAL_ERROR);
+
+	return IRQ_HANDLED;
+}
+
+static int sunxi_rproc_prepare(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	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 (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;
+}
+
+static int sunxi_rproc_unprepare(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+
+	/* Symmetrical CCU unwinding */
+	if (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;
+}
+
+static int sunxi_rproc_start(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	int ret;
+
+	dev_info(priv->dev, "Starting %s core at entry 0x%llx\n",
+		 priv->cfg ? priv->cfg->name : "remote", (u64)rproc->bootaddr);
+
+	if (rproc->bootaddr > U32_MAX)
+		return -EINVAL;
+
+	/* XuanTie RISC-V boot sequence */
+	if (priv->cfg_va) {
+		writel((u32)rproc->bootaddr, priv->cfg_va + E906_STA_ADD_REG);
+		dev_dbg(priv->dev, "STA_ADD set to 0x%08x\n", (u32)rproc->bootaddr);
+	}
+
+	/* Release core reset to begin execution */
+	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;
+}
+
+static int sunxi_rproc_stop(struct rproc *rproc)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+
+	cancel_work_sync(&priv->vq_work);
+
+	dev_info(priv->dev, "Halting %s core...\n",
+		 priv->cfg ? priv->cfg->name : "remote");
+
+	if (priv->rst_core)
+		reset_control_assert(priv->rst_core);
+	else if (priv->rst_cfg)
+		reset_control_assert(priv->rst_cfg);
+
+	return 0;
+}
+
+static void sunxi_rproc_kick(struct rproc *rproc, int vqid)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+	int ret;
+
+	if (!priv->tx_chan)
+		return;
+
+	ret = mbox_send_message(priv->tx_chan, (void *)&vqid);
+	if (ret < 0)
+		dev_err_ratelimited(priv->dev, "failed to send mailbox kick: %d\n", ret);
+
+	mbox_client_txdone(priv->tx_chan, 0);
+}
+
+static void *sunxi_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
+{
+	struct sunxi_rproc *priv = rproc->priv;
+
+	if (len == 0)
+		return NULL;
+
+	/* 1. Dedicated MCU Local SRAM Space 0 (Resource "r_sram" / "sram") */
+	if (priv->r_sram_va) {
+		/* Host physical address view (e.g., 0x07280000, 0x07200000, 0x00020000) */
+		if (da >= priv->r_sram_phys &&
+		    (da + len) <= (priv->r_sram_phys + priv->r_sram_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram_va + (da - priv->r_sram_phys);
+		}
+		/* Core DA view: 0x40000000 */
+		if (da >= 0x40000000 &&
+		    (da + len) <= (0x40000000 + priv->r_sram_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram_va + (da - 0x40000000);
+		}
+		/* High SRAM Space 0 fallback views (0x3ff80000 / 0x3ffc0000) */
+		if (da >= 0x3ff80000 && (da + len) <= (0x3ff80000 + priv->r_sram_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram_va + (da - 0x3ff80000);
+		}
+		if (da >= 0x3ffc0000 && (da + len) <= (0x3ffc0000 + priv->r_sram_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram_va + (da - 0x3ffc0000);
+		}
+		/* PubSRAM C DA view (0x00020000) */
+		if (da >= 0x00020000 && (da + len) <= (0x00020000 + priv->r_sram_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram_va + (da - 0x00020000);
+		}
+	}
+
+	/* 2. Switchable MCU Local SRAM Space 1 ("r_sram1", Core DA 0x40040000) */
+	if (priv->r_sram1_va) {
+		/* Host physical address view (e.g., 0x072c0000 or 0x07280000) */
+		if (da >= priv->r_sram1_phys &&
+		    (da + len) <= (priv->r_sram1_phys + priv->r_sram1_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram1_va + (da - priv->r_sram1_phys);
+		}
+		/* Core DA view: 0x40000000 (Space 1) and 0x40040000 */
+		if (da >= 0x40000000 &&
+		    (da + len) <= (0x40000000 + priv->r_sram1_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram1_va + (da - 0x40000000);
+		}
+		if (da >= 0x40040000 &&
+		    (da + len) <= (0x40040000 + priv->r_sram1_size)) {
+			if (is_iomem)
+				*is_iomem = true;
+			return priv->r_sram1_va + (da - 0x40040000);
+		}
+	}
+
+	/* 4. Trace / Reserved Memory (from Device Tree) */
+	if (priv->trace_va) {
+		if (da >= priv->trace_phys && (da + len) <= (priv->trace_phys + priv->trace_size)) {
+			if (is_iomem)
+				*is_iomem = false;
+			return priv->trace_va + (da - priv->trace_phys);
+		}
+	}
+
+	/* 5. Boot DRAM Carveout (Resource "dram" - Core/Host 0x40014000) */
+	if (priv->dram_va) {
+		if (da >= priv->dram_phys && (da + len) <= (priv->dram_phys + priv->dram_size)) {
+			if (is_iomem)
+				*is_iomem = false;
+			return priv->dram_va + (da - priv->dram_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;
+}
+
+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;
+}
+
+static 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,
+};
+
+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 && (res->start & 0xfff) == 0) {
+			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 ("dram" or memory-region "vram"/"dram") */
+	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 = devm_ioremap_wc(dev, res->start, resource_size(res));
+		if (!priv->dram_va)
+			dev_warn(dev, "failed to map 'dram' resource\n");
+	} else if (dev->of_node) {
+		int idx = of_property_match_string(dev->of_node, "memory-region-names", "vram");
+
+		if (idx < 0)
+			idx = of_property_match_string(dev->of_node, "memory-region-names", "dram");
+		if (idx >= 0) {
+			struct device_node *rmem_np;
+
+			rmem_np = of_parse_phandle(dev->of_node, "memory-region", idx);
+			if (rmem_np) {
+				struct resource r;
+
+				if (of_address_to_resource(rmem_np, 0, &r) == 0) {
+					priv->dram_phys = r.start;
+					priv->dram_size = resource_size(&r);
+					priv->dram_va = devm_memremap(dev, r.start,
+								      resource_size(&r),
+								      MEMREMAP_WB);
+					if (!priv->dram_va)
+						priv->dram_va = devm_ioremap_wc(dev, r.start,
+										resource_size(&r));
+					dev_info(dev, "mapped DT vram reserved-memory %pa+%zu\n",
+						 &priv->dram_phys, priv->dram_size);
+				}
+				of_node_put(rmem_np);
+			}
+		}
+	}
+
+	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");
+
+	/* 6. Map Trace Buffer / Reserved Memory from Device Tree */
+	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 = 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);
+	} else if (dev->of_node) {
+		struct device_node *rmem_np;
+		int idx;
+
+		/* Check memory-region phandle only if explicitly named 'trace' */
+		idx = of_property_match_string(dev->of_node, "memory-region-names", "trace");
+		if (idx >= 0) {
+			rmem_np = of_parse_phandle(dev->of_node, "memory-region", idx);
+			if (rmem_np) {
+				struct resource r;
+
+				if (of_address_to_resource(rmem_np, 0, &r) == 0) {
+					priv->trace_phys = r.start;
+					priv->trace_size = resource_size(&r);
+					priv->trace_va = devm_memremap(dev, r.start,
+								       resource_size(&r),
+								       MEMREMAP_WB);
+					if (!priv->trace_va)
+						priv->trace_va = devm_ioremap_wc(dev, r.start,
+										 resource_size(&r));
+					dev_info(dev, "mapped DT trace reserved-memory %pa+%zu\n",
+						 &priv->trace_phys, priv->trace_size);
+				}
+				of_node_put(rmem_np);
+			}
+		}
+	}
+
+	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 = 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 = 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 = priv->r_sram1_va;
+		else if (priv->r_sram_va && res.start == priv->r_sram_phys)
+			va = priv->r_sram_va;
+		else
+			va = 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;
+	}
+
+	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, "sunxi-rproc-crash",
+						rproc);
+		if (ret)
+			dev_warn(dev, "failed to request crash IRQ %d: %d\n", crash_irq, ret);
+	}
+
+	/* 6. Mailbox IPC Client */
+	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:
+	INIT_WORK(&priv->vq_work, sunxi_rproc_vq_work);
+	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);
+	if (priv->rx_chan)
+		mbox_free_channel(priv->rx_chan);
+	if (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;
+
+	cancel_work_sync(&priv->vq_work);
+	rproc_del(rproc);
+
+	if (priv->rx_chan)
+		mbox_free_channel(priv->rx_chan);
+	if (priv->tx_chan)
+		mbox_free_channel(priv->tx_chan);
+
+	if (priv->has_reserved_mem)
+		of_reserved_mem_device_release(&pdev->dev);
+}
+
+static const struct of_device_id sunxi_rproc_of_match[] = {
+	{ .compatible = "allwinner,sun55i-a523-rproc", .data = &sun55i_riscv_cfg },
+	{ .compatible = "allwinner,sun55i-a527-rproc", .data = &sun55i_riscv_cfg },
+	{ .compatible = "allwinner,sun55i-t527-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");
-- 
2.53.0


  parent reply	other threads:[~2026-09-22  3:47 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-22  3:47 [PATCH 0/7] remoteproc/mailbox: add Allwinner A523/A527/T527 E907 RISC-V support Tim Michals
2026-09-22  3:47 ` [PATCH 1/7] dt-bindings: mailbox: add Allwinner sun55i msgbox schema Tim Michals
2026-09-22  3:54   ` sashiko-bot
2026-09-22  8:54   ` Krzysztof Kozlowski
2026-09-22  3:47 ` [PATCH 2/7] mailbox: sun55i: add Allwinner sun55i/sun60i 4-port Message Box driver Tim Michals
2026-09-22  4:00   ` sashiko-bot
2026-09-22  3:47 ` [PATCH 3/7] mailbox: sun55i: add KUnit tests for routing and registers Tim Michals
2026-09-22  3:54   ` sashiko-bot
2026-09-22  3:47 ` [PATCH 4/7] dt-bindings: remoteproc: add allwinner sun55i rproc binding Tim Michals
2026-09-22  3:56   ` sashiko-bot
2026-09-22  8:58   ` Krzysztof Kozlowski
2026-09-22 12:46   ` Rob Herring (Arm)
2026-09-22  3:47 ` Tim Michals [this message]
2026-09-22  3:59   ` [PATCH 5/7] remoteproc: sunxi: add allwinner riscv remoteproc driver sashiko-bot
2026-09-22  3:47 ` [PATCH 6/7] remoteproc: sunxi: add KUnit tests for da_to_va address translation Tim Michals
2026-09-22  3:54   ` sashiko-bot
2026-09-22  3:47 ` [PATCH 7/7] arm64: dts: allwinner: add a523 msgbox and remoteproc nodes Tim Michals
2026-09-22  3:59   ` sashiko-bot
2026-09-22  6:34 ` [PATCH 0/7] remoteproc/mailbox: add Allwinner A523/A527/T527 E907 RISC-V support Chen-Yu Tsai
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:30     ` sashiko-bot
2026-10-01  6:16     ` Krzysztof Kozlowski
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:33     ` sashiko-bot
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:31     ` sashiko-bot
2026-09-27  0:20   ` [PATCH v2 4/7] dt-bindings: remoteproc: add Allwinner sun55i-rproc schema Tim Michals
2026-09-27  0:27     ` sashiko-bot
2026-10-01  6:17     ` Krzysztof Kozlowski
2026-09-27  0:20   ` [PATCH v2 5/7] remoteproc: sunxi: add Allwinner XuanTie RISC-V remoteproc driver Tim Michals
2026-09-27  0:33     ` sashiko-bot
2026-09-27  0:20   ` [PATCH v2 6/7] remoteproc: sunxi: add KUnit test suite for sunxi " Tim Michals
2026-09-27  0:32     ` sashiko-bot
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=20260922034711.190253-6-tcmichals@gmail.com \
    --to=tcmichals@gmail.com \
    --cc=andersson@kernel.org \
    --cc=jernej.skrabec@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-remoteproc@vger.kernel.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=mathieu.poirier@linaro.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.