nexus.c revision 133862
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 * $FreeBSD: head/sys/sparc64/sparc64/nexus.c 133862 2004-08-16 15:45:27Z marius $ 34 */ 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/cons.h> 40#include <sys/kernel.h> 41#include <sys/malloc.h> 42#include <sys/module.h> 43 44#include <dev/ofw/openfirm.h> 45 46#include <machine/bus.h> 47#include <machine/frame.h> 48#include <machine/intr_machdep.h> 49#include <machine/nexusvar.h> 50#include <machine/ofw_upa.h> 51#include <machine/resource.h> 52#include <machine/upa.h> 53 54#include <sys/rman.h> 55 56/* 57 * The nexus (which is a pseudo-bus actually) iterates over the nodes that 58 * hang from the Open Firmware root node and adds them as devices to this bus 59 * (except some special nodes which are excluded) so that drivers can be 60 * attached to them. 61 * 62 * Additionally, interrupt setup/teardown and some resource management are 63 * done at this level. 64 * 65 * Maybe this code should get into dev/ofw to some extent, as some of it should 66 * work for all Open Firmware based machines... 67 */ 68 69static MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information"); 70 71struct nexus_devinfo { 72 phandle_t ndi_node; 73 /* Some common properties. */ 74 char *ndi_name; 75 char *ndi_device_type; 76 char *ndi_model; 77 struct upa_regs *ndi_reg; 78 int ndi_nreg; 79 u_int *ndi_interrupts; 80 int ndi_ninterrupts; 81}; 82 83struct nexus_softc { 84 struct rman sc_intr_rman; 85 struct rman sc_mem_rman; 86}; 87 88static device_probe_t nexus_probe; 89static device_attach_t nexus_attach; 90static bus_probe_nomatch_t nexus_probe_nomatch; 91static bus_read_ivar_t nexus_read_ivar; 92static bus_setup_intr_t nexus_setup_intr; 93static bus_teardown_intr_t nexus_teardown_intr; 94static bus_alloc_resource_t nexus_alloc_resource; 95static bus_activate_resource_t nexus_activate_resource; 96static bus_deactivate_resource_t nexus_deactivate_resource; 97static bus_release_resource_t nexus_release_resource; 98 99static device_method_t nexus_methods[] = { 100 /* Device interface */ 101 DEVMETHOD(device_probe, nexus_probe), 102 DEVMETHOD(device_attach, nexus_attach), 103 DEVMETHOD(device_detach, bus_generic_detach), 104 DEVMETHOD(device_shutdown, bus_generic_shutdown), 105 DEVMETHOD(device_suspend, bus_generic_suspend), 106 DEVMETHOD(device_resume, bus_generic_resume), 107 108 /* Bus interface. */ 109 DEVMETHOD(bus_print_child, bus_generic_print_child), 110 DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 111 DEVMETHOD(bus_read_ivar, nexus_read_ivar), 112 DEVMETHOD(bus_setup_intr, nexus_setup_intr), 113 DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 114 DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 115 DEVMETHOD(bus_activate_resource, nexus_activate_resource), 116 DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 117 DEVMETHOD(bus_release_resource, nexus_release_resource), 118 119 { 0, 0 } 120}; 121 122static driver_t nexus_driver = { 123 "nexus", 124 nexus_methods, 125 sizeof(struct nexus_softc), 126}; 127 128static devclass_t nexus_devclass; 129 130DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 131 132static char *nexus_excl_name[] = { 133 "aliases", 134 "chosen", 135 "counter-timer", /* No separate device; handled by psycho/sbus */ 136 "memory", 137 "openprom", 138 "options", 139 "packages", 140 "virtual-memory", 141 NULL 142}; 143 144static char *nexus_excl_type[] = { 145 "cpu", 146 NULL 147}; 148 149extern struct bus_space_tag nexus_bustag; 150extern struct bus_dma_tag nexus_dmatag; 151 152static int 153nexus_inlist(char *name, char *list[]) 154{ 155 int i; 156 157 for (i = 0; list[i] != NULL; i++) 158 if (strcmp(name, list[i]) == 0) 159 return (1); 160 return (0); 161} 162 163#define NEXUS_EXCLUDED(name, type) \ 164 (nexus_inlist((name), nexus_excl_name) || \ 165 ((type) != NULL && nexus_inlist((type), nexus_excl_type))) 166 167static int 168nexus_probe(device_t dev) 169{ 170 171 /* Nexus does always match. */ 172 device_set_desc(dev, "Open Firmware Nexus device"); 173 return (0); 174} 175 176static int 177nexus_attach(device_t dev) 178{ 179 phandle_t root; 180 phandle_t child; 181 device_t cdev; 182 struct nexus_devinfo *dinfo; 183 struct nexus_softc *sc; 184 char *name, *type; 185 186 if ((root = OF_peer(0)) == -1) 187 panic("nexus_probe: OF_peer failed."); 188 189 sc = device_get_softc(dev); 190 sc->sc_intr_rman.rm_type = RMAN_ARRAY; 191 sc->sc_intr_rman.rm_descr = "Interrupts"; 192 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 193 sc->sc_mem_rman.rm_descr = "UPA Device Memory"; 194 if (rman_init(&sc->sc_intr_rman) != 0 || 195 rman_init(&sc->sc_mem_rman) != 0 || 196 rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 || 197 rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0) 198 panic("nexus_attach(): failed to set up rmans"); 199 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 200 if (child == -1) 201 panic("nexus_attach(): OF_child() failed."); 202 if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1) 203 continue; 204 OF_getprop_alloc(child, "device_type", 1, (void **)&type); 205 if (NEXUS_EXCLUDED(name, type)) { 206 free(name, M_OFWPROP); 207 free(type, M_OFWPROP); 208 continue; 209 } 210 cdev = device_add_child(dev, NULL, -1); 211 if (cdev == NULL) 212 panic("nexus_attach(): device_add_child() failed."); 213 dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 214 dinfo->ndi_node = child; 215 dinfo->ndi_name = name; 216 dinfo->ndi_device_type = type; 217 OF_getprop_alloc(child, "model", 1, 218 (void **)&dinfo->ndi_model); 219 dinfo->ndi_nreg = OF_getprop_alloc(child, "reg", 220 sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg); 221 dinfo->ndi_ninterrupts = OF_getprop_alloc(child, 222 "interrupts", sizeof(*dinfo->ndi_interrupts), 223 (void **)&dinfo->ndi_interrupts); 224 device_set_ivars(cdev, dinfo); 225 } 226 return (bus_generic_attach(dev)); 227} 228 229static void 230nexus_probe_nomatch(device_t dev, device_t child) 231{ 232 char *type; 233 234 if ((type = nexus_get_device_type(child)) == NULL) 235 type = "(unknown)"; 236 device_printf(dev, "<%s>, type %s (no driver attached)\n", 237 nexus_get_name(child), type); 238} 239 240static int 241nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 242{ 243 struct nexus_devinfo *dinfo; 244 245 if ((dinfo = device_get_ivars(child)) == 0) 246 return (ENOENT); 247 switch (which) { 248 case NEXUS_IVAR_NODE: 249 *result = dinfo->ndi_node; 250 break; 251 case NEXUS_IVAR_NAME: 252 *result = (uintptr_t)dinfo->ndi_name; 253 break; 254 case NEXUS_IVAR_DEVICE_TYPE: 255 *result = (uintptr_t)dinfo->ndi_device_type; 256 break; 257 case NEXUS_IVAR_MODEL: 258 *result = (uintptr_t)dinfo->ndi_model; 259 break; 260 case NEXUS_IVAR_REG: 261 *result = (uintptr_t)dinfo->ndi_reg; 262 break; 263 case NEXUS_IVAR_NREG: 264 *result = dinfo->ndi_nreg; 265 break; 266 case NEXUS_IVAR_INTERRUPTS: 267 *result = (uintptr_t)dinfo->ndi_interrupts; 268 break; 269 case NEXUS_IVAR_NINTERRUPTS: 270 *result = dinfo->ndi_ninterrupts; 271 break; 272 case NEXUS_IVAR_DMATAG: 273 *result = (uintptr_t)&nexus_dmatag; 274 break; 275 default: 276 return (ENOENT); 277 } 278 return 0; 279} 280 281static int 282nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 283 driver_intr_t *intr, void *arg, void **cookiep) 284{ 285 int error; 286 287 if (res == NULL) 288 panic("nexus_setup_intr: NULL interrupt resource!"); 289 290 if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 291 flags |= INTR_EXCL; 292 293 /* We depend here on rman_activate_resource() being idempotent. */ 294 error = rman_activate_resource(res); 295 if (error) 296 return (error); 297 298 error = inthand_add(device_get_nameunit(child), rman_get_start(res), 299 intr, arg, flags, cookiep); 300 301 return (error); 302} 303 304static int 305nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 306{ 307 inthand_remove(rman_get_start(r), ih); 308 return (0); 309} 310 311static struct resource * 312nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 313 u_long start, u_long end, u_long count, u_int flags) 314{ 315 struct nexus_softc *sc = device_get_softc(bus); 316 struct resource *rv; 317 struct rman *rm; 318 int needactivate = flags & RF_ACTIVE; 319 320 flags &= ~RF_ACTIVE; 321 322 switch (type) { 323 case SYS_RES_IRQ: 324 rm = &sc->sc_intr_rman; 325 break; 326 case SYS_RES_MEMORY: 327 rm = &sc->sc_mem_rman; 328 break; 329 default: 330 return (NULL); 331 } 332 333 rv = rman_reserve_resource(rm, start, end, count, flags, child); 334 if (rv == NULL) 335 return (NULL); 336 if (type == SYS_RES_MEMORY) { 337 rman_set_bustag(rv, &nexus_bustag); 338 rman_set_bushandle(rv, rman_get_start(rv)); 339 } 340 341 if (needactivate) { 342 if (bus_activate_resource(child, type, *rid, rv) != 0) { 343 rman_release_resource(rv); 344 return (NULL); 345 } 346 } 347 348 return (rv); 349} 350 351static int 352nexus_activate_resource(device_t bus, device_t child, int type, int rid, 353 struct resource *r) 354{ 355 356 /* Not much to be done yet... */ 357 return (rman_activate_resource(r)); 358} 359 360static int 361nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 362 struct resource *r) 363{ 364 365 /* Not much to be done yet... */ 366 return (rman_deactivate_resource(r)); 367} 368 369static int 370nexus_release_resource(device_t bus, device_t child, int type, int rid, 371 struct resource *r) 372{ 373 int error; 374 375 if (rman_get_flags(r) & RF_ACTIVE) { 376 error = bus_deactivate_resource(child, type, rid, r); 377 if (error) 378 return (error); 379 } 380 return (rman_release_resource(r)); 381} 382