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$"); 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) { 170239366Shrs if (strlen(compat) == onelen && 171239366Shrs strncasecmp(compat, onecompat, onelen) == 0) 172208614Sraj /* Found it. */ 173208614Sraj return (1); 174208614Sraj 175208614Sraj /* Slide to the next sub-string. */ 176208614Sraj l = strlen(compat) + 1; 177208614Sraj compat += l; 178208614Sraj len -= l; 179208614Sraj } 180208614Sraj return (0); 181208614Sraj} 182208614Sraj 183208614Srajint 184208614Srajofw_bus_is_compatible_strict(device_t dev, const char *compatible) 185208614Sraj{ 186208614Sraj const char *compat; 187239366Shrs size_t len; 188208614Sraj 189208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 190208614Sraj return (0); 191208614Sraj 192239366Shrs len = strlen(compatible); 193239366Shrs if (strlen(compat) == len && 194239366Shrs strncasecmp(compat, compatible, len) == 0) 195208614Sraj return (1); 196208614Sraj 197208614Sraj return (0); 198208614Sraj} 199208614Sraj 200239366Shrsint 201239366Shrsofw_bus_has_prop(device_t dev, const char *propname) 202239366Shrs{ 203239366Shrs phandle_t node; 204239366Shrs 205239366Shrs if ((node = ofw_bus_get_node(dev)) == -1) 206239366Shrs return (0); 207239366Shrs 208239366Shrs return (OF_hasprop(node, propname)); 209239366Shrs} 210239366Shrs 211208614Sraj#ifndef FDT 212186128Snwhitehornvoid 213186128Snwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 214186128Snwhitehorn{ 215186128Snwhitehorn pcell_t addrc; 216186128Snwhitehorn int msksz; 217186128Snwhitehorn 218186128Snwhitehorn if (OF_getprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 219186128Snwhitehorn addrc = 2; 220186128Snwhitehorn ii->opi_addrc = addrc * sizeof(pcell_t); 221186128Snwhitehorn 222186128Snwhitehorn ii->opi_imapsz = OF_getprop_alloc(node, "interrupt-map", 1, 223186128Snwhitehorn (void **)&ii->opi_imap); 224186128Snwhitehorn if (ii->opi_imapsz > 0) { 225186128Snwhitehorn msksz = OF_getprop_alloc(node, "interrupt-map-mask", 1, 226186128Snwhitehorn (void **)&ii->opi_imapmsk); 227186128Snwhitehorn /* 228194138Smarius * Failure to get the mask is ignored; a full mask is used 229194138Smarius * then. We barf on bad mask sizes, however. 230186128Snwhitehorn */ 231194138Smarius if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 232186128Snwhitehorn panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 233186128Snwhitehorn "property!"); 234186128Snwhitehorn } 235186128Snwhitehorn} 236186128Snwhitehorn 237186128Snwhitehornint 238186128Snwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 239186128Snwhitehorn int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 240209298Snwhitehorn phandle_t *iparent, void *maskbuf) 241186128Snwhitehorn{ 242186128Snwhitehorn int rv; 243186128Snwhitehorn 244186128Snwhitehorn if (ii->opi_imapsz <= 0) 245186128Snwhitehorn return (0); 246186128Snwhitehorn KASSERT(regsz >= ii->opi_addrc, 247186128Snwhitehorn ("ofw_bus_lookup_imap: register size too small: %d < %d", 248186128Snwhitehorn regsz, ii->opi_addrc)); 249186128Snwhitehorn rv = OF_getprop(node, "reg", reg, regsz); 250186128Snwhitehorn if (rv < regsz) 251186128Snwhitehorn panic("ofw_bus_lookup_imap: could not get reg property"); 252186128Snwhitehorn return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 253186128Snwhitehorn ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 254209298Snwhitehorn mintrsz, iparent)); 255186128Snwhitehorn} 256186128Snwhitehorn 257186128Snwhitehorn/* 258186128Snwhitehorn * Map an interrupt using the firmware reg, interrupt-map and 259186128Snwhitehorn * interrupt-map-mask properties. 260186128Snwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to 261194138Smarius * by intr. The regs property of the node for which the mapping is done must 262186128Snwhitehorn * be passed as regs. This property is an array of register specifications; 263186128Snwhitehorn * the size of the address part of such a specification must be passed as 264194138Smarius * physsz. Only the first element of the property is used. 265186128Snwhitehorn * imap and imapsz hold the interrupt mask and it's size. 266186128Snwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have 267186128Snwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is 268186128Snwhitehorn * assumed. 269186128Snwhitehorn * maskbuf must point to a buffer of length physsz + intrsz. 270186128Snwhitehorn * The interrupt is returned in result, which must point to a buffer of length 271186128Snwhitehorn * rintrsz (which gives the expected size of the mapped interrupt). 272186128Snwhitehorn * Returns 1 if a mapping was found, 0 otherwise. 273186128Snwhitehorn */ 274186128Snwhitehornint 275186128Snwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 276186128Snwhitehorn void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 277209298Snwhitehorn int rintrsz, phandle_t *iparent) 278186128Snwhitehorn{ 279186128Snwhitehorn phandle_t parent; 280194138Smarius uint8_t *ref = maskbuf; 281194138Smarius uint8_t *uiintr = intr; 282194138Smarius uint8_t *uiregs = regs; 283194138Smarius uint8_t *uiimapmsk = imapmsk; 284194138Smarius uint8_t *mptr; 285186128Snwhitehorn pcell_t pintrsz; 286186128Snwhitehorn int i, rsz, tsz; 287186128Snwhitehorn 288186128Snwhitehorn rsz = -1; 289186128Snwhitehorn if (imapmsk != NULL) { 290186128Snwhitehorn for (i = 0; i < physsz; i++) 291186128Snwhitehorn ref[i] = uiregs[i] & uiimapmsk[i]; 292186128Snwhitehorn for (i = 0; i < intrsz; i++) 293186128Snwhitehorn ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 294186128Snwhitehorn } else { 295186128Snwhitehorn bcopy(regs, ref, physsz); 296186128Snwhitehorn bcopy(intr, ref + physsz, intrsz); 297186128Snwhitehorn } 298186128Snwhitehorn 299186128Snwhitehorn mptr = imap; 300186128Snwhitehorn i = imapsz; 301186128Snwhitehorn while (i > 0) { 302186128Snwhitehorn bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 303255596Snwhitehorn if (OF_searchprop(OF_xref_phandle(parent), "#interrupt-cells", 304186128Snwhitehorn &pintrsz, sizeof(pintrsz)) == -1) 305186128Snwhitehorn pintrsz = 1; /* default */ 306186128Snwhitehorn pintrsz *= sizeof(pcell_t); 307186728Snwhitehorn 308194138Smarius /* Compute the map stride size. */ 309186728Snwhitehorn tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz; 310186728Snwhitehorn KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 311194138Smarius 312186128Snwhitehorn /* 313186128Snwhitehorn * XXX: Apple hardware uses a second cell to set information 314194138Smarius * on the interrupt trigger type. This information should 315186128Snwhitehorn * be used somewhere to program the PIC. 316186128Snwhitehorn */ 317186128Snwhitehorn 318186128Snwhitehorn if (bcmp(ref, mptr, physsz + intrsz) == 0) { 319186128Snwhitehorn bcopy(mptr + physsz + intrsz + sizeof(parent), 320186128Snwhitehorn result, rintrsz); 321209298Snwhitehorn 322209298Snwhitehorn if (iparent != NULL) 323209298Snwhitehorn *iparent = parent; 324186128Snwhitehorn return (1); 325186128Snwhitehorn } 326186128Snwhitehorn mptr += tsz; 327186128Snwhitehorn i -= tsz; 328186128Snwhitehorn } 329186128Snwhitehorn return (0); 330186128Snwhitehorn} 331208614Sraj#endif /* !FDT */ 332