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