📄 pci.c
字号:
/* * Copyright (c) 1997, Stefan Esser <se@freebsd.org> * 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 unmodified, 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. * * 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. * * $Id: pci.c,v 1.93 1999/01/19 23:29:18 se Exp $ * */#include "pci.h"#if NPCI > 0#include "opt_devfs.h"#include "opt_simos.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/malloc.h>#include <sys/fcntl.h>#include <sys/conf.h>#include <sys/kernel.h>#include <sys/queue.h>#include <sys/types.h>#include <sys/buf.h>#ifdef DEVFS#include <sys/devfsext.h>#endif /* DEVFS */#include <vm/vm.h>#include <vm/pmap.h>#include <vm/vm_extern.h>#include <pci/pcireg.h>#include <pci/pcivar.h>#include <pci/pci_ioctl.h>#ifdef APIC_IO#include <machine/smp.h>#endif /* APIC_IO */STAILQ_HEAD(devlist, pci_devinfo) pci_devq;u_int32_t pci_numdevs = 0;u_int32_t pci_generation = 0;/* return highest PCI bus number known to be used, or -1 if none */static intpci_bushigh(void){ if (pci_cfgopen() == 0) return (-1); return (0);}/* return base address of memory or port map */static intpci_mapbase(unsigned mapreg){ int mask = 0x03; if ((mapreg & 0x01) == 0) mask = 0x0f; return (mapreg & ~mask);}/* return map type of memory or port map */static intpci_maptype(unsigned mapreg){ static u_int8_t maptype[0x10] = { PCI_MAPMEM, PCI_MAPPORT, PCI_MAPMEM, 0, PCI_MAPMEM, PCI_MAPPORT, 0, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, PCI_MAPMEM|PCI_MAPMEMP, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 0, 0, }; return maptype[mapreg & 0x0f];}/* return log2 of map size decoded for memory or port map */static intpci_mapsize(unsigned testval){ int ln2size; testval = pci_mapbase(testval); ln2size = 0; if (testval != 0) { while ((testval & 1) == 0) { ln2size++; testval >>= 1; } } return (ln2size);}/* return log2 of address range supported by map register */static intpci_maprange(unsigned mapreg){ int ln2range = 0; switch (mapreg & 0x07) { case 0x00: case 0x01: case 0x05: ln2range = 32; break; case 0x02: ln2range = 20; break; case 0x04: ln2range = 64; break; } return (ln2range);}/* extract map parameters into newly allocated array of pcimap structures */static pcimap *pci_readmaps(pcicfgregs *cfg, int maxmaps){ int i, j = 0; pcimap *map; int map64 = 0; int reg = PCIR_MAPS; for (i = 0; i < maxmaps; i++) { int reg = PCIR_MAPS + i*4; u_int32_t base; u_int32_t ln2range; base = pci_cfgread(cfg, reg, 4); ln2range = pci_maprange(base); if (base == 0 || ln2range == 0 || base == 0xffffffff) continue; /* skip invalid entry */ else { j++; if (ln2range > 32) { i++; j++; } } } map = malloc(j * sizeof (pcimap), M_DEVBUF, M_WAITOK); if (map != NULL) { bzero(map, sizeof(pcimap) * j); cfg->nummaps = j; for (i = 0, j = 0; i < maxmaps; i++, reg += 4) { u_int32_t base; u_int32_t testval; base = pci_cfgread(cfg, reg, 4); if (map64 == 0) { if (base == 0 || base == 0xffffffff) continue; /* skip invalid entry */ pci_cfgwrite(cfg, reg, 0xffffffff, 4); testval = pci_cfgread(cfg, reg, 4); pci_cfgwrite(cfg, reg, base, 4); map[j].reg = reg; map[j].base = pci_mapbase(base); map[j].type = pci_maptype(base); map[j].ln2size = pci_mapsize(testval); map[j].ln2range = pci_maprange(testval); map64 = map[j].ln2range == 64; } else { /* only fill in base, other fields are 0 */ map[j].base = base; map64 = 0; } j++; } } return (map);}/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */static voidpci_fixancient(pcicfgregs *cfg){ if (cfg->hdrtype != 0) return; /* PCI to PCI bridges use header type 1 */ if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) cfg->hdrtype = 1;}/* read config data specific to header type 1 device (PCI to PCI bridge) */static void *pci_readppb(pcicfgregs *cfg){ pcih1cfgregs *p; p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); p->membase = PCI_PPBMEMBASE (0, pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); p->memlimit = PCI_PPBMEMLIMIT (0, pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); p->pmembase = PCI_PPBMEMBASE ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); p->pmemlimit = PCI_PPBMEMLIMIT ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); return (p);}/* read config data specific to header type 2 device (PCI to CardBus bridge) */static void *pci_readpcb(pcicfgregs *cfg){ pcih2cfgregs *p; p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); return p;}/* extract header type specific config data */static voidpci_hdrtypedata(pcicfgregs *cfg){ switch (cfg->hdrtype) { case 0: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_0); break; case 1: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_1); cfg->hdrspec = pci_readppb(cfg); break; case 2: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_2); cfg->hdrspec = pci_readpcb(cfg); break; }}/* read configuration header into pcicfgrect structure */static struct pci_devinfo *pci_readcfg(pcicfgregs *probe){ pcicfgregs *cfg = NULL; struct pci_devinfo *devlist_entry; struct devlist *devlist_head; devlist_head = &pci_devq; devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { devlist_entry = malloc(sizeof(struct pci_devinfo), M_DEVBUF, M_WAITOK); if (devlist_entry == NULL) return (NULL); cfg = &devlist_entry->cfg; bzero(cfg, sizeof *cfg); cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); cfg->baseclass = pci_cfgread(cfg, PCIR_CLASS, 1); cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1);#ifdef __alpha__ alpha_platform_assign_pciintr(cfg);#endif#ifdef APIC_IO if (cfg->intpin != 0) { int airq; airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != cfg->intline) { undirect_pci_irq(cfg->intline); cfg->intline = airq; } } else { /* * PCI interrupts might be redirected to the * ISA bus according to some MP tables. Use the * same methods as used by the ISA devices * devices to find the proper IOAPIC int pin. */ airq = isa_apic_irq(cfg->intline); if ((airq >= 0) && (airq != cfg->intline)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(cfg->intline); cfg->intline = airq; } } }#endif /* APIC_IO */ cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; pci_fixancient(cfg); pci_hdrtypedata(cfg); STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); devlist_entry->conf.pc_sel.pc_bus = cfg->bus; devlist_entry->conf.pc_sel.pc_dev = cfg->slot; devlist_entry->conf.pc_sel.pc_func = cfg->func; devlist_entry->conf.pc_hdr = cfg->hdrtype; devlist_entry->conf.pc_subvendor = cfg->subvendor; devlist_entry->conf.pc_subdevice = cfg->subdevice; devlist_entry->conf.pc_vendor = cfg->vendor; devlist_entry->conf.pc_device = cfg->device; devlist_entry->conf.pc_class = cfg->baseclass; devlist_entry->conf.pc_subclass = cfg->subclass; devlist_entry->conf.pc_progif = cfg->progif; devlist_entry->conf.pc_revid = cfg->revid; pci_numdevs++; pci_generation++; } return (devlist_entry);}#if 0/* free pcicfgregs structure and all depending data structures */static intpci_freecfg(struct pci_devinfo *dinfo){ struct devlist *devlist_head; devlist_head = &pci_devq; if (dinfo->cfg.hdrspec != NULL) free(dinfo->cfg.hdrspec, M_DEVBUF); if (dinfo->cfg.map != NULL) free(dinfo->cfg.map, M_DEVBUF); /* XXX this hasn't been tested */ STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); free(dinfo, M_DEVBUF); /* increment the generation count */ pci_generation++; /* we're losing one device */ pci_numdevs--; return (0);}#endifstatic voidpci_addcfg(struct pci_devinfo *dinfo){ if (bootverbose) { int i; pcicfgregs *cfg = &dinfo->cfg; printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", cfg->vendor, cfg->device, cfg->revid); printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype, cfg->mfdev); printf("\tsubordinatebus=%x \tsecondarybus=%x\n", cfg->subordinatebus, cfg->secondarybus);#ifdef PCI_DEBUG printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", cfg->cmdreg, cfg->statreg, cfg->cachelnsz); printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", cfg->lattimer, cfg->lattimer * 30, cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250);#endif /* PCI_DEBUG */ if (cfg->intpin > 0) printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -