[RFT/PATCH 05/10] cbus: retu: move to threaded IRQ and GENIRQ

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Start moving retu to threaded IRQ and while
at that also give retu an irq_chip so children
can use generic request_threaded_irq() calls.

Signed-off-by: Felipe Balbi <balbi@xxxxxx>
---
 drivers/cbus/Makefile |   10 +-
 drivers/cbus/retu.c   |  264 +++++++++++++++++++++----------------------------
 drivers/cbus/retu.h   |    5 -
 3 files changed, 117 insertions(+), 162 deletions(-)

diff --git a/drivers/cbus/Makefile b/drivers/cbus/Makefile
index 0ad112f..3375b82 100644
--- a/drivers/cbus/Makefile
+++ b/drivers/cbus/Makefile
@@ -6,8 +6,10 @@ obj-$(CONFIG_CBUS)		+= cbus.o
 obj-$(CONFIG_CBUS_TAHVO)	+= tahvo.o
 obj-$(CONFIG_CBUS_RETU)		+= retu.o
 obj-$(CONFIG_CBUS_TAHVO_USB)	+= tahvo-usb.o
-obj-$(CONFIG_CBUS_RETU_POWERBUTTON) += retu-pwrbutton.o
-obj-$(CONFIG_CBUS_RETU_RTC)	+= retu-rtc.o
-obj-$(CONFIG_CBUS_RETU_WDT)	+= retu-wdt.o
 obj-$(CONFIG_CBUS_TAHVO_USER)	+= tahvo-user.o
-obj-$(CONFIG_CBUS_RETU_HEADSET)	+= retu-headset.o
+
+## Disable Retu children until converted to threaded IRQ
+#obj-$(CONFIG_CBUS_RETU_POWERBUTTON) += retu-pwrbutton.o
+#obj-$(CONFIG_CBUS_RETU_RTC)	+= retu-rtc.o
+#obj-$(CONFIG_CBUS_RETU_WDT)	+= retu-wdt.o
+#obj-$(CONFIG_CBUS_RETU_HEADSET)	+= retu-headset.o
diff --git a/drivers/cbus/retu.c b/drivers/cbus/retu.c
index 7e67e1a..b67e918 100644
--- a/drivers/cbus/retu.c
+++ b/drivers/cbus/retu.c
@@ -33,6 +33,7 @@
 #include <linux/miscdevice.h>
 #include <linux/poll.h>
 #include <linux/fs.h>
+#include <linux/mutex.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
@@ -53,23 +54,23 @@
 struct retu {
 	/* Device lock */
 	spinlock_t		lock;
-	struct tasklet_struct	tasklet;
+	struct mutex		irq_lock;
 	struct device		*dev;
 
+	int			irq_base;
+	int			nirqs;
 	int			irq;
 
-	bool			is_vilma;
-};
+	int			ack;
+	bool			ack_pending;
 
-static struct retu *the_retu;
+	int			mask;
+	bool			mask_pending;
 
-struct retu_irq_handler_desc {
-	int (*func)(unsigned long);
-	unsigned long arg;
-	char name[8];
+	bool			is_vilma;
 };
 
-static struct retu_irq_handler_desc retu_irq_handlers[MAX_RETU_IRQ_HANDLERS];
+static struct retu *the_retu;
 
 int retu_get_status(void)
 {
@@ -156,180 +157,135 @@ int retu_read_adc(int channel)
 }
 EXPORT_SYMBOL(retu_read_adc);
 
-static u16 retu_disable_bogus_irqs(u16 mask)
+static irqreturn_t retu_irq_handler(int irq, void *_retu)
 {
-       int i;
-
-       for (i = 0; i < MAX_RETU_IRQ_HANDLERS; i++) {
-               if (mask & (1 << i))
-                       continue;
-               if (retu_irq_handlers[i].func != NULL)
-                       continue;
-               /* an IRQ was enabled but we don't have a handler for it */
-               printk(KERN_INFO PFX "disabling bogus IRQ %d\n", i);
-               mask |= (1 << i);
-       }
-       return mask;
-}
+	struct retu		*retu = _retu;
 
-/*
- * Disable given RETU interrupt
- */
-void retu_disable_irq(int id)
-{
-	struct retu		*retu = the_retu;
-	unsigned long		flags;
-	u16			mask;
+	int			i;
 
-	spin_lock_irqsave(&retu->lock, flags);
-	mask = retu_read_reg(RETU_REG_IMR);
-	mask |= 1 << id;
-	mask = retu_disable_bogus_irqs(mask);
-	retu_write_reg(RETU_REG_IMR, mask);
-	spin_unlock_irqrestore(&retu->lock, flags);
-}
-EXPORT_SYMBOL(retu_disable_irq);
+	u16			idr;
+	u16			imr;
 
-/*
- * Enable given RETU interrupt
- */
-void retu_enable_irq(int id)
-{
-	struct retu		*retu = the_retu;
-	unsigned long		flags;
-	u16			mask;
+	idr = retu_read_reg(RETU_REG_IDR);
+	imr = retu_read_reg(RETU_REG_IMR);
+	idr &= ~imr;
 
-	if (id == 3) {
-		printk("Enabling Retu IRQ %d\n", id);
-		dump_stack();
+	if (!idr) {
+		dev_vdbg(retu->dev, "No IRQ, spurious?\n");
+		return IRQ_NONE;
 	}
 
-	spin_lock_irqsave(&retu->lock, flags);
-	mask = retu_read_reg(RETU_REG_IMR);
-	mask &= ~(1 << id);
-	mask = retu_disable_bogus_irqs(mask);
-	retu_write_reg(RETU_REG_IMR, mask);
-	spin_unlock_irqrestore(&retu->lock, flags);
+	for (i = 0; idr != 0; i++, idr >>= 1) {
+		if (!(idr & 1))
+			continue;
+
+		handle_nested_irq(i);
+	}
+
+	return IRQ_HANDLED;
 }
-EXPORT_SYMBOL(retu_enable_irq);
 
-/*
- * Acknowledge given RETU interrupt
- */
-void retu_ack_irq(int id)
+/* -------------------------------------------------------------------------- */
+
+static void retu_irq_mask(struct irq_data *data)
 {
-	retu_write_reg(RETU_REG_IDR, 1 << id);
+	struct retu		*retu = irq_data_get_irq_chip_data(data);
+	int			irq = data->irq;
+
+	retu->mask |= (1 << (irq - retu->irq_base));
+	retu->mask_pending = true;
 }
-EXPORT_SYMBOL(retu_ack_irq);
 
-/*
- * RETU interrupt handler. Only schedules the tasklet.
- */
-static irqreturn_t retu_irq_handler(int irq, void *_retu)
+static void retu_irq_unmask(struct irq_data *data)
 {
-	struct retu		*retu = _retu;
+	struct retu		*retu = irq_data_get_irq_chip_data(data);
+	int			irq = data->irq;
 
-	tasklet_schedule(&retu->tasklet);
+	retu->mask &= ~(1 << (irq - retu->irq_base));
+	retu->mask_pending = true;
 
-	return IRQ_HANDLED;
 }
 
-/*
- * Tasklet handler
- */
-static void retu_tasklet_handler(unsigned long data)
+static void retu_irq_ack(struct irq_data *data)
 {
-	struct retu_irq_handler_desc *hnd;
-	u16 id;
-	u16 im;
-	int i;
-
-	for (;;) {
-		id = retu_read_reg(RETU_REG_IDR);
-		im = ~retu_read_reg(RETU_REG_IMR);
-		id &= im;
-
-		if (!id)
-			break;
-
-		for (i = 0; id != 0; i++, id >>= 1) {
-			if (!(id & 1))
-				continue;
-			hnd = &retu_irq_handlers[i];
-			if (hnd->func == NULL) {
-                               /* Spurious retu interrupt - disable and ack it */
-				printk(KERN_INFO "Spurious Retu interrupt "
-						 "(id %d)\n", i);
-				retu_disable_irq(i);
-				retu_ack_irq(i);
-				continue;
-			}
-			hnd->func(hnd->arg);
-			/*
-			 * Don't acknowledge the interrupt here
-			 * It must be done explicitly
-			 */
-		}
-	}
+	struct retu		*retu = irq_data_get_irq_chip_data(data);
+	int			irq = data->irq;
+
+	retu->ack |= (1 << (irq - retu->irq_base));
+	retu->ack_pending = true;
 }
 
-/*
- * Register the handler for a given RETU interrupt source.
- */
-int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
+static void retu_bus_lock(struct irq_data *data)
 {
-	struct retu			*retu = the_retu;
-	struct retu_irq_handler_desc	*hnd;
+	struct retu		*retu = irq_data_get_irq_chip_data(data);
 
-	if (!retu)
-		return -ENODEV;
+	mutex_lock(&retu->irq_lock);
+}
 
-	if (irq_handler == NULL || id >= MAX_RETU_IRQ_HANDLERS ||
-	    name == NULL) {
-		printk(KERN_ERR PFX "Invalid arguments to %s\n",
-		       __FUNCTION__);
-		return -EINVAL;
+static void retu_bus_sync_unlock(struct irq_data *data)
+{
+	struct retu		*retu = irq_data_get_irq_chip_data(data);
+
+	if (retu->mask_pending) {
+		retu_write_reg(RETU_REG_IMR, retu->mask);
+		retu->mask_pending = false;
 	}
-	hnd = &retu_irq_handlers[id];
-	if (hnd->func != NULL) {
-		printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
-		return -EBUSY;
+
+	if (retu->ack_pending) {
+		retu_write_reg(RETU_REG_IDR, retu->ack);
+		retu->ack_pending = false;
 	}
-	printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
-	       id, name);
-	hnd->func = irq_handler;
-	hnd->arg = arg;
-	strlcpy(hnd->name, name, sizeof(hnd->name));
 
-	retu_ack_irq(id);
-	retu_enable_irq(id);
+	mutex_unlock(&retu->irq_lock);
+}
 
-	return 0;
+static struct irq_chip retu_irq_chip = {
+	.name			= "retu",
+	.irq_bus_lock		= retu_bus_lock,
+	.irq_bus_sync_unlock	= retu_bus_sync_unlock,
+	.irq_mask		= retu_irq_mask,
+	.irq_unmask		= retu_irq_unmask,
+	.irq_ack		= retu_irq_ack,
+};
+
+static inline void retu_irq_setup(int irq)
+{
+#ifdef CONFIG_ARM
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	set_irq_noprobe(irq);
+#endif
 }
-EXPORT_SYMBOL(retu_request_irq);
 
-/*
- * Unregister the handler for a given RETU interrupt source.
- */
-void retu_free_irq(int id)
+static void retu_irq_init(struct retu *retu)
 {
-	struct retu_irq_handler_desc *hnd;
+	int			base = retu->irq_base;
+	int			irq;
 
-	if (id >= MAX_RETU_IRQ_HANDLERS) {
-		printk(KERN_ERR PFX "Invalid argument to %s\n",
-		       __FUNCTION__);
-		return;
-	}
-	hnd = &retu_irq_handlers[id];
-	if (hnd->func == NULL) {
-		printk(KERN_ERR PFX "IRQ %d already freed\n", id);
-		return;
+	for (irq = base; irq < base + retu->nirqs; irq++) {
+		set_irq_chip_data(irq, retu);
+		set_irq_chip_and_handler(irq, &retu_irq_chip,
+				handle_simple_irq);
+		set_irq_nested_thread(irq, 1);
+		retu_irq_setup(irq);
 	}
+}
+
+static void retu_irq_exit(struct retu *retu)
+{
+	int			base = retu->irq_base;
+	int			irq;
 
-	retu_disable_irq(id);
-	hnd->func = NULL;
+	for (irq = base; irq < base + retu->nirqs; irq++) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#endif
+		set_irq_chip_and_handler(irq, NULL, NULL);
+		set_irq_chip_data(irq, NULL);
+	}
 }
-EXPORT_SYMBOL(retu_free_irq);
+
+/* -------------------------------------------------------------------------- */
 
 /**
  * retu_power_off - Shut down power to system
@@ -428,12 +384,15 @@ static int __init retu_probe(struct platform_device *pdev)
 	the_retu = retu;
 
 	/* Prepare tasklet */
-	tasklet_init(&retu->tasklet, retu_tasklet_handler, 0);
 	spin_lock_init(&retu->lock);
+	mutex_init(&retu->irq_lock);
 
 	irq = platform_get_irq(pdev, 0);
 
 	retu->irq = irq;
+	retu->nirqs = MAX_RETU_IRQ_HANDLERS;
+
+	retu_irq_init(retu);
 
 	rev = retu_read_reg(RETU_REG_ASICR) & 0xff;
 	if (rev & (1 << 7))
@@ -446,7 +405,7 @@ static int __init retu_probe(struct platform_device *pdev)
 	/* Mask all RETU interrupts */
 	retu_write_reg(RETU_REG_IMR, 0xffff);
 
-	ret = request_irq(irq, retu_irq_handler, 0,
+	ret = request_threaded_irq(irq, NULL, retu_irq_handler, 0,
 			  "retu", retu);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Unable to register IRQ handler\n");
@@ -471,7 +430,6 @@ err2:
 	free_irq(irq, retu);
 
 err1:
-	tasklet_kill(&retu->tasklet);
 	kfree(retu);
 	the_retu = NULL;
 
@@ -489,7 +447,7 @@ static int __exit retu_remove(struct platform_device *pdev)
 	/* Mask all RETU interrupts */
 	retu_write_reg(RETU_REG_IMR, 0xffff);
 	free_irq(irq, retu);
-	tasklet_kill(&retu->tasklet);
+	retu_irq_exit(retu);
 	kfree(retu);
 	the_retu = NULL;
 
diff --git a/drivers/cbus/retu.h b/drivers/cbus/retu.h
index ada7f2e..1b05f3e 100644
--- a/drivers/cbus/retu.h
+++ b/drivers/cbus/retu.h
@@ -62,10 +62,5 @@ int retu_read_reg(unsigned reg);
 void retu_write_reg(unsigned reg, u16 val);
 void retu_set_clear_reg_bits(unsigned reg, u16 set, u16 clear);
 int retu_read_adc(int channel);
-int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
-void retu_free_irq(int id);
-void retu_enable_irq(int id);
-void retu_disable_irq(int id);
-void retu_ack_irq(int id);
 
 #endif /* __DRIVERS_CBUS_RETU_H */
-- 
1.7.3.4.598.g85356

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux