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