Skip to content

Commit

Permalink
pci, acpi: reroute PCI interrupt to legacy boot interrupt equivalent
Browse files Browse the repository at this point in the history
Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the
IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel
does during interrupt handling). On chipsets where this INTx generation
cannot be disabled, we reroute the valid interrupts to their legacy
equivalent to get rid of spurious interrupts that might otherwise bring
down (vital) interrupt lines through spurious interrupt detection in
note_interrupt().

This patch benefited from discussions with Alexander Graf, Torsten Duwe,
Ihno Krumreich, Daniel Gollub, Hannes Reinecke. The conclusions we drew
and the patch itself are the authors' responsibility alone.

Signed-off-by: Stefan Assmann <sassmann@suse.de>
Signed-off-by: Olaf Dabrunz <od@suse.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
Stefan Assmann authored and Ingo Molnar committed Jul 8, 2008
1 parent 426b3b8 commit e1d3a90
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 0 deletions.
56 changes: 56 additions & 0 deletions drivers/acpi/pci_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,27 @@ acpi_pci_free_irq(struct acpi_prt_entry *entry,
return irq;
}

#ifdef CONFIG_X86_IO_APIC
extern int noioapicquirk;

static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
{
struct pci_bus *bus_it;

for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
if (!bus_it->self)
return 0;

printk(KERN_INFO "vendor=%04x device=%04x\n", bus_it->self->vendor,
bus_it->self->device);

if (bus_it->self->irq_reroute_variant)
return bus_it->self->irq_reroute_variant;
}
return 0;
}
#endif /* CONFIG_X86_IO_APIC */

/*
* acpi_pci_irq_lookup
* success: return IRQ >= 0
Expand Down Expand Up @@ -413,6 +434,41 @@ acpi_pci_irq_lookup(struct pci_bus *bus,
}

ret = func(entry, triggering, polarity, link);

#ifdef CONFIG_X86_IO_APIC
/*
* Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the
* IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel
* does during interrupt handling). When this INTx generation cannot be
* disabled, we reroute these interrupts to their legacy equivalent to
* get rid of spurious interrupts.
*/
if (!noioapicquirk) {
switch (bridge_has_boot_interrupt_variant(bus)) {
case 0:
/* no rerouting necessary */
break;

case INTEL_IRQ_REROUTE_VARIANT:
/*
* Remap according to INTx routing table in 6700PXH
* specs, intel order number 302628-002, section
* 2.15.2. Other chipsets (80332, ...) have the same
* mapping and are handled here as well.
*/
printk(KERN_INFO "pci irq %d -> rerouted to legacy "
"irq %d\n", ret, (ret % 4) + 16);
ret = (ret % 4) + 16;
break;

default:
printk(KERN_INFO "not rerouting irq %d to legacy irq: "
"unknown mapping\n", ret);
break;
}
}
#endif /* CONFIG_X86_IO_APIC */

return ret;
}

Expand Down
28 changes: 28 additions & 0 deletions drivers/pci/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,34 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm);

#ifdef CONFIG_X86_IO_APIC
/*
* Boot interrupts on some chipsets cannot be turned off. For these chipsets,
* remap the original interrupt in the linux kernel to the boot interrupt, so
* that a PCI device's interrupt handler is installed on the boot interrupt
* line instead.
*/
static void quirk_reroute_to_boot_interrupts_intel(struct pci_dev *dev)
{
int i;

if (noioapicquirk)
return;

dev->irq_reroute_variant = INTEL_IRQ_REROUTE_VARIANT;

printk(KERN_INFO "PCI quirk: reroute interrupts for 0x%04x:0x%04x\n",
dev->vendor, dev->device);
return;
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel);

/*
* On some chipsets we can disable the generation of legacy INTx boot
* interrupts.
Expand Down
6 changes: 6 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
};

enum pci_irq_reroute_variant {
INTEL_IRQ_REROUTE_VARIANT = 1,
MAX_IRQ_REROUTE_VARIANTS = 3
};

typedef unsigned short __bitwise pci_bus_flags_t;
enum pci_bus_flags {
PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1,
Expand Down Expand Up @@ -194,6 +199,7 @@ struct pci_dev {
unsigned int no_d1d2:1; /* only allow d0 or d3 */
unsigned int block_ucfg_access:1; /* userspace config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */
unsigned int msi_enabled:1;
unsigned int msix_enabled:1;
unsigned int is_managed:1;
Expand Down

0 comments on commit e1d3a90

Please sign in to comment.