nexus.c revision 130068
186227Stmm/* 286227Stmm * Copyright 1998 Massachusetts Institute of Technology 3128776Stmm * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. 4128776Stmm * All rights reserved. 586227Stmm * 686227Stmm * Permission to use, copy, modify, and distribute this software and 786227Stmm * its documentation for any purpose and without fee is hereby 886227Stmm * granted, provided that both the above copyright notice and this 986227Stmm * permission notice appear in all copies, that both the above 1086227Stmm * copyright notice and this permission notice appear in all 1186227Stmm * supporting documentation, and that the name of M.I.T. not be used 1286227Stmm * in advertising or publicity pertaining to distribution of the 1386227Stmm * software without specific, written prior permission. M.I.T. makes 1486227Stmm * no representations about the suitability of this software for any 1586227Stmm * purpose. It is provided "as is" without express or implied 1686227Stmm * warranty. 17128776Stmm * 1886227Stmm * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1986227Stmm * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 2086227Stmm * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2186227Stmm * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2286227Stmm * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2386227Stmm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2486227Stmm * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2586227Stmm * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2686227Stmm * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2786227Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2886227Stmm * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2986227Stmm * SUCH DAMAGE. 3086227Stmm * 3186227Stmm * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09 3286227Stmm * 3386227Stmm * $FreeBSD: head/sys/sparc64/sparc64/nexus.c 130068 2004-06-04 11:52:25Z phk $ 3486227Stmm */ 3586227Stmm 3686227Stmm#include <sys/param.h> 3786227Stmm#include <sys/systm.h> 3886227Stmm#include <sys/bus.h> 3986227Stmm#include <sys/cons.h> 4086227Stmm#include <sys/kernel.h> 4186227Stmm#include <sys/malloc.h> 42130068Sphk#include <sys/module.h> 4386227Stmm 4486227Stmm#include <dev/ofw/openfirm.h> 4586227Stmm 4686227Stmm#include <machine/bus.h> 4786227Stmm#include <machine/frame.h> 4886227Stmm#include <machine/intr_machdep.h> 4986227Stmm#include <machine/nexusvar.h> 5088823Stmm#include <machine/ofw_upa.h> 5186227Stmm#include <machine/resource.h> 5288823Stmm#include <machine/upa.h> 5386227Stmm 5486227Stmm#include <sys/rman.h> 5586227Stmm 5686227Stmm/* 5786227Stmm * The nexus (which is a pseudo-bus actually) iterates over the nodes that 58128776Stmm * hang from the OpenFirmware root node and adds them as devices to this bus 5986227Stmm * (except some special nodes which are excluded) so that drivers can be 60128776Stmm * attached to them. 6186227Stmm * 6286227Stmm * Additionally, interrupt setup/teardown and some resource management are 6386227Stmm * done at this level. 6486227Stmm * 6586227Stmm * Maybe this code should get into dev/ofw to some extent, as some of it should 6686227Stmm * work for all OpenFirmware based machines... 6786227Stmm */ 6886227Stmm 6986227Stmmstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information"); 7086227Stmm 7186227Stmmstruct nexus_devinfo { 7286227Stmm phandle_t ndi_node; 7386227Stmm /* Some common properties. */ 7486227Stmm char *ndi_name; 7586227Stmm char *ndi_device_type; 7686227Stmm char *ndi_model; 7788823Stmm struct upa_regs *ndi_reg; 7886227Stmm int ndi_nreg; 7986227Stmm u_int *ndi_interrupts; 8086227Stmm int ndi_ninterrupts; 8186227Stmm}; 8286227Stmm 8388823Stmmstruct nexus_softc { 8488823Stmm struct rman sc_intr_rman; 8588823Stmm struct rman sc_mem_rman; 8688823Stmm}; 8788823Stmm 88128776Stmmstatic device_probe_t nexus_probe; 89128776Stmmstatic device_attach_t nexus_attach; 90128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch; 91128776Stmmstatic bus_read_ivar_t nexus_read_ivar; 92128776Stmmstatic bus_setup_intr_t nexus_setup_intr; 93128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr; 94128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource; 95128776Stmmstatic bus_activate_resource_t nexus_activate_resource; 96128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource; 97128776Stmmstatic bus_release_resource_t nexus_release_resource; 9886227Stmm 9986227Stmmstatic device_method_t nexus_methods[] = { 10086227Stmm /* Device interface */ 10186227Stmm DEVMETHOD(device_probe, nexus_probe), 102128776Stmm DEVMETHOD(device_attach, nexus_attach), 10386227Stmm DEVMETHOD(device_detach, bus_generic_detach), 10486227Stmm DEVMETHOD(device_shutdown, bus_generic_shutdown), 10586227Stmm DEVMETHOD(device_suspend, bus_generic_suspend), 10686227Stmm DEVMETHOD(device_resume, bus_generic_resume), 10786227Stmm 108128776Stmm /* Bus interface. */ 10986227Stmm DEVMETHOD(bus_print_child, bus_generic_print_child), 11086227Stmm DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 11186227Stmm DEVMETHOD(bus_read_ivar, nexus_read_ivar), 11286227Stmm DEVMETHOD(bus_setup_intr, nexus_setup_intr), 11386227Stmm DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 11486227Stmm DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 11586227Stmm DEVMETHOD(bus_activate_resource, nexus_activate_resource), 11686227Stmm DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 11786227Stmm DEVMETHOD(bus_release_resource, nexus_release_resource), 11886227Stmm 11986227Stmm { 0, 0 } 12086227Stmm}; 12186227Stmm 12286227Stmmstatic driver_t nexus_driver = { 12386227Stmm "nexus", 12486227Stmm nexus_methods, 12588823Stmm sizeof(struct nexus_softc), 12686227Stmm}; 12786227Stmm 12886227Stmmstatic devclass_t nexus_devclass; 12986227Stmm 13086227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 13186227Stmm 13286227Stmmstatic char *nexus_excl_name[] = { 13390622Stmm "aliases", 13490622Stmm "chosen", 13590622Stmm "counter-timer", /* No separate device; handled by psycho/sbus */ 13686227Stmm "memory", 13790622Stmm "openprom", 13886227Stmm "options", 13986227Stmm "packages", 14090622Stmm "virtual-memory", 14186227Stmm NULL 14286227Stmm}; 14386227Stmm 14486227Stmmstatic char *nexus_excl_type[] = { 14586227Stmm "cpu", 14686227Stmm NULL 14786227Stmm}; 14886227Stmm 14986227Stmmextern struct bus_space_tag nexus_bustag; 15086227Stmmextern struct bus_dma_tag nexus_dmatag; 15186227Stmm 15286227Stmmstatic int 15386227Stmmnexus_inlist(char *name, char *list[]) 15486227Stmm{ 15586227Stmm int i; 15686227Stmm 15786227Stmm for (i = 0; list[i] != NULL; i++) 15886227Stmm if (strcmp(name, list[i]) == 0) 15986227Stmm return (1); 16086227Stmm return (0); 16186227Stmm} 16286227Stmm 16386227Stmm#define NEXUS_EXCLUDED(name, type) \ 16486227Stmm (nexus_inlist((name), nexus_excl_name) || \ 16586227Stmm ((type) != NULL && nexus_inlist((type), nexus_excl_type))) 16686227Stmm 16786227Stmmstatic int 16886227Stmmnexus_probe(device_t dev) 16986227Stmm{ 170128776Stmm 171128776Stmm /* Nexus does always match. */ 172128776Stmm device_set_desc(dev, "OpenFirmware Nexus device"); 173128776Stmm return (0); 174128776Stmm} 175128776Stmm 176128776Stmmstatic int 177128776Stmmnexus_attach(device_t dev) 178128776Stmm{ 17986227Stmm phandle_t root; 18086227Stmm phandle_t child; 18186227Stmm device_t cdev; 18286227Stmm struct nexus_devinfo *dinfo; 18388823Stmm struct nexus_softc *sc; 18486227Stmm char *name, *type; 18586227Stmm 18686227Stmm if ((root = OF_peer(0)) == -1) 18786227Stmm panic("nexus_probe: OF_peer failed."); 18886227Stmm 18988823Stmm sc = device_get_softc(dev); 19088823Stmm sc->sc_intr_rman.rm_type = RMAN_ARRAY; 19188823Stmm sc->sc_intr_rman.rm_descr = "Interrupts"; 19288823Stmm sc->sc_mem_rman.rm_type = RMAN_ARRAY; 19388823Stmm sc->sc_mem_rman.rm_descr = "UPA Device Memory"; 19488823Stmm if (rman_init(&sc->sc_intr_rman) != 0 || 19588823Stmm rman_init(&sc->sc_mem_rman) != 0 || 19697265Sjake rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 || 19788823Stmm rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0) 198128776Stmm panic("nexus_attach(): failed to set up rmans"); 19986227Stmm for (child = OF_child(root); child != 0; child = OF_peer(child)) { 20086227Stmm if (child == -1) 201128776Stmm panic("nexus_attach(): OF_child() failed."); 20286227Stmm if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1) 20386227Stmm continue; 20486227Stmm OF_getprop_alloc(child, "device_type", 1, (void **)&type); 20586227Stmm if (NEXUS_EXCLUDED(name, type)) { 20686227Stmm free(name, M_OFWPROP); 207128776Stmm free(type, M_OFWPROP); 20886227Stmm continue; 20988823Stmm } 21086227Stmm cdev = device_add_child(dev, NULL, -1); 211128776Stmm if (cdev == NULL) 212128776Stmm panic("nexus_attach(): device_add_child() failed."); 213128776Stmm dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 214128776Stmm dinfo->ndi_node = child; 215128776Stmm dinfo->ndi_name = name; 216128776Stmm dinfo->ndi_device_type = type; 217128776Stmm OF_getprop_alloc(child, "model", 1, 218128776Stmm (void **)&dinfo->ndi_model); 219128776Stmm dinfo->ndi_nreg = OF_getprop_alloc(child, "reg", 220128776Stmm sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg); 221128776Stmm dinfo->ndi_ninterrupts = OF_getprop_alloc(child, 222128776Stmm "interrupts", sizeof(*dinfo->ndi_interrupts), 223128776Stmm (void **)&dinfo->ndi_interrupts); 224128776Stmm device_set_ivars(cdev, dinfo); 22586227Stmm } 226128776Stmm return (bus_generic_attach(dev)); 22786227Stmm} 22886227Stmm 22986227Stmmstatic void 23086227Stmmnexus_probe_nomatch(device_t dev, device_t child) 23186227Stmm{ 23286227Stmm char *type; 23386227Stmm 234128939Smarius if ((type = nexus_get_device_type(child)) == NULL) 23586227Stmm type = "(unknown)"; 23686227Stmm device_printf(dev, "<%s>, type %s (no driver attached)\n", 237128939Smarius nexus_get_name(child), type); 23886227Stmm} 23986227Stmm 24086227Stmmstatic int 24186227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 24286227Stmm{ 24386227Stmm struct nexus_devinfo *dinfo; 24486227Stmm 24586227Stmm if ((dinfo = device_get_ivars(child)) == 0) 24686227Stmm return (ENOENT); 24786227Stmm switch (which) { 24886227Stmm case NEXUS_IVAR_NODE: 24986227Stmm *result = dinfo->ndi_node; 25086227Stmm break; 25186227Stmm case NEXUS_IVAR_NAME: 25286227Stmm *result = (uintptr_t)dinfo->ndi_name; 25386227Stmm break; 25486227Stmm case NEXUS_IVAR_DEVICE_TYPE: 25586227Stmm *result = (uintptr_t)dinfo->ndi_device_type; 25686227Stmm break; 25786227Stmm case NEXUS_IVAR_MODEL: 25886227Stmm *result = (uintptr_t)dinfo->ndi_model; 25986227Stmm break; 26086227Stmm case NEXUS_IVAR_REG: 26186227Stmm *result = (uintptr_t)dinfo->ndi_reg; 26286227Stmm break; 26386227Stmm case NEXUS_IVAR_NREG: 26486227Stmm *result = dinfo->ndi_nreg; 26586227Stmm break; 26686227Stmm case NEXUS_IVAR_INTERRUPTS: 26786227Stmm *result = (uintptr_t)dinfo->ndi_interrupts; 26886227Stmm break; 26986227Stmm case NEXUS_IVAR_NINTERRUPTS: 27086227Stmm *result = dinfo->ndi_ninterrupts; 27186227Stmm break; 27286227Stmm case NEXUS_IVAR_DMATAG: 273128776Stmm *result = (uintptr_t)&nexus_dmatag; 27486227Stmm break; 27586227Stmm default: 27686227Stmm return (ENOENT); 27786227Stmm } 27886227Stmm return 0; 27986227Stmm} 28086227Stmm 28186227Stmmstatic int 28286227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 28386227Stmm driver_intr_t *intr, void *arg, void **cookiep) 28486227Stmm{ 28586227Stmm int error; 28688823Stmm 28786227Stmm if (res == NULL) 28886227Stmm panic("nexus_setup_intr: NULL interrupt resource!"); 28986227Stmm 29086227Stmm if ((res->r_flags & RF_SHAREABLE) == 0) 29186227Stmm flags |= INTR_EXCL; 29286227Stmm 293128776Stmm /* We depend here on rman_activate_resource() being idempotent. */ 29486227Stmm error = rman_activate_resource(res); 29586227Stmm if (error) 29686227Stmm return (error); 29786227Stmm 29886227Stmm error = inthand_add(device_get_nameunit(child), res->r_start, 29986227Stmm intr, arg, flags, cookiep); 30086227Stmm 30186227Stmm return (error); 30286227Stmm} 30386227Stmm 30486227Stmmstatic int 30586227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 30686227Stmm{ 30786227Stmm inthand_remove(r->r_start, ih); 30886227Stmm return (0); 30986227Stmm} 31086227Stmm 31186227Stmmstatic struct resource * 31286227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 31386227Stmm u_long start, u_long end, u_long count, u_int flags) 31486227Stmm{ 31588823Stmm struct nexus_softc *sc = device_get_softc(bus); 31686227Stmm struct resource *rv; 31786227Stmm struct rman *rm; 31886227Stmm int needactivate = flags & RF_ACTIVE; 31986227Stmm 32086227Stmm flags &= ~RF_ACTIVE; 32186227Stmm 32286227Stmm switch (type) { 32386227Stmm case SYS_RES_IRQ: 32488823Stmm rm = &sc->sc_intr_rman; 32586227Stmm break; 32688823Stmm case SYS_RES_MEMORY: 32788823Stmm rm = &sc->sc_mem_rman; 32888823Stmm break; 32986227Stmm default: 33086227Stmm return (NULL); 33186227Stmm } 33286227Stmm 33386227Stmm rv = rman_reserve_resource(rm, start, end, count, flags, child); 33486227Stmm if (rv == NULL) 33586227Stmm return (NULL); 33688823Stmm if (type == SYS_RES_MEMORY) { 33788823Stmm rman_set_bustag(rv, &nexus_bustag); 33888823Stmm rman_set_bushandle(rv, rman_get_start(rv)); 33988823Stmm } 34086227Stmm 34186227Stmm if (needactivate) { 342128776Stmm if (bus_activate_resource(child, type, *rid, rv) != 0) { 34386227Stmm rman_release_resource(rv); 34486227Stmm return (NULL); 34586227Stmm } 34686227Stmm } 34788823Stmm 34886227Stmm return (rv); 34986227Stmm} 35086227Stmm 35186227Stmmstatic int 35286227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid, 35386227Stmm struct resource *r) 35486227Stmm{ 35586227Stmm 35686227Stmm /* Not much to be done yet... */ 35786227Stmm return (rman_activate_resource(r)); 35886227Stmm} 35986227Stmm 36086227Stmmstatic int 36186227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 36286227Stmm struct resource *r) 36386227Stmm{ 36488823Stmm 36586227Stmm /* Not much to be done yet... */ 36686227Stmm return (rman_deactivate_resource(r)); 36786227Stmm} 36886227Stmm 36986227Stmmstatic int 37086227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid, 37186227Stmm struct resource *r) 37286227Stmm{ 37386227Stmm int error; 37486227Stmm 37586227Stmm if (rman_get_flags(r) & RF_ACTIVE) { 37686227Stmm error = bus_deactivate_resource(child, type, rid, r); 37786227Stmm if (error) 378128776Stmm return (error); 37986227Stmm } 38086227Stmm return (rman_release_resource(r)); 38186227Stmm} 382