1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>. 5 * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions, and the following disclaimer, 13 * without modification, immediately at the beginning of the file. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include "opt_platform.h" 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/bus.h> 39 #include <sys/errno.h> 40 #include <sys/libkern.h> 41 42 #include <machine/resource.h> 43 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 #include <dev/ofw/openfirm.h> 47 48 #include "ofw_bus_if.h" 49 50 #define OFW_COMPAT_LEN 255 51 #define OFW_STATUS_LEN 16 52 53 int 54 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) 55 { 56 57 if (obd == NULL) 58 return (ENOMEM); 59 /* The 'name' property is considered mandatory. */ 60 if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) 61 return (EINVAL); 62 OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); 63 OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); 64 OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); 65 OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status); 66 obd->obd_node = node; 67 return (0); 68 } 69 70 void 71 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 72 { 73 74 if (obd == NULL) 75 return; 76 if (obd->obd_compat != NULL) 77 free(obd->obd_compat, M_OFWPROP); 78 if (obd->obd_model != NULL) 79 free(obd->obd_model, M_OFWPROP); 80 if (obd->obd_name != NULL) 81 free(obd->obd_name, M_OFWPROP); 82 if (obd->obd_type != NULL) 83 free(obd->obd_type, M_OFWPROP); 84 if (obd->obd_status != NULL) 85 free(obd->obd_status, M_OFWPROP); 86 } 87 88 int 89 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 90 size_t buflen) 91 { 92 93 if (ofw_bus_get_name(child) != NULL) { 94 strlcat(buf, "name=", buflen); 95 strlcat(buf, ofw_bus_get_name(child), buflen); 96 } 97 98 if (ofw_bus_get_compat(child) != NULL) { 99 strlcat(buf, " compat=", buflen); 100 strlcat(buf, ofw_bus_get_compat(child), buflen); 101 } 102 return (0); 103 }; 104 105 const char * 106 ofw_bus_gen_get_compat(device_t bus, device_t dev) 107 { 108 const struct ofw_bus_devinfo *obd; 109 110 obd = OFW_BUS_GET_DEVINFO(bus, dev); 111 if (obd == NULL) 112 return (NULL); 113 return (obd->obd_compat); 114 } 115 116 const char * 117 ofw_bus_gen_get_model(device_t bus, device_t dev) 118 { 119 const struct ofw_bus_devinfo *obd; 120 121 obd = OFW_BUS_GET_DEVINFO(bus, dev); 122 if (obd == NULL) 123 return (NULL); 124 return (obd->obd_model); 125 } 126 127 const char * 128 ofw_bus_gen_get_name(device_t bus, device_t dev) 129 { 130 const struct ofw_bus_devinfo *obd; 131 132 obd = OFW_BUS_GET_DEVINFO(bus, dev); 133 if (obd == NULL) 134 return (NULL); 135 return (obd->obd_name); 136 } 137 138 phandle_t 139 ofw_bus_gen_get_node(device_t bus, device_t dev) 140 { 141 const struct ofw_bus_devinfo *obd; 142 143 obd = OFW_BUS_GET_DEVINFO(bus, dev); 144 if (obd == NULL) 145 return (0); 146 return (obd->obd_node); 147 } 148 149 const char * 150 ofw_bus_gen_get_type(device_t bus, device_t dev) 151 { 152 const struct ofw_bus_devinfo *obd; 153 154 obd = OFW_BUS_GET_DEVINFO(bus, dev); 155 if (obd == NULL) 156 return (NULL); 157 return (obd->obd_type); 158 } 159 160 const char * 161 ofw_bus_get_status(device_t dev) 162 { 163 const struct ofw_bus_devinfo *obd; 164 165 obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); 166 if (obd == NULL) 167 return (NULL); 168 169 return (obd->obd_status); 170 } 171 172 int 173 ofw_bus_status_okay(device_t dev) 174 { 175 const char *status; 176 177 status = ofw_bus_get_status(dev); 178 if (status == NULL || strcmp(status, "okay") == 0 || 179 strcmp(status, "ok") == 0) 180 return (1); 181 182 return (0); 183 } 184 185 int 186 ofw_bus_node_status_okay(phandle_t node) 187 { 188 char status[OFW_STATUS_LEN]; 189 int len; 190 191 len = OF_getproplen(node, "status"); 192 if (len <= 0) 193 return (1); 194 195 OF_getprop(node, "status", status, OFW_STATUS_LEN); 196 if ((len == 5 && (bcmp(status, "okay", len) == 0)) || 197 (len == 3 && (bcmp(status, "ok", len)))) 198 return (1); 199 200 return (0); 201 } 202 203 static int 204 ofw_bus_node_is_compatible_int(const char *compat, int len, 205 const char *onecompat) 206 { 207 int onelen, l, ret; 208 209 onelen = strlen(onecompat); 210 211 ret = 0; 212 while (len > 0) { 213 if (strlen(compat) == onelen && 214 strncasecmp(compat, onecompat, onelen) == 0) { 215 /* Found it. */ 216 ret = 1; 217 break; 218 } 219 220 /* Slide to the next sub-string. */ 221 l = strlen(compat) + 1; 222 compat += l; 223 len -= l; 224 } 225 226 return (ret); 227 } 228 229 int 230 ofw_bus_node_is_compatible(phandle_t node, const char *compatstr) 231 { 232 char compat[OFW_COMPAT_LEN]; 233 int len, rv; 234 235 if ((len = OF_getproplen(node, "compatible")) <= 0) 236 return (0); 237 238 bzero(compat, OFW_COMPAT_LEN); 239 240 if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0) 241 return (0); 242 243 rv = ofw_bus_node_is_compatible_int(compat, len, compatstr); 244 245 return (rv); 246 } 247 248 int 249 ofw_bus_is_compatible(device_t dev, const char *onecompat) 250 { 251 phandle_t node; 252 const char *compat; 253 int len; 254 255 if ((compat = ofw_bus_get_compat(dev)) == NULL) 256 return (0); 257 258 if ((node = ofw_bus_get_node(dev)) == -1) 259 return (0); 260 261 /* Get total 'compatible' prop len */ 262 if ((len = OF_getproplen(node, "compatible")) <= 0) 263 return (0); 264 265 return (ofw_bus_node_is_compatible_int(compat, len, onecompat)); 266 } 267 268 int 269 ofw_bus_is_compatible_strict(device_t dev, const char *compatible) 270 { 271 const char *compat; 272 size_t len; 273 274 if ((compat = ofw_bus_get_compat(dev)) == NULL) 275 return (0); 276 277 len = strlen(compatible); 278 if (strlen(compat) == len && 279 strncasecmp(compat, compatible, len) == 0) 280 return (1); 281 282 return (0); 283 } 284 285 const struct ofw_compat_data * 286 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) 287 { 288 289 if (compat == NULL) 290 return NULL; 291 292 for (; compat->ocd_str != NULL; ++compat) { 293 if (ofw_bus_is_compatible(dev, compat->ocd_str)) 294 break; 295 } 296 297 return (compat); 298 } 299 300 int 301 ofw_bus_has_prop(device_t dev, const char *propname) 302 { 303 phandle_t node; 304 305 if ((node = ofw_bus_get_node(dev)) == -1) 306 return (0); 307 308 return (OF_hasprop(node, propname)); 309 } 310 311 void 312 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 313 { 314 pcell_t addrc; 315 int msksz; 316 317 if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 318 addrc = 2; 319 ii->opi_addrc = addrc * sizeof(pcell_t); 320 321 ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1, 322 (void **)&ii->opi_imap); 323 if (ii->opi_imapsz > 0) { 324 msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1, 325 (void **)&ii->opi_imapmsk); 326 /* 327 * Failure to get the mask is ignored; a full mask is used 328 * then. We barf on bad mask sizes, however. 329 */ 330 if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 331 panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 332 "property!"); 333 } 334 } 335 336 int 337 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 338 int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 339 phandle_t *iparent) 340 { 341 uint8_t maskbuf[regsz + pintrsz]; 342 int rv; 343 344 if (ii->opi_imapsz <= 0) 345 return (0); 346 KASSERT(regsz >= ii->opi_addrc, 347 ("ofw_bus_lookup_imap: register size too small: %d < %d", 348 regsz, ii->opi_addrc)); 349 if (node != -1) { 350 rv = OF_getencprop(node, "reg", reg, regsz); 351 if (rv < regsz) 352 panic("ofw_bus_lookup_imap: cannot get reg property"); 353 } 354 return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 355 ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 356 mintrsz, iparent)); 357 } 358 359 /* 360 * Map an interrupt using the firmware reg, interrupt-map and 361 * interrupt-map-mask properties. 362 * The interrupt property to be mapped must be of size intrsz, and pointed to 363 * by intr. The regs property of the node for which the mapping is done must 364 * be passed as regs. This property is an array of register specifications; 365 * the size of the address part of such a specification must be passed as 366 * physsz. Only the first element of the property is used. 367 * imap and imapsz hold the interrupt mask and it's size. 368 * imapmsk is a pointer to the interrupt-map-mask property, which must have 369 * a size of physsz + intrsz; it may be NULL, in which case a full mask is 370 * assumed. 371 * maskbuf must point to a buffer of length physsz + intrsz. 372 * The interrupt is returned in result, which must point to a buffer of length 373 * rintrsz (which gives the expected size of the mapped interrupt). 374 * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. 375 */ 376 int 377 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 378 void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 379 int rintrsz, phandle_t *iparent) 380 { 381 phandle_t parent; 382 uint8_t *ref = maskbuf; 383 uint8_t *uiintr = intr; 384 uint8_t *uiregs = regs; 385 uint8_t *uiimapmsk = imapmsk; 386 uint8_t *mptr; 387 pcell_t paddrsz; 388 pcell_t pintrsz; 389 int i, tsz; 390 391 if (imapmsk != NULL) { 392 for (i = 0; i < physsz; i++) 393 ref[i] = uiregs[i] & uiimapmsk[i]; 394 for (i = 0; i < intrsz; i++) 395 ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 396 } else { 397 bcopy(regs, ref, physsz); 398 bcopy(intr, ref + physsz, intrsz); 399 } 400 401 mptr = imap; 402 i = imapsz; 403 paddrsz = 0; 404 while (i > 0) { 405 bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 406 #ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS 407 /* 408 * Find if we need to read the parent address data. 409 * CHRP-derived OF bindings, including ePAPR-compliant FDTs, 410 * use this as an optional part of the specifier. 411 */ 412 if (OF_getencprop(OF_node_from_xref(parent), 413 "#address-cells", &paddrsz, sizeof(paddrsz)) == -1) 414 paddrsz = 0; /* default */ 415 paddrsz *= sizeof(pcell_t); 416 #endif 417 418 if (OF_searchencprop(OF_node_from_xref(parent), 419 "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) 420 pintrsz = 1; /* default */ 421 pintrsz *= sizeof(pcell_t); 422 423 /* Compute the map stride size. */ 424 tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz; 425 KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 426 427 if (bcmp(ref, mptr, physsz + intrsz) == 0) { 428 bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz, 429 result, MIN(rintrsz, pintrsz)); 430 431 if (iparent != NULL) 432 *iparent = parent; 433 return (pintrsz/sizeof(pcell_t)); 434 } 435 mptr += tsz; 436 i -= tsz; 437 } 438 return (0); 439 } 440 441 int 442 ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent, 443 uint32_t *msi_rid) 444 { 445 pcell_t *map, mask, msi_base, rid_base, rid_length; 446 ssize_t len; 447 uint32_t masked_rid; 448 int err, i; 449 450 /* TODO: This should be OF_searchprop_alloc if we had it */ 451 len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map); 452 if (len < 0) { 453 if (msi_parent != NULL) { 454 *msi_parent = 0; 455 OF_getencprop(node, "msi-parent", msi_parent, 456 sizeof(*msi_parent)); 457 } 458 if (msi_rid != NULL) 459 *msi_rid = pci_rid; 460 return (0); 461 } 462 463 err = ENOENT; 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 724 /* 725 * Traverse all children of 'start' node, and find first with 726 * matching 'compatible' property. 727 */ 728 for (child = OF_child(node); child != 0; child = OF_peer(child)) { 729 if (ofw_bus_node_is_compatible(child, onecompat) != 0) 730 return (child); 731 732 ret = ofw_bus_find_compatible(child, onecompat); 733 if (ret != 0) 734 return (ret); 735 } 736 return (0); 737 } 738 739 /** 740 * @brief Return child of bus whose phandle is node 741 * 742 * A direct child of @p will be returned if it its phandle in the 743 * OFW tree is @p node. Otherwise, NULL is returned. 744 * 745 * @param bus The bus to examine 746 * @param node The phandle_t to look for. 747 */ 748 device_t 749 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node) 750 { 751 device_t *children, retval, child; 752 int nkid, i; 753 754 /* 755 * Nothing can match the flag value for no node. 756 */ 757 if (node == -1) 758 return (NULL); 759 760 /* 761 * Search the children for a match. We microoptimize 762 * a bit by not using ofw_bus_get since we already know 763 * the parent. We do not recurse. 764 */ 765 if (device_get_children(bus, &children, &nkid) != 0) 766 return (NULL); 767 retval = NULL; 768 for (i = 0; i < nkid; i++) { 769 child = children[i]; 770 if (OFW_BUS_GET_NODE(bus, child) == node) { 771 retval = child; 772 break; 773 } 774 } 775 free(children, M_TEMP); 776 777 return (retval); 778 } 779 780 /* 781 * Parse property that contain list of xrefs and values 782 * (like standard "clocks" and "resets" properties) 783 * Input arguments: 784 * node - consumers device node 785 * list_name - name of parsed list - "clocks" 786 * cells_name - name of size property - "#clock-cells" 787 * idx - the index of the requested list entry, or, if -1, an indication 788 * to return the number of entries in the parsed list. 789 * Output arguments: 790 * producer - handle of producer 791 * ncells - number of cells in result or the number of items in the list when 792 * idx == -1. 793 * cells - array of decoded cells 794 */ 795 static int 796 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name, 797 const char *cells_name, int idx, phandle_t *producer, int *ncells, 798 pcell_t **cells) 799 { 800 phandle_t pnode; 801 phandle_t *elems; 802 uint32_t pcells; 803 int rv, i, j, nelems, cnt; 804 805 elems = NULL; 806 nelems = OF_getencprop_alloc(node, list_name, sizeof(*elems), 807 (void **)&elems); 808 if (nelems <= 0) 809 return (ENOENT); 810 rv = (idx == -1) ? 0 : ENOENT; 811 for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) { 812 pnode = elems[i++]; 813 if (OF_getencprop(OF_node_from_xref(pnode), 814 cells_name, &pcells, sizeof(pcells)) == -1) { 815 printf("Missing %s property\n", cells_name); 816 rv = ENOENT; 817 break; 818 } 819 820 if ((i + pcells) > nelems) { 821 printf("Invalid %s property value <%d>\n", cells_name, 822 pcells); 823 rv = ERANGE; 824 break; 825 } 826 if (cnt == idx) { 827 *cells= malloc(pcells * sizeof(**cells), M_OFWPROP, 828 M_WAITOK); 829 *producer = pnode; 830 *ncells = pcells; 831 for (j = 0; j < pcells; j++) 832 (*cells)[j] = elems[i + j]; 833 rv = 0; 834 break; 835 } 836 } 837 if (elems != NULL) 838 free(elems, M_OFWPROP); 839 if (idx == -1 && rv == 0) 840 *ncells = cnt; 841 return (rv); 842 } 843 844 /* 845 * Parse property that contain list of xrefs and values 846 * (like standard "clocks" and "resets" properties) 847 * Input arguments: 848 * node - consumers device node 849 * list_name - name of parsed list - "clocks" 850 * cells_name - name of size property - "#clock-cells" 851 * idx - the index of the requested list entry (>= 0) 852 * Output arguments: 853 * producer - handle of producer 854 * ncells - number of cells in result 855 * cells - array of decoded cells 856 */ 857 int 858 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, 859 const char *cells_name, int idx, phandle_t *producer, int *ncells, 860 pcell_t **cells) 861 { 862 863 KASSERT(idx >= 0, 864 ("ofw_bus_parse_xref_list_alloc: negative index supplied")); 865 866 return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 867 idx, producer, ncells, cells)); 868 } 869 870 /* 871 * Parse property that contain list of xrefs and values 872 * (like standard "clocks" and "resets" properties) 873 * and determine the number of items in the list 874 * Input arguments: 875 * node - consumers device node 876 * list_name - name of parsed list - "clocks" 877 * cells_name - name of size property - "#clock-cells" 878 * Output arguments: 879 * count - number of items in list 880 */ 881 int 882 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, 883 const char *cells_name, int *count) 884 { 885 886 return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 887 -1, NULL, count, NULL)); 888 } 889 890 /* 891 * Find index of string in string list property (case sensitive). 892 */ 893 int 894 ofw_bus_find_string_index(phandle_t node, const char *list_name, 895 const char *name, int *idx) 896 { 897 char *elems; 898 int rv, i, cnt, nelems; 899 900 elems = NULL; 901 nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); 902 if (nelems <= 0) 903 return (ENOENT); 904 905 rv = ENOENT; 906 for (i = 0, cnt = 0; i < nelems; cnt++) { 907 if (strcmp(elems + i, name) == 0) { 908 *idx = cnt; 909 rv = 0; 910 break; 911 } 912 i += strlen(elems + i) + 1; 913 } 914 915 if (elems != NULL) 916 free(elems, M_OFWPROP); 917 return (rv); 918 } 919 920 /* 921 * Create zero terminated array of strings from string list property. 922 */ 923 int 924 ofw_bus_string_list_to_array(phandle_t node, const char *list_name, 925 const char ***out_array) 926 { 927 char *elems, *tptr; 928 const char **array; 929 int i, cnt, nelems, len; 930 931 elems = NULL; 932 nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); 933 if (nelems <= 0) 934 return (nelems); 935 936 /* Count number of strings. */ 937 for (i = 0, cnt = 0; i < nelems; cnt++) 938 i += strlen(elems + i) + 1; 939 940 /* Allocate space for arrays and all strings. */ 941 array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP, 942 M_WAITOK); 943 944 /* Get address of first string. */ 945 tptr = (char *)(array + cnt + 1); 946 947 /* Copy strings. */ 948 memcpy(tptr, elems, nelems); 949 free(elems, M_OFWPROP); 950 951 /* Fill string pointers. */ 952 for (i = 0, cnt = 0; i < nelems; cnt++) { 953 len = strlen(tptr) + 1; 954 array[cnt] = tptr; 955 i += len; 956 tptr += len; 957 } 958 array[cnt] = NULL; 959 *out_array = array; 960 961 return (cnt); 962 } 963