ACPI_MODULE_NAME ("pci_irq")
struct acpi_prt_list acpi_prt;
-
+DEFINE_SPINLOCK(acpi_prt_lock);
/* --------------------------------------------------------------------------
PCI IRQ Routing Table (PRT) Support
* Parse through all PRT entries looking for a match on the specified
* PCI device's segment, bus, device, and pin (don't care about func).
*
- * TBD: Acquire/release lock
*/
+ spin_lock(&acpi_prt_lock);
list_for_each(node, &acpi_prt.entries) {
entry = list_entry(node, struct acpi_prt_entry, node);
if ((segment == entry->id.segment)
&& (bus == entry->id.bus)
&& (device == entry->id.device)
&& (pin == entry->pin)) {
+ spin_unlock(&acpi_prt_lock);
return_PTR(entry);
}
}
+ spin_unlock(&acpi_prt_lock);
return_PTR(NULL);
}
entry->id.segment, entry->id.bus, entry->id.device,
('A' + entry->pin), prt->source, entry->link.index));
- /* TBD: Acquire/release lock */
+ spin_lock(&acpi_prt_lock);
list_add_tail(&entry->node, &acpi_prt.entries);
acpi_prt.count++;
+ spin_unlock(&acpi_prt_lock);
return_VALUE(0);
}
+static void
+acpi_pci_irq_del_entry (
+ int segment,
+ int bus,
+ struct acpi_prt_entry *entry)
+{
+ if (segment == entry->id.segment && bus == entry->id.bus){
+ acpi_prt.count--;
+ list_del(&entry->node);
+ kfree(entry);
+ }
+}
+
+
int
acpi_pci_irq_add_prt (
acpi_handle handle,
int bus)
{
acpi_status status = AE_OK;
- char pathname[ACPI_PATHNAME_MAX] = {0};
+ char *pathname = NULL;
struct acpi_buffer buffer = {0, NULL};
struct acpi_pci_routing_table *prt = NULL;
struct acpi_pci_routing_table *entry = NULL;
ACPI_FUNCTION_TRACE("acpi_pci_irq_add_prt");
+ pathname = (char *) kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL);
+ if(!pathname)
+ return_VALUE(-ENOMEM);
+ memset(pathname, 0, ACPI_PATHNAME_MAX);
+
if (first_time) {
acpi_prt.count = 0;
INIT_LIST_HEAD(&acpi_prt.entries);
* (either a PCI root bridge or PCI-PCI bridge).
*/
- buffer.length = sizeof(pathname);
+ buffer.length = ACPI_PATHNAME_MAX;
buffer.pointer = pathname;
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
buffer.length = 0;
buffer.pointer = NULL;
+ kfree(pathname);
status = acpi_get_irq_routing_table(handle, &buffer);
if (status != AE_BUFFER_OVERFLOW) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n",
}
prt = kmalloc(buffer.length, GFP_KERNEL);
- if (!prt)
+ if (!prt){
return_VALUE(-ENOMEM);
+ }
memset(prt, 0, buffer.length);
buffer.pointer = prt;
return_VALUE(0);
}
+void
+acpi_pci_irq_del_prt (int segment, int bus)
+{
+ struct list_head *node = NULL, *n = NULL;
+ struct acpi_prt_entry *entry = NULL;
+
+ if (!acpi_prt.count) {
+ return;
+ }
+
+ printk(KERN_DEBUG "ACPI: Delete PCI Interrupt Routing Table for %x:%x\n",
+ segment, bus);
+ spin_lock(&acpi_prt_lock);
+ list_for_each_safe(node, n, &acpi_prt.entries) {
+ entry = list_entry(node, struct acpi_prt_entry, node);
+ acpi_pci_irq_del_entry(segment, bus, entry);
+ }
+ spin_unlock(&acpi_prt_lock);
+}
/* --------------------------------------------------------------------------
PCI Interrupt Routing Support
-------------------------------------------------------------------------- */
+/*
+ * acpi_pci_irq_lookup
+ * success: return IRQ >= 0
+ * failure: return -1
+ */
static int
acpi_pci_irq_lookup (
struct pci_bus *bus,
entry = acpi_pci_irq_find_prt_entry(segment, bus_nr, device, pin);
if (!entry) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PRT entry not found\n"));
- return_VALUE(0);
+ return_VALUE(-1);
}
if (entry->link.handle) {
irq = acpi_pci_link_get_irq(entry->link.handle, entry->link.index, edge_level, active_high_low);
- if (!irq) {
+ if (irq < 0) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n"));
- return_VALUE(0);
+ return_VALUE(-1);
}
} else {
irq = entry->link.index;
return_VALUE(irq);
}
+/*
+ * acpi_pci_irq_derive
+ * success: return IRQ >= 0
+ * failure: return < 0
+ */
static int
acpi_pci_irq_derive (
struct pci_dev *dev,
int *active_high_low)
{
struct pci_dev *bridge = dev;
- int irq = 0;
+ int irq = -1;
u8 bridge_pin = 0;
ACPI_FUNCTION_TRACE("acpi_pci_irq_derive");
* Attempt to derive an IRQ for this device from a parent bridge's
* PCI interrupt routing entry (eg. yenta bridge and add-in card bridge).
*/
- while (!irq && bridge->bus->self) {
+ while (irq < 0 && bridge->bus->self) {
pin = (pin + PCI_SLOT(bridge->devfn)) % 4;
bridge = bridge->bus->self;
if (!bridge_pin) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"No interrupt pin configured for device %s\n", pci_name(bridge)));
- return_VALUE(0);
+ return_VALUE(-1);
}
/* Pin is from 0 to 3 */
bridge_pin --;
pin, edge_level, active_high_low);
}
- if (!irq) {
+ if (irq < 0) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to derive IRQ for device %s\n", pci_name(dev)));
- return_VALUE(0);
+ return_VALUE(-1);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derive IRQ %d for device %s from %s\n",
return_VALUE(irq);
}
+/*
+ * acpi_pci_irq_enable
+ * success: return 0
+ * failure: return < 0
+ */
int
acpi_pci_irq_enable (
u8 pin = 0;
int edge_level = ACPI_LEVEL_SENSITIVE;
int active_high_low = ACPI_ACTIVE_LOW;
+ extern int via_interrupt_line_quirk;
ACPI_FUNCTION_TRACE("acpi_pci_irq_enable");
* If no PRT entry was found, we'll try to derive an IRQ from the
* device's parent bridge.
*/
- if (!irq)
+ if (irq < 0)
irq = acpi_pci_irq_derive(dev, pin, &edge_level, &active_high_low);
/*
* No IRQ known to the ACPI subsystem - maybe the BIOS /
* driver reported one, then use it. Exit in any case.
*/
- if (!irq) {
+ if (irq < 0) {
printk(KERN_WARNING PREFIX "PCI interrupt %s[%c]: no GSI",
pci_name(dev), ('A' + pin));
/* Interrupt Line values above 0xF are forbidden */
- if (dev->irq && (dev->irq <= 0xF)) {
+ if (dev->irq >= 0 && (dev->irq <= 0xF)) {
printk(" - using IRQ %d\n", dev->irq);
- return_VALUE(dev->irq);
+ return_VALUE(0);
}
else {
printk("\n");
}
}
+ if (via_interrupt_line_quirk)
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq & 15);
+
dev->irq = acpi_register_gsi(irq, edge_level, active_high_low);
printk(KERN_INFO PREFIX "PCI interrupt %s[%c] -> GSI %u "
(active_high_low == ACPI_ACTIVE_LOW) ? "low" : "high",
dev->irq);
- return_VALUE(dev->irq);
+ return_VALUE(0);
+}
+EXPORT_SYMBOL(acpi_pci_irq_enable);
+
+
+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void
+acpi_pci_irq_disable (
+ struct pci_dev *dev)
+{
+ int gsi = 0;
+ u8 pin = 0;
+ int edge_level = ACPI_LEVEL_SENSITIVE;
+ int active_high_low = ACPI_ACTIVE_LOW;
+
+ ACPI_FUNCTION_TRACE("acpi_pci_irq_disable");
+
+ if (!dev)
+ return_VOID;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if (!pin)
+ return_VOID;
+ pin--;
+
+ if (!dev->bus)
+ return_VOID;
+
+ /*
+ * First we check the PCI IRQ routing table (PRT) for an IRQ.
+ */
+ gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin,
+ &edge_level, &active_high_low);
+ /*
+ * If no PRT entry was found, we'll try to derive an IRQ from the
+ * device's parent bridge.
+ */
+ if (gsi < 0)
+ gsi = acpi_pci_irq_derive(dev, pin,
+ &edge_level, &active_high_low);
+ if (gsi < 0)
+ return_VOID;
+
+ /*
+ * TBD: It might be worth clearing dev->irq by magic constant
+ * (e.g. PCI_UNDEFINED_IRQ).
+ */
+
+ printk(KERN_INFO PREFIX "PCI interrupt for device %s disabled\n",
+ pci_name(dev));
+
+ acpi_unregister_gsi(gsi);
+
+ return_VOID;
}
+#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */