Skip to contentSkip to conversation

Move twl*-irq.c to threaded_irq infrastructure

PATCH In review Felipe Balbi 4 participants 19 messages Dec 28, 2010 – Dec 30, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover Move twl*-irq.c to threaded_irq infrastructure

Hi all,

This is truly untested. I only compile tested thus far as I'm not sure it's the right path. It looks like that's what's supposed to be done, but I wanted to check with you guys before going any further.

In a few minutes, I'll be starting to test these two patches and will try to find any possible errors with them but, please, give it a good review as IMO it's a rather invasive change.

Thomas, I've got one extra question for you. I remember reading you suggested all drivers should actually try to use threaded IRQ infrastructure for handling the IRQ and the hardirq handler would only check if the IRQ is coming from this device. Is that how you expect theaded IRQ to be used ? What's the purpose ?

Please, correct me if I'm wrong, but I came to the conclusion that threaded IRQs can be reniced and preempted which would mean we could be setting IRQ priorities from userland and we could also be running N IRQ threads if we have N cpu cores. Is that correct ? Is that your goal ?

I tried searching for some more documentation on the threaded IRQ infrastructure but couldn't find much by grepping Documentation/ maybe my match string wasn't good enough. Anyway, let me know what are your feeling about the following two patches and what would be your goal should we move all IRQ handlers to threaded IRQ infrastructure as I remember you suggested.

Happy new year all.

Felipe Balbi (2):
mfd: twl6030-irq: move to threaded_irq
mfd: twl4030-irq: move to threaded_irq

 drivers/mfd/twl4030-irq.c |  133 +++++++++++++-----------------------------
 drivers/mfd/twl6030-irq.c |  141 ++++++++++++++++-----------------------------
 2 files changed, 91 insertions(+), 183 deletions(-)
Signature · 1 line
1.7.3.4.598.g85356
#1 lore raw

PATCH 1/2 mfd: twl6030-irq: move to threaded_irq

... and while at that, also start using
handle_nested_irq() as we should.

---
 drivers/mfd/twl6030-irq.c |  141 ++++++++++++++++-----------------------------
 1 files changed, 49 insertions(+), 92 deletions(-)
drivers/mfd/twl6030-irq.c +49 -92
index aaedb11..1530411 100644
@@ -84,99 +84,64 @@ static int twl6030_interrupt_mapping[24] = {
 
 static unsigned twl6030_irq_base;
 
-static struct completion irq_event;
-
 /*
  * This thread processes interrupts reported by the Primary Interrupt Handler.
  */
-static int twl6030_irq_thread(void *data)
+static irqreturn_t twl6030_irq_thread(int irq, void *unused)
 {
-	long irq = (long)data;
-	static unsigned i2c_errors;
-	static const unsigned max_i2c_errors = 100;
 	int ret;
-
-	current->flags |= PF_NOFREEZE;
-
-	while (!kthread_should_stop()) {
-		int i;
-		union {
+	int i;
+	union {
 		u8 bytes[4];
 		u32 int_sts;
-		} sts;
-
-		/* Wait for IRQ, then read PIH irq status (also blocking) */
-		wait_for_completion_interruptible(&irq_event);
-
-		/* read INT_STS_A, B and C in one shot using a burst read */
-		ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
-				REG_INT_STS_A, 3);
-		if (ret) {
-			pr_warning("twl6030: I2C error %d reading PIH ISR\n",
-					ret);
-			if (++i2c_errors >= max_i2c_errors) {
-				printk(KERN_ERR "Maximum I2C error count"
-						" exceeded.  Terminating %s.\n",
-						__func__);
-				break;
-			}
-			complete(&irq_event);
-			continue;
-		}
-
+	} sts;
 
+	disable_irq_nosync(irq);
 
-		sts.bytes[3] = 0; /* Only 24 bits are valid*/
+	/* read INT_STS_A, B and C in one shot using a burst read */
+	ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
+			REG_INT_STS_A, 3);
+	if (ret) {
+		pr_warning("twl6030: I2C error %d reading PIH ISR\n",
+				ret);
+		return IRQ_NONE;
+	}
 
-		for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
-			local_irq_disable();
-			if (sts.int_sts & 0x1) {
-				int module_irq = twl6030_irq_base +
-					twl6030_interrupt_mapping[i];
-				struct irq_desc *d = irq_to_desc(module_irq);
 
-				if (!d) {
-					pr_err("twl6030: Invalid SIH IRQ: %d\n",
-					       module_irq);
-					return -EINVAL;
-				}
+	sts.bytes[3] = 0; /* Only 24 bits are valid*/
 
-				/* These can't be masked ... always warn
-				 * if we get any surprises.
-				 */
-				if (d->status & IRQ_DISABLED)
-					note_interrupt(module_irq, d,
-							IRQ_NONE);
-				else
-					d->handle_irq(module_irq, d);
+	for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
+		local_irq_disable();
+		if (sts.int_sts & 0x1) {
+			int module_irq = twl6030_irq_base +
+				twl6030_interrupt_mapping[i];
+			struct irq_desc *d = irq_to_desc(module_irq);
 
+			if (!d) {
+				pr_err("twl6030: Invalid SIH IRQ: %d\n",
+						module_irq);
+				return IRQ_NONE;
 			}
-		local_irq_enable();
-		}
-		ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
-				REG_INT_STS_A, 3); /* clear INT_STS_A */
-		if (ret)
-			pr_warning("twl6030: I2C error in clearing PIH ISR\n");
 
-		enable_irq(irq);
+			/* These can't be masked ... always warn
+			 * if we get any surprises.
+			 */
+			if (d->status & IRQ_DISABLED)
+				note_interrupt(module_irq, d,
+						IRQ_NONE);
+			else
+				handle_nested_irq(module_irq);
+
+		}
+		local_irq_enable();
 	}
+	ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
+			REG_INT_STS_A, 3); /* clear INT_STS_A */
+	if (ret)
+		pr_warning("twl6030: I2C error in clearing PIH ISR\n");
 
-	return 0;
-}
+	enable_irq(irq);
 
-/*
- * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
- * This is a chained interrupt, so there is no desc->action method for it.
- * Now we need to query the interrupt controller in the twl6030 to determine
- * which module is generating the interrupt request.  However, we can't do i2c
- * transactions in interrupt context, so we must defer that work to a kernel
- * thread.  All we do here is acknowledge and mask the interrupt and wakeup
- * the kernel thread.
- */
-static irqreturn_t handle_twl6030_pih(int irq, void *devid)
-{
-	disable_irq_nosync(irq);
-	complete(devid);
 	return IRQ_HANDLED;
 }
 
@@ -303,7 +268,6 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 
 	int	status = 0;
 	int	i;
-	struct task_struct	*task;
 	int ret;
 	u8 mask[4];
 
@@ -337,28 +301,21 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 	pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
 			irq_num, irq_base, twl6030_irq_next - 1);
 
-	/* install an irq handler to demultiplex the TWL6030 interrupt */
-	init_completion(&irq_event);
-	task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
-	if (IS_ERR(task)) {
-		pr_err("twl6030: could not create irq %d thread!\n", irq_num);
-		status = PTR_ERR(task);
-		goto fail_kthread;
-	}
-
-	status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
-				"TWL6030-PIH", &irq_event);
+	status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread,
+			IRQF_DISABLED, "TWL6030-PIH", NULL);
 	if (status < 0) {
 		pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
-		goto fail_irq;
+		goto fail;
 	}
-	return status;
-fail_irq:
-	free_irq(irq_num, &irq_event);
 
-fail_kthread:
+	return 0;
+
+fail:
+	free_irq(irq_num, NULL);
+
 	for (i = irq_base; i < irq_end; i++)
 		set_irq_chip_and_handler(i, NULL, NULL);
+
 	return status;
 }
 
Signature · 1 line
1.7.3.4.598.g85356
#2 lore raw

Re: [RFC/PATCH 1/2] mfd: twl6030-irq: move to threaded_irq

drivers/mfd/twl6030-irq.c static int twl6030_interrupt_mapping[24] = { · line 99

Quoted 1 line · Felipe Balbi
drivers/mfd/twl6030-irq.c +1 -0
+	disable_irq_nosync(irq);

You shouldn't need this any more; the driver used to be faffing around with this because it wasn't using genirq for this in the past.

drivers/mfd/twl6030-irq.c static int twl6030_interrupt_mapping[24] = { · line 114

Quoted 2 lines
drivers/mfd/twl6030-irq.c +2 -0
+	for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
+		local_irq_disable();

Simiarly here as far as I know; the original code predates genirq support for this so is doing some hairy stuff that is no longer required and may actually be harmful.

What I'd expect to see from a conversion like this would be that most of the locking/IRQ management stuff would be dropped and the bus_lock() and bus_sync_unlock() operations would be implemented.

#3 lore raw

Re: [RFC/PATCH 1/2] mfd: twl6030-irq: move to threaded_irq

Hi,

Quoted 7 lines · Mark Brown

drivers/mfd/twl6030-irq.c static int twl6030_interrupt_mapping[24] = { · line 99

Quoted 1 line · Felipe Balbi
drivers/mfd/twl6030-irq.c +1 -0
+	disable_irq_nosync(irq);

You shouldn't need this any more; the driver used to be faffing around with this because it wasn't using genirq for this in the past.

Quoted 6 lines

drivers/mfd/twl6030-irq.c static int twl6030_interrupt_mapping[24] = { · line 114

Quoted 2 lines
drivers/mfd/twl6030-irq.c +2 -0
+	for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
+		local_irq_disable();

Simiarly here as far as I know; the original code predates genirq support for this so is doing some hairy stuff that is no longer required and may actually be harmful.

Aa, true. Forgot that one.

Quoted 3 lines

What I'd expect to see from a conversion like this would be that most of the locking/IRQ management stuff would be dropped and the bus_lock() and bus_sync_unlock() operations would be implemented.

I'll look into it, thanks.

Signature · 1 line
balbi
#4 lore raw

cover TWL4030 IRQ Changes

Hi all,

I dropped the twl6030-irq.c changes because that thing is a bit messy. I hope the original author will feel inspired and fix that one up.

Anyway, twl4030-irq.c seems to be going to the right direction now. Thanks to Mark Brown for pointing out the need to drop the locking and implement bus_lock/bus_sync_unlock methods.

Compile tested only with omap2plus_defconfig.

I'll test after merge window to be sure I have all the newest code.

Felipe Balbi (3):
mfd: twl4030-irq: move to threaded_irq
mfd: twl4030-irq: drop the workqueue hackery
mfd: twl4030-irq: implement bus_*lock

 drivers/mfd/twl4030-irq.c |  363 +++++++++++++++++---------------------------
 1 files changed, 140 insertions(+), 223 deletions(-)
Signature · 1 line
1.7.3.4.598.g85356
#5 lore raw

PATCH 1/3 mfd: twl4030-irq: move to threaded_irq

... and while at that, also start using
handle_nested_irq() as we should.

---
 drivers/mfd/twl4030-irq.c |  131 +++++++++++++--------------------------------
 1 files changed, 38 insertions(+), 93 deletions(-)
drivers/mfd/twl4030-irq.c +38 -93
index 5d3a147..91331a7 100644
@@ -30,7 +30,6 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-#include <linux/kthread.h>
 #include <linux/slab.h>
 
 #include <linux/i2c/twl.h>
@@ -278,91 +277,50 @@ static const struct sih sih_modules_twl5031[8] = {
 
 static unsigned twl4030_irq_base;
 
-static struct completion irq_event;
-
 /*
  * This thread processes interrupts reported by the Primary Interrupt Handler.
  */
-static int twl4030_irq_thread(void *data)
+static irqreturn_t twl4030_irq_thread(int irq, void *unused)
 {
-	long irq = (long)data;
-	static unsigned i2c_errors;
-	static const unsigned max_i2c_errors = 100;
-
-
-	current->flags |= PF_NOFREEZE;
-
-	while (!kthread_should_stop()) {
-		int ret;
-		int module_irq;
-		u8 pih_isr;
-
-		/* Wait for IRQ, then read PIH irq status (also blocking) */
-		wait_for_completion_interruptible(&irq_event);
-
-		ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
-					  REG_PIH_ISR_P1);
-		if (ret) {
-			pr_warning("twl4030: I2C error %d reading PIH ISR\n",
-					ret);
-			if (++i2c_errors >= max_i2c_errors) {
-				printk(KERN_ERR "Maximum I2C error count"
-						" exceeded.  Terminating %s.\n",
-						__func__);
-				break;
-			}
-			complete(&irq_event);
-			continue;
-		}
+	int ret;
+	int module_irq;
+	u8 pih_isr;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+			REG_PIH_ISR_P1);
+	if (ret) {
+		pr_warning("twl4030: I2C error %d reading PIH ISR\n",
+				ret);
+		return IRQ_NONE;
+	}
 
-		/* these handlers deal with the relevant SIH irq status */
-		local_irq_disable();
-		for (module_irq = twl4030_irq_base;
-				pih_isr;
-				pih_isr >>= 1, module_irq++) {
-			if (pih_isr & 0x1) {
-				struct irq_desc *d = irq_to_desc(module_irq);
-
-				if (!d) {
-					pr_err("twl4030: Invalid SIH IRQ: %d\n",
-					       module_irq);
-					return -EINVAL;
-				}
-
-				/* These can't be masked ... always warn
-				 * if we get any surprises.
-				 */
-				if (d->status & IRQ_DISABLED)
-					note_interrupt(module_irq, d,
-							IRQ_NONE);
-				else
-					d->handle_irq(module_irq, d);
+	/* these handlers deal with the relevant SIH irq status */
+	for (module_irq = twl4030_irq_base;
+			pih_isr;
+			pih_isr >>= 1, module_irq++) {
+		if (pih_isr & 0x1) {
+			struct irq_desc *d = irq_to_desc(module_irq);
+
+			if (!d) {
+				pr_err("twl4030: Invalid SIH IRQ: %d\n",
+						module_irq);
+				return IRQ_NONE;
 			}
-		}
-		local_irq_enable();
 
-		enable_irq(irq);
+			/* These can't be masked ... always warn
+			 * if we get any surprises.
+			 */
+			if (d->status & IRQ_DISABLED)
+				note_interrupt(module_irq, d,
+						IRQ_NONE);
+			else
+				handle_nested_irq(module_irq);
+		}
 	}
 
-	return 0;
-}
-
-/*
- * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt.
- * This is a chained interrupt, so there is no desc->action method for it.
- * Now we need to query the interrupt controller in the twl4030 to determine
- * which module is generating the interrupt request.  However, we can't do i2c
- * transactions in interrupt context, so we must defer that work to a kernel
- * thread.  All we do here is acknowledge and mask the interrupt and wakeup
- * the kernel thread.
- */
-static irqreturn_t handle_twl4030_pih(int irq, void *devid)
-{
-	/* Acknowledge, clear *AND* mask the interrupt... */
-	disable_irq_nosync(irq);
-	complete(devid);
 	return IRQ_HANDLED;
 }
+
 /*----------------------------------------------------------------------*/
 
 /*
@@ -788,7 +746,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 
 	int			status;
 	int			i;
-	struct task_struct	*task;
 
 	/*
 	 * Mask and clear all TWL4030 interrupts since initially we do
@@ -817,6 +774,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 	for (i = irq_base; i < irq_end; i++) {
 		set_irq_chip_and_handler(i, &twl4030_irq_chip,
 				handle_simple_irq);
+		set_irq_nested_thread(i, 1);
 		activate_irq(i);
 	}
 	twl4030_irq_next = i;
@@ -830,28 +788,15 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 		goto fail;
 	}
 
-	/* install an irq handler to demultiplex the TWL4030 interrupt */
-
-
-	init_completion(&irq_event);
-
-	status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
-				"TWL4030-PIH", &irq_event);
+	status = request_threaded_irq(irq_num, NULL, twl4030_irq_thread,
+			IRQF_DISABLED, "TWL4030-PIH", NULL);
 	if (status < 0) {
 		pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
 		goto fail_rqirq;
 	}
 
-	task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
-								"twl4030-irq");
-	if (IS_ERR(task)) {
-		pr_err("twl4030: could not create irq %d thread!\n", irq_num);
-		status = PTR_ERR(task);
-		goto fail_kthread;
-	}
-	return status;
-fail_kthread:
-	free_irq(irq_num, &irq_event);
+	return 0;
+
 fail_rqirq:
 	/* clean up twl4030_sih_setup */
 fail:
Signature · 1 line
1.7.3.4.598.g85356
#6 lore raw

PATCH 2/3 mfd: twl4030-irq: drop the workqueue hackery

In reply to Felipe Balbi

Finally that workqueue isn't needed anymore.
Drop that hackery and move the spinlock_t to
a mutex so we can issue I2C operations with
a lock held.

---
 drivers/mfd/twl4030-irq.c |  226 +++++++++++++++++++--------------------------
 1 files changed, 96 insertions(+), 130 deletions(-)
drivers/mfd/twl4030-irq.c +96 -130
index 91331a7..298956d 100644
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
 
 #include <linux/i2c/twl.h>
 
@@ -434,46 +435,36 @@ static inline void activate_irq(int irq)
 
 /*----------------------------------------------------------------------*/
 
-static DEFINE_SPINLOCK(sih_agent_lock);
-
-static struct workqueue_struct *wq;
-
 struct sih_agent {
 	int			irq_base;
 	const struct sih	*sih;
 
 	u32			imr;
-	bool			imr_change_pending;
-	struct work_struct	mask_work;
-
 	u32			edge_change;
-	struct work_struct	edge_work;
+
+	struct mutex		irq_lock;
 };
 
-static void twl4030_sih_do_mask(struct work_struct *work)
+/*----------------------------------------------------------------------*/
+
+static void twl4030_sih_mask(unsigned irq)
 {
-	struct sih_agent	*agent;
-	const struct sih	*sih;
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+	const struct sih	*sih = agent->sih;
+
 	union {
 		u8	bytes[4];
 		u32	word;
 	}			imr;
+
 	int			status;
 
-	agent = container_of(work, struct sih_agent, mask_work);
-
-	/* see what work we have */
-	spin_lock_irq(&sih_agent_lock);
-	if (agent->imr_change_pending) {
-		sih = agent->sih;
-		/* byte[0] gets overwritten as we write ... */
-		imr.word = cpu_to_le32(agent->imr << 8);
-		agent->imr_change_pending = false;
-	} else
-		sih = NULL;
-	spin_unlock_irq(&sih_agent_lock);
-	if (!sih)
-		return;
+	agent->imr |= BIT(irq - agent->irq_base);
+
+	mutex_lock(&agent->irq_lock);
+
+	/* byte[0] gets overwritten as we write ... */
+	imr.word = cpu_to_le32(agent->imr << 8);
 
 	/* write the whole mask ... simpler than subsetting it */
 	status = twl_i2c_write(sih->module, imr.bytes,
@@ -481,111 +472,42 @@ static void twl4030_sih_do_mask(struct work_struct *work)
 	if (status)
 		pr_err("twl4030: %s, %s --> %d\n", __func__,
 				"write", status);
+	mutex_unlock(&agent->irq_lock);
 }
 
-static void twl4030_sih_do_edge(struct work_struct *work)
+static void twl4030_sih_unmask(unsigned irq)
 {
-	struct sih_agent	*agent;
-	const struct sih	*sih;
-	u8			bytes[6];
-	u32			edge_change;
-	int			status;
-
-	agent = container_of(work, struct sih_agent, edge_work);
-
-	/* see what work we have */
-	spin_lock_irq(&sih_agent_lock);
-	edge_change = agent->edge_change;
-	agent->edge_change = 0;
-	sih = edge_change ? agent->sih : NULL;
-	spin_unlock_irq(&sih_agent_lock);
-	if (!sih)
-		return;
-
-	/* Read, reserving first byte for write scratch.  Yes, this
-	 * could be cached for some speedup ... but be careful about
-	 * any processor on the other IRQ line, EDR registers are
-	 * shared.
-	 */
-	status = twl_i2c_read(sih->module, bytes + 1,
-			sih->edr_offset, sih->bytes_edr);
-	if (status) {
-		pr_err("twl4030: %s, %s --> %d\n", __func__,
-				"read", status);
-		return;
-	}
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+	const struct sih	*sih = agent->sih;
 
-	/* Modify only the bits we know must change */
-	while (edge_change) {
-		int		i = fls(edge_change) - 1;
-		struct irq_desc	*d = irq_to_desc(i + agent->irq_base);
-		int		byte = 1 + (i >> 2);
-		int		off = (i & 0x3) * 2;
-
-		if (!d) {
-			pr_err("twl4030: Invalid IRQ: %d\n",
-			       i + agent->irq_base);
-			return;
-		}
+	union {
+		u8	bytes[4];
+		u32	word;
+	}			imr;
 
-		bytes[byte] &= ~(0x03 << off);
+	int			status;
 
-		raw_spin_lock_irq(&d->lock);
-		if (d->status & IRQ_TYPE_EDGE_RISING)
-			bytes[byte] |= BIT(off + 1);
-		if (d->status & IRQ_TYPE_EDGE_FALLING)
-			bytes[byte] |= BIT(off + 0);
-		raw_spin_unlock_irq(&d->lock);
+	mutex_lock(&agent->irq_lock);
+	agent->imr &= ~BIT(irq - agent->irq_base);
 
-		edge_change &= ~BIT(i);
-	}
+	/* byte[0] gets overwritten as we write ... */
+	imr.word = cpu_to_le32(agent->imr << 8);
 
-	/* Write */
-	status = twl_i2c_write(sih->module, bytes,
-			sih->edr_offset, sih->bytes_edr);
+	/* write the whole mask ... simpler than subsetting it */
+	status = twl_i2c_write(sih->module, imr.bytes,
+			sih->mask[irq_line].imr_offset, sih->bytes_ixr);
 	if (status)
 		pr_err("twl4030: %s, %s --> %d\n", __func__,
 				"write", status);
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
- * All irq_chip methods get issued from code holding irq_desc[irq].lock,
- * which can't perform the underlying I2C operations (because they sleep).
- * So we must hand them off to a thread (workqueue) and cope with asynch
- * completion, potentially including some re-ordering, of these requests.
- */
-
-static void twl4030_sih_mask(unsigned irq)
-{
-	struct sih_agent *sih = get_irq_chip_data(irq);
-	unsigned long flags;
-
-	spin_lock_irqsave(&sih_agent_lock, flags);
-	sih->imr |= BIT(irq - sih->irq_base);
-	sih->imr_change_pending = true;
-	queue_work(wq, &sih->mask_work);
-	spin_unlock_irqrestore(&sih_agent_lock, flags);
-}
-
-static void twl4030_sih_unmask(unsigned irq)
-{
-	struct sih_agent *sih = get_irq_chip_data(irq);
-	unsigned long flags;
-
-	spin_lock_irqsave(&sih_agent_lock, flags);
-	sih->imr &= ~BIT(irq - sih->irq_base);
-	sih->imr_change_pending = true;
-	queue_work(wq, &sih->mask_work);
-	spin_unlock_irqrestore(&sih_agent_lock, flags);
+	mutex_unlock(&agent->irq_lock);
 }
 
 static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 {
-	struct sih_agent *sih = get_irq_chip_data(irq);
-	struct irq_desc *desc = irq_to_desc(irq);
-	unsigned long flags;
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+	const struct sih	*sih = agent->sih;
+	struct irq_desc		*desc = irq_to_desc(irq);
+	int			status = 0;
 
 	if (!desc) {
 		pr_err("twl4030: Invalid IRQ: %d\n", irq);
@@ -595,15 +517,67 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 	if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		return -EINVAL;
 
-	spin_lock_irqsave(&sih_agent_lock, flags);
+	mutex_lock(&agent->irq_lock);
 	if ((desc->status & IRQ_TYPE_SENSE_MASK) != trigger) {
+		u8			bytes[6];
+		u32			edge_change;
+
 		desc->status &= ~IRQ_TYPE_SENSE_MASK;
 		desc->status |= trigger;
-		sih->edge_change |= BIT(irq - sih->irq_base);
-		queue_work(wq, &sih->edge_work);
+		agent->edge_change |= BIT(irq - agent->irq_base);
+		edge_change = agent->edge_change;
+
+		/* Read, reserving first byte for write scratch.  Yes, this
+		 * could be cached for some speedup ... but be careful about
+		 * any processor on the other IRQ line, EDR registers are
+		 * shared.
+		 */
+		status = twl_i2c_read(sih->module, bytes + 1,
+				sih->edr_offset, sih->bytes_edr);
+		if (status) {
+			pr_err("twl4030: %s, %s --> %d\n", __func__,
+					"read", status);
+			goto out;
+		}
+
+		/* Modify only the bits we know must change */
+		while (edge_change) {
+			int		i = fls(edge_change) - 1;
+			struct irq_desc	*d = irq_to_desc(i + agent->irq_base);
+			int		byte = 1 + (i >> 2);
+			int		off = (i & 0x3) * 2;
+
+			if (!d) {
+				pr_err("twl4030: Invalid IRQ: %d\n",
+						i + agent->irq_base);
+				status = -ENODEV;
+				goto out;
+			}
+
+			bytes[byte] &= ~(0x03 << off);
+
+			raw_spin_lock_irq(&d->lock);
+			if (d->status & IRQ_TYPE_EDGE_RISING)
+				bytes[byte] |= BIT(off + 1);
+			if (d->status & IRQ_TYPE_EDGE_FALLING)
+				bytes[byte] |= BIT(off + 0);
+			raw_spin_unlock_irq(&d->lock);
+
+			edge_change &= ~BIT(i);
+		}
+
+		/* Write */
+		status = twl_i2c_write(sih->module, bytes,
+				sih->edr_offset, sih->bytes_edr);
+		if (status)
+			pr_err("twl4030: %s, %s --> %d\n", __func__,
+					"write", status);
 	}
-	spin_unlock_irqrestore(&sih_agent_lock, flags);
-	return 0;
+
+out:
+	mutex_unlock(&agent->irq_lock);
+
+	return status;
 }
 
 static struct irq_chip twl4030_sih_irq_chip = {
@@ -706,8 +680,7 @@ int twl4030_sih_setup(int module)
 	agent->irq_base = irq_base;
 	agent->sih = sih;
 	agent->imr = ~0;
-	INIT_WORK(&agent->mask_work, twl4030_sih_do_mask);
-	INIT_WORK(&agent->edge_work, twl4030_sih_do_edge);
+	mutex_init(&agent->irq_lock);
 
 	for (i = 0; i < sih->bits; i++) {
 		irq = irq_base + i;
@@ -755,12 +728,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 	if (status < 0)
 		return status;
 
-	wq = create_singlethread_workqueue("twl4030-irqchip");
-	if (!wq) {
-		pr_err("twl4030: workqueue FAIL\n");
-		return -ESRCH;
-	}
-
 	twl4030_irq_base = irq_base;
 
 	/* install an irq handler for each of the SIH modules;
@@ -802,8 +769,7 @@ fail_rqirq:
 fail:
 	for (i = irq_base; i < irq_end; i++)
 		set_irq_chip_and_handler(i, NULL, NULL);
-	destroy_workqueue(wq);
-	wq = NULL;
+
 	return status;
 }
 
Signature · 1 line
1.7.3.4.598.g85356
#7 lore raw

PATCH 3/3 mfd: twl4030-irq: implement bus_*lock

In reply to Felipe Balbi

drop all the locking around mask/unmask and
implement bus_lock and bus_sync_unlock methods.

---
 drivers/mfd/twl4030-irq.c |   30 ++++++++++++++++++------------
 1 files changed, 18 insertions(+), 12 deletions(-)
drivers/mfd/twl4030-irq.c +18 -12
index 298956d..ff7bb93 100644
@@ -461,8 +461,6 @@ static void twl4030_sih_mask(unsigned irq)
 
 	agent->imr |= BIT(irq - agent->irq_base);
 
-	mutex_lock(&agent->irq_lock);
-
 	/* byte[0] gets overwritten as we write ... */
 	imr.word = cpu_to_le32(agent->imr << 8);
 
@@ -472,7 +470,6 @@ static void twl4030_sih_mask(unsigned irq)
 	if (status)
 		pr_err("twl4030: %s, %s --> %d\n", __func__,
 				"write", status);
-	mutex_unlock(&agent->irq_lock);
 }
 
 static void twl4030_sih_unmask(unsigned irq)
@@ -487,7 +484,6 @@ static void twl4030_sih_unmask(unsigned irq)
 
 	int			status;
 
-	mutex_lock(&agent->irq_lock);
 	agent->imr &= ~BIT(irq - agent->irq_base);
 
 	/* byte[0] gets overwritten as we write ... */
@@ -499,7 +495,6 @@ static void twl4030_sih_unmask(unsigned irq)
 	if (status)
 		pr_err("twl4030: %s, %s --> %d\n", __func__,
 				"write", status);
-	mutex_unlock(&agent->irq_lock);
 }
 
 static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
@@ -517,7 +512,6 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 	if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		return -EINVAL;
 
-	mutex_lock(&agent->irq_lock);
 	if ((desc->status & IRQ_TYPE_SENSE_MASK) != trigger) {
 		u8			bytes[6];
 		u32			edge_change;
@@ -537,7 +531,7 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 		if (status) {
 			pr_err("twl4030: %s, %s --> %d\n", __func__,
 					"read", status);
-			goto out;
+			return status;
 		}
 
 		/* Modify only the bits we know must change */
@@ -550,8 +544,7 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 			if (!d) {
 				pr_err("twl4030: Invalid IRQ: %d\n",
 						i + agent->irq_base);
-				status = -ENODEV;
-				goto out;
+				return -ENODEV;
 			}
 
 			bytes[byte] &= ~(0x03 << off);
@@ -574,17 +567,30 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 					"write", status);
 	}
 
-out:
-	mutex_unlock(&agent->irq_lock);
-
 	return status;
 }
 
+static void twl4030_sih_bus_lock(unsigned int irq)
+{
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+
+	mutex_lock(&agent->irq_lock);
+}
+
+static void twl4030_sih_bus_sync_unlock(unsigned int irq)
+{
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+
+	mutex_unlock(&agent->irq_lock);
+}
+
 static struct irq_chip twl4030_sih_irq_chip = {
 	.name		= "twl4030",
 	.mask		= twl4030_sih_mask,
 	.unmask		= twl4030_sih_unmask,
 	.set_type	= twl4030_sih_set_type,
+	.bus_lock	= twl4030_sih_bus_lock,
+	.bus_sync_unlock = twl4030_sih_bus_sync_unlock,
 };
 
 /*----------------------------------------------------------------------*/
Signature · 1 line
1.7.3.4.598.g85356
#8 lore raw

Re: [RFC/PATCH 3/3] mfd: twl4030-irq: implement bus_*lock

drivers/mfd/twl4030-irq.c static int twl4030_sih_set_type(unsigned irq, unsigned trigger) · line 585

Quoted 6 lines · Felipe Balbi
drivers/mfd/twl4030-irq.c +6 -0
+static void twl4030_sih_bus_sync_unlock(unsigned int irq)
+{
+       struct sih_agent        *agent = get_irq_chip_data(irq);
+
+       mutex_unlock(&agent->irq_lock);
+}

I suspect you need to do some sort of sync with the hardware here - the _sync bit of the name comes from the fact that the mask and unmask stuff is still called with IRQs disabled and so can't touch and I2C chip, this is called after reenabling them give a chance for the updates done to be reflected in the hardware. The implementation everyone else has done is to update a register cache in the other functions then write that out here before dropping the mutex.

drivers/mfd/twl4030-irq.c static int twl4030_sih_set_type(unsigned irq, unsigned trigger) · line 594

Quoted 8 lines

static struct irq_chip twl4030_sih_irq_chip = {

drivers/mfd/twl4030-irq.c +2 -0
 	.name		= "twl4030",
 	.mask		= twl4030_sih_mask,
 	.unmask		= twl4030_sih_unmask,
 	.set_type	= twl4030_sih_set_type,
+	.bus_lock	= twl4030_sih_bus_lock,
+	.bus_sync_unlock = twl4030_sih_bus_sync_unlock,
 };

I just realised that this collides with the conversion to the irq_ versions that has been done on the driver in -next by either myself or Lennart (we both submitted essentially the same patches and a couple of his went in) - that was a purely mechanical conversion that didn't address any of the issues this patch addresses but they're touching the same code.

#9 lore raw

Re: [RFC/PATCH 3/3] mfd: twl4030-irq: implement bus_*lock

Hi,

Quoted 16 lines · Mark Brown

drivers/mfd/twl4030-irq.c static int twl4030_sih_set_type(unsigned irq, unsigned trigger) · line 585

Quoted 6 lines · Felipe Balbi
drivers/mfd/twl4030-irq.c +6 -0
+static void twl4030_sih_bus_sync_unlock(unsigned int irq)
+{
+       struct sih_agent        *agent = get_irq_chip_data(irq);
+
+       mutex_unlock(&agent->irq_lock);
+}

I suspect you need to do some sort of sync with the hardware here - the _sync bit of the name comes from the fact that the mask and unmask stuff is still called with IRQs disabled and so can't touch and I2C chip, this is called after reenabling them give a chance for the updates done to be reflected in the hardware. The implementation everyone else has done is to update a register cache in the other functions then write that out here before dropping the mutex.

now that I look at some gpio chips I see what you're saying, will update that tomorrow. Thanks

Quoted 7 lines
Quoted 7 lines

static struct irq_chip twl4030_sih_irq_chip = {

drivers/mfd/twl4030-irq.c +2 -0
 	.name		= "twl4030",
 	.mask		= twl4030_sih_mask,
 	.unmask		= twl4030_sih_unmask,
 	.set_type	= twl4030_sih_set_type,
+	.bus_lock	= twl4030_sih_bus_lock,
+	.bus_sync_unlock = twl4030_sih_bus_sync_unlock,
Quoted 8 lines
Quoted 1 line

};

I just realised that this collides with the conversion to the irq_ versions that has been done on the driver in -next by either myself or Lennart (we both submitted essentially the same patches and a couple of his went in) - that was a purely mechanical conversion that didn't address any of the issues this patch addresses but they're touching the same code.

no problem. This will actually only be able on 2.6.39 merge window anyway, so I'll have plenty of time to rebase on 2.6.38 and get these patches queued.

ps: sorry the mail change, out of the office.

Signature · 1 line
balbi
#10 lore raw

Re: [RFC/PATCH 3/3] mfd: twl4030-irq: implement bus_*lock

Hi,

Quoted 18 lines · Felipe Balbi
Quoted 16 lines · Mark Brown

Review

Quoted 6 lines · Felipe Balbi
(quoted diff) +6 -0
+static void twl4030_sih_bus_sync_unlock(unsigned int irq)
+{
+       struct sih_agent        *agent = get_irq_chip_data(irq);
+
+       mutex_unlock(&agent->irq_lock);
+}

I suspect you need to do some sort of sync with the hardware here - the _sync bit of the name comes from the fact that the mask and unmask stuff is still called with IRQs disabled and so can't touch and I2C chip, this is called after reenabling them give a chance for the updates done to be reflected in the hardware. The implementation everyone else has done is to update a register cache in the other functions then write that out here before dropping the mutex.

Quoted 2 lines

now that I look at some gpio chips I see what you're saying, will update that tomorrow. Thanks

I believe you meant something like below, I'll get back to this in a little while. Have lots to test:

From f15801a5d57041b7669222bdb7cccf8b592c2f63 Mon Sep 17 00:00:00 2001
From: Felipe Balbi <[email protected]>
Date: Tue, 28 Dec 2010 19:07:33 +0200
Subject: [PATCH 1/2] mfd: twl4030-irq: implement bus_*lock
Organization: Texas Instruments\n

drop all the locking around mask/unmask and
implement bus_lock and bus_sync_unlock methods.

---
 drivers/mfd/twl4030-irq.c |  101 ++++++++++++++++++++++-----------------------
 1 files changed, 50 insertions(+), 51 deletions(-)
drivers/mfd/twl4030-irq.c +50 -51
index 298956d..84f6066 100644
@@ -440,6 +440,7 @@ struct sih_agent {
 	const struct sih	*sih;
 
 	u32			imr;
+	bool			imr_change_pending;
 	u32			edge_change;
 
 	struct mutex		irq_lock;
@@ -450,64 +451,23 @@ struct sih_agent {
 static void twl4030_sih_mask(unsigned irq)
 {
 	struct sih_agent	*agent = get_irq_chip_data(irq);
-	const struct sih	*sih = agent->sih;
-
-	union {
-		u8	bytes[4];
-		u32	word;
-	}			imr;
-
-	int			status;
 
 	agent->imr |= BIT(irq - agent->irq_base);
-
-	mutex_lock(&agent->irq_lock);
-
-	/* byte[0] gets overwritten as we write ... */
-	imr.word = cpu_to_le32(agent->imr << 8);
-
-	/* write the whole mask ... simpler than subsetting it */
-	status = twl_i2c_write(sih->module, imr.bytes,
-			sih->mask[irq_line].imr_offset, sih->bytes_ixr);
-	if (status)
-		pr_err("twl4030: %s, %s --> %d\n", __func__,
-				"write", status);
-	mutex_unlock(&agent->irq_lock);
+	agent->imr_change_pending = true;
 }
 
 static void twl4030_sih_unmask(unsigned irq)
 {
 	struct sih_agent	*agent = get_irq_chip_data(irq);
-	const struct sih	*sih = agent->sih;
 
-	union {
-		u8	bytes[4];
-		u32	word;
-	}			imr;
-
-	int			status;
-
-	mutex_lock(&agent->irq_lock);
 	agent->imr &= ~BIT(irq - agent->irq_base);
-
-	/* byte[0] gets overwritten as we write ... */
-	imr.word = cpu_to_le32(agent->imr << 8);
-
-	/* write the whole mask ... simpler than subsetting it */
-	status = twl_i2c_write(sih->module, imr.bytes,
-			sih->mask[irq_line].imr_offset, sih->bytes_ixr);
-	if (status)
-		pr_err("twl4030: %s, %s --> %d\n", __func__,
-				"write", status);
-	mutex_unlock(&agent->irq_lock);
+	agent->imr_change_pending = true;
 }
 
 static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 {
 	struct sih_agent	*agent = get_irq_chip_data(irq);
-	const struct sih	*sih = agent->sih;
 	struct irq_desc		*desc = irq_to_desc(irq);
-	int			status = 0;
 
 	if (!desc) {
 		pr_err("twl4030: Invalid IRQ: %d\n", irq);
@@ -517,17 +477,57 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 	if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		return -EINVAL;
 
-	mutex_lock(&agent->irq_lock);
 	if ((desc->status & IRQ_TYPE_SENSE_MASK) != trigger) {
-		u8			bytes[6];
-		u32			edge_change;
+		agent->edge_change |= BIT(irq - agent->irq_base);
 
 		desc->status &= ~IRQ_TYPE_SENSE_MASK;
 		desc->status |= trigger;
-		agent->edge_change |= BIT(irq - agent->irq_base);
+	}
+
+	return 0;
+}
+
+static void twl4030_sih_bus_lock(unsigned int irq)
+{
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+
+	mutex_lock(&agent->irq_lock);
+}
+
+static void twl4030_sih_bus_sync_unlock(unsigned int irq)
+{
+	struct sih_agent	*agent = get_irq_chip_data(irq);
+	const struct sih	*sih = agent->sih;
+
+	int			status;
+
+	union {
+		u8	bytes[4];
+		u32	word;
+	}			imr;
+
+	if (agent->imr_change_pending) {
+		/* byte[0] gets overwritten as we write ... */
+		imr.word = cpu_to_le32(agent->imr << 8);
+
+		/* write the whole mask ... simpler than subsetting it */
+		status = twl_i2c_write(sih->module, imr.bytes,
+				sih->mask[irq_line].imr_offset, sih->bytes_ixr);
+		if (status)
+			pr_err("twl4030: %s, %s --> %d\n", __func__,
+					"write", status);
+		agent->imr_change_pending = false;
+	}
+
+	if (agent->edge_change) {
+		u32			edge_change;
+		u8			bytes[6];
+
 		edge_change = agent->edge_change;
+		agent->edge_change = 0;
 
-		/* Read, reserving first byte for write scratch.  Yes, this
+		/*
+		 * Read, reserving first byte for write scratch.  Yes, this
 		 * could be cached for some speedup ... but be careful about
 		 * any processor on the other IRQ line, EDR registers are
 		 * shared.
@@ -550,7 +550,6 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 			if (!d) {
 				pr_err("twl4030: Invalid IRQ: %d\n",
 						i + agent->irq_base);
-				status = -ENODEV;
 				goto out;
 			}
 
@@ -576,8 +575,6 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 
 out:
 	mutex_unlock(&agent->irq_lock);
-
-	return status;
 }
 
 static struct irq_chip twl4030_sih_irq_chip = {
@@ -585,6 +582,8 @@ static struct irq_chip twl4030_sih_irq_chip = {
 	.mask		= twl4030_sih_mask,
 	.unmask		= twl4030_sih_unmask,
 	.set_type	= twl4030_sih_set_type,
+	.bus_lock	= twl4030_sih_bus_lock,
+	.bus_sync_unlock = twl4030_sih_bus_sync_unlock,
 };
 
 /*----------------------------------------------------------------------*/
Signature · 4 lines
1.7.3.4.598.g85356

-- 
balbi
#11 lore raw

Re: [RFC/PATCH 3/3] mfd: twl4030-irq: implement bus_*lock

Quoted 2 lines · Felipe Balbi

I believe you meant something like below, I'll get back to this in a little while. Have lots to test:

Yes, that looks like what I'd expect.

#12 lore raw

Re: [RFC/PATCH 3/3] mfd: twl4030-irq: implement bus_*lock

Quoted 6 lines · Mark Brown
Quoted 2 lines · Felipe Balbi

I believe you meant something like below, I'll get back to this in a little while. Have lots to test:

Yes, that looks like what I'd expect.

Good, I'll clean the patches up and wait past the merge window before sending final versions.

Signature · 1 line
balbi
#13 lore raw

Re: [RFC/PATCH 0/3] TWL4030 IRQ Changes

In reply to Felipe Balbi

Hi,

Quoted 9 lines · Felipe Balbi

I dropped the twl6030-irq.c changes because that thing is a bit messy. I hope the original author will feel inspired and fix that one up.

Anyway, twl4030-irq.c seems to be going to the right direction now. Thanks to Mark Brown for pointing out the need to drop the locking and implement bus_lock/bus_sync_unlock methods.

Quoted 4 lines

Compile tested only with omap2plus_defconfig.

I'll test after merge window to be sure I have all the newest code.

when we finally move to struct irq_data, the below could be used. BTW, Thomas do you have any plans for exposing irq_data_to_desc() ?

8<------------------------------ cut here --------------------------------------

From 6069e3ddb34608fef0cb3dca3e544fca8deb3840 Mon Sep 17 00:00:00 2001
From: Felipe Balbi <[email protected]>
Date: Tue, 28 Dec 2010 19:33:22 +0200
Subject: [PATCH] mfd: twl4030-irq: switch to new methods
Organization: Texas Instruments\n

Move everybody to struct irq_data-based
methods.

---
 drivers/mfd/twl4030-irq.c |   86 ++++++++++++++++++++++++++++++++-------------
 1 files changed, 61 insertions(+), 25 deletions(-)
drivers/mfd/twl4030-irq.c +61 -25
index ff7bb93..ad6197c 100644
@@ -447,10 +447,14 @@ struct sih_agent {
 
 /*----------------------------------------------------------------------*/
 
-static void twl4030_sih_mask(unsigned irq)
+/* REVISIT define it here until IRQ Subsystem exports its implementation */
+#define irq_data_to_desc(data)	container_of(data, struct irq_desc, irq_data)
+
+static void twl4030_sih_mask(struct irq_data *data)
 {
-	struct sih_agent	*agent = get_irq_chip_data(irq);
-	const struct sih	*sih = agent->sih;
+	struct irq_desc		*d = irq_data_to_desc(data);
+	struct sih_agent	*agent;
+	const struct sih	*sih;
 
 	union {
 		u8	bytes[4];
@@ -458,7 +462,13 @@ static void twl4030_sih_mask(unsigned irq)
 	}			imr;
 
 	int			status;
+	int			irq = data->irq;
 
+	if (!d)
+		return;
+
+	agent = d->chip_data;
+	sih = agent->sih;
 	agent->imr |= BIT(irq - agent->irq_base);
 
 	/* byte[0] gets overwritten as we write ... */
@@ -472,10 +482,11 @@ static void twl4030_sih_mask(unsigned irq)
 				"write", status);
 }
 
-static void twl4030_sih_unmask(unsigned irq)
+static void twl4030_sih_unmask(struct irq_data *data)
 {
-	struct sih_agent	*agent = get_irq_chip_data(irq);
-	const struct sih	*sih = agent->sih;
+	struct irq_desc		*d = irq_data_to_desc(data);
+	struct sih_agent	*agent;
+	const struct sih	*sih;
 
 	union {
 		u8	bytes[4];
@@ -483,7 +494,13 @@ static void twl4030_sih_unmask(unsigned irq)
 	}			imr;
 
 	int			status;
+	int			irq = data->irq;
+
+	if (!d)
+		return;
 
+	agent = d->chip_data;
+	sih = agent->sih;
 	agent->imr &= ~BIT(irq - agent->irq_base);
 
 	/* byte[0] gets overwritten as we write ... */
@@ -497,14 +514,15 @@ static void twl4030_sih_unmask(unsigned irq)
 				"write", status);
 }
 
-static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
+static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger)
 {
-	struct sih_agent	*agent = get_irq_chip_data(irq);
-	const struct sih	*sih = agent->sih;
-	struct irq_desc		*desc = irq_to_desc(irq);
+	struct irq_desc		*d = irq_data_to_desc(data);
+	struct sih_agent	*agent;
+	const struct sih	*sih;
 	int			status = 0;
+	int			irq = data->irq;
 
-	if (!desc) {
+	if (!d) {
 		pr_err("twl4030: Invalid IRQ: %d\n", irq);
 		return -EINVAL;
 	}
@@ -512,12 +530,15 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 	if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		return -EINVAL;
 
-	if ((desc->status & IRQ_TYPE_SENSE_MASK) != trigger) {
+	agent = d->chip_data;
+	sih = agent->sih;
+
+	if ((d->status & IRQ_TYPE_SENSE_MASK) != trigger) {
 		u8			bytes[6];
 		u32			edge_change;
 
-		desc->status &= ~IRQ_TYPE_SENSE_MASK;
-		desc->status |= trigger;
+		d->status &= ~IRQ_TYPE_SENSE_MASK;
+		d->status |= trigger;
 		agent->edge_change |= BIT(irq - agent->irq_base);
 		edge_change = agent->edge_change;
 
@@ -537,7 +558,6 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 		/* Modify only the bits we know must change */
 		while (edge_change) {
 			int		i = fls(edge_change) - 1;
-			struct irq_desc	*d = irq_to_desc(i + agent->irq_base);
 			int		byte = 1 + (i >> 2);
 			int		off = (i & 0x3) * 2;
 
@@ -570,27 +590,43 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger)
 	return status;
 }
 
-static void twl4030_sih_bus_lock(unsigned int irq)
+static void twl4030_sih_bus_lock(struct irq_data *data)
 {
-	struct sih_agent	*agent = get_irq_chip_data(irq);
+	struct irq_desc		*d = irq_data_to_desc(data);
+	struct sih_agent	*agent;
+	int			irq = data->irq;
+
+	if (!d) {
+		pr_err("twl4030: Invalid IRQ: %d\n", irq);
+		return;
+	}
 
+	agent = d->chip_data;
 	mutex_lock(&agent->irq_lock);
 }
 
-static void twl4030_sih_bus_sync_unlock(unsigned int irq)
+static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
 {
-	struct sih_agent	*agent = get_irq_chip_data(irq);
+	struct irq_desc		*d = irq_data_to_desc(data);
+	struct sih_agent	*agent;
+	int			irq = data->irq;
+
+	if (!d) {
+		pr_err("twl4030: Invalid IRQ: %d\n", irq);
+		return;
+	}
 
+	agent = d->chip_data;
 	mutex_unlock(&agent->irq_lock);
 }
 
 static struct irq_chip twl4030_sih_irq_chip = {
-	.name		= "twl4030",
-	.mask		= twl4030_sih_mask,
-	.unmask		= twl4030_sih_unmask,
-	.set_type	= twl4030_sih_set_type,
-	.bus_lock	= twl4030_sih_bus_lock,
-	.bus_sync_unlock = twl4030_sih_bus_sync_unlock,
+	.name			= "twl4030",
+	.irq_mask		= twl4030_sih_mask,
+	.irq_unmask		= twl4030_sih_unmask,
+	.irq_set_type		= twl4030_sih_set_type,
+	.irq_bus_lock		= twl4030_sih_bus_lock,
+	.irq_bus_sync_unlock	= twl4030_sih_bus_sync_unlock,
 };
 
 /*----------------------------------------------------------------------*/
Signature · 4 lines
1.7.3.4.598.g85356

-- 
balbi
#14 lore raw

Re: [RFC/PATCH 0/3] TWL4030 IRQ Changes

Quoted 3 lines · Felipe Balbi

when we finally move to struct irq_data, the below could be used. BTW, Thomas do you have any plans for exposing irq_data_to_desc() ?

The general idea is to move to struct irq_data sooner rather than later (all the existing MFD drivers have already been converted).

drivers/mfd/twl4030-irq.c struct sih_agent { · line 451

Quoted 3 lines
drivers/mfd/twl4030-irq.c +2 -1
-static void twl4030_sih_mask(unsigned irq)
+/* REVISIT define it here until IRQ Subsystem exports its implementation */
+#define irq_data_to_desc(data)	container_of(data, struct irq_desc, irq_data)

It looks like all you're using this for is to get the chip_data? If that is the case you're looking for irq_data_get_irq_chip_data() which will go directly from the irq_data to the chip_data. I may have missed something, I only scanned the code.

#15 lore raw

Re: [RFC/PATCH 0/3] TWL4030 IRQ Changes

Hi,

Quoted 6 lines · Mark Brown
Quoted 3 lines

when we finally move to struct irq_data, the below could be used. BTW, Thomas do you have any plans for exposing irq_data_to_desc() ?

The general idea is to move to struct irq_data sooner rather than later (all the existing MFD drivers have already been converted).

Great, I'll put this one together then.

Quoted 8 lines

drivers/mfd/twl4030-irq.c struct sih_agent { · line 451

Quoted 3 lines
drivers/mfd/twl4030-irq.c +2 -1
-static void twl4030_sih_mask(unsigned irq)
+/* REVISIT define it here until IRQ Subsystem exports its implementation */
+#define irq_data_to_desc(data)	container_of(data, struct irq_desc, irq_data)

It looks like all you're using this for is to get the chip_data? If that is the case you're looking for irq_data_get_irq_chip_data() which will go directly from the irq_data to the chip_data. I may have missed something, I only scanned the code.

you're right, just didn't know that was such a helper. BTW, quite a big name.

Signature · 1 line
balbi
#16 lore raw

Re: [RFC/PATCH 1/2] mfd: twl6030-irq: move to threaded_irq

In reply to Mark Brown
Quoted 6 lines · Mark Brown

F

You shouldn't need this any more; the driver used to be faffing around
with this because it wasn't using genirq for this in the past.

Originally it couldn't, since genirq didn't support threaded IRQ handling...

...

Quoted 10 lines

Simiarly here as far as I know; the original code predates genirq
support for this so is doing some hairy stuff that is no longer
required and may actually be harmful.

What I'd expect to see from a conversion like this would be that most of
the locking/IRQ management stuff would be dropped

I'd expect that genirq solve all the issues and that its support be used. That's not the same as dropping anything except the initial code to handle what genirq didn't ... some locking/etc would still mostly need doing, but where genirq now handles it, that'd be preferable.
and the

Quoted 2 lines

bus_lock() and
bus_sync_unlock() operations would be implemented.

h
ISTR maybe four or five genirq updates in the area of threaded IRQ management, added so that issues the twl4030 driver needed to be solved could be solved in generic ways.

The first was just having threaded IRQ handlers, and another was I think removing the initial quick'n'dirty thread-per-irq restriction; there was no point in having a few dozen IRQ threads in e.g. a twl4030 driver, since two could never do constructive work concurrently.

I'm glad to see this conversion finally start. Even if all the threaded IRQ hooks don't get used to best advantage, it'll be an improvement.

#17 lore raw

Re: [RFC/PATCH 1/2] mfd: twl6030-irq: move to threaded_irq

Quoted 10 lines · David Brownell
Quoted 3 lines

What I'd expect to see from a conversion like this would be that most of
the locking/IRQ management stuff would be dropped

I'd expect that genirq solve all the issues and that its support be used. That's not the same as dropping anything except the initial code to handle what genirq didn't ... some locking/etc would still mostly need doing, but where genirq now handles it, that'd be preferable.

It should solve everything - there's rather a lot of I2C/SPI connected MFDs using genirq fully now without any hassle, the APIs are really straightforward and easy to use.

Quoted 8 lines

and the

Quoted 2 lines

bus_lock() and
bus_sync_unlock() operations would be implemented.

ISTR maybe four or five genirq updates in the area of threaded IRQ management, added so that issues the twl4030 driver needed to be solved could be solved in generic ways.

Yup - the main one on top of threaded IRQs was the bus_lock() and bus_sync_unlock() methods.

Quoted 6 lines

The first was just having threaded IRQ handlers, and another was I think removing the initial quick'n'dirty thread-per-irq restriction; there was no point in having a few dozen IRQ threads in e.g. a twl4030 driver, since two could never do constructive work concurrently.

The thread per IRQ thing is dealt with too, the secondary IRQs share the thread used for demux.

#18 lore raw

PATCH 2/2 mfd: twl4030-irq: move to threaded_irq

In reply to Felipe Balbi

... and while at that, also start using
handle_nested_irq() as we should.

---
 drivers/mfd/twl4030-irq.c |  133 ++++++++++++++-------------------------------
 1 files changed, 42 insertions(+), 91 deletions(-)
drivers/mfd/twl4030-irq.c +42 -91
index 5d3a147..e88d0c6 100644
@@ -278,91 +278,56 @@ static const struct sih sih_modules_twl5031[8] = {
 
 static unsigned twl4030_irq_base;
 
-static struct completion irq_event;
-
 /*
  * This thread processes interrupts reported by the Primary Interrupt Handler.
  */
-static int twl4030_irq_thread(void *data)
+static irqreturn_t twl4030_irq_thread(int irq, void *unused)
 {
-	long irq = (long)data;
-	static unsigned i2c_errors;
-	static const unsigned max_i2c_errors = 100;
-
-
-	current->flags |= PF_NOFREEZE;
-
-	while (!kthread_should_stop()) {
-		int ret;
-		int module_irq;
-		u8 pih_isr;
-
-		/* Wait for IRQ, then read PIH irq status (also blocking) */
-		wait_for_completion_interruptible(&irq_event);
-
-		ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
-					  REG_PIH_ISR_P1);
-		if (ret) {
-			pr_warning("twl4030: I2C error %d reading PIH ISR\n",
-					ret);
-			if (++i2c_errors >= max_i2c_errors) {
-				printk(KERN_ERR "Maximum I2C error count"
-						" exceeded.  Terminating %s.\n",
-						__func__);
-				break;
-			}
-			complete(&irq_event);
-			continue;
-		}
+	int ret;
+	int module_irq;
+	u8 pih_isr;
 
-		/* these handlers deal with the relevant SIH irq status */
-		local_irq_disable();
-		for (module_irq = twl4030_irq_base;
-				pih_isr;
-				pih_isr >>= 1, module_irq++) {
-			if (pih_isr & 0x1) {
-				struct irq_desc *d = irq_to_desc(module_irq);
-
-				if (!d) {
-					pr_err("twl4030: Invalid SIH IRQ: %d\n",
-					       module_irq);
-					return -EINVAL;
-				}
-
-				/* These can't be masked ... always warn
-				 * if we get any surprises.
-				 */
-				if (d->status & IRQ_DISABLED)
-					note_interrupt(module_irq, d,
-							IRQ_NONE);
-				else
-					d->handle_irq(module_irq, d);
+	disable_irq_nosync(irq);
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+			REG_PIH_ISR_P1);
+	if (ret) {
+		pr_warning("twl4030: I2C error %d reading PIH ISR\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	/* these handlers deal with the relevant SIH irq status */
+	local_irq_disable();
+	for (module_irq = twl4030_irq_base;
+			pih_isr;
+			pih_isr >>= 1, module_irq++) {
+		if (pih_isr & 0x1) {
+			struct irq_desc *d = irq_to_desc(module_irq);
+
+			if (!d) {
+				pr_err("twl4030: Invalid SIH IRQ: %d\n",
+						module_irq);
+				return IRQ_NONE;
 			}
-		}
-		local_irq_enable();
 
-		enable_irq(irq);
+			/* These can't be masked ... always warn
+			 * if we get any surprises.
+			 */
+			if (d->status & IRQ_DISABLED)
+				note_interrupt(module_irq, d,
+						IRQ_NONE);
+			else
+				handle_nested_irq(module_irq);
+		}
 	}
 
+	local_irq_enable();
+	enable_irq(irq);
+
 	return 0;
 }
 
-/*
- * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt.
- * This is a chained interrupt, so there is no desc->action method for it.
- * Now we need to query the interrupt controller in the twl4030 to determine
- * which module is generating the interrupt request.  However, we can't do i2c
- * transactions in interrupt context, so we must defer that work to a kernel
- * thread.  All we do here is acknowledge and mask the interrupt and wakeup
- * the kernel thread.
- */
-static irqreturn_t handle_twl4030_pih(int irq, void *devid)
-{
-	/* Acknowledge, clear *AND* mask the interrupt... */
-	disable_irq_nosync(irq);
-	complete(devid);
-	return IRQ_HANDLED;
-}
 /*----------------------------------------------------------------------*/
 
 /*
@@ -788,7 +753,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 
 	int			status;
 	int			i;
-	struct task_struct	*task;
 
 	/*
 	 * Mask and clear all TWL4030 interrupts since initially we do
@@ -830,28 +794,15 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 		goto fail;
 	}
 
-	/* install an irq handler to demultiplex the TWL4030 interrupt */
-
-
-	init_completion(&irq_event);
-
-	status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
-				"TWL4030-PIH", &irq_event);
+	status = request_threaded_irq(irq_num, NULL, twl4030_irq_thread,
+			IRQF_DISABLED, "TWL4030-PIH", NULL);
 	if (status < 0) {
 		pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
 		goto fail_rqirq;
 	}
 
-	task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
-								"twl4030-irq");
-	if (IS_ERR(task)) {
-		pr_err("twl4030: could not create irq %d thread!\n", irq_num);
-		status = PTR_ERR(task);
-		goto fail_kthread;
-	}
-	return status;
-fail_kthread:
-	free_irq(irq_num, &irq_event);
+	return 0;
+
 fail_rqirq:
 	/* clean up twl4030_sih_setup */
 fail:
Signature · 1 line
1.7.3.4.598.g85356