summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-10-17 08:10:24 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2019-10-17 08:10:24 +0200
commit9156b904baf981561a8ed486db52746b04d2e464 (patch)
tree1679c7e689d541f5c1a5663c2ee201036407857e
parent854cdfbe265ebd99619bc6edc594d59b07c156fe (diff)
parent22763dab152cb38c0912fe688f564a54aeaaea88 (diff)
downloadbarebox-9156b904baf981561a8ed486db52746b04d2e464.tar.gz
barebox-9156b904baf981561a8ed486db52746b04d2e464.tar.xz
Merge branch 'for-next/remoteproc'
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/of/base.c3
-rw-r--r--drivers/remoteproc/Kconfig26
-rw-r--r--drivers/remoteproc/Makefile7
-rw-r--r--drivers/remoteproc/imx_rproc.c402
-rw-r--r--drivers/remoteproc/remoteproc_core.c166
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c88
-rw-r--r--drivers/remoteproc/remoteproc_internal.h30
-rw-r--r--include/elf.h2
-rw-r--r--include/linux/remoteproc.h51
11 files changed, 775 insertions, 2 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index f75da26982..09595433a0 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 fb7fcd3fc2..5a52225ee0 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/of/base.c b/drivers/of/base.c
index 63e0879f06..98ef5fc0d4 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1360,7 +1360,8 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
np->full_name);
goto err;
}
- if (of_property_read_u32(node, cells_name, &count)) {
+ if (cells_name &&
+ of_property_read_u32(node, cells_name, &count)) {
pr_err("%s: could not get %s for %s\n",
np->full_name, cells_name,
node->full_name);
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
new file mode 100644
index 0000000000..8139b6442c
--- /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 0000000000..1072969229
--- /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 0000000000..c5cba3711a
--- /dev/null
+++ b/drivers/remoteproc/imx_rproc.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@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 0000000000..7cac47e06c
--- /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@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Robert Tivy <rtivy@ti.com>
+ * Armando Uribe De Leon <x0095078@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 0000000000..45d52db5c1
--- /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@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Robert Tivy <rtivy@ti.com>
+ * Armando Uribe De Leon <x0095078@ti.com>
+ * Sjur Brændeland <sjur.brandeland@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 0000000000..8893d1a400
--- /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@wizery.com>
+ * Brian Swetland <swetland@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/elf.h b/include/elf.h
index 633f4992dd..44be07f73a 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -286,7 +286,7 @@ typedef struct elf64_phdr {
#define SHN_COMMON 0xfff2
#define SHN_HIRESERVE 0xffff
-typedef struct {
+typedef struct elf32_shdr {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
new file mode 100644
index 0000000000..feee9ee4ee
--- /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 */