1 /*
2  * Copyright (c) 2021-2022, 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 = {0};
16 
17 struct ethosn_sub_allocator_t {
18 	const char *name;
19 	size_t name_len;
20 	uint32_t stream_id;
21 };
22 
fdt_node_is_enabled(const void * fdt,int node)23 static bool fdt_node_is_enabled(const void *fdt, int node)
24 {
25 	int len;
26 	const char *node_status;
27 
28 	node_status = fdt_getprop(fdt, node, "status", &len);
29 	if (node_status == NULL ||
30 	    (len == 5 && /* Includes null character */
31 	     strncmp(node_status, "okay", 4U) == 0)) {
32 		return true;
33 	}
34 
35 	return false;
36 }
37 
fdt_node_has_reserved_memory(const void * fdt,int dev_node)38 static bool fdt_node_has_reserved_memory(const void *fdt, int dev_node)
39 {
40 	return fdt_get_property(fdt, dev_node, "memory-region", NULL) != NULL;
41 }
42 
fdt_node_get_iommus_stream_id(const void * fdt,int node,uint32_t * stream_id)43 static int fdt_node_get_iommus_stream_id(const void *fdt, int node, uint32_t *stream_id)
44 {
45 	int err;
46 	uint32_t iommus_array[2] = {0U};
47 
48 	err = fdt_read_uint32_array(fdt, node, "iommus", 2U, iommus_array);
49 	if (err) {
50 		return err;
51 	}
52 
53 	*stream_id = iommus_array[1];
54 	return 0;
55 }
56 
fdt_node_populate_sub_allocators(const void * fdt,int alloc_node,struct ethosn_sub_allocator_t * sub_allocators,size_t num_allocs)57 static int fdt_node_populate_sub_allocators(const void *fdt,
58 					    int alloc_node,
59 					    struct ethosn_sub_allocator_t *sub_allocators,
60 					    size_t num_allocs)
61 {
62 	int sub_node;
63 	size_t i;
64 	int err = -FDT_ERR_NOTFOUND;
65 	uint32_t found_sub_allocators = 0U;
66 
67 	fdt_for_each_subnode(sub_node, fdt, alloc_node) {
68 		const char *node_name;
69 
70 		if (!fdt_node_is_enabled(fdt, sub_node)) {
71 			/* Ignore disabled node */
72 			continue;
73 		}
74 
75 		if (fdt_node_check_compatible(fdt, sub_node, "ethosn-memory") != 0) {
76 			continue;
77 		}
78 
79 		node_name = fdt_get_name(fdt, sub_node, NULL);
80 		for (i = 0U; i < num_allocs; ++i) {
81 			if (strncmp(node_name, sub_allocators[i].name,
82 				    sub_allocators[i].name_len) != 0) {
83 				continue;
84 			}
85 
86 			err = fdt_node_get_iommus_stream_id(fdt, sub_node,
87 							    &sub_allocators[i].stream_id);
88 			if (err) {
89 				ERROR("FCONF: Failed to get stream ID from sub-allocator %s\n",
90 				      node_name);
91 				return err;
92 			}
93 
94 			++found_sub_allocators;
95 			/* Nothing more to do for this node */
96 			break;
97 		}
98 
99 		/* Check that at least one of the sub-allocators matched */
100 		if (i == num_allocs) {
101 			ERROR("FCONF: Unknown sub-allocator %s\n", node_name);
102 			return -FDT_ERR_BADSTRUCTURE;
103 		}
104 	}
105 
106 	if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
107 		ERROR("FCONF: Failed to parse sub-allocators\n");
108 		return -FDT_ERR_BADSTRUCTURE;
109 	}
110 
111 	if (err == -FDT_ERR_NOTFOUND) {
112 		ERROR("FCONF: No matching sub-allocator found\n");
113 		return err;
114 	}
115 
116 	if (found_sub_allocators != num_allocs) {
117 		ERROR("FCONF: Not all sub-allocators were found\n");
118 		return -FDT_ERR_BADSTRUCTURE;
119 	}
120 
121 	return 0;
122 }
123 
fdt_node_populate_main_allocator(const void * fdt,int alloc_node,struct ethosn_main_allocator_t * allocator)124 static int fdt_node_populate_main_allocator(const void *fdt,
125 					    int alloc_node,
126 					    struct ethosn_main_allocator_t *allocator)
127 {
128 	int err;
129 	struct ethosn_sub_allocator_t sub_allocators[] = {
130 		{.name = "firmware", .name_len = 8U},
131 		{.name = "working_data", .name_len = 12U}
132 	};
133 
134 	err = fdt_node_populate_sub_allocators(fdt, alloc_node, sub_allocators,
135 					       ARRAY_SIZE(sub_allocators));
136 	if (err) {
137 		return err;
138 	}
139 
140 	allocator->firmware.stream_id = sub_allocators[0].stream_id;
141 	allocator->working_data.stream_id = sub_allocators[1].stream_id;
142 
143 	return 0;
144 }
145 
fdt_node_populate_asset_allocator(const void * fdt,int alloc_node,struct ethosn_asset_allocator_t * allocator)146 static int fdt_node_populate_asset_allocator(const void *fdt,
147 					    int alloc_node,
148 					    struct ethosn_asset_allocator_t *allocator)
149 {
150 	int err;
151 	struct ethosn_sub_allocator_t sub_allocators[] = {
152 		{.name = "command_stream", .name_len = 14U},
153 		{.name = "weight_data", .name_len = 11U},
154 		{.name = "buffer_data", .name_len = 11U},
155 		{.name = "intermediate_data", .name_len = 17U}
156 	};
157 
158 	err = fdt_node_populate_sub_allocators(fdt, alloc_node, sub_allocators,
159 					       ARRAY_SIZE(sub_allocators));
160 	if (err) {
161 		return err;
162 	}
163 
164 
165 	allocator->command_stream.stream_id = sub_allocators[0].stream_id;
166 	allocator->weight_data.stream_id = sub_allocators[1].stream_id;
167 	allocator->buffer_data.stream_id = sub_allocators[2].stream_id;
168 	allocator->intermediate_data.stream_id = sub_allocators[3].stream_id;
169 	return 0;
170 }
171 
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)172 static int fdt_node_populate_core(const void *fdt,
173 				  int device_node,
174 				  int core_node,
175 				  bool has_reserved_memory,
176 				  uint32_t core_index,
177 				  struct ethosn_core_t *core)
178 {
179 	int err;
180 	int sub_node;
181 	uintptr_t core_addr;
182 
183 	err = fdt_get_reg_props_by_index(fdt, device_node, core_index,
184 					 &core_addr, NULL);
185 	if (err < 0) {
186 		ERROR("FCONF: Failed to read reg property for NPU core %u\n",
187 		      core_index);
188 		return err;
189 	}
190 
191 	err = -FDT_ERR_NOTFOUND;
192 	fdt_for_each_subnode(sub_node, fdt, core_node) {
193 
194 		if (!fdt_node_is_enabled(fdt, sub_node)) {
195 			continue;
196 		}
197 
198 		if (fdt_node_check_compatible(fdt,
199 					      sub_node,
200 					      "ethosn-main_allocator") != 0) {
201 			continue;
202 		}
203 
204 		if (has_reserved_memory) {
205 			ERROR("FCONF: Main allocator not supported when using reserved memory\n");
206 			return -FDT_ERR_BADSTRUCTURE;
207 		}
208 
209 		if (err != -FDT_ERR_NOTFOUND) {
210 			ERROR("FCONF: NPU core 0x%lx has more than one main allocator\n",
211 			      core_addr);
212 			return -FDT_ERR_BADSTRUCTURE;
213 		}
214 
215 		err = fdt_node_populate_main_allocator(fdt, sub_node, &core->main_allocator);
216 		if (err) {
217 			ERROR("FCONF: Failed to parse main allocator for NPU core 0x%lx\n",
218 			      core_addr);
219 			return err;
220 		}
221 	}
222 
223 	if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
224 		ERROR("FCONF: Failed to parse core sub nodes\n");
225 		return -FDT_ERR_BADSTRUCTURE;
226 	}
227 
228 	if (!has_reserved_memory && err) {
229 		ERROR("FCONF: Main allocator not found for NPU core 0x%lx\n",
230 		      core_addr);
231 		return err;
232 	}
233 
234 	core->addr = core_addr;
235 
236 	return 0;
237 }
238 
fconf_populate_ethosn_config(uintptr_t config)239 int fconf_populate_ethosn_config(uintptr_t config)
240 {
241 	int ethosn_node;
242 	uint32_t dev_count = 0U;
243 	const void *hw_conf_dtb = (const void *)config;
244 
245 	INFO("Probing Arm(R) Ethos(TM)-N NPU\n");
246 
247 	fdt_for_each_compatible_node(hw_conf_dtb, ethosn_node, "ethosn") {
248 		struct ethosn_device_t *dev = &ethosn_config.devices[dev_count];
249 		uint32_t dev_asset_alloc_count = 0U;
250 		uint32_t dev_core_count = 0U;
251 		bool has_reserved_memory;
252 		int sub_node;
253 
254 		if (!fdt_node_is_enabled(hw_conf_dtb, ethosn_node)) {
255 			continue;
256 		}
257 
258 		if (dev_count >= ETHOSN_DEV_NUM_MAX) {
259 			ERROR("FCONF: Reached max number of NPUs\n");
260 			return -FDT_ERR_BADSTRUCTURE;
261 		}
262 
263 		has_reserved_memory = fdt_node_has_reserved_memory(hw_conf_dtb, ethosn_node);
264 		fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) {
265 			int err;
266 
267 			if (!fdt_node_is_enabled(hw_conf_dtb, sub_node)) {
268 				/* Ignore disabled sub node */
269 				continue;
270 			}
271 
272 			if (fdt_node_check_compatible(hw_conf_dtb,
273 						      sub_node,
274 						      "ethosn-core") == 0) {
275 
276 				if (dev_core_count >= ETHOSN_DEV_CORE_NUM_MAX) {
277 					ERROR("FCONF: Reached max number of NPU cores for NPU %u\n",
278 					      dev_count);
279 					return -FDT_ERR_BADSTRUCTURE;
280 				}
281 
282 				err = fdt_node_populate_core(hw_conf_dtb,
283 							     ethosn_node,
284 							     sub_node,
285 							     has_reserved_memory,
286 							     dev_core_count,
287 							     &(dev->cores[dev_core_count]));
288 				if (err) {
289 					return err;
290 				}
291 				++dev_core_count;
292 			} else if (fdt_node_check_compatible(hw_conf_dtb,
293 							     sub_node,
294 							     "ethosn-asset_allocator") == 0) {
295 
296 				if (dev_asset_alloc_count >=
297 				    ETHOSN_DEV_ASSET_ALLOCATOR_NUM_MAX) {
298 					ERROR("FCONF: Reached max number of asset allocators for NPU %u\n",
299 					      dev_count);
300 					return -FDT_ERR_BADSTRUCTURE;
301 				}
302 
303 				if (has_reserved_memory) {
304 					ERROR("FCONF: Asset allocator not supported when using reserved memory\n");
305 					return -FDT_ERR_BADSTRUCTURE;
306 				}
307 
308 				err = fdt_node_populate_asset_allocator(hw_conf_dtb,
309 									sub_node,
310 									&(dev->asset_allocators[dev_asset_alloc_count]));
311 				if (err) {
312 					ERROR("FCONF: Failed to parse asset allocator for NPU %u\n",
313 					      dev_count);
314 					return err;
315 				}
316 				++dev_asset_alloc_count;
317 			}
318 		}
319 
320 		if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) {
321 			ERROR("FCONF: Failed to parse sub nodes for NPU %u\n",
322 			      dev_count);
323 			return -FDT_ERR_BADSTRUCTURE;
324 		}
325 
326 		if (dev_core_count == 0U) {
327 			ERROR("FCONF: NPU %u must have at least one enabled core\n",
328 			      dev_count);
329 			return -FDT_ERR_BADSTRUCTURE;
330 		}
331 
332 		if (!has_reserved_memory && dev_asset_alloc_count == 0U) {
333 			ERROR("FCONF: NPU %u must have at least one asset allocator\n",
334 			      dev_count);
335 			return -FDT_ERR_BADSTRUCTURE;
336 		}
337 
338 		dev->num_cores = dev_core_count;
339 		dev->num_allocators = dev_asset_alloc_count;
340 		dev->has_reserved_memory = has_reserved_memory;
341 		++dev_count;
342 	}
343 
344 	if (dev_count == 0U) {
345 		ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n");
346 		return -FDT_ERR_BADSTRUCTURE;
347 	}
348 
349 	ethosn_config.num_devices = dev_count;
350 
351 	return 0;
352 }
353 
354 FCONF_REGISTER_POPULATOR(HW_CONFIG, ethosn_config, fconf_populate_ethosn_config);
355