Index: head/sys/conf/files.sparc64 =================================================================== --- head/sys/conf/files.sparc64 (revision 186127) +++ head/sys/conf/files.sparc64 (revision 186128) @@ -1,134 +1,133 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # sunkbdmap.h optional sunkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${SUNKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > sunkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "sunkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # crypto/blowfish/bf_enc.c optional crypto | ipsec crypto/des/des_enc.c optional crypto | ipsec | netsmb dev/atkbdc/atkbd.c optional atkbd atkbdc dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc dev/atkbdc/atkbdc.c optional atkbdc dev/atkbdc/atkbdc_ebus.c optional atkbdc ebus dev/atkbdc/atkbdc_isa.c optional atkbdc isa dev/atkbdc/atkbdc_subr.c optional atkbdc dev/atkbdc/psm.c optional psm atkbdc dev/auxio/auxio.c optional auxio sbus | auxio ebus dev/esp/esp_sbus.c optional esp sbus dev/fb/creator.c optional creator sc dev/fb/fb.c optional sc dev/fb/gallant12x22.c optional sc dev/fb/machfb.c optional machfb sc dev/hwpmc/hwpmc_sparc64.c optional hwpmc dev/kbd/kbd.c optional atkbd | sc | ukbd dev/le/if_le_lebuffer.c optional le sbus dev/le/if_le_ledma.c optional le sbus dev/le/lebuffer_sbus.c optional le sbus dev/ofw/ofw_bus_if.m standard dev/ofw/ofw_bus_subr.c standard dev/ofw/ofw_console.c optional ofw_console dev/ofw/openfirm.c standard dev/ofw/openfirmio.c standard dev/ofw/openpromio.c standard dev/pcf/envctrl.c optional pcf ebus dev/pcf/pcf_ebus.c optional pcf ebus dev/sound/sbus/cs4231.c optional snd_audiocs ebus | \ snd_audiocs sbus dev/syscons/scgfbrndr.c optional sc dev/syscons/scterm-sc.c optional sc dev/syscons/scvtb.c optional sc dev/uart/uart_cpu_sparc64.c optional uart dev/uart/uart_kbd_sun.c optional uart sc kern/syscalls.c optional ktr libkern/ffs.c standard libkern/ffsl.c standard libkern/fls.c standard libkern/flsl.c standard sparc64/central/central.c optional central sparc64/ebus/ebus.c optional ebus sparc64/fhc/clkbrd.c optional fhc sparc64/fhc/fhc.c optional fhc sparc64/isa/isa.c optional isa sparc64/isa/isa_dma.c optional isa sparc64/isa/ofw_isa.c optional ebus | isa sparc64/pci/apb.c optional pci sparc64/pci/ofw_pcib.c optional pci sparc64/pci/ofw_pcib_subr.c optional pci sparc64/pci/ofw_pcibus.c optional pci sparc64/pci/psycho.c optional pci sparc64/pci/schizo.c optional pci sparc64/sbus/dma_sbus.c optional sbus sparc64/sbus/sbus.c optional sbus sparc64/sbus/lsi64854.c optional sbus sparc64/sparc64/ata_machdep.c optional atadisk sparc64/sparc64/autoconf.c standard sparc64/sparc64/bus_machdep.c standard sparc64/sparc64/cache.c standard sparc64/sparc64/cheetah.c standard sparc64/sparc64/clock.c standard sparc64/sparc64/counter.c standard sparc64/sparc64/db_disasm.c optional ddb sparc64/sparc64/db_interface.c optional ddb sparc64/sparc64/db_trace.c optional ddb sparc64/sparc64/db_hwwatch.c optional ddb sparc64/sparc64/dump_machdep.c standard sparc64/sparc64/elf_machdep.c standard sparc64/sparc64/exception.S standard no-obj \ compile-with "${NORMAL_S} -mcpu=ultrasparc" sparc64/sparc64/eeprom.c optional eeprom ebus | eeprom fhc | \ eeprom sbus sparc64/sparc64/gdb_machdep.c optional gdb sparc64/sparc64/identcpu.c standard sparc64/sparc64/in_cksum.c optional inet sparc64/sparc64/interrupt.S standard no-obj \ compile-with "${NORMAL_S} -mcpu=ultrasparc" sparc64/sparc64/intr_machdep.c standard sparc64/sparc64/iommu.c standard sparc64/sparc64/jbusppm.c standard sparc64/sparc64/locore.S standard no-obj sparc64/sparc64/machdep.c standard sparc64/sparc64/mem.c optional mem sparc64/sparc64/mp_exception.S optional smp \ compile-with "${NORMAL_S} -mcpu=ultrasparc" sparc64/sparc64/mp_locore.S optional smp sparc64/sparc64/mp_machdep.c optional smp sparc64/sparc64/nexus.c standard -sparc64/sparc64/ofw_bus.c standard sparc64/sparc64/ofw_machdep.c standard sparc64/sparc64/pmap.c standard sparc64/sparc64/prof_machdep.c optional profiling-routine sparc64/sparc64/rtc.c optional rtc ebus | rtc isa sparc64/sparc64/rwindow.c standard sparc64/sparc64/sc_machdep.c optional sc sparc64/sparc64/schppm.c standard sparc64/sparc64/spitfire.c standard sparc64/sparc64/stack_machdep.c optional ddb | stack sparc64/sparc64/support.S standard \ compile-with "${NORMAL_S} -mcpu=ultrasparc" sparc64/sparc64/sys_machdep.c standard sparc64/sparc64/swtch.S standard sparc64/sparc64/tick.c standard sparc64/sparc64/tlb.c standard sparc64/sparc64/trap.c standard sparc64/sparc64/tsb.c standard sparc64/sparc64/uio_machdep.c standard sparc64/sparc64/upa.c optional creator sparc64/sparc64/vm_machdep.c standard Index: head/sys/conf/files.sun4v =================================================================== --- head/sys/conf/files.sun4v (revision 186127) +++ head/sys/conf/files.sun4v (revision 186128) @@ -1,103 +1,102 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # # crypto/blowfish/bf_enc.c optional crypto | ipsec crypto/des/des_enc.c optional crypto | ipsec | netsmb dev/ofw/ofw_bus_if.m standard dev/ofw/ofw_bus_subr.c standard dev/ofw/ofw_console.c optional ofw_console dev/ofw/openfirm.c standard dev/ofw/openfirmio.c standard dev/ofw/openpromio.c standard dev/uart/uart_cpu_sparc64.c optional uart kern/syscalls.c optional ktr libkern/ffs.c standard libkern/ffsl.c standard libkern/fls.c standard libkern/flsl.c standard sparc64/sparc64/autoconf.c standard sun4v/sun4v/bus_machdep.c standard sun4v/sun4v/clock.c standard sparc64/sparc64/db_disasm.c optional ddb sun4v/sun4v/db_interface.c optional ddb sun4v/sun4v/db_trace.c optional ddb sun4v/sun4v/db_hwwatch.c optional ddb sun4v/sun4v/dump_machdep.c standard sparc64/sparc64/elf_machdep.c standard sun4v/sun4v/exception.S standard no-obj sparc64/sparc64/gdb_machdep.c optional gdb sun4v/sun4v/hv_pci.c optional pci sun4v/sun4v/trap_trace.S optional trap_tracing sparc64/pci/ofw_pcib.c optional pci sparc64/pci/ofw_pcib_subr.c optional pci sparc64/pci/ofw_pcibus.c optional pci # XXX hvcons should be optional sun4v/sun4v/hvcons.c standard sun4v/sun4v/hcall.S standard sun4v/sun4v/hviommu.c standard sparc64/sparc64/identcpu.c standard sparc64/sparc64/in_cksum.c optional inet sun4v/sun4v/interrupt.S standard no-obj sun4v/sun4v/intr_machdep.c standard sun4v/sun4v/locore.S standard no-obj sun4v/sun4v/machdep.c standard sparc64/sparc64/mem.c optional mem sun4v/sun4v/mp_exception.S optional smp sun4v/sun4v/mp_locore.S optional smp sun4v/sun4v/mp_machdep.c optional smp sun4v/sun4v/nexus.c standard sun4v/cddl/t1_copy.S standard -sparc64/sparc64/ofw_bus.c standard sparc64/sparc64/ofw_machdep.c standard sun4v/sun4v/pmap.c standard sparc64/sparc64/prof_machdep.c optional profiling-routine sparc64/sparc64/rwindow.c standard sun4v/sun4v/rtc.c standard sun4v/sun4v/simdisk.c optional simulator sun4v/sun4v/stack_machdep.c optional ddb | stack sun4v/sun4v/support.S standard sparc64/sparc64/sys_machdep.c standard sun4v/sun4v/swtch.S standard sun4v/sun4v/tsb.c standard sun4v/sun4v/tte.c standard sun4v/sun4v/tte_hash.c standard sun4v/sun4v/tick.c standard sun4v/sun4v/trap.c standard sun4v/sun4v/uio_machdep.c standard sun4v/sun4v/vm_machdep.c standard sun4v/sun4v/vnex.c standard sun4v/dev/vnet.c optional vnet sun4v/mdesc/mdesc_bus_if.m standard sun4v/mdesc/mdesc_init.c standard sun4v/mdesc/mdesc_subr.c standard sun4v/mdesc/mdesc_bus_subr.c standard sun4v/cddl/mdesc/mdesc_findname.c standard sun4v/cddl/mdesc/mdesc_getpropstr.c standard sun4v/cddl/mdesc/mdesc_getpropval.c standard sun4v/cddl/mdesc/mdesc_init_intern.c standard sun4v/cddl/mdesc/mdesc_nodecount.c standard sun4v/cddl/mdesc/mdesc_findnodeprop.c standard sun4v/cddl/mdesc/mdesc_rootnode.c standard sun4v/cddl/mdesc/mdesc_scandag.c standard sun4v/mdesc/mdesc_vdevfindnode.c standard sun4v/mdesc/mdesc_vdevfindval.c standard Index: head/sys/dev/ofw/ofw_bus_subr.c =================================================================== --- head/sys/dev/ofw/ofw_bus_subr.c (revision 186127) +++ head/sys/dev/ofw/ofw_bus_subr.c (revision 186128) @@ -1,128 +1,279 @@ /*- + * Copyright (c) 2001 - 2003 by Thomas Moestl . * Copyright (c) 2005 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, * without modification, immediately at the beginning of the file. * 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 "ofw_bus_if.h" int ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) { if (obd == NULL) return (ENOMEM); /* The 'name' property is considered mandatory. */ if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) return (EINVAL); OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); obd->obd_node = node; return (0); } void ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) { if (obd == NULL) return; if (obd->obd_compat != NULL) free(obd->obd_compat, M_OFWPROP); if (obd->obd_model != NULL) free(obd->obd_model, M_OFWPROP); if (obd->obd_name != NULL) free(obd->obd_name, M_OFWPROP); if (obd->obd_type != NULL) free(obd->obd_type, M_OFWPROP); } +int +ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, + size_t buflen) +{ + if (ofw_bus_get_name(child) != NULL) { + strlcat(buf, "name=", buflen); + strlcat(buf, ofw_bus_get_name(child), buflen); + } + if (ofw_bus_get_compat(child) != NULL) { + strlcat(buf, " compat=", buflen); + strlcat(buf, ofw_bus_get_compat(child), buflen); + } + + return (0); +}; + const char * ofw_bus_gen_get_compat(device_t bus, device_t dev) { const struct ofw_bus_devinfo *obd; obd = OFW_BUS_GET_DEVINFO(bus, dev); if (obd == NULL) return (NULL); return (obd->obd_compat); } const char * ofw_bus_gen_get_model(device_t bus, device_t dev) { const struct ofw_bus_devinfo *obd; obd = OFW_BUS_GET_DEVINFO(bus, dev); if (obd == NULL) return (NULL); return (obd->obd_model); } const char * ofw_bus_gen_get_name(device_t bus, device_t dev) { const struct ofw_bus_devinfo *obd; obd = OFW_BUS_GET_DEVINFO(bus, dev); if (obd == NULL) return (NULL); return (obd->obd_name); } phandle_t ofw_bus_gen_get_node(device_t bus, device_t dev) { const struct ofw_bus_devinfo *obd; obd = OFW_BUS_GET_DEVINFO(bus, dev); if (obd == NULL) return (0); return (obd->obd_node); } const char * ofw_bus_gen_get_type(device_t bus, device_t dev) { const struct ofw_bus_devinfo *obd; obd = OFW_BUS_GET_DEVINFO(bus, dev); if (obd == NULL) return (NULL); return (obd->obd_type); +} + +static int +ofw_bus_searchprop(phandle_t node, char *propname, void *buf, int buflen) +{ + int rv; + + for (; node != 0; node = OF_parent(node)) { + if ((rv = OF_getprop(node, propname, buf, buflen)) != -1) + return (rv); + } + return (-1); +} + +void +ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) +{ + pcell_t addrc; + int msksz; + + if (OF_getprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) + addrc = 2; + ii->opi_addrc = addrc * sizeof(pcell_t); + + ii->opi_imapsz = OF_getprop_alloc(node, "interrupt-map", 1, + (void **)&ii->opi_imap); + if (ii->opi_imapsz > 0) { + msksz = OF_getprop_alloc(node, "interrupt-map-mask", 1, + (void **)&ii->opi_imapmsk); + /* + * Failure to get the mask is ignored; a full mask is used then. + * Barf on bad mask sizes, however. + */ + if (msksz != -1 && msksz != ii->opi_addrc + intrsz) { + panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " + "property!"); + } + } + +} + +int +ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, + int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, + void *maskbuf) +{ + int rv; + + if (ii->opi_imapsz <= 0) + return (0); + KASSERT(regsz >= ii->opi_addrc, + ("ofw_bus_lookup_imap: register size too small: %d < %d", + regsz, ii->opi_addrc)); + rv = OF_getprop(node, "reg", reg, regsz); + if (rv < regsz) + panic("ofw_bus_lookup_imap: could not get reg property"); + return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, + ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, + mintrsz)); +} + +/* + * Map an interrupt using the firmware reg, interrupt-map and + * interrupt-map-mask properties. + * The interrupt property to be mapped must be of size intrsz, and pointed to + * by intr. The regs property of the node for which the mapping is done must + * be passed as regs. This property is an array of register specifications; + * the size of the address part of such a specification must be passed as + * physsz. Only the first element of the property is used. + * imap and imapsz hold the interrupt mask and it's size. + * imapmsk is a pointer to the interrupt-map-mask property, which must have + * a size of physsz + intrsz; it may be NULL, in which case a full mask is + * assumed. + * maskbuf must point to a buffer of length physsz + intrsz. + * The interrupt is returned in result, which must point to a buffer of length + * rintrsz (which gives the expected size of the mapped interrupt). + * Returns 1 if a mapping was found, 0 otherwise. + */ +int +ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, + void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, + int rintrsz) +{ + phandle_t parent; + u_int8_t *ref = maskbuf; + u_int8_t *uiintr = intr; + u_int8_t *uiregs = regs; + u_int8_t *uiimapmsk = imapmsk; + u_int8_t *mptr; + pcell_t pintrsz; + int i, rsz, tsz; + + rsz = -1; + if (imapmsk != NULL) { + for (i = 0; i < physsz; i++) + ref[i] = uiregs[i] & uiimapmsk[i]; + for (i = 0; i < intrsz; i++) + ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; + } else { + bcopy(regs, ref, physsz); + bcopy(intr, ref + physsz, intrsz); + } + + mptr = imap; + i = imapsz; + tsz = physsz + intrsz + sizeof(phandle_t) + rintrsz; + while (i > 0) { + KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); + bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); + if (ofw_bus_searchprop(parent, "#interrupt-cells", + &pintrsz, sizeof(pintrsz)) == -1) + pintrsz = 1; /* default */ + pintrsz *= sizeof(pcell_t); + if (pintrsz != rintrsz) + panic("ofw_bus_search_intrmap: expected interrupt cell " + "size incorrect: %d > %d", rintrsz, pintrsz); + + /* + * XXX: Apple hardware uses a second cell to set information + * on the interrupt trigger type. This information should + * be used somewhere to program the PIC. + */ + + if (bcmp(ref, mptr, physsz + intrsz) == 0) { + bcopy(mptr + physsz + intrsz + sizeof(parent), + result, rintrsz); + return (1); + } + mptr += tsz; + i -= tsz; + } + return (0); } Index: head/sys/dev/ofw/ofw_bus_subr.h =================================================================== --- head/sys/dev/ofw/ofw_bus_subr.h (revision 186127) +++ head/sys/dev/ofw/ofw_bus_subr.h (revision 186128) @@ -1,49 +1,70 @@ /*- * Copyright (c) 2005 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, * without modification, immediately at the beginning of the file. * 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. * * $FreeBSD$ */ #ifndef _DEV_OFW_OFW_BUS_SUBR_H_ #define _DEV_OFW_OFW_BUS_SUBR_H_ #include #include #include "ofw_bus_if.h" +#define ORIP_NOINT -1 +#define ORIR_NOTFOUND 0xffffffff + +struct ofw_bus_iinfo { + u_int8_t *opi_imap; + u_int8_t *opi_imapmsk; + int opi_imapsz; + pcell_t opi_addrc; +}; + int ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *, phandle_t); void ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *); + +/* Helper method to report interesting OF properties in pnpinfo */ +int ofw_bus_gen_child_pnpinfo_str(device_t, device_t, char *, size_t); + +/* Routines for processing firmware interrupt maps */ + +void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int); +int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int, + void *, int, void *, int, void *); +int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *, + void *, void *, int); ofw_bus_get_compat_t ofw_bus_gen_get_compat; ofw_bus_get_model_t ofw_bus_gen_get_model; ofw_bus_get_name_t ofw_bus_gen_get_name; ofw_bus_get_node_t ofw_bus_gen_get_node; ofw_bus_get_type_t ofw_bus_gen_get_type; #endif /* !_DEV_OFW_OFW_BUS_SUBR_H_ */ Index: head/sys/dev/ofw/openfirm.h =================================================================== --- head/sys/dev/ofw/openfirm.h (revision 186127) +++ head/sys/dev/ofw/openfirm.h (revision 186128) @@ -1,137 +1,143 @@ /* $NetBSD: openfirm.h,v 1.1 1998/05/15 10:16:00 tsubai Exp $ */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /* * Copyright (C) 2000 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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 _OPENFIRM_H_ #define _OPENFIRM_H_ /* * Prototypes for Open Firmware Interface Routines */ typedef unsigned long cell_t; typedef unsigned int ihandle_t; typedef unsigned int phandle_t; +/* + * Other than in Open Firmware calls, the size of a bus cell seems to be + * always the same. + */ +typedef u_int32_t pcell_t; + #ifdef _KERNEL #include #include #include MALLOC_DECLARE(M_OFWPROP); /* * Stuff that is used by the Open Firmware code. */ void set_openfirm_callback(int (*)(void *)); int openfirmware(void *); /* * This isn't actually an Open Firmware function, but it seemed like the right * place for it to go. */ void OF_init(int (*openfirm)(void *)); /* Generic functions */ int OF_test(char *); void OF_printf(const char *, ...); /* Device tree functions */ phandle_t OF_peer(phandle_t); phandle_t OF_child(phandle_t); phandle_t OF_parent(phandle_t); phandle_t OF_instance_to_package(ihandle_t); int OF_getproplen(phandle_t, char *); int OF_getprop(phandle_t, char *, void *, int); int OF_getprop_alloc(phandle_t package, char *propname, int elsz, void **buf); int OF_nextprop(phandle_t, char *, char *); int OF_setprop(phandle_t, char *, void *, int); int OF_canon(const char *, char *, int); phandle_t OF_finddevice(const char *); int OF_instance_to_path(ihandle_t, char *, int); int OF_package_to_path(phandle_t, char *, int); int OF_call_method(char *, ihandle_t, int, int, ...); /* Device I/O functions */ ihandle_t OF_open(char *); void OF_close(ihandle_t); int OF_read(ihandle_t, void *, int); int OF_write(ihandle_t, void *, int); int OF_seek(ihandle_t, u_quad_t); /* Memory functions */ void *OF_claim(void *, u_int, u_int); void OF_release(void *, u_int); /* Control transfer functions */ void OF_boot(char *); void OF_enter(void); void OF_exit(void) __attribute__((noreturn)); void OF_chain(void *, u_int, void (*)(void *, u_int, void *, void *, u_int), void *, u_int); /* User interface functions */ int OF_interpret(char *, int, ...); /* Time function */ int OF_milliseconds(void); #endif /* _KERNEL */ #endif /* _OPENFIRM_H_ */ Index: head/sys/powerpc/include/param.h =================================================================== --- head/sys/powerpc/include/param.h (revision 186127) +++ head/sys/powerpc/include/param.h (revision 186128) @@ -1,107 +1,110 @@ /*- * Copyright (c) 2001 David E. O'Brien * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)param.h 5.8 (Berkeley) 6/28/91 * $FreeBSD$ */ /* * Machine dependent constants for PowerPC (32-bit only currently) */ /* * Round p (pointer or byte index) up to a correctly-aligned value * for all data types (int, long, ...). The result is unsigned int * and must be cast to any desired pointer type. */ #ifndef _ALIGNBYTES #define _ALIGNBYTES (sizeof(int) - 1) #endif #ifndef _ALIGN #define _ALIGN(p) (((unsigned)(p) + _ALIGNBYTES) & ~_ALIGNBYTES) #endif #ifndef _NO_NAMESPACE_POLLUTION +/* Needed to display interrupts on OFW PCI */ +#define __PCI_REROUTE_INTERRUPT + #ifndef _MACHINE_PARAM_H_ #define _MACHINE_PARAM_H_ #ifndef MACHINE #define MACHINE "powerpc" #endif #ifndef MACHINE_ARCH #define MACHINE_ARCH "powerpc" #endif #define MID_MACHINE MID_POWERPC #if defined(SMP) || defined(KLD_MODULE) #define MAXCPU 2 #else #define MAXCPU 1 #endif /* SMP || KLD_MODULE */ #define ALIGNBYTES _ALIGNBYTES #define ALIGN(p) _ALIGN(p) #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) /* Page size */ #define PAGE_MASK (PAGE_SIZE - 1) #define NPTEPG (PAGE_SIZE/(sizeof (pt_entry_t))) #ifndef KSTACK_PAGES #define KSTACK_PAGES 4 /* includes pcb */ #endif #define KSTACK_GUARD_PAGES 1 /* pages of kstack guard; 0 disables */ #define USPACE (KSTACK_PAGES * PAGE_SIZE) /* total size of pcb */ /* * Mach derived conversion macros */ #define trunc_page(x) ((unsigned long)(x) & ~(PAGE_MASK)) #define round_page(x) (((x) + PAGE_MASK) & ~PAGE_MASK) #define trunc_4mpage(x) ((unsigned)(x) & ~PDRMASK) #define round_4mpage(x) ((((unsigned)(x)) + PDRMASK) & ~PDRMASK) #define atop(x) ((unsigned long)(x) >> PAGE_SHIFT) #define ptoa(x) ((unsigned long)(x) << PAGE_SHIFT) #define powerpc_btop(x) ((unsigned)(x) >> PAGE_SHIFT) #define powerpc_ptob(x) ((unsigned)(x) << PAGE_SHIFT) #define pgtok(x) ((x) * (PAGE_SIZE / 1024)) #endif /* !_MACHINE_PARAM_H_ */ #endif /* !_NO_NAMESPACE_POLLUTION */ Index: head/sys/powerpc/ofw/ofw_pcib_pci.c =================================================================== --- head/sys/powerpc/ofw/ofw_pcib_pci.c (revision 186127) +++ head/sys/powerpc/ofw/ofw_pcib_pci.c (revision 186128) @@ -1,120 +1,178 @@ /*- * 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. * * $FreeBSD$ */ #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), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_maxslots), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), - DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), + DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node), {0, 0} }; 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_0(pcib, ofw_pcib_pci_driver, ofw_pcib_pci_methods, - sizeof(struct pcib_softc)); + sizeof(struct ofw_pcib_softc)); 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) == 0) 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)); } -phandle_t +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; + uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; + + sc = device_get_softc(bridge); + ii = &sc->ops_iinfo; + if (ii->opi_imapsz > 0) { + pintr = intpin; + if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, + sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), + 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 (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 186127) +++ head/sys/powerpc/ofw/ofw_pcibus.c (revision 186128) @@ -1,359 +1,340 @@ /*- - * Copyright (c) 1994 Charles M. Hannum. All rights reserved. - * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. * 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 "pcib_if.h" #include "pci_if.h" -/* Helper functions */ -static int find_node_intr(phandle_t, u_int32_t *, u_int32_t *); -static int ofw_pci_find_intline(phandle_t node, uint32_t *irqs); -static void ofw_pci_fixup_node(device_t dev, phandle_t node); +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, 1 /* no 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_pcibus_probe(device_t dev) { + if (ofw_bus_get_node(dev) == 0) return (ENXIO); device_set_desc(dev, "OFW PCI bus"); return (0); } static int ofw_pcibus_attach(device_t dev) { - device_t pcib; - struct ofw_pci_register pcir; - struct ofw_pcibus_devinfo *dinfo; - phandle_t node, child; - u_int busno, domain, func, slot; + u_int busno, domain; - pcib = device_get_parent(dev); domain = pcib_get_domain(dev); busno = pcib_get_bus(dev); if (bootverbose) device_printf(dev, "domain=%d, physical bus=%d\n", domain, busno); + + /* + * 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. + */ + + 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; - ofw_pci_fixup_node(pcib, child); + /* + * 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; - - /* Set up OFW devinfo */ 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. Add them to the appropriate resource list. - */ - if (dinfo->opd_dinfo.cfg.intpin == 0) { - uint32_t irqs[4]; + * 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; - if (ofw_pci_find_intline(child, irqs) > 0) - resource_list_add(&dinfo->opd_dinfo.resources, - SYS_RES_IRQ, 0, irqs[0], irqs[0], 1); - } + if (OF_getprop(child, "interrupts", &intr, + sizeof(intr)) > 0) { + resource_list_add(&dinfo->opd_dinfo.resources, + SYS_RES_IRQ, 0, intr, intr, 1); + } + } } - - return (bus_generic_attach(dev)); } -static int -ofw_pcibus_assign_interrupt(device_t dev, device_t child) -{ - uint32_t irqs[4]; +/* + * 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. + */ - device_printf(child,"Assigning interrupt\n"); - - if (ofw_pci_find_intline(ofw_bus_get_node(child), irqs) < 0) - return PCI_INVALID_IRQ; - - device_printf(child,"IRQ %d\n",irqs[0]); - - return irqs[0]; - -// 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) +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; - dinfo = device_get_ivars(dev); - return (&dinfo->opd_obdinfo); -} + pcib = device_get_parent(dev); -static void -ofw_pci_fixup_node(device_t dev, phandle_t node) -{ - uint32_t csr, intr, irqs[4]; - struct ofw_pci_register addr[8]; - int len, i; + 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; - len = OF_getprop(node, "assigned-addresses", addr, sizeof(addr)); - if (len < (int)sizeof(struct ofw_pci_register)) { - return; - } + dinfo = (struct ofw_pcibus_devinfo *)pci_read_device( + pcib, domain, busno, s, f, sizeof(*dinfo)); + if (dinfo != NULL) { + dinfo->opd_obdinfo.obd_node = -1; - csr = PCIB_READ_CONFIG(dev, OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi), - OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_COMMAND, 4); - csr &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN); - - for (i = 0; i < len / sizeof(struct ofw_pci_register); i++) { - switch (addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK) { - case OFW_PCI_PHYS_HI_SPACE_IO: - csr |= PCIM_CMD_PORTEN; - break; - case OFW_PCI_PHYS_HI_SPACE_MEM32: - csr |= PCIM_CMD_MEMEN; - break; + dinfo->opd_obdinfo.obd_name = NULL; + dinfo->opd_obdinfo.obd_compat = NULL; + dinfo->opd_obdinfo.obd_type = NULL; + dinfo->opd_obdinfo.obd_model = NULL; + pci_add_child(dev, (struct pci_devinfo *)dinfo); + } } } - - PCIB_WRITE_CONFIG(dev, OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi), - OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_COMMAND, csr, 4); - - if (ofw_pci_find_intline(node, irqs) != -1) { - intr = PCIB_READ_CONFIG(dev, - OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi), - OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_INTLINE, 2); - intr &= ~(0xff); - intr |= irqs[0] & 0xff; - PCIB_WRITE_CONFIG(dev, - OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi), - OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_INTLINE, - intr, 2); - } } static int -ofw_pci_find_intline(phandle_t node, uint32_t *irqs) +ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, + size_t buflen) { - uint32_t npintr, paddr[4]; - struct ofw_pci_register addr[8]; - int len; + pci_child_pnpinfo_str_method(cbdev, child, buf, buflen); - len = OF_getprop(node, "assigned-addresses", addr, sizeof(addr)); - if (len < (int)sizeof(struct ofw_pci_register)) - return -1; - /* - * Create PCI interrupt-map array element. pci-mid/pci-lo - * aren't required, but the 'interrupts' property needs - * to be appended - */ - npintr = 0; - OF_getprop(node, "interrupts", &npintr, 4); - paddr[0] = addr[0].phys_hi; - paddr[1] = 0; - paddr[2] = 0; - paddr[3] = npintr; + if (ofw_bus_get_node(child) != -1) { + strlcat(buf, " ", buflen); /* Separate info */ + ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen); + } - return find_node_intr(node, paddr, irqs); + return (0); } - + static int -find_node_intr(phandle_t node, u_int32_t *addr, u_int32_t *intr) +ofw_pcibus_assign_interrupt(device_t dev, device_t child) { - phandle_t parent, iparent; - int len, mlen, match, i; - u_int32_t map[160], *mp, imask[8], maskedaddr[8], icells; - char name[32]; + ofw_pci_intr_t intr; + phandle_t node; + int isz; - len = OF_getprop(node, "AAPL,interrupts", intr, 4); - if (len == 4) { - return (len); - } + node = ofw_bus_get_node(child); - parent = OF_parent(node); - len = OF_getprop(parent, "interrupt-map", map, sizeof(map)); - mlen = OF_getprop(parent, "interrupt-map-mask", imask, sizeof(imask)); - - if (len == -1 || mlen == -1) - goto nomap; - - memcpy(maskedaddr, addr, mlen); - for (i = 0; i < mlen/4; i++) - maskedaddr[i] &= imask[i]; - - mp = map; - while (len > mlen) { - match = bcmp(maskedaddr, mp, mlen); - mp += mlen / 4; - len -= mlen; - + if (node == -1) { + /* Non-firmware enumerated child, use standard routing */ + /* - * We must read "#interrupt-cells" for each time because - * interrupt-parent may be different. + * XXX: Right now we don't have anything sensible to do here, + * since the ofw_imap stuff relies on nodes have a reg + * property. There exists ways around this, so the ePAPR + * spec will need to be studied. */ - iparent = *mp++; - len -= 4; - if (OF_getprop(iparent, "#interrupt-cells", &icells, 4) != 4) - goto nomap; - /* Found. */ - if (match == 0) { - bcopy(mp, intr, icells * 4); - return (icells * 4); - } + return (0); - mp += icells; - len -= icells * 4; +#ifdef NOTYET + intr = pci_get_intpin(child); + return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, + intr)); +#endif } - -nomap: + /* - * Check for local properties indicating interrupts + * Any AAPL,interrupts property gets priority and is + * fully specified (i.e. does not need routing) */ - len = OF_getprop(node, "interrupts", intr, 16); - if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) == - sizeof(iparent)) { - OF_getprop(iparent, "#interrupt-cells", &icells, sizeof(icells)); - for (i = 0; i < len/icells/4; i++) - intr[i] = intr[i*icells]; + isz = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr)); + if (isz == sizeof(intr)) { + return (intr); + } - return (len); + isz = OF_getprop(node, "interrupts", &intr, sizeof(intr)); + if (isz != sizeof(intr)) { + /* No property; our best guess is the intpin. */ + intr = pci_get_intpin(child); } - /* - * If the node has no interrupt property and the parent is a PCI - * bridge, use the parent's interrupt. This occurs on a PCI slot. + * 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. */ - bzero(name, sizeof(name)); - OF_getprop(parent, "name", name, sizeof(name)); - if (strcmp(name, "pci-bridge") == 0) { - len = OF_getprop(parent, "AAPL,interrupts", intr, 4); - if (len == 4) { - return (len); - } + return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr)); +} - /* - * XXX I don't know what is the correct local address. - * XXX Use the first entry for now. - */ - len = OF_getprop(parent, "interrupt-map", map, sizeof(map)); - if (len >= 36) { - addr = &map[5]; - /* XXX Use 0 for 'interrupts' for compat */ - return (find_node_intr(parent, addr, intr)); - } - } +static const struct ofw_bus_devinfo * +ofw_pcibus_get_devinfo(device_t bus, device_t dev) +{ + struct ofw_pcibus_devinfo *dinfo; - return (-1); + dinfo = device_get_ivars(dev); + return (&dinfo->opd_obdinfo); } Index: head/sys/powerpc/powermac/grackle.c =================================================================== --- head/sys/powerpc/powermac/grackle.c (revision 186127) +++ head/sys/powerpc/powermac/grackle.c (revision 186128) @@ -1,564 +1,592 @@ /*- * Copyright 2003 by Peter Grehan. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" int badaddr(void *, size_t); /* XXX */ /* * Device interface. */ static int grackle_probe(device_t); static int grackle_attach(device_t); /* * Bus interface. */ static int grackle_read_ivar(device_t, device_t, int, uintptr_t *); static struct resource * grackle_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 grackle_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int grackle_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); static int grackle_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); /* * pcib interface. */ static int grackle_maxslots(device_t); static u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, u_int, int); static void grackle_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int grackle_route_interrupt(device_t, device_t, int); /* * ofw_bus interface */ static phandle_t grackle_get_node(device_t bus, device_t dev); /* * Local routines. */ static int grackle_enable_config(struct grackle_softc *, u_int, u_int, u_int, u_int); static void grackle_disable_config(struct grackle_softc *); /* * Driver methods. */ static device_method_t grackle_methods[] = { /* Device interface */ DEVMETHOD(device_probe, grackle_probe), DEVMETHOD(device_attach, grackle_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, grackle_read_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, grackle_alloc_resource), DEVMETHOD(bus_release_resource, grackle_release_resource), DEVMETHOD(bus_activate_resource, grackle_activate_resource), DEVMETHOD(bus_deactivate_resource, grackle_deactivate_resource), /* pcib interface */ DEVMETHOD(pcib_maxslots, grackle_maxslots), DEVMETHOD(pcib_read_config, grackle_read_config), DEVMETHOD(pcib_write_config, grackle_write_config), DEVMETHOD(pcib_route_interrupt, grackle_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, grackle_get_node), { 0, 0 } }; static driver_t grackle_driver = { "pcib", grackle_methods, sizeof(struct grackle_softc) }; static devclass_t grackle_devclass; DRIVER_MODULE(grackle, nexus, grackle_driver, grackle_devclass, 0, 0); static int grackle_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) return (ENXIO); device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); return (0); } static int grackle_attach(device_t dev) { struct grackle_softc *sc; - phandle_t node; + phandle_t node, iparent; u_int32_t busrange[2]; struct grackle_range *rp, *io, *mem[2]; int nmem, i, error; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) return (ENXIO); sc->sc_dev = dev; sc->sc_node = node; sc->sc_bus = busrange[0]; /* * The Grackle PCI config addr/data registers are actually in * PCI space, but since they are needed to actually probe the * PCI bus, use the fact that they are also available directly * on the processor bus and map them */ sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); bzero(sc->sc_range, sizeof(sc->sc_range)); sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, sizeof(sc->sc_range)); if (sc->sc_nrange == -1) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } sc->sc_range[6].pci_hi = 0; io = NULL; nmem = 0; for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: break; case OFW_PCI_PHYS_HI_SPACE_IO: io = rp; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: mem[nmem] = rp; nmem++; break; case OFW_PCI_PHYS_HI_SPACE_MEM64: break; } } if (io == NULL) { device_printf(dev, "can't find io range\n"); return (ENXIO); } sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "Grackle PCI I/O Ports"; sc->sc_iostart = io->pci_iospace; if (rman_init(&sc->sc_io_rman) != 0 || rman_manage_region(&sc->sc_io_rman, io->pci_lo, io->pci_lo + io->size_lo) != 0) { panic("grackle_attach: failed to set up I/O rman"); } if (nmem == 0) { device_printf(dev, "can't find mem ranges\n"); return (ENXIO); } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "Grackle PCI Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } for (i = 0; i < nmem; i++) { error = rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, mem[i]->pci_lo + mem[i]->size_lo); if (error) { device_printf(dev, "rman_manage_region() failed. error = %d\n", error); return (error); } } + ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); + + /* We need the number of interrupt cells to read the imap */ + if (OF_getprop(node, "interrupt-parent", &iparent,sizeof(iparent)) <= 0) + iparent = node; + + if (OF_getprop(iparent,"#interrupt-cells",&sc->sc_icells, + sizeof(sc->sc_icells)) <= 0) + sc->sc_icells = 1; + device_add_child(dev, "pci", device_get_unit(dev)); return (bus_generic_attach(dev)); } static int grackle_maxslots(device_t dev) { return (PCI_SLOTMAX); } static u_int32_t grackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct grackle_softc *sc; vm_offset_t caoff; u_int32_t retval = 0xffffffff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x03); if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { /* * Config probes to non-existent devices on the * secondary bus generates machine checks. Be sure * to catch these. */ if (bus > 0) { if (badaddr((void *)sc->sc_data, 4)) { return (retval); } } switch (width) { case 1: retval = (in8rb(caoff)); break; case 2: retval = (in16rb(caoff)); break; case 4: retval = (in32rb(caoff)); break; } } grackle_disable_config(sc); return (retval); } static void grackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct grackle_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x03); if (grackle_enable_config(sc, bus, slot, func, reg)) { switch (width) { case 1: out8rb(caoff, val); (void)in8rb(caoff); break; case 2: out16rb(caoff, val); (void)in16rb(caoff); break; case 4: out32rb(caoff, val); (void)in32rb(caoff); break; } } grackle_disable_config(sc); } static int grackle_route_interrupt(device_t bus, device_t dev, int pin) { + struct grackle_softc *sc; + struct ofw_pci_register reg; + uint32_t pintr, mintr[2]; + uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; - return (0); + sc = device_get_softc(bus); + pintr = pin; + if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, + sizeof(reg), &pintr, sizeof(pintr), &mintr, + sizeof(mintr[0])*sc->sc_icells, maskbuf)) + return (mintr[0]); + + /* 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 grackle_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct grackle_softc *sc; sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = 0; return (0); case PCIB_IVAR_BUS: *result = sc->sc_bus; return (0); } return (ENOENT); } static struct resource * grackle_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 grackle_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 grackle_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 grackle_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct grackle_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) { vm_offset_t start; start = (vm_offset_t)rman_get_start(res); /* * For i/o-ports, convert the start address to the * MPC106 PCI i/o window */ if (type == SYS_RES_IOPORT) start += sc->sc_iostart; if (bootverbose) printf("grackle mapdev: start %x, 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 grackle_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 grackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { u_int32_t cfgval; /* * Unlike UniNorth, the format of the config word is the same * for local (0) and remote busses. */ cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) | GRACKLE_CFG_ENABLE; out32rb(sc->sc_addr, cfgval); (void) in32rb(sc->sc_addr); return (1); } static void grackle_disable_config(struct grackle_softc *sc) { /* * Clear the GRACKLE_CFG_ENABLE bit to prevent stray * accesses from causing config cycles */ out32rb(sc->sc_addr, 0); } static phandle_t grackle_get_node(device_t bus, device_t dev) { struct grackle_softc *sc; sc = device_get_softc(bus); /* We only have one child, the PCI bus, which needs our own node. */ return sc->sc_node; } /* * Driver to swallow Grackle host bridges from the PCI bus side. */ static int grackle_hb_probe(device_t dev) { if (pci_get_devid(dev) == 0x00021057) { device_set_desc(dev, "Grackle Host to PCI bridge"); device_quiet(dev); return (0); } return (ENXIO); } static int grackle_hb_attach(device_t dev) { return (0); } static device_method_t grackle_hb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, grackle_hb_probe), DEVMETHOD(device_attach, grackle_hb_attach), { 0, 0 } }; static driver_t grackle_hb_driver = { "grackle_hb", grackle_hb_methods, 1, }; static devclass_t grackle_hb_devclass; DRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); Index: head/sys/powerpc/powermac/gracklevar.h =================================================================== --- head/sys/powerpc/powermac/gracklevar.h (revision 186127) +++ head/sys/powerpc/powermac/gracklevar.h (revision 186128) @@ -1,69 +1,72 @@ /*- * Copyright 2003 by Peter Grehan. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ #ifndef _POWERPC_POWERMAC_GRACKLEVAR_H_ #define _POWERPC_POWERMAC_GRACKLEVAR_H_ struct grackle_range { u_int32_t pci_hi; u_int32_t pci_mid; u_int32_t pci_lo; u_int32_t pci_iospace; u_int32_t size_hi; u_int32_t size_lo; }; struct grackle_softc { device_t sc_dev; phandle_t sc_node; vm_offset_t sc_addr; vm_offset_t sc_data; int sc_bus; struct grackle_range sc_range[6]; int sc_nrange; int sc_iostart; struct rman sc_io_rman; struct rman sc_mem_rman; bus_space_tag_t sc_memt; bus_dma_tag_t sc_dmat; + int sc_icells; + + struct ofw_bus_iinfo sc_pci_iinfo; }; /* * Apple uses address map B for the MPC106 */ #define GRACKLE_ADDR 0xFEC00000 #define GRACKLE_DATA 0xFEE00000 /* * The high bit of the config word is 'Enable'. This should always be * set. */ #define GRACKLE_CFG_ENABLE 0x80000000 #endif /* _POWERPC_POWERMAC_GRACKLEVAR_H_ */ Index: head/sys/powerpc/powermac/macio.c =================================================================== --- head/sys/powerpc/powermac/macio.c (revision 186127) +++ head/sys/powerpc/powermac/macio.c (revision 186128) @@ -1,562 +1,564 @@ /*- * Copyright 2002 by Peter Grehan. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ /* * Driver for KeyLargo/Pangea, the MacPPC south bridge ASIC. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Macio softc */ struct macio_softc { phandle_t sc_node; vm_offset_t sc_base; vm_offset_t sc_size; struct rman sc_mem_rman; }; static MALLOC_DEFINE(M_MACIO, "macio", "macio device information"); static int macio_probe(device_t); static int macio_attach(device_t); static int macio_print_child(device_t dev, device_t child); static void macio_probe_nomatch(device_t, device_t); static struct resource *macio_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int macio_activate_resource(device_t, device_t, int, int, struct resource *); static int macio_deactivate_resource(device_t, device_t, int, int, struct resource *); static int macio_release_resource(device_t, device_t, int, int, struct resource *); static struct resource_list *macio_get_resource_list (device_t, device_t); static ofw_bus_get_devinfo_t macio_get_devinfo; /* * Bus interface definition */ static device_method_t macio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, macio_probe), DEVMETHOD(device_attach, macio_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, macio_print_child), DEVMETHOD(bus_probe_nomatch, macio_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, macio_alloc_resource), DEVMETHOD(bus_release_resource, macio_release_resource), DEVMETHOD(bus_activate_resource, macio_activate_resource), DEVMETHOD(bus_deactivate_resource, macio_deactivate_resource), DEVMETHOD(bus_get_resource_list, macio_get_resource_list), + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), + /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, macio_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 } }; static driver_t macio_pci_driver = { "macio", macio_methods, sizeof(struct macio_softc) }; devclass_t macio_devclass; DRIVER_MODULE(macio, pci, macio_pci_driver, macio_devclass, 0, 0); /* * PCI ID search table */ static struct macio_pci_dev { u_int32_t mpd_devid; char *mpd_desc; } macio_pci_devlist[] = { { 0x0017106b, "Paddington I/O Controller" }, { 0x0022106b, "KeyLargo I/O Controller" }, { 0x0025106b, "Pangea I/O Controller" }, { 0x003e106b, "Intrepid I/O Controller" }, { 0x0041106b, "K2 KeyLargo I/O Controller" }, { 0x004f106b, "Shasta I/O Controller" }, { 0, NULL } }; /* * Devices to exclude from the probe * XXX some of these may be required in the future... */ #define MACIO_QUIRK_IGNORE 0x00000001 #define MACIO_QUIRK_CHILD_HAS_INTR 0x00000002 struct macio_quirk_entry { const char *mq_name; int mq_quirks; }; static struct macio_quirk_entry macio_quirks[] = { { "escc-legacy", MACIO_QUIRK_IGNORE }, { "timer", MACIO_QUIRK_IGNORE }, { "escc", MACIO_QUIRK_CHILD_HAS_INTR }, { NULL, 0 } }; static int macio_get_quirks(const char *name) { struct macio_quirk_entry *mqe; for (mqe = macio_quirks; mqe->mq_name != NULL; mqe++) if (strcmp(name, mqe->mq_name) == 0) return (mqe->mq_quirks); return (0); } /* * Add an interrupt to the dev's resource list if present */ static void macio_add_intr(phandle_t devnode, struct macio_devinfo *dinfo) { int *intr; int i, nintr; phandle_t iparent; int icells; if (dinfo->mdi_ninterrupts >= 6) { printf("macio: device has more than 6 interrupts\n"); return; } icells = 1; if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) == sizeof(iparent)) OF_getprop(iparent, "#interrupt-cells", &icells, sizeof(icells)); nintr = OF_getprop_alloc(devnode, "interrupts", sizeof(*intr), (void **)&intr); if (nintr == -1) { nintr = OF_getprop_alloc(devnode, "AAPL,interrupts", sizeof(*intr), (void **)&intr); if (nintr == -1) return; } if (intr[0] == -1) return; for (i = 0; i < nintr; i+=icells) { resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, dinfo->mdi_ninterrupts, intr[i], intr[i], 1); dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = intr[i]; dinfo->mdi_ninterrupts++; } } static void macio_add_reg(phandle_t devnode, struct macio_devinfo *dinfo) { struct macio_reg *reg; int i, nreg; nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®); if (nreg == -1) return; for (i = 0; i < nreg; i++) { resource_list_add(&dinfo->mdi_resources, SYS_RES_MEMORY, i, reg[i].mr_base, reg[i].mr_base + reg[i].mr_size, reg[i].mr_size); } } /* * PCI probe */ static int macio_probe(device_t dev) { int i; u_int32_t devid; devid = pci_get_devid(dev); for (i = 0; macio_pci_devlist[i].mpd_desc != NULL; i++) { if (devid == macio_pci_devlist[i].mpd_devid) { device_set_desc(dev, macio_pci_devlist[i].mpd_desc); return (0); } } return (ENXIO); } /* * PCI attach: scan Open Firmware child nodes, and attach these as children * of the macio bus */ static int macio_attach(device_t dev) { struct macio_softc *sc; struct macio_devinfo *dinfo; phandle_t root; phandle_t child; phandle_t subchild; device_t cdev; u_int reg[3]; int error, quirks; sc = device_get_softc(dev); root = sc->sc_node = ofw_bus_get_node(dev); /* * Locate the device node and it's base address */ if (OF_getprop(root, "assigned-addresses", reg, sizeof(reg)) < sizeof(reg)) { return (ENXIO); } sc->sc_base = reg[2]; sc->sc_size = MACIO_REG_SIZE; sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "MacIO Device Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } error = rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size); if (error) { device_printf(dev, "rman_manage_region() failed. error = %d\n", error); return (error); } /* * Iterate through the sub-devices */ for (child = OF_child(root); child != 0; child = OF_peer(child)) { dinfo = malloc(sizeof(*dinfo), M_MACIO, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 0) { free(dinfo, M_MACIO); continue; } quirks = macio_get_quirks(dinfo->mdi_obdinfo.obd_name); if ((quirks & MACIO_QUIRK_IGNORE) != 0) { ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_MACIO); continue; } resource_list_init(&dinfo->mdi_resources); dinfo->mdi_ninterrupts = 0; macio_add_intr(child, dinfo); macio_add_reg(child, dinfo); if ((quirks & MACIO_QUIRK_CHILD_HAS_INTR) != 0) for (subchild = OF_child(child); subchild != 0; subchild = OF_peer(subchild)) macio_add_intr(subchild, dinfo); cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", dinfo->mdi_obdinfo.obd_name); resource_list_free(&dinfo->mdi_resources); ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); free(dinfo, M_MACIO); continue; } device_set_ivars(cdev, dinfo); } return (bus_generic_attach(dev)); } static int macio_print_child(device_t dev, device_t child) { struct macio_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, "mem", SYS_RES_MEMORY, "%#lx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); retval += bus_print_child_footer(dev, child); return (retval); } static void macio_probe_nomatch(device_t dev, device_t child) { struct macio_devinfo *dinfo; struct resource_list *rl; const char *type; if (bootverbose) { dinfo = device_get_ivars(child); rl = &dinfo->mdi_resources; if ((type = ofw_bus_get_type(child)) == NULL) type = "(unknown)"; device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); printf(" (no driver attached)\n"); } } static struct resource * macio_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 macio_softc *sc; int needactivate; struct resource *rv; struct rman *rm; u_long adjstart, adjend, adjcount; struct macio_devinfo *dinfo; struct resource_list_entry *rle; sc = device_get_softc(bus); dinfo = device_get_ivars(child); needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; switch (type) { case SYS_RES_MEMORY: case SYS_RES_IOPORT: rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_MEMORY, *rid); if (rle == NULL) { device_printf(bus, "no rle for %s memory %d\n", device_get_nameunit(child), *rid); return (NULL); } if (start < rle->start) adjstart = rle->start; else if (start > rle->end) adjstart = rle->end; else adjstart = start; if (end < rle->start) adjend = rle->start; else if (end > rle->end) adjend = rle->end; else adjend = end; adjcount = adjend - adjstart; rm = &sc->sc_mem_rman; break; case SYS_RES_IRQ: /* Check for passthrough from subattachments like macgpio */ if (device_get_parent(child) != bus) return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_IRQ, *rid); if (rle == NULL) { if (dinfo->mdi_ninterrupts >= 6) { device_printf(bus, "%s has more than 6 interrupts\n", device_get_nameunit(child)); return (NULL); } resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, dinfo->mdi_ninterrupts, start, start, 1); dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = start; dinfo->mdi_ninterrupts++; } return (resource_list_alloc(&dinfo->mdi_resources, bus, child, 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, adjstart, adjend, adjcount, flags, child); if (rv == NULL) { device_printf(bus, "failed to reserve resource %#lx - %#lx (%#lx) for %s\n", adjstart, adjend, adjcount, 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 macio_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 macio_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct macio_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)) { p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_base, (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 macio_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 struct resource_list * macio_get_resource_list (device_t dev, device_t child) { struct macio_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_resources); } static const struct ofw_bus_devinfo * macio_get_devinfo(device_t dev, device_t child) { struct macio_devinfo *dinfo; dinfo = device_get_ivars(child); return (&dinfo->mdi_obdinfo); } Index: head/sys/powerpc/powermac/uninorth.c =================================================================== --- head/sys/powerpc/powermac/uninorth.c (revision 186127) +++ head/sys/powerpc/powermac/uninorth.c (revision 186128) @@ -1,650 +1,675 @@ /*- * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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$ */ #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #define UNINORTH_DEBUG 0 /* * Device interface. */ static int uninorth_probe(device_t); static int uninorth_attach(device_t); /* * Bus interface. */ static int uninorth_read_ivar(device_t, device_t, int, uintptr_t *); static struct resource * uninorth_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 uninorth_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); /* * pcib interface. */ static int uninorth_maxslots(device_t); static u_int32_t uninorth_read_config(device_t, u_int, u_int, u_int, u_int, int); static void uninorth_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int uninorth_route_interrupt(device_t, device_t, int); /* * OFW Bus interface */ static phandle_t uninorth_get_node(device_t bus, device_t dev); /* * Local routines. */ static int uninorth_enable_config(struct uninorth_softc *, u_int, u_int, u_int, u_int); static void unin_enable_gmac(void); /* * Driver methods. */ static device_method_t uninorth_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uninorth_probe), DEVMETHOD(device_attach, uninorth_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, uninorth_read_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, uninorth_alloc_resource), DEVMETHOD(bus_activate_resource, uninorth_activate_resource), /* pcib interface */ DEVMETHOD(pcib_maxslots, uninorth_maxslots), DEVMETHOD(pcib_read_config, uninorth_read_config), DEVMETHOD(pcib_write_config, uninorth_write_config), DEVMETHOD(pcib_route_interrupt, uninorth_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, uninorth_get_node), { 0, 0 } }; static driver_t uninorth_driver = { "pcib", uninorth_methods, sizeof(struct uninorth_softc) }; static devclass_t uninorth_devclass; DRIVER_MODULE(uninorth, nexus, uninorth_driver, uninorth_devclass, 0, 0); static int uninorth_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "pci") != 0) return (ENXIO); if (strcmp(compatible, "uni-north") == 0) { device_set_desc(dev, "Apple UniNorth Host-PCI bridge"); return (0); } else if (strcmp(compatible,"u3-agp") == 0) { device_set_desc(dev, "Apple U3 Host-AGP bridge"); return (0); } return (ENXIO); } static int uninorth_attach(device_t dev) { struct uninorth_softc *sc; const char *compatible; - phandle_t node; - phandle_t child; + phandle_t node, child, iparent; u_int32_t reg[2], busrange[2]; struct uninorth_range *rp, *io, *mem[2]; int nmem, i, error; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) return (ENXIO); if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) return (ENXIO); sc->sc_u3 = 0; compatible = ofw_bus_get_compat(dev); if (strcmp(compatible,"u3-agp") == 0) sc->sc_u3 = 1; sc->sc_dev = dev; sc->sc_node = node; if (sc->sc_u3) { sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[1] + 0x800000, PAGE_SIZE); sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1] + 0xc00000, PAGE_SIZE); } else { sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[0] + 0x800000, PAGE_SIZE); sc->sc_data = (vm_offset_t)pmap_mapdev(reg[0] + 0xc00000, PAGE_SIZE); } sc->sc_bus = busrange[0]; bzero(sc->sc_range, sizeof(sc->sc_range)); if (sc->sc_u3) { /* * On Apple U3 systems, we have an otherwise standard * Uninorth controller driving AGP. The one difference * is that it uses a new PCI ranges format, so do the * translation. */ struct uninorth_range64 range64[6]; bzero(range64, sizeof(range64)); sc->sc_nrange = OF_getprop(node, "ranges", range64, sizeof(range64)); for (i = 0; range64[i].pci_hi != 0; i++) { sc->sc_range[i].pci_hi = range64[i].pci_hi; sc->sc_range[i].pci_mid = range64[i].pci_mid; sc->sc_range[i].pci_lo = range64[i].pci_lo; sc->sc_range[i].host = range64[i].host_lo; sc->sc_range[i].size_hi = range64[i].size_hi; sc->sc_range[i].size_lo = range64[i].size_lo; } } else { sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, sizeof(sc->sc_range)); } if (sc->sc_nrange == -1) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } sc->sc_range[6].pci_hi = 0; io = NULL; nmem = 0; for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: break; case OFW_PCI_PHYS_HI_SPACE_IO: io = rp; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: mem[nmem] = rp; nmem++; break; case OFW_PCI_PHYS_HI_SPACE_MEM64: break; } } if (io == NULL) { device_printf(dev, "can't find io range\n"); return (ENXIO); } sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "UniNorth PCI I/O Ports"; sc->sc_iostart = io->host; if (rman_init(&sc->sc_io_rman) != 0 || rman_manage_region(&sc->sc_io_rman, io->pci_lo, io->pci_lo + io->size_lo - 1) != 0) { panic("uninorth_attach: failed to set up I/O rman"); } if (nmem == 0) { device_printf(dev, "can't find mem ranges\n"); return (ENXIO); } sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "UniNorth PCI Memory"; error = rman_init(&sc->sc_mem_rman); if (error) { device_printf(dev, "rman_init() failed. error = %d\n", error); return (error); } for (i = 0; i < nmem; i++) { error = rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, mem[i]->pci_lo + mem[i]->size_lo - 1); if (error) { device_printf(dev, "rman_manage_region() failed. error = %d\n", error); return (error); } } /* * Enable the GMAC Ethernet cell if Open Firmware says it is * used. */ for (child = OF_child(node); child; child = OF_peer(child)) { char compat[32]; memset(compat, 0, sizeof(compat)); OF_getprop(child, "compatible", compat, sizeof(compat)); if (strcmp(compat, "gmac") == 0) { unin_enable_gmac(); } } + ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); + + /* We need the number of interrupt cells to read the imap */ + sc->sc_icells = 2; + if (OF_getprop(node, "interrupt-parent", &iparent,sizeof(iparent)) > 0) + OF_getprop(iparent,"#interrupt-cells",&sc->sc_icells, + sizeof(sc->sc_icells)); + device_add_child(dev, "pci", device_get_unit(dev)); return (bus_generic_attach(dev)); } static int uninorth_maxslots(device_t dev) { return (PCI_SLOTMAX); } static u_int32_t uninorth_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct uninorth_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x07); if (uninorth_enable_config(sc, bus, slot, func, reg) != 0) { switch (width) { case 1: return (in8rb(caoff)); break; case 2: return (in16rb(caoff)); break; case 4: return (in32rb(caoff)); break; } } return (0xffffffff); } static void uninorth_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct uninorth_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (reg & 0x07); if (uninorth_enable_config(sc, bus, slot, func, reg)) { switch (width) { case 1: out8rb(caoff, val); break; case 2: out16rb(caoff, val); break; case 4: out32rb(caoff, val); break; } } } static int uninorth_route_interrupt(device_t bus, device_t dev, int pin) { + struct uninorth_softc *sc; + struct ofw_pci_register reg; + uint32_t pintr, mintr[2]; + uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; - return (0); + sc = device_get_softc(bus); + pintr = pin; + if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, + sizeof(reg), &pintr, sizeof(pintr), mintr, + sizeof(mintr[0])*sc->sc_icells, maskbuf)) + return (mintr[0]); + + /* 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 uninorth_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct uninorth_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 * uninorth_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 uninorth_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 uninorth_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { void *p; struct uninorth_softc *sc; 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) { vm_offset_t start; start = (vm_offset_t)rman_get_start(res); /* * For i/o-ports, convert the start address to the * uninorth PCI i/o window */ if (type == SYS_RES_IOPORT) start += sc->sc_iostart; if (bootverbose) printf("uninorth mapdev: start %x, 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 uninorth_enable_config(struct uninorth_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { uint32_t cfgval; uint32_t pass; if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "skipslot", &pass) == 0) { if (pass == slot) return (0); } if (sc->sc_bus == bus) { /* * No slots less than 11 on the primary bus */ if (slot < 11) return (0); cfgval = (1 << slot) | (func << 8) | (reg & 0xfc); } else { cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xfc) | 1; } do { out32rb(sc->sc_addr, cfgval); } while (in32rb(sc->sc_addr) != cfgval); return (1); } static phandle_t uninorth_get_node(device_t bus, device_t dev) { struct uninorth_softc *sc; sc = device_get_softc(bus); /* We only have one child, the PCI bus, which needs our own node. */ return sc->sc_node; } /* * Driver to swallow UniNorth host bridges from the PCI bus side. */ static int unhb_probe(device_t dev) { if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_HOST) { device_set_desc(dev, "Host to PCI bridge"); device_quiet(dev); return (-10000); } return (ENXIO); } static int unhb_attach(device_t dev) { return (0); } static device_method_t unhb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, unhb_probe), DEVMETHOD(device_attach, unhb_attach), { 0, 0 } }; static driver_t unhb_driver = { "unhb", unhb_methods, 1, }; static devclass_t unhb_devclass; DRIVER_MODULE(unhb, pci, unhb_driver, unhb_devclass, 0, 0); /* * Small stub driver for the Uninorth chip itself, to allow setting * of various parameters and cell enables */ static struct unin_chip_softc *uncsc; static void unin_enable_gmac(void) { volatile u_int *clkreg; u_int32_t tmpl; if (uncsc == NULL) panic("unin_enable_gmac: device not found"); clkreg = (void *)(uncsc->sc_addr + UNIN_CLOCKCNTL); tmpl = inl(clkreg); tmpl |= UNIN_CLOCKCNTL_GMAC; outl(clkreg, tmpl); } static int unin_chip_probe(device_t dev) { const char *name; name = ofw_bus_get_name(dev); if (name == NULL) return (ENXIO); if (strcmp(name, "uni-n") != 0) return (ENXIO); device_set_desc(dev, "Apple UniNorth System Controller"); return (0); } static int unin_chip_attach(device_t dev) { phandle_t node; u_int reg[2]; uncsc = device_get_softc(dev); node = ofw_bus_get_node(dev); if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) return (ENXIO); uncsc->sc_physaddr = reg[0]; uncsc->sc_size = reg[1]; /* * Only map the first page, since that is where the registers * of interest lie. */ uncsc->sc_addr = (vm_offset_t) pmap_mapdev(reg[0], PAGE_SIZE); uncsc->sc_version = *(u_int *)uncsc->sc_addr; device_printf(dev, "Version %d\n", uncsc->sc_version); return (0); } static device_method_t unin_chip_methods[] = { /* Device interface */ DEVMETHOD(device_probe, unin_chip_probe), DEVMETHOD(device_attach, unin_chip_attach), { 0, 0 } }; static driver_t unin_chip_driver = { "unin", unin_chip_methods, sizeof(struct unin_chip_softc) }; static devclass_t unin_chip_devclass; DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0); Index: head/sys/powerpc/powermac/uninorthvar.h =================================================================== --- head/sys/powerpc/powermac/uninorthvar.h (revision 186127) +++ head/sys/powerpc/powermac/uninorthvar.h (revision 186128) @@ -1,86 +1,88 @@ /*- * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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 _POWERPC_POWERMAC_UNINORTHVAR_H_ #define _POWERPC_POWERMAC_UNINORTHVAR_H_ struct uninorth_range { u_int32_t pci_hi; u_int32_t pci_mid; u_int32_t pci_lo; u_int32_t host; u_int32_t size_hi; u_int32_t size_lo; }; struct uninorth_range64 { u_int32_t pci_hi; u_int32_t pci_mid; u_int32_t pci_lo; u_int32_t host_hi; u_int32_t host_lo; u_int32_t size_hi; u_int32_t size_lo; }; struct uninorth_softc { device_t sc_dev; phandle_t sc_node; vm_offset_t sc_addr; vm_offset_t sc_data; int sc_bus; struct uninorth_range sc_range[6]; int sc_nrange; int sc_iostart; struct rman sc_io_rman; struct rman sc_mem_rman; bus_space_tag_t sc_iot; bus_space_tag_t sc_memt; bus_dma_tag_t sc_dmat; + struct ofw_bus_iinfo sc_pci_iinfo; int sc_u3; + int sc_icells; }; struct unin_chip_softc { vm_offset_t sc_physaddr; vm_offset_t sc_addr; u_int sc_size; int sc_version; }; /* * Version register */ #define UNIN_VERS 0x0 /* * Clock-control register */ #define UNIN_CLOCKCNTL 0x20 #define UNIN_CLOCKCNTL_GMAC 0x2 #endif /* _POWERPC_POWERMAC_UNINORTHVAR_H_ */ Index: head/sys/sparc64/ebus/ebus.c =================================================================== --- head/sys/sparc64/ebus/ebus.c (revision 186127) +++ head/sys/sparc64/ebus/ebus.c (revision 186128) @@ -1,456 +1,456 @@ /*- * Copyright (c) 1999, 2000 Matthew R. Green * Copyright (c) 2001 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, 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: NetBSD: ebus.c,v 1.26 2001/09/10 16:27:53 eeh Exp */ #include __FBSDID("$FreeBSD$"); /* * UltraSPARC 5 and beyond EBus support */ #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include /* * The register, ranges and interrupt map properties are identical to the ISA * ones. */ #include struct ebus_devinfo { struct ofw_bus_devinfo edi_obdinfo; struct resource_list edi_rl; }; struct ebus_rinfo { int eri_rtype; struct rman eri_rman; struct resource *eri_res; }; struct ebus_softc { struct isa_ranges *sc_range; struct ebus_rinfo *sc_rinfo; int sc_nrange; struct ofw_bus_iinfo sc_iinfo; }; static device_probe_t ebus_probe; static device_attach_t ebus_attach; static bus_print_child_t ebus_print_child; static bus_probe_nomatch_t ebus_probe_nomatch; static bus_alloc_resource_t ebus_alloc_resource; static bus_release_resource_t ebus_release_resource; static bus_get_resource_list_t ebus_get_resource_list; static ofw_bus_get_devinfo_t ebus_get_devinfo; static struct ebus_devinfo *ebus_setup_dinfo(device_t, struct ebus_softc *, phandle_t); static void ebus_destroy_dinfo(struct ebus_devinfo *); static int ebus_print_res(struct ebus_devinfo *); static device_method_t ebus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ebus_probe), DEVMETHOD(device_attach, ebus_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, ebus_print_child), DEVMETHOD(bus_probe_nomatch, ebus_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, ebus_alloc_resource), DEVMETHOD(bus_get_resource_list, ebus_get_resource_list), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_release_resource, ebus_release_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, ebus_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 } }; static driver_t ebus_driver = { "ebus", ebus_methods, sizeof(struct ebus_softc), }; static devclass_t ebus_devclass; DRIVER_MODULE(ebus, pci, ebus_driver, ebus_devclass, 0, 0); MODULE_DEPEND(ebus, pci, 1, 1, 1); MODULE_VERSION(ebus, 1); static int ebus_probe(device_t dev) { if (pci_get_class(dev) != PCIC_BRIDGE || pci_get_vendor(dev) != 0x108e || strcmp(ofw_bus_get_name(dev), "ebus") != 0) return (ENXIO); if (pci_get_device(dev) == 0x1000) device_set_desc(dev, "PCI-EBus2 bridge"); else if (pci_get_device(dev) == 0x1100) device_set_desc(dev, "PCI-EBus3 bridge"); else return (ENXIO); return (0); } static int ebus_attach(device_t dev) { struct ebus_softc *sc; struct ebus_devinfo *edi; struct ebus_rinfo *eri; struct resource *res; device_t cdev; phandle_t node; int i, rnum, rid; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->sc_nrange = OF_getprop_alloc(node, "ranges", sizeof(*sc->sc_range), (void **)&sc->sc_range); if (sc->sc_nrange == -1) { printf("ebus_attach: could not get ranges property\n"); return (ENXIO); } sc->sc_rinfo = malloc(sizeof(*sc->sc_rinfo) * sc->sc_nrange, M_DEVBUF, M_WAITOK | M_ZERO); /* For every range, there must be a matching resource. */ for (rnum = 0; rnum < sc->sc_nrange; rnum++) { eri = &sc->sc_rinfo[rnum]; eri->eri_rtype = ofw_isa_range_restype(&sc->sc_range[rnum]); rid = PCIR_BAR(rnum); res = bus_alloc_resource_any(dev, eri->eri_rtype, &rid, RF_ACTIVE); if (res == NULL) { printf("ebus_attach: failed to allocate range " "resource!\n"); goto fail; } eri->eri_res = res; eri->eri_rman.rm_type = RMAN_ARRAY; eri->eri_rman.rm_descr = "EBus range"; if (rman_init(&eri->eri_rman) != 0) { printf("ebus_attach: failed to initialize rman!"); goto fail; } if (rman_manage_region(&eri->eri_rman, rman_get_start(res), rman_get_end(res)) != 0) { printf("ebus_attach: failed to register region!"); rman_fini(&eri->eri_rman); goto fail; } } ofw_bus_setup_iinfo(node, &sc->sc_iinfo, sizeof(ofw_isa_intr_t)); /* * Now attach our children. */ for (node = OF_child(node); node > 0; node = OF_peer(node)) { if ((edi = ebus_setup_dinfo(dev, sc, node)) == NULL) continue; if ((cdev = device_add_child(dev, NULL, -1)) == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", edi->edi_obdinfo.obd_name); ebus_destroy_dinfo(edi); continue; } device_set_ivars(cdev, edi); } return (bus_generic_attach(dev)); fail: for (i = rnum; i >= 0; i--) { eri = &sc->sc_rinfo[i]; if (i < rnum) rman_fini(&eri->eri_rman); if (eri->eri_res != 0) { bus_release_resource(dev, eri->eri_rtype, PCIR_BAR(rnum), eri->eri_res); } } free(sc->sc_rinfo, M_DEVBUF); free(sc->sc_range, M_OFWPROP); return (ENXIO); } static int ebus_print_child(device_t dev, device_t child) { int retval; retval = bus_print_child_header(dev, child); retval += ebus_print_res(device_get_ivars(child)); retval += bus_print_child_footer(dev, child); return (retval); } static void ebus_probe_nomatch(device_t dev, device_t child) { device_printf(dev, "<%s>", ofw_bus_get_name(child)); ebus_print_res(device_get_ivars(child)); printf(" (no driver attached)\n"); } static struct resource * ebus_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 ebus_softc *sc; struct resource_list *rl; struct resource_list_entry *rle = NULL; struct resource *res; struct ebus_rinfo *ri; bus_space_tag_t bt; bus_space_handle_t bh; int passthrough = (device_get_parent(child) != bus); int isdefault = (start == 0UL && end == ~0UL); int ridx, rv; sc = (struct ebus_softc *)device_get_softc(bus); rl = BUS_GET_RESOURCE_LIST(bus, child); /* * Map EBus ranges to PCI ranges. This may include changing the * allocation type. */ switch (type) { case SYS_RES_MEMORY: KASSERT(!(isdefault && passthrough), ("ebus_alloc_resource: passthrough of default alloc")); if (!passthrough) { rle = resource_list_find(rl, type, *rid); if (rle == NULL) return (NULL); KASSERT(rle->res == NULL, ("ebus_alloc_resource: resource entry is busy")); if (isdefault) { start = rle->start; count = ulmax(count, rle->count); end = ulmax(rle->end, start + count - 1); } } (void)ofw_isa_range_map(sc->sc_range, sc->sc_nrange, &start, &end, &ridx); ri = &sc->sc_rinfo[ridx]; res = rman_reserve_resource(&ri->eri_rman, start, end, count, flags, child); if (res == NULL) return (NULL); rman_set_rid(res, *rid); bt = rman_get_bustag(ri->eri_res); rman_set_bustag(res, bt); rv = bus_space_subregion(bt, rman_get_bushandle(ri->eri_res), rman_get_start(res) - rman_get_start(ri->eri_res), count, &bh); if (rv != 0) { rman_release_resource(res); return (NULL); } rman_set_bushandle(res, bh); if (!passthrough) rle->res = res; return (res); case SYS_RES_IRQ: return (resource_list_alloc(rl, bus, child, type, rid, start, end, count, flags)); } return (NULL); } static int ebus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { struct resource_list *rl; struct resource_list_entry *rle; int passthrough = (device_get_parent(child) != bus); int rv; rl = BUS_GET_RESOURCE_LIST(bus, child); switch (type) { case SYS_RES_MEMORY: if ((rv = rman_release_resource(res)) != 0) return (rv); if (!passthrough) { rle = resource_list_find(rl, type, rid); KASSERT(rle != NULL, ("ebus_release_resource: " "resource entry not found!")); KASSERT(rle->res != NULL, ("ebus_alloc_resource: " "resource entry is not busy")); rle->res = NULL; } break; case SYS_RES_IRQ: return (resource_list_release(rl, bus, child, type, rid, res)); default: panic("ebus_release_resource: unsupported resource type %d", type); } return (0); } static struct resource_list * ebus_get_resource_list(device_t dev, device_t child) { struct ebus_devinfo *edi; edi = device_get_ivars(child); return (&edi->edi_rl); } static const struct ofw_bus_devinfo * ebus_get_devinfo(device_t bus, device_t dev) { struct ebus_devinfo *edi; edi = device_get_ivars(dev); return (&edi->edi_obdinfo); } static struct ebus_devinfo * ebus_setup_dinfo(device_t dev, struct ebus_softc *sc, phandle_t node) { struct ebus_devinfo *edi; struct isa_regs *reg; ofw_isa_intr_t *intrs; ofw_pci_intr_t rintr; u_int64_t start; int nreg, nintr, i; edi = malloc(sizeof(*edi), M_DEVBUF, M_ZERO | M_WAITOK); if (ofw_bus_gen_setup_devinfo(&edi->edi_obdinfo, node) != 0) { free(edi, M_DEVBUF); return (NULL); } resource_list_init(&edi->edi_rl); nreg = OF_getprop_alloc(node, "reg", sizeof(*reg), (void **)®); if (nreg == -1) { device_printf(dev, "<%s>: incomplete\n", edi->edi_obdinfo.obd_name); goto fail; } for (i = 0; i < nreg; i++) { start = ISA_REG_PHYS(reg + i); resource_list_add(&edi->edi_rl, SYS_RES_MEMORY, i, start, start + reg[i].size - 1, reg[i].size); } free(reg, M_OFWPROP); nintr = OF_getprop_alloc(node, "interrupts", sizeof(*intrs), (void **)&intrs); for (i = 0; i < nintr; i++) { rintr = ofw_isa_route_intr(dev, node, &sc->sc_iinfo, intrs[i]); if (rintr == PCI_INVALID_IRQ) { device_printf(dev, "<%s>: could not map EBus interrupt %d\n", edi->edi_obdinfo.obd_name, intrs[i]); free(intrs, M_OFWPROP); goto fail; } resource_list_add(&edi->edi_rl, SYS_RES_IRQ, i, rintr, rintr, 1); } free(intrs, M_OFWPROP); return (edi); fail: ebus_destroy_dinfo(edi); return (NULL); } static void ebus_destroy_dinfo(struct ebus_devinfo *edi) { resource_list_free(&edi->edi_rl); ofw_bus_gen_destroy_devinfo(&edi->edi_obdinfo); free(edi, M_DEVBUF); } static int ebus_print_res(struct ebus_devinfo *edi) { int retval; retval = 0; retval += resource_list_print_type(&edi->edi_rl, "addr", SYS_RES_MEMORY, "%#lx"); retval += resource_list_print_type(&edi->edi_rl, "irq", SYS_RES_IRQ, "%ld"); return (retval); } Index: head/sys/sparc64/include/ofw_bus.h =================================================================== --- head/sys/sparc64/include/ofw_bus.h (revision 186127) +++ head/sys/sparc64/include/ofw_bus.h (nonexistent) @@ -1,53 +0,0 @@ -/*- - * Copyright (c) 2001 - 2003 by 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, 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 _MACHINE_OFW_BUS_H_ -#define _MACHINE_OFW_BUS_H_ - -#define ORIP_NOINT -1 -#define ORIR_NOTFOUND 0xffffffff - -/* - * Other than in Open Firmware calls, the size of a bus cell seems to be - * always the same. - */ -typedef u_int32_t pcell_t; - -struct ofw_bus_iinfo { - u_int8_t *opi_imap; - u_int8_t *opi_imapmsk; - int opi_imapsz; - pcell_t opi_addrc; -}; - -void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int); -int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int, - void *, int, void *, int, void *); -int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *, - void *, void *, int); - -#endif /* !_MACHINE_OFW_BUS_H_ */ Property changes on: head/sys/sparc64/include/ofw_bus.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/sparc64/isa/ofw_isa.c =================================================================== --- head/sys/sparc64/isa/ofw_isa.c (revision 186127) +++ head/sys/sparc64/isa/ofw_isa.c (revision 186128) @@ -1,121 +1,121 @@ /*- * Copyright (c) 1999, 2000 Matthew R. Green * Copyright (c) 2001, 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, 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: NetBSD: ebus.c,v 1.26 2001/09/10 16:27:53 eeh Exp */ #include __FBSDID("$FreeBSD$"); /* * Helper functions which can be used in both ISA and EBus code. */ #include #include #include #include +#include #include #include -#include #include #include #include "pcib_if.h" int ofw_isa_range_restype(struct isa_ranges *range) { int ps = ISA_RANGE_PS(range); switch (ps) { case OFW_PCI_CS_IO: return (SYS_RES_IOPORT); case OFW_PCI_CS_MEM32: return (SYS_RES_MEMORY); default: panic("ofw_isa_range_restype: illegal space %x", ps); } } /* XXX: this only supports PCI as parent bus right now. */ int ofw_isa_range_map(struct isa_ranges *range, int nrange, u_long *start, u_long *end, int *which) { struct isa_ranges *r; uint64_t offs, cstart, cend; int i; for (i = 0; i < nrange; i++) { r = &range[i]; cstart = ISA_RANGE_CHILD(r); cend = cstart + r->size; if (*start < cstart || *start > cend) continue; if (*end < cstart || *end > cend) { panic("ofw_isa_map_iorange: iorange crosses pci " "ranges (%#lx not in %#lx - %#lx)", *end, cstart, cend); } offs = ISA_RANGE_PHYS(r); *start = *start + offs - cstart; *end = *end + offs - cstart; if (which != NULL) *which = i; return (ofw_isa_range_restype(r)); } panic("ofw_isa_map_iorange: could not map range %#lx - %#lx", *start, *end); } ofw_pci_intr_t ofw_isa_route_intr(device_t bridge, phandle_t node, struct ofw_bus_iinfo *ii, ofw_isa_intr_t intr) { struct isa_regs reg; uint8_t maskbuf[sizeof(reg) + sizeof(intr)]; device_t pbridge; ofw_isa_intr_t mintr; pbridge = device_get_parent(device_get_parent(bridge)); /* * If we get a match from using the map, the resulting INO is * fully specified, so we may not continue to map. */ if (!ofw_bus_lookup_imap(node, ii, ®, sizeof(reg), &intr, sizeof(intr), &mintr, sizeof(mintr), maskbuf)) { /* Try routing at the parent bridge. */ mintr = PCIB_ROUTE_INTERRUPT(pbridge, bridge, intr); } return (mintr); } Index: head/sys/sparc64/pci/apb.c =================================================================== --- head/sys/sparc64/pci/apb.c (revision 186127) +++ head/sys/sparc64/pci/apb.c (revision 186128) @@ -1,276 +1,275 @@ /*- * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * Copyright (c) 2001, 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, 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: FreeBSD: src/sys/dev/pci/pci_pci.c,v 1.3 2000/12/13 */ #include __FBSDID("$FreeBSD$"); /* * Support for the Sun APB (Advanced PCI Bridge) PCI-PCI bridge. * This bridge does not fully comply to the PCI bridge specification, and is * therefore not supported by the generic driver. * We can use some of the pcib methods anyway. */ #include "opt_ofw_pci.h" #include #include #include #include #include #include #include #include -#include #include #include #include #include #include "pcib_if.h" #include #include /* * Bridge-specific data. */ struct apb_softc { struct ofw_pcib_gen_softc sc_bsc; uint8_t sc_iomap; uint8_t sc_memmap; }; static device_probe_t apb_probe; static device_attach_t apb_attach; static bus_alloc_resource_t apb_alloc_resource; static device_method_t apb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, apb_probe), DEVMETHOD(device_attach, apb_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, apb_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_maxslots), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, ofw_pcib_gen_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pcib_gen_get_node), { 0, 0 } }; static devclass_t pcib_devclass; DEFINE_CLASS_0(pcib, apb_driver, apb_methods, sizeof(struct apb_softc)); DRIVER_MODULE(apb, pci, apb_driver, pcib_devclass, 0, 0); /* APB specific registers */ #define APBR_IOMAP 0xde #define APBR_MEMMAP 0xdf /* Definitions for the mapping registers */ #define APB_IO_SCALE 0x200000 #define APB_MEM_SCALE 0x20000000 /* * Generic device interface */ static int apb_probe(device_t dev) { if (pci_get_vendor(dev) == 0x108e && /* Sun */ pci_get_device(dev) == 0x5000) { /* APB */ device_set_desc(dev, "APB PCI-PCI bridge"); return (0); } return (ENXIO); } static void apb_map_print(uint8_t map, u_long scale) { int i, first; for (first = 1, i = 0; i < 8; i++) { if ((map & (1 << i)) != 0) { printf("%s0x%lx-0x%lx", first ? "" : ", ", i * scale, (i + 1) * scale - 1); first = 0; } } } static int apb_checkrange(uint8_t map, u_long scale, u_long start, u_long end) { int i, ei; i = start / scale; ei = end / scale; if (i > 7 || ei > 7) return (0); for (; i <= ei; i++) if ((map & (1 << i)) == 0) return (0); return (1); } static int apb_attach(device_t dev) { struct apb_softc *sc; sc = device_get_softc(dev); /* * Get current bridge configuration. */ sc->sc_bsc.ops_pcib_sc.domain = pci_get_domain(dev); sc->sc_bsc.ops_pcib_sc.secbus = pci_read_config(dev, PCIR_SECBUS_1, 1); sc->sc_bsc.ops_pcib_sc.subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1); sc->sc_iomap = pci_read_config(dev, APBR_IOMAP, 1); sc->sc_memmap = pci_read_config(dev, APBR_MEMMAP, 1); ofw_pcib_gen_setup(dev); if (bootverbose) { device_printf(dev, " domain %d\n", sc->sc_bsc.ops_pcib_sc.domain); device_printf(dev, " secondary bus %d\n", sc->sc_bsc.ops_pcib_sc.secbus); device_printf(dev, " subordinate bus %d\n", sc->sc_bsc.ops_pcib_sc.subbus); device_printf(dev, " I/O decode "); apb_map_print(sc->sc_iomap, APB_IO_SCALE); printf("\n"); device_printf(dev, " memory decode "); apb_map_print(sc->sc_memmap, APB_MEM_SCALE); printf("\n"); } device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } /* * We have to trap resource allocation requests and ensure that the bridge * is set up to, or capable of handling them. */ static struct resource * apb_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct apb_softc *sc; sc = device_get_softc(dev); /* * If this is a "default" allocation against this rid, we can't work * out where it's coming from (we should actually never see these) so * we just have to punt. */ if (start == 0 && end == ~0) { device_printf(dev, "can't decode default resource id %d for " "%s%d, bypassing\n", *rid, device_get_name(child), device_get_unit(child)); goto passup; } /* * Fail the allocation for this range if it's not supported. * XXX we should probably just fix up the bridge decode and * soldier on. */ switch (type) { case SYS_RES_IOPORT: if (!apb_checkrange(sc->sc_iomap, APB_IO_SCALE, start, end)) { device_printf(dev, "device %s%d requested unsupported " "I/O range 0x%lx-0x%lx\n", device_get_name(child), device_get_unit(child), start, end); return (NULL); } if (bootverbose) device_printf(sc->sc_bsc.ops_pcib_sc.dev, "device " "%s%d requested decoded I/O range 0x%lx-0x%lx\n", device_get_name(child), device_get_unit(child), start, end); break; case SYS_RES_MEMORY: if (!apb_checkrange(sc->sc_memmap, APB_MEM_SCALE, start, end)) { device_printf(dev, "device %s%d requested unsupported " "memory range 0x%lx-0x%lx\n", device_get_name(child), device_get_unit(child), start, end); return (NULL); } if (bootverbose) device_printf(sc->sc_bsc.ops_pcib_sc.dev, "device " "%s%d requested decoded memory range 0x%lx-0x%lx\n", device_get_name(child), device_get_unit(child), start, end); break; default: break; } passup: /* * Bridge is OK decoding this resource, so pass it up. */ return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } Index: head/sys/sparc64/pci/ofw_pci.h =================================================================== --- head/sys/sparc64/pci/ofw_pci.h (revision 186127) +++ head/sys/sparc64/pci/ofw_pci.h (revision 186128) @@ -1,68 +1,68 @@ /*- * Copyright (c) 1999, 2000 Matthew R. Green * Copyright (c) 2001, 2003 by 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, 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: NetBSD: psychoreg.h,v 1.8 2001/09/10 16:17:06 eeh Exp * * $FreeBSD$ */ #ifndef _SPARC64_PCI_OFW_PCI_H_ #define _SPARC64_PCI_OFW_PCI_H_ -#include +#include typedef uint32_t ofw_pci_intr_t; /* PCI range child spaces. XXX: are these MI? */ #define OFW_PCI_CS_CONFIG 0x00 #define OFW_PCI_CS_IO 0x01 #define OFW_PCI_CS_MEM32 0x02 #define OFW_PCI_CS_MEM64 0x03 struct ofw_pci_ranges { uint32_t cspace; uint32_t child_hi; uint32_t child_lo; uint32_t phys_hi; uint32_t phys_lo; uint32_t size_hi; uint32_t size_lo; }; #define OFW_PCI_RANGE_CHILD(r) \ (((uint64_t)(r)->child_hi << 32) | (uint64_t)(r)->child_lo) #define OFW_PCI_RANGE_PHYS(r) \ (((uint64_t)(r)->phys_hi << 32) | (uint64_t)(r)->phys_lo) #define OFW_PCI_RANGE_SIZE(r) \ (((uint64_t)(r)->size_hi << 32) | (uint64_t)(r)->size_lo) #define OFW_PCI_RANGE_CS(r) (((r)->cspace >> 24) & 0x03) /* default values */ #define OFW_PCI_LATENCY 64 #endif /* ! _SPARC64_PCI_OFW_PCI_H_ */ Index: head/sys/sparc64/pci/ofw_pcib.c =================================================================== --- head/sys/sparc64/pci/ofw_pcib.c (revision 186127) +++ head/sys/sparc64/pci/ofw_pcib.c (revision 186128) @@ -1,120 +1,119 @@ /*- * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * Copyright (c) 2001 - 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, 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: FreeBSD: src/sys/dev/pci/pci_pci.c,v 1.3 2000/12/13 */ #include __FBSDID("$FreeBSD$"); #include "opt_ofw_pci.h" #include #include #include #include #include #include #include -#include #include #include #include #include "pcib_if.h" #include #include static device_probe_t ofw_pcib_probe; static device_attach_t ofw_pcib_attach; static device_method_t ofw_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_pcib_probe), DEVMETHOD(device_attach, ofw_pcib_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_maxslots), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, ofw_pcib_gen_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ofw_pcib_gen_get_node), { 0, 0 } }; static devclass_t pcib_devclass; DEFINE_CLASS_0(pcib, ofw_pcib_driver, ofw_pcib_methods, sizeof(struct ofw_pcib_gen_softc)); DRIVER_MODULE(ofw_pcib, pci, ofw_pcib_driver, pcib_devclass, 0, 0); static int ofw_pcib_probe(device_t dev) { if ((pci_get_class(dev) == PCIC_BRIDGE) && (pci_get_subclass(dev) == PCIS_BRIDGE_PCI) && ofw_bus_get_node(dev) != 0) { device_set_desc(dev, "OFW PCI-PCI bridge"); return (0); } return (ENXIO); } static int ofw_pcib_attach(device_t dev) { ofw_pcib_gen_setup(dev); pcib_attach_common(dev); device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } Index: head/sys/sparc64/pci/ofw_pcib_subr.c =================================================================== --- head/sys/sparc64/pci/ofw_pcib_subr.c (revision 186127) +++ head/sys/sparc64/pci/ofw_pcib_subr.c (revision 186128) @@ -1,108 +1,107 @@ /*- * Copyright (c) 2003 by 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, 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_ofw_pci.h" #include #include #include #include #include #include #include -#include #include #include #include #include "pcib_if.h" #include #include void ofw_pcib_gen_setup(device_t bridge) { struct ofw_pcib_gen_softc *sc; sc = device_get_softc(bridge); sc->ops_pcib_sc.dev = bridge; sc->ops_node = ofw_bus_get_node(bridge); KASSERT(sc->ops_node != 0, ("ofw_pcib_gen_setup: no ofw pci parent bus!")); ofw_bus_setup_iinfo(sc->ops_node, &sc->ops_iinfo, sizeof(ofw_pci_intr_t)); } int ofw_pcib_gen_route_interrupt(device_t bridge, device_t dev, int intpin) { struct ofw_pcib_gen_softc *sc; struct ofw_bus_iinfo *ii; struct ofw_pci_register reg; ofw_pci_intr_t pintr, mintr; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bridge); ii = &sc->ops_iinfo; if (ii->opi_imapsz > 0) { pintr = intpin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), 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 (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)); } /* Try at the parent. */ return (PCIB_ROUTE_INTERRUPT(device_get_parent(device_get_parent( bridge)), bridge, intpin)); } phandle_t ofw_pcib_gen_get_node(device_t bridge, device_t dev) { struct ofw_pcib_gen_softc *sc; sc = device_get_softc(bridge); return (sc->ops_node); } Index: head/sys/sparc64/pci/ofw_pcibus.c =================================================================== --- head/sys/sparc64/pci/ofw_pcibus.c (revision 186127) +++ head/sys/sparc64/pci/ofw_pcibus.c (revision 186128) @@ -1,304 +1,321 @@ /*- * 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 "opt_ofw_pci.h" #include #include #include #include #include #include #include -#include #include #include #include #ifndef SUN4V #include #include #endif #include #include #include #include #include #include "pcib_if.h" #include "pci_if.h" /* Helper functions */ static void ofw_pcibus_setup_device(device_t bridge, uint32_t clock, u_int busno, u_int slot, u_int func); /* 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 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, 1 /* no 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_pcibus_probe(device_t dev) { if (ofw_bus_get_node(dev) == 0) return (ENXIO); device_set_desc(dev, "OFW PCI bus"); return (0); } /* * Perform miscellaneous setups the firmware usually does not do for us. */ static void ofw_pcibus_setup_device(device_t bridge, uint32_t clock, u_int busno, u_int slot, u_int func) { #ifndef SUN4V uint32_t reg; /* * Initialize the latency timer register for busmaster devices to * work properly. This is another task which the firmware doesn't * always perform. The Min_Gnt register can be used to compute its * recommended value: it contains the desired latency in units of * 1/4 us assuming a clock rate of 33MHz. To calculate the correct * latency timer value, the clock frequency of the bus (defaulting * to 33MHz) should be used and no wait states assumed. * For bridges, we additionally set up the bridge control and the * secondary latency registers. */ if ((PCIB_READ_CONFIG(bridge, busno, slot, func, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE) { reg = PCIB_READ_CONFIG(bridge, busno, slot, func, PCIR_BRIDGECTL_1, 1); reg |= PCIB_BCR_MASTER_ABORT_MODE | PCIB_BCR_SERR_ENABLE | PCIB_BCR_PERR_ENABLE; #ifdef OFW_PCI_DEBUG device_printf(bridge, "bridge %d/%d/%d: control 0x%x -> 0x%x\n", busno, slot, func, PCIB_READ_CONFIG(bridge, busno, slot, func, PCIR_BRIDGECTL_1, 1), reg); #endif /* OFW_PCI_DEBUG */ PCIB_WRITE_CONFIG(bridge, busno, slot, func, PCIR_BRIDGECTL_1, reg, 1); reg = OFW_PCI_LATENCY; #ifdef OFW_PCI_DEBUG device_printf(bridge, "bridge %d/%d/%d: latency timer %d -> %d\n", busno, slot, func, PCIB_READ_CONFIG(bridge, busno, slot, func, PCIR_SECLAT_1, 1), reg); #endif /* OFW_PCI_DEBUG */ PCIB_WRITE_CONFIG(bridge, busno, slot, func, PCIR_SECLAT_1, reg, 1); } else { reg = PCIB_READ_CONFIG(bridge, busno, slot, func, PCIR_MINGNT, 1); if (reg != 0) { switch (clock) { case 33000000: reg *= 8; break; case 66000000: reg *= 4; break; } reg = min(reg, 255); } else reg = OFW_PCI_LATENCY; } #ifdef OFW_PCI_DEBUG device_printf(bridge, "device %d/%d/%d: latency timer %d -> %d\n", busno, slot, func, PCIB_READ_CONFIG(bridge, busno, slot, func, PCIR_LATTIMER, 1), reg); #endif /* OFW_PCI_DEBUG */ PCIB_WRITE_CONFIG(bridge, busno, slot, func, PCIR_LATTIMER, reg, 1); /* * Compute a value to write into the cache line size register. * The role of the streaming cache is unclear in write invalidate * transfers, so it is made sure that it's line size is always * reached. Generally, the cache line size is fixed at 64 bytes * by Fireplane/Safari, JBus and UPA. */ PCIB_WRITE_CONFIG(bridge, busno, slot, func, PCIR_CACHELNSZ, STRBUF_LINESZ / sizeof(uint32_t), 1); #endif /* * The preset in the intline register is usually wrong. Reset * it to 255, so that the PCI code will reroute the interrupt if * needed. */ PCIB_WRITE_CONFIG(bridge, busno, slot, func, PCIR_INTLINE, PCI_INVALID_IRQ, 1); } static int ofw_pcibus_attach(device_t dev) { device_t pcib; struct ofw_pci_register pcir; struct ofw_pcibus_devinfo *dinfo; phandle_t node, child; uint32_t clock; u_int busno, domain, func, slot; pcib = device_get_parent(dev); domain = pcib_get_domain(dev); busno = pcib_get_bus(dev); if (bootverbose) device_printf(dev, "domain=%d, physical bus=%d\n", domain, busno); node = ofw_bus_get_node(dev); #ifndef SUN4V /* Add the PCI side of the HOST-PCI bridge itself to the bus. */ if (strcmp(device_get_name(device_get_parent(pcib)), "nexus") == 0 && (dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, domain, busno, 0, 0, sizeof(*dinfo))) != NULL) { if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, node) != 0) pci_freecfg((struct pci_devinfo *)dinfo); else pci_add_child(dev, (struct pci_devinfo *)dinfo); } #endif if (OF_getprop(ofw_bus_get_node(pcib), "clock-frequency", &clock, sizeof(clock)) == -1) clock = 33000000; 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; ofw_pcibus_setup_device(pcib, clock, busno, slot, func); 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); } return (bus_generic_attach(dev)); } static int ofw_pcibus_assign_interrupt(device_t dev, device_t child) { ofw_pci_intr_t intr; int isz; isz = OF_getprop(ofw_bus_get_node(child), "interrupts", &intr, sizeof(intr)); if (isz != sizeof(intr)) { /* No property; our best guess is the intpin. */ intr = pci_get_intpin(child); #ifndef SUN4V } else if (intr >= 255) { /* * A fully specified interrupt (including IGN), as present on * SPARCengine Ultra AX and E450. Extract the INO and return * it. */ return (INTINO(intr)); #endif } /* * 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); } + +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); +} + Index: head/sys/sparc64/pci/psycho.c =================================================================== --- head/sys/sparc64/pci/psycho.c (revision 186127) +++ head/sys/sparc64/pci/psycho.c (revision 186128) @@ -1,1394 +1,1393 @@ /*- * Copyright (c) 1999, 2000 Matthew R. Green * Copyright (c) 2001 - 2003 by Thomas Moestl * Copyright (c) 2005 - 2006 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: NetBSD: psycho.c,v 1.39 2001/10/07 20:30:41 eeh Exp */ #include __FBSDID("$FreeBSD$"); /* * Support for `Hummingbird' (UltraSPARC IIe), `Psycho' and `Psycho+' * (UltraSPARC II) and `Sabre' (UltraSPARC IIi) UPA to PCI bridges. */ #include "opt_ofw_pci.h" #include "opt_psycho.h" #include #include #include #include #include #include #include #include #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" static const struct psycho_desc *psycho_find_desc(const struct psycho_desc *, const char *); static const struct psycho_desc *psycho_get_desc(device_t); static void psycho_set_intr(struct psycho_softc *, u_int, bus_addr_t, driver_filter_t, driver_intr_t); static int psycho_find_intrmap(struct psycho_softc *, u_int, bus_addr_t *, bus_addr_t *, u_long *); static driver_filter_t psycho_dmasync; static void psycho_intr_enable(void *); static void psycho_intr_disable(void *); static void psycho_intr_assign(void *); static void psycho_intr_clear(void *); static bus_space_tag_t psycho_alloc_bus_tag(struct psycho_softc *, int); /* Interrupt handlers */ static driver_filter_t psycho_ue; static driver_filter_t psycho_ce; static driver_filter_t psycho_pci_bus; static driver_filter_t psycho_powerfail; static driver_intr_t psycho_overtemp; #ifdef PSYCHO_MAP_WAKEUP static driver_filter_t psycho_wakeup; #endif /* IOMMU support */ static void psycho_iommu_init(struct psycho_softc *, int, uint32_t); /* * Methods */ static device_probe_t psycho_probe; static device_attach_t psycho_attach; static bus_read_ivar_t psycho_read_ivar; static bus_setup_intr_t psycho_setup_intr; static bus_teardown_intr_t psycho_teardown_intr; static bus_alloc_resource_t psycho_alloc_resource; static bus_activate_resource_t psycho_activate_resource; static bus_deactivate_resource_t psycho_deactivate_resource; static bus_release_resource_t psycho_release_resource; static bus_get_dma_tag_t psycho_get_dma_tag; static pcib_maxslots_t psycho_maxslots; static pcib_read_config_t psycho_read_config; static pcib_write_config_t psycho_write_config; static pcib_route_interrupt_t psycho_route_interrupt; static ofw_bus_get_node_t psycho_get_node; static device_method_t psycho_methods[] = { /* Device interface */ DEVMETHOD(device_probe, psycho_probe), DEVMETHOD(device_attach, psycho_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, psycho_read_ivar), DEVMETHOD(bus_setup_intr, psycho_setup_intr), DEVMETHOD(bus_teardown_intr, psycho_teardown_intr), DEVMETHOD(bus_alloc_resource, psycho_alloc_resource), DEVMETHOD(bus_activate_resource, psycho_activate_resource), DEVMETHOD(bus_deactivate_resource, psycho_deactivate_resource), DEVMETHOD(bus_release_resource, psycho_release_resource), DEVMETHOD(bus_get_dma_tag, psycho_get_dma_tag), /* pcib interface */ DEVMETHOD(pcib_maxslots, psycho_maxslots), DEVMETHOD(pcib_read_config, psycho_read_config), DEVMETHOD(pcib_write_config, psycho_write_config), DEVMETHOD(pcib_route_interrupt, psycho_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, psycho_get_node), { 0, 0 } }; static devclass_t psycho_devclass; DEFINE_CLASS_0(pcib, psycho_driver, psycho_methods, sizeof(struct psycho_softc)); DRIVER_MODULE(psycho, nexus, psycho_driver, psycho_devclass, 0, 0); static SLIST_HEAD(, psycho_softc) psycho_softcs = SLIST_HEAD_INITIALIZER(psycho_softcs); static const struct intr_controller psycho_ic = { psycho_intr_enable, psycho_intr_disable, psycho_intr_assign, psycho_intr_clear }; struct psycho_icarg { struct psycho_softc *pica_sc; bus_addr_t pica_map; bus_addr_t pica_clr; }; struct psycho_dmasync { struct psycho_softc *pds_sc; driver_filter_t *pds_handler; /* handler to call */ void *pds_arg; /* argument for the handler */ void *pds_cookie; /* parent bus int. cookie */ device_t pds_ppb; /* farest PCI-PCI bridge */ uint8_t pds_bus; /* bus of farest PCI device */ uint8_t pds_slot; /* slot of farest PCI device */ uint8_t pds_func; /* func. of farest PCI device */ }; #define PSYCHO_READ8(sc, off) \ bus_read_8((sc)->sc_mem_res, (off)) #define PSYCHO_WRITE8(sc, off, v) \ bus_write_8((sc)->sc_mem_res, (off), (v)) #define PCICTL_READ8(sc, off) \ PSYCHO_READ8((sc), (sc)->sc_pcictl + (off)) #define PCICTL_WRITE8(sc, off, v) \ PSYCHO_WRITE8((sc), (sc)->sc_pcictl + (off), (v)) /* * "Sabre" is the UltraSPARC IIi onboard UPA to PCI bridge. It manages a * single PCI bus and does not have a streaming buffer. It often has an APB * (advanced PCI bridge) connected to it, which was designed specifically for * the IIi. The APB let's the IIi handle two independednt PCI buses, and * appears as two "Simba"'s underneath the Sabre. * * "Hummingbird" is the UltraSPARC IIe onboard UPA to PCI bridge. It's * basically the same as Sabre but without an APB underneath it. * * "Psycho" and "Psycho+" are dual UPA to PCI bridges. They sit on the UPA bus * and manage two PCI buses. "Psycho" has two 64-bit 33MHz buses, while * "Psycho+" controls both a 64-bit 33Mhz and a 64-bit 66Mhz PCI bus. You * will usually find a "Psycho+" since I don't think the original "Psycho" * ever shipped, and if it did it would be in the U30. * * Each "Psycho" PCI bus appears as a separate OFW node, but since they are * both part of the same IC, they only have a single register space. As such, * they need to be configured together, even though the autoconfiguration will * attach them separately. * * On UltraIIi machines, "Sabre" itself usually takes pci0, with "Simba" often * as pci1 and pci2, although they have been implemented with other PCI bus * numbers on some machines. * * On UltraII machines, there can be any number of "Psycho+" ICs, each * providing two PCI buses. */ #define OFW_PCI_TYPE "pci" struct psycho_desc { const char *pd_string; int pd_mode; const char *pd_name; }; static const struct psycho_desc psycho_compats[] = { { "pci108e,8000", PSYCHO_MODE_PSYCHO, "Psycho compatible" }, { "pci108e,a000", PSYCHO_MODE_SABRE, "Sabre compatible" }, { "pci108e,a001", PSYCHO_MODE_SABRE, "Hummingbird compatible" }, { NULL, 0, NULL } }; static const struct psycho_desc psycho_models[] = { { "SUNW,psycho", PSYCHO_MODE_PSYCHO, "Psycho" }, { "SUNW,sabre", PSYCHO_MODE_SABRE, "Sabre" }, { NULL, 0, NULL } }; static const struct psycho_desc * psycho_find_desc(const struct psycho_desc *table, const char *string) { const struct psycho_desc *desc; if (string == NULL) return (NULL); for (desc = table; desc->pd_string != NULL; desc++) if (strcmp(desc->pd_string, string) == 0) return (desc); return (NULL); } static const struct psycho_desc * psycho_get_desc(device_t dev) { const struct psycho_desc *rv; rv = psycho_find_desc(psycho_models, ofw_bus_get_model(dev)); if (rv == NULL) rv = psycho_find_desc(psycho_compats, ofw_bus_get_compat(dev)); return (rv); } static int psycho_probe(device_t dev) { const char *dtype; dtype = ofw_bus_get_type(dev); if (dtype != NULL && strcmp(dtype, OFW_PCI_TYPE) == 0 && psycho_get_desc(dev) != NULL) { device_set_desc(dev, "U2P UPA-PCI bridge"); return (0); } return (ENXIO); } static int psycho_attach(device_t dev) { char name[sizeof("pci108e,1000")]; struct psycho_icarg *pica; struct psycho_softc *asc, *sc, *osc; struct ofw_pci_ranges *range; const struct psycho_desc *desc; bus_addr_t intrclr, intrmap; uint64_t csr, dr; phandle_t child, node; uint32_t dvmabase, prop, prop_array[2]; int32_t rev; u_int ver; int i, n, nrange, rid; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); desc = psycho_get_desc(dev); sc->sc_node = node; sc->sc_dev = dev; sc->sc_mode = desc->pd_mode; /* * The Psycho gets three register banks: * (0) per-PBM configuration and status registers * (1) per-PBM PCI configuration space, containing only the * PBM 256-byte PCI header * (2) the shared Psycho configuration registers */ if (sc->sc_mode == PSYCHO_MODE_PSYCHO) { rid = 2; sc->sc_pcictl = bus_get_resource_start(dev, SYS_RES_MEMORY, 0) - bus_get_resource_start(dev, SYS_RES_MEMORY, 2); switch (sc->sc_pcictl) { case PSR_PCICTL0: sc->sc_half = 0; break; case PSR_PCICTL1: sc->sc_half = 1; break; default: panic("%s: bogus PCI control register location", __func__); /* NOTREACHED */ } } else { rid = 0; sc->sc_pcictl = PSR_PCICTL0; sc->sc_half = 0; } sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, (sc->sc_mode == PSYCHO_MODE_PSYCHO ? RF_SHAREABLE : 0) | RF_ACTIVE); if (sc->sc_mem_res == NULL) panic("%s: could not allocate registers", __func__); /* * Match other Psycho's that are already configured against * the base physical address. This will be the same for a * pair of devices that share register space. */ osc = NULL; SLIST_FOREACH(asc, &psycho_softcs, sc_link) { if (rman_get_start(asc->sc_mem_res) == rman_get_start(sc->sc_mem_res)) { /* Found partner. */ osc = asc; break; } } if (osc == NULL) { sc->sc_mtx = malloc(sizeof(*sc->sc_mtx), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_mtx == NULL) panic("%s: could not malloc mutex", __func__); mtx_init(sc->sc_mtx, "pcib_mtx", NULL, MTX_SPIN); } else { if (mtx_initialized(osc->sc_mtx) == 0) panic("%s: mutex not initialized", __func__); sc->sc_mtx = osc->sc_mtx; } csr = PSYCHO_READ8(sc, PSR_CS); ver = PSYCHO_GCSR_VERS(csr); sc->sc_ign = 0x1f; /* Hummingbird/Sabre IGN is always 0x1f. */ if (sc->sc_mode == PSYCHO_MODE_PSYCHO) sc->sc_ign = PSYCHO_GCSR_IGN(csr); if (OF_getprop(node, "clock-frequency", &prop, sizeof(prop)) == -1) prop = 33000000; device_printf(dev, "%s, impl %d, version %d, IGN %#x, bus %c, %dMHz\n", desc->pd_name, (u_int)PSYCHO_GCSR_IMPL(csr), ver, sc->sc_ign, 'A' + sc->sc_half, prop / 1000 / 1000); /* Set up the PCI control and PCI diagnostic registers. */ /* * Revision 0 EBus bridges have a bug which prevents them from * working when bus parking is enabled. */ rev = -1; csr = PCICTL_READ8(sc, PCR_CS); csr &= ~PCICTL_ARB_PARK; for (child = OF_child(node); child != 0; child = OF_peer(child)) { if (OF_getprop(child, "name", name, sizeof(name)) == -1) continue; if ((strcmp(name, "ebus") == 0 || strcmp(name, "pci108e,1000") == 0) && OF_getprop(child, "revision-id", &rev, sizeof(rev)) > 0 && rev == 0) break; } if (rev != 0 && OF_getproplen(node, "no-bus-parking") < 0) csr |= PCICTL_ARB_PARK; /* Workarounds for version specific bugs. */ dr = PCICTL_READ8(sc, PCR_DIAG); switch (ver) { case 0: dr |= DIAG_RTRY_DIS; dr &= ~DIAG_DWSYNC_DIS; /* XXX need to also disable rerun of the streaming buffers. */ break; case 1: csr &= ~PCICTL_ARB_PARK; dr |= DIAG_RTRY_DIS | DIAG_DWSYNC_DIS; /* XXX need to also disable rerun of the streaming buffers. */ break; default: dr |= DIAG_DWSYNC_DIS; dr &= ~DIAG_RTRY_DIS; break; } csr |= PCICTL_ERRINTEN | PCICTL_ARB_4; csr &= ~(PCICTL_SBHINTEN | PCICTL_WAKEUPEN); #ifdef PSYCHO_DEBUG device_printf(dev, "PCI CSR 0x%016llx -> 0x%016llx\n", (unsigned long long)PCICTL_READ8(sc, PCR_CS), (unsigned long long)csr); #endif PCICTL_WRITE8(sc, PCR_CS, csr); dr &= ~DIAG_ISYNC_DIS; #ifdef PSYCHO_DEBUG device_printf(dev, "PCI DR 0x%016llx -> 0x%016llx\n", (unsigned long long)PCICTL_READ8(sc, PCR_DIAG), (unsigned long long)dr); #endif PCICTL_WRITE8(sc, PCR_DIAG, dr); if (sc->sc_mode == PSYCHO_MODE_SABRE) { /* Use the PROM preset for now. */ csr = PCICTL_READ8(sc, PCR_TAS); if (csr == 0) panic("%s: Hummingbird/Sabre TAS not initialized.", __func__); dvmabase = (ffs(csr) - 1) << PCITAS_ADDR_SHIFT; } else dvmabase = -1; /* Initialize memory and I/O rmans. */ sc->sc_pci_io_rman.rm_type = RMAN_ARRAY; sc->sc_pci_io_rman.rm_descr = "Psycho PCI I/O Ports"; if (rman_init(&sc->sc_pci_io_rman) != 0 || rman_manage_region(&sc->sc_pci_io_rman, 0, PSYCHO_IO_SIZE) != 0) panic("%s: failed to set up I/O rman", __func__); sc->sc_pci_mem_rman.rm_type = RMAN_ARRAY; sc->sc_pci_mem_rman.rm_descr = "Psycho PCI Memory"; if (rman_init(&sc->sc_pci_mem_rman) != 0 || rman_manage_region(&sc->sc_pci_mem_rman, 0, PSYCHO_MEM_SIZE) != 0) panic("%s: failed to set up memory rman", __func__); nrange = OF_getprop_alloc(node, "ranges", sizeof(*range), (void **)&range); /* * Make sure that the expected ranges are present. The * OFW_PCI_CS_MEM64 one is not currently used though. */ if (nrange != PSYCHO_NRANGE) panic("%s: unsupported number of ranges", __func__); /* * Find the addresses of the various bus spaces. * There should not be multiple ones of one kind. * The physical start addresses of the ranges are the configuration, * memory and I/O handles. */ for (n = 0; n < PSYCHO_NRANGE; n++) { i = OFW_PCI_RANGE_CS(&range[n]); if (sc->sc_pci_bh[i] != 0) panic("%s: duplicate range for space %d", __func__, i); sc->sc_pci_bh[i] = OFW_PCI_RANGE_PHYS(&range[n]); } free(range, M_OFWPROP); /* Register the softc, this is needed for paired Psychos. */ SLIST_INSERT_HEAD(&psycho_softcs, sc, sc_link); /* * If we're a Hummingbird/Sabre or the first of a pair of Psychos * to arrive here, do the interrupt setup and start up the IOMMU. */ if (osc == NULL) { /* * Hunt through all the interrupt mapping regs and register * our interrupt controller for the corresponding interrupt * vectors. */ for (n = 0; n <= PSYCHO_MAX_INO; n++) { if (psycho_find_intrmap(sc, n, &intrmap, &intrclr, NULL) == 0) continue; pica = malloc(sizeof(*pica), M_DEVBUF, M_NOWAIT); if (pica == NULL) panic("%s: could not allocate interrupt " "controller argument", __func__); pica->pica_sc = sc; pica->pica_map = intrmap; pica->pica_clr = intrclr; #ifdef PSYCHO_DEBUG /* * Enable all interrupts and clear all interrupt * states. This aids the debugging of interrupt * routing problems. */ device_printf(dev, "intr map (INO %d, %s) %#lx: %#lx, clr: %#lx\n", n, intrmap <= PSR_PCIB3_INT_MAP ? "PCI" : "OBIO", (u_long)intrmap, (u_long)PSYCHO_READ8(sc, intrmap), (u_long)intrclr); PSYCHO_WRITE8(sc, intrmap, INTMAP_VEC(sc->sc_ign, n)); PSYCHO_WRITE8(sc, intrclr, 0); PSYCHO_WRITE8(sc, intrmap, INTMAP_ENABLE(INTMAP_VEC(sc->sc_ign, n), PCPU_GET(mid))); #endif if (intr_controller_register(INTMAP_VEC(sc->sc_ign, n), &psycho_ic, pica) != 0) panic("%s: could not register interrupt " "controller for INO %d", __func__, n); } if (sc->sc_mode == PSYCHO_MODE_PSYCHO) { /* Initialize the counter-timer. */ sparc64_counter_init(device_get_nameunit(dev), rman_get_bustag(sc->sc_mem_res), rman_get_bushandle(sc->sc_mem_res), PSR_TC0); } /* * Set up IOMMU and PCI configuration if we're the first * of a pair of Psycho's to arrive here. * * We should calculate a TSB size based on amount of RAM * and number of bus controllers and number and type of * child devices. * * For the moment, 32KB should be more than enough. */ sc->sc_is = malloc(sizeof(struct iommu_state), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_is == NULL) panic("%s: malloc iommu_state failed", __func__); if (sc->sc_mode == PSYCHO_MODE_SABRE) sc->sc_is->is_pmaxaddr = IOMMU_MAXADDR(SABRE_IOMMU_BITS); else sc->sc_is->is_pmaxaddr = IOMMU_MAXADDR(PSYCHO_IOMMU_BITS); sc->sc_is->is_sb[0] = 0; sc->sc_is->is_sb[1] = 0; if (OF_getproplen(node, "no-streaming-cache") < 0) sc->sc_is->is_sb[0] = sc->sc_pcictl + PCR_STRBUF; psycho_iommu_init(sc, 3, dvmabase); } else { /* Just copy IOMMU state, config tag and address. */ sc->sc_is = osc->sc_is; if (OF_getproplen(node, "no-streaming-cache") < 0) sc->sc_is->is_sb[1] = sc->sc_pcictl + PCR_STRBUF; iommu_reset(sc->sc_is); } /* Allocate our tags. */ sc->sc_pci_memt = psycho_alloc_bus_tag(sc, PCI_MEMORY_BUS_SPACE); sc->sc_pci_iot = psycho_alloc_bus_tag(sc, PCI_IO_BUS_SPACE); sc->sc_pci_cfgt = psycho_alloc_bus_tag(sc, PCI_CONFIG_BUS_SPACE); if (bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0, sc->sc_is->is_pmaxaddr, ~0, NULL, NULL, sc->sc_is->is_pmaxaddr, 0xff, 0xffffffff, 0, NULL, NULL, &sc->sc_pci_dmat) != 0) panic("%s: bus_dma_tag_create failed", __func__); /* Customize the tag. */ sc->sc_pci_dmat->dt_cookie = sc->sc_is; sc->sc_pci_dmat->dt_mt = &iommu_dma_methods; n = OF_getprop(node, "bus-range", (void *)prop_array, sizeof(prop_array)); if (n == -1) panic("%s: could not get bus-range", __func__); if (n != sizeof(prop_array)) panic("%s: broken bus-range (%d)", __func__, n); if (bootverbose) device_printf(dev, "bus range %u to %u; PCI bus %d\n", prop_array[0], prop_array[1], prop_array[0]); sc->sc_pci_secbus = prop_array[0]; /* Clear any pending PCI error bits. */ PCIB_WRITE_CONFIG(dev, sc->sc_pci_secbus, PCS_DEVICE, PCS_FUNC, PCIR_STATUS, PCIB_READ_CONFIG(dev, sc->sc_pci_secbus, PCS_DEVICE, PCS_FUNC, PCIR_STATUS, 2), 2); PCICTL_WRITE8(sc, PCR_CS, PCICTL_READ8(sc, PCR_CS)); PCICTL_WRITE8(sc, PCR_AFS, PCICTL_READ8(sc, PCR_AFS)); if (osc == NULL) { /* * Establish handlers for interesting interrupts... * * XXX We need to remember these and remove this to support * hotplug on the UPA/FHC bus. * * XXX Not all controllers have these, but installing them * is better than trying to sort through this mess. */ psycho_set_intr(sc, 1, PSR_UE_INT_MAP, psycho_ue, NULL); psycho_set_intr(sc, 2, PSR_CE_INT_MAP, psycho_ce, NULL); #ifdef DEBUGGER_ON_POWERFAIL psycho_set_intr(sc, 3, PSR_POWER_INT_MAP, psycho_powerfail, NULL); #else psycho_set_intr(sc, 3, PSR_POWER_INT_MAP, NULL, (driver_intr_t *)psycho_powerfail); #endif if (sc->sc_mode == PSYCHO_MODE_PSYCHO) { /* * Hummingbirds/Sabres do not have the following two * interrupts. */ /* * The spare hardware interrupt is used for the * over-temperature interrupt. */ psycho_set_intr(sc, 4, PSR_SPARE_INT_MAP, NULL, psycho_overtemp); #ifdef PSYCHO_MAP_WAKEUP /* * psycho_wakeup() doesn't do anything useful right * now. */ psycho_set_intr(sc, 5, PSR_PWRMGT_INT_MAP, psycho_wakeup, NULL); #endif /* PSYCHO_MAP_WAKEUP */ } } /* * Register a PCI bus error interrupt handler according to which * half this is. Hummingbird/Sabre don't have a PCI bus B error * interrupt but they are also only used for PCI bus A. */ psycho_set_intr(sc, 0, sc->sc_half == 0 ? PSR_PCIAERR_INT_MAP : PSR_PCIBERR_INT_MAP, psycho_pci_bus, NULL); /* * Set the latency timer register as this isn't always done by the * firmware. */ PCIB_WRITE_CONFIG(dev, sc->sc_pci_secbus, PCS_DEVICE, PCS_FUNC, PCIR_LATTIMER, OFW_PCI_LATENCY, 1); for (n = PCIR_VENDOR; n < PCIR_STATUS; n += sizeof(uint16_t)) le16enc(&sc->sc_pci_hpbcfg[n], bus_space_read_2( sc->sc_pci_cfgt, sc->sc_pci_bh[OFW_PCI_CS_CONFIG], PSYCHO_CONF_OFF(sc->sc_pci_secbus, PCS_DEVICE, PCS_FUNC, n))); for (n = PCIR_REVID; n <= PCIR_BIST; n += sizeof(uint8_t)) sc->sc_pci_hpbcfg[n] = bus_space_read_1(sc->sc_pci_cfgt, sc->sc_pci_bh[OFW_PCI_CS_CONFIG], PSYCHO_CONF_OFF( sc->sc_pci_secbus, PCS_DEVICE, PCS_FUNC, n)); ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(ofw_pci_intr_t)); /* * On E250 the interrupt map entry for the EBus bridge is wrong, * causing incorrect interrupts to be assigned to some devices on * the EBus. Work around it by changing our copy of the interrupt * map mask to perform a full comparison of the INO. That way * the interrupt map entry for the EBus bridge won't match at all * and the INOs specified in the "interrupts" properties of the * EBus devices will be used directly instead. */ if (strcmp(sparc64_model, "SUNW,Ultra-250") == 0 && sc->sc_pci_iinfo.opi_imapmsk != NULL) *(ofw_pci_intr_t *)(&sc->sc_pci_iinfo.opi_imapmsk[ sc->sc_pci_iinfo.opi_addrc]) = INTMAP_INO_MASK; device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } static void psycho_set_intr(struct psycho_softc *sc, u_int index, bus_addr_t intrmap, driver_filter_t filt, driver_intr_t intr) { u_long vec; int rid; rid = index; sc->sc_irq_res[index] = bus_alloc_resource_any(sc->sc_dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res[index] == NULL || INTIGN(vec = rman_get_start(sc->sc_irq_res[index])) != sc->sc_ign || INTVEC(PSYCHO_READ8(sc, intrmap)) != vec || intr_vectors[vec].iv_ic != &psycho_ic || bus_setup_intr(sc->sc_dev, sc->sc_irq_res[index], INTR_TYPE_MISC, filt, intr, sc, &sc->sc_ihand[index]) != 0) panic("%s: failed to set up interrupt %d", __func__, index); } static int psycho_find_intrmap(struct psycho_softc *sc, u_int ino, bus_addr_t *intrmapptr, bus_addr_t *intrclrptr, bus_addr_t *intrdiagptr) { bus_addr_t intrclr, intrmap; uint64_t diag; int found; /* * XXX we only compare INOs rather than INRs since the firmware may * not provide the IGN and the IGN is constant for all devices on * that PCI controller. * This could cause problems for the FFB/external interrupt which * has a full vector that can be set arbitrarily. */ if (ino > PSYCHO_MAX_INO) { device_printf(sc->sc_dev, "out of range INO %d requested\n", ino); return (0); } found = 0; /* Hunt through OBIO first. */ diag = PSYCHO_READ8(sc, PSR_OBIO_INT_DIAG); for (intrmap = PSR_SCSI_INT_MAP, intrclr = PSR_SCSI_INT_CLR; intrmap <= PSR_PWRMGT_INT_MAP; intrmap += 8, intrclr += 8, diag >>= 2) { if (sc->sc_mode == PSYCHO_MODE_SABRE && (intrmap == PSR_TIMER0_INT_MAP || intrmap == PSR_TIMER1_INT_MAP || intrmap == PSR_PCIBERR_INT_MAP || intrmap == PSR_PWRMGT_INT_MAP)) continue; if (INTINO(PSYCHO_READ8(sc, intrmap)) == ino) { diag &= 2; found = 1; break; } } if (!found) { diag = PSYCHO_READ8(sc, PSR_PCI_INT_DIAG); /* Now do PCI interrupts. */ for (intrmap = PSR_PCIA0_INT_MAP, intrclr = PSR_PCIA0_INT_CLR; intrmap <= PSR_PCIB3_INT_MAP; intrmap += 8, intrclr += 32, diag >>= 8) { if (sc->sc_mode == PSYCHO_MODE_PSYCHO && (intrmap == PSR_PCIA2_INT_MAP || intrmap == PSR_PCIA3_INT_MAP)) continue; if (((PSYCHO_READ8(sc, intrmap) ^ ino) & 0x3c) == 0) { intrclr += 8 * (ino & 3); diag = (diag >> ((ino & 3) * 2)) & 2; found = 1; break; } } } if (intrmapptr != NULL) *intrmapptr = intrmap; if (intrclrptr != NULL) *intrclrptr = intrclr; if (intrdiagptr != NULL) *intrdiagptr = diag; return (found); } /* * Interrupt handlers */ static int psycho_ue(void *arg) { struct psycho_softc *sc = arg; uint64_t afar, afsr; afar = PSYCHO_READ8(sc, PSR_UE_AFA); afsr = PSYCHO_READ8(sc, PSR_UE_AFS); /* * On the UltraSPARC-IIi/IIe, IOMMU misses/protection faults cause * the AFAR to be set to the physical address of the TTE entry that * was invalid/write protected. Call into the IOMMU code to have * them decoded to virtual I/O addresses. */ if ((afsr & UEAFSR_P_DTE) != 0) iommu_decode_fault(sc->sc_is, afar); panic("%s: uncorrectable DMA error AFAR %#lx AFSR %#lx", device_get_name(sc->sc_dev), (u_long)afar, (u_long)afsr); return (FILTER_HANDLED); } static int psycho_ce(void *arg) { struct psycho_softc *sc = arg; uint64_t afar, afsr; mtx_lock_spin(sc->sc_mtx); afar = PSYCHO_READ8(sc, PSR_CE_AFA); afsr = PSYCHO_READ8(sc, PSR_CE_AFS); device_printf(sc->sc_dev, "correctable DMA error AFAR %#lx " "AFSR %#lx\n", (u_long)afar, (u_long)afsr); /* Clear the error bits that we caught. */ PSYCHO_WRITE8(sc, PSR_CE_AFS, afsr); mtx_unlock_spin(sc->sc_mtx); return (FILTER_HANDLED); } static int psycho_pci_bus(void *arg) { struct psycho_softc *sc = arg; uint64_t afar, afsr; afar = PCICTL_READ8(sc, PCR_AFA); afsr = PCICTL_READ8(sc, PCR_AFS); panic("%s: PCI bus %c error AFAR %#lx AFSR %#lx", device_get_name(sc->sc_dev), 'A' + sc->sc_half, (u_long)afar, (u_long)afsr); return (FILTER_HANDLED); } static int psycho_powerfail(void *arg) { #ifdef DEBUGGER_ON_POWERFAIL struct psycho_softc *sc = arg; kdb_enter(KDB_WHY_POWERFAIL, "powerfail"); #else static int shutdown; /* As the interrupt is cleared we may be called multiple times. */ if (shutdown != 0) return (FILTER_HANDLED); shutdown++; printf("Power Failure Detected: Shutting down NOW.\n"); shutdown_nice(0); #endif return (FILTER_HANDLED); } static void psycho_overtemp(void *arg) { static int shutdown; /* As the interrupt is cleared we may be called multiple times. */ if (shutdown != 0) return; shutdown++; printf("DANGER: OVER TEMPERATURE detected.\nShutting down NOW.\n"); shutdown_nice(RB_POWEROFF); } #ifdef PSYCHO_MAP_WAKEUP static int psycho_wakeup(void *arg) { struct psycho_softc *sc = arg; /* Gee, we don't really have a framework to deal with this properly. */ device_printf(sc->sc_dev, "power management wakeup\n"); return (FILTER_HANDLED); } #endif /* PSYCHO_MAP_WAKEUP */ static void psycho_iommu_init(struct psycho_softc *sc, int tsbsize, uint32_t dvmabase) { struct iommu_state *is = sc->sc_is; /* Punch in our copies. */ is->is_bustag = rman_get_bustag(sc->sc_mem_res); is->is_bushandle = rman_get_bushandle(sc->sc_mem_res); is->is_iommu = PSR_IOMMU; is->is_dtag = PSR_IOMMU_TLB_TAG_DIAG; is->is_ddram = PSR_IOMMU_TLB_DATA_DIAG; is->is_dqueue = PSR_IOMMU_QUEUE_DIAG; is->is_dva = PSR_IOMMU_SVADIAG; is->is_dtcmp = PSR_IOMMU_TLB_CMP_DIAG; iommu_init(device_get_nameunit(sc->sc_dev), is, tsbsize, dvmabase, 0); } static int psycho_maxslots(device_t dev) { /* XXX: is this correct? */ return (PCI_SLOTMAX); } static uint32_t psycho_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct psycho_softc *sc; bus_space_handle_t bh; u_long offset = 0; uint8_t byte; uint16_t shrt; uint32_t r, wrd; int i; sc = device_get_softc(dev); bh = sc->sc_pci_bh[OFW_PCI_CS_CONFIG]; /* * The Hummingbird and Sabre bridges are picky in that they * only allow their config space to be accessed using the * "native" width of the respective register being accessed * and return semi-random other content of their config space * otherwise. Given that the PCI specs don't say anything * about such a (unusual) limitation and lots of stuff expects * to be able to access the contents of the config space at * any width we allow just that. We do this by using a copy * of the header of the bridge (the rest is all zero anyway) * read during attach (expect for PCIR_STATUS) in order to * simplify things. * The Psycho bridges contain a dupe of their header at 0x80 * which we nullify that way also. */ if (bus == sc->sc_pci_secbus && slot == PCS_DEVICE && func == PCS_FUNC) { if (offset % width != 0) return (-1); if (reg >= sizeof(sc->sc_pci_hpbcfg)) return (0); if ((reg < PCIR_STATUS && reg + width > PCIR_STATUS) || reg == PCIR_STATUS || reg == PCIR_STATUS + 1) le16enc(&sc->sc_pci_hpbcfg[PCIR_STATUS], bus_space_read_2(sc->sc_pci_cfgt, bh, PSYCHO_CONF_OFF(sc->sc_pci_secbus, PCS_DEVICE, PCS_FUNC, PCIR_STATUS))); switch (width) { case 1: return (sc->sc_pci_hpbcfg[reg]); case 2: return (le16dec(&sc->sc_pci_hpbcfg[reg])); case 4: return (le32dec(&sc->sc_pci_hpbcfg[reg])); } } offset = PSYCHO_CONF_OFF(bus, slot, func, reg); switch (width) { case 1: i = bus_space_peek_1(sc->sc_pci_cfgt, bh, offset, &byte); r = byte; break; case 2: i = bus_space_peek_2(sc->sc_pci_cfgt, bh, offset, &shrt); r = shrt; break; case 4: i = bus_space_peek_4(sc->sc_pci_cfgt, bh, offset, &wrd); r = wrd; break; default: panic("%s: bad width", __func__); /* NOTREACHED */ } if (i) { #ifdef PSYCHO_DEBUG printf("%s: read data error reading: %d.%d.%d: 0x%x\n", __func__, bus, slot, func, reg); #endif r = -1; } return (r); } static void psycho_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int width) { struct psycho_softc *sc; bus_space_handle_t bh; u_long offset = 0; sc = device_get_softc(dev); offset = PSYCHO_CONF_OFF(bus, slot, func, reg); bh = sc->sc_pci_bh[OFW_PCI_CS_CONFIG]; switch (width) { case 1: bus_space_write_1(sc->sc_pci_cfgt, bh, offset, val); break; case 2: bus_space_write_2(sc->sc_pci_cfgt, bh, offset, val); break; case 4: bus_space_write_4(sc->sc_pci_cfgt, bh, offset, val); break; default: panic("%s: bad width", __func__); /* NOTREACHED */ } } static int psycho_route_interrupt(device_t bridge, device_t dev, int pin) { struct psycho_softc *sc; struct ofw_pci_register reg; bus_addr_t intrmap; ofw_pci_intr_t pintr, mintr; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bridge); pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), maskbuf)) return (mintr); /* * If this is outside of the range for an intpin, it's likely a full * INO, and no mapping is required at all; this happens on the U30, * where there's no interrupt map at the Psycho node. Fortunately, * there seem to be no INOs in the intpin range on this boxen, so * this easy heuristics will do. */ if (pin > 4) return (pin); /* * Guess the INO; we always assume that this is a non-OBIO * device, and that pin is a "real" intpin number. Determine * the mapping register to be used by the slot number. * We only need to do this on E450s, it seems; here, the slot numbers * for bus A are one-based, while those for bus B seemingly have an * offset of 2 (hence the factor of 3 below). */ intrmap = PSR_PCIA0_INT_MAP + 8 * (pci_get_slot(dev) - 1 + 3 * sc->sc_half); mintr = INTINO(PSYCHO_READ8(sc, intrmap)) + pin - 1; device_printf(bridge, "guessing interrupt %d for device %d.%d pin %d\n", (int)mintr, pci_get_slot(dev), pci_get_function(dev), pin); return (mintr); } static int psycho_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct psycho_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_pci_secbus; return (0); } return (ENOENT); } static int psycho_dmasync(void *arg) { struct psycho_dmasync *pds = arg; (void)PCIB_READ_CONFIG(pds->pds_ppb, pds->pds_bus, pds->pds_slot, pds->pds_func, PCIR_VENDOR, 2); (void)PSYCHO_READ8(pds->pds_sc, PSR_DMA_WRITE_SYNC); return (pds->pds_handler(pds->pds_arg)); } static void psycho_intr_enable(void *arg) { struct intr_vector *iv = arg; struct psycho_icarg *pica = iv->iv_icarg; PSYCHO_WRITE8(pica->pica_sc, pica->pica_map, INTMAP_ENABLE(iv->iv_vec, iv->iv_mid)); } static void psycho_intr_disable(void *arg) { struct intr_vector *iv = arg; struct psycho_icarg *pica = iv->iv_icarg; PSYCHO_WRITE8(pica->pica_sc, pica->pica_map, iv->iv_vec); } static void psycho_intr_assign(void *arg) { struct intr_vector *iv = arg; struct psycho_icarg *pica = iv->iv_icarg; PSYCHO_WRITE8(pica->pica_sc, pica->pica_map, INTMAP_TID( PSYCHO_READ8(pica->pica_sc, pica->pica_map), iv->iv_mid)); } static void psycho_intr_clear(void *arg) { struct intr_vector *iv = arg; struct psycho_icarg *pica = iv->iv_icarg; PSYCHO_WRITE8(pica->pica_sc, pica->pica_clr, 0); } static int psycho_setup_intr(device_t dev, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { struct { int apb:1; int ppb:1; } found; devclass_t pci_devclass; device_t cdev, pdev, pcidev; struct psycho_softc *sc; struct psycho_dmasync *pds; u_long vec; int error; sc = device_get_softc(dev); /* * Make sure the vector is fully specified and we registered * our interrupt controller for it. */ vec = rman_get_start(ires); if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &psycho_ic) { device_printf(dev, "invalid interrupt vector 0x%lx\n", vec); return (EINVAL); } /* * The Sabre-APB-combination has a bug where it does not drain * DMA write data for devices behind additional PCI-PCI bridges * underneath the APB PCI-PCI bridge. The workaround is to do * a read on the farest PCI-PCI bridge followed by a read of the * PCI DMA write sync register of the Sabre. * XXX installing the wrapper for an affected device and the * actual workaround in psycho_dmasync() should be moved to * psycho(4)-specific bus_dma_tag_create() and bus_dmamap_sync() * methods, respectively, once DMA tag creation is newbus'ified, * so the workaround isn't only applied for interrupt handlers * but also for polling(4) callbacks. */ if (sc->sc_mode == PSYCHO_MODE_SABRE) { pds = malloc(sizeof(*pds), M_DEVBUF, M_NOWAIT | M_ZERO); if (pds == NULL) return (ENOMEM); pcidev = NULL; found.apb = found.ppb = 0; pci_devclass = devclass_find("pci"); for (cdev = child; cdev != dev; cdev = pdev) { pdev = device_get_parent(cdev); if (pcidev == NULL) { if (device_get_devclass(pdev) != pci_devclass) continue; pcidev = cdev; continue; } /* * NB: APB would also match as PCI-PCI bridges. */ if (pci_get_vendor(cdev) == 0x108e && pci_get_device(cdev) == 0x5000) { found.apb = 1; break; } if (pci_get_class(cdev) == PCIC_BRIDGE && pci_get_subclass(cdev) == PCIS_BRIDGE_PCI) found.ppb = 1; } if (found.apb && found.ppb && pcidev != NULL) { pds->pds_sc = sc; pds->pds_arg = arg; pds->pds_ppb = device_get_parent(device_get_parent(pcidev)); pds->pds_bus = pci_get_bus(pcidev); pds->pds_slot = pci_get_slot(pcidev); pds->pds_func = pci_get_function(pcidev); if (bootverbose) device_printf(dev, "installed DMA sync " "workaround for device %d.%d on bus %d\n", pds->pds_slot, pds->pds_func, pds->pds_bus); if (intr == NULL) { pds->pds_handler = filt; error = bus_generic_setup_intr(dev, child, ires, flags, psycho_dmasync, intr, pds, cookiep); } else { pds->pds_handler = (driver_filter_t *)intr; error = bus_generic_setup_intr(dev, child, ires, flags, filt, (driver_intr_t *)psycho_dmasync, pds, cookiep); } } else error = bus_generic_setup_intr(dev, child, ires, flags, filt, intr, arg, cookiep); if (error != 0) { free(pds, M_DEVBUF); return (error); } pds->pds_cookie = *cookiep; *cookiep = pds; return (error); } return (bus_generic_setup_intr(dev, child, ires, flags, filt, intr, arg, cookiep)); } static int psycho_teardown_intr(device_t dev, device_t child, struct resource *vec, void *cookie) { struct psycho_softc *sc; struct psycho_dmasync *pds; int error; sc = device_get_softc(dev); if (sc->sc_mode == PSYCHO_MODE_SABRE) { pds = cookie; error = bus_generic_teardown_intr(dev, child, vec, pds->pds_cookie); if (error == 0) free(pds, M_DEVBUF); return (error); } return (bus_generic_teardown_intr(dev, child, vec, cookie)); } static struct resource * psycho_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 psycho_softc *sc; struct resource *rv; struct rman *rm; bus_space_tag_t bt; bus_space_handle_t bh; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; sc = device_get_softc(bus); if (type == SYS_RES_IRQ) { /* * XXX: Don't accept blank ranges for now, only single * interrupts. The other case should not happen with * the MI PCI code... * XXX: This may return a resource that is out of the * range that was specified. Is this correct...? */ if (start != end) panic("%s: XXX: interrupt range", __func__); start = end = INTMAP_VEC(sc->sc_ign, end); return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags)); } switch (type) { case SYS_RES_MEMORY: rm = &sc->sc_pci_mem_rman; bt = sc->sc_pci_memt; bh = sc->sc_pci_bh[OFW_PCI_CS_MEM32]; break; case SYS_RES_IOPORT: rm = &sc->sc_pci_io_rman; bt = sc->sc_pci_iot; bh = sc->sc_pci_bh[OFW_PCI_CS_IO]; break; default: return (NULL); /* NOTREACHED */ } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); bh += rman_get_start(rv); rman_set_bustag(rv, bt); rman_set_bushandle(rv, bh); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int psycho_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { void *p; int error; if (type == SYS_RES_IRQ) return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (type == SYS_RES_MEMORY) { /* * Need to memory-map the device space, as some drivers depend * on the virtual address being set and useable. */ error = sparc64_bus_mem_map(rman_get_bustag(r), rman_get_bushandle(r), rman_get_size(r), 0, 0, &p); if (error != 0) return (error); rman_set_virtual(r, p); } return (rman_activate_resource(r)); } static int psycho_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (type == SYS_RES_IRQ) return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (type == SYS_RES_MEMORY) { sparc64_bus_mem_unmap(rman_get_virtual(r), rman_get_size(r)); rman_set_virtual(r, NULL); } return (rman_deactivate_resource(r)); } static int psycho_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int error; if (type == SYS_RES_IRQ) return (BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (rman_get_flags(r) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, r); if (error) return (error); } return (rman_release_resource(r)); } static bus_dma_tag_t psycho_get_dma_tag(device_t bus, device_t child) { struct psycho_softc *sc; sc = device_get_softc(bus); return (sc->sc_pci_dmat); } static phandle_t psycho_get_node(device_t bus, device_t dev) { struct psycho_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 bus_space_tag_t psycho_alloc_bus_tag(struct psycho_softc *sc, int type) { bus_space_tag_t bt; bt = malloc(sizeof(struct bus_space_tag), M_DEVBUF, M_NOWAIT | M_ZERO); if (bt == NULL) panic("%s: out of memory", __func__); bt->bst_cookie = sc; bt->bst_parent = rman_get_bustag(sc->sc_mem_res); bt->bst_type = type; return (bt); } Index: head/sys/sparc64/pci/schizo.c =================================================================== --- head/sys/sparc64/pci/schizo.c (revision 186127) +++ head/sys/sparc64/pci/schizo.c (revision 186128) @@ -1,1394 +1,1393 @@ /*- * Copyright (c) 1999, 2000 Matthew R. Green * Copyright (c) 2001 - 2003 by Thomas Moestl * Copyright (c) 2005, 2007, 2008 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * from: NetBSD: psycho.c,v 1.39 2001/10/07 20:30:41 eeh Exp * from: FreeBSD: psycho.c 183152 2008-09-18 19:45:22Z marius */ #include __FBSDID("$FreeBSD$"); /* * Driver for `Schizo' Fireplane/Safari to PCI 2.1 and `Tomatillo' JBus to * PCI 2.2 bridges */ #include "opt_ofw_pci.h" #include "opt_schizo.h" #include #include #include #include #include #include #include #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" static const struct schizo_desc *schizo_get_desc(device_t); static void schizo_set_intr(struct schizo_softc *, u_int, u_int, driver_filter_t); static driver_filter_t schizo_dma_sync_stub; static driver_filter_t ichip_dma_sync_stub; static void schizo_intr_enable(void *); static void schizo_intr_disable(void *); static void schizo_intr_assign(void *); static void schizo_intr_clear(void *); static int schizo_intr_register(struct schizo_softc *sc, u_int ino); static int schizo_get_intrmap(struct schizo_softc *, u_int, bus_addr_t *, bus_addr_t *); static bus_space_tag_t schizo_alloc_bus_tag(struct schizo_softc *, int); static timecounter_get_t schizo_get_timecount; /* Interrupt handlers */ static driver_filter_t schizo_pci_bus; static driver_filter_t schizo_ue; static driver_filter_t schizo_ce; static driver_filter_t schizo_host_bus; static driver_filter_t schizo_cdma; /* IOMMU support */ static void schizo_iommu_init(struct schizo_softc *, int, uint32_t); /* * Methods */ static device_probe_t schizo_probe; static device_attach_t schizo_attach; static bus_read_ivar_t schizo_read_ivar; static bus_setup_intr_t schizo_setup_intr; static bus_teardown_intr_t schizo_teardown_intr; static bus_alloc_resource_t schizo_alloc_resource; static bus_activate_resource_t schizo_activate_resource; static bus_deactivate_resource_t schizo_deactivate_resource; static bus_release_resource_t schizo_release_resource; static bus_get_dma_tag_t schizo_get_dma_tag; static pcib_maxslots_t schizo_maxslots; static pcib_read_config_t schizo_read_config; static pcib_write_config_t schizo_write_config; static pcib_route_interrupt_t schizo_route_interrupt; static ofw_bus_get_node_t schizo_get_node; static device_method_t schizo_methods[] = { /* Device interface */ DEVMETHOD(device_probe, schizo_probe), DEVMETHOD(device_attach, schizo_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, schizo_read_ivar), DEVMETHOD(bus_setup_intr, schizo_setup_intr), DEVMETHOD(bus_teardown_intr, schizo_teardown_intr), DEVMETHOD(bus_alloc_resource, schizo_alloc_resource), DEVMETHOD(bus_activate_resource, schizo_activate_resource), DEVMETHOD(bus_deactivate_resource, schizo_deactivate_resource), DEVMETHOD(bus_release_resource, schizo_release_resource), DEVMETHOD(bus_get_dma_tag, schizo_get_dma_tag), /* pcib interface */ DEVMETHOD(pcib_maxslots, schizo_maxslots), DEVMETHOD(pcib_read_config, schizo_read_config), DEVMETHOD(pcib_write_config, schizo_write_config), DEVMETHOD(pcib_route_interrupt, schizo_route_interrupt), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, schizo_get_node), { 0, 0 } }; static devclass_t schizo_devclass; DEFINE_CLASS_0(pcib, schizo_driver, schizo_methods, sizeof(struct schizo_softc)); DRIVER_MODULE(schizo, nexus, schizo_driver, schizo_devclass, 0, 0); static SLIST_HEAD(, schizo_softc) schizo_softcs = SLIST_HEAD_INITIALIZER(schizo_softcs); static const struct intr_controller schizo_ic = { schizo_intr_enable, schizo_intr_disable, schizo_intr_assign, schizo_intr_clear }; struct schizo_icarg { struct schizo_softc *sica_sc; bus_addr_t sica_map; bus_addr_t sica_clr; }; struct schizo_dma_sync { struct schizo_softc *sds_sc; driver_filter_t *sds_handler; void *sds_arg; void *sds_cookie; uint64_t sds_syncval; device_t sds_ppb; /* farest PCI-PCI bridge */ uint8_t sds_bus; /* bus of farest PCI device */ uint8_t sds_slot; /* slot of farest PCI device */ uint8_t sds_func; /* func. of farest PCI device */ }; #define SCHIZO_PERF_CNT_QLTY 100 #define SCHIZO_SPC_READ_8(spc, sc, offs) \ bus_read_8((sc)->sc_mem_res[(spc)], (offs)) #define SCHIZO_SPC_WRITE_8(spc, sc, offs, v) \ bus_write_8((sc)->sc_mem_res[(spc)], (offs), (v)) #define SCHIZO_PCI_READ_8(sc, offs) \ SCHIZO_SPC_READ_8(STX_PCI, (sc), (offs)) #define SCHIZO_PCI_WRITE_8(sc, offs, v) \ SCHIZO_SPC_WRITE_8(STX_PCI, (sc), (offs), (v)) #define SCHIZO_CTRL_READ_8(sc, offs) \ SCHIZO_SPC_READ_8(STX_CTRL, (sc), (offs)) #define SCHIZO_CTRL_WRITE_8(sc, offs, v) \ SCHIZO_SPC_WRITE_8(STX_CTRL, (sc), (offs), (v)) #define SCHIZO_PCICFG_READ_8(sc, offs) \ SCHIZO_SPC_READ_8(STX_PCICFG, (sc), (offs)) #define SCHIZO_PCICFG_WRITE_8(sc, offs, v) \ SCHIZO_SPC_WRITE_8(STX_PCICFG, (sc), (offs), (v)) #define SCHIZO_ICON_READ_8(sc, offs) \ SCHIZO_SPC_READ_8(STX_ICON, (sc), (offs)) #define SCHIZO_ICON_WRITE_8(sc, offs, v) \ SCHIZO_SPC_WRITE_8(STX_ICON, (sc), (offs), (v)) #define OFW_PCI_TYPE "pci" struct schizo_desc { const char *sd_string; int sd_mode; const char *sd_name; }; static const struct schizo_desc const schizo_compats[] = { { "pci108e,8001", SCHIZO_MODE_SCZ, "Schizo" }, { "pci108e,a801", SCHIZO_MODE_TOM, "Tomatillo" }, { NULL, 0, NULL } }; static const struct schizo_desc * schizo_get_desc(device_t dev) { const struct schizo_desc *desc; const char *compat; compat = ofw_bus_get_compat(dev); if (compat == NULL) return (NULL); for (desc = schizo_compats; desc->sd_string != NULL; desc++) if (strcmp(desc->sd_string, compat) == 0) return (desc); return (NULL); } static int schizo_probe(device_t dev) { const char *dtype; dtype = ofw_bus_get_type(dev); if (dtype != NULL && strcmp(dtype, OFW_PCI_TYPE) == 0 && schizo_get_desc(dev) != NULL) { device_set_desc(dev, "Sun Host-PCI bridge"); return (0); } return (ENXIO); } static int schizo_attach(device_t dev) { struct ofw_pci_ranges *range; const struct schizo_desc *desc; struct schizo_softc *asc, *sc, *osc; struct timecounter *tc; uint64_t ino_bitmap, reg; phandle_t node; uint32_t prop, prop_array[2]; int i, mode, n, nrange, rid, tsbsize; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); desc = schizo_get_desc(dev); mode = desc->sd_mode; sc->sc_dev = dev; sc->sc_node = node; sc->sc_mode = mode; sc->sc_flags = 0; /* * The Schizo has three register banks: * (0) per-PBM PCI configuration and status registers, but for bus B * shared with the UPA64s interrupt mapping register banks * (1) shared Schizo controller configuration and status registers * (2) per-PBM PCI configuration space * * The Tomatillo has four register banks: * (0) per-PBM PCI configuration and status registers * (1) per-PBM Tomatillo controller configuration registers, but on * machines having the `jbusppm' device shared with its Estar * register bank for bus A * (2) per-PBM PCI configuration space * (3) per-PBM interrupt concentrator registers */ sc->sc_half = (bus_get_resource_start(dev, SYS_RES_MEMORY, STX_PCI) >> 20) & 1; for (n = 0; n < (mode == SCHIZO_MODE_SCZ ? SCZ_NREG : TOM_NREG); n++) { rid = n; sc->sc_mem_res[n] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, (((mode == SCHIZO_MODE_SCZ && ((sc->sc_half == 1 && n == STX_PCI) || n == STX_CTRL)) || (mode == SCHIZO_MODE_TOM && sc->sc_half == 0 && n == STX_CTRL)) ? RF_SHAREABLE : 0) | RF_ACTIVE); if (sc->sc_mem_res[n] == NULL) panic("%s: could not allocate register bank %d", __func__, n); } /* * Match other Schizos that are already configured against * the controller base physical address. This will be the * same for a pair of devices that share register space. */ osc = NULL; SLIST_FOREACH(asc, &schizo_softcs, sc_link) { if (rman_get_start(asc->sc_mem_res[STX_CTRL]) == rman_get_start(sc->sc_mem_res[STX_CTRL])) { /* Found partner. */ osc = asc; break; } } if (osc == NULL) { sc->sc_mtx = malloc(sizeof(*sc->sc_mtx), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_mtx == NULL) panic("%s: could not malloc mutex", __func__); mtx_init(sc->sc_mtx, "pcib_mtx", NULL, MTX_SPIN); } else { if (sc->sc_mode != SCHIZO_MODE_SCZ) panic("%s: no partner expected", __func__); if (mtx_initialized(osc->sc_mtx) == 0) panic("%s: mutex not initialized", __func__); sc->sc_mtx = osc->sc_mtx; } if (OF_getprop(node, "portid", &sc->sc_ign, sizeof(sc->sc_ign)) == -1) panic("%s: could not determine IGN", __func__); if (OF_getprop(node, "version#", &sc->sc_ver, sizeof(sc->sc_ver)) == -1) panic("%s: could not determine version", __func__); if (OF_getprop(node, "clock-frequency", &prop, sizeof(prop)) == -1) prop = 33000000; device_printf(dev, "%s, version %d, IGN %#x, bus %c, %dMHz\n", desc->sd_name, sc->sc_ver, sc->sc_ign, 'A' + sc->sc_half, prop / 1000 / 1000); /* Set up the PCI interrupt retry timer. */ #ifdef SCHIZO_DEBUG device_printf(dev, "PCI IRT 0x%016llx\n", (unsigned long long) SCHIZO_PCI_READ_8(sc, STX_PCI_INTR_RETRY_TIM)); #endif SCHIZO_PCI_WRITE_8(sc, STX_PCI_INTR_RETRY_TIM, 5); /* Set up the PCI control register. */ reg = SCHIZO_PCI_READ_8(sc, STX_PCI_CTRL); reg |= STX_PCI_CTRL_MMU_IEN | STX_PCI_CTRL_SBH_IEN | STX_PCI_CTRL_ERR_IEN | STX_PCI_CTRL_ARB_MASK; reg &= ~(TOM_PCI_CTRL_DTO_IEN | STX_PCI_CTRL_ARB_PARK); if (OF_getproplen(node, "no-bus-parking") < 0) reg |= STX_PCI_CTRL_ARB_PARK; if (mode == SCHIZO_MODE_TOM) { reg |= TOM_PCI_CTRL_PRM | TOM_PCI_CTRL_PRO | TOM_PCI_CTRL_PRL; if (sc->sc_ver <= 1) /* revision <= 2.0 */ reg |= TOM_PCI_CTRL_DTO_IEN; else reg |= STX_PCI_CTRL_PTO; } #ifdef SCHIZO_DEBUG device_printf(dev, "PCI CSR 0x%016llx -> 0x%016llx\n", (unsigned long long)SCHIZO_PCI_READ_8(sc, STX_PCI_CTRL), (unsigned long long)reg); #endif SCHIZO_PCI_WRITE_8(sc, STX_PCI_CTRL, reg); /* Set up the PCI diagnostic register. */ reg = SCHIZO_PCI_READ_8(sc, STX_PCI_DIAG); reg &= ~(SCZ_PCI_DIAG_RTRYARB_DIS | STX_PCI_DIAG_RETRY_DIS | STX_PCI_DIAG_INTRSYNC_DIS); #ifdef SCHIZO_DEBUG device_printf(dev, "PCI DR 0x%016llx -> 0x%016llx\n", (unsigned long long)SCHIZO_PCI_READ_8(sc, STX_PCI_DIAG), (unsigned long long)reg); #endif SCHIZO_PCI_WRITE_8(sc, STX_PCI_DIAG, reg); /* * On Tomatillo clear the I/O prefetch lengths (workaround for a * Jalapeno bug). */ if (mode == SCHIZO_MODE_TOM) SCHIZO_PCI_WRITE_8(sc, TOM_PCI_IOC_CSR, TOM_PCI_IOC_PW | (1 << TOM_PCI_IOC_PREF_OFF_SHIFT) | TOM_PCI_IOC_CPRM | TOM_PCI_IOC_CPRO | TOM_PCI_IOC_CPRL); /* * Hunt through all the interrupt mapping regs and register * the interrupt controller for our interrupt vectors. This * is complicated by the fact that a pair of Schizo PBMs * share one IGN. */ n = OF_getprop(node, "ino-bitmap", (void *)prop_array, sizeof(prop_array)); if (n == -1) panic("%s: could not get ino-bitmap", __func__); ino_bitmap = ((uint64_t)prop_array[1] << 32) | prop_array[0]; for (n = 0; n <= STX_MAX_INO; n++) { if ((ino_bitmap & (1ULL << n)) == 0) continue; if (n == STX_FB0_INO || n == STX_FB1_INO) /* Leave for upa(4). */ continue; i = schizo_intr_register(sc, n); if (i != 0) panic("%s: could not register interrupt controller " "for INO %d (%d)", __func__, n, i); } /* * Setup Safari/JBus performance counter 0 in bus cycle counting * mode as timecounter. Unfortunately, this is broken with at * least the version 4 Tomatillos found in Fire V120 and Blade * 1500, which apparently actually count some different event at * ~0.5 and 3MHz respectively instead (also when running in full * power mode). Besides, one counter seems to be shared by a * "pair" of Tomatillos, too. */ if (sc->sc_half == 0) { SCHIZO_CTRL_WRITE_8(sc, STX_CTRL_PERF, (STX_CTRL_PERF_DIS << STX_CTRL_PERF_CNT1_SHIFT) | (STX_CTRL_PERF_BUSCYC << STX_CTRL_PERF_CNT0_SHIFT)); tc = malloc(sizeof(*tc), M_DEVBUF, M_NOWAIT | M_ZERO); if (tc == NULL) panic("%s: could not malloc timecounter", __func__); tc->tc_get_timecount = schizo_get_timecount; tc->tc_poll_pps = NULL; tc->tc_counter_mask = STX_CTRL_PERF_CNT_MASK; if (OF_getprop(OF_peer(0), "clock-frequency", &prop, sizeof(prop)) == -1) panic("%s: could not determine clock frequency", __func__); tc->tc_frequency = prop; tc->tc_name = strdup(device_get_nameunit(dev), M_DEVBUF); if (mode == SCHIZO_MODE_SCZ) tc->tc_quality = SCHIZO_PERF_CNT_QLTY; else tc->tc_quality = -SCHIZO_PERF_CNT_QLTY; tc->tc_priv = sc; tc_init(tc); } /* Set up the IOMMU. Both Schizo and Tomatillo have one per PBM. */ sc->sc_is.is_pmaxaddr = IOMMU_MAXADDR(STX_IOMMU_BITS); sc->sc_is.is_sb[0] = 0; sc->sc_is.is_sb[1] = 0; #ifdef notyet if (OF_getproplen(node, "no-streaming-cache") < 0) sc->sc_is.is_sb[0] = STX_PCI_STRBUF; #endif #define TSBCASE(x) \ case (IOTSB_BASESZ << (x)) << (IO_PAGE_SHIFT - IOTTE_SHIFT): \ tsbsize = (x); \ break; \ n = OF_getprop(node, "virtual-dma", (void *)prop_array, sizeof(prop_array)); if (n == -1 || n != sizeof(prop_array)) schizo_iommu_init(sc, 7, -1); else { switch (prop_array[1]) { TSBCASE(1); TSBCASE(2); TSBCASE(3); TSBCASE(4); TSBCASE(5); TSBCASE(6); TSBCASE(7); TSBCASE(8); default: panic("%s: unsupported DVMA size 0x%x", __func__, prop_array[1]); /* NOTREACHED */ } schizo_iommu_init(sc, tsbsize, prop_array[0]); } #undef TSBCASE /* Initialize memory and I/O rmans. */ sc->sc_pci_io_rman.rm_type = RMAN_ARRAY; sc->sc_pci_io_rman.rm_descr = "Schizo PCI I/O Ports"; if (rman_init(&sc->sc_pci_io_rman) != 0 || rman_manage_region(&sc->sc_pci_io_rman, 0, STX_IO_SIZE) != 0) panic("%s: failed to set up I/O rman", __func__); sc->sc_pci_mem_rman.rm_type = RMAN_ARRAY; sc->sc_pci_mem_rman.rm_descr = "Schizo PCI Memory"; if (rman_init(&sc->sc_pci_mem_rman) != 0 || rman_manage_region(&sc->sc_pci_mem_rman, 0, STX_MEM_SIZE) != 0) panic("%s: failed to set up memory rman", __func__); nrange = OF_getprop_alloc(node, "ranges", sizeof(*range), (void **)&range); /* * Make sure that the expected ranges are present. The * OFW_PCI_CS_MEM64 one is not currently used though. */ if (nrange != STX_NRANGE) panic("%s: unsupported number of ranges", __func__); /* * Find the addresses of the various bus spaces. * There should not be multiple ones of one kind. * The physical start addresses of the ranges are the configuration, * memory and I/O handles. */ for (n = 0; n < STX_NRANGE; n++) { i = OFW_PCI_RANGE_CS(&range[n]); if (sc->sc_pci_bh[i] != 0) panic("%s: duplicate range for space %d", __func__, i); sc->sc_pci_bh[i] = OFW_PCI_RANGE_PHYS(&range[n]); } free(range, M_OFWPROP); /* Register the softc, this is needed for paired Schizos. */ SLIST_INSERT_HEAD(&schizo_softcs, sc, sc_link); /* Allocate our tags. */ sc->sc_pci_memt = schizo_alloc_bus_tag(sc, PCI_MEMORY_BUS_SPACE); sc->sc_pci_iot = schizo_alloc_bus_tag(sc, PCI_IO_BUS_SPACE); sc->sc_pci_cfgt = schizo_alloc_bus_tag(sc, PCI_CONFIG_BUS_SPACE); if (bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0, sc->sc_is.is_pmaxaddr, ~0, NULL, NULL, sc->sc_is.is_pmaxaddr, 0xff, 0xffffffff, 0, NULL, NULL, &sc->sc_pci_dmat) != 0) panic("%s: bus_dma_tag_create failed", __func__); /* Customize the tag. */ sc->sc_pci_dmat->dt_cookie = &sc->sc_is; sc->sc_pci_dmat->dt_mt = &iommu_dma_methods; /* * Get the bus range from the firmware. * NB: Tomatillos don't support PCI bus reenumeration. */ n = OF_getprop(node, "bus-range", (void *)prop_array, sizeof(prop_array)); if (n == -1) panic("%s: could not get bus-range", __func__); if (n != sizeof(prop_array)) panic("%s: broken bus-range (%d)", __func__, n); if (bootverbose) device_printf(dev, "bus range %u to %u; PCI bus %d\n", prop_array[0], prop_array[1], prop_array[0]); sc->sc_pci_secbus = prop_array[0]; /* Clear any pending PCI error bits. */ PCIB_WRITE_CONFIG(dev, sc->sc_pci_secbus, STX_CS_DEVICE, STX_CS_FUNC, PCIR_STATUS, PCIB_READ_CONFIG(dev, sc->sc_pci_secbus, STX_CS_DEVICE, STX_CS_FUNC, PCIR_STATUS, 2), 2); SCHIZO_PCI_WRITE_8(sc, STX_PCI_CTRL, SCHIZO_PCI_READ_8(sc, STX_PCI_CTRL)); SCHIZO_PCI_WRITE_8(sc, STX_PCI_AFSR, SCHIZO_PCI_READ_8(sc, STX_PCI_AFSR)); /* * Establish handlers for interesting interrupts... * Someone at Sun clearly was smoking crack; with Schizos PCI * bus error interrupts for one PBM can be routed to the other * PBM though we obviously need to use the softc of the former * as the argument for the interrupt handler and the softc of * the latter as the argument for the interrupt controller. */ if (sc->sc_half == 0) { if ((ino_bitmap & (1ULL << STX_PCIERR_A_INO)) != 0 || (osc != NULL && ((struct schizo_icarg *)intr_vectors[ INTMAP_VEC(sc->sc_ign, STX_PCIERR_A_INO)].iv_icarg)-> sica_sc == osc)) /* * We are the driver for PBM A and either also * registered the interrupt controller for us or * the driver for PBM B has probed first and * registered it for us. */ schizo_set_intr(sc, 0, STX_PCIERR_A_INO, schizo_pci_bus); if ((ino_bitmap & (1ULL << STX_PCIERR_B_INO)) != 0 && osc != NULL) /* * We are the driver for PBM A but registered * the interrupt controller for PBM B, i.e. the * driver for PBM B attached first but couldn't * set up a handler for PBM B. */ schizo_set_intr(osc, 0, STX_PCIERR_B_INO, schizo_pci_bus); } else { if ((ino_bitmap & (1ULL << STX_PCIERR_B_INO)) != 0 || (osc != NULL && ((struct schizo_icarg *)intr_vectors[ INTMAP_VEC(sc->sc_ign, STX_PCIERR_B_INO)].iv_icarg)-> sica_sc == osc)) /* * We are the driver for PBM B and either also * registered the interrupt controller for us or * the driver for PBM A has probed first and * registered it for us. */ schizo_set_intr(sc, 0, STX_PCIERR_B_INO, schizo_pci_bus); if ((ino_bitmap & (1ULL << STX_PCIERR_A_INO)) != 0 && osc != NULL) /* * We are the driver for PBM B but registered * the interrupt controller for PBM A, i.e. the * driver for PBM A attached first but couldn't * set up a handler for PBM A. */ schizo_set_intr(osc, 0, STX_PCIERR_A_INO, schizo_pci_bus); } if ((ino_bitmap & (1ULL << STX_UE_INO)) != 0) schizo_set_intr(sc, 1, STX_UE_INO, schizo_ue); if ((ino_bitmap & (1ULL << STX_CE_INO)) != 0) schizo_set_intr(sc, 2, STX_CE_INO, schizo_ce); if ((ino_bitmap & (1ULL << STX_BUS_INO)) != 0) schizo_set_intr(sc, 3, STX_BUS_INO, schizo_host_bus); /* * According to the Schizo Errata I-13, consistent DMA flushing/ * syncing is FUBAR in version < 5 (i.e. revision < 2.3) bridges, * so we can't use it and need to live with the consequences. * With Schizo version >= 5, CDMA flushing/syncing is usable * but requires the the workaround described in Schizo Errata * I-23. With Tomatillo and XMITS, CDMA flushing/syncing works * as expected, Tomatillo version <= 4 (i.e. revision <= 2.3) * bridges additionally require a block store after a write to * TOMXMS_PCI_DMA_SYNC_PEND though. */ if ((sc->sc_mode == SCHIZO_MODE_SCZ && sc->sc_ver >= 5) || sc->sc_mode == SCHIZO_MODE_TOM || sc->sc_mode == SCHIZO_MODE_XMS) { sc->sc_flags |= SCHIZO_FLAGS_CDMA; if (sc->sc_mode == SCHIZO_MODE_SCZ) { n = STX_CDMA_A_INO + sc->sc_half; if (bus_set_resource(dev, SYS_RES_IRQ, 5, INTMAP_VEC(sc->sc_ign, n), 1) != 0) panic("%s: failed to add CDMA interrupt", __func__); i = schizo_intr_register(sc, n); if (i != 0) panic("%s: could not register interrupt " "controller for CDMA (%d)", __func__, i); (void)schizo_get_intrmap(sc, n, NULL, &sc->sc_cdma_clr); sc->sc_cdma_state = SCHIZO_CDMA_STATE_DONE; schizo_set_intr(sc, 5, n, schizo_cdma); } if (sc->sc_mode == SCHIZO_MODE_TOM && sc->sc_ver <= 4) sc->sc_flags |= SCHIZO_FLAGS_BSWAR; } /* * Set the latency timer register as this isn't always done by the * firmware. */ PCIB_WRITE_CONFIG(dev, sc->sc_pci_secbus, STX_CS_DEVICE, STX_CS_FUNC, PCIR_LATTIMER, OFW_PCI_LATENCY, 1); ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(ofw_pci_intr_t)); device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } static void schizo_set_intr(struct schizo_softc *sc, u_int index, u_int ino, driver_filter_t handler) { u_long vec; int rid; rid = index; sc->sc_irq_res[index] = bus_alloc_resource_any(sc->sc_dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res[index] == NULL || INTIGN(vec = rman_get_start(sc->sc_irq_res[index])) != sc->sc_ign || INTINO(vec) != ino || intr_vectors[vec].iv_ic != &schizo_ic || bus_setup_intr(sc->sc_dev, sc->sc_irq_res[index], INTR_TYPE_MISC | INTR_FAST, handler, NULL, sc, &sc->sc_ihand[index]) != 0) panic("%s: failed to set up interrupt %d", __func__, index); } static int schizo_intr_register(struct schizo_softc *sc, u_int ino) { struct schizo_icarg *sica; bus_addr_t intrclr, intrmap; int error; if (schizo_get_intrmap(sc, ino, &intrmap, &intrclr) == 0) return (ENXIO); sica = malloc(sizeof(*sica), M_DEVBUF, M_NOWAIT); if (sica == NULL) return (ENOMEM); sica->sica_sc = sc; sica->sica_map = intrmap; sica->sica_clr = intrclr; #ifdef SCHIZO_DEBUG device_printf(sc->sc_dev, "intr map (INO %d) %#lx: %#lx, clr: %#lx\n", ino, (u_long)intrmap, (u_long)SCHIZO_PCI_READ_8(sc, intrmap), (u_long)intrclr); #endif error = (intr_controller_register(INTMAP_VEC(sc->sc_ign, ino), &schizo_ic, sica)); if (error != 0) free(sica, M_DEVBUF); return (error); } static int schizo_get_intrmap(struct schizo_softc *sc, u_int ino, bus_addr_t *intrmapptr, bus_addr_t *intrclrptr) { bus_addr_t intrclr, intrmap; uint64_t mr; /* * XXX we only look for INOs rather than INRs since the firmware * may not provide the IGN and the IGN is constant for all devices * on that PCI controller. */ if (ino > STX_MAX_INO) { device_printf(sc->sc_dev, "out of range INO %d requested\n", ino); return (0); } intrmap = STX_PCI_IMAP_BASE + (ino << 3); intrclr = STX_PCI_ICLR_BASE + (ino << 3); mr = SCHIZO_PCI_READ_8(sc, intrmap); if (INTINO(mr) != ino) { device_printf(sc->sc_dev, "interrupt map entry does not match INO (%d != %d)\n", (int)INTINO(mr), ino); return (0); } if (intrmapptr != NULL) *intrmapptr = intrmap; if (intrclrptr != NULL) *intrclrptr = intrclr; return (1); } /* * Interrupt handlers */ static int schizo_pci_bus(void *arg) { struct schizo_softc *sc = arg; uint64_t afar, afsr, csr, iommu; uint32_t status; afar = SCHIZO_PCI_READ_8(sc, STX_PCI_AFAR); afsr = SCHIZO_PCI_READ_8(sc, STX_PCI_AFSR); csr = SCHIZO_PCI_READ_8(sc, STX_PCI_CTRL); iommu = SCHIZO_PCI_READ_8(sc, STX_PCI_IOMMU); status = PCIB_READ_CONFIG(sc->sc_dev, sc->sc_pci_secbus, STX_CS_DEVICE, STX_CS_FUNC, PCIR_STATUS, 2); if ((csr & STX_PCI_CTRL_MMU_ERR) != 0) { if ((iommu & TOM_PCI_IOMMU_ERR) == 0) goto clear_error; /* These are non-fatal if target abort was signaled. */ if ((status & PCIM_STATUS_STABORT) != 0 && ((iommu & TOM_PCI_IOMMU_ERRMASK) == TOM_PCI_IOMMU_INVALID_ERR || (iommu & TOM_PCI_IOMMU_ERR_ILLTSBTBW) != 0 || (iommu & TOM_PCI_IOMMU_ERR_BAD_VA) != 0)) { SCHIZO_PCI_WRITE_8(sc, STX_PCI_IOMMU, iommu); goto clear_error; } } panic("%s: PCI bus %c error AFAR %#llx AFSR %#llx PCI CSR %#llx " "IOMMU %#llx STATUS %#llx", device_get_name(sc->sc_dev), 'A' + sc->sc_half, (unsigned long long)afar, (unsigned long long)afsr, (unsigned long long)csr, (unsigned long long)iommu, (unsigned long long)status); clear_error: if (bootverbose) device_printf(sc->sc_dev, "PCI bus %c error AFAR %#llx AFSR %#llx PCI CSR %#llx " "STATUS %#llx", 'A' + sc->sc_half, (unsigned long long)afar, (unsigned long long)afsr, (unsigned long long)csr, (unsigned long long)status); /* Clear the error bits that we caught. */ PCIB_WRITE_CONFIG(sc->sc_dev, sc->sc_pci_secbus, STX_CS_DEVICE, STX_CS_FUNC, PCIR_STATUS, status, 2); SCHIZO_PCI_WRITE_8(sc, STX_PCI_CTRL, csr); SCHIZO_PCI_WRITE_8(sc, STX_PCI_AFSR, afsr); return (FILTER_HANDLED); } static int schizo_ue(void *arg) { struct schizo_softc *sc = arg; uint64_t afar, afsr; int i; mtx_lock_spin(sc->sc_mtx); afar = SCHIZO_CTRL_READ_8(sc, STX_CTRL_UE_AFAR); for (i = 0; i < 1000; i++) if (((afsr = SCHIZO_CTRL_READ_8(sc, STX_CTRL_UE_AFSR)) & STX_CTRL_CE_AFSR_ERRPNDG) == 0) break; mtx_unlock_spin(sc->sc_mtx); panic("%s: uncorrectable DMA error AFAR %#llx AFSR %#llx", device_get_name(sc->sc_dev), (unsigned long long)afar, (unsigned long long)afsr); return (FILTER_HANDLED); } static int schizo_ce(void *arg) { struct schizo_softc *sc = arg; uint64_t afar, afsr; int i; mtx_lock_spin(sc->sc_mtx); afar = SCHIZO_CTRL_READ_8(sc, STX_CTRL_CE_AFAR); for (i = 0; i < 1000; i++) if (((afsr = SCHIZO_CTRL_READ_8(sc, STX_CTRL_UE_AFSR)) & STX_CTRL_CE_AFSR_ERRPNDG) == 0) break; device_printf(sc->sc_dev, "correctable DMA error AFAR %#llx AFSR %#llx\n", (unsigned long long)afar, (unsigned long long)afsr); /* Clear the error bits that we caught. */ SCHIZO_CTRL_WRITE_8(sc, STX_CTRL_UE_AFSR, afsr); mtx_unlock_spin(sc->sc_mtx); return (FILTER_HANDLED); } static int schizo_host_bus(void *arg) { struct schizo_softc *sc = arg; uint64_t errlog; errlog = SCHIZO_CTRL_READ_8(sc, STX_CTRL_BUS_ERRLOG); panic("%s: %s error %#llx", device_get_name(sc->sc_dev), sc->sc_mode == SCHIZO_MODE_TOM ? "JBus" : "Safari", (unsigned long long)errlog); return (FILTER_HANDLED); } static int schizo_cdma(void *arg) { struct schizo_softc *sc = arg; atomic_store_rel_32(&sc->sc_cdma_state, SCHIZO_CDMA_STATE_DONE); return (FILTER_HANDLED); } static void schizo_iommu_init(struct schizo_softc *sc, int tsbsize, uint32_t dvmabase) { /* Punch in our copies. */ sc->sc_is.is_bustag = rman_get_bustag(sc->sc_mem_res[STX_PCI]); sc->sc_is.is_bushandle = rman_get_bushandle(sc->sc_mem_res[STX_PCI]); sc->sc_is.is_iommu = STX_PCI_IOMMU; sc->sc_is.is_dtag = STX_PCI_IOMMU_TLB_TAG_DIAG; sc->sc_is.is_ddram = STX_PCI_IOMMU_TLB_DATA_DIAG; sc->sc_is.is_dqueue = STX_PCI_IOMMU_QUEUE_DIAG; sc->sc_is.is_dva = STX_PCI_IOMMU_SVADIAG; sc->sc_is.is_dtcmp = STX_PCI_IOMMU_TLB_CMP_DIAG; iommu_init(device_get_nameunit(sc->sc_dev), &sc->sc_is, tsbsize, dvmabase, 0); } static int schizo_maxslots(device_t dev) { struct schizo_softc *sc; sc = device_get_softc(dev); if (sc->sc_mode == SCHIZO_MODE_SCZ) return (sc->sc_half == 0 ? 4 : 6); /* XXX: is this correct? */ return (PCI_SLOTMAX); } static uint32_t schizo_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct schizo_softc *sc; bus_space_handle_t bh; u_long offset = 0; uint32_t r, wrd; int i; uint16_t shrt; uint8_t byte; sc = device_get_softc(dev); /* * The Schizo bridges contain a dupe of their header at 0x80. */ if (sc->sc_mode == SCHIZO_MODE_SCZ && bus == sc->sc_pci_secbus && slot == STX_CS_DEVICE && func == STX_CS_FUNC && reg + width > 0x80) return (0); offset = STX_CONF_OFF(bus, slot, func, reg); bh = sc->sc_pci_bh[OFW_PCI_CS_CONFIG]; switch (width) { case 1: i = bus_space_peek_1(sc->sc_pci_cfgt, bh, offset, &byte); r = byte; break; case 2: i = bus_space_peek_2(sc->sc_pci_cfgt, bh, offset, &shrt); r = shrt; break; case 4: i = bus_space_peek_4(sc->sc_pci_cfgt, bh, offset, &wrd); r = wrd; break; default: panic("%s: bad width", __func__); /* NOTREACHED */ } if (i) { #ifdef SCHIZO_DEBUG printf("%s: read data error reading: %d.%d.%d: 0x%x\n", __func__, bus, slot, func, reg); #endif r = -1; } return (r); } static void schizo_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int width) { struct schizo_softc *sc; bus_space_handle_t bh; u_long offset = 0; sc = device_get_softc(dev); offset = STX_CONF_OFF(bus, slot, func, reg); bh = sc->sc_pci_bh[OFW_PCI_CS_CONFIG]; switch (width) { case 1: bus_space_write_1(sc->sc_pci_cfgt, bh, offset, val); break; case 2: bus_space_write_2(sc->sc_pci_cfgt, bh, offset, val); break; case 4: bus_space_write_4(sc->sc_pci_cfgt, bh, offset, val); break; default: panic("%s: bad width", __func__); /* NOTREACHED */ } } static int schizo_route_interrupt(device_t bridge, device_t dev, int pin) { struct schizo_softc *sc; struct ofw_pci_register reg; ofw_pci_intr_t pintr, mintr; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bridge); pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), maskbuf)) return (mintr); device_printf(bridge, "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 schizo_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct schizo_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_pci_secbus; return (0); } return (ENOENT); } static int schizo_dma_sync_stub(void *arg) { struct timeval cur, end; struct schizo_dma_sync *sds = arg; struct schizo_softc *sc = sds->sds_sc; uint32_t state; (void)PCIB_READ_CONFIG(sds->sds_ppb, sds->sds_bus, sds->sds_slot, sds->sds_func, PCIR_VENDOR, 2); for (; atomic_cmpset_acq_32(&sc->sc_cdma_state, SCHIZO_CDMA_STATE_DONE, SCHIZO_CDMA_STATE_PENDING) == 0;) ; SCHIZO_PCI_WRITE_8(sc, sc->sc_cdma_clr, 1); microuptime(&cur); end.tv_sec = 1; end.tv_usec = 0; timevaladd(&end, &cur); for (; (state = atomic_load_32(&sc->sc_cdma_state)) != SCHIZO_CDMA_STATE_DONE && timevalcmp(&cur, &end, <=);) microuptime(&cur); if (state != SCHIZO_CDMA_STATE_DONE) panic("%s: DMA does not sync", __func__); return (sds->sds_handler(sds->sds_arg)); } #define VIS_BLOCKSIZE 64 static int ichip_dma_sync_stub(void *arg) { static u_char buf[VIS_BLOCKSIZE] __aligned(VIS_BLOCKSIZE); struct timeval cur, end; struct schizo_dma_sync *sds = arg; struct schizo_softc *sc = sds->sds_sc; register_t reg, s; (void)PCIB_READ_CONFIG(sds->sds_ppb, sds->sds_bus, sds->sds_slot, sds->sds_func, PCIR_VENDOR, 2); SCHIZO_PCI_WRITE_8(sc, TOMXMS_PCI_DMA_SYNC_PEND, sds->sds_syncval); microuptime(&cur); end.tv_sec = 1; end.tv_usec = 0; timevaladd(&end, &cur); for (; ((reg = SCHIZO_PCI_READ_8(sc, TOMXMS_PCI_DMA_SYNC_PEND)) & sds->sds_syncval) != 0 && timevalcmp(&cur, &end, <=);) microuptime(&cur); if ((reg & sds->sds_syncval) != 0) panic("%s: DMA does not sync", __func__); if ((sc->sc_flags & SCHIZO_FLAGS_BSWAR) != 0) { s = intr_disable(); reg = rd(fprs); wr(fprs, reg | FPRS_FEF, 0); __asm __volatile("stda %%f0, [%0] %1" : : "r" (buf), "n" (ASI_BLK_COMMIT_S)); membar(Sync); wr(fprs, reg, 0); intr_restore(s); } return (sds->sds_handler(sds->sds_arg)); } static void schizo_intr_enable(void *arg) { struct intr_vector *iv = arg; struct schizo_icarg *sica = iv->iv_icarg; SCHIZO_PCI_WRITE_8(sica->sica_sc, sica->sica_map, INTMAP_ENABLE(iv->iv_vec, iv->iv_mid)); } static void schizo_intr_disable(void *arg) { struct intr_vector *iv = arg; struct schizo_icarg *sica = iv->iv_icarg; SCHIZO_PCI_WRITE_8(sica->sica_sc, sica->sica_map, iv->iv_vec); } static void schizo_intr_assign(void *arg) { struct intr_vector *iv = arg; struct schizo_icarg *sica = iv->iv_icarg; SCHIZO_PCI_WRITE_8(sica->sica_sc, sica->sica_map, INTMAP_TID( SCHIZO_PCI_READ_8(sica->sica_sc, sica->sica_map), iv->iv_mid)); } static void schizo_intr_clear(void *arg) { struct intr_vector *iv = arg; struct schizo_icarg *sica = iv->iv_icarg; SCHIZO_PCI_WRITE_8(sica->sica_sc, sica->sica_clr, 0); } static int schizo_setup_intr(device_t dev, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { devclass_t pci_devclass; device_t cdev, pdev, pcidev; struct schizo_dma_sync *sds; struct schizo_softc *sc; u_long vec; int error, found; sc = device_get_softc(dev); /* * Make sure the vector is fully specified and we registered * our interrupt controller for it. */ vec = rman_get_start(ires); if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &schizo_ic) { device_printf(dev, "invalid interrupt vector 0x%lx\n", vec); return (EINVAL); } /* * Install a a wrapper for CDMA flushing/syncing for devices * behind PCI-PCI bridges if possible. */ pcidev = NULL; found = 0; pci_devclass = devclass_find("pci"); for (cdev = child; cdev != dev; cdev = pdev) { pdev = device_get_parent(cdev); if (pcidev == NULL) { if (device_get_devclass(pdev) != pci_devclass) continue; pcidev = cdev; continue; } if (pci_get_class(cdev) == PCIC_BRIDGE && pci_get_subclass(cdev) == PCIS_BRIDGE_PCI) found = 1; } if ((sc->sc_flags & SCHIZO_FLAGS_CDMA) != 0) { sds = malloc(sizeof(*sds), M_DEVBUF, M_NOWAIT | M_ZERO); if (sds == NULL) return (ENOMEM); if (found != 0 && pcidev != NULL) { sds->sds_sc = sc; sds->sds_arg = arg; sds->sds_ppb = device_get_parent(device_get_parent(pcidev)); sds->sds_bus = pci_get_bus(pcidev); sds->sds_slot = pci_get_slot(pcidev); sds->sds_func = pci_get_function(pcidev); sds->sds_syncval = 1ULL << INTINO(vec); if (bootverbose) device_printf(dev, "installed DMA sync " "wrapper for device %d.%d on bus %d\n", sds->sds_slot, sds->sds_func, sds->sds_bus); #define DMA_SYNC_STUB \ (sc->sc_mode == SCHIZO_MODE_SCZ ? schizo_dma_sync_stub : \ ichip_dma_sync_stub) if (intr == NULL) { sds->sds_handler = filt; error = bus_generic_setup_intr(dev, child, ires, flags, DMA_SYNC_STUB, intr, sds, cookiep); } else { sds->sds_handler = (driver_filter_t *)intr; error = bus_generic_setup_intr(dev, child, ires, flags, filt, (driver_intr_t *) DMA_SYNC_STUB, sds, cookiep); } #undef DMA_SYNC_STUB } else error = bus_generic_setup_intr(dev, child, ires, flags, filt, intr, arg, cookiep); if (error != 0) { free(sds, M_DEVBUF); return (error); } sds->sds_cookie = *cookiep; *cookiep = sds; return (error); } else if (found != 0) device_printf(dev, "WARNING: using devices behind PCI-PCI " "bridges may cause data corruption"); return (bus_generic_setup_intr(dev, child, ires, flags, filt, intr, arg, cookiep)); } static int schizo_teardown_intr(device_t dev, device_t child, struct resource *vec, void *cookie) { struct schizo_dma_sync *sds; struct schizo_softc *sc; int error; sc = device_get_softc(dev); if ((sc->sc_flags & SCHIZO_FLAGS_CDMA) != 0) { sds = cookie; error = bus_generic_teardown_intr(dev, child, vec, sds->sds_cookie); if (error == 0) free(sds, M_DEVBUF); return (error); } return (bus_generic_teardown_intr(dev, child, vec, cookie)); } static struct resource * schizo_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 schizo_softc *sc; struct resource *rv; struct rman *rm; bus_space_tag_t bt; bus_space_handle_t bh; int needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; sc = device_get_softc(bus); if (type == SYS_RES_IRQ) { /* * XXX: Don't accept blank ranges for now, only single * interrupts. The other case should not happen with * the MI PCI code... * XXX: This may return a resource that is out of the * range that was specified. Is this correct...? */ if (start != end) panic("%s: XXX: interrupt range", __func__); start = end = INTMAP_VEC(sc->sc_ign, end); return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags)); } switch (type) { case SYS_RES_MEMORY: rm = &sc->sc_pci_mem_rman; bt = sc->sc_pci_memt; bh = sc->sc_pci_bh[OFW_PCI_CS_MEM32]; break; case SYS_RES_IOPORT: rm = &sc->sc_pci_io_rman; bt = sc->sc_pci_iot; bh = sc->sc_pci_bh[OFW_PCI_CS_IO]; break; default: return (NULL); /* NOTREACHED */ } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); bh += rman_get_start(rv); rman_set_bustag(rv, bt); rman_set_bushandle(rv, bh); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } return (rv); } static int schizo_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { void *p; int error; if (type == SYS_RES_IRQ) return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (type == SYS_RES_MEMORY) { /* * Need to memory-map the device space, as some drivers * depend on the virtual address being set and usable. */ error = sparc64_bus_mem_map(rman_get_bustag(r), rman_get_bushandle(r), rman_get_size(r), 0, 0, &p); if (error != 0) return (error); rman_set_virtual(r, p); } return (rman_activate_resource(r)); } static int schizo_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (type == SYS_RES_IRQ) return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (type == SYS_RES_MEMORY) { sparc64_bus_mem_unmap(rman_get_virtual(r), rman_get_size(r)); rman_set_virtual(r, NULL); } return (rman_deactivate_resource(r)); } static int schizo_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int error; if (type == SYS_RES_IRQ) return (BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r)); if (rman_get_flags(r) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, r); if (error) return (error); } return (rman_release_resource(r)); } static bus_dma_tag_t schizo_get_dma_tag(device_t bus, device_t child) { struct schizo_softc *sc; sc = device_get_softc(bus); return (sc->sc_pci_dmat); } static phandle_t schizo_get_node(device_t bus, device_t dev) { struct schizo_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 bus_space_tag_t schizo_alloc_bus_tag(struct schizo_softc *sc, int type) { bus_space_tag_t bt; bt = (bus_space_tag_t)malloc(sizeof(struct bus_space_tag), M_DEVBUF, M_NOWAIT | M_ZERO); if (bt == NULL) panic("%s: out of memory", __func__); bt->bst_cookie = sc; bt->bst_parent = rman_get_bustag(sc->sc_mem_res[STX_PCI]); bt->bst_type = type; return (bt); } static u_int schizo_get_timecount(struct timecounter *tc) { struct schizo_softc *sc; sc = tc->tc_priv; return (SCHIZO_CTRL_READ_8(sc, STX_CTRL_PERF_CNT) & (STX_CTRL_PERF_CNT_MASK << STX_CTRL_PERF_CNT_CNT0_SHIFT)); } Index: head/sys/sparc64/sbus/sbus.c =================================================================== --- head/sys/sparc64/sbus/sbus.c (revision 186127) +++ head/sys/sparc64/sbus/sbus.c (revision 186128) @@ -1,988 +1,989 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Kranenburg. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /*- * Copyright (c) 1999 Eduardo Horvath * Copyright (c) 2002 by Thomas Moestl . * Copyright (c) 2005 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. * * 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. * * from: @(#)sbus.c 8.1 (Berkeley) 6/11/93 * from: NetBSD: sbus.c,v 1.46 2001/10/07 20:30:41 eeh Exp */ #include __FBSDID("$FreeBSD$"); /* * SBus support. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct sbus_devinfo { int sdi_burstsz; int sdi_clockfreq; int sdi_slot; struct ofw_bus_devinfo sdi_obdinfo; struct resource_list sdi_rl; }; /* Range descriptor, allocated for each sc_range. */ struct sbus_rd { bus_addr_t rd_poffset; bus_addr_t rd_pend; int rd_slot; bus_addr_t rd_coffset; bus_addr_t rd_cend; struct rman rd_rman; bus_space_handle_t rd_bushandle; struct resource *rd_res; }; struct sbus_softc { device_t sc_dev; bus_dma_tag_t sc_cdmatag; bus_space_tag_t sc_cbustag; int sc_clockfreq; /* clock frequency (in Hz) */ int sc_nrange; struct sbus_rd *sc_rd; int sc_burst; /* burst transfer sizes supp. */ struct resource *sc_sysio_res; int sc_ign; /* IGN for this sysio */ struct iommu_state sc_is; /* IOMMU state (iommuvar.h) */ struct resource *sc_ot_ires; void *sc_ot_ihand; struct resource *sc_pf_ires; void *sc_pf_ihand; }; #define SYSIO_READ8(sc, off) \ bus_read_8((sc)->sc_sysio_res, (off)) #define SYSIO_WRITE8(sc, off, v) \ bus_write_8((sc)->sc_sysio_res, (off), (v)) static device_probe_t sbus_probe; static device_attach_t sbus_attach; static bus_print_child_t sbus_print_child; static bus_probe_nomatch_t sbus_probe_nomatch; static bus_read_ivar_t sbus_read_ivar; static bus_get_resource_list_t sbus_get_resource_list; static bus_setup_intr_t sbus_setup_intr; static bus_alloc_resource_t sbus_alloc_resource; static bus_release_resource_t sbus_release_resource; static bus_activate_resource_t sbus_activate_resource; static bus_deactivate_resource_t sbus_deactivate_resource; static bus_get_dma_tag_t sbus_get_dma_tag; static ofw_bus_get_devinfo_t sbus_get_devinfo; static int sbus_inlist(const char *, const char **); static struct sbus_devinfo * sbus_setup_dinfo(device_t, struct sbus_softc *, phandle_t); static void sbus_destroy_dinfo(struct sbus_devinfo *); static void sbus_intr_enable(void *); static void sbus_intr_disable(void *); static void sbus_intr_assign(void *); static void sbus_intr_clear(void *); static int sbus_find_intrmap(struct sbus_softc *, u_int, bus_addr_t *, bus_addr_t *); static bus_space_tag_t sbus_alloc_bustag(struct sbus_softc *); static driver_intr_t sbus_overtemp; static driver_intr_t sbus_pwrfail; static int sbus_print_res(struct sbus_devinfo *); static device_method_t sbus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbus_probe), DEVMETHOD(device_attach, sbus_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, sbus_print_child), DEVMETHOD(bus_probe_nomatch, sbus_probe_nomatch), DEVMETHOD(bus_read_ivar, sbus_read_ivar), DEVMETHOD(bus_setup_intr, sbus_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, sbus_alloc_resource), DEVMETHOD(bus_activate_resource, sbus_activate_resource), DEVMETHOD(bus_deactivate_resource, sbus_deactivate_resource), DEVMETHOD(bus_release_resource, sbus_release_resource), DEVMETHOD(bus_get_resource_list, sbus_get_resource_list), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_get_dma_tag, sbus_get_dma_tag), + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, sbus_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 } }; static driver_t sbus_driver = { "sbus", sbus_methods, sizeof(struct sbus_softc), }; static devclass_t sbus_devclass; DRIVER_MODULE(sbus, nexus, sbus_driver, sbus_devclass, 0, 0); MODULE_VERSION(sbus, 1); #define OFW_SBUS_TYPE "sbus" #define OFW_SBUS_NAME "sbus" static const struct intr_controller sbus_ic = { sbus_intr_enable, sbus_intr_disable, sbus_intr_assign, sbus_intr_clear }; struct sbus_icarg { struct sbus_softc *sica_sc; bus_addr_t sica_map; bus_addr_t sica_clr; }; static const char *sbus_order_first[] = { "auxio", "dma", NULL }; static int sbus_inlist(const char *name, const char **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); } static int sbus_probe(device_t dev) { const char *t; t = ofw_bus_get_type(dev); if (((t == NULL || strcmp(t, OFW_SBUS_TYPE) != 0)) && strcmp(ofw_bus_get_name(dev), OFW_SBUS_NAME) != 0) return (ENXIO); device_set_desc(dev, "U2S UPA-SBus bridge"); return (0); } static int sbus_attach(device_t dev) { struct sbus_softc *sc; struct sbus_devinfo *sdi; struct sbus_icarg *sica; struct sbus_ranges *range; struct resource *res; struct resource_list *rl; device_t cdev; bus_addr_t intrclr, intrmap, phys; bus_size_t size; u_long vec; phandle_t child, node; int clock, i, intr, rid; sc = device_get_softc(dev); sc->sc_dev = dev; node = ofw_bus_get_node(dev); rid = 0; sc->sc_sysio_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_sysio_res == NULL) panic("%s: cannot allocate device memory", __func__); if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1) panic("%s: cannot get IGN", __func__); sc->sc_ign = INTIGN(intr); sc->sc_cbustag = sbus_alloc_bustag(sc); /* * Record clock frequency for synchronous SCSI. * IS THIS THE CORRECT DEFAULT?? */ if (OF_getprop(node, "clock-frequency", &clock, sizeof(clock)) == -1) clock = 25000000; sc->sc_clockfreq = clock; clock /= 1000; device_printf(dev, "clock %d.%03d MHz\n", clock / 1000, clock % 1000); /* * Collect address translations from the OBP. */ if ((sc->sc_nrange = OF_getprop_alloc(node, "ranges", sizeof(*range), (void **)&range)) == -1) { panic("%s: error getting ranges property", __func__); } sc->sc_rd = (struct sbus_rd *)malloc(sizeof(*sc->sc_rd) * sc->sc_nrange, M_DEVBUF, M_NOWAIT); if (sc->sc_rd == NULL) panic("%s: cannot allocate rmans", __func__); /* * Preallocate all space that the SBus bridge decodes, so that nothing * else gets in the way; set up rmans etc. */ rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); for (i = 0; i < sc->sc_nrange; i++) { phys = range[i].poffset | ((bus_addr_t)range[i].pspace << 32); size = range[i].size; sc->sc_rd[i].rd_slot = range[i].cspace; sc->sc_rd[i].rd_coffset = range[i].coffset; sc->sc_rd[i].rd_cend = sc->sc_rd[i].rd_coffset + size; rid = resource_list_add_next(rl, SYS_RES_MEMORY, phys, phys + size - 1, size); if ((res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE)) == NULL) panic("%s: cannot allocate decoded range", __func__); sc->sc_rd[i].rd_bushandle = rman_get_bushandle(res); sc->sc_rd[i].rd_rman.rm_type = RMAN_ARRAY; sc->sc_rd[i].rd_rman.rm_descr = "SBus Device Memory"; if (rman_init(&sc->sc_rd[i].rd_rman) != 0 || rman_manage_region(&sc->sc_rd[i].rd_rman, 0, size) != 0) panic("%s: failed to set up memory rman", __func__); sc->sc_rd[i].rd_poffset = phys; sc->sc_rd[i].rd_pend = phys + size; sc->sc_rd[i].rd_res = res; } free(range, M_OFWPROP); /* * Get the SBus burst transfer size if burst transfers are supported. * XXX: is the default correct? */ if (OF_getprop(node, "burst-sizes", &sc->sc_burst, sizeof(sc->sc_burst)) == -1 || sc->sc_burst == 0) sc->sc_burst = SBUS_BURST_DEF; /* initalise the IOMMU */ /* punch in our copies */ sc->sc_is.is_pmaxaddr = IOMMU_MAXADDR(SBUS_IOMMU_BITS); sc->sc_is.is_bustag = rman_get_bustag(sc->sc_sysio_res); sc->sc_is.is_bushandle = rman_get_bushandle(sc->sc_sysio_res); sc->sc_is.is_iommu = SBR_IOMMU; sc->sc_is.is_dtag = SBR_IOMMU_TLB_TAG_DIAG; sc->sc_is.is_ddram = SBR_IOMMU_TLB_DATA_DIAG; sc->sc_is.is_dqueue = SBR_IOMMU_QUEUE_DIAG; sc->sc_is.is_dva = SBR_IOMMU_SVADIAG; sc->sc_is.is_dtcmp = 0; sc->sc_is.is_sb[0] = SBR_STRBUF; sc->sc_is.is_sb[1] = 0; /* * Note: the SBus IOMMU ignores the high bits of an address, so a NULL * DMA pointer will be translated by the first page of the IOTSB. * To detect bugs we'll allocate and ignore the first entry. */ iommu_init(device_get_nameunit(dev), &sc->sc_is, 3, -1, 1); /* Create the DMA tag. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0, sc->sc_is.is_pmaxaddr, ~0, NULL, NULL, sc->sc_is.is_pmaxaddr, 0xff, 0xffffffff, 0, NULL, NULL, &sc->sc_cdmatag) != 0) panic("%s: bus_dma_tag_create failed", __func__); /* Customize the tag. */ sc->sc_cdmatag->dt_cookie = &sc->sc_is; sc->sc_cdmatag->dt_mt = &iommu_dma_methods; /* * Hunt through all the interrupt mapping regs and register our * interrupt controller for the corresponding interrupt vectors. */ for (i = 0; i <= SBUS_MAX_INO; i++) { if (sbus_find_intrmap(sc, i, &intrmap, &intrclr) == 0) continue; sica = malloc(sizeof(*sica), M_DEVBUF, M_NOWAIT); if (sica == NULL) panic("%s: could not allocate interrupt controller " "argument", __func__); sica->sica_sc = sc; sica->sica_map = intrmap; sica->sica_clr = intrclr; #ifdef SBUS_DEBUG device_printf(dev, "intr map (INO %d, %s) %#lx: %#lx, clr: %#lx\n", i, (i & INTMAP_OBIO_MASK) == 0 ? "SBus slot" : "OBIO", (u_long)intrmap, (u_long)SYSIO_READ8(sc, intrmap), (u_long)intrclr); #endif if (intr_controller_register(INTMAP_VEC(sc->sc_ign, i), &sbus_ic, sica) != 0) panic("%s: could not register interrupt controller " "for INO %d", __func__, i); } /* Enable the over-temperature and power-fail interrupts. */ rid = 4; sc->sc_ot_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_ot_ires == NULL || INTIGN(vec = rman_get_start(sc->sc_ot_ires)) != sc->sc_ign || INTVEC(SYSIO_READ8(sc, SBR_THERM_INT_MAP)) != vec || intr_vectors[vec].iv_ic != &sbus_ic || bus_setup_intr(dev, sc->sc_ot_ires, INTR_TYPE_MISC, NULL, sbus_overtemp, sc, &sc->sc_ot_ihand) != 0) panic("%s: failed to set up temperature interrupt", __func__); rid = 3; sc->sc_pf_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_pf_ires == NULL || INTIGN(vec = rman_get_start(sc->sc_pf_ires)) != sc->sc_ign || INTVEC(SYSIO_READ8(sc, SBR_POWER_INT_MAP)) != vec || intr_vectors[vec].iv_ic != &sbus_ic || bus_setup_intr(dev, sc->sc_pf_ires, INTR_TYPE_MISC, NULL, sbus_pwrfail, sc, &sc->sc_pf_ihand) != 0) panic("%s: failed to set up power fail interrupt", __func__); /* Initialize the counter-timer. */ sparc64_counter_init(device_get_nameunit(dev), rman_get_bustag(sc->sc_sysio_res), rman_get_bushandle(sc->sc_sysio_res), SBR_TC0); /* * Loop through ROM children, fixing any relative addresses * and then configuring each device. */ for (child = OF_child(node); child != 0; child = OF_peer(child)) { if ((sdi = sbus_setup_dinfo(dev, sc, child)) == NULL) continue; /* * For devices where there are variants that are actually * split into two SBus devices (as opposed to the first * half of the device being a SBus device and the second * half hanging off of the first one) like 'auxio' and * 'SUNW,fdtwo' or 'dma' and 'esp' probe the SBus device * which is a prerequisite to the driver attaching to the * second one with a lower order. Saves us from dealing * with different probe orders in the respective device * drivers which generally is more hackish. */ cdev = device_add_child_ordered(dev, (OF_child(child) == 0 && sbus_inlist(sdi->sdi_obdinfo.obd_name, sbus_order_first)) ? SBUS_ORDER_FIRST : SBUS_ORDER_NORMAL, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child_ordered failed\n", sdi->sdi_obdinfo.obd_name); sbus_destroy_dinfo(sdi); continue; } device_set_ivars(cdev, sdi); } return (bus_generic_attach(dev)); } static struct sbus_devinfo * sbus_setup_dinfo(device_t dev, struct sbus_softc *sc, phandle_t node) { struct sbus_devinfo *sdi; struct sbus_regs *reg; u_int32_t base, iv, *intr; int i, nreg, nintr, slot, rslot; sdi = malloc(sizeof(*sdi), M_DEVBUF, M_ZERO | M_WAITOK); if (ofw_bus_gen_setup_devinfo(&sdi->sdi_obdinfo, node) != 0) { free(sdi, M_DEVBUF); return (NULL); } resource_list_init(&sdi->sdi_rl); slot = -1; nreg = OF_getprop_alloc(node, "reg", sizeof(*reg), (void **)®); if (nreg == -1) { if (sdi->sdi_obdinfo.obd_type == NULL || strcmp(sdi->sdi_obdinfo.obd_type, "hierarchical") != 0) { device_printf(dev, "<%s>: incomplete\n", sdi->sdi_obdinfo.obd_name); goto fail; } } else { for (i = 0; i < nreg; i++) { base = reg[i].sbr_offset; if (SBUS_ABS(base)) { rslot = SBUS_ABS_TO_SLOT(base); base = SBUS_ABS_TO_OFFSET(base); } else rslot = reg[i].sbr_slot; if (slot != -1 && slot != rslot) { device_printf(dev, "<%s>: multiple slots\n", sdi->sdi_obdinfo.obd_name); free(reg, M_OFWPROP); goto fail; } slot = rslot; resource_list_add(&sdi->sdi_rl, SYS_RES_MEMORY, i, base, base + reg[i].sbr_size, reg[i].sbr_size); } free(reg, M_OFWPROP); } sdi->sdi_slot = slot; /* * The `interrupts' property contains the SBus interrupt level. */ nintr = OF_getprop_alloc(node, "interrupts", sizeof(*intr), (void **)&intr); if (nintr != -1) { for (i = 0; i < nintr; i++) { iv = intr[i]; /* * SBus card devices need the slot number encoded into * the vector as this is generally not done. */ if ((iv & INTMAP_OBIO_MASK) == 0) iv |= slot << 3; iv = INTMAP_VEC(sc->sc_ign, iv); resource_list_add(&sdi->sdi_rl, SYS_RES_IRQ, i, iv, iv, 1); } free(intr, M_OFWPROP); } if (OF_getprop(node, "burst-sizes", &sdi->sdi_burstsz, sizeof(sdi->sdi_burstsz)) == -1) sdi->sdi_burstsz = sc->sc_burst; else sdi->sdi_burstsz &= sc->sc_burst; if (OF_getprop(node, "clock-frequency", &sdi->sdi_clockfreq, sizeof(sdi->sdi_clockfreq)) == -1) sdi->sdi_clockfreq = sc->sc_clockfreq; return (sdi); fail: sbus_destroy_dinfo(sdi); return (NULL); } static void sbus_destroy_dinfo(struct sbus_devinfo *dinfo) { resource_list_free(&dinfo->sdi_rl); ofw_bus_gen_destroy_devinfo(&dinfo->sdi_obdinfo); free(dinfo, M_DEVBUF); } static int sbus_print_child(device_t dev, device_t child) { int rv; rv = bus_print_child_header(dev, child); rv += sbus_print_res(device_get_ivars(child)); rv += bus_print_child_footer(dev, child); return (rv); } static void sbus_probe_nomatch(device_t dev, device_t child) { const char *type; device_printf(dev, "<%s>", ofw_bus_get_name(child)); sbus_print_res(device_get_ivars(child)); type = ofw_bus_get_type(child); printf(" type %s (no driver attached)\n", type != NULL ? type : "unknown"); } static int sbus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct sbus_softc *sc; struct sbus_devinfo *dinfo; sc = device_get_softc(dev); if ((dinfo = device_get_ivars(child)) == NULL) return (ENOENT); switch (which) { case SBUS_IVAR_BURSTSZ: *result = dinfo->sdi_burstsz; break; case SBUS_IVAR_CLOCKFREQ: *result = dinfo->sdi_clockfreq; break; case SBUS_IVAR_IGN: *result = sc->sc_ign; break; case SBUS_IVAR_SLOT: *result = dinfo->sdi_slot; break; default: return (ENOENT); } return (0); } static struct resource_list * sbus_get_resource_list(device_t dev, device_t child) { struct sbus_devinfo *sdi; sdi = device_get_ivars(child); return (&sdi->sdi_rl); } static void sbus_intr_enable(void *arg) { struct intr_vector *iv = arg; struct sbus_icarg *sica = iv->iv_icarg; SYSIO_WRITE8(sica->sica_sc, sica->sica_map, INTMAP_ENABLE(iv->iv_vec, iv->iv_mid)); } static void sbus_intr_disable(void *arg) { struct intr_vector *iv = arg; struct sbus_icarg *sica = iv->iv_icarg; SYSIO_WRITE8(sica->sica_sc, sica->sica_map, iv->iv_vec); } static void sbus_intr_assign(void *arg) { struct intr_vector *iv = arg; struct sbus_icarg *sica = iv->iv_icarg; SYSIO_WRITE8(sica->sica_sc, sica->sica_map, INTMAP_TID( SYSIO_READ8(sica->sica_sc, sica->sica_map), iv->iv_mid)); } static void sbus_intr_clear(void *arg) { struct intr_vector *iv = arg; struct sbus_icarg *sica = iv->iv_icarg; SYSIO_WRITE8(sica->sica_sc, sica->sica_clr, 0); } static int sbus_find_intrmap(struct sbus_softc *sc, u_int ino, bus_addr_t *intrmapptr, bus_addr_t *intrclrptr) { bus_addr_t intrclr, intrmap; int i; if (ino > SBUS_MAX_INO) { device_printf(sc->sc_dev, "out of range INO %d requested\n", ino); return (0); } if ((ino & INTMAP_OBIO_MASK) == 0) { intrmap = SBR_SLOT0_INT_MAP + INTSLOT(ino) * 8; intrclr = SBR_SLOT0_INT_CLR + (INTSLOT(ino) * 8 * 8) + (INTPRI(ino) * 8); } else { intrclr = 0; for (i = 0, intrmap = SBR_SCSI_INT_MAP; intrmap <= SBR_RESERVED_INT_MAP; intrmap += 8, i++) { if (INTVEC(SYSIO_READ8(sc, intrmap)) == INTMAP_VEC(sc->sc_ign, ino)) { intrclr = SBR_SCSI_INT_CLR + i * 8; break; } } if (intrclr == 0) return (0); } if (intrmapptr != NULL) *intrmapptr = intrmap; if (intrclrptr != NULL) *intrclrptr = intrclr; return (1); } static int sbus_setup_intr(device_t dev, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { struct sbus_softc *sc; u_long vec; sc = device_get_softc(dev); /* * Make sure the vector is fully specified and we registered * our interrupt controller for it. */ vec = rman_get_start(ires); if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &sbus_ic) { device_printf(dev, "invalid interrupt vector 0x%lx\n", vec); return (EINVAL); } return (bus_generic_setup_intr(dev, child, ires, flags, filt, intr, arg, cookiep)); } static struct resource * sbus_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 sbus_softc *sc; struct rman *rm; struct resource *rv; struct resource_list *rl; struct resource_list_entry *rle; device_t schild; bus_space_handle_t bh; bus_addr_t toffs; bus_size_t tend; int i, slot; int isdefault, needactivate, passthrough; isdefault = (start == 0UL && end == ~0UL); needactivate = flags & RF_ACTIVE; passthrough = (device_get_parent(child) != bus); rle = NULL; sc = device_get_softc(bus); rl = BUS_GET_RESOURCE_LIST(bus, child); switch (type) { case SYS_RES_IRQ: return (resource_list_alloc(rl, bus, child, type, rid, start, end, count, flags)); case SYS_RES_MEMORY: if (!passthrough) { rle = resource_list_find(rl, type, *rid); if (rle == NULL) return (NULL); if (rle->res != NULL) panic("%s: resource entry is busy", __func__); if (isdefault) { start = rle->start; count = ulmax(count, rle->count); end = ulmax(rle->end, start + count - 1); } } rm = NULL; bh = toffs = tend = 0; schild = child; while (device_get_parent(schild) != bus) schild = device_get_parent(schild); slot = sbus_get_slot(schild); for (i = 0; i < sc->sc_nrange; i++) { if (sc->sc_rd[i].rd_slot != slot || start < sc->sc_rd[i].rd_coffset || start > sc->sc_rd[i].rd_cend) continue; /* Disallow cross-range allocations. */ if (end > sc->sc_rd[i].rd_cend) return (NULL); /* We've found the connection to the parent bus */ toffs = start - sc->sc_rd[i].rd_coffset; tend = end - sc->sc_rd[i].rd_coffset; rm = &sc->sc_rd[i].rd_rman; bh = sc->sc_rd[i].rd_bushandle; break; } if (rm == NULL) return (NULL); flags &= ~RF_ACTIVE; rv = rman_reserve_resource(rm, toffs, tend, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); rman_set_bustag(rv, sc->sc_cbustag); rman_set_bushandle(rv, bh + rman_get_start(rv)); if (needactivate) { if (bus_activate_resource(child, type, *rid, rv)) { rman_release_resource(rv); return (NULL); } } if (!passthrough) rle->res = rv; return (rv); default: return (NULL); } } static int sbus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { void *p; int error; if (type == SYS_RES_IRQ) { return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); } if (type == SYS_RES_MEMORY) { /* * Need to memory-map the device space, as some drivers depend * on the virtual address being set and useable. */ error = sparc64_bus_mem_map(rman_get_bustag(r), rman_get_bushandle(r), rman_get_size(r), 0, 0, &p); if (error != 0) return (error); rman_set_virtual(r, p); } return (rman_activate_resource(r)); } static int sbus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { if (type == SYS_RES_IRQ) { return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, type, rid, r)); } if (type == SYS_RES_MEMORY) { sparc64_bus_mem_unmap(rman_get_virtual(r), rman_get_size(r)); rman_set_virtual(r, NULL); } return (rman_deactivate_resource(r)); } static int sbus_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { struct resource_list *rl; struct resource_list_entry *rle; int error, passthrough; passthrough = (device_get_parent(child) != bus); rl = BUS_GET_RESOURCE_LIST(bus, child); if (type == SYS_RES_IRQ) return (resource_list_release(rl, bus, child, type, rid, r)); if ((rman_get_flags(r) & RF_ACTIVE) != 0) { error = bus_deactivate_resource(child, type, rid, r); if (error != 0) return (error); } error = rman_release_resource(r); if (error != 0 || passthrough) return (error); rle = resource_list_find(rl, type, rid); if (rle == NULL) panic("%s: cannot find resource", __func__); if (rle->res == NULL) panic("%s: resource entry is not busy", __func__); rle->res = NULL; return (0); } static bus_dma_tag_t sbus_get_dma_tag(device_t bus, device_t child) { struct sbus_softc *sc; sc = device_get_softc(bus); return (sc->sc_cdmatag); } static const struct ofw_bus_devinfo * sbus_get_devinfo(device_t bus, device_t child) { struct sbus_devinfo *sdi; sdi = device_get_ivars(child); return (&sdi->sdi_obdinfo); } /* * Handle an overtemp situation. * * SPARCs have temperature sensors which generate interrupts * if the machine's temperature exceeds a certain threshold. * This handles the interrupt and powers off the machine. * The same needs to be done to PCI controller drivers. */ static void sbus_overtemp(void *arg) { static int shutdown; /* As the interrupt is cleared we may be called multiple times. */ if (shutdown != 0) return; shutdown++; printf("DANGER: OVER TEMPERATURE detected\nShutting down NOW.\n"); shutdown_nice(RB_POWEROFF); } /* Try to shut down in time in case of power failure. */ static void sbus_pwrfail(void *arg) { static int shutdown; /* As the interrupt is cleared we may be called multiple times. */ if (shutdown != 0) return; shutdown++; printf("Power failure detected\nShutting down NOW.\n"); shutdown_nice(0); } static bus_space_tag_t sbus_alloc_bustag(struct sbus_softc *sc) { bus_space_tag_t sbt; sbt = (bus_space_tag_t)malloc(sizeof(struct bus_space_tag), M_DEVBUF, M_NOWAIT | M_ZERO); if (sbt == NULL) panic("%s: out of memory", __func__); sbt->bst_cookie = sc; sbt->bst_parent = rman_get_bustag(sc->sc_sysio_res); sbt->bst_type = SBUS_BUS_SPACE; return (sbt); } static int sbus_print_res(struct sbus_devinfo *sdi) { int rv; rv = 0; rv += resource_list_print_type(&sdi->sdi_rl, "mem", SYS_RES_MEMORY, "%#lx"); rv += resource_list_print_type(&sdi->sdi_rl, "irq", SYS_RES_IRQ, "%ld"); return (rv); } Index: head/sys/sparc64/sparc64/ofw_bus.c =================================================================== --- head/sys/sparc64/sparc64/ofw_bus.c (revision 186127) +++ head/sys/sparc64/sparc64/ofw_bus.c (nonexistent) @@ -1,198 +0,0 @@ -/*- - * Copyright (C) 1996 Wolfgang Solfrank. - * Copyright (C) 1996 TooLs GmbH. - * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by TooLs GmbH. - * 4. The name of TooLs GmbH may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. - */ -/*- - * Copyright (c) 2001 - 2003 by 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, 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. - * - * from: $NetBSD: ofw_machdep.c,v 1.16 2001/07/20 00:07:14 eeh Exp $ - * - * $FreeBSD$ - */ - -/* - * Open Firmware bus support code that is (hopefully) independent from the - * used hardware. - * Maybe this should go into dev/ofw/; there may however be sparc specific - * bits left. - */ - -#include -#include -#include - -#include - -#include - -static int -ofw_bus_searchprop(phandle_t node, char *propname, void *buf, int buflen) -{ - int rv; - - for (; node != 0; node = OF_parent(node)) { - if ((rv = OF_getprop(node, propname, buf, buflen)) != -1) - return (rv); - } - return (-1); -} - -void -ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) -{ - pcell_t addrc; - int msksz; - - if (OF_getprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) - addrc = 2; - ii->opi_addrc = addrc * sizeof(pcell_t); - - ii->opi_imapsz = OF_getprop_alloc(node, "interrupt-map", 1, - (void **)&ii->opi_imap); - if (ii->opi_imapsz > 0) { - msksz = OF_getprop_alloc(node, "interrupt-map-mask", 1, - (void **)&ii->opi_imapmsk); - /* - * Failure to get the mask is ignored; a full mask is used then. - * Barf on bad mask sizes, however. - */ - if (msksz != -1 && msksz != ii->opi_addrc + intrsz) { - panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " - "property!"); - } - } - -} - -int -ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, - int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, - void *maskbuf) -{ - int rv; - - if (ii->opi_imapsz <= 0) - return (0); - KASSERT(regsz >= ii->opi_addrc, - ("ofw_bus_lookup_imap: register size too small: %d < %d", - regsz, ii->opi_addrc)); - rv = OF_getprop(node, "reg", reg, regsz); - if (rv < regsz) - panic("ofw_bus_lookup_imap: could not get reg property"); - return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, - ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, - mintrsz)); -} - -/* - * Map an interrupt using the firmware reg, interrupt-map and - * interrupt-map-mask properties. - * The interrupt property to be mapped must be of size intrsz, and pointed to - * by intr. The regs property of the node for which the mapping is done must - * be passed as regs. This property is an array of register specifications; - * the size of the address part of such a specification must be passed as - * physsz. Only the first element of the property is used. - * imap and imapsz hold the interrupt mask and it's size. - * imapmsk is a pointer to the interrupt-map-mask property, which must have - * a size of physsz + intrsz; it may be NULL, in which case a full mask is - * assumed. - * maskbuf must point to a buffer of length physsz + intrsz. - * The interrupt is returned in result, which must point to a buffer of length - * rintrsz (which gives the expected size of the mapped interrupt). - * Returns 1 if a mapping was found, 0 otherwise. - */ -int -ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, - void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, - int rintrsz) -{ - phandle_t parent; - u_int8_t *ref = maskbuf; - u_int8_t *uiintr = intr; - u_int8_t *uiregs = regs; - u_int8_t *uiimapmsk = imapmsk; - u_int8_t *mptr; - pcell_t pintrsz; - int i, rsz, tsz; - - rsz = -1; - if (imapmsk != NULL) { - for (i = 0; i < physsz; i++) - ref[i] = uiregs[i] & uiimapmsk[i]; - for (i = 0; i < intrsz; i++) - ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; - } else { - bcopy(regs, ref, physsz); - bcopy(intr, ref + physsz, intrsz); - } - - mptr = imap; - i = imapsz; - tsz = physsz + intrsz + sizeof(phandle_t) + rintrsz; - while (i > 0) { - KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); - bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); - if (ofw_bus_searchprop(parent, "#interrupt-cells", - &pintrsz, sizeof(pintrsz)) == -1) - pintrsz = 1; /* default */ - pintrsz *= sizeof(pcell_t); - if (pintrsz != rintrsz) - panic("ofw_bus_search_intrmap: expected interrupt cell " - "size incorrect: %d != %d", rintrsz, pintrsz); - if (bcmp(ref, mptr, physsz + intrsz) == 0) { - bcopy(mptr + physsz + intrsz + sizeof(parent), - result, rintrsz); - return (1); - } - mptr += tsz; - i -= tsz; - } - return (0); -} Property changes on: head/sys/sparc64/sparc64/ofw_bus.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/sparc64/sparc64/ofw_machdep.c =================================================================== --- head/sys/sparc64/sparc64/ofw_machdep.c (revision 186127) +++ head/sys/sparc64/sparc64/ofw_machdep.c (revision 186128) @@ -1,271 +1,270 @@ /*- * Copyright (c) 2001 by Thomas Moestl . * Copyright (c) 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. */ #include __FBSDID("$FreeBSD$"); /* * Some Open Firmware helper functions that are likely machine dependent. */ #include #include #include #include #include #include #include #include #include -#include #include void OF_getetheraddr(device_t dev, u_char *addr) { char buf[sizeof("true")]; phandle_t node; struct idprom idp; if ((node = OF_finddevice("/options")) > 0 && OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) { buf[sizeof(buf) - 1] = '\0'; if (strcmp(buf, "true") == 0 && (node = ofw_bus_get_node(dev)) > 0 && OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN) == ETHER_ADDR_LEN) return; } node = OF_peer(0); if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1) panic("Could not determine the machine ethernet address"); bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN); } static __inline uint32_t phys_hi_mask_space(const char *bus, uint32_t phys_hi) { uint32_t space; space = phys_hi; if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0) space &= 0x1; else if (strcmp(bus, "pci") == 0) space &= OFW_PCI_PHYS_HI_SPACEMASK; /* The phys.hi cells of the other busses only contain space bits. */ return (space); } /* * Return the physical address and the bus space to use for a node * referenced by its package handle and the index of the register bank * to decode. Intended to be used to together with sparc64_fake_bustag() * by console drivers in early boot only. * Works by mapping the address of the node's bank given in the address * space of its parent upward in the device tree at each bridge along the * path. * Currently only really deals with max. 64-bit addresses, i.e. addresses * consisting of max. 2 phys cells (phys.hi and phys.lo). If we encounter * a 3 phys cells address (as with PCI addresses) we assume phys.hi can * be ignored except for the space bits (generally contained in phys.hi) * and treat phys.mid as phys.hi. */ int OF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr) { char name[32]; uint64_t cend, cstart, end, phys, sz, start; pcell_t addrc, szc, paddrc; phandle_t bus, lbus, pbus; uint32_t banks[10 * 5]; /* 10 PCI banks */ uint32_t cspace, spc; int i, j, nbank; /* * In general the addresses are contained in the "reg" property * of a node. The first address in the "reg" property of a PCI * node however is the address of its configuration registers in * the configuration space of the host bridge. Additional entries * denote the memory and I/O addresses. For relocatable addresses * the "reg" property contains the BAR, for non-relocatable * addresses it contains the absolute PCI address. The PCI-only * "assigned-addresses" property however always contains the * absolute PCI addresses. * The "assigned-addresses" and "reg" properties are arrays of * address structures consisting of #address-cells 32-bit phys * cells and #size-cells 32-bit size cells. If a parent lacks * the "#address-cells" or "#size-cells" property the default * for #address-cells to use is 2 and for #size-cells 1. */ bus = OF_parent(node); if (bus == 0) return (ENXIO); if (OF_getprop(bus, "name", name, sizeof(name)) == -1) return (ENXIO); name[sizeof(name) - 1] = '\0'; if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1) addrc = 2; if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1) szc = 1; if (addrc < 2 || addrc > 3 || szc < 1 || szc > 2) return (ENXIO); if (strcmp(name, "pci") == 0) { if (addrc > 3) return (ENXIO); nbank = OF_getprop(node, "assigned-addresses", &banks, sizeof(banks)); } else { if (addrc > 2) return (ENXIO); nbank = OF_getprop(node, "reg", &banks, sizeof(banks)); } if (nbank == -1) return (ENXIO); nbank /= sizeof(banks[0]) * (addrc + szc); if (bank < 0 || bank > nbank - 1) return (ENXIO); phys = 0; for (i = 0; i < MIN(2, addrc); i++) phys |= (uint64_t)banks[(addrc + szc) * bank + addrc - 2 + i] << 32 * (MIN(2, addrc) - i - 1); sz = 0; for (i = 0; i < szc; i++) sz |= (uint64_t)banks[(addrc + szc) * bank + addrc + i] << 32 * (szc - i - 1); start = phys; end = phys + sz - 1; spc = phys_hi_mask_space(name, banks[(addrc + szc) * bank]); /* * Map upward in the device tree at every bridge we encounter * using their "ranges" properties. * The "ranges" property of a bridge is an array of a structure * consisting of that bridge's #address-cells 32-bit child-phys * cells, its parent bridge #address-cells 32-bit parent-phys * cells and that bridge's #size-cells 32-bit size cells. * If a bridge doesn't have a "ranges" property no mapping is * necessary at that bridge. */ cspace = 0; lbus = bus; while ((pbus = OF_parent(bus)) != 0) { if (OF_getprop(pbus, "#address-cells", &paddrc, sizeof(paddrc)) == -1) paddrc = 2; if (paddrc < 2 || paddrc > 3) return (ENXIO); nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks)); if (nbank == -1) { if (OF_getprop(pbus, "name", name, sizeof(name)) == -1) return (ENXIO); name[sizeof(name) - 1] = '\0'; goto skip; } if (lbus != bus) { if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1) szc = 1; if (szc < 1 || szc > 2) return (ENXIO); } nbank /= sizeof(banks[0]) * (addrc + paddrc + szc); for (i = 0; i < nbank; i++) { cspace = phys_hi_mask_space(name, banks[(addrc + paddrc + szc) * i]); if (cspace != spc) continue; phys = 0; for (j = 0; j < MIN(2, addrc); j++) phys |= (uint64_t)banks[ (addrc + paddrc + szc) * i + addrc - 2 + j] << 32 * (MIN(2, addrc) - j - 1); sz = 0; for (j = 0; j < szc; j++) sz |= (uint64_t)banks[ (addrc + paddrc + szc) * i + addrc + paddrc + j] << 32 * (szc - j - 1); cstart = phys; cend = phys + sz - 1; if (start < cstart || start > cend) continue; if (end < cstart || end > cend) return (ENXIO); phys = 0; for (j = 0; j < MIN(2, paddrc); j++) phys |= (uint64_t)banks[ (addrc + paddrc + szc) * i + addrc + paddrc - 2 + j] << 32 * (MIN(2, paddrc) - j - 1); start += phys - cstart; end += phys - cstart; if (OF_getprop(pbus, "name", name, sizeof(name)) == -1) return (ENXIO); name[sizeof(name) - 1] = '\0'; spc = phys_hi_mask_space(name, banks[(addrc + paddrc + szc) * i + addrc]); break; } if (i == nbank) return (ENXIO); skip: addrc = paddrc; lbus = bus; bus = pbus; } /* Done with mapping. Return the bus space as used by FreeBSD. */ *addr = start; if (OF_parent(lbus) == 0) { *space = NEXUS_BUS_SPACE; return (0); } if (OF_getprop(lbus, "name", name, sizeof(name)) == -1) return (ENXIO); name[sizeof(name) - 1] = '\0'; if (strcmp(name, "central") == 0 || strcmp(name, "upa") == 0) { *space = NEXUS_BUS_SPACE; return (0); } else if (strcmp(name, "pci") == 0) { switch (cspace) { case OFW_PCI_PHYS_HI_SPACE_IO: *space = PCI_IO_BUS_SPACE; return (0); case OFW_PCI_PHYS_HI_SPACE_MEM32: *space = PCI_MEMORY_BUS_SPACE; return (0); } } else if (strcmp(name, "sbus") == 0) { *space = SBUS_BUS_SPACE; return (0); } return (ENXIO); } Index: head/sys/sun4v/include/ofw_bus.h =================================================================== --- head/sys/sun4v/include/ofw_bus.h (revision 186127) +++ head/sys/sun4v/include/ofw_bus.h (nonexistent) @@ -1,53 +0,0 @@ -/*- - * Copyright (c) 2001 - 2003 by 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, 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 _MACHINE_OFW_BUS_H_ -#define _MACHINE_OFW_BUS_H_ - -#define ORIP_NOINT -1 -#define ORIR_NOTFOUND 0xffffffff - -/* - * Other than in Open Firmware calls, the size of a bus cell seems to be - * always the same. - */ -typedef u_int32_t pcell_t; - -struct ofw_bus_iinfo { - u_int8_t *opi_imap; - u_int8_t *opi_imapmsk; - int opi_imapsz; - pcell_t opi_addrc; -}; - -void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int); -int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int, - void *, int, void *, int, void *); -int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *, - void *, void *, int); - -#endif /* !_MACHINE_OFW_BUS_H_ */ Property changes on: head/sys/sun4v/include/ofw_bus.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property