Add SiP service to configure Arm Ethos-N NPU

By default the Arm Ethos-N NPU will boot up in secure mode. In this mode
the non-secure world cannot access the registers needed to use the NPU.
To still allow the non-secure world to use the NPU, a SiP service has
been added that can delegate non-secure access to the registers needed
to use it.

Only the HW_CONFIG for the Arm Juno platform has been updated to include
the device tree for the NPU and the platform currently only loads the
HW_CONFIG in AArch64 builds.

Signed-off-by: Mikael Olsson <mikael.olsson@arm.com>
Change-Id: I65dfd864042ed43faae0a259dcf319cbadb5f3d2
diff --git a/docs/plat/arm/arm-build-options.rst b/docs/plat/arm/arm-build-options.rst
index a1d2313..2aa9738 100644
--- a/docs/plat/arm/arm-build-options.rst
+++ b/docs/plat/arm/arm-build-options.rst
@@ -91,6 +91,12 @@
    platforms. If this option is specified, then the path to the CryptoCell
    SBROM library must be specified via ``CCSBROM_LIB_PATH`` flag.
 
+-  ``ARM_ETHOSN_NPU_DRIVER``: boolean option to enable a SiP service that can
+   configure an Arm Ethos-N NPU. To use this service the target platform's
+   ``HW_CONFIG`` must include the device tree nodes for the NPU. Currently, only
+   the Arm Juno platform has this included in its ``HW_CONFIG`` and the platform
+   only loads the ``HW_CONFIG`` in AArch64 builds. Default is 0.
+
 -  ``ARM_SPMC_MANIFEST_DTS`` : path to an alternate manifest file used as the
    SPMC Core manifest. Valid when ``SPD=spmd`` is selected.
 
@@ -128,4 +134,4 @@
 
 --------------
 
-*Copyright (c) 2019-2020, Arm Limited. All rights reserved.*
+*Copyright (c) 2019-2021, Arm Limited. All rights reserved.*
diff --git a/drivers/arm/ethosn/ethosn_smc.c b/drivers/arm/ethosn/ethosn_smc.c
new file mode 100644
index 0000000..299d07c
--- /dev/null
+++ b/drivers/arm/ethosn/ethosn_smc.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <common/debug.h>
+#include <common/runtime_svc.h>
+#include <drivers/arm/ethosn.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+#include <plat/arm/common/fconf_ethosn_getter.h>
+
+/* Arm Ethos-N NPU (NPU) status */
+#define ETHOSN_STATUS \
+	FCONF_GET_PROPERTY(hw_config, ethosn_config, status)
+
+/* Number of NPU cores available */
+#define ETHOSN_NUM_CORES \
+	FCONF_GET_PROPERTY(hw_config, ethosn_config, num_cores)
+
+/* Address to an NPU core  */
+#define ETHOSN_CORE_ADDR(core_idx) \
+	FCONF_GET_PROPERTY(hw_config, ethosn_core_addr, core_idx)
+
+/* NPU core sec registry address */
+#define ETHOSN_CORE_SEC_REG(core_addr, reg_offset) \
+	(core_addr + reg_offset)
+
+/* Reset timeout in us */
+#define ETHOSN_RESET_TIMEOUT_US		U(10 * 1000 * 1000)
+#define ETHOSN_RESET_WAIT_US		U(1)
+
+#define SEC_DEL_REG			U(0x0004)
+#define SEC_DEL_VAL			U(0x81C)
+#define SEC_DEL_EXCC_MASK		U(0x20)
+
+#define SEC_SECCTLR_REG			U(0x0010)
+#define SEC_SECCTLR_VAL			U(0x3)
+
+#define SEC_DEL_MMUSID_REG		U(0x2008)
+#define SEC_DEL_MMUSID_VAL		U(0x3FFFF)
+
+#define SEC_DEL_ADDR_EXT_REG		U(0x201C)
+#define SEC_DEL_ADDR_EXT_VAL		U(0x15)
+
+#define SEC_SYSCTRL0_REG		U(0x0018)
+#define SEC_SYSCTRL0_SOFT_RESET		U(3U << 29)
+#define SEC_SYSCTRL0_HARD_RESET		U(1U << 31)
+
+static void ethosn_delegate_to_ns(uintptr_t core_addr)
+{
+	mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_SECCTLR_REG),
+			SEC_SECCTLR_VAL);
+
+	mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_REG),
+			SEC_DEL_VAL);
+
+	mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_MMUSID_REG),
+			SEC_DEL_MMUSID_VAL);
+
+	mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_ADDR_EXT_REG),
+			SEC_DEL_ADDR_EXT_VAL);
+}
+
+static int ethosn_is_sec(void)
+{
+	if ((mmio_read_32(ETHOSN_CORE_SEC_REG(ETHOSN_CORE_ADDR(0), SEC_DEL_REG))
+		& SEC_DEL_EXCC_MASK) != 0U) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static bool ethosn_reset(uintptr_t core_addr, int hard_reset)
+{
+	unsigned int timeout;
+	const uintptr_t sysctrl0_reg =
+		ETHOSN_CORE_SEC_REG(core_addr, SEC_SYSCTRL0_REG);
+	const uint32_t reset_val = (hard_reset != 0) ? SEC_SYSCTRL0_HARD_RESET
+						    : SEC_SYSCTRL0_SOFT_RESET;
+
+	mmio_write_32(sysctrl0_reg, reset_val);
+
+	/* Wait for reset to complete */
+	for (timeout = 0U; timeout < ETHOSN_RESET_TIMEOUT_US;
+			   timeout += ETHOSN_RESET_WAIT_US) {
+
+		if ((mmio_read_32(sysctrl0_reg) & reset_val) == 0U) {
+			break;
+		}
+
+		udelay(ETHOSN_RESET_WAIT_US);
+	}
+
+	return timeout < ETHOSN_RESET_TIMEOUT_US;
+}
+
+uintptr_t ethosn_smc_handler(uint32_t smc_fid,
+			     u_register_t core_idx,
+			     u_register_t x2,
+			     u_register_t x3,
+			     u_register_t x4,
+			     void *cookie,
+			     void *handle,
+			     u_register_t flags)
+{
+	uintptr_t core_addr;
+	int hard_reset = 0;
+
+	/* Only SiP fast calls are expected */
+	if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) ||
+		(GET_SMC_OEN(smc_fid) != OEN_SIP_START)) {
+		SMC_RET1(handle, SMC_UNK);
+	}
+
+	/* Truncate parameters to 32-bits for SMC32 */
+	if (GET_SMC_CC(smc_fid) == SMC_32) {
+		core_idx &= 0xFFFFFFFF;
+		x2 &= 0xFFFFFFFF;
+		x3 &= 0xFFFFFFFF;
+		x4 &= 0xFFFFFFFF;
+	}
+
+	if (!is_ethosn_fid(smc_fid)) {
+		SMC_RET1(handle, SMC_UNK);
+	}
+
+	if (ETHOSN_STATUS == ETHOSN_STATUS_DISABLED) {
+		WARN("ETHOSN: Arm Ethos-N NPU not available\n");
+		SMC_RET1(handle, ETHOSN_NOT_SUPPORTED);
+	}
+
+	switch (smc_fid & FUNCID_NUM_MASK) {
+	case ETHOSN_FNUM_VERSION:
+		SMC_RET2(handle, ETHOSN_VERSION_MAJOR, ETHOSN_VERSION_MINOR);
+	case ETHOSN_FNUM_IS_SEC:
+		SMC_RET1(handle, ethosn_is_sec());
+	case ETHOSN_FNUM_HARD_RESET:
+		hard_reset = 1;
+		/* Fallthrough */
+	case ETHOSN_FNUM_SOFT_RESET:
+		if (core_idx >= ETHOSN_NUM_CORES) {
+			WARN("ETHOSN: core index out of range\n");
+			SMC_RET1(handle, ETHOSN_CORE_IDX_OUT_OF_RANGE);
+		}
+
+		core_addr = ETHOSN_CORE_ADDR(core_idx);
+
+		if (!ethosn_reset(core_addr, hard_reset)) {
+			SMC_RET1(handle, ETHOSN_FAILURE);
+		}
+
+		ethosn_delegate_to_ns(core_addr);
+
+		SMC_RET1(handle, ETHOSN_SUCCESS);
+	default:
+		SMC_RET1(handle, SMC_UNK);
+	}
+}
diff --git a/fdts/juno-ethosn.dtsi b/fdts/juno-ethosn.dtsi
new file mode 100644
index 0000000..87ab378
--- /dev/null
+++ b/fdts/juno-ethosn.dtsi
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	ethosn: ethosn@6f300000 {
+		compatible = "ethosn";
+		reg = <0 0x6f300000 0 0x00100000>;
+		status = "okay";
+
+		/*
+		 * Single-core NPU. For multi-core NPU, additional core nodes
+		 * and reg values must be added.
+		 */
+		core0 {
+			compatible = "ethosn-core";
+			status = "okay";
+		};
+	};
+};
diff --git a/fdts/juno.dts b/fdts/juno.dts
index b628d18..56fe167 100644
--- a/fdts/juno.dts
+++ b/fdts/juno.dts
@@ -9,3 +9,7 @@
 / {
 
 };
+
+#if ARM_ETHOSN_NPU_DRIVER
+	#include "juno-ethosn.dtsi"
+#endif
diff --git a/include/drivers/arm/ethosn.h b/include/drivers/arm/ethosn.h
new file mode 100644
index 0000000..6de2abb
--- /dev/null
+++ b/include/drivers/arm/ethosn.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ETHOSN_H
+#define ETHOSN_H
+
+#include <lib/smccc.h>
+
+/* Function numbers */
+#define ETHOSN_FNUM_VERSION		U(0x50)
+#define ETHOSN_FNUM_IS_SEC		U(0x51)
+#define ETHOSN_FNUM_HARD_RESET		U(0x52)
+#define ETHOSN_FNUM_SOFT_RESET		U(0x53)
+/* 0x54-0x5F reserved for future use */
+
+/* SMC64 function IDs */
+#define ETHOSN_FID_64(func_num)		U(0xC2000000 | func_num)
+#define ETHOSN_FID_VERSION_64		ETHOSN_FID_64(ETHOSN_FNUM_VERSION)
+#define ETHOSN_FID_IS_SEC_64		ETHOSN_FID_64(ETHOSN_FNUM_IS_SEC)
+#define ETHOSN_FID_HARD_RESET_64	ETHOSN_FID_64(ETHOSN_FNUM_HARD_RESET)
+#define ETHOSN_FID_SOFT_RESET_64	ETHOSN_FID_64(ETHOSN_FNUM_SOFT_RESET)
+
+/* SMC32 function IDs */
+#define ETHOSN_FID_32(func_num)		U(0x82000000 | func_num)
+#define ETHOSN_FID_VERSION_32		ETHOSN_FID_32(ETHOSN_FNUM_VERSION)
+#define ETHOSN_FID_IS_SEC_32		ETHOSN_FID_32(ETHOSN_FNUM_IS_SEC)
+#define ETHOSN_FID_HARD_RESET_32	ETHOSN_FID_32(ETHOSN_FNUM_HARD_RESET)
+#define ETHOSN_FID_SOFT_RESET_32	ETHOSN_FID_32(ETHOSN_FNUM_SOFT_RESET)
+
+#define ETHOSN_NUM_SMC_CALLS	8
+
+/* Macro to identify function calls */
+#define ETHOSN_FID_MASK		U(0xFFF0)
+#define ETHOSN_FID_VALUE	U(0x50)
+#define is_ethosn_fid(_fid) (((_fid) & ETHOSN_FID_MASK) == ETHOSN_FID_VALUE)
+
+/* Service version  */
+#define ETHOSN_VERSION_MAJOR U(0)
+#define ETHOSN_VERSION_MINOR U(1)
+
+/* Return codes for function calls */
+#define ETHOSN_SUCCESS			 0
+#define ETHOSN_NOT_SUPPORTED		-1
+/* -2 Reserved for NOT_REQUIRED */
+/* -3 Reserved for INVALID_PARAMETER */
+#define ETHOSN_FAILURE			-4
+#define ETHOSN_CORE_IDX_OUT_OF_RANGE	-5
+
+uintptr_t ethosn_smc_handler(uint32_t smc_fid,
+			     u_register_t core_idx,
+			     u_register_t x2,
+			     u_register_t x3,
+			     u_register_t x4,
+			     void *cookie,
+			     void *handle,
+			     u_register_t flags);
+
+#endif  /* ETHOSN_H */
diff --git a/include/plat/arm/common/arm_sip_svc.h b/include/plat/arm/common/arm_sip_svc.h
index 85fdb28..2eeed95 100644
--- a/include/plat/arm/common/arm_sip_svc.h
+++ b/include/plat/arm/common/arm_sip_svc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2019,2021, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -25,6 +25,12 @@
 /* DEBUGFS_SMC_32			0x82000030U */
 /* DEBUGFS_SMC_64			0xC2000030U */
 
+/*
+ * Arm Ethos-N NPU SiP SMC function IDs
+ * 0xC2000050-0xC200005F
+ * 0x82000050-0x8200005F
+ */
+
 /* ARM SiP Service Calls version numbers */
 #define ARM_SIP_SVC_VERSION_MAJOR		U(0x0)
 #define ARM_SIP_SVC_VERSION_MINOR		U(0x2)
diff --git a/include/plat/arm/common/fconf_ethosn_getter.h b/include/plat/arm/common/fconf_ethosn_getter.h
new file mode 100644
index 0000000..0fd1f02
--- /dev/null
+++ b/include/plat/arm/common/fconf_ethosn_getter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FCONF_ETHOSN_GETTER_H
+#define FCONF_ETHOSN_GETTER_H
+
+#include <assert.h>
+
+#include <lib/fconf/fconf.h>
+
+#define hw_config__ethosn_config_getter(prop) ethosn_config.prop
+#define hw_config__ethosn_core_addr_getter(idx) __extension__ ({	\
+	assert(idx < ethosn_config.num_cores);				\
+	ethosn_config.core_addr[idx];					\
+})
+
+#define ETHOSN_STATUS_DISABLED U(0)
+#define ETHOSN_STATUS_ENABLED  U(1)
+
+#define ETHOSN_CORE_NUM_MAX U(64)
+
+struct ethosn_config_t {
+	uint8_t status;
+	uint32_t num_cores;
+	uint64_t core_addr[ETHOSN_CORE_NUM_MAX];
+};
+
+int fconf_populate_arm_ethosn(uintptr_t config);
+
+extern struct ethosn_config_t ethosn_config;
+
+#endif /* FCONF_ETHOSN_GETTER_H */
diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk
index a225b40..232d562 100644
--- a/plat/arm/common/arm_common.mk
+++ b/plat/arm/common/arm_common.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -100,6 +100,11 @@
   $(eval $(call add_define,ARM_PRELOADED_DTB_BASE))
 endif
 
+# Arm Ethos-N NPU SiP service
+ARM_ETHOSN_NPU_DRIVER			:=	0
+$(eval $(call assert_boolean,ARM_ETHOSN_NPU_DRIVER))
+$(eval $(call add_define,ARM_ETHOSN_NPU_DRIVER))
+
 # Use an implementation of SHA-256 with a smaller memory footprint but reduced
 # speed.
 $(eval $(call add_define,MBEDTLS_SHA256_SMALLER))
@@ -248,14 +253,26 @@
 				plat/arm/common/arm_topology.c			\
 				plat/common/plat_psci_common.c
 
+ifneq ($(filter 1,${ENABLE_PMF} ${ARM_ETHOSN_NPU_DRIVER}),)
+ARM_SVC_HANDLER_SRCS :=
+
+ifeq (${ENABLE_PMF},1)
+ARM_SVC_HANDLER_SRCS	+=	lib/pmf/pmf_smc.c
+endif
+
+ifeq (${ARM_ETHOSN_NPU_DRIVER},1)
+ARM_SVC_HANDLER_SRCS	+=	plat/arm/common/fconf/fconf_ethosn_getter.c	\
+				drivers/delay_timer/delay_timer.c		\
+				drivers/arm/ethosn/ethosn_smc.c
+endif
+
-ifeq (${ENABLE_PMF}, 1)
 ifeq (${ARCH}, aarch64)
 BL31_SOURCES		+=	plat/arm/common/aarch64/execution_state_switch.c\
 				plat/arm/common/arm_sip_svc.c			\
-				lib/pmf/pmf_smc.c
+				${ARM_SVC_HANDLER_SRCS}
 else
 BL32_SOURCES		+=	plat/arm/common/arm_sip_svc.c			\
-				lib/pmf/pmf_smc.c
+				${ARM_SVC_HANDLER_SRCS}
 endif
 endif
 
diff --git a/plat/arm/common/arm_sip_svc.c b/plat/arm/common/arm_sip_svc.c
index 9f5d455..6456c78 100644
--- a/plat/arm/common/arm_sip_svc.c
+++ b/plat/arm/common/arm_sip_svc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2019,2021, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -8,6 +8,7 @@
 
 #include <common/debug.h>
 #include <common/runtime_svc.h>
+#include <drivers/arm/ethosn.h>
 #include <lib/debugfs.h>
 #include <lib/pmf/pmf.h>
 #include <plat/arm/common/arm_sip_svc.h>
@@ -50,6 +51,8 @@
 {
 	int call_count = 0;
 
+#if ENABLE_PMF
+
 	/*
 	 * Dispatch PMF calls to PMF SMC handler and return its return
 	 * value
@@ -59,6 +62,8 @@
 				handle, flags);
 	}
 
+#endif /* ENABLE_PMF */
+
 #if USE_DEBUGFS
 
 	if (is_debugfs_fid(smc_fid)) {
@@ -68,6 +73,15 @@
 
 #endif /* USE_DEBUGFS */
 
+#if ARM_ETHOSN_NPU_DRIVER
+
+	if (is_ethosn_fid(smc_fid)) {
+		return ethosn_smc_handler(smc_fid, x1, x2, x3, x4, cookie,
+					  handle, flags);
+	}
+
+#endif /* ARM_ETHOSN_NPU_DRIVER */
+
 	switch (smc_fid) {
 	case ARM_SIP_SVC_EXE_STATE_SWITCH: {
 		/* Execution state can be switched only if EL3 is AArch64 */
@@ -92,6 +106,11 @@
 		/* PMF calls */
 		call_count += PMF_NUM_SMC_CALLS;
 
+#if ARM_ETHOSN_NPU_DRIVER
+		/* ETHOSN calls */
+		call_count += ETHOSN_NUM_SMC_CALLS;
+#endif /* ARM_ETHOSN_NPU_DRIVER */
+
 		/* State switch call */
 		call_count += 1;
 
diff --git a/plat/arm/common/fconf/fconf_ethosn_getter.c b/plat/arm/common/fconf/fconf_ethosn_getter.c
new file mode 100644
index 0000000..1ba9f3a
--- /dev/null
+++ b/plat/arm/common/fconf/fconf_ethosn_getter.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <common/fdt_wrappers.h>
+#include <libfdt.h>
+#include <plat/arm/common/fconf_ethosn_getter.h>
+
+struct ethosn_config_t ethosn_config;
+
+static uint8_t fdt_node_get_status(const void *fdt, int node)
+{
+	int len;
+	uint8_t status = ETHOSN_STATUS_DISABLED;
+	const char *node_status;
+
+	node_status = fdt_getprop(fdt, node, "status", &len);
+	if (node_status == NULL ||
+	    (len == 5 && /* Includes null character */
+	     strncmp(node_status, "okay", 4U) == 0)) {
+		status = ETHOSN_STATUS_ENABLED;
+	}
+
+	return status;
+}
+
+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;
+	}
+
+	fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) {
+		int err;
+		uintptr_t addr;
+		uint8_t status;
+
+		/* 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;
+		}
+
+		/* 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;
+		}
+
+		status = fdt_node_get_status(hw_conf_dtb, ethosn_node);
+		if (status == ETHOSN_STATUS_DISABLED) {
+			++core_addr_idx;
+			continue;
+		}
+
+		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;
+		}
+
+		ethosn_config.core_addr[core_count++] = addr;
+		++core_addr_idx;
+	}
+
+	if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
+		ERROR("FCONF: Failed to parse sub nodes\n");
+		return sub_node;
+	}
+
+	/* 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 = core_count;
+	ethosn_config.status = ethosn_status;
+
+	return 0;
+}
+
+FCONF_REGISTER_POPULATOR(HW_CONFIG, ethosn_config, fconf_populate_ethosn_config);