diff --git a/docs/plat/arm/arm-build-options.rst b/docs/plat/arm/arm-build-options.rst index afe89b936..407c04bbb 100644 --- a/docs/plat/arm/arm-build-options.rst +++ b/docs/plat/arm/arm-build-options.rst @@ -92,7 +92,7 @@ Arm Platform Build Options 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 + 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. diff --git a/drivers/arm/ethosn/ethosn_smc.c b/drivers/arm/ethosn/ethosn_smc.c index 60364cdb2..915a0d87a 100644 --- a/drivers/arm/ethosn/ethosn_smc.c +++ b/drivers/arm/ethosn/ethosn_smc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited. All rights reserved. + * Copyright (c) 2021-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,18 +12,17 @@ #include #include #include +#include #include /* - * Number of Arm Ethos-N NPU (NPU) cores available for a - * particular parent device + * Number of Arm(R) Ethos(TM)-N NPU (NPU) devices available */ -#define ETHOSN_NUM_CORES \ - FCONF_GET_PROPERTY(hw_config, ethosn_config, num_cores) +#define ETHOSN_NUM_DEVICES \ + FCONF_GET_PROPERTY(hw_config, ethosn_config, num_devices) -/* Address to an NPU core */ -#define ETHOSN_CORE_ADDR(core_idx) \ - FCONF_GET_PROPERTY(hw_config, ethosn_core_addr, core_idx) +#define ETHOSN_GET_DEVICE(dev_idx) \ + FCONF_GET_PROPERTY(hw_config, ethosn_device, dev_idx) /* NPU core sec registry address */ #define ETHOSN_CORE_SEC_REG(core_addr, reg_offset) \ @@ -40,9 +39,6 @@ #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) @@ -50,17 +46,63 @@ #define SEC_SYSCTRL0_SOFT_RESET U(3U << 29) #define SEC_SYSCTRL0_HARD_RESET U(1U << 31) -static bool ethosn_is_core_addr_valid(uintptr_t core_addr) +#define SEC_MMUSID_REG_BASE U(0x3008) +#define SEC_MMUSID_OFFSET U(0x1000) + +static bool ethosn_get_device_and_core(uintptr_t core_addr, + const struct ethosn_device_t **dev_match, + const struct ethosn_core_t **core_match) { - for (uint32_t core_idx = 0U; core_idx < ETHOSN_NUM_CORES; core_idx++) { - if (ETHOSN_CORE_ADDR(core_idx) == core_addr) { - return true; + uint32_t dev_idx; + uint32_t core_idx; + + for (dev_idx = 0U; dev_idx < ETHOSN_NUM_DEVICES; ++dev_idx) { + const struct ethosn_device_t *dev = ETHOSN_GET_DEVICE(dev_idx); + + for (core_idx = 0U; core_idx < dev->num_cores; ++core_idx) { + const struct ethosn_core_t *core = &(dev->cores[core_idx]); + + if (core->addr == core_addr) { + *dev_match = dev; + *core_match = core; + return true; + } } } + WARN("ETHOSN: Unknown core address given to SMC call.\n"); return false; } +static void ethosn_configure_smmu_streams(const struct ethosn_device_t *device, + const struct ethosn_core_t *core, + uint32_t asset_alloc_idx) +{ + const struct ethosn_main_allocator_t *main_alloc = + &(core->main_allocator); + const struct ethosn_asset_allocator_t *asset_alloc = + &(device->asset_allocators[asset_alloc_idx]); + const uint32_t streams[9] = { + main_alloc->firmware.stream_id, + main_alloc->working_data.stream_id, + asset_alloc->command_stream.stream_id, + 0U, /* Not used*/ + main_alloc->firmware.stream_id, + asset_alloc->weight_data.stream_id, + asset_alloc->buffer_data.stream_id, + asset_alloc->intermediate_data.stream_id, + asset_alloc->buffer_data.stream_id + }; + size_t i; + + for (i = 0U; i < ARRAY_SIZE(streams); ++i) { + const uintptr_t reg_addr = SEC_MMUSID_REG_BASE + + (SEC_MMUSID_OFFSET * i); + mmio_write_32(ETHOSN_CORE_SEC_REG(core->addr, reg_addr), + streams[i]); + } +} + static void ethosn_delegate_to_ns(uintptr_t core_addr) { mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_SECCTLR_REG), @@ -69,9 +111,6 @@ static void ethosn_delegate_to_ns(uintptr_t core_addr) 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); } @@ -112,7 +151,7 @@ static bool ethosn_reset(uintptr_t core_addr, int hard_reset) uintptr_t ethosn_smc_handler(uint32_t smc_fid, u_register_t core_addr, - u_register_t x2, + u_register_t asset_alloc_idx, u_register_t x3, u_register_t x4, void *cookie, @@ -120,6 +159,8 @@ uintptr_t ethosn_smc_handler(uint32_t smc_fid, u_register_t flags) { int hard_reset = 0; + const struct ethosn_device_t *device = NULL; + const struct ethosn_core_t *core = NULL; const uint32_t fid = smc_fid & FUNCID_NUM_MASK; /* Only SiP fast calls are expected */ @@ -131,12 +172,14 @@ uintptr_t ethosn_smc_handler(uint32_t smc_fid, /* Truncate parameters to 32-bits for SMC32 */ if (GET_SMC_CC(smc_fid) == SMC_32) { core_addr &= 0xFFFFFFFF; - x2 &= 0xFFFFFFFF; + asset_alloc_idx &= 0xFFFFFFFF; x3 &= 0xFFFFFFFF; x4 &= 0xFFFFFFFF; } - if (!is_ethosn_fid(smc_fid)) { + if (!is_ethosn_fid(smc_fid) || + (fid < ETHOSN_FNUM_VERSION || fid > ETHOSN_FNUM_SOFT_RESET)) { + WARN("ETHOSN: Unknown SMC call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); } @@ -146,25 +189,41 @@ uintptr_t ethosn_smc_handler(uint32_t smc_fid, SMC_RET2(handle, ETHOSN_VERSION_MAJOR, ETHOSN_VERSION_MINOR); } - if (!ethosn_is_core_addr_valid(core_addr)) { - WARN("ETHOSN: Unknown core address given to SMC call.\n"); + if (!ethosn_get_device_and_core(core_addr, &device, &core)) { SMC_RET1(handle, ETHOSN_UNKNOWN_CORE_ADDRESS); } - /* Commands that require a valid addr */ + /* Commands that require a valid core address */ switch (fid) { case ETHOSN_FNUM_IS_SEC: - SMC_RET1(handle, ethosn_is_sec(core_addr)); + SMC_RET1(handle, ethosn_is_sec(core->addr)); + } + + if (!device->has_reserved_memory && + asset_alloc_idx >= device->num_allocators) { + WARN("ETHOSN: Unknown asset allocator index given to SMC call.\n"); + SMC_RET1(handle, ETHOSN_UNKNOWN_ALLOCATOR_IDX); + } + + /* Commands that require a valid device, core and asset allocator */ + switch (fid) { case ETHOSN_FNUM_HARD_RESET: hard_reset = 1; /* Fallthrough */ case ETHOSN_FNUM_SOFT_RESET: - if (!ethosn_reset(core_addr, hard_reset)) { + if (!ethosn_reset(core->addr, hard_reset)) { SMC_RET1(handle, ETHOSN_FAILURE); } - ethosn_delegate_to_ns(core_addr); + + if (!device->has_reserved_memory) { + ethosn_configure_smmu_streams(device, core, + asset_alloc_idx); + } + + ethosn_delegate_to_ns(core->addr); SMC_RET1(handle, ETHOSN_SUCCESS); default: + WARN("ETHOSN: Unimplemented SMC call: 0x%x\n", fid); SMC_RET1(handle, SMC_UNK); } } diff --git a/fdts/juno-ethosn.dtsi b/fdts/juno-ethosn.dtsi index e2f33550e..460952482 100644 --- a/fdts/juno-ethosn.dtsi +++ b/fdts/juno-ethosn.dtsi @@ -1,12 +1,13 @@ /* - * Copyright (c) 2021, Arm Limited. All rights reserved. + * Copyright (c) 2021-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /* - * For examples of multi-core and multi-device NPU, refer to the examples given in the - * Arm Ethos-N NPU driver stack. + * This device tree is only an example and some properties have been omitted. + * + * Refer to the Arm(R) Ethos(TM)-N driver stack for complete device tree examples. * https://github.com/ARM-software/ethos-n-driver-stack */ @@ -14,14 +15,62 @@ #address-cells = <2>; #size-cells = <2>; - ethosn0: ethosn@6f300000 { - compatible = "ethosn"; - reg = <0 0x6f300000 0 0x00100000>; + smmu_ethosn0: iommu@6f400000 { + compatible = "arm,smmu-v3"; + reg = <0 0x6f400000 0 0x80000>; status = "okay"; - - core0 { - compatible = "ethosn-core"; - status = "okay"; - }; + /* msi-parent omitted */ + #iommu-cells = <0x1>; }; + + ethosn0: ethosn@6f300000 { + compatible = "ethosn"; + reg = <0 0x6f300000 0 0x00100000>; + status = "okay"; + + core0 { + compatible = "ethosn-core"; + status = "okay"; + + main_allocator { + compatible = "ethosn-main_allocator"; + status = "okay"; + + firmware { + compatible = "ethosn-memory"; + iommus = <&smmu_ethosn0 0>; + }; + + working_data { + compatible = "ethosn-memory"; + iommus = <&smmu_ethosn0 1>; + }; + }; + }; + + asset_allocator { + compatible = "ethosn-asset_allocator"; + status = "okay"; + + command_stream { + compatible = "ethosn-memory"; + iommus = <&smmu_ethosn0 2>; + }; + + weight_data { + compatible = "ethosn-memory"; + iommus = <&smmu_ethosn0 3>; + }; + + buffer_data { + compatible = "ethosn-memory"; + iommus = <&smmu_ethosn0 4>; + }; + + intermediate_data { + compatible = "ethosn-memory"; + iommus = <&smmu_ethosn0 5>; + }; + }; + }; }; diff --git a/include/drivers/arm/ethosn.h b/include/drivers/arm/ethosn.h index 931073338..dbaf16cd7 100644 --- a/include/drivers/arm/ethosn.h +++ b/include/drivers/arm/ethosn.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited. All rights reserved. + * Copyright (c) 2021-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -38,7 +38,7 @@ #define is_ethosn_fid(_fid) (((_fid) & ETHOSN_FID_MASK) == ETHOSN_FID_VALUE) /* Service version */ -#define ETHOSN_VERSION_MAJOR U(1) +#define ETHOSN_VERSION_MAJOR U(2) #define ETHOSN_VERSION_MINOR U(0) /* Return codes for function calls */ @@ -48,10 +48,11 @@ /* -3 Reserved for INVALID_PARAMETER */ #define ETHOSN_FAILURE -4 #define ETHOSN_UNKNOWN_CORE_ADDRESS -5 +#define ETHOSN_UNKNOWN_ALLOCATOR_IDX -6 uintptr_t ethosn_smc_handler(uint32_t smc_fid, u_register_t core_addr, - u_register_t x2, + u_register_t asset_alloc_idx, u_register_t x3, u_register_t x4, void *cookie, diff --git a/include/plat/arm/common/arm_sip_svc.h b/include/plat/arm/common/arm_sip_svc.h index 2eeed95f0..025d10efc 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,2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2019,2021-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,7 +26,7 @@ /* DEBUGFS_SMC_64 0xC2000030U */ /* - * Arm Ethos-N NPU SiP SMC function IDs + * Arm(R) Ethos(TM)-N NPU SiP SMC function IDs * 0xC2000050-0xC200005F * 0x82000050-0x8200005F */ diff --git a/include/plat/arm/common/fconf_ethosn_getter.h b/include/plat/arm/common/fconf_ethosn_getter.h index fcdc31f8b..5b9a7ed9d 100644 --- a/include/plat/arm/common/fconf_ethosn_getter.h +++ b/include/plat/arm/common/fconf_ethosn_getter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited. All rights reserved. + * Copyright (c) 2021-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,27 +8,52 @@ #define FCONF_ETHOSN_GETTER_H #include +#include #include #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[idx].addr; \ +#define hw_config__ethosn_device_getter(dev_idx) __extension__ ({ \ + assert(dev_idx < ethosn_config.num_devices); \ + ðosn_config.devices[dev_idx]; \ }) -#define ETHOSN_STATUS_DISABLED U(0) -#define ETHOSN_STATUS_ENABLED U(1) +#define ETHOSN_DEV_NUM_MAX U(2) +#define ETHOSN_DEV_CORE_NUM_MAX U(8) +#define ETHOSN_DEV_ASSET_ALLOCATOR_NUM_MAX U(16) -#define ETHOSN_CORE_NUM_MAX U(64) +struct ethosn_allocator_t { + uint32_t stream_id; +}; + +struct ethosn_main_allocator_t { + struct ethosn_allocator_t firmware; + struct ethosn_allocator_t working_data; +}; + +struct ethosn_asset_allocator_t { + struct ethosn_allocator_t command_stream; + struct ethosn_allocator_t weight_data; + struct ethosn_allocator_t buffer_data; + struct ethosn_allocator_t intermediate_data; +}; struct ethosn_core_t { uint64_t addr; + struct ethosn_main_allocator_t main_allocator; +}; + +struct ethosn_device_t { + bool has_reserved_memory; + uint32_t num_cores; + struct ethosn_core_t cores[ETHOSN_DEV_CORE_NUM_MAX]; + uint32_t num_allocators; + struct ethosn_asset_allocator_t asset_allocators[ETHOSN_DEV_ASSET_ALLOCATOR_NUM_MAX]; }; struct ethosn_config_t { - uint32_t num_cores; - struct ethosn_core_t core[ETHOSN_CORE_NUM_MAX]; + uint32_t num_devices; + struct ethosn_device_t devices[ETHOSN_DEV_NUM_MAX]; }; int fconf_populate_arm_ethosn(uintptr_t config); diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index bd59ec00a..184b51740 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -114,7 +114,7 @@ ifeq (${ARM_LINUX_KERNEL_AS_BL33},1) endif endif -# Arm Ethos-N NPU SiP service +# Arm(R) Ethos(TM)-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)) diff --git a/plat/arm/common/fconf/fconf_ethosn_getter.c b/plat/arm/common/fconf/fconf_ethosn_getter.c index 0af1a20fb..0b48a9816 100644 --- a/plat/arm/common/fconf/fconf_ethosn_getter.c +++ b/plat/arm/common/fconf/fconf_ethosn_getter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Arm Limited. All rights reserved. + * Copyright (c) 2021-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,107 +12,341 @@ #include #include -struct ethosn_config_t ethosn_config = {.num_cores = 0}; +struct ethosn_config_t ethosn_config = {0}; -static uint8_t fdt_node_get_status(const void *fdt, int node) +struct ethosn_sub_allocator_t { + const char *name; + size_t name_len; + uint32_t stream_id; +}; + +static bool fdt_node_is_enabled(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 true; } - return status; + return false; +} + +static bool fdt_node_has_reserved_memory(const void *fdt, int dev_node) +{ + return fdt_get_property(fdt, dev_node, "memory-region", NULL) != NULL; +} + +static int fdt_node_get_iommus_stream_id(const void *fdt, int node, uint32_t *stream_id) +{ + int err; + uint32_t iommus_array[2] = {0U}; + + err = fdt_read_uint32_array(fdt, node, "iommus", 2U, iommus_array); + if (err) { + return err; + } + + *stream_id = iommus_array[1]; + return 0; +} + +static int fdt_node_populate_sub_allocators(const void *fdt, + int alloc_node, + struct ethosn_sub_allocator_t *sub_allocators, + size_t num_allocs) +{ + int sub_node; + size_t i; + int err = -FDT_ERR_NOTFOUND; + uint32_t found_sub_allocators = 0U; + + fdt_for_each_subnode(sub_node, fdt, alloc_node) { + const char *node_name; + + if (!fdt_node_is_enabled(fdt, sub_node)) { + /* Ignore disabled node */ + continue; + } + + if (fdt_node_check_compatible(fdt, sub_node, "ethosn-memory") != 0) { + continue; + } + + node_name = fdt_get_name(fdt, sub_node, NULL); + for (i = 0U; i < num_allocs; ++i) { + if (strncmp(node_name, sub_allocators[i].name, + sub_allocators[i].name_len) != 0) { + continue; + } + + err = fdt_node_get_iommus_stream_id(fdt, sub_node, + &sub_allocators[i].stream_id); + if (err) { + ERROR("FCONF: Failed to get stream ID from sub-allocator %s\n", + node_name); + return err; + } + + ++found_sub_allocators; + /* Nothing more to do for this node */ + break; + } + + /* Check that at least one of the sub-allocators matched */ + if (i == num_allocs) { + ERROR("FCONF: Unknown sub-allocator %s\n", node_name); + return -FDT_ERR_BADSTRUCTURE; + } + } + + if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) { + ERROR("FCONF: Failed to parse sub-allocators\n"); + return -FDT_ERR_BADSTRUCTURE; + } + + if (err == -FDT_ERR_NOTFOUND) { + ERROR("FCONF: No matching sub-allocator found\n"); + return err; + } + + if (found_sub_allocators != num_allocs) { + ERROR("FCONF: Not all sub-allocators were found\n"); + return -FDT_ERR_BADSTRUCTURE; + } + + return 0; +} + +static int fdt_node_populate_main_allocator(const void *fdt, + int alloc_node, + struct ethosn_main_allocator_t *allocator) +{ + int err; + struct ethosn_sub_allocator_t sub_allocators[] = { + {.name = "firmware", .name_len = 8U}, + {.name = "working_data", .name_len = 12U} + }; + + err = fdt_node_populate_sub_allocators(fdt, alloc_node, sub_allocators, + ARRAY_SIZE(sub_allocators)); + if (err) { + return err; + } + + allocator->firmware.stream_id = sub_allocators[0].stream_id; + allocator->working_data.stream_id = sub_allocators[1].stream_id; + + return 0; +} + +static int fdt_node_populate_asset_allocator(const void *fdt, + int alloc_node, + struct ethosn_asset_allocator_t *allocator) +{ + int err; + struct ethosn_sub_allocator_t sub_allocators[] = { + {.name = "command_stream", .name_len = 14U}, + {.name = "weight_data", .name_len = 11U}, + {.name = "buffer_data", .name_len = 11U}, + {.name = "intermediate_data", .name_len = 17U} + }; + + err = fdt_node_populate_sub_allocators(fdt, alloc_node, sub_allocators, + ARRAY_SIZE(sub_allocators)); + if (err) { + return err; + } + + + allocator->command_stream.stream_id = sub_allocators[0].stream_id; + allocator->weight_data.stream_id = sub_allocators[1].stream_id; + allocator->buffer_data.stream_id = sub_allocators[2].stream_id; + allocator->intermediate_data.stream_id = sub_allocators[3].stream_id; + return 0; +} + +static int fdt_node_populate_core(const void *fdt, + int device_node, + int core_node, + bool has_reserved_memory, + uint32_t core_index, + struct ethosn_core_t *core) +{ + int err; + int sub_node; + uintptr_t core_addr; + + err = fdt_get_reg_props_by_index(fdt, device_node, core_index, + &core_addr, NULL); + if (err < 0) { + ERROR("FCONF: Failed to read reg property for NPU core %u\n", + core_index); + return err; + } + + err = -FDT_ERR_NOTFOUND; + fdt_for_each_subnode(sub_node, fdt, core_node) { + + if (!fdt_node_is_enabled(fdt, sub_node)) { + continue; + } + + if (fdt_node_check_compatible(fdt, + sub_node, + "ethosn-main_allocator") != 0) { + continue; + } + + if (has_reserved_memory) { + ERROR("FCONF: Main allocator not supported when using reserved memory\n"); + return -FDT_ERR_BADSTRUCTURE; + } + + if (err != -FDT_ERR_NOTFOUND) { + ERROR("FCONF: NPU core 0x%lx has more than one main allocator\n", + core_addr); + return -FDT_ERR_BADSTRUCTURE; + } + + err = fdt_node_populate_main_allocator(fdt, sub_node, &core->main_allocator); + if (err) { + ERROR("FCONF: Failed to parse main allocator for NPU core 0x%lx\n", + core_addr); + return err; + } + } + + if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) { + ERROR("FCONF: Failed to parse core sub nodes\n"); + return -FDT_ERR_BADSTRUCTURE; + } + + if (!has_reserved_memory && err) { + ERROR("FCONF: Main allocator not found for NPU core 0x%lx\n", + core_addr); + return err; + } + + core->addr = core_addr; + + return 0; } int fconf_populate_ethosn_config(uintptr_t config) { int ethosn_node; + uint32_t dev_count = 0U; const void *hw_conf_dtb = (const void *)config; - /* Find offset to node with 'ethosn' compatible property */ - INFO("Probing Arm Ethos-N NPU\n"); - uint32_t total_core_count = 0U; + INFO("Probing Arm(R) Ethos(TM)-N NPU\n"); fdt_for_each_compatible_node(hw_conf_dtb, ethosn_node, "ethosn") { + struct ethosn_device_t *dev = ðosn_config.devices[dev_count]; + uint32_t dev_asset_alloc_count = 0U; + uint32_t dev_core_count = 0U; + bool has_reserved_memory; int sub_node; - uint8_t ethosn_status; - uint32_t device_core_count = 0U; - /* 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) { + if (!fdt_node_is_enabled(hw_conf_dtb, ethosn_node)) { continue; } + if (dev_count >= ETHOSN_DEV_NUM_MAX) { + ERROR("FCONF: Reached max number of NPUs\n"); + return -FDT_ERR_BADSTRUCTURE; + } + + has_reserved_memory = fdt_node_has_reserved_memory(hw_conf_dtb, ethosn_node); fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) { int err; - uintptr_t core_addr; - uint8_t core_status; - if (total_core_count >= ETHOSN_CORE_NUM_MAX) { - ERROR("FCONF: Reached max number of Arm Ethos-N NPU cores\n"); - return -FDT_ERR_BADSTRUCTURE; + if (!fdt_node_is_enabled(hw_conf_dtb, sub_node)) { + /* Ignore disabled sub node */ + continue; } - /* 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; - } + "ethosn-core") == 0) { - core_status = fdt_node_get_status(hw_conf_dtb, sub_node); - if (core_status == ETHOSN_STATUS_DISABLED) { - continue; - } + if (dev_core_count >= ETHOSN_DEV_CORE_NUM_MAX) { + ERROR("FCONF: Reached max number of NPU cores for NPU %u\n", + dev_count); + return -FDT_ERR_BADSTRUCTURE; + } - 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; - } + err = fdt_node_populate_core(hw_conf_dtb, + ethosn_node, + sub_node, + has_reserved_memory, + dev_core_count, + &(dev->cores[dev_core_count])); + if (err) { + return err; + } + ++dev_core_count; + } else if (fdt_node_check_compatible(hw_conf_dtb, + sub_node, + "ethosn-asset_allocator") == 0) { - 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++; + if (dev_asset_alloc_count >= + ETHOSN_DEV_ASSET_ALLOCATOR_NUM_MAX) { + ERROR("FCONF: Reached max number of asset allocators for NPU %u\n", + dev_count); + return -FDT_ERR_BADSTRUCTURE; + } + + if (has_reserved_memory) { + ERROR("FCONF: Asset allocator not supported when using reserved memory\n"); + return -FDT_ERR_BADSTRUCTURE; + } + + err = fdt_node_populate_asset_allocator(hw_conf_dtb, + sub_node, + &(dev->asset_allocators[dev_asset_alloc_count])); + if (err) { + ERROR("FCONF: Failed to parse asset allocator for NPU %u\n", + dev_count); + return err; + } + ++dev_asset_alloc_count; + } } if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) { - ERROR("FCONF: Failed to parse sub nodes\n"); + ERROR("FCONF: Failed to parse sub nodes for NPU %u\n", + dev_count); return -FDT_ERR_BADSTRUCTURE; } - if (device_core_count == 0U) { - ERROR( - "FCONF: Enabled Arm Ethos-N NPU device must have at least one enabled core\n"); + if (dev_core_count == 0U) { + ERROR("FCONF: NPU %u must have at least one enabled core\n", + dev_count); return -FDT_ERR_BADSTRUCTURE; } + + if (!has_reserved_memory && dev_asset_alloc_count == 0U) { + ERROR("FCONF: NPU %u must have at least one asset allocator\n", + dev_count); + return -FDT_ERR_BADSTRUCTURE; + } + + dev->num_cores = dev_core_count; + dev->num_allocators = dev_asset_alloc_count; + dev->has_reserved_memory = has_reserved_memory; + ++dev_count; } - if (total_core_count == 0U) { + if (dev_count == 0U) { ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n"); return -FDT_ERR_BADSTRUCTURE; } - ethosn_config.num_cores = total_core_count; - - INFO("%d NPU core%s probed\n", - ethosn_config.num_cores, - ethosn_config.num_cores > 1 ? "s" : ""); + ethosn_config.num_devices = dev_count; return 0; }