1 /* $NetBSD: fdt_subr.c,v 1.31 2019/09/24 15:23:34 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.31 2019/09/24 15:23:34 jmcneill Exp $"); 31 32 #include "opt_fdt.h" 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 37 #include <libfdt.h> 38 #include <dev/fdt/fdtvar.h> 39 40 static const void *fdt_data; 41 42 static struct fdt_conslist fdt_console_list = 43 TAILQ_HEAD_INITIALIZER(fdt_console_list); 44 45 bool 46 fdtbus_set_data(const void *data) 47 { 48 KASSERT(fdt_data == NULL); 49 if (fdt_check_header(data) != 0) { 50 return false; 51 } 52 fdt_data = data; 53 return true; 54 } 55 56 const void * 57 fdtbus_get_data(void) 58 { 59 return fdt_data; 60 } 61 62 int 63 fdtbus_offset2phandle(int offset) 64 { 65 if (offset < 0) 66 return 0; 67 68 return offset + fdt_off_dt_struct(fdt_data); 69 } 70 71 int 72 fdtbus_phandle2offset(int phandle) 73 { 74 const int dtoff = fdt_off_dt_struct(fdt_data); 75 76 if (phandle == -1) 77 phandle = dtoff; 78 79 if (phandle < dtoff) 80 return -1; 81 82 return phandle - dtoff; 83 } 84 85 static bool fdtbus_decoderegprop = true; 86 87 void 88 fdtbus_set_decoderegprop(bool decode) 89 { 90 fdtbus_decoderegprop = decode; 91 } 92 93 static int 94 fdtbus_get_addr_cells(int phandle) 95 { 96 uint32_t addr_cells; 97 98 if (of_getprop_uint32(phandle, "#address-cells", &addr_cells)) 99 addr_cells = 2; 100 101 return addr_cells; 102 } 103 104 static int 105 fdtbus_get_size_cells(int phandle) 106 { 107 uint32_t size_cells; 108 109 if (of_getprop_uint32(phandle, "#size-cells", &size_cells)) 110 size_cells = 0; 111 112 return size_cells; 113 } 114 115 int 116 fdtbus_get_phandle(int phandle, const char *prop) 117 { 118 u_int phandle_ref; 119 const u_int *buf; 120 int len; 121 122 buf = fdt_getprop(fdtbus_get_data(), 123 fdtbus_phandle2offset(phandle), prop, &len); 124 if (buf == NULL || len < sizeof(phandle_ref)) 125 return -1; 126 127 phandle_ref = be32dec(buf); 128 129 return fdtbus_get_phandle_from_native(phandle_ref); 130 } 131 132 int 133 fdtbus_get_phandle_with_data(int phandle, const char *prop, const char *cells, 134 int index, struct fdt_phandle_data *data) 135 { 136 int len; 137 const int offset = 1; 138 139 const u_int *p = fdtbus_get_prop(phandle, prop, &len); 140 if (p == NULL || len <= 0) 141 return EINVAL; 142 143 for (int i = 0; len > 0; i++) { 144 u_int phandle_ref = be32toh(*p); 145 const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref); 146 uint32_t cells_num; 147 of_getprop_uint32(iparent, cells, &cells_num); 148 149 if (index == i) { 150 if (data != NULL) { 151 data->phandle = iparent; 152 data->count = cells_num; 153 data->values = p + offset; 154 } 155 goto done; 156 } 157 158 const u_int reclen = offset + cells_num; 159 len -= reclen * sizeof(u_int); 160 p += reclen; 161 } 162 return EINVAL; 163 164 done: 165 return 0; 166 } 167 168 int 169 fdtbus_get_phandle_from_native(int phandle) 170 { 171 const int off = fdt_node_offset_by_phandle(fdt_data, phandle); 172 if (off < 0) { 173 return -1; 174 } 175 return fdtbus_offset2phandle(off); 176 } 177 178 bool 179 fdtbus_get_path(int phandle, char *buf, size_t buflen) 180 { 181 const int off = fdtbus_phandle2offset(phandle); 182 if (off < 0) { 183 return false; 184 } 185 if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) { 186 return false; 187 } 188 return true; 189 } 190 191 static uint64_t 192 fdtbus_get_cells(const uint8_t *buf, int cells) 193 { 194 switch (cells) { 195 case 0: return 0; 196 case 1: return be32dec(buf); 197 case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4); 198 default: panic("fdtbus_get_cells: bad cells val %d\n", cells); 199 } 200 } 201 202 static uint64_t 203 fdtbus_decode_range(int phandle, uint64_t paddr) 204 { 205 const int parent = OF_parent(phandle); 206 if (parent == -1) 207 return paddr; 208 209 if (!fdtbus_decoderegprop) 210 return paddr; 211 212 const uint8_t *buf; 213 int len; 214 215 buf = fdt_getprop(fdtbus_get_data(), 216 fdtbus_phandle2offset(phandle), "ranges", &len); 217 if (buf == NULL) 218 return paddr; 219 220 if (len == 0) { 221 /* pass through to parent */ 222 return fdtbus_decode_range(parent, paddr); 223 } 224 225 const int addr_cells = fdtbus_get_addr_cells(phandle); 226 const int size_cells = fdtbus_get_size_cells(phandle); 227 const int paddr_cells = fdtbus_get_addr_cells(parent); 228 if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) 229 return paddr; 230 231 while (len > 0) { 232 uint64_t cba, pba, cl; 233 cba = fdtbus_get_cells(buf, addr_cells); 234 buf += addr_cells * 4; 235 pba = fdtbus_get_cells(buf, paddr_cells); 236 buf += paddr_cells * 4; 237 cl = fdtbus_get_cells(buf, size_cells); 238 buf += size_cells * 4; 239 240 #ifdef FDTBUS_DEBUG 241 printf("%s: %s: cba=0x%#" PRIx64 ", pba=0x%#" PRIx64 ", cl=0x%#" PRIx64 "\n", __func__, fdt_get_name(fdtbus_get_data(), fdtbus_phandle2offset(phandle), NULL), cba, pba, cl); 242 #endif 243 244 if (paddr >= cba && paddr < cba + cl) 245 return fdtbus_decode_range(parent, pba) + (paddr - cba); 246 247 len -= (addr_cells + paddr_cells + size_cells) * 4; 248 } 249 250 /* No mapping found */ 251 return paddr; 252 } 253 254 int 255 fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, 256 bus_size_t *psize) 257 { 258 u_int index; 259 int error; 260 261 error = fdtbus_get_index(phandle, "reg-names", name, &index); 262 if (error != 0) 263 return ENOENT; 264 265 return fdtbus_get_reg(phandle, index, paddr, psize); 266 } 267 268 int 269 fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) 270 { 271 uint64_t addr, size; 272 int error; 273 274 error = fdtbus_get_reg64(phandle, index, &addr, &size); 275 if (error) 276 return error; 277 278 if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) 279 return ERANGE; 280 281 if (paddr) 282 *paddr = (bus_addr_t)addr; 283 if (psize) 284 *psize = (bus_size_t)size; 285 286 return 0; 287 } 288 289 int 290 fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) 291 { 292 uint64_t addr, size; 293 const uint8_t *buf; 294 int len; 295 296 const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); 297 const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); 298 if (addr_cells == -1 || size_cells == -1) 299 return EINVAL; 300 301 buf = fdt_getprop(fdtbus_get_data(), 302 fdtbus_phandle2offset(phandle), "reg", &len); 303 if (buf == NULL || len <= 0) 304 return EINVAL; 305 306 const u_int reglen = size_cells * 4 + addr_cells * 4; 307 if (reglen == 0) 308 return EINVAL; 309 310 if (index >= len / reglen) 311 return ENXIO; 312 313 buf += index * reglen; 314 addr = fdtbus_get_cells(buf, addr_cells); 315 buf += addr_cells * 4; 316 size = fdtbus_get_cells(buf, size_cells); 317 318 if (paddr) { 319 *paddr = fdtbus_decode_range(OF_parent(phandle), addr); 320 #ifdef FDTBUS_DEBUG 321 const char *name = fdt_get_name(fdtbus_get_data(), 322 fdtbus_phandle2offset(phandle), NULL); 323 printf("fdt: [%s] decoded addr #%u: %" PRIx64 324 " -> %" PRIx64 "\n", name, index, addr, *paddr); 325 #endif 326 } 327 if (psize) 328 *psize = size; 329 330 return 0; 331 } 332 333 #if defined(FDT) 334 const struct fdt_console * 335 fdtbus_get_console(void) 336 { 337 static const struct fdt_console_info *booted_console = NULL; 338 339 if (booted_console == NULL) { 340 __link_set_decl(fdt_consoles, struct fdt_console_info); 341 struct fdt_console_info * const *info; 342 const struct fdt_console_info *best_info = NULL; 343 const int phandle = fdtbus_get_stdout_phandle(); 344 int best_match = 0; 345 346 __link_set_foreach(info, fdt_consoles) { 347 const int match = (*info)->ops->match(phandle); 348 if (match > best_match) { 349 best_match = match; 350 best_info = *info; 351 } 352 } 353 354 booted_console = best_info; 355 } 356 357 return booted_console == NULL ? NULL : booted_console->ops; 358 } 359 #endif 360 361 const char * 362 fdtbus_get_stdout_path(void) 363 { 364 const char *prop; 365 366 const int off = fdt_path_offset(fdtbus_get_data(), "/chosen"); 367 if (off < 0) 368 return NULL; 369 370 prop = fdt_getprop(fdtbus_get_data(), off, "stdout-path", NULL); 371 if (prop != NULL) 372 return prop; 373 374 /* If the stdout-path property is not found, assume serial0 */ 375 return "serial0:115200n8"; 376 } 377 378 int 379 fdtbus_get_stdout_phandle(void) 380 { 381 const char *prop, *p; 382 int off, len; 383 384 prop = fdtbus_get_stdout_path(); 385 if (prop == NULL) 386 return -1; 387 388 p = strchr(prop, ':'); 389 len = p == NULL ? strlen(prop) : (p - prop); 390 if (*prop != '/') { 391 /* Alias */ 392 prop = fdt_get_alias_namelen(fdtbus_get_data(), prop, len); 393 if (prop == NULL) 394 return -1; 395 len = strlen(prop); 396 } 397 off = fdt_path_offset_namelen(fdtbus_get_data(), prop, len); 398 if (off < 0) 399 return -1; 400 401 return fdtbus_offset2phandle(off); 402 } 403 404 int 405 fdtbus_get_stdout_speed(void) 406 { 407 const char *prop, *p; 408 409 prop = fdtbus_get_stdout_path(); 410 if (prop == NULL) 411 return -1; 412 413 p = strchr(prop, ':'); 414 if (p == NULL) 415 return -1; 416 417 return (int)strtoul(p + 1, NULL, 10); 418 } 419 420 tcflag_t 421 fdtbus_get_stdout_flags(void) 422 { 423 const char *prop, *p; 424 tcflag_t flags = TTYDEF_CFLAG; 425 char *ep; 426 427 prop = fdtbus_get_stdout_path(); 428 if (prop == NULL) 429 return flags; 430 431 p = strchr(prop, ':'); 432 if (p == NULL) 433 return flags; 434 435 ep = NULL; 436 (void)strtoul(p + 1, &ep, 10); 437 if (ep == NULL) 438 return flags; 439 440 /* <baud>{<parity>{<bits>{<flow>}}} */ 441 while (*ep) { 442 switch (*ep) { 443 /* parity */ 444 case 'n': flags &= ~(PARENB|PARODD); break; 445 case 'e': flags &= ~PARODD; flags |= PARENB; break; 446 case 'o': flags |= (PARENB|PARODD); break; 447 /* bits */ 448 case '5': flags &= ~CSIZE; flags |= CS5; break; 449 case '6': flags &= ~CSIZE; flags |= CS6; break; 450 case '7': flags &= ~CSIZE; flags |= CS7; break; 451 case '8': flags &= ~CSIZE; flags |= CS8; break; 452 /* flow */ 453 case 'r': flags |= CRTSCTS; break; 454 } 455 ep++; 456 } 457 458 return flags; 459 } 460 461 bool 462 fdtbus_status_okay(int phandle) 463 { 464 const int off = fdtbus_phandle2offset(phandle); 465 466 const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); 467 if (prop == NULL) 468 return true; 469 470 return strncmp(prop, "ok", 2) == 0; 471 } 472 473 const void * 474 fdtbus_get_prop(int phandle, const char *prop, int *plen) 475 { 476 const int off = fdtbus_phandle2offset(phandle); 477 478 return fdt_getprop(fdtbus_get_data(), off, prop, plen); 479 } 480 481 const char * 482 fdtbus_get_string(int phandle, const char *prop) 483 { 484 const int off = fdtbus_phandle2offset(phandle); 485 486 if (strcmp(prop, "name") == 0) 487 return fdt_get_name(fdtbus_get_data(), off, NULL); 488 else 489 return fdt_getprop(fdtbus_get_data(), off, prop, NULL); 490 } 491 492 const char * 493 fdtbus_get_string_index(int phandle, const char *prop, u_int index) 494 { 495 const char *names, *name; 496 int len, cur; 497 498 if ((len = OF_getproplen(phandle, prop)) < 0) 499 return NULL; 500 501 names = fdtbus_get_string(phandle, prop); 502 503 for (name = names, cur = 0; len > 0; 504 len -= strlen(name) + 1, name += strlen(name) + 1, cur++) { 505 if (index == cur) 506 return name; 507 } 508 509 return NULL; 510 } 511 512 int 513 fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) 514 { 515 const char *p; 516 size_t pl; 517 u_int index; 518 int len; 519 520 p = fdtbus_get_prop(phandle, prop, &len); 521 if (p == NULL || len <= 0) 522 return -1; 523 524 for (index = 0; len > 0; 525 pl = strlen(p) + 1, len -= pl, p += pl, index++) { 526 if (strcmp(p, name) == 0) { 527 *idx = index; 528 return 0; 529 } 530 } 531 532 return -1; 533 } 534