aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikael Olsson <mikael.olsson@arm.com>2021-02-12 17:30:22 +0100
committerManish Pandey <manish.pandey2@arm.com>2021-04-20 15:42:18 +0200
commit76a21174d2a2f1750b13b2680461b6b80a654848 (patch)
tree0bc0b27fa431a31ae7912aaee38debc8c5f48433
parent5d5fb10f9cfe105fd1e160fdfc8bb77bd7492799 (diff)
downloadtrusted-firmware-a-76a21174d2a2f1750b13b2680461b6b80a654848.tar.gz
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
-rw-r--r--docs/plat/arm/arm-build-options.rst8
-rw-r--r--drivers/arm/ethosn/ethosn_smc.c164
-rw-r--r--fdts/juno-ethosn.dtsi25
-rw-r--r--fdts/juno.dts4
-rw-r--r--include/drivers/arm/ethosn.h61
-rw-r--r--include/plat/arm/common/arm_sip_svc.h8
-rw-r--r--include/plat/arm/common/fconf_ethosn_getter.h35
-rw-r--r--plat/arm/common/arm_common.mk25
-rw-r--r--plat/arm/common/arm_sip_svc.c21
-rw-r--r--plat/arm/common/fconf/fconf_ethosn_getter.c108
10 files changed, 452 insertions, 7 deletions
diff --git a/docs/plat/arm/arm-build-options.rst b/docs/plat/arm/arm-build-options.rst
index a1d2313575..2aa973890a 100644
--- a/docs/plat/arm/arm-build-options.rst
+++ b/docs/plat/arm/arm-build-options.rst
@@ -91,6 +91,12 @@ Arm Platform Build Options
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 @@ Arm CSS Platform-Specific Build Options
--------------
-*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 0000000000..299d07c02b
--- /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 0000000000..87ab378a2d
--- /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 b628d1809e..56fe1676f6 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 0000000000..6de2abb85f
--- /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 85fdb289f1..2eeed95f02 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 0000000000..0fd1f025a8
--- /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 a225b40529..232d562420 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 @@ ifeq (${ARM_LINUX_KERNEL_AS_BL33},1)
$(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 @@ BL31_SOURCES += plat/arm/common/arm_bl31_setup.c \
plat/arm/common/arm_topology.c \
plat/common/plat_psci_common.c
-ifeq (${ENABLE_PMF}, 1)
+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 (${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 9f5d4557c6..6456c78411 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 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid,
{
int call_count = 0;
+#if ENABLE_PMF
+
/*
* Dispatch PMF calls to PMF SMC handler and return its return
* value
@@ -59,6 +62,8 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid,
handle, flags);
}
+#endif /* ENABLE_PMF */
+
#if USE_DEBUGFS
if (is_debugfs_fid(smc_fid)) {
@@ -68,6 +73,15 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid,
#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 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid,
/* 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 0000000000..1ba9f3a23a
--- /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);