Add resource table entry for address translation

To avoid having to use a hardcoded memory map to translate between the
Linux physical addresses to device addresses in the firmware, a new
entry type has been added to the resource table that allows the NPU
remoteproc driver to provide the memory map to the firmware.

With this change, the device tree layout has been changed to wrap the
ethosu-rproc node with an ethosu parent node that represents the NPU
subsystem. This makes it possible to use the APIs provided by the Linux
kernel to perform address translation etc.

The "ethosu,dma-ranges" property has been removed from ethosu-proc
because the ethosu parent node provides information for how to translate
between the Linux Kernel physical addresses and device addresses with a
"dma-ranges" property.

Change-Id: I48719ee808a5e9391c5a3e967042d26dd92d5026
Signed-off-by: Mikael Olsson <mikael.olsson@arm.com>
diff --git a/remoteproc/ethosu_remoteproc.c b/remoteproc/ethosu_remoteproc.c
index b1f8793..0369156 100644
--- a/remoteproc/ethosu_remoteproc.c
+++ b/remoteproc/ethosu_remoteproc.c
@@ -1,5 +1,6 @@
 /*
- * Copyright 2021-2023 Arm Limited and/or its affiliates
+ * SPDX-FileCopyrightText: Copyright 2021-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ * SPDX-License-Identifier: GPL-2.0-only
  *
  * 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
@@ -14,10 +15,10 @@
  * 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/dma-mapping.h>
+#include <linux/dma-direct.h>
 #include <linux/firmware.h>
 #include <linux/io.h>
 #include <linux/irqreturn.h>
@@ -58,6 +59,35 @@
 module_param(auto_boot, bool, 0);
 MODULE_PARM_DESC(auto_boot, "Set to one to auto boot at load.");
 
+#define RSC_MAPPING RSC_VENDOR_START + 1
+
+/**
+ * struct fw_rsc_map_range - memory map range
+ * @da:		Start device address of the memory address range
+ * @pa:		Start physical address of the memory address range
+ * @len:	length of memory address range
+ *
+ * Memory range to translate between physical and device addresses.
+ */
+struct fw_rsc_map_range {
+	uint32_t da;
+	uint32_t pa;
+	uint32_t len;
+} __packed;
+
+/**
+ * struct fw_rsc_mapping - memory map for address translation
+ * @num_ranges:	Number of ranges in the memory map
+ * @range:	Array of the ranges in the memory map
+ *
+ * This resource entry requests the host to provide information for how to
+ * translate between physical and device addresses.
+ */
+struct fw_rsc_mapping {
+	uint8_t                 num_ranges;
+	struct fw_rsc_map_range range[0];
+} __packed;
+
 struct ethosu_rproc {
 	struct device           *dev;
 	struct reset_control    *rstc;
@@ -92,47 +122,6 @@
 	queue_work(erproc->wq, &erproc->work);
 }
 
-static dma_addr_t ethosu_of_pa_to_da(struct rproc *rproc,
-				     const phys_addr_t pa,
-				     const size_t size)
-{
-	static const char of_rproc_ranges[] = "ethosu,dma-ranges";
-
-	struct device *dev = rproc->dev.parent;
-	struct device_node *np = dev->of_node;
-	const __be32 *rproc_ranges = of_get_property(np, of_rproc_ranges, NULL);
-	const int addr_cells = of_n_addr_cells(np);
-	const int size_cells = of_n_size_cells(np);
-	const int ranges_cells = addr_cells + addr_cells + size_cells;
-	int ranges_cnt;
-	int i;
-
-	ranges_cnt = of_property_count_elems_of_size(
-		np, of_rproc_ranges, ranges_cells * sizeof(u32));
-
-	for (i = 0; i < ranges_cnt; i++) {
-		const int offset = i * ranges_cells;
-
-		const uint64_t of_da = of_read_number(
-			&rproc_ranges[offset], addr_cells);
-		const uint64_t of_pa = of_read_number(
-			&rproc_ranges[offset + addr_cells], addr_cells);
-		const uint64_t of_size = of_read_number(
-			&rproc_ranges[offset + addr_cells + addr_cells],
-			size_cells);
-
-		if (pa >= of_pa && (pa + size) <= (of_pa + of_size)) {
-			const dma_addr_t da = of_da + pa - of_pa;
-
-			dev_dbg(dev, "PA to DA. pa=0x%llx, da=0x%llx", pa, da);
-
-			return da;
-		}
-	}
-
-	return (dma_addr_t)(-1);
-}
-
 static int ethosu_add_carveout(struct rproc *rproc,
 			       const phys_addr_t pa,
 			       const size_t size,
@@ -143,9 +132,10 @@
 	void __iomem *va;
 	struct rproc_mem_entry *mem;
 
-	da = ethosu_of_pa_to_da(rproc, pa, size);
-	if (da == (dma_addr_t)(-1)) {
-		dev_err(dev, "No mapping found for PA. pa=%llx, size=%zu", pa,
+	da = translate_phys_to_dma(dev, pa);
+	dev_dbg(dev, "PA to DA. pa=0x%pa, da=0x%pad", &pa, &da);
+	if (da == DMA_MAPPING_ERROR) {
+		dev_err(dev, "No mapping found for PA. pa=%pa, size=%zu", &pa,
 			size);
 
 		return -ENOMEM;
@@ -153,7 +143,7 @@
 
 	va = devm_ioremap_wc(dev, pa, size);
 	if (!va) {
-		dev_err(dev, "Failed to remap address. pa=%llx, len=%zu", pa,
+		dev_err(dev, "Failed to remap address. pa=%pa, len=%zu", &pa,
 			size);
 
 		return -ENOMEM;
@@ -163,8 +153,8 @@
 	if (!mem)
 		return -ENOMEM;
 
-	dev_info(dev, "Add carveout mapping. dma=%llx, da=%x, va=%p, len=%zu",
-		 mem->dma, mem->da, mem->va, mem->len);
+	dev_info(dev, "Add carveout mapping. dma=%pad, da=%x, va=%p, len=%zu",
+		 &mem->dma, mem->da, mem->va, mem->len);
 
 	rproc_add_carveout(rproc, mem);
 
@@ -247,11 +237,58 @@
 	mbox_send_message(erproc->ch_tx, (void *)&vqid);
 }
 
+static int ethosu_rproc_handle_rsc(struct rproc *rproc,
+				   u32 rsc_type,
+				   void *rsc,
+				   int offset,
+				   int avail)
+{
+	struct ethosu_rproc *erproc = (struct ethosu_rproc *)rproc->priv;
+	struct device *dev = erproc->dev;
+	struct fw_rsc_mapping *mapping = rsc;
+	const struct bus_dma_region *map;
+	size_t num_ranges = 0U;
+	size_t i;
+
+	if (rsc_type != RSC_MAPPING)
+		return RSC_IGNORED;
+
+	if (struct_size(mapping, range, mapping->num_ranges) > avail) {
+		dev_err(dev, "mapping rsc is truncated\n");
+
+		return -EINVAL;
+	}
+
+	for (map = dev->dma_range_map; map->size; ++map)
+		num_ranges++;
+
+	if (num_ranges > mapping->num_ranges) {
+		dev_err(dev,
+			"Mapping rsc doesn't have enough room for DMA ranges\n");
+
+		return -EINVAL;
+	}
+
+	for (i = 0U; i < num_ranges; ++i) {
+		map = &dev->dma_range_map[i];
+		struct fw_rsc_map_range *range = &mapping->range[i];
+
+		range->da = map->dma_start;
+		range->pa = map->cpu_start;
+		range->len = map->size;
+	}
+
+	dev_dbg(dev, "handle_rsc: Mapping rsc setup");
+
+	return RSC_HANDLED;
+}
+
 static const struct rproc_ops ethosu_rproc_ops = {
-	.prepare = &ethosu_rproc_prepare,
-	.start   = &ethosu_rproc_start,
-	.stop    = &ethosu_rproc_stop,
-	.kick    = &ethosu_rproc_kick,
+	.prepare    = &ethosu_rproc_prepare,
+	.start      = &ethosu_rproc_start,
+	.stop       = &ethosu_rproc_stop,
+	.kick       = &ethosu_rproc_kick,
+	.handle_rsc = &ethosu_rproc_handle_rsc,
 };
 
 static int ethosu_mailbox_init(struct ethosu_rproc *erproc)