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 = ðosn_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