Index: head/sys/dev/ofw/ofw_bus.h =================================================================== --- head/sys/dev/ofw/ofw_bus.h (revision 256993) +++ head/sys/dev/ofw/ofw_bus.h (revision 256994) @@ -1,73 +1,85 @@ /*- * Copyright (c) 2001, 2003 by Thomas Moestl * Copyright (c) 2004 by Marius Strobl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_OFW_OFW_BUS_H_ #define _DEV_OFW_OFW_BUS_H_ #include #include #include "ofw_bus_if.h" static __inline const char * ofw_bus_get_compat(device_t dev) { return (OFW_BUS_GET_COMPAT(device_get_parent(dev), dev)); } static __inline const char * ofw_bus_get_model(device_t dev) { return (OFW_BUS_GET_MODEL(device_get_parent(dev), dev)); } static __inline const char * ofw_bus_get_name(device_t dev) { return (OFW_BUS_GET_NAME(device_get_parent(dev), dev)); } static __inline phandle_t ofw_bus_get_node(device_t dev) { return (OFW_BUS_GET_NODE(device_get_parent(dev), dev)); } static __inline const char * ofw_bus_get_type(device_t dev) { return (OFW_BUS_GET_TYPE(device_get_parent(dev), dev)); } +static __inline int +ofw_bus_map_intr(device_t dev, phandle_t iparent, int irq) +{ + return (OFW_BUS_MAP_INTR(dev, dev, iparent, irq)); +} + +static __inline int +ofw_bus_config_intr(device_t dev, int irq, int sense) +{ + return (OFW_BUS_CONFIG_INTR(dev, dev, irq, sense)); +} + #endif /* !_DEV_OFW_OFW_BUS_H_ */ Index: head/sys/dev/ofw/ofw_bus_if.m =================================================================== --- head/sys/dev/ofw/ofw_bus_if.m (revision 256993) +++ head/sys/dev/ofw/ofw_bus_if.m (revision 256994) @@ -1,145 +1,191 @@ #- # Copyright (c) 2001, 2003 by Thomas Moestl # Copyright (c) 2004, 2005 by Marius Strobl # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ # Interface for retrieving the package handle and a subset, namely # 'compatible', 'device_type', 'model' and 'name', of the standard # properties of a device on an Open Firmware assisted bus for use # in device drivers. The rest of the standard properties, 'address', # 'interrupts', 'reg' and 'status', are not covered by this interface # as they are expected to be only of interest in the respective bus # driver. #include #include INTERFACE ofw_bus; HEADER { struct ofw_bus_devinfo { phandle_t obd_node; char *obd_compat; char *obd_model; char *obd_name; char *obd_type; }; }; CODE { static ofw_bus_get_devinfo_t ofw_bus_default_get_devinfo; static ofw_bus_get_compat_t ofw_bus_default_get_compat; static ofw_bus_get_model_t ofw_bus_default_get_model; static ofw_bus_get_name_t ofw_bus_default_get_name; static ofw_bus_get_node_t ofw_bus_default_get_node; static ofw_bus_get_type_t ofw_bus_default_get_type; + static ofw_bus_map_intr_t ofw_bus_default_map_intr; + static ofw_bus_config_intr_t ofw_bus_default_config_intr; static const struct ofw_bus_devinfo * ofw_bus_default_get_devinfo(device_t bus, device_t dev) { return (NULL); } static const char * ofw_bus_default_get_compat(device_t bus, device_t dev) { return (NULL); } static const char * ofw_bus_default_get_model(device_t bus, device_t dev) { return (NULL); } static const char * ofw_bus_default_get_name(device_t bus, device_t dev) { return (NULL); } static phandle_t ofw_bus_default_get_node(device_t bus, device_t dev) { return (-1); } static const char * ofw_bus_default_get_type(device_t bus, device_t dev) { return (NULL); } + + int + ofw_bus_default_map_intr(device_t bus, device_t dev, phandle_t iparent, + int irq) + { + /* Propagate up the bus hierarchy until someone handles it. */ + if (device_get_parent(bus) != NULL) + return OFW_BUS_MAP_INTR(device_get_parent(bus), dev, + iparent, irq); + + /* If that fails, then assume a one-domain system */ + return (irq); + } + + int + ofw_bus_default_config_intr(device_t bus, device_t dev, int irq, + int sense) + { + /* Propagate up the bus hierarchy until someone handles it. */ + if (device_get_parent(bus) != NULL) + return OFW_BUS_CONFIG_INTR(device_get_parent(bus), dev, + irq, sense); + + return (ENXIO); + } }; # Get the ofw_bus_devinfo struct for the device dev on the bus. Used for bus # drivers which use the generic methods in ofw_bus_subr.c to implement the # reset of this interface. The default method will return NULL, which means # there is no such struct associated with the device. METHOD const struct ofw_bus_devinfo * get_devinfo { device_t bus; device_t dev; } DEFAULT ofw_bus_default_get_devinfo; # Get the alternate firmware name for the device dev on the bus. The default # method will return NULL, which means the device doesn't have such a property. METHOD const char * get_compat { device_t bus; device_t dev; } DEFAULT ofw_bus_default_get_compat; # Get the firmware model name for the device dev on the bus. The default method # will return NULL, which means the device doesn't have such a property. METHOD const char * get_model { device_t bus; device_t dev; } DEFAULT ofw_bus_default_get_model; # Get the firmware name for the device dev on the bus. The default method will # return NULL, which means the device doesn't have such a property. METHOD const char * get_name { device_t bus; device_t dev; } DEFAULT ofw_bus_default_get_name; # Get the firmware node for the device dev on the bus. The default method will # return 0, which signals that there is no such node. METHOD phandle_t get_node { device_t bus; device_t dev; } DEFAULT ofw_bus_default_get_node; # Get the firmware device type for the device dev on the bus. The default # method will return NULL, which means the device doesn't have such a property. METHOD const char * get_type { device_t bus; device_t dev; } DEFAULT ofw_bus_default_get_type; + +# Map an (interrupt parent, IRQ) pair to a unique system-wide interrupt number. +METHOD int map_intr { + device_t bus; + device_t dev; + phandle_t iparent; + int irq; +} DEFAULT ofw_bus_default_map_intr; + +# Configure an interrupt using the device-tree encoded sense key (the second +# value in the interrupts property if interrupt-cells is 2). IRQ should be +# encoded as from ofw_bus_map_intr(). +METHOD int config_intr { + device_t bus; + device_t dev; + int irq; + int sense; +} DEFAULT ofw_bus_default_config_intr; + Index: head/sys/powerpc/ofw/ofw_pci.c =================================================================== --- head/sys/powerpc/ofw/ofw_pci.c (revision 256993) +++ head/sys/powerpc/ofw/ofw_pci.c (revision 256994) @@ -1,558 +1,558 @@ /*- * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" /* * Bus interface. */ static int ofw_pci_read_ivar(device_t, device_t, int, uintptr_t *); static struct resource * ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int ofw_pci_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int ofw_pci_adjust_resource(device_t bus, device_t child, int type, struct resource *res, u_long start, u_long end); /* * pcib interface. */ static int ofw_pci_maxslots(device_t); static int ofw_pci_route_interrupt(device_t, device_t, int); /* * ofw_bus interface */ static phandle_t ofw_pci_get_node(device_t bus, device_t dev); /* * local methods */ static int ofw_pci_nranges(phandle_t node); static int ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges); /* * Driver methods. */ static device_method_t ofw_pci_methods[] = { /* Device interface */ DEVMETHOD(device_attach, ofw_pci_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource), DEVMETHOD(bus_release_resource, ofw_pci_release_resource), DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource), DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource), DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource), /* pcib interface */ DEVMETHOD(pcib_maxslots, ofw_pci_maxslots), DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pci_get_node), DEVMETHOD_END }; DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0); int ofw_pci_init(device_t dev) { struct ofw_pci_softc *sc; phandle_t node; u_int32_t busrange[2]; struct ofw_pci_range *rp; int error; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); sc->sc_initialized = 1; if (OF_getprop(node, "reg", &sc->sc_pcir, sizeof(sc->sc_pcir)) == -1) return (ENXIO); if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) busrange[0] = 0; sc->sc_dev = dev; sc->sc_node = node; sc->sc_bus = busrange[0]; if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) { phandle_t c; int n, i; sc->sc_nrange = 0; for (c = OF_child(node); c != 0; c = OF_peer(c)) { n = ofw_pci_nranges(c); if (n > 0) sc->sc_nrange += n; } if (sc->sc_nrange == 0) return (ENXIO); sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), M_DEVBUF, M_WAITOK); i = 0; for (c = OF_child(node); c != 0; c = OF_peer(c)) { n = ofw_pci_fill_ranges(c, &sc->sc_range[i]); if (n > 0) i += n; } KASSERT(i == sc->sc_nrange, ("range count mismatch")); } else { sc->sc_nrange = ofw_pci_nranges(node); if (sc->sc_nrange <= 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), M_DEVBUF, M_WAITOK); ofw_pci_fill_ranges(node, sc->sc_range); } sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "PCI I/O Ports"; error = rman_init(&sc->sc_io_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "PCI Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && rp->pci_hi != 0; rp++) { error = 0; switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: break; case OFW_PCI_PHYS_HI_SPACE_IO: error = rman_manage_region(&sc->sc_io_rman, rp->pci, rp->pci + rp->size - 1); break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: error = rman_manage_region(&sc->sc_mem_rman, rp->pci, rp->pci + rp->size - 1); break; } if (error) { device_printf(dev, "rman_manage_region(%x, %#jx, %#jx) failed. " "error = %d\n", rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK, rp->pci, rp->pci + rp->size - 1, error); return (error); } } ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); return (error); } int ofw_pci_attach(device_t dev) { struct ofw_pci_softc *sc; int error; sc = device_get_softc(dev); if (!sc->sc_initialized) { error = ofw_pci_init(dev); if (error) return (error); } device_add_child(dev, "pci", device_get_unit(dev)); return (bus_generic_attach(dev)); } static int ofw_pci_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int ofw_pci_route_interrupt(device_t bus, device_t dev, int pin) { struct ofw_pci_softc *sc; struct ofw_pci_register reg; uint32_t pintr, mintr; phandle_t iparent; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bus); pintr = pin; /* Fabricate imap information in case this isn't an OFW device */ bzero(®, sizeof(reg)); reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), &iparent, maskbuf)) - return (MAP_IRQ(iparent, mintr)); + return (ofw_bus_map_intr(dev, iparent, mintr)); /* Maybe it's a real interrupt, not an intpin */ if (pin > 4) return (pin); device_printf(bus, "could not route pin %d for device %d.%d\n", pin, pci_get_slot(dev), pci_get_function(dev)); return (PCI_INVALID_IRQ); } static int ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct ofw_pci_softc *sc; sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = device_get_unit(dev); return (0); case PCIB_IVAR_BUS: *result = sc->sc_bus; return (0); } return (ENOENT); } static struct resource * ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ofw_pci_softc *sc; struct resource *rv; struct rman *rm; int needactivate; needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; sc = device_get_softc(bus); switch (type) { case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; break; case SYS_RES_IRQ: return (bus_alloc_resource(bus, type, rid, start, end, count, flags)); default: device_printf(bus, "unknown resource request from %s\n", device_get_nameunit(child)); return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource for %s\n", device_get_nameunit(child)); return (NULL); } rman_set_rid(rv, *rid); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv) != 0) { device_printf(bus, "failed to activate resource for %s\n", device_get_nameunit(child)); rman_release_resource(rv); return (NULL); } } return (rv); } static int ofw_pci_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (rman_get_flags(res) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, res); if (error) return error; } return (rman_release_resource(res)); } static int ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct ofw_pci_softc *sc; void *p; sc = device_get_softc(bus); if (type == SYS_RES_IRQ) { return (bus_activate_resource(bus, type, rid, res)); } if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { struct ofw_pci_range *rp; vm_offset_t start; int space; start = (vm_offset_t)rman_get_start(res); /* * Map this through the ranges list */ for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && rp->pci_hi != 0; rp++) { if (start < rp->pci || start >= rp->pci + rp->size) continue; switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_IO: space = SYS_RES_IOPORT; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: space = SYS_RES_MEMORY; break; default: space = -1; } if (type == space) { start += (rp->host - rp->pci); break; } } if (bootverbose) printf("ofw_pci mapdev: start %zx, len %ld\n", start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t)rman_get_size(res)); if (p == NULL) return (ENOMEM); rman_set_virtual(res, p); rman_set_bustag(res, &bs_le_tag); rman_set_bushandle(res, (u_long)p); } return (rman_activate_resource(res)); } static int ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { /* * If this is a memory resource, unmap it. */ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { u_int32_t psize; psize = rman_get_size(res); pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); } return (rman_deactivate_resource(res)); } static int ofw_pci_adjust_resource(device_t bus, device_t child, int type, struct resource *res, u_long start, u_long end) { struct rman *rm = NULL; struct ofw_pci_softc *sc = device_get_softc(bus); KASSERT(!(rman_get_flags(res) & RF_ACTIVE), ("active resources cannot be adjusted")); if (rman_get_flags(res) & RF_ACTIVE) return (EINVAL); switch (type) { case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; break; default: return (ENXIO); } if (!rman_is_region_manager(res, rm)) return (EINVAL); return (rman_adjust_resource(res, start, end)); } static phandle_t ofw_pci_get_node(device_t bus, device_t dev) { struct ofw_pci_softc *sc; sc = device_get_softc(bus); /* We only have one child, the PCI bus, which needs our own node. */ return (sc->sc_node); } static int ofw_pci_nranges(phandle_t node) { int host_address_cells = 1, pci_address_cells = 3, size_cells = 2; ssize_t nbase_ranges; OF_getprop(OF_parent(node), "#address-cells", &host_address_cells, sizeof(host_address_cells)); OF_getprop(node, "#address-cells", &pci_address_cells, sizeof(pci_address_cells)); OF_getprop(node, "#size-cells", &size_cells, sizeof(size_cells)); nbase_ranges = OF_getproplen(node, "ranges"); if (nbase_ranges <= 0) return (-1); return (nbase_ranges / sizeof(cell_t) / (pci_address_cells + host_address_cells + size_cells)); } static int ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges) { int host_address_cells = 1, pci_address_cells = 3, size_cells = 2; cell_t *base_ranges; ssize_t nbase_ranges; int nranges; int i, j, k; OF_getprop(OF_parent(node), "#address-cells", &host_address_cells, sizeof(host_address_cells)); OF_getprop(node, "#address-cells", &pci_address_cells, sizeof(pci_address_cells)); OF_getprop(node, "#size-cells", &size_cells, sizeof(size_cells)); nbase_ranges = OF_getproplen(node, "ranges"); if (nbase_ranges <= 0) return (-1); nranges = nbase_ranges / sizeof(cell_t) / (pci_address_cells + host_address_cells + size_cells); base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); OF_getprop(node, "ranges", base_ranges, nbase_ranges); for (i = 0, j = 0; i < nranges; i++) { ranges[i].pci_hi = base_ranges[j++]; ranges[i].pci = 0; for (k = 0; k < pci_address_cells - 1; k++) { ranges[i].pci <<= 32; ranges[i].pci |= base_ranges[j++]; } ranges[i].host = 0; for (k = 0; k < host_address_cells; k++) { ranges[i].host <<= 32; ranges[i].host |= base_ranges[j++]; } ranges[i].size = 0; for (k = 0; k < size_cells; k++) { ranges[i].size <<= 32; ranges[i].size |= base_ranges[j++]; } } free(base_ranges, M_DEVBUF); return (nranges); } Index: head/sys/powerpc/ofw/ofw_pcib_pci.c =================================================================== --- head/sys/powerpc/ofw/ofw_pcib_pci.c (revision 256993) +++ head/sys/powerpc/ofw/ofw_pcib_pci.c (revision 256994) @@ -1,172 +1,172 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" static int ofw_pcib_pci_probe(device_t bus); static int ofw_pcib_pci_attach(device_t bus); static phandle_t ofw_pcib_pci_get_node(device_t bus, device_t dev); static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin); static device_method_t ofw_pcib_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_pcib_pci_probe), DEVMETHOD(device_attach, ofw_pcib_pci_attach), /* pcib interface */ DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node), DEVMETHOD_END }; static devclass_t pcib_devclass; struct ofw_pcib_softc { /* * This is here so that we can use pci bridge methods, too - the * generic routines only need the dev, secbus and subbus members * filled. */ struct pcib_softc ops_pcib_sc; phandle_t ops_node; struct ofw_bus_iinfo ops_iinfo; }; DEFINE_CLASS_1(pcib, ofw_pcib_pci_driver, ofw_pcib_pci_methods, sizeof(struct ofw_pcib_softc), pcib_driver); DRIVER_MODULE(ofw_pcib, pci, ofw_pcib_pci_driver, pcib_devclass, 0, 0); static int ofw_pcib_pci_probe(device_t dev) { if ((pci_get_class(dev) != PCIC_BRIDGE) || (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) { return (ENXIO); } if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW PCI-PCI bridge"); return (0); } static int ofw_pcib_pci_attach(device_t dev) { struct ofw_pcib_softc *sc; sc = device_get_softc(dev); sc->ops_pcib_sc.dev = dev; sc->ops_node = ofw_bus_get_node(dev); ofw_bus_setup_iinfo(sc->ops_node, &sc->ops_iinfo, sizeof(cell_t)); pcib_attach_common(dev); device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } static phandle_t ofw_pcib_pci_get_node(device_t bridge, device_t dev) { /* We have only one child, the PCI bus, so pass it our node */ return (ofw_bus_get_node(bridge)); } static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin) { struct ofw_pcib_softc *sc; struct ofw_bus_iinfo *ii; struct ofw_pci_register reg; cell_t pintr, mintr; phandle_t iparent; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bridge); ii = &sc->ops_iinfo; if (ii->opi_imapsz > 0) { pintr = intpin; /* Fabricate imap information if this isn't an OFW device */ bzero(®, sizeof(reg)); reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), &iparent, maskbuf)) { /* * If we've found a mapping, return it and don't map * it again on higher levels - that causes problems * in some cases, and never seems to be required. */ - return (MAP_IRQ(iparent, mintr)); + return (ofw_bus_map_intr(dev, iparent, mintr)); } } else if (intpin >= 1 && intpin <= 4) { /* * When an interrupt map is missing, we need to do the * standard PCI swizzle and continue mapping at the parent. */ return (pcib_route_interrupt(bridge, dev, intpin)); } return (PCIB_ROUTE_INTERRUPT(device_get_parent(device_get_parent( bridge)), bridge, intpin)); } Index: head/sys/powerpc/ofw/ofw_pcibus.c =================================================================== --- head/sys/powerpc/ofw/ofw_pcibus.c (revision 256993) +++ head/sys/powerpc/ofw/ofw_pcibus.c (revision 256994) @@ -1,378 +1,377 @@ /*- * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * Copyright (c) 2003, Thomas Moestl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "pci_if.h" typedef uint32_t ofw_pci_intr_t; /* Methods */ static device_probe_t ofw_pcibus_probe; static device_attach_t ofw_pcibus_attach; static pci_assign_interrupt_t ofw_pcibus_assign_interrupt; static ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo; static int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen); static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno); static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno); static device_method_t ofw_pcibus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_pcibus_probe), DEVMETHOD(device_attach, ofw_pcibus_attach), /* Bus interface */ DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method), /* PCI interface */ DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, ofw_pcibus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), { 0, 0 } }; struct ofw_pcibus_devinfo { struct pci_devinfo opd_dinfo; struct ofw_bus_devinfo opd_obdinfo; }; static devclass_t pci_devclass; DEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods, sizeof(struct pci_softc), pci_driver); DRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0); MODULE_VERSION(ofw_pcibus, 1); MODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1); static int ofw_devices_only = 0; TUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only); static int ofw_pcibus_probe(device_t dev) { if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW PCI bus"); return (BUS_PROBE_DEFAULT); } static int ofw_pcibus_attach(device_t dev) { u_int busno, domain; int error; error = pci_attach_common(dev); if (error) return (error); domain = pcib_get_domain(dev); busno = pcib_get_bus(dev); /* * Attach those children represented in the device tree. */ ofw_pcibus_enum_devtree(dev, domain, busno); /* * We now attach any laggard devices. FDT, for instance, allows * the device tree to enumerate only some PCI devices. Apple's * OF device tree on some Grackle-based hardware can also miss * functions on multi-function cards. */ if (!ofw_devices_only) ofw_pcibus_enum_bus(dev, domain, busno); return (bus_generic_attach(dev)); } static void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno) { device_t pcib; struct ofw_pci_register pcir; struct ofw_pcibus_devinfo *dinfo; phandle_t node, child; u_int func, slot; int intline; pcib = device_get_parent(dev); node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) continue; slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* Some OFW device trees contain dupes. */ if (pci_find_dbsf(domain, busno, slot, func) != NULL) continue; /* * The preset in the intline register is usually bogus. Reset * it such that the PCI code will reroute the interrupt if * needed. */ intline = PCI_INVALID_IRQ; if (OF_getproplen(child, "interrupts") > 0) intline = 0; PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE, intline, 1); /* * Now set up the PCI and OFW bus layer devinfo and add it * to the PCI bus. */ dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, domain, busno, slot, func, sizeof(*dinfo)); if (dinfo == NULL) continue; if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != 0) { pci_freecfg((struct pci_devinfo *)dinfo); continue; } pci_add_child(dev, (struct pci_devinfo *)dinfo); /* * Some devices don't have an intpin set, but do have * interrupts. These are fully specified, and set in the * interrupts property, so add that value to the device's * resource list. */ if (dinfo->opd_dinfo.cfg.intpin == 0) { ofw_pci_intr_t intr[2]; phandle_t iparent; int icells; if (OF_getprop(child, "interrupts", &intr, sizeof(intr)) > 0) { iparent = 0; icells = 1; OF_getprop(child, "interrupt-parent", &iparent, sizeof(iparent)); if (iparent != 0) { OF_getprop(OF_xref_phandle(iparent), "#interrupt-cells", &icells, sizeof(icells)); - intr[0] = MAP_IRQ(iparent, intr[0]); + intr[0] = ofw_bus_map_intr(dev, iparent, + intr[0]); } - if (iparent != 0 && icells > 1) { - powerpc_config_intr(intr[0], - (intr[1] & 1) ? INTR_TRIGGER_LEVEL : - INTR_TRIGGER_EDGE, - INTR_POLARITY_LOW); - } + if (iparent != 0 && icells > 1) + ofw_bus_config_intr(dev, intr[0], + intr[1]); resource_list_add(&dinfo->opd_dinfo.resources, SYS_RES_IRQ, 0, intr[0], intr[0], 1); } } } } /* * The following is an almost exact clone of pci_add_children(), with the * addition that it (a) will not add children that have already been added, * and (b) will set up the OFW devinfo to point to invalid values. This is * to handle non-enumerated PCI children as exist in FDT and on the second * function of the Rage 128 in my Blue & White G3. */ static void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno) { device_t pcib; struct ofw_pcibus_devinfo *dinfo; int maxslots; int s, f, pcifunchigh; uint8_t hdrtype; pcib = device_get_parent(dev); maxslots = PCIB_MAXSLOTS(pcib); for (s = 0; s <= maxslots; s++) { pcifunchigh = 0; f = 0; DELAY(1); hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1); if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) continue; if (hdrtype & PCIM_MFDEV) pcifunchigh = PCI_FUNCMAX; for (f = 0; f <= pcifunchigh; f++) { /* Filter devices we have already added */ if (pci_find_dbsf(domain, busno, s, f) != NULL) continue; dinfo = (struct ofw_pcibus_devinfo *)pci_read_device( pcib, domain, busno, s, f, sizeof(*dinfo)); if (dinfo == NULL) continue; dinfo->opd_obdinfo.obd_node = -1; dinfo->opd_obdinfo.obd_name = NULL; dinfo->opd_obdinfo.obd_compat = NULL; dinfo->opd_obdinfo.obd_type = NULL; dinfo->opd_obdinfo.obd_model = NULL; /* * For non OFW-devices, don't believe 0 * for an interrupt. */ if (dinfo->opd_dinfo.cfg.intline == 0) { dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ; PCIB_WRITE_CONFIG(pcib, busno, s, f, PCIR_INTLINE, PCI_INVALID_IRQ, 1); } pci_add_child(dev, (struct pci_devinfo *)dinfo); } } } static int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, size_t buflen) { pci_child_pnpinfo_str_method(cbdev, child, buf, buflen); if (ofw_bus_get_node(child) != -1) { strlcat(buf, " ", buflen); /* Separate info */ ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen); } return (0); } static int ofw_pcibus_assign_interrupt(device_t dev, device_t child) { ofw_pci_intr_t intr; phandle_t node, iparent; int isz; node = ofw_bus_get_node(child); if (node == -1) { /* Non-firmware enumerated child, use standard routing */ intr = pci_get_intpin(child); return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr)); } /* * Try to determine the node's interrupt parent so we know which * PIC to use. */ iparent = -1; if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0) iparent = -1; /* * Any AAPL,interrupts property gets priority and is * fully specified (i.e. does not need routing) */ isz = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr)); if (isz == sizeof(intr)) - return ((iparent == -1) ? intr : MAP_IRQ(iparent, intr)); + return ((iparent == -1) ? intr : ofw_bus_map_intr(dev, iparent, + intr)); isz = OF_getprop(node, "interrupts", &intr, sizeof(intr)); if (isz == sizeof(intr)) { if (iparent != -1) - intr = MAP_IRQ(iparent, intr); + intr = ofw_bus_map_intr(dev, iparent, intr); } else { /* No property: our best guess is the intpin. */ intr = pci_get_intpin(child); } /* * If we got intr from a property, it may or may not be an intpin. * For on-board devices, it frequently is not, and is completely out * of the valid intpin range. For PCI slots, it hopefully is, * otherwise we will have trouble interfacing with non-OFW buses * such as cardbus. * Since we cannot tell which it is without violating layering, we * will always use the route_interrupt method, and treat exceptions * on the level they become apparent. */ return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr)); } static const struct ofw_bus_devinfo * ofw_pcibus_get_devinfo(device_t bus, device_t dev) { struct ofw_pcibus_devinfo *dinfo; dinfo = device_get_ivars(dev); return (&dinfo->opd_obdinfo); } Index: head/sys/powerpc/powerpc/nexus.c =================================================================== --- head/sys/powerpc/powerpc/nexus.c (revision 256993) +++ head/sys/powerpc/powerpc/nexus.c (revision 256994) @@ -1,639 +1,655 @@ /*- * Copyright 1998 Massachusetts Institute of Technology * Copyright 2001 by Thomas Moestl . * Copyright 2006 by Marius Strobl . * All rights reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The nexus (which is a pseudo-bus actually) iterates over the nodes that * hang from the Open Firmware root node and adds them as devices to this bus * (except some special nodes which are excluded) so that drivers can be * attached to them. * * Additionally, interrupt setup/teardown and some resource management are * done at this level. * * Maybe this code should get into dev/ofw to some extent, as some of it should * work for all Open Firmware based machines... */ struct nexus_devinfo { struct ofw_bus_devinfo ndi_obdinfo; struct resource_list ndi_rl; }; struct nexus_softc { uint32_t acells, scells; struct rman sc_intr_rman; struct rman sc_mem_rman; }; static device_probe_t nexus_probe; static device_attach_t nexus_attach; static bus_print_child_t nexus_print_child; static bus_add_child_t nexus_add_child; static bus_probe_nomatch_t nexus_probe_nomatch; static bus_setup_intr_t nexus_setup_intr; static bus_teardown_intr_t nexus_teardown_intr; static bus_alloc_resource_t nexus_alloc_resource; static bus_activate_resource_t nexus_activate_resource; static bus_deactivate_resource_t nexus_deactivate_resource; static bus_adjust_resource_t nexus_adjust_resource; static bus_release_resource_t nexus_release_resource; static bus_get_resource_list_t nexus_get_resource_list; #ifdef SMP static bus_bind_intr_t nexus_bind_intr; #endif static bus_config_intr_t nexus_config_intr; +static ofw_bus_map_intr_t nexus_ofw_map_intr; +static ofw_bus_config_intr_t nexus_ofw_config_intr; static ofw_bus_get_devinfo_t nexus_get_devinfo; static int nexus_inlist(const char *, const char *const *); static struct nexus_devinfo * nexus_setup_dinfo(device_t, phandle_t); static void nexus_destroy_dinfo(struct nexus_devinfo *); static int nexus_print_res(struct nexus_devinfo *); static device_method_t nexus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nexus_probe), DEVMETHOD(device_attach, nexus_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, nexus_print_child), DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_add_child, nexus_add_child), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), DEVMETHOD(bus_activate_resource, nexus_activate_resource), DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), DEVMETHOD(bus_adjust_resource, nexus_adjust_resource), DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_get_resource_list, nexus_get_resource_list), #ifdef SMP DEVMETHOD(bus_bind_intr, nexus_bind_intr), #endif DEVMETHOD(bus_config_intr, nexus_config_intr), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, nexus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr), + DEVMETHOD(ofw_bus_config_intr, nexus_ofw_config_intr), DEVMETHOD_END }; static devclass_t nexus_devclass; DEFINE_CLASS_0(nexus, nexus_driver, nexus_methods, sizeof(struct nexus_softc)); EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0, BUS_PASS_BUS); MODULE_VERSION(nexus, 1); static const char *const nexus_excl_name[] = { "FJSV,system", "aliases", "associations", "chosen", "cmp", "counter-timer", /* No separate device; handled by psycho/sbus */ "failsafe", "memory", "openprom", "options", "packages", "physical-memory", "rsc", "sgcn", "todsg", "virtual-memory", NULL }; static const char *const nexus_excl_type[] = { "core", "cpu", NULL }; extern struct bus_space_tag nexus_bustag; extern struct bus_dma_tag nexus_dmatag; static int nexus_inlist(const char *name, const char *const *list) { int i; if (name == NULL) return (0); for (i = 0; list[i] != NULL; i++) if (strcmp(name, list[i]) == 0) return (1); return (0); } #define NEXUS_EXCLUDED(name, type) \ (nexus_inlist((name), nexus_excl_name) || \ ((type) != NULL && nexus_inlist((type), nexus_excl_type))) static int nexus_probe(device_t dev) { /* Nexus does always match. */ device_set_desc(dev, "Open Firmware Nexus device"); return (0); } static int nexus_attach(device_t dev) { struct nexus_devinfo *ndi; struct nexus_softc *sc; device_t cdev; phandle_t node; sc = device_get_softc(dev); if (strcmp(device_get_name(device_get_parent(dev)), "root") == 0) { node = OF_peer(0); sc->sc_intr_rman.rm_type = RMAN_ARRAY; sc->sc_intr_rman.rm_descr = "Interrupts"; sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "Device Memory"; if (rman_init(&sc->sc_intr_rman) != 0 || rman_init(&sc->sc_mem_rman) != 0 || rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0 || rman_manage_region(&sc->sc_mem_rman, 0, BUS_SPACE_MAXADDR) != 0) panic("%s: failed to set up rmans.", __func__); } else node = ofw_bus_get_node(dev); /* * Allow devices to identify. */ bus_generic_probe(dev); /* * If no Open Firmware, bail early */ if (node == -1) return (bus_generic_attach(dev)); /* * Some important numbers */ sc->acells = 2; OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); sc->scells = 1; OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); /* * Now walk the OFW tree and attach top-level devices. */ for (node = OF_child(node); node > 0; node = OF_peer(node)) { if ((ndi = nexus_setup_dinfo(dev, node)) == NULL) continue; cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", ndi->ndi_obdinfo.obd_name); nexus_destroy_dinfo(ndi); continue; } device_set_ivars(cdev, ndi); } return (bus_generic_attach(dev)); } static device_t nexus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t cdev; struct nexus_devinfo *ndi; cdev = device_add_child_ordered(dev, order, name, unit); if (cdev == NULL) return (NULL); ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); ndi->ndi_obdinfo.obd_node = -1; resource_list_init(&ndi->ndi_rl); device_set_ivars(cdev, ndi); return (cdev); } static int nexus_print_child(device_t bus, device_t child) { int rv; rv = bus_print_child_header(bus, child); rv += nexus_print_res(device_get_ivars(child)); rv += bus_print_child_footer(bus, child); return (rv); } static void nexus_probe_nomatch(device_t bus, device_t child) { const char *name, *type; if (!bootverbose) return; name = ofw_bus_get_name(child); type = ofw_bus_get_type(child); device_printf(bus, "<%s>", name != NULL ? name : "unknown"); nexus_print_res(device_get_ivars(child)); printf(" type %s (no driver attached)\n", type != NULL ? type : "unknown"); } static int nexus_setup_intr(device_t bus __unused, device_t child, struct resource *r, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { int error; if (r == NULL) panic("%s: NULL interrupt resource!", __func__); if ((rman_get_flags(r) & RF_SHAREABLE) == 0) flags |= INTR_EXCL; /* We depend here on rman_activate_resource() being idempotent. */ error = rman_activate_resource(r); if (error) return (error); error = powerpc_setup_intr(device_get_nameunit(child), rman_get_start(r), filt, intr, arg, flags, cookiep); return (error); } static int nexus_teardown_intr(device_t bus __unused, device_t child __unused, struct resource *r, void *ih) { if (r == NULL) return (EINVAL); return (powerpc_teardown_intr(ih)); } #ifdef SMP static int nexus_bind_intr(device_t bus __unused, device_t child __unused, struct resource *r, int cpu) { return (powerpc_bind_intr(rman_get_start(r), cpu)); } #endif static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { return (powerpc_config_intr(irq, trig, pol)); } +static int +nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int irq) +{ + return (MAP_IRQ(iparent, irq)); +} + +static int +nexus_ofw_config_intr(device_t dev, device_t child, int irq, int sense) +{ + + return (bus_generic_config_intr(child, irq, (sense & 1) ? + INTR_TRIGGER_LEVEL : INTR_TRIGGER_EDGE, + INTR_POLARITY_LOW)); +} + static struct resource * nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct nexus_softc *sc; struct rman *rm; struct resource *rv; struct resource_list_entry *rle; device_t nexus; int isdefault, passthrough; isdefault = (start == 0UL && end == ~0UL); passthrough = (device_get_parent(child) != bus); nexus = bus; while (strcmp(device_get_name(device_get_parent(nexus)), "root") != 0) nexus = device_get_parent(nexus); sc = device_get_softc(nexus); rle = NULL; if (!passthrough && isdefault) { rle = resource_list_find(BUS_GET_RESOURCE_LIST(bus, child), type, *rid); if (rle == NULL) return (NULL); if (rle->res != NULL) panic("%s: resource entry is busy", __func__); start = rle->start; count = ulmax(count, rle->count); end = ulmax(rle->end, start + count - 1); } switch (type) { case SYS_RES_IRQ: rm = &sc->sc_intr_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: return (NULL); } rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(child, type, *rid, rv) != 0) { rman_release_resource(rv); return (NULL); } if (!passthrough && rle != NULL) { rle->res = rv; rle->start = rman_get_start(rv); rle->end = rman_get_end(rv); rle->count = rle->end - rle->start + 1; } return (rv); } static int nexus_activate_resource(device_t bus __unused, device_t child __unused, int type, int rid __unused, struct resource *r) { if (type == SYS_RES_MEMORY) { vm_offset_t start; void *p; start = (vm_offset_t) rman_get_start(r); if (bootverbose) printf("nexus mapdev: start %zx, len %ld\n", start, rman_get_size(r)); p = pmap_mapdev(start, (vm_size_t) rman_get_size(r)); if (p == NULL) return (ENOMEM); rman_set_virtual(r, p); rman_set_bustag(r, &bs_be_tag); rman_set_bushandle(r, (u_long)p); } return (rman_activate_resource(r)); } static int nexus_deactivate_resource(device_t bus __unused, device_t child __unused, int type __unused, int rid __unused, struct resource *r) { /* * If this is a memory resource, unmap it. */ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { bus_size_t psize; psize = rman_get_size(r); pmap_unmapdev((vm_offset_t)rman_get_virtual(r), psize); } return (rman_deactivate_resource(r)); } static int nexus_adjust_resource(device_t bus, device_t child __unused, int type, struct resource *r, u_long start, u_long end) { struct nexus_softc *sc; struct rman *rm; device_t nexus; nexus = bus; while (strcmp(device_get_name(device_get_parent(nexus)), "root") != 0) nexus = device_get_parent(nexus); sc = device_get_softc(nexus); switch (type) { case SYS_RES_IRQ: rm = &sc->sc_intr_rman; break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; default: return (EINVAL); } if (rm == NULL) return (ENXIO); if (rman_is_region_manager(r, rm) == 0) return (EINVAL); return (rman_adjust_resource(r, start, end)); } static int nexus_release_resource(device_t bus __unused, device_t child, int type, int rid, struct resource *r) { int error; if ((rman_get_flags(r) & RF_ACTIVE) != 0) { error = bus_deactivate_resource(child, type, rid, r); if (error) return (error); } return (rman_release_resource(r)); } static struct resource_list * nexus_get_resource_list(device_t bus __unused, device_t child) { struct nexus_devinfo *ndi; ndi = device_get_ivars(child); return (&ndi->ndi_rl); } static const struct ofw_bus_devinfo * nexus_get_devinfo(device_t bus __unused, device_t child) { struct nexus_devinfo *ndi; ndi = device_get_ivars(child); return (&ndi->ndi_obdinfo); } static struct nexus_devinfo * nexus_setup_dinfo(device_t dev, phandle_t node) { struct nexus_softc *sc; struct nexus_devinfo *ndi; uint32_t *reg, *intr, icells; uint64_t phys, size; phandle_t iparent; int i, j; int nintr; int nreg; sc = device_get_softc(dev); ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&ndi->ndi_obdinfo, node) != 0) { free(ndi, M_DEVBUF); return (NULL); } if (NEXUS_EXCLUDED(ndi->ndi_obdinfo.obd_name, ndi->ndi_obdinfo.obd_type)) { ofw_bus_gen_destroy_devinfo(&ndi->ndi_obdinfo); free(ndi, M_DEVBUF); return (NULL); } resource_list_init(&ndi->ndi_rl); nreg = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); if (nreg == -1) nreg = 0; if (nreg % (sc->acells + sc->scells) != 0) { if (bootverbose) device_printf(dev, "Malformed reg property on <%s>\n", ndi->ndi_obdinfo.obd_name); nreg = 0; } for (i = 0; i < nreg; i += sc->acells + sc->scells) { phys = size = 0; for (j = 0; j < sc->acells; j++) { phys <<= 32; phys |= reg[i + j]; } for (j = 0; j < sc->scells; j++) { size <<= 32; size |= reg[i + sc->acells + j]; } /* Skip the dummy reg property of glue devices like ssm(4). */ if (size != 0) resource_list_add(&ndi->ndi_rl, SYS_RES_MEMORY, i, phys, phys + size - 1, size); } free(reg, M_OFWPROP); nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), (void **)&intr); if (nintr > 0) { iparent = 0; OF_searchprop(node, "interrupt-parent", &iparent, sizeof(iparent)); OF_searchprop(iparent, "#interrupt-cells", &icells, sizeof(icells)); for (i = 0; i < nintr; i+= icells) { - intr[i] = MAP_IRQ(iparent, intr[i]); + intr[i] = ofw_bus_map_intr(dev, iparent, intr[i]); resource_list_add(&ndi->ndi_rl, SYS_RES_IRQ, i, intr[i], intr[i], 1); - if (icells > 1) { - powerpc_config_intr(intr[i], (intr[i+1] & 1) ? - INTR_TRIGGER_LEVEL : INTR_TRIGGER_EDGE, - INTR_POLARITY_LOW); - } + if (icells > 1) + ofw_bus_config_intr(dev, intr[i], intr[i+1]); } free(intr, M_OFWPROP); } return (ndi); } static void nexus_destroy_dinfo(struct nexus_devinfo *ndi) { resource_list_free(&ndi->ndi_rl); ofw_bus_gen_destroy_devinfo(&ndi->ndi_obdinfo); free(ndi, M_DEVBUF); } static int nexus_print_res(struct nexus_devinfo *ndi) { int rv; rv = 0; rv += resource_list_print_type(&ndi->ndi_rl, "mem", SYS_RES_MEMORY, "%#lx"); rv += resource_list_print_type(&ndi->ndi_rl, "irq", SYS_RES_IRQ, "%ld"); return (rv); } Index: head/sys/powerpc/pseries/vdevice.c =================================================================== --- head/sys/powerpc/pseries/vdevice.c (revision 256993) +++ head/sys/powerpc/pseries/vdevice.c (revision 256994) @@ -1,232 +1,233 @@ /*- * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iommu_if.h" static int vdevice_probe(device_t); static int vdevice_attach(device_t); static const struct ofw_bus_devinfo *vdevice_get_devinfo(device_t dev, device_t child); static int vdevice_print_child(device_t dev, device_t child); static struct resource_list *vdevice_get_resource_list(device_t, device_t); static bus_dma_tag_t vdevice_get_dma_tag(device_t dev, device_t child); /* * VDevice devinfo */ struct vdevice_devinfo { struct ofw_bus_devinfo mdi_obdinfo; struct resource_list mdi_resources; bus_dma_tag_t mdi_dma_tag; }; static device_method_t vdevice_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vdevice_probe), DEVMETHOD(device_attach, vdevice_attach), /* Bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), DEVMETHOD(bus_print_child, vdevice_print_child), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_get_resource_list, vdevice_get_resource_list), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, vdevice_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), /* IOMMU interface */ DEVMETHOD(bus_get_dma_tag, vdevice_get_dma_tag), DEVMETHOD(iommu_map, phyp_iommu_map), DEVMETHOD(iommu_unmap, phyp_iommu_unmap), DEVMETHOD_END }; static driver_t vdevice_driver = { "vdevice", vdevice_methods, 0 }; static devclass_t vdevice_devclass; DRIVER_MODULE(vdevice, nexus, vdevice_driver, vdevice_devclass, 0, 0); static int vdevice_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name == NULL || strcmp(name, "vdevice") != 0) return (ENXIO); if (!ofw_bus_is_compatible(dev, "IBM,vdevice")) return (ENXIO); device_set_desc(dev, "POWER Hypervisor Virtual Device Root"); return (0); } static int vdevice_attach(device_t dev) { phandle_t root, child; device_t cdev; int icells, i, nintr, *intr; phandle_t iparent; struct vdevice_devinfo *dinfo; root = ofw_bus_get_node(dev); for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 0) { free(dinfo, M_DEVBUF); continue; } resource_list_init(&dinfo->mdi_resources); if (OF_searchprop(child, "#interrupt-cells", &icells, sizeof(icells)) <= 0) icells = 2; if (OF_getprop(child, "interrupt-parent", &iparent, sizeof(iparent)) <= 0) iparent = -1; nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr), (void **)&intr); if (nintr > 0) { for (i = 0; i < nintr; i += icells) { u_int irq = intr[i]; if (iparent != -1) - irq = MAP_IRQ(iparent, intr[i]); + irq = ofw_bus_map_intr(dev, iparent, + intr[i]); resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, i, irq, irq, i); } } cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->mdi_obdinfo.obd_name); ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_DEVBUF); continue; } device_set_ivars(cdev, dinfo); } return (bus_generic_attach(dev)); } static const struct ofw_bus_devinfo * vdevice_get_devinfo(device_t dev, device_t child) { return (device_get_ivars(child)); } static int vdevice_print_child(device_t dev, device_t child) { struct vdevice_devinfo *dinfo; struct resource_list *rl; int retval = 0; dinfo = device_get_ivars(child); rl = &dinfo->mdi_resources; retval += bus_print_child_header(dev, child); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); retval += bus_print_child_footer(dev, child); return (retval); } static struct resource_list * vdevice_get_resource_list (device_t dev, device_t child) { struct vdevice_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_resources); } static bus_dma_tag_t vdevice_get_dma_tag(device_t dev, device_t child) { struct vdevice_devinfo *dinfo; while (child != NULL && device_get_parent(child) != dev) child = device_get_parent(child); dinfo = device_get_ivars(child); if (dinfo->mdi_dma_tag == NULL) { bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &dinfo->mdi_dma_tag); phyp_iommu_set_dma_tag(dev, child, dinfo->mdi_dma_tag); } return (dinfo->mdi_dma_tag); }