ofwbus.c revision 139825
1139825Simp/*- 294755Sbenno * Copyright 1998 Massachusetts Institute of Technology 394755Sbenno * 494755Sbenno * Permission to use, copy, modify, and distribute this software and 594755Sbenno * its documentation for any purpose and without fee is hereby 694755Sbenno * granted, provided that both the above copyright notice and this 794755Sbenno * permission notice appear in all copies, that both the above 894755Sbenno * copyright notice and this permission notice appear in all 994755Sbenno * supporting documentation, and that the name of M.I.T. not be used 1094755Sbenno * in advertising or publicity pertaining to distribution of the 1194755Sbenno * software without specific, written prior permission. M.I.T. makes 1294755Sbenno * no representations about the suitability of this software for any 1394755Sbenno * purpose. It is provided "as is" without express or implied 1494755Sbenno * warranty. 15125702Sgrehan * 1694755Sbenno * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1794755Sbenno * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1894755Sbenno * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1994755Sbenno * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2094755Sbenno * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2194755Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2294755Sbenno * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2394755Sbenno * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2494755Sbenno * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2594755Sbenno * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2694755Sbenno * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2794755Sbenno * SUCH DAMAGE. 2894755Sbenno */ 2994755Sbenno/*- 3094755Sbenno * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved. 3194755Sbenno * 3294755Sbenno * Redistribution and use in source and binary forms, with or without 3394755Sbenno * modification, are permitted provided that the following conditions 3494755Sbenno * are met: 3594755Sbenno * 1. Redistributions of source code must retain the above copyright 3694755Sbenno * notice, this list of conditions and the following disclaimer. 3794755Sbenno * 2. Redistributions in binary form must reproduce the above copyright 3894755Sbenno * notice, this list of conditions and the following disclaimer in the 3994755Sbenno * documentation and/or other materials provided with the distribution. 4094755Sbenno * 4194755Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 4294755Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4394755Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4494755Sbenno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 4594755Sbenno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 4694755Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 4794755Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4894755Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 4994755Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5094755Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5194755Sbenno * SUCH DAMAGE. 5294755Sbenno * 5394755Sbenno * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09 5494755Sbenno * 5594755Sbenno * $FreeBSD: head/sys/powerpc/aim/nexus.c 139825 2005-01-07 02:29:27Z imp $ 5694755Sbenno */ 57103601Sgrehan#include "opt_psim.h" 5894755Sbenno 5994755Sbenno#include <sys/param.h> 6094755Sbenno#include <sys/systm.h> 61131102Sgrehan#include <sys/module.h> 6294755Sbenno#include <sys/bus.h> 6394755Sbenno#include <sys/cons.h> 6494755Sbenno#include <sys/kernel.h> 6594755Sbenno#include <sys/malloc.h> 6694755Sbenno 6794755Sbenno#include <dev/ofw/openfirm.h> 6894755Sbenno 6994755Sbenno#include <machine/bus.h> 7094755Sbenno#include <machine/frame.h> 7199652Sbenno#include <machine/intr_machdep.h> 7294755Sbenno#include <machine/nexusvar.h> 7394755Sbenno#include <machine/resource.h> 7494755Sbenno 7599652Sbenno#include <sys/rman.h> 7699652Sbenno 7799652Sbenno#include "pic_if.h" 7899652Sbenno 7994755Sbenno/* 8094755Sbenno * The nexus (which is a pseudo-bus actually) iterates over the nodes that 81133862Smarius * exist in Open Firmware and adds them as devices to this bus so that drivers 8294755Sbenno * can be attached to them. 8394755Sbenno * 8494755Sbenno * Maybe this code should get into dev/ofw to some extent, as some of it should 85133862Smarius * work for all Open Firmware based machines... 8694755Sbenno */ 8794755Sbenno 8894755Sbennostatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information"); 8994755Sbenno 9094755Sbennostruct nexus_devinfo { 9194755Sbenno phandle_t ndi_node; 9294755Sbenno /* Some common properties. */ 93125702Sgrehan const char *ndi_name; 94125702Sgrehan const char *ndi_device_type; 95125702Sgrehan const char *ndi_compatible; 9694755Sbenno}; 9794755Sbenno 9894755Sbennostruct nexus_softc { 9999652Sbenno device_t sc_pic; 10094755Sbenno}; 10194755Sbenno 10299652Sbenno/* 10399652Sbenno * Device interface 10499652Sbenno */ 10599652Sbennostatic int nexus_probe(device_t); 10699652Sbennostatic void nexus_probe_nomatch(device_t, device_t); 10794755Sbenno 10899652Sbenno/* 10999652Sbenno * Bus interface 11099652Sbenno */ 111124468Sgrehanstatic device_t nexus_add_child(device_t, int, const char *, int); 11299652Sbennostatic int nexus_read_ivar(device_t, device_t, int, uintptr_t *); 11399652Sbennostatic int nexus_write_ivar(device_t, device_t, int, uintptr_t); 11499652Sbennostatic int nexus_setup_intr(device_t, device_t, struct resource *, int, 11599652Sbenno driver_intr_t *, void *, void **); 11699652Sbennostatic int nexus_teardown_intr(device_t, device_t, struct resource *, 11799652Sbenno void *); 11899652Sbennostatic struct resource *nexus_alloc_resource(device_t, device_t, int, int *, 11999652Sbenno u_long, u_long, u_long, u_int); 12099652Sbennostatic int nexus_activate_resource(device_t, device_t, int, int, 12199652Sbenno struct resource *); 12299652Sbennostatic int nexus_deactivate_resource(device_t, device_t, int, int, 12399652Sbenno struct resource *); 12499652Sbennostatic int nexus_release_resource(device_t, device_t, int, int, 12599652Sbenno struct resource *); 12699652Sbenno 12799652Sbenno/* 12899652Sbenno * Local routines 12999652Sbenno */ 130124468Sgrehanstatic device_t nexus_device_from_node(device_t, phandle_t); 13199652Sbenno 13294755Sbennostatic device_method_t nexus_methods[] = { 13394755Sbenno /* Device interface */ 13494755Sbenno DEVMETHOD(device_probe, nexus_probe), 13594755Sbenno DEVMETHOD(device_attach, bus_generic_attach), 13694755Sbenno DEVMETHOD(device_detach, bus_generic_detach), 13794755Sbenno DEVMETHOD(device_shutdown, bus_generic_shutdown), 13894755Sbenno DEVMETHOD(device_suspend, bus_generic_suspend), 13994755Sbenno DEVMETHOD(device_resume, bus_generic_resume), 14094755Sbenno 14194755Sbenno /* Bus interface. Resource management is business of the children... */ 142124468Sgrehan DEVMETHOD(bus_add_child, nexus_add_child), 14394755Sbenno DEVMETHOD(bus_print_child, bus_generic_print_child), 14494755Sbenno DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), 14594755Sbenno DEVMETHOD(bus_read_ivar, nexus_read_ivar), 14694755Sbenno DEVMETHOD(bus_write_ivar, nexus_write_ivar), 14799652Sbenno DEVMETHOD(bus_setup_intr, nexus_setup_intr), 14899652Sbenno DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 14999652Sbenno DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 15099652Sbenno DEVMETHOD(bus_activate_resource, nexus_activate_resource), 15199652Sbenno DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 15299652Sbenno DEVMETHOD(bus_release_resource, nexus_release_resource), 15394755Sbenno 15494755Sbenno { 0, 0 } 15594755Sbenno}; 15694755Sbenno 15794755Sbennostatic driver_t nexus_driver = { 15894755Sbenno "nexus", 15994755Sbenno nexus_methods, 16094755Sbenno sizeof(struct nexus_softc), 16194755Sbenno}; 16294755Sbenno 16394755Sbennostatic devclass_t nexus_devclass; 16494755Sbenno 16594755SbennoDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 16694755Sbenno 16794755Sbennostatic int 16894755Sbennonexus_probe(device_t dev) 16994755Sbenno{ 17094755Sbenno phandle_t root; 171124468Sgrehan phandle_t child; 17294755Sbenno struct nexus_softc *sc; 17394755Sbenno 17494755Sbenno if ((root = OF_peer(0)) == -1) 17594755Sbenno panic("nexus_probe: OF_peer failed."); 17694755Sbenno 17799652Sbenno sc = device_get_softc(dev); 17899652Sbenno 179124468Sgrehan /* 180124468Sgrehan * Allow devices to identify 181124468Sgrehan */ 182124468Sgrehan bus_generic_probe(dev); 183125702Sgrehan 184124468Sgrehan /* 185124468Sgrehan * Now walk the OFW tree to locate top-level devices 186124468Sgrehan */ 187124468Sgrehan for (child = OF_child(root); child != 0; child = OF_peer(child)) { 188124468Sgrehan if (child == -1) 189124468Sgrehan panic("nexus_probe(): OF_child failed."); 190124468Sgrehan (void)nexus_device_from_node(dev, child); 19199652Sbenno 19294755Sbenno } 193133862Smarius device_set_desc(dev, "Open Firmware Nexus device"); 19494755Sbenno return (0); 19594755Sbenno} 19694755Sbenno 19794755Sbennostatic void 19894755Sbennonexus_probe_nomatch(device_t dev, device_t child) 19994755Sbenno{ 20094755Sbenno char *name, *type; 20194755Sbenno 20294755Sbenno if (BUS_READ_IVAR(dev, child, NEXUS_IVAR_NAME, 20394755Sbenno (uintptr_t *)&name) != 0 || 20494755Sbenno BUS_READ_IVAR(dev, child, NEXUS_IVAR_DEVICE_TYPE, 20594755Sbenno (uintptr_t *)&type) != 0) 20694755Sbenno return; 20794755Sbenno 20894755Sbenno if (type == NULL) 20994755Sbenno type = "(unknown)"; 210124468Sgrehan 211124468Sgrehan if (bootverbose) 212124468Sgrehan device_printf(dev, "<%s>, type %s (no driver attached)\n", 213124468Sgrehan name, type); 21494755Sbenno} 21594755Sbenno 216124468Sgrehanstatic device_t 217124468Sgrehannexus_add_child(device_t dev, int order, const char *name, int unit) 218124468Sgrehan{ 219124468Sgrehan device_t child; 220124468Sgrehan struct nexus_devinfo *dinfo; 221124468Sgrehan 222124468Sgrehan child = device_add_child_ordered(dev, order, name, unit); 223125702Sgrehan if (child == NULL) 224124468Sgrehan return (NULL); 225125702Sgrehan 226124468Sgrehan dinfo = malloc(sizeof(struct nexus_devinfo), M_NEXUS, M_NOWAIT|M_ZERO); 227124468Sgrehan if (dinfo == NULL) 228124468Sgrehan return (NULL); 229124468Sgrehan 230124468Sgrehan dinfo->ndi_node = -1; 231124468Sgrehan dinfo->ndi_name = name; 232125702Sgrehan device_set_ivars(child, dinfo); 233125702Sgrehan 234124468Sgrehan return (child); 235124468Sgrehan} 236124468Sgrehan 237124468Sgrehan 23894755Sbennostatic int 23994755Sbennonexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 24094755Sbenno{ 24194755Sbenno struct nexus_devinfo *dinfo; 24294755Sbenno 24394755Sbenno if ((dinfo = device_get_ivars(child)) == 0) 24494755Sbenno return (ENOENT); 24594755Sbenno switch (which) { 24694755Sbenno case NEXUS_IVAR_NODE: 24794755Sbenno *result = dinfo->ndi_node; 24894755Sbenno break; 24994755Sbenno case NEXUS_IVAR_NAME: 25094755Sbenno *result = (uintptr_t)dinfo->ndi_name; 25194755Sbenno break; 25294755Sbenno case NEXUS_IVAR_DEVICE_TYPE: 25394755Sbenno *result = (uintptr_t)dinfo->ndi_device_type; 25494755Sbenno break; 25599652Sbenno case NEXUS_IVAR_COMPATIBLE: 25699652Sbenno *result = (uintptr_t)dinfo->ndi_compatible; 25799652Sbenno break; 25894755Sbenno default: 25994755Sbenno return (ENOENT); 26094755Sbenno } 26194755Sbenno return 0; 26294755Sbenno} 26394755Sbenno 26494755Sbennostatic int 26594755Sbennonexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 26694755Sbenno{ 26794755Sbenno struct nexus_devinfo *dinfo; 26894755Sbenno 26994755Sbenno if ((dinfo = device_get_ivars(child)) == 0) 27094755Sbenno return (ENOENT); 27194755Sbenno 27294755Sbenno switch (which) { 27394755Sbenno case NEXUS_IVAR_NAME: 274124468Sgrehan return (EINVAL); 275124468Sgrehan 276125434Sgrehan /* Identified devices may want to set these */ 277125434Sgrehan case NEXUS_IVAR_NODE: 278125434Sgrehan dinfo->ndi_node = (phandle_t)value; 279125434Sgrehan break; 280125434Sgrehan 28194755Sbenno case NEXUS_IVAR_DEVICE_TYPE: 282124468Sgrehan dinfo->ndi_device_type = (char *)value; 283124468Sgrehan break; 284124468Sgrehan 28594755Sbenno default: 28694755Sbenno return (ENOENT); 28794755Sbenno } 28894755Sbenno return 0; 28994755Sbenno} 29099652Sbenno 29199652Sbennostatic int 29299652Sbennonexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, 29399652Sbenno driver_intr_t *intr, void *arg, void **cookiep) 29499652Sbenno{ 29599652Sbenno struct nexus_softc *sc; 29699652Sbenno 29799652Sbenno sc = device_get_softc(dev); 29899652Sbenno 29999652Sbenno if (device_get_state(sc->sc_pic) != DS_ATTACHED) 30099652Sbenno panic("nexus_setup_intr: no pic attached\n"); 30199652Sbenno 30299652Sbenno return (PIC_SETUP_INTR(sc->sc_pic, child, res, flags, intr, arg, 30399652Sbenno cookiep)); 30499652Sbenno} 30599652Sbenno 30699652Sbennostatic int 30799652Sbennonexus_teardown_intr(device_t dev, device_t child, struct resource *res, 30899652Sbenno void *ih) 30999652Sbenno{ 31099652Sbenno struct nexus_softc *sc; 31199652Sbenno 31299652Sbenno sc = device_get_softc(dev); 31399652Sbenno 31499652Sbenno if (device_get_state(sc->sc_pic) != DS_ATTACHED) 31599652Sbenno panic("nexus_teardown_intr: no pic attached\n"); 31699652Sbenno 31799652Sbenno return (PIC_TEARDOWN_INTR(sc->sc_pic, child, res, ih)); 31899652Sbenno} 31999652Sbenno 32099652Sbenno/* 32199652Sbenno * Allocate resources at the behest of a child. This only handles interrupts, 32299652Sbenno * since I/O resources are handled by child busses. 32399652Sbenno */ 32499652Sbennostatic struct resource * 32599652Sbennonexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 32699652Sbenno u_long start, u_long end, u_long count, u_int flags) 32799652Sbenno{ 32899652Sbenno struct nexus_softc *sc; 32999652Sbenno struct resource *rv; 33099652Sbenno 33199652Sbenno sc = device_get_softc(bus); 33299652Sbenno 33399652Sbenno if (type != SYS_RES_IRQ) { 33499652Sbenno device_printf(bus, "unknown resource request from %s\n", 33599652Sbenno device_get_nameunit(child)); 33699652Sbenno return (NULL); 33799652Sbenno } 33899652Sbenno 33999652Sbenno if (device_get_state(sc->sc_pic) != DS_ATTACHED) 34099652Sbenno panic("nexus_alloc_resource: no pic attached\n"); 34199652Sbenno 34299652Sbenno rv = PIC_ALLOCATE_INTR(sc->sc_pic, child, rid, start, flags); 34399652Sbenno 34499652Sbenno return (rv); 34599652Sbenno} 34699652Sbenno 34799652Sbennostatic int 34899652Sbennonexus_activate_resource(device_t bus, device_t child, int type, int rid, 34999652Sbenno struct resource *res) 35099652Sbenno{ 35199652Sbenno 35299652Sbenno /* Not much to be done yet... */ 35399652Sbenno return (rman_activate_resource(res)); 35499652Sbenno} 35599652Sbenno 35699652Sbennostatic int 35799652Sbennonexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 35899652Sbenno struct resource *res) 35999652Sbenno{ 36099652Sbenno 36199652Sbenno /* Not much to be done yet... */ 36299652Sbenno return (rman_deactivate_resource(res)); 36399652Sbenno} 36499652Sbenno 36599652Sbennostatic int 36699652Sbennonexus_release_resource(device_t bus, device_t child, int type, int rid, 36799652Sbenno struct resource *res) 36899652Sbenno{ 36999652Sbenno struct nexus_softc *sc; 37099652Sbenno 37199652Sbenno sc = device_get_softc(bus); 37299652Sbenno 37399652Sbenno if (type != SYS_RES_IRQ) { 37499652Sbenno device_printf(bus, "unknown resource request from %s\n", 37599652Sbenno device_get_nameunit(child)); 376124468Sgrehan return (EINVAL); 37799652Sbenno } 37899652Sbenno 37999652Sbenno if (device_get_state(sc->sc_pic) != DS_ATTACHED) 38099652Sbenno panic("nexus_release_resource: no pic attached\n"); 38199652Sbenno 38299652Sbenno return (PIC_RELEASE_INTR(sc->sc_pic, child, rid, res)); 38399652Sbenno} 38499652Sbenno 38599652Sbennostatic device_t 386124468Sgrehannexus_device_from_node(device_t parent, phandle_t node) 38799652Sbenno{ 38899652Sbenno device_t cdev; 38999652Sbenno struct nexus_devinfo *dinfo; 39099652Sbenno char *name, *type, *compatible; 39199652Sbenno 39299652Sbenno OF_getprop_alloc(node, "name", 1, (void **)&name); 39399652Sbenno OF_getprop_alloc(node, "device_type", 1, (void **)&type); 39499652Sbenno OF_getprop_alloc(node, "compatible", 1, (void **)&compatible); 39599652Sbenno cdev = device_add_child(parent, NULL, -1); 39699652Sbenno if (cdev != NULL) { 397111119Simp dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK); 39899652Sbenno dinfo->ndi_node = node; 39999652Sbenno dinfo->ndi_name = name; 40099652Sbenno dinfo->ndi_device_type = type; 40199652Sbenno dinfo->ndi_compatible = compatible; 40299652Sbenno device_set_ivars(cdev, dinfo); 40399652Sbenno } else 40499652Sbenno free(name, M_OFWPROP); 40599652Sbenno 40699652Sbenno return (cdev); 40799652Sbenno} 408124468Sgrehan 409124468Sgrehanint 410124468Sgrehannexus_install_intcntlr(device_t dev) 411124468Sgrehan{ 412124468Sgrehan struct nexus_softc *sc; 413124468Sgrehan 414124468Sgrehan sc = device_get_softc(device_get_parent(dev)); 415124468Sgrehan sc->sc_pic = dev; 416124468Sgrehan 417124468Sgrehan return (0); 418124468Sgrehan} 419