* David Brownell <[EMAIL PROTECTED]> [081006 10:42]:
> From: David Brownell <[EMAIL PROTECTED]>
>
> Simplify and correct TWL4030 GPIO IRQ handling:
>
> - support mask() not just unmask()
> - use genirq handle_edge_irq() handler not custom hacks
> - let that handle (correct) accounting of chained IRQ counts
> - use the more efficient clear-on-read mechanism
> - don't misuse IRQ_LEVEL
> - remove some superfluous locking
> - locking fix: all irq_chip data needs spinlock protection
>
> Cleanups:
> - give the helper thread a more accurate name
> - don't name the NOP ack() method misleadingly
> - use generic_handle_irq(), not a manually unrolled version thereof
> - comment fixes
>
> Note that the previous IRQ dispatch code was somewhat confused.
> It seemed not to know that it was working with edge triggered
> interrupts, much less ones which could be transparently acked.
>
> (Also note that COR=1 doesn't enable Clear-On-Read for all modules;
> some are documented as using COR=0 for that. GPIO uses COR=1.)
Pushed.
Tony
> Signed-off-by: David Brownell <[EMAIL PROTECTED]>
> ---
> First step of the plan to share more core irq code ...
>
> drivers/gpio/twl4030-gpio.c | 265 +++++++++++++++---------------------------
> 1 file changed, 96 insertions(+), 169 deletions(-)
>
> --- a/drivers/gpio/twl4030-gpio.c
> +++ b/drivers/gpio/twl4030-gpio.c
> @@ -26,11 +26,8 @@
> */
>
> #include <linux/module.h>
> -#include <linux/kernel_stat.h>
> #include <linux/init.h>
> -#include <linux/time.h>
> #include <linux/interrupt.h>
> -#include <linux/device.h>
> #include <linux/kthread.h>
> #include <linux/irq.h>
> #include <linux/gpio.h>
> @@ -92,17 +89,18 @@ static DEFINE_MUTEX(gpio_lock);
> /* store usage of each GPIO. - each bit represents one GPIO */
> static unsigned int gpio_usage_count;
>
> -/* shadow the imr register */
> -static unsigned int gpio_imr_shadow;
> +/* protect what irq_chip modifies through its helper task */
> +static DEFINE_SPINLOCK(gpio_spinlock);
>
> -/* bitmask of pending requests to unmask gpio interrupts */
> -static unsigned int gpio_pending_unmask;
> +/* shadow the imr register; updates are delayed */
> +static u32 gpio_imr_shadow;
> +static bool gpio_pending_mask;
>
> /* bitmask of requests to set gpio irq trigger type */
> static unsigned int gpio_pending_trigger;
>
> -/* pointer to gpio unmask thread struct */
> -static struct task_struct *gpio_unmask_thread;
> +/* pointer to helper thread */
> +static struct task_struct *gpio_helper_thread;
>
> /*
> * Helper functions to read and write the GPIO ISR and IMR registers as
> @@ -110,6 +108,9 @@ static struct task_struct *gpio_unmask_t
> * The caller must hold gpio_lock.
> */
>
> +/* NOTE: only the IRQ dispatcher may read ISR; reading it has
> + * side effects, because of the clear-on-read mechanism (COR=1).
> + */
> static int gpio_read_isr(unsigned int *isr)
> {
> int ret;
> @@ -123,19 +124,9 @@ static int gpio_read_isr(unsigned int *i
> return ret;
> }
>
> -static int gpio_write_isr(unsigned int isr)
> -{
> - isr &= GPIO_32_MASK;
> - /*
> - * The buffer passed to the twl4030_i2c_write() routine must have an
> - * extra byte at the beginning reserved for its internal use.
> - */
> - isr <<= 8;
> - isr = cpu_to_le32(isr);
> - return twl4030_i2c_write(TWL4030_MODULE_GPIO, (u8 *) &isr,
> - REG_GPIO_ISR1A, 3);
> -}
> -
> +/* IMR is written only during initialization and
> + * by request of the irq_chip code.
> + */
> static int gpio_write_imr(unsigned int imr)
> {
> imr &= GPIO_32_MASK;
> @@ -150,66 +141,54 @@ static int gpio_write_imr(unsigned int i
> }
>
> /*
> - * These routines are analagous to the irqchip methods, but they are designed
> - * to be called from thread context with cpu interrupts enabled and with no
> - * locked spinlocks. We call these routines from our custom IRQ handler
> - * instead of the usual irqchip methods.
> - */
> -static void twl4030_gpio_mask_and_ack(unsigned int irq)
> -{
> - int gpio = irq - twl4030_gpio_irq_base;
> -
> - mutex_lock(&gpio_lock);
> - /* mask */
> - gpio_imr_shadow |= (1 << gpio);
> - gpio_write_imr(gpio_imr_shadow);
> - /* ack */
> - gpio_write_isr(1 << gpio);
> - mutex_unlock(&gpio_lock);
> -}
> -
> -static void twl4030_gpio_unmask(unsigned int irq)
> -{
> - int gpio = irq - twl4030_gpio_irq_base;
> -
> - mutex_lock(&gpio_lock);
> - gpio_imr_shadow &= ~(1 << gpio);
> - gpio_write_imr(gpio_imr_shadow);
> - mutex_unlock(&gpio_lock);
> -}
> -
> -/*
> * These are the irqchip methods for the TWL4030 GPIO interrupts.
> * Our IRQ handle method doesn't call these, but they will be called by
> * other routines such as setup_irq() and enable_irq(). They are called
> - * with cpu interrupts disabled and with a lock on the irq_controller_lock
> + * with cpu interrupts disabled and with a lock on the irq_desc.lock
> * spinlock. This complicates matters, because accessing the TWL4030 GPIO
> * interrupt controller requires I2C bus transactions that can't be initiated
> * in this context. Our solution is to defer accessing the interrupt
> - * controller to a kernel thread. We only need to support the unmask method.
> + * controller to a kernel thread.
> */
>
> -static void twl4030_gpio_irq_mask_and_ack(unsigned int irq)
> +static void twl4030_gpio_irq_ack(unsigned int irq)
> {
> + /* NOP -- dispatcher acks when it reads ISR */
> }
>
> static void twl4030_gpio_irq_mask(unsigned int irq)
> {
> + int gpio = irq - twl4030_gpio_irq_base;
> + u32 mask = 1 << gpio;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&gpio_spinlock, flags);
> + gpio_pending_mask = true;
> + gpio_imr_shadow |= mask;
> + if (gpio_helper_thread && gpio_helper_thread->state != TASK_RUNNING)
> + wake_up_process(gpio_helper_thread);
> + spin_unlock_irqrestore(&gpio_spinlock, flags);
> }
>
> static void twl4030_gpio_irq_unmask(unsigned int irq)
> {
> int gpio = irq - twl4030_gpio_irq_base;
> + u32 mask = 1 << gpio;
> + unsigned long flags;
>
> - gpio_pending_unmask |= (1 << gpio);
> - if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING)
> - wake_up_process(gpio_unmask_thread);
> + spin_lock_irqsave(&gpio_spinlock, flags);
> + gpio_pending_mask = true;
> + gpio_imr_shadow &= ~mask;
> + if (gpio_helper_thread && gpio_helper_thread->state != TASK_RUNNING)
> + wake_up_process(gpio_helper_thread);
> + spin_unlock_irqrestore(&gpio_spinlock, flags);
> }
>
> static int twl4030_gpio_irq_set_type(unsigned int irq, unsigned trigger)
> {
> struct irq_desc *desc = irq_desc + irq;
> int gpio = irq - twl4030_gpio_irq_base;
> + unsigned long flags;
>
> trigger &= IRQ_TYPE_SENSE_MASK;
> if (trigger & ~IRQ_TYPE_EDGE_BOTH)
> @@ -220,19 +199,18 @@ static int twl4030_gpio_irq_set_type(uns
> desc->status &= ~IRQ_TYPE_SENSE_MASK;
> desc->status |= trigger;
>
> - /* REVISIT This makes the "unmask" thread do double duty,
> - * updating IRQ trigger modes too. Rename appropriately...
> - */
> + spin_lock_irqsave(&gpio_spinlock, flags);
> gpio_pending_trigger |= (1 << gpio);
> - if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING)
> - wake_up_process(gpio_unmask_thread);
> + if (gpio_helper_thread && gpio_helper_thread->state != TASK_RUNNING)
> + wake_up_process(gpio_helper_thread);
> + spin_unlock_irqrestore(&gpio_spinlock, flags);
>
> return 0;
> }
>
> static struct irq_chip twl4030_gpio_irq_chip = {
> .name = "twl4030",
> - .ack = twl4030_gpio_irq_mask_and_ack,
> + .ack = twl4030_gpio_irq_ack,
> .mask = twl4030_gpio_irq_mask,
> .unmask = twl4030_gpio_irq_unmask,
> .set_type = twl4030_gpio_irq_set_type,
> @@ -281,7 +259,7 @@ int twl4030_request_gpio(int gpio)
> if (!gpio_usage_count) {
> ret = gpio_twl4030_write(REG_GPIO_CTRL,
> MASK_GPIO_CTRL_GPIO_ON);
> - ret = gpio_twl4030_write(REG_GPIO_SIH_CTRL, 0x00);
> +
> }
> if (!ret)
> gpio_usage_count |= BIT(gpio);
> @@ -475,37 +453,42 @@ int twl4030_set_gpio_card_detect(int gpi
> /* MODULE FUNCTIONS */
>
> /*
> - * gpio_unmask_thread() runs as a kernel thread. It is awakened by the
> unmask
> - * method for the GPIO interrupts. It unmasks all of the GPIO interrupts
> - * specified in the gpio_pending_unmask bitmask. We have to do the unmasking
> - * in a kernel thread rather than directly in the unmask method because of
> the
> + * gpio_helper_thread() is a kernel thread that is awakened by irq_chip
> + * methods to update IRQ masks and trigger modes.
> + *
> + * We must do this in a thread rather than in irq_chip methods because of the
> * need to access the TWL4030 via the I2C bus. Note that we don't need to be
> * concerned about race conditions where the request to unmask a GPIO
> interrupt
> * has already been cancelled before this thread does the unmasking. If a
> GPIO
> * interrupt is improperly unmasked, then the IRQ handler for it will mask it
> * when an interrupt occurs.
> */
> -static int twl4030_gpio_unmask_thread(void *data)
> +static int twl4030_gpio_helper_thread(void *dev)
> {
> current->flags |= PF_NOFREEZE;
>
> while (!kthread_should_stop()) {
> int irq;
> - unsigned int gpio_unmask;
> + bool do_mask;
> + u32 local_imr;
> unsigned int gpio_trigger;
>
> - local_irq_disable();
> - gpio_unmask = gpio_pending_unmask;
> - gpio_pending_unmask = 0;
> + spin_lock_irq(&gpio_spinlock);
> +
> + do_mask = gpio_pending_mask;
> + gpio_pending_mask = false;
> + local_imr = gpio_imr_shadow;
>
> gpio_trigger = gpio_pending_trigger;
> gpio_pending_trigger = 0;
> - local_irq_enable();
>
> - for (irq = twl4030_gpio_irq_base; 0 != gpio_unmask;
> - gpio_unmask >>= 1, irq++) {
> - if (gpio_unmask & 0x1)
> - twl4030_gpio_unmask(irq);
> + spin_unlock_irq(&gpio_spinlock);
> +
> + if (do_mask) {
> + int status = gpio_write_imr(local_imr);
> +
> + if (status)
> + dev_err(dev, "write IMR err %d\n", status);
> }
>
> for (irq = twl4030_gpio_irq_base;
> @@ -526,10 +509,10 @@ static int twl4030_gpio_unmask_thread(vo
> type);
> }
>
> - local_irq_disable();
> - if (!gpio_pending_unmask && !gpio_pending_trigger)
> + spin_lock_irq(&gpio_spinlock);
> + if (!gpio_pending_mask && !gpio_pending_trigger)
> set_current_state(TASK_INTERRUPTIBLE);
> - local_irq_enable();
> + spin_unlock_irq(&gpio_spinlock);
>
> schedule();
> }
> @@ -538,51 +521,6 @@ static int twl4030_gpio_unmask_thread(vo
> }
>
> /*
> - * do_twl4030_gpio_irq() is the desc->handle method for each of the twl4030
> - * gpio interrupts. It executes in kernel thread context.
> - * On entry, cpu interrupts are enabled.
> - */
> -static void do_twl4030_gpio_irq(unsigned int irq, irq_desc_t *desc)
> -{
> - struct irqaction *action;
> - const unsigned int cpu = smp_processor_id();
> -
> - desc->status |= IRQ_LEVEL;
> -
> - /*
> - * Acknowledge, clear _AND_ disable the interrupt.
> - */
> - twl4030_gpio_mask_and_ack(irq);
> -
> - if (!desc->depth) {
> - kstat_cpu(cpu).irqs[irq]++;
> -
> - action = desc->action;
> - if (action) {
> - int ret;
> - int status = 0;
> - int retval = 0;
> - do {
> - /* Call the ISR with cpu interrupts enabled. */
> - ret = action->handler(irq, action->dev_id);
> - if (ret == IRQ_HANDLED)
> - status |= action->flags;
> - retval |= ret;
> - action = action->next;
> - } while (action);
> -
> - if (retval != IRQ_HANDLED)
> - printk(KERN_ERR "ISR for TWL4030 GPIO"
> - " irq %d can't handle interrupt\n",
> - irq);
> -
> - if (!desc->depth)
> - twl4030_gpio_unmask(irq);
> - }
> - }
> -}
> -
> -/*
> * do_twl4030_gpio_module_irq() is the desc->handle method for the twl4030
> gpio
> * module interrupt. It executes in kernel thread context.
> * This is a chained interrupt, so there is no desc->action method for it.
> @@ -593,38 +531,21 @@ static void do_twl4030_gpio_irq(unsigned
> */
> static void do_twl4030_gpio_module_irq(unsigned int irq, irq_desc_t *desc)
> {
> - const unsigned int cpu = smp_processor_id();
> -
> - desc->status |= IRQ_LEVEL;
> - /*
> - * The desc->handle method would normally call the desc->chip->ack
> - * method here, but we won't bother since our ack method is NULL.
> - */
> - if (!desc->depth) {
> - int gpio_irq;
> - unsigned int gpio_isr;
> -
> - kstat_cpu(cpu).irqs[irq]++;
> - local_irq_enable();
> -
> - mutex_lock(&gpio_lock);
> - if (gpio_read_isr(&gpio_isr))
> - gpio_isr = 0;
> - mutex_unlock(&gpio_lock);
> + int gpio_irq;
> + unsigned int gpio_isr;
>
> - for (gpio_irq = twl4030_gpio_irq_base; 0 != gpio_isr;
> - gpio_isr >>= 1, gpio_irq++) {
> - if (gpio_isr & 0x1) {
> - irq_desc_t *d = irq_desc + gpio_irq;
> - d->handle_irq(gpio_irq, d);
> - }
> - }
> + /* PIH irqs like this can't be individually masked or acked ...
> + * so we don't even call the PIH irq_chip stub methods.
> + */
> + local_irq_enable();
> + if (gpio_read_isr(&gpio_isr))
> + gpio_isr = 0;
> + local_irq_disable();
>
> - local_irq_disable();
> - /*
> - * Here is where we should call the unmask method, but again we
> - * won't bother since it is NULL.
> - */
> + for (gpio_irq = twl4030_gpio_irq_base; 0 != gpio_isr;
> + gpio_isr >>= 1, gpio_irq++) {
> + if (gpio_isr & 0x1)
> + generic_handle_irq(gpio_irq);
> }
> }
>
> @@ -706,7 +627,7 @@ static int __devinit gpio_twl4030_probe(
> int irq = 0;
>
> /* All GPIO interrupts are initially masked */
> - gpio_pending_unmask = 0;
> + gpio_pending_mask = false;
> gpio_imr_shadow = GPIO_32_MASK;
> ret = gpio_write_imr(gpio_imr_shadow);
>
> @@ -733,15 +654,14 @@ static int __devinit gpio_twl4030_probe(
>
> if (!ret) {
> /*
> - * Create a kernel thread to handle deferred unmasking of gpio
> + * Create a kernel thread to handle deferred operations on gpio
> * interrupts.
> */
> - gpio_unmask_thread = kthread_create(twl4030_gpio_unmask_thread,
> - NULL, "twl4030 gpio");
> - if (!gpio_unmask_thread) {
> + gpio_helper_thread = kthread_create(twl4030_gpio_helper_thread,
> + &pdev->dev, "twl4030 gpio");
> + if (!gpio_helper_thread) {
> dev_err(&pdev->dev,
> - "could not create twl4030 gpio unmask"
> - " thread!\n");
> + "could not create helper thread!\n");
> ret = -ENOMEM;
> }
> }
> @@ -751,19 +671,26 @@ static int __devinit gpio_twl4030_probe(
> for (irq = twl4030_gpio_irq_base; irq < twl4030_gpio_irq_end;
> irq++) {
> set_irq_chip_and_handler(irq, &twl4030_gpio_irq_chip,
> - do_twl4030_gpio_irq);
> + handle_edge_irq);
> activate_irq(irq);
> }
>
> /* gpio module IRQ */
> irq = platform_get_irq(pdev, 0);
>
> + /* COR=1 means irqs are acked by reading ISR
> + * PENDDIS=0 means pending events enabled
> + * EXCLEN=0 means route to both irq lines
> + */
> + gpio_twl4030_write(REG_GPIO_SIH_CTRL,
> + TWL4030_SIH_CTRL_COR_MASK);
> +
> /*
> * Install an irq handler to demultiplex the gpio module
> * interrupt.
> */
> set_irq_chained_handler(irq, do_twl4030_gpio_module_irq);
> - wake_up_process(gpio_unmask_thread);
> + wake_up_process(gpio_helper_thread);
>
> dev_info(&pdev->dev, "IRQ %d chains IRQs %d..%d\n", irq,
> twl4030_gpio_irq_base, twl4030_gpio_irq_end - 1);
> @@ -837,10 +764,10 @@ static int __devexit gpio_twl4030_remove
> for (irq = twl4030_gpio_irq_base; irq < twl4030_gpio_irq_end; irq++)
> set_irq_handler(irq, NULL);
>
> - /* stop the gpio unmask kernel thread */
> - if (gpio_unmask_thread) {
> - kthread_stop(gpio_unmask_thread);
> - gpio_unmask_thread = NULL;
> + /* stop the gpio helper kernel thread */
> + if (gpio_helper_thread) {
> + kthread_stop(gpio_helper_thread);
> + gpio_helper_thread = NULL;
> }
>
> return 0;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [EMAIL PROTECTED]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html