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) == 0))) 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 static phandle_t 638 ofw_bus_search_iparent(phandle_t node) 639 { 640 phandle_t iparent; 641 642 do { 643 if (OF_getencprop(node, "interrupt-parent", &iparent, 644 sizeof(iparent)) > 0) { 645 node = OF_node_from_xref(iparent); 646 } else { 647 node = OF_parent(node); 648 } 649 if (node == 0) 650 return (0); 651 } while (!OF_hasprop(node, "#interrupt-cells")); 652 653 return (OF_xref_from_node(node)); 654 } 655 656 static int 657 ofw_bus_traverse_imap(phandle_t inode, phandle_t node, uint32_t *intr, 658 int intrsz, pcell_t *res, int ressz, phandle_t *iparentp) 659 { 660 struct ofw_bus_iinfo ii; 661 void *reg; 662 uint32_t *intrp; 663 phandle_t iparent; 664 int rv = 0; 665 666 /* We already have an interrupt controller */ 667 if (OF_hasprop(node, "interrupt-controller")) 668 return (0); 669 670 intrp = malloc(intrsz, M_OFWPROP, M_WAITOK); 671 memcpy(intrp, intr, intrsz); 672 673 while (true) { 674 /* There is no interrupt-map to follow */ 675 if (!OF_hasprop(inode, "interrupt-map")) { 676 free(intrp, M_OFWPROP); 677 return (0); 678 } 679 680 memset(&ii, 0, sizeof(ii)); 681 ofw_bus_setup_iinfo(inode, &ii, sizeof(cell_t)); 682 683 reg = NULL; 684 if (ii.opi_addrc > 0) 685 reg = malloc(ii.opi_addrc, M_OFWPROP, M_WAITOK); 686 687 rv = ofw_bus_lookup_imap(node, &ii, reg, ii.opi_addrc, intrp, 688 intrsz, res, ressz, &iparent); 689 690 free(reg, M_OFWPROP); 691 free(ii.opi_imap, M_OFWPROP); 692 free(ii.opi_imapmsk, M_OFWPROP); 693 free(intrp, M_OFWPROP); 694 695 if (rv == 0) 696 return (0); 697 698 node = inode; 699 inode = OF_node_from_xref(iparent); 700 701 /* Stop when we have an interrupt controller */ 702 if (OF_hasprop(inode, "interrupt-controller")) { 703 *iparentp = iparent; 704 return (rv); 705 } 706 707 intrsz = rv * sizeof(pcell_t); 708 intrp = malloc(intrsz, M_OFWPROP, M_WAITOK); 709 memcpy(intrp, res, intrsz); 710 } 711 } 712 713 int 714 ofw_bus_intr_to_rl(device_t dev, phandle_t node, 715 struct resource_list *rl, int *rlen) 716 { 717 phandle_t iparent, iparent_node; 718 uint32_t result[16]; 719 uint32_t intrpcells, *intrp; 720 uint32_t icells, *intr; 721 int err, i, irqnum, nintr, rid; 722 bool extended; 723 724 nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr), 725 (void **)&intr); 726 if (nintr > 0) { 727 iparent = ofw_bus_search_iparent(node); 728 if (iparent == 0) { 729 device_printf(dev, "No interrupt-parent found, " 730 "assuming direct parent\n"); 731 iparent = OF_parent(node); 732 iparent = OF_xref_from_node(iparent); 733 } 734 iparent_node = OF_node_from_xref(iparent); 735 if (OF_searchencprop(iparent_node, "#interrupt-cells", &icells, 736 sizeof(icells)) == -1) { 737 device_printf(dev, "Missing #interrupt-cells " 738 "property, assuming <1>\n"); 739 icells = 1; 740 } 741 if (icells < 1 || icells > nintr) { 742 device_printf(dev, "Invalid #interrupt-cells property " 743 "value <%d>, assuming <1>\n", icells); 744 icells = 1; 745 } 746 extended = false; 747 } else { 748 nintr = OF_getencprop_alloc_multi(node, "interrupts-extended", 749 sizeof(*intr), (void **)&intr); 750 if (nintr <= 0) 751 return (0); 752 extended = true; 753 } 754 err = 0; 755 rid = 0; 756 for (i = 0; i < nintr; i += icells) { 757 if (extended) { 758 iparent = intr[i++]; 759 iparent_node = OF_node_from_xref(iparent); 760 if (OF_searchencprop(iparent_node, 761 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 762 device_printf(dev, "Missing #interrupt-cells " 763 "property\n"); 764 err = ENOENT; 765 break; 766 } 767 if (icells < 1 || (i + icells) > nintr) { 768 device_printf(dev, "Invalid #interrupt-cells " 769 "property value <%d>\n", icells); 770 err = ERANGE; 771 break; 772 } 773 } 774 775 intrp = &intr[i]; 776 intrpcells = ofw_bus_traverse_imap(iparent_node, node, intrp, 777 icells * sizeof(intr[0]), result, sizeof(result), &iparent); 778 if (intrpcells > 0) 779 intrp = result; 780 else 781 intrpcells = icells; 782 783 irqnum = ofw_bus_map_intr(dev, iparent, intrpcells, intrp); 784 resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1); 785 } 786 if (rlen != NULL) 787 *rlen = rid; 788 free(intr, M_OFWPROP); 789 return (err); 790 } 791 792 int 793 ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid, 794 phandle_t *producer, int *ncells, pcell_t **cells) 795 { 796 phandle_t iparent; 797 uint32_t icells, *intr; 798 int err, i, nintr, rid; 799 bool extended; 800 801 nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr), 802 (void **)&intr); 803 if (nintr > 0) { 804 iparent = ofw_bus_find_iparent(node); 805 if (iparent == 0) { 806 device_printf(dev, "No interrupt-parent found, " 807 "assuming direct parent\n"); 808 iparent = OF_parent(node); 809 iparent = OF_xref_from_node(iparent); 810 } 811 if (OF_searchencprop(OF_node_from_xref(iparent), 812 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 813 device_printf(dev, "Missing #interrupt-cells " 814 "property, assuming <1>\n"); 815 icells = 1; 816 } 817 if (icells < 1 || icells > nintr) { 818 device_printf(dev, "Invalid #interrupt-cells property " 819 "value <%d>, assuming <1>\n", icells); 820 icells = 1; 821 } 822 extended = false; 823 } else { 824 nintr = OF_getencprop_alloc_multi(node, "interrupts-extended", 825 sizeof(*intr), (void **)&intr); 826 if (nintr <= 0) 827 return (ESRCH); 828 extended = true; 829 } 830 err = ESRCH; 831 rid = 0; 832 for (i = 0; i < nintr; i += icells, rid++) { 833 if (extended) { 834 iparent = intr[i++]; 835 if (OF_searchencprop(OF_node_from_xref(iparent), 836 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 837 device_printf(dev, "Missing #interrupt-cells " 838 "property\n"); 839 err = ENOENT; 840 break; 841 } 842 if (icells < 1 || (i + icells) > nintr) { 843 device_printf(dev, "Invalid #interrupt-cells " 844 "property value <%d>\n", icells); 845 err = ERANGE; 846 break; 847 } 848 } 849 if (rid == wanted_rid) { 850 *cells = malloc(icells * sizeof(**cells), M_OFWPROP, 851 M_WAITOK); 852 *producer = iparent; 853 *ncells= icells; 854 memcpy(*cells, intr + i, icells * sizeof(**cells)); 855 err = 0; 856 break; 857 } 858 } 859 free(intr, M_OFWPROP); 860 return (err); 861 } 862 863 phandle_t 864 ofw_bus_find_child(phandle_t start, const char *child_name) 865 { 866 char *name; 867 int ret; 868 phandle_t child; 869 870 for (child = OF_child(start); child != 0; child = OF_peer(child)) { 871 ret = OF_getprop_alloc(child, "name", (void **)&name); 872 if (ret == -1) 873 continue; 874 if (strcmp(name, child_name) == 0) { 875 free(name, M_OFWPROP); 876 return (child); 877 } 878 879 free(name, M_OFWPROP); 880 } 881 882 return (0); 883 } 884 885 phandle_t 886 ofw_bus_find_compatible(phandle_t node, const char *onecompat) 887 { 888 phandle_t child, ret; 889 890 /* 891 * Traverse all children of 'start' node, and find first with 892 * matching 'compatible' property. 893 */ 894 for (child = OF_child(node); child != 0; child = OF_peer(child)) { 895 if (ofw_bus_node_is_compatible(child, onecompat) != 0) 896 return (child); 897 898 ret = ofw_bus_find_compatible(child, onecompat); 899 if (ret != 0) 900 return (ret); 901 } 902 return (0); 903 } 904 905 /** 906 * @brief Return child of bus whose phandle is node 907 * 908 * A direct child of @p will be returned if it its phandle in the 909 * OFW tree is @p node. Otherwise, NULL is returned. 910 * 911 * @param bus The bus to examine 912 * @param node The phandle_t to look for. 913 */ 914 device_t 915 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node) 916 { 917 device_t *children, retval, child; 918 int nkid, i; 919 920 /* 921 * Nothing can match the flag value for no node. 922 */ 923 if (node == -1) 924 return (NULL); 925 926 /* 927 * Search the children for a match. We microoptimize 928 * a bit by not using ofw_bus_get since we already know 929 * the parent. We do not recurse. 930 */ 931 if (device_get_children(bus, &children, &nkid) != 0) 932 return (NULL); 933 retval = NULL; 934 for (i = 0; i < nkid; i++) { 935 child = children[i]; 936 if (OFW_BUS_GET_NODE(bus, child) == node) { 937 retval = child; 938 break; 939 } 940 } 941 free(children, M_TEMP); 942 943 return (retval); 944 } 945 946 /* 947 * Parse property that contain list of xrefs and values 948 * (like standard "clocks" and "resets" properties) 949 * Input arguments: 950 * node - consumers device node 951 * list_name - name of parsed list - "clocks" 952 * cells_name - name of size property - "#clock-cells" 953 * idx - the index of the requested list entry, or, if -1, an indication 954 * to return the number of entries in the parsed list. 955 * Output arguments: 956 * producer - handle of producer 957 * ncells - number of cells in result or the number of items in the list when 958 * idx == -1. 959 * cells - array of decoded cells 960 */ 961 static int 962 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name, 963 const char *cells_name, int idx, phandle_t *producer, int *ncells, 964 pcell_t **cells) 965 { 966 phandle_t pnode; 967 phandle_t *elems; 968 uint32_t pcells; 969 int rv, i, j, nelems, cnt; 970 971 elems = NULL; 972 nelems = OF_getencprop_alloc_multi(node, list_name, sizeof(*elems), 973 (void **)&elems); 974 if (nelems <= 0) 975 return (ENOENT); 976 rv = (idx == -1) ? 0 : ENOENT; 977 for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) { 978 pnode = elems[i++]; 979 if (OF_getencprop(OF_node_from_xref(pnode), 980 cells_name, &pcells, sizeof(pcells)) == -1) { 981 printf("Missing %s property\n", cells_name); 982 rv = ENOENT; 983 break; 984 } 985 986 if ((i + pcells) > nelems) { 987 printf("Invalid %s property value <%d>\n", cells_name, 988 pcells); 989 rv = ERANGE; 990 break; 991 } 992 if (cnt == idx) { 993 *cells= malloc(pcells * sizeof(**cells), M_OFWPROP, 994 M_WAITOK); 995 *producer = pnode; 996 *ncells = pcells; 997 for (j = 0; j < pcells; j++) 998 (*cells)[j] = elems[i + j]; 999 rv = 0; 1000 break; 1001 } 1002 } 1003 if (elems != NULL) 1004 free(elems, M_OFWPROP); 1005 if (idx == -1 && rv == 0) 1006 *ncells = cnt; 1007 return (rv); 1008 } 1009 1010 /* 1011 * Parse property that contain list of xrefs and values 1012 * (like standard "clocks" and "resets" properties) 1013 * Input arguments: 1014 * node - consumers device node 1015 * list_name - name of parsed list - "clocks" 1016 * cells_name - name of size property - "#clock-cells" 1017 * idx - the index of the requested list entry (>= 0) 1018 * Output arguments: 1019 * producer - handle of producer 1020 * ncells - number of cells in result 1021 * cells - array of decoded cells 1022 */ 1023 int 1024 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, 1025 const char *cells_name, int idx, phandle_t *producer, int *ncells, 1026 pcell_t **cells) 1027 { 1028 1029 KASSERT(idx >= 0, 1030 ("ofw_bus_parse_xref_list_alloc: negative index supplied")); 1031 1032 return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 1033 idx, producer, ncells, cells)); 1034 } 1035 1036 /* 1037 * Parse property that contain list of xrefs and values 1038 * (like standard "clocks" and "resets" properties) 1039 * and determine the number of items in the list 1040 * Input arguments: 1041 * node - consumers device node 1042 * list_name - name of parsed list - "clocks" 1043 * cells_name - name of size property - "#clock-cells" 1044 * Output arguments: 1045 * count - number of items in list 1046 */ 1047 int 1048 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, 1049 const char *cells_name, int *count) 1050 { 1051 1052 return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 1053 -1, NULL, count, NULL)); 1054 } 1055 1056 /* 1057 * Find index of string in string list property (case sensitive). 1058 */ 1059 int 1060 ofw_bus_find_string_index(phandle_t node, const char *list_name, 1061 const char *name, int *idx) 1062 { 1063 char *elems; 1064 int rv, i, cnt, nelems; 1065 1066 elems = NULL; 1067 nelems = OF_getprop_alloc(node, list_name, (void **)&elems); 1068 if (nelems <= 0) 1069 return (ENOENT); 1070 1071 rv = ENOENT; 1072 for (i = 0, cnt = 0; i < nelems; cnt++) { 1073 if (strcmp(elems + i, name) == 0) { 1074 *idx = cnt; 1075 rv = 0; 1076 break; 1077 } 1078 i += strlen(elems + i) + 1; 1079 } 1080 1081 if (elems != NULL) 1082 free(elems, M_OFWPROP); 1083 return (rv); 1084 } 1085 1086 /* 1087 * Create zero terminated array of strings from string list property. 1088 */ 1089 int 1090 ofw_bus_string_list_to_array(phandle_t node, const char *list_name, 1091 const char ***out_array) 1092 { 1093 char *elems, *tptr; 1094 const char **array; 1095 int i, cnt, nelems, len; 1096 1097 elems = NULL; 1098 nelems = OF_getprop_alloc(node, list_name, (void **)&elems); 1099 if (nelems <= 0) 1100 return (nelems); 1101 1102 /* Count number of strings. */ 1103 for (i = 0, cnt = 0; i < nelems; cnt++) 1104 i += strlen(elems + i) + 1; 1105 1106 /* Allocate space for arrays and all strings. */ 1107 array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP, 1108 M_WAITOK); 1109 1110 /* Get address of first string. */ 1111 tptr = (char *)(array + cnt + 1); 1112 1113 /* Copy strings. */ 1114 memcpy(tptr, elems, nelems); 1115 free(elems, M_OFWPROP); 1116 1117 /* Fill string pointers. */ 1118 for (i = 0, cnt = 0; i < nelems; cnt++) { 1119 len = strlen(tptr) + 1; 1120 array[cnt] = tptr; 1121 i += len; 1122 tptr += len; 1123 } 1124 array[cnt] = NULL; 1125 *out_array = array; 1126 1127 return (cnt); 1128 } 1129