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, &reg))
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, &reg, 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