diff options
Diffstat (limited to 'sys/ia64')
-rw-r--r-- | sys/ia64/ia64/nexus.c | 582 | ||||
-rw-r--r-- | sys/ia64/include/nexusvar.h | 55 | ||||
-rw-r--r-- | sys/ia64/include/pci_cfgreg.h | 32 | ||||
-rw-r--r-- | sys/ia64/pci/nexus_pcib.c | 570 | ||||
-rw-r--r-- | sys/ia64/pci/pci_cfgreg.c | 65 |
5 files changed, 1304 insertions, 0 deletions
diff --git a/sys/ia64/ia64/nexus.c b/sys/ia64/ia64/nexus.c new file mode 100644 index 0000000..8ecd181 --- /dev/null +++ b/sys/ia64/ia64/nexus.c @@ -0,0 +1,582 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This code implements a `root nexus' for Intel Architecture + * machines. The function of the root nexus is to serve as an + * attachment point for both processors and buses, and to manage + * resources which are common to all of them. In particular, + * this code implements the core resource managers for interrupt + * requests, DMA requests (which rightfully should be a part of the + * ISA code but it's easier to do it here for now), I/O port addresses, + * and I/O memory address space. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/interrupt.h> + +#include <machine/vmparam.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <machine/pmap.h> + +#include <machine/nexusvar.h> +#include <machine/resource.h> + +#include <isa/isavar.h> +#include <sys/rtprio.h> + +static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device"); +struct nexus_device { + struct resource_list nx_resources; + int nx_pcibus; +}; + +#define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev)) + +static struct rman irq_rman, drq_rman, port_rman, mem_rman; + +static int nexus_probe(device_t); +static int nexus_attach(device_t); +static int nexus_print_resources(struct resource_list *rl, const char *name, int type, + const char *format); +static int nexus_print_all_resources(device_t dev); +static int nexus_print_child(device_t, device_t); +static device_t nexus_add_child(device_t bus, int order, const char *name, + int unit); +static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int nexus_read_ivar(device_t, device_t, int, uintptr_t *); +static int nexus_write_ivar(device_t, device_t, int, uintptr_t); +static int nexus_activate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_release_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, + void (*)(void *), void *, void **); +static int nexus_teardown_intr(device_t, device_t, struct resource *, + void *); +static int nexus_set_resource(device_t, device_t, int, int, u_long, u_long); +static int nexus_get_resource(device_t, device_t, int, int, u_long *, u_long *); +static void nexus_delete_resource(device_t, device_t, int, int); + +static device_method_t nexus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_probe), + DEVMETHOD(device_attach, nexus_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, nexus_print_child), + DEVMETHOD(bus_add_child, nexus_add_child), + DEVMETHOD(bus_read_ivar, nexus_read_ivar), + DEVMETHOD(bus_write_ivar, nexus_write_ivar), + DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), + DEVMETHOD(bus_release_resource, nexus_release_resource), + DEVMETHOD(bus_activate_resource, nexus_activate_resource), + DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), + DEVMETHOD(bus_setup_intr, nexus_setup_intr), + DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), + DEVMETHOD(bus_set_resource, nexus_set_resource), + DEVMETHOD(bus_get_resource, nexus_get_resource), + DEVMETHOD(bus_delete_resource, nexus_delete_resource), + + { 0, 0 } +}; + +static driver_t nexus_driver = { + "nexus", + nexus_methods, + 1, /* no softc */ +}; +static devclass_t nexus_devclass; + +DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); + +static int +nexus_probe(device_t dev) +{ + + device_quiet(dev); /* suppress attach message for neatness */ + + /* + * XXX working notes: + * + * - IRQ resource creation should be moved to the PIC/APIC driver. + * - DRQ resource creation should be moved to the DMAC driver. + * - The above should be sorted to probe earlier than any child busses. + * + * - Leave I/O and memory creation here, as child probes may need them. + * (especially eg. ACPI) + */ + + /* + * IRQ's are on the mainboard on old systems, but on the ISA part + * of PCI->ISA bridges. There would be multiple sets of IRQs on + * multi-ISA-bus systems. PCI interrupts are routed to the ISA + * component, so in a way, PCI can be a partial child of an ISA bus(!). + * APIC interrupts are global though. + * + * XXX We depend on the AT PIC driver correctly claiming IRQ 2 + * to prevent its reuse elsewhere in the !APIC_IO case. + */ + irq_rman.rm_start = 0; + irq_rman.rm_type = RMAN_ARRAY; + irq_rman.rm_descr = "Interrupt request lines"; + irq_rman.rm_end = 15; + if (rman_init(&irq_rman) + || rman_manage_region(&irq_rman, + irq_rman.rm_start, irq_rman.rm_end)) + panic("nexus_probe irq_rman"); + + /* + * ISA DMA on PCI systems is implemented in the ISA part of each + * PCI->ISA bridge and the channels can be duplicated if there are + * multiple bridges. (eg: laptops with docking stations) + */ + drq_rman.rm_start = 0; + drq_rman.rm_end = 7; + drq_rman.rm_type = RMAN_ARRAY; + drq_rman.rm_descr = "DMA request lines"; + /* XXX drq 0 not available on some machines */ + if (rman_init(&drq_rman) + || rman_manage_region(&drq_rman, + drq_rman.rm_start, drq_rman.rm_end)) + panic("nexus_probe drq_rman"); + + /* + * However, IO ports and Memory truely are global at this level, + * as are APIC interrupts (however many IO APICS there turn out + * to be on large systems..) + */ + port_rman.rm_start = 0; + port_rman.rm_end = 0xffff; + port_rman.rm_type = RMAN_ARRAY; + port_rman.rm_descr = "I/O ports"; + if (rman_init(&port_rman) + || rman_manage_region(&port_rman, 0, 0xffff)) + panic("nexus_probe port_rman"); + + mem_rman.rm_start = 0; + mem_rman.rm_end = ~0u; + mem_rman.rm_type = RMAN_ARRAY; + mem_rman.rm_descr = "I/O memory addresses"; + if (rman_init(&mem_rman) + || rman_manage_region(&mem_rman, 0, ~0)) + panic("nexus_probe mem_rman"); + + return bus_generic_probe(dev); +} + +static int +nexus_attach(device_t dev) +{ + bus_generic_attach(dev); + return 0; +} + +static int +nexus_print_resources(struct resource_list *rl, const char *name, int type, + const char *format) +{ + struct resource_list_entry *rle; + int printed, retval; + + printed = 0; + retval = 0; + /* Yes, this is kinda cheating */ + SLIST_FOREACH(rle, rl, link) { + if (rle->type == type) { + if (printed == 0) + retval += printf(" %s ", name); + else if (printed > 0) + retval += printf(","); + printed++; + retval += printf(format, rle->start); + if (rle->count > 1) { + retval += printf("-"); + retval += printf(format, rle->start + + rle->count - 1); + } + } + } + return retval; +} + +static int +nexus_print_all_resources(device_t dev) +{ + struct nexus_device *ndev = DEVTONX(dev); + struct resource_list *rl = &ndev->nx_resources; + int retval = 0; + + if (SLIST_FIRST(rl) || ndev->nx_pcibus != -1) + retval += printf(" at"); + + retval += nexus_print_resources(rl, "port", SYS_RES_IOPORT, "%#lx"); + retval += nexus_print_resources(rl, "iomem", SYS_RES_MEMORY, "%#lx"); + retval += nexus_print_resources(rl, "irq", SYS_RES_IRQ, "%ld"); + + return retval; +} + +static int +nexus_print_child(device_t bus, device_t child) +{ + struct nexus_device *ndev = DEVTONX(child); + int retval = 0; + + retval += bus_print_child_header(bus, child); + retval += nexus_print_all_resources(child); + if (ndev->nx_pcibus != -1) + retval += printf(" pcibus %d", ndev->nx_pcibus); + retval += printf(" on motherboard\n"); /* XXX "motherboard", ick */ + + return (retval); +} + +static device_t +nexus_add_child(device_t bus, int order, const char *name, int unit) +{ + device_t child; + struct nexus_device *ndev; + + ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO); + if (!ndev) + return(0); + resource_list_init(&ndev->nx_resources); + ndev->nx_pcibus = -1; + + child = device_add_child_ordered(bus, order, name, unit); + + /* should we free this in nexus_child_detached? */ + device_set_ivars(child, ndev); + + return(child); +} + +static int +nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct nexus_device *ndev = DEVTONX(child); + + switch (which) { + case NEXUS_IVAR_PCIBUS: + *result = ndev->nx_pcibus; + break; + default: + return ENOENT; + } + return 0; +} + + +static int +nexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct nexus_device *ndev = DEVTONX(child); + + switch (which) { + case NEXUS_IVAR_PCIBUS: + ndev->nx_pcibus = value; + break; + default: + return ENOENT; + } + return 0; +} + + +/* + * Allocate a resource on behalf of child. NB: child is usually going to be a + * child of one of our descendants, not a direct child of nexus0. + * (Exceptions include npx.) + */ +static struct resource * +nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct nexus_device *ndev = DEVTONX(child); + struct resource *rv; + struct resource_list_entry *rle; + struct rman *rm; + int needactivate = flags & RF_ACTIVE; + + /* + * If this is an allocation of the "default" range for a given RID, and + * we know what the resources for this device are (ie. they aren't maintained + * by a child bus), then work out the start/end values. + */ + if ((start == 0UL) && (end == ~0UL) && (count == 1)) { + if (ndev == NULL) + return(NULL); + rle = resource_list_find(&ndev->nx_resources, type, *rid); + if (rle == NULL) + return(NULL); + start = rle->start; + end = rle->end; + count = rle->count; + } + + flags &= ~RF_ACTIVE; + + switch (type) { + case SYS_RES_IRQ: + rm = &irq_rman; + break; + + case SYS_RES_DRQ: + rm = &drq_rman; + break; + + case SYS_RES_IOPORT: + rm = &port_rman; + break; + + case SYS_RES_MEMORY: + rm = &mem_rman; + break; + + default: + return 0; + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) + return 0; + + if (type == SYS_RES_MEMORY) { + rman_set_bustag(rv, IA64_BUS_SPACE_MEM); + } else if (type == SYS_RES_IOPORT) { + rman_set_bustag(rv, IA64_BUS_SPACE_IO); + /* IBM-PC: the type of bus_space_handle_t is u_int */ + rman_set_bushandle(rv, rv->r_start); + } + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv)) { + rman_release_resource(rv); + return 0; + } + } + + return rv; +} + +static int +nexus_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + /* + * If this is a memory resource, map it into the kernel. + */ + if (rman_get_bustag(r) == IA64_BUS_SPACE_MEM) { + vm_offset_t paddr = rman_get_start(r); + vm_offset_t psize = rman_get_size(r); + caddr_t vaddr = 0; + + vaddr = pmap_mapdev(paddr, psize); + rman_set_virtual(r, vaddr); + rman_set_bushandle(r, (bus_space_handle_t) paddr); + } + return (rman_activate_resource(r)); +} + +static int +nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + + return (rman_deactivate_resource(r)); +} + +static int +nexus_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + if (rman_get_flags(r) & RF_ACTIVE) { + int error = bus_deactivate_resource(child, type, rid, r); + if (error) + return error; + } + return (rman_release_resource(r)); +} + +/* + * Currently this uses the really grody interface from kern/kern_intr.c + * (which really doesn't belong in kern/anything.c). Eventually, all of + * the code in kern_intr.c and machdep_intr.c should get moved here, since + * this is going to be the official interface. + */ +static int +nexus_setup_intr(device_t bus, device_t child, struct resource *irq, + int flags, void (*ihand)(void *), void *arg, void **cookiep) +{ + driver_t *driver; + int error; + + /* somebody tried to setup an irq that failed to allocate! */ + if (irq == NULL) + panic("nexus_setup_intr: NULL irq resource!"); + + *cookiep = 0; + if ((irq->r_flags & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; + + driver = device_get_driver(child); + + /* + * We depend here on rman_activate_resource() being idempotent. + */ + error = rman_activate_resource(irq); + if (error) + return (error); + +#if 0 + error = inthand_add(device_get_nameunit(child), irq->r_start, + ihand, arg, flags, cookiep); +#endif + + return (error); +} + +static int +nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) +{ +#if 0 + return (inthand_remove(ih)); +#else + return 0; +#endif +} + +static int +nexus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) +{ + struct nexus_device *ndev = DEVTONX(child); + struct resource_list *rl = &ndev->nx_resources; + + /* XXX this should return a success/failure indicator */ + resource_list_add(rl, type, rid, start, start + count - 1, count); + return(0); +} + +static int +nexus_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) +{ + struct nexus_device *ndev = DEVTONX(child); + struct resource_list *rl = &ndev->nx_resources; + struct resource_list_entry *rle; + + rle = resource_list_find(rl, type, rid); + device_printf(child, "type %d rid %d startp %p countp %p - got %p\n", + type, rid, startp, countp, rle); + if (!rle) + return(ENOENT); + if (startp) + *startp = rle->start; + if (countp) + *countp = rle->count; + return(0); +} + +static void +nexus_delete_resource(device_t dev, device_t child, int type, int rid) +{ + struct nexus_device *ndev = DEVTONX(child); + struct resource_list *rl = &ndev->nx_resources; + + resource_list_delete(rl, type, rid); +} + +#if 0 + +/* + * Placeholder which claims PnP 'devices' which describe system + * resources. + */ +static struct isa_pnp_id sysresource_ids[] = { + { 0x010cd041 /* PNP0c01 */, "System Memory" }, + { 0x020cd041 /* PNP0c02 */, "System Resource" }, + { 0 } +}; + +static int +sysresource_probe(device_t dev) +{ + int result; + + if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, sysresource_ids)) <= 0) { + device_quiet(dev); + } + return(result); +} + +static int +sysresource_attach(device_t dev) +{ + return(0); +} + +static device_method_t sysresource_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sysresource_probe), + DEVMETHOD(device_attach, sysresource_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), + { 0, 0 } +}; + +static driver_t sysresource_driver = { + "sysresource", + sysresource_methods, + 1, /* no softc */ +}; + +static devclass_t sysresource_devclass; + +DRIVER_MODULE(sysresource, isa, sysresource_driver, sysresource_devclass, 0, 0); + +#endif diff --git a/sys/ia64/include/nexusvar.h b/sys/ia64/include/nexusvar.h new file mode 100644 index 0000000..c9dcf1c --- /dev/null +++ b/sys/ia64/include/nexusvar.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org> + * 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$ + */ + +#ifndef _MACHINE_NEXUSVAR_H_ +#define _MACHINE_NEXUSVAR_H_ 1 + +enum nexus_device_ivars { + NEXUS_IVAR_PCIBUS +}; + +#define NEXUS_ACCESSOR(A, B, T) \ + \ +static __inline T nexus_get_ ## A(device_t dev) \ +{ \ + uintptr_t v; \ + BUS_READ_IVAR(device_get_parent(dev), dev, NEXUS_IVAR_ ## B, &v); \ + return (T) v; \ +} \ + \ +static __inline void nexus_set_ ## A(device_t dev, T t) \ +{ \ + uintptr_t v = (uintptr_t) t; \ + BUS_WRITE_IVAR(device_get_parent(dev), dev, NEXUS_IVAR_ ## B, v); \ +} + +NEXUS_ACCESSOR(pcibus, PCIBUS, u_int32_t) + +#undef NEXUS_ACCESSOR + +#endif /* !_MACHINE_NEXUSVAR_H_ */ diff --git a/sys/ia64/include/pci_cfgreg.h b/sys/ia64/include/pci_cfgreg.h new file mode 100644 index 0000000..46d837b --- /dev/null +++ b/sys/ia64/include/pci_cfgreg.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1997, Stefan Esser <se@freebsd.org> + * 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. + * + * $FreeBSD$ + * + */ + +extern int pci_cfgregopen(void); +extern u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes); +extern void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes); diff --git a/sys/ia64/pci/nexus_pcib.c b/sys/ia64/pci/nexus_pcib.c new file mode 100644 index 0000000..fa47654 --- /dev/null +++ b/sys/ia64/pci/nexus_pcib.c @@ -0,0 +1,570 @@ +/* + * Copyright (c) 1997, Stefan Esser <se@freebsd.org> + * 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. + * + * $FreeBSD$ + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <isa/isavar.h> +#include <machine/nexusvar.h> +#include <machine/sal.h> + +#include "pcib_if.h" + +static int +nexus_pcib_maxslots(device_t dev) +{ + return 31; +} + +/* read configuration space register */ + +#define SAL_PCI_ADDRESS(bus, slot, func, reg) \ + (((bus) << 23) | ((slot) << 11) | ((func) << 8) | (reg)) + +static u_int32_t +nexus_pcib_read_config(device_t dev, int bus, int slot, int func, + int reg, int bytes) +{ + struct ia64_sal_result res; + + res = ia64_sal_entry(SAL_PCI_CONFIG_READ, + SAL_PCI_ADDRESS(bus, slot, func, reg), + bytes, 0, 0, 0, 0, 0); + if (res.sal_status < 0) + return (~0); + else + return (res.sal_result[0]); +} + +/* write configuration space register */ + +static void +nexus_pcib_write_config(device_t dev, int bus, int slot, int func, + int reg, u_int32_t data, int bytes) +{ + struct ia64_sal_result res; + + res = ia64_sal_entry(SAL_PCI_CONFIG_WRITE, + SAL_PCI_ADDRESS(bus, slot, func, reg), + bytes, data, 0, 0, 0, 0); +} + +/* route interrupt */ + +static int +nexus_pcib_route_interrupt(device_t pcib, device_t dev, int pin) +{ + return (0); +} + +static devclass_t pcib_devclass; + +static const char * +nexus_pcib_is_host_bridge(int bus, int slot, int func, + u_int32_t id, u_int8_t class, u_int8_t subclass, + u_int8_t *busnum) +{ + const char *s = NULL; + static u_int8_t pxb[4]; /* hack for 450nx */ + + *busnum = 0; + + switch (id) { + case 0x12258086: + s = "Intel 824?? host to PCI bridge"; + /* XXX This is a guess */ + /* *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x41, 1); */ + *busnum = bus; + break; + case 0x71208086: + s = "Intel 82810 (i810 GMCH) Host To Hub bridge"; + break; + case 0x71228086: + s = "Intel 82810-DC100 (i810-DC100 GMCH) Host To Hub bridge"; + break; + case 0x71248086: + s = "Intel 82810E (i810E GMCH) Host To Hub bridge"; + break; + case 0x11308086: + s = "Intel 82815 (i815 GMCH) Host To Hub bridge"; + break; + case 0x71808086: + s = "Intel 82443LX (440 LX) host to PCI bridge"; + break; + case 0x71908086: + s = "Intel 82443BX (440 BX) host to PCI bridge"; + break; + case 0x71928086: + s = "Intel 82443BX host to PCI bridge (AGP disabled)"; + break; + case 0x71948086: + s = "Intel 82443MX host to PCI bridge"; + break; + case 0x71a08086: + s = "Intel 82443GX host to PCI bridge"; + break; + case 0x71a18086: + s = "Intel 82443GX host to AGP bridge"; + break; + case 0x71a28086: + s = "Intel 82443GX host to PCI bridge (AGP disabled)"; + break; + case 0x84c48086: + s = "Intel 82454KX/GX (Orion) host to PCI bridge"; + *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x4a, 1); + break; + case 0x84ca8086: + /* + * For the 450nx chipset, there is a whole bundle of + * things pretending to be host bridges. The MIOC will + * be seen first and isn't really a pci bridge (the + * actual busses are attached to the PXB's). We need to + * read the registers of the MIOC to figure out the + * bus numbers for the PXB channels. + * + * Since the MIOC doesn't have a pci bus attached, we + * pretend it wasn't there. + */ + pxb[0] = nexus_pcib_read_config(0, bus, slot, func, + 0xd0, 1); /* BUSNO[0] */ + pxb[1] = nexus_pcib_read_config(0, bus, slot, func, + 0xd1, 1) + 1; /* SUBA[0]+1 */ + pxb[2] = nexus_pcib_read_config(0, bus, slot, func, + 0xd3, 1); /* BUSNO[1] */ + pxb[3] = nexus_pcib_read_config(0, bus, slot, func, + 0xd4, 1) + 1; /* SUBA[1]+1 */ + return NULL; + case 0x84cb8086: + switch (slot) { + case 0x12: + s = "Intel 82454NX PXB#0, Bus#A"; + *busnum = pxb[0]; + break; + case 0x13: + s = "Intel 82454NX PXB#0, Bus#B"; + *busnum = pxb[1]; + break; + case 0x14: + s = "Intel 82454NX PXB#1, Bus#A"; + *busnum = pxb[2]; + break; + case 0x15: + s = "Intel 82454NX PXB#1, Bus#B"; + *busnum = pxb[3]; + break; + } + break; + + /* AMD -- vendor 0x1022 */ + case 0x70061022: + s = "AMD-751 host to PCI bridge"; + break; + + /* SiS -- vendor 0x1039 */ + case 0x04961039: + s = "SiS 85c496"; + break; + case 0x04061039: + s = "SiS 85c501"; + break; + case 0x06011039: + s = "SiS 85c601"; + break; + case 0x55911039: + s = "SiS 5591 host to PCI bridge"; + break; + case 0x00011039: + s = "SiS 5591 host to AGP bridge"; + break; + + /* VLSI -- vendor 0x1004 */ + case 0x00051004: + s = "VLSI 82C592 Host to PCI bridge"; + break; + + /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ + /* totally. Please let me know if anything wrong. -F */ + /* XXX need info on the MVP3 -- any takers? */ + case 0x05981106: + s = "VIA 82C598MVP (Apollo MVP3) host bridge"; + break; + + /* AcerLabs -- vendor 0x10b9 */ + /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ + /* id is '10b9" but the register always shows "10b9". -Foxfair */ + case 0x154110b9: + s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; + break; + + /* OPTi -- vendor 0x1045 */ + case 0xc7011045: + s = "OPTi 82C700 host to PCI bridge"; + break; + case 0xc8221045: + s = "OPTi 82C822 host to PCI Bridge"; + break; + + /* ServerWorks -- vendor 0x1166 */ + case 0x00051166: + s = "ServerWorks NB6536 2.0HE host to PCI bridge"; + *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); + break; + + case 0x00061166: + /* FALLTHROUGH */ + case 0x00081166: + s = "ServerWorks host to PCI bridge"; + *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); + break; + + case 0x00091166: + s = "ServerWorks NB6635 3.0LE host to PCI bridge"; + *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); + break; + + /* Integrated Micro Solutions -- vendor 0x10e0 */ + case 0x884910e0: + s = "Integrated Micro Solutions VL Bridge"; + break; + + default: + if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) + s = "Host to PCI bridge"; + break; + } + + return s; +} + +/* + * Scan the first pci bus for host-pci bridges and add pcib instances + * to the nexus for each bridge. + */ +static void +nexus_pcib_identify(driver_t *driver, device_t parent) +{ + int bus, slot, func; + u_int8_t hdrtype; + int found = 0; + int pcifunchigh; + int found824xx = 0; + int found_orion = 0; + device_t child; + devclass_t pci_devclass; + + /* + * Check to see if we haven't already had a PCI bus added + * via some other means. If we have, bail since otherwise + * we're going to end up duplicating it. + */ + if ((pci_devclass = devclass_find("pci")) && + devclass_get_device(pci_devclass, 0)) + return; + + + bus = 0; + retry: + for (slot = 0; slot <= PCI_SLOTMAX; slot++) { + func = 0; + hdrtype = nexus_pcib_read_config(0, bus, slot, func, + PCIR_HEADERTYPE, 1); + if ((hdrtype & PCIM_MFDEV) && + (!found_orion || hdrtype != 0xff)) + pcifunchigh = 7; + else + pcifunchigh = 0; + for (func = 0; func <= pcifunchigh; func++) { + /* + * Read the IDs and class from the device. + */ + u_int32_t id; + u_int8_t class, subclass, busnum; + const char *s; + device_t *devs; + int ndevs, i; + + id = nexus_pcib_read_config(0, bus, slot, func, + PCIR_DEVVENDOR, 4); + if (id == -1) + continue; + class = nexus_pcib_read_config(0, bus, slot, func, + PCIR_CLASS, 1); + subclass = nexus_pcib_read_config(0, bus, slot, func, + PCIR_SUBCLASS, 1); + + s = nexus_pcib_is_host_bridge(bus, slot, func, + id, class, subclass, + &busnum); + if (s == NULL) + continue; + + /* + * Check to see if the physical bus has already + * been seen. Eg: hybrid 32 and 64 bit host + * bridges to the same logical bus. + */ + if (device_get_children(parent, &devs, &ndevs) == 0) { + for (i = 0; s != NULL && i < ndevs; i++) { + if (strcmp(device_get_name(devs[i]), + "pcib") != 0) + continue; + if (nexus_get_pcibus(devs[i]) == busnum) + s = NULL; + } + free(devs, M_TEMP); + } + + if (s == NULL) + continue; + /* + * Add at priority 100 to make sure we + * go after any motherboard resources + */ + child = BUS_ADD_CHILD(parent, 100, + "pcib", busnum); + device_set_desc(child, s); + nexus_set_pcibus(child, busnum); + + found = 1; + if (id == 0x12258086) + found824xx = 1; + if (id == 0x84c48086) + found_orion = 1; + } + } + if (found824xx && bus == 0) { + bus++; + goto retry; + } + + /* + * Make sure we add at least one bridge since some old + * hardware doesn't actually have a host-pci bridge device. + */ + if (!found) { + if (bootverbose) + printf( + "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); + child = BUS_ADD_CHILD(parent, 100, "pcib", 0); + nexus_set_pcibus(child, 0); + } +} + +static int +nexus_pcib_probe(device_t dev) +{ + devclass_t pci_devclass; + + /* + * Check to see if we haven't already had a PCI bus added + * via some other means. If we have, bail since otherwise + * we're going to end up duplicating it. + */ + if ((pci_devclass = devclass_find("pci")) && + devclass_get_device(pci_devclass, device_get_unit(dev))) + return ENXIO; + + return 0; +} + +static int +nexus_pcib_attach(device_t dev) +{ + device_t child; + + child = device_add_child(dev, "pci", device_get_unit(dev)); + + return bus_generic_attach(dev); +} + +static int +nexus_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + + switch (which) { + case PCIB_IVAR_BUS: + *result = nexus_get_pcibus(dev); + return 0; + } + return ENOENT; +} + +static int +nexus_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + + switch (which) { + case PCIB_IVAR_BUS: + nexus_set_pcibus(dev, value); + return 0; + } + return ENOENT; +} + + +static device_method_t nexus_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, nexus_pcib_identify), + DEVMETHOD(device_probe, nexus_pcib_probe), + DEVMETHOD(device_attach, nexus_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, nexus_pcib_read_ivar), + DEVMETHOD(bus_write_ivar, nexus_pcib_write_ivar), + DEVMETHOD(bus_alloc_resource, bus_generic_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, nexus_pcib_maxslots), + DEVMETHOD(pcib_read_config, nexus_pcib_read_config), + DEVMETHOD(pcib_write_config, nexus_pcib_write_config), + DEVMETHOD(pcib_route_interrupt, nexus_pcib_route_interrupt), + + { 0, 0 } +}; + +static driver_t nexus_pcib_driver = { + "pcib", + nexus_pcib_methods, + 1, +}; + +DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); + + +/* + * Provide a device to "eat" the host->pci bridges that we dug up above + * and stop them showing up twice on the probes. This also stops them + * showing up as 'none' in pciconf -l. + */ +static int +pci_hostb_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 +pci_hostb_attach(device_t dev) +{ + + return 0; +} + +static device_method_t pci_hostb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_hostb_probe), + DEVMETHOD(device_attach, pci_hostb_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + { 0, 0 } +}; +static driver_t pci_hostb_driver = { + "hostb", + pci_hostb_methods, + 1, +}; +static devclass_t pci_hostb_devclass; + +DRIVER_MODULE(hostb, pci, pci_hostb_driver, pci_hostb_devclass, 0, 0); + + +/* + * Install placeholder to claim the resources owned by the + * PCI bus interface. This could be used to extract the + * config space registers in the extreme case where the PnP + * ID is available and the PCI BIOS isn't, but for now we just + * eat the PnP ID and do nothing else. + * + * XXX we should silence this probe, as it will generally confuse + * people. + */ +static struct isa_pnp_id pcibus_pnp_ids[] = { + { 0x030ad041 /* PNP030A */, "PCI Bus" }, + { 0 } +}; + +static int +pcibus_pnp_probe(device_t dev) +{ + int result; + + if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0) + device_quiet(dev); + return(result); +} + +static int +pcibus_pnp_attach(device_t dev) +{ + return(0); +} + +static device_method_t pcibus_pnp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pcibus_pnp_probe), + DEVMETHOD(device_attach, pcibus_pnp_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), + { 0, 0 } +}; + +static driver_t pcibus_pnp_driver = { + "pcibus_pnp", + pcibus_pnp_methods, + 1, /* no softc */ +}; + +static devclass_t pcibus_pnp_devclass; + +DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0); diff --git a/sys/ia64/pci/pci_cfgreg.c b/sys/ia64/pci/pci_cfgreg.c new file mode 100644 index 0000000..649b7a9 --- /dev/null +++ b/sys/ia64/pci/pci_cfgreg.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2001 Doug Rabson + * 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 <sys/param.h> +#include <machine/pci_cfgreg.h> +#include <machine/sal.h> + +#define SAL_PCI_ADDRESS(bus, slot, func, reg) \ + (((bus) << 23) | ((slot) << 11) | ((func) << 8) | (reg)) + +int +pci_cfgregopen(void) +{ + return 1; +} + +u_int32_t +pci_cfgregread(int bus, int slot, int func, int reg, int bytes) +{ + struct ia64_sal_result res; + + res = ia64_sal_entry(SAL_PCI_CONFIG_READ, + SAL_PCI_ADDRESS(bus, slot, func, reg), + bytes, 0, 0, 0, 0, 0); + if (res.sal_status < 0) + return (~0); + else + return (res.sal_result[0]); + + +} +void +pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) +{ + struct ia64_sal_result res; + + res = ia64_sal_entry(SAL_PCI_CONFIG_WRITE, + SAL_PCI_ADDRESS(bus, slot, func, reg), + bytes, data, 0, 0, 0, 0); +} |