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