pci_cfgreg.c revision 82465
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
31590Srgrimes * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
41590Srgrimes * Copyright (c) 2000, BSDi
51590Srgrimes * All rights reserved.
61590Srgrimes *
71590Srgrimes * Redistribution and use in source and binary forms, with or without
81590Srgrimes * modification, are permitted provided that the following conditions
91590Srgrimes * are met:
101590Srgrimes * 1. Redistributions of source code must retain the above copyright
111590Srgrimes *    notice unmodified, this list of conditions, and the following
121590Srgrimes *    disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
181590Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
191590Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
201590Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
211590Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
221590Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231590Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
241590Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251590Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
261590Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
271590Srgrimes *
281590Srgrimes * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 82465 2001-08-28 16:35:01Z imp $
291590Srgrimes *
301590Srgrimes */
311590Srgrimes
321590Srgrimes#include <sys/param.h>		/* XXX trim includes */
331590Srgrimes#include <sys/systm.h>
341590Srgrimes#include <sys/bus.h>
351590Srgrimes#include <sys/kernel.h>
361590Srgrimes#include <sys/module.h>
371590Srgrimes#include <sys/malloc.h>
3854113Skris#include <vm/vm.h>
3941568Sarchie#include <vm/pmap.h>
4054113Skris#include <machine/md_var.h>
4154113Skris#include <pci/pcivar.h>
4254113Skris#include <pci/pcireg.h>
431590Srgrimes#include <isa/isavar.h>
441590Srgrimes#include <machine/nexusvar.h>
4567189Sbrian#include <machine/pci_cfgreg.h>
4667189Sbrian#include <machine/segments.h>
471590Srgrimes#include <machine/pc/bios.h>
481590Srgrimes
491590Srgrimes#ifdef APIC_IO
501590Srgrimes#include <machine/smp.h>
511590Srgrimes#endif /* APIC_IO */
521590Srgrimes
531590Srgrimes#include "pcib_if.h"
541590Srgrimes
551590Srgrimes#define PRVERB(a) printf a
561590Srgrimes
571590Srgrimesstatic int cfgmech;
5871326Swillstatic int devmax;
591590Srgrimesstatic int usebios;
6071326Swillstatic int enable_pcibios = 0;
6171326Swill
6270692SwillTUNABLE_INT("hw.pci.enable_pcibios", &enable_pcibios);
631590Srgrimes
6471326Swillstatic int	pci_cfgintr_unique(struct PIR_entry *pe, int pin);
6571326Swillstatic int	pci_cfgintr_linked(struct PIR_entry *pe, int pin);
6671326Swillstatic int	pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin);
6771326Swillstatic int	pci_cfgintr_virgin(struct PIR_entry *pe, int pin);
681590Srgrimes
691590Srgrimesstatic int	pcibios_cfgread(int bus, int slot, int func, int reg, int bytes);
701590Srgrimesstatic void	pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
711590Srgrimesstatic int	pcibios_cfgopen(void);
7270672Swillstatic int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
731590Srgrimesstatic void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
741590Srgrimesstatic int	pcireg_cfgopen(void);
751590Srgrimes
761590Srgrimesstatic struct PIR_table	*pci_route_table;
7754157Scharnierstatic int		pci_route_count;
781590Srgrimes
791590Srgrimesint
801590Srgrimespci_pcibios_active(void)
811590Srgrimes{
821590Srgrimes    return usebios;
831590Srgrimes}
841590Srgrimes
851590Srgrimesint
861590Srgrimespci_kill_pcibios(void)
8754157Scharnier{
881590Srgrimes    usebios = 0;
891590Srgrimes    return pcireg_cfgopen() != 0;
901590Srgrimes}
911590Srgrimes
921590Srgrimesstatic u_int16_t
931590Srgrimespcibios_get_version(void)
941590Srgrimes{
951590Srgrimes    struct bios_regs args;
961590Srgrimes
971590Srgrimes    if (PCIbios.entry == 0) {
981590Srgrimes	PRVERB(("pcibios: No call entry point\n"));
991590Srgrimes	return (0);
1001590Srgrimes    }
1011590Srgrimes    args.eax = PCIBIOS_BIOS_PRESENT;
1021590Srgrimes    if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
1031590Srgrimes	PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
1041590Srgrimes	return (0);
1051590Srgrimes    }
1061590Srgrimes    if (args.edx != 0x20494350) {
1071590Srgrimes	PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
1081590Srgrimes	return (0);
1091590Srgrimes    }
1101590Srgrimes    return (args.ebx & 0xffff);
1111590Srgrimes}
11270692Swill
11370692Swill/*
11470692Swill * Initialise access to PCI configuration space
11570692Swill */
11670692Swillint
11770692Swillpci_cfgregopen(void)
11870692Swill{
11970692Swill    static int			opened = 0;
12070692Swill    u_long			sigaddr;
12170692Swill    static struct PIR_table	*pt;
12270692Swill    u_int8_t			ck, *cv;
12370692Swill    int				i;
12470692Swill
12570692Swill    if (opened)
12670692Swill	return(1);
12770692Swill
1281590Srgrimes    if (pcibios_cfgopen() != 0) {
1291590Srgrimes	usebios = 1;
1301590Srgrimes    } else if (pcireg_cfgopen() != 0) {
13170692Swill	usebios = 0;
13270692Swill    } else {
1331590Srgrimes	return(0);
13471326Swill    }
13571326Swill
13670692Swill    /*
1371590Srgrimes     * Look for the interrupt routing table.
1388874Srgrimes     */
1391590Srgrimes    /* We use PCI BIOS's PIR table if it's available */
1401590Srgrimes    if (pcibios_get_version() >= 0x0210 && pt == NULL &&
1411590Srgrimes      (sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0)) != 0) {
1421590Srgrimes	pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
1431590Srgrimes	for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) {
1441590Srgrimes	    ck += cv[i];
14570692Swill	}
14670692Swill	if (ck == 0) {
14770692Swill	    pci_route_table = pt;
14870692Swill	    pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry);
14971615Swill	    printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table);
15070692Swill	}
15170692Swill    }
15270692Swill
15370692Swill    opened = 1;
15470692Swill    return(1);
15571615Swill}
15670692Swill
1571590Srgrimes/*
1581590Srgrimes * Read configuration space register
1591590Srgrimes */
1601590Srgrimesstatic u_int32_t
1611590Srgrimespci_do_cfgregread(int bus, int slot, int func, int reg, int bytes)
1621590Srgrimes{
1631590Srgrimes    return(usebios ?
1641590Srgrimes	   pcibios_cfgread(bus, slot, func, reg, bytes) :
16570692Swill	   pcireg_cfgread(bus, slot, func, reg, bytes));
16670692Swill}
16770692Swill
1681590Srgrimesu_int32_t
1691590Srgrimespci_cfgregread(int bus, int slot, int func, int reg, int bytes)
1701590Srgrimes{
1711590Srgrimes#ifdef APIC_IO
1721590Srgrimes    /*
1731590Srgrimes     * If we are using the APIC, the contents of the intline register will probably
1741590Srgrimes     * be wrong (since they are set up for use with the PIC.
1751590Srgrimes     * Rather than rewrite these registers (maybe that would be smarter) we trap
1761590Srgrimes     * attempts to read them and translate to our private vector numbers.
1771590Srgrimes     */
1781590Srgrimes    if ((reg == PCIR_INTLINE) && (bytes == 1)) {
1791590Srgrimes	int	pin, line;
1801590Srgrimes
1811590Srgrimes	pin = pci_do_cfgregread(bus, slot, func, PCIR_INTPIN, 1);
1821590Srgrimes	line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1);
1831590Srgrimes
1841590Srgrimes	if (pin != 0) {
1851590Srgrimes	    int airq;
1861590Srgrimes
1871590Srgrimes	    airq = pci_apic_irq(bus, slot, pin);
1881590Srgrimes	    if (airq >= 0) {
1891590Srgrimes		/* PCI specific entry found in MP table */
19054113Skris		if (airq != line)
1911590Srgrimes		    undirect_pci_irq(line);
1921590Srgrimes		return(airq);
1931590Srgrimes	    } else {
1941590Srgrimes		/*
1951590Srgrimes		 * PCI interrupts might be redirected to the
19670692Swill		 * ISA bus according to some MP tables. Use the
19770692Swill		 * same methods as used by the ISA devices
19870692Swill		 * devices to find the proper IOAPIC int pin.
19970692Swill		 */
20070692Swill		airq = isa_apic_irq(line);
20170692Swill		if ((airq >= 0) && (airq != line)) {
20271615Swill		    /* XXX: undirect_pci_irq() ? */
20370692Swill		    undirect_isa_irq(line);
2041590Srgrimes		    return(airq);
2051590Srgrimes		}
2061590Srgrimes	    }
2071590Srgrimes	}
2081590Srgrimes	return(line);
2091590Srgrimes    }
2101590Srgrimes#endif /* APIC_IO */
2111590Srgrimes    return(pci_do_cfgregread(bus, slot, func, reg, bytes));
2121590Srgrimes}
21371326Swill
2141590Srgrimes/*
2151590Srgrimes * Write configuration space register
2161590Srgrimes */
2171590Srgrimesvoid
2181590Srgrimespci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
21970692Swill{
22070692Swill    return(usebios ?
22170692Swill	   pcibios_cfgwrite(bus, slot, func, reg, data, bytes) :
22270692Swill	   pcireg_cfgwrite(bus, slot, func, reg, data, bytes));
2231590Srgrimes}
2241590Srgrimes
2251590Srgrimes/*
2261590Srgrimes * Route a PCI interrupt
22770692Swill *
22870692Swill * XXX we don't do anything "right" with the function number in the PIR table
22970692Swill *     (because the consumer isn't currently passing it in).  We don't care
2301590Srgrimes *     anyway, due to the way PCI interrupts are assigned.
23170692Swill */
23271326Swillint
2331590Srgrimespci_cfgintr(int bus, int device, int pin)
2341590Srgrimes{
23543928Seivind    struct PIR_entry	*pe;
2361590Srgrimes    int			i, irq;
2371590Srgrimes    struct bios_regs	args;
23870672Swill    u_int16_t		v;
2391590Srgrimes    int already = 0;
2401590Srgrimes
2411590Srgrimes    v = pcibios_get_version();
24260706Skris    if (v < 0x0210) {
2431590Srgrimes	PRVERB((
24460706Skris	  "pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n",
2451590Srgrimes	  (v & 0xff00) >> 8, v & 0xff));
2461590Srgrimes	return (255);
24779452Sbrian    }
24870692Swill    if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
24960706Skris      (pin < 1) || (pin > 4))
2501590Srgrimes	return(255);
2511590Srgrimes
2521590Srgrimes    /*
25343928Seivind     * Scan the entry table for a contender
2541590Srgrimes     */
2551590Srgrimes    for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, pe++) {
2561590Srgrimes	if ((bus != pe->pe_bus) || (device != pe->pe_device))
25743928Seivind	    continue;
2581590Srgrimes
2591590Srgrimes	irq = pci_cfgintr_linked(pe, pin);
2601590Srgrimes	if (irq != 255)
2611590Srgrimes	     already = 1;
2621590Srgrimes	if (irq == 255)
2631590Srgrimes	    irq = pci_cfgintr_unique(pe, pin);
2641590Srgrimes	if (irq == 255)
26570672Swill	    irq = pci_cfgintr_virgin(pe, pin);
2661590Srgrimes
2671590Srgrimes	if (irq == 255)
268	    break;
269
270	/*
271	 * Ask the BIOS to route the interrupt
272	 */
273	args.eax = PCIBIOS_ROUTE_INTERRUPT;
274	args.ebx = (bus << 8) | (device << 3);
275	args.ecx = (irq << 8) | (0xa + pin - 1);	/* pin value is 0xa - 0xd */
276	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)) && !already) {
277	    /*
278	     * XXX if it fails, we should try to smack the router
279	     * hardware directly.
280	     * XXX Also, there may be other choices that we can try that
281	     * will work.
282	     */
283	    PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n"));
284	    return(255);
285	}
286	printf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus, device, 'A' + pin - 1, irq);
287	return(irq);
288    }
289
290    PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c\n", bus, device, 'A' + pin - 1));
291    return(255);
292}
293
294/*
295 * Look to see if the routing table claims this pin is uniquely routed.
296 */
297static int
298pci_cfgintr_unique(struct PIR_entry *pe, int pin)
299{
300    int		irq;
301
302    if (powerof2(pe->pe_intpin[pin - 1].irqs)) {
303	irq = ffs(pe->pe_intpin[pin - 1].irqs) - 1;
304	PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq));
305	return(irq);
306    }
307    return(255);
308}
309
310/*
311 * Look for another device which shares the same link byte and
312 * already has a unique IRQ, or which has had one routed already.
313 */
314static int
315pci_cfgintr_linked(struct PIR_entry *pe, int pin)
316{
317    struct PIR_entry	*oe;
318    struct PIR_intpin	*pi;
319    int			i, j, irq;
320
321    /*
322     * Scan table slots.
323     */
324    for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, oe++) {
325
326	/* scan interrupt pins */
327	for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) {
328
329	    /* don't look at the entry we're trying to match with */
330	    if ((pe == oe) && (i == (pin - 1)))
331		continue;
332
333	    /* compare link bytes */
334	    if (pi->link != pe->pe_intpin[pin - 1].link)
335		continue;
336
337	    /* link destination mapped to a unique interrupt? */
338	    if (powerof2(pi->irqs)) {
339		irq = ffs(pi->irqs) - 1;
340		PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n",
341		       pi->link, irq));
342		return(irq);
343	    }
344
345	    /* look for the real PCI device that matches this table entry */
346	    if ((irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device, j, pin)) != 255)
347		return(irq);
348	}
349    }
350    return(255);
351}
352
353/*
354 * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and
355 * see if it has already been assigned an interrupt.
356 */
357static int
358pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin)
359{
360    devclass_t		pci_devclass;
361    device_t		*pci_devices;
362    int			pci_count;
363    device_t		*pci_children;
364    int			pci_childcount;
365    device_t		*busp, *childp;
366    int			i, j, irq;
367
368    /*
369     * Find all the PCI busses.
370     */
371    pci_count = 0;
372    if ((pci_devclass = devclass_find("pci")) != NULL)
373	devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
374
375    /*
376     * Scan all the PCI busses/devices looking for this one.
377     */
378    irq = 255;
379    for (i = 0, busp = pci_devices; (i < pci_count) && (irq == 255); i++, busp++) {
380	pci_childcount = 0;
381	device_get_children(*busp, &pci_children, &pci_childcount);
382
383	for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) {
384	    if ((pci_get_bus(*childp) == bus) &&
385		(pci_get_slot(*childp) == device) &&
386		(pci_get_intpin(*childp) == matchpin) &&
387		((irq = pci_get_irq(*childp)) != 255)) {
388		PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n",
389		       pe->pe_intpin[pin - 1].link, irq,
390		       pci_get_bus(*childp), pci_get_slot(*childp), pci_get_function(*childp)));
391		break;
392	    }
393	}
394	if (pci_children != NULL)
395	    free(pci_children, M_TEMP);
396    }
397    if (pci_devices != NULL)
398	free(pci_devices, M_TEMP);
399    return(irq);
400}
401
402/*
403 * Pick a suitable IRQ from those listed as routable to this device.
404 */
405static int
406pci_cfgintr_virgin(struct PIR_entry *pe, int pin)
407{
408    int		irq, ibit;
409
410    /* first scan the set of PCI-only interrupts and see if any of these are routable */
411    for (irq = 0; irq < 16; irq++) {
412	ibit = (1 << irq);
413
414	/* can we use this interrupt? */
415	if ((pci_route_table->pt_header.ph_pci_irqs & ibit) &&
416	    (pe->pe_intpin[pin - 1].irqs & ibit)) {
417	    PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq));
418	    return(irq);
419	}
420    }
421
422    /* life is tough, so just pick an interrupt */
423    for (irq = 0; irq < 16; irq++) {
424	ibit = (1 << irq);
425
426	if (pe->pe_intpin[pin - 1].irqs & ibit) {
427	    PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq));
428	    return(irq);
429	}
430    }
431    return(255);
432}
433
434
435/*
436 * Config space access using BIOS functions
437 */
438static int
439pcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
440{
441    struct bios_regs args;
442    u_int mask;
443
444    switch(bytes) {
445    case 1:
446	args.eax = PCIBIOS_READ_CONFIG_BYTE;
447	mask = 0xff;
448	break;
449    case 2:
450	args.eax = PCIBIOS_READ_CONFIG_WORD;
451	mask = 0xffff;
452	break;
453    case 4:
454	args.eax = PCIBIOS_READ_CONFIG_DWORD;
455	mask = 0xffffffff;
456	break;
457    default:
458	return(-1);
459    }
460    args.ebx = (bus << 8) | (slot << 3) | func;
461    args.edi = reg;
462    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
463    /* check call results? */
464    return(args.ecx & mask);
465}
466
467static void
468pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
469{
470    struct bios_regs args;
471
472    switch(bytes) {
473    case 1:
474	args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
475	break;
476    case 2:
477	args.eax = PCIBIOS_WRITE_CONFIG_WORD;
478	break;
479    case 4:
480	args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
481	break;
482    default:
483	return;
484    }
485    args.ebx = (bus << 8) | (slot << 3) | func;
486    args.ecx = data;
487    args.edi = reg;
488    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
489}
490
491/*
492 * Determine whether there is a PCI BIOS present
493 */
494static int
495pcibios_cfgopen(void)
496{
497    u_int16_t		v = 0;
498
499    if (PCIbios.entry != 0 && enable_pcibios) {
500	v = pcibios_get_version();
501	if (v > 0)
502	    printf("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
503	      v & 0xff);
504    }
505    return (v > 0);
506}
507
508/*
509 * Configuration space access using direct register operations
510 */
511
512/* enable configuration space accesses and return data port address */
513static int
514pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
515{
516    int dataport = 0;
517
518    if (bus <= PCI_BUSMAX
519	&& slot < devmax
520	&& func <= PCI_FUNCMAX
521	&& reg <= PCI_REGMAX
522	&& bytes != 3
523	&& (unsigned) bytes <= 4
524	&& (reg & (bytes -1)) == 0) {
525	switch (cfgmech) {
526	case 1:
527	    outl(CONF1_ADDR_PORT, (1 << 31)
528		 | (bus << 16) | (slot << 11)
529		 | (func << 8) | (reg & ~0x03));
530	    dataport = CONF1_DATA_PORT + (reg & 0x03);
531	    break;
532	case 2:
533	    outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
534	    outb(CONF2_FORWARD_PORT, bus);
535	    dataport = 0xc000 | (slot << 8) | reg;
536	    break;
537	}
538    }
539    return (dataport);
540}
541
542/* disable configuration space accesses */
543static void
544pci_cfgdisable(void)
545{
546    switch (cfgmech) {
547    case 1:
548	outl(CONF1_ADDR_PORT, 0);
549	break;
550    case 2:
551	outb(CONF2_ENABLE_PORT, 0);
552	outb(CONF2_FORWARD_PORT, 0);
553	break;
554    }
555}
556
557static int
558pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
559{
560    int data = -1;
561    int port;
562
563    port = pci_cfgenable(bus, slot, func, reg, bytes);
564
565    if (port != 0) {
566	switch (bytes) {
567	case 1:
568	    data = inb(port);
569	    break;
570	case 2:
571	    data = inw(port);
572	    break;
573	case 4:
574	    data = inl(port);
575	    break;
576	}
577	pci_cfgdisable();
578    }
579    return (data);
580}
581
582static void
583pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
584{
585    int port;
586
587    port = pci_cfgenable(bus, slot, func, reg, bytes);
588    if (port != 0) {
589	switch (bytes) {
590	case 1:
591	    outb(port, data);
592	    break;
593	case 2:
594	    outw(port, data);
595	    break;
596	case 4:
597	    outl(port, data);
598	    break;
599	}
600	pci_cfgdisable();
601    }
602}
603
604/* check whether the configuration mechanism has been correctly identified */
605static int
606pci_cfgcheck(int maxdev)
607{
608    u_char device;
609
610    if (bootverbose)
611	printf("pci_cfgcheck:\tdevice ");
612
613    for (device = 0; device < maxdev; device++) {
614	unsigned id, class, header;
615	if (bootverbose)
616	    printf("%d ", device);
617
618	id = inl(pci_cfgenable(0, device, 0, 0, 4));
619	if (id == 0 || id == -1)
620	    continue;
621
622	class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
623	if (bootverbose)
624	    printf("[class=%06x] ", class);
625	if (class == 0 || (class & 0xf870ff) != 0)
626	    continue;
627
628	header = inb(pci_cfgenable(0, device, 0, 14, 1));
629	if (bootverbose)
630	    printf("[hdr=%02x] ", header);
631	if ((header & 0x7e) != 0)
632	    continue;
633
634	if (bootverbose)
635	    printf("is there (id=%08x)\n", id);
636
637	pci_cfgdisable();
638	return (1);
639    }
640    if (bootverbose)
641	printf("-- nothing found\n");
642
643    pci_cfgdisable();
644    return (0);
645}
646
647static int
648pcireg_cfgopen(void)
649{
650    unsigned long mode1res,oldval1;
651    unsigned char mode2res,oldval2;
652
653    oldval1 = inl(CONF1_ADDR_PORT);
654
655    if (bootverbose) {
656	printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
657	       oldval1);
658    }
659
660    if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
661
662	cfgmech = 1;
663	devmax = 32;
664
665	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
666	outb(CONF1_ADDR_PORT +3, 0);
667	mode1res = inl(CONF1_ADDR_PORT);
668	outl(CONF1_ADDR_PORT, oldval1);
669
670	if (bootverbose)
671	    printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
672		   mode1res, CONF1_ENABLE_CHK);
673
674	if (mode1res) {
675	    if (pci_cfgcheck(32))
676		return (cfgmech);
677	}
678
679	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
680	mode1res = inl(CONF1_ADDR_PORT);
681	outl(CONF1_ADDR_PORT, oldval1);
682
683	if (bootverbose)
684	    printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
685		   mode1res, CONF1_ENABLE_CHK1);
686
687	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
688	    if (pci_cfgcheck(32))
689		return (cfgmech);
690	}
691    }
692
693    oldval2 = inb(CONF2_ENABLE_PORT);
694
695    if (bootverbose) {
696	printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
697	       oldval2);
698    }
699
700    if ((oldval2 & 0xf0) == 0) {
701
702	cfgmech = 2;
703	devmax = 16;
704
705	outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
706	mode2res = inb(CONF2_ENABLE_PORT);
707	outb(CONF2_ENABLE_PORT, oldval2);
708
709	if (bootverbose)
710	    printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
711		   mode2res, CONF2_ENABLE_CHK);
712
713	if (mode2res == CONF2_ENABLE_RES) {
714	    if (bootverbose)
715		printf("pci_open(2a):\tnow trying mechanism 2\n");
716
717	    if (pci_cfgcheck(16))
718		return (cfgmech);
719	}
720    }
721
722    cfgmech = 0;
723    devmax = 0;
724    return (cfgmech);
725}
726
727