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); 58266128Sian OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status); 59152683Smarius obd->obd_node = node; 60152683Smarius return (0); 61152683Smarius} 62152683Smarius 63152683Smariusvoid 64152683Smariusofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) 65152683Smarius{ 66152683Smarius 67152683Smarius if (obd == NULL) 68152683Smarius return; 69152683Smarius if (obd->obd_compat != NULL) 70152683Smarius free(obd->obd_compat, M_OFWPROP); 71152683Smarius if (obd->obd_model != NULL) 72152683Smarius free(obd->obd_model, M_OFWPROP); 73152683Smarius if (obd->obd_name != NULL) 74152683Smarius free(obd->obd_name, M_OFWPROP); 75152683Smarius if (obd->obd_type != NULL) 76152683Smarius free(obd->obd_type, M_OFWPROP); 77266128Sian if (obd->obd_status != NULL) 78266128Sian free(obd->obd_status, M_OFWPROP); 79152683Smarius} 80152683Smarius 81194138Smariusint 82186128Snwhitehornofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, 83186128Snwhitehorn size_t buflen) 84186128Snwhitehorn{ 85194138Smarius 86186128Snwhitehorn if (ofw_bus_get_name(child) != NULL) { 87186128Snwhitehorn strlcat(buf, "name=", buflen); 88186128Snwhitehorn strlcat(buf, ofw_bus_get_name(child), buflen); 89186128Snwhitehorn } 90152683Smarius 91186128Snwhitehorn if (ofw_bus_get_compat(child) != NULL) { 92186128Snwhitehorn strlcat(buf, " compat=", buflen); 93186128Snwhitehorn strlcat(buf, ofw_bus_get_compat(child), buflen); 94186128Snwhitehorn } 95186128Snwhitehorn return (0); 96186128Snwhitehorn}; 97186128Snwhitehorn 98152683Smariusconst char * 99152683Smariusofw_bus_gen_get_compat(device_t bus, device_t dev) 100152683Smarius{ 101152683Smarius const struct ofw_bus_devinfo *obd; 102194138Smarius 103194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 104152683Smarius if (obd == NULL) 105152683Smarius return (NULL); 106152683Smarius return (obd->obd_compat); 107152683Smarius} 108194138Smarius 109152683Smariusconst char * 110152683Smariusofw_bus_gen_get_model(device_t bus, device_t dev) 111152683Smarius{ 112152683Smarius const struct ofw_bus_devinfo *obd; 113152683Smarius 114194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 115152683Smarius if (obd == NULL) 116152683Smarius return (NULL); 117152683Smarius return (obd->obd_model); 118152683Smarius} 119152683Smarius 120152683Smariusconst char * 121152683Smariusofw_bus_gen_get_name(device_t bus, device_t dev) 122152683Smarius{ 123152683Smarius const struct ofw_bus_devinfo *obd; 124152683Smarius 125194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 126152683Smarius if (obd == NULL) 127152683Smarius return (NULL); 128152683Smarius return (obd->obd_name); 129152683Smarius} 130152683Smarius 131152683Smariusphandle_t 132152683Smariusofw_bus_gen_get_node(device_t bus, device_t dev) 133152683Smarius{ 134152683Smarius const struct ofw_bus_devinfo *obd; 135152683Smarius 136194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 137152683Smarius if (obd == NULL) 138152683Smarius return (0); 139152683Smarius return (obd->obd_node); 140152683Smarius} 141152683Smarius 142152683Smariusconst char * 143152683Smariusofw_bus_gen_get_type(device_t bus, device_t dev) 144152683Smarius{ 145152683Smarius const struct ofw_bus_devinfo *obd; 146152683Smarius 147194138Smarius obd = OFW_BUS_GET_DEVINFO(bus, dev); 148152683Smarius if (obd == NULL) 149152683Smarius return (NULL); 150152683Smarius return (obd->obd_type); 151152683Smarius} 152186128Snwhitehorn 153266128Sianconst char * 154266128Sianofw_bus_get_status(device_t dev) 155266128Sian{ 156266128Sian const struct ofw_bus_devinfo *obd; 157266128Sian 158266128Sian obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); 159266128Sian if (obd == NULL) 160266128Sian return (NULL); 161266128Sian 162266128Sian return (obd->obd_status); 163266128Sian} 164266128Sian 165208614Srajint 166266128Sianofw_bus_status_okay(device_t dev) 167266128Sian{ 168266128Sian const char *status; 169266128Sian 170266128Sian status = ofw_bus_get_status(dev); 171266128Sian if (status == NULL || strcmp(status, "okay") == 0) 172266128Sian return (1); 173266128Sian 174266128Sian return (0); 175266128Sian} 176266128Sian 177266128Sianint 178208614Srajofw_bus_is_compatible(device_t dev, const char *onecompat) 179208614Sraj{ 180208614Sraj phandle_t node; 181208614Sraj const char *compat; 182208614Sraj int len, onelen, l; 183208614Sraj 184208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 185208614Sraj return (0); 186208614Sraj 187233018Snwhitehorn if ((node = ofw_bus_get_node(dev)) == -1) 188208614Sraj return (0); 189208614Sraj 190208614Sraj /* Get total 'compatible' prop len */ 191208614Sraj if ((len = OF_getproplen(node, "compatible")) <= 0) 192208614Sraj return (0); 193208614Sraj 194208614Sraj onelen = strlen(onecompat); 195208614Sraj 196208614Sraj while (len > 0) { 197239366Shrs if (strlen(compat) == onelen && 198239366Shrs strncasecmp(compat, onecompat, onelen) == 0) 199208614Sraj /* Found it. */ 200208614Sraj return (1); 201208614Sraj 202208614Sraj /* Slide to the next sub-string. */ 203208614Sraj l = strlen(compat) + 1; 204208614Sraj compat += l; 205208614Sraj len -= l; 206208614Sraj } 207208614Sraj return (0); 208208614Sraj} 209208614Sraj 210208614Srajint 211208614Srajofw_bus_is_compatible_strict(device_t dev, const char *compatible) 212208614Sraj{ 213208614Sraj const char *compat; 214239366Shrs size_t len; 215208614Sraj 216208614Sraj if ((compat = ofw_bus_get_compat(dev)) == NULL) 217208614Sraj return (0); 218208614Sraj 219239366Shrs len = strlen(compatible); 220239366Shrs if (strlen(compat) == len && 221239366Shrs strncasecmp(compat, compatible, len) == 0) 222208614Sraj return (1); 223208614Sraj 224208614Sraj return (0); 225208614Sraj} 226208614Sraj 227259316Sianconst struct ofw_compat_data * 228259316Sianofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) 229259316Sian{ 230259316Sian 231259316Sian if (compat == NULL) 232259316Sian return NULL; 233259316Sian 234259316Sian for (; compat->ocd_str != NULL; ++compat) { 235259316Sian if (ofw_bus_is_compatible(dev, compat->ocd_str)) 236259316Sian break; 237259316Sian } 238259316Sian 239259316Sian return (compat); 240259316Sian} 241259316Sian 242239366Shrsint 243239366Shrsofw_bus_has_prop(device_t dev, const char *propname) 244239366Shrs{ 245239366Shrs phandle_t node; 246239366Shrs 247239366Shrs if ((node = ofw_bus_get_node(dev)) == -1) 248239366Shrs return (0); 249239366Shrs 250239366Shrs return (OF_hasprop(node, propname)); 251239366Shrs} 252239366Shrs 253186128Snwhitehornvoid 254186128Snwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) 255186128Snwhitehorn{ 256186128Snwhitehorn pcell_t addrc; 257186128Snwhitehorn int msksz; 258186128Snwhitehorn 259265967Sian if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) 260186128Snwhitehorn addrc = 2; 261186128Snwhitehorn ii->opi_addrc = addrc * sizeof(pcell_t); 262186128Snwhitehorn 263265967Sian ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1, 264186128Snwhitehorn (void **)&ii->opi_imap); 265186128Snwhitehorn if (ii->opi_imapsz > 0) { 266265967Sian msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1, 267186128Snwhitehorn (void **)&ii->opi_imapmsk); 268186128Snwhitehorn /* 269194138Smarius * Failure to get the mask is ignored; a full mask is used 270194138Smarius * then. We barf on bad mask sizes, however. 271186128Snwhitehorn */ 272194138Smarius if (msksz != -1 && msksz != ii->opi_addrc + intrsz) 273186128Snwhitehorn panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " 274186128Snwhitehorn "property!"); 275186128Snwhitehorn } 276186128Snwhitehorn} 277186128Snwhitehorn 278186128Snwhitehornint 279186128Snwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, 280186128Snwhitehorn int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, 281266020Sian phandle_t *iparent) 282186128Snwhitehorn{ 283266020Sian uint8_t maskbuf[regsz + pintrsz]; 284186128Snwhitehorn int rv; 285186128Snwhitehorn 286186128Snwhitehorn if (ii->opi_imapsz <= 0) 287186128Snwhitehorn return (0); 288186128Snwhitehorn KASSERT(regsz >= ii->opi_addrc, 289186128Snwhitehorn ("ofw_bus_lookup_imap: register size too small: %d < %d", 290186128Snwhitehorn regsz, ii->opi_addrc)); 291265954Sian if (node != -1) { 292265967Sian rv = OF_getencprop(node, "reg", reg, regsz); 293265954Sian if (rv < regsz) 294265954Sian panic("ofw_bus_lookup_imap: cannot get reg property"); 295265954Sian } 296186128Snwhitehorn return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, 297186128Snwhitehorn ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, 298209298Snwhitehorn mintrsz, iparent)); 299186128Snwhitehorn} 300186128Snwhitehorn 301186128Snwhitehorn/* 302186128Snwhitehorn * Map an interrupt using the firmware reg, interrupt-map and 303186128Snwhitehorn * interrupt-map-mask properties. 304186128Snwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to 305194138Smarius * by intr. The regs property of the node for which the mapping is done must 306186128Snwhitehorn * be passed as regs. This property is an array of register specifications; 307186128Snwhitehorn * the size of the address part of such a specification must be passed as 308194138Smarius * physsz. Only the first element of the property is used. 309186128Snwhitehorn * imap and imapsz hold the interrupt mask and it's size. 310186128Snwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have 311186128Snwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is 312186128Snwhitehorn * assumed. 313186128Snwhitehorn * maskbuf must point to a buffer of length physsz + intrsz. 314186128Snwhitehorn * The interrupt is returned in result, which must point to a buffer of length 315186128Snwhitehorn * rintrsz (which gives the expected size of the mapped interrupt). 316266020Sian * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. 317186128Snwhitehorn */ 318186128Snwhitehornint 319186128Snwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, 320186128Snwhitehorn void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, 321209298Snwhitehorn int rintrsz, phandle_t *iparent) 322186128Snwhitehorn{ 323186128Snwhitehorn phandle_t parent; 324194138Smarius uint8_t *ref = maskbuf; 325194138Smarius uint8_t *uiintr = intr; 326194138Smarius uint8_t *uiregs = regs; 327194138Smarius uint8_t *uiimapmsk = imapmsk; 328194138Smarius uint8_t *mptr; 329186128Snwhitehorn pcell_t pintrsz; 330186128Snwhitehorn int i, rsz, tsz; 331186128Snwhitehorn 332186128Snwhitehorn rsz = -1; 333186128Snwhitehorn if (imapmsk != NULL) { 334186128Snwhitehorn for (i = 0; i < physsz; i++) 335186128Snwhitehorn ref[i] = uiregs[i] & uiimapmsk[i]; 336186128Snwhitehorn for (i = 0; i < intrsz; i++) 337186128Snwhitehorn ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; 338186128Snwhitehorn } else { 339186128Snwhitehorn bcopy(regs, ref, physsz); 340186128Snwhitehorn bcopy(intr, ref + physsz, intrsz); 341186128Snwhitehorn } 342186128Snwhitehorn 343186128Snwhitehorn mptr = imap; 344186128Snwhitehorn i = imapsz; 345186128Snwhitehorn while (i > 0) { 346186128Snwhitehorn bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); 347265967Sian if (OF_searchencprop(OF_xref_phandle(parent), 348265967Sian "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) 349186128Snwhitehorn pintrsz = 1; /* default */ 350186128Snwhitehorn pintrsz *= sizeof(pcell_t); 351186728Snwhitehorn 352194138Smarius /* Compute the map stride size. */ 353186728Snwhitehorn tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz; 354186728Snwhitehorn KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); 355194138Smarius 356186128Snwhitehorn if (bcmp(ref, mptr, physsz + intrsz) == 0) { 357186128Snwhitehorn bcopy(mptr + physsz + intrsz + sizeof(parent), 358266020Sian result, MIN(rintrsz, pintrsz)); 359209298Snwhitehorn 360209298Snwhitehorn if (iparent != NULL) 361209298Snwhitehorn *iparent = parent; 362266020Sian return (pintrsz/sizeof(pcell_t)); 363186128Snwhitehorn } 364186128Snwhitehorn mptr += tsz; 365186128Snwhitehorn i -= tsz; 366186128Snwhitehorn } 367186128Snwhitehorn return (0); 368186128Snwhitehorn} 369265954Sian 370