* David Brownell <david-b@xxxxxxxxxxx> [081006 10:42]: > From: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx> > > 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 <dbrownell@xxxxxxxxxxxxxxxxxxxxx> > --- > 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 majordomo@xxxxxxxxxxxxxxx > 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html