When assigning an IRQ for classic PIC routing to an ACPI link dev,
make sure to update the PCI Interrupt Register of all PCI devices using it. This fixes interrupt with ACPI and w/o IOAPIC, when the BIOS didn't route all interrupts correctly. Reported by sborill@ and by Mathias De Belder in PR 37001, fix tested by sborill@.
This commit is contained in:
parent
54221c3e0f
commit
bedc8f14a0
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: acpi_pci_link.c,v 1.8 2007/11/05 10:30:44 joerg Exp $ */
|
||||
/* $NetBSD: acpi_pci_link.c,v 1.9 2007/11/07 15:29:15 joerg Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
|
||||
@ -27,7 +27,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: acpi_pci_link.c,v 1.8 2007/11/05 10:30:44 joerg Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: acpi_pci_link.c,v 1.9 2007/11/07 15:29:15 joerg Exp $");
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include <sys/param.h>
|
||||
@ -110,6 +110,8 @@ struct link {
|
||||
int l_num_irqs;
|
||||
int *l_irqs;
|
||||
int l_references;
|
||||
int l_dev_count;
|
||||
pcitag_t *l_devices;
|
||||
int l_routed:1;
|
||||
int l_isa_irq:1;
|
||||
ACPI_RESOURCE l_prs_template;
|
||||
@ -468,6 +470,8 @@ acpi_pci_link_attach(struct acpi_pci_link_softc *sc)
|
||||
sc->pl_links[i].l_sc = sc;
|
||||
sc->pl_links[i].l_isa_irq = FALSE;
|
||||
sc->pl_links[i].l_res_index = -1;
|
||||
sc->pl_links[i].l_dev_count = 0;
|
||||
sc->pl_links[i].l_devices = NULL;
|
||||
}
|
||||
|
||||
/* Try to read the current settings from _CRS if it is valid. */
|
||||
@ -546,13 +550,59 @@ acpi_pci_link_attach(struct acpi_pci_link_softc *sc)
|
||||
return (0);
|
||||
fail:
|
||||
ACPI_SERIAL_END(pci_link);
|
||||
for (i = 0; i < sc->pl_num_links; i++)
|
||||
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);
|
||||
if (sc->pl_links[i].l_devices != NULL)
|
||||
free(sc->pl_links[i].l_devices, M_PCI_LINK);
|
||||
}
|
||||
free(sc->pl_links, M_PCI_LINK);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_pci_link_add_functions(struct acpi_pci_link_softc *sc, struct link *link,
|
||||
int bus, int device, int pin)
|
||||
{
|
||||
uint32_t value;
|
||||
uint8_t func, maxfunc, ipin;
|
||||
pcitag_t tag;
|
||||
|
||||
tag = pci_make_tag(acpi_softc->sc_pc, bus, device, 0);
|
||||
/* See if we have a valid device at function 0. */
|
||||
value = pci_conf_read(acpi_softc->sc_pc, tag, PCI_BHLC_REG);
|
||||
if (PCI_HDRTYPE_TYPE(value) > PCI_HDRTYPE_PCB)
|
||||
return;
|
||||
if (PCI_HDRTYPE_MULTIFN(value))
|
||||
maxfunc = 7;
|
||||
else
|
||||
maxfunc = 0;
|
||||
|
||||
/* Scan all possible functions at this device. */
|
||||
for (func = 0; func <= maxfunc; func++) {
|
||||
tag = pci_make_tag(acpi_softc->sc_pc, bus, device, func);
|
||||
value = pci_conf_read(acpi_softc->sc_pc, tag, PCI_ID_REG);
|
||||
if (PCI_VENDOR(value) == 0xffff)
|
||||
continue;
|
||||
value = pci_conf_read(acpi_softc->sc_pc, tag,
|
||||
PCI_INTERRUPT_REG);
|
||||
ipin = PCI_INTERRUPT_PIN(value);
|
||||
/*
|
||||
* See if it uses the pin in question. Note that the passed
|
||||
* in pin uses 0 for A, .. 3 for D whereas the intpin
|
||||
* register uses 0 for no interrupt, 1 for A, .. 4 for D.
|
||||
*/
|
||||
if (ipin != pin + 1)
|
||||
continue;
|
||||
|
||||
link->l_devices = realloc(link->l_devices,
|
||||
sizeof(pcitag_t) * (link->l_dev_count + 1),
|
||||
M_PCI_LINK, M_WAITOK);
|
||||
link->l_devices[link->l_dev_count] = tag;
|
||||
++link->l_dev_count;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
acpi_pci_link_search_irq(struct acpi_pci_link_softc *sc, int bus, int device,
|
||||
int pin)
|
||||
@ -630,6 +680,7 @@ acpi_pci_link_add_reference(void *v, int index, int bus, int slot, int pin)
|
||||
return;
|
||||
}
|
||||
link->l_references++;
|
||||
acpi_pci_link_add_functions(sc, link, bus, slot, pin);
|
||||
if (link->l_routed)
|
||||
pci_link_interrupt_weights[link->l_irq]++;
|
||||
|
||||
@ -1009,6 +1060,8 @@ acpi_pci_link_route_interrupt(void *v, int index, int *irq, int *pol, int *trig)
|
||||
{
|
||||
struct acpi_pci_link_softc *sc = v;
|
||||
struct link *link;
|
||||
int i;
|
||||
pcireg_t reg;
|
||||
|
||||
ACPI_SERIAL_BEGIN(pci_link);
|
||||
link = acpi_pci_link_lookup(sc, index);
|
||||
@ -1029,23 +1082,36 @@ acpi_pci_link_route_interrupt(void *v, int index, int *irq, int *pol, int *trig)
|
||||
}
|
||||
|
||||
/* Choose an IRQ if we need one. */
|
||||
if (!PCI_INTERRUPT_VALID(link->l_irq)) {
|
||||
link->l_irq = acpi_pci_link_choose_irq(sc, link);
|
||||
if (PCI_INTERRUPT_VALID(link->l_irq))
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* 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(sc, irq, pol, trig);
|
||||
if (!link->l_routed)
|
||||
link->l_irq = PCI_INVALID_IRQ;
|
||||
else {
|
||||
link->l_pol = *pol;
|
||||
link->l_trig = *trig;
|
||||
}
|
||||
}
|
||||
link->l_irq = acpi_pci_link_choose_irq(sc, 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))
|
||||
goto done;
|
||||
|
||||
acpi_pci_link_route_irqs(sc, irq, pol, trig);
|
||||
if (!link->l_routed) {
|
||||
link->l_irq = PCI_INVALID_IRQ;
|
||||
goto done;
|
||||
}
|
||||
|
||||
link->l_pol = *pol;
|
||||
link->l_trig = *trig;
|
||||
for (i = 0; i < link->l_dev_count; ++i) {
|
||||
reg = pci_conf_read(acpi_softc->sc_pc, link->l_devices[i],
|
||||
PCI_INTERRUPT_REG);
|
||||
reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
|
||||
reg |= link->l_irq << PCI_INTERRUPT_LINE_SHIFT;
|
||||
pci_conf_write(acpi_softc->sc_pc, link->l_devices[i],
|
||||
PCI_INTERRUPT_REG, reg);
|
||||
}
|
||||
|
||||
done:
|
||||
ACPI_SERIAL_END(pci_link);
|
||||
|
||||
return (link->l_irq);
|
||||
|
Loading…
Reference in New Issue
Block a user