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