[PATCH] Teach OSL to handle multiple interrupts [1/2]

From: Matthew Wilcox <matthew_at_wil.cx>
Date: 2004-11-08 01:47:54
OSL assumed that there would only be one ACPI IRQ.  In the presence of
GPE blocks, there are multiple ACPI IRQs.

Index: linux-2.6/drivers/acpi/osl.c
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/acpi/osl.c,v
retrieving revision 1.13
diff -u -p -r1.13 osl.c
--- linux-2.6/drivers/acpi/osl.c	11 Oct 2004 21:41:01 -0000	1.13
+++ linux-2.6/drivers/acpi/osl.c	7 Nov 2004 14:34:33 -0000
@@ -66,9 +66,6 @@ int acpi_in_debugger;
 extern char line_buf[80];
 #endif /*ENABLE_DEBUGGER*/
 
-static unsigned int acpi_irq_irq;
-static acpi_osd_handler acpi_irq_handler;
-static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
 
 acpi_status
@@ -96,13 +93,12 @@ acpi_os_initialize1(void)
 	return AE_OK;
 }
 
+static void acpi_os_remove_interrupt_handlers(void);
+
 acpi_status
 acpi_os_terminate(void)
 {
-	if (acpi_irq_handler) {
-		acpi_os_remove_interrupt_handler(acpi_irq_irq,
-						 acpi_irq_handler);
-	}
+	acpi_os_remove_interrupt_handlers();
 
 	destroy_workqueue(kacpid_wq);
 
@@ -255,52 +251,129 @@ acpi_os_table_override (struct acpi_tabl
 	return AE_OK;
 }
 
+struct acpi_dev_id {
+	acpi_osd_handler handler;
+	void *context;
+	int irq;
+	struct acpi_dev_id *next;
+};
+
 static irqreturn_t
 acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
-	return (*acpi_irq_handler)(acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE;
+	struct acpi_dev_id *irq_ctx = dev_id;
+	acpi_osd_handler handler = irq_ctx->handler;
+	void *context = irq_ctx->context;
+	return (*handler)(context) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+struct acpi_dev_id *dev_id_list;
+
+static void *alloc_dev_id(acpi_osd_handler handler, void *context, int irq)
+{
+	struct acpi_dev_id *irq_ctx = kmalloc(sizeof(*irq_ctx), GFP_KERNEL);
+	irq_ctx->handler = handler;
+	irq_ctx->context = context;
+	irq_ctx->irq = irq;
+	irq_ctx->next = dev_id_list;
+	dev_id_list = irq_ctx;
+	return irq_ctx;
+}
+
+/*
+ * XXX: Ideally, we would match the context too, but that information
+ * isn't passed to the acpi_os_remove_interrupt_handler() function.
+ */
+static void *find_dev_id(int irq, acpi_osd_handler handler)
+{
+	struct acpi_dev_id *irq_ctx = dev_id_list;
+	for (irq_ctx = dev_id_list; irq_ctx; irq_ctx = irq_ctx->next) {
+		if ((irq_ctx->handler == handler) && (irq_ctx->irq == irq))
+			return irq_ctx;
+	}
+	printk("find_dev_id: handler %p for irq %d not found\n", handler, irq);
+	return NULL;
+}
+
+static void free_dev_id(struct acpi_dev_id *dev_id)
+{
+	struct acpi_dev_id **pprev = &dev_id_list;
+	for (;;) {
+		struct acpi_dev_id *irq_ctx = *pprev;
+		if (!irq_ctx)
+			break;
+		if (irq_ctx != dev_id) {
+			pprev = &irq_ctx->next;
+			continue;
+		}
+
+		*pprev = irq_ctx->next;
+		kfree(irq_ctx);
+		return;
+	}
+	printk("free_dev_id: dev_id %p not found\n", dev_id);
 }
 
 acpi_status
 acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, void *context)
 {
 	unsigned int irq;
+	void *dev_id;
 
 	/*
-	 * Ignore the GSI from the core, and use the value in our copy of the
-	 * FADT. It may not be the same if an interrupt source override exists
-	 * for the SCI.
+	 * If this is the FADT interrupt, ignore the GSI from the core and
+	 * use the value in our copy of the FADT instead.  It may not be the
+	 * same if an interrupt source override exists for the SCI.
 	 */
-	gsi = acpi_fadt.sci_int;
+	if (gsi == acpi_gbl_FADT->sci_int)
+		gsi = acpi_fadt.sci_int;
+
 	if (acpi_gsi_to_irq(gsi, &irq) < 0) {
 		printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n",
 		       gsi);
 		return AE_OK;
 	}
 
-	acpi_irq_handler = handler;
-	acpi_irq_context = context;
-	if (request_irq(irq, acpi_irq, SA_SHIRQ, "acpi", acpi_irq)) {
+	dev_id = alloc_dev_id(handler, context, irq);
+	if (request_irq(irq, acpi_irq, SA_SHIRQ, "acpi", dev_id)) {
 		printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
+		free_dev_id(dev_id);
 		return AE_NOT_ACQUIRED;
 	}
-	acpi_irq_irq = irq;
 
 	return AE_OK;
 }
 
 acpi_status
-acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
+acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler handler)
 {
-	if (irq) {
-		free_irq(irq, acpi_irq);
-		acpi_irq_handler = NULL;
-		acpi_irq_irq = 0;
+	int irq;
+	void *dev_id;
+
+	if (gsi == acpi_gbl_FADT->sci_int)
+		gsi = acpi_fadt.sci_int;
+
+	if (acpi_gsi_to_irq(gsi, &irq) < 0) {
+		printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n",
+		       gsi);
+		return AE_OK;
 	}
 
+	dev_id = find_dev_id(irq, handler);
+	free_irq(irq, dev_id);
+	free_dev_id(dev_id);
+
 	return AE_OK;
 }
 
+static void acpi_os_remove_interrupt_handlers(void)
+{
+	while (dev_id_list) {
+		acpi_os_remove_interrupt_handler(dev_id_list->irq,
+				dev_id_list->handler);
+	}
+}
+
 /*
  * Running in interpreter thread context, safe to sleep
  */

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain
-
To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Received on Sun Nov 7 09:50:09 2004

This archive was generated by hypermail 2.1.8 : 2005-08-02 09:20:32 EST