1 /* $NetBSD: fdt_subr.c,v 1.29 2019/02/27 16:56:00 jakllsch 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.29 2019/02/27 16:56:00 jakllsch 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_from_native(int phandle) 134 { 135 const int off = fdt_node_offset_by_phandle(fdt_data, phandle); 136 if (off < 0) { 137 return -1; 138 } 139 return fdtbus_offset2phandle(off); 140 } 141 142 bool 143 fdtbus_get_path(int phandle, char *buf, size_t buflen) 144 { 145 const int off = fdtbus_phandle2offset(phandle); 146 if (off < 0) { 147 return false; 148 } 149 if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) { 150 return false; 151 } 152 return true; 153 } 154 155 static uint64_t 156 fdtbus_get_cells(const uint8_t *buf, int cells) 157 { 158 switch (cells) { 159 case 0: return 0; 160 case 1: return be32dec(buf); 161 case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4); 162 default: panic("fdtbus_get_cells: bad cells val %d\n", cells); 163 } 164 } 165 166 static uint64_t 167 fdtbus_decode_range(int phandle, uint64_t paddr) 168 { 169 const int parent = OF_parent(phandle); 170 if (parent == -1) 171 return paddr; 172 173 if (!fdtbus_decoderegprop) 174 return paddr; 175 176 const uint8_t *buf; 177 int len; 178 179 buf = fdt_getprop(fdtbus_get_data(), 180 fdtbus_phandle2offset(phandle), "ranges", &len); 181 if (buf == NULL) 182 return paddr; 183 184 if (len == 0) { 185 /* pass through to parent */ 186 return fdtbus_decode_range(parent, paddr); 187 } 188 189 const int addr_cells = fdtbus_get_addr_cells(phandle); 190 const int size_cells = fdtbus_get_size_cells(phandle); 191 const int paddr_cells = fdtbus_get_addr_cells(OF_parent(parent)); 192 if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) 193 return paddr; 194 195 while (len > 0) { 196 uint64_t cba, pba, cl; 197 cba = fdtbus_get_cells(buf, addr_cells); 198 buf += addr_cells * 4; 199 pba = fdtbus_get_cells(buf, paddr_cells); 200 buf += paddr_cells * 4; 201 cl = fdtbus_get_cells(buf, size_cells); 202 buf += size_cells * 4; 203 204 if (paddr >= cba && paddr < cba + cl) 205 return fdtbus_decode_range(parent, pba) + (paddr - cba); 206 207 len -= (addr_cells + paddr_cells + size_cells) * 4; 208 } 209 210 /* No mapping found */ 211 return paddr; 212 } 213 214 int 215 fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, 216 bus_size_t *psize) 217 { 218 u_int index; 219 int error; 220 221 error = fdtbus_get_index(phandle, "reg-names", name, &index); 222 if (error != 0) 223 return ENOENT; 224 225 return fdtbus_get_reg(phandle, index, paddr, psize); 226 } 227 228 int 229 fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) 230 { 231 uint64_t addr, size; 232 int error; 233 234 error = fdtbus_get_reg64(phandle, index, &addr, &size); 235 if (error) 236 return error; 237 238 if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) 239 return ERANGE; 240 241 if (paddr) 242 *paddr = (bus_addr_t)addr; 243 if (psize) 244 *psize = (bus_size_t)size; 245 246 return 0; 247 } 248 249 int 250 fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) 251 { 252 uint64_t addr, size; 253 const uint8_t *buf; 254 int len; 255 256 const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); 257 const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); 258 if (addr_cells == -1 || size_cells == -1) 259 return EINVAL; 260 261 buf = fdt_getprop(fdtbus_get_data(), 262 fdtbus_phandle2offset(phandle), "reg", &len); 263 if (buf == NULL || len <= 0) 264 return EINVAL; 265 266 const u_int reglen = size_cells * 4 + addr_cells * 4; 267 if (reglen == 0) 268 return EINVAL; 269 270 if (index >= len / reglen) 271 return ENXIO; 272 273 buf += index * reglen; 274 addr = fdtbus_get_cells(buf, addr_cells); 275 buf += addr_cells * 4; 276 size = fdtbus_get_cells(buf, size_cells); 277 278 if (paddr) { 279 *paddr = fdtbus_decode_range(OF_parent(phandle), addr); 280 #ifdef FDTBUS_DEBUG 281 const char *name = fdt_get_name(fdtbus_get_data(), 282 fdtbus_phandle2offset(phandle), NULL); 283 printf("fdt: [%s] decoded addr #%u: %" PRIx64 284 " -> %" PRIx64 "\n", name, index, addr, *paddr); 285 #endif 286 } 287 if (psize) 288 *psize = size; 289 290 return 0; 291 } 292 293 #if defined(FDT) 294 const struct fdt_console * 295 fdtbus_get_console(void) 296 { 297 static const struct fdt_console_info *booted_console = NULL; 298 299 if (booted_console == NULL) { 300 __link_set_decl(fdt_consoles, struct fdt_console_info); 301 struct fdt_console_info * const *info; 302 const struct fdt_console_info *best_info = NULL; 303 const int phandle = fdtbus_get_stdout_phandle(); 304 int best_match = 0; 305 306 __link_set_foreach(info, fdt_consoles) { 307 const int match = (*info)->ops->match(phandle); 308 if (match > best_match) { 309 best_match = match; 310 best_info = *info; 311 } 312 } 313 314 booted_console = best_info; 315 } 316 317 return booted_console == NULL ? NULL : booted_console->ops; 318 } 319 #endif 320 321 const char * 322 fdtbus_get_stdout_path(void) 323 { 324 const char *prop; 325 326 const int off = fdt_path_offset(fdtbus_get_data(), "/chosen"); 327 if (off < 0) 328 return NULL; 329 330 prop = fdt_getprop(fdtbus_get_data(), off, "stdout-path", NULL); 331 if (prop != NULL) 332 return prop; 333 334 /* If the stdout-path property is not found, assume serial0 */ 335 return "serial0:115200n8"; 336 } 337 338 int 339 fdtbus_get_stdout_phandle(void) 340 { 341 const char *prop, *p; 342 int off, len; 343 344 prop = fdtbus_get_stdout_path(); 345 if (prop == NULL) 346 return -1; 347 348 p = strchr(prop, ':'); 349 len = p == NULL ? strlen(prop) : (p - prop); 350 if (*prop != '/') { 351 /* Alias */ 352 prop = fdt_get_alias_namelen(fdtbus_get_data(), prop, len); 353 if (prop == NULL) 354 return -1; 355 len = strlen(prop); 356 } 357 off = fdt_path_offset_namelen(fdtbus_get_data(), prop, len); 358 if (off < 0) 359 return -1; 360 361 return fdtbus_offset2phandle(off); 362 } 363 364 int 365 fdtbus_get_stdout_speed(void) 366 { 367 const char *prop, *p; 368 369 prop = fdtbus_get_stdout_path(); 370 if (prop == NULL) 371 return -1; 372 373 p = strchr(prop, ':'); 374 if (p == NULL) 375 return -1; 376 377 return (int)strtoul(p + 1, NULL, 10); 378 } 379 380 tcflag_t 381 fdtbus_get_stdout_flags(void) 382 { 383 const char *prop, *p; 384 tcflag_t flags = TTYDEF_CFLAG; 385 char *ep; 386 387 prop = fdtbus_get_stdout_path(); 388 if (prop == NULL) 389 return flags; 390 391 p = strchr(prop, ':'); 392 if (p == NULL) 393 return flags; 394 395 ep = NULL; 396 (void)strtoul(p + 1, &ep, 10); 397 if (ep == NULL) 398 return flags; 399 400 /* <baud>{<parity>{<bits>{<flow>}}} */ 401 while (*ep) { 402 switch (*ep) { 403 /* parity */ 404 case 'n': flags &= ~(PARENB|PARODD); break; 405 case 'e': flags &= ~PARODD; flags |= PARENB; break; 406 case 'o': flags |= (PARENB|PARODD); break; 407 /* bits */ 408 case '5': flags &= ~CSIZE; flags |= CS5; break; 409 case '6': flags &= ~CSIZE; flags |= CS6; break; 410 case '7': flags &= ~CSIZE; flags |= CS7; break; 411 case '8': flags &= ~CSIZE; flags |= CS8; break; 412 /* flow */ 413 case 'r': flags |= CRTSCTS; break; 414 } 415 ep++; 416 } 417 418 return flags; 419 } 420 421 bool 422 fdtbus_status_okay(int phandle) 423 { 424 const int off = fdtbus_phandle2offset(phandle); 425 426 const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); 427 if (prop == NULL) 428 return true; 429 430 return strncmp(prop, "ok", 2) == 0; 431 } 432 433 const void * 434 fdtbus_get_prop(int phandle, const char *prop, int *plen) 435 { 436 const int off = fdtbus_phandle2offset(phandle); 437 438 return fdt_getprop(fdtbus_get_data(), off, prop, plen); 439 } 440 441 const char * 442 fdtbus_get_string(int phandle, const char *prop) 443 { 444 const int off = fdtbus_phandle2offset(phandle); 445 446 if (strcmp(prop, "name") == 0) 447 return fdt_get_name(fdtbus_get_data(), off, NULL); 448 else 449 return fdt_getprop(fdtbus_get_data(), off, prop, NULL); 450 } 451 452 const char * 453 fdtbus_get_string_index(int phandle, const char *prop, u_int index) 454 { 455 const char *names, *name; 456 int len, cur; 457 458 if ((len = OF_getproplen(phandle, prop)) < 0) 459 return NULL; 460 461 names = fdtbus_get_string(phandle, prop); 462 463 for (name = names, cur = 0; len > 0; 464 len -= strlen(name) + 1, name += strlen(name) + 1, cur++) { 465 if (index == cur) 466 return name; 467 } 468 469 return NULL; 470 } 471 472 int 473 fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) 474 { 475 const char *p; 476 size_t pl; 477 u_int index; 478 int len; 479 480 p = fdtbus_get_prop(phandle, prop, &len); 481 if (p == NULL || len <= 0) 482 return -1; 483 484 for (index = 0; len > 0; 485 pl = strlen(p) + 1, len -= pl, p += pl, index++) { 486 if (strcmp(p, name) == 0) { 487 *idx = index; 488 return 0; 489 } 490 } 491 492 return -1; 493 } 494