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