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