diff options
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/pci.c | 550 |
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 */ |