pci_cfgreg.c revision 102976
167754Smsmith/*
267754Smsmith * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
367754Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
484491Smsmith * Copyright (c) 2000, BSDi
567754Smsmith * All rights reserved.
667754Smsmith *
767754Smsmith * Redistribution and use in source and binary forms, with or without
867754Smsmith * modification, are permitted provided that the following conditions
967754Smsmith * are met:
1067754Smsmith * 1. Redistributions of source code must retain the above copyright
1167754Smsmith *    notice unmodified, this list of conditions, and the following
1271867Smsmith *    disclaimer.
1370243Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1467754Smsmith *    notice, this list of conditions and the following disclaimer in the
1567754Smsmith *    documentation and/or other materials provided with the distribution.
1667754Smsmith *
1767754Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1867754Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1967754Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2067754Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2167754Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2267754Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2367754Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2467754Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2567754Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2667754Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2767754Smsmith *
2867754Smsmith * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 102976 2002-09-05 17:07:07Z jhb $
2967754Smsmith *
3067754Smsmith */
3167754Smsmith
3267754Smsmith#include <sys/param.h>		/* XXX trim includes */
3367754Smsmith#include <sys/systm.h>
3467754Smsmith#include <sys/bus.h>
3567754Smsmith#include <sys/kernel.h>
3667754Smsmith#include <sys/module.h>
3767754Smsmith#include <sys/malloc.h>
3867754Smsmith#include <vm/vm.h>
3967754Smsmith#include <vm/pmap.h>
4067754Smsmith#include <machine/md_var.h>
4167754Smsmith#include <dev/pci/pcivar.h>
4267754Smsmith#include <dev/pci/pcireg.h>
4367754Smsmith#include <isa/isavar.h>
4467754Smsmith#include <machine/nexusvar.h>
4567754Smsmith#include <machine/pci_cfgreg.h>
4667754Smsmith#include <machine/segments.h>
4767754Smsmith#include <machine/pc/bios.h>
4867754Smsmith
4967754Smsmith#ifdef APIC_IO
5067754Smsmith#include <machine/smp.h>
5167754Smsmith#endif /* APIC_IO */
5267754Smsmith
5367754Smsmith#include "pcib_if.h"
5467754Smsmith
5567754Smsmith#define PRVERB(a) printf a
5667754Smsmith
5767754Smsmithstatic int cfgmech;
5867754Smsmithstatic int devmax;
5967754Smsmithstatic int usebios;
6067754Smsmithstatic int enable_pcibios = 0;
6167754Smsmith
6267754SmsmithTUNABLE_INT("hw.pci.enable_pcibios", &enable_pcibios);
6367754Smsmith
6467754Smsmithstatic int	pci_cfgintr_unique(struct PIR_entry *pe, int pin);
6567754Smsmithstatic int	pci_cfgintr_linked(struct PIR_entry *pe, int pin);
6667754Smsmithstatic int	pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin);
6767754Smsmithstatic int	pci_cfgintr_virgin(struct PIR_entry *pe, int pin);
6867754Smsmith
6967754Smsmithstatic int	pcibios_cfgread(int bus, int slot, int func, int reg, int bytes);
7067754Smsmithstatic void	pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
7167754Smsmithstatic int	pcibios_cfgopen(void);
7267754Smsmithstatic int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
7367754Smsmithstatic void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
7467754Smsmithstatic int	pcireg_cfgopen(void);
7567754Smsmith
7667754Smsmithstatic struct PIR_table	*pci_route_table;
7767754Smsmithstatic int		pci_route_count;
7867754Smsmith
7967754Smsmith/*
8067754Smsmith * Some BIOS writers seem to want to ignore the spec and put
8167754Smsmith * 0 in the intline rather than 255 to indicate none.  Some use
8267754Smsmith * numbers in the range 128-254 to indicate something strange and
8367754Smsmith * apparently undocumented anywhere.  Assume these are completely bogus
8467754Smsmith * and map them to 255, which means "none".
8567754Smsmith */
8667754Smsmithstatic __inline__ int
8767754Smsmithpci_i386_map_intline(int line)
8867754Smsmith{
8967754Smsmith	if (line == 0 || line >= 128)
9067754Smsmith		return (PCI_INVALID_IRQ);
9167754Smsmith	return (line);
9267754Smsmith}
9367754Smsmith
9467754Smsmithint
9567754Smsmithpci_pcibios_active(void)
9667754Smsmith{
9767754Smsmith	return (usebios);
9867754Smsmith}
9967754Smsmith
10067754Smsmithint
10167754Smsmithpci_kill_pcibios(void)
10267754Smsmith{
10367754Smsmith	usebios = 0;
10467754Smsmith	return (pcireg_cfgopen() != 0);
10567754Smsmith}
10667754Smsmith
10767754Smsmithstatic u_int16_t
10867754Smsmithpcibios_get_version(void)
10967754Smsmith{
11067754Smsmith	struct bios_regs args;
11167754Smsmith
11267754Smsmith	if (PCIbios.ventry == 0) {
11367754Smsmith		PRVERB(("pcibios: No call entry point\n"));
11467754Smsmith		return (0);
11567754Smsmith	}
11667754Smsmith	args.eax = PCIBIOS_BIOS_PRESENT;
11767754Smsmith	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
11867754Smsmith		PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
11967754Smsmith		return (0);
12067754Smsmith	}
12167754Smsmith	if (args.edx != 0x20494350) {
12267754Smsmith		PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
12367754Smsmith		return (0);
12467754Smsmith	}
12567754Smsmith	return (args.ebx & 0xffff);
12667754Smsmith}
12777424Smsmith
12867754Smsmith/*
12967754Smsmith * Initialise access to PCI configuration space
13067754Smsmith */
13167754Smsmithint
13267754Smsmithpci_cfgregopen(void)
13369746Smsmith{
13467754Smsmith	static int		opened = 0;
13567754Smsmith	u_long			sigaddr;
13667754Smsmith	static struct PIR_table	*pt;
13767754Smsmith	u_int8_t		ck, *cv;
13867754Smsmith	int			i;
13967754Smsmith
14067754Smsmith	if (opened)
14167754Smsmith		return(1);
14267754Smsmith
14367754Smsmith	if (pcibios_cfgopen() != 0)
14467754Smsmith		usebios = 1;
14569746Smsmith	else if (pcireg_cfgopen() != 0)
14667754Smsmith		usebios = 0;
14769746Smsmith	else
14867754Smsmith		return(0);
14967754Smsmith
15069746Smsmith	/*
15167754Smsmith	 * Look for the interrupt routing table.
15267754Smsmith	 *
15377424Smsmith	 * We use PCI BIOS's PIR table if it's available $PIR is the
15477424Smsmith	 * standard way to do this.  Sadly, some machines are not
15577424Smsmith	 * standards conforming and have _PIR instead.  We shrug and cope
15669746Smsmith	 * by looking for both.
15769746Smsmith	 */
15867754Smsmith	if (pcibios_get_version() >= 0x0210 && pt == NULL) {
15982367Smsmith		sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0);
16069746Smsmith		if (sigaddr == 0)
16169746Smsmith			sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0);
16267754Smsmith		if (sigaddr != 0) {
16367754Smsmith			pt = (struct PIR_table *)(uintptr_t)
16469746Smsmith			    BIOS_PADDRTOVADDR(sigaddr);
16569746Smsmith			for (cv = (u_int8_t *)pt, ck = 0, i = 0;
16683174Smsmith			     i < (pt->pt_header.ph_length); i++) {
16782367Smsmith				ck += cv[i];
16869746Smsmith			}
16969746Smsmith			if (ck == 0) {
17069746Smsmith				pci_route_table = pt;
17167754Smsmith				pci_route_count = (pt->pt_header.ph_length -
17269746Smsmith				    sizeof(struct PIR_header)) /
17369746Smsmith				    sizeof(struct PIR_entry);
17482367Smsmith				printf("Using $PIR table, %d entries at %p\n",
17582367Smsmith				    pci_route_count, pci_route_table);
17669746Smsmith			}
17769746Smsmith		}
17869746Smsmith	}
17969746Smsmith	opened = 1;
18069746Smsmith	return(1);
18169746Smsmith}
18269746Smsmith
18382367Smsmith/*
18482367Smsmith * Read configuration space register
18577424Smsmith */
18669746Smsmithstatic u_int32_t
18767754Smsmithpci_do_cfgregread(int bus, int slot, int func, int reg, int bytes)
18867754Smsmith{
18967754Smsmith	return(usebios ?
19067754Smsmith	    pcibios_cfgread(bus, slot, func, reg, bytes) :
19167754Smsmith	    pcireg_cfgread(bus, slot, func, reg, bytes));
19267754Smsmith}
19367754Smsmith
19469746Smsmithu_int32_t
19567754Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes)
19667754Smsmith{
19767754Smsmith	uint32_t line;
19867754Smsmith#ifdef APIC_IO
19967754Smsmith	uint32_t pin;
20067754Smsmith
20167754Smsmith	/*
20267754Smsmith	 * If we are using the APIC, the contents of the intline
20367754Smsmith	 * register will probably be wrong (since they are set up for
20467754Smsmith	 * use with the PIC.  Rather than rewrite these registers
20567754Smsmith	 * (maybe that would be smarter) we trap attempts to read them
20667754Smsmith	 * and translate to our private vector numbers.
20769746Smsmith	 */
20867754Smsmith	if ((reg == PCIR_INTLINE) && (bytes == 1)) {
20969746Smsmith
21067754Smsmith		pin = pci_do_cfgregread(bus, slot, func, PCIR_INTPIN, 1);
21167754Smsmith		line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1);
21269746Smsmith
21367754Smsmith		if (pin != 0) {
21467754Smsmith			int airq;
21577424Smsmith
21677424Smsmith			airq = pci_apic_irq(bus, slot, pin);
21777424Smsmith			if (airq >= 0) {
21869746Smsmith				/* PCI specific entry found in MP table */
21969746Smsmith				if (airq != line)
22069746Smsmith					undirect_pci_irq(line);
22182367Smsmith				return(airq);
22269746Smsmith			} else {
22369746Smsmith				/*
22469746Smsmith				 * PCI interrupts might be redirected
22567754Smsmith				 * to the ISA bus according to some MP
22669746Smsmith				 * tables. Use the same methods as
22767754Smsmith				 * used by the ISA devices devices to
22882367Smsmith				 * find the proper IOAPIC int pin.
22982367Smsmith				 */
23069746Smsmith				airq = isa_apic_irq(line);
23169746Smsmith				if ((airq >= 0) && (airq != line)) {
23269746Smsmith					/* XXX: undirect_pci_irq() ? */
23369746Smsmith					undirect_isa_irq(line);
23469746Smsmith					return(airq);
23569746Smsmith				}
23669746Smsmith			}
23769746Smsmith		}
23882367Smsmith		return(line);
23982367Smsmith	}
24069746Smsmith#else
24171867Smsmith	/*
24267754Smsmith	 * Some BIOS writers seem to want to ignore the spec and put
24367754Smsmith	 * 0 in the intline rather than 255 to indicate none.  The rest of
24469746Smsmith	 * the code uses 255 as an invalid IRQ.
24567754Smsmith	 */
24669746Smsmith	if (reg == PCIR_INTLINE && bytes == 1) {
24767754Smsmith		line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1);
24869746Smsmith		return pci_i386_map_intline(line);
24969746Smsmith	}
25067754Smsmith#endif /* APIC_IO */
25182367Smsmith	return(pci_do_cfgregread(bus, slot, func, reg, bytes));
25282367Smsmith}
25377424Smsmith
25469746Smsmith/*
25569746Smsmith * Write configuration space register
25669746Smsmith */
25769746Smsmithvoid
25869746Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
25969746Smsmith{
26069746Smsmith    return (usebios ?
26169746Smsmith	pcibios_cfgwrite(bus, slot, func, reg, data, bytes) :
26269746Smsmith	pcireg_cfgwrite(bus, slot, func, reg, data, bytes));
26369746Smsmith}
26469746Smsmith
26569746Smsmith/*
26669746Smsmith * Route a PCI interrupt
26769746Smsmith *
26869746Smsmith * XXX we don't do anything "right" with the function number in the PIR table
26969746Smsmith *     (because the consumer isn't currently passing it in).  We don't care
27069746Smsmith *     anyway, due to the way PCI interrupts are assigned.
27169746Smsmith */
27269746Smsmithint
27369746Smsmithpci_cfgintr(int bus, int device, int pin)
27469746Smsmith{
27569746Smsmith	struct PIR_entry	*pe;
27669746Smsmith	int			i, irq;
27769746Smsmith	struct bios_regs	args;
27869746Smsmith	u_int16_t		v;
27969746Smsmith	int already = 0;
28069746Smsmith
28169746Smsmith	v = pcibios_get_version();
28269746Smsmith	if (v < 0x0210) {
28377424Smsmith		PRVERB((
28477424Smsmith		"pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n",
28577424Smsmith		    (v & 0xff00) >> 8, v & 0xff));
28669746Smsmith		return (PCI_INVALID_IRQ);
28769746Smsmith	}
28869746Smsmith	if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
28969746Smsmith	    (pin < 1) || (pin > 4))
29069746Smsmith		return(PCI_INVALID_IRQ);
29169746Smsmith
29269746Smsmith	/*
29369746Smsmith	 * Scan the entry table for a contender
29482367Smsmith	 */
29569746Smsmith	for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count;
29671867Smsmith	     i++, pe++) {
29769746Smsmith		if ((bus != pe->pe_bus) || (device != pe->pe_device))
29869746Smsmith			continue;
29969746Smsmith
30069746Smsmith		irq = pci_cfgintr_linked(pe, pin);
30169746Smsmith		if (irq == PCI_INVALID_IRQ)
30269746Smsmith			irq = pci_cfgintr_unique(pe, pin);
30369746Smsmith		if (irq != PCI_INVALID_IRQ)
30469746Smsmith			already = 1;
30569746Smsmith		if (irq == PCI_INVALID_IRQ)
30669746Smsmith			irq = pci_cfgintr_virgin(pe, pin);
30769746Smsmith		if (irq == PCI_INVALID_IRQ)
30869746Smsmith			break;
30969746Smsmith
31069746Smsmith		/*
31169746Smsmith		 * Ask the BIOS to route the interrupt
31282367Smsmith		 */
31377424Smsmith		args.eax = PCIBIOS_ROUTE_INTERRUPT;
31469746Smsmith		args.ebx = (bus << 8) | (device << 3);
31569746Smsmith		/* pin value is 0xa - 0xd */
31669746Smsmith		args.ecx = (irq << 8) | (0xa + pin - 1);
31769746Smsmith		if (!already &&
31869746Smsmith		    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
31969746Smsmith			/*
32082367Smsmith			 * XXX if it fails, we should try to smack the router
32171867Smsmith			 * hardware directly.
32269746Smsmith			 * XXX Also, there may be other choices that we can
32369746Smsmith			 * try that will work.
32469746Smsmith			 */
32569746Smsmith			PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n"));
32671867Smsmith			return(PCI_INVALID_IRQ);
32769746Smsmith		}
32869746Smsmith		printf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus,
32969746Smsmith		    device, 'A' + pin - 1, irq);
33069746Smsmith		return(irq);
33169746Smsmith	}
33269746Smsmith
33369746Smsmith	PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c\n", bus,
33469746Smsmith	    device, 'A' + pin - 1));
33569746Smsmith	return(PCI_INVALID_IRQ);
33669746Smsmith}
33769746Smsmith
33869746Smsmith/*
33969746Smsmith * Look to see if the routing table claims this pin is uniquely routed.
34069746Smsmith */
34169746Smsmithstatic int
34269746Smsmithpci_cfgintr_unique(struct PIR_entry *pe, int pin)
34369746Smsmith{
34469746Smsmith	int		irq;
34569746Smsmith	uint32_t	irqmask;
34669746Smsmith
34777424Smsmith	irqmask = pe->pe_intpin[pin - 1].irqs;
34877424Smsmith	if (irqmask != 0 && powerof2(irqmask)) {
34977424Smsmith		irq = ffs(irqmask) - 1;
35069746Smsmith		PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq));
35169746Smsmith		return(irq);
35269746Smsmith	}
35382367Smsmith	return(PCI_INVALID_IRQ);
35482367Smsmith}
35569746Smsmith
35669746Smsmith/*
35769746Smsmith * Look for another device which shares the same link byte and
35869746Smsmith * already has a unique IRQ, or which has had one routed already.
35969746Smsmith */
36069746Smsmithstatic int
36182367Smsmithpci_cfgintr_linked(struct PIR_entry *pe, int pin)
36271867Smsmith{
36369746Smsmith	struct PIR_entry	*oe;
36469746Smsmith	struct PIR_intpin	*pi;
36569746Smsmith	int			i, j, irq;
36669746Smsmith
36769746Smsmith	/*
36869746Smsmith	 * Scan table slots.
36969746Smsmith	 */
37069746Smsmith	for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count;
37169746Smsmith	     i++, oe++) {
37269746Smsmith		/* scan interrupt pins */
37369746Smsmith		for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) {
37469746Smsmith
37569746Smsmith			/* don't look at the entry we're trying to match */
37669746Smsmith			if ((pe == oe) && (i == (pin - 1)))
37769746Smsmith				continue;
37867754Smsmith			/* compare link bytes */
37967754Smsmith			if (pi->link != pe->pe_intpin[pin - 1].link)
38069746Smsmith				continue;
38167754Smsmith			/* link destination mapped to a unique interrupt? */
38282367Smsmith			if (pi->irqs != 0 && powerof2(pi->irqs)) {
38369746Smsmith				irq = ffs(pi->irqs) - 1;
38471867Smsmith				PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n",
38567754Smsmith				    pi->link, irq));
38667754Smsmith				return(irq);
38782367Smsmith			}
38877424Smsmith
38969746Smsmith			/*
39070243Smsmith			 * look for the real PCI device that matches this
39170243Smsmith			 * table entry
39269746Smsmith			 */
39369746Smsmith			irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device,
39467754Smsmith			    j, pin);
39569746Smsmith			if (irq != PCI_INVALID_IRQ)
39669746Smsmith				return(irq);
39769746Smsmith		}
39871867Smsmith	}
39969746Smsmith	return(PCI_INVALID_IRQ);
40069746Smsmith}
40169746Smsmith
40269746Smsmith/*
40369746Smsmith * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and
40469746Smsmith * see if it has already been assigned an interrupt.
40571867Smsmith */
40669746Smsmithstatic int
40769746Smsmithpci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin)
40869746Smsmith{
40969746Smsmith	devclass_t		pci_devclass;
41069746Smsmith	device_t		*pci_devices;
41169746Smsmith	int			pci_count;
41269746Smsmith	device_t		*pci_children;
41369746Smsmith	int			pci_childcount;
41469746Smsmith	device_t		*busp, *childp;
41569746Smsmith	int			i, j, irq;
41669746Smsmith
41777424Smsmith	/*
41877424Smsmith	 * Find all the PCI busses.
41977424Smsmith	 */
42069746Smsmith	pci_count = 0;
42169746Smsmith	if ((pci_devclass = devclass_find("pci")) != NULL)
42269746Smsmith		devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
42382367Smsmith
42471867Smsmith	/*
42569746Smsmith	 * Scan all the PCI busses/devices looking for this one.
42669746Smsmith	 */
42769746Smsmith	irq = PCI_INVALID_IRQ;
42869746Smsmith	for (i = 0, busp = pci_devices; (i < pci_count) && (irq == PCI_INVALID_IRQ);
42982367Smsmith	     i++, busp++) {
43082367Smsmith		pci_childcount = 0;
43169746Smsmith		device_get_children(*busp, &pci_children, &pci_childcount);
43269746Smsmith
43369746Smsmith		for (j = 0, childp = pci_children; j < pci_childcount; j++,
43469746Smsmith		    childp++) {
43569746Smsmith			if ((pci_get_bus(*childp) == bus) &&
43669746Smsmith			    (pci_get_slot(*childp) == device) &&
43782367Smsmith			    (pci_get_intpin(*childp) == matchpin)) {
43869746Smsmith				irq = pci_i386_map_intline(pci_get_irq(*childp));
43969746Smsmith				if (irq != PCI_INVALID_IRQ)
44069746Smsmith					PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n",
44169746Smsmith					    pe->pe_intpin[pin - 1].link, irq,
44269746Smsmith					    pci_get_bus(*childp),
44369746Smsmith					    pci_get_slot(*childp),
44469746Smsmith					    pci_get_function(*childp)));
44569746Smsmith				break;
44682367Smsmith			}
44777424Smsmith		}
44869746Smsmith		if (pci_children != NULL)
44967754Smsmith			free(pci_children, M_TEMP);
45067754Smsmith	}
45167754Smsmith	if (pci_devices != NULL)
45267754Smsmith		free(pci_devices, M_TEMP);
45367754Smsmith	return(irq);
45467754Smsmith}
45567754Smsmith
45669746Smsmith/*
45769746Smsmith * Pick a suitable IRQ from those listed as routable to this device.
45869746Smsmith */
45969746Smsmithstatic int
46069746Smsmithpci_cfgintr_virgin(struct PIR_entry *pe, int pin)
46169746Smsmith{
46269746Smsmith	int		irq, ibit;
46370243Smsmith
46469746Smsmith	/*
46569746Smsmith	 * first scan the set of PCI-only interrupts and see if any of these
46669746Smsmith	 * are routable
46769746Smsmith	 */
46869746Smsmith	for (irq = 0; irq < 16; irq++) {
46969746Smsmith		ibit = (1 << irq);
47069746Smsmith
47169746Smsmith		/* can we use this interrupt? */
47269746Smsmith		if ((pci_route_table->pt_header.ph_pci_irqs & ibit) &&
47382367Smsmith		    (pe->pe_intpin[pin - 1].irqs & ibit)) {
47470243Smsmith			PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq));
47582367Smsmith			return(irq);
47677424Smsmith		}
47769746Smsmith	}
47869746Smsmith
47969746Smsmith	/* life is tough, so just pick an interrupt */
48069746Smsmith	for (irq = 0; irq < 16; irq++) {
48169746Smsmith		ibit = (1 << irq);
48284491Smsmith		if (pe->pe_intpin[pin - 1].irqs & ibit) {
48377424Smsmith			PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq));
48470243Smsmith			return(irq);
48582367Smsmith		}
48669746Smsmith	}
48769746Smsmith	return(PCI_INVALID_IRQ);
48869746Smsmith}
48969746Smsmith
49069746Smsmith
49169746Smsmith/*
49269746Smsmith * Config space access using BIOS functions
49369746Smsmith */
49469746Smsmithstatic int
49569746Smsmithpcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
49669746Smsmith{
49769746Smsmith	struct bios_regs args;
49869746Smsmith	u_int mask;
49969746Smsmith
50070243Smsmith	switch(bytes) {
50169746Smsmith	case 1:
50269746Smsmith		args.eax = PCIBIOS_READ_CONFIG_BYTE;
50369746Smsmith		mask = 0xff;
50469746Smsmith		break;
50569746Smsmith	case 2:
50669746Smsmith		args.eax = PCIBIOS_READ_CONFIG_WORD;
50769746Smsmith		mask = 0xffff;
50869746Smsmith		break;
50969746Smsmith	case 4:
51082367Smsmith		args.eax = PCIBIOS_READ_CONFIG_DWORD;
51169746Smsmith		mask = 0xffffffff;
51282367Smsmith		break;
51369746Smsmith	default:
51469746Smsmith		return(-1);
51569746Smsmith	}
51669746Smsmith	args.ebx = (bus << 8) | (slot << 3) | func;
51782367Smsmith	args.edi = reg;
51869746Smsmith	bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
51969746Smsmith	/* check call results? */
52069746Smsmith	return(args.ecx & mask);
52169746Smsmith}
52269746Smsmith
52377424Smsmithstatic void
52469746Smsmithpcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
52582367Smsmith{
52682367Smsmith	struct bios_regs args;
52769746Smsmith
52869746Smsmith	switch(bytes) {
52977424Smsmith	case 1:
53069746Smsmith		args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
53169746Smsmith		break;
53269746Smsmith	case 2:
53369746Smsmith		args.eax = PCIBIOS_WRITE_CONFIG_WORD;
53469746Smsmith		break;
53569746Smsmith	case 4:
53669746Smsmith		args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
53767754Smsmith		break;
53867754Smsmith	default:
53967754Smsmith		return;
54067754Smsmith	}
54167754Smsmith	args.ebx = (bus << 8) | (slot << 3) | func;
54267754Smsmith	args.ecx = data;
54367754Smsmith	args.edi = reg;
54467754Smsmith	bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
54567754Smsmith}
54667754Smsmith
54767754Smsmith/*
54867754Smsmith * Determine whether there is a PCI BIOS present
54967754Smsmith */
55067754Smsmithstatic int
55167754Smsmithpcibios_cfgopen(void)
55267754Smsmith{
55367754Smsmith	u_int16_t		v = 0;
55467754Smsmith
55567754Smsmith	if (PCIbios.ventry != 0 && enable_pcibios) {
55667754Smsmith		v = pcibios_get_version();
55767754Smsmith		if (v > 0)
55867754Smsmith			printf("pcibios: BIOS version %x.%02x\n",
55967754Smsmith			    (v & 0xff00) >> 8, v & 0xff);
56067754Smsmith	}
56167754Smsmith	return (v > 0);
56267754Smsmith}
56367754Smsmith
56477424Smsmith/*
56567754Smsmith * Configuration space access using direct register operations
56667754Smsmith */
56767754Smsmith
56867754Smsmith/* enable configuration space accesses and return data port address */
56967754Smsmithstatic int
57067754Smsmithpci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
57167754Smsmith{
57267754Smsmith	int dataport = 0;
57367754Smsmith
57467754Smsmith	if (bus <= PCI_BUSMAX
57567754Smsmith	    && slot < devmax
57667754Smsmith	    && func <= PCI_FUNCMAX
57767754Smsmith	    && reg <= PCI_REGMAX
57867754Smsmith	    && bytes != 3
57967754Smsmith	    && (unsigned) bytes <= 4
58067754Smsmith	    && (reg & (bytes -1)) == 0) {
58167754Smsmith		switch (cfgmech) {
58267754Smsmith		case 1:
58367754Smsmith			outl(CONF1_ADDR_PORT, (1 << 31)
58467754Smsmith			    | (bus << 16) | (slot << 11)
58567754Smsmith			    | (func << 8) | (reg & ~0x03));
58667754Smsmith			dataport = CONF1_DATA_PORT + (reg & 0x03);
58767754Smsmith			break;
58867754Smsmith		case 2:
58967754Smsmith			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
59067754Smsmith			outb(CONF2_FORWARD_PORT, bus);
59182367Smsmith			dataport = 0xc000 | (slot << 8) | reg;
59267754Smsmith			break;
59367754Smsmith		}
59467754Smsmith	}
59567754Smsmith	return (dataport);
59667754Smsmith}
59767754Smsmith
59882367Smsmith/* disable configuration space accesses */
59982367Smsmithstatic void
60067754Smsmithpci_cfgdisable(void)
60167754Smsmith{
60267754Smsmith	switch (cfgmech) {
60367754Smsmith	case 1:
60467754Smsmith		outl(CONF1_ADDR_PORT, 0);
60567754Smsmith		break;
60667754Smsmith	case 2:
60767754Smsmith		outb(CONF2_ENABLE_PORT, 0);
60867754Smsmith		outb(CONF2_FORWARD_PORT, 0);
60982367Smsmith		break;
61077424Smsmith	}
61169746Smsmith}
61267754Smsmith
61367754Smsmithstatic int
61467754Smsmithpcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
61567754Smsmith{
61667754Smsmith	int data = -1;
61784491Smsmith	int port;
61867754Smsmith
61967754Smsmith	port = pci_cfgenable(bus, slot, func, reg, bytes);
62067754Smsmith
62167754Smsmith	if (port != 0) {
62267754Smsmith		switch (bytes) {
62367754Smsmith		case 1:
62467754Smsmith			data = inb(port);
62567754Smsmith			break;
62667754Smsmith		case 2:
62767754Smsmith			data = inw(port);
62867754Smsmith			break;
62967754Smsmith		case 4:
63067754Smsmith			data = inl(port);
63167754Smsmith			break;
63267754Smsmith		}
63367754Smsmith		pci_cfgdisable();
63467754Smsmith	}
63567754Smsmith	return (data);
63667754Smsmith}
63782367Smsmith
63867754Smsmithstatic void
63967754Smsmithpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
64067754Smsmith{
64167754Smsmith	int port;
64267754Smsmith
64367754Smsmith	port = pci_cfgenable(bus, slot, func, reg, bytes);
64482367Smsmith	if (port != 0) {
64582367Smsmith		switch (bytes) {
64667754Smsmith		case 1:
64777424Smsmith			outb(port, data);
64867754Smsmith			break;
64967754Smsmith		case 2:
65067754Smsmith			outw(port, data);
65167754Smsmith			break;
65267754Smsmith		case 4:
65367754Smsmith			outl(port, data);
65467754Smsmith			break;
65567754Smsmith		}
65667754Smsmith		pci_cfgdisable();
65767754Smsmith	}
65867754Smsmith}
65982367Smsmith
66082367Smsmith/* check whether the configuration mechanism has been correctly identified */
66167754Smsmithstatic int
66277424Smsmithpci_cfgcheck(int maxdev)
66367754Smsmith{
66467754Smsmith	u_char device;
66567754Smsmith
66667754Smsmith	if (bootverbose)
66767754Smsmith		printf("pci_cfgcheck:\tdevice ");
66867754Smsmith
66967754Smsmith	for (device = 0; device < maxdev; device++) {
67067754Smsmith		unsigned id, class, header;
67182367Smsmith		if (bootverbose)
67277424Smsmith			printf("%d ", device);
67367754Smsmith
67467754Smsmith		id = inl(pci_cfgenable(0, device, 0, 0, 4));
67567754Smsmith		if (id == 0 || id == -1)
67667754Smsmith			continue;
67784491Smsmith
67867754Smsmith		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
67967754Smsmith		if (bootverbose)
68067754Smsmith			printf("[class=%06x] ", class);
68167754Smsmith		if (class == 0 || (class & 0xf870ff) != 0)
68267754Smsmith			continue;
68367754Smsmith
68467754Smsmith		header = inb(pci_cfgenable(0, device, 0, 14, 1));
68567754Smsmith		if (bootverbose)
68667754Smsmith			printf("[hdr=%02x] ", header);
68767754Smsmith		if ((header & 0x7e) != 0)
68867754Smsmith			continue;
68967754Smsmith
69067754Smsmith		if (bootverbose)
69167754Smsmith			printf("is there (id=%08x)\n", id);
69267754Smsmith
69367754Smsmith		pci_cfgdisable();
69467754Smsmith		return (1);
69567754Smsmith	}
69667754Smsmith	if (bootverbose)
69767754Smsmith		printf("-- nothing found\n");
69867754Smsmith
69967754Smsmith	pci_cfgdisable();
70082367Smsmith	return (0);
70167754Smsmith}
70282367Smsmith
70367754Smsmithstatic int
70467754Smsmithpcireg_cfgopen(void)
70567754Smsmith{
70667754Smsmith	unsigned long mode1res,oldval1;
70767754Smsmith	unsigned char mode2res,oldval2;
70867754Smsmith
70982367Smsmith	oldval1 = inl(CONF1_ADDR_PORT);
71082367Smsmith
71167754Smsmith	if (bootverbose) {
71267754Smsmith		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
71367754Smsmith		    oldval1);
71467754Smsmith	}
71567754Smsmith
71667754Smsmith	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
71767754Smsmith
71867754Smsmith		cfgmech = 1;
71967754Smsmith		devmax = 32;
72067754Smsmith
72182367Smsmith		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
72267754Smsmith		outb(CONF1_ADDR_PORT +3, 0);
72367754Smsmith		mode1res = inl(CONF1_ADDR_PORT);
72467754Smsmith		outl(CONF1_ADDR_PORT, oldval1);
72567754Smsmith
72667754Smsmith		if (bootverbose)
72767754Smsmith			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
72867754Smsmith			    mode1res, CONF1_ENABLE_CHK);
72967754Smsmith
73067754Smsmith		if (mode1res) {
73167754Smsmith			if (pci_cfgcheck(32))
73267754Smsmith				return (cfgmech);
73367754Smsmith		}
73467754Smsmith
73567754Smsmith		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
73667754Smsmith		mode1res = inl(CONF1_ADDR_PORT);
73767754Smsmith		outl(CONF1_ADDR_PORT, oldval1);
73867754Smsmith
73967754Smsmith		if (bootverbose)
74067754Smsmith			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
74167754Smsmith			    mode1res, CONF1_ENABLE_CHK1);
74267754Smsmith
74367754Smsmith		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
74467754Smsmith			if (pci_cfgcheck(32))
74567754Smsmith				return (cfgmech);
74667754Smsmith		}
74767754Smsmith	}
74867754Smsmith
74967754Smsmith	oldval2 = inb(CONF2_ENABLE_PORT);
75082367Smsmith
75167754Smsmith	if (bootverbose) {
75282367Smsmith		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
75367754Smsmith		    oldval2);
75467754Smsmith	}
75567754Smsmith
75667754Smsmith	if ((oldval2 & 0xf0) == 0) {
75767754Smsmith
75867754Smsmith		cfgmech = 2;
75982367Smsmith		devmax = 16;
76082367Smsmith
76167754Smsmith		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
76267754Smsmith		mode2res = inb(CONF2_ENABLE_PORT);
76367754Smsmith		outb(CONF2_ENABLE_PORT, oldval2);
76467754Smsmith
76567754Smsmith		if (bootverbose)
76667754Smsmith			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
76767754Smsmith			    mode2res, CONF2_ENABLE_CHK);
76867754Smsmith
76967754Smsmith		if (mode2res == CONF2_ENABLE_RES) {
77067754Smsmith			if (bootverbose)
77177424Smsmith				printf("pci_open(2a):\tnow trying mechanism 2\n");
77267754Smsmith
77367754Smsmith			if (pci_cfgcheck(16))
77467754Smsmith				return (cfgmech);
77567754Smsmith		}
77682367Smsmith	}
77767754Smsmith
77867754Smsmith	cfgmech = 0;
77967754Smsmith	devmax = 0;
78067754Smsmith	return (cfgmech);
78167754Smsmith}
78267754Smsmith
78367754Smsmith