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