1 /* $NetBSD: fdt_subr.c,v 1.30 2019/06/14 11:08:18 hkenken 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.30 2019/06/14 11:08:18 hkenken 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(OF_parent(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 if (paddr >= cba && paddr < cba + cl) 241 return fdtbus_decode_range(parent, pba) + (paddr - cba); 242 243 len -= (addr_cells + paddr_cells + size_cells) * 4; 244 } 245 246 /* No mapping found */ 247 return paddr; 248 } 249 250 int 251 fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, 252 bus_size_t *psize) 253 { 254 u_int index; 255 int error; 256 257 error = fdtbus_get_index(phandle, "reg-names", name, &index); 258 if (error != 0) 259 return ENOENT; 260 261 return fdtbus_get_reg(phandle, index, paddr, psize); 262 } 263 264 int 265 fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) 266 { 267 uint64_t addr, size; 268 int error; 269 270 error = fdtbus_get_reg64(phandle, index, &addr, &size); 271 if (error) 272 return error; 273 274 if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) 275 return ERANGE; 276 277 if (paddr) 278 *paddr = (bus_addr_t)addr; 279 if (psize) 280 *psize = (bus_size_t)size; 281 282 return 0; 283 } 284 285 int 286 fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) 287 { 288 uint64_t addr, size; 289 const uint8_t *buf; 290 int len; 291 292 const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); 293 const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); 294 if (addr_cells == -1 || size_cells == -1) 295 return EINVAL; 296 297 buf = fdt_getprop(fdtbus_get_data(), 298 fdtbus_phandle2offset(phandle), "reg", &len); 299 if (buf == NULL || len <= 0) 300 return EINVAL; 301 302 const u_int reglen = size_cells * 4 + addr_cells * 4; 303 if (reglen == 0) 304 return EINVAL; 305 306 if (index >= len / reglen) 307 return ENXIO; 308 309 buf += index * reglen; 310 addr = fdtbus_get_cells(buf, addr_cells); 311 buf += addr_cells * 4; 312 size = fdtbus_get_cells(buf, size_cells); 313 314 if (paddr) { 315 *paddr = fdtbus_decode_range(OF_parent(phandle), addr); 316 #ifdef FDTBUS_DEBUG 317 const char *name = fdt_get_name(fdtbus_get_data(), 318 fdtbus_phandle2offset(phandle), NULL); 319 printf("fdt: [%s] decoded addr #%u: %" PRIx64 320 " -> %" PRIx64 "\n", name, index, addr, *paddr); 321 #endif 322 } 323 if (psize) 324 *psize = size; 325 326 return 0; 327 } 328 329 #if defined(FDT) 330 const struct fdt_console * 331 fdtbus_get_console(void) 332 { 333 static const struct fdt_console_info *booted_console = NULL; 334 335 if (booted_console == NULL) { 336 __link_set_decl(fdt_consoles, struct fdt_console_info); 337 struct fdt_console_info * const *info; 338 const struct fdt_console_info *best_info = NULL; 339 const int phandle = fdtbus_get_stdout_phandle(); 340 int best_match = 0; 341 342 __link_set_foreach(info, fdt_consoles) { 343 const int match = (*info)->ops->match(phandle); 344 if (match > best_match) { 345 best_match = match; 346 best_info = *info; 347 } 348 } 349 350 booted_console = best_info; 351 } 352 353 return booted_console == NULL ? NULL : booted_console->ops; 354 } 355 #endif 356 357 const char * 358 fdtbus_get_stdout_path(void) 359 { 360 const char *prop; 361 362 const int off = fdt_path_offset(fdtbus_get_data(), "/chosen"); 363 if (off < 0) 364 return NULL; 365 366 prop = fdt_getprop(fdtbus_get_data(), off, "stdout-path", NULL); 367 if (prop != NULL) 368 return prop; 369 370 /* If the stdout-path property is not found, assume serial0 */ 371 return "serial0:115200n8"; 372 } 373 374 int 375 fdtbus_get_stdout_phandle(void) 376 { 377 const char *prop, *p; 378 int off, len; 379 380 prop = fdtbus_get_stdout_path(); 381 if (prop == NULL) 382 return -1; 383 384 p = strchr(prop, ':'); 385 len = p == NULL ? strlen(prop) : (p - prop); 386 if (*prop != '/') { 387 /* Alias */ 388 prop = fdt_get_alias_namelen(fdtbus_get_data(), prop, len); 389 if (prop == NULL) 390 return -1; 391 len = strlen(prop); 392 } 393 off = fdt_path_offset_namelen(fdtbus_get_data(), prop, len); 394 if (off < 0) 395 return -1; 396 397 return fdtbus_offset2phandle(off); 398 } 399 400 int 401 fdtbus_get_stdout_speed(void) 402 { 403 const char *prop, *p; 404 405 prop = fdtbus_get_stdout_path(); 406 if (prop == NULL) 407 return -1; 408 409 p = strchr(prop, ':'); 410 if (p == NULL) 411 return -1; 412 413 return (int)strtoul(p + 1, NULL, 10); 414 } 415 416 tcflag_t 417 fdtbus_get_stdout_flags(void) 418 { 419 const char *prop, *p; 420 tcflag_t flags = TTYDEF_CFLAG; 421 char *ep; 422 423 prop = fdtbus_get_stdout_path(); 424 if (prop == NULL) 425 return flags; 426 427 p = strchr(prop, ':'); 428 if (p == NULL) 429 return flags; 430 431 ep = NULL; 432 (void)strtoul(p + 1, &ep, 10); 433 if (ep == NULL) 434 return flags; 435 436 /* <baud>{<parity>{<bits>{<flow>}}} */ 437 while (*ep) { 438 switch (*ep) { 439 /* parity */ 440 case 'n': flags &= ~(PARENB|PARODD); break; 441 case 'e': flags &= ~PARODD; flags |= PARENB; break; 442 case 'o': flags |= (PARENB|PARODD); break; 443 /* bits */ 444 case '5': flags &= ~CSIZE; flags |= CS5; break; 445 case '6': flags &= ~CSIZE; flags |= CS6; break; 446 case '7': flags &= ~CSIZE; flags |= CS7; break; 447 case '8': flags &= ~CSIZE; flags |= CS8; break; 448 /* flow */ 449 case 'r': flags |= CRTSCTS; break; 450 } 451 ep++; 452 } 453 454 return flags; 455 } 456 457 bool 458 fdtbus_status_okay(int phandle) 459 { 460 const int off = fdtbus_phandle2offset(phandle); 461 462 const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); 463 if (prop == NULL) 464 return true; 465 466 return strncmp(prop, "ok", 2) == 0; 467 } 468 469 const void * 470 fdtbus_get_prop(int phandle, const char *prop, int *plen) 471 { 472 const int off = fdtbus_phandle2offset(phandle); 473 474 return fdt_getprop(fdtbus_get_data(), off, prop, plen); 475 } 476 477 const char * 478 fdtbus_get_string(int phandle, const char *prop) 479 { 480 const int off = fdtbus_phandle2offset(phandle); 481 482 if (strcmp(prop, "name") == 0) 483 return fdt_get_name(fdtbus_get_data(), off, NULL); 484 else 485 return fdt_getprop(fdtbus_get_data(), off, prop, NULL); 486 } 487 488 const char * 489 fdtbus_get_string_index(int phandle, const char *prop, u_int index) 490 { 491 const char *names, *name; 492 int len, cur; 493 494 if ((len = OF_getproplen(phandle, prop)) < 0) 495 return NULL; 496 497 names = fdtbus_get_string(phandle, prop); 498 499 for (name = names, cur = 0; len > 0; 500 len -= strlen(name) + 1, name += strlen(name) + 1, cur++) { 501 if (index == cur) 502 return name; 503 } 504 505 return NULL; 506 } 507 508 int 509 fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) 510 { 511 const char *p; 512 size_t pl; 513 u_int index; 514 int len; 515 516 p = fdtbus_get_prop(phandle, prop, &len); 517 if (p == NULL || len <= 0) 518 return -1; 519 520 for (index = 0; len > 0; 521 pl = strlen(p) + 1, len -= pl, p += pl, index++) { 522 if (strcmp(p, name) == 0) { 523 *idx = index; 524 return 0; 525 } 526 } 527 528 return -1; 529 } 530