nexus.c revision 131535
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 131535 2004-07-03 20:48:01Z imp $ 3486227Stmm */ 3586227Stmm 36131376Smarius#define __RMAN_RESOURCE_VISIBLE 3786227Stmm#include <sys/param.h> 3886227Stmm#include <sys/systm.h> 3986227Stmm#include <sys/bus.h> 4086227Stmm#include <sys/cons.h> 4186227Stmm#include <sys/kernel.h> 4286227Stmm#include <sys/malloc.h> 43130068Sphk#include <sys/module.h> 4486227Stmm 4586227Stmm#include <dev/ofw/openfirm.h> 4686227Stmm 4786227Stmm#include <machine/bus.h> 4886227Stmm#include <machine/frame.h> 4986227Stmm#include <machine/intr_machdep.h> 5086227Stmm#include <machine/nexusvar.h> 5188823Stmm#include <machine/ofw_upa.h> 5286227Stmm#include <machine/resource.h> 5388823Stmm#include <machine/upa.h> 5486227Stmm 5586227Stmm#include <sys/rman.h> 5686227Stmm 5786227Stmm/* 5886227Stmm * The nexus (which is a pseudo-bus actually) iterates over the nodes that 59128776Stmm * hang from the OpenFirmware root node and adds them as devices to this bus 6086227Stmm * (except some special nodes which are excluded) so that drivers can be 61128776Stmm * attached to them. 6286227Stmm * 6386227Stmm * Additionally, interrupt setup/teardown and some resource management are 6486227Stmm * done at this level. 6586227Stmm * 6686227Stmm * Maybe this code should get into dev/ofw to some extent, as some of it should 6786227Stmm * work for all OpenFirmware based machines... 6886227Stmm */ 6986227Stmm 7086227Stmmstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information"); 7186227Stmm 7286227Stmmstruct nexus_devinfo { 7386227Stmm phandle_t ndi_node; 7486227Stmm /* Some common properties. */ 7586227Stmm char *ndi_name; 7686227Stmm char *ndi_device_type; 7786227Stmm char *ndi_model; 7888823Stmm struct upa_regs *ndi_reg; 7986227Stmm int ndi_nreg; 8086227Stmm u_int *ndi_interrupts; 8186227Stmm int ndi_ninterrupts; 8286227Stmm}; 8386227Stmm 8488823Stmmstruct nexus_softc { 8588823Stmm struct rman sc_intr_rman; 8688823Stmm struct rman sc_mem_rman; 8788823Stmm}; 8888823Stmm 89128776Stmmstatic device_probe_t nexus_probe; 90128776Stmmstatic device_attach_t nexus_attach; 91128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch; 92128776Stmmstatic bus_read_ivar_t nexus_read_ivar; 93128776Stmmstatic bus_setup_intr_t nexus_setup_intr; 94128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr; 95128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource; 96128776Stmmstatic bus_activate_resource_t nexus_activate_resource; 97128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource; 98128776Stmmstatic bus_release_resource_t nexus_release_resource; 9986227Stmm 10086227Stmmstatic device_method_t nexus_methods[] = { 10186227Stmm /* Device interface */ 10286227Stmm DEVMETHOD(device_probe, nexus_probe), 103128776Stmm DEVMETHOD(device_attach, nexus_attach), 10486227Stmm DEVMETHOD(device_detach, bus_generic_detach), 10586227Stmm DEVMETHOD(device_shutdown, bus_generic_shutdown), 10686227Stmm DEVMETHOD(device_suspend, bus_generic_suspend), 10786227Stmm DEVMETHOD(device_resume, bus_generic_resume), 10886227Stmm 109128776Stmm /* Bus interface. */ 11086227Stmm DEVMETHOD(bus_print_child, bus_generic_print_child), 11186227Stmm DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 11286227Stmm DEVMETHOD(bus_read_ivar, nexus_read_ivar), 11386227Stmm DEVMETHOD(bus_setup_intr, nexus_setup_intr), 11486227Stmm DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 11586227Stmm DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 11686227Stmm DEVMETHOD(bus_activate_resource, nexus_activate_resource), 11786227Stmm DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 11886227Stmm DEVMETHOD(bus_release_resource, nexus_release_resource), 11986227Stmm 12086227Stmm { 0, 0 } 12186227Stmm}; 12286227Stmm 12386227Stmmstatic driver_t nexus_driver = { 12486227Stmm "nexus", 12586227Stmm nexus_methods, 12688823Stmm sizeof(struct nexus_softc), 12786227Stmm}; 12886227Stmm 12986227Stmmstatic devclass_t nexus_devclass; 13086227Stmm 13186227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 13286227Stmm 13386227Stmmstatic char *nexus_excl_name[] = { 13490622Stmm "aliases", 13590622Stmm "chosen", 13690622Stmm "counter-timer", /* No separate device; handled by psycho/sbus */ 13786227Stmm "memory", 13890622Stmm "openprom", 13986227Stmm "options", 14086227Stmm "packages", 14190622Stmm "virtual-memory", 14286227Stmm NULL 14386227Stmm}; 14486227Stmm 14586227Stmmstatic char *nexus_excl_type[] = { 14686227Stmm "cpu", 14786227Stmm NULL 14886227Stmm}; 14986227Stmm 15086227Stmmextern struct bus_space_tag nexus_bustag; 15186227Stmmextern struct bus_dma_tag nexus_dmatag; 15286227Stmm 15386227Stmmstatic int 15486227Stmmnexus_inlist(char *name, char *list[]) 15586227Stmm{ 15686227Stmm int i; 15786227Stmm 15886227Stmm for (i = 0; list[i] != NULL; i++) 15986227Stmm if (strcmp(name, list[i]) == 0) 16086227Stmm return (1); 16186227Stmm return (0); 16286227Stmm} 16386227Stmm 16486227Stmm#define NEXUS_EXCLUDED(name, type) \ 16586227Stmm (nexus_inlist((name), nexus_excl_name) || \ 16686227Stmm ((type) != NULL && nexus_inlist((type), nexus_excl_type))) 16786227Stmm 16886227Stmmstatic int 16986227Stmmnexus_probe(device_t dev) 17086227Stmm{ 171128776Stmm 172128776Stmm /* Nexus does always match. */ 173128776Stmm device_set_desc(dev, "OpenFirmware Nexus device"); 174128776Stmm return (0); 175128776Stmm} 176128776Stmm 177128776Stmmstatic int 178128776Stmmnexus_attach(device_t dev) 179128776Stmm{ 18086227Stmm phandle_t root; 18186227Stmm phandle_t child; 18286227Stmm device_t cdev; 18386227Stmm struct nexus_devinfo *dinfo; 18488823Stmm struct nexus_softc *sc; 18586227Stmm char *name, *type; 18686227Stmm 18786227Stmm if ((root = OF_peer(0)) == -1) 18886227Stmm panic("nexus_probe: OF_peer failed."); 18986227Stmm 19088823Stmm sc = device_get_softc(dev); 19188823Stmm sc->sc_intr_rman.rm_type = RMAN_ARRAY; 19288823Stmm sc->sc_intr_rman.rm_descr = "Interrupts"; 19388823Stmm sc->sc_mem_rman.rm_type = RMAN_ARRAY; 19488823Stmm sc->sc_mem_rman.rm_descr = "UPA Device Memory"; 19588823Stmm if (rman_init(&sc->sc_intr_rman) != 0 || 19688823Stmm rman_init(&sc->sc_mem_rman) != 0 || 19797265Sjake rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 || 19888823Stmm rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0) 199128776Stmm panic("nexus_attach(): failed to set up rmans"); 20086227Stmm for (child = OF_child(root); child != 0; child = OF_peer(child)) { 20186227Stmm if (child == -1) 202128776Stmm panic("nexus_attach(): OF_child() failed."); 20386227Stmm if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1) 20486227Stmm continue; 20586227Stmm OF_getprop_alloc(child, "device_type", 1, (void **)&type); 20686227Stmm if (NEXUS_EXCLUDED(name, type)) { 20786227Stmm free(name, M_OFWPROP); 208128776Stmm free(type, M_OFWPROP); 20986227Stmm continue; 21088823Stmm } 21186227Stmm cdev = device_add_child(dev, NULL, -1); 212128776Stmm if (cdev == NULL) 213128776Stmm panic("nexus_attach(): device_add_child() failed."); 214128776Stmm dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 215128776Stmm dinfo->ndi_node = child; 216128776Stmm dinfo->ndi_name = name; 217128776Stmm dinfo->ndi_device_type = type; 218128776Stmm OF_getprop_alloc(child, "model", 1, 219128776Stmm (void **)&dinfo->ndi_model); 220128776Stmm dinfo->ndi_nreg = OF_getprop_alloc(child, "reg", 221128776Stmm sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg); 222128776Stmm dinfo->ndi_ninterrupts = OF_getprop_alloc(child, 223128776Stmm "interrupts", sizeof(*dinfo->ndi_interrupts), 224128776Stmm (void **)&dinfo->ndi_interrupts); 225128776Stmm device_set_ivars(cdev, dinfo); 22686227Stmm } 227128776Stmm return (bus_generic_attach(dev)); 22886227Stmm} 22986227Stmm 23086227Stmmstatic void 23186227Stmmnexus_probe_nomatch(device_t dev, device_t child) 23286227Stmm{ 23386227Stmm char *type; 23486227Stmm 235128939Smarius if ((type = nexus_get_device_type(child)) == NULL) 23686227Stmm type = "(unknown)"; 23786227Stmm device_printf(dev, "<%s>, type %s (no driver attached)\n", 238128939Smarius nexus_get_name(child), type); 23986227Stmm} 24086227Stmm 24186227Stmmstatic int 24286227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 24386227Stmm{ 24486227Stmm struct nexus_devinfo *dinfo; 24586227Stmm 24686227Stmm if ((dinfo = device_get_ivars(child)) == 0) 24786227Stmm return (ENOENT); 24886227Stmm switch (which) { 24986227Stmm case NEXUS_IVAR_NODE: 25086227Stmm *result = dinfo->ndi_node; 25186227Stmm break; 25286227Stmm case NEXUS_IVAR_NAME: 25386227Stmm *result = (uintptr_t)dinfo->ndi_name; 25486227Stmm break; 25586227Stmm case NEXUS_IVAR_DEVICE_TYPE: 25686227Stmm *result = (uintptr_t)dinfo->ndi_device_type; 25786227Stmm break; 25886227Stmm case NEXUS_IVAR_MODEL: 25986227Stmm *result = (uintptr_t)dinfo->ndi_model; 26086227Stmm break; 26186227Stmm case NEXUS_IVAR_REG: 26286227Stmm *result = (uintptr_t)dinfo->ndi_reg; 26386227Stmm break; 26486227Stmm case NEXUS_IVAR_NREG: 26586227Stmm *result = dinfo->ndi_nreg; 26686227Stmm break; 26786227Stmm case NEXUS_IVAR_INTERRUPTS: 26886227Stmm *result = (uintptr_t)dinfo->ndi_interrupts; 26986227Stmm break; 27086227Stmm case NEXUS_IVAR_NINTERRUPTS: 27186227Stmm *result = dinfo->ndi_ninterrupts; 27286227Stmm break; 27386227Stmm case NEXUS_IVAR_DMATAG: 274128776Stmm *result = (uintptr_t)&nexus_dmatag; 27586227Stmm break; 27686227Stmm default: 27786227Stmm return (ENOENT); 27886227Stmm } 27986227Stmm return 0; 28086227Stmm} 28186227Stmm 28286227Stmmstatic int 28386227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 28486227Stmm driver_intr_t *intr, void *arg, void **cookiep) 28586227Stmm{ 28686227Stmm int error; 28788823Stmm 28886227Stmm if (res == NULL) 28986227Stmm panic("nexus_setup_intr: NULL interrupt resource!"); 29086227Stmm 291131535Simp if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 29286227Stmm flags |= INTR_EXCL; 29386227Stmm 294128776Stmm /* We depend here on rman_activate_resource() being idempotent. */ 29586227Stmm error = rman_activate_resource(res); 29686227Stmm if (error) 29786227Stmm return (error); 29886227Stmm 299131535Simp error = inthand_add(device_get_nameunit(child), rman_get_start(res), 30086227Stmm intr, arg, flags, cookiep); 30186227Stmm 30286227Stmm return (error); 30386227Stmm} 30486227Stmm 30586227Stmmstatic int 30686227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 30786227Stmm{ 308131535Simp inthand_remove(rman_get_start(r), ih); 30986227Stmm return (0); 31086227Stmm} 31186227Stmm 31286227Stmmstatic struct resource * 31386227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 31486227Stmm u_long start, u_long end, u_long count, u_int flags) 31586227Stmm{ 31688823Stmm struct nexus_softc *sc = device_get_softc(bus); 31786227Stmm struct resource *rv; 31886227Stmm struct rman *rm; 31986227Stmm int needactivate = flags & RF_ACTIVE; 32086227Stmm 32186227Stmm flags &= ~RF_ACTIVE; 32286227Stmm 32386227Stmm switch (type) { 32486227Stmm case SYS_RES_IRQ: 32588823Stmm rm = &sc->sc_intr_rman; 32686227Stmm break; 32788823Stmm case SYS_RES_MEMORY: 32888823Stmm rm = &sc->sc_mem_rman; 32988823Stmm break; 33086227Stmm default: 33186227Stmm return (NULL); 33286227Stmm } 33386227Stmm 33486227Stmm rv = rman_reserve_resource(rm, start, end, count, flags, child); 33586227Stmm if (rv == NULL) 33686227Stmm return (NULL); 33788823Stmm if (type == SYS_RES_MEMORY) { 33888823Stmm rman_set_bustag(rv, &nexus_bustag); 33988823Stmm rman_set_bushandle(rv, rman_get_start(rv)); 34088823Stmm } 34186227Stmm 34286227Stmm if (needactivate) { 343128776Stmm if (bus_activate_resource(child, type, *rid, rv) != 0) { 34486227Stmm rman_release_resource(rv); 34586227Stmm return (NULL); 34686227Stmm } 34786227Stmm } 34888823Stmm 34986227Stmm return (rv); 35086227Stmm} 35186227Stmm 35286227Stmmstatic int 35386227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid, 35486227Stmm struct resource *r) 35586227Stmm{ 35686227Stmm 35786227Stmm /* Not much to be done yet... */ 35886227Stmm return (rman_activate_resource(r)); 35986227Stmm} 36086227Stmm 36186227Stmmstatic int 36286227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 36386227Stmm struct resource *r) 36486227Stmm{ 36588823Stmm 36686227Stmm /* Not much to be done yet... */ 36786227Stmm return (rman_deactivate_resource(r)); 36886227Stmm} 36986227Stmm 37086227Stmmstatic int 37186227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid, 37286227Stmm struct resource *r) 37386227Stmm{ 37486227Stmm int error; 37586227Stmm 37686227Stmm if (rman_get_flags(r) & RF_ACTIVE) { 37786227Stmm error = bus_deactivate_resource(child, type, rid, r); 37886227Stmm if (error) 379128776Stmm return (error); 38086227Stmm } 38186227Stmm return (rman_release_resource(r)); 38286227Stmm} 383