pci.c revision 191069
1244769Sglebius/*-
2126258Smlaier * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3223637Sbz * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4244769Sglebius * Copyright (c) 2000, BSDi
5126258Smlaier * All rights reserved.
6126258Smlaier *
7126258Smlaier * Redistribution and use in source and binary forms, with or without
8126258Smlaier * modification, are permitted provided that the following conditions
9126258Smlaier * are met:
10126258Smlaier * 1. Redistributions of source code must retain the above copyright
11126258Smlaier *    notice unmodified, this list of conditions, and the following
12126258Smlaier *    disclaimer.
13126258Smlaier * 2. Redistributions in binary form must reproduce the above copyright
14126258Smlaier *    notice, this list of conditions and the following disclaimer in the
15126258Smlaier *    documentation and/or other materials provided with the distribution.
16126258Smlaier *
17126258Smlaier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18126258Smlaier * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19126258Smlaier * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20126258Smlaier * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21126258Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22126258Smlaier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23126258Smlaier * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24126258Smlaier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25126258Smlaier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26126258Smlaier * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27126258Smlaier */
28126258Smlaier
29126258Smlaier#include <sys/cdefs.h>
30126258Smlaier__FBSDID("$FreeBSD: head/sys/dev/pci/pci.c 191069 2009-04-14 18:32:37Z jhb $");
31126258Smlaier
32126258Smlaier#include "opt_bus.h"
33126258Smlaier
34126258Smlaier#include <sys/param.h>
35244769Sglebius#include <sys/systm.h>
36126258Smlaier#include <sys/malloc.h>
37126258Smlaier#include <sys/module.h>
38240233Sglebius#include <sys/linker.h>
39171168Smlaier#include <sys/fcntl.h>
40126261Smlaier#include <sys/conf.h>
41240233Sglebius#include <sys/kernel.h>
42240233Sglebius#include <sys/queue.h>
43126261Smlaier#include <sys/sysctl.h>
44126261Smlaier#include <sys/endian.h>
45153110Sru
46126258Smlaier#include <vm/vm.h>
47240233Sglebius#include <vm/pmap.h>
48240233Sglebius#include <vm/vm_extern.h>
49240233Sglebius
50240233Sglebius#include <sys/bus.h>
51240233Sglebius#include <machine/bus.h>
52240233Sglebius#include <sys/rman.h>
53240233Sglebius#include <machine/resource.h>
54126258Smlaier
55240233Sglebius#if defined(__i386__) || defined(__amd64__)
56240233Sglebius#include <machine/intr_machdep.h>
57240233Sglebius#endif
58126258Smlaier
59126261Smlaier#include <sys/pciio.h>
60240233Sglebius#include <dev/pci/pcireg.h>
61240233Sglebius#include <dev/pci/pcivar.h>
62126258Smlaier#include <dev/pci/pci_private.h>
63126258Smlaier
64126258Smlaier#include "pcib_if.h"
65126258Smlaier#include "pci_if.h"
66171168Smlaier
67240233Sglebius#ifdef __HAVE_ACPI
68126258Smlaier#include <contrib/dev/acpica/acpi.h>
69240233Sglebius#include "acpi_if.h"
70240233Sglebius#else
71240233Sglebius#define	ACPI_PWR_FOR_SLEEP(x, y, z)
72240233Sglebius#endif
73240233Sglebius
74126258Smlaierstatic pci_addr_t	pci_mapbase(uint64_t mapreg);
75126258Smlaierstatic const char	*pci_maptype(uint64_t mapreg);
76240233Sglebiusstatic int		pci_mapsize(uint64_t testval);
77240233Sglebiusstatic int		pci_maprange(uint64_t mapreg);
78240233Sglebiusstatic void		pci_fixancient(pcicfgregs *cfg);
79126258Smlaier
80126258Smlaierstatic int		pci_porten(device_t dev);
81240233Sglebiusstatic int		pci_memen(device_t dev);
82126258Smlaierstatic void		pci_assign_interrupt(device_t bus, device_t dev,
83126258Smlaier			    int force_route);
84126258Smlaierstatic int		pci_add_map(device_t bus, device_t dev, int reg,
85240233Sglebius			    struct resource_list *rl, int force, int prefetch);
86126258Smlaierstatic int		pci_probe(device_t dev);
87126258Smlaierstatic int		pci_attach(device_t dev);
88240494Sglebiusstatic void		pci_load_vendor_data(void);
89240494Sglebiusstatic int		pci_describe_parse_line(char **ptr, int *vendor,
90126258Smlaier			    int *device, char **desc);
91126258Smlaierstatic char		*pci_describe_device(device_t dev);
92126258Smlaierstatic int		pci_modevent(module_t mod, int what, void *arg);
93126258Smlaierstatic void		pci_hdrtypedata(device_t pcib, int b, int s, int f,
94126261Smlaier			    pcicfgregs *cfg);
95126261Smlaierstatic void		pci_read_extcap(device_t pcib, pcicfgregs *cfg);
96126258Smlaierstatic int		pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg,
97126258Smlaier			    int reg, uint32_t *data);
98126261Smlaier#if 0
99163606Srwatsonstatic int		pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg,
100126258Smlaier			    int reg, uint32_t data);
101223637Sbz#endif
102126258Smlaierstatic void		pci_read_vpd(device_t pcib, pcicfgregs *cfg);
103126258Smlaierstatic void		pci_disable_msi(device_t dev);
104126258Smlaierstatic void		pci_enable_msi(device_t dev, uint64_t address,
105126258Smlaier			    uint16_t data);
106126258Smlaierstatic void		pci_enable_msix(device_t dev, u_int index,
107223637Sbz			    uint64_t address, uint32_t data);
108223637Sbzstatic void		pci_mask_msix(device_t dev, u_int index);
109223637Sbzstatic void		pci_unmask_msix(device_t dev, u_int index);
110223637Sbzstatic int		pci_msi_blacklisted(void);
111223637Sbzstatic void		pci_resume_msi(device_t dev);
112270574Sglebiusstatic void		pci_resume_msix(device_t dev);
113223637Sbz
114223637Sbzstatic device_method_t pci_methods[] = {
115223637Sbz	/* Device interface */
116223637Sbz	DEVMETHOD(device_probe,		pci_probe),
117223637Sbz	DEVMETHOD(device_attach,	pci_attach),
118223637Sbz	DEVMETHOD(device_detach,	bus_generic_detach),
119223637Sbz	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
120223637Sbz	DEVMETHOD(device_suspend,	pci_suspend),
121223637Sbz	DEVMETHOD(device_resume,	pci_resume),
122223637Sbz
123223637Sbz	/* Bus interface */
124223637Sbz	DEVMETHOD(bus_print_child,	pci_print_child),
125223637Sbz	DEVMETHOD(bus_probe_nomatch,	pci_probe_nomatch),
126223637Sbz	DEVMETHOD(bus_read_ivar,	pci_read_ivar),
127223637Sbz	DEVMETHOD(bus_write_ivar,	pci_write_ivar),
128240233Sglebius	DEVMETHOD(bus_driver_added,	pci_driver_added),
129240233Sglebius	DEVMETHOD(bus_setup_intr,	pci_setup_intr),
130240233Sglebius	DEVMETHOD(bus_teardown_intr,	pci_teardown_intr),
131240233Sglebius
132240233Sglebius	DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
133240233Sglebius	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
134240233Sglebius	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
135240233Sglebius	DEVMETHOD(bus_delete_resource,	pci_delete_resource),
136240233Sglebius	DEVMETHOD(bus_alloc_resource,	pci_alloc_resource),
137240233Sglebius	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
138240233Sglebius	DEVMETHOD(bus_activate_resource, pci_activate_resource),
139240233Sglebius	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
140240233Sglebius	DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
141240233Sglebius	DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
142240233Sglebius
143240233Sglebius	/* PCI interface */
144240233Sglebius	DEVMETHOD(pci_read_config,	pci_read_config_method),
145240233Sglebius	DEVMETHOD(pci_write_config,	pci_write_config_method),
146240233Sglebius	DEVMETHOD(pci_enable_busmaster,	pci_enable_busmaster_method),
147240233Sglebius	DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
148240233Sglebius	DEVMETHOD(pci_enable_io,	pci_enable_io_method),
149240233Sglebius	DEVMETHOD(pci_disable_io,	pci_disable_io_method),
150240233Sglebius	DEVMETHOD(pci_get_vpd_ident,	pci_get_vpd_ident_method),
151240233Sglebius	DEVMETHOD(pci_get_vpd_readonly,	pci_get_vpd_readonly_method),
152240233Sglebius	DEVMETHOD(pci_get_powerstate,	pci_get_powerstate_method),
153240233Sglebius	DEVMETHOD(pci_set_powerstate,	pci_set_powerstate_method),
154223637Sbz	DEVMETHOD(pci_assign_interrupt,	pci_assign_interrupt_method),
155240233Sglebius	DEVMETHOD(pci_find_extcap,	pci_find_extcap_method),
156240233Sglebius	DEVMETHOD(pci_alloc_msi,	pci_alloc_msi_method),
157240233Sglebius	DEVMETHOD(pci_alloc_msix,	pci_alloc_msix_method),
158126258Smlaier	DEVMETHOD(pci_remap_msix,	pci_remap_msix_method),
159240233Sglebius	DEVMETHOD(pci_release_msi,	pci_release_msi_method),
160240233Sglebius	DEVMETHOD(pci_msi_count,	pci_msi_count_method),
161240233Sglebius	DEVMETHOD(pci_msix_count,	pci_msix_count_method),
162126258Smlaier
163240233Sglebius	{ 0, 0 }
164240811Sglebius};
165240233Sglebius
166240811SglebiusDEFINE_CLASS_0(pci, pci_driver, pci_methods, 0);
167240811Sglebius
168240233Sglebiusstatic devclass_t pci_devclass;
169240233SglebiusDRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0);
170240233SglebiusMODULE_VERSION(pci, 1);
171240811Sglebius
172240233Sglebiusstatic char	*pci_vendordata;
173223637Sbzstatic size_t	pci_vendordata_size;
174240811Sglebius
175240811Sglebius
176240811Sglebiusstruct pci_quirk {
177240811Sglebius	uint32_t devid;	/* Vendor/device of the card */
178240811Sglebius	int	type;
179126261Smlaier#define	PCI_QUIRK_MAP_REG	1 /* PCI map register in weird place */
180240811Sglebius#define	PCI_QUIRK_DISABLE_MSI	2 /* MSI/MSI-X doesn't work */
181240811Sglebius	int	arg1;
182240811Sglebius	int	arg2;
183126258Smlaier};
184240233Sglebius
185240233Sglebiusstruct pci_quirk pci_quirks[] = {
186240233Sglebius	/* The Intel 82371AB and 82443MX has a map register at offset 0x90. */
187240233Sglebius	{ 0x71138086, PCI_QUIRK_MAP_REG,	0x90,	 0 },
188240233Sglebius	{ 0x719b8086, PCI_QUIRK_MAP_REG,	0x90,	 0 },
189265008Smm	/* As does the Serverworks OSB4 (the SMBus mapping register) */
190240233Sglebius	{ 0x02001166, PCI_QUIRK_MAP_REG,	0x90,	 0 },
191240233Sglebius
192240233Sglebius	/*
193240233Sglebius	 * MSI doesn't work with the ServerWorks CNB20-HE Host Bridge
194240233Sglebius	 * or the CMIC-SL (AKA ServerWorks GC_LE).
195240233Sglebius	 */
196240233Sglebius	{ 0x00141166, PCI_QUIRK_DISABLE_MSI,	0,	0 },
197240233Sglebius	{ 0x00171166, PCI_QUIRK_DISABLE_MSI,	0,	0 },
198283303Sjhb
199240233Sglebius	/*
200240233Sglebius	 * MSI doesn't work on earlier Intel chipsets including
201240233Sglebius	 * E7500, E7501, E7505, 845, 865, 875/E7210, and 855.
202145836Smlaier	 */
203240233Sglebius	{ 0x25408086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
204240233Sglebius	{ 0x254c8086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
205145836Smlaier	{ 0x25508086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
206289703Skp	{ 0x25608086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
207126258Smlaier	{ 0x25708086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
208126258Smlaier	{ 0x25788086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
209240233Sglebius	{ 0x35808086, PCI_QUIRK_DISABLE_MSI,	0,	0 },
210171168Smlaier
211240233Sglebius	/*
212126258Smlaier	 * MSI doesn't work with devices behind the AMD 8131 HT-PCIX
213126258Smlaier	 * bridge.
214126258Smlaier	 */
215240233Sglebius	{ 0x74501022, PCI_QUIRK_DISABLE_MSI,	0,	0 },
216162238Scsjp
217126258Smlaier	{ 0 }
218126258Smlaier};
219145836Smlaier
220240233Sglebius/* map register information */
221223637Sbz#define	PCI_MAPMEM	0x01	/* memory map */
222126258Smlaier#define	PCI_MAPMEMP	0x02	/* prefetchable memory map */
223240233Sglebius#define	PCI_MAPPORT	0x04	/* port map */
224240233Sglebius
225240233Sglebiusstruct devlist pci_devq;
226240233Sglebiusuint32_t pci_generation;
227240233Sglebiusuint32_t pci_numdevs = 0;
228240233Sglebiusstatic int pcie_chipset, pcix_chipset;
229240233Sglebius
230130613Smlaier/* sysctl vars */
231240233SglebiusSYSCTL_NODE(_hw, OID_AUTO, pci, CTLFLAG_RD, 0, "PCI bus tuning parameters");
232240233Sglebius
233240233Sglebiusstatic int pci_enable_io_modes = 1;
234223637SbzTUNABLE_INT("hw.pci.enable_io_modes", &pci_enable_io_modes);
235223637SbzSYSCTL_INT(_hw_pci, OID_AUTO, enable_io_modes, CTLFLAG_RW,
236223637Sbz    &pci_enable_io_modes, 1,
237223637Sbz    "Enable I/O and memory bits in the config register.  Some BIOSes do not\n\
238223637Sbzenable these bits correctly.  We'd like to do this all the time, but there\n\
239223637Sbzare some peripherals that this causes problems with.");
240240233Sglebius
241130613Smlaierstatic int pci_do_power_nodriver = 0;
242126258SmlaierTUNABLE_INT("hw.pci.do_power_nodriver", &pci_do_power_nodriver);
243126258SmlaierSYSCTL_INT(_hw_pci, OID_AUTO, do_power_nodriver, CTLFLAG_RW,
244240233Sglebius    &pci_do_power_nodriver, 0,
245200930Sdelphij  "Place a function into D3 state when no driver attaches to it.  0 means\n\
246200930Sdelphijdisable.  1 means conservatively place devices into D3 state.  2 means\n\
247200930Sdelphijagressively place devices into D3 state.  3 means put absolutely everything\n\
248240233Sglebiusin D3 state.");
249200930Sdelphij
250200930Sdelphijstatic int pci_do_power_resume = 1;
251240233SglebiusTUNABLE_INT("hw.pci.do_power_resume", &pci_do_power_resume);
252130613SmlaierSYSCTL_INT(_hw_pci, OID_AUTO, do_power_resume, CTLFLAG_RW,
253126258Smlaier    &pci_do_power_resume, 1,
254240233Sglebius  "Transition from D3 -> D0 on resume.");
255130613Smlaier
256126258Smlaierstatic int pci_do_msi = 1;
257240233SglebiusTUNABLE_INT("hw.pci.enable_msi", &pci_do_msi);
258130613SmlaierSYSCTL_INT(_hw_pci, OID_AUTO, enable_msi, CTLFLAG_RW, &pci_do_msi, 1,
259145836Smlaier    "Enable support for MSI interrupts");
260240233Sglebius
261223637Sbzstatic int pci_do_msix = 1;
262240233SglebiusTUNABLE_INT("hw.pci.enable_msix", &pci_do_msix);
263126258SmlaierSYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1,
264240233Sglebius    "Enable support for MSI-X interrupts");
265126258Smlaier
266240233Sglebiusstatic int pci_honor_msi_blacklist = 1;
267231852SbzTUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist);
268240233SglebiusSYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD,
269126258Smlaier    &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI");
270240233Sglebius
271223637Sbz/* Find a device_t by bus/slot/function in domain 0 */
272240233Sglebius
273126258Smlaierdevice_t
274240233Sglebiuspci_find_bsf(uint8_t bus, uint8_t slot, uint8_t func)
275240233Sglebius{
276240233Sglebius
277264454Smm	return (pci_find_dbsf(0, bus, slot, func));
278240233Sglebius}
279240233Sglebius
280241039Sglebius/* Find a device_t by domain/bus/slot/function */
281240233Sglebius
282265008Smmdevice_t
283240233Sglebiuspci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func)
284240233Sglebius{
285240233Sglebius	struct pci_devinfo *dinfo;
286240233Sglebius
287240233Sglebius	STAILQ_FOREACH(dinfo, &pci_devq, pci_links) {
288240233Sglebius		if ((dinfo->cfg.domain == domain) &&
289240233Sglebius		    (dinfo->cfg.bus == bus) &&
290240233Sglebius		    (dinfo->cfg.slot == slot) &&
291240233Sglebius		    (dinfo->cfg.func == func)) {
292240233Sglebius			return (dinfo->cfg.dev);
293240233Sglebius		}
294240233Sglebius	}
295240233Sglebius
296126258Smlaier	return (NULL);
297126261Smlaier}
298126258Smlaier
299223637Sbz/* Find a device_t by vendor/device ID */
300171168Smlaier
301240233Sglebiusdevice_t
302145836Smlaierpci_find_device(uint16_t vendor, uint16_t device)
303240233Sglebius{
304240233Sglebius	struct pci_devinfo *dinfo;
305126258Smlaier
306240233Sglebius	STAILQ_FOREACH(dinfo, &pci_devq, pci_links) {
307126258Smlaier		if ((dinfo->cfg.vendor == vendor) &&
308240233Sglebius		    (dinfo->cfg.device == device)) {
309261018Sglebius			return (dinfo->cfg.dev);
310126258Smlaier		}
311240233Sglebius	}
312126258Smlaier
313240233Sglebius	return (NULL);
314223637Sbz}
315223637Sbz
316223637Sbz/* return base address of memory or port map */
317223637Sbz
318223637Sbzstatic pci_addr_t
319240233Sglebiuspci_mapbase(uint64_t mapreg)
320223637Sbz{
321126258Smlaier
322126258Smlaier	if (PCI_BAR_MEM(mapreg))
323223637Sbz		return (mapreg & PCIM_BAR_MEM_BASE);
324223637Sbz	else
325126258Smlaier		return (mapreg & PCIM_BAR_IO_BASE);
326263029Sglebius}
327263029Sglebius
328263029Sglebius/* return map type of memory or port map */
329263029Sglebius
330263029Sglebiusstatic const char *
331263029Sglebiuspci_maptype(uint64_t mapreg)
332263029Sglebius{
333263029Sglebius
334263029Sglebius	if (PCI_BAR_IO(mapreg))
335263029Sglebius		return ("I/O Port");
336263029Sglebius	if (mapreg & PCIM_BAR_MEM_PREFETCH)
337263029Sglebius		return ("Prefetchable Memory");
338145836Smlaier	return ("Memory");
339145836Smlaier}
340263029Sglebius
341263029Sglebius/* return log2 of map size decoded for memory or port map */
342263029Sglebius
343263029Sglebiusstatic int
344263029Sglebiuspci_mapsize(uint64_t testval)
345263029Sglebius{
346263029Sglebius	int ln2size;
347145836Smlaier
348145836Smlaier	testval = pci_mapbase(testval);
349240233Sglebius	ln2size = 0;
350240233Sglebius	if (testval != 0) {
351240233Sglebius		while ((testval & 1) == 0)
352240233Sglebius		{
353223637Sbz			ln2size++;
354240233Sglebius			testval >>= 1;
355223637Sbz		}
356273736Shselasky	}
357273736Shselasky	return (ln2size);
358273736Shselasky}
359273736Shselasky
360130613Smlaier/* return log2 of address range supported by map register */
361273736Shselasky
362273736Shselaskystatic int
363273736Shselaskypci_maprange(uint64_t mapreg)
364273736Shselasky{
365171168Smlaier	int ln2range = 0;
366240233Sglebius
367130613Smlaier	if (PCI_BAR_IO(mapreg))
368240233Sglebius		ln2range = 32;
369240233Sglebius	else
370240233Sglebius		switch (mapreg & PCIM_BAR_MEM_TYPE) {
371284569Skp		case PCIM_BAR_MEM_32:
372284569Skp			ln2range = 32;
373284569Skp			break;
374284569Skp		case PCIM_BAR_MEM_1MB:
375284569Skp			ln2range = 20;
376284569Skp			break;
377284569Skp		case PCIM_BAR_MEM_64:
378284569Skp			ln2range = 64;
379284569Skp			break;
380284569Skp		}
381284569Skp	return (ln2range);
382284569Skp}
383284569Skp
384284569Skp/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */
385284569Skp
386284569Skpstatic void
387284569Skppci_fixancient(pcicfgregs *cfg)
388284569Skp{
389284569Skp	if (cfg->hdrtype != 0)
390284569Skp		return;
391284569Skp
392284569Skp	/* PCI to PCI bridges use header type 1 */
393284569Skp	if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI)
394284569Skp		cfg->hdrtype = 1;
395284569Skp}
396284569Skp
397284569Skp/* extract header type specific config data */
398284569Skp
399284569Skpstatic void
400284569Skppci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg)
401284569Skp{
402284569Skp#define	REG(n, w)	PCIB_READ_CONFIG(pcib, b, s, f, n, w)
403284569Skp	switch (cfg->hdrtype) {
404284569Skp	case 0:
405284569Skp		cfg->subvendor      = REG(PCIR_SUBVEND_0, 2);
406284569Skp		cfg->subdevice      = REG(PCIR_SUBDEV_0, 2);
407284569Skp		cfg->nummaps	    = PCI_MAXMAPS_0;
408284569Skp		break;
409284569Skp	case 1:
410240233Sglebius		cfg->nummaps	    = PCI_MAXMAPS_1;
411240233Sglebius		break;
412126258Smlaier	case 2:
413240233Sglebius		cfg->subvendor      = REG(PCIR_SUBVEND_2, 2);
414126258Smlaier		cfg->subdevice      = REG(PCIR_SUBDEV_2, 2);
415274486Sgnn		cfg->nummaps	    = PCI_MAXMAPS_2;
416274486Sgnn		break;
417274486Sgnn	}
418240233Sglebius#undef REG
419273736Shselasky}
420130613Smlaier
421130613Smlaier/* read configuration header into pcicfgregs structure */
422240736Sglebiusstruct pci_devinfo *
423240736Sglebiuspci_read_device(device_t pcib, int d, int b, int s, int f, size_t size)
424240736Sglebius{
425240736Sglebius#define	REG(n, w)	PCIB_READ_CONFIG(pcib, b, s, f, n, w)
426240736Sglebius	pcicfgregs *cfg = NULL;
427240736Sglebius	struct pci_devinfo *devlist_entry;
428240736Sglebius	struct devlist *devlist_head;
429274486Sgnn
430274486Sgnn	devlist_head = &pci_devq;
431240736Sglebius
432240736Sglebius	devlist_entry = NULL;
433274486Sgnn
434274486Sgnn	if (REG(PCIR_DEVVENDOR, 4) != 0xfffffffful) {
435240736Sglebius		devlist_entry = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
436240736Sglebius		if (devlist_entry == NULL)
437240736Sglebius			return (NULL);
438240736Sglebius
439240736Sglebius		cfg = &devlist_entry->cfg;
440273736Shselasky
441240736Sglebius		cfg->domain		= d;
442240736Sglebius		cfg->bus		= b;
443126258Smlaier		cfg->slot		= s;
444126258Smlaier		cfg->func		= f;
445126258Smlaier		cfg->vendor		= REG(PCIR_VENDOR, 2);
446126258Smlaier		cfg->device		= REG(PCIR_DEVICE, 2);
447126258Smlaier		cfg->cmdreg		= REG(PCIR_COMMAND, 2);
448126258Smlaier		cfg->statreg		= REG(PCIR_STATUS, 2);
449126258Smlaier		cfg->baseclass		= REG(PCIR_CLASS, 1);
450126258Smlaier		cfg->subclass		= REG(PCIR_SUBCLASS, 1);
451126258Smlaier		cfg->progif		= REG(PCIR_PROGIF, 1);
452126258Smlaier		cfg->revid		= REG(PCIR_REVID, 1);
453126258Smlaier		cfg->hdrtype		= REG(PCIR_HDRTYPE, 1);
454126258Smlaier		cfg->cachelnsz		= REG(PCIR_CACHELNSZ, 1);
455126258Smlaier		cfg->lattimer		= REG(PCIR_LATTIMER, 1);
456126258Smlaier		cfg->intpin		= REG(PCIR_INTPIN, 1);
457126258Smlaier		cfg->intline		= REG(PCIR_INTLINE, 1);
458126258Smlaier
459126258Smlaier		cfg->mingnt		= REG(PCIR_MINGNT, 1);
460126258Smlaier		cfg->maxlat		= REG(PCIR_MAXLAT, 1);
461145836Smlaier
462126258Smlaier		cfg->mfdev		= (cfg->hdrtype & PCIM_MFDEV) != 0;
463240233Sglebius		cfg->hdrtype		&= ~PCIM_MFDEV;
464145836Smlaier
465145836Smlaier		pci_fixancient(cfg);
466145836Smlaier		pci_hdrtypedata(pcib, b, s, f, cfg);
467145836Smlaier
468145836Smlaier		if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT)
469145836Smlaier			pci_read_extcap(pcib, cfg);
470240233Sglebius
471145836Smlaier		STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links);
472145836Smlaier
473240233Sglebius		devlist_entry->conf.pc_sel.pc_domain = cfg->domain;
474145836Smlaier		devlist_entry->conf.pc_sel.pc_bus = cfg->bus;
475145836Smlaier		devlist_entry->conf.pc_sel.pc_dev = cfg->slot;
476240233Sglebius		devlist_entry->conf.pc_sel.pc_func = cfg->func;
477145836Smlaier		devlist_entry->conf.pc_hdr = cfg->hdrtype;
478145836Smlaier
479145836Smlaier		devlist_entry->conf.pc_subvendor = cfg->subvendor;
480145836Smlaier		devlist_entry->conf.pc_subdevice = cfg->subdevice;
481145836Smlaier		devlist_entry->conf.pc_vendor = cfg->vendor;
482145836Smlaier		devlist_entry->conf.pc_device = cfg->device;
483145836Smlaier
484145836Smlaier		devlist_entry->conf.pc_class = cfg->baseclass;
485145836Smlaier		devlist_entry->conf.pc_subclass = cfg->subclass;
486145836Smlaier		devlist_entry->conf.pc_progif = cfg->progif;
487240233Sglebius		devlist_entry->conf.pc_revid = cfg->revid;
488145836Smlaier
489145836Smlaier		pci_numdevs++;
490145836Smlaier		pci_generation++;
491145836Smlaier	}
492145836Smlaier	return (devlist_entry);
493240233Sglebius#undef REG
494145836Smlaier}
495145836Smlaier
496240811Sglebiusstatic void
497145836Smlaierpci_read_extcap(device_t pcib, pcicfgregs *cfg)
498145836Smlaier{
499240233Sglebius#define	REG(n, w)	PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
500240233Sglebius#define	WREG(n, v, w)	PCIB_WRITE_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, v, w)
501145836Smlaier#if defined(__i386__) || defined(__amd64__)
502171168Smlaier	uint64_t addr;
503145836Smlaier#endif
504145836Smlaier	uint32_t val;
505145836Smlaier	int	ptr, nextptr, ptrptr;
506145836Smlaier
507145836Smlaier	switch (cfg->hdrtype & PCIM_HDRTYPE) {
508270574Sglebius	case 0:
509145836Smlaier	case 1:
510145836Smlaier		ptrptr = PCIR_CAP_PTR;
511145836Smlaier		break;
512145836Smlaier	case 2:
513145836Smlaier		ptrptr = PCIR_CAP_PTR_2;	/* cardbus capabilities ptr */
514270574Sglebius		break;
515145836Smlaier	default:
516145836Smlaier		return;		/* no extended capabilities support */
517145836Smlaier	}
518145836Smlaier	nextptr = REG(ptrptr, 1);	/* sanity check? */
519145836Smlaier
520145836Smlaier	/*
521240233Sglebius	 * Read capability entries.
522240233Sglebius	 */
523240233Sglebius	while (nextptr != 0) {
524145836Smlaier		/* Sanity check */
525240233Sglebius		if (nextptr > 255) {
526240233Sglebius			printf("illegal PCI extended capability offset %d\n",
527145836Smlaier			    nextptr);
528240811Sglebius			return;
529240811Sglebius		}
530240811Sglebius		/* Find the next entry */
531240233Sglebius		ptr = nextptr;
532240233Sglebius		nextptr = REG(ptr + PCICAP_NEXTPTR, 1);
533240811Sglebius
534240811Sglebius		/* Process this entry */
535240811Sglebius		switch (REG(ptr + PCICAP_ID, 1)) {
536240811Sglebius		case PCIY_PMG:		/* PCI power management */
537240811Sglebius			if (cfg->pp.pp_cap == 0) {
538240811Sglebius				cfg->pp.pp_cap = REG(ptr + PCIR_POWER_CAP, 2);
539240811Sglebius				cfg->pp.pp_status = ptr + PCIR_POWER_STATUS;
540240811Sglebius				cfg->pp.pp_pmcsr = ptr + PCIR_POWER_PMCSR;
541240233Sglebius				if ((nextptr - ptr) > PCIR_POWER_DATA)
542240233Sglebius					cfg->pp.pp_data = ptr + PCIR_POWER_DATA;
543240233Sglebius			}
544240233Sglebius			break;
545240233Sglebius#if defined(__i386__) || defined(__amd64__)
546264454Smm		case PCIY_HT:		/* HyperTransport */
547240233Sglebius			/* Determine HT-specific capability type. */
548240811Sglebius			val = REG(ptr + PCIR_HT_COMMAND, 2);
549240811Sglebius			switch (val & PCIM_HTCMD_CAP_MASK) {
550240811Sglebius			case PCIM_HTCAP_MSI_MAPPING:
551240233Sglebius				if (!(val & PCIM_HTCMD_MSI_FIXED)) {
552240233Sglebius					/* Sanity check the mapping window. */
553264454Smm					addr = REG(ptr + PCIR_HTMSI_ADDRESS_HI,
554264454Smm					    4);
555240811Sglebius					addr <<= 32;
556264454Smm					addr |= REG(ptr + PCIR_HTMSI_ADDRESS_LO,
557264454Smm					    4);
558240811Sglebius					if (addr != MSI_INTEL_ADDR_BASE)
559240233Sglebius						device_printf(pcib,
560240811Sglebius	    "HT Bridge at pci%d:%d:%d:%d has non-default MSI window 0x%llx\n",
561240811Sglebius						    cfg->domain, cfg->bus,
562270574Sglebius						    cfg->slot, cfg->func,
563240811Sglebius						    (long long)addr);
564240811Sglebius				} else
565240811Sglebius					addr = MSI_INTEL_ADDR_BASE;
566240811Sglebius
567240811Sglebius				cfg->ht.ht_msimap = ptr;
568240233Sglebius				cfg->ht.ht_msictrl = val;
569240811Sglebius				cfg->ht.ht_msiaddr = addr;
570240811Sglebius				break;
571240811Sglebius			}
572240811Sglebius			break;
573240811Sglebius#endif
574240811Sglebius		case PCIY_MSI:		/* PCI MSI */
575240811Sglebius			cfg->msi.msi_location = ptr;
576240811Sglebius			cfg->msi.msi_ctrl = REG(ptr + PCIR_MSI_CTRL, 2);
577240811Sglebius			cfg->msi.msi_msgnum = 1 << ((cfg->msi.msi_ctrl &
578240811Sglebius						     PCIM_MSICTRL_MMC_MASK)>>1);
579240811Sglebius			break;
580240811Sglebius		case PCIY_MSIX:		/* PCI MSI-X */
581240811Sglebius			cfg->msix.msix_location = ptr;
582240811Sglebius			cfg->msix.msix_ctrl = REG(ptr + PCIR_MSIX_CTRL, 2);
583240811Sglebius			cfg->msix.msix_msgnum = (cfg->msix.msix_ctrl &
584240811Sglebius			    PCIM_MSIXCTRL_TABLE_SIZE) + 1;
585240811Sglebius			val = REG(ptr + PCIR_MSIX_TABLE, 4);
586240811Sglebius			cfg->msix.msix_table_bar = PCIR_BAR(val &
587240811Sglebius			    PCIM_MSIX_BIR_MASK);
588240811Sglebius			cfg->msix.msix_table_offset = val & ~PCIM_MSIX_BIR_MASK;
589240811Sglebius			val = REG(ptr + PCIR_MSIX_PBA, 4);
590240811Sglebius			cfg->msix.msix_pba_bar = PCIR_BAR(val &
591240811Sglebius			    PCIM_MSIX_BIR_MASK);
592240811Sglebius			cfg->msix.msix_pba_offset = val & ~PCIM_MSIX_BIR_MASK;
593240811Sglebius			break;
594240811Sglebius		case PCIY_VPD:		/* PCI Vital Product Data */
595240811Sglebius			cfg->vpd.vpd_reg = ptr;
596240811Sglebius			break;
597240811Sglebius		case PCIY_SUBVENDOR:
598270574Sglebius			/* Should always be true. */
599270574Sglebius			if ((cfg->hdrtype & PCIM_HDRTYPE) == 1) {
600240811Sglebius				val = REG(ptr + PCIR_SUBVENDCAP_ID, 4);
601240811Sglebius				cfg->subvendor = val & 0xffff;
602264454Smm				cfg->subdevice = val >> 16;
603264454Smm			}
604240811Sglebius			break;
605264454Smm		case PCIY_PCIX:		/* PCI-X */
606240811Sglebius			/*
607273736Shselasky			 * Assume we have a PCI-X chipset if we have
608240233Sglebius			 * at least one PCI-PCI bridge with a PCI-X
609240233Sglebius			 * capability.  Note that some systems with
610240233Sglebius			 * PCI-express or HT chipsets might match on
611240233Sglebius			 * this check as well.
612240233Sglebius			 */
613240233Sglebius			if ((cfg->hdrtype & PCIM_HDRTYPE) == 1)
614240233Sglebius				pcix_chipset = 1;
615240811Sglebius			break;
616240811Sglebius		case PCIY_EXPRESS:	/* PCI-express */
617240811Sglebius			/*
618240811Sglebius			 * Assume we have a PCI-express chipset if we have
619240811Sglebius			 * at least one PCI-express device.
620240811Sglebius			 */
621240811Sglebius			pcie_chipset = 1;
622240811Sglebius			break;
623240233Sglebius		default:
624240233Sglebius			break;
625240233Sglebius		}
626145836Smlaier	}
627145836Smlaier/* REG and WREG use carry through to next functions */
628240233Sglebius}
629145836Smlaier
630240811Sglebius/*
631240811Sglebius * PCI Vital Product Data
632240233Sglebius */
633240233Sglebius
634264454Smm#define	PCI_VPD_TIMEOUT		1000000
635264454Smm
636240233Sglebiusstatic int
637145836Smlaierpci_read_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t *data)
638240233Sglebius{
639240233Sglebius	int count = PCI_VPD_TIMEOUT;
640240233Sglebius
641240233Sglebius	KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned"));
642240233Sglebius
643240233Sglebius	WREG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, reg, 2);
644240233Sglebius
645240233Sglebius	while ((REG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, 2) & 0x8000) != 0x8000) {
646240233Sglebius		if (--count < 0)
647240233Sglebius			return (ENXIO);
648240233Sglebius		DELAY(1);	/* limit looping */
649270574Sglebius	}
650240233Sglebius	*data = (REG(cfg->vpd.vpd_reg + PCIR_VPD_DATA, 4));
651240233Sglebius
652240233Sglebius	return (0);
653240233Sglebius}
654240233Sglebius
655240233Sglebius#if 0
656240233Sglebiusstatic int
657240233Sglebiuspci_write_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t data)
658285940Sglebius{
659285940Sglebius	int count = PCI_VPD_TIMEOUT;
660240233Sglebius
661285940Sglebius	KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned"));
662285940Sglebius
663240233Sglebius	WREG(cfg->vpd.vpd_reg + PCIR_VPD_DATA, data, 4);
664240233Sglebius	WREG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, reg | 0x8000, 2);
665145836Smlaier	while ((REG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, 2) & 0x8000) == 0x8000) {
666145836Smlaier		if (--count < 0)
667240233Sglebius			return (ENXIO);
668130613Smlaier		DELAY(1);	/* limit looping */
669130613Smlaier	}
670126258Smlaier
671126258Smlaier	return (0);
672240233Sglebius}
673240233Sglebius#endif
674240233Sglebius
675240233Sglebius#undef PCI_VPD_TIMEOUT
676240233Sglebius
677240233Sglebiusstruct vpd_readstate {
678240233Sglebius	device_t	pcib;
679130613Smlaier	pcicfgregs	*cfg;
680240233Sglebius	uint32_t	val;
681240233Sglebius	int		bytesinval;
682240233Sglebius	int		off;
683240233Sglebius	uint8_t		cksum;
684130613Smlaier};
685263029Sglebius
686240233Sglebiusstatic int
687145836Smlaiervpd_nextbyte(struct vpd_readstate *vrs, uint8_t *data)
688270574Sglebius{
689270574Sglebius	uint32_t reg;
690240233Sglebius	uint8_t byte;
691240233Sglebius
692130613Smlaier	if (vrs->bytesinval == 0) {
693240233Sglebius		if (pci_read_vpd_reg(vrs->pcib, vrs->cfg, vrs->off, &reg))
694145836Smlaier			return (ENXIO);
695145836Smlaier		vrs->val = le32toh(reg);
696145836Smlaier		vrs->off += 4;
697145836Smlaier		byte = vrs->val & 0xff;
698145836Smlaier		vrs->bytesinval = 3;
699130613Smlaier	} else {
700240233Sglebius		vrs->val = vrs->val >> 8;
701130613Smlaier		byte = vrs->val & 0xff;
702240233Sglebius		vrs->bytesinval--;
703240233Sglebius	}
704130613Smlaier
705285940Sglebius	vrs->cksum += byte;
706130613Smlaier	*data = byte;
707263029Sglebius	return (0);
708240233Sglebius}
709270574Sglebius
710130613Smlaierstatic void
711130613Smlaierpci_read_vpd(device_t pcib, pcicfgregs *cfg)
712145836Smlaier{
713270574Sglebius	struct vpd_readstate vrs;
714270574Sglebius	int state;
715130613Smlaier	int name;
716145836Smlaier	int remain;
717130613Smlaier	int i;
718130613Smlaier	int alloc, off;		/* alloc/off for RO/W arrays */
719130613Smlaier	int cksumvalid;
720126258Smlaier	int dflen;
721261019Sglebius	uint8_t byte;
722285940Sglebius	uint8_t byte2;
723240233Sglebius
724223637Sbz	/* init vpd reader */
725285940Sglebius	vrs.bytesinval = 0;
726240233Sglebius	vrs.off = 0;
727261019Sglebius	vrs.pcib = pcib;
728263029Sglebius	vrs.cfg = cfg;
729261019Sglebius	vrs.cksum = 0;
730240737Sglebius
731261019Sglebius	state = 0;
732261019Sglebius	name = remain = i = 0;	/* shut up stupid gcc */
733261019Sglebius	alloc = off = 0;	/* shut up stupid gcc */
734261019Sglebius	dflen = 0;		/* shut up stupid gcc */
735261019Sglebius	cksumvalid = -1;
736261019Sglebius	while (state >= 0) {
737261019Sglebius		if (vpd_nextbyte(&vrs, &byte)) {
738285940Sglebius			state = -2;
739261019Sglebius			break;
740261019Sglebius		}
741261019Sglebius#if 0
742285940Sglebius		printf("vpd: val: %#x, off: %d, bytesinval: %d, byte: %#hhx, " \
743285940Sglebius		    "state: %d, remain: %d, name: %#x, i: %d\n", vrs.val,
744261019Sglebius		    vrs.off, vrs.bytesinval, byte, state, remain, name, i);
745261019Sglebius#endif
746261019Sglebius		switch (state) {
747240233Sglebius		case 0:		/* item name */
748265008Smm			if (byte & 0x80) {
749265008Smm				if (vpd_nextbyte(&vrs, &byte2)) {
750265008Smm					state = -2;
751265008Smm					break;
752265008Smm				}
753265008Smm				remain = byte2;
754265008Smm				if (vpd_nextbyte(&vrs, &byte2)) {
755265008Smm					state = -2;
756265008Smm					break;
757265008Smm				}
758240233Sglebius				remain |= byte2 << 8;
759223637Sbz				if (remain > (0x7f*4 - vrs.off)) {
760240233Sglebius					state = -1;
761240233Sglebius					printf(
762240233Sglebius			    "pci%d:%d:%d:%d: invalid VPD data, remain %#x\n",
763240233Sglebius					    cfg->domain, cfg->bus, cfg->slot,
764223637Sbz					    cfg->func, remain);
765273736Shselasky				}
766273736Shselasky				name = byte & 0x7f;
767273736Shselasky			} else {
768273736Shselasky				remain = byte & 0x7;
769273736Shselasky				name = (byte >> 3) & 0xf;
770273736Shselasky			}
771240233Sglebius			switch (name) {
772240233Sglebius			case 0x2:	/* String */
773240233Sglebius				cfg->vpd.vpd_ident = malloc(remain + 1,
774240233Sglebius				    M_DEVBUF, M_WAITOK);
775240233Sglebius				i = 0;
776240233Sglebius				state = 1;
777240233Sglebius				break;
778240233Sglebius			case 0xf:	/* End */
779244347Spjd				state = -1;
780240233Sglebius				break;
781240233Sglebius			case 0x10:	/* VPD-R */
782240233Sglebius				alloc = 8;
783240233Sglebius				off = 0;
784273736Shselasky				cfg->vpd.vpd_ros = malloc(alloc *
785240233Sglebius				    sizeof(*cfg->vpd.vpd_ros), M_DEVBUF,
786273736Shselasky				    M_WAITOK | M_ZERO);
787240233Sglebius				state = 2;
788273736Shselasky				break;
789273736Shselasky			case 0x11:	/* VPD-W */
790240233Sglebius				alloc = 8;
791251681Sglebius				off = 0;
792240233Sglebius				cfg->vpd.vpd_w = malloc(alloc *
793223637Sbz				    sizeof(*cfg->vpd.vpd_w), M_DEVBUF,
794223637Sbz				    M_WAITOK | M_ZERO);
795240233Sglebius				state = 5;
796240233Sglebius				break;
797240233Sglebius			default:	/* Invalid data, abort */
798240233Sglebius				state = -1;
799240233Sglebius				break;
800240233Sglebius			}
801244347Spjd			break;
802273736Shselasky
803240233Sglebius		case 1:	/* Identifier String */
804273736Shselasky			cfg->vpd.vpd_ident[i++] = byte;
805273736Shselasky			remain--;
806240233Sglebius			if (remain == 0)  {
807223637Sbz				cfg->vpd.vpd_ident[i] = '\0';
808240233Sglebius				state = 0;
809240233Sglebius			}
810240233Sglebius			break;
811240233Sglebius
812240233Sglebius		case 2:	/* VPD-R Keyword Header */
813240233Sglebius			if (off == alloc) {
814240233Sglebius				cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros,
815240233Sglebius				    (alloc *= 2) * sizeof(*cfg->vpd.vpd_ros),
816240811Sglebius				    M_DEVBUF, M_WAITOK | M_ZERO);
817240233Sglebius			}
818240811Sglebius			cfg->vpd.vpd_ros[off].keyword[0] = byte;
819264454Smm			if (vpd_nextbyte(&vrs, &byte2)) {
820240233Sglebius				state = -2;
821240811Sglebius				break;
822240811Sglebius			}
823240233Sglebius			cfg->vpd.vpd_ros[off].keyword[1] = byte2;
824240233Sglebius			if (vpd_nextbyte(&vrs, &byte2)) {
825240233Sglebius				state = -2;
826240233Sglebius				break;
827223637Sbz			}
828223637Sbz			dflen = byte2;
829240233Sglebius			if (dflen == 0 &&
830265008Smm			    strncmp(cfg->vpd.vpd_ros[off].keyword, "RV",
831265008Smm			    2) == 0) {
832265008Smm				/*
833265008Smm				 * if this happens, we can't trust the rest
834265008Smm				 * of the VPD.
835265008Smm				 */
836265008Smm				printf(
837240233Sglebius				    "pci%d:%d:%d:%d: bad keyword length: %d\n",
838223637Sbz				    cfg->domain, cfg->bus, cfg->slot,
839240233Sglebius				    cfg->func, dflen);
840240233Sglebius				cksumvalid = 0;
841240233Sglebius				state = -1;
842240233Sglebius				break;
843240233Sglebius			} else if (dflen == 0) {
844223637Sbz				cfg->vpd.vpd_ros[off].value = malloc(1 *
845273736Shselasky				    sizeof(*cfg->vpd.vpd_ros[off].value),
846240233Sglebius				    M_DEVBUF, M_WAITOK);
847240233Sglebius				cfg->vpd.vpd_ros[off].value[0] = '\x00';
848240233Sglebius			} else
849240233Sglebius				cfg->vpd.vpd_ros[off].value = malloc(
850240233Sglebius				    (dflen + 1) *
851240233Sglebius				    sizeof(*cfg->vpd.vpd_ros[off].value),
852240233Sglebius				    M_DEVBUF, M_WAITOK);
853240233Sglebius			remain -= 3;
854240233Sglebius			i = 0;
855240233Sglebius			/* keep in sync w/ state 3's transistions */
856240233Sglebius			if (dflen == 0 && remain == 0)
857273736Shselasky				state = 0;
858240233Sglebius			else if (dflen == 0)
859240233Sglebius				state = 2;
860240233Sglebius			else
861240233Sglebius				state = 3;
862240233Sglebius			break;
863240233Sglebius
864240233Sglebius		case 3:	/* VPD-R Keyword Value */
865240233Sglebius			cfg->vpd.vpd_ros[off].value[i++] = byte;
866240233Sglebius			if (strncmp(cfg->vpd.vpd_ros[off].keyword,
867240233Sglebius			    "RV", 2) == 0 && cksumvalid == -1) {
868240233Sglebius				if (vrs.cksum == 0)
869240233Sglebius					cksumvalid = 1;
870240811Sglebius				else {
871240233Sglebius					if (bootverbose)
872240233Sglebius						printf(
873240233Sglebius				"pci%d:%d:%d:%d: bad VPD cksum, remain %hhu\n",
874240233Sglebius						    cfg->domain, cfg->bus,
875240233Sglebius						    cfg->slot, cfg->func,
876240233Sglebius						    vrs.cksum);
877240233Sglebius					cksumvalid = 0;
878240233Sglebius					state = -1;
879265008Smm					break;
880240233Sglebius				}
881240233Sglebius			}
882240233Sglebius			dflen--;
883240233Sglebius			remain--;
884240233Sglebius			/* keep in sync w/ state 2's transistions */
885240233Sglebius			if (dflen == 0)
886240233Sglebius				cfg->vpd.vpd_ros[off++].value[i++] = '\0';
887240233Sglebius			if (dflen == 0 && remain == 0) {
888240233Sglebius				cfg->vpd.vpd_rocnt = off;
889223637Sbz				cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros,
890223637Sbz				    off * sizeof(*cfg->vpd.vpd_ros),
891223637Sbz				    M_DEVBUF, M_WAITOK | M_ZERO);
892240233Sglebius				state = 0;
893240233Sglebius			} else if (dflen == 0)
894130613Smlaier				state = 2;
895223637Sbz			break;
896265008Smm
897240233Sglebius		case 4:
898223637Sbz			remain--;
899240233Sglebius			if (remain == 0)
900240233Sglebius				state = 0;
901240233Sglebius			break;
902240233Sglebius
903240233Sglebius		case 5:	/* VPD-W Keyword Header */
904240233Sglebius			if (off == alloc) {
905240233Sglebius				cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w,
906240233Sglebius				    (alloc *= 2) * sizeof(*cfg->vpd.vpd_w),
907265008Smm				    M_DEVBUF, M_WAITOK | M_ZERO);
908240233Sglebius			}
909240233Sglebius			cfg->vpd.vpd_w[off].keyword[0] = byte;
910240233Sglebius			if (vpd_nextbyte(&vrs, &byte2)) {
911240233Sglebius				state = -2;
912240233Sglebius				break;
913240233Sglebius			}
914240233Sglebius			cfg->vpd.vpd_w[off].keyword[1] = byte2;
915240233Sglebius			if (vpd_nextbyte(&vrs, &byte2)) {
916240233Sglebius				state = -2;
917240233Sglebius				break;
918240233Sglebius			}
919240233Sglebius			cfg->vpd.vpd_w[off].len = dflen = byte2;
920251681Sglebius			cfg->vpd.vpd_w[off].start = vrs.off - vrs.bytesinval;
921240233Sglebius			cfg->vpd.vpd_w[off].value = malloc((dflen + 1) *
922240233Sglebius			    sizeof(*cfg->vpd.vpd_w[off].value),
923240233Sglebius			    M_DEVBUF, M_WAITOK);
924240233Sglebius			remain -= 3;
925240233Sglebius			i = 0;
926240233Sglebius			/* keep in sync w/ state 6's transistions */
927240233Sglebius			if (dflen == 0 && remain == 0)
928240233Sglebius				state = 0;
929240233Sglebius			else if (dflen == 0)
930251681Sglebius				state = 5;
931251681Sglebius			else
932251681Sglebius				state = 6;
933251681Sglebius			break;
934251681Sglebius
935251681Sglebius		case 6:	/* VPD-W Keyword Value */
936251681Sglebius			cfg->vpd.vpd_w[off].value[i++] = byte;
937251681Sglebius			dflen--;
938251681Sglebius			remain--;
939251681Sglebius			/* keep in sync w/ state 5's transistions */
940251681Sglebius			if (dflen == 0)
941251681Sglebius				cfg->vpd.vpd_w[off++].value[i++] = '\0';
942251681Sglebius			if (dflen == 0 && remain == 0) {
943251681Sglebius				cfg->vpd.vpd_wcnt = off;
944251681Sglebius				cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w,
945251681Sglebius				    off * sizeof(*cfg->vpd.vpd_w),
946251681Sglebius				    M_DEVBUF, M_WAITOK | M_ZERO);
947251681Sglebius				state = 0;
948251681Sglebius			} else if (dflen == 0)
949251681Sglebius				state = 5;
950251681Sglebius			break;
951251681Sglebius
952251681Sglebius		default:
953251681Sglebius			printf("pci%d:%d:%d:%d: invalid state: %d\n",
954251681Sglebius			    cfg->domain, cfg->bus, cfg->slot, cfg->func,
955251681Sglebius			    state);
956251681Sglebius			state = -1;
957251681Sglebius			break;
958251681Sglebius		}
959251681Sglebius	}
960251681Sglebius
961251681Sglebius	if (cksumvalid == 0 || state < -1) {
962251681Sglebius		/* read-only data bad, clean up */
963251681Sglebius		if (cfg->vpd.vpd_ros != NULL) {
964240233Sglebius			for (off = 0; cfg->vpd.vpd_ros[off].value; off++)
965240233Sglebius				free(cfg->vpd.vpd_ros[off].value, M_DEVBUF);
966240233Sglebius			free(cfg->vpd.vpd_ros, M_DEVBUF);
967251681Sglebius			cfg->vpd.vpd_ros = NULL;
968240233Sglebius		}
969240233Sglebius	}
970240233Sglebius	if (state < -1) {
971240233Sglebius		/* I/O error, clean up */
972240233Sglebius		printf("pci%d:%d:%d:%d: failed to read VPD data.\n",
973240233Sglebius		    cfg->domain, cfg->bus, cfg->slot, cfg->func);
974240233Sglebius		if (cfg->vpd.vpd_ident != NULL) {
975240233Sglebius			free(cfg->vpd.vpd_ident, M_DEVBUF);
976240233Sglebius			cfg->vpd.vpd_ident = NULL;
977240233Sglebius		}
978240233Sglebius		if (cfg->vpd.vpd_w != NULL) {
979240233Sglebius			for (off = 0; cfg->vpd.vpd_w[off].value; off++)
980240233Sglebius				free(cfg->vpd.vpd_w[off].value, M_DEVBUF);
981240233Sglebius			free(cfg->vpd.vpd_w, M_DEVBUF);
982240233Sglebius			cfg->vpd.vpd_w = NULL;
983223637Sbz		}
984240233Sglebius	}
985240233Sglebius	cfg->vpd.vpd_cached = 1;
986251681Sglebius#undef REG
987251681Sglebius#undef WREG
988251681Sglebius}
989251681Sglebius
990251681Sglebiusint
991251681Sglebiuspci_get_vpd_ident_method(device_t dev, device_t child, const char **identptr)
992251681Sglebius{
993251681Sglebius	struct pci_devinfo *dinfo = device_get_ivars(child);
994251681Sglebius	pcicfgregs *cfg = &dinfo->cfg;
995251681Sglebius
996251681Sglebius	if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0)
997240233Sglebius		pci_read_vpd(device_get_parent(dev), cfg);
998223637Sbz
999251681Sglebius	*identptr = cfg->vpd.vpd_ident;
1000240233Sglebius
1001223637Sbz	if (*identptr == NULL)
1002223637Sbz		return (ENXIO);
1003223637Sbz
1004223637Sbz	return (0);
1005223637Sbz}
1006223637Sbz
1007223637Sbzint
1008223637Sbzpci_get_vpd_readonly_method(device_t dev, device_t child, const char *kw,
1009223637Sbz	const char **vptr)
1010223637Sbz{
1011223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
1012223637Sbz	pcicfgregs *cfg = &dinfo->cfg;
1013223637Sbz	int i;
1014240233Sglebius
1015223637Sbz	if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0)
1016223637Sbz		pci_read_vpd(device_get_parent(dev), cfg);
1017223637Sbz
1018223637Sbz	for (i = 0; i < cfg->vpd.vpd_rocnt; i++)
1019223637Sbz		if (memcmp(kw, cfg->vpd.vpd_ros[i].keyword,
1020223637Sbz		    sizeof(cfg->vpd.vpd_ros[i].keyword)) == 0) {
1021240233Sglebius			*vptr = cfg->vpd.vpd_ros[i].value;
1022251681Sglebius		}
1023240233Sglebius
1024240233Sglebius	if (i != cfg->vpd.vpd_rocnt)
1025240233Sglebius		return (0);
1026250522Sglebius
1027223637Sbz	*vptr = NULL;
1028223637Sbz	return (ENXIO);
1029240233Sglebius}
1030240233Sglebius
1031240233Sglebius/*
1032223637Sbz * Find the requested extended capability and return the offset in
1033240233Sglebius * configuration space via the pointer provided. The function returns
1034240233Sglebius * 0 on success and error code otherwise.
1035223637Sbz */
1036126258Smlaierint
1037126258Smlaierpci_find_extcap_method(device_t dev, device_t child, int capability,
1038240233Sglebius    int *capreg)
1039240233Sglebius{
1040223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
1041240233Sglebius	pcicfgregs *cfg = &dinfo->cfg;
1042223637Sbz	u_int32_t status;
1043240233Sglebius	u_int8_t ptr;
1044223637Sbz
1045251681Sglebius	/*
1046251681Sglebius	 * Check the CAP_LIST bit of the PCI status register first.
1047251681Sglebius	 */
1048251681Sglebius	status = pci_read_config(child, PCIR_STATUS, 2);
1049251681Sglebius	if (!(status & PCIM_STATUS_CAPPRESENT))
1050251681Sglebius		return (ENXIO);
1051251681Sglebius
1052240233Sglebius	/*
1053240233Sglebius	 * Determine the start pointer of the capabilities list.
1054240233Sglebius	 */
1055240233Sglebius	switch (cfg->hdrtype & PCIM_HDRTYPE) {
1056240233Sglebius	case 0:
1057240233Sglebius	case 1:
1058240233Sglebius		ptr = PCIR_CAP_PTR;
1059240233Sglebius		break;
1060240233Sglebius	case 2:
1061240233Sglebius		ptr = PCIR_CAP_PTR_2;
1062240233Sglebius		break;
1063240233Sglebius	default:
1064240233Sglebius		/* XXX: panic? */
1065240233Sglebius		return (ENXIO);		/* no extended capabilities support */
1066251681Sglebius	}
1067240233Sglebius	ptr = pci_read_config(child, ptr, 1);
1068240233Sglebius
1069240233Sglebius	/*
1070240233Sglebius	 * Traverse the capabilities list.
1071240233Sglebius	 */
1072251681Sglebius	while (ptr != 0) {
1073251681Sglebius		if (pci_read_config(child, ptr + PCICAP_ID, 1) == capability) {
1074251681Sglebius			if (capreg != NULL)
1075240233Sglebius				*capreg = ptr;
1076240233Sglebius			return (0);
1077240233Sglebius		}
1078223637Sbz		ptr = pci_read_config(child, ptr + PCICAP_NEXTPTR, 1);
1079251681Sglebius	}
1080223637Sbz
1081223637Sbz	return (ENOENT);
1082240233Sglebius}
1083223637Sbz
1084223637Sbz/*
1085240233Sglebius * Support for MSI-X message interrupts.
1086240233Sglebius */
1087223637Sbzvoid
1088240233Sglebiuspci_enable_msix(device_t dev, u_int index, uint64_t address, uint32_t data)
1089240233Sglebius{
1090240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1091240233Sglebius	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1092240233Sglebius	uint32_t offset;
1093240233Sglebius
1094240233Sglebius	KASSERT(msix->msix_table_len > index, ("bogus index"));
1095240233Sglebius	offset = msix->msix_table_offset + index * 16;
1096240233Sglebius	bus_write_4(msix->msix_table_res, offset, address & 0xffffffff);
1097240233Sglebius	bus_write_4(msix->msix_table_res, offset + 4, address >> 32);
1098240233Sglebius	bus_write_4(msix->msix_table_res, offset + 8, data);
1099240233Sglebius
1100240233Sglebius	/* Enable MSI -> HT mapping. */
1101240233Sglebius	pci_ht_map_msi(dev, address);
1102240233Sglebius}
1103223637Sbz
1104240233Sglebiusvoid
1105240233Sglebiuspci_mask_msix(device_t dev, u_int index)
1106240233Sglebius{
1107240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1108240233Sglebius	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1109240233Sglebius	uint32_t offset, val;
1110240233Sglebius
1111223637Sbz	KASSERT(msix->msix_msgnum > index, ("bogus index"));
1112223637Sbz	offset = msix->msix_table_offset + index * 16 + 12;
1113240233Sglebius	val = bus_read_4(msix->msix_table_res, offset);
1114223637Sbz	if (!(val & PCIM_MSIX_VCTRL_MASK)) {
1115223637Sbz		val |= PCIM_MSIX_VCTRL_MASK;
1116240233Sglebius		bus_write_4(msix->msix_table_res, offset, val);
1117240233Sglebius	}
1118240233Sglebius}
1119223637Sbz
1120240233Sglebiusvoid
1121223637Sbzpci_unmask_msix(device_t dev, u_int index)
1122240233Sglebius{
1123240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1124223637Sbz	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1125240233Sglebius	uint32_t offset, val;
1126240233Sglebius
1127240233Sglebius	KASSERT(msix->msix_table_len > index, ("bogus index"));
1128223637Sbz	offset = msix->msix_table_offset + index * 16 + 12;
1129223637Sbz	val = bus_read_4(msix->msix_table_res, offset);
1130223637Sbz	if (val & PCIM_MSIX_VCTRL_MASK) {
1131240233Sglebius		val &= ~PCIM_MSIX_VCTRL_MASK;
1132240233Sglebius		bus_write_4(msix->msix_table_res, offset, val);
1133240233Sglebius	}
1134240233Sglebius}
1135240233Sglebius
1136240233Sglebiusint
1137240233Sglebiuspci_pending_msix(device_t dev, u_int index)
1138240233Sglebius{
1139240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1140240233Sglebius	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1141240233Sglebius	uint32_t offset, bit;
1142240233Sglebius
1143223637Sbz	KASSERT(msix->msix_table_len > index, ("bogus index"));
1144240233Sglebius	offset = msix->msix_pba_offset + (index / 32) * 4;
1145240233Sglebius	bit = 1 << index % 32;
1146223637Sbz	return (bus_read_4(msix->msix_pba_res, offset) & bit);
1147240233Sglebius}
1148223637Sbz
1149240233Sglebius/*
1150240233Sglebius * Restore MSI-X registers and table during resume.  If MSI-X is
1151223637Sbz * enabled then walk the virtual table to restore the actual MSI-X
1152223637Sbz * table.
1153240233Sglebius */
1154240233Sglebiusstatic void
1155240233Sglebiuspci_resume_msix(device_t dev)
1156240233Sglebius{
1157240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1158240233Sglebius	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1159240233Sglebius	struct msix_table_entry *mte;
1160223637Sbz	struct msix_vector *mv;
1161223637Sbz	int i;
1162223637Sbz
1163240233Sglebius	if (msix->msix_alloc > 0) {
1164240233Sglebius		/* First, mask all vectors. */
1165223637Sbz		for (i = 0; i < msix->msix_msgnum; i++)
1166240233Sglebius			pci_mask_msix(dev, i);
1167223637Sbz
1168240233Sglebius		/* Second, program any messages with at least one handler. */
1169240233Sglebius		for (i = 0; i < msix->msix_table_len; i++) {
1170240233Sglebius			mte = &msix->msix_table[i];
1171223637Sbz			if (mte->mte_vector == 0 || mte->mte_handlers == 0)
1172240233Sglebius				continue;
1173223637Sbz			mv = &msix->msix_vectors[mte->mte_vector - 1];
1174240233Sglebius			pci_enable_msix(dev, i, mv->mv_address, mv->mv_data);
1175223637Sbz			pci_unmask_msix(dev, i);
1176223637Sbz		}
1177223637Sbz	}
1178223637Sbz	pci_write_config(dev, msix->msix_location + PCIR_MSIX_CTRL,
1179223637Sbz	    msix->msix_ctrl, 2);
1180223637Sbz}
1181240233Sglebius
1182240233Sglebius/*
1183250522Sglebius * Attempt to allocate *count MSI-X messages.  The actual number allocated is
1184223637Sbz * returned in *count.  After this function returns, each message will be
1185240233Sglebius * available to the driver as SYS_RES_IRQ resources starting at rid 1.
1186240233Sglebius */
1187240233Sglebiusint
1188240233Sglebiuspci_alloc_msix_method(device_t dev, device_t child, int *count)
1189240233Sglebius{
1190240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(child);
1191223637Sbz	pcicfgregs *cfg = &dinfo->cfg;
1192223637Sbz	struct resource_list_entry *rle;
1193223637Sbz	int actual, error, i, irq, max;
1194240233Sglebius
1195240233Sglebius	/* Don't let count == 0 get us into trouble. */
1196240233Sglebius	if (*count == 0)
1197240233Sglebius		return (EINVAL);
1198240233Sglebius
1199223637Sbz	/* If rid 0 is allocated, then fail. */
1200130613Smlaier	rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0);
1201240233Sglebius	if (rle != NULL && rle->res != NULL)
1202251681Sglebius		return (ENXIO);
1203250522Sglebius
1204250522Sglebius	/* Already have allocated messages? */
1205244184Sglebius	if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
1206240233Sglebius		return (ENXIO);
1207251681Sglebius
1208240233Sglebius	/* If MSI is blacklisted for this system, fail. */
1209240233Sglebius	if (pci_msi_blacklisted())
1210240233Sglebius		return (ENXIO);
1211240233Sglebius
1212240233Sglebius	/* MSI-X capability present? */
1213240233Sglebius	if (cfg->msix.msix_location == 0 || !pci_do_msix)
1214223637Sbz		return (ENODEV);
1215250521Sglebius
1216250312Sglebius	/* Make sure the appropriate BARs are mapped. */
1217240233Sglebius	rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
1218240233Sglebius	    cfg->msix.msix_table_bar);
1219130613Smlaier	if (rle == NULL || rle->res == NULL ||
1220223637Sbz	    !(rman_get_flags(rle->res) & RF_ACTIVE))
1221250522Sglebius		return (ENXIO);
1222130613Smlaier	cfg->msix.msix_table_res = rle->res;
1223240233Sglebius	if (cfg->msix.msix_pba_bar != cfg->msix.msix_table_bar) {
1224240233Sglebius		rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
1225240233Sglebius		    cfg->msix.msix_pba_bar);
1226240233Sglebius		if (rle == NULL || rle->res == NULL ||
1227270574Sglebius		    !(rman_get_flags(rle->res) & RF_ACTIVE))
1228223637Sbz			return (ENXIO);
1229223637Sbz	}
1230240233Sglebius	cfg->msix.msix_pba_res = rle->res;
1231240233Sglebius
1232126258Smlaier	if (bootverbose)
1233126258Smlaier		device_printf(child,
1234126258Smlaier		    "attempting to allocate %d MSI-X vectors (%d supported)\n",
1235240233Sglebius		    *count, cfg->msix.msix_msgnum);
1236240233Sglebius	max = min(*count, cfg->msix.msix_msgnum);
1237240233Sglebius	for (i = 0; i < max; i++) {
1238223637Sbz		/* Allocate a message. */
1239240233Sglebius		error = PCIB_ALLOC_MSIX(device_get_parent(dev), child, &irq);
1240223637Sbz		if (error)
1241240233Sglebius			break;
1242240233Sglebius		resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
1243240233Sglebius		    irq, 1);
1244270574Sglebius	}
1245223637Sbz	actual = i;
1246273736Shselasky
1247223637Sbz	if (bootverbose) {
1248240233Sglebius		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 1);
1249240233Sglebius		if (actual == 1)
1250240233Sglebius			device_printf(child, "using IRQ %lu for MSI-X\n",
1251240233Sglebius			    rle->start);
1252223637Sbz		else {
1253240233Sglebius			int run;
1254240233Sglebius
1255240233Sglebius			/*
1256240233Sglebius			 * Be fancy and try to print contiguous runs of
1257223637Sbz			 * IRQ values as ranges.  'irq' is the previous IRQ.
1258223637Sbz			 * 'run' is true if we are in a range.
1259240233Sglebius			 */
1260240233Sglebius			device_printf(child, "using IRQs %lu", rle->start);
1261240233Sglebius			irq = rle->start;
1262240233Sglebius			run = 0;
1263240233Sglebius			for (i = 1; i < actual; i++) {
1264240233Sglebius				rle = resource_list_find(&dinfo->resources,
1265223637Sbz				    SYS_RES_IRQ, i + 1);
1266240233Sglebius
1267223637Sbz				/* Still in a run? */
1268240233Sglebius				if (rle->start == irq + 1) {
1269240233Sglebius					run = 1;
1270223637Sbz					irq++;
1271270574Sglebius					continue;
1272223637Sbz				}
1273240233Sglebius
1274240233Sglebius				/* Finish previous range. */
1275240233Sglebius				if (run) {
1276240233Sglebius					printf("-%d", irq);
1277240233Sglebius					run = 0;
1278240233Sglebius				}
1279240233Sglebius
1280240233Sglebius				/* Start new range. */
1281240233Sglebius				printf(",%lu", rle->start);
1282223637Sbz				irq = rle->start;
1283223637Sbz			}
1284240233Sglebius
1285223637Sbz			/* Unfinished range? */
1286240233Sglebius			if (run)
1287240233Sglebius				printf("-%d", irq);
1288240233Sglebius			printf(" for MSI-X\n");
1289240233Sglebius		}
1290240233Sglebius	}
1291261018Sglebius
1292240233Sglebius	/* Mask all vectors. */
1293261018Sglebius	for (i = 0; i < cfg->msix.msix_msgnum; i++)
1294261018Sglebius		pci_mask_msix(child, i);
1295261018Sglebius
1296240233Sglebius	/* Allocate and initialize vector data and virtual table. */
1297240233Sglebius	cfg->msix.msix_vectors = malloc(sizeof(struct msix_vector) * actual,
1298240233Sglebius	    M_DEVBUF, M_WAITOK | M_ZERO);
1299240233Sglebius	cfg->msix.msix_table = malloc(sizeof(struct msix_table_entry) * actual,
1300240233Sglebius	    M_DEVBUF, M_WAITOK | M_ZERO);
1301240233Sglebius	for (i = 0; i < actual; i++) {
1302240233Sglebius		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
1303223637Sbz		cfg->msix.msix_vectors[i].mv_irq = rle->start;
1304223637Sbz		cfg->msix.msix_table[i].mte_vector = i + 1;
1305223637Sbz	}
1306223637Sbz
1307223637Sbz	/* Update control register to enable MSI-X. */
1308223637Sbz	cfg->msix.msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE;
1309223637Sbz	pci_write_config(child, cfg->msix.msix_location + PCIR_MSIX_CTRL,
1310240233Sglebius	    cfg->msix.msix_ctrl, 2);
1311223637Sbz
1312240233Sglebius	/* Update counts of alloc'd messages. */
1313240233Sglebius	cfg->msix.msix_alloc = actual;
1314223637Sbz	cfg->msix.msix_table_len = actual;
1315270574Sglebius	*count = actual;
1316223637Sbz	return (0);
1317240233Sglebius}
1318223637Sbz
1319240233Sglebius/*
1320240233Sglebius * By default, pci_alloc_msix() will assign the allocated IRQ
1321240233Sglebius * resources consecutively to the first N messages in the MSI-X table.
1322240233Sglebius * However, device drivers may want to use different layouts if they
1323240233Sglebius * either receive fewer messages than they asked for, or they wish to
1324240233Sglebius * populate the MSI-X table sparsely.  This method allows the driver
1325240233Sglebius * to specify what layout it wants.  It must be called after a
1326223637Sbz * successful pci_alloc_msix() but before any of the associated
1327240233Sglebius * SYS_RES_IRQ resources are allocated via bus_alloc_resource().
1328240233Sglebius *
1329240233Sglebius * The 'vectors' array contains 'count' message vectors.  The array
1330240233Sglebius * maps directly to the MSI-X table in that index 0 in the array
1331240233Sglebius * specifies the vector for the first message in the MSI-X table, etc.
1332240233Sglebius * The vector value in each array index can either be 0 to indicate
1333240233Sglebius * that no vector should be assigned to a message slot, or it can be a
1334240233Sglebius * number from 1 to N (where N is the count returned from a
1335240233Sglebius * succcessful call to pci_alloc_msix()) to indicate which message
1336240233Sglebius * vector (IRQ) to be used for the corresponding message.
1337240233Sglebius *
1338240233Sglebius * On successful return, each message with a non-zero vector will have
1339240233Sglebius * an associated SYS_RES_IRQ whose rid is equal to the array index +
1340240233Sglebius * 1.  Additionally, if any of the IRQs allocated via the previous
1341240233Sglebius * call to pci_alloc_msix() are not used in the mapping, those IRQs
1342240233Sglebius * will be freed back to the system automatically.
1343240233Sglebius *
1344240233Sglebius * For example, suppose a driver has a MSI-X table with 6 messages and
1345240233Sglebius * asks for 6 messages, but pci_alloc_msix() only returns a count of
1346240233Sglebius * 3.  Call the three vectors allocated by pci_alloc_msix() A, B, and
1347240233Sglebius * C.  After the call to pci_alloc_msix(), the device will be setup to
1348240233Sglebius * have an MSI-X table of ABC--- (where - means no vector assigned).
1349240233Sglebius * If the driver ten passes a vector array of { 1, 0, 1, 2, 0, 2 },
1350240233Sglebius * then the MSI-X table will look like A-AB-B, and the 'C' vector will
1351240233Sglebius * be freed back to the system.  This device will also have valid
1352240233Sglebius * SYS_RES_IRQ rids of 1, 3, 4, and 6.
1353240233Sglebius *
1354240233Sglebius * In any case, the SYS_RES_IRQ rid X will always map to the message
1355240233Sglebius * at MSI-X table index X - 1 and will only be valid if a vector is
1356240233Sglebius * assigned to that table entry.
1357240233Sglebius */
1358240233Sglebiusint
1359240233Sglebiuspci_remap_msix_method(device_t dev, device_t child, int count,
1360240233Sglebius    const u_int *vectors)
1361223637Sbz{
1362223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
1363223637Sbz	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1364223637Sbz	struct resource_list_entry *rle;
1365240233Sglebius	int i, irq, j, *used;
1366240233Sglebius
1367240233Sglebius	/*
1368223637Sbz	 * Have to have at least one message in the table but the
1369240233Sglebius	 * table can't be bigger than the actual MSI-X table in the
1370240233Sglebius	 * device.
1371240233Sglebius	 */
1372240233Sglebius	if (count == 0 || count > msix->msix_msgnum)
1373240233Sglebius		return (EINVAL);
1374240233Sglebius
1375126258Smlaier	/* Sanity check the vectors. */
1376240233Sglebius	for (i = 0; i < count; i++)
1377240233Sglebius		if (vectors[i] > msix->msix_alloc)
1378240233Sglebius			return (EINVAL);
1379240233Sglebius
1380240233Sglebius	/*
1381240233Sglebius	 * Make sure there aren't any holes in the vectors to be used.
1382240233Sglebius	 * It's a big pain to support it, and it doesn't really make
1383240233Sglebius	 * sense anyway.  Also, at least one vector must be used.
1384240233Sglebius	 */
1385240233Sglebius	used = malloc(sizeof(int) * msix->msix_alloc, M_DEVBUF, M_WAITOK |
1386240233Sglebius	    M_ZERO);
1387240233Sglebius	for (i = 0; i < count; i++)
1388240233Sglebius		if (vectors[i] != 0)
1389240233Sglebius			used[vectors[i] - 1] = 1;
1390240233Sglebius	for (i = 0; i < msix->msix_alloc - 1; i++)
1391240233Sglebius		if (used[i] == 0 && used[i + 1] == 1) {
1392240233Sglebius			free(used, M_DEVBUF);
1393240233Sglebius			return (EINVAL);
1394240233Sglebius		}
1395240233Sglebius	if (used[0] != 1) {
1396240233Sglebius		free(used, M_DEVBUF);
1397240233Sglebius		return (EINVAL);
1398240233Sglebius	}
1399240233Sglebius
1400240233Sglebius	/* Make sure none of the resources are allocated. */
1401240233Sglebius	for (i = 0; i < msix->msix_table_len; i++) {
1402240233Sglebius		if (msix->msix_table[i].mte_vector == 0)
1403240233Sglebius			continue;
1404240233Sglebius		if (msix->msix_table[i].mte_handlers > 0)
1405240233Sglebius			return (EBUSY);
1406240233Sglebius		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
1407240233Sglebius		KASSERT(rle != NULL, ("missing resource"));
1408240233Sglebius		if (rle->res != NULL)
1409240233Sglebius			return (EBUSY);
1410240233Sglebius	}
1411240233Sglebius
1412240233Sglebius	/* Free the existing resource list entries. */
1413240233Sglebius	for (i = 0; i < msix->msix_table_len; i++) {
1414240233Sglebius		if (msix->msix_table[i].mte_vector == 0)
1415240233Sglebius			continue;
1416240233Sglebius		resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
1417240233Sglebius	}
1418171168Smlaier
1419126258Smlaier	/*
1420241039Sglebius	 * Build the new virtual table keeping track of which vectors are
1421171168Smlaier	 * used.
1422223637Sbz	 */
1423223637Sbz	free(msix->msix_table, M_DEVBUF);
1424171168Smlaier	msix->msix_table = malloc(sizeof(struct msix_table_entry) * count,
1425240233Sglebius	    M_DEVBUF, M_WAITOK | M_ZERO);
1426240233Sglebius	for (i = 0; i < count; i++)
1427171168Smlaier		msix->msix_table[i].mte_vector = vectors[i];
1428226527Sbz	msix->msix_table_len = count;
1429240233Sglebius
1430240233Sglebius	/* Free any unused IRQs and resize the vectors array if necessary. */
1431240233Sglebius	j = msix->msix_alloc - 1;
1432240233Sglebius	if (used[j] == 0) {
1433240233Sglebius		struct msix_vector *vec;
1434240233Sglebius
1435240233Sglebius		while (used[j] == 0) {
1436240233Sglebius			PCIB_RELEASE_MSIX(device_get_parent(dev), child,
1437240233Sglebius			    msix->msix_vectors[j].mv_irq);
1438171168Smlaier			j--;
1439240233Sglebius		}
1440240233Sglebius		vec = malloc(sizeof(struct msix_vector) * (j + 1), M_DEVBUF,
1441240233Sglebius		    M_WAITOK);
1442273736Shselasky		bcopy(msix->msix_vectors, vec, sizeof(struct msix_vector) *
1443226527Sbz		    (j + 1));
1444240233Sglebius		free(msix->msix_vectors, M_DEVBUF);
1445240233Sglebius		msix->msix_vectors = vec;
1446240233Sglebius		msix->msix_alloc = j + 1;
1447240233Sglebius	}
1448240233Sglebius	free(used, M_DEVBUF);
1449240233Sglebius
1450240233Sglebius	/* Map the IRQs onto the rids. */
1451240233Sglebius	for (i = 0; i < count; i++) {
1452240233Sglebius		if (vectors[i] == 0)
1453240233Sglebius			continue;
1454240233Sglebius		irq = msix->msix_vectors[vectors[i]].mv_irq;
1455240233Sglebius		resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
1456240233Sglebius		    irq, 1);
1457226527Sbz	}
1458240233Sglebius
1459226527Sbz	if (bootverbose) {
1460226527Sbz		device_printf(child, "Remapped MSI-X IRQs as: ");
1461226527Sbz		for (i = 0; i < count; i++) {
1462240233Sglebius			if (i != 0)
1463126258Smlaier				printf(", ");
1464240233Sglebius			if (vectors[i] == 0)
1465273736Shselasky				printf("---");
1466240233Sglebius			else
1467196372Smlaier				printf("%d",
1468240233Sglebius				    msix->msix_vectors[vectors[i]].mv_irq);
1469241039Sglebius		}
1470240233Sglebius		printf("\n");
1471240233Sglebius	}
1472240233Sglebius
1473240233Sglebius	return (0);
1474240233Sglebius}
1475171168Smlaier
1476240233Sglebiusstatic int
1477240233Sglebiuspci_release_msix(device_t dev, device_t child)
1478240233Sglebius{
1479171168Smlaier	struct pci_devinfo *dinfo = device_get_ivars(child);
1480171168Smlaier	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1481240233Sglebius	struct resource_list_entry *rle;
1482223637Sbz	int i;
1483126258Smlaier
1484126258Smlaier	/* Do we have any messages to release? */
1485126258Smlaier	if (msix->msix_alloc == 0)
1486126258Smlaier		return (ENODEV);
1487126258Smlaier
1488126258Smlaier	/* Make sure none of the resources are allocated. */
1489126258Smlaier	for (i = 0; i < msix->msix_table_len; i++) {
1490126258Smlaier		if (msix->msix_table[i].mte_vector == 0)
1491126258Smlaier			continue;
1492126258Smlaier		if (msix->msix_table[i].mte_handlers > 0)
1493126258Smlaier			return (EBUSY);
1494126258Smlaier		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
1495240233Sglebius		KASSERT(rle != NULL, ("missing resource"));
1496171168Smlaier		if (rle->res != NULL)
1497171168Smlaier			return (EBUSY);
1498240233Sglebius	}
1499126261Smlaier
1500126258Smlaier	/* Update control register to disable MSI-X. */
1501126258Smlaier	msix->msix_ctrl &= ~PCIM_MSIXCTRL_MSIX_ENABLE;
1502223637Sbz	pci_write_config(child, msix->msix_location + PCIR_MSIX_CTRL,
1503126258Smlaier	    msix->msix_ctrl, 2);
1504126258Smlaier
1505126258Smlaier	/* Free the resource list entries. */
1506263029Sglebius	for (i = 0; i < msix->msix_table_len; i++) {
1507126258Smlaier		if (msix->msix_table[i].mte_vector == 0)
1508223637Sbz			continue;
1509223637Sbz		resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
1510223637Sbz	}
1511126258Smlaier	free(msix->msix_table, M_DEVBUF);
1512126258Smlaier	msix->msix_table_len = 0;
1513126258Smlaier
1514126258Smlaier	/* Release the IRQs. */
1515126258Smlaier	for (i = 0; i < msix->msix_alloc; i++)
1516126258Smlaier		PCIB_RELEASE_MSIX(device_get_parent(dev), child,
1517240233Sglebius		    msix->msix_vectors[i].mv_irq);
1518126258Smlaier	free(msix->msix_vectors, M_DEVBUF);
1519126258Smlaier	msix->msix_alloc = 0;
1520126258Smlaier	return (0);
1521126258Smlaier}
1522126258Smlaier
1523240233Sglebius/*
1524126258Smlaier * Return the max supported MSI-X messages this device supports.
1525261019Sglebius * Basically, assuming the MD code can alloc messages, this function
1526240233Sglebius * should return the maximum value that pci_alloc_msix() can return.
1527240233Sglebius * Thus, it is subject to the tunables, etc.
1528240233Sglebius */
1529126258Smlaierint
1530261019Sglebiuspci_msix_count_method(device_t dev, device_t child)
1531273736Shselasky{
1532240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(child);
1533240233Sglebius	struct pcicfg_msix *msix = &dinfo->cfg.msix;
1534261018Sglebius
1535285940Sglebius	if (pci_do_msix && msix->msix_location != 0)
1536261019Sglebius		return (msix->msix_msgnum);
1537240233Sglebius	return (0);
1538240233Sglebius}
1539240233Sglebius
1540223637Sbz/*
1541261019Sglebius * HyperTransport MSI mapping control
1542261019Sglebius */
1543270574Sglebiusvoid
1544270574Sglebiuspci_ht_map_msi(device_t dev, uint64_t addr)
1545130613Smlaier{
1546126258Smlaier	struct pci_devinfo *dinfo = device_get_ivars(dev);
1547240233Sglebius	struct pcicfg_ht *ht = &dinfo->cfg.ht;
1548130613Smlaier
1549130613Smlaier	if (!ht->ht_msimap)
1550285940Sglebius		return;
1551285940Sglebius
1552285940Sglebius	if (addr && !(ht->ht_msictrl & PCIM_HTCMD_MSI_ENABLE) &&
1553126258Smlaier	    ht->ht_msiaddr >> 20 == addr >> 20) {
1554285940Sglebius		/* Enable MSI -> HT mapping. */
1555285940Sglebius		ht->ht_msictrl |= PCIM_HTCMD_MSI_ENABLE;
1556285940Sglebius		pci_write_config(dev, ht->ht_msimap + PCIR_HT_COMMAND,
1557285940Sglebius		    ht->ht_msictrl, 2);
1558130613Smlaier	}
1559285940Sglebius
1560285940Sglebius	if (!addr && ht->ht_msictrl & PCIM_HTCMD_MSI_ENABLE) {
1561285940Sglebius		/* Disable MSI -> HT mapping. */
1562223637Sbz		ht->ht_msictrl &= ~PCIM_HTCMD_MSI_ENABLE;
1563285940Sglebius		pci_write_config(dev, ht->ht_msimap + PCIR_HT_COMMAND,
1564285940Sglebius		    ht->ht_msictrl, 2);
1565285940Sglebius	}
1566285940Sglebius}
1567130613Smlaier
1568130613Smlaier/*
1569285940Sglebius * Support for MSI message signalled interrupts.
1570285940Sglebius */
1571285940Sglebiusvoid
1572285940Sglebiuspci_enable_msi(device_t dev, uint64_t address, uint16_t data)
1573285940Sglebius{
1574285940Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1575130613Smlaier	struct pcicfg_msi *msi = &dinfo->cfg.msi;
1576130613Smlaier
1577130613Smlaier	/* Write data and address values. */
1578126258Smlaier	pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR,
1579240233Sglebius	    address & 0xffffffff, 4);
1580240233Sglebius	if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) {
1581240233Sglebius		pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR_HIGH,
1582240233Sglebius		    address >> 32, 4);
1583240233Sglebius		pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA_64BIT,
1584240233Sglebius		    data, 2);
1585240233Sglebius	} else
1586145836Smlaier		pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA, data,
1587240233Sglebius		    2);
1588223637Sbz
1589240233Sglebius	/* Enable MSI in the control register. */
1590240233Sglebius	msi->msi_ctrl |= PCIM_MSICTRL_MSI_ENABLE;
1591240233Sglebius	pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
1592240233Sglebius	    2);
1593240233Sglebius
1594240233Sglebius	/* Enable MSI -> HT mapping. */
1595240233Sglebius	pci_ht_map_msi(dev, address);
1596240233Sglebius}
1597240233Sglebius
1598240233Sglebiusvoid
1599240233Sglebiuspci_disable_msi(device_t dev)
1600240233Sglebius{
1601240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1602240233Sglebius	struct pcicfg_msi *msi = &dinfo->cfg.msi;
1603240233Sglebius
1604240233Sglebius	/* Disable MSI -> HT mapping. */
1605223637Sbz	pci_ht_map_msi(dev, 0);
1606240233Sglebius
1607240233Sglebius	/* Disable MSI in the control register. */
1608240233Sglebius	msi->msi_ctrl &= ~PCIM_MSICTRL_MSI_ENABLE;
1609240233Sglebius	pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
1610240233Sglebius	    2);
1611240233Sglebius}
1612240233Sglebius
1613171168Smlaier/*
1614240233Sglebius * Restore MSI registers during resume.  If MSI is enabled then
1615240233Sglebius * restore the data and address registers in addition to the control
1616240233Sglebius * register.
1617240233Sglebius */
1618223637Sbzstatic void
1619240233Sglebiuspci_resume_msi(device_t dev)
1620240233Sglebius{
1621263029Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1622243944Sglebius	struct pcicfg_msi *msi = &dinfo->cfg.msi;
1623243941Sglebius	uint64_t address;
1624243941Sglebius	uint16_t data;
1625243941Sglebius
1626243941Sglebius	if (msi->msi_ctrl & PCIM_MSICTRL_MSI_ENABLE) {
1627240233Sglebius		address = msi->msi_addr;
1628240233Sglebius		data = msi->msi_data;
1629240233Sglebius		pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR,
1630240233Sglebius		    address & 0xffffffff, 4);
1631171168Smlaier		if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) {
1632171168Smlaier			pci_write_config(dev, msi->msi_location +
1633171168Smlaier			    PCIR_MSI_ADDR_HIGH, address >> 32, 4);
1634171168Smlaier			pci_write_config(dev, msi->msi_location +
1635171168Smlaier			    PCIR_MSI_DATA_64BIT, data, 2);
1636223637Sbz		} else
1637240233Sglebius			pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA,
1638240233Sglebius			    data, 2);
1639240233Sglebius	}
1640243944Sglebius	pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
1641145836Smlaier	    2);
1642240233Sglebius}
1643270574Sglebius
1644145836Smlaierint
1645145836Smlaierpci_remap_msi_irq(device_t dev, u_int irq)
1646240233Sglebius{
1647240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(dev);
1648240233Sglebius	pcicfgregs *cfg = &dinfo->cfg;
1649241039Sglebius	struct resource_list_entry *rle;
1650241039Sglebius	struct msix_table_entry *mte;
1651130613Smlaier	struct msix_vector *mv;
1652240233Sglebius	device_t bus;
1653240233Sglebius	uint64_t addr;
1654240233Sglebius	uint32_t data;
1655240233Sglebius	int error, i, j;
1656240233Sglebius
1657240233Sglebius	bus = device_get_parent(dev);
1658240233Sglebius
1659240233Sglebius	/*
1660240233Sglebius	 * Handle MSI first.  We try to find this IRQ among our list
1661240233Sglebius	 * of MSI IRQs.  If we find it, we request updated address and
1662240233Sglebius	 * data registers and apply the results.
1663240233Sglebius	 */
1664240233Sglebius	if (cfg->msi.msi_alloc > 0) {
1665240233Sglebius
1666240233Sglebius		/* If we don't have any active handlers, nothing to do. */
1667240233Sglebius		if (cfg->msi.msi_handlers == 0)
1668240233Sglebius			return (0);
1669240233Sglebius		for (i = 0; i < cfg->msi.msi_alloc; i++) {
1670171168Smlaier			rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ,
1671240233Sglebius			    i + 1);
1672240233Sglebius			if (rle->start == irq) {
1673240233Sglebius				error = PCIB_MAP_MSI(device_get_parent(bus),
1674240233Sglebius				    dev, irq, &addr, &data);
1675240233Sglebius				if (error)
1676240233Sglebius					return (error);
1677240233Sglebius				pci_disable_msi(dev);
1678240233Sglebius				dinfo->cfg.msi.msi_addr = addr;
1679171168Smlaier				dinfo->cfg.msi.msi_data = data;
1680240233Sglebius				pci_enable_msi(dev, addr, data);
1681241039Sglebius				return (0);
1682241039Sglebius			}
1683273736Shselasky		}
1684241039Sglebius		return (ENOENT);
1685241039Sglebius	}
1686241039Sglebius
1687241039Sglebius	/*
1688240233Sglebius	 * For MSI-X, we check to see if we have this IRQ.  If we do,
1689126258Smlaier	 * we request the updated mapping info.  If that works, we go
1690171168Smlaier	 * through all the slots that use this IRQ and update them.
1691240233Sglebius	 */
1692196372Smlaier	if (cfg->msix.msix_alloc > 0) {
1693241039Sglebius		for (i = 0; i < cfg->msix.msix_alloc; i++) {
1694126258Smlaier			mv = &cfg->msix.msix_vectors[i];
1695126258Smlaier			if (mv->mv_irq == irq) {
1696240233Sglebius				error = PCIB_MAP_MSI(device_get_parent(bus),
1697240233Sglebius				    dev, irq, &addr, &data);
1698126258Smlaier				if (error)
1699240233Sglebius					return (error);
1700240233Sglebius				mv->mv_address = addr;
1701126258Smlaier				mv->mv_data = data;
1702240233Sglebius				for (j = 0; j < cfg->msix.msix_table_len; j++) {
1703240811Sglebius					mte = &cfg->msix.msix_table[j];
1704240811Sglebius					if (mte->mte_vector != i + 1)
1705240811Sglebius						continue;
1706240811Sglebius					if (mte->mte_handlers == 0)
1707240811Sglebius						continue;
1708240811Sglebius					pci_mask_msix(dev, j);
1709240811Sglebius					pci_enable_msix(dev, j, addr, data);
1710240811Sglebius					pci_unmask_msix(dev, j);
1711240811Sglebius				}
1712240811Sglebius			}
1713240811Sglebius		}
1714240811Sglebius		return (ENOENT);
1715240811Sglebius	}
1716240233Sglebius
1717240233Sglebius	return (ENOENT);
1718240233Sglebius}
1719240233Sglebius
1720240233Sglebius/*
1721240233Sglebius * Returns true if the specified device is blacklisted because MSI
1722240233Sglebius * doesn't work.
1723240233Sglebius */
1724240233Sglebiusint
1725240233Sglebiuspci_msi_device_blacklisted(device_t dev)
1726240233Sglebius{
1727240233Sglebius	struct pci_quirk *q;
1728240233Sglebius
1729240233Sglebius	if (!pci_honor_msi_blacklist)
1730240233Sglebius		return (0);
1731240233Sglebius
1732240233Sglebius	for (q = &pci_quirks[0]; q->devid; q++) {
1733126258Smlaier		if (q->devid == pci_get_devid(dev) &&
1734240233Sglebius		    q->type == PCI_QUIRK_DISABLE_MSI)
1735240233Sglebius			return (1);
1736240233Sglebius	}
1737240233Sglebius	return (0);
1738240233Sglebius}
1739240233Sglebius
1740240233Sglebius/*
1741240233Sglebius * Determine if MSI is blacklisted globally on this sytem.  Currently,
1742126258Smlaier * we just check for blacklisted chipsets as represented by the
1743126258Smlaier * host-PCI bridge at device 0:0:0.  In the future, it may become
1744126258Smlaier * necessary to check other system attributes, such as the kenv values
1745126258Smlaier * that give the motherboard manufacturer and model number.
1746126258Smlaier */
1747126258Smlaierstatic int
1748126258Smlaierpci_msi_blacklisted(void)
1749126258Smlaier{
1750126258Smlaier	device_t dev;
1751126258Smlaier
1752126258Smlaier	if (!pci_honor_msi_blacklist)
1753126258Smlaier		return (0);
1754126258Smlaier
1755126258Smlaier	/* Blacklist all non-PCI-express and non-PCI-X chipsets. */
1756126258Smlaier	if (!(pcie_chipset || pcix_chipset))
1757126258Smlaier		return (1);
1758126258Smlaier
1759126258Smlaier	dev = pci_find_bsf(0, 0, 0);
1760126258Smlaier	if (dev != NULL)
1761126258Smlaier		return (pci_msi_device_blacklisted(dev));
1762126258Smlaier	return (0);
1763223637Sbz}
1764223637Sbz
1765126258Smlaier/*
1766126258Smlaier * Attempt to allocate *count MSI messages.  The actual number allocated is
1767126258Smlaier * returned in *count.  After this function returns, each message will be
1768126258Smlaier * available to the driver as SYS_RES_IRQ resources starting at a rid 1.
1769223637Sbz */
1770126258Smlaierint
1771223637Sbzpci_alloc_msi_method(device_t dev, device_t child, int *count)
1772223637Sbz{
1773223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
1774223637Sbz	pcicfgregs *cfg = &dinfo->cfg;
1775126258Smlaier	struct resource_list_entry *rle;
1776223637Sbz	int actual, error, i, irqs[32];
1777126258Smlaier	uint16_t ctrl;
1778126258Smlaier
1779223637Sbz	/* Don't let count == 0 get us into trouble. */
1780223637Sbz	if (*count == 0)
1781223637Sbz		return (EINVAL);
1782223637Sbz
1783223637Sbz	/* If rid 0 is allocated, then fail. */
1784126258Smlaier	rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0);
1785126258Smlaier	if (rle != NULL && rle->res != NULL)
1786223637Sbz		return (ENXIO);
1787223637Sbz
1788223637Sbz	/* Already have allocated messages? */
1789223637Sbz	if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
1790126258Smlaier		return (ENXIO);
1791126258Smlaier
1792126258Smlaier	/* If MSI is blacklisted for this system, fail. */
1793126258Smlaier	if (pci_msi_blacklisted())
1794126258Smlaier		return (ENXIO);
1795126258Smlaier
1796126258Smlaier	/* MSI capability present? */
1797126258Smlaier	if (cfg->msi.msi_location == 0 || !pci_do_msi)
1798126258Smlaier		return (ENODEV);
1799126258Smlaier
1800126258Smlaier	if (bootverbose)
1801126258Smlaier		device_printf(child,
1802126258Smlaier		    "attempting to allocate %d MSI vectors (%d supported)\n",
1803126258Smlaier		    *count, cfg->msi.msi_msgnum);
1804126258Smlaier
1805126258Smlaier	/* Don't ask for more than the device supports. */
1806126258Smlaier	actual = min(*count, cfg->msi.msi_msgnum);
1807126258Smlaier
1808126258Smlaier	/* Don't ask for more than 32 messages. */
1809126258Smlaier	actual = min(actual, 32);
1810223637Sbz
1811223637Sbz	/* MSI requires power of 2 number of messages. */
1812223637Sbz	if (!powerof2(actual))
1813240233Sglebius		return (EINVAL);
1814223637Sbz
1815223637Sbz	for (;;) {
1816223637Sbz		/* Try to allocate N messages. */
1817223637Sbz		error = PCIB_ALLOC_MSI(device_get_parent(dev), child, actual,
1818223637Sbz		    cfg->msi.msi_msgnum, irqs);
1819223637Sbz		if (error == 0)
1820223637Sbz			break;
1821223637Sbz		if (actual == 1)
1822223637Sbz			return (error);
1823223637Sbz
1824223637Sbz		/* Try N / 2. */
1825223637Sbz		actual >>= 1;
1826223637Sbz	}
1827223637Sbz
1828223637Sbz	/*
1829223637Sbz	 * We now have N actual messages mapped onto SYS_RES_IRQ
1830223637Sbz	 * resources in the irqs[] array, so add new resources
1831223637Sbz	 * starting at rid 1.
1832223637Sbz	 */
1833126258Smlaier	for (i = 0; i < actual; i++)
1834223637Sbz		resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1,
1835126258Smlaier		    irqs[i], irqs[i], 1);
1836126258Smlaier
1837223637Sbz	if (bootverbose) {
1838126258Smlaier		if (actual == 1)
1839126258Smlaier			device_printf(child, "using IRQ %d for MSI\n", irqs[0]);
1840223637Sbz		else {
1841126258Smlaier			int run;
1842126258Smlaier
1843223637Sbz			/*
1844126258Smlaier			 * Be fancy and try to print contiguous runs
1845126258Smlaier			 * of IRQ values as ranges.  'run' is true if
1846223637Sbz			 * we are in a range.
1847126258Smlaier			 */
1848126258Smlaier			device_printf(child, "using IRQs %d", irqs[0]);
1849223637Sbz			run = 0;
1850223637Sbz			for (i = 1; i < actual; i++) {
1851223637Sbz
1852223637Sbz				/* Still in a run? */
1853223637Sbz				if (irqs[i] == irqs[i - 1] + 1) {
1854223637Sbz					run = 1;
1855223637Sbz					continue;
1856223637Sbz				}
1857223637Sbz
1858223637Sbz				/* Finish previous range. */
1859223637Sbz				if (run) {
1860223637Sbz					printf("-%d", irqs[i - 1]);
1861223637Sbz					run = 0;
1862223637Sbz				}
1863223637Sbz
1864223637Sbz				/* Start new range. */
1865223637Sbz				printf(",%d", irqs[i]);
1866223637Sbz			}
1867223637Sbz
1868223637Sbz			/* Unfinished range? */
1869223637Sbz			if (run)
1870223637Sbz				printf("-%d", irqs[actual - 1]);
1871223637Sbz			printf(" for MSI\n");
1872223637Sbz		}
1873223637Sbz	}
1874223637Sbz
1875223637Sbz	/* Update control register with actual count. */
1876223637Sbz	ctrl = cfg->msi.msi_ctrl;
1877223637Sbz	ctrl &= ~PCIM_MSICTRL_MME_MASK;
1878223637Sbz	ctrl |= (ffs(actual) - 1) << 4;
1879223637Sbz	cfg->msi.msi_ctrl = ctrl;
1880223637Sbz	pci_write_config(child, cfg->msi.msi_location + PCIR_MSI_CTRL, ctrl, 2);
1881223637Sbz
1882223637Sbz	/* Update counts of alloc'd messages. */
1883223637Sbz	cfg->msi.msi_alloc = actual;
1884223637Sbz	cfg->msi.msi_handlers = 0;
1885223637Sbz	*count = actual;
1886223637Sbz	return (0);
1887223637Sbz}
1888223637Sbz
1889223637Sbz/* Release the MSI messages associated with this device. */
1890223637Sbzint
1891126258Smlaierpci_release_msi_method(device_t dev, device_t child)
1892126258Smlaier{
1893126258Smlaier	struct pci_devinfo *dinfo = device_get_ivars(child);
1894126258Smlaier	struct pcicfg_msi *msi = &dinfo->cfg.msi;
1895126258Smlaier	struct resource_list_entry *rle;
1896126258Smlaier	int error, i, irqs[32];
1897126258Smlaier
1898126258Smlaier	/* Try MSI-X first. */
1899126258Smlaier	error = pci_release_msix(dev, child);
1900126258Smlaier	if (error != ENODEV)
1901126258Smlaier		return (error);
1902126258Smlaier
1903126258Smlaier	/* Do we have any messages to release? */
1904126258Smlaier	if (msi->msi_alloc == 0)
1905126258Smlaier		return (ENODEV);
1906126258Smlaier	KASSERT(msi->msi_alloc <= 32, ("more than 32 alloc'd messages"));
1907126258Smlaier
1908126258Smlaier	/* Make sure none of the resources are allocated. */
1909126258Smlaier	if (msi->msi_handlers > 0)
1910126258Smlaier		return (EBUSY);
1911126258Smlaier	for (i = 0; i < msi->msi_alloc; i++) {
1912126258Smlaier		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
1913126258Smlaier		KASSERT(rle != NULL, ("missing MSI resource"));
1914126258Smlaier		if (rle->res != NULL)
1915126258Smlaier			return (EBUSY);
1916126258Smlaier		irqs[i] = rle->start;
1917126258Smlaier	}
1918126258Smlaier
1919126258Smlaier	/* Update control register with 0 count. */
1920126258Smlaier	KASSERT(!(msi->msi_ctrl & PCIM_MSICTRL_MSI_ENABLE),
1921126258Smlaier	    ("%s: MSI still enabled", __func__));
1922126258Smlaier	msi->msi_ctrl &= ~PCIM_MSICTRL_MME_MASK;
1923126258Smlaier	pci_write_config(child, msi->msi_location + PCIR_MSI_CTRL,
1924126258Smlaier	    msi->msi_ctrl, 2);
1925126258Smlaier
1926126258Smlaier	/* Release the messages. */
1927126258Smlaier	PCIB_RELEASE_MSI(device_get_parent(dev), child, msi->msi_alloc, irqs);
1928126258Smlaier	for (i = 0; i < msi->msi_alloc; i++)
1929126258Smlaier		resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
1930126258Smlaier
1931126258Smlaier	/* Update alloc count. */
1932126258Smlaier	msi->msi_alloc = 0;
1933126258Smlaier	msi->msi_addr = 0;
1934126258Smlaier	msi->msi_data = 0;
1935126258Smlaier	return (0);
1936130613Smlaier}
1937126258Smlaier
1938126258Smlaier/*
1939126258Smlaier * Return the max supported MSI messages this device supports.
1940126258Smlaier * Basically, assuming the MD code can alloc messages, this function
1941126258Smlaier * should return the maximum value that pci_alloc_msi() can return.
1942126258Smlaier * Thus, it is subject to the tunables, etc.
1943126258Smlaier */
1944145836Smlaierint
1945126258Smlaierpci_msi_count_method(device_t dev, device_t child)
1946126258Smlaier{
1947126258Smlaier	struct pci_devinfo *dinfo = device_get_ivars(child);
1948126258Smlaier	struct pcicfg_msi *msi = &dinfo->cfg.msi;
1949126258Smlaier
1950126258Smlaier	if (pci_do_msi && msi->msi_location != 0)
1951145836Smlaier		return (msi->msi_msgnum);
1952126258Smlaier	return (0);
1953126258Smlaier}
1954126258Smlaier
1955126258Smlaier/* free pcicfgregs structure and all depending data structures */
1956126258Smlaier
1957126258Smlaierint
1958126258Smlaierpci_freecfg(struct pci_devinfo *dinfo)
1959126258Smlaier{
1960126258Smlaier	struct devlist *devlist_head;
1961126258Smlaier	int i;
1962126258Smlaier
1963126258Smlaier	devlist_head = &pci_devq;
1964126258Smlaier
1965126258Smlaier	if (dinfo->cfg.vpd.vpd_reg) {
1966240233Sglebius		free(dinfo->cfg.vpd.vpd_ident, M_DEVBUF);
1967126258Smlaier		for (i = 0; i < dinfo->cfg.vpd.vpd_rocnt; i++)
1968126258Smlaier			free(dinfo->cfg.vpd.vpd_ros[i].value, M_DEVBUF);
1969126258Smlaier		free(dinfo->cfg.vpd.vpd_ros, M_DEVBUF);
1970126258Smlaier		for (i = 0; i < dinfo->cfg.vpd.vpd_wcnt; i++)
1971126258Smlaier			free(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF);
1972126258Smlaier		free(dinfo->cfg.vpd.vpd_w, M_DEVBUF);
1973223637Sbz	}
1974126258Smlaier	STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
1975126258Smlaier	free(dinfo, M_DEVBUF);
1976126258Smlaier
1977126258Smlaier	/* increment the generation count */
1978126258Smlaier	pci_generation++;
1979126258Smlaier
1980130613Smlaier	/* we're losing one device */
1981126258Smlaier	pci_numdevs--;
1982171168Smlaier	return (0);
1983126258Smlaier}
1984126258Smlaier
1985126258Smlaier/*
1986126258Smlaier * PCI power manangement
1987126258Smlaier */
1988126258Smlaierint
1989126258Smlaierpci_set_powerstate_method(device_t dev, device_t child, int state)
1990126258Smlaier{
1991126258Smlaier	struct pci_devinfo *dinfo = device_get_ivars(child);
1992289703Skp	pcicfgregs *cfg = &dinfo->cfg;
1993289703Skp	uint16_t status;
1994289703Skp	int result, oldstate, highest, delay;
1995289703Skp
1996289703Skp	if (cfg->pp.pp_cap == 0)
1997289703Skp		return (EOPNOTSUPP);
1998289703Skp
1999289703Skp	/*
2000289703Skp	 * Optimize a no state change request away.  While it would be OK to
2001289703Skp	 * write to the hardware in theory, some devices have shown odd
2002289703Skp	 * behavior when going from D3 -> D3.
2003289703Skp	 */
2004289703Skp	oldstate = pci_get_powerstate(child);
2005289703Skp	if (oldstate == state)
2006289703Skp		return (0);
2007289703Skp
2008126258Smlaier	/*
2009126258Smlaier	 * The PCI power management specification states that after a state
2010126258Smlaier	 * transition between PCI power states, system software must
2011126258Smlaier	 * guarantee a minimal delay before the function accesses the device.
2012126258Smlaier	 * Compute the worst case delay that we need to guarantee before we
2013126258Smlaier	 * access the device.  Many devices will be responsive much more
2014126258Smlaier	 * quickly than this delay, but there are some that don't respond
2015126258Smlaier	 * instantly to state changes.  Transitions to/from D3 state require
2016126258Smlaier	 * 10ms, while D2 requires 200us, and D0/1 require none.  The delay
2017126258Smlaier	 * is done below with DELAY rather than a sleeper function because
2018126258Smlaier	 * this function can be called from contexts where we cannot sleep.
2019126258Smlaier	 */
2020126258Smlaier	highest = (oldstate > state) ? oldstate : state;
2021126258Smlaier	if (highest == PCI_POWERSTATE_D3)
2022126258Smlaier	    delay = 10000;
2023289703Skp	else if (highest == PCI_POWERSTATE_D2)
2024289703Skp	    delay = 200;
2025289703Skp	else
2026289703Skp	    delay = 0;
2027289703Skp	status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2)
2028289703Skp	    & ~PCIM_PSTAT_DMASK;
2029289703Skp	result = 0;
2030289703Skp	switch (state) {
2031289703Skp	case PCI_POWERSTATE_D0:
2032289703Skp		status |= PCIM_PSTAT_D0;
2033240233Sglebius		break;
2034289703Skp	case PCI_POWERSTATE_D1:
2035289703Skp		if ((cfg->pp.pp_cap & PCIM_PCAP_D1SUPP) == 0)
2036289703Skp			return (EOPNOTSUPP);
2037126258Smlaier		status |= PCIM_PSTAT_D1;
2038126258Smlaier		break;
2039126258Smlaier	case PCI_POWERSTATE_D2:
2040126258Smlaier		if ((cfg->pp.pp_cap & PCIM_PCAP_D2SUPP) == 0)
2041126258Smlaier			return (EOPNOTSUPP);
2042126258Smlaier		status |= PCIM_PSTAT_D2;
2043126258Smlaier		break;
2044289703Skp	case PCI_POWERSTATE_D3:
2045289703Skp		status |= PCIM_PSTAT_D3;
2046289703Skp		break;
2047126258Smlaier	default:
2048126258Smlaier		return (EINVAL);
2049126258Smlaier	}
2050126258Smlaier
2051126258Smlaier	if (bootverbose)
2052126258Smlaier		printf(
2053126258Smlaier		    "pci%d:%d:%d:%d: Transition from D%d to D%d\n",
2054126258Smlaier		    dinfo->cfg.domain, dinfo->cfg.bus, dinfo->cfg.slot,
2055126258Smlaier		    dinfo->cfg.func, oldstate, state);
2056289703Skp
2057289703Skp	PCI_WRITE_CONFIG(dev, child, cfg->pp.pp_status, status, 2);
2058126258Smlaier	if (delay)
2059289703Skp		DELAY(delay);
2060289703Skp	return (0);
2061289703Skp}
2062126258Smlaier
2063126258Smlaierint
2064126258Smlaierpci_get_powerstate_method(device_t dev, device_t child)
2065126258Smlaier{
2066126258Smlaier	struct pci_devinfo *dinfo = device_get_ivars(child);
2067126258Smlaier	pcicfgregs *cfg = &dinfo->cfg;
2068289703Skp	uint16_t status;
2069126258Smlaier	int result;
2070126258Smlaier
2071126258Smlaier	if (cfg->pp.pp_cap != 0) {
2072126258Smlaier		status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2);
2073126258Smlaier		switch (status & PCIM_PSTAT_DMASK) {
2074126258Smlaier		case PCIM_PSTAT_D0:
2075126258Smlaier			result = PCI_POWERSTATE_D0;
2076289703Skp			break;
2077289703Skp		case PCIM_PSTAT_D1:
2078289703Skp			result = PCI_POWERSTATE_D1;
2079126258Smlaier			break;
2080126258Smlaier		case PCIM_PSTAT_D2:
2081126258Smlaier			result = PCI_POWERSTATE_D2;
2082289703Skp			break;
2083289703Skp		case PCIM_PSTAT_D3:
2084289703Skp			result = PCI_POWERSTATE_D3;
2085289703Skp			break;
2086289703Skp		default:
2087289703Skp			result = PCI_POWERSTATE_UNKNOWN;
2088289703Skp			break;
2089126258Smlaier		}
2090126258Smlaier	} else {
2091126258Smlaier		/* No support, device is always at D0 */
2092126258Smlaier		result = PCI_POWERSTATE_D0;
2093126258Smlaier	}
2094126258Smlaier	return (result);
2095126258Smlaier}
2096126258Smlaier
2097126258Smlaier/*
2098126258Smlaier * Some convenience functions for PCI device drivers.
2099126258Smlaier */
2100126258Smlaier
2101126258Smlaierstatic __inline void
2102126258Smlaierpci_set_command_bit(device_t dev, device_t child, uint16_t bit)
2103289703Skp{
2104289703Skp	uint16_t	command;
2105289703Skp
2106289703Skp	command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2);
2107289703Skp	command |= bit;
2108289703Skp	PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2);
2109289703Skp}
2110289703Skp
2111289703Skpstatic __inline void
2112289703Skppci_clear_command_bit(device_t dev, device_t child, uint16_t bit)
2113289703Skp{
2114289703Skp	uint16_t	command;
2115289703Skp
2116126258Smlaier	command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2);
2117240233Sglebius	command &= ~bit;
2118126258Smlaier	PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2);
2119126258Smlaier}
2120126258Smlaier
2121126258Smlaierint
2122126258Smlaierpci_enable_busmaster_method(device_t dev, device_t child)
2123126258Smlaier{
2124126258Smlaier	pci_set_command_bit(dev, child, PCIM_CMD_BUSMASTEREN);
2125126258Smlaier	return (0);
2126126258Smlaier}
2127126258Smlaier
2128126258Smlaierint
2129126258Smlaierpci_disable_busmaster_method(device_t dev, device_t child)
2130126258Smlaier{
2131126258Smlaier	pci_clear_command_bit(dev, child, PCIM_CMD_BUSMASTEREN);
2132126258Smlaier	return (0);
2133126258Smlaier}
2134126258Smlaier
2135126258Smlaierint
2136126258Smlaierpci_enable_io_method(device_t dev, device_t child, int space)
2137126258Smlaier{
2138126258Smlaier	uint16_t command;
2139240233Sglebius	uint16_t bit;
2140126258Smlaier	char *error;
2141126258Smlaier
2142126258Smlaier	bit = 0;
2143126258Smlaier	error = NULL;
2144126258Smlaier
2145126258Smlaier	switch(space) {
2146126258Smlaier	case SYS_RES_IOPORT:
2147223637Sbz		bit = PCIM_CMD_PORTEN;
2148223637Sbz		error = "port";
2149126258Smlaier		break;
2150126258Smlaier	case SYS_RES_MEMORY:
2151126258Smlaier		bit = PCIM_CMD_MEMEN;
2152126258Smlaier		error = "memory";
2153223637Sbz		break;
2154126258Smlaier	default:
2155126258Smlaier		return (EINVAL);
2156126258Smlaier	}
2157126258Smlaier	pci_set_command_bit(dev, child, bit);
2158126258Smlaier	/* Some devices seem to need a brief stall here, what do to? */
2159126258Smlaier	command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2);
2160126258Smlaier	if (command & bit)
2161126258Smlaier		return (0);
2162126258Smlaier	device_printf(child, "failed to enable %s mapping!\n", error);
2163126258Smlaier	return (ENXIO);
2164126258Smlaier}
2165126258Smlaier
2166126258Smlaierint
2167126258Smlaierpci_disable_io_method(device_t dev, device_t child, int space)
2168126258Smlaier{
2169126258Smlaier	uint16_t command;
2170126258Smlaier	uint16_t bit;
2171126258Smlaier	char *error;
2172126258Smlaier
2173126258Smlaier	bit = 0;
2174126258Smlaier	error = NULL;
2175126258Smlaier
2176126258Smlaier	switch(space) {
2177126258Smlaier	case SYS_RES_IOPORT:
2178126258Smlaier		bit = PCIM_CMD_PORTEN;
2179126258Smlaier		error = "port";
2180126258Smlaier		break;
2181126258Smlaier	case SYS_RES_MEMORY:
2182126258Smlaier		bit = PCIM_CMD_MEMEN;
2183126258Smlaier		error = "memory";
2184126258Smlaier		break;
2185126258Smlaier	default:
2186126258Smlaier		return (EINVAL);
2187126258Smlaier	}
2188126258Smlaier	pci_clear_command_bit(dev, child, bit);
2189126258Smlaier	command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2);
2190126258Smlaier	if (command & bit) {
2191126258Smlaier		device_printf(child, "failed to disable %s mapping!\n", error);
2192126258Smlaier		return (ENXIO);
2193126258Smlaier	}
2194126258Smlaier	return (0);
2195126258Smlaier}
2196126258Smlaier
2197223637Sbz/*
2198223637Sbz * New style pci driver.  Parent device is either a pci-host-bridge or a
2199223637Sbz * pci-pci-bridge.  Both kinds are represented by instances of pcib.
2200223637Sbz */
2201126258Smlaier
2202223637Sbzvoid
2203223637Sbzpci_print_verbose(struct pci_devinfo *dinfo)
2204223637Sbz{
2205223637Sbz
2206223637Sbz	if (bootverbose) {
2207126258Smlaier		pcicfgregs *cfg = &dinfo->cfg;
2208126258Smlaier
2209223637Sbz		printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n",
2210223637Sbz		    cfg->vendor, cfg->device, cfg->revid);
2211223637Sbz		printf("\tdomain=%d, bus=%d, slot=%d, func=%d\n",
2212223637Sbz		    cfg->domain, cfg->bus, cfg->slot, cfg->func);
2213223637Sbz		printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n",
2214223637Sbz		    cfg->baseclass, cfg->subclass, cfg->progif, cfg->hdrtype,
2215223637Sbz		    cfg->mfdev);
2216223637Sbz		printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n",
2217223637Sbz		    cfg->cmdreg, cfg->statreg, cfg->cachelnsz);
2218223637Sbz		printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n",
2219223637Sbz		    cfg->lattimer, cfg->lattimer * 30, cfg->mingnt,
2220223637Sbz		    cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250);
2221223637Sbz		if (cfg->intpin > 0)
2222126258Smlaier			printf("\tintpin=%c, irq=%d\n",
2223223637Sbz			    cfg->intpin +'a' -1, cfg->intline);
2224126258Smlaier		if (cfg->pp.pp_cap) {
2225126258Smlaier			uint16_t status;
2226126258Smlaier
2227171168Smlaier			status = pci_read_config(cfg->dev, cfg->pp.pp_status, 2);
2228171168Smlaier			printf("\tpowerspec %d  supports D0%s%s D3  current D%d\n",
2229171168Smlaier			    cfg->pp.pp_cap & PCIM_PCAP_SPEC,
2230171168Smlaier			    cfg->pp.pp_cap & PCIM_PCAP_D1SUPP ? " D1" : "",
2231171168Smlaier			    cfg->pp.pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "",
2232240233Sglebius			    status & PCIM_PSTAT_DMASK);
2233171168Smlaier		}
2234171168Smlaier		if (cfg->msi.msi_location) {
2235171168Smlaier			int ctrl;
2236171168Smlaier
2237171168Smlaier			ctrl = cfg->msi.msi_ctrl;
2238171168Smlaier			printf("\tMSI supports %d message%s%s%s\n",
2239171168Smlaier			    cfg->msi.msi_msgnum,
2240171168Smlaier			    (cfg->msi.msi_msgnum == 1) ? "" : "s",
2241223637Sbz			    (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "",
2242171168Smlaier			    (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks":"");
2243171168Smlaier		}
2244171168Smlaier		if (cfg->msix.msix_location) {
2245171168Smlaier			printf("\tMSI-X supports %d message%s ",
2246171168Smlaier			    cfg->msix.msix_msgnum,
2247171168Smlaier			    (cfg->msix.msix_msgnum == 1) ? "" : "s");
2248171168Smlaier			if (cfg->msix.msix_table_bar == cfg->msix.msix_pba_bar)
2249171168Smlaier				printf("in map 0x%x\n",
2250171168Smlaier				    cfg->msix.msix_table_bar);
2251171168Smlaier			else
2252171168Smlaier				printf("in maps 0x%x and 0x%x\n",
2253171168Smlaier				    cfg->msix.msix_table_bar,
2254171168Smlaier				    cfg->msix.msix_pba_bar);
2255171168Smlaier		}
2256171168Smlaier	}
2257171168Smlaier}
2258171168Smlaier
2259171168Smlaierstatic int
2260171168Smlaierpci_porten(device_t dev)
2261289703Skp{
2262289703Skp	return (pci_read_config(dev, PCIR_COMMAND, 2) & PCIM_CMD_PORTEN) != 0;
2263289703Skp}
2264289703Skp
2265171168Smlaierstatic int
2266171168Smlaierpci_memen(device_t dev)
2267171168Smlaier{
2268171168Smlaier	return (pci_read_config(dev, PCIR_COMMAND, 2) & PCIM_CMD_MEMEN) != 0;
2269171168Smlaier}
2270171168Smlaier
2271171168Smlaierstatic void
2272171168Smlaierpci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp)
2273171168Smlaier{
2274171168Smlaier	pci_addr_t map, testval;
2275171168Smlaier	int ln2range;
2276171168Smlaier	uint16_t cmd;
2277171168Smlaier
2278171168Smlaier	map = pci_read_config(dev, reg, 4);
2279171168Smlaier	ln2range = pci_maprange(map);
2280171168Smlaier	if (ln2range == 64)
2281171168Smlaier		map |= (pci_addr_t)pci_read_config(dev, reg + 4, 4) << 32;
2282171168Smlaier
2283240233Sglebius	/*
2284162238Scsjp	 * Disable decoding via the command register before
2285126258Smlaier	 * determining the BAR's length since we will be placing it in
2286126258Smlaier	 * a weird state.
2287145836Smlaier	 */
2288240233Sglebius	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2289126258Smlaier	pci_write_config(dev, PCIR_COMMAND,
2290240233Sglebius	    cmd & ~(PCI_BAR_MEM(map) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 2);
2291126258Smlaier
2292171168Smlaier	/*
2293126258Smlaier	 * Determine the BAR's length by writing all 1's.  The bottom
2294240233Sglebius	 * log_2(size) bits of the BAR will stick as 0 when we read
2295126258Smlaier	 * the value back.
2296126258Smlaier	 */
2297240233Sglebius	pci_write_config(dev, reg, 0xffffffff, 4);
2298126258Smlaier	testval = pci_read_config(dev, reg, 4);
2299171168Smlaier	if (ln2range == 64) {
2300171168Smlaier		pci_write_config(dev, reg + 4, 0xffffffff, 4);
2301223637Sbz		testval |= (pci_addr_t)pci_read_config(dev, reg + 4, 4) << 32;
2302126258Smlaier	}
2303171168Smlaier
2304171168Smlaier	/*
2305171168Smlaier	 * Restore the original value of the BAR.  We may have reprogrammed
2306126258Smlaier	 * the BAR of the low-level console device and when booting verbose,
2307126258Smlaier	 * we need the console device addressable.
2308126258Smlaier	 */
2309126258Smlaier	pci_write_config(dev, reg, map, 4);
2310126258Smlaier	if (ln2range == 64)
2311126258Smlaier		pci_write_config(dev, reg + 4, map >> 32, 4);
2312126258Smlaier	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2313126258Smlaier
2314126258Smlaier	*mapp = map;
2315126258Smlaier	*testvalp = testval;
2316126258Smlaier}
2317126258Smlaier
2318126258Smlaierstatic void
2319126258Smlaierpci_write_bar(device_t dev, int reg, pci_addr_t base)
2320126258Smlaier{
2321126258Smlaier	pci_addr_t map;
2322240233Sglebius	int ln2range;
2323240233Sglebius
2324126258Smlaier	map = pci_read_config(dev, reg, 4);
2325126258Smlaier	ln2range = pci_maprange(map);
2326240233Sglebius	pci_write_config(dev, reg, base, 4);
2327240233Sglebius	if (ln2range == 64)
2328240233Sglebius		pci_write_config(dev, reg + 4, base >> 32, 4);
2329132280Smlaier}
2330248324Sglebius
2331240233Sglebius/*
2332240233Sglebius * Add a resource based on a pci map register. Return 1 if the map
2333240233Sglebius * register is a 32bit map register or 2 if it is a 64bit register.
2334240233Sglebius */
2335162238Scsjpstatic int
2336223637Sbzpci_add_map(device_t bus, device_t dev, int reg, struct resource_list *rl,
2337162238Scsjp    int force, int prefetch)
2338171168Smlaier{
2339240233Sglebius	pci_addr_t base, map, testval;
2340171168Smlaier	pci_addr_t start, end, count;
2341171168Smlaier	int barlen, maprange, mapsize, type;
2342171168Smlaier	uint16_t cmd;
2343171168Smlaier	struct resource *res;
2344145836Smlaier
2345223637Sbz	pci_read_bar(dev, reg, &map, &testval);
2346145836Smlaier	if (PCI_BAR_MEM(map)) {
2347171168Smlaier		type = SYS_RES_MEMORY;
2348178888Sjulian		if (map & PCIM_BAR_MEM_PREFETCH)
2349223637Sbz			prefetch = 1;
2350126258Smlaier	} else
2351126258Smlaier		type = SYS_RES_IOPORT;
2352171168Smlaier	mapsize = pci_mapsize(testval);
2353223637Sbz	base = pci_mapbase(map);
2354171168Smlaier	maprange = pci_maprange(map);
2355171168Smlaier	barlen = maprange == 64 ? 2 : 1;
2356126258Smlaier
2357145836Smlaier	/*
2358126258Smlaier	 * For I/O registers, if bottom bit is set, and the next bit up
2359126258Smlaier	 * isn't clear, we know we have a BAR that doesn't conform to the
2360126258Smlaier	 * spec, so ignore it.  Also, sanity check the size of the data
2361126258Smlaier	 * areas to the type of memory involved.  Memory must be at least
2362126258Smlaier	 * 16 bytes in size, while I/O ranges must be at least 4.
2363126258Smlaier	 */
2364126258Smlaier	if (PCI_BAR_IO(testval) && (testval & PCIM_BAR_IO_RESERVED) != 0)
2365126258Smlaier		return (barlen);
2366126258Smlaier	if ((type == SYS_RES_MEMORY && mapsize < 4) ||
2367126258Smlaier	    (type == SYS_RES_IOPORT && mapsize < 2))
2368126258Smlaier		return (barlen);
2369126258Smlaier
2370126258Smlaier	if (bootverbose) {
2371126258Smlaier		printf("\tmap[%02x]: type %s, range %2d, base %#jx, size %2d",
2372126258Smlaier		    reg, pci_maptype(map), maprange, (uintmax_t)base, mapsize);
2373126258Smlaier		if (type == SYS_RES_IOPORT && !pci_porten(dev))
2374126258Smlaier			printf(", port disabled\n");
2375126258Smlaier		else if (type == SYS_RES_MEMORY && !pci_memen(dev))
2376126258Smlaier			printf(", memory disabled\n");
2377126258Smlaier		else
2378126258Smlaier			printf(", enabled\n");
2379126258Smlaier	}
2380126258Smlaier
2381126258Smlaier	/*
2382126258Smlaier	 * If base is 0, then we have problems.  It is best to ignore
2383126258Smlaier	 * such entries for the moment.  These will be allocated later if
2384126258Smlaier	 * the driver specifically requests them.  However, some
2385126258Smlaier	 * removable busses look better when all resources are allocated,
2386126258Smlaier	 * so allow '0' to be overriden.
2387126258Smlaier	 *
2388126258Smlaier	 * Similarly treat maps whose values is the same as the test value
2389126258Smlaier	 * read back.  These maps have had all f's written to them by the
2390126258Smlaier	 * BIOS in an attempt to disable the resources.
2391126258Smlaier	 */
2392126258Smlaier	if (!force && (base == 0 || map == testval))
2393126258Smlaier		return (barlen);
2394126258Smlaier	if ((u_long)base != base) {
2395126258Smlaier		device_printf(bus,
2396126258Smlaier		    "pci%d:%d:%d:%d bar %#x too many address bits",
2397126258Smlaier		    pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
2398126258Smlaier		    pci_get_function(dev), reg);
2399126258Smlaier		return (barlen);
2400126258Smlaier	}
2401126258Smlaier
2402126258Smlaier	/*
2403126258Smlaier	 * This code theoretically does the right thing, but has
2404126258Smlaier	 * undesirable side effects in some cases where peripherals
2405126258Smlaier	 * respond oddly to having these bits enabled.  Let the user
2406126258Smlaier	 * be able to turn them off (since pci_enable_io_modes is 1 by
2407126258Smlaier	 * default).
2408126258Smlaier	 */
2409126258Smlaier	if (pci_enable_io_modes) {
2410126258Smlaier		/* Turn on resources that have been left off by a lazy BIOS */
2411126258Smlaier		if (type == SYS_RES_IOPORT && !pci_porten(dev)) {
2412126258Smlaier			cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2413126258Smlaier			cmd |= PCIM_CMD_PORTEN;
2414126258Smlaier			pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2415126258Smlaier		}
2416126258Smlaier		if (type == SYS_RES_MEMORY && !pci_memen(dev)) {
2417126258Smlaier			cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2418241913Sglebius			cmd |= PCIM_CMD_MEMEN;
2419241913Sglebius			pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2420223637Sbz		}
2421126258Smlaier	} else {
2422145836Smlaier		if (type == SYS_RES_IOPORT && !pci_porten(dev))
2423240233Sglebius			return (barlen);
2424126258Smlaier		if (type == SYS_RES_MEMORY && !pci_memen(dev))
2425126258Smlaier			return (barlen);
2426126258Smlaier	}
2427126258Smlaier
2428126258Smlaier	count = 1 << mapsize;
2429126258Smlaier	if (base == 0 || base == pci_mapbase(testval)) {
2430126258Smlaier		start = 0;	/* Let the parent decide. */
2431126258Smlaier		end = ~0ULL;
2432126258Smlaier	} else {
2433126258Smlaier		start = base;
2434126258Smlaier		end = base + (1 << mapsize) - 1;
2435240233Sglebius	}
2436126258Smlaier	resource_list_add(rl, type, reg, start, end, count);
2437126258Smlaier
2438126258Smlaier	/*
2439240233Sglebius	 * Try to allocate the resource for this BAR from our parent
2440240233Sglebius	 * so that this resource range is already reserved.  The
2441126258Smlaier	 * driver for this device will later inherit this resource in
2442126258Smlaier	 * pci_alloc_resource().
2443223637Sbz	 */
2444126258Smlaier	res = resource_list_alloc(rl, bus, dev, type, &reg, start, end, count,
2445126258Smlaier	    prefetch ? RF_PREFETCHABLE : 0);
2446126258Smlaier	if (res == NULL) {
2447240233Sglebius		/*
2448240233Sglebius		 * If the allocation fails, clear the BAR and delete
2449223637Sbz		 * the resource list entry to force
2450126258Smlaier		 * pci_alloc_resource() to allocate resources from the
2451240233Sglebius		 * parent.
2452240233Sglebius		 */
2453240233Sglebius		resource_list_delete(rl, type, reg);
2454132280Smlaier		start = 0;
2455240233Sglebius	} else {
2456240233Sglebius		start = rman_get_start(res);
2457240233Sglebius		rman_set_device(res, bus);
2458223637Sbz	}
2459240233Sglebius	pci_write_bar(dev, reg, start);
2460223637Sbz	return (barlen);
2461240233Sglebius}
2462240233Sglebius
2463126258Smlaier/*
2464240233Sglebius * For ATA devices we need to decide early what addressing mode to use.
2465171168Smlaier * Legacy demands that the primary and secondary ATA ports sits on the
2466171168Smlaier * same addresses that old ISA hardware did. This dictates that we use
2467126258Smlaier * those addresses and ignore the BAR's if we cannot set PCI native
2468171168Smlaier * addressing mode.
2469178888Sjulian */
2470171168Smlaierstatic void
2471126258Smlaierpci_ata_maps(device_t bus, device_t dev, struct resource_list *rl, int force,
2472126258Smlaier    uint32_t prefetchmask)
2473171168Smlaier{
2474171168Smlaier	struct resource *r;
2475171168Smlaier	int rid, type, progif;
2476126258Smlaier#if 0
2477145836Smlaier	/* if this device supports PCI native addressing use it */
2478126258Smlaier	progif = pci_read_config(dev, PCIR_PROGIF, 1);
2479126258Smlaier	if ((progif & 0x8a) == 0x8a) {
2480126258Smlaier		if (pci_mapbase(pci_read_config(dev, PCIR_BAR(0), 4)) &&
2481126258Smlaier		    pci_mapbase(pci_read_config(dev, PCIR_BAR(2), 4))) {
2482240233Sglebius			printf("Trying ATA native PCI addressing mode\n");
2483126258Smlaier			pci_write_config(dev, PCIR_PROGIF, progif | 0x05, 1);
2484126258Smlaier		}
2485126258Smlaier	}
2486126258Smlaier#endif
2487240233Sglebius	progif = pci_read_config(dev, PCIR_PROGIF, 1);
2488126258Smlaier	type = SYS_RES_IOPORT;
2489126258Smlaier	if (progif & PCIP_STORAGE_IDE_MODEPRIM) {
2490126258Smlaier		pci_add_map(bus, dev, PCIR_BAR(0), rl, force,
2491240233Sglebius		    prefetchmask & (1 << 0));
2492240233Sglebius		pci_add_map(bus, dev, PCIR_BAR(1), rl, force,
2493240233Sglebius		    prefetchmask & (1 << 1));
2494240233Sglebius	} else {
2495126258Smlaier		rid = PCIR_BAR(0);
2496126258Smlaier		resource_list_add(rl, type, rid, 0x1f0, 0x1f7, 8);
2497126258Smlaier		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x1f0, 0x1f7,
2498126258Smlaier		    8, 0);
2499126258Smlaier		rman_set_device(r, bus);
2500126258Smlaier		rid = PCIR_BAR(1);
2501126258Smlaier		resource_list_add(rl, type, rid, 0x3f6, 0x3f6, 1);
2502126258Smlaier		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x3f6, 0x3f6,
2503126258Smlaier		    1, 0);
2504126258Smlaier		rman_set_device(r, bus);
2505126258Smlaier	}
2506126258Smlaier	if (progif & PCIP_STORAGE_IDE_MODESEC) {
2507126258Smlaier		pci_add_map(bus, dev, PCIR_BAR(2), rl, force,
2508126258Smlaier		    prefetchmask & (1 << 2));
2509126258Smlaier		pci_add_map(bus, dev, PCIR_BAR(3), rl, force,
2510126258Smlaier		    prefetchmask & (1 << 3));
2511126258Smlaier	} else {
2512126258Smlaier		rid = PCIR_BAR(2);
2513126258Smlaier		resource_list_add(rl, type, rid, 0x170, 0x177, 8);
2514126258Smlaier		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x170, 0x177,
2515126258Smlaier		    8, 0);
2516126258Smlaier		rman_set_device(r, bus);
2517126258Smlaier		rid = PCIR_BAR(3);
2518126258Smlaier		resource_list_add(rl, type, rid, 0x376, 0x376, 1);
2519126258Smlaier		r = resource_list_alloc(rl, bus, dev, type, &rid, 0x376, 0x376,
2520126258Smlaier		    1, 0);
2521126258Smlaier		rman_set_device(r, bus);
2522126258Smlaier	}
2523126258Smlaier	pci_add_map(bus, dev, PCIR_BAR(4), rl, force,
2524126258Smlaier	    prefetchmask & (1 << 4));
2525126258Smlaier	pci_add_map(bus, dev, PCIR_BAR(5), rl, force,
2526126258Smlaier	    prefetchmask & (1 << 5));
2527126258Smlaier}
2528126258Smlaier
2529126258Smlaierstatic void
2530126258Smlaierpci_assign_interrupt(device_t bus, device_t dev, int force_route)
2531126258Smlaier{
2532126258Smlaier	struct pci_devinfo *dinfo = device_get_ivars(dev);
2533126258Smlaier	pcicfgregs *cfg = &dinfo->cfg;
2534126258Smlaier	char tunable_name[64];
2535126258Smlaier	int irq;
2536126258Smlaier
2537126258Smlaier	/* Has to have an intpin to have an interrupt. */
2538126258Smlaier	if (cfg->intpin == 0)
2539126258Smlaier		return;
2540126258Smlaier
2541126258Smlaier	/* Let the user override the IRQ with a tunable. */
2542126258Smlaier	irq = PCI_INVALID_IRQ;
2543223637Sbz	snprintf(tunable_name, sizeof(tunable_name),
2544223637Sbz	    "hw.pci%d.%d.%d.INT%c.irq",
2545223637Sbz	    cfg->domain, cfg->bus, cfg->slot, cfg->intpin + 'A' - 1);
2546126258Smlaier	if (TUNABLE_INT_FETCH(tunable_name, &irq) && (irq >= 255 || irq <= 0))
2547223637Sbz		irq = PCI_INVALID_IRQ;
2548223637Sbz
2549223637Sbz	/*
2550223637Sbz	 * If we didn't get an IRQ via the tunable, then we either use the
2551223637Sbz	 * IRQ value in the intline register or we ask the bus to route an
2552223637Sbz	 * interrupt for us.  If force_route is true, then we only use the
2553223637Sbz	 * value in the intline register if the bus was unable to assign an
2554223637Sbz	 * IRQ.
2555223637Sbz	 */
2556223637Sbz	if (!PCI_INTERRUPT_VALID(irq)) {
2557223637Sbz		if (!PCI_INTERRUPT_VALID(cfg->intline) || force_route)
2558223637Sbz			irq = PCI_ASSIGN_INTERRUPT(bus, dev);
2559223637Sbz		if (!PCI_INTERRUPT_VALID(irq))
2560223637Sbz			irq = cfg->intline;
2561223637Sbz	}
2562223637Sbz
2563223637Sbz	/* If after all that we don't have an IRQ, just bail. */
2564223637Sbz	if (!PCI_INTERRUPT_VALID(irq))
2565223637Sbz		return;
2566223637Sbz
2567223637Sbz	/* Update the config register if it changed. */
2568223637Sbz	if (irq != cfg->intline) {
2569223637Sbz		cfg->intline = irq;
2570223637Sbz		pci_write_config(dev, PCIR_INTLINE, irq, 1);
2571223637Sbz	}
2572223637Sbz
2573223637Sbz	/* Add this IRQ as rid 0 interrupt resource. */
2574223637Sbz	resource_list_add(&dinfo->resources, SYS_RES_IRQ, 0, irq, irq, 1);
2575223637Sbz}
2576223637Sbz
2577223637Sbzvoid
2578223637Sbzpci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
2579223637Sbz{
2580223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(dev);
2581240233Sglebius	pcicfgregs *cfg = &dinfo->cfg;
2582126258Smlaier	struct resource_list *rl = &dinfo->resources;
2583126258Smlaier	struct pci_quirk *q;
2584126258Smlaier	int i;
2585126258Smlaier
2586126258Smlaier	/* ATA devices needs special map treatment */
2587126258Smlaier	if ((pci_get_class(dev) == PCIC_STORAGE) &&
2588126258Smlaier	    (pci_get_subclass(dev) == PCIS_STORAGE_IDE) &&
2589126258Smlaier	    ((pci_get_progif(dev) & PCIP_STORAGE_IDE_MASTERDEV) ||
2590126258Smlaier	     (!pci_read_config(dev, PCIR_BAR(0), 4) &&
2591126258Smlaier	      !pci_read_config(dev, PCIR_BAR(2), 4))) )
2592126258Smlaier		pci_ata_maps(bus, dev, rl, force, prefetchmask);
2593126258Smlaier	else
2594126258Smlaier		for (i = 0; i < cfg->nummaps;)
2595126258Smlaier			i += pci_add_map(bus, dev, PCIR_BAR(i), rl, force,
2596126258Smlaier			    prefetchmask & (1 << i));
2597126258Smlaier
2598126258Smlaier	/*
2599126258Smlaier	 * Add additional, quirked resources.
2600126258Smlaier	 */
2601126258Smlaier	for (q = &pci_quirks[0]; q->devid; q++) {
2602126258Smlaier		if (q->devid == ((cfg->device << 16) | cfg->vendor)
2603126258Smlaier		    && q->type == PCI_QUIRK_MAP_REG)
2604126258Smlaier			pci_add_map(bus, dev, q->arg1, rl, force, 0);
2605126258Smlaier	}
2606126258Smlaier
2607126258Smlaier	if (cfg->intpin > 0 && PCI_INTERRUPT_VALID(cfg->intline)) {
2608126258Smlaier#ifdef __PCI_REROUTE_INTERRUPT
2609126258Smlaier		/*
2610126258Smlaier		 * Try to re-route interrupts. Sometimes the BIOS or
2611126258Smlaier		 * firmware may leave bogus values in these registers.
2612126258Smlaier		 * If the re-route fails, then just stick with what we
2613126258Smlaier		 * have.
2614126258Smlaier		 */
2615126258Smlaier		pci_assign_interrupt(bus, dev, 1);
2616240233Sglebius#else
2617126258Smlaier		pci_assign_interrupt(bus, dev, 0);
2618126258Smlaier#endif
2619126258Smlaier	}
2620126258Smlaier}
2621126258Smlaier
2622126258Smlaiervoid
2623126258Smlaierpci_add_children(device_t dev, int domain, int busno, size_t dinfo_size)
2624240233Sglebius{
2625126258Smlaier#define	REG(n, w)	PCIB_READ_CONFIG(pcib, busno, s, f, n, w)
2626126258Smlaier	device_t pcib = device_get_parent(dev);
2627126258Smlaier	struct pci_devinfo *dinfo;
2628126258Smlaier	int maxslots;
2629126258Smlaier	int s, f, pcifunchigh;
2630126258Smlaier	uint8_t hdrtype;
2631126258Smlaier
2632223637Sbz	KASSERT(dinfo_size >= sizeof(struct pci_devinfo),
2633240233Sglebius	    ("dinfo_size too small"));
2634171168Smlaier	maxslots = PCIB_MAXSLOTS(pcib);
2635171168Smlaier	for (s = 0; s <= maxslots; s++) {
2636240233Sglebius		pcifunchigh = 0;
2637171168Smlaier		f = 0;
2638126258Smlaier		DELAY(1);
2639126258Smlaier		hdrtype = REG(PCIR_HDRTYPE, 1);
2640126258Smlaier		if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
2641126258Smlaier			continue;
2642126258Smlaier		if (hdrtype & PCIM_MFDEV)
2643240233Sglebius			pcifunchigh = PCI_FUNCMAX;
2644126258Smlaier		for (f = 0; f <= pcifunchigh; f++) {
2645126258Smlaier			dinfo = pci_read_device(pcib, domain, busno, s, f,
2646240233Sglebius			    dinfo_size);
2647126258Smlaier			if (dinfo != NULL) {
2648240233Sglebius				pci_add_child(dev, dinfo);
2649240233Sglebius			}
2650240233Sglebius		}
2651240233Sglebius	}
2652240233Sglebius#undef REG
2653126258Smlaier}
2654126258Smlaier
2655126258Smlaiervoid
2656240641Sglebiuspci_add_child(device_t bus, struct pci_devinfo *dinfo)
2657240641Sglebius{
2658240641Sglebius	dinfo->cfg.dev = device_add_child(bus, NULL, -1);
2659240641Sglebius	device_set_ivars(dinfo->cfg.dev, dinfo);
2660240641Sglebius	resource_list_init(&dinfo->resources);
2661240641Sglebius	pci_cfg_save(dinfo->cfg.dev, dinfo, 0);
2662240641Sglebius	pci_cfg_restore(dinfo->cfg.dev, dinfo);
2663240641Sglebius	pci_print_verbose(dinfo);
2664240641Sglebius	pci_add_resources(bus, dinfo->cfg.dev, 0, 0);
2665240641Sglebius}
2666240641Sglebius
2667240641Sglebiusstatic int
2668240641Sglebiuspci_probe(device_t dev)
2669240641Sglebius{
2670240641Sglebius
2671240641Sglebius	device_set_desc(dev, "PCI bus");
2672240641Sglebius
2673240641Sglebius	/* Allow other subclasses to override this driver. */
2674240641Sglebius	return (BUS_PROBE_GENERIC);
2675240641Sglebius}
2676223637Sbz
2677240641Sglebiusstatic int
2678240641Sglebiuspci_attach(device_t dev)
2679240641Sglebius{
2680145836Smlaier	int busno, domain;
2681145836Smlaier
2682126258Smlaier	/*
2683240233Sglebius	 * Since there can be multiple independantly numbered PCI
2684240233Sglebius	 * busses on systems with multiple PCI domains, we can't use
2685171168Smlaier	 * the unit number to decide which bus we are probing. We ask
2686171168Smlaier	 * the parent pcib what our domain and bus numbers are.
2687240641Sglebius	 */
2688240641Sglebius	domain = pcib_get_domain(dev);
2689240641Sglebius	busno = pcib_get_bus(dev);
2690145836Smlaier	if (bootverbose)
2691145836Smlaier		device_printf(dev, "domain=%d, physical bus=%d\n",
2692145836Smlaier		    domain, busno);
2693145836Smlaier	pci_add_children(dev, domain, busno, sizeof(struct pci_devinfo));
2694240641Sglebius	return (bus_generic_attach(dev));
2695145836Smlaier}
2696145836Smlaier
2697145836Smlaierint
2698240641Sglebiuspci_suspend(device_t dev)
2699240641Sglebius{
2700240641Sglebius	int dstate, error, i, numdevs;
2701145836Smlaier	device_t acpi_dev, child, *devlist;
2702145836Smlaier	struct pci_devinfo *dinfo;
2703145836Smlaier
2704145836Smlaier	/*
2705145836Smlaier	 * Save the PCI configuration space for each child and set the
2706145836Smlaier	 * device in the appropriate power state for this sleep state.
2707145836Smlaier	 */
2708145836Smlaier	acpi_dev = NULL;
2709145836Smlaier	if (pci_do_power_resume)
2710145836Smlaier		acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
2711126258Smlaier	if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
2712171168Smlaier		return (error);
2713240641Sglebius	for (i = 0; i < numdevs; i++) {
2714240641Sglebius		child = devlist[i];
2715240641Sglebius		dinfo = (struct pci_devinfo *) device_get_ivars(child);
2716145836Smlaier		pci_cfg_save(child, dinfo, 0);
2717145836Smlaier	}
2718240641Sglebius
2719171168Smlaier	/* Suspend devices before potentially powering them down. */
2720145836Smlaier	error = bus_generic_suspend(dev);
2721240233Sglebius	if (error) {
2722240233Sglebius		free(devlist, M_TEMP);
2723145836Smlaier		return (error);
2724145836Smlaier	}
2725145836Smlaier
2726240641Sglebius	/*
2727240641Sglebius	 * Always set the device to D3.  If ACPI suggests a different
2728240641Sglebius	 * power state, use it instead.  If ACPI is not present, the
2729240641Sglebius	 * firmware is responsible for managing device power.  Skip
2730240641Sglebius	 * children who aren't attached since they are powered down
2731240641Sglebius	 * separately.  Only manage type 0 devices for now.
2732240641Sglebius	 */
2733240641Sglebius	for (i = 0; acpi_dev && i < numdevs; i++) {
2734240641Sglebius		child = devlist[i];
2735240641Sglebius		dinfo = (struct pci_devinfo *) device_get_ivars(child);
2736240641Sglebius		if (device_is_attached(child) && dinfo->cfg.hdrtype == 0) {
2737240641Sglebius			dstate = PCI_POWERSTATE_D3;
2738240641Sglebius			ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate);
2739240641Sglebius			pci_set_powerstate(child, dstate);
2740240641Sglebius		}
2741240641Sglebius	}
2742240641Sglebius	free(devlist, M_TEMP);
2743171168Smlaier	return (0);
2744171168Smlaier}
2745240641Sglebius
2746145836Smlaierint
2747145836Smlaierpci_resume(device_t dev)
2748145836Smlaier{
2749145836Smlaier	int i, numdevs, error;
2750145836Smlaier	device_t acpi_dev, child, *devlist;
2751145836Smlaier	struct pci_devinfo *dinfo;
2752145836Smlaier
2753145836Smlaier	/*
2754145836Smlaier	 * Set each child to D0 and restore its PCI configuration space.
2755145836Smlaier	 */
2756145836Smlaier	acpi_dev = NULL;
2757145836Smlaier	if (pci_do_power_resume)
2758145836Smlaier		acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
2759240641Sglebius	if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
2760240641Sglebius		return (error);
2761240641Sglebius	for (i = 0; i < numdevs; i++) {
2762145836Smlaier		/*
2763171168Smlaier		 * Notify ACPI we're going to D0 but ignore the result.  If
2764171168Smlaier		 * ACPI is not present, the firmware is responsible for
2765145836Smlaier		 * managing device power.  Only manage type 0 devices for now.
2766145836Smlaier		 */
2767126258Smlaier		child = devlist[i];
2768126258Smlaier		dinfo = (struct pci_devinfo *) device_get_ivars(child);
2769126258Smlaier		if (acpi_dev && device_is_attached(child) &&
2770126258Smlaier		    dinfo->cfg.hdrtype == 0) {
2771126258Smlaier			ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL);
2772126258Smlaier			pci_set_powerstate(child, PCI_POWERSTATE_D0);
2773126258Smlaier		}
2774126258Smlaier
2775126258Smlaier		/* Now the device is powered up, restore its config space. */
2776126258Smlaier		pci_cfg_restore(child, dinfo);
2777126258Smlaier	}
2778126258Smlaier	free(devlist, M_TEMP);
2779126258Smlaier	return (bus_generic_resume(dev));
2780126258Smlaier}
2781126258Smlaier
2782126258Smlaierstatic void
2783126258Smlaierpci_load_vendor_data(void)
2784126258Smlaier{
2785126258Smlaier	caddr_t vendordata, info;
2786126258Smlaier
2787126258Smlaier	if ((vendordata = preload_search_by_type("pci_vendor_data")) != NULL) {
2788126258Smlaier		info = preload_search_info(vendordata, MODINFO_ADDR);
2789126258Smlaier		pci_vendordata = *(char **)info;
2790126258Smlaier		info = preload_search_info(vendordata, MODINFO_SIZE);
2791126258Smlaier		pci_vendordata_size = *(size_t *)info;
2792126258Smlaier		/* terminate the database */
2793130613Smlaier		pci_vendordata[pci_vendordata_size] = '\n';
2794126258Smlaier	}
2795126258Smlaier}
2796126258Smlaier
2797126258Smlaiervoid
2798126258Smlaierpci_driver_added(device_t dev, driver_t *driver)
2799126258Smlaier{
2800126258Smlaier	int numdevs;
2801126258Smlaier	device_t *devlist;
2802126258Smlaier	device_t child;
2803126258Smlaier	struct pci_devinfo *dinfo;
2804126258Smlaier	int i;
2805126258Smlaier
2806126258Smlaier	if (bootverbose)
2807126258Smlaier		device_printf(dev, "driver added\n");
2808126258Smlaier	DEVICE_IDENTIFY(driver, dev);
2809126258Smlaier	if (device_get_children(dev, &devlist, &numdevs) != 0)
2810126258Smlaier		return;
2811126258Smlaier	for (i = 0; i < numdevs; i++) {
2812126258Smlaier		child = devlist[i];
2813126258Smlaier		if (device_get_state(child) != DS_NOTPRESENT)
2814126258Smlaier			continue;
2815126258Smlaier		dinfo = device_get_ivars(child);
2816126258Smlaier		pci_print_verbose(dinfo);
2817126258Smlaier		if (bootverbose)
2818126258Smlaier			printf("pci%d:%d:%d:%d: reprobing on driver added\n",
2819126258Smlaier			    dinfo->cfg.domain, dinfo->cfg.bus, dinfo->cfg.slot,
2820126258Smlaier			    dinfo->cfg.func);
2821126258Smlaier		pci_cfg_restore(child, dinfo);
2822126258Smlaier		if (device_probe_and_attach(child) != 0)
2823126258Smlaier			pci_cfg_save(child, dinfo, 1);
2824126258Smlaier	}
2825240233Sglebius	free(devlist, M_TEMP);
2826126258Smlaier}
2827126258Smlaier
2828126258Smlaierint
2829126261Smlaierpci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags,
2830126258Smlaier    driver_filter_t *filter, driver_intr_t *intr, void *arg, void **cookiep)
2831126258Smlaier{
2832171168Smlaier	struct pci_devinfo *dinfo;
2833171168Smlaier	struct msix_table_entry *mte;
2834223637Sbz	struct msix_vector *mv;
2835130613Smlaier	uint64_t addr;
2836126258Smlaier	uint32_t data;
2837171168Smlaier	void *cookie;
2838171168Smlaier	int error, rid;
2839126258Smlaier
2840126258Smlaier	error = bus_generic_setup_intr(dev, child, irq, flags, filter, intr,
2841181803Sbz	    arg, &cookie);
2842126258Smlaier	if (error)
2843126258Smlaier		return (error);
2844171168Smlaier
2845171168Smlaier	/* If this is not a direct child, just bail out. */
2846126258Smlaier	if (device_get_parent(child) != dev) {
2847126258Smlaier		*cookiep = cookie;
2848181803Sbz		return(0);
2849126258Smlaier	}
2850126258Smlaier
2851171168Smlaier	rid = rman_get_rid(irq);
2852126258Smlaier	if (rid == 0) {
2853126258Smlaier		/* Make sure that INTx is enabled */
2854126258Smlaier		pci_clear_command_bit(dev, child, PCIM_CMD_INTxDIS);
2855126258Smlaier	} else {
2856126258Smlaier		/*
2857126258Smlaier		 * Check to see if the interrupt is MSI or MSI-X.
2858126258Smlaier		 * Ask our parent to map the MSI and give
2859126258Smlaier		 * us the address and data register values.
2860126258Smlaier		 * If we fail for some reason, teardown the
2861126258Smlaier		 * interrupt handler.
2862126258Smlaier		 */
2863126258Smlaier		dinfo = device_get_ivars(child);
2864126258Smlaier		if (dinfo->cfg.msi.msi_alloc > 0) {
2865130613Smlaier			if (dinfo->cfg.msi.msi_addr == 0) {
2866145836Smlaier				KASSERT(dinfo->cfg.msi.msi_handlers == 0,
2867126258Smlaier			    ("MSI has handlers, but vectors not mapped"));
2868240233Sglebius				error = PCIB_MAP_MSI(device_get_parent(dev),
2869240233Sglebius				    child, rman_get_start(irq), &addr, &data);
2870126261Smlaier				if (error)
2871240233Sglebius					goto bad;
2872222488Srwatson				dinfo->cfg.msi.msi_addr = addr;
2873240233Sglebius				dinfo->cfg.msi.msi_data = data;
2874222488Srwatson				pci_enable_msi(child, addr, data);
2875171168Smlaier			}
2876126261Smlaier			dinfo->cfg.msi.msi_handlers++;
2877126258Smlaier		} else {
2878145836Smlaier			KASSERT(dinfo->cfg.msix.msix_alloc > 0,
2879126258Smlaier			    ("No MSI or MSI-X interrupts allocated"));
2880126258Smlaier			KASSERT(rid <= dinfo->cfg.msix.msix_table_len,
2881240233Sglebius			    ("MSI-X index too high"));
2882240233Sglebius			mte = &dinfo->cfg.msix.msix_table[rid - 1];
2883126261Smlaier			KASSERT(mte->mte_vector != 0, ("no message vector"));
2884240233Sglebius			mv = &dinfo->cfg.msix.msix_vectors[mte->mte_vector - 1];
2885222488Srwatson			KASSERT(mv->mv_irq == rman_get_start(irq),
2886240233Sglebius			    ("IRQ mismatch"));
2887222488Srwatson			if (mv->mv_address == 0) {
2888171168Smlaier				KASSERT(mte->mte_handlers == 0,
2889126261Smlaier		    ("MSI-X table entry has handlers, but vector not mapped"));
2890126258Smlaier				error = PCIB_MAP_MSI(device_get_parent(dev),
2891126258Smlaier				    child, rman_get_start(irq), &addr, &data);
2892126258Smlaier				if (error)
2893126258Smlaier					goto bad;
2894171168Smlaier				mv->mv_address = addr;
2895126258Smlaier				mv->mv_data = data;
2896222488Srwatson			}
2897183606Sbz			if (mte->mte_handlers == 0) {
2898183606Sbz				pci_enable_msix(child, rid - 1, mv->mv_address,
2899222488Srwatson				    mv->mv_data);
2900240233Sglebius				pci_unmask_msix(child, rid - 1);
2901126258Smlaier			}
2902126258Smlaier			mte->mte_handlers++;
2903126258Smlaier		}
2904240233Sglebius
2905126258Smlaier		/* Make sure that INTx is disabled if we are using MSI/MSIX */
2906126258Smlaier		pci_set_command_bit(dev, child, PCIM_CMD_INTxDIS);
2907126258Smlaier	bad:
2908126258Smlaier		if (error) {
2909126258Smlaier			(void)bus_generic_teardown_intr(dev, child, irq,
2910126258Smlaier			    cookie);
2911126258Smlaier			return (error);
2912126258Smlaier		}
2913126258Smlaier	}
2914126258Smlaier	*cookiep = cookie;
2915126258Smlaier	return (0);
2916126258Smlaier}
2917126258Smlaier
2918126258Smlaierint
2919126258Smlaierpci_teardown_intr(device_t dev, device_t child, struct resource *irq,
2920126258Smlaier    void *cookie)
2921126258Smlaier{
2922126258Smlaier	struct msix_table_entry *mte;
2923126258Smlaier	struct resource_list_entry *rle;
2924126258Smlaier	struct pci_devinfo *dinfo;
2925126258Smlaier	int error, rid;
2926126258Smlaier
2927126258Smlaier	if (irq == NULL || !(rman_get_flags(irq) & RF_ACTIVE))
2928126258Smlaier		return (EINVAL);
2929126258Smlaier
2930126258Smlaier	/* If this isn't a direct child, just bail out */
2931130613Smlaier	if (device_get_parent(child) != dev)
2932126258Smlaier		return(bus_generic_teardown_intr(dev, child, irq, cookie));
2933126258Smlaier
2934126258Smlaier	rid = rman_get_rid(irq);
2935126258Smlaier	if (rid == 0) {
2936126258Smlaier		/* Mask INTx */
2937126258Smlaier		pci_set_command_bit(dev, child, PCIM_CMD_INTxDIS);
2938130613Smlaier	} else {
2939126258Smlaier		/*
2940126258Smlaier		 * Check to see if the interrupt is MSI or MSI-X.  If so,
2941126258Smlaier		 * decrement the appropriate handlers count and mask the
2942126258Smlaier		 * MSI-X message, or disable MSI messages if the count
2943126258Smlaier		 * drops to 0.
2944240233Sglebius		 */
2945126258Smlaier		dinfo = device_get_ivars(child);
2946126258Smlaier		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, rid);
2947126258Smlaier		if (rle->res != irq)
2948126258Smlaier			return (EINVAL);
2949126258Smlaier		if (dinfo->cfg.msi.msi_alloc > 0) {
2950181803Sbz			KASSERT(rid <= dinfo->cfg.msi.msi_alloc,
2951126258Smlaier			    ("MSI-X index too high"));
2952126258Smlaier			if (dinfo->cfg.msi.msi_handlers == 0)
2953126258Smlaier				return (EINVAL);
2954126258Smlaier			dinfo->cfg.msi.msi_handlers--;
2955126258Smlaier			if (dinfo->cfg.msi.msi_handlers == 0)
2956126258Smlaier				pci_disable_msi(child);
2957126258Smlaier		} else {
2958126258Smlaier			KASSERT(dinfo->cfg.msix.msix_alloc > 0,
2959126258Smlaier			    ("No MSI or MSI-X interrupts allocated"));
2960126258Smlaier			KASSERT(rid <= dinfo->cfg.msix.msix_table_len,
2961126258Smlaier			    ("MSI-X index too high"));
2962126258Smlaier			mte = &dinfo->cfg.msix.msix_table[rid - 1];
2963126258Smlaier			if (mte->mte_handlers == 0)
2964126258Smlaier				return (EINVAL);
2965126258Smlaier			mte->mte_handlers--;
2966126258Smlaier			if (mte->mte_handlers == 0)
2967126258Smlaier				pci_mask_msix(child, rid - 1);
2968145030Sglebius		}
2969130613Smlaier	}
2970126258Smlaier	error = bus_generic_teardown_intr(dev, child, irq, cookie);
2971126258Smlaier	if (rid > 0)
2972126258Smlaier		KASSERT(error == 0,
2973126258Smlaier		    ("%s: generic teardown failed for MSI/MSI-X", __func__));
2974126258Smlaier	return (error);
2975126258Smlaier}
2976130613Smlaier
2977126258Smlaierint
2978126258Smlaierpci_print_child(device_t dev, device_t child)
2979126258Smlaier{
2980126258Smlaier	struct pci_devinfo *dinfo;
2981126258Smlaier	struct resource_list *rl;
2982240233Sglebius	int retval = 0;
2983231852Sbz
2984126258Smlaier	dinfo = device_get_ivars(child);
2985126258Smlaier	rl = &dinfo->resources;
2986126258Smlaier
2987126258Smlaier	retval += bus_print_child_header(dev, child);
2988126258Smlaier
2989126258Smlaier	retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx");
2990126258Smlaier	retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
2991126258Smlaier	retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
2992126258Smlaier	if (device_get_flags(dev))
2993126258Smlaier		retval += printf(" flags %#x", device_get_flags(dev));
2994223637Sbz
2995181803Sbz	retval += printf(" at device %d.%d", pci_get_slot(child),
2996126258Smlaier	    pci_get_function(child));
2997126258Smlaier
2998126258Smlaier	retval += bus_print_child_footer(dev, child);
2999126258Smlaier
3000126258Smlaier	return (retval);
3001126258Smlaier}
3002126258Smlaier
3003126258Smlaierstatic struct
3004126258Smlaier{
3005126258Smlaier	int	class;
3006231852Sbz	int	subclass;
3007126258Smlaier	char	*desc;
3008126258Smlaier} pci_nomatch_tab[] = {
3009126258Smlaier	{PCIC_OLD,		-1,			"old"},
3010126258Smlaier	{PCIC_OLD,		PCIS_OLD_NONVGA,	"non-VGA display device"},
3011126258Smlaier	{PCIC_OLD,		PCIS_OLD_VGA,		"VGA-compatible display device"},
3012126258Smlaier	{PCIC_STORAGE,		-1,			"mass storage"},
3013126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_SCSI,	"SCSI"},
3014126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_IDE,	"ATA"},
3015126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_FLOPPY,	"floppy disk"},
3016126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_IPI,	"IPI"},
3017126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_RAID,	"RAID"},
3018231852Sbz	{PCIC_STORAGE,		PCIS_STORAGE_ATA_ADMA,	"ATA (ADMA)"},
3019126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_SATA,	"SATA"},
3020126258Smlaier	{PCIC_STORAGE,		PCIS_STORAGE_SAS,	"SAS"},
3021126258Smlaier	{PCIC_NETWORK,		-1,			"network"},
3022126258Smlaier	{PCIC_NETWORK,		PCIS_NETWORK_ETHERNET,	"ethernet"},
3023126258Smlaier	{PCIC_NETWORK,		PCIS_NETWORK_TOKENRING,	"token ring"},
3024126258Smlaier	{PCIC_NETWORK,		PCIS_NETWORK_FDDI,	"fddi"},
3025126258Smlaier	{PCIC_NETWORK,		PCIS_NETWORK_ATM,	"ATM"},
3026181803Sbz	{PCIC_NETWORK,		PCIS_NETWORK_ISDN,	"ISDN"},
3027126258Smlaier	{PCIC_DISPLAY,		-1,			"display"},
3028126258Smlaier	{PCIC_DISPLAY,		PCIS_DISPLAY_VGA,	"VGA"},
3029126258Smlaier	{PCIC_DISPLAY,		PCIS_DISPLAY_XGA,	"XGA"},
3030126258Smlaier	{PCIC_DISPLAY,		PCIS_DISPLAY_3D,	"3D"},
3031126258Smlaier	{PCIC_MULTIMEDIA,	-1,			"multimedia"},
3032126258Smlaier	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_VIDEO,	"video"},
3033126258Smlaier	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_AUDIO,	"audio"},
3034240233Sglebius	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_TELE,	"telephony"},
3035223637Sbz	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_HDA,	"HDA"},
3036223637Sbz	{PCIC_MEMORY,		-1,			"memory"},
3037223637Sbz	{PCIC_MEMORY,		PCIS_MEMORY_RAM,	"RAM"},
3038223637Sbz	{PCIC_MEMORY,		PCIS_MEMORY_FLASH,	"flash"},
3039223637Sbz	{PCIC_BRIDGE,		-1,			"bridge"},
3040223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_HOST,	"HOST-PCI"},
3041223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_ISA,	"PCI-ISA"},
3042223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_EISA,	"PCI-EISA"},
3043223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_MCA,	"PCI-MCA"},
3044223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI,	"PCI-PCI"},
3045223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_PCMCIA,	"PCI-PCMCIA"},
3046223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_NUBUS,	"PCI-NuBus"},
3047223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_CARDBUS,	"PCI-CardBus"},
3048223637Sbz	{PCIC_BRIDGE,		PCIS_BRIDGE_RACEWAY,	"PCI-RACEway"},
3049223637Sbz	{PCIC_SIMPLECOMM,	-1,			"simple comms"},
3050223637Sbz	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_UART,	"UART"},	/* could detect 16550 */
3051223637Sbz	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_PAR,	"parallel port"},
3052223637Sbz	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MULSER,	"multiport serial"},
3053223637Sbz	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MODEM,	"generic modem"},
3054223637Sbz	{PCIC_BASEPERIPH,	-1,			"base peripheral"},
3055223637Sbz	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PIC,	"interrupt controller"},
3056223637Sbz	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_DMA,	"DMA controller"},
3057223637Sbz	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_TIMER,	"timer"},
3058223637Sbz	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RTC,	"realtime clock"},
3059223637Sbz	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PCIHOT,	"PCI hot-plug controller"},
3060223637Sbz	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_SDHC,	"SD host controller"},
3061223637Sbz	{PCIC_INPUTDEV,		-1,			"input device"},
3062223637Sbz	{PCIC_INPUTDEV,		PCIS_INPUTDEV_KEYBOARD,	"keyboard"},
3063223637Sbz	{PCIC_INPUTDEV,		PCIS_INPUTDEV_DIGITIZER,"digitizer"},
3064223637Sbz	{PCIC_INPUTDEV,		PCIS_INPUTDEV_MOUSE,	"mouse"},
3065223637Sbz	{PCIC_INPUTDEV,		PCIS_INPUTDEV_SCANNER,	"scanner"},
3066223637Sbz	{PCIC_INPUTDEV,		PCIS_INPUTDEV_GAMEPORT,	"gameport"},
3067240233Sglebius	{PCIC_DOCKING,		-1,			"docking station"},
3068223637Sbz	{PCIC_PROCESSOR,	-1,			"processor"},
3069240233Sglebius	{PCIC_SERIALBUS,	-1,			"serial bus"},
3070240233Sglebius	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FW,	"FireWire"},
3071126258Smlaier	{PCIC_SERIALBUS,	PCIS_SERIALBUS_ACCESS,	"AccessBus"},
3072130613Smlaier	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SSA,	"SSA"},
3073240233Sglebius	{PCIC_SERIALBUS,	PCIS_SERIALBUS_USB,	"USB"},
3074240233Sglebius	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FC,	"Fibre Channel"},
3075126258Smlaier	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SMBUS,	"SMBus"},
3076126258Smlaier	{PCIC_WIRELESS,		-1,			"wireless controller"},
3077126258Smlaier	{PCIC_WIRELESS,		PCIS_WIRELESS_IRDA,	"iRDA"},
3078130613Smlaier	{PCIC_WIRELESS,		PCIS_WIRELESS_IR,	"IR"},
3079223637Sbz	{PCIC_WIRELESS,		PCIS_WIRELESS_RF,	"RF"},
3080223637Sbz	{PCIC_INTELLIIO,	-1,			"intelligent I/O controller"},
3081126258Smlaier	{PCIC_INTELLIIO,	PCIS_INTELLIIO_I2O,	"I2O"},
3082223637Sbz	{PCIC_SATCOM,		-1,			"satellite communication"},
3083171168Smlaier	{PCIC_SATCOM,		PCIS_SATCOM_TV,		"sat TV"},
3084145836Smlaier	{PCIC_SATCOM,		PCIS_SATCOM_AUDIO,	"sat audio"},
3085171168Smlaier	{PCIC_SATCOM,		PCIS_SATCOM_VOICE,	"sat voice"},
3086223637Sbz	{PCIC_SATCOM,		PCIS_SATCOM_DATA,	"sat data"},
3087223637Sbz	{PCIC_CRYPTO,		-1,			"encrypt/decrypt"},
3088223637Sbz	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"network/computer crypto"},
3089223637Sbz	{PCIC_CRYPTO,		PCIS_CRYPTO_ENTERTAIN,	"entertainment crypto"},
3090240641Sglebius	{PCIC_DASP,		-1,			"dasp"},
3091126258Smlaier	{PCIC_DASP,		PCIS_DASP_DPIO,		"DPIO module"},
3092240233Sglebius	{0, 0,		NULL}
3093223637Sbz};
3094240233Sglebius
3095240233Sglebiusvoid
3096240233Sglebiuspci_probe_nomatch(device_t dev, device_t child)
3097240233Sglebius{
3098240233Sglebius	int	i;
3099145836Smlaier	char	*cp, *scp, *device;
3100145836Smlaier
3101223637Sbz	/*
3102223637Sbz	 * Look for a listing for this device in a loaded device database.
3103223637Sbz	 */
3104223637Sbz	if ((device = pci_describe_device(child)) != NULL) {
3105223637Sbz		device_printf(dev, "<%s>", device);
3106223637Sbz		free(device, M_DEVBUF);
3107223637Sbz	} else {
3108223637Sbz		/*
3109223637Sbz		 * Scan the class/subclass descriptions for a general
3110223637Sbz		 * description.
3111223637Sbz		 */
3112223637Sbz		cp = "unknown";
3113223637Sbz		scp = NULL;
3114223637Sbz		for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
3115223637Sbz			if (pci_nomatch_tab[i].class == pci_get_class(child)) {
3116223637Sbz				if (pci_nomatch_tab[i].subclass == -1) {
3117223637Sbz					cp = pci_nomatch_tab[i].desc;
3118223637Sbz				} else if (pci_nomatch_tab[i].subclass ==
3119223637Sbz				    pci_get_subclass(child)) {
3120223637Sbz					scp = pci_nomatch_tab[i].desc;
3121223637Sbz				}
3122223637Sbz			}
3123223637Sbz		}
3124223637Sbz		device_printf(dev, "<%s%s%s>",
3125223637Sbz		    cp ? cp : "",
3126223637Sbz		    ((cp != NULL) && (scp != NULL)) ? ", " : "",
3127223637Sbz		    scp ? scp : "");
3128223637Sbz	}
3129223637Sbz	printf(" at device %d.%d (no driver attached)\n",
3130223637Sbz	    pci_get_slot(child), pci_get_function(child));
3131223637Sbz	pci_cfg_save(child, (struct pci_devinfo *)device_get_ivars(child), 1);
3132223637Sbz	return;
3133223637Sbz}
3134223637Sbz
3135223637Sbz/*
3136223637Sbz * Parse the PCI device database, if loaded, and return a pointer to a
3137223637Sbz * description of the device.
3138223637Sbz *
3139223637Sbz * The database is flat text formatted as follows:
3140223637Sbz *
3141223637Sbz * Any line not in a valid format is ignored.
3142223637Sbz * Lines are terminated with newline '\n' characters.
3143223637Sbz *
3144223637Sbz * A VENDOR line consists of the 4 digit (hex) vendor code, a TAB, then
3145223637Sbz * the vendor name.
3146223637Sbz *
3147223637Sbz * A DEVICE line is entered immediately below the corresponding VENDOR ID.
3148223637Sbz * - devices cannot be listed without a corresponding VENDOR line.
3149223637Sbz * A DEVICE line consists of a TAB, the 4 digit (hex) device code,
3150126258Smlaier * another TAB, then the device name.
3151126258Smlaier */
3152223637Sbz
3153240233Sglebius/*
3154240641Sglebius * Assuming (ptr) points to the beginning of a line in the database,
3155240233Sglebius * return the vendor or device and description of the next entry.
3156240233Sglebius * The value of (vendor) or (device) inappropriate for the entry type
3157223637Sbz * is set to -1.  Returns nonzero at the end of the database.
3158223637Sbz *
3159223637Sbz * Note that this is slightly unrobust in the face of corrupt data;
3160223637Sbz * we attempt to safeguard against this by spamming the end of the
3161223637Sbz * database with a newline when we initialise.
3162223637Sbz */
3163223637Sbzstatic int
3164223637Sbzpci_describe_parse_line(char **ptr, int *vendor, int *device, char **desc)
3165223637Sbz{
3166223637Sbz	char	*cp = *ptr;
3167223637Sbz	int	left;
3168289703Skp
3169223637Sbz	*device = -1;
3170223637Sbz	*vendor = -1;
3171223637Sbz	**desc = '\0';
3172223637Sbz	for (;;) {
3173223637Sbz		left = pci_vendordata_size - (cp - pci_vendordata);
3174223637Sbz		if (left <= 0) {
3175223637Sbz			*ptr = cp;
3176223637Sbz			return(1);
3177289703Skp		}
3178223637Sbz
3179223637Sbz		/* vendor entry? */
3180223637Sbz		if (*cp != '\t' &&
3181223637Sbz		    sscanf(cp, "%x\t%80[^\n]", vendor, *desc) == 2)
3182223637Sbz			break;
3183126258Smlaier		/* device entry? */
3184223637Sbz		if (*cp == '\t' &&
3185223637Sbz		    sscanf(cp, "%x\t%80[^\n]", device, *desc) == 2)
3186223637Sbz			break;
3187223637Sbz
3188223637Sbz		/* skip to next line */
3189223637Sbz		while (*cp != '\n' && left > 0) {
3190223637Sbz			cp++;
3191289703Skp			left--;
3192223637Sbz		}
3193223637Sbz		if (*cp == '\n') {
3194223637Sbz			cp++;
3195223637Sbz			left--;
3196223637Sbz		}
3197223637Sbz	}
3198223637Sbz	/* skip to next line */
3199223637Sbz	while (*cp != '\n' && left > 0) {
3200223637Sbz		cp++;
3201289703Skp		left--;
3202223637Sbz	}
3203223637Sbz	if (*cp == '\n' && left > 0)
3204223637Sbz		cp++;
3205223637Sbz	*ptr = cp;
3206223637Sbz	return(0);
3207223637Sbz}
3208223637Sbz
3209223637Sbzstatic char *
3210223637Sbzpci_describe_device(device_t dev)
3211223637Sbz{
3212223637Sbz	int	vendor, device;
3213223637Sbz	char	*desc, *vp, *dp, *line;
3214223637Sbz
3215223637Sbz	desc = vp = dp = NULL;
3216223637Sbz
3217223637Sbz	/*
3218223637Sbz	 * If we have no vendor data, we can't do anything.
3219223637Sbz	 */
3220223637Sbz	if (pci_vendordata == NULL)
3221223637Sbz		goto out;
3222223637Sbz
3223223637Sbz	/*
3224223637Sbz	 * Scan the vendor data looking for this device
3225223637Sbz	 */
3226223637Sbz	line = pci_vendordata;
3227223637Sbz	if ((vp = malloc(80, M_DEVBUF, M_NOWAIT)) == NULL)
3228223637Sbz		goto out;
3229223637Sbz	for (;;) {
3230223637Sbz		if (pci_describe_parse_line(&line, &vendor, &device, &vp))
3231223637Sbz			goto out;
3232223637Sbz		if (vendor == pci_get_vendor(dev))
3233223637Sbz			break;
3234223637Sbz	}
3235223637Sbz	if ((dp = malloc(80, M_DEVBUF, M_NOWAIT)) == NULL)
3236223637Sbz		goto out;
3237223637Sbz	for (;;) {
3238223637Sbz		if (pci_describe_parse_line(&line, &vendor, &device, &dp)) {
3239223637Sbz			*dp = 0;
3240223637Sbz			break;
3241223637Sbz		}
3242223637Sbz		if (vendor != -1) {
3243223637Sbz			*dp = 0;
3244223637Sbz			break;
3245223637Sbz		}
3246223637Sbz		if (device == pci_get_device(dev))
3247223637Sbz			break;
3248223637Sbz	}
3249223637Sbz	if (dp[0] == '\0')
3250223637Sbz		snprintf(dp, 80, "0x%x", pci_get_device(dev));
3251223637Sbz	if ((desc = malloc(strlen(vp) + strlen(dp) + 3, M_DEVBUF, M_NOWAIT)) !=
3252223637Sbz	    NULL)
3253223637Sbz		sprintf(desc, "%s, %s", vp, dp);
3254223637Sbz out:
3255223637Sbz	if (vp != NULL)
3256223637Sbz		free(vp, M_DEVBUF);
3257223637Sbz	if (dp != NULL)
3258223637Sbz		free(dp, M_DEVBUF);
3259223637Sbz	return(desc);
3260223637Sbz}
3261223637Sbz
3262223637Sbzint
3263223637Sbzpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
3264223637Sbz{
3265223637Sbz	struct pci_devinfo *dinfo;
3266223637Sbz	pcicfgregs *cfg;
3267223637Sbz
3268223637Sbz	dinfo = device_get_ivars(child);
3269223637Sbz	cfg = &dinfo->cfg;
3270223637Sbz
3271223637Sbz	switch (which) {
3272223637Sbz	case PCI_IVAR_ETHADDR:
3273223637Sbz		/*
3274126258Smlaier		 * The generic accessor doesn't deal with failure, so
3275223637Sbz		 * we set the return value, then return an error.
3276223637Sbz		 */
3277223637Sbz		*((uint8_t **) result) = NULL;
3278126258Smlaier		return (EINVAL);
3279126258Smlaier	case PCI_IVAR_SUBVENDOR:
3280126258Smlaier		*result = cfg->subvendor;
3281126258Smlaier		break;
3282171168Smlaier	case PCI_IVAR_SUBDEVICE:
3283126258Smlaier		*result = cfg->subdevice;
3284126258Smlaier		break;
3285126258Smlaier	case PCI_IVAR_VENDOR:
3286126258Smlaier		*result = cfg->vendor;
3287126258Smlaier		break;
3288223637Sbz	case PCI_IVAR_DEVICE:
3289126258Smlaier		*result = cfg->device;
3290171168Smlaier		break;
3291231852Sbz	case PCI_IVAR_DEVID:
3292126258Smlaier		*result = (cfg->device << 16) | cfg->vendor;
3293223637Sbz		break;
3294126258Smlaier	case PCI_IVAR_CLASS:
3295223637Sbz		*result = cfg->baseclass;
3296126258Smlaier		break;
3297171168Smlaier	case PCI_IVAR_SUBCLASS:
3298231852Sbz		*result = cfg->subclass;
3299126258Smlaier		break;
3300223637Sbz	case PCI_IVAR_PROGIF:
3301126258Smlaier		*result = cfg->progif;
3302223637Sbz		break;
3303126258Smlaier	case PCI_IVAR_REVID:
3304223637Sbz		*result = cfg->revid;
3305223637Sbz		break;
3306223637Sbz	case PCI_IVAR_INTPIN:
3307223637Sbz		*result = cfg->intpin;
3308223637Sbz		break;
3309223637Sbz	case PCI_IVAR_IRQ:
3310171168Smlaier		*result = cfg->intline;
3311126258Smlaier		break;
3312126258Smlaier	case PCI_IVAR_DOMAIN:
3313126258Smlaier		*result = cfg->domain;
3314223637Sbz		break;
3315223637Sbz	case PCI_IVAR_BUS:
3316126258Smlaier		*result = cfg->bus;
3317223637Sbz		break;
3318171168Smlaier	case PCI_IVAR_SLOT:
3319240233Sglebius		*result = cfg->slot;
3320126258Smlaier		break;
3321171168Smlaier	case PCI_IVAR_FUNCTION:
3322126258Smlaier		*result = cfg->func;
3323223637Sbz		break;
3324171168Smlaier	case PCI_IVAR_CMDREG:
3325240233Sglebius		*result = cfg->cmdreg;
3326126258Smlaier		break;
3327171168Smlaier	case PCI_IVAR_CACHELNSZ:
3328126258Smlaier		*result = cfg->cachelnsz;
3329223637Sbz		break;
3330223637Sbz	case PCI_IVAR_MINGNT:
3331126258Smlaier		*result = cfg->mingnt;
3332240233Sglebius		break;
3333240233Sglebius	case PCI_IVAR_MAXLAT:
3334126258Smlaier		*result = cfg->maxlat;
3335223637Sbz		break;
3336223637Sbz	case PCI_IVAR_LATTIMER:
3337223637Sbz		*result = cfg->lattimer;
3338223637Sbz		break;
3339126258Smlaier	default:
3340126258Smlaier		return (ENOENT);
3341126258Smlaier	}
3342126258Smlaier	return (0);
3343171168Smlaier}
3344171168Smlaier
3345126258Smlaierint
3346171168Smlaierpci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
3347126258Smlaier{
3348126258Smlaier	struct pci_devinfo *dinfo;
3349126258Smlaier
3350126258Smlaier	dinfo = device_get_ivars(child);
3351126258Smlaier
3352126258Smlaier	switch (which) {
3353126258Smlaier	case PCI_IVAR_INTPIN:
3354240641Sglebius		dinfo->cfg.intpin = value;
3355240641Sglebius		return (0);
3356240641Sglebius	case PCI_IVAR_ETHADDR:
3357126258Smlaier	case PCI_IVAR_SUBVENDOR:
3358240641Sglebius	case PCI_IVAR_SUBDEVICE:
3359240641Sglebius	case PCI_IVAR_VENDOR:
3360171168Smlaier	case PCI_IVAR_DEVICE:
3361126258Smlaier	case PCI_IVAR_DEVID:
3362126258Smlaier	case PCI_IVAR_CLASS:
3363126258Smlaier	case PCI_IVAR_SUBCLASS:
3364126258Smlaier	case PCI_IVAR_PROGIF:
3365126258Smlaier	case PCI_IVAR_REVID:
3366126258Smlaier	case PCI_IVAR_IRQ:
3367126258Smlaier	case PCI_IVAR_DOMAIN:
3368223637Sbz	case PCI_IVAR_BUS:
3369126258Smlaier	case PCI_IVAR_SLOT:
3370223637Sbz	case PCI_IVAR_FUNCTION:
3371240233Sglebius		return (EINVAL);	/* disallow for now */
3372240233Sglebius
3373126258Smlaier	default:
3374126258Smlaier		return (ENOENT);
3375126258Smlaier	}
3376126258Smlaier}
3377126258Smlaier
3378126258Smlaier
3379126258Smlaier#include "opt_ddb.h"
3380130613Smlaier#ifdef DDB
3381223637Sbz#include <ddb/ddb.h>
3382223637Sbz#include <sys/cons.h>
3383223637Sbz
3384223637Sbz/*
3385223637Sbz * List resources based on pci map registers, used for within ddb
3386223637Sbz */
3387223637Sbz
3388223637SbzDB_SHOW_COMMAND(pciregs, db_pci_dump)
3389223637Sbz{
3390223637Sbz	struct pci_devinfo *dinfo;
3391223637Sbz	struct devlist *devlist_head;
3392126258Smlaier	struct pci_conf *p;
3393223637Sbz	const char *name;
3394223637Sbz	int i, error, none_count;
3395126258Smlaier
3396126258Smlaier	none_count = 0;
3397223637Sbz	/* get the head of the device queue */
3398223637Sbz	devlist_head = &pci_devq;
3399223637Sbz
3400223637Sbz	/*
3401223637Sbz	 * Go through the list of devices and print out devices
3402223637Sbz	 */
3403223637Sbz	for (error = 0, i = 0,
3404223637Sbz	     dinfo = STAILQ_FIRST(devlist_head);
3405126258Smlaier	     (dinfo != NULL) && (error == 0) && (i < pci_numdevs) && !db_pager_quit;
3406223637Sbz	     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
3407223637Sbz
3408223637Sbz		/* Populate pd_name and pd_unit */
3409223637Sbz		name = NULL;
3410223637Sbz		if (dinfo->cfg.dev)
3411223637Sbz			name = device_get_name(dinfo->cfg.dev);
3412223637Sbz
3413223637Sbz		p = &dinfo->conf;
3414223637Sbz		db_printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x "
3415223637Sbz			"chip=0x%08x rev=0x%02x hdr=0x%02x\n",
3416223637Sbz			(name && *name) ? name : "none",
3417223637Sbz			(name && *name) ? (int)device_get_unit(dinfo->cfg.dev) :
3418223637Sbz			none_count++,
3419223637Sbz			p->pc_sel.pc_domain, p->pc_sel.pc_bus, p->pc_sel.pc_dev,
3420223637Sbz			p->pc_sel.pc_func, (p->pc_class << 16) |
3421223637Sbz			(p->pc_subclass << 8) | p->pc_progif,
3422223637Sbz			(p->pc_subdevice << 16) | p->pc_subvendor,
3423223637Sbz			(p->pc_device << 16) | p->pc_vendor,
3424223637Sbz			p->pc_revid, p->pc_hdr);
3425223637Sbz	}
3426223637Sbz}
3427223637Sbz#endif /* DDB */
3428223637Sbz
3429223637Sbzstatic struct resource *
3430223637Sbzpci_alloc_map(device_t dev, device_t child, int type, int *rid,
3431240233Sglebius    u_long start, u_long end, u_long count, u_int flags)
3432223637Sbz{
3433223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
3434223637Sbz	struct resource_list *rl = &dinfo->resources;
3435126258Smlaier	struct resource_list_entry *rle;
3436126258Smlaier	struct resource *res;
3437223637Sbz	pci_addr_t map, testval;
3438223637Sbz	int mapsize;
3439126258Smlaier
3440126258Smlaier	/*
3441126258Smlaier	 * Weed out the bogons, and figure out how large the BAR/map
3442126258Smlaier	 * is.  Bars that read back 0 here are bogus and unimplemented.
3443126258Smlaier	 * Note: atapci in legacy mode are special and handled elsewhere
3444223637Sbz	 * in the code.  If you have a atapci device in legacy mode and
3445126258Smlaier	 * it fails here, that other code is broken.
3446240233Sglebius	 */
3447126258Smlaier	res = NULL;
3448223637Sbz	pci_read_bar(child, *rid, &map, &testval);
3449126258Smlaier
3450240233Sglebius	/* Ignore a BAR with a base of 0. */
3451240233Sglebius	if (pci_mapbase(testval) == 0)
3452126258Smlaier		goto out;
3453223637Sbz
3454223637Sbz	if (PCI_BAR_MEM(testval)) {
3455223637Sbz		if (type != SYS_RES_MEMORY) {
3456240233Sglebius			if (bootverbose)
3457240233Sglebius				device_printf(dev,
3458240233Sglebius				    "child %s requested type %d for rid %#x,"
3459223637Sbz				    " but the BAR says it is an memio\n",
3460223637Sbz				    device_get_nameunit(child), type, *rid);
3461223637Sbz			goto out;
3462223637Sbz		}
3463240233Sglebius	} else {
3464223637Sbz		if (type != SYS_RES_IOPORT) {
3465240233Sglebius			if (bootverbose)
3466223637Sbz				device_printf(dev,
3467126258Smlaier				    "child %s requested type %d for rid %#x,"
3468223637Sbz				    " but the BAR says it is an ioport\n",
3469223637Sbz				    device_get_nameunit(child), type, *rid);
3470223637Sbz			goto out;
3471130613Smlaier		}
3472240233Sglebius	}
3473240233Sglebius
3474240233Sglebius	/*
3475223637Sbz	 * For real BARs, we need to override the size that
3476223637Sbz	 * the driver requests, because that's what the BAR
3477223637Sbz	 * actually uses and we would otherwise have a
3478223637Sbz	 * situation where we might allocate the excess to
3479223637Sbz	 * another driver, which won't work.
3480223637Sbz	 */
3481240233Sglebius	mapsize = pci_mapsize(testval);
3482223637Sbz	count = 1UL << mapsize;
3483223637Sbz	if (RF_ALIGNMENT(flags) < mapsize)
3484223637Sbz		flags = (flags & ~RF_ALIGNMENT_MASK) | RF_ALIGNMENT_LOG2(mapsize);
3485130613Smlaier	if (PCI_BAR_MEM(testval) && (testval & PCIM_BAR_MEM_PREFETCH))
3486223637Sbz		flags |= RF_PREFETCHABLE;
3487240233Sglebius
3488223637Sbz	/*
3489240233Sglebius	 * Allocate enough resource, and then write back the
3490223637Sbz	 * appropriate bar for that resource.
3491223637Sbz	 */
3492126258Smlaier	res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid,
3493240233Sglebius	    start, end, count, flags & ~RF_ACTIVE);
3494223637Sbz	if (res == NULL) {
3495240233Sglebius		device_printf(child,
3496240233Sglebius		    "%#lx bytes of rid %#x res %d failed (%#lx, %#lx).\n",
3497240233Sglebius		    count, *rid, type, start, end);
3498240233Sglebius		goto out;
3499223637Sbz	}
3500223637Sbz	rman_set_device(res, dev);
3501223637Sbz	resource_list_add(rl, type, *rid, start, end, count);
3502223637Sbz	rle = resource_list_find(rl, type, *rid);
3503223637Sbz	if (rle == NULL)
3504223637Sbz		panic("pci_alloc_map: unexpectedly can't find resource.");
3505223637Sbz	rle->res = res;
3506223637Sbz	rle->start = rman_get_start(res);
3507263029Sglebius	rle->end = rman_get_end(res);
3508263029Sglebius	rle->count = count;
3509270574Sglebius	if (bootverbose)
3510223637Sbz		device_printf(child,
3511223637Sbz		    "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n",
3512223637Sbz		    count, *rid, type, rman_get_start(res));
3513223637Sbz	map = rman_get_start(res);
3514223637Sbz	pci_write_bar(child, *rid, map);
3515223637Sbzout:;
3516223637Sbz	return (res);
3517223637Sbz}
3518223637Sbz
3519223637Sbz
3520223637Sbzstruct resource *
3521223637Sbzpci_alloc_resource(device_t dev, device_t child, int type, int *rid,
3522223637Sbz		   u_long start, u_long end, u_long count, u_int flags)
3523223637Sbz{
3524223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
3525223637Sbz	struct resource_list *rl = &dinfo->resources;
3526240233Sglebius	struct resource_list_entry *rle;
3527223637Sbz	struct resource *res;
3528223637Sbz	pcicfgregs *cfg = &dinfo->cfg;
3529223637Sbz
3530223637Sbz	if (device_get_parent(child) != dev)
3531223637Sbz		return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
3532223637Sbz		    type, rid, start, end, count, flags));
3533223637Sbz
3534223637Sbz	/*
3535223637Sbz	 * Perform lazy resource allocation
3536223637Sbz	 */
3537223637Sbz	switch (type) {
3538223637Sbz	case SYS_RES_IRQ:
3539223637Sbz		/*
3540223637Sbz		 * Can't alloc legacy interrupt once MSI messages have
3541223637Sbz		 * been allocated.
3542223637Sbz		 */
3543223637Sbz		if (*rid == 0 && (cfg->msi.msi_alloc > 0 ||
3544223637Sbz		    cfg->msix.msix_alloc > 0))
3545126258Smlaier			return (NULL);
3546223637Sbz
3547126258Smlaier		/*
3548126258Smlaier		 * If the child device doesn't have an interrupt
3549126258Smlaier		 * routed and is deserving of an interrupt, try to
3550223637Sbz		 * assign it one.
3551223637Sbz		 */
3552223637Sbz		if (*rid == 0 && !PCI_INTERRUPT_VALID(cfg->intline) &&
3553289703Skp		    (cfg->intpin != 0))
3554126258Smlaier			pci_assign_interrupt(dev, child, 0);
3555223637Sbz		break;
3556126258Smlaier	case SYS_RES_IOPORT:
3557126258Smlaier	case SYS_RES_MEMORY:
3558126258Smlaier		/* Allocate resources for this BAR if needed. */
3559126258Smlaier		rle = resource_list_find(rl, type, *rid);
3560223637Sbz		if (rle == NULL) {
3561223637Sbz			res = pci_alloc_map(dev, child, type, rid, start, end,
3562126258Smlaier			    count, flags);
3563126258Smlaier			if (res == NULL)
3564126258Smlaier				return (NULL);
3565126258Smlaier			rle = resource_list_find(rl, type, *rid);
3566126258Smlaier		}
3567126258Smlaier
3568126258Smlaier		/*
3569126258Smlaier		 * If the resource belongs to the bus, then give it to
3570126258Smlaier		 * the child.  We need to activate it if requested
3571126258Smlaier		 * since the bus always allocates inactive resources.
3572126258Smlaier		 */
3573126258Smlaier		if (rle != NULL && rle->res != NULL &&
3574126258Smlaier		    rman_get_device(rle->res) == dev) {
3575126258Smlaier			if (bootverbose)
3576126258Smlaier				device_printf(child,
3577126258Smlaier			    "Reserved %#lx bytes for rid %#x type %d at %#lx\n",
3578223637Sbz				    rman_get_size(rle->res), *rid, type,
3579223637Sbz				    rman_get_start(rle->res));
3580223637Sbz			rman_set_device(rle->res, child);
3581223637Sbz			if ((flags & RF_ACTIVE) &&
3582223637Sbz			    bus_activate_resource(child, type, *rid,
3583223637Sbz			    rle->res) != 0)
3584223637Sbz				return (NULL);
3585223637Sbz			return (rle->res);
3586223637Sbz		}
3587223637Sbz	}
3588223637Sbz	return (resource_list_alloc(rl, dev, child, type, rid,
3589223637Sbz	    start, end, count, flags));
3590223637Sbz}
3591223637Sbz
3592223637Sbzint
3593223637Sbzpci_release_resource(device_t dev, device_t child, int type, int rid,
3594223637Sbz    struct resource *r)
3595223637Sbz{
3596270576Sglebius	int error;
3597270576Sglebius
3598270925Sglebius	if (device_get_parent(child) != dev)
3599270576Sglebius		return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
3600270576Sglebius		    type, rid, r));
3601270576Sglebius
3602270576Sglebius	/*
3603270576Sglebius	 * For BARs we don't actually want to release the resource.
3604270576Sglebius	 * Instead, we deactivate the resource if needed and then give
3605270576Sglebius	 * ownership of the BAR back to the bus.
3606270576Sglebius	 */
3607240233Sglebius	switch (type) {
3608240233Sglebius	case SYS_RES_IOPORT:
3609223637Sbz	case SYS_RES_MEMORY:
3610285940Sglebius		if (rman_get_device(r) != child)
3611223637Sbz			return (EINVAL);
3612223637Sbz		if (rman_get_flags(r) & RF_ACTIVE) {
3613223637Sbz			error = bus_deactivate_resource(child, type, rid, r);
3614223637Sbz			if (error)
3615223637Sbz				return (error);
3616223637Sbz		}
3617223637Sbz		rman_set_device(r, dev);
3618126258Smlaier		return (0);
3619126258Smlaier	}
3620126258Smlaier	return (bus_generic_rl_release_resource(dev, child, type, rid, r));
3621130613Smlaier}
3622145836Smlaier
3623240233Sglebiusint
3624126258Smlaierpci_activate_resource(device_t dev, device_t child, int type, int rid,
3625126258Smlaier    struct resource *r)
3626126258Smlaier{
3627145836Smlaier	int error;
3628223637Sbz
3629145836Smlaier	error = bus_generic_activate_resource(dev, child, type, rid, r);
3630145836Smlaier	if (error)
3631145836Smlaier		return (error);
3632126258Smlaier
3633130613Smlaier	/* Enable decoding in the command register when activating BARs. */
3634145836Smlaier	if (device_get_parent(child) == dev) {
3635240233Sglebius		switch (type) {
3636223637Sbz		case SYS_RES_IOPORT:
3637126258Smlaier		case SYS_RES_MEMORY:
3638126258Smlaier			error = PCI_ENABLE_IO(dev, child, type);
3639223637Sbz			break;
3640126258Smlaier		}
3641240233Sglebius	}
3642240233Sglebius	return (error);
3643240233Sglebius}
3644240233Sglebius
3645240233Sglebiusvoid
3646240233Sglebiuspci_delete_resource(device_t dev, device_t child, int type, int rid)
3647240233Sglebius{
3648240233Sglebius	struct pci_devinfo *dinfo;
3649240233Sglebius	struct resource_list *rl;
3650240233Sglebius	struct resource_list_entry *rle;
3651240233Sglebius
3652240233Sglebius	if (device_get_parent(child) != dev)
3653240233Sglebius		return;
3654126258Smlaier
3655240233Sglebius	dinfo = device_get_ivars(child);
3656240233Sglebius	rl = &dinfo->resources;
3657240233Sglebius	rle = resource_list_find(rl, type, rid);
3658240233Sglebius	if (rle == NULL)
3659223637Sbz		return;
3660223637Sbz
3661223637Sbz	if (rle->res) {
3662223637Sbz		if (rman_get_device(rle->res) != dev ||
3663223637Sbz		    rman_get_flags(rle->res) & RF_ACTIVE) {
3664240233Sglebius			device_printf(dev, "delete_resource: "
3665223637Sbz			    "Resource still owned by child, oops. "
3666223637Sbz			    "(type=%d, rid=%d, addr=%lx)\n",
3667223637Sbz			    rle->type, rle->rid,
3668126258Smlaier			    rman_get_start(rle->res));
3669240233Sglebius			return;
3670223637Sbz		}
3671223637Sbz
3672223637Sbz		/*
3673223637Sbz		 * If this is a BAR, clear the BAR so it stops
3674223637Sbz		 * decoding before releasing the resource.
3675223637Sbz		 */
3676223637Sbz		switch (type) {
3677223637Sbz		case SYS_RES_IOPORT:
3678223637Sbz		case SYS_RES_MEMORY:
3679223637Sbz			pci_write_bar(child, rid, 0);
3680223637Sbz			break;
3681223637Sbz		}
3682223637Sbz		bus_release_resource(dev, type, rid, rle->res);
3683223637Sbz	}
3684223637Sbz	resource_list_delete(rl, type, rid);
3685223637Sbz}
3686223637Sbz
3687223637Sbzstruct resource_list *
3688223637Sbzpci_get_resource_list (device_t dev, device_t child)
3689223637Sbz{
3690223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
3691223637Sbz
3692223637Sbz	return (&dinfo->resources);
3693231852Sbz}
3694223637Sbz
3695231852Sbzuint32_t
3696231852Sbzpci_read_config_method(device_t dev, device_t child, int reg, int width)
3697223637Sbz{
3698223637Sbz	struct pci_devinfo *dinfo = device_get_ivars(child);
3699223637Sbz	pcicfgregs *cfg = &dinfo->cfg;
3700240233Sglebius
3701223637Sbz	return (PCIB_READ_CONFIG(device_get_parent(dev),
3702223637Sbz	    cfg->bus, cfg->slot, cfg->func, reg, width));
3703171168Smlaier}
3704165631Smlaier
3705223637Sbzvoid
3706126258Smlaierpci_write_config_method(device_t dev, device_t child, int reg,
3707223637Sbz    uint32_t val, int width)
3708223637Sbz{
3709240233Sglebius	struct pci_devinfo *dinfo = device_get_ivars(child);
3710223637Sbz	pcicfgregs *cfg = &dinfo->cfg;
3711240233Sglebius
3712223637Sbz	PCIB_WRITE_CONFIG(device_get_parent(dev),
3713285940Sglebius	    cfg->bus, cfg->slot, cfg->func, reg, val, width);
3714285940Sglebius}
3715285940Sglebius
3716285940Sglebiusint
3717285940Sglebiuspci_child_location_str_method(device_t dev, device_t child, char *buf,
3718285940Sglebius    size_t buflen)
3719285940Sglebius{
3720285940Sglebius
3721285940Sglebius	snprintf(buf, buflen, "slot=%d function=%d", pci_get_slot(child),
3722285940Sglebius	    pci_get_function(child));
3723285940Sglebius	return (0);
3724285940Sglebius}
3725261019Sglebius
3726240737Sglebiusint
3727285940Sglebiuspci_child_pnpinfo_str_method(device_t dev, device_t child, char *buf,
3728285940Sglebius    size_t buflen)
3729285940Sglebius{
3730285940Sglebius	struct pci_devinfo *dinfo;
3731285940Sglebius	pcicfgregs *cfg;
3732286004Sglebius
3733285940Sglebius	dinfo = device_get_ivars(child);
3734285940Sglebius	cfg = &dinfo->cfg;
3735285940Sglebius	snprintf(buf, buflen, "vendor=0x%04x device=0x%04x subvendor=0x%04x "
3736285940Sglebius	    "subdevice=0x%04x class=0x%02x%02x%02x", cfg->vendor, cfg->device,
3737285940Sglebius	    cfg->subvendor, cfg->subdevice, cfg->baseclass, cfg->subclass,
3738285940Sglebius	    cfg->progif);
3739261019Sglebius	return (0);
3740240737Sglebius}
3741223637Sbz
3742126258Smlaierint
3743126258Smlaierpci_assign_interrupt_method(device_t dev, device_t child)
3744240233Sglebius{
3745130613Smlaier	struct pci_devinfo *dinfo = device_get_ivars(child);
3746126258Smlaier	pcicfgregs *cfg = &dinfo->cfg;
3747126258Smlaier
3748126258Smlaier	return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child,
3749126258Smlaier	    cfg->intpin));
3750126258Smlaier}
3751126258Smlaier
3752126258Smlaierstatic int
3753126258Smlaierpci_modevent(module_t mod, int what, void *arg)
3754145836Smlaier{
3755171168Smlaier	static struct cdev *pci_cdev;
3756240641Sglebius
3757126258Smlaier	switch (what) {
3758240233Sglebius	case MOD_LOAD:
3759240233Sglebius		STAILQ_INIT(&pci_devq);
3760126258Smlaier		pci_generation = 0;
3761126258Smlaier		pci_cdev = make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644,
3762126258Smlaier		    "pci");
3763171168Smlaier		pci_load_vendor_data();
3764126258Smlaier		break;
3765126258Smlaier
3766126258Smlaier	case MOD_UNLOAD:
3767126258Smlaier		destroy_dev(pci_cdev);
3768126258Smlaier		break;
3769126258Smlaier	}
3770126258Smlaier
3771171168Smlaier	return (0);
3772231852Sbz}
3773126258Smlaier
3774171168Smlaiervoid
3775231852Sbzpci_cfg_restore(device_t dev, struct pci_devinfo *dinfo)
3776126258Smlaier{
3777171168Smlaier	int i;
3778126258Smlaier
3779173815Smlaier	/*
3780126258Smlaier	 * Only do header type 0 devices.  Type 1 devices are bridges,
3781173815Smlaier	 * which we know need special treatment.  Type 2 devices are
3782173815Smlaier	 * cardbus bridges which also require special treatment.
3783173815Smlaier	 * Other types are unknown, and we err on the side of safety
3784173815Smlaier	 * by ignoring them.
3785173815Smlaier	 */
3786173815Smlaier	if (dinfo->cfg.hdrtype != 0)
3787173815Smlaier		return;
3788173815Smlaier
3789173815Smlaier	/*
3790173815Smlaier	 * Restore the device to full power mode.  We must do this
3791223637Sbz	 * before we restore the registers because moving from D3 to
3792223637Sbz	 * D0 will cause the chip's BARs and some other registers to
3793126258Smlaier	 * be reset to some unknown power on reset values.  Cut down
3794240233Sglebius	 * the noise on boot by doing nothing if we are already in
3795240233Sglebius	 * state D0.
3796126258Smlaier	 */
3797126258Smlaier	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
3798126258Smlaier		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
3799171168Smlaier	}
3800126258Smlaier	for (i = 0; i < dinfo->cfg.nummaps; i++)
3801126258Smlaier		pci_write_config(dev, PCIR_BAR(i), dinfo->cfg.bar[i], 4);
3802126258Smlaier	pci_write_config(dev, PCIR_BIOS, dinfo->cfg.bios, 4);
3803126258Smlaier	pci_write_config(dev, PCIR_COMMAND, dinfo->cfg.cmdreg, 2);
3804126258Smlaier	pci_write_config(dev, PCIR_INTLINE, dinfo->cfg.intline, 1);
3805126258Smlaier	pci_write_config(dev, PCIR_INTPIN, dinfo->cfg.intpin, 1);
3806126258Smlaier	pci_write_config(dev, PCIR_MINGNT, dinfo->cfg.mingnt, 1);
3807240641Sglebius	pci_write_config(dev, PCIR_MAXLAT, dinfo->cfg.maxlat, 1);
3808240641Sglebius	pci_write_config(dev, PCIR_CACHELNSZ, dinfo->cfg.cachelnsz, 1);
3809240641Sglebius	pci_write_config(dev, PCIR_LATTIMER, dinfo->cfg.lattimer, 1);
3810126258Smlaier	pci_write_config(dev, PCIR_PROGIF, dinfo->cfg.progif, 1);
3811240641Sglebius	pci_write_config(dev, PCIR_REVID, dinfo->cfg.revid, 1);
3812240641Sglebius
3813171168Smlaier	/* Restore MSI and MSI-X configurations if they are present. */
3814126258Smlaier	if (dinfo->cfg.msi.msi_location != 0)
3815126258Smlaier		pci_resume_msi(dev);
3816126258Smlaier	if (dinfo->cfg.msix.msix_location != 0)
3817126258Smlaier		pci_resume_msix(dev);
3818126258Smlaier}
3819126258Smlaier
3820130613Smlaiervoid
3821126258Smlaierpci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate)
3822240233Sglebius{
3823240233Sglebius	int i;
3824126258Smlaier	uint32_t cls;
3825126258Smlaier	int ps;
3826126258Smlaier
3827126258Smlaier	/*
3828240233Sglebius	 * Only do header type 0 devices.  Type 1 devices are bridges, which
3829126258Smlaier	 * we know need special treatment.  Type 2 devices are cardbus bridges
3830126258Smlaier	 * which also require special treatment.  Other types are unknown, and
3831126258Smlaier	 * we err on the side of safety by ignoring them.  Powering down
3832126258Smlaier	 * bridges should not be undertaken lightly.
3833126258Smlaier	 */
3834126258Smlaier	if (dinfo->cfg.hdrtype != 0)
3835126258Smlaier		return;
3836240233Sglebius	for (i = 0; i < dinfo->cfg.nummaps; i++)
3837200930Sdelphij		dinfo->cfg.bar[i] = pci_read_config(dev, PCIR_BAR(i), 4);
3838200930Sdelphij	dinfo->cfg.bios = pci_read_config(dev, PCIR_BIOS, 4);
3839200930Sdelphij
3840126258Smlaier	/*
3841223637Sbz	 * Some drivers apparently write to these registers w/o updating our
3842223637Sbz	 * cached copy.  No harm happens if we update the copy, so do so here
3843223637Sbz	 * so we can restore them.  The COMMAND register is modified by the
3844223637Sbz	 * bus w/o updating the cache.  This should represent the normally
3845223637Sbz	 * writable portion of the 'defined' part of type 0 headers.  In
3846126258Smlaier	 * theory we also need to save/restore the PCI capability structures
3847126258Smlaier	 * we know about, but apart from power we don't know any that are
3848126258Smlaier	 * writable.
3849126258Smlaier	 */
3850126258Smlaier	dinfo->cfg.subvendor = pci_read_config(dev, PCIR_SUBVEND_0, 2);
3851126258Smlaier	dinfo->cfg.subdevice = pci_read_config(dev, PCIR_SUBDEV_0, 2);
3852126258Smlaier	dinfo->cfg.vendor = pci_read_config(dev, PCIR_VENDOR, 2);
3853126258Smlaier	dinfo->cfg.device = pci_read_config(dev, PCIR_DEVICE, 2);
3854126258Smlaier	dinfo->cfg.cmdreg = pci_read_config(dev, PCIR_COMMAND, 2);
3855126258Smlaier	dinfo->cfg.intline = pci_read_config(dev, PCIR_INTLINE, 1);
3856126258Smlaier	dinfo->cfg.intpin = pci_read_config(dev, PCIR_INTPIN, 1);
3857126258Smlaier	dinfo->cfg.mingnt = pci_read_config(dev, PCIR_MINGNT, 1);
3858126258Smlaier	dinfo->cfg.maxlat = pci_read_config(dev, PCIR_MAXLAT, 1);
3859145836Smlaier	dinfo->cfg.cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1);
3860126258Smlaier	dinfo->cfg.lattimer = pci_read_config(dev, PCIR_LATTIMER, 1);
3861126258Smlaier	dinfo->cfg.baseclass = pci_read_config(dev, PCIR_CLASS, 1);
3862126258Smlaier	dinfo->cfg.subclass = pci_read_config(dev, PCIR_SUBCLASS, 1);
3863126258Smlaier	dinfo->cfg.progif = pci_read_config(dev, PCIR_PROGIF, 1);
3864126258Smlaier	dinfo->cfg.revid = pci_read_config(dev, PCIR_REVID, 1);
3865126258Smlaier
3866126258Smlaier	/*
3867126258Smlaier	 * don't set the state for display devices, base peripherals and
3868126258Smlaier	 * memory devices since bad things happen when they are powered down.
3869126258Smlaier	 * We should (a) have drivers that can easily detach and (b) use
3870126258Smlaier	 * generic drivers for these devices so that some device actually
3871126258Smlaier	 * attaches.  We need to make sure that when we implement (a) we don't
3872126258Smlaier	 * power the device down on a reattach.
3873223637Sbz	 */
3874223637Sbz	cls = pci_get_class(dev);
3875126258Smlaier	if (!setstate)
3876126258Smlaier		return;
3877289703Skp	switch (pci_do_power_nodriver)
3878126258Smlaier	{
3879289703Skp		case 0:		/* NO powerdown at all */
3880200930Sdelphij			return;
3881126258Smlaier		case 1:		/* Conservative about what to power down */
3882126258Smlaier			if (cls == PCIC_STORAGE)
3883126258Smlaier				return;
3884126258Smlaier			/*FALLTHROUGH*/
3885126258Smlaier		case 2:		/* Agressive about what to power down */
3886126258Smlaier			if (cls == PCIC_DISPLAY || cls == PCIC_MEMORY ||
3887126258Smlaier			    cls == PCIC_BASEPERIPH)
3888126258Smlaier				return;
3889126258Smlaier			/*FALLTHROUGH*/
3890126258Smlaier		case 3:		/* Power down everything */
3891126258Smlaier			break;
3892126258Smlaier	}
3893126258Smlaier	/*
3894126258Smlaier	 * PCI spec says we can only go into D3 state from D0 state.
3895126258Smlaier	 * Transition from D[12] into D0 before going to D3 state.
3896126258Smlaier	 */
3897126258Smlaier	ps = pci_get_powerstate(dev);
3898126258Smlaier	if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3)
3899126258Smlaier		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
3900126258Smlaier	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3)
3901126258Smlaier		pci_set_powerstate(dev, PCI_POWERSTATE_D3);
3902126258Smlaier}
3903126258Smlaier