pci.c revision 184141
160484Sobrien/*- 278828Sobrien * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 378828Sobrien * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 460484Sobrien * Copyright (c) 2000, BSDi 560484Sobrien * All rights reserved. 660484Sobrien * 760484Sobrien * Redistribution and use in source and binary forms, with or without 860484Sobrien * modification, are permitted provided that the following conditions 960484Sobrien * are met: 1060484Sobrien * 1. Redistributions of source code must retain the above copyright 1160484Sobrien * notice unmodified, this list of conditions, and the following 1260484Sobrien * disclaimer. 1360484Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1460484Sobrien * notice, this list of conditions and the following disclaimer in the 1560484Sobrien * documentation and/or other materials provided with the distribution. 1660484Sobrien * 1760484Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1860484Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1960484Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2060484Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2160484Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2260484Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2360484Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2460484Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2560484Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2660484Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2760484Sobrien */ 2860484Sobrien 2960484Sobrien#include <sys/cdefs.h> 3060484Sobrien__FBSDID("$FreeBSD: head/sys/dev/pci/pci.c 184141 2008-10-21 21:53:55Z mav $"); 3160484Sobrien 3260484Sobrien#include "opt_bus.h" 3360484Sobrien 3460484Sobrien#include <sys/param.h> 3560484Sobrien#include <sys/systm.h> 3660484Sobrien#include <sys/malloc.h> 3760484Sobrien#include <sys/module.h> 3860484Sobrien#include <sys/linker.h> 3960484Sobrien#include <sys/fcntl.h> 4060484Sobrien#include <sys/conf.h> 4189857Sobrien#include <sys/kernel.h> 4289857Sobrien#include <sys/queue.h> 4389857Sobrien#include <sys/sysctl.h> 4460484Sobrien#include <sys/endian.h> 4560484Sobrien 4660484Sobrien#include <vm/vm.h> 4760484Sobrien#include <vm/pmap.h> 4860484Sobrien#include <vm/vm_extern.h> 4960484Sobrien 5060484Sobrien#include <sys/bus.h> 5160484Sobrien#include <machine/bus.h> 5260484Sobrien#include <sys/rman.h> 5360484Sobrien#include <machine/resource.h> 5460484Sobrien 5560484Sobrien#if defined(__i386__) || defined(__amd64__) 5660484Sobrien#include <machine/intr_machdep.h> 5760484Sobrien#endif 5860484Sobrien 5960484Sobrien#include <sys/pciio.h> 6060484Sobrien#include <dev/pci/pcireg.h> 6160484Sobrien#include <dev/pci/pcivar.h> 6260484Sobrien#include <dev/pci/pci_private.h> 6360484Sobrien 6460484Sobrien#include "pcib_if.h" 6560484Sobrien#include "pci_if.h" 6660484Sobrien 6760484Sobrien#ifdef __HAVE_ACPI 6860484Sobrien#include <contrib/dev/acpica/acpi.h> 6960484Sobrien#include "acpi_if.h" 7060484Sobrien#else 7160484Sobrien#define ACPI_PWR_FOR_SLEEP(x, y, z) 7260484Sobrien#endif 7360484Sobrien 7460484Sobrienstatic uint32_t pci_mapbase(unsigned mapreg); 7560484Sobrienstatic const char *pci_maptype(unsigned mapreg); 7660484Sobrienstatic int pci_mapsize(unsigned testval); 7760484Sobrienstatic int pci_maprange(unsigned mapreg); 7860484Sobrienstatic void pci_fixancient(pcicfgregs *cfg); 7960484Sobrien 8060484Sobrienstatic int pci_porten(device_t pcib, int b, int s, int f); 8160484Sobrienstatic int pci_memen(device_t pcib, int b, int s, int f); 8260484Sobrienstatic void pci_assign_interrupt(device_t bus, device_t dev, 8360484Sobrien int force_route); 8460484Sobrienstatic int pci_add_map(device_t pcib, device_t bus, device_t dev, 8560484Sobrien int b, int s, int f, int reg, 8660484Sobrien struct resource_list *rl, int force, int prefetch); 8760484Sobrienstatic int pci_probe(device_t dev); 8860484Sobrienstatic int pci_attach(device_t dev); 8960484Sobrienstatic void pci_load_vendor_data(void); 9060484Sobrienstatic int pci_describe_parse_line(char **ptr, int *vendor, 9160484Sobrien int *device, char **desc); 9260484Sobrienstatic char *pci_describe_device(device_t dev); 9360484Sobrienstatic int pci_modevent(module_t mod, int what, void *arg); 9460484Sobrienstatic void pci_hdrtypedata(device_t pcib, int b, int s, int f, 9560484Sobrien pcicfgregs *cfg); 9660484Sobrienstatic void pci_read_extcap(device_t pcib, pcicfgregs *cfg); 9760484Sobrienstatic int pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg, 9860484Sobrien int reg, uint32_t *data); 9960484Sobrien#if 0 10060484Sobrienstatic int pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg, 10160484Sobrien int reg, uint32_t data); 10260484Sobrien#endif 10360484Sobrienstatic void pci_read_vpd(device_t pcib, pcicfgregs *cfg); 10460484Sobrienstatic void pci_disable_msi(device_t dev); 10560484Sobrienstatic void pci_enable_msi(device_t dev, uint64_t address, 10660484Sobrien uint16_t data); 10760484Sobrienstatic void pci_enable_msix(device_t dev, u_int index, 10860484Sobrien uint64_t address, uint32_t data); 10960484Sobrienstatic void pci_mask_msix(device_t dev, u_int index); 11089857Sobrienstatic void pci_unmask_msix(device_t dev, u_int index); 11189857Sobrienstatic int pci_msi_blacklisted(void); 11289857Sobrienstatic void pci_resume_msi(device_t dev); 11389857Sobrienstatic void pci_resume_msix(device_t dev); 11489857Sobrien 11589857Sobrienstatic device_method_t pci_methods[] = { 11660484Sobrien /* Device interface */ 11760484Sobrien DEVMETHOD(device_probe, pci_probe), 11860484Sobrien DEVMETHOD(device_attach, pci_attach), 11960484Sobrien DEVMETHOD(device_detach, bus_generic_detach), 12060484Sobrien DEVMETHOD(device_shutdown, bus_generic_shutdown), 12160484Sobrien DEVMETHOD(device_suspend, pci_suspend), 12260484Sobrien DEVMETHOD(device_resume, pci_resume), 12360484Sobrien 12460484Sobrien /* Bus interface */ 12560484Sobrien DEVMETHOD(bus_print_child, pci_print_child), 12660484Sobrien DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), 12760484Sobrien DEVMETHOD(bus_read_ivar, pci_read_ivar), 12860484Sobrien DEVMETHOD(bus_write_ivar, pci_write_ivar), 12960484Sobrien DEVMETHOD(bus_driver_added, pci_driver_added), 13060484Sobrien DEVMETHOD(bus_setup_intr, pci_setup_intr), 13160484Sobrien DEVMETHOD(bus_teardown_intr, pci_teardown_intr), 13260484Sobrien 13360484Sobrien DEVMETHOD(bus_get_resource_list,pci_get_resource_list), 13460484Sobrien DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 13577298Sobrien DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 13677298Sobrien DEVMETHOD(bus_delete_resource, pci_delete_resource), 13777298Sobrien DEVMETHOD(bus_alloc_resource, pci_alloc_resource), 13877298Sobrien DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 13977298Sobrien DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 14077298Sobrien DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 14177298Sobrien DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method), 14277298Sobrien DEVMETHOD(bus_child_location_str, pci_child_location_str_method), 14377298Sobrien 14477298Sobrien /* PCI interface */ 14577298Sobrien DEVMETHOD(pci_read_config, pci_read_config_method), 14677298Sobrien DEVMETHOD(pci_write_config, pci_write_config_method), 14777298Sobrien DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method), 14877298Sobrien DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method), 14977298Sobrien DEVMETHOD(pci_enable_io, pci_enable_io_method), 15077298Sobrien DEVMETHOD(pci_disable_io, pci_disable_io_method), 15177298Sobrien DEVMETHOD(pci_get_vpd_ident, pci_get_vpd_ident_method), 15260484Sobrien DEVMETHOD(pci_get_vpd_readonly, pci_get_vpd_readonly_method), 15377298Sobrien DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method), 15460484Sobrien DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method), 15577298Sobrien DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method), 15660484Sobrien DEVMETHOD(pci_find_extcap, pci_find_extcap_method), 15760484Sobrien DEVMETHOD(pci_alloc_msi, pci_alloc_msi_method), 15860484Sobrien DEVMETHOD(pci_alloc_msix, pci_alloc_msix_method), 15960484Sobrien DEVMETHOD(pci_remap_msix, pci_remap_msix_method), 16060484Sobrien DEVMETHOD(pci_release_msi, pci_release_msi_method), 16160484Sobrien DEVMETHOD(pci_msi_count, pci_msi_count_method), 16260484Sobrien DEVMETHOD(pci_msix_count, pci_msix_count_method), 16360484Sobrien 16460484Sobrien { 0, 0 } 16560484Sobrien}; 16660484Sobrien 16760484SobrienDEFINE_CLASS_0(pci, pci_driver, pci_methods, 0); 16860484Sobrien 16960484Sobrienstatic devclass_t pci_devclass; 17060484SobrienDRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); 17160484SobrienMODULE_VERSION(pci, 1); 17260484Sobrien 17360484Sobrienstatic char *pci_vendordata; 17460484Sobrienstatic size_t pci_vendordata_size; 17560484Sobrien 17660484Sobrien 17760484Sobrienstruct pci_quirk { 17860484Sobrien uint32_t devid; /* Vendor/device of the card */ 17960484Sobrien int type; 18060484Sobrien#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */ 18160484Sobrien#define PCI_QUIRK_DISABLE_MSI 2 /* MSI/MSI-X doesn't work */ 18260484Sobrien int arg1; 18360484Sobrien int arg2; 18460484Sobrien}; 18560484Sobrien 18660484Sobrienstruct pci_quirk pci_quirks[] = { 18760484Sobrien /* The Intel 82371AB and 82443MX has a map register at offset 0x90. */ 18860484Sobrien { 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 }, 18960484Sobrien { 0x719b8086, PCI_QUIRK_MAP_REG, 0x90, 0 }, 19060484Sobrien /* As does the Serverworks OSB4 (the SMBus mapping register) */ 19160484Sobrien { 0x02001166, PCI_QUIRK_MAP_REG, 0x90, 0 }, 19260484Sobrien 19360484Sobrien /* 19460484Sobrien * MSI doesn't work with the ServerWorks CNB20-HE Host Bridge 19560484Sobrien * or the CMIC-SL (AKA ServerWorks GC_LE). 19660484Sobrien */ 19760484Sobrien { 0x00141166, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 19860484Sobrien { 0x00171166, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 19960484Sobrien 20060484Sobrien /* 20160484Sobrien * MSI doesn't work on earlier Intel chipsets including 20260484Sobrien * E7500, E7501, E7505, 845, 865, 875/E7210, and 855. 20360484Sobrien */ 20460484Sobrien { 0x25408086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 20560484Sobrien { 0x254c8086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 20660484Sobrien { 0x25508086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 20760484Sobrien { 0x25608086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 20860484Sobrien { 0x25708086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 20960484Sobrien { 0x25788086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 21060484Sobrien { 0x35808086, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 21160484Sobrien 21260484Sobrien /* 21360484Sobrien * MSI doesn't work with devices behind the AMD 8131 HT-PCIX 21460484Sobrien * bridge. 21560484Sobrien */ 21660484Sobrien { 0x74501022, PCI_QUIRK_DISABLE_MSI, 0, 0 }, 21760484Sobrien 21860484Sobrien { 0 } 21960484Sobrien}; 22060484Sobrien 22160484Sobrien/* map register information */ 22260484Sobrien#define PCI_MAPMEM 0x01 /* memory map */ 22360484Sobrien#define PCI_MAPMEMP 0x02 /* prefetchable memory map */ 22460484Sobrien#define PCI_MAPPORT 0x04 /* port map */ 22560484Sobrien 22660484Sobrienstruct devlist pci_devq; 22760484Sobrienuint32_t pci_generation; 22860484Sobrienuint32_t pci_numdevs = 0; 22960484Sobrienstatic int pcie_chipset, pcix_chipset; 23060484Sobrien 23160484Sobrien/* sysctl vars */ 23260484SobrienSYSCTL_NODE(_hw, OID_AUTO, pci, CTLFLAG_RD, 0, "PCI bus tuning parameters"); 23360484Sobrien 23460484Sobrienstatic int pci_enable_io_modes = 1; 23560484SobrienTUNABLE_INT("hw.pci.enable_io_modes", &pci_enable_io_modes); 23660484SobrienSYSCTL_INT(_hw_pci, OID_AUTO, enable_io_modes, CTLFLAG_RW, 23760484Sobrien &pci_enable_io_modes, 1, 23860484Sobrien "Enable I/O and memory bits in the config register. Some BIOSes do not\n\ 23960484Sobrienenable these bits correctly. We'd like to do this all the time, but there\n\ 24060484Sobrienare some peripherals that this causes problems with."); 24160484Sobrien 24260484Sobrienstatic int pci_do_power_nodriver = 0; 24360484SobrienTUNABLE_INT("hw.pci.do_power_nodriver", &pci_do_power_nodriver); 24460484SobrienSYSCTL_INT(_hw_pci, OID_AUTO, do_power_nodriver, CTLFLAG_RW, 24560484Sobrien &pci_do_power_nodriver, 0, 24660484Sobrien "Place a function into D3 state when no driver attaches to it. 0 means\n\ 24760484Sobriendisable. 1 means conservatively place devices into D3 state. 2 means\n\ 24877298Sobrienagressively place devices into D3 state. 3 means put absolutely everything\n\ 24960484Sobrienin D3 state."); 25060484Sobrien 25160484Sobrienstatic int pci_do_power_resume = 1; 25260484SobrienTUNABLE_INT("hw.pci.do_power_resume", &pci_do_power_resume); 25360484SobrienSYSCTL_INT(_hw_pci, OID_AUTO, do_power_resume, CTLFLAG_RW, 25460484Sobrien &pci_do_power_resume, 1, 25560484Sobrien "Transition from D3 -> D0 on resume."); 25660484Sobrien 25760484Sobrienstatic int pci_do_msi = 1; 25860484SobrienTUNABLE_INT("hw.pci.enable_msi", &pci_do_msi); 25960484SobrienSYSCTL_INT(_hw_pci, OID_AUTO, enable_msi, CTLFLAG_RW, &pci_do_msi, 1, 26060484Sobrien "Enable support for MSI interrupts"); 26160484Sobrien 26260484Sobrienstatic int pci_do_msix = 1; 26360484SobrienTUNABLE_INT("hw.pci.enable_msix", &pci_do_msix); 26460484SobrienSYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1, 26560484Sobrien "Enable support for MSI-X interrupts"); 26660484Sobrien 26760484Sobrienstatic int pci_honor_msi_blacklist = 1; 26860484SobrienTUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist); 26960484SobrienSYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD, 27060484Sobrien &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI"); 27160484Sobrien 27260484Sobrien/* Find a device_t by bus/slot/function in domain 0 */ 27360484Sobrien 27460484Sobriendevice_t 27560484Sobrienpci_find_bsf(uint8_t bus, uint8_t slot, uint8_t func) 27660484Sobrien{ 27760484Sobrien 27860484Sobrien return (pci_find_dbsf(0, bus, slot, func)); 27960484Sobrien} 28060484Sobrien 28177298Sobrien/* Find a device_t by domain/bus/slot/function */ 28260484Sobrien 28360484Sobriendevice_t 28460484Sobrienpci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func) 28560484Sobrien{ 28660484Sobrien struct pci_devinfo *dinfo; 28760484Sobrien 28860484Sobrien STAILQ_FOREACH(dinfo, &pci_devq, pci_links) { 28960484Sobrien if ((dinfo->cfg.domain == domain) && 29060484Sobrien (dinfo->cfg.bus == bus) && 29160484Sobrien (dinfo->cfg.slot == slot) && 29260484Sobrien (dinfo->cfg.func == func)) { 29360484Sobrien return (dinfo->cfg.dev); 29460484Sobrien } 29560484Sobrien } 29660484Sobrien 29760484Sobrien return (NULL); 29860484Sobrien} 29960484Sobrien 30060484Sobrien/* Find a device_t by vendor/device ID */ 30160484Sobrien 30260484Sobriendevice_t 30360484Sobrienpci_find_device(uint16_t vendor, uint16_t device) 30460484Sobrien{ 30560484Sobrien struct pci_devinfo *dinfo; 30660484Sobrien 30760484Sobrien STAILQ_FOREACH(dinfo, &pci_devq, pci_links) { 30860484Sobrien if ((dinfo->cfg.vendor == vendor) && 30960484Sobrien (dinfo->cfg.device == device)) { 31060484Sobrien return (dinfo->cfg.dev); 31160484Sobrien } 31260484Sobrien } 31377298Sobrien 31460484Sobrien return (NULL); 31560484Sobrien} 31660484Sobrien 31760484Sobrien/* return base address of memory or port map */ 31860484Sobrien 31960484Sobrienstatic uint32_t 32060484Sobrienpci_mapbase(uint32_t mapreg) 32160484Sobrien{ 32260484Sobrien 32360484Sobrien if (PCI_BAR_MEM(mapreg)) 32460484Sobrien return (mapreg & PCIM_BAR_MEM_BASE); 32560484Sobrien else 32660484Sobrien return (mapreg & PCIM_BAR_IO_BASE); 32760484Sobrien} 32877298Sobrien 32960484Sobrien/* return map type of memory or port map */ 33060484Sobrien 33160484Sobrienstatic const char * 33260484Sobrienpci_maptype(unsigned mapreg) 33360484Sobrien{ 33460484Sobrien 33560484Sobrien if (PCI_BAR_IO(mapreg)) 33660484Sobrien return ("I/O Port"); 33760484Sobrien if (mapreg & PCIM_BAR_MEM_PREFETCH) 33860484Sobrien return ("Prefetchable Memory"); 33960484Sobrien return ("Memory"); 34060484Sobrien} 34160484Sobrien 34260484Sobrien/* return log2 of map size decoded for memory or port map */ 34377298Sobrien 34460484Sobrienstatic int 34560484Sobrienpci_mapsize(uint32_t testval) 34660484Sobrien{ 34760484Sobrien int ln2size; 34860484Sobrien 34960484Sobrien testval = pci_mapbase(testval); 35060484Sobrien ln2size = 0; 35160484Sobrien if (testval != 0) { 35260484Sobrien while ((testval & 1) == 0) 35360484Sobrien { 35460484Sobrien ln2size++; 35560484Sobrien testval >>= 1; 35660484Sobrien } 35760484Sobrien } 35860484Sobrien return (ln2size); 35960484Sobrien} 36077298Sobrien 36160484Sobrien/* return log2 of address range supported by map register */ 36260484Sobrien 36360484Sobrienstatic int 36460484Sobrienpci_maprange(unsigned mapreg) 36560484Sobrien{ 36660484Sobrien int ln2range = 0; 36760484Sobrien 36860484Sobrien if (PCI_BAR_IO(mapreg)) 36960484Sobrien ln2range = 32; 37060484Sobrien else 37160484Sobrien switch (mapreg & PCIM_BAR_MEM_TYPE) { 37260484Sobrien case PCIM_BAR_MEM_32: 37360484Sobrien ln2range = 32; 37460484Sobrien break; 37560484Sobrien case PCIM_BAR_MEM_1MB: 37660484Sobrien ln2range = 20; 37760484Sobrien break; 37860484Sobrien case PCIM_BAR_MEM_64: 37960484Sobrien ln2range = 64; 38060484Sobrien break; 38160484Sobrien } 38260484Sobrien return (ln2range); 38360484Sobrien} 38460484Sobrien 38560484Sobrien/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ 38660484Sobrien 38760484Sobrienstatic void 38860484Sobrienpci_fixancient(pcicfgregs *cfg) 38960484Sobrien{ 39060484Sobrien if (cfg->hdrtype != 0) 39160484Sobrien return; 39260484Sobrien 39360484Sobrien /* PCI to PCI bridges use header type 1 */ 39460484Sobrien if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) 39560484Sobrien cfg->hdrtype = 1; 39660484Sobrien} 39760484Sobrien 39860484Sobrien/* extract header type specific config data */ 39960484Sobrien 40060484Sobrienstatic void 40160484Sobrienpci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg) 40260484Sobrien{ 40360484Sobrien#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w) 40460484Sobrien switch (cfg->hdrtype) { 40560484Sobrien case 0: 40660484Sobrien cfg->subvendor = REG(PCIR_SUBVEND_0, 2); 40760484Sobrien cfg->subdevice = REG(PCIR_SUBDEV_0, 2); 40860484Sobrien cfg->nummaps = PCI_MAXMAPS_0; 40960484Sobrien break; 41060484Sobrien case 1: 41160484Sobrien cfg->nummaps = PCI_MAXMAPS_1; 41260484Sobrien break; 41360484Sobrien case 2: 41460484Sobrien cfg->subvendor = REG(PCIR_SUBVEND_2, 2); 41560484Sobrien cfg->subdevice = REG(PCIR_SUBDEV_2, 2); 41660484Sobrien cfg->nummaps = PCI_MAXMAPS_2; 41760484Sobrien break; 41860484Sobrien } 41960484Sobrien#undef REG 42060484Sobrien} 42160484Sobrien 42260484Sobrien/* read configuration header into pcicfgregs structure */ 42360484Sobrienstruct pci_devinfo * 42460484Sobrienpci_read_device(device_t pcib, int d, int b, int s, int f, size_t size) 42560484Sobrien{ 42660484Sobrien#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w) 42760484Sobrien pcicfgregs *cfg = NULL; 42860484Sobrien struct pci_devinfo *devlist_entry; 42960484Sobrien struct devlist *devlist_head; 43060484Sobrien 43160484Sobrien devlist_head = &pci_devq; 43260484Sobrien 43360484Sobrien devlist_entry = NULL; 43460484Sobrien 43560484Sobrien if (REG(PCIR_DEVVENDOR, 4) != 0xfffffffful) { 43660484Sobrien devlist_entry = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); 43760484Sobrien if (devlist_entry == NULL) 43860484Sobrien return (NULL); 43960484Sobrien 44060484Sobrien cfg = &devlist_entry->cfg; 44160484Sobrien 44260484Sobrien cfg->domain = d; 44360484Sobrien cfg->bus = b; 44460484Sobrien cfg->slot = s; 44560484Sobrien cfg->func = f; 44660484Sobrien cfg->vendor = REG(PCIR_VENDOR, 2); 44760484Sobrien cfg->device = REG(PCIR_DEVICE, 2); 44860484Sobrien cfg->cmdreg = REG(PCIR_COMMAND, 2); 44960484Sobrien cfg->statreg = REG(PCIR_STATUS, 2); 45060484Sobrien cfg->baseclass = REG(PCIR_CLASS, 1); 45160484Sobrien cfg->subclass = REG(PCIR_SUBCLASS, 1); 45260484Sobrien cfg->progif = REG(PCIR_PROGIF, 1); 45360484Sobrien cfg->revid = REG(PCIR_REVID, 1); 45460484Sobrien cfg->hdrtype = REG(PCIR_HDRTYPE, 1); 45560484Sobrien cfg->cachelnsz = REG(PCIR_CACHELNSZ, 1); 45660484Sobrien cfg->lattimer = REG(PCIR_LATTIMER, 1); 45760484Sobrien cfg->intpin = REG(PCIR_INTPIN, 1); 45860484Sobrien cfg->intline = REG(PCIR_INTLINE, 1); 45960484Sobrien 46060484Sobrien cfg->mingnt = REG(PCIR_MINGNT, 1); 46160484Sobrien cfg->maxlat = REG(PCIR_MAXLAT, 1); 46260484Sobrien 46360484Sobrien cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; 46460484Sobrien cfg->hdrtype &= ~PCIM_MFDEV; 46560484Sobrien 46660484Sobrien pci_fixancient(cfg); 46760484Sobrien pci_hdrtypedata(pcib, b, s, f, cfg); 46860484Sobrien 46960484Sobrien if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) 47060484Sobrien pci_read_extcap(pcib, cfg); 47160484Sobrien 47260484Sobrien STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); 47360484Sobrien 47460484Sobrien devlist_entry->conf.pc_sel.pc_domain = cfg->domain; 47560484Sobrien devlist_entry->conf.pc_sel.pc_bus = cfg->bus; 47660484Sobrien devlist_entry->conf.pc_sel.pc_dev = cfg->slot; 47760484Sobrien devlist_entry->conf.pc_sel.pc_func = cfg->func; 47860484Sobrien devlist_entry->conf.pc_hdr = cfg->hdrtype; 47960484Sobrien 48060484Sobrien devlist_entry->conf.pc_subvendor = cfg->subvendor; 48160484Sobrien devlist_entry->conf.pc_subdevice = cfg->subdevice; 48260484Sobrien devlist_entry->conf.pc_vendor = cfg->vendor; 48360484Sobrien devlist_entry->conf.pc_device = cfg->device; 48460484Sobrien 48560484Sobrien devlist_entry->conf.pc_class = cfg->baseclass; 48660484Sobrien devlist_entry->conf.pc_subclass = cfg->subclass; 48760484Sobrien devlist_entry->conf.pc_progif = cfg->progif; 48860484Sobrien devlist_entry->conf.pc_revid = cfg->revid; 48960484Sobrien 49060484Sobrien pci_numdevs++; 49160484Sobrien pci_generation++; 49260484Sobrien } 49360484Sobrien return (devlist_entry); 49460484Sobrien#undef REG 49560484Sobrien} 49660484Sobrien 49760484Sobrienstatic void 49860484Sobrienpci_read_extcap(device_t pcib, pcicfgregs *cfg) 49960484Sobrien{ 50060484Sobrien#define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w) 50160484Sobrien#define WREG(n, v, w) PCIB_WRITE_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, v, w) 50260484Sobrien#if defined(__i386__) || defined(__amd64__) 50360484Sobrien uint64_t addr; 50460484Sobrien#endif 50560484Sobrien uint32_t val; 50660484Sobrien int ptr, nextptr, ptrptr; 50760484Sobrien 50860484Sobrien switch (cfg->hdrtype & PCIM_HDRTYPE) { 50960484Sobrien case 0: 51060484Sobrien case 1: 51160484Sobrien ptrptr = PCIR_CAP_PTR; 51260484Sobrien break; 51360484Sobrien case 2: 51460484Sobrien ptrptr = PCIR_CAP_PTR_2; /* cardbus capabilities ptr */ 51560484Sobrien break; 51660484Sobrien default: 51760484Sobrien return; /* no extended capabilities support */ 51860484Sobrien } 51960484Sobrien nextptr = REG(ptrptr, 1); /* sanity check? */ 52060484Sobrien 52160484Sobrien /* 52260484Sobrien * Read capability entries. 52360484Sobrien */ 52460484Sobrien while (nextptr != 0) { 52560484Sobrien /* Sanity check */ 52660484Sobrien if (nextptr > 255) { 52760484Sobrien printf("illegal PCI extended capability offset %d\n", 52860484Sobrien nextptr); 52960484Sobrien return; 53060484Sobrien } 53160484Sobrien /* Find the next entry */ 53260484Sobrien ptr = nextptr; 53360484Sobrien nextptr = REG(ptr + PCICAP_NEXTPTR, 1); 53460484Sobrien 53560484Sobrien /* Process this entry */ 53660484Sobrien switch (REG(ptr + PCICAP_ID, 1)) { 53760484Sobrien case PCIY_PMG: /* PCI power management */ 53860484Sobrien if (cfg->pp.pp_cap == 0) { 53960484Sobrien cfg->pp.pp_cap = REG(ptr + PCIR_POWER_CAP, 2); 54060484Sobrien cfg->pp.pp_status = ptr + PCIR_POWER_STATUS; 54160484Sobrien cfg->pp.pp_pmcsr = ptr + PCIR_POWER_PMCSR; 54260484Sobrien if ((nextptr - ptr) > PCIR_POWER_DATA) 54360484Sobrien cfg->pp.pp_data = ptr + PCIR_POWER_DATA; 54460484Sobrien } 54560484Sobrien break; 54660484Sobrien#if defined(__i386__) || defined(__amd64__) 54760484Sobrien case PCIY_HT: /* HyperTransport */ 54860484Sobrien /* Determine HT-specific capability type. */ 54960484Sobrien val = REG(ptr + PCIR_HT_COMMAND, 2); 55060484Sobrien switch (val & PCIM_HTCMD_CAP_MASK) { 55160484Sobrien case PCIM_HTCAP_MSI_MAPPING: 55260484Sobrien if (!(val & PCIM_HTCMD_MSI_FIXED)) { 55360484Sobrien /* Sanity check the mapping window. */ 55460484Sobrien addr = REG(ptr + PCIR_HTMSI_ADDRESS_HI, 55560484Sobrien 4); 55660484Sobrien addr <<= 32; 55760484Sobrien addr = REG(ptr + PCIR_HTMSI_ADDRESS_LO, 55860484Sobrien 4); 55960484Sobrien if (addr != MSI_INTEL_ADDR_BASE) 56060484Sobrien device_printf(pcib, 56160484Sobrien "HT Bridge at pci%d:%d:%d:%d has non-default MSI window 0x%llx\n", 56260484Sobrien cfg->domain, cfg->bus, 56360484Sobrien cfg->slot, cfg->func, 56460484Sobrien (long long)addr); 56560484Sobrien } else 56660484Sobrien addr = MSI_INTEL_ADDR_BASE; 56760484Sobrien 56860484Sobrien cfg->ht.ht_msimap = ptr; 56960484Sobrien cfg->ht.ht_msictrl = val; 57060484Sobrien cfg->ht.ht_msiaddr = addr; 57160484Sobrien break; 57260484Sobrien } 57360484Sobrien break; 57460484Sobrien#endif 57560484Sobrien case PCIY_MSI: /* PCI MSI */ 57660484Sobrien cfg->msi.msi_location = ptr; 57760484Sobrien cfg->msi.msi_ctrl = REG(ptr + PCIR_MSI_CTRL, 2); 57860484Sobrien cfg->msi.msi_msgnum = 1 << ((cfg->msi.msi_ctrl & 57960484Sobrien PCIM_MSICTRL_MMC_MASK)>>1); 58060484Sobrien break; 58160484Sobrien case PCIY_MSIX: /* PCI MSI-X */ 58260484Sobrien cfg->msix.msix_location = ptr; 58360484Sobrien cfg->msix.msix_ctrl = REG(ptr + PCIR_MSIX_CTRL, 2); 58460484Sobrien cfg->msix.msix_msgnum = (cfg->msix.msix_ctrl & 58560484Sobrien PCIM_MSIXCTRL_TABLE_SIZE) + 1; 58660484Sobrien val = REG(ptr + PCIR_MSIX_TABLE, 4); 58777298Sobrien cfg->msix.msix_table_bar = PCIR_BAR(val & 58860484Sobrien PCIM_MSIX_BIR_MASK); 58960484Sobrien cfg->msix.msix_table_offset = val & ~PCIM_MSIX_BIR_MASK; 59060484Sobrien val = REG(ptr + PCIR_MSIX_PBA, 4); 59160484Sobrien cfg->msix.msix_pba_bar = PCIR_BAR(val & 59260484Sobrien PCIM_MSIX_BIR_MASK); 59360484Sobrien cfg->msix.msix_pba_offset = val & ~PCIM_MSIX_BIR_MASK; 59460484Sobrien break; 59560484Sobrien case PCIY_VPD: /* PCI Vital Product Data */ 59660484Sobrien cfg->vpd.vpd_reg = ptr; 59760484Sobrien break; 59860484Sobrien case PCIY_SUBVENDOR: 59960484Sobrien /* Should always be true. */ 60060484Sobrien if ((cfg->hdrtype & PCIM_HDRTYPE) == 1) { 60160484Sobrien val = REG(ptr + PCIR_SUBVENDCAP_ID, 4); 60260484Sobrien cfg->subvendor = val & 0xffff; 60377298Sobrien cfg->subdevice = val >> 16; 60460484Sobrien } 60560484Sobrien break; 60660484Sobrien case PCIY_PCIX: /* PCI-X */ 60760484Sobrien /* 60860484Sobrien * Assume we have a PCI-X chipset if we have 60960484Sobrien * at least one PCI-PCI bridge with a PCI-X 61060484Sobrien * capability. Note that some systems with 61160484Sobrien * PCI-express or HT chipsets might match on 61260484Sobrien * this check as well. 61360484Sobrien */ 61460484Sobrien if ((cfg->hdrtype & PCIM_HDRTYPE) == 1) 61560484Sobrien pcix_chipset = 1; 61660484Sobrien break; 61760484Sobrien case PCIY_EXPRESS: /* PCI-express */ 61860484Sobrien /* 61960484Sobrien * Assume we have a PCI-express chipset if we have 62060484Sobrien * at least one PCI-express device. 62160484Sobrien */ 62260484Sobrien pcie_chipset = 1; 62360484Sobrien break; 62460484Sobrien default: 62560484Sobrien break; 62660484Sobrien } 62760484Sobrien } 62860484Sobrien/* REG and WREG use carry through to next functions */ 62960484Sobrien} 63060484Sobrien 63160484Sobrien/* 63260484Sobrien * PCI Vital Product Data 63360484Sobrien */ 63460484Sobrien 63560484Sobrien#define PCI_VPD_TIMEOUT 1000000 63660484Sobrien 63760484Sobrienstatic int 63860484Sobrienpci_read_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t *data) 63960484Sobrien{ 64060484Sobrien int count = PCI_VPD_TIMEOUT; 64160484Sobrien 64260484Sobrien KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned")); 64360484Sobrien 64460484Sobrien WREG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, reg, 2); 64560484Sobrien 64660484Sobrien while ((REG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, 2) & 0x8000) != 0x8000) { 64760484Sobrien if (--count < 0) 64860484Sobrien return (ENXIO); 64960484Sobrien DELAY(1); /* limit looping */ 65060484Sobrien } 65160484Sobrien *data = (REG(cfg->vpd.vpd_reg + PCIR_VPD_DATA, 4)); 65260484Sobrien 65360484Sobrien return (0); 65460484Sobrien} 65560484Sobrien 65660484Sobrien#if 0 65760484Sobrienstatic int 65860484Sobrienpci_write_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t data) 65960484Sobrien{ 66060484Sobrien int count = PCI_VPD_TIMEOUT; 66160484Sobrien 66260484Sobrien KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned")); 66360484Sobrien 66460484Sobrien WREG(cfg->vpd.vpd_reg + PCIR_VPD_DATA, data, 4); 66560484Sobrien WREG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, reg | 0x8000, 2); 66660484Sobrien while ((REG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, 2) & 0x8000) == 0x8000) { 66760484Sobrien if (--count < 0) 66860484Sobrien return (ENXIO); 66960484Sobrien DELAY(1); /* limit looping */ 67060484Sobrien } 67160484Sobrien 67260484Sobrien return (0); 67360484Sobrien} 67460484Sobrien#endif 67560484Sobrien 67660484Sobrien#undef PCI_VPD_TIMEOUT 67760484Sobrien 67860484Sobrienstruct vpd_readstate { 67960484Sobrien device_t pcib; 68060484Sobrien pcicfgregs *cfg; 68160484Sobrien uint32_t val; 68277298Sobrien int bytesinval; 68360484Sobrien int off; 68460484Sobrien uint8_t cksum; 68560484Sobrien}; 68660484Sobrien 68760484Sobrienstatic int 68860484Sobrienvpd_nextbyte(struct vpd_readstate *vrs, uint8_t *data) 68960484Sobrien{ 69060484Sobrien uint32_t reg; 69160484Sobrien uint8_t byte; 69260484Sobrien 69360484Sobrien if (vrs->bytesinval == 0) { 69460484Sobrien if (pci_read_vpd_reg(vrs->pcib, vrs->cfg, vrs->off, ®)) 69560484Sobrien return (ENXIO); 69660484Sobrien vrs->val = le32toh(reg); 69777298Sobrien vrs->off += 4; 69860484Sobrien byte = vrs->val & 0xff; 69960484Sobrien vrs->bytesinval = 3; 70060484Sobrien } else { 70160484Sobrien vrs->val = vrs->val >> 8; 70260484Sobrien byte = vrs->val & 0xff; 70360484Sobrien vrs->bytesinval--; 70460484Sobrien } 70560484Sobrien 70660484Sobrien vrs->cksum += byte; 70760484Sobrien *data = byte; 70860484Sobrien return (0); 70960484Sobrien} 71060484Sobrien 71160484Sobrienstatic void 71277298Sobrienpci_read_vpd(device_t pcib, pcicfgregs *cfg) 71360484Sobrien{ 71460484Sobrien struct vpd_readstate vrs; 71560484Sobrien int state; 71660484Sobrien int name; 71760484Sobrien int remain; 71860484Sobrien int i; 71960484Sobrien int alloc, off; /* alloc/off for RO/W arrays */ 72060484Sobrien int cksumvalid; 72160484Sobrien int dflen; 72260484Sobrien uint8_t byte; 72360484Sobrien uint8_t byte2; 72460484Sobrien 72560484Sobrien /* init vpd reader */ 72660484Sobrien vrs.bytesinval = 0; 72777298Sobrien vrs.off = 0; 72860484Sobrien vrs.pcib = pcib; 72960484Sobrien vrs.cfg = cfg; 73060484Sobrien vrs.cksum = 0; 73160484Sobrien 73260484Sobrien state = 0; 73360484Sobrien name = remain = i = 0; /* shut up stupid gcc */ 73460484Sobrien alloc = off = 0; /* shut up stupid gcc */ 73560484Sobrien dflen = 0; /* shut up stupid gcc */ 73660484Sobrien cksumvalid = -1; 73760484Sobrien while (state >= 0) { 73860484Sobrien if (vpd_nextbyte(&vrs, &byte)) { 73960484Sobrien state = -2; 74060484Sobrien break; 74160484Sobrien } 74260484Sobrien#if 0 74360484Sobrien printf("vpd: val: %#x, off: %d, bytesinval: %d, byte: %#hhx, " \ 74460484Sobrien "state: %d, remain: %d, name: %#x, i: %d\n", vrs.val, 74560484Sobrien vrs.off, vrs.bytesinval, byte, state, remain, name, i); 74660484Sobrien#endif 74760484Sobrien switch (state) { 74860484Sobrien case 0: /* item name */ 74960484Sobrien if (byte & 0x80) { 75060484Sobrien if (vpd_nextbyte(&vrs, &byte2)) { 75160484Sobrien state = -2; 75260484Sobrien break; 75360484Sobrien } 75460484Sobrien remain = byte2; 75560484Sobrien if (vpd_nextbyte(&vrs, &byte2)) { 75660484Sobrien state = -2; 75760484Sobrien break; 75860484Sobrien } 75960484Sobrien remain |= byte2 << 8; 76060484Sobrien if (remain > (0x7f*4 - vrs.off)) { 76160484Sobrien state = -1; 76260484Sobrien printf( 76360484Sobrien "pci%d:%d:%d:%d: invalid VPD data, remain %#x\n", 76460484Sobrien cfg->domain, cfg->bus, cfg->slot, 76560484Sobrien cfg->func, remain); 76660484Sobrien } 76760484Sobrien name = byte & 0x7f; 76860484Sobrien } else { 76960484Sobrien remain = byte & 0x7; 77060484Sobrien name = (byte >> 3) & 0xf; 77160484Sobrien } 77260484Sobrien switch (name) { 77360484Sobrien case 0x2: /* String */ 77460484Sobrien cfg->vpd.vpd_ident = malloc(remain + 1, 77560484Sobrien M_DEVBUF, M_WAITOK); 77660484Sobrien i = 0; 77760484Sobrien state = 1; 77860484Sobrien break; 77960484Sobrien case 0xf: /* End */ 78060484Sobrien state = -1; 78160484Sobrien break; 78260484Sobrien case 0x10: /* VPD-R */ 78360484Sobrien alloc = 8; 78460484Sobrien off = 0; 78560484Sobrien cfg->vpd.vpd_ros = malloc(alloc * 78660484Sobrien sizeof(*cfg->vpd.vpd_ros), M_DEVBUF, 78760484Sobrien M_WAITOK | M_ZERO); 78860484Sobrien state = 2; 78960484Sobrien break; 79060484Sobrien case 0x11: /* VPD-W */ 79160484Sobrien alloc = 8; 79260484Sobrien off = 0; 79360484Sobrien cfg->vpd.vpd_w = malloc(alloc * 79460484Sobrien sizeof(*cfg->vpd.vpd_w), M_DEVBUF, 79560484Sobrien M_WAITOK | M_ZERO); 79660484Sobrien state = 5; 79760484Sobrien break; 79860484Sobrien default: /* Invalid data, abort */ 79960484Sobrien state = -1; 80060484Sobrien break; 80160484Sobrien } 80260484Sobrien break; 80360484Sobrien 80460484Sobrien case 1: /* Identifier String */ 80560484Sobrien cfg->vpd.vpd_ident[i++] = byte; 80660484Sobrien remain--; 80760484Sobrien if (remain == 0) { 80860484Sobrien cfg->vpd.vpd_ident[i] = '\0'; 80960484Sobrien state = 0; 81060484Sobrien } 81160484Sobrien break; 81260484Sobrien 81360484Sobrien case 2: /* VPD-R Keyword Header */ 81460484Sobrien if (off == alloc) { 81560484Sobrien cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros, 81660484Sobrien (alloc *= 2) * sizeof(*cfg->vpd.vpd_ros), 81760484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 81860484Sobrien } 81960484Sobrien cfg->vpd.vpd_ros[off].keyword[0] = byte; 82060484Sobrien if (vpd_nextbyte(&vrs, &byte2)) { 82160484Sobrien state = -2; 82260484Sobrien break; 82360484Sobrien } 82460484Sobrien cfg->vpd.vpd_ros[off].keyword[1] = byte2; 82560484Sobrien if (vpd_nextbyte(&vrs, &byte2)) { 82660484Sobrien state = -2; 82760484Sobrien break; 82860484Sobrien } 82960484Sobrien dflen = byte2; 83060484Sobrien if (dflen == 0 && 83160484Sobrien strncmp(cfg->vpd.vpd_ros[off].keyword, "RV", 83260484Sobrien 2) == 0) { 83360484Sobrien /* 83460484Sobrien * if this happens, we can't trust the rest 83560484Sobrien * of the VPD. 83660484Sobrien */ 83760484Sobrien printf( 83860484Sobrien "pci%d:%d:%d:%d: bad keyword length: %d\n", 83960484Sobrien cfg->domain, cfg->bus, cfg->slot, 84060484Sobrien cfg->func, dflen); 84160484Sobrien cksumvalid = 0; 84260484Sobrien state = -1; 84360484Sobrien break; 84460484Sobrien } else if (dflen == 0) { 84560484Sobrien cfg->vpd.vpd_ros[off].value = malloc(1 * 84660484Sobrien sizeof(*cfg->vpd.vpd_ros[off].value), 84760484Sobrien M_DEVBUF, M_WAITOK); 84860484Sobrien cfg->vpd.vpd_ros[off].value[0] = '\x00'; 84960484Sobrien } else 85060484Sobrien cfg->vpd.vpd_ros[off].value = malloc( 85160484Sobrien (dflen + 1) * 85260484Sobrien sizeof(*cfg->vpd.vpd_ros[off].value), 85360484Sobrien M_DEVBUF, M_WAITOK); 85460484Sobrien remain -= 3; 85560484Sobrien i = 0; 85660484Sobrien /* keep in sync w/ state 3's transistions */ 85760484Sobrien if (dflen == 0 && remain == 0) 85860484Sobrien state = 0; 85960484Sobrien else if (dflen == 0) 86060484Sobrien state = 2; 86160484Sobrien else 86260484Sobrien state = 3; 86360484Sobrien break; 86460484Sobrien 86560484Sobrien case 3: /* VPD-R Keyword Value */ 86660484Sobrien cfg->vpd.vpd_ros[off].value[i++] = byte; 86760484Sobrien if (strncmp(cfg->vpd.vpd_ros[off].keyword, 86860484Sobrien "RV", 2) == 0 && cksumvalid == -1) { 86960484Sobrien if (vrs.cksum == 0) 87060484Sobrien cksumvalid = 1; 87160484Sobrien else { 87260484Sobrien if (bootverbose) 87360484Sobrien printf( 87460484Sobrien "pci%d:%d:%d:%d: bad VPD cksum, remain %hhu\n", 87560484Sobrien cfg->domain, cfg->bus, 87660484Sobrien cfg->slot, cfg->func, 87760484Sobrien vrs.cksum); 87860484Sobrien cksumvalid = 0; 87960484Sobrien state = -1; 88060484Sobrien break; 88160484Sobrien } 88260484Sobrien } 88360484Sobrien dflen--; 88460484Sobrien remain--; 88560484Sobrien /* keep in sync w/ state 2's transistions */ 88660484Sobrien if (dflen == 0) 88760484Sobrien cfg->vpd.vpd_ros[off++].value[i++] = '\0'; 88860484Sobrien if (dflen == 0 && remain == 0) { 88960484Sobrien cfg->vpd.vpd_rocnt = off; 89060484Sobrien cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros, 89160484Sobrien off * sizeof(*cfg->vpd.vpd_ros), 89260484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 89360484Sobrien state = 0; 89460484Sobrien } else if (dflen == 0) 89560484Sobrien state = 2; 89660484Sobrien break; 89760484Sobrien 89860484Sobrien case 4: 89960484Sobrien remain--; 90060484Sobrien if (remain == 0) 90160484Sobrien state = 0; 90260484Sobrien break; 90360484Sobrien 90460484Sobrien case 5: /* VPD-W Keyword Header */ 90560484Sobrien if (off == alloc) { 90660484Sobrien cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w, 90760484Sobrien (alloc *= 2) * sizeof(*cfg->vpd.vpd_w), 90860484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 90960484Sobrien } 91060484Sobrien cfg->vpd.vpd_w[off].keyword[0] = byte; 91160484Sobrien if (vpd_nextbyte(&vrs, &byte2)) { 91260484Sobrien state = -2; 91360484Sobrien break; 91460484Sobrien } 91560484Sobrien cfg->vpd.vpd_w[off].keyword[1] = byte2; 91660484Sobrien if (vpd_nextbyte(&vrs, &byte2)) { 91760484Sobrien state = -2; 91860484Sobrien break; 91960484Sobrien } 92060484Sobrien cfg->vpd.vpd_w[off].len = dflen = byte2; 92160484Sobrien cfg->vpd.vpd_w[off].start = vrs.off - vrs.bytesinval; 92260484Sobrien cfg->vpd.vpd_w[off].value = malloc((dflen + 1) * 92360484Sobrien sizeof(*cfg->vpd.vpd_w[off].value), 92460484Sobrien M_DEVBUF, M_WAITOK); 92560484Sobrien remain -= 3; 92660484Sobrien i = 0; 92760484Sobrien /* keep in sync w/ state 6's transistions */ 92860484Sobrien if (dflen == 0 && remain == 0) 92960484Sobrien state = 0; 93060484Sobrien else if (dflen == 0) 93160484Sobrien state = 5; 93260484Sobrien else 93360484Sobrien state = 6; 93460484Sobrien break; 93560484Sobrien 93660484Sobrien case 6: /* VPD-W Keyword Value */ 93760484Sobrien cfg->vpd.vpd_w[off].value[i++] = byte; 93860484Sobrien dflen--; 93960484Sobrien remain--; 94060484Sobrien /* keep in sync w/ state 5's transistions */ 94160484Sobrien if (dflen == 0) 94260484Sobrien cfg->vpd.vpd_w[off++].value[i++] = '\0'; 94360484Sobrien if (dflen == 0 && remain == 0) { 94460484Sobrien cfg->vpd.vpd_wcnt = off; 94560484Sobrien cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w, 94660484Sobrien off * sizeof(*cfg->vpd.vpd_w), 94760484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 94860484Sobrien state = 0; 94960484Sobrien } else if (dflen == 0) 95060484Sobrien state = 5; 95160484Sobrien break; 95260484Sobrien 95360484Sobrien default: 95460484Sobrien printf("pci%d:%d:%d:%d: invalid state: %d\n", 95560484Sobrien cfg->domain, cfg->bus, cfg->slot, cfg->func, 95660484Sobrien state); 95760484Sobrien state = -1; 95860484Sobrien break; 95960484Sobrien } 96060484Sobrien } 96160484Sobrien 96260484Sobrien if (cksumvalid == 0 || state < -1) { 96360484Sobrien /* read-only data bad, clean up */ 96460484Sobrien if (cfg->vpd.vpd_ros != NULL) { 96560484Sobrien for (off = 0; cfg->vpd.vpd_ros[off].value; off++) 96660484Sobrien free(cfg->vpd.vpd_ros[off].value, M_DEVBUF); 96760484Sobrien free(cfg->vpd.vpd_ros, M_DEVBUF); 96860484Sobrien cfg->vpd.vpd_ros = NULL; 96977298Sobrien } 97060484Sobrien } 97160484Sobrien if (state < -1) { 97260484Sobrien /* I/O error, clean up */ 97360484Sobrien printf("pci%d:%d:%d:%d: failed to read VPD data.\n", 97460484Sobrien cfg->domain, cfg->bus, cfg->slot, cfg->func); 97560484Sobrien if (cfg->vpd.vpd_ident != NULL) { 97660484Sobrien free(cfg->vpd.vpd_ident, M_DEVBUF); 97760484Sobrien cfg->vpd.vpd_ident = NULL; 97860484Sobrien } 97960484Sobrien if (cfg->vpd.vpd_w != NULL) { 98060484Sobrien for (off = 0; cfg->vpd.vpd_w[off].value; off++) 98160484Sobrien free(cfg->vpd.vpd_w[off].value, M_DEVBUF); 98260484Sobrien free(cfg->vpd.vpd_w, M_DEVBUF); 98360484Sobrien cfg->vpd.vpd_w = NULL; 98477298Sobrien } 98560484Sobrien } 98660484Sobrien cfg->vpd.vpd_cached = 1; 98760484Sobrien#undef REG 98860484Sobrien#undef WREG 98960484Sobrien} 99060484Sobrien 99160484Sobrienint 99260484Sobrienpci_get_vpd_ident_method(device_t dev, device_t child, const char **identptr) 99360484Sobrien{ 99460484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 99560484Sobrien pcicfgregs *cfg = &dinfo->cfg; 99660484Sobrien 99760484Sobrien if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0) 99860484Sobrien pci_read_vpd(device_get_parent(dev), cfg); 99960484Sobrien 100089857Sobrien *identptr = cfg->vpd.vpd_ident; 100189857Sobrien 100260484Sobrien if (*identptr == NULL) 100360484Sobrien return (ENXIO); 100460484Sobrien 100560484Sobrien return (0); 100677298Sobrien} 100760484Sobrien 100860484Sobrienint 100960484Sobrienpci_get_vpd_readonly_method(device_t dev, device_t child, const char *kw, 101060484Sobrien const char **vptr) 101160484Sobrien{ 101277298Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 101377298Sobrien pcicfgregs *cfg = &dinfo->cfg; 101477298Sobrien int i; 101577298Sobrien 101660484Sobrien if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0) 101760484Sobrien pci_read_vpd(device_get_parent(dev), cfg); 101860484Sobrien 101960484Sobrien for (i = 0; i < cfg->vpd.vpd_rocnt; i++) 102060484Sobrien if (memcmp(kw, cfg->vpd.vpd_ros[i].keyword, 102160484Sobrien sizeof(cfg->vpd.vpd_ros[i].keyword)) == 0) { 102260484Sobrien *vptr = cfg->vpd.vpd_ros[i].value; 102360484Sobrien } 102460484Sobrien 102560484Sobrien if (i != cfg->vpd.vpd_rocnt) 102677298Sobrien return (0); 102760484Sobrien 102860484Sobrien *vptr = NULL; 102960484Sobrien return (ENXIO); 103060484Sobrien} 103160484Sobrien 103260484Sobrien/* 103360484Sobrien * Return the offset in configuration space of the requested extended 103477298Sobrien * capability entry or 0 if the specified capability was not found. 103560484Sobrien */ 103660484Sobrienint 103760484Sobrienpci_find_extcap_method(device_t dev, device_t child, int capability, 103860484Sobrien int *capreg) 103960484Sobrien{ 104077298Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 104160484Sobrien pcicfgregs *cfg = &dinfo->cfg; 104260484Sobrien u_int32_t status; 104360484Sobrien u_int8_t ptr; 104460484Sobrien 104560484Sobrien /* 104660484Sobrien * Check the CAP_LIST bit of the PCI status register first. 104760484Sobrien */ 104860484Sobrien status = pci_read_config(child, PCIR_STATUS, 2); 104977298Sobrien if (!(status & PCIM_STATUS_CAPPRESENT)) 105060484Sobrien return (ENXIO); 105160484Sobrien 105260484Sobrien /* 105360484Sobrien * Determine the start pointer of the capabilities list. 105477298Sobrien */ 105560484Sobrien switch (cfg->hdrtype & PCIM_HDRTYPE) { 105660484Sobrien case 0: 105760484Sobrien case 1: 105860484Sobrien ptr = PCIR_CAP_PTR; 105960484Sobrien break; 106077298Sobrien case 2: 106160484Sobrien ptr = PCIR_CAP_PTR_2; 106289857Sobrien break; 106360484Sobrien default: 106460484Sobrien /* XXX: panic? */ 106560484Sobrien return (ENXIO); /* no extended capabilities support */ 106677298Sobrien } 106760484Sobrien ptr = pci_read_config(child, ptr, 1); 106860484Sobrien 106960484Sobrien /* 107060484Sobrien * Traverse the capabilities list. 107160484Sobrien */ 107277298Sobrien while (ptr != 0) { 107377298Sobrien if (pci_read_config(child, ptr + PCICAP_ID, 1) == capability) { 107489857Sobrien if (capreg != NULL) 107589857Sobrien *capreg = ptr; 107689857Sobrien return (0); 107760484Sobrien } 107860484Sobrien ptr = pci_read_config(child, ptr + PCICAP_NEXTPTR, 1); 107977298Sobrien } 108060484Sobrien 108160484Sobrien return (ENOENT); 108277298Sobrien} 108360484Sobrien 108477298Sobrien/* 108560484Sobrien * Support for MSI-X message interrupts. 108677298Sobrien */ 108760484Sobrienvoid 108860484Sobrienpci_enable_msix(device_t dev, u_int index, uint64_t address, uint32_t data) 108960484Sobrien{ 109060484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 109160484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 109260484Sobrien uint32_t offset; 109360484Sobrien 109477298Sobrien KASSERT(msix->msix_table_len > index, ("bogus index")); 109560484Sobrien offset = msix->msix_table_offset + index * 16; 109660484Sobrien bus_write_4(msix->msix_table_res, offset, address & 0xffffffff); 109760484Sobrien bus_write_4(msix->msix_table_res, offset + 4, address >> 32); 109877298Sobrien bus_write_4(msix->msix_table_res, offset + 8, data); 109960484Sobrien 110077298Sobrien /* Enable MSI -> HT mapping. */ 110160484Sobrien pci_ht_map_msi(dev, address); 110260484Sobrien} 110377298Sobrien 110460484Sobrienvoid 110560484Sobrienpci_mask_msix(device_t dev, u_int index) 110660484Sobrien{ 110760484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 110860484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 110977298Sobrien uint32_t offset, val; 111077298Sobrien 111160484Sobrien KASSERT(msix->msix_msgnum > index, ("bogus index")); 111277298Sobrien offset = msix->msix_table_offset + index * 16 + 12; 111360484Sobrien val = bus_read_4(msix->msix_table_res, offset); 111460484Sobrien if (!(val & PCIM_MSIX_VCTRL_MASK)) { 111560484Sobrien val |= PCIM_MSIX_VCTRL_MASK; 111660484Sobrien bus_write_4(msix->msix_table_res, offset, val); 111760484Sobrien } 111877298Sobrien} 111960484Sobrien 112060484Sobrienvoid 112160484Sobrienpci_unmask_msix(device_t dev, u_int index) 112260484Sobrien{ 112360484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 112477298Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 112560484Sobrien uint32_t offset, val; 112660484Sobrien 112760484Sobrien KASSERT(msix->msix_table_len > index, ("bogus index")); 112860484Sobrien offset = msix->msix_table_offset + index * 16 + 12; 112960484Sobrien val = bus_read_4(msix->msix_table_res, offset); 113060484Sobrien if (val & PCIM_MSIX_VCTRL_MASK) { 113160484Sobrien val &= ~PCIM_MSIX_VCTRL_MASK; 113277298Sobrien bus_write_4(msix->msix_table_res, offset, val); 113360484Sobrien } 113460484Sobrien} 113560484Sobrien 113660484Sobrienint 113760484Sobrienpci_pending_msix(device_t dev, u_int index) 113860484Sobrien{ 113960484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 114060484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 114160484Sobrien uint32_t offset, bit; 114260484Sobrien 114360484Sobrien KASSERT(msix->msix_table_len > index, ("bogus index")); 114460484Sobrien offset = msix->msix_pba_offset + (index / 32) * 4; 114560484Sobrien bit = 1 << index % 32; 114660484Sobrien return (bus_read_4(msix->msix_pba_res, offset) & bit); 114760484Sobrien} 114877298Sobrien 114960484Sobrien/* 115060484Sobrien * Restore MSI-X registers and table during resume. If MSI-X is 115160484Sobrien * enabled then walk the virtual table to restore the actual MSI-X 115260484Sobrien * table. 115360484Sobrien */ 115460484Sobrienstatic void 115560484Sobrienpci_resume_msix(device_t dev) 115660484Sobrien{ 115760484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 115860484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 115960484Sobrien struct msix_table_entry *mte; 116060484Sobrien struct msix_vector *mv; 116177298Sobrien int i; 116260484Sobrien 116360484Sobrien if (msix->msix_alloc > 0) { 116460484Sobrien /* First, mask all vectors. */ 116560484Sobrien for (i = 0; i < msix->msix_msgnum; i++) 116660484Sobrien pci_mask_msix(dev, i); 116760484Sobrien 116860484Sobrien /* Second, program any messages with at least one handler. */ 116960484Sobrien for (i = 0; i < msix->msix_table_len; i++) { 117060484Sobrien mte = &msix->msix_table[i]; 117160484Sobrien if (mte->mte_vector == 0 || mte->mte_handlers == 0) 117260484Sobrien continue; 117360484Sobrien mv = &msix->msix_vectors[mte->mte_vector - 1]; 117460484Sobrien pci_enable_msix(dev, i, mv->mv_address, mv->mv_data); 117560484Sobrien pci_unmask_msix(dev, i); 117660484Sobrien } 117760484Sobrien } 117860484Sobrien pci_write_config(dev, msix->msix_location + PCIR_MSIX_CTRL, 117960484Sobrien msix->msix_ctrl, 2); 118060484Sobrien} 118160484Sobrien 118277298Sobrien/* 118360484Sobrien * Attempt to allocate *count MSI-X messages. The actual number allocated is 118460484Sobrien * returned in *count. After this function returns, each message will be 118560484Sobrien * available to the driver as SYS_RES_IRQ resources starting at rid 1. 118660484Sobrien */ 118760484Sobrienint 118860484Sobrienpci_alloc_msix_method(device_t dev, device_t child, int *count) 118960484Sobrien{ 119060484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 119160484Sobrien pcicfgregs *cfg = &dinfo->cfg; 119260484Sobrien struct resource_list_entry *rle; 119360484Sobrien int actual, error, i, irq, max; 119460484Sobrien 119577298Sobrien /* Don't let count == 0 get us into trouble. */ 119660484Sobrien if (*count == 0) 119760484Sobrien return (EINVAL); 119860484Sobrien 119960484Sobrien /* If rid 0 is allocated, then fail. */ 120060484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0); 120160484Sobrien if (rle != NULL && rle->res != NULL) 120260484Sobrien return (ENXIO); 120360484Sobrien 120460484Sobrien /* Already have allocated messages? */ 120589857Sobrien if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0) 120660484Sobrien return (ENXIO); 120760484Sobrien 120860484Sobrien /* If MSI is blacklisted for this system, fail. */ 120960484Sobrien if (pci_msi_blacklisted()) 121060484Sobrien return (ENXIO); 121160484Sobrien 121260484Sobrien /* MSI-X capability present? */ 121360484Sobrien if (cfg->msix.msix_location == 0 || !pci_do_msix) 121460484Sobrien return (ENODEV); 121560484Sobrien 121660484Sobrien /* Make sure the appropriate BARs are mapped. */ 121760484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, 121860484Sobrien cfg->msix.msix_table_bar); 121960484Sobrien if (rle == NULL || rle->res == NULL || 122060484Sobrien !(rman_get_flags(rle->res) & RF_ACTIVE)) 122160484Sobrien return (ENXIO); 122260484Sobrien cfg->msix.msix_table_res = rle->res; 122360484Sobrien if (cfg->msix.msix_pba_bar != cfg->msix.msix_table_bar) { 122460484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, 122560484Sobrien cfg->msix.msix_pba_bar); 122660484Sobrien if (rle == NULL || rle->res == NULL || 122760484Sobrien !(rman_get_flags(rle->res) & RF_ACTIVE)) 122860484Sobrien return (ENXIO); 122960484Sobrien } 123060484Sobrien cfg->msix.msix_pba_res = rle->res; 123160484Sobrien 123260484Sobrien if (bootverbose) 123360484Sobrien device_printf(child, 123460484Sobrien "attempting to allocate %d MSI-X vectors (%d supported)\n", 123560484Sobrien *count, cfg->msix.msix_msgnum); 123660484Sobrien max = min(*count, cfg->msix.msix_msgnum); 123760484Sobrien for (i = 0; i < max; i++) { 123860484Sobrien /* Allocate a message. */ 123960484Sobrien error = PCIB_ALLOC_MSIX(device_get_parent(dev), child, &irq); 124060484Sobrien if (error) 124160484Sobrien break; 124260484Sobrien resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq, 124360484Sobrien irq, 1); 124460484Sobrien } 124560484Sobrien actual = i; 124660484Sobrien 124760484Sobrien if (bootverbose) { 124860484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 1); 124960484Sobrien if (actual == 1) 125060484Sobrien device_printf(child, "using IRQ %lu for MSI-X\n", 125160484Sobrien rle->start); 125260484Sobrien else { 125360484Sobrien int run; 125460484Sobrien 125560484Sobrien /* 125660484Sobrien * Be fancy and try to print contiguous runs of 125760484Sobrien * IRQ values as ranges. 'irq' is the previous IRQ. 125860484Sobrien * 'run' is true if we are in a range. 125960484Sobrien */ 126060484Sobrien device_printf(child, "using IRQs %lu", rle->start); 126177298Sobrien irq = rle->start; 126260484Sobrien run = 0; 126360484Sobrien for (i = 1; i < actual; i++) { 126477298Sobrien rle = resource_list_find(&dinfo->resources, 126560484Sobrien SYS_RES_IRQ, i + 1); 126660484Sobrien 126777298Sobrien /* Still in a run? */ 126860484Sobrien if (rle->start == irq + 1) { 126960484Sobrien run = 1; 127060484Sobrien irq++; 127160484Sobrien continue; 127260484Sobrien } 127360484Sobrien 127460484Sobrien /* Finish previous range. */ 127560484Sobrien if (run) { 127660484Sobrien printf("-%d", irq); 127760484Sobrien run = 0; 127860484Sobrien } 127960484Sobrien 128060484Sobrien /* Start new range. */ 128160484Sobrien printf(",%lu", rle->start); 128260484Sobrien irq = rle->start; 128360484Sobrien } 128460484Sobrien 128560484Sobrien /* Unfinished range? */ 128660484Sobrien if (run) 128760484Sobrien printf("-%d", irq); 128860484Sobrien printf(" for MSI-X\n"); 128960484Sobrien } 129060484Sobrien } 129160484Sobrien 129260484Sobrien /* Mask all vectors. */ 129360484Sobrien for (i = 0; i < cfg->msix.msix_msgnum; i++) 129460484Sobrien pci_mask_msix(child, i); 129560484Sobrien 129660484Sobrien /* Allocate and initialize vector data and virtual table. */ 129760484Sobrien cfg->msix.msix_vectors = malloc(sizeof(struct msix_vector) * actual, 129860484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 129960484Sobrien cfg->msix.msix_table = malloc(sizeof(struct msix_table_entry) * actual, 130060484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 130160484Sobrien for (i = 0; i < actual; i++) { 130260484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); 130360484Sobrien cfg->msix.msix_vectors[i].mv_irq = rle->start; 130460484Sobrien cfg->msix.msix_table[i].mte_vector = i + 1; 130560484Sobrien } 130660484Sobrien 130760484Sobrien /* Update control register to enable MSI-X. */ 130860484Sobrien cfg->msix.msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE; 130960484Sobrien pci_write_config(child, cfg->msix.msix_location + PCIR_MSIX_CTRL, 131060484Sobrien cfg->msix.msix_ctrl, 2); 131160484Sobrien 131260484Sobrien /* Update counts of alloc'd messages. */ 131360484Sobrien cfg->msix.msix_alloc = actual; 131460484Sobrien cfg->msix.msix_table_len = actual; 131560484Sobrien *count = actual; 131660484Sobrien return (0); 131760484Sobrien} 131860484Sobrien 131960484Sobrien/* 132060484Sobrien * By default, pci_alloc_msix() will assign the allocated IRQ 132160484Sobrien * resources consecutively to the first N messages in the MSI-X table. 132260484Sobrien * However, device drivers may want to use different layouts if they 132360484Sobrien * either receive fewer messages than they asked for, or they wish to 132477298Sobrien * populate the MSI-X table sparsely. This method allows the driver 132560484Sobrien * to specify what layout it wants. It must be called after a 132660484Sobrien * successful pci_alloc_msix() but before any of the associated 132760484Sobrien * SYS_RES_IRQ resources are allocated via bus_alloc_resource(). 132860484Sobrien * 132960484Sobrien * The 'vectors' array contains 'count' message vectors. The array 133060484Sobrien * maps directly to the MSI-X table in that index 0 in the array 133160484Sobrien * specifies the vector for the first message in the MSI-X table, etc. 133260484Sobrien * The vector value in each array index can either be 0 to indicate 133360484Sobrien * that no vector should be assigned to a message slot, or it can be a 133460484Sobrien * number from 1 to N (where N is the count returned from a 133577298Sobrien * succcessful call to pci_alloc_msix()) to indicate which message 133677298Sobrien * vector (IRQ) to be used for the corresponding message. 133760484Sobrien * 133860484Sobrien * On successful return, each message with a non-zero vector will have 133960484Sobrien * an associated SYS_RES_IRQ whose rid is equal to the array index + 134060484Sobrien * 1. Additionally, if any of the IRQs allocated via the previous 134160484Sobrien * call to pci_alloc_msix() are not used in the mapping, those IRQs 134260484Sobrien * will be freed back to the system automatically. 134360484Sobrien * 134460484Sobrien * For example, suppose a driver has a MSI-X table with 6 messages and 134560484Sobrien * asks for 6 messages, but pci_alloc_msix() only returns a count of 134660484Sobrien * 3. Call the three vectors allocated by pci_alloc_msix() A, B, and 134760484Sobrien * C. After the call to pci_alloc_msix(), the device will be setup to 134860484Sobrien * have an MSI-X table of ABC--- (where - means no vector assigned). 134960484Sobrien * If the driver ten passes a vector array of { 1, 0, 1, 2, 0, 2 }, 135060484Sobrien * then the MSI-X table will look like A-AB-B, and the 'C' vector will 135160484Sobrien * be freed back to the system. This device will also have valid 135260484Sobrien * SYS_RES_IRQ rids of 1, 3, 4, and 6. 135360484Sobrien * 135460484Sobrien * In any case, the SYS_RES_IRQ rid X will always map to the message 135560484Sobrien * at MSI-X table index X - 1 and will only be valid if a vector is 135660484Sobrien * assigned to that table entry. 135760484Sobrien */ 135860484Sobrienint 135960484Sobrienpci_remap_msix_method(device_t dev, device_t child, int count, 136060484Sobrien const u_int *vectors) 136160484Sobrien{ 136260484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 136360484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 136460484Sobrien struct resource_list_entry *rle; 136560484Sobrien int i, irq, j, *used; 136660484Sobrien 136760484Sobrien /* 136860484Sobrien * Have to have at least one message in the table but the 136960484Sobrien * table can't be bigger than the actual MSI-X table in the 137060484Sobrien * device. 137160484Sobrien */ 137260484Sobrien if (count == 0 || count > msix->msix_msgnum) 137360484Sobrien return (EINVAL); 137460484Sobrien 137560484Sobrien /* Sanity check the vectors. */ 137660484Sobrien for (i = 0; i < count; i++) 137760484Sobrien if (vectors[i] > msix->msix_alloc) 137860484Sobrien return (EINVAL); 137960484Sobrien 138060484Sobrien /* 138177298Sobrien * Make sure there aren't any holes in the vectors to be used. 138260484Sobrien * It's a big pain to support it, and it doesn't really make 138360484Sobrien * sense anyway. Also, at least one vector must be used. 138460484Sobrien */ 138560484Sobrien used = malloc(sizeof(int) * msix->msix_alloc, M_DEVBUF, M_WAITOK | 138660484Sobrien M_ZERO); 138760484Sobrien for (i = 0; i < count; i++) 138860484Sobrien if (vectors[i] != 0) 138960484Sobrien used[vectors[i] - 1] = 1; 139060484Sobrien for (i = 0; i < msix->msix_alloc - 1; i++) 139160484Sobrien if (used[i] == 0 && used[i + 1] == 1) { 139260484Sobrien free(used, M_DEVBUF); 139360484Sobrien return (EINVAL); 139460484Sobrien } 139560484Sobrien if (used[0] != 1) { 139660484Sobrien free(used, M_DEVBUF); 139760484Sobrien return (EINVAL); 139860484Sobrien } 139960484Sobrien 140060484Sobrien /* Make sure none of the resources are allocated. */ 140160484Sobrien for (i = 0; i < msix->msix_table_len; i++) { 140260484Sobrien if (msix->msix_table[i].mte_vector == 0) 140360484Sobrien continue; 140460484Sobrien if (msix->msix_table[i].mte_handlers > 0) 140560484Sobrien return (EBUSY); 140660484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); 140760484Sobrien KASSERT(rle != NULL, ("missing resource")); 140877298Sobrien if (rle->res != NULL) 140977298Sobrien return (EBUSY); 141060484Sobrien } 141160484Sobrien 141260484Sobrien /* Free the existing resource list entries. */ 141360484Sobrien for (i = 0; i < msix->msix_table_len; i++) { 141460484Sobrien if (msix->msix_table[i].mte_vector == 0) 141560484Sobrien continue; 141660484Sobrien resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1); 141760484Sobrien } 141860484Sobrien 141960484Sobrien /* 142060484Sobrien * Build the new virtual table keeping track of which vectors are 142160484Sobrien * used. 142260484Sobrien */ 142360484Sobrien free(msix->msix_table, M_DEVBUF); 142460484Sobrien msix->msix_table = malloc(sizeof(struct msix_table_entry) * count, 142560484Sobrien M_DEVBUF, M_WAITOK | M_ZERO); 142660484Sobrien for (i = 0; i < count; i++) 142760484Sobrien msix->msix_table[i].mte_vector = vectors[i]; 142860484Sobrien msix->msix_table_len = count; 142960484Sobrien 143060484Sobrien /* Free any unused IRQs and resize the vectors array if necessary. */ 143160484Sobrien j = msix->msix_alloc - 1; 143260484Sobrien if (used[j] == 0) { 143360484Sobrien struct msix_vector *vec; 143460484Sobrien 143560484Sobrien while (used[j] == 0) { 143660484Sobrien PCIB_RELEASE_MSIX(device_get_parent(dev), child, 143789857Sobrien msix->msix_vectors[j].mv_irq); 143860484Sobrien j--; 143960484Sobrien } 144060484Sobrien vec = malloc(sizeof(struct msix_vector) * (j + 1), M_DEVBUF, 144160484Sobrien M_WAITOK); 144260484Sobrien bcopy(msix->msix_vectors, vec, sizeof(struct msix_vector) * 144360484Sobrien (j + 1)); 144460484Sobrien free(msix->msix_vectors, M_DEVBUF); 144589857Sobrien msix->msix_vectors = vec; 144660484Sobrien msix->msix_alloc = j + 1; 144760484Sobrien } 144860484Sobrien free(used, M_DEVBUF); 144960484Sobrien 145060484Sobrien /* Map the IRQs onto the rids. */ 145160484Sobrien for (i = 0; i < count; i++) { 145260484Sobrien if (vectors[i] == 0) 145360484Sobrien continue; 145460484Sobrien irq = msix->msix_vectors[vectors[i]].mv_irq; 145560484Sobrien resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq, 145660484Sobrien irq, 1); 145760484Sobrien } 145860484Sobrien 145960484Sobrien if (bootverbose) { 146060484Sobrien device_printf(child, "Remapped MSI-X IRQs as: "); 146160484Sobrien for (i = 0; i < count; i++) { 146260484Sobrien if (i != 0) 146360484Sobrien printf(", "); 146460484Sobrien if (vectors[i] == 0) 146560484Sobrien printf("---"); 146660484Sobrien else 146760484Sobrien printf("%d", 146860484Sobrien msix->msix_vectors[vectors[i]].mv_irq); 146960484Sobrien } 147060484Sobrien printf("\n"); 147189857Sobrien } 147260484Sobrien 147360484Sobrien return (0); 147460484Sobrien} 147560484Sobrien 147660484Sobrienstatic int 147760484Sobrienpci_release_msix(device_t dev, device_t child) 147860484Sobrien{ 147960484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 148060484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 148160484Sobrien struct resource_list_entry *rle; 148260484Sobrien int i; 148360484Sobrien 148460484Sobrien /* Do we have any messages to release? */ 148560484Sobrien if (msix->msix_alloc == 0) 148660484Sobrien return (ENODEV); 148760484Sobrien 148860484Sobrien /* Make sure none of the resources are allocated. */ 148960484Sobrien for (i = 0; i < msix->msix_table_len; i++) { 149060484Sobrien if (msix->msix_table[i].mte_vector == 0) 149160484Sobrien continue; 149260484Sobrien if (msix->msix_table[i].mte_handlers > 0) 149360484Sobrien return (EBUSY); 149460484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); 149560484Sobrien KASSERT(rle != NULL, ("missing resource")); 149660484Sobrien if (rle->res != NULL) 149760484Sobrien return (EBUSY); 149860484Sobrien } 149960484Sobrien 150060484Sobrien /* Update control register to disable MSI-X. */ 150160484Sobrien msix->msix_ctrl &= ~PCIM_MSIXCTRL_MSIX_ENABLE; 150260484Sobrien pci_write_config(child, msix->msix_location + PCIR_MSIX_CTRL, 150360484Sobrien msix->msix_ctrl, 2); 150460484Sobrien 150560484Sobrien /* Free the resource list entries. */ 150660484Sobrien for (i = 0; i < msix->msix_table_len; i++) { 150760484Sobrien if (msix->msix_table[i].mte_vector == 0) 150860484Sobrien continue; 150960484Sobrien resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1); 151060484Sobrien } 151160484Sobrien free(msix->msix_table, M_DEVBUF); 151260484Sobrien msix->msix_table_len = 0; 151360484Sobrien 151460484Sobrien /* Release the IRQs. */ 151560484Sobrien for (i = 0; i < msix->msix_alloc; i++) 151660484Sobrien PCIB_RELEASE_MSIX(device_get_parent(dev), child, 151760484Sobrien msix->msix_vectors[i].mv_irq); 151860484Sobrien free(msix->msix_vectors, M_DEVBUF); 151960484Sobrien msix->msix_alloc = 0; 152060484Sobrien return (0); 152160484Sobrien} 152260484Sobrien 152360484Sobrien/* 152460484Sobrien * Return the max supported MSI-X messages this device supports. 152560484Sobrien * Basically, assuming the MD code can alloc messages, this function 152660484Sobrien * should return the maximum value that pci_alloc_msix() can return. 152760484Sobrien * Thus, it is subject to the tunables, etc. 152860484Sobrien */ 152960484Sobrienint 153060484Sobrienpci_msix_count_method(device_t dev, device_t child) 153160484Sobrien{ 153260484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 153360484Sobrien struct pcicfg_msix *msix = &dinfo->cfg.msix; 153460484Sobrien 153560484Sobrien if (pci_do_msix && msix->msix_location != 0) 153660484Sobrien return (msix->msix_msgnum); 153760484Sobrien return (0); 153860484Sobrien} 153960484Sobrien 154060484Sobrien/* 154160484Sobrien * HyperTransport MSI mapping control 154260484Sobrien */ 154360484Sobrienvoid 154460484Sobrienpci_ht_map_msi(device_t dev, uint64_t addr) 154560484Sobrien{ 154660484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 154760484Sobrien struct pcicfg_ht *ht = &dinfo->cfg.ht; 154860484Sobrien 154960484Sobrien if (!ht->ht_msimap) 155060484Sobrien return; 155160484Sobrien 155260484Sobrien if (addr && !(ht->ht_msictrl & PCIM_HTCMD_MSI_ENABLE) && 155360484Sobrien ht->ht_msiaddr >> 20 == addr >> 20) { 155460484Sobrien /* Enable MSI -> HT mapping. */ 155560484Sobrien ht->ht_msictrl |= PCIM_HTCMD_MSI_ENABLE; 155660484Sobrien pci_write_config(dev, ht->ht_msimap + PCIR_HT_COMMAND, 155760484Sobrien ht->ht_msictrl, 2); 155860484Sobrien } 155960484Sobrien 156060484Sobrien if (!addr && ht->ht_msictrl & PCIM_HTCMD_MSI_ENABLE) { 156160484Sobrien /* Disable MSI -> HT mapping. */ 156260484Sobrien ht->ht_msictrl &= ~PCIM_HTCMD_MSI_ENABLE; 156360484Sobrien pci_write_config(dev, ht->ht_msimap + PCIR_HT_COMMAND, 156460484Sobrien ht->ht_msictrl, 2); 156560484Sobrien } 156660484Sobrien} 156760484Sobrien 156860484Sobrien/* 156960484Sobrien * Support for MSI message signalled interrupts. 157077298Sobrien */ 157160484Sobrienvoid 157260484Sobrienpci_enable_msi(device_t dev, uint64_t address, uint16_t data) 157377298Sobrien{ 157460484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 157560484Sobrien struct pcicfg_msi *msi = &dinfo->cfg.msi; 157660484Sobrien 157760484Sobrien /* Write data and address values. */ 157860484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR, 157960484Sobrien address & 0xffffffff, 4); 158060484Sobrien if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) { 158160484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR_HIGH, 158260484Sobrien address >> 32, 4); 158360484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA_64BIT, 158460484Sobrien data, 2); 158560484Sobrien } else 158660484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA, data, 158760484Sobrien 2); 158860484Sobrien 158960484Sobrien /* Enable MSI in the control register. */ 159060484Sobrien msi->msi_ctrl |= PCIM_MSICTRL_MSI_ENABLE; 159160484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl, 159260484Sobrien 2); 159360484Sobrien 159460484Sobrien /* Enable MSI -> HT mapping. */ 159560484Sobrien pci_ht_map_msi(dev, address); 159660484Sobrien} 159760484Sobrien 159860484Sobrienvoid 159977298Sobrienpci_disable_msi(device_t dev) 160077298Sobrien{ 160160484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 160260484Sobrien struct pcicfg_msi *msi = &dinfo->cfg.msi; 160360484Sobrien 160460484Sobrien /* Disable MSI -> HT mapping. */ 160560484Sobrien pci_ht_map_msi(dev, 0); 160660484Sobrien 160760484Sobrien /* Disable MSI in the control register. */ 160860484Sobrien msi->msi_ctrl &= ~PCIM_MSICTRL_MSI_ENABLE; 160960484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl, 161060484Sobrien 2); 161160484Sobrien} 161260484Sobrien 161360484Sobrien/* 161460484Sobrien * Restore MSI registers during resume. If MSI is enabled then 161560484Sobrien * restore the data and address registers in addition to the control 161660484Sobrien * register. 161760484Sobrien */ 161860484Sobrienstatic void 161960484Sobrienpci_resume_msi(device_t dev) 162060484Sobrien{ 162160484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 162260484Sobrien struct pcicfg_msi *msi = &dinfo->cfg.msi; 162360484Sobrien uint64_t address; 162460484Sobrien uint16_t data; 162560484Sobrien 162677298Sobrien if (msi->msi_ctrl & PCIM_MSICTRL_MSI_ENABLE) { 162760484Sobrien address = msi->msi_addr; 162860484Sobrien data = msi->msi_data; 162960484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR, 163060484Sobrien address & 0xffffffff, 4); 163160484Sobrien if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) { 163260484Sobrien pci_write_config(dev, msi->msi_location + 163360484Sobrien PCIR_MSI_ADDR_HIGH, address >> 32, 4); 163460484Sobrien pci_write_config(dev, msi->msi_location + 163560484Sobrien PCIR_MSI_DATA_64BIT, data, 2); 163660484Sobrien } else 163760484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA, 163860484Sobrien data, 2); 163960484Sobrien } 164060484Sobrien pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl, 164160484Sobrien 2); 164260484Sobrien} 164360484Sobrien 164460484Sobrienint 164560484Sobrienpci_remap_msi_irq(device_t dev, u_int irq) 164660484Sobrien{ 164777298Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 164860484Sobrien pcicfgregs *cfg = &dinfo->cfg; 164960484Sobrien struct resource_list_entry *rle; 165060484Sobrien struct msix_table_entry *mte; 165160484Sobrien struct msix_vector *mv; 165260484Sobrien device_t bus; 165360484Sobrien uint64_t addr; 165460484Sobrien uint32_t data; 165560484Sobrien int error, i, j; 165660484Sobrien 165760484Sobrien bus = device_get_parent(dev); 165860484Sobrien 165960484Sobrien /* 166060484Sobrien * Handle MSI first. We try to find this IRQ among our list 166160484Sobrien * of MSI IRQs. If we find it, we request updated address and 166260484Sobrien * data registers and apply the results. 166360484Sobrien */ 166460484Sobrien if (cfg->msi.msi_alloc > 0) { 166560484Sobrien 166660484Sobrien /* If we don't have any active handlers, nothing to do. */ 166760484Sobrien if (cfg->msi.msi_handlers == 0) 166860484Sobrien return (0); 166960484Sobrien for (i = 0; i < cfg->msi.msi_alloc; i++) { 167060484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 167160484Sobrien i + 1); 167260484Sobrien if (rle->start == irq) { 167360484Sobrien error = PCIB_MAP_MSI(device_get_parent(bus), 167460484Sobrien dev, irq, &addr, &data); 167560484Sobrien if (error) 167660484Sobrien return (error); 167760484Sobrien pci_disable_msi(dev); 167860484Sobrien dinfo->cfg.msi.msi_addr = addr; 167960484Sobrien dinfo->cfg.msi.msi_data = data; 168060484Sobrien pci_enable_msi(dev, addr, data); 168160484Sobrien return (0); 168260484Sobrien } 168360484Sobrien } 168460484Sobrien return (ENOENT); 168560484Sobrien } 168660484Sobrien 168760484Sobrien /* 168860484Sobrien * For MSI-X, we check to see if we have this IRQ. If we do, 168960484Sobrien * we request the updated mapping info. If that works, we go 169060484Sobrien * through all the slots that use this IRQ and update them. 169160484Sobrien */ 169260484Sobrien if (cfg->msix.msix_alloc > 0) { 169360484Sobrien for (i = 0; i < cfg->msix.msix_alloc; i++) { 169460484Sobrien mv = &cfg->msix.msix_vectors[i]; 169560484Sobrien if (mv->mv_irq == irq) { 169660484Sobrien error = PCIB_MAP_MSI(device_get_parent(bus), 169760484Sobrien dev, irq, &addr, &data); 169860484Sobrien if (error) 169960484Sobrien return (error); 170060484Sobrien mv->mv_address = addr; 170160484Sobrien mv->mv_data = data; 170260484Sobrien for (j = 0; j < cfg->msix.msix_table_len; j++) { 170360484Sobrien mte = &cfg->msix.msix_table[j]; 170460484Sobrien if (mte->mte_vector != i + 1) 170560484Sobrien continue; 170660484Sobrien if (mte->mte_handlers == 0) 170777298Sobrien continue; 170860484Sobrien pci_mask_msix(dev, j); 170960484Sobrien pci_enable_msix(dev, j, addr, data); 171060484Sobrien pci_unmask_msix(dev, j); 171160484Sobrien } 171260484Sobrien } 171360484Sobrien } 171460484Sobrien return (ENOENT); 171577298Sobrien } 171677298Sobrien 171760484Sobrien return (ENOENT); 171860484Sobrien} 171960484Sobrien 172060484Sobrien/* 172160484Sobrien * Returns true if the specified device is blacklisted because MSI 172260484Sobrien * doesn't work. 172360484Sobrien */ 172460484Sobrienint 172560484Sobrienpci_msi_device_blacklisted(device_t dev) 172660484Sobrien{ 172760484Sobrien struct pci_quirk *q; 172860484Sobrien 172960484Sobrien if (!pci_honor_msi_blacklist) 173060484Sobrien return (0); 173160484Sobrien 173260484Sobrien for (q = &pci_quirks[0]; q->devid; q++) { 173360484Sobrien if (q->devid == pci_get_devid(dev) && 173460484Sobrien q->type == PCI_QUIRK_DISABLE_MSI) 173560484Sobrien return (1); 173660484Sobrien } 173760484Sobrien return (0); 173860484Sobrien} 173960484Sobrien 174060484Sobrien/* 174160484Sobrien * Determine if MSI is blacklisted globally on this sytem. Currently, 174260484Sobrien * we just check for blacklisted chipsets as represented by the 174360484Sobrien * host-PCI bridge at device 0:0:0. In the future, it may become 174460484Sobrien * necessary to check other system attributes, such as the kenv values 174560484Sobrien * that give the motherboard manufacturer and model number. 174660484Sobrien */ 174760484Sobrienstatic int 174860484Sobrienpci_msi_blacklisted(void) 174960484Sobrien{ 175060484Sobrien device_t dev; 175160484Sobrien 175260484Sobrien if (!pci_honor_msi_blacklist) 175360484Sobrien return (0); 175460484Sobrien 175560484Sobrien /* Blacklist all non-PCI-express and non-PCI-X chipsets. */ 175660484Sobrien if (!(pcie_chipset || pcix_chipset)) 175760484Sobrien return (1); 175860484Sobrien 175960484Sobrien dev = pci_find_bsf(0, 0, 0); 176060484Sobrien if (dev != NULL) 176160484Sobrien return (pci_msi_device_blacklisted(dev)); 176260484Sobrien return (0); 176360484Sobrien} 176460484Sobrien 176560484Sobrien/* 176660484Sobrien * Attempt to allocate *count MSI messages. The actual number allocated is 176760484Sobrien * returned in *count. After this function returns, each message will be 176860484Sobrien * available to the driver as SYS_RES_IRQ resources starting at a rid 1. 176960484Sobrien */ 177060484Sobrienint 177160484Sobrienpci_alloc_msi_method(device_t dev, device_t child, int *count) 177260484Sobrien{ 177360484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 177460484Sobrien pcicfgregs *cfg = &dinfo->cfg; 177560484Sobrien struct resource_list_entry *rle; 177660484Sobrien int actual, error, i, irqs[32]; 177760484Sobrien uint16_t ctrl; 177860484Sobrien 177960484Sobrien /* Don't let count == 0 get us into trouble. */ 178060484Sobrien if (*count == 0) 178189857Sobrien return (EINVAL); 178289857Sobrien 178360484Sobrien /* If rid 0 is allocated, then fail. */ 178460484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0); 178560484Sobrien if (rle != NULL && rle->res != NULL) 178660484Sobrien return (ENXIO); 178760484Sobrien 178860484Sobrien /* Already have allocated messages? */ 178960484Sobrien if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0) 179060484Sobrien return (ENXIO); 179160484Sobrien 179260484Sobrien /* If MSI is blacklisted for this system, fail. */ 179360484Sobrien if (pci_msi_blacklisted()) 179460484Sobrien return (ENXIO); 179560484Sobrien 179660484Sobrien /* MSI capability present? */ 179760484Sobrien if (cfg->msi.msi_location == 0 || !pci_do_msi) 179860484Sobrien return (ENODEV); 179960484Sobrien 180060484Sobrien if (bootverbose) 180160484Sobrien device_printf(child, 180260484Sobrien "attempting to allocate %d MSI vectors (%d supported)\n", 180360484Sobrien *count, cfg->msi.msi_msgnum); 180460484Sobrien 180560484Sobrien /* Don't ask for more than the device supports. */ 180660484Sobrien actual = min(*count, cfg->msi.msi_msgnum); 180760484Sobrien 180860484Sobrien /* Don't ask for more than 32 messages. */ 180960484Sobrien actual = min(actual, 32); 181060484Sobrien 181160484Sobrien /* MSI requires power of 2 number of messages. */ 181260484Sobrien if (!powerof2(actual)) 181360484Sobrien return (EINVAL); 181460484Sobrien 181560484Sobrien for (;;) { 181660484Sobrien /* Try to allocate N messages. */ 181760484Sobrien error = PCIB_ALLOC_MSI(device_get_parent(dev), child, actual, 181860484Sobrien cfg->msi.msi_msgnum, irqs); 181960484Sobrien if (error == 0) 182060484Sobrien break; 182160484Sobrien if (actual == 1) 182260484Sobrien return (error); 182360484Sobrien 182460484Sobrien /* Try N / 2. */ 182560484Sobrien actual >>= 1; 182660484Sobrien } 182760484Sobrien 182860484Sobrien /* 182960484Sobrien * We now have N actual messages mapped onto SYS_RES_IRQ 183060484Sobrien * resources in the irqs[] array, so add new resources 183160484Sobrien * starting at rid 1. 183260484Sobrien */ 183360484Sobrien for (i = 0; i < actual; i++) 183460484Sobrien resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, 183560484Sobrien irqs[i], irqs[i], 1); 183660484Sobrien 183760484Sobrien if (bootverbose) { 183860484Sobrien if (actual == 1) 183960484Sobrien device_printf(child, "using IRQ %d for MSI\n", irqs[0]); 184060484Sobrien else { 184160484Sobrien int run; 184260484Sobrien 184360484Sobrien /* 184460484Sobrien * Be fancy and try to print contiguous runs 184560484Sobrien * of IRQ values as ranges. 'run' is true if 184660484Sobrien * we are in a range. 184760484Sobrien */ 184860484Sobrien device_printf(child, "using IRQs %d", irqs[0]); 184960484Sobrien run = 0; 185060484Sobrien for (i = 1; i < actual; i++) { 185160484Sobrien 185260484Sobrien /* Still in a run? */ 185360484Sobrien if (irqs[i] == irqs[i - 1] + 1) { 185460484Sobrien run = 1; 185560484Sobrien continue; 185660484Sobrien } 185760484Sobrien 185860484Sobrien /* Finish previous range. */ 185960484Sobrien if (run) { 186060484Sobrien printf("-%d", irqs[i - 1]); 186160484Sobrien run = 0; 186260484Sobrien } 186360484Sobrien 186460484Sobrien /* Start new range. */ 186560484Sobrien printf(",%d", irqs[i]); 186660484Sobrien } 186760484Sobrien 186860484Sobrien /* Unfinished range? */ 186960484Sobrien if (run) 187060484Sobrien printf("-%d", irqs[actual - 1]); 187160484Sobrien printf(" for MSI\n"); 187289857Sobrien } 187360484Sobrien } 187460484Sobrien 187560484Sobrien /* Update control register with actual count. */ 187660484Sobrien ctrl = cfg->msi.msi_ctrl; 187760484Sobrien ctrl &= ~PCIM_MSICTRL_MME_MASK; 187860484Sobrien ctrl |= (ffs(actual) - 1) << 4; 187960484Sobrien cfg->msi.msi_ctrl = ctrl; 188060484Sobrien pci_write_config(child, cfg->msi.msi_location + PCIR_MSI_CTRL, ctrl, 2); 188160484Sobrien 188260484Sobrien /* Update counts of alloc'd messages. */ 188360484Sobrien cfg->msi.msi_alloc = actual; 188460484Sobrien cfg->msi.msi_handlers = 0; 188560484Sobrien *count = actual; 188660484Sobrien return (0); 188760484Sobrien} 188860484Sobrien 188960484Sobrien/* Release the MSI messages associated with this device. */ 189060484Sobrienint 189160484Sobrienpci_release_msi_method(device_t dev, device_t child) 189260484Sobrien{ 189360484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 189460484Sobrien struct pcicfg_msi *msi = &dinfo->cfg.msi; 189560484Sobrien struct resource_list_entry *rle; 189660484Sobrien int error, i, irqs[32]; 189760484Sobrien 189860484Sobrien /* Try MSI-X first. */ 189960484Sobrien error = pci_release_msix(dev, child); 190060484Sobrien if (error != ENODEV) 190160484Sobrien return (error); 190260484Sobrien 190360484Sobrien /* Do we have any messages to release? */ 190460484Sobrien if (msi->msi_alloc == 0) 190560484Sobrien return (ENODEV); 190660484Sobrien KASSERT(msi->msi_alloc <= 32, ("more than 32 alloc'd messages")); 190777298Sobrien 190860484Sobrien /* Make sure none of the resources are allocated. */ 190960484Sobrien if (msi->msi_handlers > 0) 191077298Sobrien return (EBUSY); 191160484Sobrien for (i = 0; i < msi->msi_alloc; i++) { 191260484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); 191360484Sobrien KASSERT(rle != NULL, ("missing MSI resource")); 191460484Sobrien if (rle->res != NULL) 191560484Sobrien return (EBUSY); 191660484Sobrien irqs[i] = rle->start; 191760484Sobrien } 191860484Sobrien 191960484Sobrien /* Update control register with 0 count. */ 192060484Sobrien KASSERT(!(msi->msi_ctrl & PCIM_MSICTRL_MSI_ENABLE), 192160484Sobrien ("%s: MSI still enabled", __func__)); 192260484Sobrien msi->msi_ctrl &= ~PCIM_MSICTRL_MME_MASK; 192360484Sobrien pci_write_config(child, msi->msi_location + PCIR_MSI_CTRL, 192460484Sobrien msi->msi_ctrl, 2); 192560484Sobrien 192660484Sobrien /* Release the messages. */ 192760484Sobrien PCIB_RELEASE_MSI(device_get_parent(dev), child, msi->msi_alloc, irqs); 192860484Sobrien for (i = 0; i < msi->msi_alloc; i++) 192960484Sobrien resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1); 193060484Sobrien 193160484Sobrien /* Update alloc count. */ 193260484Sobrien msi->msi_alloc = 0; 193360484Sobrien msi->msi_addr = 0; 193460484Sobrien msi->msi_data = 0; 193560484Sobrien return (0); 193660484Sobrien} 193760484Sobrien 193860484Sobrien/* 193960484Sobrien * Return the max supported MSI messages this device supports. 194060484Sobrien * Basically, assuming the MD code can alloc messages, this function 194160484Sobrien * should return the maximum value that pci_alloc_msi() can return. 194260484Sobrien * Thus, it is subject to the tunables, etc. 194360484Sobrien */ 194460484Sobrienint 194560484Sobrienpci_msi_count_method(device_t dev, device_t child) 194660484Sobrien{ 194760484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 194860484Sobrien struct pcicfg_msi *msi = &dinfo->cfg.msi; 194960484Sobrien 195060484Sobrien if (pci_do_msi && msi->msi_location != 0) 195160484Sobrien return (msi->msi_msgnum); 195260484Sobrien return (0); 195360484Sobrien} 195460484Sobrien 195560484Sobrien/* free pcicfgregs structure and all depending data structures */ 195660484Sobrien 195760484Sobrienint 195860484Sobrienpci_freecfg(struct pci_devinfo *dinfo) 195960484Sobrien{ 196060484Sobrien struct devlist *devlist_head; 196160484Sobrien int i; 196260484Sobrien 196360484Sobrien devlist_head = &pci_devq; 196460484Sobrien 196560484Sobrien if (dinfo->cfg.vpd.vpd_reg) { 196660484Sobrien free(dinfo->cfg.vpd.vpd_ident, M_DEVBUF); 196760484Sobrien for (i = 0; i < dinfo->cfg.vpd.vpd_rocnt; i++) 196877298Sobrien free(dinfo->cfg.vpd.vpd_ros[i].value, M_DEVBUF); 196960484Sobrien free(dinfo->cfg.vpd.vpd_ros, M_DEVBUF); 197060484Sobrien for (i = 0; i < dinfo->cfg.vpd.vpd_wcnt; i++) 197160484Sobrien free(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF); 197260484Sobrien free(dinfo->cfg.vpd.vpd_w, M_DEVBUF); 197360484Sobrien } 197460484Sobrien STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); 197560484Sobrien free(dinfo, M_DEVBUF); 197660484Sobrien 197760484Sobrien /* increment the generation count */ 197860484Sobrien pci_generation++; 197960484Sobrien 198060484Sobrien /* we're losing one device */ 198160484Sobrien pci_numdevs--; 198260484Sobrien return (0); 198360484Sobrien} 198460484Sobrien 198560484Sobrien/* 198660484Sobrien * PCI power manangement 198760484Sobrien */ 198860484Sobrienint 198960484Sobrienpci_set_powerstate_method(device_t dev, device_t child, int state) 199060484Sobrien{ 199160484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 199260484Sobrien pcicfgregs *cfg = &dinfo->cfg; 199360484Sobrien uint16_t status; 199460484Sobrien int result, oldstate, highest, delay; 199560484Sobrien 199660484Sobrien if (cfg->pp.pp_cap == 0) 199760484Sobrien return (EOPNOTSUPP); 199860484Sobrien 199960484Sobrien /* 200060484Sobrien * Optimize a no state change request away. While it would be OK to 200160484Sobrien * write to the hardware in theory, some devices have shown odd 200260484Sobrien * behavior when going from D3 -> D3. 200389857Sobrien */ 200489857Sobrien oldstate = pci_get_powerstate(child); 200589857Sobrien if (oldstate == state) 200689857Sobrien return (0); 200760484Sobrien 200889857Sobrien /* 200960484Sobrien * The PCI power management specification states that after a state 201060484Sobrien * transition between PCI power states, system software must 201160484Sobrien * guarantee a minimal delay before the function accesses the device. 201260484Sobrien * Compute the worst case delay that we need to guarantee before we 201360484Sobrien * access the device. Many devices will be responsive much more 201489857Sobrien * quickly than this delay, but there are some that don't respond 201589857Sobrien * instantly to state changes. Transitions to/from D3 state require 201689857Sobrien * 10ms, while D2 requires 200us, and D0/1 require none. The delay 201789857Sobrien * is done below with DELAY rather than a sleeper function because 201860484Sobrien * this function can be called from contexts where we cannot sleep. 201960484Sobrien */ 202060484Sobrien highest = (oldstate > state) ? oldstate : state; 202160484Sobrien if (highest == PCI_POWERSTATE_D3) 202260484Sobrien delay = 10000; 202389857Sobrien else if (highest == PCI_POWERSTATE_D2) 202489857Sobrien delay = 200; 202589857Sobrien else 202660484Sobrien delay = 0; 202760484Sobrien status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2) 202860484Sobrien & ~PCIM_PSTAT_DMASK; 202989857Sobrien result = 0; 203060484Sobrien switch (state) { 203189857Sobrien case PCI_POWERSTATE_D0: 203260484Sobrien status |= PCIM_PSTAT_D0; 203377298Sobrien break; 203460484Sobrien case PCI_POWERSTATE_D1: 203560484Sobrien if ((cfg->pp.pp_cap & PCIM_PCAP_D1SUPP) == 0) 203689857Sobrien return (EOPNOTSUPP); 203760484Sobrien status |= PCIM_PSTAT_D1; 203860484Sobrien break; 203960484Sobrien case PCI_POWERSTATE_D2: 204060484Sobrien if ((cfg->pp.pp_cap & PCIM_PCAP_D2SUPP) == 0) 204160484Sobrien return (EOPNOTSUPP); 204260484Sobrien status |= PCIM_PSTAT_D2; 204360484Sobrien break; 204460484Sobrien case PCI_POWERSTATE_D3: 204560484Sobrien status |= PCIM_PSTAT_D3; 204660484Sobrien break; 204760484Sobrien default: 204860484Sobrien return (EINVAL); 204960484Sobrien } 205060484Sobrien 205160484Sobrien if (bootverbose) 205260484Sobrien printf( 205360484Sobrien "pci%d:%d:%d:%d: Transition from D%d to D%d\n", 205460484Sobrien dinfo->cfg.domain, dinfo->cfg.bus, dinfo->cfg.slot, 205560484Sobrien dinfo->cfg.func, oldstate, state); 205660484Sobrien 205760484Sobrien PCI_WRITE_CONFIG(dev, child, cfg->pp.pp_status, status, 2); 205860484Sobrien if (delay) 205960484Sobrien DELAY(delay); 206060484Sobrien return (0); 206160484Sobrien} 206260484Sobrien 206360484Sobrienint 206460484Sobrienpci_get_powerstate_method(device_t dev, device_t child) 206560484Sobrien{ 206660484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 206760484Sobrien pcicfgregs *cfg = &dinfo->cfg; 206860484Sobrien uint16_t status; 206960484Sobrien int result; 207089857Sobrien 207160484Sobrien if (cfg->pp.pp_cap != 0) { 207260484Sobrien status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2); 207360484Sobrien switch (status & PCIM_PSTAT_DMASK) { 207460484Sobrien case PCIM_PSTAT_D0: 207560484Sobrien result = PCI_POWERSTATE_D0; 207660484Sobrien break; 207760484Sobrien case PCIM_PSTAT_D1: 207860484Sobrien result = PCI_POWERSTATE_D1; 207960484Sobrien break; 208060484Sobrien case PCIM_PSTAT_D2: 208160484Sobrien result = PCI_POWERSTATE_D2; 208260484Sobrien break; 208360484Sobrien case PCIM_PSTAT_D3: 208460484Sobrien result = PCI_POWERSTATE_D3; 208560484Sobrien break; 208660484Sobrien default: 208760484Sobrien result = PCI_POWERSTATE_UNKNOWN; 208860484Sobrien break; 208960484Sobrien } 209060484Sobrien } else { 209160484Sobrien /* No support, device is always at D0 */ 209260484Sobrien result = PCI_POWERSTATE_D0; 209360484Sobrien } 209460484Sobrien return (result); 209577298Sobrien} 209660484Sobrien 209760484Sobrien/* 209860484Sobrien * Some convenience functions for PCI device drivers. 209960484Sobrien */ 210060484Sobrien 210160484Sobrienstatic __inline void 210260484Sobrienpci_set_command_bit(device_t dev, device_t child, uint16_t bit) 210360484Sobrien{ 210460484Sobrien uint16_t command; 210560484Sobrien 210660484Sobrien command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); 210760484Sobrien command |= bit; 210860484Sobrien PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2); 210960484Sobrien} 211060484Sobrien 211160484Sobrienstatic __inline void 211260484Sobrienpci_clear_command_bit(device_t dev, device_t child, uint16_t bit) 211360484Sobrien{ 211460484Sobrien uint16_t command; 211560484Sobrien 211660484Sobrien command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); 211760484Sobrien command &= ~bit; 211860484Sobrien PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2); 211960484Sobrien} 212060484Sobrien 212160484Sobrienint 212260484Sobrienpci_enable_busmaster_method(device_t dev, device_t child) 212360484Sobrien{ 212460484Sobrien pci_set_command_bit(dev, child, PCIM_CMD_BUSMASTEREN); 212560484Sobrien return (0); 212660484Sobrien} 212760484Sobrien 212860484Sobrienint 212960484Sobrienpci_disable_busmaster_method(device_t dev, device_t child) 213060484Sobrien{ 213160484Sobrien pci_clear_command_bit(dev, child, PCIM_CMD_BUSMASTEREN); 213260484Sobrien return (0); 213360484Sobrien} 213460484Sobrien 213560484Sobrienint 213660484Sobrienpci_enable_io_method(device_t dev, device_t child, int space) 213760484Sobrien{ 213860484Sobrien uint16_t command; 213960484Sobrien uint16_t bit; 214060484Sobrien char *error; 214160484Sobrien 214260484Sobrien bit = 0; 214360484Sobrien error = NULL; 214460484Sobrien 214560484Sobrien switch(space) { 214660484Sobrien case SYS_RES_IOPORT: 214760484Sobrien bit = PCIM_CMD_PORTEN; 214860484Sobrien error = "port"; 214960484Sobrien break; 215060484Sobrien case SYS_RES_MEMORY: 215160484Sobrien bit = PCIM_CMD_MEMEN; 215260484Sobrien error = "memory"; 215360484Sobrien break; 215460484Sobrien default: 215560484Sobrien return (EINVAL); 215660484Sobrien } 215760484Sobrien pci_set_command_bit(dev, child, bit); 215860484Sobrien /* Some devices seem to need a brief stall here, what do to? */ 215960484Sobrien command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); 216060484Sobrien if (command & bit) 216160484Sobrien return (0); 216260484Sobrien device_printf(child, "failed to enable %s mapping!\n", error); 216360484Sobrien return (ENXIO); 216460484Sobrien} 216560484Sobrien 216660484Sobrienint 216760484Sobrienpci_disable_io_method(device_t dev, device_t child, int space) 216860484Sobrien{ 216960484Sobrien uint16_t command; 217089857Sobrien uint16_t bit; 217160484Sobrien char *error; 217260484Sobrien 217360484Sobrien bit = 0; 217460484Sobrien error = NULL; 217560484Sobrien 217660484Sobrien switch(space) { 217760484Sobrien case SYS_RES_IOPORT: 217860484Sobrien bit = PCIM_CMD_PORTEN; 217960484Sobrien error = "port"; 218060484Sobrien break; 218160484Sobrien case SYS_RES_MEMORY: 218289857Sobrien bit = PCIM_CMD_MEMEN; 218360484Sobrien error = "memory"; 218460484Sobrien break; 218560484Sobrien default: 218660484Sobrien return (EINVAL); 218760484Sobrien } 218860484Sobrien pci_clear_command_bit(dev, child, bit); 218989857Sobrien command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2); 219060484Sobrien if (command & bit) { 219189857Sobrien device_printf(child, "failed to disable %s mapping!\n", error); 219289857Sobrien return (ENXIO); 219389857Sobrien } 219489857Sobrien return (0); 219560484Sobrien} 219660484Sobrien 219760484Sobrien/* 219860484Sobrien * New style pci driver. Parent device is either a pci-host-bridge or a 219989857Sobrien * pci-pci-bridge. Both kinds are represented by instances of pcib. 220060484Sobrien */ 220160484Sobrien 220260484Sobrienvoid 220360484Sobrienpci_print_verbose(struct pci_devinfo *dinfo) 220460484Sobrien{ 220560484Sobrien 220660484Sobrien if (bootverbose) { 220760484Sobrien pcicfgregs *cfg = &dinfo->cfg; 220860484Sobrien 220989857Sobrien printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", 221060484Sobrien cfg->vendor, cfg->device, cfg->revid); 221160484Sobrien printf("\tdomain=%d, bus=%d, slot=%d, func=%d\n", 221260484Sobrien cfg->domain, cfg->bus, cfg->slot, cfg->func); 221360484Sobrien printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", 221460484Sobrien cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype, 221560484Sobrien cfg->mfdev); 221660484Sobrien printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", 221760484Sobrien cfg->cmdreg, cfg->statreg, cfg->cachelnsz); 221860484Sobrien printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", 221989857Sobrien cfg->lattimer, cfg->lattimer * 30, cfg->mingnt, 222077298Sobrien cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); 222160484Sobrien if (cfg->intpin > 0) 222260484Sobrien printf("\tintpin=%c, irq=%d\n", 222360484Sobrien cfg->intpin +'a' -1, cfg->intline); 222460484Sobrien if (cfg->pp.pp_cap) { 222560484Sobrien uint16_t status; 222660484Sobrien 222760484Sobrien status = pci_read_config(cfg->dev, cfg->pp.pp_status, 2); 222860484Sobrien printf("\tpowerspec %d supports D0%s%s D3 current D%d\n", 222960484Sobrien cfg->pp.pp_cap & PCIM_PCAP_SPEC, 223060484Sobrien cfg->pp.pp_cap & PCIM_PCAP_D1SUPP ? " D1" : "", 223160484Sobrien cfg->pp.pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "", 223260484Sobrien status & PCIM_PSTAT_DMASK); 223360484Sobrien } 223460484Sobrien if (cfg->msi.msi_location) { 223560484Sobrien int ctrl; 223660484Sobrien 223760484Sobrien ctrl = cfg->msi.msi_ctrl; 223860484Sobrien printf("\tMSI supports %d message%s%s%s\n", 223960484Sobrien cfg->msi.msi_msgnum, 224060484Sobrien (cfg->msi.msi_msgnum == 1) ? "" : "s", 224160484Sobrien (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "", 224260484Sobrien (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks":""); 224360484Sobrien } 224460484Sobrien if (cfg->msix.msix_location) { 224560484Sobrien printf("\tMSI-X supports %d message%s ", 224660484Sobrien cfg->msix.msix_msgnum, 224760484Sobrien (cfg->msix.msix_msgnum == 1) ? "" : "s"); 224860484Sobrien if (cfg->msix.msix_table_bar == cfg->msix.msix_pba_bar) 224960484Sobrien printf("in map 0x%x\n", 225060484Sobrien cfg->msix.msix_table_bar); 225160484Sobrien else 225260484Sobrien printf("in maps 0x%x and 0x%x\n", 225389857Sobrien cfg->msix.msix_table_bar, 225460484Sobrien cfg->msix.msix_pba_bar); 225560484Sobrien } 225660484Sobrien } 225760484Sobrien} 225860484Sobrien 225960484Sobrienstatic int 226060484Sobrienpci_porten(device_t pcib, int b, int s, int f) 226160484Sobrien{ 226260484Sobrien return (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2) 226360484Sobrien & PCIM_CMD_PORTEN) != 0; 226460484Sobrien} 226560484Sobrien 226660484Sobrienstatic int 226760484Sobrienpci_memen(device_t pcib, int b, int s, int f) 226860484Sobrien{ 226960484Sobrien return (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2) 227060484Sobrien & PCIM_CMD_MEMEN) != 0; 227160484Sobrien} 227260484Sobrien 227360484Sobrien/* 227460484Sobrien * Add a resource based on a pci map register. Return 1 if the map 227560484Sobrien * register is a 32bit map register or 2 if it is a 64bit register. 227660484Sobrien */ 227760484Sobrienstatic int 227860484Sobrienpci_add_map(device_t pcib, device_t bus, device_t dev, 227960484Sobrien int b, int s, int f, int reg, struct resource_list *rl, int force, 228060484Sobrien int prefetch) 228160484Sobrien{ 228260484Sobrien uint32_t map; 228360484Sobrien pci_addr_t base; 228460484Sobrien pci_addr_t start, end, count; 228560484Sobrien uint8_t ln2size; 228660484Sobrien uint8_t ln2range; 228760484Sobrien uint32_t testval; 228889857Sobrien uint16_t cmd; 228977298Sobrien int type; 229060484Sobrien int barlen; 229160484Sobrien struct resource *res; 229260484Sobrien 229360484Sobrien map = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4); 229460484Sobrien PCIB_WRITE_CONFIG(pcib, b, s, f, reg, 0xffffffff, 4); 229560484Sobrien testval = PCIB_READ_CONFIG(pcib, b, s, f, reg, 4); 229660484Sobrien PCIB_WRITE_CONFIG(pcib, b, s, f, reg, map, 4); 229760484Sobrien 229860484Sobrien if (PCI_BAR_MEM(map)) 229960484Sobrien type = SYS_RES_MEMORY; 230060484Sobrien else 230160484Sobrien type = SYS_RES_IOPORT; 230260484Sobrien ln2size = pci_mapsize(testval); 230360484Sobrien ln2range = pci_maprange(testval); 230460484Sobrien base = pci_mapbase(map); 230560484Sobrien barlen = ln2range == 64 ? 2 : 1; 230660484Sobrien 230760484Sobrien /* 230860484Sobrien * For I/O registers, if bottom bit is set, and the next bit up 230960484Sobrien * isn't clear, we know we have a BAR that doesn't conform to the 231060484Sobrien * spec, so ignore it. Also, sanity check the size of the data 231160484Sobrien * areas to the type of memory involved. Memory must be at least 231260484Sobrien * 16 bytes in size, while I/O ranges must be at least 4. 231360484Sobrien */ 231460484Sobrien if (PCI_BAR_IO(testval) && (testval & PCIM_BAR_IO_RESERVED) != 0) 231560484Sobrien return (barlen); 231660484Sobrien if ((type == SYS_RES_MEMORY && ln2size < 4) || 231760484Sobrien (type == SYS_RES_IOPORT && ln2size < 2)) 231860484Sobrien return (barlen); 231960484Sobrien 232060484Sobrien if (ln2range == 64) 232160484Sobrien /* Read the other half of a 64bit map register */ 232289857Sobrien base |= (uint64_t) PCIB_READ_CONFIG(pcib, b, s, f, reg + 4, 4) << 32; 232389857Sobrien if (bootverbose) { 232460484Sobrien printf("\tmap[%02x]: type %s, range %2d, base %#jx, size %2d", 232560484Sobrien reg, pci_maptype(map), ln2range, (uintmax_t)base, ln2size); 232660484Sobrien if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) 232760484Sobrien printf(", port disabled\n"); 232860484Sobrien else if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) 232960484Sobrien printf(", memory disabled\n"); 233060484Sobrien else 233160484Sobrien printf(", enabled\n"); 233260484Sobrien } 233360484Sobrien 233460484Sobrien /* 233560484Sobrien * If base is 0, then we have problems. It is best to ignore 233660484Sobrien * such entries for the moment. These will be allocated later if 233760484Sobrien * the driver specifically requests them. However, some 233860484Sobrien * removable busses look better when all resources are allocated, 233960484Sobrien * so allow '0' to be overriden. 234060484Sobrien * 234160484Sobrien * Similarly treat maps whose values is the same as the test value 234260484Sobrien * read back. These maps have had all f's written to them by the 234360484Sobrien * BIOS in an attempt to disable the resources. 234460484Sobrien */ 234560484Sobrien if (!force && (base == 0 || map == testval)) 234660484Sobrien return (barlen); 234760484Sobrien if ((u_long)base != base) { 234860484Sobrien device_printf(bus, 234960484Sobrien "pci%d:%d:%d:%d bar %#x too many address bits", 235060484Sobrien pci_get_domain(dev), b, s, f, reg); 235160484Sobrien return (barlen); 235260484Sobrien } 235360484Sobrien 235460484Sobrien /* 235560484Sobrien * This code theoretically does the right thing, but has 235660484Sobrien * undesirable side effects in some cases where peripherals 235760484Sobrien * respond oddly to having these bits enabled. Let the user 235860484Sobrien * be able to turn them off (since pci_enable_io_modes is 1 by 235960484Sobrien * default). 236060484Sobrien */ 236160484Sobrien if (pci_enable_io_modes) { 236277298Sobrien /* Turn on resources that have been left off by a lazy BIOS */ 236377298Sobrien if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) { 236460484Sobrien cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2); 236560484Sobrien cmd |= PCIM_CMD_PORTEN; 236660484Sobrien PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, cmd, 2); 236760484Sobrien } 236860484Sobrien if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) { 236960484Sobrien cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2); 237060484Sobrien cmd |= PCIM_CMD_MEMEN; 237160484Sobrien PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, cmd, 2); 237260484Sobrien } 237360484Sobrien } else { 237460484Sobrien if (type == SYS_RES_IOPORT && !pci_porten(pcib, b, s, f)) 237560484Sobrien return (barlen); 237660484Sobrien if (type == SYS_RES_MEMORY && !pci_memen(pcib, b, s, f)) 237760484Sobrien return (barlen); 237860484Sobrien } 237960484Sobrien 238060484Sobrien count = 1 << ln2size; 238160484Sobrien if (base == 0 || base == pci_mapbase(testval)) { 238260484Sobrien start = 0; /* Let the parent decide. */ 238360484Sobrien end = ~0ULL; 238460484Sobrien } else { 238560484Sobrien start = base; 238660484Sobrien end = base + (1 << ln2size) - 1; 238760484Sobrien } 238860484Sobrien resource_list_add(rl, type, reg, start, end, count); 238960484Sobrien 239060484Sobrien /* 239160484Sobrien * Try to allocate the resource for this BAR from our parent 239260484Sobrien * so that this resource range is already reserved. The 239360484Sobrien * driver for this device will later inherit this resource in 239460484Sobrien * pci_alloc_resource(). 239560484Sobrien */ 239660484Sobrien res = resource_list_alloc(rl, bus, dev, type, ®, start, end, count, 239760484Sobrien prefetch ? RF_PREFETCHABLE : 0); 239860484Sobrien if (res == NULL) { 239960484Sobrien /* 240060484Sobrien * If the allocation fails, clear the BAR and delete 240160484Sobrien * the resource list entry to force 240260484Sobrien * pci_alloc_resource() to allocate resources from the 240360484Sobrien * parent. 240489857Sobrien */ 240589857Sobrien resource_list_delete(rl, type, reg); 240660484Sobrien start = 0; 240760484Sobrien } else 240860484Sobrien start = rman_get_start(res); 240960484Sobrien pci_write_config(dev, reg, start, 4); 241060484Sobrien if (ln2range == 64) 241160484Sobrien pci_write_config(dev, reg + 4, start >> 32, 4); 241260484Sobrien return (barlen); 241360484Sobrien} 241460484Sobrien 241560484Sobrien/* 241660484Sobrien * For ATA devices we need to decide early what addressing mode to use. 241760484Sobrien * Legacy demands that the primary and secondary ATA ports sits on the 241860484Sobrien * same addresses that old ISA hardware did. This dictates that we use 241960484Sobrien * those addresses and ignore the BAR's if we cannot set PCI native 242060484Sobrien * addressing mode. 242160484Sobrien */ 242260484Sobrienstatic void 242360484Sobrienpci_ata_maps(device_t pcib, device_t bus, device_t dev, int b, 242460484Sobrien int s, int f, struct resource_list *rl, int force, uint32_t prefetchmask) 242560484Sobrien{ 242660484Sobrien int rid, type, progif; 242760484Sobrien#if 0 242860484Sobrien /* if this device supports PCI native addressing use it */ 242960484Sobrien progif = pci_read_config(dev, PCIR_PROGIF, 1); 243060484Sobrien if ((progif & 0x8a) == 0x8a) { 243160484Sobrien if (pci_mapbase(pci_read_config(dev, PCIR_BAR(0), 4)) && 243260484Sobrien pci_mapbase(pci_read_config(dev, PCIR_BAR(2), 4))) { 243360484Sobrien printf("Trying ATA native PCI addressing mode\n"); 243460484Sobrien pci_write_config(dev, PCIR_PROGIF, progif | 0x05, 1); 243560484Sobrien } 243660484Sobrien } 243760484Sobrien#endif 243860484Sobrien progif = pci_read_config(dev, PCIR_PROGIF, 1); 243960484Sobrien type = SYS_RES_IOPORT; 244060484Sobrien if (progif & PCIP_STORAGE_IDE_MODEPRIM) { 244160484Sobrien pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(0), rl, force, 244260484Sobrien prefetchmask & (1 << 0)); 244360484Sobrien pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(1), rl, force, 244460484Sobrien prefetchmask & (1 << 1)); 244560484Sobrien } else { 244660484Sobrien rid = PCIR_BAR(0); 244760484Sobrien resource_list_add(rl, type, rid, 0x1f0, 0x1f7, 8); 244860484Sobrien resource_list_alloc(rl, bus, dev, type, &rid, 0x1f0, 0x1f7, 8, 244960484Sobrien 0); 245060484Sobrien rid = PCIR_BAR(1); 245160484Sobrien resource_list_add(rl, type, rid, 0x3f6, 0x3f6, 1); 245260484Sobrien resource_list_alloc(rl, bus, dev, type, &rid, 0x3f6, 0x3f6, 1, 245360484Sobrien 0); 245460484Sobrien } 245560484Sobrien if (progif & PCIP_STORAGE_IDE_MODESEC) { 245660484Sobrien pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(2), rl, force, 245760484Sobrien prefetchmask & (1 << 2)); 245860484Sobrien pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(3), rl, force, 245960484Sobrien prefetchmask & (1 << 3)); 246060484Sobrien } else { 246189857Sobrien rid = PCIR_BAR(2); 246260484Sobrien resource_list_add(rl, type, rid, 0x170, 0x177, 8); 246360484Sobrien resource_list_alloc(rl, bus, dev, type, &rid, 0x170, 0x177, 8, 246460484Sobrien 0); 246560484Sobrien rid = PCIR_BAR(3); 246660484Sobrien resource_list_add(rl, type, rid, 0x376, 0x376, 1); 246760484Sobrien resource_list_alloc(rl, bus, dev, type, &rid, 0x376, 0x376, 1, 246860484Sobrien 0); 246960484Sobrien } 247060484Sobrien pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(4), rl, force, 247160484Sobrien prefetchmask & (1 << 4)); 247260484Sobrien pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(5), rl, force, 247360484Sobrien prefetchmask & (1 << 5)); 247460484Sobrien} 247560484Sobrien 247660484Sobrienstatic void 247760484Sobrienpci_assign_interrupt(device_t bus, device_t dev, int force_route) 247860484Sobrien{ 247960484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 248060484Sobrien pcicfgregs *cfg = &dinfo->cfg; 248160484Sobrien char tunable_name[64]; 248260484Sobrien int irq; 248360484Sobrien 248460484Sobrien /* Has to have an intpin to have an interrupt. */ 248560484Sobrien if (cfg->intpin == 0) 248660484Sobrien return; 248760484Sobrien 248860484Sobrien /* Let the user override the IRQ with a tunable. */ 248960484Sobrien irq = PCI_INVALID_IRQ; 249060484Sobrien snprintf(tunable_name, sizeof(tunable_name), 249160484Sobrien "hw.pci%d.%d.%d.INT%c.irq", 249260484Sobrien cfg->domain, cfg->bus, cfg->slot, cfg->intpin + 'A' - 1); 249360484Sobrien if (TUNABLE_INT_FETCH(tunable_name, &irq) && (irq >= 255 || irq <= 0)) 249460484Sobrien irq = PCI_INVALID_IRQ; 249560484Sobrien 249660484Sobrien /* 249760484Sobrien * If we didn't get an IRQ via the tunable, then we either use the 249860484Sobrien * IRQ value in the intline register or we ask the bus to route an 249960484Sobrien * interrupt for us. If force_route is true, then we only use the 250060484Sobrien * value in the intline register if the bus was unable to assign an 250160484Sobrien * IRQ. 250277298Sobrien */ 250360484Sobrien if (!PCI_INTERRUPT_VALID(irq)) { 250460484Sobrien if (!PCI_INTERRUPT_VALID(cfg->intline) || force_route) 250560484Sobrien irq = PCI_ASSIGN_INTERRUPT(bus, dev); 250660484Sobrien if (!PCI_INTERRUPT_VALID(irq)) 250760484Sobrien irq = cfg->intline; 250860484Sobrien } 250960484Sobrien 251060484Sobrien /* If after all that we don't have an IRQ, just bail. */ 251160484Sobrien if (!PCI_INTERRUPT_VALID(irq)) 251260484Sobrien return; 251360484Sobrien 251460484Sobrien /* Update the config register if it changed. */ 251560484Sobrien if (irq != cfg->intline) { 251660484Sobrien cfg->intline = irq; 251760484Sobrien pci_write_config(dev, PCIR_INTLINE, irq, 1); 251860484Sobrien } 251960484Sobrien 252060484Sobrien /* Add this IRQ as rid 0 interrupt resource. */ 252160484Sobrien resource_list_add(&dinfo->resources, SYS_RES_IRQ, 0, irq, irq, 1); 252260484Sobrien} 252360484Sobrien 252460484Sobrienvoid 252560484Sobrienpci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask) 252660484Sobrien{ 252760484Sobrien device_t pcib; 252860484Sobrien struct pci_devinfo *dinfo = device_get_ivars(dev); 252960484Sobrien pcicfgregs *cfg = &dinfo->cfg; 253060484Sobrien struct resource_list *rl = &dinfo->resources; 253160484Sobrien struct pci_quirk *q; 253260484Sobrien int b, i, f, s; 253360484Sobrien 253460484Sobrien pcib = device_get_parent(bus); 253560484Sobrien 253660484Sobrien b = cfg->bus; 253760484Sobrien s = cfg->slot; 253860484Sobrien f = cfg->func; 253960484Sobrien 254060484Sobrien /* ATA devices needs special map treatment */ 254160484Sobrien if ((pci_get_class(dev) == PCIC_STORAGE) && 254260484Sobrien (pci_get_subclass(dev) == PCIS_STORAGE_IDE) && 254360484Sobrien ((pci_get_progif(dev) & PCIP_STORAGE_IDE_MASTERDEV) || 254460484Sobrien (!pci_read_config(dev, PCIR_BAR(0), 4) && 254589857Sobrien !pci_read_config(dev, PCIR_BAR(2), 4))) ) 254660484Sobrien pci_ata_maps(pcib, bus, dev, b, s, f, rl, force, prefetchmask); 254760484Sobrien else 254860484Sobrien for (i = 0; i < cfg->nummaps;) 254960484Sobrien i += pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(i), 255060484Sobrien rl, force, prefetchmask & (1 << i)); 255160484Sobrien 255260484Sobrien /* 255360484Sobrien * Add additional, quirked resources. 255489857Sobrien */ 255589857Sobrien for (q = &pci_quirks[0]; q->devid; q++) { 255660484Sobrien if (q->devid == ((cfg->device << 16) | cfg->vendor) 255760484Sobrien && q->type == PCI_QUIRK_MAP_REG) 255860484Sobrien pci_add_map(pcib, bus, dev, b, s, f, q->arg1, rl, 255960484Sobrien force, 0); 256089857Sobrien } 256189857Sobrien 256260484Sobrien if (cfg->intpin > 0 && PCI_INTERRUPT_VALID(cfg->intline)) { 256360484Sobrien#ifdef __PCI_REROUTE_INTERRUPT 256460484Sobrien /* 256560484Sobrien * Try to re-route interrupts. Sometimes the BIOS or 256660484Sobrien * firmware may leave bogus values in these registers. 256789857Sobrien * If the re-route fails, then just stick with what we 256889857Sobrien * have. 256989857Sobrien */ 257060484Sobrien pci_assign_interrupt(bus, dev, 1); 257189857Sobrien#else 257260484Sobrien pci_assign_interrupt(bus, dev, 0); 257360484Sobrien#endif 257460484Sobrien } 257560484Sobrien} 257660484Sobrien 257760484Sobrienvoid 257860484Sobrienpci_add_children(device_t dev, int domain, int busno, size_t dinfo_size) 257960484Sobrien{ 258060484Sobrien#define REG(n, w) PCIB_READ_CONFIG(pcib, busno, s, f, n, w) 258160484Sobrien device_t pcib = device_get_parent(dev); 258260484Sobrien struct pci_devinfo *dinfo; 258360484Sobrien int maxslots; 258460484Sobrien int s, f, pcifunchigh; 258560484Sobrien uint8_t hdrtype; 258660484Sobrien 258760484Sobrien KASSERT(dinfo_size >= sizeof(struct pci_devinfo), 258860484Sobrien ("dinfo_size too small")); 258960484Sobrien maxslots = PCIB_MAXSLOTS(pcib); 259060484Sobrien for (s = 0; s <= maxslots; s++) { 259160484Sobrien pcifunchigh = 0; 259260484Sobrien f = 0; 259360484Sobrien DELAY(1); 259460484Sobrien hdrtype = REG(PCIR_HDRTYPE, 1); 259560484Sobrien if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 259660484Sobrien continue; 259760484Sobrien if (hdrtype & PCIM_MFDEV) 259860484Sobrien pcifunchigh = PCI_FUNCMAX; 259960484Sobrien for (f = 0; f <= pcifunchigh; f++) { 260060484Sobrien dinfo = pci_read_device(pcib, domain, busno, s, f, 260160484Sobrien dinfo_size); 260260484Sobrien if (dinfo != NULL) { 260360484Sobrien pci_add_child(dev, dinfo); 260460484Sobrien } 260560484Sobrien } 260660484Sobrien } 260760484Sobrien#undef REG 260860484Sobrien} 260960484Sobrien 261060484Sobrienvoid 261160484Sobrienpci_add_child(device_t bus, struct pci_devinfo *dinfo) 261260484Sobrien{ 261360484Sobrien dinfo->cfg.dev = device_add_child(bus, NULL, -1); 261460484Sobrien device_set_ivars(dinfo->cfg.dev, dinfo); 261560484Sobrien resource_list_init(&dinfo->resources); 261660484Sobrien pci_cfg_save(dinfo->cfg.dev, dinfo, 0); 261760484Sobrien pci_cfg_restore(dinfo->cfg.dev, dinfo); 261860484Sobrien pci_print_verbose(dinfo); 261960484Sobrien pci_add_resources(bus, dinfo->cfg.dev, 0, 0); 262060484Sobrien} 262160484Sobrien 262260484Sobrienstatic int 262360484Sobrienpci_probe(device_t dev) 262460484Sobrien{ 262560484Sobrien 262660484Sobrien device_set_desc(dev, "PCI bus"); 262760484Sobrien 262860484Sobrien /* Allow other subclasses to override this driver. */ 262960484Sobrien return (-1000); 263060484Sobrien} 263160484Sobrien 263260484Sobrienstatic int 263360484Sobrienpci_attach(device_t dev) 263460484Sobrien{ 263560484Sobrien int busno, domain; 263660484Sobrien 263760484Sobrien /* 263860484Sobrien * Since there can be multiple independantly numbered PCI 263960484Sobrien * busses on systems with multiple PCI domains, we can't use 264060484Sobrien * the unit number to decide which bus we are probing. We ask 264160484Sobrien * the parent pcib what our domain and bus numbers are. 264260484Sobrien */ 264360484Sobrien domain = pcib_get_domain(dev); 264460484Sobrien busno = pcib_get_bus(dev); 264560484Sobrien if (bootverbose) 264660484Sobrien device_printf(dev, "domain=%d, physical bus=%d\n", 264760484Sobrien domain, busno); 264860484Sobrien pci_add_children(dev, domain, busno, sizeof(struct pci_devinfo)); 264960484Sobrien return (bus_generic_attach(dev)); 265068765Sobrien} 265168765Sobrien 265268765Sobrienint 265368765Sobrienpci_suspend(device_t dev) 265477298Sobrien{ 265568765Sobrien int dstate, error, i, numdevs; 265668765Sobrien device_t acpi_dev, child, *devlist; 265760484Sobrien struct pci_devinfo *dinfo; 265860484Sobrien 265960484Sobrien /* 266060484Sobrien * Save the PCI configuration space for each child and set the 266160484Sobrien * device in the appropriate power state for this sleep state. 266260484Sobrien */ 266360484Sobrien acpi_dev = NULL; 266460484Sobrien if (pci_do_power_resume) 266560484Sobrien acpi_dev = devclass_get_device(devclass_find("acpi"), 0); 266660484Sobrien if ((error = device_get_children(dev, &devlist, &numdevs)) != 0) 266760484Sobrien return (error); 266860484Sobrien for (i = 0; i < numdevs; i++) { 266960484Sobrien child = devlist[i]; 267060484Sobrien dinfo = (struct pci_devinfo *) device_get_ivars(child); 267160484Sobrien pci_cfg_save(child, dinfo, 0); 267260484Sobrien } 267360484Sobrien 267460484Sobrien /* Suspend devices before potentially powering them down. */ 267589857Sobrien error = bus_generic_suspend(dev); 267660484Sobrien if (error) { 267760484Sobrien free(devlist, M_TEMP); 267860484Sobrien return (error); 267960484Sobrien } 268060484Sobrien 268160484Sobrien /* 268277298Sobrien * Always set the device to D3. If ACPI suggests a different 268360484Sobrien * power state, use it instead. If ACPI is not present, the 268460484Sobrien * firmware is responsible for managing device power. Skip 268560484Sobrien * children who aren't attached since they are powered down 268660484Sobrien * separately. Only manage type 0 devices for now. 268760484Sobrien */ 268860484Sobrien for (i = 0; acpi_dev && i < numdevs; i++) { 268960484Sobrien child = devlist[i]; 269060484Sobrien dinfo = (struct pci_devinfo *) device_get_ivars(child); 269177298Sobrien if (device_is_attached(child) && dinfo->cfg.hdrtype == 0) { 269260484Sobrien dstate = PCI_POWERSTATE_D3; 269360484Sobrien ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate); 269460484Sobrien pci_set_powerstate(child, dstate); 269560484Sobrien } 269660484Sobrien } 269760484Sobrien free(devlist, M_TEMP); 269860484Sobrien return (0); 269960484Sobrien} 270060484Sobrien 270160484Sobrienint 270260484Sobrienpci_resume(device_t dev) 270360484Sobrien{ 270460484Sobrien int i, numdevs, error; 270560484Sobrien device_t acpi_dev, child, *devlist; 270660484Sobrien struct pci_devinfo *dinfo; 270760484Sobrien 270860484Sobrien /* 270960484Sobrien * Set each child to D0 and restore its PCI configuration space. 271060484Sobrien */ 271160484Sobrien acpi_dev = NULL; 271260484Sobrien if (pci_do_power_resume) 271360484Sobrien acpi_dev = devclass_get_device(devclass_find("acpi"), 0); 271460484Sobrien if ((error = device_get_children(dev, &devlist, &numdevs)) != 0) 271560484Sobrien return (error); 271660484Sobrien for (i = 0; i < numdevs; i++) { 271760484Sobrien /* 271860484Sobrien * Notify ACPI we're going to D0 but ignore the result. If 271960484Sobrien * ACPI is not present, the firmware is responsible for 272060484Sobrien * managing device power. Only manage type 0 devices for now. 272160484Sobrien */ 272260484Sobrien child = devlist[i]; 272360484Sobrien dinfo = (struct pci_devinfo *) device_get_ivars(child); 272460484Sobrien if (acpi_dev && device_is_attached(child) && 272560484Sobrien dinfo->cfg.hdrtype == 0) { 272660484Sobrien ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL); 272760484Sobrien pci_set_powerstate(child, PCI_POWERSTATE_D0); 272860484Sobrien } 272960484Sobrien 273060484Sobrien /* Now the device is powered up, restore its config space. */ 273160484Sobrien pci_cfg_restore(child, dinfo); 273260484Sobrien } 273360484Sobrien free(devlist, M_TEMP); 273460484Sobrien return (bus_generic_resume(dev)); 273560484Sobrien} 273660484Sobrien 273760484Sobrienstatic void 273860484Sobrienpci_load_vendor_data(void) 273960484Sobrien{ 274060484Sobrien caddr_t vendordata, info; 274160484Sobrien 274260484Sobrien if ((vendordata = preload_search_by_type("pci_vendor_data")) != NULL) { 274360484Sobrien info = preload_search_info(vendordata, MODINFO_ADDR); 274460484Sobrien pci_vendordata = *(char **)info; 274560484Sobrien info = preload_search_info(vendordata, MODINFO_SIZE); 274660484Sobrien pci_vendordata_size = *(size_t *)info; 274760484Sobrien /* terminate the database */ 274860484Sobrien pci_vendordata[pci_vendordata_size] = '\n'; 274960484Sobrien } 275060484Sobrien} 275160484Sobrien 275260484Sobrienvoid 275360484Sobrienpci_driver_added(device_t dev, driver_t *driver) 275460484Sobrien{ 275560484Sobrien int numdevs; 275660484Sobrien device_t *devlist; 275760484Sobrien device_t child; 275860484Sobrien struct pci_devinfo *dinfo; 275960484Sobrien int i; 276060484Sobrien 276160484Sobrien if (bootverbose) 276260484Sobrien device_printf(dev, "driver added\n"); 276360484Sobrien DEVICE_IDENTIFY(driver, dev); 276460484Sobrien if (device_get_children(dev, &devlist, &numdevs) != 0) 276560484Sobrien return; 276660484Sobrien for (i = 0; i < numdevs; i++) { 276760484Sobrien child = devlist[i]; 276860484Sobrien if (device_get_state(child) != DS_NOTPRESENT) 276960484Sobrien continue; 277060484Sobrien dinfo = device_get_ivars(child); 277160484Sobrien pci_print_verbose(dinfo); 277260484Sobrien if (bootverbose) 277360484Sobrien printf("pci%d:%d:%d:%d: reprobing on driver added\n", 277460484Sobrien dinfo->cfg.domain, dinfo->cfg.bus, dinfo->cfg.slot, 277560484Sobrien dinfo->cfg.func); 277660484Sobrien pci_cfg_restore(child, dinfo); 277760484Sobrien if (device_probe_and_attach(child) != 0) 277860484Sobrien pci_cfg_save(child, dinfo, 1); 277960484Sobrien } 278060484Sobrien free(devlist, M_TEMP); 278160484Sobrien} 278260484Sobrien 278360484Sobrienint 278460484Sobrienpci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, 278560484Sobrien driver_filter_t *filter, driver_intr_t *intr, void *arg, void **cookiep) 278660484Sobrien{ 278760484Sobrien struct pci_devinfo *dinfo; 278860484Sobrien struct msix_table_entry *mte; 278960484Sobrien struct msix_vector *mv; 279060484Sobrien uint64_t addr; 279160484Sobrien uint32_t data; 279260484Sobrien void *cookie; 279360484Sobrien int error, rid; 279460484Sobrien 279560484Sobrien error = bus_generic_setup_intr(dev, child, irq, flags, filter, intr, 279660484Sobrien arg, &cookie); 279760484Sobrien if (error) 279860484Sobrien return (error); 279960484Sobrien 280060484Sobrien /* 280160484Sobrien * If this is a direct child, check to see if the interrupt is 280260484Sobrien * MSI or MSI-X. If so, ask our parent to map the MSI and give 280360484Sobrien * us the address and data register values. If we fail for some 280460484Sobrien * reason, teardown the interrupt handler. 280560484Sobrien */ 280660484Sobrien rid = rman_get_rid(irq); 280760484Sobrien if (device_get_parent(child) == dev && rid > 0) { 280860484Sobrien dinfo = device_get_ivars(child); 280960484Sobrien if (dinfo->cfg.msi.msi_alloc > 0) { 281060484Sobrien if (dinfo->cfg.msi.msi_addr == 0) { 281160484Sobrien KASSERT(dinfo->cfg.msi.msi_handlers == 0, 281260484Sobrien ("MSI has handlers, but vectors not mapped")); 281360484Sobrien error = PCIB_MAP_MSI(device_get_parent(dev), 281460484Sobrien child, rman_get_start(irq), &addr, &data); 281560484Sobrien if (error) 281660484Sobrien goto bad; 281760484Sobrien dinfo->cfg.msi.msi_addr = addr; 281860484Sobrien dinfo->cfg.msi.msi_data = data; 281960484Sobrien pci_enable_msi(child, addr, data); 282089857Sobrien } 282160484Sobrien dinfo->cfg.msi.msi_handlers++; 282260484Sobrien } else { 282360484Sobrien KASSERT(dinfo->cfg.msix.msix_alloc > 0, 282460484Sobrien ("No MSI or MSI-X interrupts allocated")); 282560484Sobrien KASSERT(rid <= dinfo->cfg.msix.msix_table_len, 282660484Sobrien ("MSI-X index too high")); 282760484Sobrien mte = &dinfo->cfg.msix.msix_table[rid - 1]; 282860484Sobrien KASSERT(mte->mte_vector != 0, ("no message vector")); 282960484Sobrien mv = &dinfo->cfg.msix.msix_vectors[mte->mte_vector - 1]; 283060484Sobrien KASSERT(mv->mv_irq == rman_get_start(irq), 283160484Sobrien ("IRQ mismatch")); 283260484Sobrien if (mv->mv_address == 0) { 283360484Sobrien KASSERT(mte->mte_handlers == 0, 283460484Sobrien ("MSI-X table entry has handlers, but vector not mapped")); 283560484Sobrien error = PCIB_MAP_MSI(device_get_parent(dev), 283660484Sobrien child, rman_get_start(irq), &addr, &data); 283760484Sobrien if (error) 283860484Sobrien goto bad; 283960484Sobrien mv->mv_address = addr; 284060484Sobrien mv->mv_data = data; 284160484Sobrien } 284260484Sobrien if (mte->mte_handlers == 0) { 284360484Sobrien pci_enable_msix(child, rid - 1, mv->mv_address, 284460484Sobrien mv->mv_data); 284560484Sobrien pci_unmask_msix(child, rid - 1); 284660484Sobrien } 284760484Sobrien mte->mte_handlers++; 284860484Sobrien } 284960484Sobrien bad: 285060484Sobrien if (error) { 285160484Sobrien (void)bus_generic_teardown_intr(dev, child, irq, 285260484Sobrien cookie); 285360484Sobrien return (error); 285460484Sobrien } 285560484Sobrien } 285660484Sobrien *cookiep = cookie; 285760484Sobrien return (0); 285860484Sobrien} 285960484Sobrien 286060484Sobrienint 286160484Sobrienpci_teardown_intr(device_t dev, device_t child, struct resource *irq, 286260484Sobrien void *cookie) 286360484Sobrien{ 286460484Sobrien struct msix_table_entry *mte; 286560484Sobrien struct resource_list_entry *rle; 286660484Sobrien struct pci_devinfo *dinfo; 286760484Sobrien int error, rid; 286860484Sobrien 286960484Sobrien /* 287060484Sobrien * If this is a direct child, check to see if the interrupt is 287160484Sobrien * MSI or MSI-X. If so, decrement the appropriate handlers 287260484Sobrien * count and mask the MSI-X message, or disable MSI messages 287360484Sobrien * if the count drops to 0. 287460484Sobrien */ 287560484Sobrien if (irq == NULL || !(rman_get_flags(irq) & RF_ACTIVE)) 287660484Sobrien return (EINVAL); 287760484Sobrien rid = rman_get_rid(irq); 287860484Sobrien if (device_get_parent(child) == dev && rid > 0) { 287960484Sobrien dinfo = device_get_ivars(child); 288060484Sobrien rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, rid); 288160484Sobrien if (rle->res != irq) 288260484Sobrien return (EINVAL); 288360484Sobrien if (dinfo->cfg.msi.msi_alloc > 0) { 288460484Sobrien KASSERT(rid <= dinfo->cfg.msi.msi_alloc, 288560484Sobrien ("MSI-X index too high")); 288660484Sobrien if (dinfo->cfg.msi.msi_handlers == 0) 288760484Sobrien return (EINVAL); 288860484Sobrien dinfo->cfg.msi.msi_handlers--; 288960484Sobrien if (dinfo->cfg.msi.msi_handlers == 0) 289060484Sobrien pci_disable_msi(child); 289160484Sobrien } else { 289289857Sobrien KASSERT(dinfo->cfg.msix.msix_alloc > 0, 289360484Sobrien ("No MSI or MSI-X interrupts allocated")); 289477298Sobrien KASSERT(rid <= dinfo->cfg.msix.msix_table_len, 289560484Sobrien ("MSI-X index too high")); 289660484Sobrien mte = &dinfo->cfg.msix.msix_table[rid - 1]; 289760484Sobrien if (mte->mte_handlers == 0) 289877298Sobrien return (EINVAL); 289977298Sobrien mte->mte_handlers--; 290060484Sobrien if (mte->mte_handlers == 0) 290160484Sobrien pci_mask_msix(child, rid - 1); 290260484Sobrien } 290360484Sobrien } 290460484Sobrien error = bus_generic_teardown_intr(dev, child, irq, cookie); 290560484Sobrien if (device_get_parent(child) == dev && rid > 0) 290660484Sobrien KASSERT(error == 0, 290760484Sobrien ("%s: generic teardown failed for MSI/MSI-X", __func__)); 290860484Sobrien return (error); 290960484Sobrien} 291060484Sobrien 291160484Sobrienint 291260484Sobrienpci_print_child(device_t dev, device_t child) 291360484Sobrien{ 291460484Sobrien struct pci_devinfo *dinfo; 291560484Sobrien struct resource_list *rl; 291660484Sobrien int retval = 0; 291777298Sobrien 291877298Sobrien dinfo = device_get_ivars(child); 291977298Sobrien rl = &dinfo->resources; 292077298Sobrien 292160484Sobrien retval += bus_print_child_header(dev, child); 292260484Sobrien 292360484Sobrien retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); 292477298Sobrien retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 292560484Sobrien retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 292660484Sobrien if (device_get_flags(dev)) 292777298Sobrien retval += printf(" flags %#x", device_get_flags(dev)); 292877298Sobrien 292960484Sobrien retval += printf(" at device %d.%d", pci_get_slot(child), 293060484Sobrien pci_get_function(child)); 293189857Sobrien 293277298Sobrien retval += bus_print_child_footer(dev, child); 293360484Sobrien 293460484Sobrien return (retval); 293560484Sobrien} 293660484Sobrien 293760484Sobrienstatic struct 293860484Sobrien{ 293977298Sobrien int class; 294060484Sobrien int subclass; 294160484Sobrien char *desc; 294260484Sobrien} pci_nomatch_tab[] = { 294360484Sobrien {PCIC_OLD, -1, "old"}, 294460484Sobrien {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"}, 294560484Sobrien {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"}, 294660484Sobrien {PCIC_STORAGE, -1, "mass storage"}, 294760484Sobrien {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"}, 294860484Sobrien {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"}, 294960484Sobrien {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"}, 295060484Sobrien {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"}, 295177298Sobrien {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"}, 295260484Sobrien {PCIC_NETWORK, -1, "network"}, 295360484Sobrien {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, 295460484Sobrien {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, 295560484Sobrien {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"}, 295660484Sobrien {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"}, 295760484Sobrien {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"}, 295860484Sobrien {PCIC_DISPLAY, -1, "display"}, 295960484Sobrien {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"}, 296060484Sobrien {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"}, 296177298Sobrien {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"}, 296260484Sobrien {PCIC_MULTIMEDIA, -1, "multimedia"}, 296377298Sobrien {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"}, 296477298Sobrien {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"}, 296560484Sobrien {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"}, 296660484Sobrien {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"}, 296760484Sobrien {PCIC_MEMORY, -1, "memory"}, 296860484Sobrien {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"}, 296960484Sobrien {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"}, 297060484Sobrien {PCIC_BRIDGE, -1, "bridge"}, 297160484Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"}, 297260484Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"}, 297360484Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"}, 297460484Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"}, 297560484Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"}, 297689857Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"}, 297789857Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"}, 297877298Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"}, 297977298Sobrien {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"}, 298060484Sobrien {PCIC_SIMPLECOMM, -1, "simple comms"}, 298160484Sobrien {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */ 298260484Sobrien {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"}, 298360484Sobrien {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"}, 298460484Sobrien {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"}, 298560484Sobrien {PCIC_BASEPERIPH, -1, "base peripheral"}, 298660484Sobrien {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"}, 298760484Sobrien {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"}, 298877298Sobrien {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"}, 298977298Sobrien {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"}, 299077298Sobrien {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"}, 299177298Sobrien {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"}, 299260484Sobrien {PCIC_INPUTDEV, -1, "input device"}, 299360484Sobrien {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"}, 299460484Sobrien {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"}, 299560484Sobrien {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"}, 299677298Sobrien {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"}, 299777298Sobrien {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"}, 299877298Sobrien {PCIC_DOCKING, -1, "docking station"}, 299960484Sobrien {PCIC_PROCESSOR, -1, "processor"}, 300060484Sobrien {PCIC_SERIALBUS, -1, "serial bus"}, 300160484Sobrien {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"}, 300260484Sobrien {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"}, 300360484Sobrien {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"}, 300460484Sobrien {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"}, 300560484Sobrien {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"}, 300660484Sobrien {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"}, 300777298Sobrien {PCIC_WIRELESS, -1, "wireless controller"}, 300860484Sobrien {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"}, 300977298Sobrien {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"}, 301060484Sobrien {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"}, 301160484Sobrien {PCIC_INTELLIIO, -1, "intelligent I/O controller"}, 301260484Sobrien {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"}, 301360484Sobrien {PCIC_SATCOM, -1, "satellite communication"}, 301460484Sobrien {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"}, 301577298Sobrien {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"}, 301677298Sobrien {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"}, 301777298Sobrien {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"}, 301860484Sobrien {PCIC_CRYPTO, -1, "encrypt/decrypt"}, 301960484Sobrien {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"}, 302060484Sobrien {PCIC_CRYPTO, PCIS_CRYPTO_ENTERTAIN, "entertainment crypto"}, 302160484Sobrien {PCIC_DASP, -1, "dasp"}, 302260484Sobrien {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"}, 302360484Sobrien {0, 0, NULL} 302460484Sobrien}; 302560484Sobrien 302660484Sobrienvoid 302760484Sobrienpci_probe_nomatch(device_t dev, device_t child) 302860484Sobrien{ 302960484Sobrien int i; 303060484Sobrien char *cp, *scp, *device; 303160484Sobrien 303260484Sobrien /* 303360484Sobrien * Look for a listing for this device in a loaded device database. 303460484Sobrien */ 303560484Sobrien if ((device = pci_describe_device(child)) != NULL) { 303660484Sobrien device_printf(dev, "<%s>", device); 303760484Sobrien free(device, M_DEVBUF); 303860484Sobrien } else { 303960484Sobrien /* 304060484Sobrien * Scan the class/subclass descriptions for a general 304160484Sobrien * description. 304260484Sobrien */ 304360484Sobrien cp = "unknown"; 304460484Sobrien scp = NULL; 304560484Sobrien for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { 304660484Sobrien if (pci_nomatch_tab[i].class == pci_get_class(child)) { 304760484Sobrien if (pci_nomatch_tab[i].subclass == -1) { 304860484Sobrien cp = pci_nomatch_tab[i].desc; 304960484Sobrien } else if (pci_nomatch_tab[i].subclass == 305060484Sobrien pci_get_subclass(child)) { 305160484Sobrien scp = pci_nomatch_tab[i].desc; 305260484Sobrien } 305360484Sobrien } 305460484Sobrien } 305560484Sobrien device_printf(dev, "<%s%s%s>", 305660484Sobrien cp ? cp : "", 305760484Sobrien ((cp != NULL) && (scp != NULL)) ? ", " : "", 305860484Sobrien scp ? scp : ""); 305960484Sobrien } 306077298Sobrien printf(" at device %d.%d (no driver attached)\n", 306160484Sobrien pci_get_slot(child), pci_get_function(child)); 306260484Sobrien pci_cfg_save(child, (struct pci_devinfo *)device_get_ivars(child), 1); 306389857Sobrien return; 306460484Sobrien} 306560484Sobrien 306677298Sobrien/* 306760484Sobrien * Parse the PCI device database, if loaded, and return a pointer to a 306860484Sobrien * description of the device. 306960484Sobrien * 307060484Sobrien * The database is flat text formatted as follows: 307160484Sobrien * 307260484Sobrien * Any line not in a valid format is ignored. 307360484Sobrien * Lines are terminated with newline '\n' characters. 307489857Sobrien * 307589857Sobrien * A VENDOR line consists of the 4 digit (hex) vendor code, a TAB, then 307689857Sobrien * the vendor name. 307768765Sobrien * 307860484Sobrien * A DEVICE line is entered immediately below the corresponding VENDOR ID. 307960484Sobrien * - devices cannot be listed without a corresponding VENDOR line. 308060484Sobrien * A DEVICE line consists of a TAB, the 4 digit (hex) device code, 308177298Sobrien * another TAB, then the device name. 308260484Sobrien */ 308360484Sobrien 308460484Sobrien/* 308560484Sobrien * Assuming (ptr) points to the beginning of a line in the database, 308660484Sobrien * return the vendor or device and description of the next entry. 308768765Sobrien * The value of (vendor) or (device) inappropriate for the entry type 308868765Sobrien * is set to -1. Returns nonzero at the end of the database. 308960484Sobrien * 309060484Sobrien * Note that this is slightly unrobust in the face of corrupt data; 309160484Sobrien * we attempt to safeguard against this by spamming the end of the 309260484Sobrien * database with a newline when we initialise. 309360484Sobrien */ 309477298Sobrienstatic int 309560484Sobrienpci_describe_parse_line(char **ptr, int *vendor, int *device, char **desc) 309660484Sobrien{ 309760484Sobrien char *cp = *ptr; 309889857Sobrien int left; 309977298Sobrien 310060484Sobrien *device = -1; 310160484Sobrien *vendor = -1; 310260484Sobrien **desc = '\0'; 310360484Sobrien for (;;) { 310460484Sobrien left = pci_vendordata_size - (cp - pci_vendordata); 310589857Sobrien if (left <= 0) { 310689857Sobrien *ptr = cp; 310789857Sobrien return(1); 310860484Sobrien } 310977298Sobrien 311060484Sobrien /* vendor entry? */ 311160484Sobrien if (*cp != '\t' && 311260484Sobrien sscanf(cp, "%x\t%80[^\n]", vendor, *desc) == 2) 311360484Sobrien break; 311460484Sobrien /* device entry? */ 311560484Sobrien if (*cp == '\t' && 311660484Sobrien sscanf(cp, "%x\t%80[^\n]", device, *desc) == 2) 311760484Sobrien break; 311860484Sobrien 311960484Sobrien /* skip to next line */ 312060484Sobrien while (*cp != '\n' && left > 0) { 312160484Sobrien cp++; 312260484Sobrien left--; 312360484Sobrien } 312460484Sobrien if (*cp == '\n') { 312560484Sobrien cp++; 312660484Sobrien left--; 312760484Sobrien } 312860484Sobrien } 312960484Sobrien /* skip to next line */ 313077298Sobrien while (*cp != '\n' && left > 0) { 313177298Sobrien cp++; 313277298Sobrien left--; 313360484Sobrien } 313460484Sobrien if (*cp == '\n' && left > 0) 313560484Sobrien cp++; 313677298Sobrien *ptr = cp; 313777298Sobrien return(0); 313860484Sobrien} 313960484Sobrien 314060484Sobrienstatic char * 314160484Sobrienpci_describe_device(device_t dev) 314260484Sobrien{ 314377298Sobrien int vendor, device; 314477298Sobrien char *desc, *vp, *dp, *line; 314577298Sobrien 314677298Sobrien desc = vp = dp = NULL; 314777298Sobrien 314877298Sobrien /* 314977298Sobrien * If we have no vendor data, we can't do anything. 315077298Sobrien */ 315177298Sobrien if (pci_vendordata == NULL) 315289857Sobrien goto out; 315360484Sobrien 315460484Sobrien /* 315591041Sobrien * Scan the vendor data looking for this device 315660484Sobrien */ 315760484Sobrien line = pci_vendordata; 315860484Sobrien if ((vp = malloc(80, M_DEVBUF, M_NOWAIT)) == NULL) 315960484Sobrien goto out; 316060484Sobrien for (;;) { 316160484Sobrien if (pci_describe_parse_line(&line, &vendor, &device, &vp)) 316260484Sobrien goto out; 316360484Sobrien if (vendor == pci_get_vendor(dev)) 316460484Sobrien break; 316560484Sobrien } 316660484Sobrien if ((dp = malloc(80, M_DEVBUF, M_NOWAIT)) == NULL) 316760484Sobrien goto out; 316860484Sobrien for (;;) { 316960484Sobrien if (pci_describe_parse_line(&line, &vendor, &device, &dp)) { 317060484Sobrien *dp = 0; 317160484Sobrien break; 317260484Sobrien } 317360484Sobrien if (vendor != -1) { 317460484Sobrien *dp = 0; 317560484Sobrien break; 317660484Sobrien } 317760484Sobrien if (device == pci_get_device(dev)) 317860484Sobrien break; 317960484Sobrien } 318060484Sobrien if (dp[0] == '\0') 318160484Sobrien snprintf(dp, 80, "0x%x", pci_get_device(dev)); 318260484Sobrien if ((desc = malloc(strlen(vp) + strlen(dp) + 3, M_DEVBUF, M_NOWAIT)) != 318360484Sobrien NULL) 318460484Sobrien sprintf(desc, "%s, %s", vp, dp); 318560484Sobrien out: 318691041Sobrien if (vp != NULL) 318760484Sobrien free(vp, M_DEVBUF); 318889857Sobrien if (dp != NULL) 318989857Sobrien free(dp, M_DEVBUF); 319089857Sobrien return(desc); 319191041Sobrien} 319291041Sobrien 319391041Sobrienint 319460484Sobrienpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 319560484Sobrien{ 319660484Sobrien struct pci_devinfo *dinfo; 319760484Sobrien pcicfgregs *cfg; 319860484Sobrien 319960484Sobrien dinfo = device_get_ivars(child); 320060484Sobrien cfg = &dinfo->cfg; 320177298Sobrien 320260484Sobrien switch (which) { 320360484Sobrien case PCI_IVAR_ETHADDR: 320460484Sobrien /* 320560484Sobrien * The generic accessor doesn't deal with failure, so 320660484Sobrien * we set the return value, then return an error. 320760484Sobrien */ 320860484Sobrien *((uint8_t **) result) = NULL; 320960484Sobrien return (EINVAL); 321060484Sobrien case PCI_IVAR_SUBVENDOR: 321160484Sobrien *result = cfg->subvendor; 321260484Sobrien break; 321360484Sobrien case PCI_IVAR_SUBDEVICE: 321460484Sobrien *result = cfg->subdevice; 321560484Sobrien break; 321660484Sobrien case PCI_IVAR_VENDOR: 321760484Sobrien *result = cfg->vendor; 321860484Sobrien break; 321960484Sobrien case PCI_IVAR_DEVICE: 322060484Sobrien *result = cfg->device; 322160484Sobrien break; 322260484Sobrien case PCI_IVAR_DEVID: 322360484Sobrien *result = (cfg->device << 16) | cfg->vendor; 322460484Sobrien break; 322560484Sobrien case PCI_IVAR_CLASS: 322660484Sobrien *result = cfg->baseclass; 322760484Sobrien break; 322860484Sobrien case PCI_IVAR_SUBCLASS: 322960484Sobrien *result = cfg->subclass; 323060484Sobrien break; 323160484Sobrien case PCI_IVAR_PROGIF: 323260484Sobrien *result = cfg->progif; 323360484Sobrien break; 323460484Sobrien case PCI_IVAR_REVID: 323560484Sobrien *result = cfg->revid; 323660484Sobrien break; 323760484Sobrien case PCI_IVAR_INTPIN: 323860484Sobrien *result = cfg->intpin; 323977298Sobrien break; 324060484Sobrien case PCI_IVAR_IRQ: 324160484Sobrien *result = cfg->intline; 324260484Sobrien break; 324377298Sobrien case PCI_IVAR_DOMAIN: 324460484Sobrien *result = cfg->domain; 324560484Sobrien break; 324660484Sobrien case PCI_IVAR_BUS: 324760484Sobrien *result = cfg->bus; 324860484Sobrien break; 324960484Sobrien case PCI_IVAR_SLOT: 325060484Sobrien *result = cfg->slot; 325160484Sobrien break; 325260484Sobrien case PCI_IVAR_FUNCTION: 325360484Sobrien *result = cfg->func; 325460484Sobrien break; 325560484Sobrien case PCI_IVAR_CMDREG: 325660484Sobrien *result = cfg->cmdreg; 325760484Sobrien break; 325860484Sobrien case PCI_IVAR_CACHELNSZ: 325960484Sobrien *result = cfg->cachelnsz; 326060484Sobrien break; 326160484Sobrien case PCI_IVAR_MINGNT: 326260484Sobrien *result = cfg->mingnt; 326360484Sobrien break; 326491041Sobrien case PCI_IVAR_MAXLAT: 326560484Sobrien *result = cfg->maxlat; 326660484Sobrien break; 326760484Sobrien case PCI_IVAR_LATTIMER: 326860484Sobrien *result = cfg->lattimer; 326960484Sobrien break; 327060484Sobrien default: 327160484Sobrien return (ENOENT); 327260484Sobrien } 327360484Sobrien return (0); 327460484Sobrien} 327560484Sobrien 327660484Sobrienint 327760484Sobrienpci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 327860484Sobrien{ 327960484Sobrien struct pci_devinfo *dinfo; 328060484Sobrien 328160484Sobrien dinfo = device_get_ivars(child); 328260484Sobrien 328360484Sobrien switch (which) { 328477298Sobrien case PCI_IVAR_INTPIN: 328577298Sobrien dinfo->cfg.intpin = value; 328660484Sobrien return (0); 328760484Sobrien case PCI_IVAR_ETHADDR: 328860484Sobrien case PCI_IVAR_SUBVENDOR: 328960484Sobrien case PCI_IVAR_SUBDEVICE: 329060484Sobrien case PCI_IVAR_VENDOR: 329189857Sobrien case PCI_IVAR_DEVICE: 329260484Sobrien case PCI_IVAR_DEVID: 329360484Sobrien case PCI_IVAR_CLASS: 329460484Sobrien case PCI_IVAR_SUBCLASS: 329577298Sobrien case PCI_IVAR_PROGIF: 329677298Sobrien case PCI_IVAR_REVID: 329760484Sobrien case PCI_IVAR_IRQ: 329860484Sobrien case PCI_IVAR_DOMAIN: 329960484Sobrien case PCI_IVAR_BUS: 330060484Sobrien case PCI_IVAR_SLOT: 330160484Sobrien case PCI_IVAR_FUNCTION: 330289857Sobrien return (EINVAL); /* disallow for now */ 330360484Sobrien 330460484Sobrien default: 330560484Sobrien return (ENOENT); 330677298Sobrien } 330777298Sobrien} 330877298Sobrien 330977298Sobrien 331060484Sobrien#include "opt_ddb.h" 331160484Sobrien#ifdef DDB 331260484Sobrien#include <ddb/ddb.h> 331360484Sobrien#include <sys/cons.h> 331460484Sobrien 331560484Sobrien/* 331660484Sobrien * List resources based on pci map registers, used for within ddb 331760484Sobrien */ 331860484Sobrien 331960484SobrienDB_SHOW_COMMAND(pciregs, db_pci_dump) 332060484Sobrien{ 332160484Sobrien struct pci_devinfo *dinfo; 332260484Sobrien struct devlist *devlist_head; 332377298Sobrien struct pci_conf *p; 332460484Sobrien const char *name; 332560484Sobrien int i, error, none_count; 332660484Sobrien 332760484Sobrien none_count = 0; 332860484Sobrien /* get the head of the device queue */ 332960484Sobrien devlist_head = &pci_devq; 333060484Sobrien 333160484Sobrien /* 333260484Sobrien * Go through the list of devices and print out devices 333360484Sobrien */ 333460484Sobrien for (error = 0, i = 0, 333560484Sobrien dinfo = STAILQ_FIRST(devlist_head); 333660484Sobrien (dinfo != NULL) && (error == 0) && (i < pci_numdevs) && !db_pager_quit; 333760484Sobrien dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 333860484Sobrien 333960484Sobrien /* Populate pd_name and pd_unit */ 334060484Sobrien name = NULL; 334160484Sobrien if (dinfo->cfg.dev) 334260484Sobrien name = device_get_name(dinfo->cfg.dev); 334360484Sobrien 334460484Sobrien p = &dinfo->conf; 334560484Sobrien db_printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x " 334660484Sobrien "chip=0x%08x rev=0x%02x hdr=0x%02x\n", 334760484Sobrien (name && *name) ? name : "none", 334860484Sobrien (name && *name) ? (int)device_get_unit(dinfo->cfg.dev) : 334960484Sobrien none_count++, 335060484Sobrien p->pc_sel.pc_domain, p->pc_sel.pc_bus, p->pc_sel.pc_dev, 335160484Sobrien p->pc_sel.pc_func, (p->pc_class << 16) | 335260484Sobrien (p->pc_subclass << 8) | p->pc_progif, 335360484Sobrien (p->pc_subdevice << 16) | p->pc_subvendor, 335460484Sobrien (p->pc_device << 16) | p->pc_vendor, 335560484Sobrien p->pc_revid, p->pc_hdr); 335660484Sobrien } 335760484Sobrien} 335860484Sobrien#endif /* DDB */ 335960484Sobrien 336060484Sobrienstatic struct resource * 336160484Sobrienpci_alloc_map(device_t dev, device_t child, int type, int *rid, 336260484Sobrien u_long start, u_long end, u_long count, u_int flags) 336360484Sobrien{ 336460484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 336560484Sobrien struct resource_list *rl = &dinfo->resources; 336660484Sobrien struct resource_list_entry *rle; 336760484Sobrien struct resource *res; 336860484Sobrien pci_addr_t map, testval; 336960484Sobrien int mapsize; 337060484Sobrien 337160484Sobrien /* 337260484Sobrien * Weed out the bogons, and figure out how large the BAR/map 337360484Sobrien * is. Bars that read back 0 here are bogus and unimplemented. 337460484Sobrien * Note: atapci in legacy mode are special and handled elsewhere 337560484Sobrien * in the code. If you have a atapci device in legacy mode and 337660484Sobrien * it fails here, that other code is broken. 337760484Sobrien */ 337860484Sobrien res = NULL; 337960484Sobrien map = pci_read_config(child, *rid, 4); 338060484Sobrien pci_write_config(child, *rid, 0xffffffff, 4); 338160484Sobrien testval = pci_read_config(child, *rid, 4); 338260484Sobrien if (pci_maprange(testval) == 64) 338360484Sobrien map |= (pci_addr_t)pci_read_config(child, *rid + 4, 4) << 32; 338460484Sobrien if (pci_mapbase(testval) == 0) 338560484Sobrien goto out; 338660484Sobrien 338760484Sobrien /* 338860484Sobrien * Restore the original value of the BAR. We may have reprogrammed 338960484Sobrien * the BAR of the low-level console device and when booting verbose, 339060484Sobrien * we need the console device addressable. 339160484Sobrien */ 339260484Sobrien pci_write_config(child, *rid, map, 4); 339360484Sobrien 339460484Sobrien if (PCI_BAR_MEM(testval)) { 339560484Sobrien if (type != SYS_RES_MEMORY) { 339677298Sobrien if (bootverbose) 339760484Sobrien device_printf(dev, 339860484Sobrien "child %s requested type %d for rid %#x," 339960484Sobrien " but the BAR says it is an memio\n", 340060484Sobrien device_get_nameunit(child), type, *rid); 340160484Sobrien goto out; 340260484Sobrien } 340360484Sobrien } else { 340477298Sobrien if (type != SYS_RES_IOPORT) { 340560484Sobrien if (bootverbose) 340660484Sobrien device_printf(dev, 340760484Sobrien "child %s requested type %d for rid %#x," 340860484Sobrien " but the BAR says it is an ioport\n", 340960484Sobrien device_get_nameunit(child), type, *rid); 341060484Sobrien goto out; 341160484Sobrien } 341260484Sobrien } 341360484Sobrien /* 341460484Sobrien * For real BARs, we need to override the size that 341577298Sobrien * the driver requests, because that's what the BAR 341677298Sobrien * actually uses and we would otherwise have a 341760484Sobrien * situation where we might allocate the excess to 341860484Sobrien * another driver, which won't work. 341960484Sobrien */ 342060484Sobrien mapsize = pci_mapsize(testval); 342160484Sobrien count = 1UL << mapsize; 342260484Sobrien if (RF_ALIGNMENT(flags) < mapsize) 342360484Sobrien flags = (flags & ~RF_ALIGNMENT_MASK) | RF_ALIGNMENT_LOG2(mapsize); 342477298Sobrien 342560484Sobrien /* 342660484Sobrien * Allocate enough resource, and then write back the 342760484Sobrien * appropriate bar for that resource. 342860484Sobrien */ 342960484Sobrien res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, 343060484Sobrien start, end, count, flags); 343160484Sobrien if (res == NULL) { 343260484Sobrien device_printf(child, 343360484Sobrien "%#lx bytes of rid %#x res %d failed (%#lx, %#lx).\n", 343460484Sobrien count, *rid, type, start, end); 343560484Sobrien goto out; 343660484Sobrien } 343760484Sobrien resource_list_add(rl, type, *rid, start, end, count); 343860484Sobrien rle = resource_list_find(rl, type, *rid); 343960484Sobrien if (rle == NULL) 344060484Sobrien panic("pci_alloc_map: unexpectedly can't find resource."); 344177298Sobrien rle->res = res; 344260484Sobrien rle->start = rman_get_start(res); 344360484Sobrien rle->end = rman_get_end(res); 344477298Sobrien rle->count = count; 344560484Sobrien if (bootverbose) 344660484Sobrien device_printf(child, 344760484Sobrien "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n", 344877298Sobrien count, *rid, type, rman_get_start(res)); 344960484Sobrien map = rman_get_start(res); 345060484Sobrienout:; 345160484Sobrien pci_write_config(child, *rid, map, 4); 345260484Sobrien if (pci_maprange(testval) == 64) 345360484Sobrien pci_write_config(child, *rid + 4, map >> 32, 4); 345489857Sobrien return (res); 345560484Sobrien} 345677298Sobrien 345760484Sobrien 345860484Sobrienstruct resource * 345960484Sobrienpci_alloc_resource(device_t dev, device_t child, int type, int *rid, 346060484Sobrien u_long start, u_long end, u_long count, u_int flags) 346160484Sobrien{ 346260484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 346360484Sobrien struct resource_list *rl = &dinfo->resources; 346460484Sobrien struct resource_list_entry *rle; 346560484Sobrien pcicfgregs *cfg = &dinfo->cfg; 346677298Sobrien 346760484Sobrien /* 346860484Sobrien * Perform lazy resource allocation 346960484Sobrien */ 347077298Sobrien if (device_get_parent(child) == dev) { 347160484Sobrien switch (type) { 347260484Sobrien case SYS_RES_IRQ: 347360484Sobrien /* 347460484Sobrien * Can't alloc legacy interrupt once MSI messages 347589857Sobrien * have been allocated. 347660484Sobrien */ 347777298Sobrien if (*rid == 0 && (cfg->msi.msi_alloc > 0 || 347860484Sobrien cfg->msix.msix_alloc > 0)) 347977298Sobrien return (NULL); 348060484Sobrien /* 348160484Sobrien * If the child device doesn't have an 348260484Sobrien * interrupt routed and is deserving of an 348360484Sobrien * interrupt, try to assign it one. 348460484Sobrien */ 348560484Sobrien if (*rid == 0 && !PCI_INTERRUPT_VALID(cfg->intline) && 348660484Sobrien (cfg->intpin != 0)) 348760484Sobrien pci_assign_interrupt(dev, child, 0); 348860484Sobrien break; 348960484Sobrien case SYS_RES_IOPORT: 349060484Sobrien case SYS_RES_MEMORY: 349177298Sobrien if (*rid < PCIR_BAR(cfg->nummaps)) { 349277298Sobrien /* 349360484Sobrien * Enable the I/O mode. We should 349460484Sobrien * also be assigning resources too 349560484Sobrien * when none are present. The 349660484Sobrien * resource_list_alloc kind of sorta does 349777298Sobrien * this... 349860484Sobrien */ 349960484Sobrien if (PCI_ENABLE_IO(dev, child, type)) 350060484Sobrien return (NULL); 350160484Sobrien } 350260484Sobrien rle = resource_list_find(rl, type, *rid); 350360484Sobrien if (rle == NULL) 350460484Sobrien return (pci_alloc_map(dev, child, type, rid, 350560484Sobrien start, end, count, flags)); 350660484Sobrien break; 350777298Sobrien } 350877298Sobrien /* 350960484Sobrien * If we've already allocated the resource, then 351060484Sobrien * return it now. But first we may need to activate 351160484Sobrien * it, since we don't allocate the resource as active 351260484Sobrien * above. Normally this would be done down in the 351360484Sobrien * nexus, but since we short-circuit that path we have 351460484Sobrien * to do its job here. Not sure if we should free the 351560484Sobrien * resource if it fails to activate. 351677298Sobrien */ 351777298Sobrien rle = resource_list_find(rl, type, *rid); 351860484Sobrien if (rle != NULL && rle->res != NULL) { 351960484Sobrien if (bootverbose) 352060484Sobrien device_printf(child, 352160484Sobrien "Reserved %#lx bytes for rid %#x type %d at %#lx\n", 352260484Sobrien rman_get_size(rle->res), *rid, type, 352360484Sobrien rman_get_start(rle->res)); 352460484Sobrien if ((flags & RF_ACTIVE) && 352589857Sobrien bus_generic_activate_resource(dev, child, type, 352660484Sobrien *rid, rle->res) != 0) 352777298Sobrien return (NULL); 352860484Sobrien return (rle->res); 352960484Sobrien } 353060484Sobrien } 353160484Sobrien return (resource_list_alloc(rl, dev, child, type, rid, 353260484Sobrien start, end, count, flags)); 353360484Sobrien} 353460484Sobrien 353560484Sobrienvoid 353660484Sobrienpci_delete_resource(device_t dev, device_t child, int type, int rid) 353760484Sobrien{ 353860484Sobrien struct pci_devinfo *dinfo; 353989857Sobrien struct resource_list *rl; 354060484Sobrien struct resource_list_entry *rle; 354160484Sobrien 354260484Sobrien if (device_get_parent(child) != dev) 354360484Sobrien return; 354460484Sobrien 354577298Sobrien dinfo = device_get_ivars(child); 354677298Sobrien rl = &dinfo->resources; 354777298Sobrien rle = resource_list_find(rl, type, rid); 354877298Sobrien if (rle) { 354960484Sobrien if (rle->res) { 355060484Sobrien if (rman_get_device(rle->res) != dev || 355160484Sobrien rman_get_flags(rle->res) & RF_ACTIVE) { 355277298Sobrien device_printf(dev, "delete_resource: " 355377298Sobrien "Resource still owned by child, oops. " 355460484Sobrien "(type=%d, rid=%d, addr=%lx)\n", 355560484Sobrien rle->type, rle->rid, 355660484Sobrien rman_get_start(rle->res)); 355760484Sobrien return; 355860484Sobrien } 355977298Sobrien bus_release_resource(dev, type, rid, rle->res); 356077298Sobrien } 356177298Sobrien resource_list_delete(rl, type, rid); 356277298Sobrien } 356377298Sobrien /* 356460484Sobrien * Why do we turn off the PCI configuration BAR when we delete a 356560484Sobrien * resource? -- imp 356677298Sobrien */ 356777298Sobrien pci_write_config(child, rid, 0, 4); 356860484Sobrien BUS_DELETE_RESOURCE(device_get_parent(dev), child, type, rid); 356960484Sobrien} 357060484Sobrien 357160484Sobrienstruct resource_list * 357260484Sobrienpci_get_resource_list (device_t dev, device_t child) 357377298Sobrien{ 357460484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 357560484Sobrien 357677298Sobrien return (&dinfo->resources); 357777298Sobrien} 357877298Sobrien 357977298Sobrienuint32_t 358077298Sobrienpci_read_config_method(device_t dev, device_t child, int reg, int width) 358177298Sobrien{ 358277298Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 358377298Sobrien pcicfgregs *cfg = &dinfo->cfg; 358477298Sobrien 358577298Sobrien return (PCIB_READ_CONFIG(device_get_parent(dev), 358677298Sobrien cfg->bus, cfg->slot, cfg->func, reg, width)); 358777298Sobrien} 358877298Sobrien 358977298Sobrienvoid 359060484Sobrienpci_write_config_method(device_t dev, device_t child, int reg, 359189857Sobrien uint32_t val, int width) 359277298Sobrien{ 359360484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 359460484Sobrien pcicfgregs *cfg = &dinfo->cfg; 359560484Sobrien 359660484Sobrien PCIB_WRITE_CONFIG(device_get_parent(dev), 359760484Sobrien cfg->bus, cfg->slot, cfg->func, reg, val, width); 359860484Sobrien} 359977298Sobrien 360077298Sobrienint 360160484Sobrienpci_child_location_str_method(device_t dev, device_t child, char *buf, 360260484Sobrien size_t buflen) 360360484Sobrien{ 360460484Sobrien 360560484Sobrien snprintf(buf, buflen, "slot=%d function=%d", pci_get_slot(child), 360660484Sobrien pci_get_function(child)); 360760484Sobrien return (0); 360877298Sobrien} 360960484Sobrien 361060484Sobrienint 361177298Sobrienpci_child_pnpinfo_str_method(device_t dev, device_t child, char *buf, 361277298Sobrien size_t buflen) 361360484Sobrien{ 361460484Sobrien struct pci_devinfo *dinfo; 361560484Sobrien pcicfgregs *cfg; 361660484Sobrien 361760484Sobrien dinfo = device_get_ivars(child); 361860484Sobrien cfg = &dinfo->cfg; 361960484Sobrien snprintf(buf, buflen, "vendor=0x%04x device=0x%04x subvendor=0x%04x " 362060484Sobrien "subdevice=0x%04x class=0x%02x%02x%02x", cfg->vendor, cfg->device, 362160484Sobrien cfg->subvendor, cfg->subdevice, cfg->baseclass, cfg->subclass, 362260484Sobrien cfg->progif); 362360484Sobrien return (0); 362460484Sobrien} 362560484Sobrien 362660484Sobrienint 362760484Sobrienpci_assign_interrupt_method(device_t dev, device_t child) 362860484Sobrien{ 362960484Sobrien struct pci_devinfo *dinfo = device_get_ivars(child); 363060484Sobrien pcicfgregs *cfg = &dinfo->cfg; 363160484Sobrien 363260484Sobrien return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, 363360484Sobrien cfg->intpin)); 363460484Sobrien} 363560484Sobrien 363660484Sobrienstatic int 363760484Sobrienpci_modevent(module_t mod, int what, void *arg) 363860484Sobrien{ 363960484Sobrien static struct cdev *pci_cdev; 364060484Sobrien 364160484Sobrien switch (what) { 364260484Sobrien case MOD_LOAD: 364360484Sobrien STAILQ_INIT(&pci_devq); 364460484Sobrien pci_generation = 0; 364560484Sobrien pci_cdev = make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644, 364660484Sobrien "pci"); 364760484Sobrien pci_load_vendor_data(); 364860484Sobrien break; 364960484Sobrien 365060484Sobrien case MOD_UNLOAD: 365160484Sobrien destroy_dev(pci_cdev); 365260484Sobrien break; 365360484Sobrien } 365460484Sobrien 365560484Sobrien return (0); 365677298Sobrien} 365760484Sobrien 365860484Sobrienvoid 365960484Sobrienpci_cfg_restore(device_t dev, struct pci_devinfo *dinfo) 366060484Sobrien{ 366160484Sobrien int i; 366260484Sobrien 366360484Sobrien /* 366460484Sobrien * Only do header type 0 devices. Type 1 devices are bridges, 366560484Sobrien * which we know need special treatment. Type 2 devices are 366660484Sobrien * cardbus bridges which also require special treatment. 366760484Sobrien * Other types are unknown, and we err on the side of safety 366860484Sobrien * by ignoring them. 366960484Sobrien */ 367060484Sobrien if (dinfo->cfg.hdrtype != 0) 367160484Sobrien return; 367260484Sobrien 367360484Sobrien /* 367460484Sobrien * Restore the device to full power mode. We must do this 367589857Sobrien * before we restore the registers because moving from D3 to 367689857Sobrien * D0 will cause the chip's BARs and some other registers to 367789857Sobrien * be reset to some unknown power on reset values. Cut down 367889857Sobrien * the noise on boot by doing nothing if we are already in 367989857Sobrien * state D0. 368089857Sobrien */ 368189857Sobrien if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 368289857Sobrien pci_set_powerstate(dev, PCI_POWERSTATE_D0); 368389857Sobrien } 368489857Sobrien for (i = 0; i < dinfo->cfg.nummaps; i++) 368589857Sobrien pci_write_config(dev, PCIR_BAR(i), dinfo->cfg.bar[i], 4); 368689857Sobrien pci_write_config(dev, PCIR_BIOS, dinfo->cfg.bios, 4); 368789857Sobrien pci_write_config(dev, PCIR_COMMAND, dinfo->cfg.cmdreg, 2); 368889857Sobrien pci_write_config(dev, PCIR_INTLINE, dinfo->cfg.intline, 1); 368989857Sobrien pci_write_config(dev, PCIR_INTPIN, dinfo->cfg.intpin, 1); 369089857Sobrien pci_write_config(dev, PCIR_MINGNT, dinfo->cfg.mingnt, 1); 369189857Sobrien pci_write_config(dev, PCIR_MAXLAT, dinfo->cfg.maxlat, 1); 369289857Sobrien pci_write_config(dev, PCIR_CACHELNSZ, dinfo->cfg.cachelnsz, 1); 369389857Sobrien pci_write_config(dev, PCIR_LATTIMER, dinfo->cfg.lattimer, 1); 369460484Sobrien pci_write_config(dev, PCIR_PROGIF, dinfo->cfg.progif, 1); 369589857Sobrien pci_write_config(dev, PCIR_REVID, dinfo->cfg.revid, 1); 369689857Sobrien 369789857Sobrien /* Restore MSI and MSI-X configurations if they are present. */ 369889857Sobrien if (dinfo->cfg.msi.msi_location != 0) 369989857Sobrien pci_resume_msi(dev); 370089857Sobrien if (dinfo->cfg.msix.msix_location != 0) 370189857Sobrien pci_resume_msix(dev); 370289857Sobrien} 370389857Sobrien 370489857Sobrienvoid 370589857Sobrienpci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate) 370689857Sobrien{ 370789857Sobrien int i; 370889857Sobrien uint32_t cls; 370989857Sobrien int ps; 371089857Sobrien 371189857Sobrien /* 371289857Sobrien * Only do header type 0 devices. Type 1 devices are bridges, which 371389857Sobrien * we know need special treatment. Type 2 devices are cardbus bridges 371489857Sobrien * which also require special treatment. Other types are unknown, and 371589857Sobrien * we err on the side of safety by ignoring them. Powering down 371689857Sobrien * bridges should not be undertaken lightly. 371789857Sobrien */ 371889857Sobrien if (dinfo->cfg.hdrtype != 0) 371989857Sobrien return; 372089857Sobrien for (i = 0; i < dinfo->cfg.nummaps; i++) 372189857Sobrien dinfo->cfg.bar[i] = pci_read_config(dev, PCIR_BAR(i), 4); 372289857Sobrien dinfo->cfg.bios = pci_read_config(dev, PCIR_BIOS, 4); 372389857Sobrien 372489857Sobrien /* 372589857Sobrien * Some drivers apparently write to these registers w/o updating our 372689857Sobrien * cached copy. No harm happens if we update the copy, so do so here 372789857Sobrien * so we can restore them. The COMMAND register is modified by the 372889857Sobrien * bus w/o updating the cache. This should represent the normally 372989857Sobrien * writable portion of the 'defined' part of type 0 headers. In 373089857Sobrien * theory we also need to save/restore the PCI capability structures 373189857Sobrien * we know about, but apart from power we don't know any that are 373289857Sobrien * writable. 373389857Sobrien */ 373489857Sobrien dinfo->cfg.subvendor = pci_read_config(dev, PCIR_SUBVEND_0, 2); 373589857Sobrien dinfo->cfg.subdevice = pci_read_config(dev, PCIR_SUBDEV_0, 2); 373689857Sobrien dinfo->cfg.vendor = pci_read_config(dev, PCIR_VENDOR, 2); 373789857Sobrien dinfo->cfg.device = pci_read_config(dev, PCIR_DEVICE, 2); 373889857Sobrien dinfo->cfg.cmdreg = pci_read_config(dev, PCIR_COMMAND, 2); 373989857Sobrien dinfo->cfg.intline = pci_read_config(dev, PCIR_INTLINE, 1); 374089857Sobrien dinfo->cfg.intpin = pci_read_config(dev, PCIR_INTPIN, 1); 374189857Sobrien dinfo->cfg.mingnt = pci_read_config(dev, PCIR_MINGNT, 1); 374289857Sobrien dinfo->cfg.maxlat = pci_read_config(dev, PCIR_MAXLAT, 1); 374389857Sobrien dinfo->cfg.cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); 374489857Sobrien dinfo->cfg.lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); 374589857Sobrien dinfo->cfg.baseclass = pci_read_config(dev, PCIR_CLASS, 1); 374689857Sobrien dinfo->cfg.subclass = pci_read_config(dev, PCIR_SUBCLASS, 1); 374789857Sobrien dinfo->cfg.progif = pci_read_config(dev, PCIR_PROGIF, 1); 374889857Sobrien dinfo->cfg.revid = pci_read_config(dev, PCIR_REVID, 1); 374989857Sobrien 375089857Sobrien /* 375189857Sobrien * don't set the state for display devices, base peripherals and 375289857Sobrien * memory devices since bad things happen when they are powered down. 375389857Sobrien * We should (a) have drivers that can easily detach and (b) use 375489857Sobrien * generic drivers for these devices so that some device actually 375589857Sobrien * attaches. We need to make sure that when we implement (a) we don't 375689857Sobrien * power the device down on a reattach. 375789857Sobrien */ 375889857Sobrien cls = pci_get_class(dev); 375989857Sobrien if (!setstate) 376060484Sobrien return; 376160484Sobrien switch (pci_do_power_nodriver) 376260484Sobrien { 376360484Sobrien case 0: /* NO powerdown at all */ 376460484Sobrien return; 376560484Sobrien case 1: /* Conservative about what to power down */ 376660484Sobrien if (cls == PCIC_STORAGE) 376760484Sobrien return; 376860484Sobrien /*FALLTHROUGH*/ 376960484Sobrien case 2: /* Agressive about what to power down */ 377060484Sobrien if (cls == PCIC_DISPLAY || cls == PCIC_MEMORY || 377160484Sobrien cls == PCIC_BASEPERIPH) 377260484Sobrien return; 377360484Sobrien /*FALLTHROUGH*/ 377460484Sobrien case 3: /* Power down everything */ 377560484Sobrien break; 377660484Sobrien } 377760484Sobrien /* 377860484Sobrien * PCI spec says we can only go into D3 state from D0 state. 377960484Sobrien * Transition from D[12] into D0 before going to D3 state. 378089857Sobrien */ 378160484Sobrien ps = pci_get_powerstate(dev); 378260484Sobrien if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3) 378360484Sobrien pci_set_powerstate(dev, PCI_POWERSTATE_D0); 378460484Sobrien if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3) 378560484Sobrien pci_set_powerstate(dev, PCI_POWERSTATE_D3); 378660484Sobrien} 378760484Sobrien