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