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