diff options
Diffstat (limited to 'sys/i386/pci/pci.c')
-rw-r--r-- | sys/i386/pci/pci.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/sys/i386/pci/pci.c b/sys/i386/pci/pci.c new file mode 100644 index 0000000000000..878c97c681653 --- /dev/null +++ b/sys/i386/pci/pci.c @@ -0,0 +1,494 @@ +/************************************************************************** +** +** $Id: pci.c,v 2.0.0.1 94/07/19 19:06:44 wolf Exp $ +** +** General subroutines for the PCI bus on 80*86 systems. +** pci_configure () +** +**------------------------------------------------------------------------- +** +** Copyright (c) 1994 Wolfgang Stanglmeier, Koeln, Germany +** <wolf@dentaro.GUN.de> +** +** This is a beta version - use with care. +** +**------------------------------------------------------------------------- +** +** $Log: pci.c,v $ +** Revision 2.0.0.1 94/07/19 19:06:44 wolf +** New vendor entry: MATROX +** +** Revision 2.0 94/07/10 15:53:29 wolf +** FreeBSD release. +** +** Revision 1.0 94/06/07 20:02:19 wolf +** Beta release. +** +*************************************************************************** +*/ + +#include <pci.h> +#if NPCI > 0 + +/*======================================================== +** +** Configuration +** +**======================================================== +*/ + +/* +** maximum number of devices which share one interrupt line +*/ + +#define PCI_MAX_DPI 1 + +/* +** there may be up to 255 busses on pci. +** don't probe them all :-) +*/ + +#define FIRST_BUS 0 +#define LAST_BUS 0 + +/* +** there may be up to 32 devices per bus. +** do probe them all ;-) +*/ + +#define FIRST_DEVICE 0 +#define LAST_DEVICE 31 + +/*======================================================== +** +** #includes and declarations +** +**======================================================== +*/ + +#include <types.h> +#include <cdefs.h> +#include <errno.h> +#include <param.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> + + +static char ident[] = + "\n$Id: pci.c,v 2.0.0.1 94/07/19 19:06:44 wolf Exp $\n" + "Copyright (c) 1994, Wolfgang Stanglmeier\n"; + +/* +** Function prototypes missing in system headers +*/ + +extern int printf(); +extern int ffs(); +extern pmap_t pmap_kernel(void); + +/* +** function prototypes +*/ + +int pci_map_mem + (pcici_t tag, u_long reg, u_long *va, vm_offset_t *pa); +int pci_map_port (pcici_t tag, u_long reg, u_short* pa); +void pci_configure (void); + +/*======================================================== +** +** Autoconfiguration (of isa bus) (Free/386) +** +**======================================================== +*/ + +/* +** per device (interrupt) data structure. +*/ + +static struct { + u_short number; + u_short isanum; + struct { + int (*proc)(int dev); + dev_t unit; + } vector[PCI_MAX_DPI]; +} pcidata [NPCI]; + +#ifndef __NetBSD__ + +/* +** 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); +} + +/* +** 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. +** +**======================================================== +*/ + + +void pciintr (int unit) +{ + u_short i; + if (unit >= NPCI) return; + +#if defined (GENERICAH) || defined (GENERICBT) || defined (GENERICNCR) \ + || defined (GENERICAHA) || defined (GENERICISA) + for (unit=0; unit < NPCI; unit++) +#endif + + for (i=0; i<pcidata[unit].number; i++) { + (void)(*pcidata[unit].vector[i].proc)(pcidata[unit].vector[i].unit); + }; +} + +#endif /* __NetBSD__ */ + +/*======================================================== +** +** Autoconfiguration of pci devices. +** +** This is reverse to the isa configuration. +** (1) find a pci device. +** (2) look for a driver. +** +**======================================================== +*/ + +/*-------------------------------------------------------- +** +** The pci devices can be mapped to any address. +** As default we start at the last gigabyte. +** +**-------------------------------------------------------- +*/ + +#ifndef PCI_PMEM_START +#define PCI_PMEM_START 0xc0000000 +#endif + +static vm_offset_t pci_paddr = PCI_PMEM_START; + +/*--------------------------------------------------------- +** +** pci_configure () +** +**--------------------------------------------------------- +*/ + +void pci_configure() +{ + u_char bus, device, reg; + pcici_t tag; + pcidi_t type; + u_long data; + int unit; + int intpin; + int pci_mode; + + struct pci_driver *drp; + struct pci_device *dvp; + + /* + ** check pci bus present + */ + + pci_mode = pci_conf_mode (); + if (!pci_mode) return; + + /* + ** hello world .. + */ + + printf ("PCI configuration mode %d.\n", pci_mode); + + for (bus=FIRST_BUS;bus<=LAST_BUS; bus++) + for (device=FIRST_DEVICE; device<=LAST_DEVICE; device ++) { + tag = pcitag (bus, device, 0); + type = pci_conf_read (tag, 0); + + if ((!type) || (type==0xfffffffful)) continue; + printf ("on pci%d:%d ", bus, device); + + /* + ** lookup device in ioconfiguration: + */ + + for (dvp = pci_devtab; drp=dvp->pd_driver; dvp++) { + if (drp->device_id == type) break; + }; + + if (!drp) { + + /* + ** not found + */ + + switch (type & 0xffff) { + + case 0x1002: + printf ("ATI TECHNOLOGIES INC"); + break; + case 0x101A: + printf ("NCR"); + break; + case 0x102B: + printf ("MATROX"); + break; + case 0x1045: + printf ("OPTI"); + break; + case 0x8086: + printf ("INTEL CORPORATION"); + break; + + default: + printf ("vendor=%x", type & 0xffff); + }; + + switch (type) { + + case 0x04848086: + printf (" 82378IB pci-isa bridge"); + break; + case 0x04838086: + printf (" 82424ZX cache dram controller"); + break; + case 0x04828086: + printf (" 82375EB pci-eisa bridge"); + break; + case 0x04A38086: + printf (" 82434LX pci cache memory controller"); + break; + default: + printf (", device=%x", type >> 16); + }; + printf (" [not supported]\n"); + + for (reg=0x10; reg<=0x20; reg+=4) { + data = pci_conf_read (tag, reg); + if (!data) continue; + switch (data&7) { + + case 1: + case 5: + printf (" map(%x): io(%x)\n", + reg, data & ~3); + break; + case 0: + printf (" map(%x): mem32(%x)\n", + reg, data & ~7); + break; + case 2: + printf (" map(%x): mem20(%x)\n", + reg, data & ~7); + break; + case 4: + printf (" map(%x): mem64(%x)\n", + reg, data & ~7); + break; + }; + }; + continue; + }; + + /* + ** found it. + ** probe returns the device unit. + */ + + printf ("<%s>", drp -> vendor); + + unit = (*drp->probe) (tag); + + if (unit<0) { + printf (" probe failed.\n"); + continue; + }; + + /* + ** install interrupts + */ + + intpin = (pci_conf_read (tag, 0x3c) >> 8) & 0xff; + if (intpin) { + printf (" irq %c", 0x60+intpin); + intpin--; + if (intpin < NPCI) { + u_short entry = pcidata[intpin].number; + if (entry < PCI_MAX_DPI) { + pcidata[intpin].vector[entry].proc = drp->intr; + pcidata[intpin].vector[entry].unit = unit; + entry++; + }; + printf (" isa=%d [%d]",pcidata[intpin].isanum, entry); + pcidata[intpin].number=entry; + } else printf (" not installed"); + }; + + /* + ** enable memory access + */ + data = pci_conf_read (tag, 0x04) & 0xffff | 0x0002; + pci_conf_write (tag, (u_char) 0x04, data); + + /* + ** attach device + ** may produce additional log messages, + ** i.e. when installing subdevices. + */ + + printf (" as %s%d\n", drp->name,unit); + (void) (*drp->attach) (tag); + + }; + + printf ("pci uses physical addresses from %x to %x\n", + PCI_PMEM_START, pci_paddr); +} + +/*----------------------------------------------------------------------- +** +** Map device into virtual and physical space +** +**----------------------------------------------------------------------- +*/ + +extern vm_map_t kernel_map; + +int pci_map_port (pcici_t tag, u_long reg, u_short* pa) +{ + /* + ** @MAPIO@ not yet implemented. + */ + return (ENOSYS); +} + + +int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) +{ + u_long data, result; + vm_size_t vsize; + vm_offset_t vaddr; + + /* + ** sanity check + */ + + if (reg <= 0x10 || reg >= 0x20 || (reg & 3)) + return (EINVAL); + + /* + ** get size and type of memory + */ + + pci_conf_write (tag, reg, 0xfffffffful); + data = pci_conf_read (tag, reg); + + switch (data & 0x0f) { + + case 0x0: /* 32 bit non cachable */ + break; + + default: /* unknown */ + return (EINVAL); + }; + + vsize = round_page (-(data & 0xfffffff0)); + + printf (" memory size=0x%x", vsize); + + if (!vsize) return (EINVAL); + + /* + ** try to map device to virtual space + */ + + 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 (ENOMEM); + }; + + /* + ** align physical address to virtual size + */ + + if (data = pci_paddr % vsize) + pci_paddr += vsize - data; + + /* + ** display values. + */ + + printf (" virtual=0x%x physical=0x%x\n", vaddr, pci_paddr); + + *va = vaddr; + *pa = pci_paddr; + + /* + ** set device address + */ + + pci_conf_write (tag, reg, pci_paddr); + + /* + ** map physical + */ + + while (vsize >= NBPG) { + pmap_enter (pmap_kernel(), vaddr, pci_paddr, + VM_PROT_READ|VM_PROT_WRITE, TRUE); + vaddr += NBPG; + pci_paddr += NBPG; + vsize -= NBPG; + }; + + return (0); +} +#endif |