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
This commit is contained in:
Laurent Carlier 2021-09-16 15:10:35 +01:00
parent ff766148b5
commit 1c65989e70
5 changed files with 123 additions and 100 deletions

View File

@ -14,11 +14,10 @@
#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 */
/*
* 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);

View File

@ -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";

View File

@ -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,

View File

@ -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);

View File

@ -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 @@ 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;
}