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