1 /*
2  * Copyright (c) 2021, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <string.h>
9 
10 #include <common/debug.h>
11 #include <common/fdt_wrappers.h>
12 #include <libfdt.h>
13 #include <plat/arm/common/fconf_ethosn_getter.h>
14 
15 struct ethosn_config_t ethosn_config = {.num_cores = 0};
16 
fdt_node_get_status(const void * fdt,int node)17 static uint8_t fdt_node_get_status(const void *fdt, int node)
18 {
19 	int len;
20 	uint8_t status = ETHOSN_STATUS_DISABLED;
21 	const char *node_status;
22 
23 	node_status = fdt_getprop(fdt, node, "status", &len);
24 	if (node_status == NULL ||
25 	    (len == 5 && /* Includes null character */
26 	     strncmp(node_status, "okay", 4U) == 0)) {
27 		status = ETHOSN_STATUS_ENABLED;
28 	}
29 
30 	return status;
31 }
32 
fconf_populate_ethosn_config(uintptr_t config)33 int fconf_populate_ethosn_config(uintptr_t config)
34 {
35 	int ethosn_node;
36 	const void *hw_conf_dtb = (const void *)config;
37 
38 	/* Find offset to node with 'ethosn' compatible property */
39 	INFO("Probing Arm Ethos-N NPU\n");
40 	uint32_t total_core_count = 0U;
41 
42 	fdt_for_each_compatible_node(hw_conf_dtb, ethosn_node, "ethosn") {
43 		int sub_node;
44 		uint8_t ethosn_status;
45 		uint32_t device_core_count = 0U;
46 
47 		/* If the Arm Ethos-N NPU is disabled the core check can be skipped */
48 		ethosn_status = fdt_node_get_status(hw_conf_dtb, ethosn_node);
49 		if (ethosn_status == ETHOSN_STATUS_DISABLED) {
50 			continue;
51 		}
52 
53 		fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) {
54 			int err;
55 			uintptr_t core_addr;
56 			uint8_t core_status;
57 
58 			if (total_core_count >= ETHOSN_CORE_NUM_MAX) {
59 				ERROR("FCONF: Reached max number of Arm Ethos-N NPU cores\n");
60 				return -FDT_ERR_BADSTRUCTURE;
61 			}
62 
63 			/* Check that the sub node is "ethosn-core" compatible */
64 			if (fdt_node_check_compatible(hw_conf_dtb,
65 						      sub_node,
66 						      "ethosn-core") != 0) {
67 				/* Ignore incompatible sub node */
68 				continue;
69 			}
70 
71 			core_status = fdt_node_get_status(hw_conf_dtb, sub_node);
72 			if (core_status == ETHOSN_STATUS_DISABLED) {
73 				continue;
74 			}
75 
76 			err = fdt_get_reg_props_by_index(hw_conf_dtb,
77 							 ethosn_node,
78 							 device_core_count,
79 							 &core_addr,
80 							 NULL);
81 			if (err < 0) {
82 				ERROR(
83 				"FCONF: Failed to read reg property for Arm Ethos-N NPU core %u\n",
84 						device_core_count);
85 				return err;
86 			}
87 
88 			INFO("NPU core probed at address 0x%lx\n", core_addr);
89 			ethosn_config.core[total_core_count].addr = core_addr;
90 			total_core_count++;
91 			device_core_count++;
92 		}
93 
94 		if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
95 			ERROR("FCONF: Failed to parse sub nodes\n");
96 			return -FDT_ERR_BADSTRUCTURE;
97 		}
98 
99 		if (device_core_count == 0U) {
100 			ERROR(
101 			"FCONF: Enabled Arm Ethos-N NPU device must have at least one enabled core\n");
102 			return -FDT_ERR_BADSTRUCTURE;
103 		}
104 	}
105 
106 	if (total_core_count == 0U) {
107 		ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n");
108 		return -FDT_ERR_BADSTRUCTURE;
109 	}
110 
111 	ethosn_config.num_cores = total_core_count;
112 
113 	INFO("%d NPU core%s probed\n",
114 	     ethosn_config.num_cores,
115 	     ethosn_config.num_cores > 1 ? "s" : "");
116 
117 	return 0;
118 }
119 
120 FCONF_REGISTER_POPULATOR(HW_CONFIG, ethosn_config, fconf_populate_ethosn_config);
121