nexus.c revision 146474
1/*- 2 * Copyright 1998 Massachusetts Institute of Technology 3 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. 4 * All rights reserved. 5 * 6 * Permission to use, copy, modify, and distribute this software and 7 * its documentation for any purpose and without fee is hereby 8 * granted, provided that both the above copyright notice and this 9 * permission notice appear in all copies, that both the above 10 * copyright notice and this permission notice appear in all 11 * supporting documentation, and that the name of M.I.T. not be used 12 * in advertising or publicity pertaining to distribution of the 13 * software without specific, written prior permission. M.I.T. makes 14 * no representations about the suitability of this software for any 15 * purpose. It is provided "as is" without express or implied 16 * warranty. 17 * 18 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 19 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 22 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 25 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/nexus.c 146474 2005-05-21 20:19:27Z marius $"); 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/bus.h> 40#include <sys/cons.h> 41#include <sys/kernel.h> 42#include <sys/malloc.h> 43#include <sys/module.h> 44 45#include <dev/ofw/openfirm.h> 46 47#include <machine/bus.h> 48#include <machine/frame.h> 49#include <machine/intr_machdep.h> 50#include <machine/nexusvar.h> 51#include <machine/ofw_upa.h> 52#include <machine/resource.h> 53#include <machine/upa.h> 54 55#include <sys/rman.h> 56 57/* 58 * The nexus (which is a pseudo-bus actually) iterates over the nodes that 59 * hang from the Open Firmware root node and adds them as devices to this bus 60 * (except some special nodes which are excluded) so that drivers can be 61 * attached to them. 62 * 63 * Additionally, interrupt setup/teardown and some resource management are 64 * done at this level. 65 * 66 * Maybe this code should get into dev/ofw to some extent, as some of it should 67 * work for all Open Firmware based machines... 68 */ 69 70static MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information"); 71 72struct nexus_devinfo { 73 phandle_t ndi_node; 74 /* Some common properties. */ 75 char *ndi_name; 76 char *ndi_device_type; 77 char *ndi_model; 78 struct upa_regs *ndi_reg; 79 int ndi_nreg; 80 u_int *ndi_interrupts; 81 int ndi_ninterrupts; 82}; 83 84struct nexus_softc { 85 struct rman sc_intr_rman; 86 struct rman sc_mem_rman; 87}; 88 89static device_probe_t nexus_probe; 90static device_attach_t nexus_attach; 91static bus_add_child_t nexus_add_child; 92static bus_probe_nomatch_t nexus_probe_nomatch; 93static bus_read_ivar_t nexus_read_ivar; 94static bus_setup_intr_t nexus_setup_intr; 95static bus_teardown_intr_t nexus_teardown_intr; 96static bus_alloc_resource_t nexus_alloc_resource; 97static bus_activate_resource_t nexus_activate_resource; 98static bus_deactivate_resource_t nexus_deactivate_resource; 99static bus_release_resource_t nexus_release_resource; 100 101static device_method_t nexus_methods[] = { 102 /* Device interface */ 103 DEVMETHOD(device_probe, nexus_probe), 104 DEVMETHOD(device_attach, nexus_attach), 105 DEVMETHOD(device_detach, bus_generic_detach), 106 DEVMETHOD(device_shutdown, bus_generic_shutdown), 107 DEVMETHOD(device_suspend, bus_generic_suspend), 108 DEVMETHOD(device_resume, bus_generic_resume), 109 110 /* Bus interface. */ 111 DEVMETHOD(bus_add_child, nexus_add_child), 112 DEVMETHOD(bus_print_child, bus_generic_print_child), 113 DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 114 DEVMETHOD(bus_read_ivar, nexus_read_ivar), 115 DEVMETHOD(bus_setup_intr, nexus_setup_intr), 116 DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 117 DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 118 DEVMETHOD(bus_activate_resource, nexus_activate_resource), 119 DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 120 DEVMETHOD(bus_release_resource, nexus_release_resource), 121 122 { 0, 0 } 123}; 124 125static driver_t nexus_driver = { 126 "nexus", 127 nexus_methods, 128 sizeof(struct nexus_softc), 129}; 130 131static devclass_t nexus_devclass; 132 133DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 134 135static char *nexus_excl_name[] = { 136 "aliases", 137 "chosen", 138 "counter-timer", /* No separate device; handled by psycho/sbus */ 139 "memory", 140 "openprom", 141 "options", 142 "packages", 143 "virtual-memory", 144 NULL 145}; 146 147static char *nexus_excl_type[] = { 148 "cpu", 149 NULL 150}; 151 152extern struct bus_space_tag nexus_bustag; 153extern struct bus_dma_tag nexus_dmatag; 154 155static int 156nexus_inlist(char *name, char *list[]) 157{ 158 int i; 159 160 for (i = 0; list[i] != NULL; i++) 161 if (strcmp(name, list[i]) == 0) 162 return (1); 163 return (0); 164} 165 166#define NEXUS_EXCLUDED(name, type) \ 167 (nexus_inlist((name), nexus_excl_name) || \ 168 ((type) != NULL && nexus_inlist((type), nexus_excl_type))) 169 170static int 171nexus_probe(device_t dev) 172{ 173 174 /* Nexus does always match. */ 175 device_set_desc(dev, "Open Firmware Nexus device"); 176 return (0); 177} 178 179static int 180nexus_attach(device_t dev) 181{ 182 phandle_t root; 183 phandle_t child; 184 device_t cdev; 185 struct nexus_devinfo *dinfo; 186 struct nexus_softc *sc; 187 char *name, *type; 188 189 if ((root = OF_peer(0)) == -1) 190 panic("nexus_probe: OF_peer failed."); 191 192 sc = device_get_softc(dev); 193 sc->sc_intr_rman.rm_type = RMAN_ARRAY; 194 sc->sc_intr_rman.rm_descr = "Interrupts"; 195 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 196 sc->sc_mem_rman.rm_descr = "UPA Device Memory"; 197 if (rman_init(&sc->sc_intr_rman) != 0 || 198 rman_init(&sc->sc_mem_rman) != 0 || 199 rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 || 200 rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0) 201 panic("nexus_attach(): failed to set up rmans"); 202 203 /* 204 * Allow devices to identify. 205 */ 206 bus_generic_probe(dev); 207 208 /* 209 * Now walk the OFW tree and attach top-level devices. 210 */ 211 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 212 if (child == -1) 213 panic("nexus_attach(): OF_child() failed."); 214 if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1) 215 continue; 216 OF_getprop_alloc(child, "device_type", 1, (void **)&type); 217 if (NEXUS_EXCLUDED(name, type)) { 218 free(name, M_OFWPROP); 219 free(type, M_OFWPROP); 220 continue; 221 } 222 cdev = device_add_child(dev, NULL, -1); 223 if (cdev == NULL) 224 panic("nexus_attach(): device_add_child() failed."); 225 dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 226 dinfo->ndi_node = child; 227 dinfo->ndi_name = name; 228 dinfo->ndi_device_type = type; 229 OF_getprop_alloc(child, "model", 1, 230 (void **)&dinfo->ndi_model); 231 dinfo->ndi_nreg = OF_getprop_alloc(child, "reg", 232 sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg); 233 dinfo->ndi_ninterrupts = OF_getprop_alloc(child, 234 "interrupts", sizeof(*dinfo->ndi_interrupts), 235 (void **)&dinfo->ndi_interrupts); 236 device_set_ivars(cdev, dinfo); 237 } 238 return (bus_generic_attach(dev)); 239} 240 241static device_t 242nexus_add_child(device_t dev, int order, const char *name, int unit) 243{ 244 device_t cdev; 245 struct nexus_devinfo *dinfo; 246 247 cdev = device_add_child_ordered(dev, order, name, unit); 248 if (cdev == NULL) 249 return (NULL); 250 251 dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_NOWAIT | M_ZERO); 252 if (dinfo == NULL) 253 return (NULL); 254 255 dinfo->ndi_node = -1; 256 dinfo->ndi_name = strdup(name, M_OFWPROP); 257 device_set_ivars(cdev, dinfo); 258 259 return (cdev); 260} 261 262static void 263nexus_probe_nomatch(device_t dev, device_t child) 264{ 265 char *type; 266 267 if ((type = nexus_get_device_type(child)) == NULL) 268 type = "(unknown)"; 269 device_printf(dev, "<%s>, type %s (no driver attached)\n", 270 nexus_get_name(child), type); 271} 272 273static int 274nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 275{ 276 struct nexus_devinfo *dinfo; 277 278 if ((dinfo = device_get_ivars(child)) == 0) 279 return (ENOENT); 280 switch (which) { 281 case NEXUS_IVAR_NODE: 282 *result = dinfo->ndi_node; 283 break; 284 case NEXUS_IVAR_NAME: 285 *result = (uintptr_t)dinfo->ndi_name; 286 break; 287 case NEXUS_IVAR_DEVICE_TYPE: 288 *result = (uintptr_t)dinfo->ndi_device_type; 289 break; 290 case NEXUS_IVAR_MODEL: 291 *result = (uintptr_t)dinfo->ndi_model; 292 break; 293 case NEXUS_IVAR_REG: 294 *result = (uintptr_t)dinfo->ndi_reg; 295 break; 296 case NEXUS_IVAR_NREG: 297 *result = dinfo->ndi_nreg; 298 break; 299 case NEXUS_IVAR_INTERRUPTS: 300 *result = (uintptr_t)dinfo->ndi_interrupts; 301 break; 302 case NEXUS_IVAR_NINTERRUPTS: 303 *result = dinfo->ndi_ninterrupts; 304 break; 305 case NEXUS_IVAR_DMATAG: 306 *result = (uintptr_t)&nexus_dmatag; 307 break; 308 default: 309 return (ENOENT); 310 } 311 return 0; 312} 313 314static int 315nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 316 driver_intr_t *intr, void *arg, void **cookiep) 317{ 318 int error; 319 320 if (res == NULL) 321 panic("nexus_setup_intr: NULL interrupt resource!"); 322 323 if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 324 flags |= INTR_EXCL; 325 326 /* We depend here on rman_activate_resource() being idempotent. */ 327 error = rman_activate_resource(res); 328 if (error) 329 return (error); 330 331 error = inthand_add(device_get_nameunit(child), rman_get_start(res), 332 intr, arg, flags, cookiep); 333 334 return (error); 335} 336 337static int 338nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 339{ 340 341 inthand_remove(rman_get_start(r), ih); 342 return (0); 343} 344 345static struct resource * 346nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 347 u_long start, u_long end, u_long count, u_int flags) 348{ 349 struct nexus_softc *sc = device_get_softc(bus); 350 struct resource *rv; 351 struct rman *rm; 352 int needactivate = flags & RF_ACTIVE; 353 354 flags &= ~RF_ACTIVE; 355 356 switch (type) { 357 case SYS_RES_IRQ: 358 rm = &sc->sc_intr_rman; 359 break; 360 case SYS_RES_MEMORY: 361 rm = &sc->sc_mem_rman; 362 break; 363 default: 364 return (NULL); 365 } 366 367 rv = rman_reserve_resource(rm, start, end, count, flags, child); 368 if (rv == NULL) 369 return (NULL); 370 if (type == SYS_RES_MEMORY) { 371 rman_set_bustag(rv, &nexus_bustag); 372 rman_set_bushandle(rv, rman_get_start(rv)); 373 } 374 375 if (needactivate) { 376 if (bus_activate_resource(child, type, *rid, rv) != 0) { 377 rman_release_resource(rv); 378 return (NULL); 379 } 380 } 381 382 return (rv); 383} 384 385static int 386nexus_activate_resource(device_t bus, device_t child, int type, int rid, 387 struct resource *r) 388{ 389 390 /* Not much to be done yet... */ 391 return (rman_activate_resource(r)); 392} 393 394static int 395nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 396 struct resource *r) 397{ 398 399 /* Not much to be done yet... */ 400 return (rman_deactivate_resource(r)); 401} 402 403static int 404nexus_release_resource(device_t bus, device_t child, int type, int rid, 405 struct resource *r) 406{ 407 int error; 408 409 if (rman_get_flags(r) & RF_ACTIVE) { 410 error = bus_deactivate_resource(child, type, rid, r); 411 if (error) 412 return (error); 413 } 414 return (rman_release_resource(r)); 415} 416