pci.c revision 47625
162587Sitojun/* 278064Sume * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 362587Sitojun * All rights reserved. 453541Sshin * 553541Sshin * Redistribution and use in source and binary forms, with or without 653541Sshin * modification, are permitted provided that the following conditions 753541Sshin * are met: 853541Sshin * 1. Redistributions of source code must retain the above copyright 953541Sshin * notice unmodified, this list of conditions, and the following 1053541Sshin * disclaimer. 1153541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1253541Sshin * notice, this list of conditions and the following disclaimer in the 1353541Sshin * documentation and/or other materials provided with the distribution. 1453541Sshin * 1553541Sshin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1653541Sshin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1753541Sshin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1853541Sshin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1953541Sshin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2053541Sshin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2153541Sshin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2253541Sshin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2353541Sshin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2453541Sshin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2553541Sshin * 2653541Sshin * $Id: pci.c,v 1.105 1999/05/30 10:54:31 dfr Exp $ 2753541Sshin * 2853541Sshin */ 2953541Sshin 3053541Sshin#include "opt_bus.h" 3153541Sshin 3253541Sshin#include "pci.h" 3353541Sshin#if NPCI > 0 3453541Sshin 3553541Sshin#include "opt_devfs.h" 3653541Sshin#include "opt_simos.h" 3753541Sshin 3853541Sshin#include <sys/param.h> 3953541Sshin#include <sys/systm.h> 4053541Sshin#include <sys/malloc.h> 4153541Sshin#include <sys/module.h> 4253541Sshin#include <sys/fcntl.h> 4353541Sshin#include <sys/conf.h> 4453541Sshin#include <sys/kernel.h> 4553541Sshin#include <sys/queue.h> 4653541Sshin#include <sys/types.h> 4753541Sshin#include <sys/buf.h> 4853541Sshin#ifdef DEVFS 4953541Sshin#include <sys/devfsext.h> 5053541Sshin#endif /* DEVFS */ 5153541Sshin 5253541Sshin#include <vm/vm.h> 5353541Sshin#include <vm/pmap.h> 5453541Sshin#include <vm/vm_extern.h> 5553541Sshin 5653541Sshin#include <sys/bus.h> 5753541Sshin#include <machine/bus.h> 5853541Sshin#include <sys/rman.h> 5953541Sshin#include <machine/resource.h> 6053541Sshin 6153541Sshin#include <pci/pcireg.h> 6253541Sshin#include <pci/pcivar.h> 6353541Sshin#include <pci/pci_ioctl.h> 6453541Sshin 6553541Sshin#ifdef APIC_IO 6653541Sshin#include <machine/smp.h> 6753541Sshin#endif /* APIC_IO */ 6862587Sitojun 6962587Sitojunstatic STAILQ_HEAD(devlist, pci_devinfo) pci_devq; 7055009Sshinu_int32_t pci_numdevs = 0; 7153541Sshinstatic u_int32_t pci_generation = 0; 7253541Sshin 7395759Stanimura/* return base address of memory or port map */ 7495759Stanimura 7595759Stanimurastatic int 7678064Sumepci_mapbase(unsigned mapreg) 7753541Sshin{ 7853541Sshin int mask = 0x03; 7995759Stanimura if ((mapreg & 0x01) == 0) 8053541Sshin mask = 0x0f; 8153541Sshin return (mapreg & ~mask); 8295759Stanimura} 8395759Stanimura 8495759Stanimura/* return map type of memory or port map */ 8553541Sshin 8653541Sshinstatic int 8753541Sshinpci_maptype(unsigned mapreg) 8853541Sshin{ 8953541Sshin static u_int8_t maptype[0x10] = { 9095759Stanimura PCI_MAPMEM, PCI_MAPPORT, 9153541Sshin PCI_MAPMEM, 0, 9253541Sshin PCI_MAPMEM, PCI_MAPPORT, 9395759Stanimura 0, 0, 9453541Sshin PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 9562587Sitojun PCI_MAPMEM|PCI_MAPMEMP, 0, 9695759Stanimura PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 9795759Stanimura 0, 0, 9895759Stanimura }; 9995759Stanimura 10053541Sshin return maptype[mapreg & 0x0f]; 10153541Sshin} 10253541Sshin 10353541Sshin/* return log2 of map size decoded for memory or port map */ 10453541Sshin 10553541Sshinstatic int 10653541Sshinpci_mapsize(unsigned testval) 10755009Sshin{ 10853541Sshin int ln2size; 10953541Sshin 11053541Sshin testval = pci_mapbase(testval); 11178064Sume ln2size = 0; 11278064Sume if (testval != 0) { 11378064Sume while ((testval & 1) == 0) 11478064Sume { 11578064Sume ln2size++; 11678064Sume testval >>= 1; 11778064Sume } 11878064Sume } 11978064Sume return (ln2size); 12078064Sume} 12178064Sume 12278064Sume/* return log2 of address range supported by map register */ 12378064Sume 12478064Sumestatic int 12578064Sumepci_maprange(unsigned mapreg) 12678064Sume{ 12778064Sume int ln2range = 0; 12878064Sume switch (mapreg & 0x07) { 12978064Sume case 0x00: 13078064Sume case 0x01: 13178064Sume case 0x05: 13278064Sume ln2range = 32; 13378064Sume break; 13478064Sume case 0x02: 13578064Sume ln2range = 20; 13678064Sume break; 13778064Sume case 0x04: 13878064Sume ln2range = 64; 13978064Sume break; 14062587Sitojun } 14153541Sshin return (ln2range); 14262587Sitojun} 14353541Sshin 14462587Sitojun/* extract map parameters into newly allocated array of pcimap structures */ 14562587Sitojun 14662587Sitojunstatic pcimap * 14778064Sumepci_readmaps(pcicfgregs *cfg, int maxmaps) 14862587Sitojun{ 14953541Sshin int i, j = 0; 15062587Sitojun pcimap *map; 15162587Sitojun int map64 = 0; 15262587Sitojun int reg = PCIR_MAPS; 15362587Sitojun 15462587Sitojun for (i = 0; i < maxmaps; i++) { 15578064Sume int reg = PCIR_MAPS + i*4; 15678064Sume u_int32_t base; 15762587Sitojun u_int32_t ln2range; 15862587Sitojun 15962587Sitojun base = pci_cfgread(cfg, reg, 4); 16062587Sitojun ln2range = pci_maprange(base); 16162587Sitojun 16278064Sume if (base == 0 || ln2range == 0 || base == 0xffffffff) 16362587Sitojun continue; /* skip invalid entry */ 16462587Sitojun else { 16578064Sume j++; 16653541Sshin if (ln2range > 32) { 16753541Sshin i++; 16862587Sitojun j++; 16953541Sshin } 17053541Sshin } 17178064Sume } 17253541Sshin 17353541Sshin map = malloc(j * sizeof (pcimap), M_DEVBUF, M_WAITOK); 17453541Sshin if (map != NULL) { 17553541Sshin bzero(map, sizeof(pcimap) * j); 17653541Sshin cfg->nummaps = j; 17753541Sshin 17862587Sitojun for (i = 0, j = 0; i < maxmaps; i++, reg += 4) { 17962587Sitojun u_int32_t base; 18062587Sitojun u_int32_t testval; 18162587Sitojun 18262587Sitojun base = pci_cfgread(cfg, reg, 4); 18378064Sume 18462587Sitojun if (map64 == 0) { 18562587Sitojun if (base == 0 || base == 0xffffffff) 18662587Sitojun continue; /* skip invalid entry */ 18762587Sitojun pci_cfgwrite(cfg, reg, 0xffffffff, 4); 18862587Sitojun testval = pci_cfgread(cfg, reg, 4); 18962587Sitojun pci_cfgwrite(cfg, reg, base, 4); 19062587Sitojun 19162587Sitojun map[j].reg = reg; 19262587Sitojun map[j].base = pci_mapbase(base); 19362587Sitojun map[j].type = pci_maptype(base); 19462587Sitojun map[j].ln2size = pci_mapsize(testval); 19562587Sitojun map[j].ln2range = pci_maprange(testval); 19662587Sitojun map64 = map[j].ln2range == 64; 19762587Sitojun } else { 19862587Sitojun /* only fill in base, other fields are 0 */ 19962587Sitojun map[j].base = base; 20062587Sitojun map64 = 0; 20162587Sitojun } 20262587Sitojun#ifdef __alpha__ 20362587Sitojun /* 20462587Sitojun * XXX: encode hose number in the base addr, 20562587Sitojun * This will go away once the bus_space functions 20662587Sitojun * can deal with multiple hoses 20778064Sume */ 20862587Sitojun 20962587Sitojun if(cfg->hose){ 21062587Sitojun if(map[j].base & 0x80000000){ 21162587Sitojun printf("base addr = 0x%x\n", map[j].base); 21262587Sitojun printf("hacked addr = 0x%x\n", 21362587Sitojun map[j].base | (cfg->hose << 31)); 21462587Sitojun 21562587Sitojun panic("hose encoding hack would clobber base addr"); 21662587Sitojun } 21778064Sume if(cfg->hose > 1 ) 21862587Sitojun panic("only one hose supported!"); 21962587Sitojun map[j].base |= (cfg->hose << 31); 22062587Sitojun } 22162587Sitojun#endif 22262587Sitojun j++; 22362587Sitojun } 22462587Sitojun } 22562587Sitojun return (map); 22662587Sitojun} 22762587Sitojun 22862587Sitojun/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ 22962587Sitojun 23062587Sitojunstatic void 23162587Sitojunpci_fixancient(pcicfgregs *cfg) 23262587Sitojun{ 23362587Sitojun if (cfg->hdrtype != 0) 23462587Sitojun return; 23562587Sitojun 23653541Sshin /* PCI to PCI bridges use header type 1 */ 23753541Sshin if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) 23853541Sshin cfg->hdrtype = 1; 23953541Sshin} 24053541Sshin 24153541Sshin/* read config data specific to header type 1 device (PCI to PCI bridge) */ 24253541Sshin 24353541Sshinstatic void * 24453541Sshinpci_readppb(pcicfgregs *cfg) 24553541Sshin{ 24662587Sitojun pcih1cfgregs *p; 24753541Sshin 24862587Sitojun p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); 24953541Sshin if (p == NULL) 25053541Sshin return (NULL); 25153541Sshin 25262587Sitojun bzero(p, sizeof *p); 25362587Sitojun 25462587Sitojun p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); 25562587Sitojun p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); 25662587Sitojun 25762587Sitojun p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); 25853541Sshin 25962587Sitojun p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), 26062587Sitojun pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); 26153541Sshin p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), 26262587Sitojun pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); 26362587Sitojun 26462587Sitojun p->membase = PCI_PPBMEMBASE (0, 26562587Sitojun pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); 26662587Sitojun p->memlimit = PCI_PPBMEMLIMIT (0, 26762587Sitojun pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); 26862587Sitojun 26962587Sitojun p->pmembase = PCI_PPBMEMBASE ( 27062587Sitojun (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), 27153541Sshin pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); 27253541Sshin 27353541Sshin p->pmemlimit = PCI_PPBMEMLIMIT ( 27453541Sshin (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), 27553541Sshin pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); 27653541Sshin return (p); 27753541Sshin} 27853541Sshin 27953541Sshin/* read config data specific to header type 2 device (PCI to CardBus bridge) */ 28053541Sshin 28153541Sshinstatic void * 28253541Sshinpci_readpcb(pcicfgregs *cfg) 28353541Sshin{ 28453541Sshin pcih2cfgregs *p; 28553541Sshin 28653541Sshin p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); 28753541Sshin if (p == NULL) 28853541Sshin return (NULL); 28953541Sshin 29053541Sshin bzero(p, sizeof *p); 29162587Sitojun 29262587Sitojun p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); 29353541Sshin p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); 29462587Sitojun 29562587Sitojun p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); 29662587Sitojun 29753541Sshin p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); 29853541Sshin p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); 29962587Sitojun p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); 30062587Sitojun p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); 30162587Sitojun 30262587Sitojun p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); 30362587Sitojun p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); 30462587Sitojun p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); 30562587Sitojun p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); 30662587Sitojun 30762587Sitojun p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); 30862587Sitojun return p; 30962587Sitojun} 31062587Sitojun 31162587Sitojun/* extract header type specific config data */ 31253541Sshin 31362587Sitojunstatic void 31462587Sitojunpci_hdrtypedata(pcicfgregs *cfg) 31562587Sitojun{ 31653541Sshin switch (cfg->hdrtype) { 31762587Sitojun case 0: 31862587Sitojun cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); 31962587Sitojun cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); 32062587Sitojun cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_0); 32153541Sshin break; 32262587Sitojun case 1: 32362587Sitojun cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); 32453541Sshin cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); 32553541Sshin cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); 32653541Sshin cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); 32753541Sshin cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_1); 32853541Sshin cfg->hdrspec = pci_readppb(cfg); 32953541Sshin break; 33053541Sshin case 2: 33153541Sshin cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); 33253541Sshin cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); 33353541Sshin cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); 33453541Sshin cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); 33553541Sshin cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_2); 33653541Sshin cfg->hdrspec = pci_readpcb(cfg); 33753541Sshin break; 33853541Sshin } 33953541Sshin} 34053541Sshin 34162587Sitojun/* read configuration header into pcicfgrect structure */ 34262587Sitojun 34362587Sitojunstatic struct pci_devinfo * 34462587Sitojunpci_readcfg(pcicfgregs *probe) 34553541Sshin{ 34678064Sume pcicfgregs *cfg = NULL; 34753541Sshin struct pci_devinfo *devlist_entry; 34853541Sshin struct devlist *devlist_head; 34953541Sshin 35053541Sshin devlist_head = &pci_devq; 35153541Sshin 35253541Sshin devlist_entry = NULL; 35353541Sshin 35453541Sshin if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { 35553541Sshin devlist_entry = malloc(sizeof(struct pci_devinfo), 35653541Sshin M_DEVBUF, M_WAITOK); 35753541Sshin if (devlist_entry == NULL) 35853541Sshin return (NULL); 35953541Sshin bzero(devlist_entry, sizeof *devlist_entry); 36053541Sshin 36153541Sshin cfg = &devlist_entry->cfg; 36253541Sshin 36353541Sshin cfg->hose = probe->hose; 36478064Sume cfg->bus = probe->bus; 36578064Sume cfg->slot = probe->slot; 36678064Sume cfg->func = probe->func; 36778064Sume cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); 36878064Sume cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); 36978064Sume cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); 37078064Sume cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); 37178064Sume cfg->baseclass = pci_cfgread(cfg, PCIR_CLASS, 1); 37278064Sume cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); 37353541Sshin cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); 37495023Ssuz cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); 37562587Sitojun cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); 37662587Sitojun cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); 37762587Sitojun cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); 37862587Sitojun cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); 37962587Sitojun cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1); 38062587Sitojun#ifdef __alpha__ 38162587Sitojun alpha_platform_assign_pciintr(cfg); 38262587Sitojun#endif 38353541Sshin 38453541Sshin#ifdef APIC_IO 38553541Sshin if (cfg->intpin != 0) { 38653541Sshin int airq; 38753541Sshin 38853541Sshin airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); 38953541Sshin if (airq >= 0) { 39053541Sshin /* PCI specific entry found in MP table */ 39153541Sshin if (airq != cfg->intline) { 39253541Sshin undirect_pci_irq(cfg->intline); 39353541Sshin cfg->intline = airq; 39453541Sshin } 39553541Sshin } else { 39653541Sshin /* 39753541Sshin * PCI interrupts might be redirected to the 39853541Sshin * ISA bus according to some MP tables. Use the 39953541Sshin * same methods as used by the ISA devices 40062587Sitojun * devices to find the proper IOAPIC int pin. 40153541Sshin */ 40295023Ssuz airq = isa_apic_irq(cfg->intline); 40362587Sitojun if ((airq >= 0) && (airq != cfg->intline)) { 40453541Sshin /* XXX: undirect_pci_irq() ? */ 40553541Sshin undirect_isa_irq(cfg->intline); 40653541Sshin cfg->intline = airq; 40753541Sshin } 40853541Sshin } 40953541Sshin } 41053541Sshin#endif /* APIC_IO */ 41153541Sshin 41253541Sshin cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); 41353541Sshin cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); 41453541Sshin 41553541Sshin cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; 41653541Sshin cfg->hdrtype &= ~PCIM_MFDEV; 41753541Sshin 41853541Sshin pci_fixancient(cfg); 41962587Sitojun pci_hdrtypedata(cfg); 42053541Sshin 42162587Sitojun STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); 42262587Sitojun 42362587Sitojun devlist_entry->conf.pc_sel.pc_bus = cfg->bus; 42462587Sitojun devlist_entry->conf.pc_sel.pc_dev = cfg->slot; 42562587Sitojun devlist_entry->conf.pc_sel.pc_func = cfg->func; 42662587Sitojun devlist_entry->conf.pc_hdr = cfg->hdrtype; 42762587Sitojun 42853541Sshin devlist_entry->conf.pc_subvendor = cfg->subvendor; 42953541Sshin devlist_entry->conf.pc_subdevice = cfg->subdevice; 43053541Sshin devlist_entry->conf.pc_vendor = cfg->vendor; 43178064Sume devlist_entry->conf.pc_device = cfg->device; 43253541Sshin 43378064Sume devlist_entry->conf.pc_class = cfg->baseclass; 43453541Sshin devlist_entry->conf.pc_subclass = cfg->subclass; 43553541Sshin devlist_entry->conf.pc_progif = cfg->progif; 43653541Sshin devlist_entry->conf.pc_revid = cfg->revid; 43753541Sshin 43883934Sbrooks pci_numdevs++; 43953541Sshin pci_generation++; 44053541Sshin } 44153541Sshin return (devlist_entry); 44253541Sshin} 44353541Sshin 44453541Sshin#if 0 44553541Sshin/* free pcicfgregs structure and all depending data structures */ 44653541Sshin 44753541Sshinstatic int 44853541Sshinpci_freecfg(struct pci_devinfo *dinfo) 44953541Sshin{ 45053541Sshin struct devlist *devlist_head; 45153541Sshin 45253541Sshin devlist_head = &pci_devq; 45353541Sshin 45453541Sshin if (dinfo->cfg.hdrspec != NULL) 45553541Sshin free(dinfo->cfg.hdrspec, M_DEVBUF); 45653541Sshin if (dinfo->cfg.map != NULL) 45753541Sshin free(dinfo->cfg.map, M_DEVBUF); 45853541Sshin /* XXX this hasn't been tested */ 45953541Sshin STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); 46053541Sshin free(dinfo, M_DEVBUF); 46153541Sshin 46253541Sshin /* increment the generation count */ 46353541Sshin pci_generation++; 46453541Sshin 46553541Sshin /* we're losing one device */ 46653541Sshin pci_numdevs--; 46753541Sshin return (0); 46862587Sitojun} 46962587Sitojun#endif 47053541Sshin 47162587Sitojun 47253541Sshin/* 47362587Sitojun * This is the user interface to PCI configuration space. 47453541Sshin */ 47553541Sshin 47653541Sshinstatic int 47762587Sitojunpci_open(dev_t dev, int oflags, int devtype, struct proc *p) 47862587Sitojun{ 47962587Sitojun if ((oflags & FWRITE) && securelevel > 0) { 48062587Sitojun return EPERM; 48162587Sitojun } 48262587Sitojun return 0; 48353541Sshin} 48453541Sshin 48553541Sshinstatic int 48653541Sshinpci_close(dev_t dev, int flag, int devtype, struct proc *p) 48753541Sshin{ 48853541Sshin return 0; 48953541Sshin} 49053541Sshin 49153541Sshin/* 49253541Sshin * Match a single pci_conf structure against an array of pci_match_conf 49353541Sshin * structures. The first argument, 'matches', is an array of num_matches 49453541Sshin * pci_match_conf structures. match_buf is a pointer to the pci_conf 49553541Sshin * structure that will be compared to every entry in the matches array. 49653541Sshin * This function returns 1 on failure, 0 on success. 49753541Sshin */ 49853541Sshinstatic int 49962587Sitojunpci_conf_match(struct pci_match_conf *matches, int num_matches, 50062587Sitojun struct pci_conf *match_buf) 50162587Sitojun{ 50262587Sitojun int i; 50353541Sshin 50453541Sshin if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 50553541Sshin return(1); 50653541Sshin 50753541Sshin for (i = 0; i < num_matches; i++) { 50853541Sshin /* 50953541Sshin * I'm not sure why someone would do this...but... 51053541Sshin */ 51153541Sshin if (matches[i].flags == PCI_GETCONF_NO_MATCH) 51253541Sshin continue; 51353541Sshin 51453541Sshin /* 51553541Sshin * Look at each of the match flags. If it's set, do the 51653541Sshin * comparison. If the comparison fails, we don't have a 51753541Sshin * match, go on to the next item if there is one. 51853541Sshin */ 51953541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 52053541Sshin && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 52153541Sshin continue; 52253541Sshin 52353541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 52453541Sshin && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 52553541Sshin continue; 52653541Sshin 52753541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 52853541Sshin && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 52953541Sshin continue; 53053541Sshin 53153541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 53253541Sshin && (match_buf->pc_vendor != matches[i].pc_vendor)) 53353541Sshin continue; 53453541Sshin 53553541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 53653541Sshin && (match_buf->pc_device != matches[i].pc_device)) 53753541Sshin continue; 53853541Sshin 53953541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 54053541Sshin && (match_buf->pc_class != matches[i].pc_class)) 54153541Sshin continue; 54253541Sshin 54362587Sitojun if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 54462587Sitojun && (match_buf->pd_unit != matches[i].pd_unit)) 54553541Sshin continue; 54662587Sitojun 54753541Sshin if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 54853541Sshin && (strncmp(matches[i].pd_name, match_buf->pd_name, 54953541Sshin sizeof(match_buf->pd_name)) != 0)) 55053541Sshin continue; 55153541Sshin 55262587Sitojun return(0); 55362587Sitojun } 55462587Sitojun 55562587Sitojun return(1); 55662587Sitojun} 55753541Sshin 55862587Sitojunstatic int 55962587Sitojunpci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 56062587Sitojun{ 56162587Sitojun struct pci_io *io; 56262587Sitojun const char *name; 56362587Sitojun int error; 56462587Sitojun 56553541Sshin if (!(flag & FWRITE)) 56653541Sshin return EPERM; 56753541Sshin 56853541Sshin 56953541Sshin switch(cmd) { 57053541Sshin case PCIOCGETCONF: 57153541Sshin { 57253541Sshin struct pci_devinfo *dinfo; 57353541Sshin struct pci_conf_io *cio; 57453541Sshin struct devlist *devlist_head; 57553541Sshin struct pci_match_conf *pattern_buf; 57653541Sshin int num_patterns; 57753541Sshin size_t iolen; 57862587Sitojun int ionum, i; 57962587Sitojun 58062587Sitojun cio = (struct pci_conf_io *)data; 58153541Sshin 58262587Sitojun num_patterns = 0; 58362587Sitojun dinfo = NULL; 58453541Sshin 58562587Sitojun /* 58662587Sitojun * Hopefully the user won't pass in a null pointer, but it 58762587Sitojun * can't hurt to check. 58862587Sitojun */ 58953541Sshin if (cio == NULL) { 59053541Sshin error = EINVAL; 59153541Sshin break; 59253541Sshin } 59353541Sshin 59453541Sshin /* 59553541Sshin * If the user specified an offset into the device list, 59653541Sshin * but the list has changed since they last called this 59753541Sshin * ioctl, tell them that the list has changed. They will 59853541Sshin * have to get the list from the beginning. 59953541Sshin */ 60053541Sshin if ((cio->offset != 0) 60153541Sshin && (cio->generation != pci_generation)){ 60253541Sshin cio->num_matches = 0; 60353541Sshin cio->status = PCI_GETCONF_LIST_CHANGED; 60453541Sshin error = 0; 60553541Sshin break; 60653541Sshin } 60753541Sshin 60853541Sshin /* 60996116Sume * Check to see whether the user has asked for an offset 61096116Sume * past the end of our list. 61196116Sume */ 61253541Sshin if (cio->offset >= pci_numdevs) { 61396116Sume cio->num_matches = 0; 61453541Sshin cio->status = PCI_GETCONF_LAST_DEVICE; 61553541Sshin error = 0; 61653541Sshin break; 61762587Sitojun } 61862587Sitojun 61962587Sitojun /* get the head of the device queue */ 62062587Sitojun devlist_head = &pci_devq; 62162587Sitojun 62262587Sitojun /* 62362587Sitojun * Determine how much room we have for pci_conf structures. 62453541Sshin * Round the user's buffer size down to the nearest 62553541Sshin * multiple of sizeof(struct pci_conf) in case the user 62653541Sshin * didn't specify a multiple of that size. 62796116Sume */ 62853541Sshin iolen = min(cio->match_buf_len - 62996116Sume (cio->match_buf_len % sizeof(struct pci_conf)), 63053541Sshin pci_numdevs * sizeof(struct pci_conf)); 63153541Sshin 63253541Sshin /* 63396116Sume * Since we know that iolen is a multiple of the size of 63496116Sume * the pciconf union, it's okay to do this. 63595023Ssuz */ 63653541Sshin ionum = iolen / sizeof(struct pci_conf); 63762587Sitojun 63853541Sshin /* 63953541Sshin * If this test is true, the user wants the pci_conf 64053541Sshin * structures returned to match the supplied entries. 64153541Sshin */ 64253541Sshin if ((cio->num_patterns > 0) 64362587Sitojun && (cio->pat_buf_len > 0)) { 64462587Sitojun /* 64562587Sitojun * pat_buf_len needs to be: 64653541Sshin * num_patterns * sizeof(struct pci_match_conf) 64753541Sshin * While it is certainly possible the user just 64862587Sitojun * allocated a large buffer, but set the number of 64953541Sshin * matches correctly, it is far more likely that 65053541Sshin * their kernel doesn't match the userland utility 65153541Sshin * they're using. It's also possible that the user 65253541Sshin * forgot to initialize some variables. Yes, this 65353541Sshin * may be overly picky, but I hazard to guess that 65453541Sshin * it's far more likely to just catch folks that 65562587Sitojun * updated their kernel but not their userland. 65653541Sshin */ 65753541Sshin if ((cio->num_patterns * 65862587Sitojun sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 65962587Sitojun /* The user made a mistake, return an error*/ 66062587Sitojun cio->status = PCI_GETCONF_ERROR; 66162587Sitojun printf("pci_ioctl: pat_buf_len %d != " 66262587Sitojun "num_patterns (%d) * sizeof(struct " 66353541Sshin "pci_match_conf) (%d)\npci_ioctl: " 66453541Sshin "pat_buf_len should be = %d\n", 66553541Sshin cio->pat_buf_len, cio->num_patterns, 66662587Sitojun (int)sizeof(struct pci_match_conf), 66753541Sshin (int)sizeof(struct pci_match_conf) * 66878064Sume cio->num_patterns); 66978064Sume printf("pci_ioctl: do your headers match your " 67078064Sume "kernel?\n"); 67162587Sitojun cio->num_matches = 0; 67262587Sitojun error = EINVAL; 67362587Sitojun break; 67462587Sitojun } 67562587Sitojun 67662587Sitojun /* 67762587Sitojun * Check the user's buffer to make sure it's readable. 67853541Sshin */ 67962587Sitojun if ((error = useracc((caddr_t)cio->patterns, 68062587Sitojun cio->pat_buf_len, B_READ)) != 1){ 68162587Sitojun printf("pci_ioctl: pattern buffer %p, " 68262587Sitojun "length %u isn't user accessible for" 68362587Sitojun " READ\n", cio->patterns, 68462587Sitojun cio->pat_buf_len); 68562587Sitojun error = EACCES; 68653541Sshin break; 68753541Sshin } 68853541Sshin /* 68953541Sshin * Allocate a buffer to hold the patterns. 69078064Sume */ 69162587Sitojun pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 69262587Sitojun M_WAITOK); 69362587Sitojun error = copyin(cio->patterns, pattern_buf, 69462587Sitojun cio->pat_buf_len); 69553541Sshin if (error != 0) 69653541Sshin break; 69753541Sshin num_patterns = cio->num_patterns; 69853541Sshin 69953541Sshin } else if ((cio->num_patterns > 0) 70053541Sshin || (cio->pat_buf_len > 0)) { 70153541Sshin /* 70253541Sshin * The user made a mistake, spit out an error. 70353541Sshin */ 70495023Ssuz cio->status = PCI_GETCONF_ERROR; 70553541Sshin cio->num_matches = 0; 70695023Ssuz printf("pci_ioctl: invalid GETCONF arguments\n"); 70753541Sshin error = EINVAL; 70862587Sitojun break; 70953541Sshin } else 71053541Sshin pattern_buf = NULL; 71153541Sshin 71253541Sshin /* 71353541Sshin * Make sure we can write to the match buffer. 71453541Sshin */ 71553541Sshin if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len, 71653541Sshin B_WRITE)) != 1) { 71753541Sshin printf("pci_ioctl: match buffer %p, length %u " 71853541Sshin "isn't user accessible for WRITE\n", 71953541Sshin cio->matches, cio->match_buf_len); 72053541Sshin error = EACCES; 72153541Sshin break; 72253541Sshin } 72353541Sshin 72453541Sshin /* 72553541Sshin * Go through the list of devices and copy out the devices 72653541Sshin * that match the user's criteria. 72753541Sshin */ 72853541Sshin for (cio->num_matches = 0, error = 0, i = 0, 72953541Sshin dinfo = STAILQ_FIRST(devlist_head); 73053541Sshin (dinfo != NULL) && (cio->num_matches < ionum) 73153541Sshin && (error == 0) && (i < pci_numdevs); 73262587Sitojun dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 73362587Sitojun 73462587Sitojun if (i < cio->offset) 73562587Sitojun continue; 73662587Sitojun 73762587Sitojun /* Populate pd_name and pd_unit */ 73862587Sitojun name = NULL; 73953541Sshin if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 74053541Sshin name = device_get_name(dinfo->cfg.dev); 74153541Sshin if (name) { 74253541Sshin strncpy(dinfo->conf.pd_name, name, 74353541Sshin sizeof(dinfo->conf.pd_name)); 74453541Sshin dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 74553541Sshin dinfo->conf.pd_unit = 74653541Sshin device_get_unit(dinfo->cfg.dev); 74753541Sshin } 74862587Sitojun 74962587Sitojun if ((pattern_buf == NULL) || 75062587Sitojun (pci_conf_match(pattern_buf, num_patterns, 75162587Sitojun &dinfo->conf) == 0)) { 75262587Sitojun 75362587Sitojun /* 75462587Sitojun * If we've filled up the user's buffer, 75553541Sshin * break out at this point. Since we've 75653541Sshin * got a match here, we'll pick right back 75753541Sshin * up at the matching entry. We can also 75853541Sshin * tell the user that there are more matches 75953541Sshin * left. 76053541Sshin */ 76153541Sshin if (cio->num_matches >= ionum) 76253541Sshin break; 76353541Sshin 76462587Sitojun error = copyout(&dinfo->conf, 76562587Sitojun &cio->matches[cio->num_matches], 76662587Sitojun sizeof(struct pci_conf)); 76762587Sitojun cio->num_matches++; 76862587Sitojun } 76962587Sitojun } 77062587Sitojun 77153541Sshin /* 77253541Sshin * Set the pointer into the list, so if the user is getting 77353541Sshin * n records at a time, where n < pci_numdevs, 77453541Sshin */ 77553541Sshin cio->offset = i; 77653541Sshin 77753541Sshin /* 77853541Sshin * Set the generation, the user will need this if they make 77953541Sshin * another ioctl call with offset != 0. 78062587Sitojun */ 78162587Sitojun cio->generation = pci_generation; 78262587Sitojun 78362587Sitojun /* 78462587Sitojun * If this is the last device, inform the user so he won't 78562587Sitojun * bother asking for more devices. If dinfo isn't NULL, we 78662587Sitojun * know that there are more matches in the list because of 78753541Sshin * the way the traversal is done. 78853541Sshin */ 78953541Sshin if (dinfo == NULL) 79053541Sshin cio->status = PCI_GETCONF_LAST_DEVICE; 79153541Sshin else 79253541Sshin cio->status = PCI_GETCONF_MORE_DEVS; 79353541Sshin 79453541Sshin if (pattern_buf != NULL) 79553541Sshin free(pattern_buf, M_TEMP); 79662587Sitojun 79762587Sitojun break; 79862587Sitojun } 79962587Sitojun case PCIOCREAD: 80062587Sitojun io = (struct pci_io *)data; 80162587Sitojun switch(io->pi_width) { 80262587Sitojun pcicfgregs probe; 80353541Sshin case 4: 80453541Sshin case 2: 80553541Sshin case 1: 80653541Sshin probe.bus = io->pi_sel.pc_bus; 80753541Sshin probe.slot = io->pi_sel.pc_dev; 80853541Sshin probe.func = io->pi_sel.pc_func; 80953541Sshin io->pi_data = pci_cfgread(&probe, 81053541Sshin io->pi_reg, io->pi_width); 81153541Sshin error = 0; 81253541Sshin break; 81353541Sshin default: 81453541Sshin error = ENODEV; 81578064Sume break; 81678064Sume } 81778064Sume break; 81878064Sume 81978064Sume case PCIOCWRITE: 82053541Sshin io = (struct pci_io *)data; 82153541Sshin switch(io->pi_width) { 82253541Sshin pcicfgregs probe; 82353541Sshin case 4: 82453541Sshin case 2: 82553541Sshin case 1: 82653541Sshin probe.bus = io->pi_sel.pc_bus; 82753541Sshin probe.slot = io->pi_sel.pc_dev; 82853541Sshin probe.func = io->pi_sel.pc_func; 82978064Sume pci_cfgwrite(&probe, 83078064Sume io->pi_reg, io->pi_data, io->pi_width); 83178064Sume error = 0; 83253541Sshin break; 83378064Sume default: 83478064Sume error = ENODEV; 83578064Sume break; 83678064Sume } 83778064Sume break; 83878064Sume 83978064Sume default: 84078064Sume error = ENOTTY; 84178064Sume break; 84278064Sume } 84378064Sume 84478064Sume return (error); 84578064Sume} 84678064Sume 84778064Sume#define PCI_CDEV 78 84878064Sume 84978064Sumestatic struct cdevsw pcicdev = { 85078064Sume /* open */ pci_open, 85178064Sume /* close */ pci_close, 85278064Sume /* read */ noread, 85378064Sume /* write */ nowrite, 85478064Sume /* ioctl */ pci_ioctl, 85578064Sume /* stop */ nostop, 85678064Sume /* reset */ noreset, 85778064Sume /* devtotty */ nodevtotty, 85878064Sume /* poll */ nopoll, 85978064Sume /* mmap */ nommap, 86078064Sume /* strategy */ nostrategy, 86178064Sume /* name */ "pci", 86278064Sume /* parms */ noparms, 86378064Sume /* maj */ PCI_CDEV, 86478064Sume /* dump */ nodump, 86578064Sume /* psize */ nopsize, 86678064Sume /* flags */ 0, 86778064Sume /* maxio */ 0, 86862587Sitojun /* bmaj */ -1 86978064Sume}; 87078064Sume 87178064Sume#ifdef DEVFS 87278064Sumestatic void *pci_devfs_token; 87362587Sitojun#endif 87478064Sume 87578064Sumestatic void 87678064Sumepci_cdevinit(void *dummy) 87778064Sume{ 87878064Sume dev_t dev; 87978064Sume 88062587Sitojun dev = makedev(PCI_CDEV, 0); 88178064Sume cdevsw_add(&dev, &pcicdev, NULL); 88253541Sshin#ifdef DEVFS 88378064Sume pci_devfs_token = devfs_add_devswf(&pcicdev, 0, DV_CHR, 88478064Sume UID_ROOT, GID_WHEEL, 0644, "pci"); 88553541Sshin#endif 88653541Sshin} 88753541Sshin 88853541SshinSYSINIT(pcidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+PCI_CDEV, pci_cdevinit, NULL); 88953541Sshin 89062587Sitojun#include "pci_if.h" 89162587Sitojun 89262587Sitojun/* 89362587Sitojun * A simple driver to wrap the old pci driver mechanism for back-compat. 89462587Sitojun */ 89562587Sitojun 89653541Sshinstatic int 89795023Ssuzpci_compat_probe(device_t dev) 89853541Sshin{ 89953541Sshin struct pci_device *dvp; 90078064Sume struct pci_devinfo *dinfo; 90153541Sshin pcicfgregs *cfg; 90253541Sshin const char *name; 90353541Sshin int error; 90462587Sitojun 90553541Sshin dinfo = device_get_ivars(dev); 90653541Sshin cfg = &dinfo->cfg; 90778064Sume dvp = device_get_driver(dev)->priv; 90853541Sshin 90953541Sshin /* 91062587Sitojun * Do the wrapped probe. 91162587Sitojun */ 91278064Sume error = ENXIO; 91362587Sitojun if (dvp && dvp->pd_probe) { 91462587Sitojun name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor); 91578064Sume if (name) { 91662587Sitojun device_set_desc_copy(dev, name); 91762587Sitojun error = 0; 91862587Sitojun } 91953541Sshin } 92053541Sshin 92153541Sshin return error; 92253541Sshin} 92353541Sshin 92453541Sshinstatic int 92562587Sitojunpci_compat_attach(device_t dev) 92662587Sitojun{ 92762587Sitojun struct pci_device *dvp; 92862587Sitojun struct pci_devinfo *dinfo; 92962587Sitojun pcicfgregs *cfg; 93062587Sitojun int unit; 93162587Sitojun 93262587Sitojun dinfo = device_get_ivars(dev); 93362587Sitojun cfg = &dinfo->cfg; 93462587Sitojun dvp = device_get_driver(dev)->priv; 93562587Sitojun 93678064Sume unit = device_get_unit(dev); 93762587Sitojun if (unit > *dvp->pd_count) 93862587Sitojun *dvp->pd_count = unit; 93962587Sitojun if (dvp->pd_attach) 94062587Sitojun dvp->pd_attach(cfg, unit); 94178064Sume return 0; 94262587Sitojun} 94362587Sitojun 94478064Sumestatic device_method_t pci_compat_methods[] = { 94562587Sitojun /* Device interface */ 94662587Sitojun DEVMETHOD(device_probe, pci_compat_probe), 94762587Sitojun DEVMETHOD(device_attach, pci_compat_attach), 94862587Sitojun 94962587Sitojun { 0, 0 } 95062587Sitojun}; 95162587Sitojun 95262587Sitojunstatic devclass_t pci_devclass; 95362587Sitojun 95462587Sitojun/* 95562587Sitojun * Create a new style driver around each old pci driver. 95662587Sitojun */ 95762587Sitojunint 95862587Sitojuncompat_pci_handler(module_t mod, int type, void *data) 95962587Sitojun{ 96062587Sitojun struct pci_device *dvp = (struct pci_device *)data; 96162587Sitojun driver_t *driver; 96278064Sume 96362587Sitojun switch (type) { 96462587Sitojun case MOD_LOAD: 96562587Sitojun driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT); 96662587Sitojun if (!driver) 96762587Sitojun return ENOMEM; 96862587Sitojun bzero(driver, sizeof(driver_t)); 96962587Sitojun driver->name = dvp->pd_name; 97078064Sume driver->methods = pci_compat_methods; 97162587Sitojun driver->softc = sizeof(struct pci_devinfo *); 97262587Sitojun driver->priv = dvp; 97362587Sitojun devclass_add_driver(pci_devclass, driver); 97462587Sitojun break; 97562587Sitojun case MOD_UNLOAD: 97662587Sitojun printf("%s: module unload not supported!\n", dvp->pd_name); 97762587Sitojun return EOPNOTSUPP; 97862587Sitojun default: 97962587Sitojun break; 98062587Sitojun } 98162587Sitojun return 0; 98262587Sitojun} 98362587Sitojun 98462587Sitojun/* 98578064Sume * New style pci driver. Parent device is either a pci-host-bridge or a 98662587Sitojun * pci-pci-bridge. Both kinds are represented by instances of pcib. 98762587Sitojun */ 98862587Sitojun 98962587Sitojunstatic void 99078064Sumepci_print_verbose(struct pci_devinfo *dinfo) 99162587Sitojun{ 99262587Sitojun if (bootverbose) { 99378064Sume int i; 99462587Sitojun pcicfgregs *cfg = &dinfo->cfg; 99562587Sitojun 99662587Sitojun printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", 99762587Sitojun cfg->vendor, cfg->device, cfg->revid); 99862587Sitojun printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", 99962587Sitojun cfg->baseclass, cfg->subclass, cfg->progif, 100062587Sitojun cfg->hdrtype, cfg->mfdev); 100162587Sitojun printf("\tsubordinatebus=%x \tsecondarybus=%x\n", 100262587Sitojun cfg->subordinatebus, cfg->secondarybus); 100362587Sitojun#ifdef PCI_DEBUG 100462587Sitojun printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", 100562587Sitojun cfg->cmdreg, cfg->statreg, cfg->cachelnsz); 100662587Sitojun printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", 100762587Sitojun cfg->lattimer, cfg->lattimer * 30, 100853541Sshin cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); 100962587Sitojun#endif /* PCI_DEBUG */ 101062587Sitojun if (cfg->intpin > 0) 101195023Ssuz printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); 101262587Sitojun 101362587Sitojun for (i = 0; i < cfg->nummaps; i++) { 101462587Sitojun pcimap *m = &cfg->map[i]; 101562587Sitojun printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n", 101662587Sitojun i, m->type, m->ln2range, m->base, m->ln2size); 101753541Sshin } 101853541Sshin } 101953541Sshin} 102078064Sume 102162587Sitojunstatic int 102253541Sshinpci_add_children(device_t dev, int busno) 102362587Sitojun{ 102462587Sitojun pcicfgregs probe; 102578064Sume int bushigh = busno; 102662587Sitojun 102762587Sitojun#ifdef SIMOS 102878064Sume#undef PCI_SLOTMAX 102962587Sitojun#define PCI_SLOTMAX 0 103062587Sitojun#endif 103178064Sume 103278064Sume bzero(&probe, sizeof probe); 103378064Sume#ifdef __alpha__ 103478064Sume probe.hose = pcib_get_hose(dev); 103578064Sume#endif 103678064Sume#ifdef __i386__ 103778064Sume probe.hose = 0; 103878064Sume#endif 103978064Sume probe.bus = busno; 104078064Sume 104178064Sume for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { 104278064Sume int pcifunchigh = 0; 104378064Sume for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { 104478064Sume struct pci_devinfo *dinfo = pci_readcfg(&probe); 104578064Sume if (dinfo != NULL) { 104678064Sume if (dinfo->cfg.mfdev) 104778064Sume pcifunchigh = 7; 104878064Sume 104978064Sume pci_print_verbose(dinfo); 105078064Sume dinfo->cfg.dev = 105178064Sume device_add_child(dev, NULL, -1, dinfo); 105278064Sume 105378064Sume if (bushigh < dinfo->cfg.subordinatebus) 105478064Sume bushigh = dinfo->cfg.subordinatebus; 105578064Sume if (bushigh < dinfo->cfg.secondarybus) 105678064Sume bushigh = dinfo->cfg.secondarybus; 105778064Sume } 105878064Sume } 105978064Sume } 106078064Sume 106178064Sume return bushigh; 106278064Sume} 106378064Sume 106478064Sumestatic int 106578064Sumepci_new_probe(device_t dev) 106678064Sume{ 106778064Sume device_set_desc(dev, "PCI bus"); 106878064Sume 106978064Sume pci_add_children(dev, device_get_unit(dev)); 107078064Sume 107178064Sume return 0; 107278064Sume} 107378064Sume 107478064Sumestatic void 107578064Sumepci_print_child(device_t dev, device_t child) 107678064Sume{ 107778064Sume struct pci_devinfo *dinfo; 107878064Sume pcicfgregs *cfg; 107978064Sume 108078064Sume dinfo = device_get_ivars(child); 108178064Sume cfg = &dinfo->cfg; 108278064Sume if (cfg->intpin > 0 && cfg->intline != 255) 108378064Sume printf(" irq %d", cfg->intline); 108462587Sitojun printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child)); 108578064Sume printf(" on %s%d", device_get_name(dev), device_get_unit(dev)); 108678064Sume} 108778064Sume 108862587Sitojunstatic int 108962587Sitojunpci_read_ivar(device_t dev, device_t child, int which, u_long *result) 109053541Sshin{ 109153541Sshin struct pci_devinfo *dinfo; 109253541Sshin pcicfgregs *cfg; 109378064Sume 109478064Sume dinfo = device_get_ivars(child); 109553541Sshin cfg = &dinfo->cfg; 109653541Sshin 109778064Sume switch (which) { 109853541Sshin case PCI_IVAR_SUBVENDOR: 109978064Sume *result = cfg->subvendor; 110053541Sshin break; 110178064Sume case PCI_IVAR_SUBDEVICE: 110253541Sshin *result = cfg->subdevice; 110353541Sshin break; 110478064Sume case PCI_IVAR_VENDOR: 110578064Sume *result = cfg->vendor; 110678064Sume break; 110778064Sume case PCI_IVAR_DEVICE: 110862587Sitojun *result = cfg->device; 110978064Sume break; 111078064Sume case PCI_IVAR_DEVID: 111178064Sume *result = (cfg->device << 16) | cfg->vendor; 111262587Sitojun break; 111362587Sitojun case PCI_IVAR_CLASS: 111462587Sitojun *result = cfg->baseclass; 111562587Sitojun break; 111678064Sume case PCI_IVAR_SUBCLASS: 111778064Sume *result = cfg->subclass; 111878064Sume break; 111962587Sitojun case PCI_IVAR_PROGIF: 112062587Sitojun *result = cfg->progif; 112162587Sitojun break; 112262587Sitojun case PCI_IVAR_REVID: 112378064Sume *result = cfg->revid; 112478064Sume break; 112578064Sume case PCI_IVAR_INTPIN: 112678064Sume *result = cfg->intpin; 112778064Sume break; 112862587Sitojun case PCI_IVAR_IRQ: 112962587Sitojun *result = cfg->intline; 113062587Sitojun break; 113162587Sitojun case PCI_IVAR_BUS: 113262587Sitojun *result = cfg->bus; 113362587Sitojun break; 113462587Sitojun case PCI_IVAR_SLOT: 113562587Sitojun *result = cfg->slot; 113662587Sitojun break; 113762587Sitojun case PCI_IVAR_FUNCTION: 113862587Sitojun *result = cfg->func; 113978064Sume break; 114062587Sitojun case PCI_IVAR_SECONDARYBUS: 114162587Sitojun *result = cfg->secondarybus; 114262587Sitojun break; 114395023Ssuz case PCI_IVAR_SUBORDINATEBUS: 114462587Sitojun *result = cfg->subordinatebus; 114595023Ssuz break; 114662587Sitojun case PCI_IVAR_HOSE: 114762587Sitojun /* 114853541Sshin * Pass up to parent bridge. 114978064Sume */ 115078064Sume *result = pcib_get_hose(dev); 115162587Sitojun break; 115262587Sitojun default: 115362587Sitojun return ENOENT; 115462587Sitojun } 115562587Sitojun return 0; 115662587Sitojun} 115762587Sitojun 115853541Sshinstatic int 115962587Sitojunpci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 116053541Sshin{ 116153541Sshin struct pci_devinfo *dinfo; 116253541Sshin pcicfgregs *cfg; 116353541Sshin 116453541Sshin dinfo = device_get_ivars(child); 116562587Sitojun cfg = &dinfo->cfg; 116653541Sshin 116762587Sitojun switch (which) { 116862587Sitojun case PCI_IVAR_SUBVENDOR: 116953541Sshin case PCI_IVAR_SUBDEVICE: 117053541Sshin case PCI_IVAR_VENDOR: 117153541Sshin case PCI_IVAR_DEVICE: 117253541Sshin case PCI_IVAR_DEVID: 117378064Sume case PCI_IVAR_CLASS: 117478064Sume case PCI_IVAR_SUBCLASS: 117562587Sitojun case PCI_IVAR_PROGIF: 117662587Sitojun case PCI_IVAR_REVID: 117778064Sume case PCI_IVAR_INTPIN: 117878064Sume case PCI_IVAR_IRQ: 117953541Sshin case PCI_IVAR_BUS: 118062587Sitojun case PCI_IVAR_SLOT: 118162587Sitojun case PCI_IVAR_FUNCTION: 118262587Sitojun return EINVAL; /* disallow for now */ 118362587Sitojun 118462587Sitojun case PCI_IVAR_SECONDARYBUS: 118562587Sitojun cfg->secondarybus = value; 118662587Sitojun break; 118762587Sitojun case PCI_IVAR_SUBORDINATEBUS: 118862587Sitojun cfg->subordinatebus = value; 118962587Sitojun break; 119053541Sshin default: 119162587Sitojun return ENOENT; 119262587Sitojun } 119362587Sitojun return 0; 119478064Sume} 119578064Sume 119678064Sumestatic int 119778064Sumepci_mapno(pcicfgregs *cfg, int reg) 119862587Sitojun{ 119962587Sitojun int i, nummaps; 120062587Sitojun pcimap *map; 120162587Sitojun 120262587Sitojun nummaps = cfg->nummaps; 120362587Sitojun map = cfg->map; 120478064Sume 120578064Sume for (i = 0; i < nummaps; i++) 120678064Sume if (map[i].reg == reg) 120778064Sume return (i); 120878064Sume return (-1); 120978064Sume} 121078064Sume 121178064Sumestatic int 121278064Sumepci_porten(pcicfgregs *cfg) 121378064Sume{ 121478064Sume return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0); 121562587Sitojun} 121662587Sitojun 121762587Sitojunstatic int 121878064Sumepci_isportmap(pcicfgregs *cfg, int map) 121962587Sitojun 122062587Sitojun{ 122162587Sitojun return ((unsigned)map < cfg->nummaps 122262587Sitojun && (cfg->map[map].type & PCI_MAPPORT) != 0); 122362587Sitojun} 122478064Sume 122578064Sumestatic int 122678064Sumepci_memen(pcicfgregs *cfg) 122778064Sume{ 122862587Sitojun return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0); 122962587Sitojun} 123062587Sitojun 123162587Sitojunstatic int 123262587Sitojunpci_ismemmap(pcicfgregs *cfg, int map) 123362587Sitojun{ 123462587Sitojun return ((unsigned)map < cfg->nummaps 123562587Sitojun && (cfg->map[map].type & PCI_MAPMEM) != 0); 123662587Sitojun} 123762587Sitojun 123862587Sitojunstatic struct resource * 123978064Sumepci_alloc_resource(device_t dev, device_t child, int type, int *rid, 124078064Sume u_long start, u_long end, u_long count, u_int flags) 124162587Sitojun{ 124262587Sitojun int isdefault; 124362587Sitojun struct pci_devinfo *dinfo = device_get_ivars(child); 124478064Sume pcicfgregs *cfg = &dinfo->cfg; 124578064Sume struct resource *rv, **rvp = 0; 124678064Sume int map; 124778064Sume 124862587Sitojun isdefault = (device_get_parent(child) == dev 124962587Sitojun && start == 0UL && end == ~0UL); 125062587Sitojun 125162587Sitojun switch (type) { 125262587Sitojun case SYS_RES_IRQ: 125362587Sitojun if (*rid != 0) 125462587Sitojun return 0; 125578064Sume if (isdefault && cfg->intline != 255) { 125678064Sume start = cfg->intline; 125762587Sitojun end = cfg->intline; 125862587Sitojun count = 1; 125962587Sitojun } 126062587Sitojun break; 126162587Sitojun 126262587Sitojun case SYS_RES_DRQ: /* passthru for child isa */ 126362587Sitojun break; 126462587Sitojun 126562587Sitojun case SYS_RES_MEMORY: 126662587Sitojun if (isdefault) { 126762587Sitojun map = pci_mapno(cfg, *rid); 126862587Sitojun if (pci_memen(cfg) && pci_ismemmap(cfg, map)) { 126978064Sume start = cfg->map[map].base; 127078064Sume count = 1 << cfg->map[map].ln2size; 127178064Sume end = start + count; 127278064Sume rvp = &cfg->map[map].res; 127362587Sitojun } else 127478064Sume return 0; 127578064Sume } 127678064Sume break; 127778064Sume 127878064Sume case SYS_RES_IOPORT: 127978064Sume if (isdefault) { 128078064Sume map = pci_mapno(cfg, *rid); 128178064Sume if (pci_porten(cfg) && pci_isportmap(cfg, map)) { 128278064Sume start = cfg->map[map].base; 128378064Sume count = 1 << cfg->map[map].ln2size; 128478064Sume end = start + count; 128562587Sitojun rvp = &cfg->map[map].res; 128678064Sume } else 128762587Sitojun return 0; 128862587Sitojun } 128962587Sitojun break; 129062587Sitojun 129162587Sitojun default: 129262587Sitojun return 0; 129362587Sitojun } 129462587Sitojun 129562587Sitojun rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, 129662587Sitojun type, rid, start, end, count, flags); 129762587Sitojun if (rvp) 129862587Sitojun *rvp = rv; 129962587Sitojun 130062587Sitojun return rv; 130162587Sitojun} 130262587Sitojun 130362587Sitojunstatic int 130462587Sitojunpci_release_resource(device_t dev, device_t child, int type, int rid, 130562587Sitojun struct resource *r) 130662587Sitojun{ 130762587Sitojun int rv; 130862587Sitojun struct pci_devinfo *dinfo = device_get_ivars(child); 130962587Sitojun pcicfgregs *cfg = &dinfo->cfg; 131062587Sitojun int map = 0; 131162587Sitojun 131262587Sitojun switch (type) { 131362587Sitojun case SYS_RES_IRQ: 131462587Sitojun if (rid != 0) 131562587Sitojun return EINVAL; 131662587Sitojun break; 131762587Sitojun 131862587Sitojun case SYS_RES_DRQ: /* passthru for child isa */ 131962587Sitojun break; 132062587Sitojun 132162587Sitojun case SYS_RES_MEMORY: 132262587Sitojun case SYS_RES_IOPORT: 132362587Sitojun /* 132478064Sume * Only check the map registers if this is a direct 132562587Sitojun * descendant. 132662587Sitojun */ 132762587Sitojun if (device_get_parent(child) == dev) 132862587Sitojun map = pci_mapno(cfg, rid); 132978064Sume else 133062587Sitojun map = -1; 133178064Sume break; 133278064Sume 133378064Sume default: 133478064Sume return (ENOENT); 133578064Sume } 133678064Sume 133778064Sume rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r); 133878064Sume 133978064Sume if (rv == 0) { 134078064Sume switch (type) { 134178064Sume case SYS_RES_IRQ: 134278064Sume cfg->irqres = 0; 134378064Sume break; 134478064Sume 134578064Sume case SYS_RES_DRQ: /* passthru for child isa */ 134678064Sume break; 134778064Sume 134878064Sume case SYS_RES_MEMORY: 134978064Sume case SYS_RES_IOPORT: 135078064Sume if (map != -1) 135178064Sume cfg->map[map].res = 0; 135278064Sume break; 135378064Sume 135478064Sume default: 135578064Sume return ENOENT; 135678064Sume } 135778064Sume } 135878064Sume 135978064Sume return rv; 136062587Sitojun} 136178064Sume 136278064Sumestatic u_int32_t 136378064Sumepci_read_config_method(device_t dev, device_t child, int reg, int width) 136478064Sume{ 136578064Sume struct pci_devinfo *dinfo = device_get_ivars(child); 136678064Sume pcicfgregs *cfg = &dinfo->cfg; 136778064Sume return pci_cfgread(cfg, reg, width); 136878064Sume} 136978064Sume 137078064Sumestatic void 137178064Sumepci_write_config_method(device_t dev, device_t child, int reg, 137278064Sume u_int32_t val, int width) 137378064Sume{ 137478064Sume struct pci_devinfo *dinfo = device_get_ivars(child); 137562587Sitojun pcicfgregs *cfg = &dinfo->cfg; 137662587Sitojun pci_cfgwrite(cfg, reg, val, width); 137778064Sume} 137853541Sshin 137962587Sitojunstatic int 138062587Sitojunpci_modevent(module_t mod, int what, void *arg) 138153541Sshin{ 138262587Sitojun switch (what) { 138353541Sshin case MOD_LOAD: 138453541Sshin STAILQ_INIT(&pci_devq); 138562587Sitojun break; 138678064Sume 138778064Sume case MOD_UNLOAD: 138878064Sume break; 138978064Sume } 139053541Sshin 139162587Sitojun return 0; 139253541Sshin} 139353541Sshin 139453541Sshinstatic device_method_t pci_methods[] = { 139553541Sshin /* Device interface */ 139653541Sshin DEVMETHOD(device_probe, pci_new_probe), 139753541Sshin DEVMETHOD(device_attach, bus_generic_attach), 139853541Sshin DEVMETHOD(device_shutdown, bus_generic_shutdown), 139953541Sshin DEVMETHOD(device_suspend, bus_generic_suspend), 140053541Sshin DEVMETHOD(device_resume, bus_generic_resume), 140153541Sshin 140262587Sitojun /* Bus interface */ 140353541Sshin DEVMETHOD(bus_print_child, pci_print_child), 140453541Sshin DEVMETHOD(bus_read_ivar, pci_read_ivar), 140553541Sshin DEVMETHOD(bus_write_ivar, pci_write_ivar), 140662587Sitojun DEVMETHOD(bus_driver_added, bus_generic_driver_added), 140778064Sume DEVMETHOD(bus_alloc_resource, pci_alloc_resource), 140862587Sitojun DEVMETHOD(bus_release_resource, pci_release_resource), 140962587Sitojun DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 141062587Sitojun DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 141178064Sume DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 141278064Sume DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 141378064Sume 141478064Sume /* PCI interface */ 141578064Sume DEVMETHOD(pci_read_config, pci_read_config_method), 141678064Sume DEVMETHOD(pci_write_config, pci_write_config_method), 141778064Sume 141862587Sitojun { 0, 0 } 141978064Sume}; 142062587Sitojun 142178064Sumestatic driver_t pci_driver = { 142262587Sitojun "pci", 142362587Sitojun pci_methods, 142462587Sitojun 1, /* no softc */ 142562587Sitojun}; 142662587Sitojun 142762587SitojunDRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); 142862587Sitojun 142962587Sitojun#endif /* NPCI > 0 */ 143062587Sitojun