nexus.c revision 128939
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 128939 2004-05-04 22:31:16Z marius $ 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> 4286227Stmm 4386227Stmm#include <dev/ofw/openfirm.h> 4486227Stmm 4586227Stmm#include <machine/bus.h> 4686227Stmm#include <machine/frame.h> 4786227Stmm#include <machine/intr_machdep.h> 4886227Stmm#include <machine/nexusvar.h> 4988823Stmm#include <machine/ofw_upa.h> 5086227Stmm#include <machine/resource.h> 5188823Stmm#include <machine/upa.h> 5286227Stmm 5386227Stmm#include <sys/rman.h> 5486227Stmm 5586227Stmm/* 5686227Stmm * The nexus (which is a pseudo-bus actually) iterates over the nodes that 57128776Stmm * hang from the OpenFirmware root node and adds them as devices to this bus 5886227Stmm * (except some special nodes which are excluded) so that drivers can be 59128776Stmm * attached to them. 6086227Stmm * 6186227Stmm * Additionally, interrupt setup/teardown and some resource management are 6286227Stmm * done at this level. 6386227Stmm * 6486227Stmm * Maybe this code should get into dev/ofw to some extent, as some of it should 6586227Stmm * work for all OpenFirmware based machines... 6686227Stmm */ 6786227Stmm 6886227Stmmstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information"); 6986227Stmm 7086227Stmmstruct nexus_devinfo { 7186227Stmm phandle_t ndi_node; 7286227Stmm /* Some common properties. */ 7386227Stmm char *ndi_name; 7486227Stmm char *ndi_device_type; 7586227Stmm char *ndi_model; 7688823Stmm struct upa_regs *ndi_reg; 7786227Stmm int ndi_nreg; 7886227Stmm u_int *ndi_interrupts; 7986227Stmm int ndi_ninterrupts; 8086227Stmm}; 8186227Stmm 8288823Stmmstruct nexus_softc { 8388823Stmm struct rman sc_intr_rman; 8488823Stmm struct rman sc_mem_rman; 8588823Stmm}; 8688823Stmm 87128776Stmmstatic device_probe_t nexus_probe; 88128776Stmmstatic device_attach_t nexus_attach; 89128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch; 90128776Stmmstatic bus_read_ivar_t nexus_read_ivar; 91128776Stmmstatic bus_setup_intr_t nexus_setup_intr; 92128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr; 93128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource; 94128776Stmmstatic bus_activate_resource_t nexus_activate_resource; 95128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource; 96128776Stmmstatic bus_release_resource_t nexus_release_resource; 9786227Stmm 9886227Stmmstatic device_method_t nexus_methods[] = { 9986227Stmm /* Device interface */ 10086227Stmm DEVMETHOD(device_probe, nexus_probe), 101128776Stmm DEVMETHOD(device_attach, nexus_attach), 10286227Stmm DEVMETHOD(device_detach, bus_generic_detach), 10386227Stmm DEVMETHOD(device_shutdown, bus_generic_shutdown), 10486227Stmm DEVMETHOD(device_suspend, bus_generic_suspend), 10586227Stmm DEVMETHOD(device_resume, bus_generic_resume), 10686227Stmm 107128776Stmm /* Bus interface. */ 10886227Stmm DEVMETHOD(bus_print_child, bus_generic_print_child), 10986227Stmm DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 11086227Stmm DEVMETHOD(bus_read_ivar, nexus_read_ivar), 11186227Stmm DEVMETHOD(bus_setup_intr, nexus_setup_intr), 11286227Stmm DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 11386227Stmm DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 11486227Stmm DEVMETHOD(bus_activate_resource, nexus_activate_resource), 11586227Stmm DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 11686227Stmm DEVMETHOD(bus_release_resource, nexus_release_resource), 11786227Stmm 11886227Stmm { 0, 0 } 11986227Stmm}; 12086227Stmm 12186227Stmmstatic driver_t nexus_driver = { 12286227Stmm "nexus", 12386227Stmm nexus_methods, 12488823Stmm sizeof(struct nexus_softc), 12586227Stmm}; 12686227Stmm 12786227Stmmstatic devclass_t nexus_devclass; 12886227Stmm 12986227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 13086227Stmm 13186227Stmmstatic char *nexus_excl_name[] = { 13290622Stmm "aliases", 13390622Stmm "chosen", 13490622Stmm "counter-timer", /* No separate device; handled by psycho/sbus */ 13586227Stmm "memory", 13690622Stmm "openprom", 13786227Stmm "options", 13886227Stmm "packages", 13990622Stmm "virtual-memory", 14086227Stmm NULL 14186227Stmm}; 14286227Stmm 14386227Stmmstatic char *nexus_excl_type[] = { 14486227Stmm "cpu", 14586227Stmm NULL 14686227Stmm}; 14786227Stmm 14886227Stmmextern struct bus_space_tag nexus_bustag; 14986227Stmmextern struct bus_dma_tag nexus_dmatag; 15086227Stmm 15186227Stmmstatic int 15286227Stmmnexus_inlist(char *name, char *list[]) 15386227Stmm{ 15486227Stmm int i; 15586227Stmm 15686227Stmm for (i = 0; list[i] != NULL; i++) 15786227Stmm if (strcmp(name, list[i]) == 0) 15886227Stmm return (1); 15986227Stmm return (0); 16086227Stmm} 16186227Stmm 16286227Stmm#define NEXUS_EXCLUDED(name, type) \ 16386227Stmm (nexus_inlist((name), nexus_excl_name) || \ 16486227Stmm ((type) != NULL && nexus_inlist((type), nexus_excl_type))) 16586227Stmm 16686227Stmmstatic int 16786227Stmmnexus_probe(device_t dev) 16886227Stmm{ 169128776Stmm 170128776Stmm /* Nexus does always match. */ 171128776Stmm device_set_desc(dev, "OpenFirmware Nexus device"); 172128776Stmm return (0); 173128776Stmm} 174128776Stmm 175128776Stmmstatic int 176128776Stmmnexus_attach(device_t dev) 177128776Stmm{ 17886227Stmm phandle_t root; 17986227Stmm phandle_t child; 18086227Stmm device_t cdev; 18186227Stmm struct nexus_devinfo *dinfo; 18288823Stmm struct nexus_softc *sc; 18386227Stmm char *name, *type; 18486227Stmm 18586227Stmm if ((root = OF_peer(0)) == -1) 18686227Stmm panic("nexus_probe: OF_peer failed."); 18786227Stmm 18888823Stmm sc = device_get_softc(dev); 18988823Stmm sc->sc_intr_rman.rm_type = RMAN_ARRAY; 19088823Stmm sc->sc_intr_rman.rm_descr = "Interrupts"; 19188823Stmm sc->sc_mem_rman.rm_type = RMAN_ARRAY; 19288823Stmm sc->sc_mem_rman.rm_descr = "UPA Device Memory"; 19388823Stmm if (rman_init(&sc->sc_intr_rman) != 0 || 19488823Stmm rman_init(&sc->sc_mem_rman) != 0 || 19597265Sjake rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 || 19688823Stmm rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0) 197128776Stmm panic("nexus_attach(): failed to set up rmans"); 19886227Stmm for (child = OF_child(root); child != 0; child = OF_peer(child)) { 19986227Stmm if (child == -1) 200128776Stmm panic("nexus_attach(): OF_child() failed."); 20186227Stmm if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1) 20286227Stmm continue; 20386227Stmm OF_getprop_alloc(child, "device_type", 1, (void **)&type); 20486227Stmm if (NEXUS_EXCLUDED(name, type)) { 20586227Stmm free(name, M_OFWPROP); 206128776Stmm free(type, M_OFWPROP); 20786227Stmm continue; 20888823Stmm } 20986227Stmm cdev = device_add_child(dev, NULL, -1); 210128776Stmm if (cdev == NULL) 211128776Stmm panic("nexus_attach(): device_add_child() failed."); 212128776Stmm dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 213128776Stmm dinfo->ndi_node = child; 214128776Stmm dinfo->ndi_name = name; 215128776Stmm dinfo->ndi_device_type = type; 216128776Stmm OF_getprop_alloc(child, "model", 1, 217128776Stmm (void **)&dinfo->ndi_model); 218128776Stmm dinfo->ndi_nreg = OF_getprop_alloc(child, "reg", 219128776Stmm sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg); 220128776Stmm dinfo->ndi_ninterrupts = OF_getprop_alloc(child, 221128776Stmm "interrupts", sizeof(*dinfo->ndi_interrupts), 222128776Stmm (void **)&dinfo->ndi_interrupts); 223128776Stmm device_set_ivars(cdev, dinfo); 22486227Stmm } 225128776Stmm return (bus_generic_attach(dev)); 22686227Stmm} 22786227Stmm 22886227Stmmstatic void 22986227Stmmnexus_probe_nomatch(device_t dev, device_t child) 23086227Stmm{ 23186227Stmm char *type; 23286227Stmm 233128939Smarius if ((type = nexus_get_device_type(child)) == NULL) 23486227Stmm type = "(unknown)"; 23586227Stmm device_printf(dev, "<%s>, type %s (no driver attached)\n", 236128939Smarius nexus_get_name(child), type); 23786227Stmm} 23886227Stmm 23986227Stmmstatic int 24086227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 24186227Stmm{ 24286227Stmm struct nexus_devinfo *dinfo; 24386227Stmm 24486227Stmm if ((dinfo = device_get_ivars(child)) == 0) 24586227Stmm return (ENOENT); 24686227Stmm switch (which) { 24786227Stmm case NEXUS_IVAR_NODE: 24886227Stmm *result = dinfo->ndi_node; 24986227Stmm break; 25086227Stmm case NEXUS_IVAR_NAME: 25186227Stmm *result = (uintptr_t)dinfo->ndi_name; 25286227Stmm break; 25386227Stmm case NEXUS_IVAR_DEVICE_TYPE: 25486227Stmm *result = (uintptr_t)dinfo->ndi_device_type; 25586227Stmm break; 25686227Stmm case NEXUS_IVAR_MODEL: 25786227Stmm *result = (uintptr_t)dinfo->ndi_model; 25886227Stmm break; 25986227Stmm case NEXUS_IVAR_REG: 26086227Stmm *result = (uintptr_t)dinfo->ndi_reg; 26186227Stmm break; 26286227Stmm case NEXUS_IVAR_NREG: 26386227Stmm *result = dinfo->ndi_nreg; 26486227Stmm break; 26586227Stmm case NEXUS_IVAR_INTERRUPTS: 26686227Stmm *result = (uintptr_t)dinfo->ndi_interrupts; 26786227Stmm break; 26886227Stmm case NEXUS_IVAR_NINTERRUPTS: 26986227Stmm *result = dinfo->ndi_ninterrupts; 27086227Stmm break; 27186227Stmm case NEXUS_IVAR_DMATAG: 272128776Stmm *result = (uintptr_t)&nexus_dmatag; 27386227Stmm break; 27486227Stmm default: 27586227Stmm return (ENOENT); 27686227Stmm } 27786227Stmm return 0; 27886227Stmm} 27986227Stmm 28086227Stmmstatic int 28186227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 28286227Stmm driver_intr_t *intr, void *arg, void **cookiep) 28386227Stmm{ 28486227Stmm int error; 28588823Stmm 28686227Stmm if (res == NULL) 28786227Stmm panic("nexus_setup_intr: NULL interrupt resource!"); 28886227Stmm 28986227Stmm if ((res->r_flags & RF_SHAREABLE) == 0) 29086227Stmm flags |= INTR_EXCL; 29186227Stmm 292128776Stmm /* We depend here on rman_activate_resource() being idempotent. */ 29386227Stmm error = rman_activate_resource(res); 29486227Stmm if (error) 29586227Stmm return (error); 29686227Stmm 29786227Stmm error = inthand_add(device_get_nameunit(child), res->r_start, 29886227Stmm intr, arg, flags, cookiep); 29986227Stmm 30086227Stmm return (error); 30186227Stmm} 30286227Stmm 30386227Stmmstatic int 30486227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 30586227Stmm{ 30686227Stmm inthand_remove(r->r_start, ih); 30786227Stmm return (0); 30886227Stmm} 30986227Stmm 31086227Stmmstatic struct resource * 31186227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 31286227Stmm u_long start, u_long end, u_long count, u_int flags) 31386227Stmm{ 31488823Stmm struct nexus_softc *sc = device_get_softc(bus); 31586227Stmm struct resource *rv; 31686227Stmm struct rman *rm; 31786227Stmm int needactivate = flags & RF_ACTIVE; 31886227Stmm 31986227Stmm flags &= ~RF_ACTIVE; 32086227Stmm 32186227Stmm switch (type) { 32286227Stmm case SYS_RES_IRQ: 32388823Stmm rm = &sc->sc_intr_rman; 32486227Stmm break; 32588823Stmm case SYS_RES_MEMORY: 32688823Stmm rm = &sc->sc_mem_rman; 32788823Stmm break; 32886227Stmm default: 32986227Stmm return (NULL); 33086227Stmm } 33186227Stmm 33286227Stmm rv = rman_reserve_resource(rm, start, end, count, flags, child); 33386227Stmm if (rv == NULL) 33486227Stmm return (NULL); 33588823Stmm if (type == SYS_RES_MEMORY) { 33688823Stmm rman_set_bustag(rv, &nexus_bustag); 33788823Stmm rman_set_bushandle(rv, rman_get_start(rv)); 33888823Stmm } 33986227Stmm 34086227Stmm if (needactivate) { 341128776Stmm if (bus_activate_resource(child, type, *rid, rv) != 0) { 34286227Stmm rman_release_resource(rv); 34386227Stmm return (NULL); 34486227Stmm } 34586227Stmm } 34688823Stmm 34786227Stmm return (rv); 34886227Stmm} 34986227Stmm 35086227Stmmstatic int 35186227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid, 35286227Stmm struct resource *r) 35386227Stmm{ 35486227Stmm 35586227Stmm /* Not much to be done yet... */ 35686227Stmm return (rman_activate_resource(r)); 35786227Stmm} 35886227Stmm 35986227Stmmstatic int 36086227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 36186227Stmm struct resource *r) 36286227Stmm{ 36388823Stmm 36486227Stmm /* Not much to be done yet... */ 36586227Stmm return (rman_deactivate_resource(r)); 36686227Stmm} 36786227Stmm 36886227Stmmstatic int 36986227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid, 37086227Stmm struct resource *r) 37186227Stmm{ 37286227Stmm int error; 37386227Stmm 37486227Stmm if (rman_get_flags(r) & RF_ACTIVE) { 37586227Stmm error = bus_deactivate_resource(child, type, rid, r); 37686227Stmm if (error) 377128776Stmm return (error); 37886227Stmm } 37986227Stmm return (rman_release_resource(r)); 38086227Stmm} 381