ofw_bus_subr.c revision 233018
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: head/sys/dev/ofw/ofw_bus_subr.c 233018 2012-03-15 22:53:39Z nwhitehorn $"); 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 40186128Snwhitehorn#include <dev/ofw/ofw_bus.h> 41152683Smarius#include <dev/ofw/ofw_bus_subr.h> 42152683Smarius#include <dev/ofw/openfirm.h> 43152683Smarius 44152683Smarius#include "ofw_bus_if.h" 45152683Smarius 46152683Smariusint 47152683Smariusofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) 48152683Smarius{ 49152683Smarius 50152683Smarius if (obd == NULL) 51152683Smarius return (ENOMEM); 52152683Smarius /* The 'name' property is considered mandatory. */ 53152683Smarius if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) 54152683Smarius return (EINVAL); 55152683Smarius OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); 56152683Smarius OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); 57152683Smarius OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); 58152683Smarius obd->obd_node = node; 59152683Smarius return (0); 60152683Smarius} 61152683Smarius 62152683Smariusvoid 63152683Smariusofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 64152683Smarius{ 65152683Smarius 66152683Smarius if (obd == NULL) 67152683Smarius return; 68152683Smarius if (obd->obd_compat != NULL) 69152683Smarius free(obd->obd_compat, M_OFWPROP); 70152683Smarius if (obd->obd_model != NULL) 71152683Smarius free(obd->obd_model, M_OFWPROP); 72152683Smarius if (obd->obd_name != NULL) 73152683Smarius free(obd->obd_name, M_OFWPROP); 74152683Smarius if (obd->obd_type != NULL) 75152683Smarius free(obd->obd_type, M_OFWPROP); 76152683Smarius} 77152683Smarius 78194138Smariusint 79186128Snwhitehornofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 80186128Snwhitehorn size_t buflen) 81186128Snwhitehorn{ 82194138Smarius 83186128Snwhitehorn if (ofw_bus_get_name(child) != NULL) { 84186128Snwhitehorn strlcat(buf, "name=", buflen); 85186128Snwhitehorn strlcat(buf, ofw_bus_get_name(child), buflen); 86186128Snwhitehorn } 87152683Smarius 88186128Snwhitehorn if (ofw_bus_get_compat(child) != NULL) { 89186128Snwhitehorn strlcat(buf, " compat=", buflen); 90186128Snwhitehorn strlcat(buf, ofw_bus_get_compat(child), buflen); 91186128Snwhitehorn } 92186128Snwhitehorn return (0); 93186128Snwhitehorn}; 94186128Snwhitehorn 95152683Smariusconst char * 96152683Smariusofw_bus_gen_get_compat(device_t bus, device_t dev) 97152683Smarius{ 98152683Smarius const struct ofw_bus_devinfo *obd; 99194138Smarius 100194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 101152683Smarius if (obd == NULL) 102152683Smarius return (NULL); 103152683Smarius return (obd->obd_compat); 104152683Smarius} 105194138Smarius 106152683Smariusconst char * 107152683Smariusofw_bus_gen_get_model(device_t bus, device_t dev) 108152683Smarius{ 109152683Smarius const struct ofw_bus_devinfo *obd; 110152683Smarius 111194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 112152683Smarius if (obd == NULL) 113152683Smarius return (NULL); 114152683Smarius return (obd->obd_model); 115152683Smarius} 116152683Smarius 117152683Smariusconst char * 118152683Smariusofw_bus_gen_get_name(device_t bus, device_t dev) 119152683Smarius{ 120152683Smarius const struct ofw_bus_devinfo *obd; 121152683Smarius 122194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 123152683Smarius if (obd == NULL) 124152683Smarius return (NULL); 125152683Smarius return (obd->obd_name); 126152683Smarius} 127152683Smarius 128152683Smariusphandle_t 129152683Smariusofw_bus_gen_get_node(device_t bus, device_t dev) 130152683Smarius{ 131152683Smarius const struct ofw_bus_devinfo *obd; 132152683Smarius 133194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 134152683Smarius if (obd == NULL) 135152683Smarius return (0); 136152683Smarius return (obd->obd_node); 137152683Smarius} 138152683Smarius 139152683Smariusconst char * 140152683Smariusofw_bus_gen_get_type(device_t bus, device_t dev) 141152683Smarius{ 142152683Smarius const struct ofw_bus_devinfo *obd; 143152683Smarius 144194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 145152683Smarius if (obd == NULL) 146152683Smarius return (NULL); 147152683Smarius return (obd->obd_type); 148152683Smarius} 149186128Snwhitehorn 150208614Srajint 151208614Srajofw_bus_is_compatible(device_t dev, const char *onecompat) 152208614Sraj{ 153208614Sraj phandle_t node; 154208614Sraj const char *compat; 155208614Sraj int len, onelen, l; 156208614Sraj 157208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 158208614Sraj return (0); 159208614Sraj 160233018Snwhitehorn if ((node = ofw_bus_get_node(dev)) == -1) 161208614Sraj return (0); 162208614Sraj 163208614Sraj /* Get total 'compatible' prop len */ 164208614Sraj if ((len = OF_getproplen(node, "compatible")) <= 0) 165208614Sraj return (0); 166208614Sraj 167208614Sraj onelen = strlen(onecompat); 168208614Sraj 169208614Sraj while (len > 0) { 170208614Sraj if (strncasecmp(compat, onecompat, onelen) == 0) 171208614Sraj /* Found it. */ 172208614Sraj return (1); 173208614Sraj 174208614Sraj /* Slide to the next sub-string. */ 175208614Sraj l = strlen(compat) + 1; 176208614Sraj compat += l; 177208614Sraj len -= l; 178208614Sraj } 179208614Sraj return (0); 180208614Sraj} 181208614Sraj 182208614Srajint 183208614Srajofw_bus_is_compatible_strict(device_t dev, const char *compatible) 184208614Sraj{ 185208614Sraj const char *compat; 186208614Sraj 187208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 188208614Sraj return (0); 189208614Sraj 190208614Sraj if (strncasecmp(compat, compatible, strlen(compatible)) == 0) 191208614Sraj return (1); 192208614Sraj 193208614Sraj return (0); 194208614Sraj} 195208614Sraj 196208614Sraj#ifndef FDT 197186128Snwhitehornvoid 198186128Snwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 199186128Snwhitehorn{ 200186128Snwhitehorn pcell_t addrc; 201186128Snwhitehorn int msksz; 202186128Snwhitehorn 203186128Snwhitehorn if (OF_getprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 204186128Snwhitehorn addrc = 2; 205186128Snwhitehorn ii->opi_addrc = addrc * sizeof(pcell_t); 206186128Snwhitehorn 207186128Snwhitehorn ii->opi_imapsz = OF_getprop_alloc(node, "interrupt-map", 1, 208186128Snwhitehorn (void **)&ii->opi_imap); 209186128Snwhitehorn if (ii->opi_imapsz > 0) { 210186128Snwhitehorn msksz = OF_getprop_alloc(node, "interrupt-map-mask", 1, 211186128Snwhitehorn (void **)&ii->opi_imapmsk); 212186128Snwhitehorn /* 213194138Smarius * Failure to get the mask is ignored; a full mask is used 214194138Smarius * then. We barf on bad mask sizes, however. 215186128Snwhitehorn */ 216194138Smarius if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 217186128Snwhitehorn panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 218186128Snwhitehorn "property!"); 219186128Snwhitehorn } 220186128Snwhitehorn} 221186128Snwhitehorn 222186128Snwhitehornint 223186128Snwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 224186128Snwhitehorn int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 225209298Snwhitehorn phandle_t *iparent, void *maskbuf) 226186128Snwhitehorn{ 227186128Snwhitehorn int rv; 228186128Snwhitehorn 229186128Snwhitehorn if (ii->opi_imapsz <= 0) 230186128Snwhitehorn return (0); 231186128Snwhitehorn KASSERT(regsz >= ii->opi_addrc, 232186128Snwhitehorn ("ofw_bus_lookup_imap: register size too small: %d < %d", 233186128Snwhitehorn regsz, ii->opi_addrc)); 234186128Snwhitehorn rv = OF_getprop(node, "reg", reg, regsz); 235186128Snwhitehorn if (rv < regsz) 236186128Snwhitehorn panic("ofw_bus_lookup_imap: could not get reg property"); 237186128Snwhitehorn return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 238186128Snwhitehorn ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 239209298Snwhitehorn mintrsz, iparent)); 240186128Snwhitehorn} 241186128Snwhitehorn 242186128Snwhitehorn/* 243186128Snwhitehorn * Map an interrupt using the firmware reg, interrupt-map and 244186128Snwhitehorn * interrupt-map-mask properties. 245186128Snwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to 246194138Smarius * by intr. The regs property of the node for which the mapping is done must 247186128Snwhitehorn * be passed as regs. This property is an array of register specifications; 248186128Snwhitehorn * the size of the address part of such a specification must be passed as 249194138Smarius * physsz. Only the first element of the property is used. 250186128Snwhitehorn * imap and imapsz hold the interrupt mask and it's size. 251186128Snwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have 252186128Snwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is 253186128Snwhitehorn * assumed. 254186128Snwhitehorn * maskbuf must point to a buffer of length physsz + intrsz. 255186128Snwhitehorn * The interrupt is returned in result, which must point to a buffer of length 256186128Snwhitehorn * rintrsz (which gives the expected size of the mapped interrupt). 257186128Snwhitehorn * Returns 1 if a mapping was found, 0 otherwise. 258186128Snwhitehorn */ 259186128Snwhitehornint 260186128Snwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 261186128Snwhitehorn void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 262209298Snwhitehorn int rintrsz, phandle_t *iparent) 263186128Snwhitehorn{ 264186128Snwhitehorn phandle_t parent; 265194138Smarius uint8_t *ref = maskbuf; 266194138Smarius uint8_t *uiintr = intr; 267194138Smarius uint8_t *uiregs = regs; 268194138Smarius uint8_t *uiimapmsk = imapmsk; 269194138Smarius uint8_t *mptr; 270186128Snwhitehorn pcell_t pintrsz; 271186128Snwhitehorn int i, rsz, tsz; 272186128Snwhitehorn 273186128Snwhitehorn rsz = -1; 274186128Snwhitehorn if (imapmsk != NULL) { 275186128Snwhitehorn for (i = 0; i < physsz; i++) 276186128Snwhitehorn ref[i] = uiregs[i] & uiimapmsk[i]; 277186128Snwhitehorn for (i = 0; i < intrsz; i++) 278186128Snwhitehorn ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 279186128Snwhitehorn } else { 280186128Snwhitehorn bcopy(regs, ref, physsz); 281186128Snwhitehorn bcopy(intr, ref + physsz, intrsz); 282186128Snwhitehorn } 283186128Snwhitehorn 284186128Snwhitehorn mptr = imap; 285186128Snwhitehorn i = imapsz; 286186128Snwhitehorn while (i > 0) { 287186128Snwhitehorn bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 288186728Snwhitehorn if (OF_searchprop(parent, "#interrupt-cells", 289186128Snwhitehorn &pintrsz, sizeof(pintrsz)) == -1) 290186128Snwhitehorn pintrsz = 1; /* default */ 291186128Snwhitehorn pintrsz *= sizeof(pcell_t); 292186728Snwhitehorn 293194138Smarius /* Compute the map stride size. */ 294186728Snwhitehorn tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz; 295186728Snwhitehorn KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 296194138Smarius 297186128Snwhitehorn /* 298186128Snwhitehorn * XXX: Apple hardware uses a second cell to set information 299194138Smarius * on the interrupt trigger type. This information should 300186128Snwhitehorn * be used somewhere to program the PIC. 301186128Snwhitehorn */ 302186128Snwhitehorn 303186128Snwhitehorn if (bcmp(ref, mptr, physsz + intrsz) == 0) { 304186128Snwhitehorn bcopy(mptr + physsz + intrsz + sizeof(parent), 305186128Snwhitehorn result, rintrsz); 306209298Snwhitehorn 307209298Snwhitehorn if (iparent != NULL) 308209298Snwhitehorn *iparent = parent; 309186128Snwhitehorn return (1); 310186128Snwhitehorn } 311186128Snwhitehorn mptr += tsz; 312186128Snwhitehorn i -= tsz; 313186128Snwhitehorn } 314186128Snwhitehorn return (0); 315186128Snwhitehorn} 316208614Sraj#endif /* !FDT */ 317