[PATCH v2 4/4] port reduced version of remoteproc framework from linux

Oleksij Rempel linux at rempel-privat.de
Fri Jun 7 00:32:10 PDT 2019


I tested it on phytec imx7 board with remoteproc ELF image previously used on
Linux. Linux would load this image, create appropriate resource (if defined in
image) and boot it. The barebox version is only loading image and boot it.

Currently barebox version do not extract resources defined by rproc ELF image.
On this early stage it is hard to say, if it is needed.

Previously there was an attempt to port bootaux command from u-boot.
Porting of remoteproc framework is my attempt to lead this topic in to the
(IMO) right direction.

To start remoteproc image, firmwareload command should be used:
firmwareload /mnt/tftp/rproc-imx-rproc-fw

Since firmwareload already support multiple targets, it is possible to
specify which exact cortex m4 CPU should be started (if multiple CPU are
present). This example shows the list of available firmware targets:
barebox at Phytec i.MX7 phyBOARD-Zeta:/ firmwareload -l
firmware programming handlers:

name:      model:
soc:imx7d-rp0 at 0.of

Signed-off-by: Oleksij Rempel <linux at rempel-privat.de>
---
 drivers/Kconfig                            |   1 +
 drivers/Makefile                           |   1 +
 drivers/remoteproc/Kconfig                 |  26 ++
 drivers/remoteproc/Makefile                |   7 +
 drivers/remoteproc/imx_rproc.c             | 402 +++++++++++++++++++++
 drivers/remoteproc/remoteproc_core.c       | 166 +++++++++
 drivers/remoteproc/remoteproc_elf_loader.c |  88 +++++
 drivers/remoteproc/remoteproc_internal.h   |  30 ++
 include/linux/remoteproc.h                 |  51 +++
 9 files changed, 772 insertions(+)
 create mode 100644 drivers/remoteproc/Kconfig
 create mode 100644 drivers/remoteproc/Makefile
 create mode 100644 drivers/remoteproc/imx_rproc.c
 create mode 100644 drivers/remoteproc/remoteproc_core.c
 create mode 100644 drivers/remoteproc/remoteproc_elf_loader.c
 create mode 100644 drivers/remoteproc/remoteproc_internal.h
 create mode 100644 include/linux/remoteproc.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index f75da2698..09595433a 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -31,6 +31,7 @@ source "drivers/pinctrl/Kconfig"
 source "drivers/nvmem/Kconfig"
 source "drivers/bus/Kconfig"
 source "drivers/regulator/Kconfig"
+source "drivers/remoteproc/Kconfig"
 source "drivers/reset/Kconfig"
 source "drivers/pci/Kconfig"
 source "drivers/rtc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index fb7fcd3fc..5a52225ee 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_W1) += w1/
 obj-y += pinctrl/
 obj-y += bus/
 obj-$(CONFIG_REGULATOR) += regulator/
+obj-$(CONFIG_REMOTEPROC) += remoteproc/
 obj-$(CONFIG_RESET_CONTROLLER) += reset/
 obj-$(CONFIG_PCI) += pci/
 obj-y += rtc/
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
new file mode 100644
index 000000000..8139b6442
--- /dev/null
+++ b/drivers/remoteproc/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Remoteproc drivers"
+
+config REMOTEPROC
+	tristate "Support for Remote Processor subsystem"
+	select CRC32
+	select FIRMWARE
+	help
+	  Support for remote processors (such as DSP coprocessors). These
+	  are mainly used on embedded systems.
+
+if REMOTEPROC
+
+config IMX_REMOTEPROC
+	tristate "IMX6/7 remoteproc support"
+	depends on ARCH_IMX
+	select MFD_SYSCON
+	help
+	  Say y here to support iMX's remote processors (Cortex M4
+	  on iMX7D) via the remote processor framework.
+
+	  It's safe to say N here.
+
+endif # REMOTEPROC
+
+endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
new file mode 100644
index 000000000..107296922
--- /dev/null
+++ b/drivers/remoteproc/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Generic framework for controlling remote processors
+#
+
+obj-$(CONFIG_REMOTEPROC)		+= remoteproc_core.o remoteproc_elf_loader.o
+obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
new file mode 100644
index 000000000..c5cba3711
--- /dev/null
+++ b/drivers/remoteproc/imx_rproc.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/remoteproc.h>
+#include <mfd/syscon.h>
+#include <module.h>
+#include <memory.h>
+#include <of_address.h>
+#include <of_device.h>
+#include <regmap.h>
+
+#define IMX7D_SRC_SCR			0x0C
+#define IMX7D_ENABLE_M4			BIT(3)
+#define IMX7D_SW_M4P_RST		BIT(2)
+#define IMX7D_SW_M4C_RST		BIT(1)
+#define IMX7D_SW_M4C_NON_SCLR_RST	BIT(0)
+
+#define IMX7D_M4_RST_MASK		(IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
+					 | IMX7D_SW_M4C_RST \
+					 | IMX7D_SW_M4C_NON_SCLR_RST)
+
+#define IMX7D_M4_START			(IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
+					 | IMX7D_SW_M4C_RST)
+#define IMX7D_M4_STOP			IMX7D_SW_M4C_NON_SCLR_RST
+
+/* Address: 0x020D8000 */
+#define IMX6SX_SRC_SCR			0x00
+#define IMX6SX_ENABLE_M4		BIT(22)
+#define IMX6SX_SW_M4P_RST		BIT(12)
+#define IMX6SX_SW_M4C_NON_SCLR_RST	BIT(4)
+#define IMX6SX_SW_M4C_RST		BIT(3)
+
+#define IMX6SX_M4_START			(IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
+					 | IMX6SX_SW_M4C_RST)
+#define IMX6SX_M4_STOP			IMX6SX_SW_M4C_NON_SCLR_RST
+#define IMX6SX_M4_RST_MASK		(IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
+					 | IMX6SX_SW_M4C_NON_SCLR_RST \
+					 | IMX6SX_SW_M4C_RST)
+
+#define IMX7D_RPROC_MEM_MAX		8
+
+/**
+ * struct imx_rproc_mem - slim internal memory structure
+ * @cpu_addr: MPU virtual address of the memory region
+ * @sys_addr: Bus address used to access the memory region
+ * @size: Size of the memory region
+ */
+struct imx_rproc_mem {
+	void __iomem *cpu_addr;
+	phys_addr_t sys_addr;
+	size_t size;
+};
+
+/* att flags */
+/* M4 own area. Can be mapped at probe */
+#define ATT_OWN		BIT(1)
+
+/* address translation table */
+struct imx_rproc_att {
+	u32 da;	/* device address (From Cortex M4 view)*/
+	u32 sa;	/* system bus address */
+	u32 size; /* size of reg range */
+	int flags;
+};
+
+struct imx_rproc_dcfg {
+	u32				src_reg;
+	u32				src_mask;
+	u32				src_start;
+	u32				src_stop;
+	const struct imx_rproc_att	*att;
+	size_t				att_size;
+};
+
+struct imx_rproc {
+	struct device_d			*dev;
+	struct regmap			*regmap;
+	struct rproc			*rproc;
+	const struct imx_rproc_dcfg	*dcfg;
+	struct imx_rproc_mem		mem[IMX7D_RPROC_MEM_MAX];
+	struct clk			*clk;
+};
+
+static const struct imx_rproc_att imx_rproc_att_imx7d[] = {
+	/* dev addr , sys addr  , size	    , flags */
+	/* OCRAM_S (M4 Boot code) - alias */
+	{ 0x00000000, 0x00180000, 0x00008000, 0 },
+	/* OCRAM_S (Code) */
+	{ 0x00180000, 0x00180000, 0x00008000, ATT_OWN },
+	/* OCRAM (Code) - alias */
+	{ 0x00900000, 0x00900000, 0x00020000, 0 },
+	/* OCRAM_EPDC (Code) - alias */
+	{ 0x00920000, 0x00920000, 0x00020000, 0 },
+	/* OCRAM_PXP (Code) - alias */
+	{ 0x00940000, 0x00940000, 0x00008000, 0 },
+	/* TCML (Code) */
+	{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
+	/* DDR (Code) - alias, first part of DDR (Data) */
+	{ 0x10000000, 0x80000000, 0x0FFF0000, 0 },
+
+	/* TCMU (Data) */
+	{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
+	/* OCRAM (Data) */
+	{ 0x20200000, 0x00900000, 0x00020000, 0 },
+	/* OCRAM_EPDC (Data) */
+	{ 0x20220000, 0x00920000, 0x00020000, 0 },
+	/* OCRAM_PXP (Data) */
+	{ 0x20240000, 0x00940000, 0x00008000, 0 },
+	/* DDR (Data) */
+	{ 0x80000000, 0x80000000, 0x60000000, 0 },
+};
+
+static const struct imx_rproc_att imx_rproc_att_imx6sx[] = {
+	/* dev addr , sys addr  , size	    , flags */
+	/* TCML (M4 Boot Code) - alias */
+	{ 0x00000000, 0x007F8000, 0x00008000, 0 },
+	/* OCRAM_S (Code) */
+	{ 0x00180000, 0x008F8000, 0x00004000, 0 },
+	/* OCRAM_S (Code) - alias */
+	{ 0x00180000, 0x008FC000, 0x00004000, 0 },
+	/* TCML (Code) */
+	{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
+	/* DDR (Code) - alias, first part of DDR (Data) */
+	{ 0x10000000, 0x80000000, 0x0FFF8000, 0 },
+
+	/* TCMU (Data) */
+	{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
+	/* OCRAM_S (Data) - alias? */
+	{ 0x208F8000, 0x008F8000, 0x00004000, 0 },
+	/* DDR (Data) */
+	{ 0x80000000, 0x80000000, 0x60000000, 0 },
+};
+
+static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
+	.src_reg	= IMX7D_SRC_SCR,
+	.src_mask	= IMX7D_M4_RST_MASK,
+	.src_start	= IMX7D_M4_START,
+	.src_stop	= IMX7D_M4_STOP,
+	.att		= imx_rproc_att_imx7d,
+	.att_size	= ARRAY_SIZE(imx_rproc_att_imx7d),
+};
+
+static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
+	.src_reg	= IMX6SX_SRC_SCR,
+	.src_mask	= IMX6SX_M4_RST_MASK,
+	.src_start	= IMX6SX_M4_START,
+	.src_stop	= IMX6SX_M4_STOP,
+	.att		= imx_rproc_att_imx6sx,
+	.att_size	= ARRAY_SIZE(imx_rproc_att_imx6sx),
+};
+
+static int imx_rproc_start(struct rproc *rproc)
+{
+	struct imx_rproc *priv = rproc->priv;
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+	struct device_d *dev = priv->dev;
+	int ret;
+
+	ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
+				 dcfg->src_mask, dcfg->src_start);
+	if (ret)
+		dev_err(dev, "Filed to enable M4!\n");
+
+	return ret;
+}
+
+static int imx_rproc_stop(struct rproc *rproc)
+{
+	struct imx_rproc *priv = rproc->priv;
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+	struct device_d *dev = priv->dev;
+	int ret;
+
+	ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
+				 dcfg->src_mask, dcfg->src_stop);
+	if (ret)
+		dev_err(dev, "Filed to stop M4!\n");
+
+	return ret;
+}
+
+static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
+			       int len, u64 *sys)
+{
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+	int i;
+
+	/* parse address translation table */
+	for (i = 0; i < dcfg->att_size; i++) {
+		const struct imx_rproc_att *att = &dcfg->att[i];
+
+		if (da >= att->da && da + len < att->da + att->size) {
+			unsigned int offset = da - att->da;
+
+			*sys = att->sa + offset;
+			return 0;
+		}
+	}
+
+	dev_warn(priv->dev, "Translation filed: da = 0x%llx len = 0x%x\n",
+		 da, len);
+	return -ENOENT;
+}
+
+static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+	struct imx_rproc *priv = rproc->priv;
+	void *va = NULL;
+	u64 sys;
+	int i;
+
+	if (len <= 0)
+		return NULL;
+
+	/*
+	 * On device side we have many aliases, so we need to convert device
+	 * address (M4) to system bus address first.
+	 */
+	if (imx_rproc_da_to_sys(priv, da, len, &sys))
+		return NULL;
+
+	for (i = 0; i < IMX7D_RPROC_MEM_MAX; i++) {
+		if (sys >= priv->mem[i].sys_addr && sys + len <
+		    priv->mem[i].sys_addr +  priv->mem[i].size) {
+			unsigned int offset = sys - priv->mem[i].sys_addr;
+			/* __force to make sparse happy with type conversion */
+			va = (__force void *)(priv->mem[i].cpu_addr + offset);
+			break;
+		}
+	}
+
+	dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va);
+
+	return va;
+}
+
+static const struct rproc_ops imx_rproc_ops = {
+	.start		= imx_rproc_start,
+	.stop		= imx_rproc_stop,
+	.da_to_va       = imx_rproc_da_to_va,
+};
+
+static int imx_rproc_addr_init(struct imx_rproc *priv,
+			       struct device_d *dev)
+{
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+	struct device_node *np = dev->device_node;
+	int a, b = 0, err, nph;
+
+	/* remap required addresses */
+	for (a = 0; a < dcfg->att_size; a++) {
+		const struct imx_rproc_att *att = &dcfg->att[a];
+		struct resource *res_cpu;
+
+		if (!(att->flags & ATT_OWN))
+			continue;
+
+		if (b >= IMX7D_RPROC_MEM_MAX)
+			break;
+
+		res_cpu = request_iomem_region(dev_name(dev),
+							     att->sa,
+							     att->sa + att->size - 1);
+		if (!res_cpu) {
+			dev_err(dev, "remap required addresses failed\n");
+			return PTR_ERR(priv->mem[b].cpu_addr);
+		}
+		priv->mem[b].cpu_addr = (void *)res_cpu->start;
+		priv->mem[b].sys_addr = att->sa;
+		priv->mem[b].size = att->size;
+		b++;
+	}
+
+	/* memory-region is optional property */
+	nph = of_count_phandle_with_args(np, "memory-region", NULL);
+	if (nph <= 0)
+		return 0;
+
+	/* remap optional addresses */
+	for (a = 0; a < nph; a++) {
+		struct device_node *node;
+		struct resource res, *res_cpu;
+
+		node = of_parse_phandle(np, "memory-region", a);
+		err = of_address_to_resource(node, 0, &res);
+		if (err) {
+			dev_err(dev, "unable to resolve memory region\n");
+			return err;
+		}
+
+		if (b >= IMX7D_RPROC_MEM_MAX)
+			break;
+
+		res_cpu = request_sdram_region(dev_name(dev), res.start,
+					       res.end - res.start);
+		if (!res_cpu) {
+			dev_err(dev, "remap optional addresses failed\n");
+			return -ENOMEM;
+		}
+		priv->mem[b].cpu_addr = (void *)res_cpu->start;
+		priv->mem[b].sys_addr = res.start;
+		priv->mem[b].size = resource_size(&res);
+		b++;
+	}
+
+	return 0;
+}
+
+static int imx_rproc_probe(struct device_d *dev)
+{
+	struct device_node *np = dev->device_node;
+	struct imx_rproc *priv;
+	struct rproc *rproc;
+	const struct imx_rproc_dcfg *dcfg;
+	struct regmap *regmap;
+	int ret;
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
+	if (IS_ERR(regmap)) {
+		dev_err(dev, "failed to find syscon\n");
+		return PTR_ERR(regmap);
+	}
+
+	/* set some other name then imx */
+	rproc = rproc_alloc(dev, dev_name(dev), &imx_rproc_ops, sizeof(*priv));
+	if (!rproc)
+		return -ENOMEM;
+
+	dcfg = of_device_get_match_data(dev);
+	if (!dcfg) {
+		ret = -EINVAL;
+		goto err_put_rproc;
+	}
+
+	priv = rproc->priv;
+	priv->rproc = rproc;
+	priv->regmap = regmap;
+	priv->dcfg = dcfg;
+	priv->dev = dev;
+
+	ret = imx_rproc_addr_init(priv, dev);
+	if (ret) {
+		dev_err(dev, "filed on imx_rproc_addr_init\n");
+		goto err_put_rproc;
+	}
+
+	priv->clk = clk_get(dev, 0);
+	if (IS_ERR(priv->clk)) {
+		dev_err(dev, "Failed to get clock\n");
+		ret = PTR_ERR(priv->clk);
+		goto err_put_rproc;
+	}
+
+	/*
+	 * clk for M4 block including memory. Should be
+	 * enabled before .start for FW transfer.
+	 */
+	ret = clk_enable(priv->clk);
+	if (ret) {
+		dev_err(&rproc->dev, "Failed to enable clock\n");
+		goto err_put_rproc;
+	}
+
+	ret = rproc_add(rproc);
+	if (ret) {
+		dev_err(dev, "rproc_add failed\n");
+		goto err_put_clk;
+	}
+
+	return 0;
+
+err_put_clk:
+	clk_disable(priv->clk);
+err_put_rproc:
+	return ret;
+}
+
+static const struct of_device_id imx_rproc_of_match[] = {
+	{ .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d },
+	{ .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx },
+	{},
+};
+
+static struct driver_d imx_rproc_driver = {
+	.name = "imx-rproc",
+	.probe = imx_rproc_probe,
+	.of_compatible = DRV_OF_COMPAT(imx_rproc_of_match),
+};
+device_platform_driver(imx_rproc_driver);
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
new file mode 100644
index 000000000..7cac47e06
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Remote Processor Framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad at wizery.com>
+ * Brian Swetland <swetland at google.com>
+ * Mark Grosen <mgrosen at ti.com>
+ * Fernando Guzman Lugo <fernando.lugo at ti.com>
+ * Suman Anna <s-anna at ti.com>
+ * Robert Tivy <rtivy at ti.com>
+ * Armando Uribe De Leon <x0095078 at ti.com>
+ */
+
+#include <common.h>
+#include <firmware.h>
+#include <linux/remoteproc.h>
+
+#include "remoteproc_internal.h"
+
+void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+	void *ptr;
+
+	if (rproc->ops->da_to_va) {
+		ptr = rproc->ops->da_to_va(rproc, da, len);
+		if (ptr)
+			return ptr;
+	}
+
+	return NULL;
+}
+
+static int rproc_start(struct rproc *rproc, const struct firmware *fw)
+{
+	struct device_d *dev = &rproc->dev;
+	int ret;
+
+	/* load the ELF segments to memory */
+	ret = rproc_load_segments(rproc, fw);
+	if (ret) {
+		dev_err(dev, "Failed to load program segments: %d\n", ret);
+		return ret;
+	}
+
+	/* power up the remote processor */
+	ret = rproc->ops->start(rproc);
+	if (ret) {
+		dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
+		return ret;
+	}
+
+	dev_info(dev, "remote processor %s is now up\n", rproc->name);
+
+	return 0;
+}
+
+static int rproc_firmware_start(struct firmware_handler *fh)
+{
+	struct rproc *rproc = container_of(fh, struct rproc, fh);
+
+	rproc->fw_buf = kzalloc((4096 * 1024), GFP_KERNEL);
+	rproc->fw_buf_ofs = 0;
+	return 0;
+}
+
+static int rproc_firmware_write_buf(struct firmware_handler *fh, const void *buf,
+		size_t size)
+{
+	struct rproc *rproc = container_of(fh, struct rproc, fh);
+
+	if (rproc->fw_buf_ofs + size > (4096 * 1024)) {
+		return -ENOMEM;
+	}
+
+	memcpy(rproc->fw_buf + rproc->fw_buf_ofs, buf, size);
+	rproc->fw_buf_ofs += size;
+
+	return 0;
+}
+
+static int rproc_firmware_finish(struct firmware_handler *fh)
+{
+	struct rproc *rproc = container_of(fh, struct rproc, fh);
+	struct firmware fw;
+	struct device_d *dev;
+	int ret;
+
+	if (!rproc) {
+		pr_err("invalid rproc handle\n");
+		return -EINVAL;
+	}
+
+	dev = &rproc->dev;
+
+	dev_info(dev, "powering up %s\n", rproc->name);
+
+	fw.data = rproc->fw_buf;
+	fw.size = rproc->fw_buf_ofs;
+
+	ret = rproc_start(rproc, &fw);
+
+	kfree(rproc->fw_buf);
+
+	return ret;
+}
+
+int rproc_add(struct rproc *rproc)
+{
+	struct device_d *dev = &rproc->dev;
+	struct firmware_handler *fh;
+	int ret;
+
+	fh = &rproc->fh;
+	fh->id = xstrdup(rproc->name);
+	fh->open = rproc_firmware_start;
+	fh->write = rproc_firmware_write_buf;
+	fh->close = rproc_firmware_finish;
+
+	ret = firmwaremgr_register(fh);
+	if (ret)
+		dev_err(dev, "filed to register firmware handler %s\n", rproc->name);
+	else
+		dev_info(dev, "%s is available\n", rproc->name);
+
+	return ret;
+}
+
+struct rproc *rproc_alloc(struct device_d *dev, const char *name,
+			  const struct rproc_ops *ops, int len)
+{
+	struct rproc *rproc;
+
+	if (!dev || !name || !ops)
+		return NULL;
+
+	rproc = xzalloc(sizeof(struct rproc) + len);
+	if (!rproc) {
+		return NULL;
+	}
+
+	rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);
+	if (!rproc->ops) {
+		kfree(rproc);
+		return NULL;
+	}
+
+	rproc->name = name;
+	rproc->priv = &rproc[1];
+
+	rproc->dev.parent = dev;
+	rproc->dev.priv = rproc;
+
+	/* Assign a unique device index and name */
+	rproc->index = 1;
+
+	dev_set_name(&rproc->dev, "remoteproc%d", rproc->index);
+
+	/* Default to ELF loader if no load function is specified */
+	if (!rproc->ops->load)
+		rproc->ops->load = rproc_elf_load_segments;
+
+	return rproc;
+}
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
new file mode 100644
index 000000000..45d52db5c
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Remote Processor Framework Elf loader
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad at wizery.com>
+ * Brian Swetland <swetland at google.com>
+ * Mark Grosen <mgrosen at ti.com>
+ * Fernando Guzman Lugo <fernando.lugo at ti.com>
+ * Suman Anna <s-anna at ti.com>
+ * Robert Tivy <rtivy at ti.com>
+ * Armando Uribe De Leon <x0095078 at ti.com>
+ * Sjur Brændeland <sjur.brandeland at stericsson.com>
+ */
+
+#include <common.h>
+#include <elf.h>
+#include <linux/remoteproc.h>
+
+#include "remoteproc_internal.h"
+
+int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
+{
+	struct device_d *dev = &rproc->dev;
+	struct elf32_hdr *ehdr;
+	struct elf32_phdr *phdr;
+	int i, ret = 0;
+	const u8 *elf_data = fw->data;
+
+	ehdr = (struct elf32_hdr *)elf_data;
+	phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
+
+	/* go through the available ELF segments */
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		u32 da = phdr->p_paddr;
+		u32 memsz = phdr->p_memsz;
+		u32 filesz = phdr->p_filesz;
+		u32 offset = phdr->p_offset;
+		void *ptr;
+
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+			phdr->p_type, da, memsz, filesz);
+
+		if (filesz > memsz) {
+			dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+				filesz, memsz);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (offset + filesz > fw->size) {
+			dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
+				offset + filesz, fw->size);
+			ret = -EINVAL;
+			break;
+		}
+
+		/* grab the kernel address for this device address */
+		ptr = rproc_da_to_va(rproc, da, memsz);
+		if (!ptr) {
+			dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
+			ret = -EINVAL;
+			break;
+		}
+
+		/* put the segment where the remote processor expects it */
+		if (phdr->p_filesz)
+			memcpy(ptr, elf_data + phdr->p_offset, filesz);
+
+		/*
+		 * Zero out remaining memory for this segment.
+		 *
+		 * This isn't strictly required since dma_alloc_coherent already
+		 * did this for us. albeit harmless, we may consider removing
+		 * this.
+		 */
+		if (memsz > filesz)
+			memset(ptr + filesz, 0, memsz - filesz);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(rproc_elf_load_segments);
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
new file mode 100644
index 000000000..8893d1a40
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Remote processor framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad at wizery.com>
+ * Brian Swetland <swetland at google.com>
+ */
+
+#ifndef REMOTEPROC_INTERNAL_H
+#define REMOTEPROC_INTERNAL_H
+
+struct rproc;
+
+void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
+
+int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw);
+
+static inline
+int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
+{
+	if (rproc->ops->load)
+		return rproc->ops->load(rproc, fw);
+
+	return -EINVAL;
+}
+
+#endif /* REMOTEPROC_INTERNAL_H */
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
new file mode 100644
index 000000000..feee9ee4e
--- /dev/null
+++ b/include/linux/remoteproc.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Remote Processor Framework
+ *
+ * Copyright(c) 2011 Texas Instruments, Inc.
+ * Copyright(c) 2011 Google, Inc.
+ */
+
+#ifndef REMOTEPROC_H
+#define REMOTEPROC_H
+
+#include <firmware.h>
+
+struct resource_table {
+	u32 ver;
+	u32 num;
+	u32 reserved[2];
+	u32 offset[0];
+} __packed;
+
+struct firmware {
+	size_t size;
+	const u8 *data;
+};
+
+struct rproc;
+
+struct rproc_ops {
+	int (*start)(struct rproc *rproc);
+	int (*stop)(struct rproc *rproc);
+	void * (*da_to_va)(struct rproc *rproc, u64 da, int len);
+	int (*load)(struct rproc *rproc, const struct firmware *fw);
+};
+
+struct rproc {
+	struct firmware_handler fh;
+	const char *name;
+	void *priv;
+	struct rproc_ops *ops;
+	struct device_d dev;
+	int index;
+
+	void *fw_buf;
+	size_t fw_buf_ofs;
+};
+
+struct rproc *rproc_alloc(struct device_d *dev, const char *name,
+			  const struct rproc_ops *ops, int len);
+int rproc_add(struct rproc *rproc);
+
+#endif /* REMOTEPROC_H */
--
2.20.1




More information about the barebox mailing list