1152683Smarius/*- 2186128Snwhitehorn * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>. 3152683Smarius * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 4152683Smarius * All rights reserved. 5152683Smarius * 6152683Smarius * Redistribution and use in source and binary forms, with or without 7152683Smarius * modification, are permitted provided that the following conditions 8152683Smarius * are met: 9152683Smarius * 1. Redistributions of source code must retain the above copyright 10152683Smarius * notice, this list of conditions, and the following disclaimer, 11152683Smarius * without modification, immediately at the beginning of the file. 12152683Smarius * 2. Redistributions in binary form must reproduce the above copyright 13152683Smarius * notice, this list of conditions and the following disclaimer in 14152683Smarius * the documentation and/or other materials provided with the 15152683Smarius * distribution. 16152683Smarius * 17152683Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18152683Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19152683Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20152683Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 21152683Smarius * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22152683Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23152683Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24152683Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25152683Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26152683Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27152683Smarius * SUCH DAMAGE. 28152683Smarius */ 29152683Smarius 30152683Smarius#include <sys/cdefs.h> 31152683Smarius__FBSDID("$FreeBSD: releng/10.3/sys/dev/ofw/ofw_bus_subr.c 283334 2015-05-23 22:36:41Z ian $"); 32152683Smarius 33208614Sraj#include "opt_platform.h" 34152683Smarius#include <sys/param.h> 35152683Smarius#include <sys/systm.h> 36152683Smarius#include <sys/bus.h> 37152683Smarius#include <sys/errno.h> 38186128Snwhitehorn#include <sys/libkern.h> 39152683Smarius 40273675Sian#include <machine/resource.h> 41273675Sian 42186128Snwhitehorn#include <dev/ofw/ofw_bus.h> 43152683Smarius#include <dev/ofw/ofw_bus_subr.h> 44152683Smarius#include <dev/ofw/openfirm.h> 45152683Smarius 46152683Smarius#include "ofw_bus_if.h" 47152683Smarius 48152683Smariusint 49152683Smariusofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) 50152683Smarius{ 51152683Smarius 52152683Smarius if (obd == NULL) 53152683Smarius return (ENOMEM); 54152683Smarius /* The 'name' property is considered mandatory. */ 55152683Smarius if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) 56152683Smarius return (EINVAL); 57152683Smarius OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); 58152683Smarius OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); 59152683Smarius OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); 60266128Sian OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status); 61152683Smarius obd->obd_node = node; 62152683Smarius return (0); 63152683Smarius} 64152683Smarius 65152683Smariusvoid 66152683Smariusofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 67152683Smarius{ 68152683Smarius 69152683Smarius if (obd == NULL) 70152683Smarius return; 71152683Smarius if (obd->obd_compat != NULL) 72152683Smarius free(obd->obd_compat, M_OFWPROP); 73152683Smarius if (obd->obd_model != NULL) 74152683Smarius free(obd->obd_model, M_OFWPROP); 75152683Smarius if (obd->obd_name != NULL) 76152683Smarius free(obd->obd_name, M_OFWPROP); 77152683Smarius if (obd->obd_type != NULL) 78152683Smarius free(obd->obd_type, M_OFWPROP); 79266128Sian if (obd->obd_status != NULL) 80266128Sian free(obd->obd_status, M_OFWPROP); 81152683Smarius} 82152683Smarius 83194138Smariusint 84186128Snwhitehornofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 85186128Snwhitehorn size_t buflen) 86186128Snwhitehorn{ 87194138Smarius 88186128Snwhitehorn if (ofw_bus_get_name(child) != NULL) { 89186128Snwhitehorn strlcat(buf, "name=", buflen); 90186128Snwhitehorn strlcat(buf, ofw_bus_get_name(child), buflen); 91186128Snwhitehorn } 92152683Smarius 93186128Snwhitehorn if (ofw_bus_get_compat(child) != NULL) { 94186128Snwhitehorn strlcat(buf, " compat=", buflen); 95186128Snwhitehorn strlcat(buf, ofw_bus_get_compat(child), buflen); 96186128Snwhitehorn } 97186128Snwhitehorn return (0); 98186128Snwhitehorn}; 99186128Snwhitehorn 100152683Smariusconst char * 101152683Smariusofw_bus_gen_get_compat(device_t bus, device_t dev) 102152683Smarius{ 103152683Smarius const struct ofw_bus_devinfo *obd; 104194138Smarius 105194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 106152683Smarius if (obd == NULL) 107152683Smarius return (NULL); 108152683Smarius return (obd->obd_compat); 109152683Smarius} 110194138Smarius 111152683Smariusconst char * 112152683Smariusofw_bus_gen_get_model(device_t bus, device_t dev) 113152683Smarius{ 114152683Smarius const struct ofw_bus_devinfo *obd; 115152683Smarius 116194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 117152683Smarius if (obd == NULL) 118152683Smarius return (NULL); 119152683Smarius return (obd->obd_model); 120152683Smarius} 121152683Smarius 122152683Smariusconst char * 123152683Smariusofw_bus_gen_get_name(device_t bus, device_t dev) 124152683Smarius{ 125152683Smarius const struct ofw_bus_devinfo *obd; 126152683Smarius 127194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 128152683Smarius if (obd == NULL) 129152683Smarius return (NULL); 130152683Smarius return (obd->obd_name); 131152683Smarius} 132152683Smarius 133152683Smariusphandle_t 134152683Smariusofw_bus_gen_get_node(device_t bus, device_t dev) 135152683Smarius{ 136152683Smarius const struct ofw_bus_devinfo *obd; 137152683Smarius 138194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 139152683Smarius if (obd == NULL) 140152683Smarius return (0); 141152683Smarius return (obd->obd_node); 142152683Smarius} 143152683Smarius 144152683Smariusconst char * 145152683Smariusofw_bus_gen_get_type(device_t bus, device_t dev) 146152683Smarius{ 147152683Smarius const struct ofw_bus_devinfo *obd; 148152683Smarius 149194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 150152683Smarius if (obd == NULL) 151152683Smarius return (NULL); 152152683Smarius return (obd->obd_type); 153152683Smarius} 154186128Snwhitehorn 155266128Sianconst char * 156266128Sianofw_bus_get_status(device_t dev) 157266128Sian{ 158266128Sian const struct ofw_bus_devinfo *obd; 159266128Sian 160266128Sian obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); 161266128Sian if (obd == NULL) 162266128Sian return (NULL); 163266128Sian 164266128Sian return (obd->obd_status); 165266128Sian} 166266128Sian 167208614Srajint 168266128Sianofw_bus_status_okay(device_t dev) 169266128Sian{ 170266128Sian const char *status; 171266128Sian 172266128Sian status = ofw_bus_get_status(dev); 173266128Sian if (status == NULL || strcmp(status, "okay") == 0) 174266128Sian return (1); 175266128Sian 176266128Sian return (0); 177266128Sian} 178266128Sian 179266128Sianint 180208614Srajofw_bus_is_compatible(device_t dev, const char *onecompat) 181208614Sraj{ 182208614Sraj phandle_t node; 183208614Sraj const char *compat; 184208614Sraj int len, onelen, l; 185208614Sraj 186208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 187208614Sraj return (0); 188208614Sraj 189233018Snwhitehorn if ((node = ofw_bus_get_node(dev)) == -1) 190208614Sraj return (0); 191208614Sraj 192208614Sraj /* Get total 'compatible' prop len */ 193208614Sraj if ((len = OF_getproplen(node, "compatible")) <= 0) 194208614Sraj return (0); 195208614Sraj 196208614Sraj onelen = strlen(onecompat); 197208614Sraj 198208614Sraj while (len > 0) { 199239366Shrs if (strlen(compat) == onelen && 200239366Shrs strncasecmp(compat, onecompat, onelen) == 0) 201208614Sraj /* Found it. */ 202208614Sraj return (1); 203208614Sraj 204208614Sraj /* Slide to the next sub-string. */ 205208614Sraj l = strlen(compat) + 1; 206208614Sraj compat += l; 207208614Sraj len -= l; 208208614Sraj } 209208614Sraj return (0); 210208614Sraj} 211208614Sraj 212208614Srajint 213208614Srajofw_bus_is_compatible_strict(device_t dev, const char *compatible) 214208614Sraj{ 215208614Sraj const char *compat; 216239366Shrs size_t len; 217208614Sraj 218208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 219208614Sraj return (0); 220208614Sraj 221239366Shrs len = strlen(compatible); 222239366Shrs if (strlen(compat) == len && 223239366Shrs strncasecmp(compat, compatible, len) == 0) 224208614Sraj return (1); 225208614Sraj 226208614Sraj return (0); 227208614Sraj} 228208614Sraj 229259316Sianconst struct ofw_compat_data * 230259316Sianofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) 231259316Sian{ 232259316Sian 233259316Sian if (compat == NULL) 234259316Sian return NULL; 235259316Sian 236259316Sian for (; compat->ocd_str != NULL; ++compat) { 237259316Sian if (ofw_bus_is_compatible(dev, compat->ocd_str)) 238259316Sian break; 239259316Sian } 240259316Sian 241259316Sian return (compat); 242259316Sian} 243259316Sian 244239366Shrsint 245239366Shrsofw_bus_has_prop(device_t dev, const char *propname) 246239366Shrs{ 247239366Shrs phandle_t node; 248239366Shrs 249239366Shrs if ((node = ofw_bus_get_node(dev)) == -1) 250239366Shrs return (0); 251239366Shrs 252239366Shrs return (OF_hasprop(node, propname)); 253239366Shrs} 254239366Shrs 255186128Snwhitehornvoid 256186128Snwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 257186128Snwhitehorn{ 258186128Snwhitehorn pcell_t addrc; 259186128Snwhitehorn int msksz; 260186128Snwhitehorn 261265967Sian if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 262186128Snwhitehorn addrc = 2; 263186128Snwhitehorn ii->opi_addrc = addrc * sizeof(pcell_t); 264186128Snwhitehorn 265265967Sian ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1, 266186128Snwhitehorn (void **)&ii->opi_imap); 267186128Snwhitehorn if (ii->opi_imapsz > 0) { 268265967Sian msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1, 269186128Snwhitehorn (void **)&ii->opi_imapmsk); 270186128Snwhitehorn /* 271194138Smarius * Failure to get the mask is ignored; a full mask is used 272194138Smarius * then. We barf on bad mask sizes, however. 273186128Snwhitehorn */ 274194138Smarius if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 275186128Snwhitehorn panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 276186128Snwhitehorn "property!"); 277186128Snwhitehorn } 278186128Snwhitehorn} 279186128Snwhitehorn 280186128Snwhitehornint 281186128Snwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 282186128Snwhitehorn int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 283266020Sian phandle_t *iparent) 284186128Snwhitehorn{ 285266020Sian uint8_t maskbuf[regsz + pintrsz]; 286186128Snwhitehorn int rv; 287186128Snwhitehorn 288186128Snwhitehorn if (ii->opi_imapsz <= 0) 289186128Snwhitehorn return (0); 290186128Snwhitehorn KASSERT(regsz >= ii->opi_addrc, 291186128Snwhitehorn ("ofw_bus_lookup_imap: register size too small: %d < %d", 292186128Snwhitehorn regsz, ii->opi_addrc)); 293265954Sian if (node != -1) { 294265967Sian rv = OF_getencprop(node, "reg", reg, regsz); 295265954Sian if (rv < regsz) 296265954Sian panic("ofw_bus_lookup_imap: cannot get reg property"); 297265954Sian } 298186128Snwhitehorn return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 299186128Snwhitehorn ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 300209298Snwhitehorn mintrsz, iparent)); 301186128Snwhitehorn} 302186128Snwhitehorn 303186128Snwhitehorn/* 304186128Snwhitehorn * Map an interrupt using the firmware reg, interrupt-map and 305186128Snwhitehorn * interrupt-map-mask properties. 306186128Snwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to 307194138Smarius * by intr. The regs property of the node for which the mapping is done must 308186128Snwhitehorn * be passed as regs. This property is an array of register specifications; 309186128Snwhitehorn * the size of the address part of such a specification must be passed as 310194138Smarius * physsz. Only the first element of the property is used. 311186128Snwhitehorn * imap and imapsz hold the interrupt mask and it's size. 312186128Snwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have 313186128Snwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is 314186128Snwhitehorn * assumed. 315186128Snwhitehorn * maskbuf must point to a buffer of length physsz + intrsz. 316186128Snwhitehorn * The interrupt is returned in result, which must point to a buffer of length 317186128Snwhitehorn * rintrsz (which gives the expected size of the mapped interrupt). 318266020Sian * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. 319186128Snwhitehorn */ 320186128Snwhitehornint 321186128Snwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 322186128Snwhitehorn void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 323209298Snwhitehorn int rintrsz, phandle_t *iparent) 324186128Snwhitehorn{ 325186128Snwhitehorn phandle_t parent; 326194138Smarius uint8_t *ref = maskbuf; 327194138Smarius uint8_t *uiintr = intr; 328194138Smarius uint8_t *uiregs = regs; 329194138Smarius uint8_t *uiimapmsk = imapmsk; 330194138Smarius uint8_t *mptr; 331186128Snwhitehorn pcell_t pintrsz; 332186128Snwhitehorn int i, rsz, tsz; 333186128Snwhitehorn 334186128Snwhitehorn rsz = -1; 335186128Snwhitehorn if (imapmsk != NULL) { 336186128Snwhitehorn for (i = 0; i < physsz; i++) 337186128Snwhitehorn ref[i] = uiregs[i] & uiimapmsk[i]; 338186128Snwhitehorn for (i = 0; i < intrsz; i++) 339186128Snwhitehorn ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 340186128Snwhitehorn } else { 341186128Snwhitehorn bcopy(regs, ref, physsz); 342186128Snwhitehorn bcopy(intr, ref + physsz, intrsz); 343186128Snwhitehorn } 344186128Snwhitehorn 345186128Snwhitehorn mptr = imap; 346186128Snwhitehorn i = imapsz; 347186128Snwhitehorn while (i > 0) { 348186128Snwhitehorn bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 349273652Sian if (OF_searchencprop(OF_node_from_xref(parent), 350265967Sian "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) 351186128Snwhitehorn pintrsz = 1; /* default */ 352186128Snwhitehorn pintrsz *= sizeof(pcell_t); 353186728Snwhitehorn 354194138Smarius /* Compute the map stride size. */ 355186728Snwhitehorn tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz; 356186728Snwhitehorn KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 357194138Smarius 358186128Snwhitehorn if (bcmp(ref, mptr, physsz + intrsz) == 0) { 359186128Snwhitehorn bcopy(mptr + physsz + intrsz + sizeof(parent), 360266020Sian result, MIN(rintrsz, pintrsz)); 361209298Snwhitehorn 362209298Snwhitehorn if (iparent != NULL) 363209298Snwhitehorn *iparent = parent; 364266020Sian return (pintrsz/sizeof(pcell_t)); 365186128Snwhitehorn } 366186128Snwhitehorn mptr += tsz; 367186128Snwhitehorn i -= tsz; 368186128Snwhitehorn } 369186128Snwhitehorn return (0); 370186128Snwhitehorn} 371265954Sian 372273675Sianint 373283334Sianofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells, 374283334Sian struct resource_list *rl) 375283334Sian{ 376283334Sian uint64_t phys, size; 377283334Sian ssize_t i, j, rid, nreg, ret; 378283334Sian uint32_t *reg; 379283334Sian char *name; 380283334Sian 381283334Sian /* 382283334Sian * This may be just redundant when having ofw_bus_devinfo 383283334Sian * but makes this routine independent of it. 384283334Sian */ 385283334Sian ret = OF_getencprop_alloc(node, "name", sizeof(*name), (void **)&name); 386283334Sian if (ret == -1) 387283334Sian name = NULL; 388283334Sian 389283334Sian ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); 390283334Sian nreg = (ret == -1) ? 0 : ret; 391283334Sian 392283334Sian if (nreg % (acells + scells) != 0) { 393283334Sian if (bootverbose) 394283334Sian device_printf(dev, "Malformed reg property on <%s>\n", 395283334Sian (name == NULL) ? "unknown" : name); 396283334Sian nreg = 0; 397283334Sian } 398283334Sian 399283334Sian for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) { 400283334Sian phys = size = 0; 401283334Sian for (j = 0; j < acells; j++) { 402283334Sian phys <<= 32; 403283334Sian phys |= reg[i + j]; 404283334Sian } 405283334Sian for (j = 0; j < scells; j++) { 406283334Sian size <<= 32; 407283334Sian size |= reg[i + acells + j]; 408283334Sian } 409283334Sian /* Skip the dummy reg property of glue devices like ssm(4). */ 410283334Sian if (size != 0) 411283334Sian resource_list_add(rl, SYS_RES_MEMORY, rid, 412283334Sian phys, phys + size - 1, size); 413283334Sian } 414283334Sian free(name, M_OFWPROP); 415283334Sian free(reg, M_OFWPROP); 416283334Sian 417283334Sian return (0); 418283334Sian} 419283334Sian 420283334Sianint 421273675Sianofw_bus_intr_to_rl(device_t dev, phandle_t node, struct resource_list *rl) 422273675Sian{ 423273675Sian phandle_t iparent; 424273675Sian uint32_t icells, *intr; 425273675Sian int err, i, irqnum, nintr, rid; 426273675Sian boolean_t extended; 427273675Sian 428273675Sian nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), 429273675Sian (void **)&intr); 430273675Sian if (nintr > 0) { 431273675Sian if (OF_searchencprop(node, "interrupt-parent", &iparent, 432273675Sian sizeof(iparent)) == -1) { 433273675Sian device_printf(dev, "No interrupt-parent found, " 434273675Sian "assuming direct parent\n"); 435273675Sian iparent = OF_parent(node); 436273675Sian } 437273675Sian if (OF_searchencprop(OF_node_from_xref(iparent), 438273675Sian "#interrupt-cells", &icells, sizeof(icells)) == -1) { 439273675Sian device_printf(dev, "Missing #interrupt-cells " 440273675Sian "property, assuming <1>\n"); 441273675Sian icells = 1; 442273675Sian } 443273675Sian if (icells < 1 || icells > nintr) { 444273675Sian device_printf(dev, "Invalid #interrupt-cells property " 445273675Sian "value <%d>, assuming <1>\n", icells); 446273675Sian icells = 1; 447273675Sian } 448273675Sian extended = false; 449273675Sian } else { 450273675Sian nintr = OF_getencprop_alloc(node, "interrupts-extended", 451273675Sian sizeof(*intr), (void **)&intr); 452273675Sian if (nintr <= 0) 453273675Sian return (0); 454273675Sian extended = true; 455273675Sian } 456273675Sian err = 0; 457273675Sian rid = 0; 458273675Sian for (i = 0; i < nintr; i += icells) { 459273675Sian if (extended) { 460273675Sian iparent = intr[i++]; 461273675Sian if (OF_searchencprop(OF_node_from_xref(iparent), 462273675Sian "#interrupt-cells", &icells, sizeof(icells)) == -1) { 463273675Sian device_printf(dev, "Missing #interrupt-cells " 464273675Sian "property\n"); 465273675Sian err = ENOENT; 466273675Sian break; 467273675Sian } 468273675Sian if (icells < 1 || (i + icells) > nintr) { 469273675Sian device_printf(dev, "Invalid #interrupt-cells " 470273675Sian "property value <%d>\n", icells); 471273675Sian err = ERANGE; 472273675Sian break; 473273675Sian } 474273675Sian } 475273675Sian irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]); 476273675Sian resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1); 477273675Sian } 478273675Sian free(intr, M_OFWPROP); 479273675Sian return (err); 480273675Sian} 481