[PATCH] IRQ params patch#4

Andrew de Quincey
2003-07-15T10:42:12Z

[-- Attachment #1: Type: text/plain, Size: 721 bytes --]

OK, patch#3 is way too strict... thought a bit more about it, and I suppose
the BIOS not allocating some devices an IRQ is actually OK....

Anyway, patch#4 now just warns the user if an attempt to set an IRQ results in
a different IRQ than expected, and attempts to abort ACPI irq routing.
Unfortunately, it isn't able to undo the changes it has made to the routing
as it would have to call the _SRS method to do it... and its just found out
that that is buggy. So at the moment it probably won't be able to
automatically "fix" devices already programmed when dropping back to a
different method (it can't on my board). Sigh.

Anyway, the warning should at least help find which boards have bad ACPI IRQ
routing.

[-- Attachment #2: linux-2.5.75-acpi-irqparams-4.patch --]
[-- Type: text/x-diff, Size: 8836 bytes --]

diff -Naurb linux-2.5.75.orig/arch/i386/kernel/io_apic.c linux-2.5.75/arch/i386/kernel/io_apic.c
--- linux-2.5.75.orig/arch/i386/kernel/io_apic.c 2003-07-10 21:07:34.000000000 +0100
+++ linux-2.5.75/arch/i386/kernel/io_apic.c 2003-07-13 23:53:12.000000000 +0100
@@ -2313,7 +2313,7 @@
}

-int io_apic_set_pci_routing (int ioapic, int pin, int irq)
+int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low)
{
struct IO_APIC_route_entry entry;
unsigned long flags;
@@ -2336,18 +2336,22 @@
entry.dest_mode = INT_DEST_MODE;
entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
entry.mask = 1; /* Disabled (masked) */
- entry.trigger = 1; /* Level sensitive */
- entry.polarity = 1; /* Low active */
+ entry.trigger = edge_level;
+ entry.polarity = active_high_low;

add_pin_to_irq(irq, ioapic, pin);

entry.vector = assign_irq_vector(irq);

printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> "
- "IRQ %d)\n", ioapic,
- mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq);
+ "IRQ %d Mode:%i Active:%i)\n", ioapic,
+ mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, edge_level, active_high_low);

+ if (edge_level) {
irq_desc[irq].handler = &ioapic_level_irq_type;
+ } else {
+ irq_desc[irq].handler = &ioapic_edge_irq_type;
+ }

set_intr_gate(entry.vector, interrupt[irq]);

diff -Naurb linux-2.5.75.orig/arch/i386/kernel/mpparse.c linux-2.5.75/arch/i386/kernel/mpparse.c
--- linux-2.5.75.orig/arch/i386/kernel/mpparse.c 2003-07-10 21:06:59.000000000 +0100
+++ linux-2.5.75/arch/i386/kernel/mpparse.c 2003-07-14 22:31:00.000000000 +0100
@@ -1065,7 +1065,7 @@

ioapic_pin = irq - mp_ioapic_routing[ioapic].irq_start;

- io_apic_set_pci_routing(ioapic, ioapic_pin, irq);
+ io_apic_set_pci_routing(ioapic, ioapic_pin, irq, 1, 1); // Active low, edge triggered
}

#endif /*CONFIG_ACPI_HT_ONLY*/
@@ -1080,6 +1080,8 @@
int ioapic_pin = 0;
int irq = 0;
int idx, bit = 0;
+ int edge_level = 0;
+ int active_high_low = 0;

/*
* Parsing through the PCI Interrupt Routing Table (PRT) and program
@@ -1090,7 +1092,7 @@

/* Need to get irq for dynamic entry */
if (entry->link.handle) {
- irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index);
+ irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index, &edge_level, &active_high_low);
if (!irq)
continue;
}
@@ -1130,7 +1132,7 @@

mp_ioapic_routing[ioapic].pin_programmed[idx] |= (1<<bit);

- if (!io_apic_set_pci_routing(ioapic, ioapic_pin, irq))
+ if (!io_apic_set_pci_routing(ioapic, ioapic_pin, irq, edge_level, active_high_low))
entry->irq = irq;

printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> IRQ %d\n",
diff -Naurb linux-2.5.75.orig/drivers/acpi/pci_irq.c linux-2.5.75/drivers/acpi/pci_irq.c
--- linux-2.5.75.orig/drivers/acpi/pci_irq.c 2003-07-10 21:08:54.000000000 +0100
+++ linux-2.5.75/drivers/acpi/pci_irq.c 2003-07-15 02:56:11.000000000 +0100
@@ -249,7 +249,7 @@
}

if (!entry->irq && entry->link.handle) {
- entry->irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index);
+ entry->irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index, NULL, NULL);
if (!entry->irq) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n"));
return_VALUE(0);
@@ -389,7 +389,9 @@
}

/* Make sure all link devices have a valid IRQ. */
- acpi_pci_link_check();
+ if (acpi_pci_link_check()) {
+ return_VALUE(-ENODEV);
+ }

#ifdef CONFIG_X86_IO_APIC
/* Program IOAPICs using data from PRT entries. */
diff -Naurb linux-2.5.75.orig/drivers/acpi/pci_link.c linux-2.5.75/drivers/acpi/pci_link.c
--- linux-2.5.75.orig/drivers/acpi/pci_link.c 2003-07-10 21:04:58.000000000 +0100
+++ linux-2.5.75/drivers/acpi/pci_link.c 2003-07-15 12:28:08.000000000 +0100
@@ -69,6 +69,8 @@

struct acpi_pci_link_irq {
u8 active; /* Current IRQ */
+ u8 edge_level; /* All IRQs */
+ u8 active_high_low; /* All IRQs */
u8 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
};
@@ -118,6 +120,8 @@
link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++;
}
+ link->irq.edge_level = p->edge_level;
+ link->irq.active_high_low = p->active_high_low;
break;
}
case ACPI_RSTYPE_EXT_IRQ:
@@ -136,6 +140,8 @@
link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++;
}
+ link->irq.edge_level = p->edge_level;
+ link->irq.active_high_low = p->active_high_low;
break;
}
default:
@@ -310,12 +316,14 @@

memset(&resource, 0, sizeof(resource));

- /* NOTE: PCI interrupts are always level / active_low / shared. */
+ /* NOTE: PCI interrupts are always level / active_low / shared. But not all
+ interrupts > 15 are PCI interrupts. Rely on the ACPI IRQ definition for
+ parameters */
if (irq <= 15) {
resource.res.id = ACPI_RSTYPE_IRQ;
resource.res.length = sizeof(struct acpi_resource);
- resource.res.data.irq.edge_level = ACPI_LEVEL_SENSITIVE;
- resource.res.data.irq.active_high_low = ACPI_ACTIVE_LOW;
+ resource.res.data.irq.edge_level = link->irq.edge_level;
+ resource.res.data.irq.active_high_low = link->irq.active_high_low;
resource.res.data.irq.shared_exclusive = ACPI_SHARED;
resource.res.data.irq.number_of_interrupts = 1;
resource.res.data.irq.interrupts[0] = irq;
@@ -324,8 +332,8 @@
resource.res.id = ACPI_RSTYPE_EXT_IRQ;
resource.res.length = sizeof(struct acpi_resource);
resource.res.data.extended_irq.producer_consumer = ACPI_CONSUMER;
- resource.res.data.extended_irq.edge_level = ACPI_LEVEL_SENSITIVE;
- resource.res.data.extended_irq.active_high_low = ACPI_ACTIVE_LOW;
+ resource.res.data.extended_irq.edge_level = link->irq.edge_level;
+ resource.res.data.extended_irq.active_high_low = link->irq.active_high_low;
resource.res.data.extended_irq.shared_exclusive = ACPI_SHARED;
resource.res.data.extended_irq.number_of_interrupts = 1;
resource.res.data.extended_irq.interrupts[0] = irq;
@@ -333,6 +341,7 @@
}
resource.end.id = ACPI_RSTYPE_END_TAG;

+ /* Attempt to set the resource */
status = acpi_set_current_resources(link->handle, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n"));
@@ -455,15 +464,20 @@
irq = link->irq.possible[i];
}

- /* Enable the link device at this IRQ. */
- acpi_pci_link_set(link, irq);
-
+ /* Attempt to enable the link device at this IRQ. */
+ if (acpi_pci_link_set(link, irq)) {
+ printk(PREFIX "Unable to set IRQ for %s [%s] (likely buggy ACPI BIOS). Aborting ACPI-based IRQ routing. Try pci=noacpi or acpi=off\n",
+ acpi_device_name(link->device),
+ acpi_device_bid(link->device));
+ return_VALUE(-ENODEV);
+ } else {
acpi_irq_penalty[link->irq.active] += 100;

printk(PREFIX "%s [%s] enabled at IRQ %d\n",
acpi_device_name(link->device),
acpi_device_bid(link->device), link->irq.active);
}
+ }

return_VALUE(0);
}
@@ -472,7 +486,9 @@
int
acpi_pci_link_get_irq (
acpi_handle handle,
- int index)
+ int index,
+ int* edge_level,
+ int* active_high_low)
{
int result = 0;
struct acpi_device *device = NULL;
@@ -503,6 +519,8 @@
return_VALUE(0);
}

+ if (edge_level) *edge_level = link->irq.edge_level;
+ if (active_high_low) *active_high_low = link->irq.active_high_low;
return_VALUE(link->irq.active);
}

diff -Naurb linux-2.5.75.orig/include/acpi/acpi_drivers.h linux-2.5.75/include/acpi/acpi_drivers.h
--- linux-2.5.75.orig/include/acpi/acpi_drivers.h 2003-07-10 21:12:26.000000000 +0100
+++ linux-2.5.75/include/acpi/acpi_drivers.h 2003-07-13 22:21:05.000000000 +0100
@@ -60,7 +60,7 @@
/* ACPI PCI Interrupt Link (pci_link.c) */

int acpi_pci_link_check (void);
-int acpi_pci_link_get_irq (acpi_handle handle, int index);
+int acpi_pci_link_get_irq (acpi_handle handle, int index, int* edge_level, int* active_high_low);

/* ACPI PCI Interrupt Routing (pci_irq.c) */

diff -Naurb linux-2.5.75.orig/include/asm-i386/io_apic.h linux-2.5.75/include/asm-i386/io_apic.h
--- linux-2.5.75.orig/include/asm-i386/io_apic.h 2003-07-10 21:12:15.000000000 +0100
+++ linux-2.5.75/include/asm-i386/io_apic.h 2003-07-13 22:21:05.000000000 +0100
@@ -170,7 +170,7 @@
extern int io_apic_get_unique_id (int ioapic, int apic_id);
extern int io_apic_get_version (int ioapic);
extern int io_apic_get_redir_entries (int ioapic);
-extern int io_apic_set_pci_routing (int ioapic, int pin, int irq);
+extern int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low);
#endif /*CONFIG_ACPI_BOOT*/

#else /* !CONFIG_X86_IO_APIC */