diff options
author | se <se@FreeBSD.org> | 1995-03-21 23:01:06 +0000 |
---|---|---|
committer | se <se@FreeBSD.org> | 1995-03-21 23:01:06 +0000 |
commit | 6e667246cd96976e175eec9db61940c42681a155 (patch) | |
tree | b8fe80494fb6a00dba99beff8518fb3df91f57f1 /sys/dev/pci | |
parent | 4f9285470770c38619fdabbc8b376026e3ed4767 (diff) | |
download | FreeBSD-src-6e667246cd96976e175eec9db61940c42681a155.zip FreeBSD-src-6e667246cd96976e175eec9db61940c42681a155.tar.gz |
Completely new PCI code:
1) Supports PCI to PCI bridge devices (and tries to initialise them,
even if the BIOS is brain dead).
2) Supports shared PCI interrupts. Interrupt handlers now MUST return
'0' if they found nothing to do, '1' otherwise.
New features tested with i486 systems based on the Intel Saturn and
a DEC 4channel Ethernet card only, but expected to work on most systems.
The option PCI_REMAP has been removed !
Submitted by: Wolfgang Stanglmeier <wolf@kintaro.cologne.de>
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/pci.c | 1537 | ||||
-rw-r--r-- | sys/dev/pci/pcireg.h | 31 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 114 |
3 files changed, 1114 insertions, 568 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index aa51596..32428c6 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1,11 +1,11 @@ /************************************************************************** ** -** $Id: pci.c,v 1.18 1995/03/02 21:51:53 se Exp $ +** $Id: pci.c,v 1.19 1995/03/17 04:27:19 davidg Exp $ ** -** General subroutines for the PCI bus on 80*86 systems. +** General subroutines for the PCI bus. ** pci_configure () ** -** 386bsd / FreeBSD +** FreeBSD ** **------------------------------------------------------------------------- ** @@ -36,17 +36,11 @@ *************************************************************************** */ -#define PCI_PATCHLEVEL "pl5 95/02/27" +#define __PCI_C__ "pl13 95/03/21" #include <pci.h> #if NPCI > 0 -#ifndef __FreeBSD2__ -#if __FreeBSD__ >= 2 -#define __FreeBSD2__ -#endif -#endif - /*======================================================== ** ** #includes and declarations @@ -59,199 +53,334 @@ #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/devconf.h> + +#include <machine/cpu.h> /* bootverbose */ #include <vm/vm.h> #include <vm/vm_param.h> +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/pcibus.h> + #include <machine/pmap.h> -#ifdef __FreeBSD2__ #include <sys/devconf.h> +#define PCI_MAX_IRQ (16) + + +/*======================================================== +** +** Structs and Functions +** +**======================================================== +*/ + struct pci_devconf { struct kern_devconf pdc_kdc; struct pci_info pdc_pi; }; +struct pcicb { + struct pcicb *pcicb_next; + struct pcicb *pcicb_up; + struct pcicb *pcicb_down; + pcici_t pcicb_bridge; + + u_long pcicb_seen; + u_char pcicb_bus; + u_char pcicb_subordinate; + u_char pcicb_flags; +#define PCICB_ISAMEM 0x01 + 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; +}; + static int pci_externalize (struct proc *, struct kern_devconf *, void *, size_t); static int pci_internalize (struct proc *, struct kern_devconf *, void *, size_t); -#else /* __FreeBSD2__ */ - -/* -** Function prototypes missing in system headers -*/ -extern pmap_t pmap_kernel(void); -static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize); -#endif /* __FreeBSD2__ */ +static void +not_supported (pcici_t tag, u_long type); -#include <pci/pcivar.h> -#include <pci/pcireg.h> -#include <pci/pcibus.h> +static void +pci_bus_config (void); +static void +pci_bridge_config (void); - /*======================================================== ** -** Autoconfiguration of pci devices. -** -** This is reverse to the isa configuration. -** (1) find a pci device. -** (2) look for a driver. +** Variables ** **======================================================== */ +/* +** log2 of safe burst len (in words) +*/ + +unsigned pci_max_burst_len = 2; +unsigned pci_mechanism = 0; +unsigned pci_maxdevice = 0; +struct pcibus* pcibus; + /*-------------------------------------------------------- ** -** Limit for pci bus numbers. +** Local variables. ** **-------------------------------------------------------- */ -#ifndef PCI_MAX_BUS -#define PCI_MAX_BUS (256) -#endif - -static u_long pci_bus_max = 1; +static int pci_conf_count; +static int pci_info_done; +static struct pcicb pcibus0 = { + NULL, NULL, NULL, + { 0 }, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* real allocation */ + 0, 0xFFFF, /* iobase/limit */ + 0x4000000, 0xFFFFFFFFu, /* nonprefetch membase/limit */ + 0x4000000, 0xFFFFFFFFu /* prefetch membase/limit */ +}; +static struct pcicb *pcicb; -/*-------------------------------------------------------- +/*======================================================== ** -** The pci devices can be mapped to any address. -** This is a list of possible starting addresses. -** It can be prepended by a config option. +** pci_configure () ** -**-------------------------------------------------------- +** Autoconfiguration of pci devices. +** +** May be called more than once. +** Any device is attached only once. +** +** Has to take care of mirrored devices, which are +** entailed by incomplete decoding of pci address lines. +** +**======================================================== */ -static u_long pci_stable[] = { -#ifdef PCI_PMEM_START - (PCI_PMEM_START), -#endif - 0xf1000000, - 0x53900000, - 0xc0000000, - 0x81000000, - 0x0f000000, -}; +void pci_configure() +{ + int i; -static vm_offset_t pci_paddr = 0; -static vm_offset_t pci_pold = 0; -static vm_offset_t pci_pidx = 0; + /* + ** check pci bus present + */ + for (i=0; i<pcibus_set.ls_length; i++) { + if (pci_maxdevice) break; + pcibus = (struct pcibus*) pcibus_set.ls_items[i]; + pcibus->pb_setup (); + } + if (!pci_maxdevice) return; -/*-------------------------------------------------------- + /* + ** hello world .. + */ + + for (pcicb = &pcibus0; pcicb != NULL;) { + pci_bus_config (); + + if (pcicb->pcicb_down) { + pcicb = pcicb->pcicb_down; + continue; + }; + + while (pcicb && !pcicb->pcicb_next) + pcicb = pcicb->pcicb_up; + + if (pcicb) + pcicb = pcicb->pcicb_next; + } + pci_conf_count++; +} + +/*======================================================== ** -** The pci ports can be mapped to any address. -** As default we start at 0x400 +** Subroutines for configuration. ** -**-------------------------------------------------------- +**======================================================== */ -#ifndef PCI_PORT_START -#define PCI_PORT_START 0xbc00 +static void +pci_register_io (struct pcicb * cb, u_int base, u_int limit) +{ +#ifdef PCI_BRIDGE_DEBUG + printf ("register_io: bus=%d base=%x limit=%x\n", + cb->pcicb_bus, base, limit); #endif -static u_short pci_ioaddr = PCI_PORT_START; + if (!cb->pcicb_pfrom || base < cb->pcicb_pfrom) + cb->pcicb_pfrom = base; + if (limit > cb->pcicb_pupto) + cb->pcicb_pupto = limit; -/*-------------------------------------------------------- -** -** The pci device interrupt lines should have been -** assigned by the bios. But if the bios failed to -** to it, we set it. -** -**-------------------------------------------------------- -*/ + /* + ** XXX should set bridge io mapping here + ** but it can be mapped in 4k blocks only, + ** leading to conflicts with isa/eisa .. + */ +} -#ifndef PCI_IRQ -#define PCI_IRQ 0 +static void +pci_register_memory (struct pcicb * cb, u_int base, u_int limit) +{ +#ifdef PCI_BRIDGE_DEBUG + printf ("register_mem: bus=%d base=%x limit=%x\n", + cb->pcicb_bus, base, limit); #endif -static u_long pci_irq = PCI_IRQ; - -/*--------------------------------------------------------- -** -** pci_configure () -** -** Probe all devices on pci bus and attach them. -** -** May be called more than once. -** Any device is attached only once. -** (Attached devices are remembered in pci_seen.) -** Has to take care of mirrored devices, which are -** entailed by incomplete decoding of pci address lines. -** -**--------------------------------------------------------- + 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)); + 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 void not_supported (pcici_t tag, u_long type); +static u_int +pci_memalloc (struct pcicb * cb, u_int addr, u_int size) +{ + u_int result = 0, limit=0, newbase=0; +#ifdef PCI_BRIDGE_DEBUG + printf ("memalloc: bus=%d addr=%x size=%x ..\n", + cb->pcicb_bus, addr, size); +#endif -static unsigned long pci_seen[PCI_MAX_BUS]; + if (!cb) goto done; -static int pci_conf_count; -static int pci_info_done; + if (!cb->pcicb_membase) { + printf ("memalloc: bus%d: membase not set.\n", + cb->pcicb_bus); + goto done; + } -void pci_configure() -{ - u_char device,max_device; - u_short bus; - pcici_t tag; - pcidi_t type, type8, type16; - u_long data; - int unit; - int pci_mechanism; - int pciint; - int irq; - char* name=0; - u_short old_ioaddr=pci_ioaddr; - - int dvi; - struct pci_device *dvp=0; + /* + ** get upper allocation limit + */ + limit = cb->pcicb_memlimit; + if (cb->pcicb_mfrom && cb->pcicb_mfrom <= limit) + limit = cb->pcicb_mfrom-1; -#ifdef __FreeBSD2__ - struct pci_devconf *pdcp; -#endif + /* + ** address fixed, and impossible to allocate ? + */ + if (addr && addr+size-1 > limit) + goto done; /* - ** first check pci bus driver available + ** get possible address */ - if (pcibus_set.ls_length <= 0) - return; + result = addr; + if (!result) result = ((limit + 1) / size - 1) * size; -#define pcibus (*((struct pcibus*) pcibus_set.ls_items[0])) /* - ** check pci bus present + ** if not local available, request from parent. */ - pci_mechanism = pcibus.pb_mode (); - if (!pci_mechanism) return; - max_device = pci_mechanism==1 ? 32 : 16; + 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); +} + +/*======================================================== +** +** pci_bus_config() +** +** Autoconfiguration of one pci bus. +** +**======================================================== +*/ + +static void +pci_bus_config (void) +{ + u_char device; + u_char reg; + pcici_t tag, mtag; + pcidi_t type; + u_long data; + int unit; + int pciint; + int irq; + char* name=0; + + int dvi; + struct pci_device *dvp=0; + + struct pci_devconf *pdcp; /* - ** hello world .. + ** first initialize the bridge (bus controller chip) */ - - pci_pold=pci_paddr; - for (bus=0; bus<pci_bus_max; bus++) { + pci_bridge_config (); + #ifndef PCI_QUIET - printf ("Probing for devices on the %s%d bus:\n", - pcibus.pb_name, bus); - if (!pci_info_done) { + printf ("Probing for devices on the %s%d bus:\n", + pcibus->pb_name, pcicb->pcicb_bus); + if (!pci_info_done) { pci_info_done=1; printf ("\tconfiguration mode %d allows %d devices.\n", - pci_mechanism, max_device); - }; + pci_mechanism, pci_maxdevice); + }; #endif - for (device=0; device<max_device; device ++) { + for (device=0; device<pci_maxdevice; device ++) { - if (pci_seen[bus] & (1ul << device)) + if ((pcicb->pcicb_seen >> device) & 1) continue; - tag = pcibus.pb_tag (bus, device, 0); - type = pcibus.pb_read (tag, PCI_ID_REG); + tag = pcibus->pb_tag (pcicb->pcicb_bus, device, 0); + type = pcibus->pb_read (tag, PCI_ID_REG); if ((!type) || (type==0xfffffffful)) continue; @@ -259,7 +388,7 @@ void pci_configure() ** lookup device in ioconfiguration: */ - for (dvi=0; dvi<pcidevice_set.ls_length; dvi++) { + for (dvi=0; dvi<pcidevice_set.ls_length; dvi++) { dvp = (struct pci_device*) pcidevice_set.ls_items[dvi]; if ((name=(*dvp->pd_probe)(tag, type))) break; @@ -269,36 +398,42 @@ void pci_configure() /* ** check for mirrored devices. */ - if (device & 0x08) { - pcici_t mtag; - mtag = pcibus.pb_tag (bus, device & ~0x08, 0); - type8 = pcibus.pb_read (mtag, PCI_ID_REG); - } else type8 = 0; if (device & 0x10) { - pcici_t mtag; - mtag = pcibus.pb_tag (bus, device & ~0x10, 0); - type16 = pcibus.pb_read (mtag, PCI_ID_REG); - } else type16 = 0; - if ((type==type8) || (type==type16)) { + mtag=pcibus->pb_tag (pcicb->pcicb_bus, + (u_char)(device & ~0x10), 0); + } else if (device & 0x08) { + mtag=pcibus->pb_tag (pcicb->pcicb_bus, + (u_char)(device & ~0x08), 0); + } else goto real_device; + + if (type!=pcibus->pb_read (mtag, PCI_ID_REG)) + goto real_device; + + for (reg=PCI_MAP_REG_START;reg<PCI_MAP_REG_END;reg+=4) + if (pcibus->pb_read(tag,reg)!=pcibus->pb_read(mtag,reg)) + goto real_device; + #ifndef PCI_QUIET - if (dvp==NULL) continue; - printf ("%s? <%s> mirrored on pci%d:%d\n", - dvp->pd_name, name, bus, device); + if (dvp==NULL) continue; + printf ("%s? <%s> mirrored on pci%d:%d\n", + dvp->pd_name, name, pcicb->pcicb_bus, device); #endif - continue; - }; + continue; + + real_device: if (dvp==NULL) { #ifndef PCI_QUIET if (pci_conf_count) continue; - printf("%s%d:%d: ", pcibus.pb_name, bus, device); + printf("%s%d:%d: ", pcibus->pb_name, + pcicb->pcicb_bus, device); not_supported (tag, type); #endif continue; }; - - pci_seen[bus] |= (1ul << device); + + pcicb->pcicb_seen |= (1ul << device); /* ** Get and increment the unit. */ @@ -315,85 +450,102 @@ void pci_configure() ** Announce this device */ - printf ("%s%d <%s>", dvp->pd_name, unit, name); - + 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 = pcibus.pb_read (tag, PCI_INTERRUPT_REG); + data = pcibus->pb_read (tag, PCI_INTERRUPT_REG); pciint = PCI_INTERRUPT_PIN_EXTRACT(data); if (pciint) { - printf (" int %c", 0x60+pciint); - - /* - ** If the interrupt line register is not set, - ** set it now from PCI_IRQ. - */ - - if (!(PCI_INTERRUPT_LINE_EXTRACT(data))) { - - irq = pci_irq & 0x0f; - pci_irq >>= 4; - - data = PCI_INTERRUPT_LINE_INSERT(data, irq); - printf (" (config)"); - pcibus.pb_write (tag, PCI_INTERRUPT_REG, data); - }; + 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 to isa. + ** and we cannot bind the pci interrupt. */ if (irq) - printf (" irq %d", irq); + printf ("%d", irq); else - printf (" not bound"); + printf ("??"); }; + printf (" on pci%d:%d\n", pcicb->pcicb_bus, device); + /* - ** enable memory access + ** Read the current mapping, + ** and update the pcicb fields. */ - data = (pcibus.pb_read (tag, PCI_COMMAND_STATUS_REG) - & 0xffff) | PCI_COMMAND_MEM_ENABLE; + for (reg=PCI_MAP_REG_START;reg<PCI_MAP_REG_END;reg+=4) { + u_int map, addr, size; - pcibus.pb_write (tag, (u_char) PCI_COMMAND_STATUS_REG, data); + data = pci_conf_read(tag, PCI_CLASS_REG); + switch (data & (PCI_CLASS_MASK|PCI_SUBCLASS_MASK)) { + case PCI_CLASS_BRIDGE|PCI_SUBCLASS_BRIDGE_PCI: + continue; + }; - /* - ** show pci slot. - */ + map = pcibus->pb_read (tag, reg); + if (!(map & PCI_MAP_MEMORY_ADDRESS_MASK)) + continue; - printf (" on pci%d:%d\n", bus, device); + pcibus->pb_write (tag, reg, 0xffffffff); + data = pcibus->pb_read (tag, reg); + pcibus->pb_write (tag, reg, map); -#ifdef __FreeBSD2__ + switch (data & 7) { - /* - ** Allocate a devconf structure - */ + default: + continue; + case 1: + case 5: + size = -(data & PCI_MAP_IO_ADDRESS_MASK); + addr = map & PCI_MAP_IO_ADDRESS_MASK; - pdcp = (struct pci_devconf *) - malloc (sizeof (struct pci_devconf),M_DEVBUF,M_WAITOK); + 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; + } else { + pcicb->pcicb_flags |= PCICB_ISAMEM; + }; + break; + }; + if (!bootverbose) + continue; + printf ("\tmapreg[%02x] type=%d addr=%08x size=%04x.\n", + reg, map&7, addr, size); + }; /* - ** Fill in. - ** - ** Sorry, this is not yet complete. + ** Allocate a devconf structure ** We should, and eventually will, set the ** parent pointer to a pci bus devconf structure, ** and arrange to set the state field dynamically. - ** - ** But I'll go to vacation today, and after all, - ** wasn't there a new feature freeze on Oct 1.? */ - pdcp -> pdc_pi.pi_bus = bus; + pdcp = (struct pci_devconf *) + malloc (sizeof (struct pci_devconf),M_DEVBUF,M_WAITOK); + + pdcp -> pdc_pi.pi_bus = pcicb->pcicb_bus; pdcp -> pdc_pi.pi_device = device; pdcp -> pdc_kdc.kdc_name = dvp->pd_name; @@ -408,7 +560,6 @@ void pci_configure() pdcp -> pdc_kdc.kdc_parentdata = &pdcp->pdc_pi; pdcp -> pdc_kdc.kdc_state = DC_UNKNOWN; pdcp -> pdc_kdc.kdc_description = name; - pdcp -> pdc_kdc.kdc_shutdown = dvp->pd_shutdown; /* ** And register this device @@ -416,9 +567,6 @@ void pci_configure() dev_attach (&pdcp->pdc_kdc); -#endif /* __FreeBSD2__ */ - - /* ** attach device ** may produce additional log messages, @@ -426,20 +574,255 @@ void pci_configure() */ (*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 (pci_paddr != pci_pold) - printf ("pci uses physical addresses from 0x%lx to 0x%lx\n", - (u_long)pci_pold, (u_long)pci_paddr); - if (pci_ioaddr != old_ioaddr) - printf ("pci devices use ioports from 0x%x to 0x%x\n", - (unsigned)PCI_PORT_START, (unsigned)pci_ioaddr); + printf ("\tbridge from pci%d to pci%d through %d.\n", + primary, secondary, subordinate); #endif - pci_conf_count++; + /* + ** check for uninitialized bridge. + */ + if (secondary == 0 || secondary < primary || + pcicb->pcicb_bus != primary) + { + printf ("\tINCORRECTLY or NEVER CONFIGURED.\n"); + /* + ** disable this bridge + */ + pcibus->pb_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 = &(*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_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. + */ + u_int reg, data, mask; + + reg = pci_conf_read (tag, + PCI_PCI_BRIDGE_IO_REG); + pci_conf_write(tag, + PCI_PCI_BRIDGE_IO_REG, 0xFFFF); + data = pci_conf_read (tag, + PCI_PCI_BRIDGE_IO_REG); + pci_conf_write(tag, + PCI_PCI_BRIDGE_IO_REG, reg & 0xffff); + + mask = (0xFF00 ^ (data & 0xFF00)) | 0xFF; + + this->pcicb_iobase = + PCI_PPB_IOBASE_EXTRACT (reg); + this->pcicb_iolimit = + PCI_PPB_IOLIMIT_EXTRACT(reg) | mask; + + /* + ** 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. + */ + u_int reg, data, mask; + + /* + ** non prefetchable memory + */ + reg = pci_conf_read (tag, + PCI_PCI_BRIDGE_MEM_REG); + pci_conf_write(tag, + PCI_PCI_BRIDGE_MEM_REG, 0xFFFFFFFF); + data = pci_conf_read (tag, + PCI_PCI_BRIDGE_MEM_REG); + pci_conf_write(tag, + PCI_PCI_BRIDGE_MEM_REG, reg); + mask = (0xFFFF0000 ^ (data & 0xFFFF0000)) + | 0xFFFF; + + this->pcicb_membase = + PCI_PPB_MEMBASE_EXTRACT (reg); + this->pcicb_memlimit = + PCI_PPB_MEMLIMIT_EXTRACT(reg) | mask; + + /* + ** 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); + pci_conf_write(tag, + PCI_PCI_BRIDGE_PMEM_REG, 0xFFFFFFFF); + data = pci_conf_read (tag, + PCI_PCI_BRIDGE_PMEM_REG); + pci_conf_write(tag, + PCI_PCI_BRIDGE_PMEM_REG, reg); + + mask = (0xFFFF0000 ^ (data & 0xFFFF0000)) + | 0xFFFF; + this->pcicb_p_membase= + PCI_PPB_MEMBASE_EXTRACT (reg); + this->pcicb_p_memlimit= + PCI_PPB_MEMLIMIT_EXTRACT(reg) | mask; + + /* + ** 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; + + break; + } + } + +#ifndef PCI_QUIET + if (pcicb->pcicb_mamount) + printf ("%s%d: uses %d bytes of memory from %x upto %x.\n", + pcibus->pb_name, pcicb->pcicb_bus, + pcicb->pcicb_mamount, + pcicb->pcicb_mfrom, pcicb->pcicb_mupto); + if (pcicb->pcicb_pamount) + printf ("%s%d: uses %d bytes of I/O space from %x upto %x.\n", + pcibus->pb_name, pcicb->pcicb_bus, + 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, pcicb->pcicb_bus, + pcicb->pcicb_bfrom, pcicb->pcicb_bupto); +#endif +} + +/*======================================================== +** +** pci_bridge_config() +** +** Configuration of a pci bridge. +** +**======================================================== +*/ + +static void +pci_bridge_config (void) +{ + pcici_t tag; + struct pcicb* parent; + + tag = pcicb->pcicb_bridge; + if (!tag.tag) return; + + if (!pcicb->pcicb_bus) { + u_int data; + /* + ** Get the lowest available bus number. + */ + pcicb->pcicb_bus = ++pcibus0.pcicb_subordinate; + + /* + ** 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; + } } - /*----------------------------------------------------------------- ** ** The following functions are provided for the device driver @@ -461,19 +844,22 @@ void pci_configure() u_long pci_conf_read (pcici_t tag, u_long reg) { - return (pcibus.pb_read (tag, 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); + pcibus->pb_write (tag, reg, data); } - + /*----------------------------------------------------------------------- ** ** 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 ** **----------------------------------------------------------------------- @@ -481,8 +867,8 @@ pci_conf_write (pcici_t tag, u_long reg, u_long data) int pci_map_port (pcici_t tag, u_long reg, u_short* pa) { - u_long data,oldmap; - u_short size, ioaddr; + unsigned data, ioaddr, iosize; + struct pcicb *link = pcicb; /* ** sanity check @@ -490,10 +876,16 @@ int pci_map_port (pcici_t tag, u_long reg, u_short* pa) 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); + (unsigned)reg); return (0); }; + /*if (pcicb->pcicb_flags & PCICB_NOIOSET) { + printf ("pci_map_port failed: pci%d has not been configured for I/O access\n", + pcicb->pcicb_bus); + return (0); + }*/ + /* ** get size and type of port ** @@ -502,88 +894,75 @@ int pci_map_port (pcici_t tag, u_long reg, u_short* pa) ** n-2 bits are hardwired as 0. */ -#ifdef PCI_REMAP - oldmap = 0; -#else - oldmap = pcibus.pb_read (tag, reg) & 0xfffffffc; - if (oldmap==0xfffffffc) oldmap=0; -#endif - pcibus.pb_write (tag, reg, 0xfffffffful); - data = pcibus.pb_read (tag, reg); - - switch (data & 0x03) { - - case PCI_MAP_IO: - break; - - default: /* unknown */ - printf ("pci_map_port failed: bad port type=0x%x\n", - (unsigned) data); + ioaddr = pcibus->pb_read (tag, reg) & PCI_MAP_IO_ADDRESS_MASK; + if (!ioaddr || ioaddr > 0xfffful) { + printf ("pci_map_port failed: not configured by bios.\n"); return (0); }; - /* - ** get the size - */ - - size = -(data & PCI_MAP_IO_ADDRESS_MASK); + pcibus->pb_write (tag, reg, 0xfffffffful); + data = pcibus->pb_read (tag, reg); + pcibus->pb_write (tag, reg, ioaddr); - if (!size) return (0); - - /* - ** align physical address to virtual size, - ** set ioaddr, - ** and don't forget to increment pci_ioaddr - */ - - if (oldmap) { - ioaddr = oldmap; - } else { - if ((data = pci_ioaddr % size)) - pci_ioaddr += size - data; - ioaddr = pci_ioaddr; - pci_ioaddr += size; + 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); + if (ioaddr < pcicb->pcicb_iobase + || ioaddr + iosize > 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); + } #ifndef PCI_QUIET - /* - ** display values. - */ - printf ("\treg%d: ioaddr=0x%x size=0x%x\n", - (unsigned) reg, (unsigned) ioaddr, (unsigned) size); + (unsigned) reg, (unsigned) ioaddr, (unsigned) iosize); #endif - /* - ** set device address + ** 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. */ - pcibus.pb_write (tag, reg, (u_long) ioaddr); - - /* - ** return them to the driver - */ + for (;;) { + data = pcibus->pb_read (tag, PCI_COMMAND_STATUS_REG) & 0xffff; + data |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE; + (void) pcibus->pb_write(tag, PCI_COMMAND_STATUS_REG, data); + if ((link = link->pcicb_up) == NULL) + break; + tag = link->pcicb_bridge; + } - *pa = pci_ioaddr; + *pa = ioaddr; return (1); } - + /*----------------------------------------------------------------------- ** ** Map device into virtual and physical space ** -** PCI-Specification: 6.2.5.1: address maps +** 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) { - u_long data,oldmap,paddr; - vm_size_t vsize; + struct pcicb *link = pcicb; + unsigned data ,paddr; + vm_size_t psize, poffs; vm_offset_t vaddr; - int i; /* ** sanity check @@ -591,7 +970,7 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) 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); + (unsigned)reg); return (0); }; @@ -603,179 +982,90 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) ** n-4 bits are read as 0. */ -#ifdef PCI_REMAP - oldmap = 0; -#else - oldmap = pcibus.pb_read (tag, reg) & 0xfffffff0; - if (oldmap==0xfffffff0) oldmap = 0; -#endif - pcibus.pb_write (tag, reg, 0xfffffffful); - data = pcibus.pb_read (tag, reg); + paddr = pcibus->pb_read (tag, reg) & PCI_MAP_MEMORY_ADDRESS_MASK; + pcibus->pb_write (tag, reg, 0xfffffffful); + data = pcibus->pb_read (tag, reg); + pcibus->pb_write (tag, reg, paddr); - switch (data & 0x0f) { - - case PCI_MAP_MEMORY_TYPE_32BIT: /* 32 bit non cachable */ - break; + /* + ** check the type + */ - default: /* unknown */ + if ((data & PCI_MAP_MEMORY_TYPE_MASK) != PCI_MAP_MEMORY_TYPE_32BIT) { printf ("pci_map_mem failed: bad memory type=0x%x\n", - (unsigned) data); + (unsigned) data); return (0); }; - + /* - ** mask out the type, - ** and round up to a page size + ** get the size. */ - vsize = round_page (-(data & PCI_MAP_MEMORY_ADDRESS_MASK)); - - if (!vsize) return (0); - - if (oldmap) { - paddr = oldmap; - goto domap; - }; + psize = -(data & PCI_MAP_MEMORY_ADDRESS_MASK); -next_try: - if (!pci_paddr) { - /* - ** Get a starting address. - */ - if (pci_pidx >= sizeof(pci_stable)/sizeof(u_long)) { - printf ("pci_map_mem: out of start addresses.\n"); + 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_paddr = pci_stable[pci_pidx++]; - pci_pold = 0; - - if (pci_pidx>1) - printf ("\t(retry at 0x%x)\n", - (unsigned) pci_paddr); + pci_register_memory (pcicb, paddr, paddr+psize-1); }; - /* - ** align physical address to virtual size - */ - - if ((data = pci_paddr % vsize)) - pci_paddr += vsize - data; - if (!pci_pold) - pci_pold = pci_paddr; + 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); + } + pcibus->pb_write (tag, reg, paddr); /* - ** set physical mapping address, - ** and reserve physical address range + ** Truncate paddr to page boundary. + ** (Or does pmap_mapdev the job?) */ - paddr = pci_paddr; - pci_paddr += vsize; - -domap: - vaddr = (vm_offset_t) pmap_mapdev (paddr, vsize); + poffs = paddr - trunc_page (paddr); + vaddr = (vm_offset_t) pmap_mapdev (paddr-poffs, psize+poffs); if (!vaddr) return (0); - + + vaddr += poffs; + #ifndef PCI_QUIET /* ** display values. */ - printf ("\treg%d: virtual=0x%lx physical=0x%lx\n", - (unsigned) reg, (u_long)vaddr, (u_long)paddr); + 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 - /* - ** probe for already mapped device. - */ - - if (!oldmap) for (i=0; i<vsize; i+=4) { - u_long* addr = (u_long*) (vaddr+i); - data = *addr; - if (data != 0xffffffff) { - printf ("\t(possible address conflict: " - "at 0x%x read: 0x%x)\n", - (unsigned) paddr+i, (unsigned) data); - pci_paddr = 0; - goto next_try; - }; - }; - - /* - ** Set device address - */ - - pcibus.pb_write (tag, reg, paddr); - - /* - ** Check if correctly mapped. - ** - ** W A R N I N G - ** - ** This code assumes that the device will NOT return - ** only ones (0xffffffff) from all offsets. + ** 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. */ - if (!oldmap) { - for (i=0; i<vsize; i+=4) { - u_long* addr = (u_long*) (vaddr+i); - data = *addr; - if (data != 0xffffffff) - break; - }; - - if (data==0xffffffff) { - printf ("\t(possible mapping problem: " - "at 0x%x read 0xffffffff)\n", - (unsigned) paddr); - pci_paddr = 0; - goto next_try; - }; - }; - - /* - ** Return addresses to the driver - */ + for (;;) { + data = pcibus->pb_read (tag, PCI_COMMAND_STATUS_REG) & 0xffff; + data |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE; + (void) pcibus->pb_write(tag, PCI_COMMAND_STATUS_REG, data); + if ((link = link->pcicb_up) == NULL) + break; + tag = link->pcicb_bridge; + } *va = vaddr; *pa = paddr; return (1); } - -/*----------------------------------------------------------------------- -** -** Map new pci bus. (XXX under construction) -** -** PCI-Specification: ____________? -** -**----------------------------------------------------------------------- -*/ -int pci_map_bus (pcici_t tag, u_long bus) -{ - if (bus >= PCI_MAX_BUS) { - printf ("pci_map_bus failed: bus number %d too big.\n", - (int) bus); - return (0); - }; - - if (bus >= pci_bus_max) - pci_bus_max = bus + 1; - -#ifndef PCI_QUIET - /* - ** display values. - */ - - printf ("\tmapped pci bus %d.\n", - (int) bus); -#endif - - return (1); -} - /*------------------------------------------------------------ ** ** Interface functions for the devconf module. @@ -795,12 +1085,12 @@ pci_externalize (struct proc *p, struct kern_devconf *kdcp, void *u, size_t l) return ENOMEM; }; - tag = pcibus.pb_tag (pip->pi_bus, pip->pi_device, 0); + tag = pcibus->pb_tag (pip->pi_bus, pip->pi_device, 0); buffer.peb_pci_info = *pip; for (i=0; i<PCI_EXT_CONF_LEN; i++) { - buffer.peb_config[i] = pcibus.pb_read (tag, i*4); + buffer.peb_config[i] = pcibus->pb_read (tag, i*4); }; return copyout(&buffer, u, sizeof buffer); @@ -812,36 +1102,354 @@ pci_internalize (struct proc *p, struct kern_devconf *kdcp, void *u, size_t s) { return EOPNOTSUPP; } - + +/*----------------------------------------------------------------------- +** +** 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 :-) :-) :-) +** +**----------------------------------------------------------------------- +*/ + +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; +} + +static void +pci_int (int irq) +{ + struct pci_int_desc * p; + int c, s; +#ifdef PCI_EDGE_INT + int i, n; +#endif + if (irq<0 || irq >= PCI_MAX_IRQ) { + printf ("pci_int(%d)\n", irq); + return; + }; + +#ifdef PCI_EDGE_INT + for (i=0; i<1000; i++) { + n = 0; +#endif + for (p = pci_int_desc[irq]; p!=NULL; p=p->pcid_next) { + s = splq (*p->pcid_maskptr); + c= (*p->pcid_handler) (p->pcid_argument); + p-> pcid_tally += c; + splx (s); +#ifdef PCI_EDGE_INT + n += c; +#endif +#if 0 + if (c && 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 + }; +#ifdef PCI_EDGE_INT + if (!n) return; + }; + printf ("pci_int(%d): permanent interrupt request.\n", irq); +#endif +} +#endif + /*----------------------------------------------------------------------- ** -** Map pci interrupts to isa interrupts. +** Auxiliary function for interrupt (un)mapping. ** **----------------------------------------------------------------------- */ +static u_int +getirq (pcici_t tag) +{ + u_int irq; + + irq = PCI_INTERRUPT_LINE_EXTRACT( + pcibus->pb_read (tag, PCI_INTERRUPT_REG)); + + if (irq <= 0) { + printf ("\tint line register not set by bios\n"); + return (0); + } + + if (irq >= pcibus->pb_maxirq || irq >= PCI_MAX_IRQ) { + printf ("\tirq %d invalid.\n", irq); + return (0); + } + + return (irq); +} + +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); + + return (pp); +} + +static struct pci_int_desc * +getintdescbymptr (u_int irq, unsigned * mptr) +{ + struct pci_int_desc *p; + + for (p=pci_int_desc[irq];p;p=p->pcid_next) + if (p->pcid_maskptr == mptr) break; + return (p); +} + +/*----------------------------------------------------------------------- +** +** Map pci interrupt. +** +**----------------------------------------------------------------------- +*/ + +static unsigned pci_mask0 = 0; + int pci_map_int (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr) { - int irq, result; + u_int irq; + int result, oldspl; + unsigned mask; + struct pci_int_desc *tail, *mdp=NULL, *new=NULL; - irq = PCI_INTERRUPT_LINE_EXTRACT( - pcibus.pb_read (tag, PCI_INTERRUPT_REG)); + /* + ** Get irq line from configuration space, + ** and check for consistency. + */ - if (irq >= 16 || irq <= 0) { - printf ("pci_map_int failed: no int line set.\n"); + irq = getirq (tag); + if (irq >= PCI_MAX_IRQ) { + printf ("\tillegal irq %d.\n", irq); 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 + (irq, (void(*)()) 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. + */ + + printf ("\tusing shared irq %d.\n", irq); + + /* + ** replace old handler by shared-int-handler. + */ + + result = pcibus->pb_idetach (irq,(void(*)())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; + }; + } +#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 (free) free (new, M_DEVBUF); + if (!mdp) (void) pcibus->pb_imaskexc (irq, maskptr); + splx (oldspl); + return (0); +} + +/*----------------------------------------------------------------------- +** +** Unmap pci interrupt. +** +**----------------------------------------------------------------------- +*/ + +int pci_unmap_int (pcici_t tag) +{ + 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 >= PCI_MAX_IRQ) { + printf ("\tillegal irq %d.\n", irq); + return (0); + }; - result = pcibus.pb_regint (tag, func, arg, maskptr); + /* + ** Search and unlink interrupt descriptor. + */ - if (!result) { - printf ("pci_map_int failed.\n"); + 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,(void(*)())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, + (void(*)()) 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); } - + /*----------------------------------------------------------- ** ** Display of unknown devices. @@ -868,7 +1476,7 @@ static const char *const majclasses[] = { "old", "storage", "network", "display", "multimedia", "memory", "bridge" }; - + void not_supported (pcici_t tag, u_long type) { u_char reg; @@ -892,15 +1500,15 @@ void not_supported (pcici_t tag, u_long type) printf (", device=0x%lx", type >> 16); - data = (pcibus.pb_read(tag, PCI_CLASS_REG) >> 24) & 0xff; + data = (pcibus->pb_read(tag, PCI_CLASS_REG) >> 24) & 0xff; if (data < sizeof(majclasses) / sizeof(majclasses[0])) printf(", class=%s", majclasses[data]); printf (" [not supported]\n"); for (reg=PCI_MAP_REG_START; reg<PCI_MAP_REG_END; reg+=4) { - data = pcibus.pb_read (tag, reg); - if (!data) continue; + data = pcibus->pb_read (tag, reg); + if ((data&~7)==0) continue; switch (data&7) { case 1: @@ -923,89 +1531,4 @@ void not_supported (pcici_t tag, u_long type) } } } - -#ifndef __FreeBSD2__ -/*----------------------------------------------------------- -** -** Mapping of physical to virtual memory -** -**----------------------------------------------------------- -*/ - -extern vm_map_t kernel_map; - -static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize) -{ - vm_offset_t vaddr,value; - u_long result; - - vaddr = vm_map_min (kernel_map); - - result = vm_map_find (kernel_map, (void*)0, (vm_offset_t) 0, - &vaddr, vsize, TRUE); - - if (result != KERN_SUCCESS) { - printf (" vm_map_find failed(%d)\n", result); - return (0); - }; - - /* - ** map physical - */ - - value = vaddr; - while (vsize >= NBPG) { - pmap_enter (pmap_kernel(), vaddr, paddr, - VM_PROT_READ|VM_PROT_WRITE, TRUE); - vaddr += NBPG; - paddr += NBPG; - vsize -= NBPG; - }; - return (value); -} - -/*------------------------------------------------------------ -** -** Emulate the register_intr() function of FreeBSD 2.0 -** -** requires a patch: -** FreeBSD 2.0: "/sys/i386/isa/vector.s" -** 386bsd0.1: "/sys/i386/isa/icu.s" -** 386bsd1.0: Please ask Jesus Monroy Jr. -** -**------------------------------------------------------------ -*/ - -#include <machine/segments.h> - -int pci_int_unit [16]; -inthand2_t* (pci_int_hdlr [16]); -unsigned int * pci_int_mptr [16]; -unsigned int pci_int_count[16]; - -extern void - Vpci3(), Vpci4(), Vpci5(), Vpci6(), Vpci7(), Vpci8(), Vpci9(), - Vpci10(), Vpci11(), Vpci12(), Vpci13(), Vpci14(), Vpci15(); - -static inthand_t* pci_int_glue[16] = { - 0, 0, 0, Vpci3, Vpci4, Vpci5, Vpci6, Vpci7, Vpci8, - Vpci9, Vpci10, Vpci11, Vpci12, Vpci13, Vpci14, Vpci15 }; - -static int -register_intr __P((int intr, int device_id, unsigned int flags, - inthand2_t *handler, unsigned int* mptr, int unit)) -{ - if (intr >= 16 || intr <= 2) - return (EINVAL); - if (pci_int_hdlr [intr]) - return (EBUSY); - - pci_int_hdlr [intr] = handler; - pci_int_unit [intr] = unit; - pci_int_mptr [intr] = mptr; - - setidt(NRSVIDT + intr, pci_int_glue[intr], SDT_SYS386IGT, SEL_KPL); - return (0); -} -#endif /* __FreeBSD2__ */ #endif /* NPCI */ diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h index 1f687f1..a1fbcdf 100644 --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** $Id: pcireg.h,v 1.3 1995/02/02 13:12:18 davidg Exp $ +** $Id: pcireg.h,v 1.4 1995/02/02 22:01:40 se Exp $ ** ** Names for PCI configuration space registers. ** @@ -33,8 +33,7 @@ */ #ifndef __PCI_REG_H__ -#define __PCI_REG_H__ - +#define __PCI_REG_H__ "pl2 95/03/21" /* ** Device identification register; contains a vendor ID and a device ID. @@ -146,6 +145,32 @@ #define PCI_MAP_MEMORY_ADDRESS_MASK 0xfffffff0 #define PCI_MAP_IO_ADDRESS_MASK 0xfffffffc +/* +** PCI-PCI bridge mapping registers +*/ +#define PCI_PCI_BRIDGE_BUS_REG 0x18 +#define PCI_PCI_BRIDGE_IO_REG 0x1c +#define PCI_PCI_BRIDGE_MEM_REG 0x20 +#define PCI_PCI_BRIDGE_PMEM_REG 0x24 + + +#define PCI_SUBORDINATE_BUS_MASK 0x00ff0000 +#define PCI_SECONDARY_BUS_MASK 0x0000ff00 +#define PCI_PRIMARY_BUS_MASK 0x000000ff + +#define PCI_SUBORDINATE_BUS_EXTRACT(x) (((x) > 16) & 0xff) +#define PCI_SECONDARY_BUS_EXTRACT(x) (((x) > 8) & 0xff) +#define PCI_PRIMARY_BUS_EXTRACT(x) (((x) ) & 0xff) + +#define PCI_PRIMARY_BUS_INSERT(x, y) (((x) & ~PCI_PRIMARY_BUS_MASK) | ((y) << 0)) +#define PCI_SECONDARY_BUS_INSERT(x, y) (((x) & ~PCI_SECONDARY_BUS_MASK) | ((y) << 8)) +#define PCI_SUBORDINATE_BUS_INSERT(x, y) (((x) & ~PCI_SUBORDINATE_BUS_MASK) | ((y) << 16)) + +#define PCI_PPB_IOBASE_EXTRACT(x) (((x) << 8) & 0xFF00) +#define PCI_PPB_IOLIMIT_EXTRACT(x) (((x) << 0) & 0xFF00) + +#define PCI_PPB_MEMBASE_EXTRACT(x) (((x) << 16) & 0xFFFF0000) +#define PCI_PPB_MEMLIMIT_EXTRACT(x) (((x) << 0) & 0xFFFF0000) /* ** Interrupt configuration register diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index 4e62dbf..23bc8ae 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -1,10 +1,10 @@ /************************************************************************** ** -** $Id: pcivar.h,v 1.2 1995/02/27 17:17:14 se Exp $ +** $Id: pcivar.h,v 1.3 1995/03/17 04:27:21 davidg Exp $ ** ** Declarations for pci device drivers. ** -** 386bsd / FreeBSD +** FreeBSD ** **------------------------------------------------------------------------- ** @@ -36,10 +36,8 @@ */ #ifndef __PCI_VAR_H__ -#define __PCI_VAR_H__ +#define __PCI_VAR_H__ "pl2 95/03/21" -#define PCIVAR_H_PATCHLEVEL "pl1 95/02/27" - /*----------------------------------------------------------------- ** ** main pci initialization function. @@ -60,13 +58,16 @@ void pci_configure (void); typedef union { u_long cfg1; - struct { + struct { u_char enable; u_char forward; u_short port; } cfg2; + unsigned tag; } pcici_t; +#define sametag(x,y) ((x).tag == (y).tag) + /*----------------------------------------------------------------- ** ** Each pci device has an unique device id. @@ -98,7 +99,7 @@ typedef u_long pcidi_t; u_long pci_conf_read (pcici_t tag, u_long reg ); void pci_conf_write (pcici_t tag, u_long reg, u_long data); - + /*----------------------------------------------------------------- ** ** The pci driver structure. @@ -141,7 +142,11 @@ struct pci_device { */ extern struct linker_set pcidevice_set; - + +extern unsigned pci_max_burst_len; /* log2 of safe burst transfer length */ +extern unsigned pci_mechanism; +extern unsigned pci_maxdevice; + /*----------------------------------------------------------------- ** ** The pci-devconf interface. @@ -150,8 +155,8 @@ extern struct linker_set pcidevice_set; */ struct pci_info { - u_short pi_bus; - u_short pi_device; + u_short pi_bus; + u_short pi_device; }; #define PCI_EXT_CONF_LEN (16) @@ -162,31 +167,18 @@ struct pci_externalize_buffer { u_long peb_config[PCI_EXT_CONF_LEN]; }; - -/*----------------------------------------------------------------- -** -** Register an additional pci bus for probing. -** Called by pci-pci bridge handlers. -** -**----------------------------------------------------------------- -*/ - -int pci_map_bus (pcici_t tag, u_long bus); - /*----------------------------------------------------------------- ** ** Map a pci device to physical and virtual memory. ** -** The va and pa addresses are "in/out" parameters. -** If they are 0 on entry, the function assigns an address. -** -** Entry selects the register in the pci configuration +** Entry selects the register in the pci configuration ** space, which supplies the size of the region, and ** receives the physical address. ** -** If there is any error, a message is written, and -** the function returns with zero. -** Else it returns with a value different to zero. +** In case of success the function sets the addresses +** in *va and *pa, and returns 1. +** In case of errors a message is written, +** and the function returns 0. ** **----------------------------------------------------------------- */ @@ -197,59 +189,65 @@ int pci_map_mem (pcici_t tag, u_long entry, u_long * va, u_long * pa); ** ** Map a pci device to an io port area. ** -** *pa is an "in/out" parameter. -** If it's 0 on entry, the function assigns an port number.. -** ** Entry selects the register in the pci configuration ** space, which supplies the size of the region, and ** receives the port number. ** -** If there is any error, a message is written, and -** the function returns with zero. -** Else it returns with a value different to zero. +** In case of success the function sets the port number in pa, +** and returns 1. +** In case of errors a message is written, +** and the function returns 0. ** **----------------------------------------------------------------- */ -int pci_map_port(pcici_t tag, u_long entry, u_short * pa); - +int pci_map_port (pcici_t tag, u_long entry, u_short * pa); + /*----------------------------------------------------------------- ** -** Map a pci interrupt to an isa irq line, -** and enable the interrupt. +** Map a pci interrupt to an isa irq line, and enable the interrupt. ** -** func is the interrupt handler, arg is the argument -** to this function. +** ----------------- ** -** The maskptr argument should be &bio_imask, -** &net_imask etc. or NULL. +** func is the interrupt handler, arg is the argument +** to the handler (usually a pointer to a softc). ** -** If there is any error, a message is written, and -** the function returns with zero. -** Else it returns with a value different to zero. +** The maskptr argument should be &bio_imask, +** &net_imask etc. or NULL. ** -** A word of caution for FreeBSD 2.0: +** If there is any error, a message is written, and +** the function returns with zero. +** Else it returns with a value different to zero. ** -** We use the register_intr() function. +** ----------------- ** -** The interrupt line of the selected device is included -** into the supplied mask: after the corresponding splXXX -** this drivers interrupts are blocked. +** The irq number is read from the configuration space. +** (Should have been set by the bios). ** -** But in the interrupt handlers startup code ONLY -** the interrupt of the driver is blocked, and NOT -** all interrupts of the spl group. +** Supports multiple handlers per irq (shared interrupts). ** -** It may be required to additional block the group -** interrupts by splXXX() inside the interrupt handler. +** ----------------- ** -** In pre 2.0 kernels we emulate the register_intr -** function. The emulating function blocks all interrupts -** of the group in the interrupt handler prefix code. +** There is code to support shared edge triggered ints. +** This relies on the cooperation of the interrupt handlers: +** they have to return a value <>0 if and only if something +** was done. Beware of the performance penalty. ** **----------------------------------------------------------------- */ +struct pci_int_desc { + struct pci_int_desc * pcid_next; + pcici_t pcid_tag; + int (*pcid_handler)(); + void* pcid_argument; + unsigned * pcid_maskptr; + unsigned pcid_tally; + unsigned pcid_mask; +}; + int pci_map_int (pcici_t tag, int (*func)(), void* arg, unsigned * maskptr); +int pci_unmap_int (pcici_t tag); + #endif |