diff options
Diffstat (limited to 'sys/pci/pci.c')
-rw-r--r-- | sys/pci/pci.c | 2181 |
1 files changed, 365 insertions, 1816 deletions
diff --git a/sys/pci/pci.c b/sys/pci/pci.c index 4456fe6..b6f056e 100644 --- a/sys/pci/pci.c +++ b/sys/pci/pci.c @@ -1,1893 +1,459 @@ -/************************************************************************** -** -** $Id: pci.c,v 1.70 1997/04/26 11:46:18 peter Exp $ -** -** General subroutines for the PCI bus. -** pci_configure () -** -** FreeBSD -** -**------------------------------------------------------------------------- -** -** Copyright (c) 1994 Wolfgang Stanglmeier. 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. -** -*************************************************************************** -*/ +/* + * 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. + * + * $Id$ + * + */ #include "pci.h" #if NPCI > 0 -/*======================================================== -** -** #includes and declarations -** -**======================================================== -*/ - -#include "opt_smp.h" +#include <stddef.h> +#include <sys/types.h> #include <sys/param.h> +#include <sys/time.h> #include <sys/systm.h> #include <sys/malloc.h> -#include <sys/errno.h> -#include <sys/kernel.h> -#include <sys/proc.h> /* declaration of wakeup(), used by vm.h */ +#include <sys/fcntl.h> #include <sys/conf.h> +#include <sys/kernel.h> #ifdef DEVFS #include <sys/devfsext.h> #endif /* DEVFS */ -#include <sys/fcntl.h> #include <vm/vm.h> -#include <vm/vm_param.h> #include <vm/pmap.h> - -#include <i386/isa/isa_device.h> /* XXX inthand2_t */ - -#include <pci/pcivar.h> #include <pci/pcireg.h> -#include <pci/pcibus.h> +#include <pci/pcivar.h> #include <pci/pci_ioctl.h> -/*======================================================== -** -** Structs and Functions -** -**======================================================== -*/ - -struct pcicb { - struct pcicb *pcicb_next; - struct pcicb *pcicb_up; - struct pcicb *pcicb_down; - pcici_t pcicb_bridge; - - u_char pcicb_bus; - u_char pcicb_subordinate; - u_int pcicb_mfrom; - u_int pcicb_mupto; - u_int pcicb_mamount; - u_short pcicb_pfrom; - u_short pcicb_pupto; - u_short pcicb_pamount; - u_char pcicb_bfrom; - u_char pcicb_bupto; - - u_long pcicb_iobase; - u_long pcicb_iolimit; - u_long pcicb_membase; - u_long pcicb_memlimit; - u_long pcicb_p_membase; - u_long pcicb_p_memlimit; -}; - -struct pci_lkm { - struct pci_device *dvp; - struct pci_lkm *next; -}; - -static void -not_supported (pcici_t tag, u_long type); - -static void -pci_bus_config (void); - -static void -pci_rescan (void); - -static void pci_attach (int bus, int dev, int func, - struct pci_device *dvp, const char *name); - -static int -pci_bridge_config (void); +/* return highest PCI bus number known to be used, or -1 if none */ static int -pci_mfdev (int bus, int device); - -static void pci_remember (int bus, int dev, int func, struct pci_device *dvp); - -/*======================================================== -** -** Variables -** -**======================================================== -*/ - -/* -** log2 of safe burst len (in words) -*/ - -unsigned pci_max_burst_len = 3; /* 2=16Byte, 3=32Byte, 4=64Byte, ... */ -unsigned pci_mechanism = 0; -unsigned pci_maxdevice = 0; -unsigned pciroots = 0; /* XXX pcisupport.c increments this - * for the Orion host to PCI bridge - * UGLY hack ... :( Will be changed :) - */ -/*-------------------------------------------------------- -** -** Local variables. -** -**-------------------------------------------------------- -*/ - -static struct pcibus *pcibus; - -static int pci_conf_count; -static int pci_info_done; -static int pcibusmax; -static struct pcicb *pcicb; - -static struct pci_conf *pci_dev_list; -static unsigned pci_dev_list_count; -static unsigned pci_dev_list_size; - -static struct pci_lkm *pci_lkm_head; - -/*----------------------------------------------------------------- -** -** The following functions are provided for the device driver -** to read/write the configuration space. -** -** pci_conf_read(): -** Read a long word from the pci configuration space. -** Requires a tag (from pcitag) and the register -** number (should be a long word alligned one). -** -** pci_conf_write(): -** Writes a long word to the pci configuration space. -** Requires a tag (from pcitag), the register number -** (should be a long word alligned one), and a value. -** -**----------------------------------------------------------------- -*/ - -u_long -pci_conf_read (pcici_t tag, u_long reg) -{ - return (pcibus->pb_read (tag, reg)); -} - -void -pci_conf_write (pcici_t tag, u_long reg, u_long data) -{ - pcibus->pb_write (tag, reg, data); -} - -/*======================================================== -** -** Subroutines for configuration. -** -**======================================================== -*/ - -static void -pci_register_io (struct pcicb * cb, u_int base, u_int limit) +pci_bushigh(void) { -#ifdef PCI_BRIDGE_DEBUG - if (bootverbose) - printf ("register_io: bus=%d base=%x limit=%x\n", - cb->pcicb_bus, base, limit); -#endif - - if (!cb->pcicb_pfrom || base < cb->pcicb_pfrom) - cb->pcicb_pfrom = base; - if (limit > cb->pcicb_pupto) - cb->pcicb_pupto = limit; - - /* - ** XXX should set bridge io mapping here - ** but it can be mapped in 4k blocks only, - ** leading to conflicts with isa/eisa .. - */ + if (pci_cfgopen() == 0) + return (-1); + return (0); } -static void -pci_register_memory (struct pcicb * cb, u_int base, u_int limit) -{ -#ifdef PCI_BRIDGE_DEBUG - if (bootverbose) - printf ("register_mem: bus=%d base=%x limit=%x\n", - cb->pcicb_bus, base, limit); -#endif +/* return base address of memory or port map */ - if (!cb->pcicb_mfrom || base < cb->pcicb_mfrom) - cb->pcicb_mfrom = base; - if (limit > cb->pcicb_mupto) - cb->pcicb_mupto = limit; - /* - ** set the bridges mapping - ** - ** XXX should handle the 1Mb granularity. - */ - if (cb->pcicb_bridge.tag) { - pci_conf_write(cb->pcicb_bridge, - PCI_PCI_BRIDGE_MEM_REG, - (cb->pcicb_memlimit & 0xffff0000) | - (cb->pcicb_membase >> 16)); - if (bootverbose) - printf ("\t[pci%d uses memory from %x to %x]\n", - cb->pcicb_bus, - (unsigned) cb->pcicb_membase, - (unsigned) cb->pcicb_memlimit); - } -} - -/* -** XXX This function is neither complete nor tested. -** It's only used if the bios hasn't done it's job -** of mapping the pci devices in the physical memory. -*/ - -static u_int -pci_memalloc (struct pcicb * cb, u_int addr, u_int size) +static int +pci_mapbase(unsigned mapreg) { - u_int result = 0, limit=0, newbase=0; -#ifdef PCI_BRIDGE_DEBUG - if (bootverbose) - printf ("memalloc: bus=%d addr=%x size=%x ..\n", - cb->pcicb_bus, addr, size); -#endif - - if (!cb) goto done; - - if (!cb->pcicb_membase) { - printf ("memalloc: bus%d: membase not set.\n", - cb->pcicb_bus); - goto done; - } - - /* - ** get upper allocation limit - */ - limit = cb->pcicb_memlimit; - if (cb->pcicb_mfrom && cb->pcicb_mfrom <= limit) - limit = cb->pcicb_mfrom-1; - - /* - ** address fixed, and impossible to allocate ? - */ - if (addr && addr+size-1 > limit) - goto done; - - /* - ** get possible address - */ - - result = addr; - if (!result) result = ((limit + 1) / size - 1) * size; - - /* - ** if not local available, request from parent. - */ - - if (result < cb->pcicb_membase) { - newbase = pci_memalloc (cb->pcicb_up, result, size); - if (newbase) cb->pcicb_membase = result; - else result=0; - } -done: - if (result) - pci_register_memory (cb, result, result+size-1); - -#ifdef PCI_BRIDGE_DEBUG - printf ("memalloc: bus=%d addr=%x size=%x --> %x (limit=%x).\n", - cb->pcicb_bus, addr, size, result, limit); -#endif - - return (result); + int mask = 0x03; + if ((mapreg & 0x01) == 0) + mask = 0x0f; + return (mapreg & ~mask); } -/*======================================================== -** -** pci_bridge_config() -** -** Configuration of a pci bridge. -** -**======================================================== -*/ +/* return map type of memory or port map */ static int -pci_bridge_config (void) -{ - pcici_t tag; - struct pcicb* parent; - - tag = pcicb->pcicb_bridge; - if (tag.tag) { - - if (!pcicb->pcicb_bus) { - u_int data; - /* - ** Get the lowest available bus number. - */ - pcicb->pcicb_bus = ++pcibusmax; - - /* - ** and configure the bridge - */ - data = pci_conf_read (tag, PCI_PCI_BRIDGE_BUS_REG); - data = PCI_PRIMARY_BUS_INSERT(data, pcicb->pcicb_up->pcicb_bus); - data = PCI_SECONDARY_BUS_INSERT(data, pcicb->pcicb_bus); - data = PCI_SUBORDINATE_BUS_INSERT(data, pcicb->pcicb_bus); - pci_conf_write (tag, PCI_PCI_BRIDGE_BUS_REG, data); - - /* - ** Propagate the new upper bus number limit. - */ - for (parent = pcicb->pcicb_up; parent != NULL; - parent = parent->pcicb_up) - { - if (parent->pcicb_subordinate >= pcicb->pcicb_bus) - continue; - parent->pcicb_subordinate = pcicb->pcicb_bus; - if (!parent->pcicb_bridge.tag) - continue; - data = pci_conf_read - (parent->pcicb_bridge, PCI_PCI_BRIDGE_BUS_REG); - data = PCI_SUBORDINATE_BUS_INSERT - (data, pcicb->pcicb_bus); - pci_conf_write (parent->pcicb_bridge, - PCI_PCI_BRIDGE_BUS_REG, data); - } - } - - if (!pcicb->pcicb_membase) { - u_int size = 0x100000; - pcicb->pcicb_membase = pci_memalloc (pcicb->pcicb_up, 0, size); - if (pcicb->pcicb_membase) - pcicb->pcicb_memlimit = pcicb->pcicb_membase+size-1; - } - } - return pcicb->pcicb_bus; -} - -/*======================================================== -** -** pci_attach() -** -** Attach one device -** -**======================================================== -*/ - -static void pci_attach (int bus, int dev, int func, - struct pci_device *dvp, const char *name) -{ - u_long data; - int unit; - u_char reg; - u_char pciint; - int irq; -#if defined(APIC_IO) - u_char airq = 0xff; - u_char rirq = 0xff; -#endif /* APIC_IO */ - pcici_t tag = pcibus->pb_tag (bus, dev, func); - - /* - ** Get and increment the unit. - */ - - unit = (*dvp->pd_count)++; - - /* - ** Announce this device - */ - - printf ("%s%d <%s> rev %d", dvp->pd_name, unit, name, - (unsigned) pci_conf_read (tag, PCI_CLASS_REG) & 0xff); - - /* - ** Get the int pin number (pci interrupt number a-d) - ** from the pci configuration space. - */ - - data = pci_conf_read (tag, PCI_INTERRUPT_REG); - pciint = PCI_INTERRUPT_PIN_EXTRACT(data); - - if (pciint) { - - printf (" int %c irq ", 0x60+pciint); - - irq = PCI_INTERRUPT_LINE_EXTRACT(data); - - /* - ** If it's zero, the isa irq number is unknown, - ** and we cannot bind the pci interrupt. - */ - -#if defined(APIC_IO) - if (irq && (irq != 0xff)) { - airq = get_pci_apic_irq (bus, dev, pciint); - if (airq != 0xff) { /* APIC IRQ exists */ - rirq = irq; /* 're-directed' IRQ */ - irq = airq; /* use APIC IRQ */ - } - printf ("%d", irq); - } -#else - if (irq && (irq != 0xff)) - printf ("%d", irq); -#endif /* APIC_IO */ - else - printf ("??"); +pci_maptype(unsigned mapreg) +{ + static u_int8_t maptype[0x10] = { + PCI_MAPMEM, PCI_MAPPORT, + PCI_MAPMEM, 0, + PCI_MAPMEM, PCI_MAPPORT, + 0, 0, + PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, + PCI_MAPMEM|PCI_MAPMEMP, 0, + PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, + 0, 0, }; - printf (" on pci%d:%d:%d\n", bus, dev, func); - -#if defined(APIC_IO) - if (airq != 0xff) { /* APIC IRQ exists */ - data = PCI_INTERRUPT_LINE_INSERT(data, airq); - pci_conf_write (tag, PCI_INTERRUPT_REG, data); - undirect_pci_irq (rirq); /* free for ISA card */ - } -#endif /* APIC_IO */ - - /* - ** Read the current mapping, - ** and update the pcicb fields. - */ - - data = pci_conf_read(tag, PCI_CLASS_REG); - data &= (PCI_CLASS_MASK|PCI_SUBCLASS_MASK); - switch (data) { - case PCI_CLASS_BRIDGE|PCI_SUBCLASS_BRIDGE_PCI: - break; - case PCI_CLASS_BRIDGE|PCI_SUBCLASS_BRIDGE_CARDBUS: { - u_int map, addr, size; - map = pci_conf_read(tag, PCI_CARDBUS_SOCKET_REG); - pci_conf_write (tag, PCI_CARDBUS_SOCKET_REG, 0xffffffff); - size = pci_conf_read(tag, PCI_CARDBUS_SOCKET_REG); - size = (~size) + 1; - addr = pci_memalloc (pcicb, map, size); - pci_conf_write (tag, PCI_CARDBUS_SOCKET_REG, addr); - pcicb->pcicb_mamount += size; - break; - } - default: - for (reg=PCI_MAP_REG_START;reg<PCI_MAP_REG_END;reg+=4) { - u_int map, addr, size; - - - map = pci_conf_read (tag, reg); - if (!(map & PCI_MAP_MEMORY_ADDRESS_MASK)) - continue; - - pci_conf_write (tag, reg, 0xffffffff); - data = pci_conf_read (tag, reg); - pci_conf_write (tag, reg, map); - - switch (data & 7) { - - default: - continue; - case 1: - case 5: - addr = map & PCI_MAP_IO_ADDRESS_MASK; - size = -(data & PCI_MAP_IO_ADDRESS_MASK); - size &= ~(addr ^ -addr); - - pci_register_io (pcicb, addr, addr+size-1); - pcicb->pcicb_pamount += size; - break; - - case 0: - case 2: - case 4: - size = -(data & PCI_MAP_MEMORY_ADDRESS_MASK); - addr = map & PCI_MAP_MEMORY_ADDRESS_MASK; - if (addr >= 0x100000) { - pci_register_memory (pcicb, addr, addr+size-1); - pcicb->pcicb_mamount += size; - }; - break; - } - if (bootverbose) - printf ("\tmapreg[%02x] type=%d addr=%08x size=%04x.\n", - reg, map&7, addr, size); - } - } - - /* - ** attach device - ** may produce additional log messages, - ** i.e. when installing subdevices. - */ - - (*dvp->pd_attach) (tag, unit); - - /* - ** Special processing of certain classes - */ - - data = pci_conf_read(tag, PCI_CLASS_REG); - - switch (data & (PCI_CLASS_MASK|PCI_SUBCLASS_MASK)) { - struct pcicb *this, **link; - unsigned char primary, secondary, subordinate; - u_int command; - - case PCI_CLASS_BRIDGE|PCI_SUBCLASS_BRIDGE_PCI: - - /* - ** get current configuration of the bridge. - */ - data = pci_conf_read (tag, PCI_PCI_BRIDGE_BUS_REG); - primary = PCI_PRIMARY_BUS_EXTRACT (data); - secondary = PCI_SECONDARY_BUS_EXTRACT(data); - subordinate = PCI_SUBORDINATE_BUS_EXTRACT(data); -#ifndef PCI_QUIET - if (bootverbose) { - printf ("\tbridge from pci%d to pci%d through %d.\n", - primary, secondary, subordinate); - printf ("\tmapping regs: io:%08lx mem:%08lx pmem:%08lx\n", - pci_conf_read (tag, PCI_PCI_BRIDGE_IO_REG), - pci_conf_read (tag, PCI_PCI_BRIDGE_MEM_REG), - pci_conf_read (tag, PCI_PCI_BRIDGE_PMEM_REG)); - } -#endif - /* - ** check for uninitialized bridge. - */ - if (!(primary < secondary - && secondary <= subordinate - && bus == primary)) { - - printf ("\tINCORRECTLY or NEVER CONFIGURED.\n"); - /* - ** disable this bridge - */ - pci_conf_write (tag, PCI_COMMAND_STATUS_REG, 0xffff0000); - secondary = 0; - subordinate = 0; - }; - - /* - ** allocate bus descriptor for bus behind the bridge - */ - link = &pcicb->pcicb_down; - while (*link && (*link)->pcicb_bus < secondary) - link = &(*link)->pcicb_next; - - this = malloc (sizeof (*this), M_DEVBUF, M_WAITOK); - - /* - ** Initialize this descriptor so far. - ** (the initialization is completed just before - ** scanning the bus behind the bridge. - */ - bzero (this, sizeof(*this)); - this->pcicb_next = *link; - this->pcicb_up = pcicb; - this->pcicb_bridge = tag; - this->pcicb_bus = secondary; - this->pcicb_subordinate = subordinate; - - command = pci_conf_read(tag,PCI_COMMAND_STATUS_REG); - - if (command & PCI_COMMAND_IO_ENABLE){ - /* - ** Bridge was configured by the bios. - ** Read out the mapped io region. - */ - unsigned reg; - - reg = pci_conf_read (tag, PCI_PCI_BRIDGE_IO_REG); - this->pcicb_iobase = PCI_PPB_IOBASE_EXTRACT (reg); - this->pcicb_iolimit = PCI_PPB_IOLIMIT_EXTRACT(reg); - - /* - ** Note the used io space. - */ - pci_register_io (pcicb, this->pcicb_iobase, - this->pcicb_iolimit); - - }; - - if (command & PCI_COMMAND_MEM_ENABLE) { - /* - ** Bridge was configured by the bios. - ** Read out the mapped memory regions. - */ - unsigned reg; - - /* - ** non prefetchable memory - */ - reg = pci_conf_read (tag, PCI_PCI_BRIDGE_MEM_REG); - this->pcicb_membase = PCI_PPB_MEMBASE_EXTRACT (reg); - this->pcicb_memlimit = PCI_PPB_MEMLIMIT_EXTRACT(reg); - - /* - ** Register used memory space. - */ - pci_register_memory (pcicb, - this->pcicb_membase, - this->pcicb_memlimit); - - /* - ** prefetchable memory - */ - reg = pci_conf_read (tag, PCI_PCI_BRIDGE_PMEM_REG); - this->pcicb_p_membase = PCI_PPB_MEMBASE_EXTRACT (reg); - this->pcicb_p_memlimit = PCI_PPB_MEMLIMIT_EXTRACT(reg); - - /* - ** Register used memory space. - */ - pci_register_memory (pcicb, - this->pcicb_p_membase, - this->pcicb_p_memlimit); - } - - /* - ** Link it in chain. - */ - *link=this; - - /* - ** Update mapping info of parent bus. - */ - if (!pcicb->pcicb_bfrom||secondary< pcicb->pcicb_bfrom) - pcicb->pcicb_bfrom = secondary; - if (subordinate > pcicb->pcicb_bupto) - pcicb->pcicb_bupto = subordinate; - } + return maptype[mapreg & 0x0f]; } -/*======================================================== -** -** pci_bus_config() -** -** Autoconfiguration of one pci bus. -** -**======================================================== -*/ +/* return log2 of map size decoded for memory or port map */ static int -pci_mfdev (int bus, int device) +pci_mapsize(unsigned testval) { - pcici_t tag0,tag1; - unsigned pci_id0, pci_id1; - - /* - ** Detect a multi-function device that complies to the PCI 2.0 spec - */ - tag0 = pcibus->pb_tag (bus, device, 0); - if (pci_conf_read (tag0, PCI_HEADER_MISC) & PCI_HEADER_MULTIFUNCTION) - return 1; - - /* - ** Well, as always: Theory and implementation of PCI ... - ** - ** If there is a valid device ID returned for function 1 AND - ** the device ID of function 0 and 1 is different OR - ** the first mapping register of 0 and 1 differs, - ** then assume a multi-function device anyway ... - ** - ** Example of such a broken device: ISA and IDE chip i83371FB (Triton) - */ - tag1 = pcibus->pb_tag (bus, device, 1); - pci_id1 = pci_conf_read (tag1, PCI_ID_REG); - - if (pci_id1 != 0xffffffff) { - - pci_id0 = pci_conf_read (tag0, PCI_ID_REG); - - if (pci_id0 != pci_id1) - return 1; - - if (pci_conf_read (tag0, PCI_MAP_REG_START) - != pci_conf_read (tag1, PCI_MAP_REG_START)) - return 1; - } - return 0; -} + int ln2size; -static void -pci_bus_config (void) -{ - int bus_no; - u_char device; - u_char reg; - pcici_t tag, mtag; - pcidi_t type; - - struct pci_device *dvp; - - /* - ** first initialize the bridge (bus controller chip) - */ - bus_no = pci_bridge_config (); - - printf ("Probing for devices on PCI bus %d:\n", bus_no); -#ifndef PCI_QUIET - if (bootverbose && !pci_info_done) { - pci_info_done=1; - printf ("\tconfiguration mode %d allows %d devices.\n", - pci_mechanism, pci_maxdevice); - }; -#endif - for (device=0; device<pci_maxdevice; device ++) { - char *name = NULL; - struct pci_device **dvpp; - int func, maxfunc = 0; - - for (func=0; func <= maxfunc; func++) { - tag = pcibus->pb_tag (bus_no, device, func); - type = pci_conf_read (tag, PCI_ID_REG); - - if ((!type) || (type==0xfffffffful)) continue; - - /* - ** lookup device in ioconfiguration: - */ - - dvpp = (struct pci_device **)pcidevice_set.ls_items; - - while (dvp = *dvpp++) { - if (dvp->pd_probe) { - if (name=(*dvp->pd_probe)(tag, type)) - break; - } - }; - /* - ** check for mirrored devices. - */ - if (func != 0) { - goto real_device; - } - if (device & 0x10) { - mtag=pcibus->pb_tag (bus_no, - (u_char)(device & ~0x10), 0); - } else if (device & 0x08) { - mtag=pcibus->pb_tag (bus_no, - (u_char)(device & ~0x08), 0); - } else goto real_device; - - if (type!=pci_conf_read (mtag, PCI_ID_REG)) - goto real_device; - - for (reg=PCI_MAP_REG_START;reg<PCI_MAP_REG_END;reg+=4) - if (pci_conf_read(tag,reg)!=pci_conf_read(mtag,reg)) - goto real_device; - -#ifndef PCI_QUIET - if (dvp==NULL) continue; - if (bootverbose) - printf ("%s? <%s> mirrored on pci%d:%d\n", - dvp->pd_name, name, bus_no, device); -#endif - continue; - - real_device: - -#ifndef PCI_QUIET -#ifdef PCI_BRIDGE_DEBUG - if (bootverbose) { - printf ("\tconfig header: 0x%08x 0x%08x 0x%08x 0x%08x\n", - pci_conf_read (tag, 0), - pci_conf_read (tag, 4), - pci_conf_read (tag, 8), - pci_conf_read (tag, 12)); - } -#endif -#endif - - if (func == 0 && pci_mfdev (bus_no, device)) { - maxfunc = 7; - } - - pci_remember(bus_no, device, func, dvp); - - if (dvp==NULL) { -#ifndef PCI_QUIET - if (pci_conf_count) - continue; - - if (maxfunc == 0) - printf("%s%d:%d: ", - pcibus->pb_name, bus_no, device); - else - printf("%s%d:%d:%d: ", - pcibus->pb_name, bus_no, device, func); - not_supported (tag, type); -#endif - continue; - }; - - if (*name) { - pci_attach (bus_no, device, func, dvp, name); - } - } - } - -#ifndef PCI_QUIET - if (bootverbose) { - if (pcicb->pcicb_mamount) - printf ("%s%d: uses %ud bytes of memory from %x upto %x.\n", - pcibus->pb_name, bus_no, - pcicb->pcicb_mamount, - pcicb->pcicb_mfrom, pcicb->pcicb_mupto); - if (pcicb->pcicb_pamount) - printf ("%s%d: uses %ud bytes of I/O space from %x upto %x.\n", - pcibus->pb_name, bus_no, - pcicb->pcicb_pamount, - pcicb->pcicb_pfrom, pcicb->pcicb_pupto); - if (pcicb->pcicb_bfrom) - printf ("%s%d: subordinate busses from %x upto %x.\n", - pcibus->pb_name, bus_no, - pcicb->pcicb_bfrom, pcicb->pcicb_bupto); + testval = pci_mapbase(testval); + ln2size = 32; + while ((testval & 0x80000000) != 0) + { + ln2size--; + testval <<= 1; } -#endif + return (ln2size); } -/*======================================================== -** -** pci_configure () -** -** Autoconfiguration of pci devices. -** -** Has to take care of mirrored devices, which are -** entailed by incomplete decoding of pci address lines. -** -**======================================================== -*/ - -void pci_configure() -{ - struct pcibus **pbp = (struct pcibus**) pcibus_set.ls_items; - - /* - ** check pci bus present - */ +/* return log2 of address range supported by map register */ - while (!pci_maxdevice && (pcibus = *pbp++)) { - (*pcibus->pb_setup)(); - } - - if (!pci_maxdevice) return; - - /* - ** hello world .. - */ - - pciroots = 1; - while (pciroots--) { - - pcicb = malloc (sizeof (struct pcicb), M_DEVBUF, M_WAITOK); - if (pcicb == NULL) { - return; - } - bzero (pcicb, sizeof (struct pcicb)); - pcicb->pcicb_bus = pcibusmax; - pcicb->pcicb_iolimit = 0xffff; - pcicb->pcicb_membase = 0x02000000; - pcicb->pcicb_p_membase = 0x02000000; - pcicb->pcicb_memlimit = 0xffffffff; - pcicb->pcicb_p_memlimit = 0xffffffff; - - while (pcicb != NULL) { - pci_bus_config (); - - if (pcibusmax < pcicb->pcicb_bus) - (pcibusmax = pcicb->pcicb_bus); - - if (pcicb->pcicb_down) { - pcicb = pcicb->pcicb_down; - continue; - }; - - while (pcicb && !pcicb->pcicb_next) - pcicb = pcicb->pcicb_up; - - if (pcicb) - pcicb = pcicb->pcicb_next; - } - pcibusmax++; +static int +pci_maprange(unsigned mapreg) +{ + int ln2range = 0; + switch (mapreg & 0x07) { + case 0x00: + case 0x01: + case 0x05: + ln2range = 32; + break; + case 0x02: + ln2range = 20; + break; + case 0x04: + ln2range = 64; + break; } - pci_conf_count++; + return (ln2range); } -/*======================================================== -** -** pci_rescan () -** -** try to find lkm driver for device -** -** May be called more than once. -** Any device is attached only once. -** -**======================================================== -*/ - -static void pci_rescan() +/* extract map parameters into newly allocated array of pcimap structures */ + +static pcimap * +pci_readmaps(pcicfgregs *cfg, int maxmaps) { int i; - for (i = 0; i < pci_dev_list_count; i++) - { - struct pci_lkm *lkm; - pcici_t tag; - struct pci_device *dvp; - pcidi_t type = pci_dev_list[i].pc_devid; - char *name = NULL; - int bus, dev, func; - - if (pci_dev_list[i].pc_dvp) - continue; - - bus = pci_dev_list[i].pc_sel.pc_bus; - dev = pci_dev_list[i].pc_sel.pc_dev; - func = pci_dev_list[i].pc_sel.pc_func; - - tag = pcibus->pb_tag (bus, dev, func); - - for (lkm = pci_lkm_head; lkm; lkm = lkm->next) { - dvp = lkm->dvp; - if (name=(*dvp->pd_probe)(tag, type)) - break; - } - if (name && *name) { - pcicb = pci_dev_list[i].pc_cb; - pci_attach (bus, dev, func, dvp, name); - pci_dev_list[i].pc_dvp = dvp; + pcimap *map; + int map64 = 0; + + while (maxmaps > 0 + && pci_cfgread(cfg, PCIR_MAPS + (maxmaps -1) *4, 4) == 0) + maxmaps--; + + map = malloc(maxmaps * sizeof (pcimap), M_DEVBUF, M_WAITOK); + if (map != NULL) { + bzero(map, sizeof(pcimap) * maxmaps); + + for (i = 0; i < maxmaps; i++) { + int reg = PCIR_MAPS + i*4; + u_int32_t base; + u_int32_t testval; + + base = pci_cfgread(cfg, reg, 4); + + if (map64 == 0) { + pci_cfgwrite(cfg, reg, 0xffffffff, 4); + testval = pci_cfgread(cfg, reg, 4); + pci_cfgwrite(cfg, reg, base, 4); + + map[i].base = pci_mapbase(base); + map[i].type = pci_maptype(base); + map[i].ln2size = pci_mapsize(testval); + map[i].ln2range = pci_maprange(testval); + map64 = map[i].ln2range == 64; + } else { + /* only fill in base, other fields are 0 */ + map[i].base = base; + map64 = 0; + } + if (map[i].type == 0) { + /* + * This indicates, that some config space register + * was mistaken to contain a map, while it in fact + * contains unrelated information! + * Ignore this map and all that might have been + * expected to follow ... + */ + maxmaps = i; + } } + cfg->nummaps = maxmaps; } + return (map); } -/*======================================================== -** -** pci_register_lkm () -** -** Add LKM PCI driver's struct pci_device to pci_lkm chain -** -**======================================================== -*/ +/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ -int pci_register_lkm (struct pci_device *dvp, int if_revision) +static void +pci_fixancient(pcicfgregs *cfg) { - struct pci_lkm *lkm; - - if (if_revision != 0) { - return -1; - } - - if (!dvp || !dvp->pd_probe || !dvp->pd_attach) { - return -1; - } - - lkm = malloc (sizeof (*lkm), M_DEVBUF, M_WAITOK); - if (!lkm) { - return -1; - } + if (cfg->hdrtype != 0) + return; - lkm->dvp = dvp; - lkm->next = pci_lkm_head; - pci_lkm_head = lkm; - pci_rescan(); - return 0; + /* PCI to PCI bridges use header type 1 */ + if (cfg->class == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) + cfg->hdrtype = 1; } -/*----------------------------------------------------------------------- -** -** Map device into port space. -** -** Actually the device should have been mapped by the bios. -** This function only reads and verifies the value. -** -** PCI-Specification: 6.2.5.1: address maps -** -**----------------------------------------------------------------------- -*/ - -int pci_map_port (pcici_t tag, u_long reg, u_short* pa) +/* read config data specific to header type 1 device (PCI to PCI bridge) */ + +static void * +pci_readppb(pcicfgregs *cfg) { - unsigned data, ioaddr, iosize; - struct pcicb *link = pcicb; + pcih1cfgregs *p; - /* - ** sanity check - */ + p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); + if (p == NULL) + return (NULL); - if (reg < PCI_MAP_REG_START || reg >= PCI_MAP_REG_END || (reg & 3)) { - printf ("pci_map_port failed: bad register=0x%x\n", - (unsigned)reg); - return (0); - }; + bzero(p, sizeof *p); - /* - ** get size and type of port - ** - ** type is in the lowest two bits. - ** If device requires 2^n bytes, the next - ** n-2 bits are hardwired as 0. - */ - - ioaddr = pci_conf_read (tag, reg) & PCI_MAP_IO_ADDRESS_MASK; - if (!ioaddr) { - printf ("pci_map_port failed: not configured by bios.\n"); - return (0); - }; + p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); + p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); - pci_conf_write (tag, reg, 0xfffffffful); - data = pci_conf_read (tag, reg); - pci_conf_write (tag, reg, ioaddr); + p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); - if ((data & 0x03) != PCI_MAP_IO) { - printf ("pci_map_port failed: bad port type=0x%x\n", - (unsigned) data); - return (0); - }; - iosize = -(data & PCI_MAP_IO_ADDRESS_MASK); - iosize &= ~(ioaddr ^ -ioaddr); - if (ioaddr < pcicb->pcicb_iobase - || ioaddr + iosize -1 > pcicb->pcicb_iolimit) { - printf ("pci_map_port failed: device's iorange 0x%x-0x%x " - "is incompatible with its bridge's range 0x%x-0x%x\n", - (unsigned) ioaddr, (unsigned) ioaddr + iosize - 1, - (unsigned) pcicb->pcicb_iobase, - (unsigned) pcicb->pcicb_iolimit); - return (0); - } + p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), + pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); + p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), + pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); -#ifndef PCI_QUIET - if (bootverbose) - printf ("\treg%d: ioaddr=0x%x size=0x%x\n", - (unsigned) reg, (unsigned) ioaddr, (unsigned) iosize); -#endif - /* - ** set the configuration register of and - ** return the address to the driver. - ** Make sure to enable each upstream bridge - ** so I/O and DMA can go all the way. - */ - - for (;;) { - data = pci_conf_read (tag, PCI_COMMAND_STATUS_REG) & 0xffff; - data |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE; - (void) pci_conf_write(tag, PCI_COMMAND_STATUS_REG, data); - if ((link = link->pcicb_up) == NULL) - break; - tag = link->pcicb_bridge; - } + p->membase = PCI_PPBMEMBASE (0, + pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); + p->memlimit = PCI_PPBMEMLIMIT (0, + pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); - *pa = ioaddr; + p->pmembase = PCI_PPBMEMBASE ( + (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), + pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); - return (1); + p->pmemlimit = PCI_PPBMEMLIMIT ( + (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), + pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); + return (p); } -/*----------------------------------------------------------------------- -** -** Map device into virtual and physical space -** -** Actually the device should have been mapped by the bios. -** This function only reads and verifies the value. -** -** PCI-Specification: 6.2.5.1: address maps -** -**----------------------------------------------------------------------- -*/ - -int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) -{ - struct pcicb *link = pcicb; - unsigned data ,paddr; - vm_size_t psize, poffs; - vm_offset_t vaddr; - - /* - ** sanity check - */ - - if (reg < PCI_MAP_REG_START || reg >= PCI_MAP_REG_END || (reg & 3)) { - printf ("pci_map_mem failed: bad register=0x%x\n", - (unsigned)reg); - return (0); - }; - - /* - ** save old mapping, get size and type of memory - ** - ** type is in the lowest four bits. - ** If device requires 2^n bytes, the next - ** n-4 bits are read as 0. - */ - - paddr = pci_conf_read (tag, reg) & PCI_MAP_MEMORY_ADDRESS_MASK; - pci_conf_write (tag, reg, 0xfffffffful); - data = pci_conf_read (tag, reg); - pci_conf_write (tag, reg, paddr); - - /* - ** check the type - */ - - if (!((data & PCI_MAP_MEMORY_TYPE_MASK) == PCI_MAP_MEMORY_TYPE_32BIT_1M - && (paddr & ~0xfffff) == 0) - && (data & PCI_MAP_MEMORY_TYPE_MASK) != PCI_MAP_MEMORY_TYPE_32BIT){ - printf ("pci_map_mem failed: bad memory type=0x%x\n", - (unsigned) data); - return (0); - }; - - /* - ** get the size. - */ - - psize = -(data & PCI_MAP_MEMORY_ADDRESS_MASK); - - if (!paddr || paddr == PCI_MAP_MEMORY_ADDRESS_MASK) { - paddr = pci_memalloc (pcicb, 0, psize); - if (!paddr) { - printf ("pci_map_mem: not configured by bios.\n"); - return (0); - }; - pci_register_memory (pcicb, paddr, paddr+psize-1); - }; +/* read config data specific to header type 2 device (PCI to CardBus bridge) */ - if (paddr < pcicb->pcicb_membase || - paddr + psize - 1 > pcicb->pcicb_memlimit) { - printf ("pci_map_mem failed: device's memrange 0x%x-0x%x is " - "incompatible with its bridge's memrange 0x%x-0x%x\n", - (unsigned) paddr, - (unsigned) (paddr + psize - 1), - (unsigned) pcicb->pcicb_membase, - (unsigned) pcicb->pcicb_memlimit); -/* return (0);*/ -/* ACHTUNG: Ist der Code richtig, wenn eine PCI-PCI-Bridge fuer - * die PCI-Slots verwendet wird, aber die Onboard-Devices direkt - * an der CPU-PCI-Bridge haengen (Siehe Compaq Prolinea Problem) ??? - */ - } - pci_conf_write (tag, reg, paddr); - - /* - ** Truncate paddr to page boundary. - ** (Or does pmap_mapdev the job?) - */ +static void * +pci_readpcb(pcicfgregs *cfg) +{ + pcih2cfgregs *p; - poffs = paddr - trunc_page (paddr); - vaddr = (vm_offset_t) pmap_mapdev (paddr-poffs, psize+poffs); + p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); + if (p == NULL) + return (NULL); - if (!vaddr) return (0); + bzero(p, sizeof *p); - vaddr += poffs; + p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); + p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); + + p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); -#ifndef PCI_QUIET - /* - ** display values. - */ + p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); + p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); + p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); + p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); - if (bootverbose) - printf ("\treg%d: virtual=0x%lx physical=0x%lx size=0x%lx\n", - (unsigned) reg, (u_long)vaddr, (u_long)paddr, (u_long)psize); -#endif - /* - ** set the configuration register and - ** return the address to the driver - ** Make sure to enable each upstream bridge - ** so memory and DMA can go all the way. - */ - - for (;;) { - data = pci_conf_read (tag, PCI_COMMAND_STATUS_REG) & 0xffff; - data |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE; - (void) pci_conf_write(tag, PCI_COMMAND_STATUS_REG, data); - if ((link = link->pcicb_up) == NULL) - break; - tag = link->pcicb_bridge; - } + p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); + p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); + p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); + p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); - *va = vaddr; - *pa = paddr; - - return (1); + p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); + return p; } -/*----------------------------------------------------------------------- -** -** Pci meta interrupt handler -** -** This handler assumes level triggered interrupts. -** It's possible to build a kernel which handles shared -** edge triggered interrupts by the options "PCI_EDGE_INT". -** But there is a performance penalty. -** -** (Of course you can delete the #ifdef PCI_EDGE_INT bracketed -** code at all :-) :-) :-) -** -**----------------------------------------------------------------------- -*/ - -static struct pci_int_desc* - pci_int_desc [PCI_MAX_IRQ]; - -#ifndef NO_SHARED_IRQ - -static inline unsigned -splq (unsigned mask) -{ - unsigned temp=cpl; - cpl |= mask; - return temp; -} +/* extract header type specific config data */ static void -pci_int (int irq) +pci_hdrtypedata(pcicfgregs *cfg) { - struct pci_int_desc * p; - int s; - - if (irq<0 || irq >= PCI_MAX_IRQ) { - printf ("pci_int: irq %d out of range, ignored\n", irq); - return; - }; - for (p = pci_int_desc[irq]; p!=NULL; p=p->pcid_next) { - s = splq (*p->pcid_maskptr); - (*p->pcid_handler) (p->pcid_argument); - p-> pcid_tally++; - splx (s); -#if 0 - if (p->pcid_tally<20) - printf ("PCI_INT: irq=%d h=%p cpl o=%x n=%x val=%d\n", - irq, p->pcid_handler, s, cpl, c); -#endif - }; -} -#endif - -/*----------------------------------------------------------------------- -** -** Auxiliary function for interrupt (un)mapping. -** -**----------------------------------------------------------------------- -*/ - -static u_int -getirq (pcici_t tag) -{ - u_int irq; - - irq = PCI_INTERRUPT_LINE_EXTRACT( - pci_conf_read (tag, PCI_INTERRUPT_REG)); - - if (irq == 0 || irq == 0xff) { - printf ("\tint line register not set by bios\n"); - return (0xff); + switch (cfg->hdrtype) { + case 0: + cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); + cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); + cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_0); + break; + case 1: + cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); + cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); + cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); + cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); + cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_1); + cfg->hdrspec = pci_readppb(cfg); + break; + case 2: + cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); + cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); + cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); + cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); + cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_2); + cfg->hdrspec = pci_readpcb(cfg); + break; } +} - if (irq >= PCI_MAX_IRQ) { - printf ("\tirq %d out of bounds (must be < %d).\n", - irq, PCI_MAX_IRQ); - return (0xff); +/* read configuration header into pcicfgrect structure */ + +static pcicfgregs * +pci_readcfg(pcicfgregs *probe) +{ + pcicfgregs *cfg = NULL; + + if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { + cfg = malloc(sizeof (pcicfgregs), M_DEVBUF, M_WAITOK); + if (cfg == NULL) + return (cfg); + + bzero(cfg, sizeof *cfg); + + cfg->bus = probe->bus; + cfg->slot = probe->slot; + cfg->func = probe->func; + cfg->parent = probe->parent; + + cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); + cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); + cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); + cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); + cfg->class = pci_cfgread(cfg, PCIR_CLASS, 1); + cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); + cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); + cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); + cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); + cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); + cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); + cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); + cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1); + cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); + cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); + + cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; + cfg->hdrtype &= ~PCIM_MFDEV; + + pci_fixancient(cfg); + pci_hdrtypedata(cfg); } - - return (irq); + return (cfg); } -static struct pci_int_desc ** -getintdescbytag (u_int irq, pcici_t tag) -{ - struct pci_int_desc *p, **pp; - - pp=&pci_int_desc[irq]; - while (((p=*pp)) && !sametag(p->pcid_tag,tag)) - pp=&p->pcid_next; - - if (!p) return (NULL); +/* free pcicfgregs structure and all depending data structures */ - return (pp); -} - -static struct pci_int_desc * -getintdescbymptr (u_int irq, unsigned * mptr) +static int +pci_freecfg(pcicfgregs *cfg) { - struct pci_int_desc *p; - - for (p=pci_int_desc[irq];p;p=p->pcid_next) - if (p->pcid_maskptr == mptr) break; - return (p); + if (cfg->hdrspec != NULL) + free(cfg->hdrspec, M_DEVBUF); + if (cfg->map != NULL) + free(cfg->map, M_DEVBUF); + free(cfg, M_DEVBUF); + return (0); } -/*----------------------------------------------------------------------- -** -** Map pci interrupt. -** -**----------------------------------------------------------------------- -*/ - -static unsigned pci_mask0 = 0; - -int pci_map_int (pcici_t tag, pci_inthand_t *func, void *arg, unsigned *maskptr) +static void +pci_addcfg(pcicfgregs *cfg) { - u_int irq; - int result, oldspl; - unsigned mask; - struct pci_int_desc *tail, *mdp=NULL, *new=NULL; - - /* - ** Get irq line from configuration space, - ** and check for consistency. - */ - - irq = getirq (tag); - if (irq == 0xff) { - return (0); - }; - mask= 1ul << irq; - - /* - ** disable this interrupt. - */ - - oldspl = splq (mask); - - /* - ** If handler for this tag already installed, - ** remove it first. - */ - - if (getintdescbytag (irq, tag) != NULL) - pci_unmap_int (tag); - - /* - ** If this irq not yet included in the mask, include it. - */ - - mdp = getintdescbymptr (irq, maskptr); - if (!mdp) { - result = pcibus->pb_imaskinc (irq, maskptr); - if (result) - goto conflict; - }; - - /* - ** Allocate descriptor and initialize it. - */ - - tail = pci_int_desc[irq]; - - new = malloc (sizeof (*new), M_DEVBUF, M_WAITOK); - bzero (new, sizeof (*new)); - - new->pcid_next = tail; - new->pcid_tag = tag; - new->pcid_handler = func; - new->pcid_argument = arg; - new->pcid_maskptr = maskptr; - new->pcid_tally = 0; - new->pcid_mask = mask; - - /* - ** If first handler: install it. - ** If second handler: install shared-int-handler. - */ - - if (!tail) { - /* - ** first handler for this irq. - */ - - result = pcibus->pb_iattach - /* - * XXX if we get here, then `func' must be pci_int - * so the bogus casts are almost OK since they just - * undo the bogus casts that were needed to pass - * pci_int and its arg to pci_map_int(). - */ - (irq, (inthand2_t *) func, (int) arg, maskptr); - if (result) goto conflict; - -#ifdef NO_SHARED_IRQ - } else goto conflict; -#else - } else if (!tail->pcid_next) { - /* - ** Second handler for this irq. - */ - - if (bootverbose) - printf ("\tusing shared irq %d.\n", irq); - - /* - ** replace old handler by shared-int-handler. - */ - - result = pcibus->pb_idetach (irq, - (inthand2_t *) tail->pcid_handler); - if (result) - printf ("\tCANNOT DETACH INT HANDLER.\n"); - - result = pcibus->pb_iattach (irq, pci_int, irq, &pci_mask0); - if (result) { - printf ("\tCANNOT ATTACH SHARED INT HANDLER.\n"); - goto fail; - }; +#ifdef PCI_DEBUG + if (bootverbose) { + int i; + printf("new pci: vendor=0x%04x, dev=0x%04x, revid=0x%02x\n", + cfg->vendor, cfg->device, cfg->revid); + printf("\t cmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", + cfg->cmdreg, cfg->statreg, cfg->cachelnsz); + printf("\t class=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", + cfg->class, cfg->subclass, cfg->progif, cfg->hdrtype, cfg->mfdev); + printf("\t lattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", + cfg->lattimer, cfg->lattimer * 30, + cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); + + if (cfg->intpin > 0) + printf("\t intpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); + + for (i = 0; i < cfg->nummaps; i++) { + pcimap *m = &cfg->map[i]; + printf("\t map[%d]: type %x, range %2d, base %08x, size %2d\n", + i, m->type, m->ln2range, m->base, m->ln2size); + } } -#endif - /* - ** Link new descriptor, reenable ints and done. - */ - - pci_int_desc[irq] = new; - splx (oldspl); - return (1); - - /* - ** Handle some problems. - */ - -conflict: - printf ("\tirq %d already in use.\n", irq); -fail: - /* - ** If descriptor allocated, free it. - ** If included in mask, remove it. - */ - - if (new) free(new, M_DEVBUF); - if (!mdp) (void) pcibus->pb_imaskexc (irq, maskptr); - splx (oldspl); - return (0); +#endif /* PCI_DEBUG */ + pci_drvattach(cfg); /* XXX currently defined in pci_compat.c */ } -/*----------------------------------------------------------------------- -** -** Unmap pci interrupt. -** -**----------------------------------------------------------------------- -*/ +/* return pointer to device that is a bridge to this bus */ -int pci_unmap_int (pcici_t tag) +static pcicfgregs * +pci_bridgeto(int bus) { - int result, oldspl; - struct pci_int_desc *this, **hook, *tail; - unsigned irq; - - /* - ** Get irq line from configuration space, - ** and check for consistency. - */ - - irq = getirq (tag); - if (irq == 0xff) { - return (0); - }; - - /* - ** Search and unlink interrupt descriptor. - */ - - hook = getintdescbytag (irq, tag); - if (hook == NULL) { - printf ("\tno irq %d handler for pci %x\n", - irq, tag.tag); - return (0); - }; - - this = *hook; - *hook= this->pcid_next; - - /* - ** Message - */ - - printf ("\tirq %d handler %p(%p) unmapped for pci %x after %d ints.\n", - irq, this->pcid_handler, this->pcid_argument, - this->pcid_tag.tag, this->pcid_tally); - - /* - ** If this irq no longer included in the mask, remove it. - */ - - if (!getintdescbymptr (irq, this->pcid_maskptr)) - (void) pcibus->pb_imaskexc (irq, this->pcid_maskptr); - - tail = pci_int_desc[irq]; - - if (tail == NULL) { - - /* - ** Remove the old handler. - */ - - result = pcibus->pb_idetach (irq, - (inthand2_t *) this->pcid_handler); - if (result) - printf ("\tirq %d: cannot remove handler.\n", irq); - - } else if (tail->pcid_next == NULL) { - - /* - ** Remove the shared int handler. - ** Install the last remaining handler. - */ - - oldspl = splq (1ul << irq); - - result = pcibus->pb_idetach (irq, pci_int); - if (result) - printf ("\tirq %d: cannot remove handler.\n", irq); - - result = pcibus->pb_iattach (irq, - (inthand2_t *) tail->pcid_handler, - (int) tail->pcid_argument, - tail->pcid_maskptr); - - if (result) - printf ("\tirq %d: cannot install handler.\n", irq); - - splx (oldspl); - }; - - free (this, M_DEVBUF); - return (1); + return (NULL); /* XXX not yet implemented */ } -/*----------------------------------------------------------- -** -** Display of unknown devices. -** -**----------------------------------------------------------- -*/ -struct vt { - u_short ident; - char* name; -}; - -static struct vt VendorTable[] = { - {0x0e11, "Compaq"}, - {0x1000, "NCR/Symbios"}, - {0x1002, "ATI Technologies Inc."}, - {0x1004, "VLSI"}, - {0x100B, "National Semiconductor"}, - {0x100E, "Weitek"}, - {0x1011, "Digital Equipment Corporation"}, - {0x1013, "Cirrus Logic"}, - {0x101A, "NCR"}, - {0x1022, "AMD"}, - {0x102B, "Matrox"}, - {0x102C, "Chips & Technologies"}, - {0x1039, "Silicon Integrated Systems"}, - {0x1042, "SMC"}, - {0x1044, "DPT"}, - {0x1045, "OPTI"}, - {0x104B, "Bus Logic"}, - {0x104C, "TI"}, - {0x1060, "UMC"}, - {0x1080, "Contaq"}, - {0x1095, "CMD"}, - {0x10b9, "ACER Labs"}, - {0x10c8, "NeoMagic"}, - {0x1106, "VIA Technologies"}, - {0x5333, "S3 Inc."}, - {0x8086, "Intel Corporation"}, - {0x9004, "Adaptec"}, - {0,0} -}; - -typedef struct { - const int subclass; - const char *name; -} subclass_name; - -/* 0x00 prehistoric subclasses */ -static const subclass_name old_subclasses[] = -{ - { 0x00, "misc" }, - { 0x01, "vga" }, - { 0x00, NULL } -}; - -/* 0x01 mass storage subclasses */ -static const subclass_name storage_subclasses[] = -{ - { 0x00, "scsi" }, - { 0x01, "ide" }, - { 0x02, "floppy"}, - { 0x03, "ipi" }, - { 0x80, "misc" }, - { 0x00, NULL } -}; - -/* 0x02 network subclasses */ -static const subclass_name network_subclasses[] = -{ - { 0x00, "ethernet" }, - { 0x01, "tokenring" }, - { 0x02, "fddi" }, - { 0x80, "misc" }, - { 0x00, NULL } -}; - -/* 0x03 display subclasses */ -static const subclass_name display_subclasses[] = -{ - { 0x00, "vga" }, - { 0x01, "xga" }, - { 0x80, "misc" }, - { 0x00, NULL } -}; - -/* 0x04 multimedia subclasses */ -static const subclass_name multimedia_subclasses[] = -{ - { 0x00, "video" }, - { 0x01, "audio" }, - { 0x80, "misc" }, - { 0x00, NULL } -}; - -/* 0x05 memory subclasses */ -static const subclass_name memory_subclasses[] = -{ - { 0x00, "ram" }, - { 0x01, "flash" }, - { 0x80, "misc" }, - { 0x00, NULL } -}; - -/* 0x06 bridge subclasses */ -static const subclass_name bridge_subclasses[] = -{ - { 0x00, "host" }, - { 0x01, "isa" }, - { 0x02, "eisa" }, - { 0x03, "mc" }, - { 0x04, "pci" }, - { 0x05, "pcmcia"}, - { 0x07, "cardbus"}, - { 0x80, "misc" }, - { 0x00, NULL } -}; - -static const subclass_name *const subclasses[] = { - old_subclasses, - storage_subclasses, - network_subclasses, - display_subclasses, - multimedia_subclasses, - memory_subclasses, - bridge_subclasses, -}; - -static const char *const majclasses[] = { - "old", - "storage", - "network", - "display", - "multimedia", - "memory", - "bridge", - "comms", - "system", - "input", - "docking", - "processor", - "serial" -}; - - -void not_supported (pcici_t tag, u_long type) -{ - u_long reg; - u_long data; - u_char class; - u_char subclass; - struct vt * vp; - int pciint; - int irq; - - /* - ** lookup the names. - */ - - for (vp=VendorTable; vp->ident; vp++) - if (vp->ident == (type & 0xffff)) - break; +/* scan one PCI bus for devices */ - /* - ** and display them. - */ - - if (vp->ident) printf (vp->name); - else printf ("vendor=0x%04lx", type & 0xffff); - - printf (", device=0x%04lx", type >> 16); - - data = pci_conf_read(tag, PCI_CLASS_REG); - class = (data >> 24) & 0xff; - subclass = (data >> 16) & 0xff; - - if (class < sizeof(majclasses) / sizeof(majclasses[0])) { - printf(", class=%s", majclasses[class]); - } else { - printf(", class=0x%02x", class); - } - - if (class < sizeof(subclasses) / sizeof(subclasses[0])) { - const subclass_name *p = subclasses[class]; - while (p->name && (p->subclass != subclass)) - p++; - if (p->name) { - printf(" (%s)", p->name); - } else { - printf(" (unknown subclass 0x%02lx)", subclass); +static int +pci_probebus(int bus) +{ + pcicfgregs probe; + int bushigh = bus; + + bzero(&probe, sizeof probe); + probe.parent = pci_bridgeto(bus); + probe.bus = bus; + for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { + int pcifunchigh = 0; + for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { + pcicfgregs *cfg = pci_readcfg(&probe); + if (cfg != NULL) { + if (cfg->mfdev) + pcifunchigh = 7; + + if (bushigh < cfg->subordinatebus) + bushigh = cfg->subordinatebus; + + pci_addcfg(cfg); + cfg = NULL; /* we don't own this anymore ... */ + } } - } else { - printf(", subclass=0x%02x", subclass); } + return (bushigh); +} - data = pci_conf_read (tag, PCI_INTERRUPT_REG); - pciint = PCI_INTERRUPT_PIN_EXTRACT(data); - - if (pciint) { - - printf (" int %c irq ", 0x60+pciint); - - irq = PCI_INTERRUPT_LINE_EXTRACT(data); - - /* - ** If it's zero, the isa irq number is unknown, - ** and we cannot bind the pci interrupt. - */ +/* scan a PCI bus tree reached through one PCI attachment point */ - if (irq && (irq != 0xff)) - printf ("%d", irq); - else - printf ("??"); - }; +int +pci_probe(pciattach *parent) +{ + int bushigh; + int bus = 0; - if (class != (PCI_CLASS_BRIDGE >> 24)) - printf (" [no driver assigned]"); - printf ("\n"); + bushigh = pci_bushigh(); + while (bus <= bushigh) { + int newbushigh; - if (bootverbose) { - if (class == (PCI_CLASS_BRIDGE >> 24)) { - printf ("configuration space registers:"); - for (reg = 0; reg < 0x100; reg+=4) { - if ((reg & 0x0f) == 0) printf ("\n%02x:\t", reg); - printf ("%08x ", pci_conf_read (tag, reg)); - } - printf ("\n"); - } else { - for (reg=PCI_MAP_REG_START; reg<PCI_MAP_REG_END; reg+=4) { - data = pci_conf_read (tag, reg); - if ((data&~7)==0) continue; - switch (data&7) { + printf("Probing for devices on PCI bus %d:\n", bus); + newbushigh = pci_probebus(bus); - case 1: - case 5: - printf ("\tmap(%x): io(%04lx)\n", - reg, data & ~3); - break; - case 0: - printf ("\tmap(%x): mem32(%08lx)\n", - reg, data & ~7); - break; - case 2: - printf ("\tmap(%x): mem20(%05lx)\n", - reg, data & ~7); - break; - case 4: - printf ("\tmap(%x): mem64(%08x%08lx)\n", - reg, pci_conf_read (tag, reg +4), data & ~7); - reg += 4; - break; - } - } - } + if (bushigh < newbushigh) + bushigh = newbushigh; + bus++; } + return (bushigh); } /* - * This is the user interface to the PCI configuration space. + * This is the user interface to PCI configuration space. */ - - -static void -pci_remember(int bus, int dev, int func, struct pci_device *dvp) -{ - struct pci_conf *p; - pcici_t tag; - - if (++pci_dev_list_count > pci_dev_list_size) { - struct pci_conf *new; - - pci_dev_list_size += 8; - MALLOC(new, struct pci_conf *, pci_dev_list_size * sizeof *new, - M_DEVL, M_NOWAIT); - if (!new) { - pci_dev_list_size -= 8; - pci_dev_list_count--; - return; - } - - if (pci_dev_list) { - bcopy(pci_dev_list, new, ((pci_dev_list_size - 8) * - sizeof *new)); - FREE(pci_dev_list, M_DEVL); - } - pci_dev_list = new; - } - - p = &pci_dev_list[pci_dev_list_count - 1]; - p->pc_sel.pc_bus = bus; - p->pc_sel.pc_dev = dev; - p->pc_sel.pc_func = func; - p->pc_dvp = dvp; - p->pc_cb = pcicb; - - tag = pcibus->pb_tag (bus, dev, func); - p->pc_hdr = (pci_conf_read (tag, PCI_HEADER_MISC) >> 16) & 0xff; - p->pc_devid = pci_conf_read(tag, PCI_ID_REG); - p->pc_class = pci_conf_read(tag, PCI_CLASS_REG); - switch (p->pc_hdr & 0x7f) { - case 0: - p->pc_subid = pci_conf_read(tag, PCI_SUBID_REG0); - break; - case 1: - p->pc_subid = pci_conf_read(tag, PCI_SUBID_REG1); - break; - case 2: - p->pc_subid = pci_conf_read(tag, PCI_SUBID_REG2); - break; - default: - p->pc_subid = 0; - } -} - + static int pci_open(dev_t dev, int oflags, int devtype, struct proc *p) { if ((oflags & FWRITE) && securelevel > 0) { return EPERM; } - return 0; } @@ -1904,79 +470,62 @@ pci_ioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) struct pci_io *io; size_t iolen; int error; - pcici_t tag; if (cmd != PCIOCGETCONF && !(flag & FWRITE)) return EPERM; switch(cmd) { case PCIOCGETCONF: +#ifdef NOTYET +static struct pci_conf *pci_dev_list; +static unsigned pci_dev_list_count; +static unsigned pci_dev_list_size; + cio = (struct pci_conf_io *)data; iolen = min(cio->pci_len, pci_dev_list_count * sizeof(struct pci_conf)); cio->pci_len = pci_dev_list_count * sizeof(struct pci_conf); error = copyout(pci_dev_list, cio->pci_buf, iolen); +#else + error = ENODEV; +#endif break; case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { + pcicfgregs probe; case 4: - tag = pcibus->pb_tag (io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func); - io->pi_data = pci_conf_read(tag, io->pi_reg); - error = 0; - break; case 2: case 1: - default: - error = ENODEV; - break; - } - break; - - case PCIOCWRITE: - io = (struct pci_io *)data; - switch(io->pi_width) { - case 4: - tag = pcibus->pb_tag (io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func); - pci_conf_write(tag, io->pi_reg, io->pi_data); + probe.bus = io->pi_sel.pc_bus; + probe.slot = io->pi_sel.pc_dev; + probe.func = io->pi_sel.pc_func; + io->pi_data = pci_cfgread(&probe, + io->pi_reg, io->pi_width); error = 0; break; - case 2: - case 1: default: error = ENODEV; break; } break; - case PCIOCATTACHED: + case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { + pcicfgregs probe; case 4: - { - int i = pci_dev_list_count; - struct pci_conf *p = pci_dev_list; - error = ENODEV; - while (i--) { - if (io->pi_sel.pc_bus == p->pc_sel.pc_bus && - io->pi_sel.pc_dev == p->pc_sel.pc_dev && - io->pi_sel.pc_func == p->pc_sel.pc_func) { - io->pi_data = (u_int32_t)p->pc_dvp; - error = 0; - break; - } - p++; - } - } - break; case 2: case 1: + probe.bus = io->pi_sel.pc_bus; + probe.slot = io->pi_sel.pc_dev; + probe.func = io->pi_sel.pc_func; + pci_cfgwrite(&probe, + io->pi_reg, io->pi_data, io->pi_width); + error = 0; + break; default: error = ENODEV; break; @@ -2017,4 +566,4 @@ pci_cdevinit(void *dummy) SYSINIT(pcidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+PCI_CDEV, pci_cdevinit, NULL); -#endif /* NPCI */ +#endif /* NPCI > 0 */ |