summaryrefslogtreecommitdiffstats
path: root/drivers/remoteproc
diff options
context:
space:
mode:
authorOleksij Rempel <linux@rempel-privat.de>2019-09-23 11:55:27 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2019-09-26 08:42:52 +0200
commit22763dab152cb38c0912fe688f564a54aeaaea88 (patch)
tree81252e6e25da91896de155d875c3badb67335299 /drivers/remoteproc
parentf114b0479a2217c19cdf77a5aeeeb2a329255ff0 (diff)
downloadbarebox-22763dab152cb38c0912fe688f564a54aeaaea88.tar.gz
barebox-22763dab152cb38c0912fe688f564a54aeaaea88.tar.xz
port reduced version of remoteproc framework from linux
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@Phytec i.MX7 phyBOARD-Zeta:/ firmwareload -l firmware programming handlers: name: model: soc:imx7d-rp0@0.of Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/remoteproc')
-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
6 files changed, 719 insertions, 0 deletions
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 */