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