nexus.c revision 157896
1139825Simp/*- 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 34146474Smarius#include <sys/cdefs.h> 35146474Smarius__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/nexus.c 157896 2006-04-20 04:20:41Z imp $"); 36146474Smarius 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 59133862Smarius * hang from the Open Firmware 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 67133862Smarius * work for all Open Firmware 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; 91146474Smariusstatic bus_add_child_t nexus_add_child; 92128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch; 93128776Stmmstatic bus_read_ivar_t nexus_read_ivar; 94128776Stmmstatic bus_setup_intr_t nexus_setup_intr; 95128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr; 96128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource; 97128776Stmmstatic bus_activate_resource_t nexus_activate_resource; 98128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource; 99128776Stmmstatic bus_release_resource_t nexus_release_resource; 10086227Stmm 10186227Stmmstatic device_method_t nexus_methods[] = { 10286227Stmm /* Device interface */ 10386227Stmm DEVMETHOD(device_probe, nexus_probe), 104128776Stmm DEVMETHOD(device_attach, nexus_attach), 10586227Stmm DEVMETHOD(device_detach, bus_generic_detach), 10686227Stmm DEVMETHOD(device_shutdown, bus_generic_shutdown), 10786227Stmm DEVMETHOD(device_suspend, bus_generic_suspend), 10886227Stmm DEVMETHOD(device_resume, bus_generic_resume), 10986227Stmm 110128776Stmm /* Bus interface. */ 111146474Smarius DEVMETHOD(bus_add_child, nexus_add_child), 11286227Stmm DEVMETHOD(bus_print_child, bus_generic_print_child), 11386227Stmm DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 11486227Stmm DEVMETHOD(bus_read_ivar, nexus_read_ivar), 11586227Stmm DEVMETHOD(bus_setup_intr, nexus_setup_intr), 11686227Stmm DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 11786227Stmm DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 11886227Stmm DEVMETHOD(bus_activate_resource, nexus_activate_resource), 11986227Stmm DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 12086227Stmm DEVMETHOD(bus_release_resource, nexus_release_resource), 12186227Stmm 12286227Stmm { 0, 0 } 12386227Stmm}; 12486227Stmm 12586227Stmmstatic driver_t nexus_driver = { 12686227Stmm "nexus", 12786227Stmm nexus_methods, 12888823Stmm sizeof(struct nexus_softc), 12986227Stmm}; 13086227Stmm 13186227Stmmstatic devclass_t nexus_devclass; 13286227Stmm 13386227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 13486227Stmm 13586227Stmmstatic char *nexus_excl_name[] = { 13690622Stmm "aliases", 13790622Stmm "chosen", 13890622Stmm "counter-timer", /* No separate device; handled by psycho/sbus */ 13986227Stmm "memory", 14090622Stmm "openprom", 14186227Stmm "options", 14286227Stmm "packages", 14390622Stmm "virtual-memory", 14486227Stmm NULL 14586227Stmm}; 14686227Stmm 14786227Stmmstatic char *nexus_excl_type[] = { 14886227Stmm "cpu", 14986227Stmm NULL 15086227Stmm}; 15186227Stmm 15286227Stmmextern struct bus_space_tag nexus_bustag; 15386227Stmmextern struct bus_dma_tag nexus_dmatag; 15486227Stmm 15586227Stmmstatic int 15686227Stmmnexus_inlist(char *name, char *list[]) 15786227Stmm{ 15886227Stmm int i; 15986227Stmm 16086227Stmm for (i = 0; list[i] != NULL; i++) 16186227Stmm if (strcmp(name, list[i]) == 0) 16286227Stmm return (1); 16386227Stmm return (0); 16486227Stmm} 16586227Stmm 16686227Stmm#define NEXUS_EXCLUDED(name, type) \ 16786227Stmm (nexus_inlist((name), nexus_excl_name) || \ 16886227Stmm ((type) != NULL && nexus_inlist((type), nexus_excl_type))) 16986227Stmm 17086227Stmmstatic int 17186227Stmmnexus_probe(device_t dev) 17286227Stmm{ 173128776Stmm 174128776Stmm /* Nexus does always match. */ 175133862Smarius device_set_desc(dev, "Open Firmware Nexus device"); 176128776Stmm return (0); 177128776Stmm} 178128776Stmm 179128776Stmmstatic int 180128776Stmmnexus_attach(device_t dev) 181128776Stmm{ 18286227Stmm phandle_t root; 18386227Stmm phandle_t child; 18486227Stmm device_t cdev; 18586227Stmm struct nexus_devinfo *dinfo; 18688823Stmm struct nexus_softc *sc; 18786227Stmm char *name, *type; 18886227Stmm 18986227Stmm if ((root = OF_peer(0)) == -1) 19086227Stmm panic("nexus_probe: OF_peer failed."); 19186227Stmm 19288823Stmm sc = device_get_softc(dev); 19388823Stmm sc->sc_intr_rman.rm_type = RMAN_ARRAY; 19488823Stmm sc->sc_intr_rman.rm_descr = "Interrupts"; 19588823Stmm sc->sc_mem_rman.rm_type = RMAN_ARRAY; 19688823Stmm sc->sc_mem_rman.rm_descr = "UPA Device Memory"; 19788823Stmm if (rman_init(&sc->sc_intr_rman) != 0 || 19888823Stmm rman_init(&sc->sc_mem_rman) != 0 || 19997265Sjake rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 || 20088823Stmm rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0) 201128776Stmm panic("nexus_attach(): failed to set up rmans"); 202146474Smarius 203146474Smarius /* 204146474Smarius * Allow devices to identify. 205146474Smarius */ 206146474Smarius bus_generic_probe(dev); 207146474Smarius 208146474Smarius /* 209146474Smarius * Now walk the OFW tree and attach top-level devices. 210146474Smarius */ 21186227Stmm for (child = OF_child(root); child != 0; child = OF_peer(child)) { 21286227Stmm if (child == -1) 213128776Stmm panic("nexus_attach(): OF_child() failed."); 21486227Stmm if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1) 21586227Stmm continue; 21686227Stmm OF_getprop_alloc(child, "device_type", 1, (void **)&type); 21786227Stmm if (NEXUS_EXCLUDED(name, type)) { 21886227Stmm free(name, M_OFWPROP); 219128776Stmm free(type, M_OFWPROP); 22086227Stmm continue; 22188823Stmm } 22286227Stmm cdev = device_add_child(dev, NULL, -1); 223128776Stmm if (cdev == NULL) 224128776Stmm panic("nexus_attach(): device_add_child() failed."); 225128776Stmm dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 226128776Stmm dinfo->ndi_node = child; 227128776Stmm dinfo->ndi_name = name; 228128776Stmm dinfo->ndi_device_type = type; 229128776Stmm OF_getprop_alloc(child, "model", 1, 230128776Stmm (void **)&dinfo->ndi_model); 231128776Stmm dinfo->ndi_nreg = OF_getprop_alloc(child, "reg", 232128776Stmm sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg); 233128776Stmm dinfo->ndi_ninterrupts = OF_getprop_alloc(child, 234128776Stmm "interrupts", sizeof(*dinfo->ndi_interrupts), 235128776Stmm (void **)&dinfo->ndi_interrupts); 236128776Stmm device_set_ivars(cdev, dinfo); 23786227Stmm } 238128776Stmm return (bus_generic_attach(dev)); 23986227Stmm} 24086227Stmm 241146474Smariusstatic device_t 242146474Smariusnexus_add_child(device_t dev, int order, const char *name, int unit) 243146474Smarius{ 244146474Smarius device_t cdev; 245146474Smarius struct nexus_devinfo *dinfo; 246146474Smarius 247146474Smarius cdev = device_add_child_ordered(dev, order, name, unit); 248146474Smarius if (cdev == NULL) 249146474Smarius return (NULL); 250146474Smarius 251146474Smarius dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_NOWAIT | M_ZERO); 252146474Smarius if (dinfo == NULL) 253146474Smarius return (NULL); 254146474Smarius 255146474Smarius dinfo->ndi_node = -1; 256146474Smarius dinfo->ndi_name = strdup(name, M_OFWPROP); 257146474Smarius device_set_ivars(cdev, dinfo); 258146474Smarius 259146474Smarius return (cdev); 260146474Smarius} 261146474Smarius 26286227Stmmstatic void 26386227Stmmnexus_probe_nomatch(device_t dev, device_t child) 26486227Stmm{ 26586227Stmm char *type; 26686227Stmm 267128939Smarius if ((type = nexus_get_device_type(child)) == NULL) 26886227Stmm type = "(unknown)"; 26986227Stmm device_printf(dev, "<%s>, type %s (no driver attached)\n", 270128939Smarius nexus_get_name(child), type); 27186227Stmm} 27286227Stmm 27386227Stmmstatic int 27486227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 27586227Stmm{ 27686227Stmm struct nexus_devinfo *dinfo; 27786227Stmm 27886227Stmm if ((dinfo = device_get_ivars(child)) == 0) 27986227Stmm return (ENOENT); 28086227Stmm switch (which) { 28186227Stmm case NEXUS_IVAR_NODE: 28286227Stmm *result = dinfo->ndi_node; 28386227Stmm break; 28486227Stmm case NEXUS_IVAR_NAME: 28586227Stmm *result = (uintptr_t)dinfo->ndi_name; 28686227Stmm break; 28786227Stmm case NEXUS_IVAR_DEVICE_TYPE: 28886227Stmm *result = (uintptr_t)dinfo->ndi_device_type; 28986227Stmm break; 29086227Stmm case NEXUS_IVAR_MODEL: 29186227Stmm *result = (uintptr_t)dinfo->ndi_model; 29286227Stmm break; 29386227Stmm case NEXUS_IVAR_REG: 29486227Stmm *result = (uintptr_t)dinfo->ndi_reg; 29586227Stmm break; 29686227Stmm case NEXUS_IVAR_NREG: 29786227Stmm *result = dinfo->ndi_nreg; 29886227Stmm break; 29986227Stmm case NEXUS_IVAR_INTERRUPTS: 30086227Stmm *result = (uintptr_t)dinfo->ndi_interrupts; 30186227Stmm break; 30286227Stmm case NEXUS_IVAR_NINTERRUPTS: 30386227Stmm *result = dinfo->ndi_ninterrupts; 30486227Stmm break; 30586227Stmm case NEXUS_IVAR_DMATAG: 306128776Stmm *result = (uintptr_t)&nexus_dmatag; 30786227Stmm break; 30886227Stmm default: 30986227Stmm return (ENOENT); 31086227Stmm } 31186227Stmm return 0; 31286227Stmm} 31386227Stmm 31486227Stmmstatic int 31586227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 31686227Stmm driver_intr_t *intr, void *arg, void **cookiep) 31786227Stmm{ 31886227Stmm int error; 31988823Stmm 32086227Stmm if (res == NULL) 32186227Stmm panic("nexus_setup_intr: NULL interrupt resource!"); 32286227Stmm 323131535Simp if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 32486227Stmm flags |= INTR_EXCL; 32586227Stmm 326128776Stmm /* We depend here on rman_activate_resource() being idempotent. */ 32786227Stmm error = rman_activate_resource(res); 32886227Stmm if (error) 32986227Stmm return (error); 33086227Stmm 331131535Simp error = inthand_add(device_get_nameunit(child), rman_get_start(res), 33286227Stmm intr, arg, flags, cookiep); 33386227Stmm 33486227Stmm return (error); 33586227Stmm} 33686227Stmm 33786227Stmmstatic int 33886227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 33986227Stmm{ 340146474Smarius 341131535Simp inthand_remove(rman_get_start(r), ih); 34286227Stmm return (0); 34386227Stmm} 34486227Stmm 34586227Stmmstatic struct resource * 34686227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 34786227Stmm u_long start, u_long end, u_long count, u_int flags) 34886227Stmm{ 34988823Stmm struct nexus_softc *sc = device_get_softc(bus); 35086227Stmm struct resource *rv; 35186227Stmm struct rman *rm; 35286227Stmm int needactivate = flags & RF_ACTIVE; 35386227Stmm 35486227Stmm flags &= ~RF_ACTIVE; 35586227Stmm 35686227Stmm switch (type) { 35786227Stmm case SYS_RES_IRQ: 35888823Stmm rm = &sc->sc_intr_rman; 35986227Stmm break; 36088823Stmm case SYS_RES_MEMORY: 36188823Stmm rm = &sc->sc_mem_rman; 36288823Stmm break; 36386227Stmm default: 36486227Stmm return (NULL); 36586227Stmm } 36686227Stmm 36786227Stmm rv = rman_reserve_resource(rm, start, end, count, flags, child); 36886227Stmm if (rv == NULL) 36986227Stmm return (NULL); 370157896Simp rman_set_rid(rv, *rid); 37188823Stmm if (type == SYS_RES_MEMORY) { 37288823Stmm rman_set_bustag(rv, &nexus_bustag); 37388823Stmm rman_set_bushandle(rv, rman_get_start(rv)); 37488823Stmm } 37586227Stmm 37686227Stmm if (needactivate) { 377128776Stmm if (bus_activate_resource(child, type, *rid, rv) != 0) { 37886227Stmm rman_release_resource(rv); 37986227Stmm return (NULL); 38086227Stmm } 38186227Stmm } 38288823Stmm 38386227Stmm return (rv); 38486227Stmm} 38586227Stmm 38686227Stmmstatic int 38786227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid, 38886227Stmm struct resource *r) 38986227Stmm{ 39086227Stmm 39186227Stmm /* Not much to be done yet... */ 39286227Stmm return (rman_activate_resource(r)); 39386227Stmm} 39486227Stmm 39586227Stmmstatic int 39686227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 39786227Stmm struct resource *r) 39886227Stmm{ 39988823Stmm 40086227Stmm /* Not much to be done yet... */ 40186227Stmm return (rman_deactivate_resource(r)); 40286227Stmm} 40386227Stmm 40486227Stmmstatic int 40586227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid, 40686227Stmm struct resource *r) 40786227Stmm{ 40886227Stmm int error; 40986227Stmm 41086227Stmm if (rman_get_flags(r) & RF_ACTIVE) { 41186227Stmm error = bus_deactivate_resource(child, type, rid, r); 41286227Stmm if (error) 413128776Stmm return (error); 41486227Stmm } 41586227Stmm return (rman_release_resource(r)); 41686227Stmm} 417