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