code:
/*	$OpenBSD: pci_bus_fixup.c,v 1.9 2006/08/18 22:18:18 kettenis Exp $	*/
/*	$NetBSD: pci_bus_fixup.c,v 1.1 1999/11/17 07:32:58 thorpej Exp $  */

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * PCI bus renumbering support.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>

#include <machine/bus.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/ppbreg.h>

#include <i386/pci/pcibiosvar.h>

int	pci_bus_check(pci_chipset_tag_t, int);
int	pci_bus_assign(pci_chipset_tag_t, int);
void	pcibus_print_devid(pci_chipset_tag_t, pcitag_t);

int
pci_bus_check(pci_chipset_tag_t pc, int bus)
{
	int device, maxdevs, function, nfuncs, bus_max, bus_sub;
	const struct pci_quirkdata *qd;
	pcireg_t reg;
	pcitag_t tag;

	bus_max = bus;

	maxdevs = pci_bus_maxdevs(pc, bus);
	for (device = 0; device < maxdevs; device++) {
		tag = pci_make_tag(pc, bus, device, 0);
		reg = pci_conf_read(pc, tag, PCI_ID_REG);

		/* can't be that many */
		if (bus_max == 255)
			break;

		/* Invalid vendor ID value? */
		if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
			continue;
		/* XXX Not invalid, but we've done this ~forever. */
		if (PCI_VENDOR(reg) == 0)
			continue;

		qd = pci_lookup_quirkdata(PCI_VENDOR(reg), PCI_PRODUCT(reg));

		reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
		if (PCI_HDRTYPE_MULTIFN(reg) ||
		    (qd != NULL &&
		     (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
			nfuncs = 8;
		else
			nfuncs = 1;

		for (function = 0; function < nfuncs; function++) {
			tag = pci_make_tag(pc, bus, device, function);
			reg = pci_conf_read(pc, tag, PCI_ID_REG);

			/* Invalid vendor ID value? */
			if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
				continue;
			/* XXX Not invalid, but we've done this ~forever. */
			if (PCI_VENDOR(reg) == 0)
				continue;

			reg = pci_conf_read(pc, tag, PCI_CLASS_REG);
			if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
			    (PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_PCI ||
			     PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_CARDBUS)) {

				reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
				if (PPB_BUSINFO_PRIMARY(reg) != bus) {
					if (pcibios_flags & PCIBIOS_VERBOSE) {
						pcibus_print_devid(pc, tag);
						printf("Mismatched primary bus: "
						    "primary %d, secondary %d, "
						    "subordinate %d\n",
						    PPB_BUSINFO_PRIMARY(reg),
						    PPB_BUSINFO_SECONDARY(reg),
						    PPB_BUSINFO_SUBORDINATE(reg));
					}
					return (-1);
				}
				if (PPB_BUSINFO_SECONDARY(reg) <= bus) {
					if (pcibios_flags & PCIBIOS_VERBOSE) {
						pcibus_print_devid(pc, tag);
						printf("Incorrect secondary bus: "
						    "primary %d, secondary %d, "
						    "subordinate %d\n",
						    PPB_BUSINFO_PRIMARY(reg),
						    PPB_BUSINFO_SECONDARY(reg),
						    PPB_BUSINFO_SUBORDINATE(reg));
					}
					return (-1);
				}

				/* Scan subordinate bus. */
				bus_sub = pci_bus_check(pc,
				    PPB_BUSINFO_SECONDARY(reg));
				if (bus_sub == -1)
					return (-1);

				if (PPB_BUSINFO_SUBORDINATE(reg) < bus_sub) {
					if (pcibios_flags & PCIBIOS_VERBOSE) {
						pcibus_print_devid(pc, tag);
						printf("Incorrect subordinate bus %d: "
						    "primary %d, secondary %d, "
						    "subordinate %d\n", bus_sub,
						    PPB_BUSINFO_PRIMARY(reg),
						    PPB_BUSINFO_SECONDARY(reg),
						    PPB_BUSINFO_SUBORDINATE(reg));
					}
					return (-1);
				}

				bus_max = (bus_sub > bus_max) ?
				    bus_sub : bus_max;
			}
		}
	}

	return (bus_max);	/* last # of subordinate bus */
}

int
pci_bus_assign(pci_chipset_tag_t pc, int bus)
{
	static int bridge_cnt;
	int bridge, device, maxdevs, function, nfuncs, bus_max, bus_sub;
	const struct pci_quirkdata *qd;
	pcireg_t reg;
	pcitag_t tag;

	bus_max = bus;

	maxdevs = pci_bus_maxdevs(pc, bus);
	for (device = 0; device < maxdevs; device++) {
		tag = pci_make_tag(pc, bus, device, 0);
		reg = pci_conf_read(pc, tag, PCI_ID_REG);

		/* can't be that many */
		if (bus_max == 255)
			break;

		/* Invalid vendor ID value? */
		if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
			continue;
		/* XXX Not invalid, but we've done this ~forever. */
		if (PCI_VENDOR(reg) == 0)
			continue;

		qd = pci_lookup_quirkdata(PCI_VENDOR(reg), PCI_PRODUCT(reg));

		reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
		if (PCI_HDRTYPE_MULTIFN(reg) ||
		    (qd != NULL &&
		     (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
			nfuncs = 8;
		else
			nfuncs = 1;

		for (function = 0; function < nfuncs; function++) {
			tag = pci_make_tag(pc, bus, device, function);
			reg = pci_conf_read(pc, tag, PCI_ID_REG);

			/* Invalid vendor ID value? */
			if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
				continue;
			/* XXX Not invalid, but we've done this ~forever. */
			if (PCI_VENDOR(reg) == 0)
				continue;

			reg = pci_conf_read(pc, tag, PCI_CLASS_REG);
			if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
			    (PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_PCI ||
			     PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_CARDBUS)) {
				/* Assign the bridge's secondary bus #. */
				bus_max++;

				reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
				reg &= 0xff000000;
				reg |= bus | (bus_max << 8) | (0xff << 16);
				pci_conf_write(pc, tag, PPB_REG_BUSINFO, reg);

				/* Scan subordinate bus. */
				bus_sub = pci_bus_assign(pc, bus_max);

				/* Configure the bridge. */
				reg &= 0xff000000;
				reg |= bus | (bus_max << 8) | (bus_sub << 16);
				pci_conf_write(pc, tag, PPB_REG_BUSINFO, reg);

				if (pcibios_flags & PCIBIOS_VERBOSE) {
					/* Assign the bridge #. */
					bridge = bridge_cnt++;

					printf("PCI bridge %d: primary %d, "
					    "secondary %d, subordinate %d\n",
					    bridge, bus, bus_max, bus_sub);
				}

				/* Next bridge's secondary bus #. */
				bus_max = (bus_sub > bus_max) ?
				    bus_sub : bus_max;
			}
		}
	}

	return (bus_max);	/* last # of subordinate bus */
}

int
pci_bus_fixup(pci_chipset_tag_t pc, int bus)
{
	int bus_max;

	bus_max = pci_bus_check(pc, bus);
	if (bus_max != -1)
		return (bus_max);

	if (pcibios_flags & PCIBIOS_VERBOSE)
		printf("PCI bus renumbering needed\n");
	return pci_bus_assign(pc, bus);
}

void
pcibus_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
{
	int bus, device, function;
	pcireg_t id;

	id = pci_conf_read(pc, tag, PCI_ID_REG);
	pci_decompose_tag(pc, tag, &bus, &device, &function);
	printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
	       PCI_VENDOR(id), PCI_PRODUCT(id));
}


code:
/*	$OpenBSD: pci_intr_fixup.c,v 1.62 2008/12/07 14:33:26 kettenis Exp $	*/
/*	$NetBSD: pci_intr_fixup.c,v 1.10 2000/08/10 21:18:27 soda Exp $	*/

/*
 * Copyright (c) 2001 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * PCI Interrupt Router support.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/device.h>

#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/i8259.h>
#include <machine/i82093var.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <i386/pci/pcibiosvar.h>

struct pciintr_link_map {
	int link, clink, irq, fixup_stage;
	u_int16_t bitmap;
	SIMPLEQ_ENTRY(pciintr_link_map) list;
};

pciintr_icu_tag_t pciintr_icu_tag = NULL;
pciintr_icu_handle_t pciintr_icu_handle;

#ifdef PCIBIOS_IRQS_HINT
int pcibios_irqs_hint = PCIBIOS_IRQS_HINT;
#endif

struct pciintr_link_map *pciintr_link_lookup(int);
struct pcibios_intr_routing *pciintr_pir_lookup(int, int);
int	pciintr_bitmap_count_irq(int, int *);

SIMPLEQ_HEAD(, pciintr_link_map) pciintr_link_map_list;

const struct pciintr_icu_table {
	pci_vendor_id_t	piit_vendor;
	pci_product_id_t piit_product;
	int (*piit_init)(pci_chipset_tag_t,
		bus_space_tag_t, pcitag_t, pciintr_icu_tag_t *,
		pciintr_icu_handle_t *);
} pciintr_icu_table[] = {
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_6300ESB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_6321ESB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82371MX,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82371AB_ISA,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82371FB_ISA,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82371SB_ISA,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82440MX_ISA,
	  piix_init },
	{ PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82801AA_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801AB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82801BA_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82801BAM_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82801CA_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82801CAM_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,     PCI_PRODUCT_INTEL_82801DB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801DBM_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801E_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801EB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801FB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801FBM_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801GB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801GBM_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801GH_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801GHM_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801IB_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801IH_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801IO_LPC,
	  piix_init },
	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_82801IR_LPC,
	  piix_init },

	{ PCI_VENDOR_OPTI,	PCI_PRODUCT_OPTI_82C558,
	  opti82c558_init },
	{ PCI_VENDOR_OPTI,	PCI_PRODUCT_OPTI_82C700,
	  opti82c700_init },

	{ PCI_VENDOR_RCC,	PCI_PRODUCT_RCC_OSB4,
	  osb4_init },
	{ PCI_VENDOR_RCC,	PCI_PRODUCT_RCC_CSB5,
	  osb4_init },

	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT82C586_ISA,
	  via82c586_init, },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT82C596A,
	  via82c586_init, },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT82C686A_ISA,
	  via82c586_init },

	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8231_ISA,
	  via8231_init },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8233_ISA,
	  via8231_init },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8233A_ISA,
	  via8231_init },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8235_ISA,
	  via8231_init },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8237_ISA,
	  via8231_init },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8237A_ISA,
	  via8231_init },
	{ PCI_VENDOR_VIATECH,	PCI_PRODUCT_VIATECH_VT8237S_ISA,
	  via8231_init },

	{ PCI_VENDOR_SIS,	PCI_PRODUCT_SIS_85C503,
	  sis85c503_init },
	{ PCI_VENDOR_SIS,	PCI_PRODUCT_SIS_962,
	  sis85c503_init },
	{ PCI_VENDOR_SIS,	PCI_PRODUCT_SIS_963,
	  sis85c503_init },

	{ PCI_VENDOR_AMD,	PCI_PRODUCT_AMD_PBC756_PMC,
	  amd756_init },
	{ PCI_VENDOR_AMD,	PCI_PRODUCT_AMD_766_PMC,
	  amd756_init },
	{ PCI_VENDOR_AMD,	PCI_PRODUCT_AMD_PBC768_PMC,
	  amd756_init },

	{ PCI_VENDOR_ALI,	PCI_PRODUCT_ALI_M1533,
	  ali1543_init },

	{ PCI_VENDOR_ALI,	PCI_PRODUCT_ALI_M1543,
	  ali1543_init },

	{ 0,			0,
	  NULL },
};

const struct pciintr_icu_table *pciintr_icu_lookup(pcireg_t);

const struct pciintr_icu_table *
pciintr_icu_lookup(id)
	pcireg_t id;
{
	const struct pciintr_icu_table *piit;

	for (piit = pciintr_icu_table; piit->piit_init != NULL; piit++)
		if (PCI_VENDOR(id) == piit->piit_vendor &&
		    PCI_PRODUCT(id) == piit->piit_product)
			return (piit);

	return (NULL);
}

struct pciintr_link_map *
pciintr_link_lookup(int link)
{
	struct pciintr_link_map *l;

	for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
	     l = SIMPLEQ_NEXT(l, list))
		if (l->link == link)
			return (l);

	return (NULL);
}

static __inline struct pciintr_link_map *
pciintr_link_alloc(pci_chipset_tag_t pc, struct pcibios_intr_routing *pir, int pin)
{
	int link = pir->linkmap[pin].link, clink, irq;
	struct pciintr_link_map *l, *lstart;

	if (pciintr_icu_tag != NULL) {
		/*
		 * Get the canonical link value for this entry.
		 */
		if (pciintr_icu_getclink(pciintr_icu_tag, pciintr_icu_handle,
		    link, &clink) != 0) {
			/*
			 * ICU doesn't understand the link value.
			 * Just ignore this PIR entry.
			 */
			PCIBIOS_PRINTV(("pciintr_link_alloc: bus %d device %d: "
			    "ignoring link 0x%02x\n", pir->bus,
			    PIR_DEVFUNC_DEVICE(pir->device), link));
			return (NULL);
		}

		/*
		 * Check the link value by asking the ICU for the
		 * canonical link value.
		 * Also, determine if this PIRQ is mapped to an IRQ.
		 */
		if (pciintr_icu_get_intr(pciintr_icu_tag, pciintr_icu_handle,
		    clink, &irq) != 0) {
			/*
			 * ICU doesn't understand the canonical link value.
			 * Just ignore this PIR entry.
			 */
			PCIBIOS_PRINTV(("pciintr_link_alloc: "
			    "bus %d device %d link 0x%02x: "
			    "ignoring PIRQ 0x%02x\n", pir->bus,
			    PIR_DEVFUNC_DEVICE(pir->device), link, clink));
			return (NULL);
		}
	}

	if ((l = malloc(sizeof(*l), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
		return (NULL);

	l->link = link;
	l->bitmap = pir->linkmap[pin].bitmap;
	if (pciintr_icu_tag != NULL) { /* compatible PCI ICU found */
		l->clink = clink;
		l->irq = irq; /* maybe I386_PCI_INTERRUPT_LINE_NO_CONNECTION */
	} else {
		l->clink = link;
		l->irq = I386_PCI_INTERRUPT_LINE_NO_CONNECTION;
	}

	lstart = SIMPLEQ_FIRST(&pciintr_link_map_list);
	if (lstart == NULL || lstart->link < l->link)
		SIMPLEQ_INSERT_TAIL(&pciintr_link_map_list, l, list);
	else
		SIMPLEQ_INSERT_HEAD(&pciintr_link_map_list, l, list);

	return (l);
}

struct pcibios_intr_routing *
pciintr_pir_lookup(int bus, int device)
{
	struct pcibios_intr_routing *pir;
	int entry;

	if (pcibios_pir_table == NULL)
		return (NULL);

	for (entry = 0; entry < pcibios_pir_table_nentries; entry++) {
		pir = &pcibios_pir_table[entry];
		if (pir->bus == bus &&
		    PIR_DEVFUNC_DEVICE(pir->device) == device)
			return (pir);
	}

	return (NULL);
}

int
pciintr_bitmap_count_irq(int irq_bitmap, int *irqp)
{
	int i, bit, count = 0, irq = I386_PCI_INTERRUPT_LINE_NO_CONNECTION;

	if (irq_bitmap != 0)
		for (i = 0, bit = 1; i < 16; i++, bit <<= 1)
			if (irq_bitmap & bit) {
				irq = i;
				count++;
			}

	*irqp = irq;
	return (count);
}

static __inline int
pciintr_link_init(pci_chipset_tag_t pc)
{
	int entry, pin, link;
	struct pcibios_intr_routing *pir;
	struct pciintr_link_map *l;

	if (pcibios_pir_table == NULL) {
		/* No PIR table; can't do anything. */
		printf("pciintr_link_init: no PIR table\n");
		return (1);
	}

	SIMPLEQ_INIT(&pciintr_link_map_list);

	for (entry = 0; entry < pcibios_pir_table_nentries; entry++) {
		pir = &pcibios_pir_table[entry];
		for (pin = 0; pin < PCI_INTERRUPT_PIN_MAX; pin++) {
			if ((link = pir->linkmap[pin].link) == 0)
				/* No connection for this pin. */
				continue;

			/*
			 * Multiple devices may be wired to the same
			 * interrupt; check to see if we've seen this
			 * one already.  If not, allocate a new link
			 * map entry and stuff it in the map.
			 */
			if ((l = pciintr_link_lookup(link)) == NULL)
				pciintr_link_alloc(pc, pir, pin);
			else if (pir->linkmap[pin].bitmap != l->bitmap) {
				/*
				 * violates PCI IRQ Routing Table Specification
				 */
				PCIBIOS_PRINTV(("pciintr_link_init: "
				    "bus %d device %d link 0x%02x: "
				    "bad irq bitmap 0x%04x, "
				    "should be 0x%04x\n", pir->bus,
				    PIR_DEVFUNC_DEVICE(pir->device), link,
				    pir->linkmap[pin].bitmap, l->bitmap));
				/* safer value. */
				l->bitmap &= pir->linkmap[pin].bitmap;
				/* XXX - or, should ignore this entry? */
			}
		}
	}

	return (0);
}

/*
 * No compatible PCI ICU found.
 * Hopes the BIOS already setup the ICU.
 */
static __inline int
pciintr_guess_irq(void)
{
	struct pciintr_link_map *l;
	int irq, guessed = 0;

	/*
	 * Stage 1: If only one IRQ is available for the link, use it.
	 */
	for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
	     l = SIMPLEQ_NEXT(l, list)) {
		if (l->irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
			continue;
		if (pciintr_bitmap_count_irq(l->bitmap, &irq) == 1) {
			l->irq = irq;
			l->fixup_stage = 1;
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf("pciintr_guess_irq (stage 1): "
				    "guessing PIRQ 0x%02x to be IRQ %d\n",
				    l->clink, l->irq);
			guessed = 1;
		}
	}

	return (guessed ? 0 : -1);
}

static __inline int
pciintr_link_fixup(void)
{
	struct pciintr_link_map *l;
	u_int16_t pciirq = 0;
	int irq;

	/*
	 * First stage: Attempt to connect PIRQs which aren't
	 * yet connected.
	 */
	for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
	     l = SIMPLEQ_NEXT(l, list)) {
		if (l->irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
			/*
			 * Interrupt is already connected.  Don't do
			 * anything to it.
			 * In this case, l->fixup_stage == 0.
			 */
			pciirq |= 1 << l->irq;
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf("pciintr_link_fixup: PIRQ 0x%02x is "
				    "already connected to IRQ %d\n",
				    l->clink, l->irq);
			continue;
		}
		/*
		 * Interrupt isn't connected.  Attempt to assign it to an IRQ.
		 */
		if (pcibios_flags & PCIBIOS_INTRDEBUG)
			printf("pciintr_link_fixup: PIRQ 0x%02x not connected",
			    l->clink);

		/*
		 * Just do the easy case now; we'll defer the harder ones
		 * to Stage 2.
		 */
		if (pciintr_bitmap_count_irq(l->bitmap, &irq) == 1) {
			l->irq = irq;
			l->fixup_stage = 1;
			pciirq |= 1 << irq;
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf(", assigning IRQ %d", l->irq);
		}
		if (pcibios_flags & PCIBIOS_INTRDEBUG)
			printf("\n");
	}

	/*
	 * Stage 2: Attempt to connect PIRQs which we didn't
	 * connect in Stage 1.
	 */
	for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
	     l = SIMPLEQ_NEXT(l, list))
		if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION &&
		    (irq = ffs(l->bitmap & pciirq)) > 0) {
			/*
			 * This IRQ is a valid PCI IRQ already
			 * connected to another PIRQ, and also an
			 * IRQ our PIRQ can use; connect it up!
			 */
			l->fixup_stage = 2;
			l->irq = irq - 1;
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf("pciintr_link_fixup (stage 2): "
				       "assigning IRQ %d to PIRQ 0x%02x\n",
				       l->irq, l->clink);
		}

#ifdef PCIBIOS_IRQS_HINT
	/*
	 * Stage 3: The worst case. I need configuration hint that
	 * user supplied a mask for the PCI irqs
	 */
	for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
	     l = SIMPLEQ_NEXT(l, list)) {
		if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION &&
		    (irq = ffs(l->bitmap & pcibios_irqs_hint)) > 0) {
			l->fixup_stage = 3;
			l->irq = irq - 1;
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf("pciintr_link_fixup (stage 3): "
				    "assigning IRQ %d to PIRQ 0x%02x\n",
				    l->irq, l->clink);
		}
	}
#endif /* PCIBIOS_IRQS_HINT */

	if (pcibios_flags & PCIBIOS_INTRDEBUG)
		printf("pciintr_link_fixup: piirq 0x%04x\n", pciirq);

	return (0);
}

int
pci_intr_route_link(pci_chipset_tag_t pc, pci_intr_handle_t *ihp)
{
	struct pciintr_link_map *l;
	pcireg_t intr;
	int irq, rv = 1;
	char *p = NULL;

	if (pcibios_flags & PCIBIOS_INTR_FIXUP)
		return 1;

	irq = ihp->line & APIC_INT_LINE_MASK;
	if (irq != 0 && irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
		pcibios_pir_header.exclusive_irq |= 1 << irq;

	l = ihp->link;
	if (!l || pciintr_icu_tag == NULL)
		return (1);

	if (l->fixup_stage == 0) {
		if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
			/* Appropriate interrupt was not found. */
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf("pci_intr_route_link: PIRQ 0x%02x: "
				    "no IRQ, try "
				    "\"option PCIBIOS_IRQS_HINT=0x%04x\"\n",
				    l->clink,
				    /* suggest irq 9/10/11, if possible */
				    (l->bitmap & 0x0e00) ? (l->bitmap & 0x0e00)
				    : l->bitmap);
		} else
			p = " preserved BIOS setting";
	} else {

		if (pciintr_icu_set_intr(pciintr_icu_tag, pciintr_icu_handle,
		    l->clink, l->irq) != 0 ||
		    pciintr_icu_set_trigger(pciintr_icu_tag, pciintr_icu_handle,
		    l->irq, IST_LEVEL) != 0) {
			p = " failed";
			rv = 0;
		} else
			p = "";
	}
	if (p && pcibios_flags & PCIBIOS_INTRDEBUG)
		printf("pci_intr_route_link: route PIRQ 0x%02x -> IRQ %d%s\n",
		    l->clink, l->irq, p);

	if (!rv)
		return (0);

	/*
	 * IRQs 14 and 15 are reserved for PCI IDE interrupts; don't muck
	 * with them.
	 */
	if (irq == 14 || irq == 15)
		return (1);

	intr = pci_conf_read(pc, ihp->tag, PCI_INTERRUPT_REG);
	if (irq != PCI_INTERRUPT_LINE(intr)) {
		intr &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
		intr |= irq << PCI_INTERRUPT_LINE_SHIFT;
		pci_conf_write(pc, ihp->tag, PCI_INTERRUPT_REG, intr);
	}

	return (1);
}

int
pci_intr_post_fixup(void)
{
	struct pciintr_link_map *l;
	int i, pciirq;

	if (pcibios_flags & PCIBIOS_INTR_FIXUP)
		return 1;

	if (!pciintr_icu_handle)
		return 0;

	pciirq = pcibios_pir_header.exclusive_irq;
	if (pcibios_flags & PCIBIOS_INTRDEBUG)
		printf("pci_intr_post_fixup: PCI IRQs:");
	for (l = SIMPLEQ_FIRST(&pciintr_link_map_list);
	    l != NULL; l = SIMPLEQ_NEXT(l, list))
		if (l->fixup_stage == 0 && l->irq != 0 &&
		    l->irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf(" %d", l->irq);
			pciirq |= (1 << l->irq);
		}

	if (pcibios_flags & PCIBIOS_INTRDEBUG)
		printf("; ISA IRQs:");
	for (i = 0; i < 16; i++)
		if (!(pciirq & (1 << i))) {
			if (pcibios_flags & PCIBIOS_INTRDEBUG)
				printf(" %d", i);
			pciintr_icu_set_trigger(pciintr_icu_tag,
			    pciintr_icu_handle, i, IST_EDGE);
		}

	if (pcibios_flags & PCIBIOS_INTRDEBUG)
		printf("\n");

	return (0);
}

int
pci_intr_header_fixup(pci_chipset_tag_t pc, pcitag_t tag,
    pci_intr_handle_t *ihp)
{
	struct pcibios_intr_routing *pir;
	struct pciintr_link_map *l;
	int irq, link, bus, device, function;
	char *p = NULL;

	if (pcibios_flags & PCIBIOS_INTR_FIXUP)
		return 1;

	irq = ihp->line & APIC_INT_LINE_MASK;
	ihp->link = NULL;
	pci_decompose_tag(pc, tag, &bus, &device, &function);

	if ((pir = pciintr_pir_lookup(bus, device)) == NULL ||
	    (link = pir->linkmap[ihp->pin - 1].link) == 0) {
		PCIBIOS_PRINTV(("Interrupt not connected; no need to change."));
		return 1;
	}

	if ((l = pciintr_link_lookup(link)) == NULL) {
		/*
		 * No link map entry.
		 * Probably pciintr_icu_getclink() or pciintr_icu_get_intr()
		 * has failed.
		 */
		if (pcibios_flags & PCIBIOS_INTRDEBUG)
			printf("pci_intr_header_fixup: no entry for link "
			    "0x%02x (%d:%d:%d:%c)\n",
			    link, bus, device, function, '@' + ihp->pin);
		return 1;
	}

	ihp->link = l;
	if (irq == 14 || irq == 15) {
		p = " WARNING: ignored";
		ihp->link = NULL;
	} else if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {

		/* Appropriate interrupt was not found. */
		if (pciintr_icu_tag == NULL && irq != 0 &&
		    irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
			/*
			 * Do not print warning,
			 * if no compatible PCI ICU found,
			 * but the irq is already assigned by BIOS.
			 */
			p = "";
		else
			p = " WARNING: missing";
	} else if (irq == 0 || irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {

		p = " fixed up";
		ihp->line = irq = l->irq;

	} else if (pcibios_flags & PCIBIOS_FIXUP_FORCE) {
		/* routed by BIOS, but inconsistent */
		/* believe PCI IRQ Routing table */
		p = " WARNING: overriding";
		ihp->line = irq = l->irq;
	} else {
		/* believe PCI Interrupt Configuration Register (default) */
		p = " WARNING: preserving";
		ihp->line = (l->irq = irq) | (l->clink & PCI_INT_VIA_ISA);
	}

	if (pcibios_flags & PCIBIOS_INTRDEBUG) {
		pcireg_t id = pci_conf_read(pc, tag, PCI_ID_REG);

		printf("\n%d:%d:%d %04x:%04x pin %c clink 0x%02x irq %d "
		    "stage %d %s irq %d\n", bus, device, function,
		    PCI_VENDOR(id), PCI_PRODUCT(id), '@' + ihp->pin, l->clink,
		    ((l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION)?
		    -1 : l->irq), l->fixup_stage, p, irq);
	}

	return (1);
}

int
pci_intr_fixup(struct pcibios_softc *sc, pci_chipset_tag_t pc,
    bus_space_tag_t iot)
{
	struct pcibios_pir_header *pirh = &pcibios_pir_header;
	const struct pciintr_icu_table *piit = NULL;
	pcitag_t icutag;

	/*
	 * Attempt to initialize our PCI interrupt router.  If
	 * the PIR Table is present in ROM, use the location
	 * specified by the PIR Table, and use the compat ID,
	 * if present.  Otherwise, we have to look for the router
	 * ourselves (the PCI-ISA bridge).
	 *
	 * A number of buggy BIOS implementations leave the router
	 * entry as 000:00:0, which is typically not the correct
	 * device/function.  If the router device address is set to
	 * this value, and the compatible router entry is undefined
	 * (zero is the correct value to indicate undefined), then we
	 * work on the basis it is most likely an error, and search
	 * the entire device-space of bus 0 (but obviously starting
	 * with 000:00:0, in case that really is the right one).
	 */
	if (pirh->signature != 0 && (pirh->router_bus != 0 ||
	    pirh->router_devfunc != 0 || pirh->compat_router != 0)) {

		icutag = pci_make_tag(pc, pirh->router_bus,
		    PIR_DEVFUNC_DEVICE(pirh->router_devfunc),
		    PIR_DEVFUNC_FUNCTION(pirh->router_devfunc));
		if (pirh->compat_router == 0 ||
		    (piit = pciintr_icu_lookup(pirh->compat_router)) == NULL) {
			/*
			 * No compat ID, or don't know the compat ID?  Read
			 * it from the configuration header.
			 */
			pirh->compat_router = pci_conf_read(pc, icutag,
			    PCI_ID_REG);
		}
		if (piit == NULL)
			piit = pciintr_icu_lookup(pirh->compat_router);
	} else {
		int device, maxdevs = pci_bus_maxdevs(pc, 0);

		/*
		 * Search configuration space for a known interrupt
		 * router.
		 */
		for (device = 0; device < maxdevs; device++) {
			const struct pci_quirkdata *qd;
			int function, nfuncs;
			pcireg_t icuid;
			pcireg_t bhlcr;

			icutag = pci_make_tag(pc, 0, device, 0);
			icuid = pci_conf_read(pc, icutag, PCI_ID_REG);

			/* Invalid vendor ID value? */
			if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
				continue;
			/* XXX Not invalid, but we've done this ~forever. */
			if (PCI_VENDOR(icuid) == 0)
				continue;

			qd = pci_lookup_quirkdata(PCI_VENDOR(icuid),
			    PCI_PRODUCT(icuid));

			bhlcr = pci_conf_read(pc, icutag, PCI_BHLC_REG);
			if (PCI_HDRTYPE_MULTIFN(bhlcr) || (qd != NULL &&
			    (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
				nfuncs = 8;
			else
				nfuncs = 1;

			for (function = 0; function < nfuncs; function++) {
				icutag = pci_make_tag(pc, 0, device, function);
				icuid = pci_conf_read(pc, icutag, PCI_ID_REG);

				/* Invalid vendor ID value? */
				if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
					continue;
				/* Not invalid, but we've done this ~forever. */
				if (PCI_VENDOR(icuid) == 0)
					continue;

				if ((piit = pciintr_icu_lookup(icuid))) {
					pirh->compat_router = icuid;
					pirh->router_bus = 0;
					pirh->router_devfunc =
					    PIR_DEVFUNC_COMPOSE(device, 0);
					break;
				}
			}

			if (piit != NULL)
				break;
		}
	}

	if (piit == NULL) {
		printf("%s: no compatible PCI ICU found", sc->sc_dev.dv_xname);
		if (pirh->signature != 0 && pirh->compat_router != 0)
			printf(": ICU vendor 0x%04x product 0x%04x",
			    PCI_VENDOR(pirh->compat_router),
			    PCI_PRODUCT(pirh->compat_router));
		printf("\n");
		if (!(pcibios_flags & PCIBIOS_INTR_GUESS)) {
			if (pciintr_link_init(pc))
				return (-1);	/* non-fatal */
			if (pciintr_guess_irq())
				return (-1);	/* non-fatal */
		}
		return (0);
	} else {
		char devinfo[256];

		printf("%s: PCI Interrupt Router at %03d:%02d:%01d",
		    sc->sc_dev.dv_xname, pirh->router_bus,
		    PIR_DEVFUNC_DEVICE(pirh->router_devfunc),
		    PIR_DEVFUNC_FUNCTION(pirh->router_devfunc));
		if (pirh->compat_router != 0) {
			pci_devinfo(pirh->compat_router, 0, 0, devinfo,
			    sizeof devinfo);
			printf(" (%s)", devinfo);
		}
		printf("\n");
	}

	/*
	 * Initialize the PCI ICU.
	 */
	if ((*piit->piit_init)(pc, iot, icutag, &pciintr_icu_tag,
	    &pciintr_icu_handle) != 0)
		return (-1);		/* non-fatal */

	/*
	 * Initialize the PCI interrupt link map.
	 */
	if (pciintr_link_init(pc))
		return (-1);		/* non-fatal */

	/*
	 * Fix up the link->IRQ mappings.
	 */
	if (pciintr_link_fixup() != 0)
		return (-1);		/* non-fatal */

	return (0);
}

license: unknown


code:
/*	$OpenBSD: pci_machdep.c,v 1.80 2015/01/24 15:13:55 kettenis Exp $	*/
/*	$NetBSD: pci_machdep.c,v 1.28 1997/06/06 23:29:17 thorpej Exp $	*/

/*-
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
 * Copyright (c) 1994 Charles Hannum.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Charles Hannum.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Machine-specific functions for PCI autoconfiguration.
 *
 * On PCs, there are two methods of generating PCI configuration cycles.
 * We try to detect the appropriate mechanism for this machine and set
 * up a few function pointers to access the correct method directly.
 *
 * The configuration method can be hard-coded in the config file by
 * using `options PCI_CONF_MODE=N', where `N' is the configuration mode
 * as defined section 3.6.4.1, `Generating Configuration Cycles'.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/extent.h>
#include <sys/malloc.h>

#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <machine/pio.h>
#include <machine/i8259.h>
#include <machine/biosvar.h>

#include "bios.h"
#if NBIOS > 0
extern bios_pciinfo_t *bios_pciinfo;
#endif

#include <dev/isa/isavar.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/ppbreg.h>

#include "ioapic.h"

#include <machine/i82093var.h>
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#if NIOAPIC > 0
#include <machine/mpbiosvar.h>
#endif

#include "pcibios.h"
#if NPCIBIOS > 0
#include <i386/pci/pcibiosvar.h>
#endif

int pci_mode = -1;

/*
 * Memory Mapped Configuration space access.
 *
 * Since mapping the whole configuration space will cost us up to
 * 256MB of kernel virtual memory, we use seperate mappings per bus.
 * The mappings are created on-demand, such that we only use kernel
 * virtual memory for busses that are actually present.
 */
bus_addr_t pci_mcfg_addr;
int pci_mcfg_min_bus, pci_mcfg_max_bus;
bus_space_tag_t pci_mcfgt = I386_BUS_SPACE_MEM;
bus_space_handle_t pci_mcfgh[256];
void pci_mcfg_map_bus(int);

struct mutex pci_conf_lock = MUTEX_INITIALIZER(IPL_HIGH);

#define	PCI_CONF_LOCK()							\
do {									\
	mtx_enter(&pci_conf_lock);					\
} while (0)

#define	PCI_CONF_UNLOCK()						\
do {									\
	mtx_leave(&pci_conf_lock);					\
} while (0)

#define	PCI_MODE1_ENABLE	0x80000000UL
#define	PCI_MODE1_ADDRESS_REG	0x0cf8
#define	PCI_MODE1_DATA_REG	0x0cfc

#define	PCI_MODE2_ENABLE_REG	0x0cf8
#define	PCI_MODE2_FORWARD_REG	0x0cfa

#define _m1tag(b, d, f) \
	(PCI_MODE1_ENABLE | ((b) << 16) | ((d) << 11) | ((f) << 8))
#define _qe(bus, dev, fcn, vend, prod) \
	{_m1tag(bus, dev, fcn), PCI_ID_CODE(vend, prod)}
struct {
	u_int32_t tag;
	pcireg_t id;
} pcim1_quirk_tbl[] = {
	_qe(0, 0, 0, PCI_VENDOR_COMPAQ, PCI_PRODUCT_COMPAQ_TRIFLEX1),
	/* XXX Triflex2 not tested */
	_qe(0, 0, 0, PCI_VENDOR_COMPAQ, PCI_PRODUCT_COMPAQ_TRIFLEX2),
	_qe(0, 0, 0, PCI_VENDOR_COMPAQ, PCI_PRODUCT_COMPAQ_TRIFLEX4),
	/* Triton needed for Connectix Virtual PC */
	_qe(0, 0, 0, PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437FX),
	/* Connectix Virtual PC 5 has a 440BX */
	_qe(0, 0, 0, PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX_NOAGP),
	{0, 0xffffffff} /* patchable */
};
#undef _m1tag
#undef _qe

/*
 * PCI doesn't have any special needs; just use the generic versions
 * of these functions.
 */
struct bus_dma_tag pci_bus_dma_tag = {
	NULL,			/* _cookie */
	_bus_dmamap_create,
	_bus_dmamap_destroy,
	_bus_dmamap_load,
	_bus_dmamap_load_mbuf,
	_bus_dmamap_load_uio,
	_bus_dmamap_load_raw,
	_bus_dmamap_unload,
	_bus_dmamap_sync,
	_bus_dmamem_alloc,
	_bus_dmamem_alloc_range,
	_bus_dmamem_free,
	_bus_dmamem_map,
	_bus_dmamem_unmap,
	_bus_dmamem_mmap,
};

void
pci_attach_hook(struct device *parent, struct device *self,
    struct pcibus_attach_args *pba)
{
	pci_chipset_tag_t pc = pba->pba_pc;
	pcitag_t tag;
	pcireg_t id, class;

#if NBIOS > 0
	if (pba->pba_bus == 0)
		printf(": configuration mode %d (%s)",
			pci_mode, (bios_pciinfo?"bios":"no bios"));
#else
	if (pba->pba_bus == 0)
		printf(": configuration mode %d", pci_mode);
#endif

	if (pba->pba_bus != 0)
		return;

	/*
	 * Machines that use the non-standard method of generating PCI
	 * configuration cycles are way too old to support MSI.
	 */
	if (pci_mode == 2)
		return;

	/*
	 * In order to decide whether the system supports MSI we look
	 * at the host bridge, which should be device 0 function 0 on
	 * bus 0.  It is better to not enable MSI on systems that
	 * support it than the other way around, so be conservative
	 * here.  So we don't enable MSI if we don't find a host
	 * bridge there.  We also deliberately don't enable MSI on
	 * chipsets from low-end manifacturers like VIA and SiS.
	 */
	tag = pci_make_tag(pc, 0, 0, 0);
	id = pci_conf_read(pc, tag, PCI_ID_REG);
	class = pci_conf_read(pc, tag, PCI_CLASS_REG);

	if (PCI_CLASS(class) != PCI_CLASS_BRIDGE ||
	    PCI_SUBCLASS(class) != PCI_SUBCLASS_BRIDGE_HOST)
		return;

	switch (PCI_VENDOR(id)) {
	case PCI_VENDOR_INTEL:
		/*
		 * For Intel platforms, MSI support was introduced
		 * with the new Pentium 4 processor interrupt delivery
		 * mechanism, so we blacklist all PCI chipsets that
		 * support Pentium III and earlier CPUs.
		 */
		switch (PCI_PRODUCT(id)) {
		case PCI_PRODUCT_INTEL_PCMC: /* 82434LX/NX */
		case PCI_PRODUCT_INTEL_82437FX:
		case PCI_PRODUCT_INTEL_82437MX:
		case PCI_PRODUCT_INTEL_82437VX:
		case PCI_PRODUCT_INTEL_82439HX:
		case PCI_PRODUCT_INTEL_82439TX:
		case PCI_PRODUCT_INTEL_82440BX:
		case PCI_PRODUCT_INTEL_82440BX_AGP:
		case PCI_PRODUCT_INTEL_82440MX_HB:
		case PCI_PRODUCT_INTEL_82441FX:
		case PCI_PRODUCT_INTEL_82443BX:
		case PCI_PRODUCT_INTEL_82443BX_AGP:
		case PCI_PRODUCT_INTEL_82443BX_NOAGP:
		case PCI_PRODUCT_INTEL_82443GX:
		case PCI_PRODUCT_INTEL_82443LX:
		case PCI_PRODUCT_INTEL_82443LX_AGP:
		case PCI_PRODUCT_INTEL_82810_HB:
		case PCI_PRODUCT_INTEL_82810E_HB:
		case PCI_PRODUCT_INTEL_82815_HB:
		case PCI_PRODUCT_INTEL_82820_HB:
		case PCI_PRODUCT_INTEL_82830M_HB:
		case PCI_PRODUCT_INTEL_82840_HB:
			break;
		default:
			pba->pba_flags |= PCI_FLAGS_MSI_ENABLED;
			break;
		}
		break;
	case PCI_VENDOR_NVIDIA:
		/*
		 * Since NVIDIA chipsets are completely undocumented,
		 * we have to make a guess here.  We assume that all
		 * chipsets that support PCIe include support for MSI,
		 * since support for MSI is mandated by the PCIe
		 * standard.
		 */
		switch (PCI_PRODUCT(id)) {
		case PCI_PRODUCT_NVIDIA_NFORCE_PCHB:
		case PCI_PRODUCT_NVIDIA_NFORCE2_PCHB:
			break;
		default:
			pba->pba_flags |= PCI_FLAGS_MSI_ENABLED;
			break;
		}
		break;
	case PCI_VENDOR_AMD:
		/*
		 * The AMD-750 and AMD-760 chipsets don't support MSI.
		 */
		switch (PCI_PRODUCT(id)) {
		case PCI_PRODUCT_AMD_SC751_SC:
		case PCI_PRODUCT_AMD_761_PCHB:
		case PCI_PRODUCT_AMD_762_PCHB:
			break;
		default:
			pba->pba_flags |= PCI_FLAGS_MSI_ENABLED;
			break;
		}
		break;
	}

	/*
	 * Don't enable MSI on a HyperTransport bus.  In order to
	 * determine that bus 0 is a HyperTransport bus, we look at
	 * device 24 function 0, which is the HyperTransport
	 * host/primary interface integrated on most 64-bit AMD CPUs.
	 * If that device has a HyperTransport capability, bus 0 must
	 * be a HyperTransport bus and we disable MSI.
	 */
	tag = pci_make_tag(pc, 0, 24, 0);
	if (pci_get_capability(pc, tag, PCI_CAP_HT, NULL, NULL))
		pba->pba_flags &= ~PCI_FLAGS_MSI_ENABLED;
}

int
pci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
{

	/*
	 * Bus number is irrelevant.  If Configuration Mechanism 2 is in
	 * use, can only have devices 0-15 on any bus.  If Configuration
	 * Mechanism 1 is in use, can have devices 0-31 (i.e. the `normal'
	 * range).
	 */
	if (pci_mode == 2)
		return (16);
	else
		return (32);
}

pcitag_t
pci_make_tag(pci_chipset_tag_t pc, int bus, int device, int function)
{
	pcitag_t tag;

	switch (pci_mode) {
	case 1:
		if (bus >= 256 || device >= 32 || function >= 8)
			panic("pci_make_tag: bad request");

		tag.mode1 = PCI_MODE1_ENABLE |
		    	(bus << 16) | (device << 11) | (function << 8);
		break;
	case 2:
		if (bus >= 256 || device >= 16 || function >= 8)
			panic("pci_make_tag: bad request");

		tag.mode2.port = 0xc000 | (device << 8);
		tag.mode2.enable = 0xf0 | (function << 1);
		tag.mode2.forward = bus;
		break;
	default:
		panic("pci_make_tag: mode not configured");
	}

	return tag;
}

void
pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *bp, int *dp, int *fp)
{
	switch (pci_mode) {
	case 1:
		if (bp != NULL)
			*bp = (tag.mode1 >> 16) & 0xff;
		if (dp != NULL)
			*dp = (tag.mode1 >> 11) & 0x1f;
		if (fp != NULL)
			*fp = (tag.mode1 >> 8) & 0x7;
		break;
	case 2:
		if (bp != NULL)
			*bp = tag.mode2.forward & 0xff;
		if (dp != NULL)
			*dp = (tag.mode2.port >> 8) & 0xf;
		if (fp != NULL)
			*fp = (tag.mode2.enable >> 1) & 0x7;
		break;
	default:
		panic("pci_decompose_tag: mode not configured");
	}
}

int
pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
{
	int bus;

	if (pci_mcfg_addr) {
		pci_decompose_tag(pc, tag, &bus, NULL, NULL);
		if (bus >= pci_mcfg_min_bus && bus <= pci_mcfg_max_bus)
			return PCIE_CONFIG_SPACE_SIZE;
	}

	return PCI_CONFIG_SPACE_SIZE;
}

void
pci_mcfg_map_bus(int bus)
{
	if (pci_mcfgh[bus])
		return;

	if (bus_space_map(pci_mcfgt, pci_mcfg_addr + (bus << 20), 1 << 20,
	    0, &pci_mcfgh[bus]))
		panic("pci_conf_read: cannot map mcfg space");
}

pcireg_t
pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
	pcireg_t data;
	int bus;

	KASSERT((reg & 0x3) == 0);

	if (pci_mcfg_addr && reg >= PCI_CONFIG_SPACE_SIZE) {
		pci_decompose_tag(pc, tag, &bus, NULL, NULL);
		if (bus >= pci_mcfg_min_bus && bus <= pci_mcfg_max_bus) {
			pci_mcfg_map_bus(bus);
			data = bus_space_read_4(pci_mcfgt, pci_mcfgh[bus],
			    (tag.mode1 & 0x000ff00) << 4 | reg);
			return data;
		}
	}

	PCI_CONF_LOCK();
	switch (pci_mode) {
	case 1:
		outl(PCI_MODE1_ADDRESS_REG, tag.mode1 | reg);
		data = inl(PCI_MODE1_DATA_REG);
		outl(PCI_MODE1_ADDRESS_REG, 0);
		break;
	case 2:
		outb(PCI_MODE2_ENABLE_REG, tag.mode2.enable);
		outb(PCI_MODE2_FORWARD_REG, tag.mode2.forward);
		data = inl(tag.mode2.port | reg);
		outb(PCI_MODE2_ENABLE_REG, 0);
		break;
	default:
		panic("pci_conf_read: mode not configured");
	}
	PCI_CONF_UNLOCK();

	return data;
}

void
pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
{
	int bus;

	KASSERT((reg & 0x3) == 0);

	if (pci_mcfg_addr && reg >= PCI_CONFIG_SPACE_SIZE) {
		pci_decompose_tag(pc, tag, &bus, NULL, NULL);
		if (bus >= pci_mcfg_min_bus && bus <= pci_mcfg_max_bus) {
			pci_mcfg_map_bus(bus);
			bus_space_write_4(pci_mcfgt, pci_mcfgh[bus],
			    (tag.mode1 & 0x000ff00) << 4 | reg, data);
			return;
		}
	}

	PCI_CONF_LOCK();
	switch (pci_mode) {
	case 1:
		outl(PCI_MODE1_ADDRESS_REG, tag.mode1 | reg);
		outl(PCI_MODE1_DATA_REG, data);
		outl(PCI_MODE1_ADDRESS_REG, 0);
		break;
	case 2:
		outb(PCI_MODE2_ENABLE_REG, tag.mode2.enable);
		outb(PCI_MODE2_FORWARD_REG, tag.mode2.forward);
		outl(tag.mode2.port | reg, data);
		outb(PCI_MODE2_ENABLE_REG, 0);
		break;
	default:
		panic("pci_conf_write: mode not configured");
	}
	PCI_CONF_UNLOCK();
}

int
pci_mode_detect(void)
{

#ifdef PCI_CONF_MODE
#if (PCI_CONF_MODE == 1) || (PCI_CONF_MODE == 2)
	return (pci_mode = PCI_CONF_MODE);
#else
#error Invalid PCI configuration mode.
#endif
#else
	u_int32_t sav, val;
	int i;
	pcireg_t idreg;

	if (pci_mode != -1)
		return (pci_mode);

#if NBIOS > 0
	/*
	 * If we have PCI info passed from the BIOS, use the mode given there
	 * for all of this code.  If not, pass on through to the previous tests
	 * to try and divine the correct mode.
	 */
	if (bios_pciinfo != NULL) {
		if (bios_pciinfo->pci_chars & 0x2)
			return (pci_mode = 2);

		if (bios_pciinfo->pci_chars & 0x1)
			return (pci_mode = 1);

		/* We should never get here, but if we do, fall through... */
	}
#endif

	/*
	 * We try to divine which configuration mode the host bridge wants.
	 *
	 * This should really be done using the PCI BIOS.  If we get here, the
	 * PCI BIOS does not exist, or the boot blocks did not provide the
	 * information.
	 */

	sav = inl(PCI_MODE1_ADDRESS_REG);

	pci_mode = 1; /* assume this for now */
	/*
	 * catch some known buggy implementations of mode 1
	 */
	for (i = 0; i < sizeof(pcim1_quirk_tbl) / sizeof(pcim1_quirk_tbl[0]);
	     i++) {
		pcitag_t t;

		if (!pcim1_quirk_tbl[i].tag)
			break;
		t.mode1 = pcim1_quirk_tbl[i].tag;
		idreg = pci_conf_read(0, t, PCI_ID_REG); /* needs "pci_mode" */
		if (idreg == pcim1_quirk_tbl[i].id) {
#ifdef DEBUG
			printf("known mode 1 PCI chipset (%08x)\n",
			       idreg);
#endif
			return (pci_mode);
		}
	}

	/*
	 * Strong check for standard compliant mode 1:
	 * 1. bit 31 ("enable") can be set
	 * 2. byte/word access does not affect register
 	 */
	outl(PCI_MODE1_ADDRESS_REG, PCI_MODE1_ENABLE);
	outb(PCI_MODE1_ADDRESS_REG + 3, 0);
	outw(PCI_MODE1_ADDRESS_REG + 2, 0);
	val = inl(PCI_MODE1_ADDRESS_REG);
	if ((val & 0x80fffffc) != PCI_MODE1_ENABLE) {
#ifdef DEBUG
		printf("pci_mode_detect: mode 1 enable failed (%x)\n",
		       val);
#endif
		goto not1;
	}
	outl(PCI_MODE1_ADDRESS_REG, 0);
	val = inl(PCI_MODE1_ADDRESS_REG);
	if ((val & 0x80fffffc) != 0)
		goto not1;
	return (pci_mode);
not1:
	outl(PCI_MODE1_ADDRESS_REG, sav);

	/*
	 * This mode 2 check is quite weak (and known to give false
	 * positives on some Compaq machines).
	 * However, this doesn't matter, because this is the
	 * last test, and simply no PCI devices will be found if
	 * this happens.
	 */
	outb(PCI_MODE2_ENABLE_REG, 0);
	outb(PCI_MODE2_FORWARD_REG, 0);
	if (inb(PCI_MODE2_ENABLE_REG) != 0 ||
	    inb(PCI_MODE2_FORWARD_REG) != 0)
		goto not2;
	return (pci_mode = 2);
not2:
	return (pci_mode = 0);
#endif
}

int
pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	pci_chipset_tag_t pc = pa->pa_pc;
	pcitag_t tag = pa->pa_tag;

	if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 || mp_busses == NULL ||
	    pci_get_capability(pc, tag, PCI_CAP_MSI, NULL, NULL) == 0)
		return 1;

	ihp->tag = tag;
	ihp->line = APIC_INT_VIA_MSG;
	ihp->pin = 0;
	return 0;
}

int
pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	int pin = pa->pa_rawintrpin;
	int line = pa->pa_intrline;
#if NIOAPIC > 0
	struct mp_intr_map *mip;
	int bus, dev, func;
#endif

	if (pin == 0) {
		/* No IRQ used. */
		goto bad;
	}

	if (pin > PCI_INTERRUPT_PIN_MAX) {
		printf("pci_intr_map: bad interrupt pin %d\n", pin);
		goto bad;
	}

	ihp->tag = pa->pa_tag;
	ihp->line = line;
	ihp->pin = pin;

#if NIOAPIC > 0
	pci_decompose_tag (pa->pa_pc, pa->pa_tag, &bus, &dev, &func);

	if (!(ihp->line & PCI_INT_VIA_ISA) && mp_busses != NULL) {
		int mpspec_pin = (dev << 2) | (pin - 1);

		if (bus < mp_nbusses) {
			for (mip = mp_busses[bus].mb_intrs;
			     mip != NULL; mip = mip->next) {
				if (&mp_busses[bus] == mp_isa_bus ||
				    &mp_busses[bus] == mp_eisa_bus)
					continue;
				if (mip->bus_pin == mpspec_pin) {
					ihp->line = mip->ioapic_ih | line;
					return 0;
				}
			}
		}

		if (pa->pa_bridgetag) {
			int swizpin = PPB_INTERRUPT_SWIZZLE(pin, dev);
			if (pa->pa_bridgeih[swizpin - 1].line != -1) {
				ihp->line = pa->pa_bridgeih[swizpin - 1].line;
				ihp->line |= line;
				return 0;
			}
		}
		/*
		 * No explicit PCI mapping found. This is not fatal,
		 * we'll try the ISA (or possibly EISA) mappings next.
		 */
	}
#endif

#if NPCIBIOS > 0
	pci_intr_header_fixup(pa->pa_pc, pa->pa_tag, ihp);
	line = ihp->line & APIC_INT_LINE_MASK;
#endif

	/*
	 * Section 6.2.4, `Miscellaneous Functions', says that 255 means
	 * `unknown' or `no connection' on a PC.  We assume that a device with
	 * `no connection' either doesn't have an interrupt (in which case the
	 * pin number should be 0, and would have been noticed above), or
	 * wasn't configured by the BIOS (in which case we punt, since there's
	 * no real way we can know how the interrupt lines are mapped in the
	 * hardware).
	 *
	 * XXX
	 * Since IRQ 0 is only used by the clock, and we can't actually be sure
	 * that the BIOS did its job, we also recognize that as meaning that
	 * the BIOS has not configured the device.
	 */
	if (line == 0 || line == I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
		goto bad;

	if (line >= ICU_LEN) {
		printf("pci_intr_map: bad interrupt line %d\n", line);
		goto bad;
	}
	if (line == 2) {
		printf("pci_intr_map: changed line 2 to line 9\n");
		line = 9;
	}

#if NIOAPIC > 0
	if (!(ihp->line & PCI_INT_VIA_ISA) && mp_busses != NULL) {
		if (mip == NULL && mp_isa_bus) {
			for (mip = mp_isa_bus->mb_intrs; mip != NULL;
			    mip = mip->next) {
				if (mip->bus_pin == line) {
					ihp->line = mip->ioapic_ih | line;
					return 0;
				}
			}
		}
		if (mip == NULL && mp_eisa_bus) {
			for (mip = mp_eisa_bus->mb_intrs;  mip != NULL;
			    mip = mip->next) {
				if (mip->bus_pin == line) {
					ihp->line = mip->ioapic_ih | line;
					return 0;
				}
			}
		}
		if (mip == NULL) {
			printf("pci_intr_map: "
			    "bus %d dev %d func %d pin %d; line %d\n",
			    bus, dev, func, pin, line);
			printf("pci_intr_map: no MP mapping found\n");
		}
	}
#endif

	return 0;

bad:
	ihp->line = -1;
	return 1;
}

const char *
pci_intr_string(pci_chipset_tag_t pc, pci_intr_handle_t ih)
{
	static char irqstr[64];
	int line = ih.line & APIC_INT_LINE_MASK;

	if (ih.line & APIC_INT_VIA_MSG)
		return ("msi");

#if NIOAPIC > 0
	if (ih.line & APIC_INT_VIA_APIC) {
		snprintf(irqstr, sizeof irqstr, "apic %d int %d",
		     APIC_IRQ_APIC(ih.line), APIC_IRQ_PIN(ih.line));
		return (irqstr);
	}
#endif

	if (line == 0 || line >= ICU_LEN || line == 2)
		panic("pci_intr_string: bogus handle 0x%x", line);

	snprintf(irqstr, sizeof irqstr, "irq %d", line);
	return (irqstr);
}

#include "acpiprt.h"
#if NACPIPRT > 0
void	acpiprt_route_interrupt(int bus, int dev, int pin);
#endif

extern struct intrhand *apic_intrhand[256];
extern int apic_maxlevel[256];

void *
pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, int level,
    int (*func)(void *), void *arg, const char *what)
{
	void *ret;
	int bus, dev;
	int l = ih.line & APIC_INT_LINE_MASK;
	pcitag_t tag = ih.tag;
	int irq = ih.line;

	if (ih.line & APIC_INT_VIA_MSG) {
		struct intrhand *ih;
		pcireg_t reg, addr;
		int off, vec;
		int flags;

		flags = level & IPL_MPSAFE;
		level &= ~IPL_MPSAFE;

		KASSERT(level <= IPL_TTY || flags & IPL_MPSAFE);

		if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
			panic("%s: no msi capability", __func__);

		vec = idt_vec_alloc(level, level + 15);
		if (vec == 0)
			return (NULL);

		ih = malloc(sizeof(*ih), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
		if (ih == NULL)
			panic("%s: can't malloc handler info", __func__);

		ih->ih_fun = func;
		ih->ih_arg = arg;
		ih->ih_next = NULL;
		ih->ih_level = level;
		ih->ih_flags = flags;
		ih->ih_irq = irq;
		ih->ih_pin = tag.mode1;
		ih->ih_vec = vec;
		evcount_attach(&ih->ih_count, what, &ih->ih_vec);

		apic_maxlevel[vec] = level;
		apic_intrhand[vec] = ih;
		idt_vec_set(vec, apichandler[vec & 0xf]);

		addr = 0xfee00000UL | (cpu_info_primary.ci_apicid << 12);

		if (reg & PCI_MSI_MC_C64) {
			pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
			pci_conf_write(pc, tag, off + PCI_MSI_MAU32, 0);
			pci_conf_write(pc, tag, off + PCI_MSI_MD64, vec);
		} else {
			pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
			pci_conf_write(pc, tag, off + PCI_MSI_MD32, vec);
		}
		pci_conf_write(pc, tag, off, reg | PCI_MSI_MC_MSIE);
		return (ih);
	}

	pci_decompose_tag(pc, ih.tag, &bus, &dev, NULL);
#if NACPIPRT > 0
	acpiprt_route_interrupt(bus, dev, ih.pin);
#endif

#if NIOAPIC > 0
	if (l != -1 && ih.line & APIC_INT_VIA_APIC)
		return (apic_intr_establish(ih.line, IST_LEVEL, level, func,
		    arg, what));
#endif
	if (l == 0 || l >= ICU_LEN || l == 2)
		panic("pci_intr_establish: bogus handle 0x%x", l);

	ret = isa_intr_establish(NULL, l, IST_LEVEL, level, func, arg, what);
#if NPCIBIOS > 0
	if (ret)
		pci_intr_route_link(pc, &ih);
#endif
	return (ret);
}

void
pci_intr_disestablish(pci_chipset_tag_t pc, void *cookie)
{
	struct intrhand *ih = cookie;

	if (ih->ih_irq & APIC_INT_VIA_MSG) {
		pcitag_t tag = { .mode1 = ih->ih_pin };
		pcireg_t reg;
		int off;

		if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg))
			pci_conf_write(pc, tag, off, reg &= ~PCI_MSI_MC_MSIE);

		apic_maxlevel[ih->ih_vec] = 0;
		apic_intrhand[ih->ih_vec] = NULL;
		idt_vec_free(ih->ih_vec);

		evcount_detach(&ih->ih_count);
		free(ih, M_DEVBUF, 0);
		return;
	}

	/* XXX oh, unroute the pci int link? */
	isa_intr_disestablish(NULL, cookie);
}

struct extent *pciio_ex;
struct extent *pcimem_ex;
struct extent *pcibus_ex;

void
pci_init_extents(void)
{
	bios_memmap_t *bmp;
	u_int64_t size;

	if (pciio_ex == NULL) {
		/*
		 * We only have 64K of addressable I/O space.
		 * However, since BARs may contain garbage, we cover
		 * the full 32-bit address space defined by PCI of
		 * which we only make the first 64K available.
		 */
		pciio_ex = extent_create("pciio", 0, 0xffffffff, M_DEVBUF,
		    NULL, 0, EX_NOWAIT | EX_FILLED);
		if (pciio_ex == NULL)
			return;
		extent_free(pciio_ex, 0, 0x10000, M_NOWAIT);
	}

	if (pcimem_ex == NULL) {
		pcimem_ex = extent_create("pcimem", 0, 0xffffffff, M_DEVBUF,
		    NULL, 0, EX_NOWAIT);
		if (pcimem_ex == NULL)
			return;

		for (bmp = bios_memmap; bmp->type != BIOS_MAP_END; bmp++) {
			/*
			 * Ignore address space beyond 4G.
			 */
			if (bmp->addr >= 0x100000000ULL)
				continue;
			size = bmp->size;
			if (bmp->addr + size >= 0x100000000ULL)
				size = 0x100000000ULL - bmp->addr;

			/* Ignore zero-sized regions. */
			if (size == 0)
				continue;

			if (extent_alloc_region(pcimem_ex, bmp->addr, size,
			    EX_NOWAIT))
				printf("memory map conflict 0x%llx/0x%llx\n",
				    bmp->addr, bmp->size);
		}

		/* Take out the video buffer area and BIOS areas. */
		extent_alloc_region(pcimem_ex, IOM_BEGIN, IOM_SIZE,
		    EX_CONFLICTOK | EX_NOWAIT);
	}

	if (pcibus_ex == NULL) {
		pcibus_ex = extent_create("pcibus", 0, 0xff, M_DEVBUF,
		    NULL, 0, EX_NOWAIT);
	}
}

#include "acpi.h"
#if NACPI > 0
void acpi_pci_match(struct device *, struct pci_attach_args *);
pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
void acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int);
#endif

void
pci_dev_postattach(struct device *dev, struct pci_attach_args *pa)
{
#if NACPI > 0
	acpi_pci_match(dev, pa);
#endif
}

pcireg_t
pci_min_powerstate(pci_chipset_tag_t pc, pcitag_t tag)
{
#if NACPI > 0
	return acpi_pci_min_powerstate(pc, tag);
#else
	return pci_get_powerstate(pc, tag);
#endif
}

void
pci_set_powerstate_md(pci_chipset_tag_t pc, pcitag_t tag, int state, int pre)
{
#if NACPI > 0
	acpi_pci_set_powerstate(pc, tag, state, pre);
#endif
}


code:
/*	$OpenBSD: pci_machdep.h,v 1.27 2015/07/17 22:42:09 kettenis Exp $	*/
/*	$NetBSD: pci_machdep.h,v 1.7 1997/06/06 23:29:18 thorpej Exp $	*/

/*
 * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
 * Copyright (c) 1994 Charles Hannum.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Charles Hannum.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Machine-specific definitions for PCI autoconfiguration.
 */

/*
 * i386-specific PCI structure and type definitions.
 * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE.
 *
 * Configuration tag; created from a {bus,device,function} triplet by
 * pci_make_tag(), and passed to pci_conf_read() and pci_conf_write().
 * We could instead always pass the {bus,device,function} triplet to
 * the read and write routines, but this would cause extra overhead.
 *
 * Mode 2 is historical and deprecated by the Revision 2.0 specification.
 */
union i386_pci_tag_u {
	u_int32_t mode1;
	struct {
		u_int16_t port;
		u_int8_t enable;
		u_int8_t forward;
	} mode2;
};

extern struct bus_dma_tag pci_bus_dma_tag;

/*
 * Types provided to machine-independent PCI code
 */
typedef void *pci_chipset_tag_t;
typedef union i386_pci_tag_u pcitag_t;

typedef
struct {
	pcitag_t tag;
	int line, pin;
	void *link;
} pci_intr_handle_t;

/*
 * i386-specific PCI variables and functions.
 * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE.
 */
extern int pci_mode;
extern bus_addr_t pci_mcfg_addr;
extern int pci_mcfg_min_bus, pci_mcfg_max_bus;

int		pci_mode_detect(void);

extern struct extent *pciio_ex;
extern struct extent *pcimem_ex;
extern struct extent *pcibus_ex;
void		pci_init_extents(void);

/*
 * Functions provided to machine-independent PCI code.
 */
void		pci_attach_hook(struct device *, struct device *,
		    struct pcibus_attach_args *);
int		pci_bus_maxdevs(pci_chipset_tag_t, int);
pcitag_t	pci_make_tag(pci_chipset_tag_t, int, int, int);
int		pci_conf_size(pci_chipset_tag_t, pcitag_t);
pcireg_t	pci_conf_read(pci_chipset_tag_t, pcitag_t, int);
void		pci_conf_write(pci_chipset_tag_t, pcitag_t, int,
		    pcireg_t);
struct pci_attach_args;
int		pci_intr_map_msi(struct pci_attach_args *, pci_intr_handle_t *);
int		pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
#define		pci_intr_line(c, ih)	((ih).line)
const char	*pci_intr_string(pci_chipset_tag_t, pci_intr_handle_t);
void		*pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t,
		    int, int (*)(void *), void *, const char *);
void		pci_intr_disestablish(pci_chipset_tag_t, void *);
void		pci_decompose_tag(pci_chipset_tag_t, pcitag_t,
		    int *, int *, int *);
#define	pci_probe_device_hook(c, a)	(0)

void 		pci_dev_postattach(struct device *, struct pci_attach_args *);

pcireg_t	pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
void		pci_set_powerstate_md(pci_chipset_tag_t, pcitag_t, int, int);

/*
 * Section 6.2.4, `Miscellaneous Functions' of the PIC Specification,
 * says that 255 means `unknown' or `no connection' to the interrupt
 * controller on a PC.
 */
#define	I386_PCI_INTERRUPT_LINE_NO_CONNECTION	0xff

/*
 * PCI address space is shared with ISA, so avoid legacy ISA I/O
 * registers.
 */
#define PCI_IO_START	0x400
#define PCI_IO_END	0xffff

/*
 * Avoid the DOS Compatibility Memory area.
 */
#define PCI_MEM_START	0x100000


code:
/*	$OpenBSD: pcib.c,v 1.24 2013/05/30 16:15:01 deraadt Exp $	*/
/*	$NetBSD: pcib.c,v 1.6 1997/06/06 23:29:16 thorpej Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <machine/bus.h>
#include <dev/isa/isavar.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>

#include <dev/pci/pcidevs.h>

#include "isa.h"
#include "pcibios.h"
#if NPCIBIOS > 0
#include <i386/pci/pcibiosvar.h>
#endif

int	pcibmatch(struct device *, void *, void *);
void	pcibattach(struct device *, struct device *, void *);
void	pcib_callback(struct device *);
int	pcib_print(void *, const char *);

struct cfattach pcib_ca = {
	sizeof(struct device), pcibmatch, pcibattach
};

struct cfdriver pcib_cd = {
	NULL, "pcib", DV_DULL
};

int
pcibmatch(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = aux;

	switch (PCI_VENDOR(pa->pa_id)) {
	case PCI_VENDOR_INTEL:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_INTEL_SIO:
		case PCI_PRODUCT_INTEL_82371MX:
		case PCI_PRODUCT_INTEL_82371AB_ISA:
		case PCI_PRODUCT_INTEL_82440MX_ISA:
			/* The above bridges mis-identify themselves */
			return (1);
		}
		break;
	case PCI_VENDOR_SIS:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_SIS_85C503:
			/* mis-identifies itself as a miscellaneous prehistoric */
			return (1);
		}
		break;
	case PCI_VENDOR_VIATECH:
		switch (PCI_PRODUCT(pa->pa_id)) {
		case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
			/* mis-identifies itself as a ISA bridge */
			return (0);
		}
		break;
	}

	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_ISA)
		return (1);

	return (0);
}

void
pcibattach(struct device *parent, struct device *self, void *aux)
{
	/*
	 * Cannot attach isa bus now; must postpone for various reasons
	 */
	printf("\n");

	config_defer(self, pcib_callback);
}

void
pcib_callback(struct device *self)
{
	struct isabus_attach_args iba;

#if NPCIBIOS > 0
	pci_intr_post_fixup();
#endif

	/*
	 * Attach the ISA bus behind this bridge.
	 */
	memset(&iba, 0, sizeof(iba));
	iba.iba_busname = "isa";
	iba.iba_iot = I386_BUS_SPACE_IO;
	iba.iba_memt = I386_BUS_SPACE_MEM;
#if NISADMA > 0
	iba.iba_dmat = &isa_bus_dma_tag;
#endif
	config_found(self, &iba, pcib_print);
}

int
pcib_print(void *aux, const char *pnp)
{
	/* Only ISAs can attach to pcib's; easy. */
	if (pnp)
		printf("isa at %s", pnp);
	return (UNCONF);
}


code:
/*	$OpenBSD: pcibios.c,v 1.46 2015/07/17 21:36:56 mlarkin Exp $	*/
/*	$NetBSD: pcibios.c,v 1.5 2000/08/01 05:23:59 uch Exp $	*/

/*
 * Copyright (c) 2000 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Interface to the PCI BIOS and PCI Interrupt Routing table.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <dev/isa/isareg.h>
#include <i386/isa/isa_machdep.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <i386/pci/pcibiosvar.h>

#include <machine/biosvar.h>

int pcibios_flags;
int pcibios_present;

struct pcibios_pir_header pcibios_pir_header;
struct pcibios_intr_routing *pcibios_pir_table;
int pcibios_pir_table_nentries;
int pcibios_flags = 0;

struct bios32_entry pcibios_entry;
struct bios32_entry_info pcibios_entry_info;

struct pcibios_intr_routing *pcibios_pir_init(struct pcibios_softc *);

int	pcibios_get_status(struct pcibios_softc *,
	    u_int32_t *, u_int32_t *, u_int32_t *,
	    u_int32_t *, u_int32_t *, u_int32_t *, u_int32_t *);
int	pcibios_get_intr_routing(struct pcibios_softc *,
	    struct pcibios_intr_routing *, int *, u_int16_t *);

int	pcibios_return_code(struct pcibios_softc *, u_int16_t, const char *);

void	pcibios_print_exclirq(struct pcibios_softc *);
void	pcibios_print_pir_table(void);

#define	PCI_IRQ_TABLE_START	0xf0000
#define	PCI_IRQ_TABLE_END	0xfffff

struct cfdriver pcibios_cd = {
	NULL, "pcibios", DV_DULL
};

int pcibiosprobe(struct device *, void *, void *);
void pcibiosattach(struct device *, struct device *, void *);

struct cfattach pcibios_ca = {
	sizeof(struct pcibios_softc), pcibiosprobe, pcibiosattach
};

int
pcibiosprobe(struct device *parent, void *match, void *aux)
{
	struct bios_attach_args *ba = aux;
	u_int32_t rev_maj, rev_min, mech1, mech2, scmech1, scmech2, maxbus;
	int rv;

	if (strcmp(ba->ba_name, "pcibios"))
		return 0;

	rv = bios32_service(PCIBIOS_SIGNATURE, &pcibios_entry,
		&pcibios_entry_info);

	PCIBIOS_PRINTV(("pcibiosprobe: 0x%hx:0x%x at 0x%x[0x%x]\n",
	    pcibios_entry.segment, pcibios_entry.offset,
	    pcibios_entry_info.bei_base, pcibios_entry_info.bei_size));

	return rv &&
	    pcibios_get_status(NULL, &rev_maj, &rev_min, &mech1, &mech2,
	        &scmech1, &scmech2, &maxbus) == PCIBIOS_SUCCESS;
}

void
pcibiosattach(struct device *parent, struct device *self, void *aux)
{
	struct pcibios_softc *sc = (struct pcibios_softc *)self;
	u_int32_t rev_maj, rev_min, mech1, mech2, scmech1, scmech2;

	pcibios_flags = sc->sc_dev.dv_cfdata->cf_flags;

	pcibios_get_status((struct pcibios_softc *)self, &rev_maj,
	    &rev_min, &mech1, &mech2,
	    &scmech1, &scmech2, &sc->max_bus);

	printf(": rev %d.%d @ 0x%x/0x%x\n",
	    rev_maj, rev_min >> 4, pcibios_entry_info.bei_base,
	    pcibios_entry_info.bei_size);

	PCIBIOS_PRINTV(("%s: config mechanism %s%s, special cycles %s%s, "
	    "last bus %d\n", sc->sc_dev.dv_xname,
	    mech1 ? "[1]" : "[x]", mech2 ? "[2]" : "[x]",
	    scmech1 ? "[1]" : "[x]", scmech2 ? "[2]" : "[x]", sc->max_bus));

	/*
	 * The PCI BIOS tells us the config mechanism; fill it in now
	 * so that pci_mode_detect() doesn't have to look for it.
	 */
	pci_mode = mech1 ? 1 : 2;

	pcibios_present = 1;

	/*
	 * Find the PCI IRQ Routing table.
	 */

	if (!(pcibios_flags & PCIBIOS_INTR_FIXUP) &&
	    pcibios_pir_init((struct pcibios_softc *)self) != NULL) {
		int rv;

		/*
		 * Fixup interrupt routing.
		 */
		rv = pci_intr_fixup(sc, NULL, I386_BUS_SPACE_IO);
		switch (rv) {
		case -1:
			/* Non-fatal error. */
			printf("%s: Warning, unable to fix up PCI interrupt "
			    "routing\n", sc->sc_dev.dv_xname);
			break;

		case 1:
			/* Fatal error. */
			printf("%s: interrupt fixup failed\n", sc->sc_dev.dv_xname);
			return;
		}

		/*
		 * XXX Clear `pciirq' from the ISA interrupt allocation
		 * XXX mask.
		 */
	}

	if (!(pcibios_flags & PCIBIOS_BUS_FIXUP)) {
		sc->max_bus = pci_bus_fixup(NULL, 0);
		printf("%s: PCI bus #%d is the last bus\n",
		    sc->sc_dev.dv_xname, sc->max_bus);
	}

	if (!(pcibios_flags & PCIBIOS_ADDR_FIXUP))
		pci_addr_fixup(sc, NULL, sc->max_bus);

	bios32_cleanup();
}

struct pcibios_intr_routing *
pcibios_pir_init(struct pcibios_softc *sc)
{
	paddr_t pa;

	pcibios_pir_table = NULL;
	for (pa = PCI_IRQ_TABLE_START; pa < PCI_IRQ_TABLE_END; pa += 16) {
		u_int8_t *p, cksum;
		struct pcibios_pir_header *pirh;
		int i;

		p = ISA_HOLE_VADDR(pa);
		pirh = (struct pcibios_pir_header *)p;
		/*
		 * Some laptops (such as the Toshiba Libretto L series)
		 * use _PIR instead of the standard $PIR for the signature
		 * so we check for that too.
		 */
		if (pirh->signature != BIOS32_MAKESIG('$', 'P', 'I', 'R') &&
		    pirh->signature != BIOS32_MAKESIG('_', 'P', 'I', 'R'))
			continue;

		if (pirh->tablesize < sizeof(*pirh))
			continue;

		cksum = 0;
		for (i = 0; i < pirh->tablesize; i++)
			cksum += p[i];

		printf("%s: PCI IRQ Routing Table rev %d.%d @ 0x%lx/%d "
		    "(%zd entries)\n", sc->sc_dev.dv_xname,
		    pirh->version >> 8, pirh->version & 0xff, pa,
		    pirh->tablesize, (pirh->tablesize - sizeof(*pirh)) / 16);

		if (cksum != 0) {
			printf("%s: bad IRQ table checksum\n",
			    sc->sc_dev.dv_xname);
			continue;
		}

		if (pirh->tablesize % 16 != 0) {
			printf("%s: bad IRQ table size\n", sc->sc_dev.dv_xname);
			continue;
		}

		if (pirh->version != 0x0100) {
			printf("%s: unsupported IRQ table version\n",
			    sc->sc_dev.dv_xname);
			continue;
		}

		/*
		 * We can handle this table!  Make a copy of it.
		 */
		pcibios_pir_header = *pirh;
		pcibios_pir_table =
		    malloc(pirh->tablesize - sizeof(*pirh), M_DEVBUF, M_NOWAIT);
		if (pcibios_pir_table == NULL) {
			printf("%s: no memory for $PIR\n", sc->sc_dev.dv_xname);
			return NULL;
		}
		bcopy(p + sizeof(*pirh), pcibios_pir_table,
		    pirh->tablesize - sizeof(*pirh));
		pcibios_pir_table_nentries =
		    (pirh->tablesize - sizeof(*pirh)) / 16;

	}

	/*
	 * If there was no PIR table found, try using the PCI BIOS
	 * Get Interrupt Routing call.
	 *
	 * XXX The interface to this call sucks; just allocate enough
	 * XXX room for 32 entries.
	 */
	if (pcibios_pir_table == NULL) {

		pcibios_pir_table_nentries = 32;
		pcibios_pir_table = malloc(pcibios_pir_table_nentries *
		    sizeof(*pcibios_pir_table), M_DEVBUF, M_NOWAIT);
		if (pcibios_pir_table == NULL) {
			printf("%s: no memory for $PIR\n", sc->sc_dev.dv_xname);
			return NULL;
		}
		if (pcibios_get_intr_routing(sc, pcibios_pir_table,
		    &pcibios_pir_table_nentries,
		    &pcibios_pir_header.exclusive_irq) != PCIBIOS_SUCCESS) {
			printf("%s: PCI IRQ Routing information unavailable.\n",
			    sc->sc_dev.dv_xname);
			free(pcibios_pir_table, M_DEVBUF, 0);
			pcibios_pir_table = NULL;
			pcibios_pir_table_nentries = 0;
			return NULL;
		}
		printf("%s: PCI BIOS has %d Interrupt Routing table entries\n",
		    sc->sc_dev.dv_xname, pcibios_pir_table_nentries);
	}

	pcibios_print_exclirq(sc);
	if (pcibios_flags & PCIBIOS_INTRDEBUG)
		pcibios_print_pir_table();
	return pcibios_pir_table;
}

int
pcibios_get_status(struct pcibios_softc *sc, u_int32_t *rev_maj,
    u_int32_t *rev_min, u_int32_t *mech1, u_int32_t *mech2, u_int32_t *scmech1,
    u_int32_t *scmech2, u_int32_t *maxbus)
{
	u_int32_t ax, bx, cx, edx;
	int rv;

	__asm volatile("pushl	%%es\n\t"
			 "pushl	%%ds\n\t"
			 "movw	4(%%edi), %%cx\n\t"
			 "movl	%%ecx, %%ds\n\t"
			 "lcall	*%%cs:(%%edi)\n\t"
			 "pop	%%ds\n\t"
			 "pop	%%es\n\t"
			 "jc	1f\n\t"
			 "xor	%%ah, %%ah\n"
		    "1:"
		: "=a" (ax), "=b" (bx), "=c" (cx), "=d" (edx)
		: "0" (0xb101), "D" (&pcibios_entry)
		: "cc", "memory");

	rv = pcibios_return_code(sc, ax, "pcibios_get_status");
	if (rv != PCIBIOS_SUCCESS)
		return (rv);

	if (edx != BIOS32_MAKESIG('P', 'C', 'I', ' '))
		return (PCIBIOS_SERVICE_NOT_PRESENT);	/* XXX */

	/*
	 * Fill in the various pieces of info we're looking for.
	 */
	*mech1 = ax & 1;
	*mech2 = ax & (1 << 1);
	*scmech1 = ax & (1 << 4);
	*scmech2 = ax & (1 << 5);
	*rev_maj = (bx >> 8) & 0xff;
	*rev_min = bx & 0xff;
	*maxbus = cx & 0xff;

	return (PCIBIOS_SUCCESS);
}

int
pcibios_get_intr_routing(struct pcibios_softc *sc,
    struct pcibios_intr_routing *table, int *nentries, u_int16_t *exclirq)
{
	u_int32_t ax, bx;
	int rv;
	struct {
		u_int16_t size;
		u_int32_t offset;
		u_int16_t segment;
	} __packed args;

	args.size = *nentries * sizeof(*table);
	args.offset = (u_int32_t)table;
	args.segment = GSEL(GDATA_SEL, SEL_KPL);

	memset(table, 0, args.size);

	__asm volatile("pushl	%%es\n\t"
			 "pushl	%%ds\n\t"
			 "movw	4(%%esi), %%cx\n\t"
			 "movl	%%ecx, %%ds\n\t"
			 "lcall	*%%cs:(%%esi)\n\t"
			 "popl	%%ds\n\t"
			 "popl	%%es\n\t"
			 "jc	1f\n\t"
			 "xor	%%ah, %%ah\n"
		    "1:\n"
		: "=a" (ax), "=b" (bx)
		: "0" (0xb10e), "1" (0), "D" (&args), "S" (&pcibios_entry)
		: "%ecx", "%edx", "cc", "memory");

	rv = pcibios_return_code(sc, ax, "pcibios_get_intr_routing");
	if (rv != PCIBIOS_SUCCESS)
		return (rv);

	*nentries = args.size / sizeof(*table);
	*exclirq |= bx;

	return (PCIBIOS_SUCCESS);
}

int
pcibios_return_code(struct pcibios_softc *sc, u_int16_t ax, const char *func)
{
	const char *errstr;
	int rv = ax >> 8;
	char *nam;

	if (sc)
		nam = sc->sc_dev.dv_xname;
	else
		nam = "pcibios0";

	switch (rv) {
	case PCIBIOS_SUCCESS:
		return (PCIBIOS_SUCCESS);

	case PCIBIOS_SERVICE_NOT_PRESENT:
		errstr = "service not present";
		break;

	case PCIBIOS_FUNCTION_NOT_SUPPORTED:
		errstr = "function not supported";
		break;

	case PCIBIOS_BAD_VENDOR_ID:
		errstr = "bad vendor ID";
		break;

	case PCIBIOS_DEVICE_NOT_FOUND:
		errstr = "device not found";
		break;

	case PCIBIOS_BAD_REGISTER_NUMBER:
		errstr = "bad register number";
		break;

	case PCIBIOS_SET_FAILED:
		errstr = "set failed";
		break;

	case PCIBIOS_BUFFER_TOO_SMALL:
		errstr = "buffer too small";
		break;

	default:
		printf("%s: %s - unknown return code 0x%x\n",
		    nam, func, rv);
		return (rv);
	}

	printf("%s: %s - %s\n", nam, func, errstr);
	return (rv);
}

void
pcibios_print_exclirq(struct pcibios_softc *sc)
{
	int i;

	if (pcibios_pir_header.exclusive_irq) {
		printf("%s: PCI Exclusive IRQs:", sc->sc_dev.dv_xname);
		for (i = 0; i < 16; i++) {
			if (pcibios_pir_header.exclusive_irq & (1 << i))
				printf(" %d", i);
		}
		printf("\n");
	}
}

void
pcibios_print_pir_table(void)
{
	int i, j;

	for (i = 0; i < pcibios_pir_table_nentries; i++) {
		printf("PIR Entry %d:\n", i);
		printf("\tBus: %d  Device: %d\n",
		    pcibios_pir_table[i].bus,
		    PIR_DEVFUNC_DEVICE(pcibios_pir_table[i].device));
		for (j = 0; j < 4; j++) {
			printf("\t\tINT%c: link 0x%02x bitmap 0x%04x\n",
			    'A' + j,
			    pcibios_pir_table[i].linkmap[j].link,
			    pcibios_pir_table[i].linkmap[j].bitmap);
		}
	}
}

void
pci_device_foreach(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus,
    void (*func)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t))
{
	const struct pci_quirkdata *qd;
	int bus, device, function, maxdevs, nfuncs;
	pcireg_t id, bhlcr;
	pcitag_t tag;

	for (bus = 0; bus <= maxbus; bus++) {
		maxdevs = pci_bus_maxdevs(pc, bus);
		for (device = 0; device < maxdevs; device++) {
			tag = pci_make_tag(pc, bus, device, 0);
			id = pci_conf_read(pc, tag, PCI_ID_REG);

			/* Invalid vendor ID value? */
			if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
				continue;
			/* XXX Not invalid, but we've done this ~forever. */
			if (PCI_VENDOR(id) == 0)
				continue;

			qd = pci_lookup_quirkdata(PCI_VENDOR(id),
			    PCI_PRODUCT(id));

			bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
			if (PCI_HDRTYPE_MULTIFN(bhlcr) ||
			    (qd != NULL &&
			     (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
				nfuncs = 8;
			else
				nfuncs = 1;

			for (function = 0; function < nfuncs; function++) {
				tag = pci_make_tag(pc, bus, device, function);
				id = pci_conf_read(pc, tag, PCI_ID_REG);

				/* Invalid vendor ID value? */
				if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
					continue;
				/*
				 * XXX Not invalid, but we've done this
				 * ~forever.
				 */
				if (PCI_VENDOR(id) == 0)
					continue;
				(*func)(sc, pc, tag);
			}
		}
	}
}


code:
/*	$OpenBSD: pcibiosvar.h,v 1.18 2010/07/02 16:11:19 jsg Exp $	*/
/*	$NetBSD: pcibios.h,v 1.2 2000/04/28 17:15:16 uch Exp $	*/

/*
 * Copyright (c) 1999, by UCHIYAMA Yasushi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the developer may NOT be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Data structure definitions for the PCI BIOS interface.
 */

#define	PCIBIOS_ADDR_FIXUP	0x001
#define	PCIBIOS_BUS_FIXUP	0x002
#define	PCIBIOS_INTR_FIXUP	0x004
#define	PCIBIOS_INTR_GUESS	0x008
#define	PCIBIOS_VERBOSE		0x010
#define	PCIBIOS_INTRDEBUG	0x020
#define	PCIBIOS_FIXUP_FORCE	0x040

/*
 * PCI BIOS return codes.
 */
#define	PCIBIOS_SUCCESS			0x00
#define	PCIBIOS_SERVICE_NOT_PRESENT	0x80
#define	PCIBIOS_FUNCTION_NOT_SUPPORTED	0x81
#define	PCIBIOS_BAD_VENDOR_ID		0x83
#define	PCIBIOS_DEVICE_NOT_FOUND	0x86
#define	PCIBIOS_BAD_REGISTER_NUMBER	0x87
#define	PCIBIOS_SET_FAILED		0x88
#define	PCIBIOS_BUFFER_TOO_SMALL	0x89

struct pcibios_softc {
	struct device sc_dev;

	int max_bus;

	/* address fixup guts */
	struct extent *extent_mem;
	struct extent *extent_port;
	bus_addr_t mem_alloc_start;
	bus_addr_t port_alloc_start;
	int nbogus;
};

/*
 * PCI IRQ Routing Table definitions.
 */

/*
 * Slot entry (per PCI 2.1)
 */
struct pcibios_linkmap {
	u_int8_t	link;
	u_int16_t	bitmap;
} __packed;

struct pcibios_intr_routing {
	u_int8_t	bus;
	u_int8_t	device;
	struct pcibios_linkmap linkmap[4];	/* INT[A:D]# */
	u_int8_t	slot;
	u_int8_t	reserved;
} __packed;

/*
 * $PIR header.  Reference:
 *
 *	http://www.microsoft.com/whdc/archive/pciirq.mspx
 */
struct pcibios_pir_header {
	u_int32_t	signature;		/* $PIR */
	u_int16_t	version;
	u_int16_t	tablesize;
	u_int8_t	router_bus;
	u_int8_t	router_devfunc;
	u_int16_t	exclusive_irq;
	u_int32_t	compat_router;		/* PCI vendor/product */
	u_int32_t	miniport;
	u_int8_t	reserved[11];
	u_int8_t	checksum;
} __packed;

#define	PIR_DEVFUNC_DEVICE(devfunc)	(((devfunc) >> 3) & 0x1f)
#define	PIR_DEVFUNC_FUNCTION(devfunc)	((devfunc) & 7)
#define	PIR_DEVFUNC_COMPOSE(dev,func)	((((dev) &0x1f) << 3) | ((func) & 7))

void	pcibios_init(void);

extern struct pcibios_pir_header pcibios_pir_header;
extern struct pcibios_intr_routing *pcibios_pir_table;
extern int pcibios_pir_table_nentries;

extern int pcibios_flags;

typedef void *pciintr_icu_handle_t;

struct pciintr_icu {
	int	(*pi_getclink)(pciintr_icu_handle_t, int, int *);
	int	(*pi_get_intr)(pciintr_icu_handle_t, int, int *);
	int	(*pi_set_intr)(pciintr_icu_handle_t, int, int);
	int	(*pi_get_trigger)(pciintr_icu_handle_t, int, int *);
	int	(*pi_set_trigger)(pciintr_icu_handle_t, int, int);
};

typedef const struct pciintr_icu *pciintr_icu_tag_t;

#define	pciintr_icu_getclink(t, h, link, pirqp)				\
	(*(t)->pi_getclink)((h), (link), (pirqp))
#define	pciintr_icu_get_intr(t, h, pirq, irqp)				\
	(*(t)->pi_get_intr)((h), (pirq), (irqp))
#define	pciintr_icu_set_intr(t, h, pirq, irq)				\
	(*(t)->pi_set_intr)((h), (pirq), (irq))
#define	pciintr_icu_get_trigger(t, h, irq, triggerp)			\
	(*(t)->pi_get_trigger)((h), (irq), (triggerp))
#define	pciintr_icu_set_trigger(t, h, irq, trigger)			\
	(*(t)->pi_set_trigger)((h), (irq), (trigger))

#define	PCIBIOS_PRINTV(arg) \
	do { \
		if (pcibios_flags & PCIBIOS_VERBOSE) \
			printf arg; \
	} while (0)

#define	PCIADDR_SEARCH_IO	0
#define	PCIADDR_SEARCH_MEM	1

#define	PCI_INT_VIA_ISA		0x20000000 /* XXX see APIC_INT_VIA_APIC */

int  pci_intr_fixup(struct pcibios_softc *, pci_chipset_tag_t, bus_space_tag_t);
int  pci_bus_fixup(pci_chipset_tag_t, int);
void pci_addr_fixup(struct pcibios_softc *, pci_chipset_tag_t, int);
void pci_device_foreach(struct pcibios_softc *, pci_chipset_tag_t, int,
    void (*)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t));
int  pci_intr_header_fixup(pci_chipset_tag_t, pcitag_t, pci_intr_handle_t *);
int  pci_intr_route_link(pci_chipset_tag_t, pci_intr_handle_t *);
int  pci_intr_post_fixup(void);

/*
 * Init functions for our known PCI ICUs.
 */
int	piix_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	opti82c558_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	opti82c700_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	via82c586_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	via8231_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	sis85c503_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	amd756_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	ali1543_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);
int	osb4_init(pci_chipset_tag_t, bus_space_tag_t, pcitag_t,
	    pciintr_icu_tag_t *, pciintr_icu_handle_t *);


code:
/*	$OpenBSD: pcic_pci_machdep.c,v 1.5 2008/06/26 05:42:11 ray Exp $ */
/*	$NetBSD: pcic_pci_machdep.c,v 1.1 1998/12/20 17:53:29 nathanw Exp $	*/

/*
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <dev/pcmcia/pcmciachip.h>
#include <dev/ic/i82365reg.h>
#include <dev/ic/i82365var.h>

#include <dev/isa/isavar.h>
#include <dev/isa/i82365_isavar.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/i82365_pcivar.h>

void *
pcic_pci_machdep_intr_est(pci_chipset_tag_t pc)
{
	return NULL;
}

void *
pcic_pci_machdep_pcic_intr_establish(struct pcic_softc *sc, int (*fct)(void *))
{
	if (isa_intr_alloc(NULL, PCIC_CSC_INTR_IRQ_VALIDMASK & 0xffff,
			   IST_EDGE, &(sc->irq)))
		return (NULL);
	printf("%s: interrupting at irq %d\n", sc->dev.dv_xname, sc->irq);
	return (isa_intr_establish(NULL, sc->irq, IST_EDGE, IPL_TTY,
				   fct, sc, sc->dev.dv_xname));
}

void *
pcic_pci_machdep_chip_intr_establish(pcmcia_chipset_handle_t pch,
    struct pcmcia_function *pf, int ipl, int (*fct)(void *), void *arg,
    char *xname)
{
	return (pcic_isa_chip_intr_establish(pch, pf, ipl, fct, arg, xname));
}

void
pcic_pci_machdep_chip_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
{
	pcic_isa_chip_intr_disestablish(pch, ih);
}


code:
/*	$OpenBSD: pciide_gcsc_reg.h,v 1.2 2013/11/26 20:33:13 deraadt Exp $	*/
/*	$NetBSD: gcscide.c,v 1.6 2007/10/06 07:21:03 xtraeme Exp $	*/

/*-
 * Copyright (c) 2007 Juan Romero Pardines.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * 6.4 - ATA-5 Controller Register Definitions.
 */
#define GCSC_MSR_ATAC_BASE 		0x51300000
#define GCSC_ATAC_GLD_MSR_CAP 		(GCSC_MSR_ATAC_BASE + 0)
#define GCSC_ATAC_GLD_MSR_CONFIG 	(GCSC_MSR_ATAC_BASE + 0x01)
#define GCSC_ATAC_GLD_MSR_SMI 		(GCSC_MSR_ATAC_BASE + 0x02)
#define GCSC_ATAC_GLD_MSR_ERROR 	(GCSC_MSR_ATAC_BASE + 0x03)
#define GCSC_ATAC_GLD_MSR_PM 		(GCSC_MSR_ATAC_BASE + 0x04)
#define GCSC_ATAC_GLD_MSR_DIAG 		(GCSC_MSR_ATAC_BASE + 0x05)
#define GCSC_ATAC_IO_BAR 		(GCSC_MSR_ATAC_BASE + 0x08)
#define GCSC_ATAC_RESET 		(GCSC_MSR_ATAC_BASE + 0x10)
#define GCSC_ATAC_CH0D0_PIO 		(GCSC_MSR_ATAC_BASE + 0x20)
#define GCSC_ATAC_CH0D0_DMA 		(GCSC_MSR_ATAC_BASE + 0x21)
#define GCSC_ATAC_CH0D1_PIO 		(GCSC_MSR_ATAC_BASE + 0x22)
#define GCSC_ATAC_CH0D1_DMA 		(GCSC_MSR_ATAC_BASE + 0x23)
#define GCSC_ATAC_PCI_ABRTERR 		(GCSC_MSR_ATAC_BASE + 0x24)
#define GCSC_ATAC_BM0_CMD_PRIM 		0x00
#define GCSC_ATAC_BM0_STS_PRIM 		0x02
#define GCSC_ATAC_BM0_PRD 		0x04

/*
 * ATAC_CH0D0_DMA registers:
 *
 * PIO Format (bit 31): Format 1 allows independent control of command
 * and data per drive, while Format 0 selects the slowest speed
 * of the two drives.
 */
#define GCSC_ATAC_PIO_FORMAT		(1U << 31) /* PIO Mode Format 1 */
/*
 * DMA_SEL (bit 20): sets Ultra DMA mode (if enabled) or Multi-word
 * DMA mode (if disabled).
 */
#define GCSC_ATAC_DMA_SEL		(1 << 20)

/* PIO Format 1 settings */
static const uint32_t gcsc_pio_timings[] = {
	0xf7f4f7f4,	/* PIO Mode 0 */
	0x53f3f173,	/* PIO Mode 1 */
	0x13f18141,	/* PIO Mode 2 */
	0x51315131,	/* PIO Mode 3 */
	0x11311131	/* PIO Mode 4 */
};

static const uint32_t gcsc_mdma_timings[] = {
	0x7f0ffff3,	/* MDMA Mode 0 */
	0x7f035352,	/* MDMA Mode 1 */
	0x7f024241 	/* MDMA Mode 2 */
};

static const uint32_t gcsc_udma_timings[] = {
	0x7f7436a1,	/* Ultra DMA Mode 0 */
	0x7f733481,	/* Ultra DMA Mode 1 */
	0x7f723261,	/* Ultra DMA Mode 2 */
	0x7f713161,	/* Ultra DMA Mode 3 */
	0x7f703061	/* Ultra DMA Mode 4 */
};