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 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 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 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 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 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 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 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 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