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