]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Stefan Assmann <sassmann@suse.de> |
2 | Subject: pci, acpi: reroute PCI interrupt to legacy boot interrupt equivalent | |
3 | ||
4 | Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the | |
5 | IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel | |
6 | does during interrupt handling). On chipsets where this INTx generation | |
7 | cannot be disabled, we reroute the valid interrupts to their legacy | |
8 | equivalent to get rid of spurious interrupts that might otherwise bring | |
9 | down (vital) interrupt lines through spurious interrupt detection in | |
10 | note_interrupt(). | |
11 | ||
12 | This patch benefited from discussions with Alexander Graf, Torsten Duwe, | |
13 | Ihno Krumreich, Daniel Gollub, Hannes Reinecke. The conclusions we drew | |
14 | and the patch itself are the authors' responsibility alone. | |
15 | ||
16 | Signed-off-by: Stefan Assmann <sassmann@suse.de> | |
17 | Signed-off-by: Olaf Dabrunz <od@suse.de> | |
18 | Signed-off-by: Ingo Molnar <mingo@elte.hu> | |
19 | --- | |
20 | drivers/acpi/pci_irq.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
21 | drivers/pci/quirks.c | 26 ++++++++++++++++++++++ | |
22 | include/linux/pci.h | 6 +++++ | |
23 | 3 files changed, 88 insertions(+) | |
24 | ||
25 | --- a/drivers/acpi/pci_irq.c | |
26 | +++ b/drivers/acpi/pci_irq.c | |
27 | @@ -384,6 +384,27 @@ acpi_pci_free_irq(struct acpi_prt_entry | |
28 | return irq; | |
29 | } | |
30 | ||
31 | +#ifdef CONFIG_X86_IO_APIC | |
32 | +extern int noioapicquirk; | |
33 | + | |
34 | +static int bridge_has_boot_interrupt_variant(struct pci_bus *bus) | |
35 | +{ | |
36 | + struct pci_bus *bus_it; | |
37 | + | |
38 | + for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { | |
39 | + if (!bus_it->self) | |
40 | + return 0; | |
41 | + | |
42 | + printk(KERN_INFO "vendor=%04x device=%04x\n", bus_it->self->vendor, | |
43 | + bus_it->self->device); | |
44 | + | |
45 | + if (bus_it->self->irq_reroute_variant) | |
46 | + return bus_it->self->irq_reroute_variant; | |
47 | + } | |
48 | + return 0; | |
49 | +} | |
50 | +#endif /* CONFIG_X86_IO_APIC */ | |
51 | + | |
52 | /* | |
53 | * acpi_pci_irq_lookup | |
54 | * success: return IRQ >= 0 | |
55 | @@ -413,6 +434,41 @@ acpi_pci_irq_lookup(struct pci_bus *bus, | |
56 | } | |
57 | ||
58 | ret = func(entry, triggering, polarity, link); | |
59 | + | |
60 | +#ifdef CONFIG_X86_IO_APIC | |
61 | + /* | |
62 | + * Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the | |
63 | + * IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel | |
64 | + * does during interrupt handling). When this INTx generation cannot be | |
65 | + * disabled, we reroute these interrupts to their legacy equivalent to | |
66 | + * get rid of spurious interrupts. | |
67 | + */ | |
68 | + if (!noioapicquirk) { | |
69 | + switch (bridge_has_boot_interrupt_variant(bus)) { | |
70 | + case 0: | |
71 | + /* no rerouting necessary */ | |
72 | + break; | |
73 | + | |
74 | + case INTEL_IRQ_REROUTE_VARIANT: | |
75 | + /* | |
76 | + * Remap according to INTx routing table in 6700PXH | |
77 | + * specs, intel order number 302628-002, section | |
78 | + * 2.15.2. Other chipsets (80332, ...) have the same | |
79 | + * mapping and are handled here as well. | |
80 | + */ | |
81 | + printk(KERN_INFO "pci irq %d -> rerouted to legacy " | |
82 | + "irq %d\n", ret, (ret % 4) + 16); | |
83 | + ret = (ret % 4) + 16; | |
84 | + break; | |
85 | + | |
86 | + default: | |
87 | + printk(KERN_INFO "not rerouting irq %d to legacy irq: " | |
88 | + "unknown mapping\n", ret); | |
89 | + break; | |
90 | + } | |
91 | + } | |
92 | +#endif /* CONFIG_X86_IO_APIC */ | |
93 | + | |
94 | return ret; | |
95 | } | |
96 | ||
97 | --- a/drivers/pci/quirks.c | |
98 | +++ b/drivers/pci/quirks.c | |
99 | @@ -1425,6 +1425,32 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN | |
100 | ||
101 | #ifdef CONFIG_X86_IO_APIC | |
102 | /* | |
103 | + * Boot interrupts on some chipsets cannot be turned off. For these chipsets, | |
104 | + * remap the original interrupt in the linux kernel to the boot interrupt, so | |
105 | + * that a PCI device's interrupt handler is installed on the boot interrupt | |
106 | + * line instead. | |
107 | + */ | |
108 | +static void quirk_reroute_to_boot_interrupts_intel(struct pci_dev *dev) | |
109 | +{ | |
110 | + if (noioapicquirk) | |
111 | + return; | |
112 | + | |
113 | + dev->irq_reroute_variant = INTEL_IRQ_REROUTE_VARIANT; | |
114 | + | |
115 | + printk(KERN_INFO "PCI quirk: reroute interrupts for 0x%04x:0x%04x\n", | |
116 | + dev->vendor, dev->device); | |
117 | + return; | |
118 | +} | |
119 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel); | |
120 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel); | |
121 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel); | |
122 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel); | |
123 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel); | |
124 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel); | |
125 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel); | |
126 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel); | |
127 | + | |
128 | +/* | |
129 | * On some chipsets we can disable the generation of legacy INTx boot | |
130 | * interrupts. | |
131 | */ | |
132 | --- a/include/linux/pci.h | |
133 | +++ b/include/linux/pci.h | |
134 | @@ -133,6 +133,11 @@ enum pci_dev_flags { | |
135 | PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, | |
136 | }; | |
137 | ||
138 | +enum pci_irq_reroute_variant { | |
139 | + INTEL_IRQ_REROUTE_VARIANT = 1, | |
140 | + MAX_IRQ_REROUTE_VARIANTS = 3 | |
141 | +}; | |
142 | + | |
143 | typedef unsigned short __bitwise pci_bus_flags_t; | |
144 | enum pci_bus_flags { | |
145 | PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1, | |
146 | @@ -217,6 +222,7 @@ struct pci_dev { | |
147 | unsigned int no_msi:1; /* device may not use msi */ | |
148 | unsigned int block_ucfg_access:1; /* userspace config space access is blocked */ | |
149 | unsigned int broken_parity_status:1; /* Device generates false positive parity */ | |
150 | + unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */ | |
151 | unsigned int msi_enabled:1; | |
152 | unsigned int msix_enabled:1; | |
153 | unsigned int is_managed:1; |