xref: /freebsd/sys/i386/pci/pci_cfgreg.c (revision bb0d0a8efc748ae3a7a6f639d373bac067cf8ba1)
1ac19f918SStefan Eßer /*
25bec6157SStefan Eßer  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
312a02d6eSMike Smith  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
412a02d6eSMike Smith  * Copyright (c) 2000, BSDi
55bec6157SStefan Eßer  * All rights reserved.
65bec6157SStefan Eßer  *
75bec6157SStefan Eßer  * Redistribution and use in source and binary forms, with or without
85bec6157SStefan Eßer  * modification, are permitted provided that the following conditions
95bec6157SStefan Eßer  * are met:
105bec6157SStefan Eßer  * 1. Redistributions of source code must retain the above copyright
115bec6157SStefan Eßer  *    notice unmodified, this list of conditions, and the following
125bec6157SStefan Eßer  *    disclaimer.
135bec6157SStefan Eßer  * 2. Redistributions in binary form must reproduce the above copyright
145bec6157SStefan Eßer  *    notice, this list of conditions and the following disclaimer in the
155bec6157SStefan Eßer  *    documentation and/or other materials provided with the distribution.
165bec6157SStefan Eßer  *
175bec6157SStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
185bec6157SStefan Eßer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
195bec6157SStefan Eßer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
205bec6157SStefan Eßer  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
215bec6157SStefan Eßer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
225bec6157SStefan Eßer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235bec6157SStefan Eßer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245bec6157SStefan Eßer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255bec6157SStefan Eßer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
265bec6157SStefan Eßer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275bec6157SStefan Eßer  *
28c3aac50fSPeter Wemm  * $FreeBSD$
295bec6157SStefan Eßer  *
30ac19f918SStefan Eßer  */
31ac19f918SStefan Eßer 
3212a02d6eSMike Smith #include <sys/param.h>		/* XXX trim includes */
335bec6157SStefan Eßer #include <sys/systm.h>
348dc26439SPeter Wemm #include <sys/bus.h>
358dc26439SPeter Wemm #include <sys/kernel.h>
362a50a6d7SMike Smith #include <sys/module.h>
37280b4748SPeter Wemm #include <sys/malloc.h>
3854c9005fSWarner Losh #include <vm/vm.h>
3954c9005fSWarner Losh #include <vm/pmap.h>
4054c9005fSWarner Losh #include <machine/md_var.h>
415bec6157SStefan Eßer #include <pci/pcivar.h>
4285001303SMike Smith #include <pci/pcireg.h>
432a50a6d7SMike Smith #include <isa/isavar.h>
44b6c84078SPeter Wemm #include <machine/nexusvar.h>
4512a02d6eSMike Smith #include <machine/pci_cfgreg.h>
46300451c4SMike Smith #include <machine/segments.h>
47300451c4SMike Smith #include <machine/pc/bios.h>
48300451c4SMike Smith 
49bb0d0a8eSMike Smith #ifdef APIC_IO
50bb0d0a8eSMike Smith #include <machine/smp.h>
51bb0d0a8eSMike Smith #endif /* APIC_IO */
52bb0d0a8eSMike Smith 
5321c3015aSDoug Rabson #include "pcib_if.h"
5421c3015aSDoug Rabson 
555bec6157SStefan Eßer static int cfgmech;
565bec6157SStefan Eßer static int devmax;
57300451c4SMike Smith static int usebios;
58300451c4SMike Smith 
59099d058bSMike Smith static int	pci_cfgintr_unique(struct PIR_entry *pe, int pin);
60099d058bSMike Smith static int	pci_cfgintr_linked(struct PIR_entry *pe, int pin);
61099d058bSMike Smith static int	pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin);
62099d058bSMike Smith static int	pci_cfgintr_virgin(struct PIR_entry *pe, int pin);
63099d058bSMike Smith 
6412a02d6eSMike Smith static int	pcibios_cfgread(int bus, int slot, int func, int reg, int bytes);
6512a02d6eSMike Smith static void	pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
66300451c4SMike Smith static int	pcibios_cfgopen(void);
6712a02d6eSMike Smith static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
6812a02d6eSMike Smith static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
69300451c4SMike Smith static int	pcireg_cfgopen(void);
70300451c4SMike Smith 
71099d058bSMike Smith static struct PIR_table	*pci_route_table;
7254c9005fSWarner Losh static int		pci_route_count;
7354c9005fSWarner Losh 
7412a02d6eSMike Smith /*
7512a02d6eSMike Smith  * Initialise access to PCI configuration space
7612a02d6eSMike Smith  */
7712a02d6eSMike Smith int
7812a02d6eSMike Smith pci_cfgregopen(void)
7921c3015aSDoug Rabson {
8012a02d6eSMike Smith     static int			opened = 0;
8154c9005fSWarner Losh     u_long			sigaddr;
8254c9005fSWarner Losh     static struct PIR_table	*pt;
8354c9005fSWarner Losh     u_int8_t			ck, *cv;
8454c9005fSWarner Losh     int				i;
8521c3015aSDoug Rabson 
8612a02d6eSMike Smith     if (opened)
8712a02d6eSMike Smith 	return(1);
88300451c4SMike Smith 
89300451c4SMike Smith     if (pcibios_cfgopen() != 0) {
90300451c4SMike Smith 	usebios = 1;
91300451c4SMike Smith     } else if (pcireg_cfgopen() != 0) {
92300451c4SMike Smith 	usebios = 0;
93300451c4SMike Smith     } else {
94300451c4SMike Smith 	return(0);
95300451c4SMike Smith     }
9654c9005fSWarner Losh 
9754c9005fSWarner Losh     /*
9854c9005fSWarner Losh      * Look for the interrupt routing table.
9954c9005fSWarner Losh      */
10054c9005fSWarner Losh     /* XXX use PCI BIOS if it's available */
10154c9005fSWarner Losh 
10254c9005fSWarner Losh     if ((pt == NULL) && ((sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0)) != 0)) {
10354c9005fSWarner Losh 	pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
10454c9005fSWarner Losh 	for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) {
10554c9005fSWarner Losh 	    ck += cv[i];
10654c9005fSWarner Losh 	}
10754c9005fSWarner Losh 	if (ck == 0) {
108099d058bSMike Smith 	    pci_route_table = pt;
10954c9005fSWarner Losh 	    pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry);
11054c9005fSWarner Losh 	    printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table);
11154c9005fSWarner Losh 	}
11254c9005fSWarner Losh     }
11354c9005fSWarner Losh 
11412a02d6eSMike Smith     opened = 1;
115300451c4SMike Smith     return(1);
116300451c4SMike Smith }
117300451c4SMike Smith 
11812a02d6eSMike Smith /*
11912a02d6eSMike Smith  * Read configuration space register
12012a02d6eSMike Smith  */
12112a02d6eSMike Smith u_int32_t
122bb0d0a8eSMike Smith pci_do_cfgregread(int bus, int slot, int func, int reg, int bytes)
12312a02d6eSMike Smith {
12412a02d6eSMike Smith     return(usebios ?
12512a02d6eSMike Smith 	   pcibios_cfgread(bus, slot, func, reg, bytes) :
12612a02d6eSMike Smith 	   pcireg_cfgread(bus, slot, func, reg, bytes));
12712a02d6eSMike Smith }
128300451c4SMike Smith 
129bb0d0a8eSMike Smith u_int32_t
130bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
131bb0d0a8eSMike Smith {
132bb0d0a8eSMike Smith #ifdef APIC_IO
133bb0d0a8eSMike Smith     /*
134bb0d0a8eSMike Smith      * If we are using the APIC, the contents of the intline register will probably
135bb0d0a8eSMike Smith      * be wrong (since they are set up for use with the PIC.
136bb0d0a8eSMike Smith      * Rather than rewrite these registers (maybe that would be smarter) we trap
137bb0d0a8eSMike Smith      * attempts to read them and translate to our private vector numbers.
138bb0d0a8eSMike Smith      */
139bb0d0a8eSMike Smith     if ((reg == PCIR_INTLINE) && (bytes == 1)) {
140bb0d0a8eSMike Smith 	int	pin, line, airq;
141bb0d0a8eSMike Smith 
142bb0d0a8eSMike Smith 	pin = pci_do_cfgregread(bus, slot, func, PCIR_INTPIN, 1);
143bb0d0a8eSMike Smith 	line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1);
144bb0d0a8eSMike Smith 
145bb0d0a8eSMike Smith 	if (pin != 0) {
146bb0d0a8eSMike Smith 	    int airq;
147bb0d0a8eSMike Smith 
148bb0d0a8eSMike Smith 	    airq = pci_apic_irq(bus, slot, pin);
149bb0d0a8eSMike Smith 	    if (airq >= 0) {
150bb0d0a8eSMike Smith 		/* PCI specific entry found in MP table */
151bb0d0a8eSMike Smith 		if (airq != line)
152bb0d0a8eSMike Smith 		    undirect_pci_irq(line);
153bb0d0a8eSMike Smith 		return(airq);
154bb0d0a8eSMike Smith 	    } else {
155bb0d0a8eSMike Smith 		/*
156bb0d0a8eSMike Smith 		 * PCI interrupts might be redirected to the
157bb0d0a8eSMike Smith 		 * ISA bus according to some MP tables. Use the
158bb0d0a8eSMike Smith 		 * same methods as used by the ISA devices
159bb0d0a8eSMike Smith 		 * devices to find the proper IOAPIC int pin.
160bb0d0a8eSMike Smith 		 */
161bb0d0a8eSMike Smith 		airq = isa_apic_irq(line);
162bb0d0a8eSMike Smith 		if ((airq >= 0) && (airq != line)) {
163bb0d0a8eSMike Smith 		    /* XXX: undirect_pci_irq() ? */
164bb0d0a8eSMike Smith 		    undirect_isa_irq(line);
165bb0d0a8eSMike Smith 		    return(airq);
166bb0d0a8eSMike Smith 		}
167bb0d0a8eSMike Smith 	    }
168bb0d0a8eSMike Smith 	}
169bb0d0a8eSMike Smith 	return(line);
170bb0d0a8eSMike Smith     }
171bb0d0a8eSMike Smith #endif /* APIC_IO */
172bb0d0a8eSMike Smith     return(pci_do_cfgregread(bus, slot, func, reg, bytes));
173bb0d0a8eSMike Smith }
174bb0d0a8eSMike Smith 
17512a02d6eSMike Smith /*
17612a02d6eSMike Smith  * Write configuration space register
17712a02d6eSMike Smith  */
17812a02d6eSMike Smith void
17912a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
18012a02d6eSMike Smith {
18112a02d6eSMike Smith     return(usebios ?
18212a02d6eSMike Smith 	   pcibios_cfgwrite(bus, slot, func, reg, data, bytes) :
18312a02d6eSMike Smith 	   pcireg_cfgwrite(bus, slot, func, reg, data, bytes));
18412a02d6eSMike Smith }
18512a02d6eSMike Smith 
18612a02d6eSMike Smith /*
18754c9005fSWarner Losh  * Route a PCI interrupt
18854c9005fSWarner Losh  *
1899d558634SMike Smith  * XXX we don't do anything "right" with the function number in the PIR table
190099d058bSMike Smith  *     (because the consumer isn't currently passing it in).  We don't care
191099d058bSMike Smith  *     anyway, due to the way PCI interrupts are assigned.
19254c9005fSWarner Losh  */
19354c9005fSWarner Losh int
19454c9005fSWarner Losh pci_cfgintr(int bus, int device, int pin)
19554c9005fSWarner Losh {
19654c9005fSWarner Losh     struct PIR_entry	*pe;
1979d558634SMike Smith     int			i, irq;
1989d558634SMike Smith     struct bios_regs	args;
19954c9005fSWarner Losh 
200a3793252SWarner Losh     if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
201a3793252SWarner Losh       (pin < 1) || (pin > 4))
20254c9005fSWarner Losh 	return(255);
20354c9005fSWarner Losh 
20454c9005fSWarner Losh     /*
20554c9005fSWarner Losh      * Scan the entry table for a contender
20654c9005fSWarner Losh      */
207099d058bSMike Smith     for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, pe++) {
20854c9005fSWarner Losh 	if ((bus != pe->pe_bus) || (device != pe->pe_device))
20954c9005fSWarner Losh 	    continue;
210099d058bSMike Smith 
211099d058bSMike Smith 	irq = pci_cfgintr_unique(pe, pin);
212099d058bSMike Smith 	if (irq == 255)
213099d058bSMike Smith 	    irq = pci_cfgintr_linked(pe, pin);
214099d058bSMike Smith 	if (irq == 255)
215099d058bSMike Smith 	    irq = pci_cfgintr_virgin(pe, pin);
216099d058bSMike Smith 
217099d058bSMike Smith 	if (irq == 255)
21854c9005fSWarner Losh 	    break;
219099d058bSMike Smith 
2209d558634SMike Smith 
2219d558634SMike Smith 	/*
2229d558634SMike Smith 	 * Ask the BIOS to route the interrupt
2239d558634SMike Smith 	 */
2249d558634SMike Smith 	args.eax = PCIBIOS_ROUTE_INTERRUPT;
2259d558634SMike Smith 	args.ebx = (bus << 8) | (device << 3);
2269d558634SMike Smith 	args.ecx = (irq << 8) | (0xa + pin - 1);	/* pin value is 0xa - 0xd */
2279d558634SMike Smith 	bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
2289d558634SMike Smith 
229099d058bSMike Smith 	/*
230099d058bSMike Smith 	 * XXX if it fails, we should try to smack the router hardware directly
231099d058bSMike Smith 	 */
2329d558634SMike Smith 
233099d058bSMike Smith 	printf("pci_cfgintr: %d:%d INT%c routed to irq %d\n",
234099d058bSMike Smith 	       bus, device, 'A' + pin - 1, irq);
2359d558634SMike Smith 	return(irq);
23654c9005fSWarner Losh     }
237099d058bSMike Smith 
238099d058bSMike Smith     printf("pci_cfgintr: can't route an interrupt to %d:%d INT%c\n", bus, device, 'A' + pin - 1);
239099d058bSMike Smith     return(255);
240099d058bSMike Smith }
241099d058bSMike Smith 
242099d058bSMike Smith /*
243099d058bSMike Smith  * Look to see if the routing table claims this pin is uniquely routed.
244099d058bSMike Smith  */
245099d058bSMike Smith static int
246099d058bSMike Smith pci_cfgintr_unique(struct PIR_entry *pe, int pin)
247099d058bSMike Smith {
248099d058bSMike Smith     int		irq;
249099d058bSMike Smith 
250099d058bSMike Smith     if (powerof2(pe->pe_intpin[pin - 1].irqs)) {
251099d058bSMike Smith 	irq = ffs(pe->pe_intpin[pin - 1].irqs) - 1;
252099d058bSMike Smith 	printf("pci_cfgintr_unique: hard-routed to irq %d\n", irq);
253099d058bSMike Smith 	return(irq);
254099d058bSMike Smith     }
255099d058bSMike Smith     return(255);
256099d058bSMike Smith }
257099d058bSMike Smith 
258099d058bSMike Smith /*
259099d058bSMike Smith  * Look for another device which shares the same link byte and
260099d058bSMike Smith  * already has a unique IRQ, or which has had one routed already.
261099d058bSMike Smith  */
262099d058bSMike Smith static int
263099d058bSMike Smith pci_cfgintr_linked(struct PIR_entry *pe, int pin)
264099d058bSMike Smith {
265099d058bSMike Smith     struct PIR_entry	*oe;
266099d058bSMike Smith     struct PIR_intpin	*pi;
267099d058bSMike Smith     int			i, j, irq;
268099d058bSMike Smith 
269099d058bSMike Smith     /*
270099d058bSMike Smith      * Scan table slots.
271099d058bSMike Smith      */
272099d058bSMike Smith     for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, oe++) {
273099d058bSMike Smith 
274099d058bSMike Smith 	/* scan interrupt pins */
275099d058bSMike Smith 	for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) {
276099d058bSMike Smith 
277099d058bSMike Smith 	    /* don't look at the entry we're trying to match with */
278099d058bSMike Smith 	    if ((pe == oe) && (i == (pin - 1)))
279099d058bSMike Smith 		continue;
280099d058bSMike Smith 
281099d058bSMike Smith 	    /* compare link bytes */
282099d058bSMike Smith 	    if (pi->link != pe->pe_intpin[pin - 1].link)
283099d058bSMike Smith 		continue;
284099d058bSMike Smith 
285099d058bSMike Smith 	    /* link destination mapped to a unique interrupt? */
286099d058bSMike Smith 	    if (powerof2(pi->irqs)) {
287099d058bSMike Smith 		irq = ffs(pi->irqs) - 1;
288099d058bSMike Smith 		printf("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n",
289099d058bSMike Smith 		       pi->link, irq);
290099d058bSMike Smith 		return(irq);
291099d058bSMike Smith 	    }
292099d058bSMike Smith 
293099d058bSMike Smith 	    /* look for the real PCI device that matches this table entry */
294099d058bSMike Smith 	    if ((irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device, j, pin)) != 255)
295099d058bSMike Smith 		return(irq);
296099d058bSMike Smith 	}
297099d058bSMike Smith     }
298099d058bSMike Smith     return(255);
299099d058bSMike Smith }
300099d058bSMike Smith 
301099d058bSMike Smith /*
302099d058bSMike Smith  * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and
303099d058bSMike Smith  * see if it has already been assigned an interrupt.
304099d058bSMike Smith  */
305099d058bSMike Smith static int
306099d058bSMike Smith pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin)
307099d058bSMike Smith {
308099d058bSMike Smith     devclass_t		pci_devclass;
309099d058bSMike Smith     device_t		*pci_devices;
310099d058bSMike Smith     int			pci_count;
311099d058bSMike Smith     device_t		*pci_children;
312099d058bSMike Smith     int			pci_childcount;
313099d058bSMike Smith     device_t		*busp, *childp;
314099d058bSMike Smith     int			i, j, irq;
315099d058bSMike Smith 
316099d058bSMike Smith     /*
317099d058bSMike Smith      * Find all the PCI busses.
318099d058bSMike Smith      */
319099d058bSMike Smith     pci_count = 0;
320099d058bSMike Smith     if ((pci_devclass = devclass_find("pci")) != NULL)
321099d058bSMike Smith 	devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
322099d058bSMike Smith 
323099d058bSMike Smith     /*
324099d058bSMike Smith      * Scan all the PCI busses/devices looking for this one.
325099d058bSMike Smith      */
326099d058bSMike Smith     for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) {
327099d058bSMike Smith 	pci_childcount = 0;
328099d058bSMike Smith 	device_get_children(*busp, &pci_children, &pci_childcount);
329099d058bSMike Smith 
330099d058bSMike Smith 	for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) {
331099d058bSMike Smith 	    if ((pci_get_bus(*childp) == bus) &&
332099d058bSMike Smith 		(pci_get_slot(*childp) == device) &&
333099d058bSMike Smith 		(pci_get_intpin(*childp) == matchpin) &&
334099d058bSMike Smith 		((irq = pci_get_irq(*childp)) != 255)) {
335099d058bSMike Smith 		printf("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n",
336099d058bSMike Smith 		       irq, pe->pe_intpin[pin - 1].link,
337099d058bSMike Smith 		       pci_get_bus(*childp), pci_get_slot(*childp), pci_get_function(*childp));
338099d058bSMike Smith 		return(irq);
339099d058bSMike Smith 	    }
340099d058bSMike Smith 	}
341099d058bSMike Smith     }
342099d058bSMike Smith     return(255);
343099d058bSMike Smith }
344099d058bSMike Smith 
345099d058bSMike Smith /*
346099d058bSMike Smith  * Pick a suitable IRQ from those listed as routable to this device.
347099d058bSMike Smith  */
348099d058bSMike Smith static int
349099d058bSMike Smith pci_cfgintr_virgin(struct PIR_entry *pe, int pin)
350099d058bSMike Smith {
351099d058bSMike Smith     int		irq, ibit;
352099d058bSMike Smith 
353099d058bSMike Smith     /* first scan the set of PCI-only interrupts and see if any of these are routable */
354099d058bSMike Smith     for (irq = 0; irq < 16; irq++) {
355099d058bSMike Smith 	ibit = (1 << irq);
356099d058bSMike Smith 
357099d058bSMike Smith 	/* can we use this interrupt? */
358099d058bSMike Smith 	if ((pci_route_table->pt_header.ph_pci_irqs & ibit) &&
359099d058bSMike Smith 	    (pe->pe_intpin[pin - 1].irqs & ibit)) {
360099d058bSMike Smith 	    printf("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq);
361099d058bSMike Smith 	    return(irq);
362099d058bSMike Smith 	}
363099d058bSMike Smith     }
364099d058bSMike Smith 
365099d058bSMike Smith     /* life is tough, so just pick an interrupt */
366099d058bSMike Smith     for (irq = 0; irq < 16; irq++) {
367099d058bSMike Smith 	ibit = (1 << irq);
368099d058bSMike Smith 
369099d058bSMike Smith 	if (pe->pe_intpin[pin - 1].irqs & ibit) {
370099d058bSMike Smith 	    printf("pci_cfgintr_virgin: using routable interrupt %d\n", irq);
371099d058bSMike Smith 	    return(irq);
372099d058bSMike Smith 	}
373099d058bSMike Smith     }
37454c9005fSWarner Losh     return(255);
37554c9005fSWarner Losh }
37654c9005fSWarner Losh 
37754c9005fSWarner Losh 
37854c9005fSWarner Losh /*
37912a02d6eSMike Smith  * Config space access using BIOS functions
38012a02d6eSMike Smith  */
381300451c4SMike Smith static int
38221c3015aSDoug Rabson pcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
383300451c4SMike Smith {
384300451c4SMike Smith     struct bios_regs args;
385ac9b3dacSMike Smith     u_int mask;
386300451c4SMike Smith 
387300451c4SMike Smith     switch(bytes) {
388300451c4SMike Smith     case 1:
389300451c4SMike Smith 	args.eax = PCIBIOS_READ_CONFIG_BYTE;
390ac9b3dacSMike Smith 	mask = 0xff;
391300451c4SMike Smith 	break;
392300451c4SMike Smith     case 2:
393300451c4SMike Smith 	args.eax = PCIBIOS_READ_CONFIG_WORD;
394ac9b3dacSMike Smith 	mask = 0xffff;
395300451c4SMike Smith 	break;
396300451c4SMike Smith     case 4:
397300451c4SMike Smith 	args.eax = PCIBIOS_READ_CONFIG_DWORD;
398ac9b3dacSMike Smith 	mask = 0xffffffff;
399300451c4SMike Smith 	break;
400300451c4SMike Smith     default:
401300451c4SMike Smith 	return(-1);
402300451c4SMike Smith     }
40321c3015aSDoug Rabson     args.ebx = (bus << 8) | (slot << 3) | func;
404300451c4SMike Smith     args.edi = reg;
405300451c4SMike Smith     bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
406300451c4SMike Smith     /* check call results? */
407ac9b3dacSMike Smith     return(args.ecx & mask);
408300451c4SMike Smith }
409300451c4SMike Smith 
410300451c4SMike Smith static void
41121c3015aSDoug Rabson pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
412300451c4SMike Smith {
413300451c4SMike Smith     struct bios_regs args;
414300451c4SMike Smith 
415300451c4SMike Smith     switch(bytes) {
416300451c4SMike Smith     case 1:
417300451c4SMike Smith 	args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
418300451c4SMike Smith 	break;
419300451c4SMike Smith     case 2:
420300451c4SMike Smith 	args.eax = PCIBIOS_WRITE_CONFIG_WORD;
421300451c4SMike Smith 	break;
422300451c4SMike Smith     case 4:
423300451c4SMike Smith 	args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
424300451c4SMike Smith 	break;
425300451c4SMike Smith     default:
426300451c4SMike Smith 	return;
427300451c4SMike Smith     }
42821c3015aSDoug Rabson     args.ebx = (bus << 8) | (slot << 3) | func;
429300451c4SMike Smith     args.ecx = data;
430300451c4SMike Smith     args.edi = reg;
431300451c4SMike Smith     bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
432300451c4SMike Smith }
433300451c4SMike Smith 
43412a02d6eSMike Smith /*
43512a02d6eSMike Smith  * Determine whether there is a PCI BIOS present
43612a02d6eSMike Smith  */
437300451c4SMike Smith static int
438300451c4SMike Smith pcibios_cfgopen(void)
439300451c4SMike Smith {
440300451c4SMike Smith     /* check for a found entrypoint */
441300451c4SMike Smith     return(PCIbios.entry != 0);
442300451c4SMike Smith }
443300451c4SMike Smith 
44412a02d6eSMike Smith /*
44512a02d6eSMike Smith  * Configuration space access using direct register operations
44612a02d6eSMike Smith  */
447ac19f918SStefan Eßer 
4485bec6157SStefan Eßer /* enable configuration space accesses and return data port address */
449a3adc4f8SStefan Eßer static int
4505bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
4515bec6157SStefan Eßer {
4525bec6157SStefan Eßer     int dataport = 0;
4535bec6157SStefan Eßer 
4545bec6157SStefan Eßer     if (bus <= PCI_BUSMAX
4555bec6157SStefan Eßer 	&& slot < devmax
4565bec6157SStefan Eßer 	&& func <= PCI_FUNCMAX
4575bec6157SStefan Eßer 	&& reg <= PCI_REGMAX
4585bec6157SStefan Eßer 	&& bytes != 3
4595bec6157SStefan Eßer 	&& (unsigned) bytes <= 4
4605bec6157SStefan Eßer 	&& (reg & (bytes -1)) == 0) {
4615bec6157SStefan Eßer 	switch (cfgmech) {
4625bec6157SStefan Eßer 	case 1:
463b3daa02eSStefan Eßer 	    outl(CONF1_ADDR_PORT, (1 << 31)
464b3daa02eSStefan Eßer 		 | (bus << 16) | (slot << 11)
465b3daa02eSStefan Eßer 		 | (func << 8) | (reg & ~0x03));
466b3daa02eSStefan Eßer 	    dataport = CONF1_DATA_PORT + (reg & 0x03);
4675bec6157SStefan Eßer 	    break;
4685bec6157SStefan Eßer 	case 2:
4695bec6157SStefan Eßer 	    outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
4705bec6157SStefan Eßer 	    outb(CONF2_FORWARD_PORT, bus);
4715bec6157SStefan Eßer 	    dataport = 0xc000 | (slot << 8) | reg;
4725bec6157SStefan Eßer 	    break;
4735bec6157SStefan Eßer 	}
4745bec6157SStefan Eßer     }
4755bec6157SStefan Eßer     return (dataport);
4765bec6157SStefan Eßer }
4775bec6157SStefan Eßer 
4785bec6157SStefan Eßer /* disable configuration space accesses */
4795bec6157SStefan Eßer static void
4805bec6157SStefan Eßer pci_cfgdisable(void)
4815bec6157SStefan Eßer {
4825bec6157SStefan Eßer     switch (cfgmech) {
4835bec6157SStefan Eßer     case 1:
4845bec6157SStefan Eßer 	outl(CONF1_ADDR_PORT, 0);
4855bec6157SStefan Eßer 	break;
4865bec6157SStefan Eßer     case 2:
4875bec6157SStefan Eßer 	outb(CONF2_ENABLE_PORT, 0);
4885bec6157SStefan Eßer 	outb(CONF2_FORWARD_PORT, 0);
4895bec6157SStefan Eßer 	break;
4905bec6157SStefan Eßer     }
4915bec6157SStefan Eßer }
4925bec6157SStefan Eßer 
493300451c4SMike Smith static int
49421c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
4955bec6157SStefan Eßer {
4965bec6157SStefan Eßer     int data = -1;
4975bec6157SStefan Eßer     int port;
4985bec6157SStefan Eßer 
49921c3015aSDoug Rabson     port = pci_cfgenable(bus, slot, func, reg, bytes);
5005bec6157SStefan Eßer 
5015bec6157SStefan Eßer     if (port != 0) {
5025bec6157SStefan Eßer 	switch (bytes) {
5035bec6157SStefan Eßer 	case 1:
5045bec6157SStefan Eßer 	    data = inb(port);
5055bec6157SStefan Eßer 	    break;
5065bec6157SStefan Eßer 	case 2:
5075bec6157SStefan Eßer 	    data = inw(port);
5085bec6157SStefan Eßer 	    break;
5095bec6157SStefan Eßer 	case 4:
5105bec6157SStefan Eßer 	    data = inl(port);
5115bec6157SStefan Eßer 	    break;
5125bec6157SStefan Eßer 	}
5135bec6157SStefan Eßer 	pci_cfgdisable();
5145bec6157SStefan Eßer     }
5155bec6157SStefan Eßer     return (data);
5165bec6157SStefan Eßer }
5175bec6157SStefan Eßer 
518300451c4SMike Smith static void
51921c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
5205bec6157SStefan Eßer {
5215bec6157SStefan Eßer     int port;
5225bec6157SStefan Eßer 
52321c3015aSDoug Rabson     port = pci_cfgenable(bus, slot, func, reg, bytes);
5245bec6157SStefan Eßer     if (port != 0) {
5255bec6157SStefan Eßer 	switch (bytes) {
5265bec6157SStefan Eßer 	case 1:
5275bec6157SStefan Eßer 	    outb(port, data);
5285bec6157SStefan Eßer 	    break;
5295bec6157SStefan Eßer 	case 2:
5305bec6157SStefan Eßer 	    outw(port, data);
5315bec6157SStefan Eßer 	    break;
5325bec6157SStefan Eßer 	case 4:
5335bec6157SStefan Eßer 	    outl(port, data);
5345bec6157SStefan Eßer 	    break;
5355bec6157SStefan Eßer 	}
5365bec6157SStefan Eßer 	pci_cfgdisable();
5375bec6157SStefan Eßer     }
5385bec6157SStefan Eßer }
5395bec6157SStefan Eßer 
54012a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */
5415bec6157SStefan Eßer static int
5425bec6157SStefan Eßer pci_cfgcheck(int maxdev)
543a3adc4f8SStefan Eßer {
544a3adc4f8SStefan Eßer     u_char device;
545a3adc4f8SStefan Eßer 
5465bec6157SStefan Eßer     if (bootverbose)
5475bec6157SStefan Eßer 	printf("pci_cfgcheck:\tdevice ");
54877b57314SStefan Eßer 
5495bec6157SStefan Eßer     for (device = 0; device < maxdev; device++) {
5505bec6157SStefan Eßer 	unsigned id, class, header;
551c7483249SStefan Eßer 	if (bootverbose)
552c7483249SStefan Eßer 	    printf("%d ", device);
5535bec6157SStefan Eßer 
5545bec6157SStefan Eßer 	id = inl(pci_cfgenable(0, device, 0, 0, 4));
5555bec6157SStefan Eßer 	if (id == 0 || id == -1)
55681cf5d7aSStefan Eßer 	    continue;
55781cf5d7aSStefan Eßer 
5585bec6157SStefan Eßer 	class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
55981cf5d7aSStefan Eßer 	if (bootverbose)
5605bec6157SStefan Eßer 	    printf("[class=%06x] ", class);
5618277ac25SStefan Eßer 	if (class == 0 || (class & 0xf870ff) != 0)
56281cf5d7aSStefan Eßer 	    continue;
56381cf5d7aSStefan Eßer 
5645bec6157SStefan Eßer 	header = inb(pci_cfgenable(0, device, 0, 14, 1));
56581cf5d7aSStefan Eßer 	if (bootverbose)
5665bec6157SStefan Eßer 	    printf("[hdr=%02x] ", header);
5675bec6157SStefan Eßer 	if ((header & 0x7e) != 0)
56881cf5d7aSStefan Eßer 	    continue;
56981cf5d7aSStefan Eßer 
5705bec6157SStefan Eßer 	if (bootverbose)
5715bec6157SStefan Eßer 	    printf("is there (id=%08x)\n", id);
5725bec6157SStefan Eßer 
5735bec6157SStefan Eßer 	pci_cfgdisable();
5745bec6157SStefan Eßer 	return (1);
575a3adc4f8SStefan Eßer     }
576c7483249SStefan Eßer     if (bootverbose)
577c7483249SStefan Eßer 	printf("-- nothing found\n");
5785bec6157SStefan Eßer 
5795bec6157SStefan Eßer     pci_cfgdisable();
5805bec6157SStefan Eßer     return (0);
581a3adc4f8SStefan Eßer }
582d7ea35fcSStefan Eßer 
5838dc26439SPeter Wemm static int
584300451c4SMike Smith pcireg_cfgopen(void)
585ac19f918SStefan Eßer {
586287911bdSStefan Eßer     unsigned long mode1res,oldval1;
587287911bdSStefan Eßer     unsigned char mode2res,oldval2;
5880847c06dSStefan Eßer 
589287911bdSStefan Eßer     oldval1 = inl(CONF1_ADDR_PORT);
590a3adc4f8SStefan Eßer 
59177b57314SStefan Eßer     if (bootverbose) {
5925bec6157SStefan Eßer 	printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
5935bec6157SStefan Eßer 	       oldval1);
594a3adc4f8SStefan Eßer     }
595a3adc4f8SStefan Eßer 
5960e2f699bSStefan Eßer     if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
597287911bdSStefan Eßer 
5985bec6157SStefan Eßer 	cfgmech = 1;
5995bec6157SStefan Eßer 	devmax = 32;
60077b57314SStefan Eßer 
60177b57314SStefan Eßer 	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
60277b57314SStefan Eßer 	outb(CONF1_ADDR_PORT +3, 0);
60377b57314SStefan Eßer 	mode1res = inl(CONF1_ADDR_PORT);
604287911bdSStefan Eßer 	outl(CONF1_ADDR_PORT, oldval1);
60577b57314SStefan Eßer 
60677b57314SStefan Eßer 	if (bootverbose)
6075bec6157SStefan Eßer 	    printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
60877b57314SStefan Eßer 		   mode1res, CONF1_ENABLE_CHK);
60977b57314SStefan Eßer 
61077b57314SStefan Eßer 	if (mode1res) {
6115bec6157SStefan Eßer 	    if (pci_cfgcheck(32))
6125bec6157SStefan Eßer 		return (cfgmech);
6135bec6157SStefan Eßer 	}
61477b57314SStefan Eßer 
61577b57314SStefan Eßer 	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
61677b57314SStefan Eßer 	mode1res = inl(CONF1_ADDR_PORT);
617287911bdSStefan Eßer 	outl(CONF1_ADDR_PORT, oldval1);
61877b57314SStefan Eßer 
61977b57314SStefan Eßer 	if (bootverbose)
6205bec6157SStefan Eßer 	    printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
62177b57314SStefan Eßer 		   mode1res, CONF1_ENABLE_CHK1);
62277b57314SStefan Eßer 
623c7483249SStefan Eßer 	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
6245bec6157SStefan Eßer 	    if (pci_cfgcheck(32))
6255bec6157SStefan Eßer 		return (cfgmech);
626287911bdSStefan Eßer 	}
6275bec6157SStefan Eßer     }
62877b57314SStefan Eßer 
629287911bdSStefan Eßer     oldval2 = inb(CONF2_ENABLE_PORT);
630287911bdSStefan Eßer 
631287911bdSStefan Eßer     if (bootverbose) {
6325bec6157SStefan Eßer 	printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
6335bec6157SStefan Eßer 	       oldval2);
634287911bdSStefan Eßer     }
635287911bdSStefan Eßer 
636287911bdSStefan Eßer     if ((oldval2 & 0xf0) == 0) {
637c7483249SStefan Eßer 
6385bec6157SStefan Eßer 	cfgmech = 2;
6395bec6157SStefan Eßer 	devmax = 16;
64077b57314SStefan Eßer 
641287911bdSStefan Eßer 	outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
642287911bdSStefan Eßer 	mode2res = inb(CONF2_ENABLE_PORT);
643287911bdSStefan Eßer 	outb(CONF2_ENABLE_PORT, oldval2);
644287911bdSStefan Eßer 
645287911bdSStefan Eßer 	if (bootverbose)
6465bec6157SStefan Eßer 	    printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
647287911bdSStefan Eßer 		   mode2res, CONF2_ENABLE_CHK);
648287911bdSStefan Eßer 
649287911bdSStefan Eßer 	if (mode2res == CONF2_ENABLE_RES) {
650287911bdSStefan Eßer 	    if (bootverbose)
6515bec6157SStefan Eßer 		printf("pci_open(2a):\tnow trying mechanism 2\n");
652287911bdSStefan Eßer 
6535bec6157SStefan Eßer 	    if (pci_cfgcheck(16))
6545bec6157SStefan Eßer 		return (cfgmech);
655287911bdSStefan Eßer 	}
656287911bdSStefan Eßer     }
65777b57314SStefan Eßer 
6585bec6157SStefan Eßer     cfgmech = 0;
6595bec6157SStefan Eßer     devmax = 0;
6605bec6157SStefan Eßer     return (cfgmech);
661ac19f918SStefan Eßer }
6628dc26439SPeter Wemm 
663