1fa92f23marius/*- 21537078pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31537078pfg * 4f0e8374nwhitehorn * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>. 5fa92f23marius * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 6fa92f23marius * All rights reserved. 7fa92f23marius * 8fa92f23marius * Redistribution and use in source and binary forms, with or without 9fa92f23marius * modification, are permitted provided that the following conditions 10fa92f23marius * are met: 11fa92f23marius * 1. Redistributions of source code must retain the above copyright 12fa92f23marius * notice, this list of conditions, and the following disclaimer, 13fa92f23marius * without modification, immediately at the beginning of the file. 14fa92f23marius * 2. Redistributions in binary form must reproduce the above copyright 15fa92f23marius * notice, this list of conditions and the following disclaimer in 16fa92f23marius * the documentation and/or other materials provided with the 17fa92f23marius * distribution. 18fa92f23marius * 19fa92f23marius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20fa92f23marius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21fa92f23marius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22fa92f23marius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 23fa92f23marius * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24fa92f23marius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25fa92f23marius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26fa92f23marius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27fa92f23marius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28fa92f23marius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29fa92f23marius * SUCH DAMAGE. 30fa92f23marius */ 31fa92f23marius 32fa92f23marius#include <sys/cdefs.h> 33fa92f23marius__FBSDID("$FreeBSD$"); 34fa92f23marius 35ef99eb1raj#include "opt_platform.h" 36fa92f23marius#include <sys/param.h> 37fa92f23marius#include <sys/systm.h> 38fa92f23marius#include <sys/bus.h> 39fa92f23marius#include <sys/errno.h> 40f0e8374nwhitehorn#include <sys/libkern.h> 41fa92f23marius 42e2b20dfian#include <machine/resource.h> 43e2b20dfian 44f0e8374nwhitehorn#include <dev/ofw/ofw_bus.h> 45fa92f23marius#include <dev/ofw/ofw_bus_subr.h> 46fa92f23marius#include <dev/ofw/openfirm.h> 47fa92f23marius 48fa92f23marius#include "ofw_bus_if.h" 49fa92f23marius 501592bb3jhibbits#define OFW_COMPAT_LEN 255 515b53c99wma#define OFW_STATUS_LEN 16 521592bb3jhibbits 53fa92f23mariusint 54fa92f23mariusofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) 55fa92f23marius{ 56fa92f23marius 57fa92f23marius if (obd == NULL) 58fa92f23marius return (ENOMEM); 59fa92f23marius /* The 'name' property is considered mandatory. */ 601252c93gonzo if ((OF_getprop_alloc(node, "name", (void **)&obd->obd_name)) == -1) 61fa92f23marius return (EINVAL); 621252c93gonzo OF_getprop_alloc(node, "compatible", (void **)&obd->obd_compat); 631252c93gonzo OF_getprop_alloc(node, "device_type", (void **)&obd->obd_type); 641252c93gonzo OF_getprop_alloc(node, "model", (void **)&obd->obd_model); 651252c93gonzo OF_getprop_alloc(node, "status", (void **)&obd->obd_status); 66fa92f23marius obd->obd_node = node; 67fa92f23marius return (0); 68fa92f23marius} 69fa92f23marius 70fa92f23mariusvoid 71fa92f23mariusofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 72fa92f23marius{ 73fa92f23marius 74fa92f23marius if (obd == NULL) 75fa92f23marius return; 76fa92f23marius if (obd->obd_compat != NULL) 77fa92f23marius free(obd->obd_compat, M_OFWPROP); 78fa92f23marius if (obd->obd_model != NULL) 79fa92f23marius free(obd->obd_model, M_OFWPROP); 80fa92f23marius if (obd->obd_name != NULL) 81fa92f23marius free(obd->obd_name, M_OFWPROP); 82fa92f23marius if (obd->obd_type != NULL) 83fa92f23marius free(obd->obd_type, M_OFWPROP); 846c040d5nwhitehorn if (obd->obd_status != NULL) 856c040d5nwhitehorn free(obd->obd_status, M_OFWPROP); 86fa92f23marius} 87fa92f23marius 883961775mariusint 89f0e8374nwhitehornofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 90f0e8374nwhitehorn size_t buflen) 91f0e8374nwhitehorn{ 923961775marius 932e698f5imp *buf = '\0'; 94fb8ca85manu if (!ofw_bus_status_okay(child)) 95fb8ca85manu return (0); 96fb8ca85manu 97f0e8374nwhitehorn if (ofw_bus_get_name(child) != NULL) { 98f0e8374nwhitehorn strlcat(buf, "name=", buflen); 99f0e8374nwhitehorn strlcat(buf, ofw_bus_get_name(child), buflen); 100f0e8374nwhitehorn } 101f0e8374nwhitehorn 102f0e8374nwhitehorn if (ofw_bus_get_compat(child) != NULL) { 103f0e8374nwhitehorn strlcat(buf, " compat=", buflen); 104f0e8374nwhitehorn strlcat(buf, ofw_bus_get_compat(child), buflen); 105f0e8374nwhitehorn } 106fb8ca85manu 107f0e8374nwhitehorn return (0); 108f0e8374nwhitehorn}; 109fa92f23marius 110fa92f23mariusconst char * 111fa92f23mariusofw_bus_gen_get_compat(device_t bus, device_t dev) 112fa92f23marius{ 113fa92f23marius const struct ofw_bus_devinfo *obd; 1143961775marius 1153961775marius obd = OFW_BUS_GET_DEVINFO(bus, dev); 116fa92f23marius if (obd == NULL) 117fa92f23marius return (NULL); 118fa92f23marius return (obd->obd_compat); 119fa92f23marius} 1203961775marius 121fa92f23mariusconst char * 122fa92f23mariusofw_bus_gen_get_model(device_t bus, device_t dev) 123fa92f23marius{ 124fa92f23marius const struct ofw_bus_devinfo *obd; 125fa92f23marius 1263961775marius obd = OFW_BUS_GET_DEVINFO(bus, dev); 127fa92f23marius if (obd == NULL) 128fa92f23marius return (NULL); 129fa92f23marius return (obd->obd_model); 130fa92f23marius} 131fa92f23marius 132fa92f23mariusconst char * 133fa92f23mariusofw_bus_gen_get_name(device_t bus, device_t dev) 134fa92f23marius{ 135fa92f23marius const struct ofw_bus_devinfo *obd; 136fa92f23marius 1373961775marius obd = OFW_BUS_GET_DEVINFO(bus, dev); 138fa92f23marius if (obd == NULL) 139fa92f23marius return (NULL); 140fa92f23marius return (obd->obd_name); 141fa92f23marius} 142fa92f23marius 143fa92f23mariusphandle_t 144fa92f23mariusofw_bus_gen_get_node(device_t bus, device_t dev) 145fa92f23marius{ 146fa92f23marius const struct ofw_bus_devinfo *obd; 147fa92f23marius 1483961775marius obd = OFW_BUS_GET_DEVINFO(bus, dev); 149fa92f23marius if (obd == NULL) 150fa92f23marius return (0); 151fa92f23marius return (obd->obd_node); 152fa92f23marius} 153fa92f23marius 154fa92f23mariusconst char * 155fa92f23mariusofw_bus_gen_get_type(device_t bus, device_t dev) 156fa92f23marius{ 157fa92f23marius const struct ofw_bus_devinfo *obd; 158fa92f23marius 1593961775marius obd = OFW_BUS_GET_DEVINFO(bus, dev); 160fa92f23marius if (obd == NULL) 161fa92f23marius return (NULL); 162fa92f23marius return (obd->obd_type); 163fa92f23marius} 164f0e8374nwhitehorn 1656c040d5nwhitehornconst char * 1666c040d5nwhitehornofw_bus_get_status(device_t dev) 1676c040d5nwhitehorn{ 1686c040d5nwhitehorn const struct ofw_bus_devinfo *obd; 1696c040d5nwhitehorn 1706c040d5nwhitehorn obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); 1716c040d5nwhitehorn if (obd == NULL) 1726c040d5nwhitehorn return (NULL); 1736c040d5nwhitehorn 1746c040d5nwhitehorn return (obd->obd_status); 1756c040d5nwhitehorn} 1766c040d5nwhitehorn 1776c040d5nwhitehornint 1786c040d5nwhitehornofw_bus_status_okay(device_t dev) 1796c040d5nwhitehorn{ 1806c040d5nwhitehorn const char *status; 1816c040d5nwhitehorn 1826c040d5nwhitehorn status = ofw_bus_get_status(dev); 1830901860andrew if (status == NULL || strcmp(status, "okay") == 0 || 1840901860andrew strcmp(status, "ok") == 0) 1856c040d5nwhitehorn return (1); 186c71e73bmjg 1876c040d5nwhitehorn return (0); 1886c040d5nwhitehorn} 1896c040d5nwhitehorn 1905b53c99wmaint 1915b53c99wmaofw_bus_node_status_okay(phandle_t node) 1925b53c99wma{ 1935b53c99wma char status[OFW_STATUS_LEN]; 1945b53c99wma int len; 1955b53c99wma 1965b53c99wma len = OF_getproplen(node, "status"); 1975b53c99wma if (len <= 0) 1985b53c99wma return (1); 1995b53c99wma 2005b53c99wma OF_getprop(node, "status", status, OFW_STATUS_LEN); 2015b53c99wma if ((len == 5 && (bcmp(status, "okay", len) == 0)) || 2025b53c99wma (len == 3 && (bcmp(status, "ok", len)))) 2035b53c99wma return (1); 2045b53c99wma 2055b53c99wma return (0); 2065b53c99wma} 2075b53c99wma 208cb5057aandrewstatic int 2091592bb3jhibbitsofw_bus_node_is_compatible_int(const char *compat, int len, 2101592bb3jhibbits const char *onecompat) 211cb5057aandrew{ 212cb5057aandrew int onelen, l, ret; 213cb5057aandrew 214cb5057aandrew onelen = strlen(onecompat); 215cb5057aandrew 216cb5057aandrew ret = 0; 217cb5057aandrew while (len > 0) { 218cb5057aandrew if (strlen(compat) == onelen && 219cb5057aandrew strncasecmp(compat, onecompat, onelen) == 0) { 220cb5057aandrew /* Found it. */ 221cb5057aandrew ret = 1; 222cb5057aandrew break; 223cb5057aandrew } 224cb5057aandrew 225cb5057aandrew /* Slide to the next sub-string. */ 226cb5057aandrew l = strlen(compat) + 1; 227cb5057aandrew compat += l; 228cb5057aandrew len -= l; 229cb5057aandrew } 230cb5057aandrew 231cb5057aandrew return (ret); 232cb5057aandrew} 233cb5057aandrew 234ef99eb1rajint 2351592bb3jhibbitsofw_bus_node_is_compatible(phandle_t node, const char *compatstr) 2361592bb3jhibbits{ 2371592bb3jhibbits char compat[OFW_COMPAT_LEN]; 2381592bb3jhibbits int len, rv; 2391592bb3jhibbits 2401592bb3jhibbits if ((len = OF_getproplen(node, "compatible")) <= 0) 2411592bb3jhibbits return (0); 2421592bb3jhibbits 2431592bb3jhibbits bzero(compat, OFW_COMPAT_LEN); 2441592bb3jhibbits 2451592bb3jhibbits if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0) 2461592bb3jhibbits return (0); 2471592bb3jhibbits 2481592bb3jhibbits rv = ofw_bus_node_is_compatible_int(compat, len, compatstr); 2491592bb3jhibbits 2501592bb3jhibbits return (rv); 2511592bb3jhibbits} 2521592bb3jhibbits 2531592bb3jhibbitsint 254ef99eb1rajofw_bus_is_compatible(device_t dev, const char *onecompat) 255ef99eb1raj{ 256ef99eb1raj phandle_t node; 257ef99eb1raj const char *compat; 258cb5057aandrew int len; 259ef99eb1raj 260ef99eb1raj if ((compat = ofw_bus_get_compat(dev)) == NULL) 261ef99eb1raj return (0); 262ef99eb1raj 2636a20b9dnwhitehorn if ((node = ofw_bus_get_node(dev)) == -1) 264ef99eb1raj return (0); 265ef99eb1raj 266ef99eb1raj /* Get total 'compatible' prop len */ 267ef99eb1raj if ((len = OF_getproplen(node, "compatible")) <= 0) 268ef99eb1raj return (0); 269ef99eb1raj 2701592bb3jhibbits return (ofw_bus_node_is_compatible_int(compat, len, onecompat)); 271ef99eb1raj} 272ef99eb1raj 273ef99eb1rajint 274ef99eb1rajofw_bus_is_compatible_strict(device_t dev, const char *compatible) 275ef99eb1raj{ 276ef99eb1raj const char *compat; 2775320d21hrs size_t len; 278ef99eb1raj 279ef99eb1raj if ((compat = ofw_bus_get_compat(dev)) == NULL) 280ef99eb1raj return (0); 281ef99eb1raj 2825320d21hrs len = strlen(compatible); 2835320d21hrs if (strlen(compat) == len && 2845320d21hrs strncasecmp(compat, compatible, len) == 0) 285ef99eb1raj return (1); 286ef99eb1raj 287ef99eb1raj return (0); 288ef99eb1raj} 289ef99eb1raj 2904469fbfianconst struct ofw_compat_data * 2914469fbfianofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) 2924469fbfian{ 2934469fbfian 2944469fbfian if (compat == NULL) 2954469fbfian return NULL; 2964469fbfian 2974469fbfian for (; compat->ocd_str != NULL; ++compat) { 2984469fbfian if (ofw_bus_is_compatible(dev, compat->ocd_str)) 2994469fbfian break; 3004469fbfian } 3014469fbfian 3024469fbfian return (compat); 3034469fbfian} 3044469fbfian 3055320d21hrsint 3065320d21hrsofw_bus_has_prop(device_t dev, const char *propname) 3075320d21hrs{ 3085320d21hrs phandle_t node; 3095320d21hrs 3105320d21hrs if ((node = ofw_bus_get_node(dev)) == -1) 3115320d21hrs return (0); 3125320d21hrs 3135320d21hrs return (OF_hasprop(node, propname)); 3145320d21hrs} 3155320d21hrs 316f0e8374nwhitehornvoid 317f0e8374nwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 318f0e8374nwhitehorn{ 319f0e8374nwhitehorn pcell_t addrc; 320f0e8374nwhitehorn int msksz; 321f0e8374nwhitehorn 3229a7fe80nwhitehorn if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 323f0e8374nwhitehorn addrc = 2; 324f0e8374nwhitehorn ii->opi_addrc = addrc * sizeof(pcell_t); 325f0e8374nwhitehorn 3263308d36gonzo ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 327f0e8374nwhitehorn (void **)&ii->opi_imap); 328f0e8374nwhitehorn if (ii->opi_imapsz > 0) { 3293308d36gonzo msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 330f0e8374nwhitehorn (void **)&ii->opi_imapmsk); 331f0e8374nwhitehorn /* 3323961775marius * Failure to get the mask is ignored; a full mask is used 3333961775marius * then. We barf on bad mask sizes, however. 334f0e8374nwhitehorn */ 3353961775marius if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 336f0e8374nwhitehorn panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 337f0e8374nwhitehorn "property!"); 338f0e8374nwhitehorn } 339f0e8374nwhitehorn} 340f0e8374nwhitehorn 341f0e8374nwhitehornint 342f0e8374nwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 343f0e8374nwhitehorn int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 3445841c2dnwhitehorn phandle_t *iparent) 345f0e8374nwhitehorn{ 3465841c2dnwhitehorn uint8_t maskbuf[regsz + pintrsz]; 347f0e8374nwhitehorn int rv; 348f0e8374nwhitehorn 349f0e8374nwhitehorn if (ii->opi_imapsz <= 0) 350f0e8374nwhitehorn return (0); 351f0e8374nwhitehorn KASSERT(regsz >= ii->opi_addrc, 352f0e8374nwhitehorn ("ofw_bus_lookup_imap: register size too small: %d < %d", 353f0e8374nwhitehorn regsz, ii->opi_addrc)); 3541907ae5nwhitehorn if (node != -1) { 3559a7fe80nwhitehorn rv = OF_getencprop(node, "reg", reg, regsz); 3561907ae5nwhitehorn if (rv < regsz) 3571907ae5nwhitehorn panic("ofw_bus_lookup_imap: cannot get reg property"); 3581907ae5nwhitehorn } 359f0e8374nwhitehorn return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 360f0e8374nwhitehorn ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 361c757ee9nwhitehorn mintrsz, iparent)); 362f0e8374nwhitehorn} 363f0e8374nwhitehorn 364f0e8374nwhitehorn/* 365f0e8374nwhitehorn * Map an interrupt using the firmware reg, interrupt-map and 366f0e8374nwhitehorn * interrupt-map-mask properties. 367f0e8374nwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to 3683961775marius * by intr. The regs property of the node for which the mapping is done must 369f0e8374nwhitehorn * be passed as regs. This property is an array of register specifications; 370f0e8374nwhitehorn * the size of the address part of such a specification must be passed as 3713961775marius * physsz. Only the first element of the property is used. 372f0e8374nwhitehorn * imap and imapsz hold the interrupt mask and it's size. 373f0e8374nwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have 374f0e8374nwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is 375f0e8374nwhitehorn * assumed. 376f0e8374nwhitehorn * maskbuf must point to a buffer of length physsz + intrsz. 377f0e8374nwhitehorn * The interrupt is returned in result, which must point to a buffer of length 378f0e8374nwhitehorn * rintrsz (which gives the expected size of the mapped interrupt). 379f1e1cbdnwhitehorn * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. 380f0e8374nwhitehorn */ 381f0e8374nwhitehornint 382f0e8374nwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 383f0e8374nwhitehorn void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 384c757ee9nwhitehorn int rintrsz, phandle_t *iparent) 385f0e8374nwhitehorn{ 386f0e8374nwhitehorn phandle_t parent; 3873961775marius uint8_t *ref = maskbuf; 3883961775marius uint8_t *uiintr = intr; 3893961775marius uint8_t *uiregs = regs; 3903961775marius uint8_t *uiimapmsk = imapmsk; 3913961775marius uint8_t *mptr; 392567d111andrew pcell_t paddrsz; 393f0e8374nwhitehorn pcell_t pintrsz; 394c8da6fakan int i, tsz; 395f0e8374nwhitehorn 396f0e8374nwhitehorn if (imapmsk != NULL) { 397f0e8374nwhitehorn for (i = 0; i < physsz; i++) 398f0e8374nwhitehorn ref[i] = uiregs[i] & uiimapmsk[i]; 399f0e8374nwhitehorn for (i = 0; i < intrsz; i++) 400f0e8374nwhitehorn ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 401f0e8374nwhitehorn } else { 402f0e8374nwhitehorn bcopy(regs, ref, physsz); 403f0e8374nwhitehorn bcopy(intr, ref + physsz, intrsz); 404f0e8374nwhitehorn } 405f0e8374nwhitehorn 406f0e8374nwhitehorn mptr = imap; 407f0e8374nwhitehorn i = imapsz; 408567d111andrew paddrsz = 0; 409f0e8374nwhitehorn while (i > 0) { 410f0e8374nwhitehorn bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 411b634505nwhitehorn#ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS 412567d111andrew /* 413b634505nwhitehorn * Find if we need to read the parent address data. 414b634505nwhitehorn * CHRP-derived OF bindings, including ePAPR-compliant FDTs, 415b634505nwhitehorn * use this as an optional part of the specifier. 416567d111andrew */ 417567d111andrew if (OF_getencprop(OF_node_from_xref(parent), 418567d111andrew "#address-cells", &paddrsz, sizeof(paddrsz)) == -1) 419567d111andrew paddrsz = 0; /* default */ 420567d111andrew paddrsz *= sizeof(pcell_t); 421567d111andrew#endif 422567d111andrew 423b149a9eian if (OF_searchencprop(OF_node_from_xref(parent), 4249a7fe80nwhitehorn "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) 425f0e8374nwhitehorn pintrsz = 1; /* default */ 426f0e8374nwhitehorn pintrsz *= sizeof(pcell_t); 4274ca1211nwhitehorn 4283961775marius /* Compute the map stride size. */ 429567d111andrew tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz; 4304ca1211nwhitehorn KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 4313961775marius 432f0e8374nwhitehorn if (bcmp(ref, mptr, physsz + intrsz) == 0) { 433567d111andrew bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz, 434f1e1cbdnwhitehorn result, MIN(rintrsz, pintrsz)); 435c757ee9nwhitehorn 436c757ee9nwhitehorn if (iparent != NULL) 437c757ee9nwhitehorn *iparent = parent; 438f1e1cbdnwhitehorn return (pintrsz/sizeof(pcell_t)); 439f0e8374nwhitehorn } 440f0e8374nwhitehorn mptr += tsz; 441f0e8374nwhitehorn i -= tsz; 442f0e8374nwhitehorn } 443f0e8374nwhitehorn return (0); 444f0e8374nwhitehorn} 4451907ae5nwhitehorn 446e2b20dfianint 447cb60d23andrewofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent, 448cb60d23andrew uint32_t *msi_rid) 449cb60d23andrew{ 450cb60d23andrew pcell_t *map, mask, msi_base, rid_base, rid_length; 451cb60d23andrew ssize_t len; 452c8da6fakan uint32_t masked_rid; 453cb60d23andrew int err, i; 454cb60d23andrew 455cb60d23andrew /* TODO: This should be OF_searchprop_alloc if we had it */ 4563308d36gonzo len = OF_getencprop_alloc_multi(node, "msi-map", sizeof(*map), 4573308d36gonzo (void **)&map); 458cb60d23andrew if (len < 0) { 459cb60d23andrew if (msi_parent != NULL) { 460cb60d23andrew *msi_parent = 0; 461cb60d23andrew OF_getencprop(node, "msi-parent", msi_parent, 462cb60d23andrew sizeof(*msi_parent)); 463cb60d23andrew } 464cb60d23andrew if (msi_rid != NULL) 465cb60d23andrew *msi_rid = pci_rid; 466cb60d23andrew return (0); 467cb60d23andrew } 468cb60d23andrew 469cb60d23andrew err = ENOENT; 470cb60d23andrew mask = 0xffffffff; 471cb60d23andrew OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask)); 472cb60d23andrew 473cb60d23andrew masked_rid = pci_rid & mask; 474cb60d23andrew for (i = 0; i < len; i += 4) { 475cb60d23andrew rid_base = map[i + 0]; 476cb60d23andrew rid_length = map[i + 3]; 477cb60d23andrew 478cb60d23andrew if (masked_rid < rid_base || 479cb60d23andrew masked_rid >= (rid_base + rid_length)) 480cb60d23andrew continue; 481cb60d23andrew 482cb60d23andrew msi_base = map[i + 2]; 483cb60d23andrew 484cb60d23andrew if (msi_parent != NULL) 485cb60d23andrew *msi_parent = map[i + 1]; 486cb60d23andrew if (msi_rid != NULL) 487cb60d23andrew *msi_rid = masked_rid - rid_base + msi_base; 488cb60d23andrew err = 0; 489cb60d23andrew break; 490cb60d23andrew } 491cb60d23andrew 492cb60d23andrew free(map, M_OFWPROP); 493cb60d23andrew 494cb60d23andrew return (err); 495cb60d23andrew} 496cb60d23andrew 497ad8db5bmwstatic int 498ad8db5bmwofw_bus_reg_to_rl_helper(device_t dev, phandle_t node, pcell_t acells, pcell_t scells, 499ad8db5bmw struct resource_list *rl, const char *reg_source) 50005a9ec2zbb{ 50105a9ec2zbb uint64_t phys, size; 50205a9ec2zbb ssize_t i, j, rid, nreg, ret; 50305a9ec2zbb uint32_t *reg; 50405a9ec2zbb char *name; 50505a9ec2zbb 50605a9ec2zbb /* 50705a9ec2zbb * This may be just redundant when having ofw_bus_devinfo 50805a9ec2zbb * but makes this routine independent of it. 50905a9ec2zbb */ 5101252c93gonzo ret = OF_getprop_alloc(node, "name", (void **)&name); 51105a9ec2zbb if (ret == -1) 51205a9ec2zbb name = NULL; 51305a9ec2zbb 5143308d36gonzo ret = OF_getencprop_alloc_multi(node, reg_source, sizeof(*reg), 5153308d36gonzo (void **)®); 51605a9ec2zbb nreg = (ret == -1) ? 0 : ret; 51705a9ec2zbb 51805a9ec2zbb if (nreg % (acells + scells) != 0) { 51905a9ec2zbb if (bootverbose) 52005a9ec2zbb device_printf(dev, "Malformed reg property on <%s>\n", 52105a9ec2zbb (name == NULL) ? "unknown" : name); 52205a9ec2zbb nreg = 0; 52305a9ec2zbb } 52405a9ec2zbb 52505a9ec2zbb for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) { 52605a9ec2zbb phys = size = 0; 52705a9ec2zbb for (j = 0; j < acells; j++) { 52805a9ec2zbb phys <<= 32; 52905a9ec2zbb phys |= reg[i + j]; 53005a9ec2zbb } 53105a9ec2zbb for (j = 0; j < scells; j++) { 53205a9ec2zbb size <<= 32; 53305a9ec2zbb size |= reg[i + acells + j]; 53405a9ec2zbb } 53505a9ec2zbb /* Skip the dummy reg property of glue devices like ssm(4). */ 53605a9ec2zbb if (size != 0) 53705a9ec2zbb resource_list_add(rl, SYS_RES_MEMORY, rid, 53805a9ec2zbb phys, phys + size - 1, size); 53905a9ec2zbb } 54005a9ec2zbb free(name, M_OFWPROP); 54105a9ec2zbb free(reg, M_OFWPROP); 54205a9ec2zbb 54305a9ec2zbb return (0); 54405a9ec2zbb} 54505a9ec2zbb 546ad8db5bmwint 547ad8db5bmwofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells, 548ad8db5bmw struct resource_list *rl) 549ad8db5bmw{ 550ad8db5bmw 551ad8db5bmw return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells, rl, "reg")); 552ad8db5bmw} 553ad8db5bmw 554ad8db5bmwint 555ad8db5bmwofw_bus_assigned_addresses_to_rl(device_t dev, phandle_t node, pcell_t acells, 556ad8db5bmw pcell_t scells, struct resource_list *rl) 557ad8db5bmw{ 558ad8db5bmw 559ad8db5bmw return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells, 560ad8db5bmw rl, "assigned-addresses")); 561ad8db5bmw} 562ad8db5bmw 563f4f37aammel/* 564f4f37aammel * Get interrupt parent for given node. 565f4f37aammel * Returns 0 if interrupt parent doesn't exist. 566f4f37aammel */ 567f4f37aammelphandle_t 568f4f37aammelofw_bus_find_iparent(phandle_t node) 569f4f37aammel{ 570f4f37aammel phandle_t iparent; 571f4f37aammel 572f4f37aammel if (OF_searchencprop(node, "interrupt-parent", &iparent, 573f4f37aammel sizeof(iparent)) == -1) { 574f4f37aammel for (iparent = node; iparent != 0; 575f4f37aammel iparent = OF_parent(iparent)) { 576f4f37aammel if (OF_hasprop(iparent, "interrupt-controller")) 577f4f37aammel break; 578f4f37aammel } 579f4f37aammel iparent = OF_xref_from_node(iparent); 580f4f37aammel } 581f4f37aammel return (iparent); 582f4f37aammel} 583f4f37aammel 58405a9ec2zbbint 585deecdc3brofw_bus_intr_to_rl(device_t dev, phandle_t node, 586deecdc3br struct resource_list *rl, int *rlen) 587e2b20dfian{ 588e2b20dfian phandle_t iparent; 589