aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPer Åstrand <per.astrand@arm.com>2021-04-12 13:46:04 +0200
committerPer Åstrand <per.astrand@arm.com>2021-06-21 11:31:29 +0200
commit2cd539705ba4b510859f18e9815a72950ddd96fa (patch)
treedebfabdd521b93566fa3612e27a412a350b97208
parent87ca5dac68ffd40007e31e11b32980a6d5b7cf83 (diff)
downloadethos-u-linux-driver-stack-2cd539705ba4b510859f18e9815a72950ddd96fa.tar.gz
Use remoteproc for controlling Ethos-U cpu
Implement a remoteproc driver for controlling firmware loading and powering up and down of the CPU on a Ethos-U subsystem. Change-Id: I075d24c780b306aa8152c4a9e24a31df29574f9e
-rw-r--r--README.md8
-rw-r--r--remoteproc/CMakeLists.txt2
-rw-r--r--remoteproc/Kbuild1
-rw-r--r--remoteproc/Kconfig8
-rw-r--r--remoteproc/ethosu_remoteproc.c326
-rw-r--r--remoteproc/remoteproc.dtsi15
6 files changed, 359 insertions, 1 deletions
diff --git a/README.md b/README.md
index c46e1fe..4d48584 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,13 @@ The kernel driver uses the mailbox APIs as a doorbell mechanism.
clock-names = "apb_pclk";
};
+ ethosu_mcu {
+ compatible ="arm, ethosu-sgm775-rproc";
+ reg = <0x0 0x500f0000 0x0 0x1000>,
+ <0x0 0x50100000 0x0 0x100000>;
+ reg-names = "bridge", "firmware";
+ };
+
ethosu {
#address-cells = <2>;
#size-cells = <2>;
@@ -62,6 +69,7 @@ The kernel driver uses the mailbox APIs as a doorbell mechanism.
dma-ranges = <0 0x60000000 0 0x80000000 0 0x20000000>;
mboxes= <&ethosu_mailbox 0>, <&ethosu_mailbox 0>;
mbox-names = "tx", "rx";
+ ethosu-rproc = <&ethosu_mcu>;
};
};
```
diff --git a/remoteproc/CMakeLists.txt b/remoteproc/CMakeLists.txt
index 9c88df1..1532f6c 100644
--- a/remoteproc/CMakeLists.txt
+++ b/remoteproc/CMakeLists.txt
@@ -40,7 +40,7 @@ list(TRANSFORM OBJ PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/)
add_custom_target(ethosu-remoteproc-module ALL
COMMAND ${CMAKE_MAKE_PROGRAM} -C ${KDIR}
EXTRA_CFLAGS=-I${KDIR}/../../../drivers/remoteproc M=${CMAKE_CURRENT_SOURCE_DIR}
- CONFIG_ETHOSU_RESET=m
+ CONFIG_ETHOSU_RESET=m CONFIG_ETHOSU_REMOTEPROC=m
CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 modules
BYPRODUCTS
${CMAKE_CURRENT_SOURCE_DIR}/ethosu_remoteproc.ko
diff --git a/remoteproc/Kbuild b/remoteproc/Kbuild
index 1f02245..8a88c43 100644
--- a/remoteproc/Kbuild
+++ b/remoteproc/Kbuild
@@ -19,3 +19,4 @@
#
obj-$(CONFIG_ETHOSU_RESET) += ethosu_bridge_reset.o
+obj-$(CONFIG_ETHOSU_REMOTEPROC) += ethosu_remoteproc.o
diff --git a/remoteproc/Kconfig b/remoteproc/Kconfig
index 4884cd2..deca3e6 100644
--- a/remoteproc/Kconfig
+++ b/remoteproc/Kconfig
@@ -26,3 +26,11 @@ config ARM_ETHOSU_SGM775_RESET
reference module for controlling the reset of Cortex-M CPU on
the Ethos-U subsystem.
+config ARM_ETHOSU_RPROC
+ tristate "Arm MHUv2 Mailbox"
+ depends on REMOTEPROC
+ help
+ Say Y here if you want to build the Arm Ethos-U remoteproc
+ reference module for controlling the Cortex-M CPU on the Ethos-U
+ subsystem.
+
diff --git a/remoteproc/ethosu_remoteproc.c b/remoteproc/ethosu_remoteproc.c
new file mode 100644
index 0000000..9fb561a
--- /dev/null
+++ b/remoteproc/ethosu_remoteproc.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2021 Arm Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/reset.h>
+
+#define ETHOSU_RPROC_DRIVER_VERSION "0.0.1"
+
+#define DEFAULT_FW_FILE "arm-ethos-u65.fw"
+#define DEFAULT_AUTO_BOOT (false)
+
+/* firmware naming module parameter */
+static char fw_filename_param[256] = DEFAULT_FW_FILE;
+/* As the remoteproc is setup at probe, just allow the filename readonly */
+module_param_string(filename, fw_filename_param, sizeof(fw_filename_param),
+ 0444);
+MODULE_PARM_DESC(filename,
+ "Filename for firmware image for Ethos-U remoteproc");
+
+static bool auto_boot = DEFAULT_AUTO_BOOT;
+module_param(auto_boot, bool, DEFAULT_AUTO_BOOT);
+MODULE_PARM_DESC(auto_boot, "Set to one to auto boot at load.");
+
+struct ethosu_rproc {
+ struct device *dev;
+ struct reset_control *rstc;
+ struct rproc_mem_mapping *map;
+ size_t map_size;
+};
+
+struct rproc_mem_mapping {
+ const char *name;
+ phys_addr_t rproc_addr;
+ void __iomem *vaddr;
+ size_t size;
+};
+
+struct ethosu_rproc_config {
+ struct fw_config *fw;
+};
+
+/*****************************************************************************/
+
+static int ethosu_rproc_start(struct rproc *rproc)
+{
+ struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
+ struct device *dev = ethosu->dev;
+
+ dev_info(dev, "Starting up Ethos-U subsystem CPU!");
+
+ return reset_control_deassert(ethosu->rstc);
+}
+
+static int ethosu_rproc_stop(struct rproc *rproc)
+{
+ struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
+ struct device *dev = ethosu->dev;
+
+ dev_info(dev, "Stopping Ethos-U subsystem CPU!");
+
+ return reset_control_assert(ethosu->rstc);
+}
+
+static void ethosu_rproc_kick(struct rproc *rproc,
+ int vqid)
+{
+ return;
+}
+
+static void *ethosu_da_to_va(struct rproc *rproc,
+ u64 da,
+ int len)
+{
+ struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
+ int offset;
+ int i;
+
+ for (i = 0; i < ethosu->map_size; i++)
+ if (da >= ethosu->map[i].rproc_addr &&
+ da < (ethosu->map[i].rproc_addr + ethosu->map[i].size)) {
+ offset = da - ethosu->map[i].rproc_addr;
+ dev_info(ethosu->dev,
+ "mapping %llx to %p (offset: 0x%x)", da,
+ (void *)(ethosu->map[i].vaddr + offset),
+ offset);
+
+ return (void *)(ethosu->map[i].vaddr + offset);
+ }
+
+ return NULL;
+}
+
+static const struct rproc_ops ethosu_rproc_ops = {
+ .start = &ethosu_rproc_start,
+ .stop = &ethosu_rproc_stop,
+ .kick = &ethosu_rproc_kick,
+ .da_to_va = &ethosu_da_to_va,
+};
+
+/**
+ * Since the remote side doesn't yet support rpmsg just return an
+ * empty resource table when asked about it.
+ */
+struct resource_table *ethosu_rproc_find_rsc_table(struct rproc *rproc,
+ const struct firmware *fw,
+ int *tablesz)
+{
+ static struct resource_table table = { .ver = 1, };
+ struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
+
+ dev_info(ethosu->dev, "Sizeof struct resource_table : %zu",
+ sizeof(table));
+ *tablesz = sizeof(table);
+
+ return &table;
+}
+
+/*****************************************************************************/
+
+static int ethosu_rproc_of_memory_translations(struct platform_device *pdev,
+ struct ethosu_rproc *ethosu_rproc)
+{
+ const char *const of_rproc_address_cells =
+ "#ethosu,rproc-address-cells";
+ const char *const of_rproc_ranges = "ethosu,rproc-ranges";
+ const char *const of_rproc_ranges_names = "ethosu,rproc-names";
+
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct rproc_mem_mapping *mem_map;
+ const __be32 *rproc_ranges;
+
+ int addr_cells, rproc_addr_cells, size_cells, cells_for_array_element;
+ int i, len, cnt, name_cnt, ret = 0;
+
+ if (of_property_read_u32(np, of_rproc_address_cells,
+ &rproc_addr_cells)) {
+ dev_info(dev, "%s not defined in dtb", of_rproc_address_cells);
+
+ return -ENODEV;
+ }
+
+ addr_cells = of_n_addr_cells(np);
+ size_cells = of_n_size_cells(np);
+
+ dev_dbg(dev, "Using %d remote proc address cells for parsing mapping",
+ rproc_addr_cells);
+ dev_dbg(dev,
+ "Using %d of size %d parent address cells for parsing mapping",
+ addr_cells, size_cells);
+
+ cells_for_array_element = addr_cells + rproc_addr_cells + size_cells;
+
+ cnt = of_property_count_elems_of_size(np, of_rproc_ranges,
+ cells_for_array_element);
+ cnt /= sizeof(u32);
+
+ if (cnt <= 0) {
+ dev_info(dev, "No remoteproc memory mapping ranges found.");
+
+ return 0;
+ }
+
+ name_cnt = of_property_count_strings(np, of_rproc_ranges_names);
+ if (name_cnt > 0 && name_cnt != cnt) {
+ dev_err(dev, "Mismatch length for %s and %s", of_rproc_ranges,
+ of_rproc_ranges_names);
+
+ return -EINVAL;
+ }
+
+ mem_map = devm_kcalloc(dev, cnt, sizeof(*mem_map), GFP_KERNEL);
+ if (!mem_map)
+ return -ENOMEM;
+
+ rproc_ranges = of_get_property(np, of_rproc_ranges, &len);
+
+ for (i = 0; i < cnt; i++) {
+ struct resource *r;
+ const char *name = NULL;
+ int n;
+
+ of_property_read_string_index(np, of_rproc_ranges_names, i,
+ &name);
+ mem_map[i].name = name;
+ n = i * cells_for_array_element;
+ mem_map[i].rproc_addr =
+ of_read_number(&rproc_ranges[n + addr_cells],
+ rproc_addr_cells);
+ mem_map[i].size =
+ of_read_number(&rproc_ranges[n + addr_cells +
+ rproc_addr_cells],
+ size_cells);
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!r) {
+ dev_err(&pdev->dev, "Failed to get '%s' resource.\n",
+ name);
+
+ return -EINVAL;
+ }
+
+ mem_map[i].vaddr = devm_ioremap_wc(dev, r->start,
+ mem_map[i].size);
+ if (IS_ERR(mem_map[i].vaddr)) {
+ dev_err(dev, "Failed to remap '%s'", name);
+
+ return PTR_ERR(mem_map[i].vaddr);
+ }
+
+ dev_dbg(dev,
+ "rproc memory mapping[%i]=%s: da %llx, va, %pa, size %zx:\n",
+ i, name, mem_map[i].rproc_addr, &mem_map[i].vaddr,
+ mem_map[i].size);
+ }
+
+ ethosu_rproc->map = mem_map;
+ ethosu_rproc->map_size = cnt;
+ dev_dbg(dev, "rproc memory mapped %zx regions", ethosu_rproc->map_size);
+
+ return ret;
+}
+
+static const struct of_device_id ethosu_rproc_match[] = {
+ { .compatible = "arm,ethosu-sgm775-rproc" },
+};
+
+static int ethosu_rproc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct ethosu_rproc *ethosu_rproc;
+ struct rproc *rproc;
+ int ret = -ENODEV;
+
+ rproc = rproc_alloc(dev, np->name, &ethosu_rproc_ops,
+ fw_filename_param,
+ sizeof(*ethosu_rproc));
+ if (!rproc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Configure rproc */
+ rproc->has_iommu = false;
+ rproc->auto_boot = auto_boot;
+
+ platform_set_drvdata(pdev, rproc);
+
+ ethosu_rproc = rproc->priv;
+ ethosu_rproc->dev = dev;
+
+ /* Get the reset handler for the subsystem */
+ ethosu_rproc->rstc = devm_reset_control_get_exclusive_by_index(dev, 0);
+ if (IS_ERR(ethosu_rproc->rstc)) {
+ dev_err(&pdev->dev, "Failed to get reset controller.\n");
+ ret = PTR_ERR(ethosu_rproc->rstc);
+ goto free_rproc;
+ }
+
+ /* Get the translation from device memory to kernel space */
+ ret = ethosu_rproc_of_memory_translations(pdev, ethosu_rproc);
+ if (ret)
+ goto free_rproc;
+
+ ret = rproc_add(rproc);
+
+free_rproc:
+ if (ret)
+ rproc_free(rproc);
+
+out:
+
+ return ret;
+}
+
+static int ethosu_rproc_remove(struct platform_device *pdev)
+{
+ struct rproc *rproc = platform_get_drvdata(pdev);
+
+ rproc_del(rproc);
+ rproc_free(rproc);
+
+ return 0;
+}
+
+static struct platform_driver ethosu_rproc_driver = {
+ .probe = ethosu_rproc_probe,
+ .remove = ethosu_rproc_remove,
+ .driver = {
+ .name = "ethosu-rproc",
+ .of_match_table = of_match_ptr(ethosu_rproc_match),
+ },
+};
+
+module_platform_driver(ethosu_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Arm Ltd");
+MODULE_DESCRIPTION("Arm Ethos-U NPU RemoteProc Driver");
+MODULE_VERSION(ETHOSU_RPROC_DRIVER_VERSION);
diff --git a/remoteproc/remoteproc.dtsi b/remoteproc/remoteproc.dtsi
index 9f1a21c..2e38e17 100644
--- a/remoteproc/remoteproc.dtsi
+++ b/remoteproc/remoteproc.dtsi
@@ -27,5 +27,20 @@
reg = <0x0 0x500f0000 0x0 0x00001000>;
};
+ /* configuration for the remote proc driver. */
+ ethosu_cpu {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ range;
+ compatible ="arm,ethosu-sgm775-rproc";
+ reg = <0x0 0x50100000 0x0 0x00100000>,
+ <0x0 0x84000000 0x0 0x00100000>;
+ reg-names = "rom", "shared";
+ resets = <&ethosu_reset 0>;
+ #ethosu,rproc-address-cells = <1>;
+ ethosu,rproc-ranges = <0x0 0x50100000 0x00000000 0x0 0x00100000
+ 0x0 0x84000000 0x64000000 0x0 0x00100000>;
+ ethosu,rproc-names = "rom", "shared";
+ };
};