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. Signed-off-by: Felipe Balbi <balbi@xxxxxx> --- drivers/mfd/twl4030-irq.c | 226 +++++++++++++++++++-------------------------- 1 files changed, 96 insertions(+), 130 deletions(-) diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 91331a7..298956d 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -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; } -- 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