vserver 1.9.5.x5
[linux-2.6.git] / drivers / acpi / pci_irq.c
index 23e9945..a475019 100644 (file)
@@ -43,7 +43,7 @@
 ACPI_MODULE_NAME               ("pci_irq")
 
 struct acpi_prt_list           acpi_prt;
-
+DEFINE_SPINLOCK(acpi_prt_lock);
 
 /* --------------------------------------------------------------------------
                          PCI IRQ Routing Table (PRT) Support
@@ -68,18 +68,20 @@ acpi_pci_irq_find_prt_entry (
         * 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);
 }
 
@@ -141,14 +143,29 @@ acpi_pci_irq_add_entry (
                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,
@@ -156,7 +173,7 @@ acpi_pci_irq_add_prt (
        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;
@@ -164,6 +181,11 @@ acpi_pci_irq_add_prt (
 
        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);
@@ -175,7 +197,7 @@ acpi_pci_irq_add_prt (
         *       (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);
 
@@ -188,6 +210,7 @@ acpi_pci_irq_add_prt (
 
        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",
@@ -196,8 +219,9 @@ acpi_pci_irq_add_prt (
        }
 
        prt = kmalloc(buffer.length, GFP_KERNEL);
-       if (!prt)
+       if (!prt){
                return_VALUE(-ENOMEM);
+       }
        memset(prt, 0, buffer.length);
        buffer.pointer = prt;
 
@@ -222,11 +246,35 @@ acpi_pci_irq_add_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,
@@ -249,14 +297,14 @@ acpi_pci_irq_lookup (
        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;
@@ -269,6 +317,11 @@ acpi_pci_irq_lookup (
        return_VALUE(irq);
 }
 
+/*
+ * acpi_pci_irq_derive
+ * success: return IRQ >= 0
+ * failure: return < 0
+ */
 static int
 acpi_pci_irq_derive (
        struct pci_dev          *dev,
@@ -277,7 +330,7 @@ acpi_pci_irq_derive (
        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");
@@ -289,7 +342,7 @@ 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;
 
@@ -299,7 +352,7 @@ acpi_pci_irq_derive (
                        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 --;
@@ -310,9 +363,9 @@ acpi_pci_irq_derive (
                        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",
@@ -321,6 +374,11 @@ acpi_pci_irq_derive (
        return_VALUE(irq);
 }
 
+/*
+ * acpi_pci_irq_enable
+ * success: return 0
+ * failure: return < 0
+ */
 
 int
 acpi_pci_irq_enable (
@@ -330,6 +388,7 @@ 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");
 
@@ -358,20 +417,20 @@ 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");
@@ -379,6 +438,9 @@ acpi_pci_irq_enable (
                }
        }
 
+       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 "
@@ -388,5 +450,59 @@ acpi_pci_irq_enable (
                (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 */