Esempio n. 1
0
static ACPI_STATUS
acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
    ACPI_BUFFER *srsbuf)
{
	ACPI_RESOURCE newres;
	ACPI_STATUS status;
	struct link *link;
	int i;

	/* Start off with an empty buffer. */
	srsbuf->Pointer = NULL;
	link = sc->pl_links;
	for (i = 0; i < sc->pl_num_links; i++) {

		/* Add a new IRQ resource from each link. */
		link = &sc->pl_links[i];
		if (link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ) {

			/* Build an IRQ resource. */
			bcopy(&link->l_prs_template, &newres,
			    ACPI_RS_SIZE(newres.Data.Irq));
			newres.Data.Irq.InterruptCount = 1;
			if (PCI_INTERRUPT_VALID(link->l_irq)) {
				KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
		("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
				    __func__, link->l_irq));
				newres.Data.Irq.Interrupts[0] = link->l_irq;
			} else
				newres.Data.Irq.Interrupts[0] = 0;
		} else {

			/* Build an ExtIRQ resuorce. */
			bcopy(&link->l_prs_template, &newres,
			    ACPI_RS_SIZE(newres.Data.ExtendedIrq));
			newres.Data.ExtendedIrq.InterruptCount = 1;
			if (PCI_INTERRUPT_VALID(link->l_irq))
				newres.Data.ExtendedIrq.Interrupts[0] =
				    link->l_irq;
			else
				newres.Data.ExtendedIrq.Interrupts[0] = 0;
		}

		/* Add the new resource to the end of the _SRS buffer. */
		status = acpi_AppendBufferResource(srsbuf, &newres);
		if (ACPI_FAILURE(status)) {
			device_printf(sc->pl_dev,
			    "Unable to build resources: %s\n",
			    AcpiFormatException(status));
			if (srsbuf->Pointer != NULL)
				AcpiOsFree(srsbuf->Pointer);
			return (status);
		}
	}
	return (AE_OK);
}
Esempio n. 2
0
static int
link_valid_irq(struct link *link, int irq)
{
	int i;

	ACPI_SERIAL_ASSERT(pci_link);

	/* Invalid interrupts are never valid. */
	if (!PCI_INTERRUPT_VALID(irq))
		return (FALSE);

	/* Any interrupt in the list of possible interrupts is valid. */
	for (i = 0; i < link->l_num_irqs; i++)
		if (link->l_irqs[i] == irq)
			 return (TRUE);

	/*
	 * For links routed via an ISA interrupt, if the SCI is routed via
	 * an ISA interrupt, the SCI is always treated as a valid IRQ.
	 */
	if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
	    irq < NUM_ISA_INTERRUPTS)
		return (TRUE);

	/* If the interrupt wasn't found in the list it is not valid. */
	return (FALSE);
}
Esempio n. 3
0
/*
 * Route an interrupt across a PCI bridge.
 */
static int
pcib_route_interrupt(device_t pcib, device_t dev, int pin)
{
    device_t	bus;
    int		parent_intpin;
    int		intnum;

    /*	
     *
     * The PCI standard defines a swizzle of the child-side device/intpin to
     * the parent-side intpin as follows.
     *
     * device = device on child bus
     * child_intpin = intpin on child bus slot (0-3)
     * parent_intpin = intpin on parent bus slot (0-3)
     *
     * parent_intpin = (device + child_intpin) % 4
     */
    parent_intpin = (pci_get_slot(pcib) + (pin - 1)) % 4;

    /*
     * Our parent is a PCI bus.  Its parent must export the pcib interface
     * which includes the ability to route interrupts.
     */
    bus = device_get_parent(pcib);
    intnum = PCIB_ROUTE_INTERRUPT(device_get_parent(bus), pcib, parent_intpin + 1);
    if (PCI_INTERRUPT_VALID(intnum)) {
	device_printf(pcib, "slot %d INT%c is routed to irq %d\n",
	    pci_get_slot(dev), 'A' + pin - 1, intnum);
    }
    return(intnum);
}
Esempio n. 4
0
int
acpi_pci_link_route_interrupt(device_t dev, int index)
{
	struct link *link;

	if (acpi_disabled("pci_link"))
		return (PCI_INVALID_IRQ);

	ACPI_SERIAL_BEGIN(pci_link);
	link = acpi_pci_link_lookup(dev, index);
	if (link == NULL)
		panic("%s: apparently invalid index %d", __func__, index);

	/*
	 * If this link device is already routed to an interrupt, just return
	 * the interrupt it is routed to.
	 */
	if (link->l_routed) {
		KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
		    ("%s: link is routed but has an invalid IRQ", __func__));
		ACPI_SERIAL_END(pci_link);
		return (link->l_irq);
	}

	/* Choose an IRQ if we need one. */
	if (!PCI_INTERRUPT_VALID(link->l_irq)) {
		link->l_irq = acpi_pci_link_choose_irq(dev, link);

		/*
		 * Try to route the interrupt we picked.  If it fails, then
		 * assume the interrupt is not routed.
		 */
		if (PCI_INTERRUPT_VALID(link->l_irq)) {
			acpi_pci_link_route_irqs(dev);
			if (!link->l_routed)
				link->l_irq = PCI_INVALID_IRQ;
		}
	}
	ACPI_SERIAL_END(pci_link);

	return (link->l_irq);
}
Esempio n. 5
0
/*
 * Pick an IRQ to use for this unrouted link.
 */
static uint8_t
acpi_pci_link_choose_irq(device_t dev, struct link *link)
{
	char tunable_buffer[64], link_name[5];
	u_int8_t best_irq, pos_irq;
	int best_weight, pos_weight, i;

	KASSERT(!link->l_routed, ("%s: link already routed", __func__));
	KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
	    ("%s: link already has an IRQ", __func__));

	/* Check for a tunable override. */
	if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
	    sizeof(link_name)))) {
		snprintf(tunable_buffer, sizeof(tunable_buffer),
		    "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
		if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
			if (!link_valid_irq(link, i))
				device_printf(dev,
				    "Warning, IRQ %d is not listed as valid\n",
				    i);
			return (i);
		}
		snprintf(tunable_buffer, sizeof(tunable_buffer),
		    "hw.pci.link.%s.irq", link_name);
		if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
			if (!link_valid_irq(link, i))
				device_printf(dev,
				    "Warning, IRQ %d is not listed as valid\n",
				    i);
			return (i);
		}
	}

	/*
	 * If we have a valid BIOS IRQ, use that.  We trust what the BIOS
	 * says it routed over what _CRS says the link thinks is routed.
	 */
	if (PCI_INTERRUPT_VALID(link->l_bios_irq))
		return (link->l_bios_irq);

	/*
	 * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
	 * then use that.
	 */
	if (PCI_INTERRUPT_VALID(link->l_initial_irq))
		return (link->l_initial_irq);

	/*
	 * Ok, we have no useful hints, so we have to pick from the
	 * possible IRQs.  For ISA IRQs we only use interrupts that
	 * have already been used by the BIOS.
	 */
	best_irq = PCI_INVALID_IRQ;
	best_weight = INT_MAX;
	for (i = 0; i < link->l_num_irqs; i++) {
		pos_irq = link->l_irqs[i];
		if (pos_irq < NUM_ISA_INTERRUPTS &&
		    (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
			continue;
		pos_weight = pci_link_interrupt_weights[pos_irq];
		if (pos_weight < best_weight) {
			best_weight = pos_weight;
			best_irq = pos_irq;
		}
	}

	/*
	 * If this is an ISA IRQ, try using the SCI if it is also an ISA
	 * interrupt as a fallback.
	 */
	if (link->l_isa_irq) {
		pos_irq = AcpiGbl_FADT.SciInterrupt;
		pos_weight = pci_link_interrupt_weights[pos_irq];
		if (pos_weight < best_weight) {
			best_weight = pos_weight;
			best_irq = pos_irq;
		}
	}

	if (PCI_INTERRUPT_VALID(best_irq)) {
		if (bootverbose)
			device_printf(dev, "Picked IRQ %u with weight %d\n",
			    best_irq, best_weight);
	} else
		device_printf(dev, "Unable to choose an IRQ\n");
	return (best_irq);
}
Esempio n. 6
0
static ACPI_STATUS
acpi_pci_link_route_irqs(device_t dev)
{
	struct acpi_pci_link_softc *sc;
	ACPI_RESOURCE *resource, *end;
	ACPI_BUFFER srsbuf;
	ACPI_STATUS status;
	struct link *link;
	int i;

	ACPI_SERIAL_ASSERT(pci_link);
	sc = device_get_softc(dev);
	if (sc->pl_crs_bad)
		status = acpi_pci_link_srs_from_links(sc, &srsbuf);
	else
		status = acpi_pci_link_srs_from_crs(sc, &srsbuf);

	/* Write out new resources via _SRS. */
	status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
	if (ACPI_FAILURE(status)) {
		device_printf(dev, "Unable to route IRQs: %s\n",
		    AcpiFormatException(status));
		AcpiOsFree(srsbuf.Pointer);
		return (status);
	}

	/*
	 * Perform acpi_config_intr() on each IRQ resource if it was just
	 * routed for the first time.
	 */
	link = sc->pl_links;
	i = 0;
	resource = (ACPI_RESOURCE *)srsbuf.Pointer;
	end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
	for (;;) {
		if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
			break;
		switch (resource->Type) {
		case ACPI_RESOURCE_TYPE_IRQ:
		case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
			MPASS(i < sc->pl_num_links);

			/*
			 * Only configure the interrupt and update the
			 * weights if this link has a valid IRQ and was
			 * previously unrouted.
			 */
			if (!link->l_routed &&
			    PCI_INTERRUPT_VALID(link->l_irq)) {
				link->l_routed = TRUE;
				acpi_config_intr(dev, resource);
				pci_link_interrupt_weights[link->l_irq] +=
				    link->l_references;
			}
			link++;
			i++;
			break;
		}
		resource = ACPI_NEXT_RESOURCE(resource);
		if (resource >= end)
			break;
	}
	AcpiOsFree(srsbuf.Pointer);
	return (AE_OK);
}
Esempio n. 7
0
static ACPI_STATUS
acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
{
	ACPI_RESOURCE *end, *res;
	ACPI_STATUS status;
	struct link *link;
	int i, in_dpf;

	/* Fetch the _CRS. */
	ACPI_SERIAL_ASSERT(pci_link);
	srsbuf->Pointer = NULL;
	srsbuf->Length = ACPI_ALLOCATE_BUFFER;
	status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), srsbuf);
	if (ACPI_SUCCESS(status) && srsbuf->Pointer == NULL)
		status = AE_NO_MEMORY;
	if (ACPI_FAILURE(status)) {
		if (bootverbose)
			device_printf(sc->pl_dev,
			    "Unable to fetch current resources: %s\n",
			    AcpiFormatException(status));
		return (status);
	}

	/* Fill in IRQ resources via link structures. */
	link = sc->pl_links;
	i = 0;
	in_dpf = DPF_OUTSIDE;
	res = (ACPI_RESOURCE *)srsbuf->Pointer;
	end = (ACPI_RESOURCE *)((char *)srsbuf->Pointer + srsbuf->Length);
	for (;;) {
		switch (res->Type) {
		case ACPI_RESOURCE_TYPE_START_DEPENDENT:
			switch (in_dpf) {
			case DPF_OUTSIDE:
				/* We've started the first DPF. */
				in_dpf = DPF_FIRST;
				break;
			case DPF_FIRST:
				/* We've started the second DPF. */
				panic(
		"%s: Multiple dependent functions within a current resource",
				    __func__);
				break;
			}
			break;
		case ACPI_RESOURCE_TYPE_END_DEPENDENT:
			/* We are finished with DPF parsing. */
			KASSERT(in_dpf != DPF_OUTSIDE,
			    ("%s: end dpf when not parsing a dpf", __func__));
			in_dpf = DPF_OUTSIDE;
			break;
		case ACPI_RESOURCE_TYPE_IRQ:
			MPASS(i < sc->pl_num_links);
			res->Data.Irq.InterruptCount = 1;
			if (PCI_INTERRUPT_VALID(link->l_irq)) {
				KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
		("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
				    __func__, link->l_irq));
				res->Data.Irq.Interrupts[0] = link->l_irq;
			} else
				res->Data.Irq.Interrupts[0] = 0;
			link++;
			i++;
			break;
		case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
			MPASS(i < sc->pl_num_links);
			res->Data.ExtendedIrq.InterruptCount = 1;
			if (PCI_INTERRUPT_VALID(link->l_irq))
				res->Data.ExtendedIrq.Interrupts[0] =
				    link->l_irq;
			else
				res->Data.ExtendedIrq.Interrupts[0] = 0;
			link++;
			i++;
			break;
		}
		if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
			break;
		res = ACPI_NEXT_RESOURCE(res);
		if (res >= end)
			break;
	}
	return (AE_OK);
}
Esempio n. 8
0
void
acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
    int pin)
{
	struct link *link;
	uint8_t bios_irq;
	uintptr_t bus;

	/*
	 * Look up the PCI bus for the specified PCI bridge device.  Note
	 * that the PCI bridge device might not have any children yet.
	 * However, looking up its bus number doesn't require a valid child
	 * device, so we just pass NULL.
	 */
	if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
		device_printf(pcib, "Unable to read PCI bus number");
		panic("PCI bridge without a bus number");
	}
		
	/* Bump the reference count. */
	ACPI_SERIAL_BEGIN(pci_link);
	link = acpi_pci_link_lookup(dev, index);
	if (link == NULL) {
		device_printf(dev, "apparently invalid index %d\n", index);
		ACPI_SERIAL_END(pci_link);
		return;
	}
	link->l_references++;
	if (link->l_routed)
		pci_link_interrupt_weights[link->l_irq]++;

	/*
	 * The BIOS only routes interrupts via ISA IRQs using the ATPICs
	 * (8259As).  Thus, if this link is routed via an ISA IRQ, go
	 * look to see if the BIOS routed an IRQ for this link at the
	 * indicated (bus, slot, pin).  If so, we prefer that IRQ for
	 * this link and add that IRQ to our list of known-good IRQs.
	 * This provides a good work-around for link devices whose _CRS
	 * method is either broken or bogus.  We only use the value
	 * returned by _CRS if we can't find a valid IRQ via this method
	 * in fact.
	 *
	 * If this link is not routed via an ISA IRQ (because we are using
	 * APIC for example), then don't bother looking up the BIOS IRQ
	 * as if we find one it won't be valid anyway.
	 */
	if (!link->l_isa_irq) {
		ACPI_SERIAL_END(pci_link);
		return;
	}

	/* Try to find a BIOS IRQ setting from any matching devices. */
	bios_irq = acpi_pci_link_search_irq(bus, slot, pin);
	if (!PCI_INTERRUPT_VALID(bios_irq)) {
		ACPI_SERIAL_END(pci_link);
		return;
	}

	/* Validate the BIOS IRQ. */
	if (!link_valid_irq(link, bios_irq)) {
		device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
		    bios_irq, (int)bus, slot, pin + 'A');
	} else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
		link->l_bios_irq = bios_irq;
		if (bios_irq < NUM_ISA_INTERRUPTS)
			pci_link_bios_isa_irqs |= (1 << bios_irq);
		if (bios_irq != link->l_initial_irq &&
		    PCI_INTERRUPT_VALID(link->l_initial_irq))
			device_printf(dev,
			    "BIOS IRQ %u does not match initial IRQ %u\n",
			    bios_irq, link->l_initial_irq);
	} else if (bios_irq != link->l_bios_irq)
		device_printf(dev,
	    "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
		    bios_irq, (int)bus, slot, pin + 'A',
		    link->l_bios_irq);
	ACPI_SERIAL_END(pci_link);
}
Esempio n. 9
0
static int
acpi_pci_link_attach(device_t dev)
{
	struct acpi_pci_link_softc *sc;
	struct link_count_request creq;
	struct link_res_request rreq;
	ACPI_STATUS status;
	int i;

	sc = device_get_softc(dev);
	sc->pl_dev = dev;
	ACPI_SERIAL_BEGIN(pci_link);

	/*
	 * Count the number of current resources so we know how big of
	 * a link array to allocate.  On some systems, _CRS is broken,
	 * so for those systems try to derive the count from _PRS instead.
	 */
	creq.in_dpf = DPF_OUTSIDE;
	creq.count = 0;
	status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
	    acpi_count_irq_resources, &creq);
	sc->pl_crs_bad = ACPI_FAILURE(status);
	if (sc->pl_crs_bad) {
		creq.in_dpf = DPF_OUTSIDE;
		creq.count = 0;
		status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
		    acpi_count_irq_resources, &creq);
		if (ACPI_FAILURE(status)) {
			device_printf(dev,
			    "Unable to parse _CRS or _PRS: %s\n",
			    AcpiFormatException(status));
			ACPI_SERIAL_END(pci_link);
			return (ENXIO);
		}
	}
	sc->pl_num_links = creq.count;
	if (creq.count == 0) {
		ACPI_SERIAL_END(pci_link);
		return (0);
	}
	sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
	    M_PCI_LINK, M_WAITOK | M_ZERO);

	/* Initialize the child links. */
	for (i = 0; i < sc->pl_num_links; i++) {
		sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
		sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
		sc->pl_links[i].l_sc = sc;
		sc->pl_links[i].l_isa_irq = FALSE;
		sc->pl_links[i].l_res_index = -1;
	}

	/* Try to read the current settings from _CRS if it is valid. */
	if (!sc->pl_crs_bad) {
		rreq.in_dpf = DPF_OUTSIDE;
		rreq.link_index = 0;
		rreq.res_index = 0;
		rreq.sc = sc;
		status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
		    link_add_crs, &rreq);
		if (ACPI_FAILURE(status)) {
			device_printf(dev, "Unable to parse _CRS: %s\n",
			    AcpiFormatException(status));
			goto fail;
		}
	}

	/*
	 * Try to read the possible settings from _PRS.  Note that if the
	 * _CRS is toast, we depend on having a working _PRS.  However, if
	 * _CRS works, then it is ok for _PRS to be missing.
	 */
	rreq.in_dpf = DPF_OUTSIDE;
	rreq.link_index = 0;
	rreq.res_index = 0;
	rreq.sc = sc;
	status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
	    link_add_prs, &rreq);
	if (ACPI_FAILURE(status) &&
	    (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
		device_printf(dev, "Unable to parse _PRS: %s\n",
		    AcpiFormatException(status));
		goto fail;
	}
	if (bootverbose)
		acpi_pci_link_dump(sc, 1, "Initial Probe");

	/* Verify initial IRQs if we have _PRS. */
	if (status != AE_NOT_FOUND)
		for (i = 0; i < sc->pl_num_links; i++)
			if (!link_valid_irq(&sc->pl_links[i],
			    sc->pl_links[i].l_irq))
				sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
	if (bootverbose)
		acpi_pci_link_dump(sc, 0, "Validation");

	/* Save initial IRQs. */
	for (i = 0; i < sc->pl_num_links; i++)
		sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;

	/*
	 * Try to disable this link.  If successful, set the current IRQ to
	 * zero and flags to indicate this link is not routed.  If we can't
	 * run _DIS (i.e., the method doesn't exist), assume the initial
	 * IRQ was routed by the BIOS.
	 */
	if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
	    NULL)))
		for (i = 0; i < sc->pl_num_links; i++)
			sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
	else
		for (i = 0; i < sc->pl_num_links; i++)
			if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
				sc->pl_links[i].l_routed = TRUE;
	if (bootverbose)
		acpi_pci_link_dump(sc, 0, "After Disable");
	ACPI_SERIAL_END(pci_link);
	return (0);
fail:
	ACPI_SERIAL_END(pci_link);
	for (i = 0; i < sc->pl_num_links; i++)
		if (sc->pl_links[i].l_irqs != NULL)
			free(sc->pl_links[i].l_irqs, M_PCI_LINK);
	free(sc->pl_links, M_PCI_LINK);
	return (ENXIO);
}
Esempio n. 10
0
/*
 * Route an interrupt for a child of the bridge.
 */
int
acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
    ACPI_BUFFER *prtbuf)
{
    ACPI_PCI_ROUTING_TABLE *prt;
    struct prt_lookup_request pr;
    ACPI_HANDLE lnkdev;
    int interrupt;

    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

    interrupt = PCI_INVALID_IRQ;

    /* ACPI numbers pins 0-3, not 1-4 like the BIOS. */
    pin--;

    ACPI_SERIAL_BEGIN(pcib);

    /* Search for a matching entry in the routing table. */
    pr.pr_entry = NULL;
    pr.pr_pin = pin;
    pr.pr_slot = pci_get_slot(dev);
    prt_walk_table(prtbuf, prt_lookup_device, &pr);
    if (pr.pr_entry == NULL) {
	device_printf(pcib, "no PRT entry for %d.%d.INT%c\n", pci_get_bus(dev),
	    pci_get_slot(dev), 'A' + pin);
	goto out;
    }
    prt = pr.pr_entry;

    if (bootverbose) {
	device_printf(pcib, "matched entry for %d.%d.INT%c",
	    pci_get_bus(dev), pci_get_slot(dev), 'A' + pin);
	if (prt->Source != NULL && prt->Source[0] != '\0')
		printf(" (src %s:%u)", prt->Source, prt->SourceIndex);
	printf("\n");
    }

    /*
     * If source is empty/NULL, the source index is a global IRQ number
     * and it's hard-wired so we're done.
     *
     * XXX: If the source index is non-zero, ignore the source device and
     * assume that this is a hard-wired entry.
     */
    if (prt->Source == NULL || prt->Source[0] == '\0' ||
	prt->SourceIndex != 0) {
	if (bootverbose)
	    device_printf(pcib, "slot %d INT%c hardwired to IRQ %d\n",
		pci_get_slot(dev), 'A' + pin, prt->SourceIndex);
	if (prt->SourceIndex) {
	    interrupt = prt->SourceIndex;
	    BUS_CONFIG_INTR(dev, interrupt, INTR_TRIGGER_LEVEL,
		INTR_POLARITY_LOW);
	} else
	    device_printf(pcib, "error: invalid hard-wired IRQ of 0\n");
	goto out;
    }

    /*
     * We have to find the source device (PCI interrupt link device).
     */
    if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
	device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
	    prt->Source);
	goto out;
    }
    interrupt = acpi_pci_link_route_interrupt(acpi_get_device(lnkdev),
	prt->SourceIndex);

    if (bootverbose && PCI_INTERRUPT_VALID(interrupt))
	device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
	    pci_get_slot(dev), 'A' + pin, interrupt, acpi_name(lnkdev));

out:
    ACPI_SERIAL_END(pcib);

    return_VALUE(interrupt);
}