|
@@ -68,6 +68,10 @@ static struct acpi_driver acpi_pci_link_driver = {
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * If a link is initialized, we never change its active and initialized
|
|
|
|
+ * later even the link is disable. Instead, we just repick the active irq
|
|
|
|
+ */
|
|
struct acpi_pci_link_irq {
|
|
struct acpi_pci_link_irq {
|
|
u8 active; /* Current IRQ */
|
|
u8 active; /* Current IRQ */
|
|
u8 edge_level; /* All IRQs */
|
|
u8 edge_level; /* All IRQs */
|
|
@@ -76,8 +80,7 @@ struct acpi_pci_link_irq {
|
|
u8 possible_count;
|
|
u8 possible_count;
|
|
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
|
|
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
|
|
u8 initialized:1;
|
|
u8 initialized:1;
|
|
- u8 suspend_resume:1;
|
|
|
|
- u8 reserved:6;
|
|
|
|
|
|
+ u8 reserved:7;
|
|
};
|
|
};
|
|
|
|
|
|
struct acpi_pci_link {
|
|
struct acpi_pci_link {
|
|
@@ -85,12 +88,14 @@ struct acpi_pci_link {
|
|
struct acpi_device *device;
|
|
struct acpi_device *device;
|
|
acpi_handle handle;
|
|
acpi_handle handle;
|
|
struct acpi_pci_link_irq irq;
|
|
struct acpi_pci_link_irq irq;
|
|
|
|
+ int refcnt;
|
|
};
|
|
};
|
|
|
|
|
|
static struct {
|
|
static struct {
|
|
int count;
|
|
int count;
|
|
struct list_head entries;
|
|
struct list_head entries;
|
|
} acpi_link;
|
|
} acpi_link;
|
|
|
|
+DECLARE_MUTEX(acpi_link_lock);
|
|
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------
|
|
/* --------------------------------------------------------------------------
|
|
@@ -532,12 +537,12 @@ static int acpi_pci_link_allocate(
|
|
|
|
|
|
ACPI_FUNCTION_TRACE("acpi_pci_link_allocate");
|
|
ACPI_FUNCTION_TRACE("acpi_pci_link_allocate");
|
|
|
|
|
|
- if (link->irq.suspend_resume) {
|
|
|
|
- acpi_pci_link_set(link, link->irq.active);
|
|
|
|
- link->irq.suspend_resume = 0;
|
|
|
|
- }
|
|
|
|
- if (link->irq.initialized)
|
|
|
|
|
|
+ if (link->irq.initialized) {
|
|
|
|
+ if (link->refcnt == 0)
|
|
|
|
+ /* This means the link is disabled but initialized */
|
|
|
|
+ acpi_pci_link_set(link, link->irq.active);
|
|
return_VALUE(0);
|
|
return_VALUE(0);
|
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
/*
|
|
* search for active IRQ in list of possible IRQs.
|
|
* search for active IRQ in list of possible IRQs.
|
|
@@ -596,13 +601,13 @@ static int acpi_pci_link_allocate(
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * acpi_pci_link_get_irq
|
|
|
|
|
|
+ * acpi_pci_link_allocate_irq
|
|
* success: return IRQ >= 0
|
|
* success: return IRQ >= 0
|
|
* failure: return -1
|
|
* failure: return -1
|
|
*/
|
|
*/
|
|
|
|
|
|
int
|
|
int
|
|
-acpi_pci_link_get_irq (
|
|
|
|
|
|
+acpi_pci_link_allocate_irq (
|
|
acpi_handle handle,
|
|
acpi_handle handle,
|
|
int index,
|
|
int index,
|
|
int *edge_level,
|
|
int *edge_level,
|
|
@@ -613,7 +618,7 @@ acpi_pci_link_get_irq (
|
|
struct acpi_device *device = NULL;
|
|
struct acpi_device *device = NULL;
|
|
struct acpi_pci_link *link = NULL;
|
|
struct acpi_pci_link *link = NULL;
|
|
|
|
|
|
- ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq");
|
|
|
|
|
|
+ ACPI_FUNCTION_TRACE("acpi_pci_link_allocate_irq");
|
|
|
|
|
|
result = acpi_bus_get_device(handle, &device);
|
|
result = acpi_bus_get_device(handle, &device);
|
|
if (result) {
|
|
if (result) {
|
|
@@ -633,21 +638,70 @@ acpi_pci_link_get_irq (
|
|
return_VALUE(-1);
|
|
return_VALUE(-1);
|
|
}
|
|
}
|
|
|
|
|
|
- if (acpi_pci_link_allocate(link))
|
|
|
|
|
|
+ down(&acpi_link_lock);
|
|
|
|
+ if (acpi_pci_link_allocate(link)) {
|
|
|
|
+ up(&acpi_link_lock);
|
|
return_VALUE(-1);
|
|
return_VALUE(-1);
|
|
|
|
+ }
|
|
|
|
|
|
if (!link->irq.active) {
|
|
if (!link->irq.active) {
|
|
|
|
+ up(&acpi_link_lock);
|
|
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n"));
|
|
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n"));
|
|
return_VALUE(-1);
|
|
return_VALUE(-1);
|
|
}
|
|
}
|
|
|
|
+ link->refcnt ++;
|
|
|
|
+ up(&acpi_link_lock);
|
|
|
|
|
|
if (edge_level) *edge_level = link->irq.edge_level;
|
|
if (edge_level) *edge_level = link->irq.edge_level;
|
|
if (active_high_low) *active_high_low = link->irq.active_high_low;
|
|
if (active_high_low) *active_high_low = link->irq.active_high_low;
|
|
if (name) *name = acpi_device_bid(link->device);
|
|
if (name) *name = acpi_device_bid(link->device);
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
|
|
+ "Link %s is referenced\n", acpi_device_bid(link->device)));
|
|
return_VALUE(link->irq.active);
|
|
return_VALUE(link->irq.active);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * We don't change link's irq information here. After it is reenabled, we
|
|
|
|
+ * continue use the info
|
|
|
|
+ */
|
|
|
|
+int
|
|
|
|
+acpi_pci_link_free_irq(acpi_handle handle)
|
|
|
|
+{
|
|
|
|
+ struct acpi_device *device = NULL;
|
|
|
|
+ struct acpi_pci_link *link = NULL;
|
|
|
|
+ acpi_status result;
|
|
|
|
+
|
|
|
|
+ ACPI_FUNCTION_TRACE("acpi_pci_link_free_irq");
|
|
|
|
+
|
|
|
|
+ result = acpi_bus_get_device(handle, &device);
|
|
|
|
+ if (result) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n"));
|
|
|
|
+ return_VALUE(-1);
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ link = (struct acpi_pci_link *) acpi_driver_data(device);
|
|
|
|
+ if (!link) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
|
|
|
|
+ return_VALUE(-1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ down(&acpi_link_lock);
|
|
|
|
+ if (!link->irq.initialized) {
|
|
|
|
+ up(&acpi_link_lock);
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link isn't initialized\n"));
|
|
|
|
+ return_VALUE(-1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ link->refcnt --;
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
|
|
+ "Link %s is dereferenced\n", acpi_device_bid(link->device)));
|
|
|
|
+
|
|
|
|
+ if (link->refcnt == 0) {
|
|
|
|
+ acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL);
|
|
|
|
+ }
|
|
|
|
+ up(&acpi_link_lock);
|
|
|
|
+ return_VALUE(link->irq.active);
|
|
|
|
+}
|
|
/* --------------------------------------------------------------------------
|
|
/* --------------------------------------------------------------------------
|
|
Driver Interface
|
|
Driver Interface
|
|
-------------------------------------------------------------------------- */
|
|
-------------------------------------------------------------------------- */
|
|
@@ -677,6 +731,7 @@ acpi_pci_link_add (
|
|
strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
|
|
strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
|
|
acpi_driver_data(device) = link;
|
|
acpi_driver_data(device) = link;
|
|
|
|
|
|
|
|
+ down(&acpi_link_lock);
|
|
result = acpi_pci_link_get_possible(link);
|
|
result = acpi_pci_link_get_possible(link);
|
|
if (result)
|
|
if (result)
|
|
goto end;
|
|
goto end;
|
|
@@ -712,6 +767,7 @@ acpi_pci_link_add (
|
|
end:
|
|
end:
|
|
/* disable all links -- to be activated on use */
|
|
/* disable all links -- to be activated on use */
|
|
acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL);
|
|
acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL);
|
|
|
|
+ up(&acpi_link_lock);
|
|
|
|
|
|
if (result)
|
|
if (result)
|
|
kfree(link);
|
|
kfree(link);
|
|
@@ -726,19 +782,32 @@ irqrouter_suspend(
|
|
{
|
|
{
|
|
struct list_head *node = NULL;
|
|
struct list_head *node = NULL;
|
|
struct acpi_pci_link *link = NULL;
|
|
struct acpi_pci_link *link = NULL;
|
|
|
|
+ int ret = 0;
|
|
|
|
|
|
ACPI_FUNCTION_TRACE("irqrouter_suspend");
|
|
ACPI_FUNCTION_TRACE("irqrouter_suspend");
|
|
|
|
|
|
list_for_each(node, &acpi_link.entries) {
|
|
list_for_each(node, &acpi_link.entries) {
|
|
link = list_entry(node, struct acpi_pci_link, node);
|
|
link = list_entry(node, struct acpi_pci_link, node);
|
|
if (!link) {
|
|
if (!link) {
|
|
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
|
|
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
|
|
|
+ "Invalid link context\n"));
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
- if (link->irq.active && link->irq.initialized)
|
|
|
|
- link->irq.suspend_resume = 1;
|
|
|
|
|
|
+ if (link->irq.initialized && link->refcnt != 0
|
|
|
|
+ /* We ignore legacy IDE device irq */
|
|
|
|
+ && link->irq.active != 14 && link->irq.active !=15) {
|
|
|
|
+ printk(KERN_WARNING PREFIX
|
|
|
|
+ "%d drivers with interrupt %d neglected to call"
|
|
|
|
+ " pci_disable_device at .suspend\n",
|
|
|
|
+ link->refcnt,
|
|
|
|
+ link->irq.active);
|
|
|
|
+ printk(KERN_WARNING PREFIX
|
|
|
|
+ "Fix the driver, or rmmod before suspend\n");
|
|
|
|
+ link->refcnt = 0;
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- return_VALUE(0);
|
|
|
|
|
|
+ return_VALUE(ret);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -756,8 +825,9 @@ acpi_pci_link_remove (
|
|
|
|
|
|
link = (struct acpi_pci_link *) acpi_driver_data(device);
|
|
link = (struct acpi_pci_link *) acpi_driver_data(device);
|
|
|
|
|
|
- /* TBD: Acquire/release lock */
|
|
|
|
|
|
+ down(&acpi_link_lock);
|
|
list_del(&link->node);
|
|
list_del(&link->node);
|
|
|
|
+ up(&acpi_link_lock);
|
|
|
|
|
|
kfree(link);
|
|
kfree(link);
|
|
|
|
|
|
@@ -849,6 +919,7 @@ int __init acpi_irq_balance_set(char *str)
|
|
__setup("acpi_irq_balance", acpi_irq_balance_set);
|
|
__setup("acpi_irq_balance", acpi_irq_balance_set);
|
|
|
|
|
|
|
|
|
|
|
|
+/* FIXME: we will remove this interface after all drivers call pci_disable_device */
|
|
static struct sysdev_class irqrouter_sysdev_class = {
|
|
static struct sysdev_class irqrouter_sysdev_class = {
|
|
set_kset_name("irqrouter"),
|
|
set_kset_name("irqrouter"),
|
|
.suspend = irqrouter_suspend,
|
|
.suspend = irqrouter_suspend,
|