pci.c revision 82440
1244769Sglebius/* 2126258Smlaier * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3223637Sbz * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4244769Sglebius * Copyright (c) 2000, BSDi 5126258Smlaier * All rights reserved. 6126258Smlaier * 7126258Smlaier * Redistribution and use in source and binary forms, with or without 8126258Smlaier * modification, are permitted provided that the following conditions 9126258Smlaier * are met: 10126258Smlaier * 1. Redistributions of source code must retain the above copyright 11126258Smlaier * notice unmodified, this list of conditions, and the following 12126258Smlaier * disclaimer. 13126258Smlaier * 2. Redistributions in binary form must reproduce the above copyright 14126258Smlaier * notice, this list of conditions and the following disclaimer in the 15126258Smlaier * documentation and/or other materials provided with the distribution. 16126258Smlaier * 17126258Smlaier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18126258Smlaier * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19126258Smlaier * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20126258Smlaier * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21126258Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22126258Smlaier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23126258Smlaier * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24126258Smlaier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25126258Smlaier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26126258Smlaier * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27126258Smlaier * 28126258Smlaier * $FreeBSD: head/sys/dev/pci/pci.c 82440 2001-08-27 20:42:07Z imp $ 29126258Smlaier * 30126258Smlaier */ 31126258Smlaier 32126258Smlaier#include "opt_bus.h" 33126258Smlaier 34126258Smlaier#include <sys/param.h> 35244769Sglebius#include <sys/systm.h> 36126258Smlaier#include <sys/malloc.h> 37126258Smlaier#include <sys/module.h> 38240233Sglebius#include <sys/linker.h> 39171168Smlaier#include <sys/fcntl.h> 40126261Smlaier#include <sys/conf.h> 41240233Sglebius#include <sys/kernel.h> 42240233Sglebius#include <sys/queue.h> 43126261Smlaier#include <sys/types.h> 44126261Smlaier 45153110Sru#include <vm/vm.h> 46126258Smlaier#include <vm/pmap.h> 47240233Sglebius#include <vm/vm_extern.h> 48240233Sglebius 49240233Sglebius#include <sys/bus.h> 50240233Sglebius#include <machine/bus.h> 51240233Sglebius#include <sys/rman.h> 52240233Sglebius#include <machine/resource.h> 53240233Sglebius 54126258Smlaier#include <sys/pciio.h> 55240233Sglebius#include <pci/pcireg.h> 56240233Sglebius#include <pci/pcivar.h> 57240233Sglebius 58126258Smlaier#include "pcib_if.h" 59126261Smlaier#include "pci_if.h" 60240233Sglebius 61240233Sglebiusstatic u_int32_t pci_mapbase(unsigned mapreg); 62126258Smlaierstatic int pci_maptype(unsigned mapreg); 63126258Smlaierstatic int pci_mapsize(unsigned testval); 64126258Smlaierstatic int pci_maprange(unsigned mapreg); 65126258Smlaierstatic void pci_fixancient(pcicfgregs *cfg); 66171168Smlaierstatic void pci_hdrtypedata(device_t pcib, int b, int s, int f, 67240233Sglebius pcicfgregs *cfg); 68126258Smlaierstatic struct pci_devinfo *pci_read_device(device_t pcib, int b, int s, int f); 69240233Sglebiusstatic void pci_read_extcap(device_t pcib, pcicfgregs *cfg); 70240233Sglebius 71240233Sglebiusstatic void pci_print_verbose(struct pci_devinfo *dinfo); 72240233Sglebiusstatic int pci_porten(device_t pcib, int b, int s, int f); 73240233Sglebiusstatic int pci_memen(device_t pcib, int b, int s, int f); 74240233Sglebiusstatic int pci_add_map(device_t pcib, int b, int s, int f, int reg, 75126258Smlaier struct resource_list *rl); 76126258Smlaierstatic void pci_add_resources(device_t pcib, int b, int s, int f, 77240233Sglebius device_t dev); 78240233Sglebiusstatic void pci_add_children(device_t dev, int busno); 79240233Sglebiusstatic int pci_probe(device_t dev); 80126258Smlaierstatic int pci_print_resources(struct resource_list *rl, 81126258Smlaier const char *name, int type, 82240233Sglebius const char *format); 83126258Smlaierstatic int pci_print_child(device_t dev, device_t child); 84126258Smlaierstatic void pci_probe_nomatch(device_t dev, device_t child); 85126258Smlaierstatic int pci_describe_parse_line(char **ptr, int *vendor, 86240233Sglebius int *device, char **desc); 87126258Smlaierstatic char *pci_describe_device(device_t dev); 88126258Smlaierstatic int pci_read_ivar(device_t dev, device_t child, int which, 89240494Sglebius uintptr_t *result); 90240494Sglebiusstatic int pci_write_ivar(device_t dev, device_t child, int which, 91126258Smlaier uintptr_t value); 92126258Smlaierstatic struct resource *pci_alloc_resource(device_t dev, device_t child, 93126258Smlaier int type, int *rid, u_long start, 94126258Smlaier u_long end, u_long count, u_int flags); 95126261Smlaierstatic void pci_delete_resource(device_t dev, device_t child, 96126261Smlaier int type, int rid); 97126258Smlaierstatic struct resource_list *pci_get_resource_list (device_t dev, device_t child); 98126258Smlaierstatic u_int32_t pci_read_config_method(device_t dev, device_t child, 99126261Smlaier int reg, int width); 100163606Srwatsonstatic void pci_write_config_method(device_t dev, device_t child, 101126258Smlaier int reg, u_int32_t val, int width); 102223637Sbzstatic void pci_enable_busmaster_method(device_t dev, 103126258Smlaier device_t child); 104126258Smlaierstatic void pci_disable_busmaster_method(device_t dev, 105126258Smlaier device_t child); 106126258Smlaierstatic void pci_enable_io_method(device_t dev, device_t child, 107126258Smlaier int space); 108223637Sbzstatic void pci_disable_io_method(device_t dev, device_t child, 109223637Sbz int space); 110223637Sbzstatic int pci_set_powerstate_method(device_t dev, device_t child, 111223637Sbz int state); 112223637Sbzstatic int pci_get_powerstate_method(device_t dev, device_t child); 113223637Sbzstatic int pci_modevent(module_t mod, int what, void *arg); 114223637Sbz 115223637Sbzstatic device_method_t pci_methods[] = { 116223637Sbz /* Device interface */ 117223637Sbz DEVMETHOD(device_probe, pci_probe), 118223637Sbz DEVMETHOD(device_attach, bus_generic_attach), 119223637Sbz DEVMETHOD(device_shutdown, bus_generic_shutdown), 120223637Sbz DEVMETHOD(device_suspend, bus_generic_suspend), 121223637Sbz DEVMETHOD(device_resume, bus_generic_resume), 122223637Sbz 123223637Sbz /* Bus interface */ 124223637Sbz DEVMETHOD(bus_print_child, pci_print_child), 125223637Sbz DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), 126223637Sbz DEVMETHOD(bus_read_ivar, pci_read_ivar), 127223637Sbz DEVMETHOD(bus_write_ivar, pci_write_ivar), 128223637Sbz DEVMETHOD(bus_driver_added, bus_generic_driver_added), 129240233Sglebius DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 130240233Sglebius DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 131240233Sglebius 132240233Sglebius DEVMETHOD(bus_get_resource_list,pci_get_resource_list), 133240233Sglebius DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 134240233Sglebius DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 135240233Sglebius DEVMETHOD(bus_delete_resource, pci_delete_resource), 136240233Sglebius DEVMETHOD(bus_alloc_resource, pci_alloc_resource), 137240233Sglebius DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 138240233Sglebius DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 139240233Sglebius DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 140240233Sglebius 141240233Sglebius /* PCI interface */ 142240233Sglebius DEVMETHOD(pci_read_config, pci_read_config_method), 143240233Sglebius DEVMETHOD(pci_write_config, pci_write_config_method), 144240233Sglebius DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method), 145240233Sglebius DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method), 146240233Sglebius DEVMETHOD(pci_enable_io, pci_enable_io_method), 147240233Sglebius DEVMETHOD(pci_disable_io, pci_disable_io_method), 148240233Sglebius DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method), 149240233Sglebius DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method), 150240233Sglebius 151240233Sglebius { 0, 0 } 152240233Sglebius}; 153240233Sglebius 154240233Sglebiusstatic driver_t pci_driver = { 155223637Sbz "pci", 156240233Sglebius pci_methods, 157240233Sglebius 0, /* no softc */ 158240233Sglebius}; 159126258Smlaier 160240233Sglebiusstatic devclass_t pci_devclass; 161240233SglebiusDRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); 162240233SglebiusDRIVER_MODULE(pci, acpi_pcib, pci_driver, pci_devclass, pci_modevent, 0); 163126258Smlaier 164240233Sglebiusstatic char *pci_vendordata; 165240811Sglebiusstatic size_t pci_vendordata_size; 166240233Sglebius 167240811Sglebius 168240811Sglebiusstruct pci_quirk { 169240233Sglebius u_int32_t devid; /* Vendor/device of the card */ 170240233Sglebius int type; 171240233Sglebius#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */ 172240811Sglebius int arg1; 173240233Sglebius int arg2; 174223637Sbz}; 175240811Sglebius 176240811Sglebiusstruct pci_quirk pci_quirks[] = { 177240811Sglebius /* The Intel 82371AB has a map register at offset 0x90. */ 178240811Sglebius { 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 }, 179240811Sglebius /* As does the Serverworks OSB4 (the SMBus mapping register) */ 180126261Smlaier { 0x02001166, PCI_QUIRK_MAP_REG, 0x90, 0 }, 181240811Sglebius 182240811Sglebius { 0 } 183240811Sglebius}; 184126258Smlaier 185240233Sglebius/* map register information */ 186240233Sglebius#define PCI_MAPMEM 0x01 /* memory map */ 187240233Sglebius#define PCI_MAPMEMP 0x02 /* prefetchable memory map */ 188240233Sglebius#define PCI_MAPPORT 0x04 /* port map */ 189240233Sglebius 190240233Sglebiusu_int32_t pci_numdevs = 0; 191240233Sglebius 192240233Sglebius/* return base address of memory or port map */ 193240233Sglebius 194240233Sglebiusstatic u_int32_t 195240233Sglebiuspci_mapbase(unsigned mapreg) 196240233Sglebius{ 197240233Sglebius int mask = 0x03; 198240233Sglebius if ((mapreg & 0x01) == 0) 199240233Sglebius mask = 0x0f; 200240233Sglebius return (mapreg & ~mask); 201240233Sglebius} 202240233Sglebius 203240233Sglebius/* return map type of memory or port map */ 204145836Smlaier 205240233Sglebiusstatic int 206240233Sglebiuspci_maptype(unsigned mapreg) 207145836Smlaier{ 208240233Sglebius static u_int8_t maptype[0x10] = { 209126258Smlaier PCI_MAPMEM, PCI_MAPPORT, 210126258Smlaier PCI_MAPMEM, 0, 211240233Sglebius PCI_MAPMEM, PCI_MAPPORT, 212171168Smlaier 0, 0, 213240233Sglebius PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 214126258Smlaier PCI_MAPMEM|PCI_MAPMEMP, 0, 215126258Smlaier PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 216126258Smlaier 0, 0, 217240233Sglebius }; 218162238Scsjp 219126258Smlaier return maptype[mapreg & 0x0f]; 220126258Smlaier} 221145836Smlaier 222240233Sglebius/* return log2 of map size decoded for memory or port map */ 223223637Sbz 224126258Smlaierstatic int 225240233Sglebiuspci_mapsize(unsigned testval) 226240233Sglebius{ 227240233Sglebius int ln2size; 228240233Sglebius 229240233Sglebius testval = pci_mapbase(testval); 230240233Sglebius ln2size = 0; 231240233Sglebius if (testval != 0) { 232130613Smlaier while ((testval & 1) == 0) 233240233Sglebius { 234240233Sglebius ln2size++; 235240233Sglebius testval >>= 1; 236223637Sbz } 237223637Sbz } 238223637Sbz return (ln2size); 239223637Sbz} 240223637Sbz 241223637Sbz/* return log2 of address range supported by map register */ 242240233Sglebius 243130613Smlaierstatic int 244126258Smlaierpci_maprange(unsigned mapreg) 245126258Smlaier{ 246240233Sglebius int ln2range = 0; 247200930Sdelphij switch (mapreg & 0x07) { 248200930Sdelphij case 0x00: 249200930Sdelphij case 0x01: 250240233Sglebius case 0x05: 251200930Sdelphij ln2range = 32; 252200930Sdelphij break; 253240233Sglebius case 0x02: 254130613Smlaier ln2range = 20; 255126258Smlaier break; 256240233Sglebius case 0x04: 257130613Smlaier ln2range = 64; 258126258Smlaier break; 259240233Sglebius } 260130613Smlaier return (ln2range); 261145836Smlaier} 262240233Sglebius 263223637Sbz/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ 264240233Sglebius 265126258Smlaierstatic void 266240233Sglebiuspci_fixancient(pcicfgregs *cfg) 267126258Smlaier{ 268240233Sglebius if (cfg->hdrtype != 0) 269231852Sbz return; 270240233Sglebius 271126258Smlaier /* PCI to PCI bridges use header type 1 */ 272240233Sglebius if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) 273126258Smlaier cfg->hdrtype = 1; 274240233Sglebius} 275223637Sbz 276240233Sglebius/* extract header type specific config data */ 277126258Smlaier 278240233Sglebiusstatic void 279240233Sglebiuspci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg) 280240233Sglebius{ 281240811Sglebius#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w) 282240233Sglebius switch (cfg->hdrtype) { 283240233Sglebius case 0: 284241039Sglebius cfg->subvendor = REG(PCIR_SUBVEND_0, 2); 285240233Sglebius cfg->subdevice = REG(PCIR_SUBDEV_0, 2); 286240233Sglebius cfg->nummaps = PCI_MAXMAPS_0; 287240233Sglebius break; 288240233Sglebius case 1: 289240233Sglebius cfg->subvendor = REG(PCIR_SUBVEND_1, 2); 290240233Sglebius cfg->subdevice = REG(PCIR_SUBDEV_1, 2); 291240233Sglebius cfg->nummaps = PCI_MAXMAPS_1; 292240233Sglebius break; 293240233Sglebius case 2: 294240233Sglebius cfg->subvendor = REG(PCIR_SUBVEND_2, 2); 295240233Sglebius cfg->subdevice = REG(PCIR_SUBDEV_2, 2); 296240233Sglebius cfg->nummaps = PCI_MAXMAPS_2; 297240233Sglebius break; 298240233Sglebius } 299240233Sglebius#undef REG 300126258Smlaier} 301126261Smlaier 302126258Smlaier/* read configuration header into pcicfgregs structure */ 303223637Sbz 304171168Smlaierstatic struct pci_devinfo * 305240233Sglebiuspci_read_device(device_t pcib, int b, int s, int f) 306145836Smlaier{ 307240233Sglebius#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w) 308240233Sglebius pcicfgregs *cfg = NULL; 309126258Smlaier struct pci_devinfo *devlist_entry; 310240233Sglebius struct devlist *devlist_head; 311126258Smlaier 312240233Sglebius devlist_head = &pci_devq; 313261018Sglebius 314126258Smlaier devlist_entry = NULL; 315240233Sglebius 316126258Smlaier if (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_DEVVENDOR, 4) != -1) { 317240233Sglebius devlist_entry = malloc(sizeof(struct pci_devinfo), 318223637Sbz M_DEVBUF, M_WAITOK | M_ZERO); 319223637Sbz if (devlist_entry == NULL) 320223637Sbz return (NULL); 321223637Sbz 322223637Sbz cfg = &devlist_entry->cfg; 323240233Sglebius 324223637Sbz cfg->bus = b; 325126258Smlaier cfg->slot = s; 326126258Smlaier cfg->func = f; 327223637Sbz cfg->vendor = REG(PCIR_VENDOR, 2); 328223637Sbz cfg->device = REG(PCIR_DEVICE, 2); 329126258Smlaier cfg->cmdreg = REG(PCIR_COMMAND, 2); 330223637Sbz cfg->statreg = REG(PCIR_STATUS, 2); 331145836Smlaier cfg->baseclass = REG(PCIR_CLASS, 1); 332223637Sbz cfg->subclass = REG(PCIR_SUBCLASS, 1); 333223637Sbz cfg->progif = REG(PCIR_PROGIF, 1); 334223637Sbz cfg->revid = REG(PCIR_REVID, 1); 335223637Sbz cfg->hdrtype = REG(PCIR_HEADERTYPE, 1); 336223637Sbz cfg->cachelnsz = REG(PCIR_CACHELNSZ, 1); 337223637Sbz cfg->lattimer = REG(PCIR_LATTIMER, 1); 338223637Sbz cfg->intpin = REG(PCIR_INTPIN, 1); 339223637Sbz cfg->intline = REG(PCIR_INTLINE, 1); 340223637Sbz 341223637Sbz cfg->mingnt = REG(PCIR_MINGNT, 1); 342145836Smlaier cfg->maxlat = REG(PCIR_MAXLAT, 1); 343145836Smlaier 344223637Sbz cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; 345145836Smlaier cfg->hdrtype &= ~PCIM_MFDEV; 346145836Smlaier 347223637Sbz pci_fixancient(cfg); 348145836Smlaier pci_hdrtypedata(pcib, b, s, f, cfg); 349223637Sbz 350223637Sbz if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) 351145836Smlaier pci_read_extcap(pcib, cfg); 352145836Smlaier 353240233Sglebius STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); 354240233Sglebius 355240233Sglebius devlist_entry->conf.pc_sel.pc_bus = cfg->bus; 356240233Sglebius devlist_entry->conf.pc_sel.pc_dev = cfg->slot; 357240233Sglebius devlist_entry->conf.pc_sel.pc_func = cfg->func; 358240233Sglebius devlist_entry->conf.pc_hdr = cfg->hdrtype; 359223637Sbz 360240233Sglebius devlist_entry->conf.pc_subvendor = cfg->subvendor; 361223637Sbz devlist_entry->conf.pc_subdevice = cfg->subdevice; 362240233Sglebius devlist_entry->conf.pc_vendor = cfg->vendor; 363240233Sglebius devlist_entry->conf.pc_device = cfg->device; 364240233Sglebius 365240233Sglebius devlist_entry->conf.pc_class = cfg->baseclass; 366130613Smlaier devlist_entry->conf.pc_subclass = cfg->subclass; 367240233Sglebius devlist_entry->conf.pc_progif = cfg->progif; 368240233Sglebius devlist_entry->conf.pc_revid = cfg->revid; 369240233Sglebius 370240233Sglebius pci_numdevs++; 371171168Smlaier pci_generation++; 372240233Sglebius } 373130613Smlaier return (devlist_entry); 374240233Sglebius#undef REG 375240233Sglebius} 376240233Sglebius 377240233Sglebiusstatic void 378240233Sglebiuspci_read_extcap(device_t pcib, pcicfgregs *cfg) 379126258Smlaier{ 380240233Sglebius#define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w) 381126258Smlaier int ptr, nextptr, ptrptr; 382240233Sglebius 383240233Sglebius switch (cfg->hdrtype) { 384240233Sglebius case 0: 385240233Sglebius ptrptr = 0x34; 386240233Sglebius break; 387130613Smlaier case 2: 388130613Smlaier ptrptr = 0x14; 389240736Sglebius break; 390240736Sglebius default: 391240736Sglebius return; /* no extended capabilities support */ 392240736Sglebius } 393240736Sglebius nextptr = REG(ptrptr, 1); /* sanity check? */ 394240736Sglebius 395240736Sglebius /* 396240736Sglebius * Read capability entries. 397240736Sglebius */ 398240736Sglebius while (nextptr != 0) { 399240736Sglebius /* Sanity check */ 400240736Sglebius if (nextptr > 255) { 401240736Sglebius printf("illegal PCI extended capability offset %d\n", 402240736Sglebius nextptr); 403240736Sglebius return; 404240736Sglebius } 405240736Sglebius /* Find the next entry */ 406240736Sglebius ptr = nextptr; 407240736Sglebius nextptr = REG(ptr + 1, 1); 408240736Sglebius 409240736Sglebius /* Process this entry */ 410126258Smlaier switch (REG(ptr, 1)) { 411126258Smlaier case 0x01: /* PCI power management */ 412126258Smlaier if (cfg->pp_cap == 0) { 413126258Smlaier cfg->pp_cap = REG(ptr + PCIR_POWER_CAP, 2); 414126258Smlaier cfg->pp_status = ptr + PCIR_POWER_STATUS; 415126258Smlaier cfg->pp_pmcsr = ptr + PCIR_POWER_PMCSR; 416126258Smlaier if ((nextptr - ptr) > PCIR_POWER_DATA) 417126258Smlaier cfg->pp_data = ptr + PCIR_POWER_DATA; 418126258Smlaier } 419126258Smlaier break; 420126258Smlaier default: 421126258Smlaier break; 422126258Smlaier } 423126258Smlaier } 424126258Smlaier#undef REG 425126258Smlaier} 426126258Smlaier 427126258Smlaier#if 0 428145836Smlaier/* free pcicfgregs structure and all depending data structures */ 429126258Smlaier 430240233Sglebiusstatic int 431145836Smlaierpci_freecfg(struct pci_devinfo *dinfo) 432145836Smlaier{ 433145836Smlaier struct devlist *devlist_head; 434145836Smlaier 435145836Smlaier devlist_head = &pci_devq; 436145836Smlaier 437240233Sglebius if (dinfo->cfg.map != NULL) 438145836Smlaier free(dinfo->cfg.map, M_DEVBUF); 439145836Smlaier /* XXX this hasn't been tested */ 440240233Sglebius STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); 441145836Smlaier free(dinfo, M_DEVBUF); 442145836Smlaier 443240233Sglebius /* increment the generation count */ 444145836Smlaier pci_generation++; 445145836Smlaier 446145836Smlaier /* we're losing one device */ 447145836Smlaier pci_numdevs--; 448145836Smlaier return (0); 449145836Smlaier} 450145836Smlaier#endif 451145836Smlaier 452145836Smlaier/* 453145836Smlaier * PCI power manangement 454240233Sglebius */ 455145836Smlaierstatic int 456145836Smlaierpci_set_powerstate_method(device_t dev, device_t child, int state) 457145836Smlaier{ 458145836Smlaier struct pci_devinfo *dinfo = device_get_ivars(child); 459145836Smlaier pcicfgregs *cfg = &dinfo->cfg; 460240233Sglebius u_int16_t status; 461145836Smlaier int result; 462145836Smlaier 463240811Sglebius if (cfg->pp_cap != 0) { 464145836Smlaier status = PCI_READ_CONFIG(dev, child, cfg->pp_status, 2) & ~PCIM_PSTAT_DMASK; 465145836Smlaier result = 0; 466240233Sglebius switch (state) { 467240233Sglebius case PCI_POWERSTATE_D0: 468145836Smlaier status |= PCIM_PSTAT_D0; 469171168Smlaier break; 470145836Smlaier case PCI_POWERSTATE_D1: 471145836Smlaier if (cfg->pp_cap & PCIM_PCAP_D1SUPP) { 472145836Smlaier status |= PCIM_PSTAT_D1; 473145836Smlaier } else { 474145836Smlaier result = EOPNOTSUPP; 475223637Sbz } 476145836Smlaier break; 477145836Smlaier case PCI_POWERSTATE_D2: 478145836Smlaier if (cfg->pp_cap & PCIM_PCAP_D2SUPP) { 479145836Smlaier status |= PCIM_PSTAT_D2; 480145836Smlaier } else { 481223637Sbz result = EOPNOTSUPP; 482145836Smlaier } 483145836Smlaier break; 484145836Smlaier case PCI_POWERSTATE_D3: 485145836Smlaier status |= PCIM_PSTAT_D3; 486145836Smlaier break; 487145836Smlaier default: 488240233Sglebius result = EINVAL; 489240233Sglebius } 490240233Sglebius if (result == 0) 491145836Smlaier PCI_WRITE_CONFIG(dev, child, cfg->pp_status, status, 2); 492240233Sglebius } else { 493240233Sglebius result = ENXIO; 494145836Smlaier } 495240811Sglebius return(result); 496240811Sglebius} 497240811Sglebius 498240233Sglebiusstatic int 499240233Sglebiuspci_get_powerstate_method(device_t dev, device_t child) 500240811Sglebius{ 501240811Sglebius struct pci_devinfo *dinfo = device_get_ivars(child); 502240811Sglebius pcicfgregs *cfg = &dinfo->cfg; 503240811Sglebius u_int16_t status; 504240811Sglebius int result; 505240811Sglebius 506240811Sglebius if (cfg->pp_cap != 0) { 507240811Sglebius status = PCI_READ_CONFIG(dev, child, cfg->pp_status, 2); 508240233Sglebius switch (status & PCIM_PSTAT_DMASK) { 509240233Sglebius case PCIM_PSTAT_D0: 510240233Sglebius result = PCI_POWERSTATE_D0; 511240233Sglebius break; 512240233Sglebius case PCIM_PSTAT_D1: 513240811Sglebius result = PCI_POWERSTATE_D1; 514240233Sglebius break; 515240811Sglebius case PCIM_PSTAT_D2: 516240811Sglebius result = PCI_POWERSTATE_D2; 517240811Sglebius break; 518240233Sglebius case PCIM_PSTAT_D3: 519240233Sglebius result = PCI_POWERSTATE_D3; 520240811Sglebius break; 521240811Sglebius default: 522240811Sglebius result = PCI_POWERSTATE_UNKNOWN; 523240811Sglebius break; 524240233Sglebius } 525240811Sglebius } else { 526240811Sglebius /* No support, device is always at D0 */ 527240811Sglebius result = PCI_POWERSTATE_D0; 528240811Sglebius } 529240811Sglebius return(result); 530240811Sglebius} 531240811Sglebius 532240811Sglebius/* 533240233Sglebius * Some convenience functions for PCI device drivers. 534240811Sglebius */ 535240811Sglebius 536240811Sglebiusstatic __inline void 537240811Sglebiuspci_set_command_bit(device_t dev, device_t child, u_int16_t bit) 538240811Sglebius{ 539240811Sglebius u_int16_t command; 540240811Sglebius 541240811Sglebius command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); 542240811Sglebius command |= bit; 543240811Sglebius PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2); 544240811Sglebius} 545240811Sglebius 546240811Sglebiusstatic __inline void 547240811Sglebiuspci_clear_command_bit(device_t dev, device_t child, u_int16_t bit) 548240811Sglebius{ 549240811Sglebius u_int16_t command; 550240811Sglebius 551240811Sglebius command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); 552240811Sglebius command &= ~bit; 553240811Sglebius PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2); 554240811Sglebius} 555240811Sglebius 556240811Sglebiusstatic void 557240811Sglebiuspci_enable_busmaster_method(device_t dev, device_t child) 558240811Sglebius{ 559240811Sglebius pci_set_command_bit(dev, child, PCIM_CMD_BUSMASTEREN); 560240811Sglebius} 561240811Sglebius 562240811Sglebiusstatic void 563240811Sglebiuspci_disable_busmaster_method(device_t dev, device_t child) 564240811Sglebius{ 565240811Sglebius pci_clear_command_bit(dev, child, PCIM_CMD_BUSMASTEREN); 566240811Sglebius} 567240811Sglebius 568240811Sglebiusstatic void 569240233Sglebiuspci_enable_io_method(device_t dev, device_t child, int space) 570240233Sglebius{ 571240233Sglebius switch(space) { 572240233Sglebius case SYS_RES_IOPORT: 573240233Sglebius pci_set_command_bit(dev, child, PCIM_CMD_PORTEN); 574240233Sglebius break; 575240233Sglebius case SYS_RES_MEMORY: 576240233Sglebius pci_set_command_bit(dev, child, PCIM_CMD_MEMEN); 577240811Sglebius break; 578240811Sglebius } 579240811Sglebius} 580240811Sglebius 581240811Sglebiusstatic void 582240811Sglebiuspci_disable_io_method(device_t dev, device_t child, int space) 583240811Sglebius{ 584240811Sglebius switch(space) { 585240233Sglebius case SYS_RES_IOPORT: 586240233Sglebius pci_clear_command_bit(dev, child, PCIM_CMD_PORTEN); 587240233Sglebius break; 588145836Smlaier case SYS_RES_MEMORY: 589145836Smlaier pci_clear_command_bit(dev, child, PCIM_CMD_MEMEN); 590240233Sglebius break; 591145836Smlaier } 592240811Sglebius} 593240811Sglebius 594240233Sglebius/* 595240233Sglebius * New style pci driver. Parent device is either a pci-host-bridge or a 596240233Sglebius * pci-pci-bridge. Both kinds are represented by instances of pcib. 597145836Smlaier */ 598240233Sglebius 599240233Sglebiusstatic void 600240233Sglebiuspci_print_verbose(struct pci_devinfo *dinfo) 601240233Sglebius{ 602240233Sglebius if (bootverbose) { 603240233Sglebius pcicfgregs *cfg = &dinfo->cfg; 604240233Sglebius 605240233Sglebius printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", 606240233Sglebius cfg->vendor, cfg->device, cfg->revid); 607240233Sglebius printf("\tbus=%d, slot=%d, func=%d\n", 608240233Sglebius cfg->bus, cfg->slot, cfg->func); 609240233Sglebius printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", 610240233Sglebius cfg->baseclass, cfg->subclass, cfg->progif, 611240233Sglebius cfg->hdrtype, cfg->mfdev); 612240233Sglebius#ifdef PCI_DEBUG 613240233Sglebius printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", 614240233Sglebius cfg->cmdreg, cfg->statreg, cfg->cachelnsz); 615240233Sglebius printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", 616240233Sglebius cfg->lattimer, cfg->lattimer * 30, 617240233Sglebius cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); 618240233Sglebius#endif /* PCI_DEBUG */ 619240233Sglebius if (cfg->intpin > 0) 620240233Sglebius printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); 621240233Sglebius if (cfg->pp_cap) { 622145836Smlaier u_int16_t status; 623145836Smlaier 624240233Sglebius status = pci_read_config(cfg->dev, cfg->pp_status, 2); 625130613Smlaier printf("\tpowerspec %d supports D0%s%s D3 current D%d\n", 626130613Smlaier cfg->pp_cap & PCIM_PCAP_SPEC, 627126258Smlaier cfg->pp_cap & PCIM_PCAP_D1SUPP ? " D1" : "", 628126258Smlaier cfg->pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "", 629240233Sglebius status & PCIM_PSTAT_DMASK); 630240233Sglebius } 631240233Sglebius } 632240233Sglebius} 633240233Sglebius 634240233Sglebiusstatic int 635240233Sglebiuspci_porten(device_t pcib, int b, int s, int f) 636130613Smlaier{ 637240233Sglebius return (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2) 638240233Sglebius & PCIM_CMD_PORTEN) != 0; 639240233Sglebius} 640240233Sglebius 641130613Smlaierstatic int 642130613Smlaierpci_memen(device_t pcib, int b, int s, int f) 643240233Sglebius{ 644145836Smlaier return (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2) 645223637Sbz & PCIM_CMD_MEMEN) != 0; 646240233Sglebius} 647240233Sglebius 648130613Smlaier/* 649240233Sglebius * Add a resource based on a pci map register. Return 1 if the map 650145836Smlaier * register is a 32bit map register or 2 if it is a 64bit register. 651145836Smlaier */ 652145836Smlaierstatic int 653145836Smlaierpci_add_map(device_t pcib, int b, int s, int f, int reg, 654145836Smlaier struct resource_list *rl) 655130613Smlaier{ 656240233Sglebius u_int32_t map; 657130613Smlaier u_int64_t base; 658240233Sglebius u_int8_t ln2size; 659240233Sglebius u_int8_t ln2range; 660130613Smlaier u_int32_t testval; 661130613Smlaier#ifdef PCI_ENABLE_IO_MODES 662130613Smlaier u_int16_t cmd; 663240233Sglebius#endif 664223637Sbz int type; 665223637Sbz 666130613Smlaier map = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4); 667130613Smlaier 668145836Smlaier if (map == 0 || map == 0xffffffff) 669223637Sbz return 1; /* skip invalid entry */ 670130613Smlaier 671145836Smlaier PCIB_WRITE_CONFIG(pcib, b, s, f, reg, 0xffffffff, 4); 672130613Smlaier testval = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4); 673130613Smlaier PCIB_WRITE_CONFIG(pcib, b, s, f, reg, map, 4); 674130613Smlaier 675126258Smlaier base = pci_mapbase(map); 676261019Sglebius if (pci_maptype(map) & PCI_MAPMEM) 677261019Sglebius type = SYS_RES_MEMORY; 678240233Sglebius else 679261019Sglebius type = SYS_RES_IOPORT; 680240233Sglebius ln2size = pci_mapsize(testval); 681223637Sbz ln2range = pci_maprange(testval); 682240233Sglebius if (ln2range == 64) { 683261019Sglebius /* Read the other half of a 64bit map register */ 684261019Sglebius base |= (u_int64_t) PCIB_READ_CONFIG(pcib, b, s, f, reg + 4, 4) << 32; 685240233Sglebius } 686261019Sglebius 687261019Sglebius if (bootverbose) { 688240737Sglebius printf("\tmap[%02x]: type %x, range %2d, base %08x, size %2d", 689240737Sglebius reg, pci_maptype(map), ln2range, 690261019Sglebius (unsigned int) base, ln2size); 691240737Sglebius if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) 692261019Sglebius printf(", port disabled\n"); 693261019Sglebius else if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) 694261019Sglebius printf(", memory disabled\n"); 695261019Sglebius else 696261019Sglebius printf(", enabled\n"); 697261019Sglebius } 698261019Sglebius 699261019Sglebius /* 700261019Sglebius * This code theoretically does the right thing, but has 701240233Sglebius * undesirable side effects in some cases where 702240233Sglebius * peripherals respond oddly to having these bits 703261019Sglebius * enabled. Leave them alone by default. 704261019Sglebius */ 705261019Sglebius#ifdef PCI_ENABLE_IO_MODES 706261019Sglebius /* Turn on resources that have been left off by a lazy BIOS */ 707261019Sglebius if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) { 708261019Sglebius cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2); 709261019Sglebius cmd |= PCIM_CMD_PORTEN; 710261019Sglebius PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, cmd, 2); 711261019Sglebius } 712261019Sglebius if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) { 713261019Sglebius cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2); 714261019Sglebius cmd |= PCIM_CMD_MEMEN; 715261019Sglebius PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, cmd, 2); 716261019Sglebius } 717261019Sglebius#else 718261019Sglebius if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) 719261019Sglebius return 1; 720261019Sglebius if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) 721261019Sglebius return 1; 722261019Sglebius#endif 723261019Sglebius 724261019Sglebius resource_list_add(rl, type, reg, 725240233Sglebius base, base + (1 << ln2size) - 1, 726240233Sglebius (1 << ln2size)); 727240233Sglebius 728223637Sbz return (ln2range == 64) ? 2 : 1; 729240233Sglebius} 730240233Sglebius 731240233Sglebiusstatic void 732240233Sglebiuspci_add_resources(device_t pcib, int b, int s, int f, device_t dev) 733223637Sbz{ 734240233Sglebius struct pci_devinfo *dinfo = device_get_ivars(dev); 735240233Sglebius pcicfgregs *cfg = &dinfo->cfg; 736240233Sglebius struct resource_list *rl = &dinfo->resources; 737240233Sglebius struct pci_quirk *q; 738240233Sglebius int i; 739240233Sglebius 740240233Sglebius for (i = 0; i < cfg->nummaps;) { 741240233Sglebius i += pci_add_map(pcib, b, s, f, PCIR_MAPS + i*4, rl); 742240233Sglebius } 743240233Sglebius 744240233Sglebius for (q = &pci_quirks[0]; q->devid; q++) { 745240233Sglebius if (q->devid == ((cfg->device << 16) | cfg->vendor) 746240233Sglebius && q->type == PCI_QUIRK_MAP_REG) 747240233Sglebius pci_add_map(pcib, b, s, f, q->arg1, rl); 748244347Spjd } 749240233Sglebius 750240233Sglebius if (cfg->intpin > 0 && cfg->intline != 255) 751240233Sglebius resource_list_add(rl, SYS_RES_IRQ, 0, 752240233Sglebius cfg->intline, cfg->intline, 1); 753240233Sglebius} 754240233Sglebius 755240233Sglebiusstatic void 756240233Sglebiuspci_add_children(device_t dev, int busno) 757240233Sglebius{ 758240233Sglebius device_t pcib = device_get_parent(dev); 759240233Sglebius int maxslots; 760251681Sglebius int s, f; 761240233Sglebius 762223637Sbz maxslots = PCIB_MAXSLOTS(pcib); 763223637Sbz 764240233Sglebius for (s = 0; s <= maxslots; s++) { 765240233Sglebius int pcifunchigh = 0; 766240233Sglebius for (f = 0; f <= pcifunchigh; f++) { 767240233Sglebius struct pci_devinfo *dinfo = 768240233Sglebius pci_read_device(pcib, busno, s, f); 769240233Sglebius if (dinfo != NULL) { 770244347Spjd if (dinfo->cfg.mfdev) 771240233Sglebius pcifunchigh = PCI_FUNCMAX; 772240233Sglebius 773240233Sglebius dinfo->cfg.dev = device_add_child(dev, NULL, -1); 774240233Sglebius device_set_ivars(dinfo->cfg.dev, dinfo); 775240233Sglebius pci_add_resources(pcib, busno, s, f, 776223637Sbz dinfo->cfg.dev); 777240233Sglebius pci_print_verbose(dinfo); 778240233Sglebius } 779240233Sglebius } 780240233Sglebius } 781240233Sglebius} 782240233Sglebius 783240233Sglebiusstatic int 784240233Sglebiuspci_probe(device_t dev) 785240233Sglebius{ 786240233Sglebius static int once, busno; 787240233Sglebius caddr_t vendordata, info; 788240233Sglebius 789240811Sglebius device_set_desc(dev, "PCI bus"); 790240233Sglebius 791240811Sglebius if (bootverbose) 792240811Sglebius device_printf(dev, "physical bus=%d\n", pcib_get_bus(dev)); 793240233Sglebius 794240811Sglebius /* 795240811Sglebius * Since there can be multiple independantly numbered PCI 796240233Sglebius * busses on some large alpha systems, we can't use the unit 797240233Sglebius * number to decide what bus we are probing. We ask the parent 798240233Sglebius * pcib what our bus number is. 799240233Sglebius */ 800223637Sbz busno = pcib_get_bus(dev); 801223637Sbz if (busno < 0) 802240233Sglebius return ENXIO; 803240233Sglebius pci_add_children(dev, busno); 804223637Sbz 805240233Sglebius if (!once) { 806240233Sglebius make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644, "pci"); 807240233Sglebius if ((vendordata = preload_search_by_type("pci_vendor_data")) != NULL) { 808240233Sglebius info = preload_search_info(vendordata, MODINFO_ADDR); 809240233Sglebius pci_vendordata = *(char **)info; 810223637Sbz info = preload_search_info(vendordata, MODINFO_SIZE); 811240233Sglebius pci_vendordata_size = *(size_t *)info; 812240233Sglebius /* terminate the database */ 813240233Sglebius pci_vendordata[pci_vendordata_size] = '\n'; 814240233Sglebius } 815240233Sglebius once++; 816240233Sglebius } 817240233Sglebius 818240233Sglebius return 0; 819240233Sglebius} 820240233Sglebius 821240233Sglebiusstatic int 822240233Sglebiuspci_print_resources(struct resource_list *rl, const char *name, int type, 823240233Sglebius const char *format) 824240233Sglebius{ 825240233Sglebius struct resource_list_entry *rle; 826240233Sglebius int printed, retval; 827240233Sglebius 828240233Sglebius printed = 0; 829240233Sglebius retval = 0; 830240233Sglebius /* Yes, this is kinda cheating */ 831240233Sglebius SLIST_FOREACH(rle, rl, link) { 832240233Sglebius if (rle->type == type) { 833240233Sglebius if (printed == 0) 834240233Sglebius retval += printf(" %s ", name); 835240233Sglebius else if (printed > 0) 836240811Sglebius retval += printf(","); 837240233Sglebius printed++; 838240233Sglebius retval += printf(format, rle->start); 839240233Sglebius if (rle->count > 1) { 840240233Sglebius retval += printf("-"); 841240233Sglebius retval += printf(format, rle->start + 842240233Sglebius rle->count - 1); 843240233Sglebius } 844240233Sglebius } 845240233Sglebius } 846240233Sglebius return retval; 847240233Sglebius} 848240233Sglebius 849240233Sglebiusstatic int 850240233Sglebiuspci_print_child(device_t dev, device_t child) 851240233Sglebius{ 852240233Sglebius struct pci_devinfo *dinfo; 853240233Sglebius struct resource_list *rl; 854240233Sglebius pcicfgregs *cfg; 855240233Sglebius int retval = 0; 856223637Sbz 857223637Sbz dinfo = device_get_ivars(child); 858223637Sbz cfg = &dinfo->cfg; 859240233Sglebius rl = &dinfo->resources; 860240233Sglebius 861130613Smlaier retval += bus_print_child_header(dev, child); 862223637Sbz 863240233Sglebius retval += pci_print_resources(rl, "port", SYS_RES_IOPORT, "%#lx"); 864240233Sglebius retval += pci_print_resources(rl, "mem", SYS_RES_MEMORY, "%#lx"); 865223637Sbz retval += pci_print_resources(rl, "irq", SYS_RES_IRQ, "%ld"); 866240233Sglebius if (device_get_flags(dev)) 867240233Sglebius retval += printf(" flags %#x", device_get_flags(dev)); 868240233Sglebius 869240233Sglebius retval += printf(" at device %d.%d", pci_get_slot(child), 870240233Sglebius pci_get_function(child)); 871240233Sglebius 872240233Sglebius retval += bus_print_child_footer(dev, child); 873240233Sglebius 874240233Sglebius return (retval); 875240233Sglebius} 876240233Sglebius 877240233Sglebiusstatic struct 878240233Sglebius{ 879240233Sglebius int class; 880240233Sglebius int subclass; 881240233Sglebius char *desc; 882240233Sglebius} pci_nomatch_tab[] = { 883240233Sglebius {PCIC_OLD, -1, "old"}, 884240233Sglebius {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"}, 885240233Sglebius {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"}, 886240233Sglebius {PCIC_STORAGE, -1, "mass storage"}, 887251681Sglebius {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"}, 888240233Sglebius {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"}, 889240233Sglebius {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"}, 890240233Sglebius {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"}, 891240233Sglebius {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"}, 892240233Sglebius {PCIC_NETWORK, -1, "network"}, 893240233Sglebius {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, 894240233Sglebius {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, 895240233Sglebius {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"}, 896240233Sglebius {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"}, 897251681Sglebius {PCIC_DISPLAY, -1, "display"}, 898251681Sglebius {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"}, 899251681Sglebius {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"}, 900251681Sglebius {PCIC_MULTIMEDIA, -1, "multimedia"}, 901251681Sglebius {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"}, 902251681Sglebius {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"}, 903251681Sglebius {PCIC_MEMORY, -1, "memory"}, 904251681Sglebius {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"}, 905251681Sglebius {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"}, 906251681Sglebius {PCIC_BRIDGE, -1, "bridge"}, 907251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"}, 908251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"}, 909251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"}, 910251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"}, 911251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"}, 912251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"}, 913251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"}, 914251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"}, 915251681Sglebius {PCIC_BRIDGE, PCIS_BRIDGE_OTHER, "PCI-unknown"}, 916251681Sglebius {PCIC_SIMPLECOMM, -1, "simple comms"}, 917251681Sglebius {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */ 918251681Sglebius {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"}, 919251681Sglebius {PCIC_BASEPERIPH, -1, "base peripheral"}, 920251681Sglebius {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"}, 921251681Sglebius {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"}, 922251681Sglebius {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"}, 923251681Sglebius {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"}, 924251681Sglebius {PCIC_INPUTDEV, -1, "input device"}, 925251681Sglebius {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"}, 926251681Sglebius {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"}, 927251681Sglebius {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"}, 928251681Sglebius {PCIC_DOCKING, -1, "docking station"}, 929251681Sglebius {PCIC_PROCESSOR, -1, "processor"}, 930251681Sglebius {PCIC_SERIALBUS, -1, "serial bus"}, 931240233Sglebius {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"}, 932240233Sglebius {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"}, 933240233Sglebius {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"}, 934251681Sglebius {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"}, 935240233Sglebius {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"}, 936240233Sglebius {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"}, 937240233Sglebius {0, 0, NULL} 938240233Sglebius}; 939240233Sglebius 940240233Sglebiusstatic void 941240233Sglebiuspci_probe_nomatch(device_t dev, device_t child) 942240233Sglebius{ 943240233Sglebius int i; 944240233Sglebius char *cp, *scp, *device; 945240233Sglebius 946240233Sglebius /* 947240233Sglebius * Look for a listing for this device in a loaded device database. 948240233Sglebius */ 949240233Sglebius if ((device = pci_describe_device(child)) != NULL) { 950223637Sbz device_printf(dev, "<%s>", device); 951240233Sglebius free(device, M_DEVBUF); 952240233Sglebius } else { 953251681Sglebius /* 954251681Sglebius * Scan the class/subclass descriptions for a general description. 955251681Sglebius */ 956251681Sglebius cp = "unknown"; 957251681Sglebius scp = NULL; 958251681Sglebius for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { 959251681Sglebius if (pci_nomatch_tab[i].class == pci_get_class(child)) { 960251681Sglebius if (pci_nomatch_tab[i].subclass == -1) { 961251681Sglebius cp = pci_nomatch_tab[i].desc; 962251681Sglebius } else if (pci_nomatch_tab[i].subclass == pci_get_subclass(child)) { 963251681Sglebius scp = pci_nomatch_tab[i].desc; 964240233Sglebius } 965223637Sbz } 966251681Sglebius } 967240233Sglebius device_printf(dev, "<%s%s%s>", 968223637Sbz cp ? : "", 969223637Sbz ((cp != NULL) && (scp != NULL)) ? ", " : "", 970223637Sbz scp ? : ""); 971223637Sbz } 972223637Sbz printf(" at %d.%d (no driver attached)\n", 973223637Sbz pci_get_slot(child), 974223637Sbz pci_get_function(child)); 975223637Sbz return; 976223637Sbz} 977223637Sbz 978223637Sbz/* 979223637Sbz * Parse the PCI device database, if loaded, and return a pointer to a 980223637Sbz * description of the device. 981240233Sglebius * 982223637Sbz * The database is flat text formatted as follows: 983223637Sbz * 984223637Sbz * Any line not in a valid format is ignored. 985223637Sbz * Lines are terminated with newline '\n' characters. 986223637Sbz * 987223637Sbz * A VENDOR line consists of the 4 digit (hex) vendor code, a TAB, then 988240233Sglebius * the vendor name. 989251681Sglebius * 990240233Sglebius * A DEVICE line is entered immediately below the corresponding VENDOR ID. 991240233Sglebius * - devices cannot be listed without a corresponding VENDOR line. 992240233Sglebius * A DEVICE line consists of a TAB, the 4 digit (hex) device code, 993250522Sglebius * another TAB, then the device name. 994223637Sbz */ 995223637Sbz 996240233Sglebius/* 997240233Sglebius * Assuming (ptr) points to the beginning of a line in the database, 998240233Sglebius * return the vendor or device and description of the next entry. 999223637Sbz * The value of (vendor) or (device) inappropriate for the entry type 1000240233Sglebius * is set to -1. Returns nonzero at the end of the database. 1001240233Sglebius * 1002223637Sbz * Note that this is slightly unrobust in the face of corrupt data; 1003126258Smlaier * we attempt to safeguard against this by spamming the end of the 1004126258Smlaier * database with a newline when we initialise. 1005240233Sglebius */ 1006240233Sglebiusstatic int 1007223637Sbzpci_describe_parse_line(char **ptr, int *vendor, int *device, char **desc) 1008240233Sglebius{ 1009223637Sbz char *cp = *ptr; 1010240233Sglebius int left; 1011223637Sbz 1012251681Sglebius *device = -1; 1013251681Sglebius *vendor = -1; 1014251681Sglebius **desc = '\0'; 1015251681Sglebius for (;;) { 1016251681Sglebius left = pci_vendordata_size - (cp - pci_vendordata); 1017251681Sglebius if (left <= 0) { 1018251681Sglebius *ptr = cp; 1019240233Sglebius return(1); 1020240233Sglebius } 1021240233Sglebius 1022240233Sglebius /* vendor entry? */ 1023240233Sglebius if (*cp != '\t' && sscanf(cp, "%x\t%80[^\n]", vendor, *desc) == 2) 1024240233Sglebius break; 1025240233Sglebius /* device entry? */ 1026240233Sglebius if (*cp == '\t' && sscanf(cp, "%x\t%80[^\n]", device, *desc) == 2) 1027240233Sglebius break; 1028240233Sglebius 1029240233Sglebius /* skip to next line */ 1030240233Sglebius while (*cp != '\n' && left > 0) { 1031240233Sglebius cp++; 1032240233Sglebius left--; 1033251681Sglebius } 1034240233Sglebius if (*cp == '\n') { 1035240233Sglebius cp++; 1036240233Sglebius left--; 1037240233Sglebius } 1038240233Sglebius } 1039251681Sglebius /* skip to next line */ 1040251681Sglebius while (*cp != '\n' && left > 0) { 1041251681Sglebius cp++; 1042240233Sglebius left--; 1043240233Sglebius } 1044240233Sglebius if (*cp == '\n' && left > 0) 1045223637Sbz cp++; 1046251681Sglebius *ptr = cp; 1047223637Sbz return(0); 1048223637Sbz} 1049240233Sglebius 1050223637Sbzstatic char * 1051223637Sbzpci_describe_device(device_t dev) 1052240233Sglebius{ 1053240233Sglebius int vendor, device; 1054223637Sbz char *desc, *vp, *dp, *line; 1055240233Sglebius 1056240233Sglebius desc = vp = dp = NULL; 1057240233Sglebius 1058240233Sglebius /* 1059240233Sglebius * If we have no vendor data, we can't do anything. 1060240233Sglebius */ 1061240233Sglebius if (pci_vendordata == NULL) 1062240233Sglebius goto out; 1063240233Sglebius 1064240233Sglebius /* 1065240233Sglebius * Scan the vendor data looking for this device 1066240233Sglebius */ 1067240233Sglebius line = pci_vendordata; 1068240233Sglebius if ((vp = malloc(80, M_DEVBUF, M_NOWAIT)) == NULL) 1069240233Sglebius goto out; 1070223637Sbz for (;;) { 1071240233Sglebius if (pci_describe_parse_line(&line, &vendor, &device, &vp)) 1072240233Sglebius goto out; 1073240233Sglebius if (vendor == pci_get_vendor(dev)) 1074240233Sglebius break; 1075240233Sglebius } 1076240233Sglebius if ((dp = malloc(80, M_DEVBUF, M_NOWAIT)) == NULL) 1077240233Sglebius goto out; 1078223637Sbz for (;;) { 1079223637Sbz if (pci_describe_parse_line(&line, &vendor, &device, &dp)) { 1080240233Sglebius *dp = 0; 1081223637Sbz break; 1082223637Sbz } 1083240233Sglebius if (vendor != -1) { 1084240233Sglebius *dp = 0; 1085240233Sglebius break; 1086223637Sbz } 1087240233Sglebius if (device == pci_get_device(dev)) 1088223637Sbz break; 1089240233Sglebius } 1090240233Sglebius if (dp[0] == '\0') 1091223637Sbz snprintf(dp, 80, "0x%x", pci_get_device(dev)); 1092240233Sglebius if ((desc = malloc(strlen(vp) + strlen(dp) + 3, M_DEVBUF, M_NOWAIT)) != NULL) 1093240233Sglebius sprintf(desc, "%s, %s", vp, dp); 1094240233Sglebius out: 1095223637Sbz if (vp != NULL) 1096223637Sbz free(vp, M_DEVBUF); 1097223637Sbz if (dp != NULL) 1098240233Sglebius free(dp, M_DEVBUF); 1099240233Sglebius return(desc); 1100240233Sglebius} 1101240233Sglebius 1102240233Sglebiusstatic int 1103240233Sglebiuspci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 1104240233Sglebius{ 1105240233Sglebius struct pci_devinfo *dinfo; 1106240233Sglebius pcicfgregs *cfg; 1107240233Sglebius 1108240233Sglebius dinfo = device_get_ivars(child); 1109240233Sglebius cfg = &dinfo->cfg; 1110223637Sbz 1111240233Sglebius switch (which) { 1112240233Sglebius case PCI_IVAR_SUBVENDOR: 1113223637Sbz *result = cfg->subvendor; 1114240233Sglebius break; 1115223637Sbz case PCI_IVAR_SUBDEVICE: 1116240233Sglebius *result = cfg->subdevice; 1117240233Sglebius break; 1118223637Sbz case PCI_IVAR_VENDOR: 1119223637Sbz *result = cfg->vendor; 1120240233Sglebius break; 1121240233Sglebius case PCI_IVAR_DEVICE: 1122240233Sglebius *result = cfg->device; 1123240233Sglebius break; 1124240233Sglebius case PCI_IVAR_DEVID: 1125240233Sglebius *result = (cfg->device << 16) | cfg->vendor; 1126240233Sglebius break; 1127223637Sbz case PCI_IVAR_CLASS: 1128223637Sbz *result = cfg->baseclass; 1129223637Sbz break; 1130240233Sglebius case PCI_IVAR_SUBCLASS: 1131240233Sglebius *result = cfg->subclass; 1132223637Sbz break; 1133240233Sglebius case PCI_IVAR_PROGIF: 1134223637Sbz *result = cfg->progif; 1135240233Sglebius break; 1136240233Sglebius case PCI_IVAR_REVID: 1137240233Sglebius *result = cfg->revid; 1138223637Sbz break; 1139240233Sglebius case PCI_IVAR_INTPIN: 1140223637Sbz *result = cfg->intpin; 1141240233Sglebius break; 1142223637Sbz case PCI_IVAR_IRQ: 1143223637Sbz *result = cfg->intline; 1144223637Sbz break; 1145223637Sbz case PCI_IVAR_BUS: 1146223637Sbz *result = cfg->bus; 1147223637Sbz break; 1148240233Sglebius case PCI_IVAR_SLOT: 1149240233Sglebius *result = cfg->slot; 1150250522Sglebius break; 1151223637Sbz case PCI_IVAR_FUNCTION: 1152240233Sglebius *result = cfg->func; 1153240233Sglebius break; 1154240233Sglebius default: 1155240233Sglebius return ENOENT; 1156240233Sglebius } 1157240233Sglebius return 0; 1158223637Sbz} 1159223637Sbz 1160223637Sbzstatic int 1161240233Sglebiuspci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 1162240233Sglebius{ 1163240233Sglebius struct pci_devinfo *dinfo; 1164240233Sglebius pcicfgregs *cfg; 1165240233Sglebius 1166223637Sbz dinfo = device_get_ivars(child); 1167130613Smlaier cfg = &dinfo->cfg; 1168240233Sglebius 1169251681Sglebius switch (which) { 1170250522Sglebius case PCI_IVAR_SUBVENDOR: 1171250522Sglebius case PCI_IVAR_SUBDEVICE: 1172244184Sglebius case PCI_IVAR_VENDOR: 1173240233Sglebius case PCI_IVAR_DEVICE: 1174251681Sglebius case PCI_IVAR_DEVID: 1175240233Sglebius case PCI_IVAR_CLASS: 1176240233Sglebius case PCI_IVAR_SUBCLASS: 1177240233Sglebius case PCI_IVAR_PROGIF: 1178240233Sglebius case PCI_IVAR_REVID: 1179240233Sglebius case PCI_IVAR_INTPIN: 1180240233Sglebius case PCI_IVAR_IRQ: 1181223637Sbz case PCI_IVAR_BUS: 1182250521Sglebius case PCI_IVAR_SLOT: 1183250312Sglebius case PCI_IVAR_FUNCTION: 1184240233Sglebius return EINVAL; /* disallow for now */ 1185240233Sglebius 1186130613Smlaier default: 1187223637Sbz return ENOENT; 1188250522Sglebius } 1189130613Smlaier return 0; 1190240233Sglebius} 1191240233Sglebius 1192240233Sglebiusstatic struct resource * 1193240233Sglebiuspci_alloc_resource(device_t dev, device_t child, int type, int *rid, 1194223637Sbz u_long start, u_long end, u_long count, u_int flags) 1195223637Sbz{ 1196223637Sbz struct pci_devinfo *dinfo = device_get_ivars(child); 1197240233Sglebius struct resource_list *rl = &dinfo->resources; 1198240233Sglebius pcicfgregs *cfg = &dinfo->cfg; 1199126258Smlaier 1200126258Smlaier /* 1201126258Smlaier * Perform lazy resource allocation 1202240233Sglebius * 1203240233Sglebius * XXX add support here for SYS_RES_IOPORT and SYS_RES_MEMORY 1204240233Sglebius */ 1205223637Sbz if (device_get_parent(child) == dev) { 1206240233Sglebius /* 1207223637Sbz * If device doesn't have an interrupt routed, and is deserving of 1208240233Sglebius * an interrupt, try to assign it one. 1209240233Sglebius */ 1210240233Sglebius if ((type == SYS_RES_IRQ) && (cfg->intline == 255 || cfg->intline == 0) && (cfg->intpin != 0)) { 1211223637Sbz cfg->intline = PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, 1212223637Sbz cfg->intpin); 1213240233Sglebius if (cfg->intline != 255) { 1214223637Sbz pci_write_config(child, PCIR_INTLINE, cfg->intline, 1); 1215240233Sglebius resource_list_add(rl, SYS_RES_IRQ, 0, 1216240233Sglebius cfg->intline, cfg->intline, 1); 1217240233Sglebius } 1218240233Sglebius } 1219223637Sbz } 1220240233Sglebius 1221240233Sglebius return resource_list_alloc(rl, dev, child, type, rid, 1222240233Sglebius start, end, count, flags); 1223240233Sglebius} 1224223637Sbz 1225223637Sbzstatic void 1226240233Sglebiuspci_delete_resource(device_t dev, device_t child, int type, int rid) 1227240233Sglebius{ 1228240233Sglebius printf("pci_delete_resource: PCI resources can not be deleted\n"); 1229240233Sglebius} 1230240233Sglebius 1231240233Sglebiusstatic struct resource_list * 1232223637Sbzpci_get_resource_list (device_t dev, device_t child) 1233240233Sglebius{ 1234223637Sbz struct pci_devinfo * dinfo = device_get_ivars(child); 1235240233Sglebius struct resource_list * rl = &dinfo->resources; 1236240233Sglebius 1237223637Sbz if (!rl) 1238223637Sbz return (NULL); 1239223637Sbz 1240240233Sglebius return (rl); 1241240233Sglebius} 1242240233Sglebius 1243240233Sglebiusstatic u_int32_t 1244240233Sglebiuspci_read_config_method(device_t dev, device_t child, int reg, int width) 1245240233Sglebius{ 1246240233Sglebius struct pci_devinfo *dinfo = device_get_ivars(child); 1247240233Sglebius pcicfgregs *cfg = &dinfo->cfg; 1248240233Sglebius 1249223637Sbz return PCIB_READ_CONFIG(device_get_parent(dev), 1250223637Sbz cfg->bus, cfg->slot, cfg->func, 1251240233Sglebius reg, width); 1252223637Sbz} 1253240233Sglebius 1254240233Sglebiusstatic void 1255240233Sglebiuspci_write_config_method(device_t dev, device_t child, int reg, 1256240233Sglebius u_int32_t val, int width) 1257240233Sglebius{ 1258261018Sglebius struct pci_devinfo *dinfo = device_get_ivars(child); 1259240233Sglebius pcicfgregs *cfg = &dinfo->cfg; 1260261018Sglebius 1261261018Sglebius PCIB_WRITE_CONFIG(device_get_parent(dev), 1262261018Sglebius cfg->bus, cfg->slot, cfg->func, 1263240233Sglebius reg, val, width); 1264240233Sglebius} 1265240233Sglebius 1266240233Sglebiusstatic int 1267240233Sglebiuspci_modevent(module_t mod, int what, void *arg) 1268240233Sglebius{ 1269240233Sglebius switch (what) { 1270223637Sbz case MOD_LOAD: 1271223637Sbz STAILQ_INIT(&pci_devq); 1272223637Sbz pci_generation = 0; 1273223637Sbz break; 1274223637Sbz 1275223637Sbz case MOD_UNLOAD: 1276223637Sbz break; 1277240233Sglebius } 1278223637Sbz 1279240233Sglebius return 0; 1280240233Sglebius} 1281223637Sbz