feat(drivers/arm/ethosn)!: multi-device support

Add support for Arm Ethos-N NPU multi-device.

The device tree parsing currently only supports one NPU device with
multiple cores. To be able to support multi-device NPU configurations
this patch adds support for having multiple NPU devices in the device
tree.

To be able to support multiple NPU devices in the SMC API, it has been
changed in an incompatible way so the API version has been bumped.

Signed-off-by: Laurent Carlier <laurent.carlier@arm.com>
Change-Id: Ide279ce949bd06e8939268b9601c267e45f3edc3
diff --git a/plat/arm/common/fconf/fconf_ethosn_getter.c b/plat/arm/common/fconf/fconf_ethosn_getter.c
index 1ba9f3a..0af1a20 100644
--- a/plat/arm/common/fconf/fconf_ethosn_getter.c
+++ b/plat/arm/common/fconf/fconf_ethosn_getter.c
@@ -12,7 +12,7 @@
 #include <libfdt.h>
 #include <plat/arm/common/fconf_ethosn_getter.h>
 
-struct ethosn_config_t ethosn_config;
+struct ethosn_config_t ethosn_config = {.num_cores = 0};
 
 static uint8_t fdt_node_get_status(const void *fdt, int node)
 {
@@ -33,74 +33,86 @@
 int fconf_populate_ethosn_config(uintptr_t config)
 {
 	int ethosn_node;
-	int sub_node;
-	uint8_t ethosn_status;
-	uint32_t core_count = 0U;
-	uint32_t core_addr_idx = 0U;
 	const void *hw_conf_dtb = (const void *)config;
 
 	/* Find offset to node with 'ethosn' compatible property */
-	ethosn_node = fdt_node_offset_by_compatible(hw_conf_dtb, -1, "ethosn");
-	if (ethosn_node < 0) {
-		ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n");
-		return ethosn_node;
-	}
-
-	/* If the Arm Ethos-N NPU is disabled the core check can be skipped */
-	ethosn_status = fdt_node_get_status(hw_conf_dtb, ethosn_node);
-	if (ethosn_status == ETHOSN_STATUS_DISABLED) {
-		return 0;
-	}
+	INFO("Probing Arm Ethos-N NPU\n");
+	uint32_t total_core_count = 0U;
 
-	fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) {
-		int err;
-		uintptr_t addr;
-		uint8_t status;
+	fdt_for_each_compatible_node(hw_conf_dtb, ethosn_node, "ethosn") {
+		int sub_node;
+		uint8_t ethosn_status;
+		uint32_t device_core_count = 0U;
 
-		/* Check that the sub node is "ethosn-core" compatible */
-		if (fdt_node_check_compatible(hw_conf_dtb, sub_node,
-					      "ethosn-core") != 0) {
-			/* Ignore incompatible sub node */
+		/* If the Arm Ethos-N NPU is disabled the core check can be skipped */
+		ethosn_status = fdt_node_get_status(hw_conf_dtb, ethosn_node);
+		if (ethosn_status == ETHOSN_STATUS_DISABLED) {
 			continue;
 		}
 
-		/* Including disabled cores */
-		if (core_addr_idx >= ETHOSN_CORE_NUM_MAX) {
-			ERROR("FCONF: Reached max number of Arm Ethos-N NPU cores\n");
-			return -1;
-		}
+		fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) {
+			int err;
+			uintptr_t core_addr;
+			uint8_t core_status;
 
-		status = fdt_node_get_status(hw_conf_dtb, ethosn_node);
-		if (status == ETHOSN_STATUS_DISABLED) {
-			++core_addr_idx;
-			continue;
+			if (total_core_count >= ETHOSN_CORE_NUM_MAX) {
+				ERROR("FCONF: Reached max number of Arm Ethos-N NPU cores\n");
+				return -FDT_ERR_BADSTRUCTURE;
+			}
+
+			/* Check that the sub node is "ethosn-core" compatible */
+			if (fdt_node_check_compatible(hw_conf_dtb,
+						      sub_node,
+						      "ethosn-core") != 0) {
+				/* Ignore incompatible sub node */
+				continue;
+			}
+
+			core_status = fdt_node_get_status(hw_conf_dtb, sub_node);
+			if (core_status == ETHOSN_STATUS_DISABLED) {
+				continue;
+			}
+
+			err = fdt_get_reg_props_by_index(hw_conf_dtb,
+							 ethosn_node,
+							 device_core_count,
+							 &core_addr,
+							 NULL);
+			if (err < 0) {
+				ERROR(
+				"FCONF: Failed to read reg property for Arm Ethos-N NPU core %u\n",
+						device_core_count);
+				return err;
+			}
+
+			INFO("NPU core probed at address 0x%lx\n", core_addr);
+			ethosn_config.core[total_core_count].addr = core_addr;
+			total_core_count++;
+			device_core_count++;
 		}
 
-		err = fdt_get_reg_props_by_index(hw_conf_dtb, ethosn_node,
-						 core_addr_idx, &addr, NULL);
-		if (err < 0) {
-			ERROR("FCONF: Failed to read reg property for Arm Ethos-N NPU core %u\n",
-			      core_addr_idx);
-			return err;
+		if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
+			ERROR("FCONF: Failed to parse sub nodes\n");
+			return -FDT_ERR_BADSTRUCTURE;
 		}
 
-		ethosn_config.core_addr[core_count++] = addr;
-		++core_addr_idx;
+		if (device_core_count == 0U) {
+			ERROR(
+			"FCONF: Enabled Arm Ethos-N NPU device must have at least one enabled core\n");
+			return -FDT_ERR_BADSTRUCTURE;
+		}
 	}
 
-	if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
-		ERROR("FCONF: Failed to parse sub nodes\n");
-		return sub_node;
+	if (total_core_count == 0U) {
+		ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n");
+		return -FDT_ERR_BADSTRUCTURE;
 	}
 
-	/* The Arm Ethos-N NPU can't be used if no cores were found */
-	if (core_count == 0) {
-		ERROR("FCONF: No Arm Ethos-N NPU cores found\n");
-		return -1;
-	}
+	ethosn_config.num_cores = total_core_count;
 
-	ethosn_config.num_cores = core_count;
-	ethosn_config.status = ethosn_status;
+	INFO("%d NPU core%s probed\n",
+	     ethosn_config.num_cores,
+	     ethosn_config.num_cores > 1 ? "s" : "");
 
 	return 0;
 }