Commit 3939eacf authored by Andy Grover's avatar Andy Grover

ACPI: Allow irqs > 15 to use interrupt semantics other than PCI's standard

active-low, level trigger. Make other changes as required for this.  (Andrew de Quincey)
parent 6e430522
...@@ -2328,7 +2328,7 @@ int __init io_apic_get_redir_entries (int ioapic) ...@@ -2328,7 +2328,7 @@ int __init io_apic_get_redir_entries (int ioapic)
} }
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; struct IO_APIC_route_entry entry;
unsigned long flags; unsigned long flags;
...@@ -2350,19 +2350,23 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq) ...@@ -2350,19 +2350,23 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq)
entry.delivery_mode = INT_DELIVERY_MODE; entry.delivery_mode = INT_DELIVERY_MODE;
entry.dest_mode = INT_DEST_MODE; entry.dest_mode = INT_DEST_MODE;
entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
entry.mask = 1; /* Disabled (masked) */ entry.trigger = edge_level;
entry.trigger = 1; /* Level sensitive */ entry.polarity = active_high_low;
entry.polarity = 1; /* Low active */ entry.mask = 1;
add_pin_to_irq(irq, ioapic, pin); add_pin_to_irq(irq, ioapic, pin);
entry.vector = assign_irq_vector(irq); entry.vector = assign_irq_vector(irq);
printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> "
"IRQ %d)\n", ioapic, "IRQ %d Mode:%i Active:%i)\n", ioapic,
mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq); 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; irq_desc[irq].handler = &ioapic_level_irq_type;
} else {
irq_desc[irq].handler = &ioapic_edge_irq_type;
}
set_intr_gate(entry.vector, interrupt[irq]); set_intr_gate(entry.vector, interrupt[irq]);
......
...@@ -1065,7 +1065,7 @@ void __init mp_config_ioapic_for_sci(int irq) ...@@ -1065,7 +1065,7 @@ void __init mp_config_ioapic_for_sci(int irq)
ioapic_pin = irq - mp_ioapic_routing[ioapic].irq_start; 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, level triggered
} }
#endif /*CONFIG_ACPI_HT_ONLY*/ #endif /*CONFIG_ACPI_HT_ONLY*/
...@@ -1080,6 +1080,8 @@ void __init mp_parse_prt (void) ...@@ -1080,6 +1080,8 @@ void __init mp_parse_prt (void)
int ioapic_pin = 0; int ioapic_pin = 0;
int irq = 0; int irq = 0;
int idx, bit = 0; int idx, bit = 0;
int edge_level = 0;
int active_high_low = 0;
/* /*
* Parsing through the PCI Interrupt Routing Table (PRT) and program * Parsing through the PCI Interrupt Routing Table (PRT) and program
...@@ -1090,12 +1092,16 @@ void __init mp_parse_prt (void) ...@@ -1090,12 +1092,16 @@ void __init mp_parse_prt (void)
/* Need to get irq for dynamic entry */ /* Need to get irq for dynamic entry */
if (entry->link.handle) { 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) if (!irq)
continue; continue;
} }
else else {
/* Hardwired IRQ. Assume PCI standard settings */
irq = entry->link.index; irq = entry->link.index;
edge_level = 1;
active_high_low = 1;
}
/* Don't set up the ACPI SCI because it's already set up */ /* Don't set up the ACPI SCI because it's already set up */
if (acpi_fadt.sci_int == irq) if (acpi_fadt.sci_int == irq)
...@@ -1130,7 +1136,7 @@ void __init mp_parse_prt (void) ...@@ -1130,7 +1136,7 @@ void __init mp_parse_prt (void)
mp_ioapic_routing[ioapic].pin_programmed[idx] |= (1<<bit); 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; entry->irq = irq;
printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> IRQ %d\n", printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> IRQ %d\n",
...@@ -1139,8 +1145,6 @@ void __init mp_parse_prt (void) ...@@ -1139,8 +1145,6 @@ void __init mp_parse_prt (void)
mp_ioapic_routing[ioapic].apic_id, ioapic_pin, mp_ioapic_routing[ioapic].apic_id, ioapic_pin,
entry->irq); entry->irq);
} }
return;
} }
#endif /*CONFIG_ACPI_PCI*/ #endif /*CONFIG_ACPI_PCI*/
......
...@@ -254,7 +254,7 @@ acpi_pci_irq_lookup (struct pci_bus *bus, int device, int pin) ...@@ -254,7 +254,7 @@ acpi_pci_irq_lookup (struct pci_bus *bus, int device, int pin)
} }
if (!entry->irq && entry->link.handle) { 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) { if (!entry->irq) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n"));
return_VALUE(0); return_VALUE(0);
...@@ -398,7 +398,9 @@ acpi_pci_irq_init (void) ...@@ -398,7 +398,9 @@ acpi_pci_irq_init (void)
} }
/* Make sure all link devices have a valid IRQ. */ /* 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 #ifdef CONFIG_X86_IO_APIC
/* Program IOAPICs using data from PRT entries. */ /* Program IOAPICs using data from PRT entries. */
......
...@@ -69,6 +69,9 @@ static struct acpi_driver acpi_pci_link_driver = { ...@@ -69,6 +69,9 @@ static struct acpi_driver acpi_pci_link_driver = {
struct acpi_pci_link_irq { struct acpi_pci_link_irq {
u8 active; /* Current IRQ */ u8 active; /* Current IRQ */
u8 edge_level; /* All IRQs */
u8 active_high_low; /* All IRQs */
u8 setonboot;
u8 possible_count; u8 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
}; };
...@@ -118,6 +121,8 @@ acpi_pci_link_check_possible ( ...@@ -118,6 +121,8 @@ acpi_pci_link_check_possible (
link->irq.possible[i] = p->interrupts[i]; link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++; link->irq.possible_count++;
} }
link->irq.edge_level = p->edge_level;
link->irq.active_high_low = p->active_high_low;
break; break;
} }
case ACPI_RSTYPE_EXT_IRQ: case ACPI_RSTYPE_EXT_IRQ:
...@@ -136,6 +141,8 @@ acpi_pci_link_check_possible ( ...@@ -136,6 +141,8 @@ acpi_pci_link_check_possible (
link->irq.possible[i] = p->interrupts[i]; link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++; link->irq.possible_count++;
} }
link->irq.edge_level = p->edge_level;
link->irq.active_high_low = p->active_high_low;
break; break;
} }
default: default:
...@@ -264,7 +271,6 @@ acpi_pci_link_get_current ( ...@@ -264,7 +271,6 @@ acpi_pci_link_get_current (
* IRQ a boot-enabled Link device is set to is the correct one. * IRQ a boot-enabled Link device is set to is the correct one.
* (Required to support systems such as the Toshiba 5005-S504.) * (Required to support systems such as the Toshiba 5005-S504.)
*/ */
link->irq.active = irq; link->irq.active = irq;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active)); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active));
...@@ -294,29 +300,33 @@ acpi_pci_link_set ( ...@@ -294,29 +300,33 @@ acpi_pci_link_set (
if (!link || !irq) if (!link || !irq)
return_VALUE(-EINVAL); return_VALUE(-EINVAL);
/* See if we're already at the target IRQ. */ /* We don't check irqs the first time around */
if (irq == link->irq.active) if (link->irq.setonboot) {
return_VALUE(0); /* See if we're already at the target IRQ. */
if (irq == link->irq.active)
return_VALUE(0);
/* Make sure the target IRQ in the list of possible IRQs. */ /* Make sure the target IRQ in the list of possible IRQs. */
for (i=0; i<link->irq.possible_count; i++) { for (i=0; i<link->irq.possible_count; i++) {
if (irq == link->irq.possible[i]) if (irq == link->irq.possible[i])
valid = 1; valid = 1;
} }
if (!valid) { if (!valid) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ %d invalid\n", irq)); ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ %d invalid\n", irq));
return_VALUE(-EINVAL); return_VALUE(-EINVAL);
}
} }
memset(&resource, 0, sizeof(resource)); 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) { if (irq <= 15) {
resource.res.id = ACPI_RSTYPE_IRQ; resource.res.id = ACPI_RSTYPE_IRQ;
resource.res.length = sizeof(struct acpi_resource); resource.res.length = sizeof(struct acpi_resource);
resource.res.data.irq.edge_level = ACPI_LEVEL_SENSITIVE; resource.res.data.irq.edge_level = link->irq.edge_level;
resource.res.data.irq.active_high_low = ACPI_ACTIVE_LOW; 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.number_of_interrupts = 1;
resource.res.data.irq.interrupts[0] = irq; resource.res.data.irq.interrupts[0] = irq;
} }
...@@ -324,15 +334,15 @@ acpi_pci_link_set ( ...@@ -324,15 +334,15 @@ acpi_pci_link_set (
resource.res.id = ACPI_RSTYPE_EXT_IRQ; resource.res.id = ACPI_RSTYPE_EXT_IRQ;
resource.res.length = sizeof(struct acpi_resource); resource.res.length = sizeof(struct acpi_resource);
resource.res.data.extended_irq.producer_consumer = ACPI_CONSUMER; resource.res.data.extended_irq.producer_consumer = ACPI_CONSUMER;
resource.res.data.extended_irq.edge_level = ACPI_LEVEL_SENSITIVE; resource.res.data.extended_irq.edge_level = link->irq.edge_level;
resource.res.data.extended_irq.active_high_low = ACPI_ACTIVE_LOW; 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.number_of_interrupts = 1;
resource.res.data.extended_irq.interrupts[0] = irq; resource.res.data.extended_irq.interrupts[0] = irq;
/* ignore resource_source, it's optional */ /* ignore resource_source, it's optional */
} }
resource.end.id = ACPI_RSTYPE_END_TAG; resource.end.id = ACPI_RSTYPE_END_TAG;
/* Attempt to set the resource */
status = acpi_set_current_resources(link->handle, &buffer); status = acpi_set_current_resources(link->handle, &buffer);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n")); ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n"));
...@@ -355,11 +365,13 @@ acpi_pci_link_set ( ...@@ -355,11 +365,13 @@ acpi_pci_link_set (
if (result) { if (result) {
return_VALUE(result); return_VALUE(result);
} }
if (link->irq.active != irq) { if (link->irq.active != irq) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Attempt to enable at IRQ %d resulted in IRQ %d\n", "Attempt to enable at IRQ %d resulted in IRQ %d\n",
irq, link->irq.active)); irq, link->irq.active));
link->irq.active = 0; link->irq.active = 0;
acpi_ut_evaluate_object (link->handle, "_DIS", 0, NULL);
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
} }
...@@ -407,7 +419,7 @@ acpi_pci_link_check (void) ...@@ -407,7 +419,7 @@ acpi_pci_link_check (void)
ACPI_FUNCTION_TRACE("acpi_pci_link_check"); ACPI_FUNCTION_TRACE("acpi_pci_link_check");
/* /*
* Pass #1: Update penalties to facilitate IRQ balancing. * Update penalties to facilitate IRQ balancing.
*/ */
list_for_each(node, &acpi_link.entries) { list_for_each(node, &acpi_link.entries) {
...@@ -428,23 +440,23 @@ acpi_pci_link_check (void) ...@@ -428,23 +440,23 @@ acpi_pci_link_check (void)
} }
} }
/* return_VALUE(0);
* Pass #2: Enable boot-disabled Links at 'best' IRQ. }
*/
list_for_each(node, &acpi_link.entries) {
int irq = 0;
int i = 0;
link = list_entry(node, struct acpi_pci_link, node); static int acpi_pci_link_allocate(struct acpi_pci_link* link) {
if (!link || !link->irq.possible_count) { int irq;
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); int i;
continue;
}
if (link->irq.active) ACPI_FUNCTION_TRACE("acpi_pci_link_allocate");
continue;
if (link->irq.setonboot)
return_VALUE(0);
if (link->irq.active) {
irq = link->irq.active;
} else {
irq = link->irq.possible[0]; irq = link->irq.possible[0];
}
/* /*
* Select the best IRQ. This is done in reverse to promote * Select the best IRQ. This is done in reverse to promote
...@@ -455,16 +467,20 @@ acpi_pci_link_check (void) ...@@ -455,16 +467,20 @@ acpi_pci_link_check (void)
irq = link->irq.possible[i]; irq = link->irq.possible[i];
} }
/* Enable the link device at this IRQ. */ /* Attempt to enable the link device at this IRQ. */
acpi_pci_link_set(link, 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; acpi_irq_penalty[link->irq.active] += 100;
printk(PREFIX "%s [%s] enabled at IRQ %d\n", printk(PREFIX "%s [%s] enabled at IRQ %d\n",
acpi_device_name(link->device), acpi_device_name(link->device),
acpi_device_bid(link->device), link->irq.active); acpi_device_bid(link->device), link->irq.active);
} }
link->irq.setonboot = 1;
return_VALUE(0); return_VALUE(0);
} }
...@@ -472,7 +488,9 @@ acpi_pci_link_check (void) ...@@ -472,7 +488,9 @@ acpi_pci_link_check (void)
int int
acpi_pci_link_get_irq ( acpi_pci_link_get_irq (
acpi_handle handle, acpi_handle handle,
int index) int index,
int* edge_level,
int* active_high_low)
{ {
int result = 0; int result = 0;
struct acpi_device *device = NULL; struct acpi_device *device = NULL;
...@@ -498,11 +516,17 @@ acpi_pci_link_get_irq ( ...@@ -498,11 +516,17 @@ acpi_pci_link_get_irq (
return_VALUE(0); return_VALUE(0);
} }
if (acpi_pci_link_allocate(link)) {
return -ENODEV;
}
if (!link->irq.active) { if (!link->irq.active) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n")); ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n"));
return_VALUE(0); 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); return_VALUE(link->irq.active);
} }
......
...@@ -60,7 +60,7 @@ void acpi_pci_get_translations (struct acpi_pci_id* id, u64* mem_tra, u64* io_tr ...@@ -60,7 +60,7 @@ void acpi_pci_get_translations (struct acpi_pci_id* id, u64* mem_tra, u64* io_tr
/* ACPI PCI Interrupt Link (pci_link.c) */ /* ACPI PCI Interrupt Link (pci_link.c) */
int acpi_pci_link_check (void); 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) */ /* ACPI PCI Interrupt Routing (pci_irq.c) */
......
...@@ -170,7 +170,7 @@ extern int skip_ioapic_setup; ...@@ -170,7 +170,7 @@ extern int skip_ioapic_setup;
extern int io_apic_get_unique_id (int ioapic, int apic_id); 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_version (int ioapic);
extern int io_apic_get_redir_entries (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*/ #endif /*CONFIG_ACPI_BOOT*/
#else /* !CONFIG_X86_IO_APIC */ #else /* !CONFIG_X86_IO_APIC */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment