1 /*- 2 * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>. 3 * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer, 11 * without modification, immediately at the beginning of the file. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include "opt_platform.h" 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/bus.h> 37 #include <sys/errno.h> 38 #include <sys/libkern.h> 39 40 #include <machine/resource.h> 41 42 #include <dev/ofw/ofw_bus.h> 43 #include <dev/ofw/ofw_bus_subr.h> 44 #include <dev/ofw/openfirm.h> 45 46 #include "ofw_bus_if.h" 47 48 #define OFW_COMPAT_LEN 255 49 #define OFW_STATUS_LEN 16 50 51 int 52 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) 53 { 54 55 if (obd == NULL) 56 return (ENOMEM); 57 /* The 'name' property is considered mandatory. */ 58 if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) 59 return (EINVAL); 60 OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); 61 OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); 62 OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); 63 OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status); 64 obd->obd_node = node; 65 return (0); 66 } 67 68 void 69 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 70 { 71 72 if (obd == NULL) 73 return; 74 if (obd->obd_compat != NULL) 75 free(obd->obd_compat, M_OFWPROP); 76 if (obd->obd_model != NULL) 77 free(obd->obd_model, M_OFWPROP); 78 if (obd->obd_name != NULL) 79 free(obd->obd_name, M_OFWPROP); 80 if (obd->obd_type != NULL) 81 free(obd->obd_type, M_OFWPROP); 82 if (obd->obd_status != NULL) 83 free(obd->obd_status, M_OFWPROP); 84 } 85 86 int 87 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 88 size_t buflen) 89 { 90 91 if (ofw_bus_get_name(child) != NULL) { 92 strlcat(buf, "name=", buflen); 93 strlcat(buf, ofw_bus_get_name(child), buflen); 94 } 95 96 if (ofw_bus_get_compat(child) != NULL) { 97 strlcat(buf, " compat=", buflen); 98 strlcat(buf, ofw_bus_get_compat(child), buflen); 99 } 100 return (0); 101 }; 102 103 const char * 104 ofw_bus_gen_get_compat(device_t bus, device_t dev) 105 { 106 const struct ofw_bus_devinfo *obd; 107 108 obd = OFW_BUS_GET_DEVINFO(bus, dev); 109 if (obd == NULL) 110 return (NULL); 111 return (obd->obd_compat); 112 } 113 114 const char * 115 ofw_bus_gen_get_model(device_t bus, device_t dev) 116 { 117 const struct ofw_bus_devinfo *obd; 118 119 obd = OFW_BUS_GET_DEVINFO(bus, dev); 120 if (obd == NULL) 121 return (NULL); 122 return (obd->obd_model); 123 } 124 125 const char * 126 ofw_bus_gen_get_name(device_t bus, device_t dev) 127 { 128 const struct ofw_bus_devinfo *obd; 129 130 obd = OFW_BUS_GET_DEVINFO(bus, dev); 131 if (obd == NULL) 132 return (NULL); 133 return (obd->obd_name); 134 } 135 136 phandle_t 137 ofw_bus_gen_get_node(device_t bus, device_t dev) 138 { 139 const struct ofw_bus_devinfo *obd; 140 141 obd = OFW_BUS_GET_DEVINFO(bus, dev); 142 if (obd == NULL) 143 return (0); 144 return (obd->obd_node); 145 } 146 147 const char * 148 ofw_bus_gen_get_type(device_t bus, device_t dev) 149 { 150 const struct ofw_bus_devinfo *obd; 151 152 obd = OFW_BUS_GET_DEVINFO(bus, dev); 153 if (obd == NULL) 154 return (NULL); 155 return (obd->obd_type); 156 } 157 158 const char * 159 ofw_bus_get_status(device_t dev) 160 { 161 const struct ofw_bus_devinfo *obd; 162 163 obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); 164 if (obd == NULL) 165 return (NULL); 166 167 return (obd->obd_status); 168 } 169 170 int 171 ofw_bus_status_okay(device_t dev) 172 { 173 const char *status; 174 175 status = ofw_bus_get_status(dev); 176 if (status == NULL || strcmp(status, "okay") == 0 || 177 strcmp(status, "ok") == 0) 178 return (1); 179 180 return (0); 181 } 182 183 int 184 ofw_bus_node_status_okay(phandle_t node) 185 { 186 char status[OFW_STATUS_LEN]; 187 int len; 188 189 len = OF_getproplen(node, "status"); 190 if (len <= 0) 191 return (1); 192 193 OF_getprop(node, "status", status, OFW_STATUS_LEN); 194 if ((len == 5 && (bcmp(status, "okay", len) == 0)) || 195 (len == 3 && (bcmp(status, "ok", len)))) 196 return (1); 197 198 return (0); 199 } 200 201 static int 202 ofw_bus_node_is_compatible_int(const char *compat, int len, 203 const char *onecompat) 204 { 205 int onelen, l, ret; 206 207 onelen = strlen(onecompat); 208 209 ret = 0; 210 while (len > 0) { 211 if (strlen(compat) == onelen && 212 strncasecmp(compat, onecompat, onelen) == 0) { 213 /* Found it. */ 214 ret = 1; 215 break; 216 } 217 218 /* Slide to the next sub-string. */ 219 l = strlen(compat) + 1; 220 compat += l; 221 len -= l; 222 } 223 224 return (ret); 225 } 226 227 int 228 ofw_bus_node_is_compatible(phandle_t node, const char *compatstr) 229 { 230 char compat[OFW_COMPAT_LEN]; 231 int len, rv; 232 233 if ((len = OF_getproplen(node, "compatible")) <= 0) 234 return (0); 235 236 bzero(compat, OFW_COMPAT_LEN); 237 238 if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0) 239 return (0); 240 241 rv = ofw_bus_node_is_compatible_int(compat, len, compatstr); 242 243 return (rv); 244 } 245 246 int 247 ofw_bus_is_compatible(device_t dev, const char *onecompat) 248 { 249 phandle_t node; 250 const char *compat; 251 int len; 252 253 if ((compat = ofw_bus_get_compat(dev)) == NULL) 254 return (0); 255 256 if ((node = ofw_bus_get_node(dev)) == -1) 257 return (0); 258 259 /* Get total 'compatible' prop len */ 260 if ((len = OF_getproplen(node, "compatible")) <= 0) 261 return (0); 262 263 return (ofw_bus_node_is_compatible_int(compat, len, onecompat)); 264 } 265 266 int 267 ofw_bus_is_compatible_strict(device_t dev, const char *compatible) 268 { 269 const char *compat; 270 size_t len; 271 272 if ((compat = ofw_bus_get_compat(dev)) == NULL) 273 return (0); 274 275 len = strlen(compatible); 276 if (strlen(compat) == len && 277 strncasecmp(compat, compatible, len) == 0) 278 return (1); 279 280 return (0); 281 } 282 283 const struct ofw_compat_data * 284 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) 285 { 286 287 if (compat == NULL) 288 return NULL; 289 290 for (; compat->ocd_str != NULL; ++compat) { 291 if (ofw_bus_is_compatible(dev, compat->ocd_str)) 292 break; 293 } 294 295 return (compat); 296 } 297 298 int 299 ofw_bus_has_prop(device_t dev, const char *propname) 300 { 301 phandle_t node; 302 303 if ((node = ofw_bus_get_node(dev)) == -1) 304 return (0); 305 306 return (OF_hasprop(node, propname)); 307 } 308 309 void 310 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 311 { 312 pcell_t addrc; 313 int msksz; 314 315 if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 316 addrc = 2; 317 ii->opi_addrc = addrc * sizeof(pcell_t); 318 319 ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1, 320 (void **)&ii->opi_imap); 321 if (ii->opi_imapsz > 0) { 322 msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1, 323 (void **)&ii->opi_imapmsk); 324 /* 325 * Failure to get the mask is ignored; a full mask is used 326 * then. We barf on bad mask sizes, however. 327 */ 328 if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 329 panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 330 "property!"); 331 } 332 } 333 334 int 335 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 336 int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 337 phandle_t *iparent) 338 { 339 uint8_t maskbuf[regsz + pintrsz]; 340 int rv; 341 342 if (ii->opi_imapsz <= 0) 343 return (0); 344 KASSERT(regsz >= ii->opi_addrc, 345 ("ofw_bus_lookup_imap: register size too small: %d < %d", 346 regsz, ii->opi_addrc)); 347 if (node != -1) { 348 rv = OF_getencprop(node, "reg", reg, regsz); 349 if (rv < regsz) 350 panic("ofw_bus_lookup_imap: cannot get reg property"); 351 } 352 return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 353 ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 354 mintrsz, iparent)); 355 } 356 357 /* 358 * Map an interrupt using the firmware reg, interrupt-map and 359 * interrupt-map-mask properties. 360 * The interrupt property to be mapped must be of size intrsz, and pointed to 361 * by intr. The regs property of the node for which the mapping is done must 362 * be passed as regs. This property is an array of register specifications; 363 * the size of the address part of such a specification must be passed as 364 * physsz. Only the first element of the property is used. 365 * imap and imapsz hold the interrupt mask and it's size. 366 * imapmsk is a pointer to the interrupt-map-mask property, which must have 367 * a size of physsz + intrsz; it may be NULL, in which case a full mask is 368 * assumed. 369 * maskbuf must point to a buffer of length physsz + intrsz. 370 * The interrupt is returned in result, which must point to a buffer of length 371 * rintrsz (which gives the expected size of the mapped interrupt). 372 * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. 373 */ 374 int 375 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 376 void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 377 int rintrsz, phandle_t *iparent) 378 { 379 phandle_t parent; 380 uint8_t *ref = maskbuf; 381 uint8_t *uiintr = intr; 382 uint8_t *uiregs = regs; 383 uint8_t *uiimapmsk = imapmsk; 384 uint8_t *mptr; 385 pcell_t paddrsz; 386 pcell_t pintrsz; 387 int i, rsz, tsz; 388 389 rsz = -1; 390 if (imapmsk != NULL) { 391 for (i = 0; i < physsz; i++) 392 ref[i] = uiregs[i] & uiimapmsk[i]; 393 for (i = 0; i < intrsz; i++) 394 ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 395 } else { 396 bcopy(regs, ref, physsz); 397 bcopy(intr, ref + physsz, intrsz); 398 } 399 400 mptr = imap; 401 i = imapsz; 402 paddrsz = 0; 403 while (i > 0) { 404 bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 405 #ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS 406 /* 407 * Find if we need to read the parent address data. 408 * CHRP-derived OF bindings, including ePAPR-compliant FDTs, 409 * use this as an optional part of the specifier. 410 */ 411 if (OF_getencprop(OF_node_from_xref(parent), 412 "#address-cells", &paddrsz, sizeof(paddrsz)) == -1) 413 paddrsz = 0; /* default */ 414 paddrsz *= sizeof(pcell_t); 415 #endif 416 417 if (OF_searchencprop(OF_node_from_xref(parent), 418 "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) 419 pintrsz = 1; /* default */ 420 pintrsz *= sizeof(pcell_t); 421 422 /* Compute the map stride size. */ 423 tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz; 424 KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 425 426 if (bcmp(ref, mptr, physsz + intrsz) == 0) { 427 bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz, 428 result, MIN(rintrsz, pintrsz)); 429 430 if (iparent != NULL) 431 *iparent = parent; 432 return (pintrsz/sizeof(pcell_t)); 433 } 434 mptr += tsz; 435 i -= tsz; 436 } 437 return (0); 438 } 439 440 int 441 ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent, 442 uint32_t *msi_rid) 443 { 444 pcell_t *map, mask, msi_base, rid_base, rid_length; 445 ssize_t len; 446 uint32_t masked_rid, rid; 447 int err, i; 448 449 /* TODO: This should be OF_searchprop_alloc if we had it */ 450 len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map); 451 if (len < 0) { 452 if (msi_parent != NULL) { 453 *msi_parent = 0; 454 OF_getencprop(node, "msi-parent", msi_parent, 455 sizeof(*msi_parent)); 456 } 457 if (msi_rid != NULL) 458 *msi_rid = pci_rid; 459 return (0); 460 } 461 462 err = ENOENT; 463 rid = 0; 464 mask = 0xffffffff; 465 OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask)); 466 467 masked_rid = pci_rid & mask; 468 for (i = 0; i < len; i += 4) { 469 rid_base = map[i + 0]; 470 rid_length = map[i + 3]; 471 472 if (masked_rid < rid_base || 473 masked_rid >= (rid_base + rid_length)) 474 continue; 475 476 msi_base = map[i + 2]; 477 478 if (msi_parent != NULL) 479 *msi_parent = map[i + 1]; 480 if (msi_rid != NULL) 481 *msi_rid = masked_rid - rid_base + msi_base; 482 err = 0; 483 break; 484 } 485 486 free(map, M_OFWPROP); 487 488 return (err); 489 } 490 491 int 492 ofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells, 493 struct resource_list *rl) 494 { 495 uint64_t phys, size; 496 ssize_t i, j, rid, nreg, ret; 497 uint32_t *reg; 498 char *name; 499 500 /* 501 * This may be just redundant when having ofw_bus_devinfo 502 * but makes this routine independent of it. 503 */ 504 ret = OF_getprop_alloc(node, "name", sizeof(*name), (void **)&name); 505 if (ret == -1) 506 name = NULL; 507 508 ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); 509 nreg = (ret == -1) ? 0 : ret; 510 511 if (nreg % (acells + scells) != 0) { 512 if (bootverbose) 513 device_printf(dev, "Malformed reg property on <%s>\n", 514 (name == NULL) ? "unknown" : name); 515 nreg = 0; 516 } 517 518 for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) { 519 phys = size = 0; 520 for (j = 0; j < acells; j++) { 521 phys <<= 32; 522 phys |= reg[i + j]; 523 } 524 for (j = 0; j < scells; j++) { 525 size <<= 32; 526 size |= reg[i + acells + j]; 527 } 528 /* Skip the dummy reg property of glue devices like ssm(4). */ 529 if (size != 0) 530 resource_list_add(rl, SYS_RES_MEMORY, rid, 531 phys, phys + size - 1, size); 532 } 533 free(name, M_OFWPROP); 534 free(reg, M_OFWPROP); 535 536 return (0); 537 } 538 539 /* 540 * Get interrupt parent for given node. 541 * Returns 0 if interrupt parent doesn't exist. 542 */ 543 phandle_t 544 ofw_bus_find_iparent(phandle_t node) 545 { 546 phandle_t iparent; 547 548 if (OF_searchencprop(node, "interrupt-parent", &iparent, 549 sizeof(iparent)) == -1) { 550 for (iparent = node; iparent != 0; 551 iparent = OF_parent(iparent)) { 552 if (OF_hasprop(iparent, "interrupt-controller")) 553 break; 554 } 555 iparent = OF_xref_from_node(iparent); 556 } 557 return (iparent); 558 } 559 560 int 561 ofw_bus_intr_to_rl(device_t dev, phandle_t node, 562 struct resource_list *rl, int *rlen) 563 { 564 phandle_t iparent; 565 uint32_t icells, *intr; 566 int err, i, irqnum, nintr, rid; 567 boolean_t extended; 568 569 nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), 570 (void **)&intr); 571 if (nintr > 0) { 572 iparent = ofw_bus_find_iparent(node); 573 if (iparent == 0) { 574 device_printf(dev, "No interrupt-parent found, " 575 "assuming direct parent\n"); 576 iparent = OF_parent(node); 577 iparent = OF_xref_from_node(iparent); 578 } 579 if (OF_searchencprop(OF_node_from_xref(iparent), 580 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 581 device_printf(dev, "Missing #interrupt-cells " 582 "property, assuming <1>\n"); 583 icells = 1; 584 } 585 if (icells < 1 || icells > nintr) { 586 device_printf(dev, "Invalid #interrupt-cells property " 587 "value <%d>, assuming <1>\n", icells); 588 icells = 1; 589 } 590 extended = false; 591 } else { 592 nintr = OF_getencprop_alloc(node, "interrupts-extended", 593 sizeof(*intr), (void **)&intr); 594 if (nintr <= 0) 595 return (0); 596 extended = true; 597 } 598 err = 0; 599 rid = 0; 600 for (i = 0; i < nintr; i += icells) { 601 if (extended) { 602 iparent = intr[i++]; 603 if (OF_searchencprop(OF_node_from_xref(iparent), 604 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 605 device_printf(dev, "Missing #interrupt-cells " 606 "property\n"); 607 err = ENOENT; 608 break; 609 } 610 if (icells < 1 || (i + icells) > nintr) { 611 device_printf(dev, "Invalid #interrupt-cells " 612 "property value <%d>\n", icells); 613 err = ERANGE; 614 break; 615 } 616 } 617 irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]); 618 resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1); 619 } 620 if (rlen != NULL) 621 *rlen = rid; 622 free(intr, M_OFWPROP); 623 return (err); 624 } 625 626 int 627 ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid, 628 phandle_t *producer, int *ncells, pcell_t **cells) 629 { 630 phandle_t iparent; 631 uint32_t icells, *intr; 632 int err, i, nintr, rid; 633 boolean_t extended; 634 635 nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), 636 (void **)&intr); 637 if (nintr > 0) { 638 iparent = ofw_bus_find_iparent(node); 639 if (iparent == 0) { 640 device_printf(dev, "No interrupt-parent found, " 641 "assuming direct parent\n"); 642 iparent = OF_parent(node); 643 iparent = OF_xref_from_node(iparent); 644 } 645 if (OF_searchencprop(OF_node_from_xref(iparent), 646 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 647 device_printf(dev, "Missing #interrupt-cells " 648 "property, assuming <1>\n"); 649 icells = 1; 650 } 651 if (icells < 1 || icells > nintr) { 652 device_printf(dev, "Invalid #interrupt-cells property " 653 "value <%d>, assuming <1>\n", icells); 654 icells = 1; 655 } 656 extended = false; 657 } else { 658 nintr = OF_getencprop_alloc(node, "interrupts-extended", 659 sizeof(*intr), (void **)&intr); 660 if (nintr <= 0) 661 return (ESRCH); 662 extended = true; 663 } 664 err = ESRCH; 665 rid = 0; 666 for (i = 0; i < nintr; i += icells, rid++) { 667 if (extended) { 668 iparent = intr[i++]; 669 if (OF_searchencprop(OF_node_from_xref(iparent), 670 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 671 device_printf(dev, "Missing #interrupt-cells " 672 "property\n"); 673 err = ENOENT; 674 break; 675 } 676 if (icells < 1 || (i + icells) > nintr) { 677 device_printf(dev, "Invalid #interrupt-cells " 678 "property value <%d>\n", icells); 679 err = ERANGE; 680 break; 681 } 682 } 683 if (rid == wanted_rid) { 684 *cells = malloc(icells * sizeof(**cells), M_OFWPROP, 685 M_WAITOK); 686 *producer = iparent; 687 *ncells= icells; 688 memcpy(*cells, intr + i, icells * sizeof(**cells)); 689 err = 0; 690 break; 691 } 692 } 693 free(intr, M_OFWPROP); 694 return (err); 695 } 696 697 phandle_t 698 ofw_bus_find_child(phandle_t start, const char *child_name) 699 { 700 char *name; 701 int ret; 702 phandle_t child; 703 704 for (child = OF_child(start); child != 0; child = OF_peer(child)) { 705 ret = OF_getprop_alloc(child, "name", sizeof(*name), (void **)&name); 706 if (ret == -1) 707 continue; 708 if (strcmp(name, child_name) == 0) { 709 free(name, M_OFWPROP); 710 return (child); 711 } 712 713 free(name, M_OFWPROP); 714 } 715 716 return (0); 717 } 718 719 phandle_t 720 ofw_bus_find_compatible(phandle_t node, const char *onecompat) 721 { 722 phandle_t child, ret; 723 void *compat; 724 int len; 725 726 /* 727 * Traverse all children of 'start' node, and find first with 728 * matching 'compatible' property. 729 */ 730 for (child = OF_child(node); child != 0; child = OF_peer(child)) { 731 len = OF_getprop_alloc(child, "compatible", 1, &compat); 732 if (len >= 0) { 733 ret = ofw_bus_node_is_compatible_int(compat, len, 734 onecompat); 735 free(compat, M_OFWPROP); 736 if (ret != 0) 737 return (child); 738 } 739 740 ret = ofw_bus_find_compatible(child, onecompat); 741 if (ret != 0) 742 return (ret); 743 } 744 return (0); 745 } 746 747 /** 748 * @brief Return child of bus whose phandle is node 749 * 750 * A direct child of @p will be returned if it its phandle in the 751 * OFW tree is @p node. Otherwise, NULL is returned. 752 * 753 * @param bus The bus to examine 754 * @param node The phandle_t to look for. 755 */ 756 device_t 757 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node) 758 { 759 device_t *children, retval, child; 760 int nkid, i; 761 762 /* 763 * Nothing can match the flag value for no node. 764 */ 765 if (node == -1) 766 return (NULL); 767 768 /* 769 * Search the children for a match. We microoptimize 770 * a bit by not using ofw_bus_get since we already know 771 * the parent. We do not recurse. 772 */ 773 if (device_get_children(bus, &children, &nkid) != 0) 774 return (NULL); 775 retval = NULL; 776 for (i = 0; i < nkid; i++) { 777 child = children[i]; 778 if (OFW_BUS_GET_NODE(bus, child) == node) { 779 retval = child; 780 break; 781 } 782 } 783 free(children, M_TEMP); 784 785 return (retval); 786 } 787 788 /* 789 * Parse property that contain list of xrefs and values 790 * (like standard "clocks" and "resets" properties) 791 * Input arguments: 792 * node - consumers device node 793 * list_name - name of parsed list - "clocks" 794 * cells_name - name of size property - "#clock-cells" 795 * idx - the index of the requested list entry, or, if -1, an indication 796 * to return the number of entries in the parsed list. 797 * Output arguments: 798 * producer - handle of producer 799 * ncells - number of cells in result or the number of items in the list when 800 * idx == -1. 801 * cells - array of decoded cells 802 */ 803 static int 804 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name, 805 const char *cells_name, int idx, phandle_t *producer, int *ncells, 806 pcell_t **cells) 807 { 808 phandle_t pnode; 809 phandle_t *elems; 810 uint32_t pcells; 811 int rv, i, j, nelems, cnt; 812 813 elems = NULL; 814 nelems = OF_getencprop_alloc(node, list_name, sizeof(*elems), 815 (void **)&elems); 816 if (nelems <= 0) 817 return (ENOENT); 818 rv = (idx == -1) ? 0 : ENOENT; 819 for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) { 820 pnode = elems[i++]; 821 if (OF_getencprop(OF_node_from_xref(pnode), 822 cells_name, &pcells, sizeof(pcells)) == -1) { 823 printf("Missing %s property\n", cells_name); 824 rv = ENOENT; 825 break; 826 } 827 828 if ((i + pcells) > nelems) { 829 printf("Invalid %s property value <%d>\n", cells_name, 830 pcells); 831 rv = ERANGE; 832 break; 833 } 834 if (cnt == idx) { 835 *cells= malloc(pcells * sizeof(**cells), M_OFWPROP, 836 M_WAITOK); 837 *producer = pnode; 838 *ncells = pcells; 839 for (j = 0; j < pcells; j++) 840 (*cells)[j] = elems[i + j]; 841 rv = 0; 842 break; 843 } 844 } 845 if (elems != NULL) 846 free(elems, M_OFWPROP); 847 if (idx == -1 && rv == 0) 848 *ncells = cnt; 849 return (rv); 850 } 851 852 /* 853 * Parse property that contain list of xrefs and values 854 * (like standard "clocks" and "resets" properties) 855 * Input arguments: 856 * node - consumers device node 857 * list_name - name of parsed list - "clocks" 858 * cells_name - name of size property - "#clock-cells" 859 * idx - the index of the requested list entry (>= 0) 860 * Output arguments: 861 * producer - handle of producer 862 * ncells - number of cells in result 863 * cells - array of decoded cells 864 */ 865 int 866 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, 867 const char *cells_name, int idx, phandle_t *producer, int *ncells, 868 pcell_t **cells) 869 { 870 871 KASSERT(idx >= 0, 872 ("ofw_bus_parse_xref_list_alloc: negative index supplied")); 873 874 return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 875 idx, producer, ncells, cells)); 876 } 877 878 /* 879 * Parse property that contain list of xrefs and values 880 * (like standard "clocks" and "resets" properties) 881 * and determine the number of items in the list 882 * Input arguments: 883 * node - consumers device node 884 * list_name - name of parsed list - "clocks" 885 * cells_name - name of size property - "#clock-cells" 886 * Output arguments: 887 * count - number of items in list 888 */ 889 int 890 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, 891 const char *cells_name, int *count) 892 { 893 894 return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 895 -1, NULL, count, NULL)); 896 } 897 898 /* 899 * Find index of string in string list property (case sensitive). 900 */ 901 int 902 ofw_bus_find_string_index(phandle_t node, const char *list_name, 903 const char *name, int *idx) 904 { 905 char *elems; 906 int rv, i, cnt, nelems; 907 908 elems = NULL; 909 nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); 910 if (nelems <= 0) 911 return (ENOENT); 912 913 rv = ENOENT; 914 for (i = 0, cnt = 0; i < nelems; cnt++) { 915 if (strcmp(elems + i, name) == 0) { 916 *idx = cnt; 917 rv = 0; 918 break; 919 } 920 i += strlen(elems + i) + 1; 921 } 922 923 if (elems != NULL) 924 free(elems, M_OFWPROP); 925 return (rv); 926 } 927 928 /* 929 * Create zero terminated array of strings from string list property. 930 */ 931 int 932 ofw_bus_string_list_to_array(phandle_t node, const char *list_name, 933 const char ***out_array) 934 { 935 char *elems, *tptr; 936 const char **array; 937 int i, cnt, nelems, len; 938 939 elems = NULL; 940 nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); 941 if (nelems <= 0) 942 return (nelems); 943 944 /* Count number of strings. */ 945 for (i = 0, cnt = 0; i < nelems; cnt++) 946 i += strlen(elems + i) + 1; 947 948 /* Allocate space for arrays and all strings. */ 949 array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP, 950 M_WAITOK); 951 952 /* Get address of first string. */ 953 tptr = (char *)(array + cnt + 1); 954 955 /* Copy strings. */ 956 memcpy(tptr, elems, nelems); 957 free(elems, M_OFWPROP); 958 959 /* Fill string pointers. */ 960 for (i = 0, cnt = 0; i < nelems; cnt++) { 961 len = strlen(tptr) + 1; 962 array[cnt] = tptr; 963 i += len; 964 tptr += len; 965 } 966 array[cnt] = NULL; 967 *out_array = array; 968 969 return (cnt); 970 } 971