1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <kernel/dt.h> 8 #include <kernel/dt_driver.h> 9 #include <kernel/interrupt.h> 10 #include <kernel/linker.h> 11 #include <libfdt.h> 12 #include <mm/core_memprot.h> 13 #include <mm/core_mmu.h> 14 #include <string.h> 15 #include <trace.h> 16 17 const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs) 18 { 19 const struct dt_device_match *dm; 20 const struct dt_driver *drv; 21 22 for_each_dt_driver(drv) { 23 for (dm = drv->match_table; dm; dm++) { 24 if (!dm->compatible) { 25 break; 26 } 27 if (!fdt_node_check_compatible(fdt, offs, 28 dm->compatible)) { 29 return drv; 30 } 31 } 32 } 33 34 return NULL; 35 } 36 37 bool dt_have_prop(const void *fdt, int offs, const char *propname) 38 { 39 const void *prop; 40 41 prop = fdt_getprop(fdt, offs, propname, NULL); 42 43 return prop; 44 } 45 46 int dt_disable_status(void *fdt, int node) 47 { 48 const char *prop = NULL; 49 int len = 0; 50 51 prop = fdt_getprop(fdt, node, "status", &len); 52 if (!prop) { 53 if (fdt_setprop_string(fdt, node, "status", "disabled")) 54 return -1; 55 } else { 56 /* 57 * Status is there, modify it. 58 * Ask to set "disabled" value to the property. The value 59 * will be automatically truncated with "len" size by the 60 * fdt_setprop_inplace function. 61 * Setting a value different from "ok" or "okay" will disable 62 * the property. 63 * Setting a truncated value of "disabled" with the original 64 * property "len" is preferred to not increase the DT size and 65 * losing time in recalculating the overall DT offsets. 66 * If original length of the status property is larger than 67 * "disabled", the property will start with "disabled" and be 68 * completed with the rest of the original property. 69 */ 70 if (fdt_setprop_inplace(fdt, node, "status", "disabled", len)) 71 return -1; 72 } 73 74 return 0; 75 } 76 77 int dt_enable_secure_status(void *fdt, int node) 78 { 79 if (dt_disable_status(fdt, node)) { 80 EMSG("Unable to disable Normal Status"); 81 return -1; 82 } 83 84 if (fdt_setprop_string(fdt, node, "secure-status", "okay")) 85 return -1; 86 87 return 0; 88 } 89 90 int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size, 91 enum dt_map_dev_directive mapping) 92 { 93 enum teecore_memtypes mtype; 94 paddr_t pbase; 95 vaddr_t vbase; 96 size_t sz; 97 int st; 98 99 assert(cpu_mmu_enabled()); 100 101 st = fdt_get_status(fdt, offs); 102 if (st == DT_STATUS_DISABLED) 103 return -1; 104 105 pbase = fdt_reg_base_address(fdt, offs); 106 if (pbase == DT_INFO_INVALID_REG) 107 return -1; 108 sz = fdt_reg_size(fdt, offs); 109 if (sz == DT_INFO_INVALID_REG_SIZE) 110 return -1; 111 112 switch (mapping) { 113 case DT_MAP_AUTO: 114 if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 115 mtype = MEM_AREA_IO_SEC; 116 else 117 mtype = MEM_AREA_IO_NSEC; 118 break; 119 case DT_MAP_SECURE: 120 mtype = MEM_AREA_IO_SEC; 121 break; 122 case DT_MAP_NON_SECURE: 123 mtype = MEM_AREA_IO_NSEC; 124 break; 125 default: 126 panic("Invalid mapping specified"); 127 break; 128 } 129 130 /* Check if we have a mapping, create one if needed */ 131 vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz); 132 if (!vbase) { 133 EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 134 (size_t)sz, pbase); 135 return -1; 136 } 137 138 *base = vbase; 139 *size = sz; 140 return 0; 141 } 142 143 /* Read a physical address (n=1 or 2 cells) */ 144 static paddr_t fdt_read_paddr(const uint32_t *cell, int n) 145 { 146 paddr_t addr; 147 148 if (n < 1 || n > 2) 149 goto bad; 150 151 addr = fdt32_to_cpu(*cell); 152 cell++; 153 if (n == 2) { 154 #ifdef ARM32 155 if (addr) { 156 /* High order 32 bits can't be nonzero */ 157 goto bad; 158 } 159 addr = fdt32_to_cpu(*cell); 160 #else 161 addr = (addr << 32) | fdt32_to_cpu(*cell); 162 #endif 163 } 164 165 return addr; 166 bad: 167 return DT_INFO_INVALID_REG; 168 169 } 170 171 paddr_t fdt_reg_base_address(const void *fdt, int offs) 172 { 173 const void *reg; 174 int ncells; 175 int len; 176 int parent; 177 178 parent = fdt_parent_offset(fdt, offs); 179 if (parent < 0) 180 return DT_INFO_INVALID_REG; 181 182 reg = fdt_getprop(fdt, offs, "reg", &len); 183 if (!reg) 184 return DT_INFO_INVALID_REG; 185 186 ncells = fdt_address_cells(fdt, parent); 187 if (ncells < 0) 188 return DT_INFO_INVALID_REG; 189 190 return fdt_read_paddr(reg, ncells); 191 } 192 193 static size_t fdt_read_size(const uint32_t *cell, int n) 194 { 195 uint32_t sz = 0; 196 197 sz = fdt32_to_cpu(*cell); 198 if (n == 2) { 199 if (sz) 200 return DT_INFO_INVALID_REG_SIZE; 201 202 cell++; 203 sz = fdt32_to_cpu(*cell); 204 } 205 206 return sz; 207 } 208 209 size_t fdt_reg_size(const void *fdt, int offs) 210 { 211 const uint32_t *reg; 212 int n; 213 int len; 214 int parent; 215 216 parent = fdt_parent_offset(fdt, offs); 217 if (parent < 0) 218 return DT_INFO_INVALID_REG_SIZE; 219 220 reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 221 if (!reg) 222 return DT_INFO_INVALID_REG_SIZE; 223 224 n = fdt_address_cells(fdt, parent); 225 if (n < 1 || n > 2) 226 return DT_INFO_INVALID_REG_SIZE; 227 228 reg += n; 229 230 n = fdt_size_cells(fdt, parent); 231 if (n < 1 || n > 2) 232 return DT_INFO_INVALID_REG_SIZE; 233 234 return fdt_read_size(reg, n); 235 } 236 237 static bool is_okay(const char *st, int len) 238 { 239 return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 240 } 241 242 int fdt_get_status(const void *fdt, int offs) 243 { 244 const char *prop; 245 int st = 0; 246 int len; 247 248 prop = fdt_getprop(fdt, offs, "status", &len); 249 if (!prop || is_okay(prop, len)) { 250 /* If status is not specified, it defaults to "okay" */ 251 st |= DT_STATUS_OK_NSEC; 252 } 253 254 prop = fdt_getprop(fdt, offs, "secure-status", &len); 255 if (!prop) { 256 /* 257 * When secure-status is not specified it defaults to the same 258 * value as status 259 */ 260 if (st & DT_STATUS_OK_NSEC) 261 st |= DT_STATUS_OK_SEC; 262 } else { 263 if (is_okay(prop, len)) 264 st |= DT_STATUS_OK_SEC; 265 } 266 267 return st; 268 } 269 270 void fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs) 271 { 272 struct dt_node_info dinfo = { 273 .reg = DT_INFO_INVALID_REG, 274 .reg_size = DT_INFO_INVALID_REG_SIZE, 275 .clock = DT_INFO_INVALID_CLOCK, 276 .reset = DT_INFO_INVALID_RESET, 277 .interrupt = DT_INFO_INVALID_INTERRUPT, 278 }; 279 const fdt32_t *cuint; 280 281 dinfo.reg = fdt_reg_base_address(fdt, offs); 282 dinfo.reg_size = fdt_reg_size(fdt, offs); 283 284 cuint = fdt_getprop(fdt, offs, "clocks", NULL); 285 if (cuint) { 286 cuint++; 287 dinfo.clock = (int)fdt32_to_cpu(*cuint); 288 } 289 290 cuint = fdt_getprop(fdt, offs, "resets", NULL); 291 if (cuint) { 292 cuint++; 293 dinfo.reset = (int)fdt32_to_cpu(*cuint); 294 } 295 296 dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type, 297 &dinfo.prio); 298 299 dinfo.status = fdt_get_status(fdt, offs); 300 301 *info = dinfo; 302 } 303 304 int fdt_read_uint32_array(const void *fdt, int node, const char *prop_name, 305 uint32_t *array, size_t count) 306 { 307 const fdt32_t *cuint = NULL; 308 int len = 0; 309 uint32_t i = 0; 310 311 cuint = fdt_getprop(fdt, node, prop_name, &len); 312 if (!cuint) 313 return len; 314 315 if ((uint32_t)len != (count * sizeof(uint32_t))) 316 return -FDT_ERR_BADLAYOUT; 317 318 for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 319 *array = fdt32_to_cpu(*cuint); 320 array++; 321 cuint++; 322 } 323 324 return 0; 325 } 326 327 int fdt_read_uint32_index(const void *fdt, int node, const char *prop_name, 328 int index, uint32_t *value) 329 { 330 const fdt32_t *cuint = NULL; 331 int len = 0; 332 333 cuint = fdt_getprop(fdt, node, prop_name, &len); 334 if (!cuint) 335 return len; 336 337 if ((uint32_t)len < (sizeof(uint32_t) * (index + 1))) 338 return -FDT_ERR_BADLAYOUT; 339 340 *value = fdt32_to_cpu(cuint[index]); 341 342 return 0; 343 } 344 345 int fdt_read_uint32(const void *fdt, int node, const char *prop_name, 346 uint32_t *value) 347 { 348 return fdt_read_uint32_array(fdt, node, prop_name, value, 1); 349 } 350 351 uint32_t fdt_read_uint32_default(const void *fdt, int node, 352 const char *prop_name, uint32_t dflt_value) 353 { 354 uint32_t ret = dflt_value; 355 356 fdt_read_uint32_index(fdt, node, prop_name, 0, &ret); 357 358 return ret; 359 } 360 361 int fdt_get_reg_props_by_index(const void *fdt, int node, int index, 362 paddr_t *base, size_t *size) 363 { 364 const fdt32_t *prop = NULL; 365 int parent = 0; 366 int len = 0; 367 int address_cells = 0; 368 int size_cells = 0; 369 int cell = 0; 370 371 parent = fdt_parent_offset(fdt, node); 372 if (parent < 0) 373 return parent; 374 375 address_cells = fdt_address_cells(fdt, parent); 376 if (address_cells < 0) 377 return address_cells; 378 379 size_cells = fdt_size_cells(fdt, parent); 380 if (size_cells < 0) 381 return size_cells; 382 383 cell = index * (address_cells + size_cells); 384 385 prop = fdt_getprop(fdt, node, "reg", &len); 386 if (!prop) 387 return len; 388 389 if (((cell + address_cells + size_cells) * (int)sizeof(uint32_t)) > len) 390 return -FDT_ERR_BADVALUE; 391 392 if (base) { 393 *base = fdt_read_paddr(&prop[cell], address_cells); 394 if (*base == DT_INFO_INVALID_REG) 395 return -FDT_ERR_BADVALUE; 396 } 397 398 if (size) { 399 *size = fdt_read_size(&prop[cell + address_cells], size_cells); 400 if (*size == DT_INFO_INVALID_REG_SIZE) 401 return -FDT_ERR_BADVALUE; 402 } 403 404 return 0; 405 } 406 407 int fdt_get_reg_props_by_name(const void *fdt, int node, const char *name, 408 paddr_t *base, size_t *size) 409 { 410 int index = 0; 411 412 index = fdt_stringlist_search(fdt, node, "reg-names", name); 413 if (index < 0) 414 return index; 415 416 return fdt_get_reg_props_by_index(fdt, node, index, base, size); 417 } 418 419 int dt_getprop_as_number(const void *fdt, int nodeoffset, const char *name, 420 uint64_t *num) 421 { 422 const void *prop = NULL; 423 int len = 0; 424 425 prop = fdt_getprop(fdt, nodeoffset, name, &len); 426 if (!prop) 427 return len; 428 429 switch (len) { 430 case sizeof(uint32_t): 431 *num = fdt32_ld(prop); 432 return 0; 433 case sizeof(uint64_t): 434 *num = fdt64_ld(prop); 435 return 0; 436 default: 437 return -FDT_ERR_BADVALUE; 438 } 439 } 440