diff --git a/drivers/arm/ethosn/ethosn_smc.c b/drivers/arm/ethosn/ethosn_smc.c index 299d07c02..60364cdb2 100644 --- a/drivers/arm/ethosn/ethosn_smc.c +++ b/drivers/arm/ethosn/ethosn_smc.c @@ -14,11 +14,10 @@ #include #include -/* Arm Ethos-N NPU (NPU) status */ -#define ETHOSN_STATUS \ - FCONF_GET_PROPERTY(hw_config, ethosn_config, status) - -/* Number of NPU cores available */ +/* + * Number of Arm Ethos-N NPU (NPU) cores available for a + * particular parent device + */ #define ETHOSN_NUM_CORES \ FCONF_GET_PROPERTY(hw_config, ethosn_config, num_cores) @@ -51,6 +50,17 @@ #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) +{ + for (uint32_t core_idx = 0U; core_idx < ETHOSN_NUM_CORES; core_idx++) { + if (ETHOSN_CORE_ADDR(core_idx) == core_addr) { + return true; + } + } + + return false; +} + static void ethosn_delegate_to_ns(uintptr_t core_addr) { mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_SECCTLR_REG), @@ -66,9 +76,9 @@ static void ethosn_delegate_to_ns(uintptr_t core_addr) SEC_DEL_ADDR_EXT_VAL); } -static int ethosn_is_sec(void) +static int ethosn_is_sec(uintptr_t core_addr) { - if ((mmio_read_32(ETHOSN_CORE_SEC_REG(ETHOSN_CORE_ADDR(0), SEC_DEL_REG)) + if ((mmio_read_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_REG)) & SEC_DEL_EXCC_MASK) != 0U) { return 0; } @@ -101,7 +111,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_idx, + u_register_t core_addr, u_register_t x2, u_register_t x3, u_register_t x4, @@ -109,8 +119,8 @@ uintptr_t ethosn_smc_handler(uint32_t smc_fid, void *handle, u_register_t flags) { - uintptr_t core_addr; int hard_reset = 0; + const uint32_t fid = smc_fid & FUNCID_NUM_MASK; /* Only SiP fast calls are expected */ if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) || @@ -120,7 +130,7 @@ 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_idx &= 0xFFFFFFFF; + core_addr &= 0xFFFFFFFF; x2 &= 0xFFFFFFFF; x3 &= 0xFFFFFFFF; x4 &= 0xFFFFFFFF; @@ -130,33 +140,29 @@ uintptr_t ethosn_smc_handler(uint32_t 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) { + /* Commands that do not require a valid core address */ + switch (fid) { case ETHOSN_FNUM_VERSION: 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"); + SMC_RET1(handle, ETHOSN_UNKNOWN_CORE_ADDRESS); + } + + /* Commands that require a valid addr */ + switch (fid) { case ETHOSN_FNUM_IS_SEC: - SMC_RET1(handle, ethosn_is_sec()); + SMC_RET1(handle, ethosn_is_sec(core_addr)); 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 index 87ab378a2..e2f33550e 100644 --- a/fdts/juno-ethosn.dtsi +++ b/fdts/juno-ethosn.dtsi @@ -4,19 +4,21 @@ * 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. + * https://github.com/ARM-software/ethos-n-driver-stack + */ + / { #address-cells = <2>; #size-cells = <2>; - ethosn: ethosn@6f300000 { + ethosn0: 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/include/drivers/arm/ethosn.h b/include/drivers/arm/ethosn.h index 6de2abb85..931073338 100644 --- a/include/drivers/arm/ethosn.h +++ b/include/drivers/arm/ethosn.h @@ -38,8 +38,8 @@ #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) +#define ETHOSN_VERSION_MAJOR U(1) +#define ETHOSN_VERSION_MINOR U(0) /* Return codes for function calls */ #define ETHOSN_SUCCESS 0 @@ -47,10 +47,10 @@ /* -2 Reserved for NOT_REQUIRED */ /* -3 Reserved for INVALID_PARAMETER */ #define ETHOSN_FAILURE -4 -#define ETHOSN_CORE_IDX_OUT_OF_RANGE -5 +#define ETHOSN_UNKNOWN_CORE_ADDRESS -5 uintptr_t ethosn_smc_handler(uint32_t smc_fid, - u_register_t core_idx, + u_register_t core_addr, u_register_t x2, u_register_t x3, u_register_t x4, diff --git a/include/plat/arm/common/fconf_ethosn_getter.h b/include/plat/arm/common/fconf_ethosn_getter.h index 0fd1f025a..fcdc31f8b 100644 --- a/include/plat/arm/common/fconf_ethosn_getter.h +++ b/include/plat/arm/common/fconf_ethosn_getter.h @@ -14,7 +14,7 @@ #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]; \ + ethosn_config.core[idx].addr; \ }) #define ETHOSN_STATUS_DISABLED U(0) @@ -22,10 +22,13 @@ #define ETHOSN_CORE_NUM_MAX U(64) +struct ethosn_core_t { + uint64_t addr; +}; + struct ethosn_config_t { - uint8_t status; uint32_t num_cores; - uint64_t core_addr[ETHOSN_CORE_NUM_MAX]; + struct ethosn_core_t core[ETHOSN_CORE_NUM_MAX]; }; int fconf_populate_arm_ethosn(uintptr_t config); diff --git a/plat/arm/common/fconf/fconf_ethosn_getter.c b/plat/arm/common/fconf/fconf_ethosn_getter.c index 1ba9f3a23..0af1a20fb 100644 --- a/plat/arm/common/fconf/fconf_ethosn_getter.c +++ b/plat/arm/common/fconf/fconf_ethosn_getter.c @@ -12,7 +12,7 @@ #include #include -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 @@ static uint8_t fdt_node_get_status(const void *fdt, int node) 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) { + INFO("Probing Arm Ethos-N NPU\n"); + uint32_t total_core_count = 0U; + + fdt_for_each_compatible_node(hw_conf_dtb, ethosn_node, "ethosn") { + 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) { + continue; + } + + 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; + } + + /* 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++; + } + + if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) { + ERROR("FCONF: Failed to parse sub nodes\n"); + 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"); + return -FDT_ERR_BADSTRUCTURE; + } + } + + if (total_core_count == 0U) { ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n"); - return ethosn_node; + return -FDT_ERR_BADSTRUCTURE; } - /* 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; - } + ethosn_config.num_cores = total_core_count; - 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; + INFO("%d NPU core%s probed\n", + ethosn_config.num_cores, + ethosn_config.num_cores > 1 ? "s" : ""); return 0; }