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)
}
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;
......@@ -2350,19 +2350,23 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq)
entry.delivery_mode = INT_DELIVERY_MODE;
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;
entry.mask = 1;
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]);
......
......@@ -1065,7 +1065,7 @@ void __init mp_config_ioapic_for_sci(int irq)
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*/
......@@ -1080,6 +1080,8 @@ void __init mp_parse_prt (void)
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,12 +1092,16 @@ void __init mp_parse_prt (void)
/* 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;
}
else
else {
/* Hardwired IRQ. Assume PCI standard settings */
irq = entry->link.index;
edge_level = 1;
active_high_low = 1;
}
/* Don't set up the ACPI SCI because it's already set up */
if (acpi_fadt.sci_int == irq)
......@@ -1130,7 +1136,7 @@ void __init mp_parse_prt (void)
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",
......@@ -1139,8 +1145,6 @@ void __init mp_parse_prt (void)
mp_ioapic_routing[ioapic].apic_id, ioapic_pin,
entry->irq);
}
return;
}
#endif /*CONFIG_ACPI_PCI*/
......
......@@ -254,7 +254,7 @@ acpi_pci_irq_lookup (struct pci_bus *bus, int device, int pin)
}
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);
......@@ -398,7 +398,9 @@ acpi_pci_irq_init (void)
}
/* 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. */
......
......@@ -69,6 +69,9 @@ static struct acpi_driver acpi_pci_link_driver = {
struct acpi_pci_link_irq {
u8 active; /* Current IRQ */
u8 edge_level; /* All IRQs */
u8 active_high_low; /* All IRQs */
u8 setonboot;
u8 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
};
......@@ -118,6 +121,8 @@ acpi_pci_link_check_possible (
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 +141,8 @@ acpi_pci_link_check_possible (
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:
......@@ -264,7 +271,6 @@ acpi_pci_link_get_current (
* IRQ a boot-enabled Link device is set to is the correct one.
* (Required to support systems such as the Toshiba 5005-S504.)
*/
link->irq.active = irq;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active));
......@@ -294,6 +300,8 @@ acpi_pci_link_set (
if (!link || !irq)
return_VALUE(-EINVAL);
/* We don't check irqs the first time around */
if (link->irq.setonboot) {
/* See if we're already at the target IRQ. */
if (irq == link->irq.active)
return_VALUE(0);
......@@ -307,16 +315,18 @@ acpi_pci_link_set (
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ %d invalid\n", irq));
return_VALUE(-EINVAL);
}
}
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.shared_exclusive = ACPI_SHARED;
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.number_of_interrupts = 1;
resource.res.data.irq.interrupts[0] = irq;
}
......@@ -324,15 +334,15 @@ acpi_pci_link_set (
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.shared_exclusive = ACPI_SHARED;
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.number_of_interrupts = 1;
resource.res.data.extended_irq.interrupts[0] = irq;
/* ignore resource_source, it's optional */
}
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"));
......@@ -355,11 +365,13 @@ acpi_pci_link_set (
if (result) {
return_VALUE(result);
}
if (link->irq.active != irq) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Attempt to enable at IRQ %d resulted in IRQ %d\n",
irq, link->irq.active));
link->irq.active = 0;
acpi_ut_evaluate_object (link->handle, "_DIS", 0, NULL);
return_VALUE(-ENODEV);
}
......@@ -407,7 +419,7 @@ acpi_pci_link_check (void)
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) {
......@@ -428,23 +440,23 @@ acpi_pci_link_check (void)
}
}
/*
* Pass #2: Enable boot-disabled Links at 'best' IRQ.
*/
list_for_each(node, &acpi_link.entries) {
int irq = 0;
int i = 0;
return_VALUE(0);
}
link = list_entry(node, struct acpi_pci_link, node);
if (!link || !link->irq.possible_count) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
continue;
}
static int acpi_pci_link_allocate(struct acpi_pci_link* link) {
int irq;
int i;
if (link->irq.active)
continue;
ACPI_FUNCTION_TRACE("acpi_pci_link_allocate");
if (link->irq.setonboot)
return_VALUE(0);
if (link->irq.active) {
irq = link->irq.active;
} else {
irq = link->irq.possible[0];
}
/*
* Select the best IRQ. This is done in reverse to promote
......@@ -455,16 +467,20 @@ acpi_pci_link_check (void)
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);
}
link->irq.setonboot = 1;
return_VALUE(0);
}
......@@ -472,7 +488,9 @@ acpi_pci_link_check (void)
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;
......@@ -498,11 +516,17 @@ acpi_pci_link_get_irq (
return_VALUE(0);
}
if (acpi_pci_link_allocate(link)) {
return -ENODEV;
}
if (!link->irq.active) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n"));
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);
}
......
......@@ -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) */
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) */
......
......@@ -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_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 */
......
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