ofw_bus_subr.c revision 299932
1226584Sdim/*- 2226584Sdim * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>. 3226584Sdim * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 4226584Sdim * All rights reserved. 5226584Sdim * 6226584Sdim * Redistribution and use in source and binary forms, with or without 7226584Sdim * modification, are permitted provided that the following conditions 8226584Sdim * are met: 9226584Sdim * 1. Redistributions of source code must retain the above copyright 10226584Sdim * notice, this list of conditions, and the following disclaimer, 11226584Sdim * without modification, immediately at the beginning of the file. 12226584Sdim * 2. Redistributions in binary form must reproduce the above copyright 13226584Sdim * notice, this list of conditions and the following disclaimer in 14249423Sdim * the documentation and/or other materials provided with the 15226584Sdim * distribution. 16226584Sdim * 17226584Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18226584Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19249423Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20249423Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 21226584Sdim * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22239462Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23226584Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24226584Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25226584Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26226584Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27226584Sdim * SUCH DAMAGE. 28276479Sdim */ 29226584Sdim 30226584Sdim#include <sys/cdefs.h> 31226584Sdim__FBSDID("$FreeBSD: head/sys/dev/ofw/ofw_bus_subr.c 299932 2016-05-16 09:31:44Z andrew $"); 32226584Sdim 33276479Sdim#include "opt_platform.h" 34276479Sdim#include <sys/param.h> 35226584Sdim#include <sys/systm.h> 36226584Sdim#include <sys/bus.h> 37226584Sdim#include <sys/errno.h> 38226584Sdim#include <sys/libkern.h> 39226584Sdim 40288943Sdim#include <machine/resource.h> 41288943Sdim 42226584Sdim#include <dev/ofw/ofw_bus.h> 43239462Sdim#include <dev/ofw/ofw_bus_subr.h> 44276479Sdim#include <dev/ofw/openfirm.h> 45226584Sdim 46226584Sdim#include "ofw_bus_if.h" 47276479Sdim 48276479Sdimint 49226584Sdimofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) 50226584Sdim{ 51288943Sdim 52226584Sdim if (obd == NULL) 53276479Sdim return (ENOMEM); 54288943Sdim /* The 'name' property is considered mandatory. */ 55226584Sdim if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) 56276479Sdim return (EINVAL); 57288943Sdim OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); 58226584Sdim OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); 59276479Sdim OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); 60288943Sdim OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status); 61276479Sdim obd->obd_node = node; 62226584Sdim return (0); 63226584Sdim} 64226584Sdim 65226584Sdimvoid 66226584Sdimofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 67226584Sdim{ 68234353Sdim 69276479Sdim if (obd == NULL) 70276479Sdim return; 71226584Sdim if (obd->obd_compat != NULL) 72226584Sdim free(obd->obd_compat, M_OFWPROP); 73226584Sdim if (obd->obd_model != NULL) 74226584Sdim free(obd->obd_model, M_OFWPROP); 75276479Sdim if (obd->obd_name != NULL) 76276479Sdim free(obd->obd_name, M_OFWPROP); 77226584Sdim if (obd->obd_type != NULL) 78226584Sdim free(obd->obd_type, M_OFWPROP); 79226584Sdim if (obd->obd_status != NULL) 80226584Sdim free(obd->obd_status, M_OFWPROP); 81226584Sdim} 82276479Sdim 83276479Sdimint 84226584Sdimofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 85226584Sdim size_t buflen) 86226584Sdim{ 87276479Sdim 88276479Sdim if (ofw_bus_get_name(child) != NULL) { 89226584Sdim strlcat(buf, "name=", buflen); 90226584Sdim strlcat(buf, ofw_bus_get_name(child), buflen); 91226584Sdim } 92226584Sdim 93276479Sdim if (ofw_bus_get_compat(child) != NULL) { 94276479Sdim strlcat(buf, " compat=", buflen); 95226584Sdim strlcat(buf, ofw_bus_get_compat(child), buflen); 96226584Sdim } 97226584Sdim return (0); 98226584Sdim}; 99276479Sdim 100276479Sdimconst char * 101226584Sdimofw_bus_gen_get_compat(device_t bus, device_t dev) 102226584Sdim{ 103226584Sdim const struct ofw_bus_devinfo *obd; 104276479Sdim 105276479Sdim obd = OFW_BUS_GET_DEVINFO(bus, dev); 106226584Sdim if (obd == NULL) 107226584Sdim return (NULL); 108226584Sdim return (obd->obd_compat); 109276479Sdim} 110276479Sdim 111226584Sdimconst char * 112226584Sdimofw_bus_gen_get_model(device_t bus, device_t dev) 113226584Sdim{ 114276479Sdim const struct ofw_bus_devinfo *obd; 115276479Sdim 116226584Sdim obd = OFW_BUS_GET_DEVINFO(bus, dev); 117226584Sdim if (obd == NULL) 118226584Sdim return (NULL); 119226584Sdim return (obd->obd_model); 120276479Sdim} 121276479Sdim 122226584Sdimconst char * 123226584Sdimofw_bus_gen_get_name(device_t bus, device_t dev) 124226584Sdim{ 125226584Sdim const struct ofw_bus_devinfo *obd; 126276479Sdim 127276479Sdim obd = OFW_BUS_GET_DEVINFO(bus, dev); 128226584Sdim if (obd == NULL) 129226584Sdim return (NULL); 130226584Sdim return (obd->obd_name); 131226584Sdim} 132276479Sdim 133276479Sdimphandle_t 134234353Sdimofw_bus_gen_get_node(device_t bus, device_t dev) 135276479Sdim{ 136276479Sdim const struct ofw_bus_devinfo *obd; 137226584Sdim 138276479Sdim obd = OFW_BUS_GET_DEVINFO(bus, dev); 139276479Sdim if (obd == NULL) 140226584Sdim return (0); 141226584Sdim return (obd->obd_node); 142226584Sdim} 143226584Sdim 144276479Sdimconst char * 145276479Sdimofw_bus_gen_get_type(device_t bus, device_t dev) 146226584Sdim{ 147276479Sdim const struct ofw_bus_devinfo *obd; 148276479Sdim 149226584Sdim obd = OFW_BUS_GET_DEVINFO(bus, dev); 150276479Sdim if (obd == NULL) 151276479Sdim return (NULL); 152226584Sdim return (obd->obd_type); 153226584Sdim} 154226584Sdim 155226584Sdimconst char * 156226584Sdimofw_bus_get_status(device_t dev) 157276479Sdim{ 158276479Sdim const struct ofw_bus_devinfo *obd; 159226584Sdim 160226584Sdim obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); 161226584Sdim if (obd == NULL) 162276479Sdim return (NULL); 163276479Sdim 164226584Sdim return (obd->obd_status); 165226584Sdim} 166226584Sdim 167226584Sdimint 168276479Sdimofw_bus_status_okay(device_t dev) 169276479Sdim{ 170226584Sdim const char *status; 171226584Sdim 172226584Sdim status = ofw_bus_get_status(dev); 173226584Sdim if (status == NULL || strcmp(status, "okay") == 0 || 174276479Sdim strcmp(status, "ok") == 0) 175276479Sdim return (1); 176226584Sdim 177226584Sdim return (0); 178226584Sdim} 179226584Sdim 180276479Sdimstatic int 181276479Sdimofw_bus_node_is_compatible(const char *compat, int len, const char *onecompat) 182226584Sdim{ 183226584Sdim int onelen, l, ret; 184226584Sdim 185226584Sdim onelen = strlen(onecompat); 186226584Sdim 187276479Sdim ret = 0; 188276479Sdim while (len > 0) { 189226584Sdim if (strlen(compat) == onelen && 190226584Sdim strncasecmp(compat, onecompat, onelen) == 0) { 191226584Sdim /* Found it. */ 192276479Sdim ret = 1; 193276479Sdim break; 194226584Sdim } 195226584Sdim 196234353Sdim /* Slide to the next sub-string. */ 197226584Sdim l = strlen(compat) + 1; 198226584Sdim compat += l; 199226584Sdim len -= l; 200226584Sdim } 201226584Sdim 202226584Sdim return (ret); 203226584Sdim} 204226584Sdim 205226584Sdimint 206226584Sdimofw_bus_is_compatible(device_t dev, const char *onecompat) 207226584Sdim{ 208226584Sdim phandle_t node; 209226584Sdim const char *compat; 210226584Sdim int len; 211226584Sdim 212226584Sdim if ((compat = ofw_bus_get_compat(dev)) == NULL) 213226584Sdim return (0); 214234353Sdim 215226584Sdim if ((node = ofw_bus_get_node(dev)) == -1) 216226584Sdim return (0); 217226584Sdim 218226584Sdim /* Get total 'compatible' prop len */ 219276479Sdim if ((len = OF_getproplen(node, "compatible")) <= 0) 220276479Sdim return (0); 221226584Sdim 222226584Sdim return (ofw_bus_node_is_compatible(compat, len, onecompat)); 223226584Sdim} 224276479Sdim 225276479Sdimint 226226584Sdimofw_bus_is_compatible_strict(device_t dev, const char *compatible) 227226584Sdim{ 228226584Sdim const char *compat; 229276479Sdim size_t len; 230276479Sdim 231226584Sdim if ((compat = ofw_bus_get_compat(dev)) == NULL) 232226584Sdim return (0); 233226584Sdim 234276479Sdim len = strlen(compatible); 235276479Sdim if (strlen(compat) == len && 236226584Sdim strncasecmp(compat, compatible, len) == 0) 237226584Sdim return (1); 238226584Sdim 239276479Sdim return (0); 240276479Sdim} 241226584Sdim 242226584Sdimconst struct ofw_compat_data * 243226584Sdimofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) 244226584Sdim{ 245276479Sdim 246276479Sdim if (compat == NULL) 247226584Sdim return NULL; 248226584Sdim 249226584Sdim for (; compat->ocd_str != NULL; ++compat) { 250276479Sdim if (ofw_bus_is_compatible(dev, compat->ocd_str)) 251276479Sdim break; 252226584Sdim } 253226584Sdim 254226584Sdim return (compat); 255276479Sdim} 256276479Sdim 257226584Sdimint 258226584Sdimofw_bus_has_prop(device_t dev, const char *propname) 259226584Sdim{ 260276479Sdim phandle_t node; 261276479Sdim 262226584Sdim if ((node = ofw_bus_get_node(dev)) == -1) 263226584Sdim return (0); 264226584Sdim 265276479Sdim return (OF_hasprop(node, propname)); 266276479Sdim} 267226584Sdim 268226584Sdimvoid 269226584Sdimofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 270226584Sdim{ 271226584Sdim pcell_t addrc; 272226584Sdim int msksz; 273226584Sdim 274276479Sdim if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 275276479Sdim addrc = 2; 276276479Sdim ii->opi_addrc = addrc * sizeof(pcell_t); 277276479Sdim 278276479Sdim ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1, 279276479Sdim (void **)&ii->opi_imap); 280276479Sdim if (ii->opi_imapsz > 0) { 281276479Sdim msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1, 282276479Sdim (void **)&ii->opi_imapmsk); 283276479Sdim /* 284276479Sdim * Failure to get the mask is ignored; a full mask is used 285276479Sdim * then. We barf on bad mask sizes, however. 286276479Sdim */ 287276479Sdim if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 288276479Sdim panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 289276479Sdim "property!"); 290288943Sdim } 291276479Sdim} 292276479Sdim 293276479Sdimint 294276479Sdimofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 295226584Sdim int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 296226584Sdim phandle_t *iparent) 297226584Sdim{ 298226584Sdim uint8_t maskbuf[regsz + pintrsz]; 299226584Sdim int rv; 300226584Sdim 301226584Sdim if (ii->opi_imapsz <= 0) 302226584Sdim return (0); 303226584Sdim KASSERT(regsz >= ii->opi_addrc, 304226584Sdim ("ofw_bus_lookup_imap: register size too small: %d < %d", 305226584Sdim regsz, ii->opi_addrc)); 306226584Sdim if (node != -1) { 307280031Sdim rv = OF_getencprop(node, "reg", reg, regsz); 308280031Sdim if (rv < regsz) 309280031Sdim panic("ofw_bus_lookup_imap: cannot get reg property"); 310280031Sdim } 311280031Sdim return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 312280031Sdim ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 313280031Sdim mintrsz, iparent)); 314280031Sdim} 315280031Sdim 316280031Sdim/* 317280031Sdim * Map an interrupt using the firmware reg, interrupt-map and 318280031Sdim * interrupt-map-mask properties. 319280031Sdim * The interrupt property to be mapped must be of size intrsz, and pointed to 320280031Sdim * by intr. The regs property of the node for which the mapping is done must 321288943Sdim * be passed as regs. This property is an array of register specifications; 322280031Sdim * the size of the address part of such a specification must be passed as 323280031Sdim * physsz. Only the first element of the property is used. 324280031Sdim * imap and imapsz hold the interrupt mask and it's size. 325280031Sdim * imapmsk is a pointer to the interrupt-map-mask property, which must have 326280031Sdim * a size of physsz + intrsz; it may be NULL, in which case a full mask is 327280031Sdim * assumed. 328280031Sdim * maskbuf must point to a buffer of length physsz + intrsz. 329226584Sdim * The interrupt is returned in result, which must point to a buffer of length 330226584Sdim * rintrsz (which gives the expected size of the mapped interrupt). 331276479Sdim * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. 332276479Sdim */ 333226584Sdimint 334226584Sdimofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 335226584Sdim void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 336226584Sdim int rintrsz, phandle_t *iparent) 337226584Sdim{ 338226584Sdim phandle_t parent; 339226584Sdim uint8_t *ref = maskbuf; 340276479Sdim uint8_t *uiintr = intr; 341276479Sdim uint8_t *uiregs = regs; 342226584Sdim uint8_t *uiimapmsk = imapmsk; 343276479Sdim uint8_t *mptr; 344276479Sdim pcell_t paddrsz; 345226584Sdim pcell_t pintrsz; 346276479Sdim int i, rsz, tsz; 347276479Sdim 348226584Sdim rsz = -1; 349276479Sdim if (imapmsk != NULL) { 350276479Sdim for (i = 0; i < physsz; i++) 351226584Sdim ref[i] = uiregs[i] & uiimapmsk[i]; 352226584Sdim for (i = 0; i < intrsz; i++) 353226584Sdim ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 354276479Sdim } else { 355276479Sdim bcopy(regs, ref, physsz); 356226584Sdim bcopy(intr, ref + physsz, intrsz); 357276479Sdim } 358276479Sdim 359226584Sdim mptr = imap; 360276479Sdim i = imapsz; 361276479Sdim paddrsz = 0; 362226584Sdim while (i > 0) { 363226584Sdim bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 364276479Sdim#ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS 365276479Sdim /* 366226584Sdim * Find if we need to read the parent address data. 367226584Sdim * CHRP-derived OF bindings, including ePAPR-compliant FDTs, 368226584Sdim * use this as an optional part of the specifier. 369226584Sdim */ 370276479Sdim if (OF_getencprop(OF_node_from_xref(parent), 371276479Sdim "#address-cells", &paddrsz, sizeof(paddrsz)) == -1) 372226584Sdim paddrsz = 0; /* default */ 373226584Sdim paddrsz *= sizeof(pcell_t); 374276479Sdim#endif 375276479Sdim 376226584Sdim if (OF_searchencprop(OF_node_from_xref(parent), 377276479Sdim "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) 378276479Sdim pintrsz = 1; /* default */ 379226584Sdim pintrsz *= sizeof(pcell_t); 380276479Sdim 381276479Sdim /* Compute the map stride size. */ 382226584Sdim tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz; 383276479Sdim KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 384276479Sdim 385226584Sdim if (bcmp(ref, mptr, physsz + intrsz) == 0) { 386276479Sdim bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz, 387276479Sdim result, MIN(rintrsz, pintrsz)); 388226584Sdim 389226584Sdim if (iparent != NULL) 390276479Sdim *iparent = parent; 391276479Sdim return (pintrsz/sizeof(pcell_t)); 392226584Sdim } 393276479Sdim mptr += tsz; 394276479Sdim i -= tsz; 395226584Sdim } 396276479Sdim return (0); 397276479Sdim} 398226584Sdim 399276479Sdimint 400276479Sdimofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent, 401226584Sdim uint32_t *msi_rid) 402226584Sdim{ 403276479Sdim pcell_t *map, mask, msi_base, rid_base, rid_length; 404276479Sdim ssize_t len; 405226584Sdim uint32_t masked_rid, rid; 406226584Sdim int err, i; 407276479Sdim 408276479Sdim /* TODO: This should be OF_searchprop_alloc if we had it */ 409226584Sdim len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map); 410276479Sdim if (len < 0) { 411276479Sdim if (msi_parent != NULL) { 412226584Sdim *msi_parent = 0; 413276479Sdim OF_getencprop(node, "msi-parent", msi_parent, 414276479Sdim sizeof(*msi_parent)); 415261991Sdim } 416276479Sdim if (msi_rid != NULL) 417276479Sdim *msi_rid = pci_rid; 418226584Sdim return (0); 419226584Sdim } 420276479Sdim 421276479Sdim err = ENOENT; 422226584Sdim rid = 0; 423226584Sdim mask = 0xffffffff; 424226584Sdim OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask)); 425226584Sdim 426226584Sdim masked_rid = pci_rid & mask; 427226584Sdim for (i = 0; i < len; i += 4) { 428226584Sdim rid_base = map[i + 0]; 429226584Sdim rid_length = map[i + 3]; 430276479Sdim 431276479Sdim if (masked_rid < rid_base || 432226584Sdim masked_rid >= (rid_base + rid_length)) 433226584Sdim continue; 434226584Sdim 435288943Sdim msi_base = map[i + 2]; 436276479Sdim 437276479Sdim if (msi_parent != NULL) 438226584Sdim *msi_parent = map[i + 1]; 439226584Sdim if (msi_rid != NULL) 440226584Sdim *msi_rid = masked_rid - rid_base + msi_base; 441226584Sdim err = 0; 442276479Sdim break; 443276479Sdim } 444276479Sdim 445276479Sdim free(map, M_OFWPROP); 446226584Sdim 447226584Sdim return (err); 448276479Sdim} 449276479Sdim 450276479Sdimint 451276479Sdimofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells, 452276479Sdim struct resource_list *rl) 453276479Sdim{ 454226584Sdim uint64_t phys, size; 455226584Sdim ssize_t i, j, rid, nreg, ret; 456226584Sdim uint32_t *reg; 457226584Sdim char *name; 458276479Sdim 459276479Sdim /* 460276479Sdim * This may be just redundant when having ofw_bus_devinfo 461226584Sdim * but makes this routine independent of it. 462226584Sdim */ 463226584Sdim ret = OF_getprop_alloc(node, "name", sizeof(*name), (void **)&name); 464226584Sdim if (ret == -1) 465226584Sdim name = NULL; 466226584Sdim 467226584Sdim ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); 468226584Sdim nreg = (ret == -1) ? 0 : ret; 469226584Sdim 470226584Sdim if (nreg % (acells + scells) != 0) { 471226584Sdim if (bootverbose) 472226584Sdim device_printf(dev, "Malformed reg property on <%s>\n", 473226584Sdim (name == NULL) ? "unknown" : name); 474226584Sdim nreg = 0; 475226584Sdim } 476226584Sdim 477226584Sdim for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) { 478276479Sdim phys = size = 0; 479276479Sdim for (j = 0; j < acells; j++) { 480276479Sdim phys <<= 32; 481226584Sdim phys |= reg[i + j]; 482226584Sdim } 483226584Sdim for (j = 0; j < scells; j++) { 484226584Sdim size <<= 32; 485226584Sdim size |= reg[i + acells + j]; 486226584Sdim } 487226584Sdim /* Skip the dummy reg property of glue devices like ssm(4). */ 488226584Sdim if (size != 0) 489226584Sdim resource_list_add(rl, SYS_RES_MEMORY, rid, 490226584Sdim phys, phys + size - 1, size); 491226584Sdim } 492276479Sdim free(name, M_OFWPROP); 493276479Sdim free(reg, M_OFWPROP); 494276479Sdim 495226584Sdim return (0); 496226584Sdim} 497226584Sdim 498226584Sdim/* 499226584Sdim * Get interrupt parent for given node. 500226584Sdim * Returns 0 if interrupt parent doesn't exist. 501226584Sdim */ 502261991Sdimphandle_t 503261991Sdimofw_bus_find_iparent(phandle_t node) 504261991Sdim{ 505276479Sdim phandle_t iparent; 506276479Sdim 507276479Sdim if (OF_searchencprop(node, "interrupt-parent", &iparent, 508261991Sdim sizeof(iparent)) == -1) { 509261991Sdim for (iparent = node; iparent != 0; 510261991Sdim iparent = OF_parent(iparent)) { 511261991Sdim if (OF_hasprop(iparent, "interrupt-controller")) 512261991Sdim break; 513261991Sdim } 514226584Sdim iparent = OF_xref_from_node(iparent); 515226584Sdim } 516226584Sdim return (iparent); 517276479Sdim} 518276479Sdim 519276479Sdimint 520226584Sdimofw_bus_intr_to_rl(device_t dev, phandle_t node, 521226584Sdim struct resource_list *rl, int *rlen) 522226584Sdim{ 523226584Sdim phandle_t iparent; 524226584Sdim uint32_t icells, *intr; 525226584Sdim int err, i, irqnum, nintr, rid; 526226584Sdim boolean_t extended; 527226584Sdim 528226584Sdim nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), 529226584Sdim (void **)&intr); 530276479Sdim if (nintr > 0) { 531276479Sdim iparent = ofw_bus_find_iparent(node); 532226584Sdim if (iparent == 0) { 533226584Sdim device_printf(dev, "No interrupt-parent found, " 534261991Sdim "assuming direct parent\n"); 535226584Sdim iparent = OF_parent(node); 536226584Sdim iparent = OF_xref_from_node(iparent); 537226584Sdim } 538226584Sdim if (OF_searchencprop(OF_node_from_xref(iparent), 539226584Sdim "#interrupt-cells", &icells, sizeof(icells)) == -1) { 540226584Sdim device_printf(dev, "Missing #interrupt-cells " 541226584Sdim "property, assuming <1>\n"); 542226584Sdim icells = 1; 543226584Sdim } 544226584Sdim if (icells < 1 || icells > nintr) { 545226584Sdim device_printf(dev, "Invalid #interrupt-cells property " 546226584Sdim "value <%d>, assuming <1>\n", icells); 547226584Sdim icells = 1; 548226584Sdim } 549226584Sdim extended = false; 550226584Sdim } else { 551226584Sdim nintr = OF_getencprop_alloc(node, "interrupts-extended", 552226584Sdim sizeof(*intr), (void **)&intr); 553226584Sdim if (nintr <= 0) 554226584Sdim return (0); 555226584Sdim extended = true; 556226584Sdim } 557226584Sdim err = 0; 558226584Sdim rid = 0; 559276479Sdim for (i = 0; i < nintr; i += icells) { 560276479Sdim if (extended) { 561226584Sdim iparent = intr[i++]; 562226584Sdim if (OF_searchencprop(OF_node_from_xref(iparent), 563226584Sdim "#interrupt-cells", &icells, sizeof(icells)) == -1) { 564261991Sdim device_printf(dev, "Missing #interrupt-cells " 565226584Sdim "property\n"); 566226584Sdim err = ENOENT; 567226584Sdim break; 568226584Sdim } 569226584Sdim if (icells < 1 || (i + icells) > nintr) { 570226584Sdim device_printf(dev, "Invalid #interrupt-cells " 571226584Sdim "property value <%d>\n", icells); 572226584Sdim err = ERANGE; 573226584Sdim break; 574226584Sdim } 575226584Sdim } 576226584Sdim irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]); 577226584Sdim resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1); 578226584Sdim } 579226584Sdim if (rlen != NULL) 580226584Sdim *rlen = rid; 581226584Sdim free(intr, M_OFWPROP); 582226584Sdim return (err); 583226584Sdim} 584226584Sdim 585226584Sdimphandle_t 586226584Sdimofw_bus_find_child(phandle_t start, const char *child_name) 587226584Sdim{ 588226584Sdim char *name; 589276479Sdim int ret; 590276479Sdim phandle_t child; 591226584Sdim 592226584Sdim for (child = OF_child(start); child != 0; child = OF_peer(child)) { 593226584Sdim ret = OF_getprop_alloc(child, "name", sizeof(*name), (void **)&name); 594226584Sdim if (ret == -1) 595226584Sdim continue; 596226584Sdim if (strcmp(name, child_name) == 0) { 597226584Sdim free(name, M_OFWPROP); 598288943Sdim return (child); 599226584Sdim } 600226584Sdim 601226584Sdim free(name, M_OFWPROP); 602226584Sdim } 603226584Sdim 604226584Sdim return (0); 605226584Sdim} 606226584Sdim 607226584Sdimphandle_t 608226584Sdimofw_bus_find_compatible(phandle_t node, const char *onecompat) 609226584Sdim{ 610226584Sdim phandle_t child, ret; 611226584Sdim void *compat; 612226584Sdim int len; 613226584Sdim 614226584Sdim /* 615226584Sdim * Traverse all children of 'start' node, and find first with 616226584Sdim * matching 'compatible' property. 617226584Sdim */ 618226584Sdim for (child = OF_child(node); child != 0; child = OF_peer(child)) { 619226584Sdim len = OF_getprop_alloc(child, "compatible", 1, &compat); 620226584Sdim if (len >= 0) { 621226584Sdim ret = ofw_bus_node_is_compatible(compat, len, 622226584Sdim onecompat); 623226584Sdim free(compat, M_OFWPROP); 624226584Sdim if (ret != 0) 625226584Sdim return (child); 626276479Sdim } 627276479Sdim 628226584Sdim ret = ofw_bus_find_compatible(child, onecompat); 629226584Sdim if (ret != 0) 630226584Sdim return (ret); 631276479Sdim } 632226584Sdim return (0); 633226584Sdim} 634226584Sdim 635226584Sdim/** 636226584Sdim * @brief Return child of bus whose phandle is node 637226584Sdim * 638226584Sdim * A direct child of @p will be returned if it its phandle in the 639276479Sdim * OFW tree is @p node. Otherwise, NULL is returned. 640276479Sdim * 641226584Sdim * @param bus The bus to examine 642226584Sdim * @param node The phandle_t to look for. 643226584Sdim */ 644276479Sdimdevice_t 645226584Sdimofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node) 646226584Sdim{ 647226584Sdim device_t *children, retval, child; 648226584Sdim int nkid, i; 649226584Sdim 650226584Sdim /* 651276479Sdim * Nothing can match the flag value for no node. 652276479Sdim */ 653226584Sdim if (node == -1) 654226584Sdim return (NULL); 655226584Sdim 656276479Sdim /* 657226584Sdim * Search the children for a match. We microoptimize 658226584Sdim * a bit by not using ofw_bus_get since we already know 659226584Sdim * the parent. We do not recurse. 660226584Sdim */ 661226584Sdim if (device_get_children(bus, &children, &nkid) != 0) 662226584Sdim return (NULL); 663276479Sdim retval = NULL; 664276479Sdim for (i = 0; i < nkid; i++) { 665226584Sdim child = children[i]; 666226584Sdim if (OFW_BUS_GET_NODE(bus, child) == node) { 667226584Sdim retval = child; 668276479Sdim break; 669226584Sdim } 670226584Sdim } 671226584Sdim free(children, M_TEMP); 672226584Sdim 673226584Sdim return (retval); 674226584Sdim} 675276479Sdim 676276479Sdim/* 677226584Sdim * Parse property that contain list of xrefs and values 678226584Sdim * (like standard "clocks" and "resets" properties) 679276479Sdim * Input arguments: 680226584Sdim * node - consumers device node 681226584Sdim * list_name - name of parsed list - "clocks" 682226584Sdim * cells_name - name of size property - "#clock-cells" 683226584Sdim * idx - the index of the requested list entry, or, if -1, an indication 684226584Sdim * to return the number of entries in the parsed list. 685226584Sdim * Output arguments: 686226584Sdim * producer - handle of producer 687226584Sdim * ncells - number of cells in result or the number of items in the list when 688226584Sdim * idx == -1. 689226584Sdim * cells - array of decoded cells 690226584Sdim */ 691226584Sdimstatic int 692226584Sdimofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name, 693226584Sdim const char *cells_name, int idx, phandle_t *producer, int *ncells, 694226584Sdim pcell_t **cells) 695226584Sdim{ 696226584Sdim phandle_t pnode; 697226584Sdim phandle_t *elems; 698226584Sdim uint32_t pcells; 699226584Sdim int rv, i, j, nelems, cnt; 700226584Sdim 701226584Sdim elems = NULL; 702226584Sdim nelems = OF_getencprop_alloc(node, list_name, sizeof(*elems), 703226584Sdim (void **)&elems); 704276479Sdim if (nelems <= 0) 705276479Sdim return (ENOENT); 706226584Sdim rv = (idx == -1) ? 0 : ENOENT; 707226584Sdim for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) { 708276479Sdim pnode = elems[i++]; 709226584Sdim if (OF_getencprop(OF_node_from_xref(pnode), 710276479Sdim cells_name, &pcells, sizeof(pcells)) == -1) { 711276479Sdim printf("Missing %s property\n", cells_name); 712226584Sdim rv = ENOENT; 713226584Sdim break; 714226584Sdim } 715226584Sdim 716226584Sdim if ((i + pcells) > nelems) { 717226584Sdim printf("Invalid %s property value <%d>\n", cells_name, 718276479Sdim pcells); 719276479Sdim rv = ERANGE; 720226584Sdim break; 721226584Sdim } 722226584Sdim if (cnt == idx) { 723226584Sdim *cells= malloc(pcells * sizeof(**cells), M_OFWPROP, 724276479Sdim M_WAITOK); 725226584Sdim *producer = pnode; 726276479Sdim *ncells = pcells; 727226584Sdim for (j = 0; j < pcells; j++) 728226584Sdim (*cells)[j] = elems[i + j]; 729226584Sdim rv = 0; 730226584Sdim break; 731226584Sdim } 732226584Sdim } 733234353Sdim if (elems != NULL) 734276479Sdim free(elems, M_OFWPROP); 735276479Sdim if (idx == -1 && rv == 0) 736226584Sdim *ncells = cnt; 737226584Sdim return (rv); 738226584Sdim} 739234353Sdim 740276479Sdim/* 741276479Sdim * Parse property that contain list of xrefs and values 742226584Sdim * (like standard "clocks" and "resets" properties) 743226584Sdim * Input arguments: 744234353Sdim * node - consumers device node 745234353Sdim * list_name - name of parsed list - "clocks" 746234353Sdim * cells_name - name of size property - "#clock-cells" 747234353Sdim * idx - the index of the requested list entry (>= 0) 748234353Sdim * Output arguments: 749276479Sdim * producer - handle of producer 750276479Sdim * ncells - number of cells in result 751234353Sdim * cells - array of decoded cells 752234353Sdim */ 753276479Sdimint 754234353Sdimofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, 755226584Sdim const char *cells_name, int idx, phandle_t *producer, int *ncells, 756226584Sdim pcell_t **cells) 757226584Sdim{ 758226584Sdim 759226584Sdim KASSERT(idx >= 0, 760226584Sdim ("ofw_bus_parse_xref_list_alloc: negative index supplied")); 761226584Sdim 762276479Sdim return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 763276479Sdim idx, producer, ncells, cells)); 764261991Sdim} 765261991Sdim 766261991Sdim/* 767261991Sdim * Parse property that contain list of xrefs and values 768276479Sdim * (like standard "clocks" and "resets" properties) 769261991Sdim * and determine the number of items in the list 770261991Sdim * Input arguments: 771261991Sdim * node - consumers device node 772226584Sdim * list_name - name of parsed list - "clocks" 773226584Sdim * cells_name - name of size property - "#clock-cells" 774226584Sdim * Output arguments: 775226584Sdim * count - number of items in list 776226584Sdim */ 777226584Sdimint 778226584Sdimofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, 779226584Sdim const char *cells_name, int *count) 780226584Sdim{ 781226584Sdim 782226584Sdim return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, 783226584Sdim -1, NULL, count, NULL)); 784226584Sdim} 785226584Sdim 786226584Sdim/* 787226584Sdim * Find index of string in string list property (case sensitive). 788239462Sdim */ 789239462Sdimint 790226584Sdimofw_bus_find_string_index(phandle_t node, const char *list_name, 791226584Sdim const char *name, int *idx) 792276479Sdim{ 793276479Sdim char *elems; 794226584Sdim int rv, i, cnt, nelems; 795226584Sdim 796226584Sdim elems = NULL; 797276479Sdim nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); 798261991Sdim if (nelems <= 0) 799226584Sdim return (ENOENT); 800239462Sdim 801249423Sdim rv = ENOENT; 802239462Sdim for (i = 0, cnt = 0; i < nelems; cnt++) { 803226584Sdim if (strcmp(elems + i, name) == 0) { 804249423Sdim *idx = cnt; 805239462Sdim rv = 0; 806239462Sdim break; 807226584Sdim } 808249423Sdim i += strlen(elems + i) + 1; 809249423Sdim } 810249423Sdim 811249423Sdim if (elems != NULL) 812249423Sdim free(elems, M_OFWPROP); 813249423Sdim return (rv); 814249423Sdim} 815249423Sdim 816249423Sdim/* 817249423Sdim * Create zero terminated array of strings from string list property. 818249423Sdim */ 819249423Sdimint 820249423Sdimofw_bus_string_list_to_array(phandle_t node, const char *list_name, 821226584Sdim const char ***out_array) 822239462Sdim{ 823239462Sdim char *elems, *tptr; 824239462Sdim const char **array; 825239462Sdim int i, cnt, nelems, len; 826226584Sdim 827226584Sdim elems = NULL; 828226584Sdim nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); 829239462Sdim if (nelems <= 0) 830226584Sdim return (nelems); 831226584Sdim 832226584Sdim /* Count number of strings. */ 833276479Sdim for (i = 0, cnt = 0; i < nelems; cnt++) 834276479Sdim i += strlen(elems + i) + 1; 835226584Sdim 836226584Sdim /* Allocate space for arrays and all strings. */ 837226584Sdim array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP, 838276479Sdim M_WAITOK); 839226584Sdim 840239462Sdim /* Get address of first string. */ 841239462Sdim tptr = (char *)(array + cnt + 1); 842239462Sdim 843226584Sdim /* Copy strings. */ 844226584Sdim memcpy(tptr, elems, nelems); 845226584Sdim free(elems, M_OFWPROP); 846226584Sdim 847226584Sdim /* Fill string pointers. */ 848226584Sdim for (i = 0, cnt = 0; i < nelems; cnt++) { 849239462Sdim len = strlen(tptr) + 1; 850226584Sdim array[cnt] = tptr; 851226584Sdim i += len; 852226584Sdim tptr += len; 853276479Sdim } 854276479Sdim array[cnt] = 0; 855226584Sdim *out_array = array; 856226584Sdim 857226584Sdim return (cnt); 858276479Sdim} 859226584Sdim