summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorse <se@FreeBSD.org>1995-03-21 23:01:06 +0000
committerse <se@FreeBSD.org>1995-03-21 23:01:06 +0000
commit6e667246cd96976e175eec9db61940c42681a155 (patch)
treeb8fe80494fb6a00dba99beff8518fb3df91f57f1
parent4f9285470770c38619fdabbc8b376026e3ed4767 (diff)
downloadFreeBSD-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>
-rw-r--r--sys/dev/pci/pci.c1537
-rw-r--r--sys/dev/pci/pcireg.h31
-rw-r--r--sys/dev/pci/pcivar.h114
-rw-r--r--sys/pci/pci.c1537
-rw-r--r--sys/pci/pcibus.h57
-rw-r--r--sys/pci/pcireg.h31
-rw-r--r--sys/pci/pcisupport.c169
-rw-r--r--sys/pci/pcivar.h114
8 files changed, 2329 insertions, 1261 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
diff --git a/sys/pci/pci.c b/sys/pci/pci.c
index aa51596..32428c6 100644
--- a/sys/pci/pci.c
+++ b/sys/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/pci/pcibus.h b/sys/pci/pcibus.h
index 1c5671c..842d97c 100644
--- a/sys/pci/pcibus.h
+++ b/sys/pci/pcibus.h
@@ -1,14 +1,14 @@
/**************************************************************************
**
-** $Id: pcibus.h,v 1.2 1994/11/02 23:47:14 se Exp $
+** $Id: pcibus.h,v 1.1 1995/02/01 22:56:47 se Exp $
**
** Declarations for pci bus driver.
**
-** 386bsd / FreeBSD
+** FreeBSD
**
**-------------------------------------------------------------------------
**
-** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved.
+** Copyright (c) 1995 Wolfgang Stanglmeier. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
@@ -36,51 +36,58 @@
*/
#ifndef __PCI_BUS_H__
-#define __PCI_BUS_H__
-
+#define __PCI_BUS_H__ "pl1 95/03/13"
+
/*-----------------------------------------------------------------
**
** The following functions are provided by the pci bios.
** They are used only by the pci configuration.
**
-** pb_mode():
+** pcibus_setup():
** Probes for a pci system.
-** Returns 1 or 2 for pci configuration mechanism.
-** Returns 0 if no pci system.
+** Sets pci_maxdevice and pci_mechanism.
+**
+** pcibus_tag():
+** Creates a handle for pci configuration space access.
+** This handle is given to the read/write functions.
**
-** pb_tag():
-** Gets a handle for accessing the pci configuration
-** space.
-** This handle is given to the mapping functions (see
-** above) or to the read/write functions.
+** pcibus_ftag():
+** Creates a modified handle.
**
-** pb_read():
+** pcibus_read():
** Read a long word from the pci configuration space.
** Requires a tag (from pcitag) and the register
-** number (should be a long word aligned one).
+** number (should be a long word alligned one).
**
-** pb_write():
+** pcibus_write():
** Writes a long word to the pci configuration space.
** Requires a tag (from pcitag), the register number
-** (should be a long word aligned one), and a value.
+** (should be a long word alligned one), and a value.
**
-** pb_regint():
+** pcibus_regirq():
** Register an interupt handler for a pci device.
-** Requires a tag (from pcitag), the handler function
-** and it's argument, and an interupt mask.
+** Requires a tag (from pcitag), the register number
+** (should be a long word alligned one), and a value.
**
**-----------------------------------------------------------------
*/
struct pcibus {
char *pb_name;
- int (*pb_mode ) (void);
- pcici_t (*pb_tag ) (u_char bus, u_char device, u_char func);
- u_long (*pb_read ) (pcici_t tag, u_long reg);
- void (*pb_write ) (pcici_t tag, u_long reg, u_long data);
- int (*pb_regint) (pcici_t tag, int(*func)(), void*arg, unsigned*mp);
+ void (*pb_setup ) (void);
+ pcici_t (*pb_tag ) (u_char bus, u_char device, u_char func);
+ pcici_t (*pb_ftag ) (pcici_t tag, u_char func);
+ u_long (*pb_read ) (pcici_t tag, u_long reg);
+ void (*pb_write ) (pcici_t tag, u_long reg, u_long data);
+ unsigned pb_maxirq;
+ int (*pb_iattach) (int irq, void(*func)(), int arg, unsigned*maskp);
+ int (*pb_idetach) (int irq, void(*func)());
+ int (*pb_imaskinc)(int irq, unsigned *maskptr);
+ int (*pb_imaskexc)(int irq, unsigned *maskptr);
};
+#define PCI_MAX_IRQ (16)
+
/*
** The following structure should be generated by the driver
*/
diff --git a/sys/pci/pcireg.h b/sys/pci/pcireg.h
index 1f687f1..a1fbcdf 100644
--- a/sys/pci/pcireg.h
+++ b/sys/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/pci/pcisupport.c b/sys/pci/pcisupport.c
index eee424a..c88b86a 100644
--- a/sys/pci/pcisupport.c
+++ b/sys/pci/pcisupport.c
@@ -1,20 +1,20 @@
/**************************************************************************
**
-** $Id: pcisupport.c,v 1.11 1995/03/02 23:29:44 se Exp $
+** $Id: pcisupport.c,v 1.12 1995/03/17 04:27:20 davidg Exp $
**
-** Device driver for INTEL PCI chipsets.
+** Device driver for DEC/INTEL PCI chipsets.
**
-** 386bsd / FreeBSD
+** FreeBSD
**
**-------------------------------------------------------------------------
**
-** Written for 386bsd and FreeBSD by
-** wolf@dentaro.gun.de Wolfgang Stanglmeier
+** Written for FreeBSD by
+** wolf@cologne.de Wolfgang Stanglmeier
** se@mi.Uni-Koeln.de Stefan Esser
**
**-------------------------------------------------------------------------
**
-** Copyright (c) 1994 Stefan Esser. All rights reserved.
+** Copyright (c) 1994,1995 Stefan Esser. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
@@ -41,28 +41,19 @@
***************************************************************************
*/
-#define __PCISUPPORT_C_PATCHLEVEL__ "pl2 95/02/27"
-
-/*==========================================================
-**
-** Include files
-**
-**==========================================================
-*/
+#define __PCISUPPORT_C__ "pl4 95/03/21"
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/devconf.h>
+#include <machine/cpu.h>
+
#include <pci/pcivar.h>
#include <pci/pcireg.h>
-extern void printf();
-
-extern int bootverbose;
-
-
/*---------------------------------------------------------
**
** Intel chipsets for 486 / Pentium processor
@@ -74,7 +65,7 @@ static char* chipset_probe (pcici_t tag, pcidi_t type);
static void chipset_attach(pcici_t tag, int unit);
static u_long chipset_count;
-struct pci_device chipset_device = {
+struct pci_device chipset_device = {
"chip",
chipset_probe,
chipset_attach,
@@ -84,8 +75,6 @@ struct pci_device chipset_device = {
DATA_SET (pcidevice_set, chipset_device);
-static char confread(pcici_t config_id, int port);
-
struct condmsg {
unsigned char port;
unsigned char mask;
@@ -94,12 +83,11 @@ struct condmsg {
char *text;
};
-#define M_EQ 0 /* mask and return true if equal */
-#define M_NE 1 /* mask and return true if not equal */
-#define TRUE 2 /* don't read config, always true */
-
-static char* chipset_probe (pcici_t tag, pcidi_t type)
+static char*
+chipset_probe (pcici_t tag, pcidi_t type)
{
+ u_long data;
+
switch (type) {
case 0x04848086:
return ("Intel 82378IB PCI-ISA bridge");
@@ -109,11 +97,28 @@ static char* chipset_probe (pcici_t tag, pcidi_t type)
return ("Intel 82375EB PCI-EISA bridge");
case 0x04a38086:
return ("Intel 82434LX PCI cache memory controller");
+ case 0x00011011:
+ return ("DEC 21050 PCI-PCI bridge");
+ };
+
+ /*
+ ** check classes
+ */
+
+ data = pci_conf_read(tag, PCI_CLASS_REG);
+ switch (data & (PCI_CLASS_MASK|PCI_SUBCLASS_MASK)) {
+
+ case PCI_CLASS_BRIDGE|PCI_SUBCLASS_BRIDGE_PCI:
+ return ("PCI-PCI bridge");
};
return ((char*)0);
}
-struct condmsg conf82424zx[] =
+#define M_EQ 0 /* mask and return true if equal */
+#define M_NE 1 /* mask and return true if not equal */
+#define TRUE 2 /* don't read config, always true */
+
+static const struct condmsg conf82424zx[] =
{
{ 0x00, 0x00, 0x00, TRUE, "\tCPU: " },
{ 0x50, 0xe0, 0x00, M_EQ, "486DX" },
@@ -177,7 +182,7 @@ struct condmsg conf82424zx[] =
{ 0 }
};
-struct condmsg conf82434lx[] =
+static const struct condmsg conf82434lx[] =
{
{ 0x00, 0x00, 0x00, TRUE, "\tCPU: " },
{ 0x50, 0xe3, 0x82, M_EQ, "Pentium, 60MHz" },
@@ -246,7 +251,8 @@ static char confread (pcici_t config_id, int port)
return (l >> ports);
}
-static void writeconfig(pcici_t config_id, struct condmsg *tbl)
+static void
+writeconfig (pcici_t config_id, const struct condmsg *tbl)
{
while (tbl->text) {
int cond = 0;
@@ -268,10 +274,13 @@ static void writeconfig(pcici_t config_id, struct condmsg *tbl)
}
}
-void chipset_attach(pcici_t config_id, int unit)
+static void
+chipset_attach (pcici_t config_id, int unit)
{
- if (bootverbose) {
- switch (pci_conf_read (config_id, 0)) {
+ if (!bootverbose)
+ return;
+
+ switch (pci_conf_read (config_id, PCI_ID_REG)) {
case 0x04838086:
writeconfig (config_id, conf82424zx);
@@ -287,64 +296,23 @@ void chipset_attach(pcici_t config_id, int unit)
pci_conf_read (config_id, 0x54));
break;
};
- }
-}
-
-/*---------------------------------------------------------
-**
-** Catchall driver for pci-pci bridges.
-**
-**---------------------------------------------------------
-*/
-
-static char* ppb_probe (pcici_t tag, pcidi_t type);
-static void ppb_attach(pcici_t tag, int unit);
-static u_long ppb_count;
-
-struct pci_device ppb_device = {
- "ppb",
- ppb_probe,
- ppb_attach,
- &ppb_count,
- NULL
-};
-
-DATA_SET (pcidevice_set, ppb_device);
-
-static char* ppb_probe (pcici_t tag, pcidi_t type)
-{
- int data = pci_conf_read(tag, PCI_CLASS_REG);
-
- if ((data & (PCI_CLASS_MASK|PCI_SUBCLASS_MASK)) ==
- (PCI_CLASS_BRIDGE|PCI_SUBCLASS_BRIDGE_PCI))
- return ("PCI-PCI bridge");
- return ((char*)0);
}
-static void ppb_attach(pcici_t tag, int unit)
-{
- /*
- ** XXX should read bus number from device
- */
- (void) pci_map_bus (tag, 1);
-}
-
/*---------------------------------------------------------
**
** Catchall driver for VGA devices
**
-**
** By Garrett Wollman
** <wollman@halloran-eldar.lcs.mit.edu>
**
**---------------------------------------------------------
*/
-static char* vga_probe (pcici_t tag, pcidi_t type);
-static void vga_attach(pcici_t tag, int unit);
+static char* vga_probe (pcici_t tag, pcidi_t type);
+static void vga_attach (pcici_t tag, int unit);
static u_long vga_count;
-struct pci_device vga_device = {
+struct pci_device vga_device = {
"vga",
vga_probe,
vga_attach,
@@ -354,7 +322,7 @@ struct pci_device vga_device = {
DATA_SET (pcidevice_set, vga_device);
-static char* vga_probe (pcici_t tag, pcidi_t type)
+static char* vga_probe (pcici_t tag, pcidi_t type)
{
int data = pci_conf_read(tag, PCI_CLASS_REG);
@@ -375,11 +343,11 @@ static char* vga_probe (pcici_t tag, pcidi_t type)
return ((char*)0);
}
-static void vga_attach(pcici_t tag, int unit)
+static void vga_attach (pcici_t tag, int unit)
{
/*
-** The assigned adresses may not be remapped,
-** because certain values are assumed by the console driver.
+** If the assigned addresses are remapped,
+** the console driver has to be informed about the new address.
*/
#if 0
vm_offset_t va;
@@ -389,7 +357,7 @@ static void vga_attach(pcici_t tag, int unit)
(void) pci_map_mem (tag, reg, &va, &pa);
#endif
}
-
+
/*---------------------------------------------------------
**
** Hook for loadable pci drivers
@@ -397,11 +365,11 @@ static void vga_attach(pcici_t tag, int unit)
**---------------------------------------------------------
*/
-static char* lkm_probe (pcici_t tag, pcidi_t type);
-static void lkm_attach(pcici_t tag, int unit);
+static char* lkm_probe (pcici_t tag, pcidi_t type);
+static void lkm_attach (pcici_t tag, int unit);
static u_long lkm_count;
-struct pci_device lkm_device = {
+struct pci_device lkm_device = {
"lkm",
lkm_probe,
lkm_attach,
@@ -411,19 +379,20 @@ struct pci_device lkm_device = {
DATA_SET (pcidevice_set, lkm_device);
-static char* lkm_probe (pcici_t tag, pcidi_t type)
+static char*
+lkm_probe (pcici_t tag, pcidi_t type)
{
/*
- ** Should try to load a matching driver.
- ** XXX Not yet!
+ ** Not yet!
+ ** (Should try to load a matching driver)
*/
return ((char*)0);
}
-static void lkm_attach(pcici_t tag, int unit)
-{
-}
-
+static void
+lkm_attach (pcici_t tag, int unit)
+{}
+
/*---------------------------------------------------------
**
** Devices to ignore
@@ -431,11 +400,11 @@ static void lkm_attach(pcici_t tag, int unit)
**---------------------------------------------------------
*/
-static char* ign_probe (pcici_t tag, pcidi_t type);
-static void ign_attach(pcici_t tag, int unit);
+static char* ign_probe (pcici_t tag, pcidi_t type);
+static void ign_attach (pcici_t tag, int unit);
static u_long ign_count;
-struct pci_device ign_device = {
+struct pci_device ign_device = {
NULL,
ign_probe,
ign_attach,
@@ -445,17 +414,17 @@ struct pci_device ign_device = {
DATA_SET (pcidevice_set, ign_device);
-static char* ign_probe (pcici_t tag, pcidi_t type)
+static char*
+ign_probe (pcici_t tag, pcidi_t type)
{
switch (type) {
case 0x10001042ul: /* wd */
return ("");
-
};
return ((char*)0);
}
-static void ign_attach(pcici_t tag, int unit)
-{
-}
+static void
+ign_attach (pcici_t tag, int unit)
+{}
diff --git a/sys/pci/pcivar.h b/sys/pci/pcivar.h
index 4e62dbf..23bc8ae 100644
--- a/sys/pci/pcivar.h
+++ b/sys/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
OpenPOWER on IntegriCloud