pci.c revision 47625
162587Sitojun/*
278064Sume * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
362587Sitojun * All rights reserved.
453541Sshin *
553541Sshin * Redistribution and use in source and binary forms, with or without
653541Sshin * modification, are permitted provided that the following conditions
753541Sshin * are met:
853541Sshin * 1. Redistributions of source code must retain the above copyright
953541Sshin *    notice unmodified, this list of conditions, and the following
1053541Sshin *    disclaimer.
1153541Sshin * 2. Redistributions in binary form must reproduce the above copyright
1253541Sshin *    notice, this list of conditions and the following disclaimer in the
1353541Sshin *    documentation and/or other materials provided with the distribution.
1453541Sshin *
1553541Sshin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1653541Sshin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1753541Sshin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1853541Sshin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1953541Sshin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2053541Sshin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2153541Sshin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2253541Sshin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2353541Sshin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2453541Sshin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2553541Sshin *
2653541Sshin * $Id: pci.c,v 1.105 1999/05/30 10:54:31 dfr Exp $
2753541Sshin *
2853541Sshin */
2953541Sshin
3053541Sshin#include "opt_bus.h"
3153541Sshin
3253541Sshin#include "pci.h"
3353541Sshin#if NPCI > 0
3453541Sshin
3553541Sshin#include "opt_devfs.h"
3653541Sshin#include "opt_simos.h"
3753541Sshin
3853541Sshin#include <sys/param.h>
3953541Sshin#include <sys/systm.h>
4053541Sshin#include <sys/malloc.h>
4153541Sshin#include <sys/module.h>
4253541Sshin#include <sys/fcntl.h>
4353541Sshin#include <sys/conf.h>
4453541Sshin#include <sys/kernel.h>
4553541Sshin#include <sys/queue.h>
4653541Sshin#include <sys/types.h>
4753541Sshin#include <sys/buf.h>
4853541Sshin#ifdef DEVFS
4953541Sshin#include <sys/devfsext.h>
5053541Sshin#endif /* DEVFS */
5153541Sshin
5253541Sshin#include <vm/vm.h>
5353541Sshin#include <vm/pmap.h>
5453541Sshin#include <vm/vm_extern.h>
5553541Sshin
5653541Sshin#include <sys/bus.h>
5753541Sshin#include <machine/bus.h>
5853541Sshin#include <sys/rman.h>
5953541Sshin#include <machine/resource.h>
6053541Sshin
6153541Sshin#include <pci/pcireg.h>
6253541Sshin#include <pci/pcivar.h>
6353541Sshin#include <pci/pci_ioctl.h>
6453541Sshin
6553541Sshin#ifdef APIC_IO
6653541Sshin#include <machine/smp.h>
6753541Sshin#endif /* APIC_IO */
6862587Sitojun
6962587Sitojunstatic STAILQ_HEAD(devlist, pci_devinfo) pci_devq;
7055009Sshinu_int32_t pci_numdevs = 0;
7153541Sshinstatic u_int32_t pci_generation = 0;
7253541Sshin
7395759Stanimura/* return base address of memory or port map */
7495759Stanimura
7595759Stanimurastatic int
7678064Sumepci_mapbase(unsigned mapreg)
7753541Sshin{
7853541Sshin	int mask = 0x03;
7995759Stanimura	if ((mapreg & 0x01) == 0)
8053541Sshin		mask = 0x0f;
8153541Sshin	return (mapreg & ~mask);
8295759Stanimura}
8395759Stanimura
8495759Stanimura/* return map type of memory or port map */
8553541Sshin
8653541Sshinstatic int
8753541Sshinpci_maptype(unsigned mapreg)
8853541Sshin{
8953541Sshin	static u_int8_t maptype[0x10] = {
9095759Stanimura		PCI_MAPMEM,		PCI_MAPPORT,
9153541Sshin		PCI_MAPMEM,		0,
9253541Sshin		PCI_MAPMEM,		PCI_MAPPORT,
9395759Stanimura		0,			0,
9453541Sshin		PCI_MAPMEM|PCI_MAPMEMP,	PCI_MAPPORT,
9562587Sitojun		PCI_MAPMEM|PCI_MAPMEMP, 0,
9695759Stanimura		PCI_MAPMEM|PCI_MAPMEMP,	PCI_MAPPORT,
9795759Stanimura		0,			0,
9895759Stanimura	};
9995759Stanimura
10053541Sshin	return maptype[mapreg & 0x0f];
10153541Sshin}
10253541Sshin
10353541Sshin/* return log2 of map size decoded for memory or port map */
10453541Sshin
10553541Sshinstatic int
10653541Sshinpci_mapsize(unsigned testval)
10755009Sshin{
10853541Sshin	int ln2size;
10953541Sshin
11053541Sshin	testval = pci_mapbase(testval);
11178064Sume	ln2size = 0;
11278064Sume	if (testval != 0) {
11378064Sume		while ((testval & 1) == 0)
11478064Sume		{
11578064Sume			ln2size++;
11678064Sume			testval >>= 1;
11778064Sume		}
11878064Sume	}
11978064Sume	return (ln2size);
12078064Sume}
12178064Sume
12278064Sume/* return log2 of address range supported by map register */
12378064Sume
12478064Sumestatic int
12578064Sumepci_maprange(unsigned mapreg)
12678064Sume{
12778064Sume	int ln2range = 0;
12878064Sume	switch (mapreg & 0x07) {
12978064Sume	case 0x00:
13078064Sume	case 0x01:
13178064Sume	case 0x05:
13278064Sume		ln2range = 32;
13378064Sume		break;
13478064Sume	case 0x02:
13578064Sume		ln2range = 20;
13678064Sume		break;
13778064Sume	case 0x04:
13878064Sume		ln2range = 64;
13978064Sume		break;
14062587Sitojun	}
14153541Sshin	return (ln2range);
14262587Sitojun}
14353541Sshin
14462587Sitojun/* extract map parameters into newly allocated array of pcimap structures */
14562587Sitojun
14662587Sitojunstatic pcimap *
14778064Sumepci_readmaps(pcicfgregs *cfg, int maxmaps)
14862587Sitojun{
14953541Sshin	int i, j = 0;
15062587Sitojun	pcimap *map;
15162587Sitojun	int map64 = 0;
15262587Sitojun	int reg = PCIR_MAPS;
15362587Sitojun
15462587Sitojun	for (i = 0; i < maxmaps; i++) {
15578064Sume		int reg = PCIR_MAPS + i*4;
15678064Sume		u_int32_t base;
15762587Sitojun		u_int32_t ln2range;
15862587Sitojun
15962587Sitojun		base = pci_cfgread(cfg, reg, 4);
16062587Sitojun		ln2range = pci_maprange(base);
16162587Sitojun
16278064Sume		if (base == 0 || ln2range == 0 || base == 0xffffffff)
16362587Sitojun			continue; /* skip invalid entry */
16462587Sitojun		else {
16578064Sume			j++;
16653541Sshin			if (ln2range > 32) {
16753541Sshin				i++;
16862587Sitojun				j++;
16953541Sshin			}
17053541Sshin		}
17178064Sume	}
17253541Sshin
17353541Sshin	map = malloc(j * sizeof (pcimap), M_DEVBUF, M_WAITOK);
17453541Sshin	if (map != NULL) {
17553541Sshin		bzero(map, sizeof(pcimap) * j);
17653541Sshin		cfg->nummaps = j;
17753541Sshin
17862587Sitojun		for (i = 0, j = 0; i < maxmaps; i++, reg += 4) {
17962587Sitojun			u_int32_t base;
18062587Sitojun			u_int32_t testval;
18162587Sitojun
18262587Sitojun			base = pci_cfgread(cfg, reg, 4);
18378064Sume
18462587Sitojun			if (map64 == 0) {
18562587Sitojun				if (base == 0 || base == 0xffffffff)
18662587Sitojun					continue; /* skip invalid entry */
18762587Sitojun				pci_cfgwrite(cfg, reg, 0xffffffff, 4);
18862587Sitojun				testval = pci_cfgread(cfg, reg, 4);
18962587Sitojun				pci_cfgwrite(cfg, reg, base, 4);
19062587Sitojun
19162587Sitojun				map[j].reg	= reg;
19262587Sitojun				map[j].base     = pci_mapbase(base);
19362587Sitojun				map[j].type     = pci_maptype(base);
19462587Sitojun				map[j].ln2size  = pci_mapsize(testval);
19562587Sitojun				map[j].ln2range = pci_maprange(testval);
19662587Sitojun				map64 = map[j].ln2range == 64;
19762587Sitojun			} else {
19862587Sitojun				/* only fill in base, other fields are 0 */
19962587Sitojun				map[j].base     = base;
20062587Sitojun				map64 = 0;
20162587Sitojun			}
20262587Sitojun#ifdef __alpha__
20362587Sitojun			/*
20462587Sitojun			 *  XXX: encode hose number in the base addr,
20562587Sitojun			 *  This will go away once the bus_space functions
20662587Sitojun			 *  can deal with multiple hoses
20778064Sume			 */
20862587Sitojun
20962587Sitojun			if(cfg->hose){
21062587Sitojun				if(map[j].base & 0x80000000){
21162587Sitojun					printf("base   addr = 0x%x\n", map[j].base);
21262587Sitojun					printf("hacked addr = 0x%x\n",
21362587Sitojun					       map[j].base | (cfg->hose << 31));
21462587Sitojun
21562587Sitojun					panic("hose encoding hack would clobber base addr");
21662587Sitojun				}
21778064Sume				if(cfg->hose > 1 )
21862587Sitojun					panic("only one hose supported!");
21962587Sitojun				map[j].base |=  (cfg->hose << 31);
22062587Sitojun			}
22162587Sitojun#endif
22262587Sitojun			j++;
22362587Sitojun		}
22462587Sitojun	}
22562587Sitojun	return (map);
22662587Sitojun}
22762587Sitojun
22862587Sitojun/* adjust some values from PCI 1.0 devices to match 2.0 standards ... */
22962587Sitojun
23062587Sitojunstatic void
23162587Sitojunpci_fixancient(pcicfgregs *cfg)
23262587Sitojun{
23362587Sitojun	if (cfg->hdrtype != 0)
23462587Sitojun		return;
23562587Sitojun
23653541Sshin	/* PCI to PCI bridges use header type 1 */
23753541Sshin	if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI)
23853541Sshin		cfg->hdrtype = 1;
23953541Sshin}
24053541Sshin
24153541Sshin/* read config data specific to header type 1 device (PCI to PCI bridge) */
24253541Sshin
24353541Sshinstatic void *
24453541Sshinpci_readppb(pcicfgregs *cfg)
24553541Sshin{
24662587Sitojun	pcih1cfgregs *p;
24753541Sshin
24862587Sitojun	p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK);
24953541Sshin	if (p == NULL)
25053541Sshin		return (NULL);
25153541Sshin
25262587Sitojun	bzero(p, sizeof *p);
25362587Sitojun
25462587Sitojun	p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2);
25562587Sitojun	p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2);
25662587Sitojun
25762587Sitojun	p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1);
25853541Sshin
25962587Sitojun	p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2),
26062587Sitojun				   pci_cfgread(cfg, PCIR_IOBASEL_1, 1));
26153541Sshin	p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2),
26262587Sitojun				     pci_cfgread(cfg, PCIR_IOLIMITL_1, 1));
26362587Sitojun
26462587Sitojun	p->membase = PCI_PPBMEMBASE (0,
26562587Sitojun				     pci_cfgread(cfg, PCIR_MEMBASE_1, 2));
26662587Sitojun	p->memlimit = PCI_PPBMEMLIMIT (0,
26762587Sitojun				       pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2));
26862587Sitojun
26962587Sitojun	p->pmembase = PCI_PPBMEMBASE (
27062587Sitojun		(pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4),
27153541Sshin		pci_cfgread(cfg, PCIR_PMBASEL_1, 2));
27253541Sshin
27353541Sshin	p->pmemlimit = PCI_PPBMEMLIMIT (
27453541Sshin		(pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4),
27553541Sshin		pci_cfgread(cfg, PCIR_PMLIMITL_1, 2));
27653541Sshin	return (p);
27753541Sshin}
27853541Sshin
27953541Sshin/* read config data specific to header type 2 device (PCI to CardBus bridge) */
28053541Sshin
28153541Sshinstatic void *
28253541Sshinpci_readpcb(pcicfgregs *cfg)
28353541Sshin{
28453541Sshin	pcih2cfgregs *p;
28553541Sshin
28653541Sshin	p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK);
28753541Sshin	if (p == NULL)
28853541Sshin		return (NULL);
28953541Sshin
29053541Sshin	bzero(p, sizeof *p);
29162587Sitojun
29262587Sitojun	p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2);
29353541Sshin	p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2);
29462587Sitojun
29562587Sitojun	p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1);
29662587Sitojun
29753541Sshin	p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4);
29853541Sshin	p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4);
29962587Sitojun	p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4);
30062587Sitojun	p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4);
30162587Sitojun
30262587Sitojun	p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4);
30362587Sitojun	p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4);
30462587Sitojun	p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4);
30562587Sitojun	p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4);
30662587Sitojun
30762587Sitojun	p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4);
30862587Sitojun	return p;
30962587Sitojun}
31062587Sitojun
31162587Sitojun/* extract header type specific config data */
31253541Sshin
31362587Sitojunstatic void
31462587Sitojunpci_hdrtypedata(pcicfgregs *cfg)
31562587Sitojun{
31653541Sshin	switch (cfg->hdrtype) {
31762587Sitojun	case 0:
31862587Sitojun		cfg->subvendor      = pci_cfgread(cfg, PCIR_SUBVEND_0, 2);
31962587Sitojun		cfg->subdevice      = pci_cfgread(cfg, PCIR_SUBDEV_0, 2);
32062587Sitojun		cfg->map            = pci_readmaps(cfg, PCI_MAXMAPS_0);
32153541Sshin		break;
32262587Sitojun	case 1:
32362587Sitojun		cfg->subvendor      = pci_cfgread(cfg, PCIR_SUBVEND_1, 2);
32453541Sshin		cfg->subdevice      = pci_cfgread(cfg, PCIR_SUBDEV_1, 2);
32553541Sshin		cfg->secondarybus   = pci_cfgread(cfg, PCIR_SECBUS_1, 1);
32653541Sshin		cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1);
32753541Sshin		cfg->map            = pci_readmaps(cfg, PCI_MAXMAPS_1);
32853541Sshin		cfg->hdrspec        = pci_readppb(cfg);
32953541Sshin		break;
33053541Sshin	case 2:
33153541Sshin		cfg->subvendor      = pci_cfgread(cfg, PCIR_SUBVEND_2, 2);
33253541Sshin		cfg->subdevice      = pci_cfgread(cfg, PCIR_SUBDEV_2, 2);
33353541Sshin		cfg->secondarybus   = pci_cfgread(cfg, PCIR_SECBUS_2, 1);
33453541Sshin		cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1);
33553541Sshin		cfg->map            = pci_readmaps(cfg, PCI_MAXMAPS_2);
33653541Sshin		cfg->hdrspec        = pci_readpcb(cfg);
33753541Sshin		break;
33853541Sshin	}
33953541Sshin}
34053541Sshin
34162587Sitojun/* read configuration header into pcicfgrect structure */
34262587Sitojun
34362587Sitojunstatic struct pci_devinfo *
34462587Sitojunpci_readcfg(pcicfgregs *probe)
34553541Sshin{
34678064Sume	pcicfgregs *cfg = NULL;
34753541Sshin	struct pci_devinfo *devlist_entry;
34853541Sshin	struct devlist *devlist_head;
34953541Sshin
35053541Sshin	devlist_head = &pci_devq;
35153541Sshin
35253541Sshin	devlist_entry = NULL;
35353541Sshin
35453541Sshin	if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) {
35553541Sshin		devlist_entry = malloc(sizeof(struct pci_devinfo),
35653541Sshin				       M_DEVBUF, M_WAITOK);
35753541Sshin		if (devlist_entry == NULL)
35853541Sshin			return (NULL);
35953541Sshin		bzero(devlist_entry, sizeof *devlist_entry);
36053541Sshin
36153541Sshin		cfg = &devlist_entry->cfg;
36253541Sshin
36353541Sshin		cfg->hose               = probe->hose;
36478064Sume		cfg->bus		= probe->bus;
36578064Sume		cfg->slot		= probe->slot;
36678064Sume		cfg->func		= probe->func;
36778064Sume		cfg->vendor		= pci_cfgread(cfg, PCIR_VENDOR, 2);
36878064Sume		cfg->device		= pci_cfgread(cfg, PCIR_DEVICE, 2);
36978064Sume		cfg->cmdreg		= pci_cfgread(cfg, PCIR_COMMAND, 2);
37078064Sume		cfg->statreg		= pci_cfgread(cfg, PCIR_STATUS, 2);
37178064Sume		cfg->baseclass		= pci_cfgread(cfg, PCIR_CLASS, 1);
37278064Sume		cfg->subclass		= pci_cfgread(cfg, PCIR_SUBCLASS, 1);
37353541Sshin		cfg->progif		= pci_cfgread(cfg, PCIR_PROGIF, 1);
37495023Ssuz		cfg->revid		= pci_cfgread(cfg, PCIR_REVID, 1);
37562587Sitojun		cfg->hdrtype		= pci_cfgread(cfg, PCIR_HEADERTYPE, 1);
37662587Sitojun		cfg->cachelnsz		= pci_cfgread(cfg, PCIR_CACHELNSZ, 1);
37762587Sitojun		cfg->lattimer		= pci_cfgread(cfg, PCIR_LATTIMER, 1);
37862587Sitojun		cfg->intpin		= pci_cfgread(cfg, PCIR_INTPIN, 1);
37962587Sitojun		cfg->intline		= pci_cfgread(cfg, PCIR_INTLINE, 1);
38062587Sitojun#ifdef __alpha__
38162587Sitojun		alpha_platform_assign_pciintr(cfg);
38262587Sitojun#endif
38353541Sshin
38453541Sshin#ifdef APIC_IO
38553541Sshin		if (cfg->intpin != 0) {
38653541Sshin			int airq;
38753541Sshin
38853541Sshin			airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin);
38953541Sshin			if (airq >= 0) {
39053541Sshin				/* PCI specific entry found in MP table */
39153541Sshin				if (airq != cfg->intline) {
39253541Sshin					undirect_pci_irq(cfg->intline);
39353541Sshin					cfg->intline = airq;
39453541Sshin				}
39553541Sshin			} else {
39653541Sshin				/*
39753541Sshin				 * PCI interrupts might be redirected to the
39853541Sshin				 * ISA bus according to some MP tables. Use the
39953541Sshin				 * same methods as used by the ISA devices
40062587Sitojun				 * devices to find the proper IOAPIC int pin.
40153541Sshin				 */
40295023Ssuz				airq = isa_apic_irq(cfg->intline);
40362587Sitojun				if ((airq >= 0) && (airq != cfg->intline)) {
40453541Sshin					/* XXX: undirect_pci_irq() ? */
40553541Sshin					undirect_isa_irq(cfg->intline);
40653541Sshin					cfg->intline = airq;
40753541Sshin				}
40853541Sshin			}
40953541Sshin		}
41053541Sshin#endif /* APIC_IO */
41153541Sshin
41253541Sshin		cfg->mingnt		= pci_cfgread(cfg, PCIR_MINGNT, 1);
41353541Sshin		cfg->maxlat		= pci_cfgread(cfg, PCIR_MAXLAT, 1);
41453541Sshin
41553541Sshin		cfg->mfdev		= (cfg->hdrtype & PCIM_MFDEV) != 0;
41653541Sshin		cfg->hdrtype		&= ~PCIM_MFDEV;
41753541Sshin
41853541Sshin		pci_fixancient(cfg);
41962587Sitojun		pci_hdrtypedata(cfg);
42053541Sshin
42162587Sitojun		STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links);
42262587Sitojun
42362587Sitojun		devlist_entry->conf.pc_sel.pc_bus = cfg->bus;
42462587Sitojun		devlist_entry->conf.pc_sel.pc_dev = cfg->slot;
42562587Sitojun		devlist_entry->conf.pc_sel.pc_func = cfg->func;
42662587Sitojun		devlist_entry->conf.pc_hdr = cfg->hdrtype;
42762587Sitojun
42853541Sshin		devlist_entry->conf.pc_subvendor = cfg->subvendor;
42953541Sshin		devlist_entry->conf.pc_subdevice = cfg->subdevice;
43053541Sshin		devlist_entry->conf.pc_vendor = cfg->vendor;
43178064Sume		devlist_entry->conf.pc_device = cfg->device;
43253541Sshin
43378064Sume		devlist_entry->conf.pc_class = cfg->baseclass;
43453541Sshin		devlist_entry->conf.pc_subclass = cfg->subclass;
43553541Sshin		devlist_entry->conf.pc_progif = cfg->progif;
43653541Sshin		devlist_entry->conf.pc_revid = cfg->revid;
43753541Sshin
43883934Sbrooks		pci_numdevs++;
43953541Sshin		pci_generation++;
44053541Sshin	}
44153541Sshin	return (devlist_entry);
44253541Sshin}
44353541Sshin
44453541Sshin#if 0
44553541Sshin/* free pcicfgregs structure and all depending data structures */
44653541Sshin
44753541Sshinstatic int
44853541Sshinpci_freecfg(struct pci_devinfo *dinfo)
44953541Sshin{
45053541Sshin	struct devlist *devlist_head;
45153541Sshin
45253541Sshin	devlist_head = &pci_devq;
45353541Sshin
45453541Sshin	if (dinfo->cfg.hdrspec != NULL)
45553541Sshin		free(dinfo->cfg.hdrspec, M_DEVBUF);
45653541Sshin	if (dinfo->cfg.map != NULL)
45753541Sshin		free(dinfo->cfg.map, M_DEVBUF);
45853541Sshin	/* XXX this hasn't been tested */
45953541Sshin	STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
46053541Sshin	free(dinfo, M_DEVBUF);
46153541Sshin
46253541Sshin	/* increment the generation count */
46353541Sshin	pci_generation++;
46453541Sshin
46553541Sshin	/* we're losing one device */
46653541Sshin	pci_numdevs--;
46753541Sshin	return (0);
46862587Sitojun}
46962587Sitojun#endif
47053541Sshin
47162587Sitojun
47253541Sshin/*
47362587Sitojun * This is the user interface to PCI configuration space.
47453541Sshin */
47553541Sshin
47653541Sshinstatic int
47762587Sitojunpci_open(dev_t dev, int oflags, int devtype, struct proc *p)
47862587Sitojun{
47962587Sitojun	if ((oflags & FWRITE) && securelevel > 0) {
48062587Sitojun		return EPERM;
48162587Sitojun	}
48262587Sitojun	return 0;
48353541Sshin}
48453541Sshin
48553541Sshinstatic int
48653541Sshinpci_close(dev_t dev, int flag, int devtype, struct proc *p)
48753541Sshin{
48853541Sshin	return 0;
48953541Sshin}
49053541Sshin
49153541Sshin/*
49253541Sshin * Match a single pci_conf structure against an array of pci_match_conf
49353541Sshin * structures.  The first argument, 'matches', is an array of num_matches
49453541Sshin * pci_match_conf structures.  match_buf is a pointer to the pci_conf
49553541Sshin * structure that will be compared to every entry in the matches array.
49653541Sshin * This function returns 1 on failure, 0 on success.
49753541Sshin */
49853541Sshinstatic int
49962587Sitojunpci_conf_match(struct pci_match_conf *matches, int num_matches,
50062587Sitojun	       struct pci_conf *match_buf)
50162587Sitojun{
50262587Sitojun	int i;
50353541Sshin
50453541Sshin	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
50553541Sshin		return(1);
50653541Sshin
50753541Sshin	for (i = 0; i < num_matches; i++) {
50853541Sshin		/*
50953541Sshin		 * I'm not sure why someone would do this...but...
51053541Sshin		 */
51153541Sshin		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
51253541Sshin			continue;
51353541Sshin
51453541Sshin		/*
51553541Sshin		 * Look at each of the match flags.  If it's set, do the
51653541Sshin		 * comparison.  If the comparison fails, we don't have a
51753541Sshin		 * match, go on to the next item if there is one.
51853541Sshin		 */
51953541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
52053541Sshin		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
52153541Sshin			continue;
52253541Sshin
52353541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
52453541Sshin		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
52553541Sshin			continue;
52653541Sshin
52753541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
52853541Sshin		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
52953541Sshin			continue;
53053541Sshin
53153541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
53253541Sshin		 && (match_buf->pc_vendor != matches[i].pc_vendor))
53353541Sshin			continue;
53453541Sshin
53553541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
53653541Sshin		 && (match_buf->pc_device != matches[i].pc_device))
53753541Sshin			continue;
53853541Sshin
53953541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
54053541Sshin		 && (match_buf->pc_class != matches[i].pc_class))
54153541Sshin			continue;
54253541Sshin
54362587Sitojun		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
54462587Sitojun		 && (match_buf->pd_unit != matches[i].pd_unit))
54553541Sshin			continue;
54662587Sitojun
54753541Sshin		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
54853541Sshin		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
54953541Sshin			     sizeof(match_buf->pd_name)) != 0))
55053541Sshin			continue;
55153541Sshin
55262587Sitojun		return(0);
55362587Sitojun	}
55462587Sitojun
55562587Sitojun	return(1);
55662587Sitojun}
55753541Sshin
55862587Sitojunstatic int
55962587Sitojunpci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
56062587Sitojun{
56162587Sitojun	struct pci_io *io;
56262587Sitojun	const char *name;
56362587Sitojun	int error;
56462587Sitojun
56553541Sshin	if (!(flag & FWRITE))
56653541Sshin		return EPERM;
56753541Sshin
56853541Sshin
56953541Sshin	switch(cmd) {
57053541Sshin	case PCIOCGETCONF:
57153541Sshin		{
57253541Sshin		struct pci_devinfo *dinfo;
57353541Sshin		struct pci_conf_io *cio;
57453541Sshin		struct devlist *devlist_head;
57553541Sshin		struct pci_match_conf *pattern_buf;
57653541Sshin		int num_patterns;
57753541Sshin		size_t iolen;
57862587Sitojun		int ionum, i;
57962587Sitojun
58062587Sitojun		cio = (struct pci_conf_io *)data;
58153541Sshin
58262587Sitojun		num_patterns = 0;
58362587Sitojun		dinfo = NULL;
58453541Sshin
58562587Sitojun		/*
58662587Sitojun		 * Hopefully the user won't pass in a null pointer, but it
58762587Sitojun		 * can't hurt to check.
58862587Sitojun		 */
58953541Sshin		if (cio == NULL) {
59053541Sshin			error = EINVAL;
59153541Sshin			break;
59253541Sshin		}
59353541Sshin
59453541Sshin		/*
59553541Sshin		 * If the user specified an offset into the device list,
59653541Sshin		 * but the list has changed since they last called this
59753541Sshin		 * ioctl, tell them that the list has changed.  They will
59853541Sshin		 * have to get the list from the beginning.
59953541Sshin		 */
60053541Sshin		if ((cio->offset != 0)
60153541Sshin		 && (cio->generation != pci_generation)){
60253541Sshin			cio->num_matches = 0;
60353541Sshin			cio->status = PCI_GETCONF_LIST_CHANGED;
60453541Sshin			error = 0;
60553541Sshin			break;
60653541Sshin		}
60753541Sshin
60853541Sshin		/*
60996116Sume		 * Check to see whether the user has asked for an offset
61096116Sume		 * past the end of our list.
61196116Sume		 */
61253541Sshin		if (cio->offset >= pci_numdevs) {
61396116Sume			cio->num_matches = 0;
61453541Sshin			cio->status = PCI_GETCONF_LAST_DEVICE;
61553541Sshin			error = 0;
61653541Sshin			break;
61762587Sitojun		}
61862587Sitojun
61962587Sitojun		/* get the head of the device queue */
62062587Sitojun		devlist_head = &pci_devq;
62162587Sitojun
62262587Sitojun		/*
62362587Sitojun		 * Determine how much room we have for pci_conf structures.
62453541Sshin		 * Round the user's buffer size down to the nearest
62553541Sshin		 * multiple of sizeof(struct pci_conf) in case the user
62653541Sshin		 * didn't specify a multiple of that size.
62796116Sume		 */
62853541Sshin		iolen = min(cio->match_buf_len -
62996116Sume			    (cio->match_buf_len % sizeof(struct pci_conf)),
63053541Sshin			    pci_numdevs * sizeof(struct pci_conf));
63153541Sshin
63253541Sshin		/*
63396116Sume		 * Since we know that iolen is a multiple of the size of
63496116Sume		 * the pciconf union, it's okay to do this.
63595023Ssuz		 */
63653541Sshin		ionum = iolen / sizeof(struct pci_conf);
63762587Sitojun
63853541Sshin		/*
63953541Sshin		 * If this test is true, the user wants the pci_conf
64053541Sshin		 * structures returned to match the supplied entries.
64153541Sshin		 */
64253541Sshin		if ((cio->num_patterns > 0)
64362587Sitojun		 && (cio->pat_buf_len > 0)) {
64462587Sitojun			/*
64562587Sitojun			 * pat_buf_len needs to be:
64653541Sshin			 * num_patterns * sizeof(struct pci_match_conf)
64753541Sshin			 * While it is certainly possible the user just
64862587Sitojun			 * allocated a large buffer, but set the number of
64953541Sshin			 * matches correctly, it is far more likely that
65053541Sshin			 * their kernel doesn't match the userland utility
65153541Sshin			 * they're using.  It's also possible that the user
65253541Sshin			 * forgot to initialize some variables.  Yes, this
65353541Sshin			 * may be overly picky, but I hazard to guess that
65453541Sshin			 * it's far more likely to just catch folks that
65562587Sitojun			 * updated their kernel but not their userland.
65653541Sshin			 */
65753541Sshin			if ((cio->num_patterns *
65862587Sitojun			    sizeof(struct pci_match_conf)) != cio->pat_buf_len){
65962587Sitojun				/* The user made a mistake, return an error*/
66062587Sitojun				cio->status = PCI_GETCONF_ERROR;
66162587Sitojun				printf("pci_ioctl: pat_buf_len %d != "
66262587Sitojun				       "num_patterns (%d) * sizeof(struct "
66353541Sshin				       "pci_match_conf) (%d)\npci_ioctl: "
66453541Sshin				       "pat_buf_len should be = %d\n",
66553541Sshin				       cio->pat_buf_len, cio->num_patterns,
66662587Sitojun				       (int)sizeof(struct pci_match_conf),
66753541Sshin				       (int)sizeof(struct pci_match_conf) *
66878064Sume				       cio->num_patterns);
66978064Sume				printf("pci_ioctl: do your headers match your "
67078064Sume				       "kernel?\n");
67162587Sitojun				cio->num_matches = 0;
67262587Sitojun				error = EINVAL;
67362587Sitojun				break;
67462587Sitojun			}
67562587Sitojun
67662587Sitojun			/*
67762587Sitojun			 * Check the user's buffer to make sure it's readable.
67853541Sshin			 */
67962587Sitojun			if ((error = useracc((caddr_t)cio->patterns,
68062587Sitojun			                     cio->pat_buf_len, B_READ)) != 1){
68162587Sitojun				printf("pci_ioctl: pattern buffer %p, "
68262587Sitojun				       "length %u isn't user accessible for"
68362587Sitojun				       " READ\n", cio->patterns,
68462587Sitojun				       cio->pat_buf_len);
68562587Sitojun				error = EACCES;
68653541Sshin				break;
68753541Sshin			}
68853541Sshin			/*
68953541Sshin			 * Allocate a buffer to hold the patterns.
69078064Sume			 */
69162587Sitojun			pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
69262587Sitojun					     M_WAITOK);
69362587Sitojun			error = copyin(cio->patterns, pattern_buf,
69462587Sitojun				       cio->pat_buf_len);
69553541Sshin			if (error != 0)
69653541Sshin				break;
69753541Sshin			num_patterns = cio->num_patterns;
69853541Sshin
69953541Sshin		} else if ((cio->num_patterns > 0)
70053541Sshin			|| (cio->pat_buf_len > 0)) {
70153541Sshin			/*
70253541Sshin			 * The user made a mistake, spit out an error.
70353541Sshin			 */
70495023Ssuz			cio->status = PCI_GETCONF_ERROR;
70553541Sshin			cio->num_matches = 0;
70695023Ssuz			printf("pci_ioctl: invalid GETCONF arguments\n");
70753541Sshin			error = EINVAL;
70862587Sitojun			break;
70953541Sshin		} else
71053541Sshin			pattern_buf = NULL;
71153541Sshin
71253541Sshin		/*
71353541Sshin		 * Make sure we can write to the match buffer.
71453541Sshin		 */
71553541Sshin		if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len,
71653541Sshin				     B_WRITE)) != 1) {
71753541Sshin			printf("pci_ioctl: match buffer %p, length %u "
71853541Sshin			       "isn't user accessible for WRITE\n",
71953541Sshin			       cio->matches, cio->match_buf_len);
72053541Sshin			error = EACCES;
72153541Sshin			break;
72253541Sshin		}
72353541Sshin
72453541Sshin		/*
72553541Sshin		 * Go through the list of devices and copy out the devices
72653541Sshin		 * that match the user's criteria.
72753541Sshin		 */
72853541Sshin		for (cio->num_matches = 0, error = 0, i = 0,
72953541Sshin		     dinfo = STAILQ_FIRST(devlist_head);
73053541Sshin		     (dinfo != NULL) && (cio->num_matches < ionum)
73153541Sshin		     && (error == 0) && (i < pci_numdevs);
73262587Sitojun		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
73362587Sitojun
73462587Sitojun			if (i < cio->offset)
73562587Sitojun				continue;
73662587Sitojun
73762587Sitojun			/* Populate pd_name and pd_unit */
73862587Sitojun			name = NULL;
73953541Sshin			if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0')
74053541Sshin				name = device_get_name(dinfo->cfg.dev);
74153541Sshin			if (name) {
74253541Sshin				strncpy(dinfo->conf.pd_name, name,
74353541Sshin					sizeof(dinfo->conf.pd_name));
74453541Sshin				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
74553541Sshin				dinfo->conf.pd_unit =
74653541Sshin					device_get_unit(dinfo->cfg.dev);
74753541Sshin			}
74862587Sitojun
74962587Sitojun			if ((pattern_buf == NULL) ||
75062587Sitojun			    (pci_conf_match(pattern_buf, num_patterns,
75162587Sitojun					    &dinfo->conf) == 0)) {
75262587Sitojun
75362587Sitojun				/*
75462587Sitojun				 * If we've filled up the user's buffer,
75553541Sshin				 * break out at this point.  Since we've
75653541Sshin				 * got a match here, we'll pick right back
75753541Sshin				 * up at the matching entry.  We can also
75853541Sshin				 * tell the user that there are more matches
75953541Sshin				 * left.
76053541Sshin				 */
76153541Sshin				if (cio->num_matches >= ionum)
76253541Sshin					break;
76353541Sshin
76462587Sitojun				error = copyout(&dinfo->conf,
76562587Sitojun					        &cio->matches[cio->num_matches],
76662587Sitojun						sizeof(struct pci_conf));
76762587Sitojun				cio->num_matches++;
76862587Sitojun			}
76962587Sitojun		}
77062587Sitojun
77153541Sshin		/*
77253541Sshin		 * Set the pointer into the list, so if the user is getting
77353541Sshin		 * n records at a time, where n < pci_numdevs,
77453541Sshin		 */
77553541Sshin		cio->offset = i;
77653541Sshin
77753541Sshin		/*
77853541Sshin		 * Set the generation, the user will need this if they make
77953541Sshin		 * another ioctl call with offset != 0.
78062587Sitojun		 */
78162587Sitojun		cio->generation = pci_generation;
78262587Sitojun
78362587Sitojun		/*
78462587Sitojun		 * If this is the last device, inform the user so he won't
78562587Sitojun		 * bother asking for more devices.  If dinfo isn't NULL, we
78662587Sitojun		 * know that there are more matches in the list because of
78753541Sshin		 * the way the traversal is done.
78853541Sshin		 */
78953541Sshin		if (dinfo == NULL)
79053541Sshin			cio->status = PCI_GETCONF_LAST_DEVICE;
79153541Sshin		else
79253541Sshin			cio->status = PCI_GETCONF_MORE_DEVS;
79353541Sshin
79453541Sshin		if (pattern_buf != NULL)
79553541Sshin			free(pattern_buf, M_TEMP);
79662587Sitojun
79762587Sitojun		break;
79862587Sitojun		}
79962587Sitojun	case PCIOCREAD:
80062587Sitojun		io = (struct pci_io *)data;
80162587Sitojun		switch(io->pi_width) {
80262587Sitojun			pcicfgregs probe;
80353541Sshin		case 4:
80453541Sshin		case 2:
80553541Sshin		case 1:
80653541Sshin			probe.bus = io->pi_sel.pc_bus;
80753541Sshin			probe.slot = io->pi_sel.pc_dev;
80853541Sshin			probe.func = io->pi_sel.pc_func;
80953541Sshin			io->pi_data = pci_cfgread(&probe,
81053541Sshin						  io->pi_reg, io->pi_width);
81153541Sshin			error = 0;
81253541Sshin			break;
81353541Sshin		default:
81453541Sshin			error = ENODEV;
81578064Sume			break;
81678064Sume		}
81778064Sume		break;
81878064Sume
81978064Sume	case PCIOCWRITE:
82053541Sshin		io = (struct pci_io *)data;
82153541Sshin		switch(io->pi_width) {
82253541Sshin			pcicfgregs probe;
82353541Sshin		case 4:
82453541Sshin		case 2:
82553541Sshin		case 1:
82653541Sshin			probe.bus = io->pi_sel.pc_bus;
82753541Sshin			probe.slot = io->pi_sel.pc_dev;
82853541Sshin			probe.func = io->pi_sel.pc_func;
82978064Sume			pci_cfgwrite(&probe,
83078064Sume				    io->pi_reg, io->pi_data, io->pi_width);
83178064Sume			error = 0;
83253541Sshin			break;
83378064Sume		default:
83478064Sume			error = ENODEV;
83578064Sume			break;
83678064Sume		}
83778064Sume		break;
83878064Sume
83978064Sume	default:
84078064Sume		error = ENOTTY;
84178064Sume		break;
84278064Sume	}
84378064Sume
84478064Sume	return (error);
84578064Sume}
84678064Sume
84778064Sume#define	PCI_CDEV	78
84878064Sume
84978064Sumestatic struct cdevsw pcicdev = {
85078064Sume	/* open */	pci_open,
85178064Sume	/* close */	pci_close,
85278064Sume	/* read */	noread,
85378064Sume	/* write */	nowrite,
85478064Sume	/* ioctl */	pci_ioctl,
85578064Sume	/* stop */	nostop,
85678064Sume	/* reset */	noreset,
85778064Sume	/* devtotty */	nodevtotty,
85878064Sume	/* poll */	nopoll,
85978064Sume	/* mmap */	nommap,
86078064Sume	/* strategy */	nostrategy,
86178064Sume	/* name */	"pci",
86278064Sume	/* parms */	noparms,
86378064Sume	/* maj */	PCI_CDEV,
86478064Sume	/* dump */	nodump,
86578064Sume	/* psize */	nopsize,
86678064Sume	/* flags */	0,
86778064Sume	/* maxio */	0,
86862587Sitojun	/* bmaj */	-1
86978064Sume};
87078064Sume
87178064Sume#ifdef DEVFS
87278064Sumestatic void *pci_devfs_token;
87362587Sitojun#endif
87478064Sume
87578064Sumestatic void
87678064Sumepci_cdevinit(void *dummy)
87778064Sume{
87878064Sume	dev_t dev;
87978064Sume
88062587Sitojun	dev = makedev(PCI_CDEV, 0);
88178064Sume	cdevsw_add(&dev, &pcicdev, NULL);
88253541Sshin#ifdef	DEVFS
88378064Sume	pci_devfs_token = devfs_add_devswf(&pcicdev, 0, DV_CHR,
88478064Sume					   UID_ROOT, GID_WHEEL, 0644, "pci");
88553541Sshin#endif
88653541Sshin}
88753541Sshin
88853541SshinSYSINIT(pcidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+PCI_CDEV, pci_cdevinit, NULL);
88953541Sshin
89062587Sitojun#include "pci_if.h"
89162587Sitojun
89262587Sitojun/*
89362587Sitojun * A simple driver to wrap the old pci driver mechanism for back-compat.
89462587Sitojun */
89562587Sitojun
89653541Sshinstatic int
89795023Ssuzpci_compat_probe(device_t dev)
89853541Sshin{
89953541Sshin	struct pci_device *dvp;
90078064Sume	struct pci_devinfo *dinfo;
90153541Sshin	pcicfgregs *cfg;
90253541Sshin	const char *name;
90353541Sshin	int error;
90462587Sitojun
90553541Sshin	dinfo = device_get_ivars(dev);
90653541Sshin	cfg = &dinfo->cfg;
90778064Sume	dvp = device_get_driver(dev)->priv;
90853541Sshin
90953541Sshin	/*
91062587Sitojun	 * Do the wrapped probe.
91162587Sitojun	 */
91278064Sume	error = ENXIO;
91362587Sitojun	if (dvp && dvp->pd_probe) {
91462587Sitojun		name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor);
91578064Sume		if (name) {
91662587Sitojun			device_set_desc_copy(dev, name);
91762587Sitojun			error = 0;
91862587Sitojun		}
91953541Sshin	}
92053541Sshin
92153541Sshin	return error;
92253541Sshin}
92353541Sshin
92453541Sshinstatic int
92562587Sitojunpci_compat_attach(device_t dev)
92662587Sitojun{
92762587Sitojun	struct pci_device *dvp;
92862587Sitojun	struct pci_devinfo *dinfo;
92962587Sitojun	pcicfgregs *cfg;
93062587Sitojun	int unit;
93162587Sitojun
93262587Sitojun	dinfo = device_get_ivars(dev);
93362587Sitojun	cfg = &dinfo->cfg;
93462587Sitojun	dvp = device_get_driver(dev)->priv;
93562587Sitojun
93678064Sume	unit = device_get_unit(dev);
93762587Sitojun	if (unit > *dvp->pd_count)
93862587Sitojun		*dvp->pd_count = unit;
93962587Sitojun	if (dvp->pd_attach)
94062587Sitojun		dvp->pd_attach(cfg, unit);
94178064Sume	return 0;
94262587Sitojun}
94362587Sitojun
94478064Sumestatic device_method_t pci_compat_methods[] = {
94562587Sitojun	/* Device interface */
94662587Sitojun	DEVMETHOD(device_probe,		pci_compat_probe),
94762587Sitojun	DEVMETHOD(device_attach,	pci_compat_attach),
94862587Sitojun
94962587Sitojun	{ 0, 0 }
95062587Sitojun};
95162587Sitojun
95262587Sitojunstatic devclass_t	pci_devclass;
95362587Sitojun
95462587Sitojun/*
95562587Sitojun * Create a new style driver around each old pci driver.
95662587Sitojun */
95762587Sitojunint
95862587Sitojuncompat_pci_handler(module_t mod, int type, void *data)
95962587Sitojun{
96062587Sitojun	struct pci_device *dvp = (struct pci_device *)data;
96162587Sitojun	driver_t *driver;
96278064Sume
96362587Sitojun	switch (type) {
96462587Sitojun	case MOD_LOAD:
96562587Sitojun		driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT);
96662587Sitojun		if (!driver)
96762587Sitojun			return ENOMEM;
96862587Sitojun		bzero(driver, sizeof(driver_t));
96962587Sitojun		driver->name = dvp->pd_name;
97078064Sume		driver->methods = pci_compat_methods;
97162587Sitojun		driver->softc = sizeof(struct pci_devinfo *);
97262587Sitojun		driver->priv = dvp;
97362587Sitojun		devclass_add_driver(pci_devclass, driver);
97462587Sitojun		break;
97562587Sitojun	case MOD_UNLOAD:
97662587Sitojun		printf("%s: module unload not supported!\n", dvp->pd_name);
97762587Sitojun		return EOPNOTSUPP;
97862587Sitojun	default:
97962587Sitojun		break;
98062587Sitojun	}
98162587Sitojun	return 0;
98262587Sitojun}
98362587Sitojun
98462587Sitojun/*
98578064Sume * New style pci driver.  Parent device is either a pci-host-bridge or a
98662587Sitojun * pci-pci-bridge.  Both kinds are represented by instances of pcib.
98762587Sitojun */
98862587Sitojun
98962587Sitojunstatic void
99078064Sumepci_print_verbose(struct pci_devinfo *dinfo)
99162587Sitojun{
99262587Sitojun	if (bootverbose) {
99378064Sume		int i;
99462587Sitojun		pcicfgregs *cfg = &dinfo->cfg;
99562587Sitojun
99662587Sitojun		printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n",
99762587Sitojun		       cfg->vendor, cfg->device, cfg->revid);
99862587Sitojun		printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n",
99962587Sitojun		       cfg->baseclass, cfg->subclass, cfg->progif,
100062587Sitojun		       cfg->hdrtype, cfg->mfdev);
100162587Sitojun		printf("\tsubordinatebus=%x \tsecondarybus=%x\n",
100262587Sitojun		       cfg->subordinatebus, cfg->secondarybus);
100362587Sitojun#ifdef PCI_DEBUG
100462587Sitojun		printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n",
100562587Sitojun		       cfg->cmdreg, cfg->statreg, cfg->cachelnsz);
100662587Sitojun		printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n",
100762587Sitojun		       cfg->lattimer, cfg->lattimer * 30,
100853541Sshin		       cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250);
100962587Sitojun#endif /* PCI_DEBUG */
101062587Sitojun		if (cfg->intpin > 0)
101195023Ssuz			printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline);
101262587Sitojun
101362587Sitojun		for (i = 0; i < cfg->nummaps; i++) {
101462587Sitojun			pcimap *m = &cfg->map[i];
101562587Sitojun			printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n",
101662587Sitojun			       i, m->type, m->ln2range, m->base, m->ln2size);
101753541Sshin		}
101853541Sshin	}
101953541Sshin}
102078064Sume
102162587Sitojunstatic int
102253541Sshinpci_add_children(device_t dev, int busno)
102362587Sitojun{
102462587Sitojun	pcicfgregs probe;
102578064Sume	int bushigh = busno;
102662587Sitojun
102762587Sitojun#ifdef SIMOS
102878064Sume#undef PCI_SLOTMAX
102962587Sitojun#define PCI_SLOTMAX 0
103062587Sitojun#endif
103178064Sume
103278064Sume	bzero(&probe, sizeof probe);
103378064Sume#ifdef __alpha__
103478064Sume	probe.hose = pcib_get_hose(dev);
103578064Sume#endif
103678064Sume#ifdef __i386__
103778064Sume	probe.hose = 0;
103878064Sume#endif
103978064Sume	probe.bus = busno;
104078064Sume
104178064Sume	for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) {
104278064Sume		int pcifunchigh = 0;
104378064Sume		for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) {
104478064Sume			struct pci_devinfo *dinfo = pci_readcfg(&probe);
104578064Sume			if (dinfo != NULL) {
104678064Sume				if (dinfo->cfg.mfdev)
104778064Sume					pcifunchigh = 7;
104878064Sume
104978064Sume				pci_print_verbose(dinfo);
105078064Sume				dinfo->cfg.dev =
105178064Sume					device_add_child(dev, NULL, -1, dinfo);
105278064Sume
105378064Sume				if (bushigh < dinfo->cfg.subordinatebus)
105478064Sume					bushigh = dinfo->cfg.subordinatebus;
105578064Sume				if (bushigh < dinfo->cfg.secondarybus)
105678064Sume					bushigh = dinfo->cfg.secondarybus;
105778064Sume			}
105878064Sume		}
105978064Sume	}
106078064Sume
106178064Sume	return bushigh;
106278064Sume}
106378064Sume
106478064Sumestatic int
106578064Sumepci_new_probe(device_t dev)
106678064Sume{
106778064Sume	device_set_desc(dev, "PCI bus");
106878064Sume
106978064Sume	pci_add_children(dev, device_get_unit(dev));
107078064Sume
107178064Sume	return 0;
107278064Sume}
107378064Sume
107478064Sumestatic void
107578064Sumepci_print_child(device_t dev, device_t child)
107678064Sume{
107778064Sume	struct pci_devinfo *dinfo;
107878064Sume	pcicfgregs *cfg;
107978064Sume
108078064Sume	dinfo = device_get_ivars(child);
108178064Sume	cfg = &dinfo->cfg;
108278064Sume	if (cfg->intpin > 0 && cfg->intline != 255)
108378064Sume		printf(" irq %d", cfg->intline);
108462587Sitojun	printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child));
108578064Sume	printf(" on %s%d", device_get_name(dev), device_get_unit(dev));
108678064Sume}
108778064Sume
108862587Sitojunstatic int
108962587Sitojunpci_read_ivar(device_t dev, device_t child, int which, u_long *result)
109053541Sshin{
109153541Sshin	struct pci_devinfo *dinfo;
109253541Sshin	pcicfgregs *cfg;
109378064Sume
109478064Sume	dinfo = device_get_ivars(child);
109553541Sshin	cfg = &dinfo->cfg;
109653541Sshin
109778064Sume	switch (which) {
109853541Sshin	case PCI_IVAR_SUBVENDOR:
109978064Sume		*result = cfg->subvendor;
110053541Sshin		break;
110178064Sume	case PCI_IVAR_SUBDEVICE:
110253541Sshin		*result = cfg->subdevice;
110353541Sshin		break;
110478064Sume	case PCI_IVAR_VENDOR:
110578064Sume		*result = cfg->vendor;
110678064Sume		break;
110778064Sume	case PCI_IVAR_DEVICE:
110862587Sitojun		*result = cfg->device;
110978064Sume		break;
111078064Sume	case PCI_IVAR_DEVID:
111178064Sume		*result = (cfg->device << 16) | cfg->vendor;
111262587Sitojun		break;
111362587Sitojun	case PCI_IVAR_CLASS:
111462587Sitojun		*result = cfg->baseclass;
111562587Sitojun		break;
111678064Sume	case PCI_IVAR_SUBCLASS:
111778064Sume		*result = cfg->subclass;
111878064Sume		break;
111962587Sitojun	case PCI_IVAR_PROGIF:
112062587Sitojun		*result = cfg->progif;
112162587Sitojun		break;
112262587Sitojun	case PCI_IVAR_REVID:
112378064Sume		*result = cfg->revid;
112478064Sume		break;
112578064Sume	case PCI_IVAR_INTPIN:
112678064Sume		*result = cfg->intpin;
112778064Sume		break;
112862587Sitojun	case PCI_IVAR_IRQ:
112962587Sitojun		*result = cfg->intline;
113062587Sitojun		break;
113162587Sitojun	case PCI_IVAR_BUS:
113262587Sitojun		*result = cfg->bus;
113362587Sitojun		break;
113462587Sitojun	case PCI_IVAR_SLOT:
113562587Sitojun		*result = cfg->slot;
113662587Sitojun		break;
113762587Sitojun	case PCI_IVAR_FUNCTION:
113862587Sitojun		*result = cfg->func;
113978064Sume		break;
114062587Sitojun	case PCI_IVAR_SECONDARYBUS:
114162587Sitojun		*result = cfg->secondarybus;
114262587Sitojun		break;
114395023Ssuz	case PCI_IVAR_SUBORDINATEBUS:
114462587Sitojun		*result = cfg->subordinatebus;
114595023Ssuz		break;
114662587Sitojun	case PCI_IVAR_HOSE:
114762587Sitojun		/*
114853541Sshin		 * Pass up to parent bridge.
114978064Sume		 */
115078064Sume		*result = pcib_get_hose(dev);
115162587Sitojun		break;
115262587Sitojun	default:
115362587Sitojun		return ENOENT;
115462587Sitojun	}
115562587Sitojun	return 0;
115662587Sitojun}
115762587Sitojun
115853541Sshinstatic int
115962587Sitojunpci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
116053541Sshin{
116153541Sshin	struct pci_devinfo *dinfo;
116253541Sshin	pcicfgregs *cfg;
116353541Sshin
116453541Sshin	dinfo = device_get_ivars(child);
116562587Sitojun	cfg = &dinfo->cfg;
116653541Sshin
116762587Sitojun	switch (which) {
116862587Sitojun	case PCI_IVAR_SUBVENDOR:
116953541Sshin	case PCI_IVAR_SUBDEVICE:
117053541Sshin	case PCI_IVAR_VENDOR:
117153541Sshin	case PCI_IVAR_DEVICE:
117253541Sshin	case PCI_IVAR_DEVID:
117378064Sume	case PCI_IVAR_CLASS:
117478064Sume	case PCI_IVAR_SUBCLASS:
117562587Sitojun	case PCI_IVAR_PROGIF:
117662587Sitojun	case PCI_IVAR_REVID:
117778064Sume	case PCI_IVAR_INTPIN:
117878064Sume	case PCI_IVAR_IRQ:
117953541Sshin	case PCI_IVAR_BUS:
118062587Sitojun	case PCI_IVAR_SLOT:
118162587Sitojun	case PCI_IVAR_FUNCTION:
118262587Sitojun		return EINVAL;	/* disallow for now */
118362587Sitojun
118462587Sitojun	case PCI_IVAR_SECONDARYBUS:
118562587Sitojun		cfg->secondarybus = value;
118662587Sitojun		break;
118762587Sitojun	case PCI_IVAR_SUBORDINATEBUS:
118862587Sitojun		cfg->subordinatebus = value;
118962587Sitojun		break;
119053541Sshin	default:
119162587Sitojun		return ENOENT;
119262587Sitojun	}
119362587Sitojun	return 0;
119478064Sume}
119578064Sume
119678064Sumestatic int
119778064Sumepci_mapno(pcicfgregs *cfg, int reg)
119862587Sitojun{
119962587Sitojun	int i, nummaps;
120062587Sitojun	pcimap *map;
120162587Sitojun
120262587Sitojun	nummaps = cfg->nummaps;
120362587Sitojun	map = cfg->map;
120478064Sume
120578064Sume	for (i = 0; i < nummaps; i++)
120678064Sume		if (map[i].reg == reg)
120778064Sume			return (i);
120878064Sume	return (-1);
120978064Sume}
121078064Sume
121178064Sumestatic int
121278064Sumepci_porten(pcicfgregs *cfg)
121378064Sume{
121478064Sume	return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0);
121562587Sitojun}
121662587Sitojun
121762587Sitojunstatic int
121878064Sumepci_isportmap(pcicfgregs *cfg, int map)
121962587Sitojun
122062587Sitojun{
122162587Sitojun	return ((unsigned)map < cfg->nummaps
122262587Sitojun		&& (cfg->map[map].type & PCI_MAPPORT) != 0);
122362587Sitojun}
122478064Sume
122578064Sumestatic int
122678064Sumepci_memen(pcicfgregs *cfg)
122778064Sume{
122862587Sitojun	return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0);
122962587Sitojun}
123062587Sitojun
123162587Sitojunstatic int
123262587Sitojunpci_ismemmap(pcicfgregs *cfg, int map)
123362587Sitojun{
123462587Sitojun	return ((unsigned)map < cfg->nummaps
123562587Sitojun		&& (cfg->map[map].type & PCI_MAPMEM) != 0);
123662587Sitojun}
123762587Sitojun
123862587Sitojunstatic struct resource *
123978064Sumepci_alloc_resource(device_t dev, device_t child, int type, int *rid,
124078064Sume		   u_long start, u_long end, u_long count, u_int flags)
124162587Sitojun{
124262587Sitojun	int isdefault;
124362587Sitojun	struct pci_devinfo *dinfo = device_get_ivars(child);
124478064Sume	pcicfgregs *cfg = &dinfo->cfg;
124578064Sume	struct resource *rv, **rvp = 0;
124678064Sume	int map;
124778064Sume
124862587Sitojun	isdefault = (device_get_parent(child) == dev
124962587Sitojun		     && start == 0UL && end == ~0UL);
125062587Sitojun
125162587Sitojun	switch (type) {
125262587Sitojun	case SYS_RES_IRQ:
125362587Sitojun		if (*rid != 0)
125462587Sitojun			return 0;
125578064Sume		if (isdefault && cfg->intline != 255) {
125678064Sume			start = cfg->intline;
125762587Sitojun			end = cfg->intline;
125862587Sitojun			count = 1;
125962587Sitojun		}
126062587Sitojun		break;
126162587Sitojun
126262587Sitojun	case SYS_RES_DRQ:		/* passthru for child isa */
126362587Sitojun		break;
126462587Sitojun
126562587Sitojun	case SYS_RES_MEMORY:
126662587Sitojun		if (isdefault) {
126762587Sitojun			map = pci_mapno(cfg, *rid);
126862587Sitojun			if (pci_memen(cfg) && pci_ismemmap(cfg, map)) {
126978064Sume				start = cfg->map[map].base;
127078064Sume				count = 1 << cfg->map[map].ln2size;
127178064Sume				end = start + count;
127278064Sume				rvp = &cfg->map[map].res;
127362587Sitojun			} else
127478064Sume				return 0;
127578064Sume		}
127678064Sume		break;
127778064Sume
127878064Sume	case SYS_RES_IOPORT:
127978064Sume		if (isdefault) {
128078064Sume			map = pci_mapno(cfg, *rid);
128178064Sume			if (pci_porten(cfg) && pci_isportmap(cfg, map)) {
128278064Sume				start = cfg->map[map].base;
128378064Sume				count = 1 << cfg->map[map].ln2size;
128478064Sume				end = start + count;
128562587Sitojun				rvp = &cfg->map[map].res;
128678064Sume			} else
128762587Sitojun				return 0;
128862587Sitojun		}
128962587Sitojun		break;
129062587Sitojun
129162587Sitojun	default:
129262587Sitojun		return 0;
129362587Sitojun	}
129462587Sitojun
129562587Sitojun	rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
129662587Sitojun				 type, rid, start, end, count, flags);
129762587Sitojun	if (rvp)
129862587Sitojun		*rvp = rv;
129962587Sitojun
130062587Sitojun	return rv;
130162587Sitojun}
130262587Sitojun
130362587Sitojunstatic int
130462587Sitojunpci_release_resource(device_t dev, device_t child, int type, int rid,
130562587Sitojun		     struct resource *r)
130662587Sitojun{
130762587Sitojun	int rv;
130862587Sitojun	struct pci_devinfo *dinfo = device_get_ivars(child);
130962587Sitojun	pcicfgregs *cfg = &dinfo->cfg;
131062587Sitojun	int map = 0;
131162587Sitojun
131262587Sitojun	switch (type) {
131362587Sitojun	case SYS_RES_IRQ:
131462587Sitojun		if (rid != 0)
131562587Sitojun			return EINVAL;
131662587Sitojun		break;
131762587Sitojun
131862587Sitojun	case SYS_RES_DRQ:		/* passthru for child isa */
131962587Sitojun		break;
132062587Sitojun
132162587Sitojun	case SYS_RES_MEMORY:
132262587Sitojun	case SYS_RES_IOPORT:
132362587Sitojun		/*
132478064Sume		 * Only check the map registers if this is a direct
132562587Sitojun		 * descendant.
132662587Sitojun		 */
132762587Sitojun		if (device_get_parent(child) == dev)
132862587Sitojun			map = pci_mapno(cfg, rid);
132978064Sume		else
133062587Sitojun			map = -1;
133178064Sume		break;
133278064Sume
133378064Sume	default:
133478064Sume		return (ENOENT);
133578064Sume	}
133678064Sume
133778064Sume	rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r);
133878064Sume
133978064Sume	if (rv == 0) {
134078064Sume		switch (type) {
134178064Sume		case SYS_RES_IRQ:
134278064Sume			cfg->irqres = 0;
134378064Sume			break;
134478064Sume
134578064Sume		case SYS_RES_DRQ:	/* passthru for child isa */
134678064Sume			break;
134778064Sume
134878064Sume		case SYS_RES_MEMORY:
134978064Sume		case SYS_RES_IOPORT:
135078064Sume			if (map != -1)
135178064Sume				cfg->map[map].res = 0;
135278064Sume			break;
135378064Sume
135478064Sume		default:
135578064Sume			return ENOENT;
135678064Sume		}
135778064Sume	}
135878064Sume
135978064Sume	return rv;
136062587Sitojun}
136178064Sume
136278064Sumestatic u_int32_t
136378064Sumepci_read_config_method(device_t dev, device_t child, int reg, int width)
136478064Sume{
136578064Sume	struct pci_devinfo *dinfo = device_get_ivars(child);
136678064Sume	pcicfgregs *cfg = &dinfo->cfg;
136778064Sume	return pci_cfgread(cfg, reg, width);
136878064Sume}
136978064Sume
137078064Sumestatic void
137178064Sumepci_write_config_method(device_t dev, device_t child, int reg,
137278064Sume			u_int32_t val, int width)
137378064Sume{
137478064Sume	struct pci_devinfo *dinfo = device_get_ivars(child);
137562587Sitojun	pcicfgregs *cfg = &dinfo->cfg;
137662587Sitojun	pci_cfgwrite(cfg, reg, val, width);
137778064Sume}
137853541Sshin
137962587Sitojunstatic int
138062587Sitojunpci_modevent(module_t mod, int what, void *arg)
138153541Sshin{
138262587Sitojun	switch (what) {
138353541Sshin	case MOD_LOAD:
138453541Sshin		STAILQ_INIT(&pci_devq);
138562587Sitojun		break;
138678064Sume
138778064Sume	case MOD_UNLOAD:
138878064Sume		break;
138978064Sume	}
139053541Sshin
139162587Sitojun	return 0;
139253541Sshin}
139353541Sshin
139453541Sshinstatic device_method_t pci_methods[] = {
139553541Sshin	/* Device interface */
139653541Sshin	DEVMETHOD(device_probe,		pci_new_probe),
139753541Sshin	DEVMETHOD(device_attach,	bus_generic_attach),
139853541Sshin	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
139953541Sshin	DEVMETHOD(device_suspend,	bus_generic_suspend),
140053541Sshin	DEVMETHOD(device_resume,	bus_generic_resume),
140153541Sshin
140262587Sitojun	/* Bus interface */
140353541Sshin	DEVMETHOD(bus_print_child,	pci_print_child),
140453541Sshin	DEVMETHOD(bus_read_ivar,	pci_read_ivar),
140553541Sshin	DEVMETHOD(bus_write_ivar,	pci_write_ivar),
140662587Sitojun	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
140778064Sume	DEVMETHOD(bus_alloc_resource,	pci_alloc_resource),
140862587Sitojun	DEVMETHOD(bus_release_resource,	pci_release_resource),
140962587Sitojun	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
141062587Sitojun	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
141178064Sume	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
141278064Sume	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
141378064Sume
141478064Sume	/* PCI interface */
141578064Sume	DEVMETHOD(pci_read_config,	pci_read_config_method),
141678064Sume	DEVMETHOD(pci_write_config,	pci_write_config_method),
141778064Sume
141862587Sitojun	{ 0, 0 }
141978064Sume};
142062587Sitojun
142178064Sumestatic driver_t pci_driver = {
142262587Sitojun	"pci",
142362587Sitojun	pci_methods,
142462587Sitojun	1,			/* no softc */
142562587Sitojun};
142662587Sitojun
142762587SitojunDRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0);
142862587Sitojun
142962587Sitojun#endif /* NPCI > 0 */
143062587Sitojun