/************************************************************************** ** ** $Id: pci.c,v 1.8 1994/10/25 23:09:08 se Exp $ ** ** General subroutines for the PCI bus on 80*86 systems. ** pci_configure () ** ** 386bsd / FreeBSD ** **------------------------------------------------------------------------- ** ** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (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 #if NPCI > 0 #ifndef __FreeBSD2__ #if __FreeBSD__ >= 2 #define __FreeBSD2__ #endif #endif /*======================================================== ** ** #includes and declarations ** **======================================================== */ #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD2__ #include struct pci_devconf { struct kern_devconf pdc_kdc; struct pci_info pdc_pi; }; #endif /* ** Function prototypes missing in system headers */ #ifndef __FreeBSD2__ extern pmap_t pmap_kernel(void); static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize); /* * 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)); /* * 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)); /* ** 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. ** (This is just going to be changed ... :) ** 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]; #endif /* !__FreeBSD2__ */ /*======================================================== ** ** 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; /*-------------------------------------------------------- ** ** 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; #ifdef __FreeBSD2__ static int pci_externalize (struct proc *, struct kern_devconf *, void *, size_t); static int pci_internalize (struct proc *, struct kern_devconf *, void *, size_t); #endif /* __FreeBSD2__ */ void pci_configure() { u_char device,last_device; u_short bus; pcici_t tag; pcidi_t type; u_long data; int unit; int pci_mechanism; int pciint; int irq; char* name=0; vm_offset_t old_addr=pci_paddr; struct pci_driver *drp=0; struct pci_device *dvp; #ifdef __FreeBSD2__ struct pci_devconf *pdcp; #endif /* ** check pci bus present */ pci_mechanism = pci_conf_mode (); if (!pci_mechanism) return; last_device = pci_mechanism==1 ? 31 : 15; /* ** hello world .. */ for (bus=0;buspd_name; dvp++) { drp = dvp->pd_driver; if (!drp) continue; if ((name=(*drp->probe)(tag, type))) break; }; if (!dvp->pd_name) { #ifndef PCI_QUIET if (pci_conf_count) continue; printf("pci%d:%d: ", bus, device); not_supported (tag, type); #endif continue; }; pci_seen[bus] |= (1ul << device); /* ** Get and increment the unit. */ unit = (*drp->count)++; /* ** ignore device ? */ if (!*name) continue; /* ** Announce this device */ printf ("%s%d <%s>", dvp->pd_name, unit, name); /* ** Get the int pin number (pci interrupt number a-d) ** from the pci configuration space. */ data = pci_conf_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)"); pci_conf_write (tag, PCI_INTERRUPT_REG, data); }; 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. */ 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; pci_conf_write (tag, (u_char) PCI_COMMAND_STATUS_REG, data); /* ** show pci slot. */ printf (" on pci%d:%d\n", bus, device); #ifdef __FreeBSD2__ /* ** Allocate a devconf structure */ pdcp = (struct pci_devconf *) malloc (sizeof (struct pci_devconf),M_DEVBUF,M_WAITOK); /* ** Fill in. ** ** Sorry, this is not yet complete. ** 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 -> pdc_pi.pi_device = device; pdcp -> pdc_kdc.kdc_name = dvp->pd_name; pdcp -> pdc_kdc.kdc_unit = unit; pdcp -> pdc_kdc.kdc_md.mddc_devtype = MDDT_PCI; pdcp -> pdc_kdc.kdc_externalize = pci_externalize; pdcp -> pdc_kdc.kdc_internalize = pci_internalize; pdcp -> pdc_kdc.kdc_datalen = PCI_EXTERNAL_LEN; pdcp -> pdc_kdc.kdc_parentdata = &pdcp->pdc_pi; pdcp -> pdc_kdc.kdc_state = DC_UNKNOWN; pdcp -> pdc_kdc.kdc_description = name; /* ** And register this device */ dev_attach (&pdcp->pdc_kdc); #endif /* __FreeBSD2__ */ /* ** attach device ** may produce additional log messages, ** i.e. when installing subdevices. */ (*drp->attach) (tag, unit); }; }; #ifndef PCI_QUIET if (pci_paddr != old_addr) 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++; } /*----------------------------------------------------------------------- ** ** Map device into port space. ** ** PCI-Specification: 6.2.5.1: address maps ** **----------------------------------------------------------------------- */ int pci_map_port (pcici_t tag, u_long reg, u_short* pa) { /* ** @MAPIO@ not yet implemented. */ printf ("pci_map_port failed: not yet implemented\n"); return (0); } /*----------------------------------------------------------------------- ** ** Map device into virtual and physical space ** ** 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; vm_size_t vsize; vm_offset_t vaddr; /* ** sanity check */ 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 ** ** type is in the lowest four bits. ** If device requires 2^n bytes, the next ** n-4 bits are read as 0. */ pci_conf_write (tag, reg, 0xfffffffful); data = pci_conf_read (tag, reg); switch (data & 0x0f) { case PCI_MAP_MEMORY_TYPE_32BIT: /* 32 bit non cachable */ break; default: /* unknown */ printf ("pci_map_mem failed: bad memory type=0x%x\n", (unsigned) data); return (0); }; /* ** mask out the type, ** and round up to a page size */ vsize = round_page (-(data & PCI_MAP_MEMORY_ADDRESS_MASK)); if (!vsize) return (0); /* ** align physical address to virtual size */ if ((data = pci_paddr % vsize)) pci_paddr += vsize - data; vaddr = (vm_offset_t) pmap_mapdev (pci_paddr, vsize); if (!vaddr) return (0); #ifndef PCI_QUIET /* ** display values. */ printf ("\treg%d: virtual=0x%lx physical=0x%lx\n", (unsigned) reg, (u_long)vaddr, (u_long)pci_paddr); #endif /* ** return them to the driver */ *va = vaddr; *pa = pci_paddr; /* ** set device address */ pci_conf_write (tag, reg, pci_paddr); /* ** and don't forget to increment pci_paddr */ pci_paddr += vsize; return (1); } /*------------------------------------------------------------ ** ** Interface functions for the devconf module. ** **------------------------------------------------------------ */ static int pci_externalize (struct proc *p, struct kern_devconf *kdcp, void *u, size_t l) { struct pci_externalize_buffer buffer; struct pci_info * pip = kdcp->kdc_parentdata; pcici_t tag; int i; if (l < sizeof buffer) { return ENOMEM; }; tag = pcitag (pip->pi_bus, pip->pi_device, 0); buffer.peb_pci_info = *pip; for (i=0; i= 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 */ maskptr, /* mask pointer */ (int) arg); /* handler arg */ #ifdef __FreeBSD2__ /* ** XXX See comment at beginning of file. ** ** Have to update all the interrupt masks ... Grrrrr!!! */ { 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; }; #endif INTREN (mask); return (1); } /*----------------------------------------------------------- ** ** Display of unknown devices. ** **----------------------------------------------------------- */ struct vt { u_short ident; char* name; }; static struct vt VendorTable[] = { {0x1002, "ATI TECHNOLOGIES INC"}, {0x1011, "DIGITAL EQUIPMENT CORPORATION"}, {0x101A, "NCR"}, {0x102B, "MATROX"}, {0x1045, "OPTI"}, {0x5333, "S3 INC."}, {0x8086, "INTEL CORPORATION"}, {0,0} }; static const char *const majclasses[] = { "old", "storage", "network", "display", "multimedia", "memory", "bridge" }; void not_supported (pcici_t tag, u_long type) { u_char reg; u_long data; struct vt * vp; /* ** lookup the names. */ for (vp=VendorTable; vp->ident; vp++) if (vp->ident == (type & 0xffff)) break; /* ** and display them. */ if (vp->ident) printf (vp->name); else printf ("vendor=0x%lx", type & 0xffff); printf (", device=0x%lx", type >> 16); data = (pci_conf_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= 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 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 */