aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/pci.c550
1 files changed, 301 insertions, 249 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index c34523d46ba6..396b96f8ab98 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/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 */