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