aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/pci_link.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/pci_link.c')
-rw-r--r--drivers/acpi/pci_link.c118
1 files changed, 100 insertions, 18 deletions
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index 520b28a..834c2ce 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -68,14 +68,19 @@ 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 {
u8 active; /* Current IRQ */
u8 edge_level; /* All IRQs */
u8 active_high_low; /* All IRQs */
- u8 initialized;
u8 resource_type;
u8 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
+ u8 initialized:1;
+ u8 reserved:7;
};
struct acpi_pci_link {
@@ -83,12 +88,14 @@ struct acpi_pci_link {
struct acpi_device *device;
acpi_handle handle;
struct acpi_pci_link_irq irq;
+ int refcnt;
};
static struct {
int count;
struct list_head entries;
} acpi_link;
+DECLARE_MUTEX(acpi_link_lock);
/* --------------------------------------------------------------------------
@@ -530,8 +537,12 @@ static int acpi_pci_link_allocate(
ACPI_FUNCTION_TRACE("acpi_pci_link_allocate");
- 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);
+ }
/*
* search for active IRQ in list of possible IRQs.
@@ -590,13 +601,13 @@ static int acpi_pci_link_allocate(
}
/*
- * acpi_pci_link_get_irq
+ * acpi_pci_link_allocate_irq
* success: return IRQ >= 0
* failure: return -1
*/
int
-acpi_pci_link_get_irq (
+acpi_pci_link_allocate_irq (
acpi_handle handle,
int index,
int *edge_level,
@@ -607,7 +618,7 @@ acpi_pci_link_get_irq (
struct acpi_device *device = 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);
if (result) {
@@ -627,21 +638,81 @@ acpi_pci_link_get_irq (
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);
+ }
if (!link->irq.active) {
+ up(&acpi_link_lock);
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n"));
return_VALUE(-1);
}
+ link->refcnt ++;
+ up(&acpi_link_lock);
if (edge_level) *edge_level = link->irq.edge_level;
if (active_high_low) *active_high_low = link->irq.active_high_low;
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);
}
+/*
+ * 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);
+ }
+#ifdef FUTURE_USE
+ /*
+ * The Link reference count allows us to _DISable an unused link
+ * and suspend time, and set it again on resume.
+ * However, 2.6.12 still has irq_router.resume
+ * which blindly restores the link state.
+ * So we disable the reference count method
+ * to prevent duplicate acpi_pci_link_set()
+ * which would harm some systems
+ */
+ link->refcnt --;
+#endif
+ 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
-------------------------------------------------------------------------- */
@@ -671,6 +742,7 @@ acpi_pci_link_add (
strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
acpi_driver_data(device) = link;
+ down(&acpi_link_lock);
result = acpi_pci_link_get_possible(link);
if (result)
goto end;
@@ -706,6 +778,7 @@ acpi_pci_link_add (
end:
/* disable all links -- to be activated on use */
acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL);
+ up(&acpi_link_lock);
if (result)
kfree(link);
@@ -713,20 +786,23 @@ end:
return_VALUE(result);
}
-
static int
-acpi_pci_link_resume (
- struct acpi_pci_link *link)
+acpi_pci_link_resume(
+ struct acpi_pci_link *link)
{
ACPI_FUNCTION_TRACE("acpi_pci_link_resume");
-
- if (link->irq.active && link->irq.initialized)
+
+ if (link->refcnt && link->irq.active && link->irq.initialized)
return_VALUE(acpi_pci_link_set(link, link->irq.active));
else
return_VALUE(0);
}
-
+/*
+ * FIXME: this is a workaround to avoid nasty warning. It will be removed
+ * after every device calls pci_disable_device in .resume.
+ */
+int acpi_in_resume;
static int
irqrouter_resume(
struct sys_device *dev)
@@ -736,16 +812,17 @@ irqrouter_resume(
ACPI_FUNCTION_TRACE("irqrouter_resume");
+ acpi_in_resume = 1;
list_for_each(node, &acpi_link.entries) {
-
link = list_entry(node, struct acpi_pci_link, node);
if (!link) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Invalid link context\n"));
continue;
}
-
acpi_pci_link_resume(link);
}
+ acpi_in_resume = 0;
return_VALUE(0);
}
@@ -764,8 +841,9 @@ acpi_pci_link_remove (
link = (struct acpi_pci_link *) acpi_driver_data(device);
- /* TBD: Acquire/release lock */
+ down(&acpi_link_lock);
list_del(&link->node);
+ up(&acpi_link_lock);
kfree(link);
@@ -812,9 +890,12 @@ static int __init acpi_irq_penalty_update(char *str, int used)
* There is no ISA_POSSIBLE weight, so we simply use
* the (small) PCI_USING penalty.
*/
-void acpi_penalize_isa_irq(int irq)
+void acpi_penalize_isa_irq(int irq, int active)
{
- acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING;
+ if (active)
+ acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED;
+ else
+ acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING;
}
/*
@@ -854,6 +935,7 @@ int __init acpi_irq_balance_set(char *str)
__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 = {
set_kset_name("irqrouter"),
.resume = irqrouter_resume,