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