},
};
+/*
+ * 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 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
u8 initialized:1;
- u8 suspend_resume:1;
- u8 reserved:6;
+ u8 reserved:7;
};
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);
/* --------------------------------------------------------------------------
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);
+ }
/*
* search for active IRQ in list of possible IRQs.
}
/*
- * 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,
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) {
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
-------------------------------------------------------------------------- */
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;
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);
}
static int
-irqrouter_suspend(
- struct sys_device *dev,
- u32 state)
+acpi_pci_link_resume(
+ struct acpi_pci_link *link)
+{
+ ACPI_FUNCTION_TRACE("acpi_pci_link_resume");
+
+ 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)
{
struct list_head *node = NULL;
struct acpi_pci_link *link = NULL;
- ACPI_FUNCTION_TRACE("irqrouter_suspend");
+ 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;
}
- if (link->irq.active && link->irq.initialized)
- link->irq.suspend_resume = 1;
+ acpi_pci_link_resume(link);
}
+ acpi_in_resume = 0;
return_VALUE(0);
}
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);
__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"),
- .suspend = irqrouter_suspend,
+ .resume = irqrouter_resume,
};