summaryrefslogtreecommitdiffstats
path: root/sys/pci/pci.c
diff options
context:
space:
mode:
authorse <se@FreeBSD.org>1994-10-12 02:33:23 +0000
committerse <se@FreeBSD.org>1994-10-12 02:33:23 +0000
commitbdd76ad243b9f88ad1b56b11f205255f509573f6 (patch)
treedb202e3f99921d3059a152bd1e6ab5f4e2429fbe /sys/pci/pci.c
parent58cfb27b7ad4e22d870e30893ca490da961495c0 (diff)
downloadFreeBSD-src-bdd76ad243b9f88ad1b56b11f205255f509573f6.zip
FreeBSD-src-bdd76ad243b9f88ad1b56b11f205255f509573f6.tar.gz
Submitted by: Wolfgang Stanglmeier <wolf@dentaro.GUN.de>
Bug fixed, that caused system hang on first interrupt on some motherboards. New version of PCI bus configuration code, now supports dynamic interrupt configuration (using BIOS supplied values). NCR SCSI and DEC Ethernet driver patched to use this feature. *** Remove PCI IRQ specifications from your kernel config file ! ***
Diffstat (limited to 'sys/pci/pci.c')
-rw-r--r--sys/pci/pci.c550
1 files changed, 301 insertions, 249 deletions
diff --git a/sys/pci/pci.c b/sys/pci/pci.c
index c34523d..396b96f 100644
--- a/sys/pci/pci.c
+++ b/sys/pci/pci.c
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** $Id: pci.c,v 1.5 1994/09/28 16:34:07 se Exp $
+** $Id: pci.c,v 2.12 94/10/11 22:20:37 wolf Oct11 $
**
** General subroutines for the PCI bus on 80*86 systems.
** pci_configure ()
@@ -33,27 +33,17 @@
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
-**-------------------------------------------------------------------------
+***************************************************************************
*/
#include <pci.h>
#if NPCI > 0
-/*========================================================
-**
-** Configuration
-**
-**========================================================
-*/
-
-/*
-** maximum number of devices which share one interrupt line
-*/
-
-#ifndef PCI_MAX_DPI
-#define PCI_MAX_DPI (4)
-#endif /*PCI_MAX_DPI*/
-
+#ifndef __FreeBSD2__
+#if __FreeBSD__ >= 2
+#define __FreeBSD2__
+#endif
+#endif
/*========================================================
**
@@ -64,101 +54,54 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/malloc.h>
#include <sys/errno.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
-#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
-
-#include <i386/pci/pci.h>
-#include <i386/pci/pci_device.h>
-#include <i386/pci/pcibios.h>
+#include <i386/isa/icu.h>
+#include <i386/pci/pcireg.h>
/*
** Function prototypes missing in system headers
*/
-#if ! (__FreeBSD__ >= 2)
+#ifndef __FreeBSD2__
extern pmap_t pmap_kernel(void);
static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize);
-#endif
-
-
-/*========================================================
-**
-** Autoconfiguration (of isa bus)
-**
-**========================================================
-*/
/*
-** per slot data structure for passing interupts..
-*/
-
-static struct {
- u_short number;
- u_short isanum;
- struct {
- int (*proc)(int dev);
- dev_t unit;
- } vector[PCI_MAX_DPI];
-} pcidata [NPCI];
+ * Type of the first (asm) part of an interrupt handler.
+ */
+typedef void inthand_t __P((u_int cs, u_int ef, u_int esp, u_int ss));
/*
-** check device ready
-*/
-static int pciprobe (struct isa_device *dev)
-{
- if (dev->id_unit >= NPCI)
- return (0);
-
- if (!pci_conf_mode())
- return (0);
-
- return (-1);
-}
+ * Usual type of the second (C) part of an interrupt handler. Some bogus
+ * ones need the arg to be the interrupt frame (and not a copy of it, which
+ * is all that is possible in C).
+ */
+typedef void inthand2_t __P((int unit));
/*
-** initialize the driver structure
-*/
-static int pciattach (struct isa_device *isdp)
-{
- pcidata[isdp->id_unit].number = 0;
- pcidata[isdp->id_unit].isanum = ffs(isdp->id_irq)-1;
- return (1);
-}
-
-/*
-** ISA driver structure
-*/
-
-struct isa_driver pcidriver = {
- pciprobe,
- pciattach,
- "pci"
-};
-
-/*========================================================
-**
-** Interrupt forward from isa to pci devices.
-**
-**========================================================
+** XXX @FreeBSD2@
+**
+** Unfortunately, the mptr argument is _no_ pointer in 2.0 FreeBSD.
+** We would prefer a pointer because it enables us to install
+** new interrupt handlers at any time.
+** In 2.0 FreeBSD later installed interrupt handlers may change
+** the xyz_imask, but this would not be recognized by handlers
+** which are installed before.
*/
+static int
+register_intr __P((int intr, int device_id, unsigned int flags,
+ inthand2_t *handler, unsigned int * mptr, int unit));
+extern unsigned intr_mask[ICU_LEN];
-void pciintr (int unit)
-{
- u_short i;
- if (unit >= NPCI) return;
- for (i=0; i<pcidata[unit].number; i++) {
- (void)(*pcidata[unit].vector[i].proc)
- (pcidata[unit].vector[i].unit);
- };
-}
-
+#endif /* !__FreeBSD2__ */
/*========================================================
**
@@ -183,52 +126,82 @@ void pciintr (int unit)
#define PCI_PMEM_START 0xc0000000
#endif
-static vm_offset_t pci_paddr = PCI_PMEM_START;
+static vm_offset_t pci_paddr = PCI_PMEM_START;
+/*--------------------------------------------------------
+**
+** The pci device interrupt lines should have been
+** assigned by the bios. But if the bios failed to
+** to it, we set it.
+**
+**--------------------------------------------------------
+*/
+
+#ifndef PCI_IRQ
+#define PCI_IRQ 0
+#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.)
+**
**---------------------------------------------------------
*/
static void not_supported (pcici_t tag, u_long type);
+static unsigned long pci_seen[NPCI];
+
+static int pci_conf_count;
+
void pci_configure()
{
u_char device,last_device;
- u_short bus,last_bus;
+ u_short bus;
pcici_t tag;
pcidi_t type;
u_long data;
int unit;
- int intpin;
- int isanum;
- int pci_mode;
+ int pci_mechanism;
+ int pciint;
+ int irq;
+ char* name=0;
+ int newdev=0;
- struct pci_driver *drp;
+ struct pci_driver *drp=0;
struct pci_device *dvp;
/*
** check pci bus present
*/
- pci_mode = pci_conf_mode ();
- if (!pci_mode) return;
- last_bus = pci_last_bus ();
- last_device = pci_mode==1 ? 31 : 15;
-
+ pci_mechanism = pci_conf_mode ();
+ if (!pci_mechanism) return;
+ last_device = pci_mechanism==1 ? 31 : 15;
+
/*
** hello world ..
*/
+
+ for (bus=0;bus<NPCI;bus++) {
#ifndef PCI_QUIET
- printf ("pci*: mode=%d, scanning bus 0..%d, device 0..%d.\n",
- pci_mode, last_bus, last_device);
+ printf ("pci%d: scanning device 0..%d, mechanism=%d.\n",
+ bus, last_device, pci_mechanism);
#endif
-
- for (bus=0;bus<=last_bus; bus++)
for (device=0; device<=last_device; device ++) {
+
+ if (pci_seen[bus] & (1ul << device))
+ continue;
+
tag = pcitag (bus, device, 0);
type = pci_conf_read (tag, PCI_ID_REG);
@@ -238,137 +211,90 @@ void pci_configure()
** lookup device in ioconfiguration:
*/
- for (dvp = pci_devtab; dvp->pd_device_id; dvp++) {
- if (dvp->pd_device_id == type) break;
+ for (dvp = pci_devtab; dvp->pd_name; dvp++) {
+ drp = dvp->pd_driver;
+ if (!drp)
+ continue;
+ if ((name=(*drp->probe)(tag, type)))
+ break;
};
- drp = dvp->pd_driver;
-
- if (!dvp->pd_device_id) {
- /*
- ** not found
- ** try to dig out some information.
- **
- ** By Garrett Wollman
- ** <wollman@halloran-eldar.lcs.mit.edu>
- */
-
- int data = pci_conf_read(tag, PCI_CLASS_REG);
- vm_offset_t va;
- vm_offset_t pa;
- int reg;
-
- switch (data & PCI_CLASS_MASK) {
-
- case PCI_CLASS_PREHISTORIC:
- if ((data & PCI_SUBCLASS_MASK)
- != PCI_SUBCLASS_PREHISTORIC_VGA)
- break;
-
- case PCI_CLASS_DISPLAY:
- for (reg = PCI_MAP_REG_START;
- reg < PCI_MAP_REG_END;
- reg += 4) {
- data = pci_map_mem(tag, reg, &va, &pa);
- if (data == 0)
- printf (
- "pci%d:%d: mapped VGA-like device at physaddr 0x%lx\n",
- bus, device, (u_long)pa);
-
- }
- continue;
- };
+ if (!dvp->pd_name) {
#ifndef PCI_QUIET
+ if (pci_conf_count)
+ continue;
printf("pci%d:%d: ", bus, device);
not_supported (tag, type);
#endif
- };
-
- if (!drp) {
- if(dvp->pd_flags & PDF_LOADABLE) {
- printf("%s: loadable device on pci%d:%d\n",
- dvp->pd_name, bus, device);
- }
continue;
- }
+ };
+ pci_seen[bus] |= (1ul << device);
/*
- ** found it.
- ** probe returns the device unit.
+ ** Get and increment the unit.
*/
- unit = (*drp->probe) (tag);
+ unit = (*drp->count)++;
- if (unit<0) {
- printf ("%s <%s>: probe failed on pci%d:%d\n",
- dvp->pd_name, drp->name, bus, device);
- continue;
- };
+ /*
+ ** ignore device ?
+ */
+
+ if (!*name) continue;
+
+ /*
+ ** Announce this device
+ */
- printf ("%s%d <%s>", dvp->pd_name, unit, drp->name);
+ newdev++;
+ printf ("%s%d <%s>", dvp->pd_name, unit, name);
/*
- ** install interrupts
+ ** Get the int pin number (pci interrupt number a-d)
+ ** from the pci configuration space.
*/
data = pci_conf_read (tag, PCI_INTERRUPT_REG);
- intpin = PCI_INTERRUPT_PIN_EXTRACT(data);
- if (intpin) {
- int idx=NPCI;
+ pciint = PCI_INTERRUPT_PIN_EXTRACT(data);
- /*
- ** Usage of int line register (if set by bios)
- ** Matt Thomas <thomas@lkg.dec.com>
- */
+ if (pciint) {
- isanum = PCI_INTERRUPT_LINE_EXTRACT(data);
- if (isanum) {
-
- /*
- ** @INT@ FIXME!!!
- **
- ** Should try to use "register_interupt"
- ** at this point.
- */
-
- printf (" line=%d", isanum);
- for (idx = 0; idx < NPCI; idx++) {
- if (pcidata[idx].isanum == isanum)
- break;
- };
- };
+ printf (" int %c", 0x60+pciint);
/*
- ** Or take the device number as index ...
+ ** If the interrupt line register is not set,
+ ** set it now from PCI_IRQ.
*/
- if (idx >= NPCI) idx = device;
+ if (!(PCI_INTERRUPT_LINE_EXTRACT(data))) {
+
+ irq = pci_irq & 0x0f;
+ pci_irq >>= 4;
+
+ data = PCI_INTERRUPT_LINE_INSERT(data, irq);
+ printf (" (config)");
+ pci_conf_write (tag, PCI_INTERRUPT_REG, data);
+ };
+
+ irq = PCI_INTERRUPT_LINE_EXTRACT(data);
/*
- ** And install the interrupt.
+ ** If it's zero, the isa irq number is unknown,
+ ** and we cannot bind the pci interrupt to isa.
*/
- if (idx<NPCI) {
- u_short entry = pcidata[idx].number;
- printf (" irq %c", 0x60+intpin);
- if (entry < PCI_MAX_DPI) {
- pcidata[idx].vector[entry].proc = drp->intr;
- pcidata[idx].vector[entry].unit = unit;
- entry++;
- };
- printf (" isa=%d [%d]",pcidata[idx].isanum, entry);
- pcidata[idx].number=entry;
- } else {
- printf (" no int");
- };
+ if (irq)
+ printf (" irq %d", irq);
+ else
+ printf (" not bound");
};
-
+
/*
** enable memory access
*/
- data = pci_conf_read (tag, PCI_COMMAND_STATUS_REG)
- & 0xffff | PCI_COMMAND_MEM_ENABLE;
+ data = (pci_conf_read (tag, PCI_COMMAND_STATUS_REG)
+ & 0xffff) | PCI_COMMAND_MEM_ENABLE;
pci_conf_write (tag, (u_char) PCI_COMMAND_STATUS_REG, data);
@@ -380,13 +306,16 @@ void pci_configure()
printf (" on pci%d:%d\n", bus, device);
- (void) (*drp->attach) (tag);
- }
+ (*drp->attach) (tag, unit);
+ };
+ };
#ifndef PCI_QUIET
- printf ("pci uses physical addresses from 0x%lx to 0x%lx\n",
+ if (newdev)
+ printf ("pci uses physical addresses from 0x%lx to 0x%lx\n",
(u_long)PCI_PMEM_START, (u_long)pci_paddr);
#endif
+ pci_conf_count++;
}
/*-----------------------------------------------------------------------
@@ -403,7 +332,8 @@ int pci_map_port (pcici_t tag, u_long reg, u_short* pa)
/*
** @MAPIO@ not yet implemented.
*/
- return (ENOSYS);
+ printf ("pci_map_port failed: not yet implemented\n");
+ return (0);
}
/*-----------------------------------------------------------------------
@@ -425,8 +355,11 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa)
** sanity check
*/
- if (reg <= PCI_MAP_REG_START || reg >= PCI_MAP_REG_END || (reg & 3))
- return (EINVAL);
+ if (reg < PCI_MAP_REG_START || reg >= PCI_MAP_REG_END || (reg & 3)) {
+ printf ("pci_map_mem failed: bad register=0x%x\n",
+ (unsigned)reg);
+ return (0);
+ };
/*
** get size and type of memory
@@ -445,7 +378,9 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa)
break;
default: /* unknown */
- return (EINVAL);
+ printf ("pci_map_mem failed: bad memory type=0x%x\n",
+ (unsigned) data);
+ return (0);
};
/*
@@ -455,18 +390,19 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa)
vsize = round_page (-(data & PCI_MAP_MEMORY_ADDRESS_MASK));
- if (!vsize) return (EINVAL);
-
+ if (!vsize) return (0);
+
/*
** align physical address to virtual size
*/
- if (data = pci_paddr % vsize)
+ if ((data = pci_paddr % vsize))
pci_paddr += vsize - data;
- vaddr = pmap_mapdev (pci_paddr, vsize);
+ vaddr = (vm_offset_t) pmap_mapdev (pci_paddr, vsize);
+
- if (!vaddr) return (EINVAL);
+ if (!vaddr) return (0);
#ifndef PCI_QUIET
/*
@@ -474,7 +410,7 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa)
*/
printf ("\treg%d: virtual=0x%lx physical=0x%lx\n",
- reg, (u_long)vaddr, (u_long)pci_paddr);
+ (unsigned) reg, (u_long)vaddr, (u_long)pci_paddr);
#endif
/*
@@ -496,50 +432,77 @@ int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa)
pci_paddr += vsize;
- return (0);
+ return (1);
}
-/*-----------------------------------------------------------
+/*-----------------------------------------------------------------------
**
-** Mapping of physical to virtual memory
+** Map pci interrupts to isa interrupts.
**
-**-----------------------------------------------------------
+**-----------------------------------------------------------------------
*/
-#if ! (__FreeBSD__ >= 2)
+static unsigned int pci_int_mask [16];
-extern vm_map_t kernel_map;
-
-static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize)
+int pci_map_int (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr)
{
- vm_offset_t vaddr,value;
- u_long result;
+ int irq;
+ unsigned mask;
- vaddr = vm_map_min (kernel_map);
+ irq = PCI_INTERRUPT_LINE_EXTRACT(
+ pci_conf_read (tag, PCI_INTERRUPT_REG));
- 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);
+ if (irq >= 16 || irq <= 0) {
+ printf ("pci_map_int failed: no int line set.\n");
return (0);
- };
+ }
+
+ mask = 1ul << irq;
+
+ if (!maskptr)
+ maskptr = &pci_int_mask[irq];
+
+ INTRMASK (*maskptr, mask);
+ register_intr(
+ irq, /* isa irq */
+ 0, /* deviced?? */
+ 0, /* flags? */
+ (inthand2_t*) func, /* handler */
+#ifdef __FreeBSD2__
+ *maskptr, /* mask */
+#else
+ maskptr, /* mask pointer */
+#endif
+ (int) arg); /* handler arg */
+
+#ifdef __FreeBSD2__
/*
- ** map physical
+ ** XXX See comment at beginning of file.
+ **
+ ** Have to update all the interrupt masks ... Grrrrr!!!
*/
-
- value = vaddr;
- while (vsize >= NBPG) {
- pmap_enter (pmap_kernel(), vaddr, paddr,
- VM_PROT_READ|VM_PROT_WRITE, TRUE);
- vaddr += NBPG;
- paddr += NBPG;
- vsize -= NBPG;
+ {
+ unsigned * mp = &intr_mask[0];
+ /*
+ ** update the isa interrupt masks.
+ */
+ for (mp=&intr_mask[0]; mp<&intr_mask[ICU_LEN]; mp++)
+ if (*mp & *maskptr)
+ *mp |= mask;
+ /*
+ ** update the pci interrupt masks.
+ */
+ for (mp=&pci_int_mask[0]; mp<&pci_int_mask[16]; mp++)
+ if (*mp & *maskptr)
+ *mp |= mask;
};
- return (value);
-}
#endif
+
+ INTREN (mask);
+
+ return (1);
+}
/*-----------------------------------------------------------
**
@@ -554,7 +517,7 @@ struct vt {
static struct vt VendorTable[] = {
{0x1002, "ATI TECHNOLOGIES INC"},
- {0x1011, "DIGITAL EQUIPMENT CORP."},
+ {0x1011, "DIGITAL EQUIPMENT CORPORATION"},
{0x101A, "NCR"},
{0x102B, "MATROX"},
{0x1045, "OPTI"},
@@ -604,18 +567,107 @@ void not_supported (pcici_t tag, u_long type)
case 1:
case 5:
- printf (" map(%lx): io(%lx)\n", reg, data & ~3);
+ printf (" map(%x): io(%lx)\n",
+ reg, data & ~3);
break;
case 0:
- printf (" map(%lx): mem32(%lx)\n", reg, data & ~7);
+ printf (" map(%x): mem32(%lx)\n",
+ reg, data & ~7);
break;
case 2:
- printf (" map(%lx): mem20(%lx)\n", reg, data & ~7);
+ printf (" map(%x): mem20(%lx)\n",
+ reg, data & ~7);
break;
case 4:
- printf (" map(%lx): mem64(%lx)\n", reg, data & ~7);
+ printf (" map(%x): mem64(%lx)\n",
+ reg, data & ~7);
break;
}
}
}
-#endif
+
+#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 */
OpenPOWER on IntegriCloud