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