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

Oleksij Rempel linux at rempel-privat.de
Wed Jun 5 09:52:42 PDT 2019


With this port it is possible to start same ELF firmware images as
for linux remoteproc.

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