From: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx> Make twl4030 GPIO support set_type() request. Plus a bit of cleanup for the simple functions. Signed-off-by: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx> Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx> --- drivers/gpio/twl4030-gpio.c | 84 +++++++++++++++++++++++++++++++++++++----- 1 files changed, 74 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c index b65d476..e709fbb 100644 --- a/drivers/gpio/twl4030-gpio.c +++ b/drivers/gpio/twl4030-gpio.c @@ -153,6 +153,9 @@ static unsigned int gpio_imr_shadow; /* bitmask of pending requests to unmask gpio interrupts */ static unsigned int gpio_pending_unmask; +/* 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; @@ -241,10 +244,15 @@ static void twl4030_gpio_unmask(unsigned int irq) * controller to a kernel thread. We only need to support the unmask method. */ -static void twl4030_gpio_mask_and_ack_irqchip(unsigned int irq) {} -static void twl4030_gpio_mask_irqchip(unsigned int irq) {} +static void twl4030_gpio_irq_mask_and_ack(unsigned int irq) +{ +} + +static void twl4030_gpio_irq_mask(unsigned int irq) +{ +} -static void twl4030_gpio_unmask_irqchip(unsigned int irq) +static void twl4030_gpio_irq_unmask(unsigned int irq) { int gpio = irq - twl4030_gpio_irq_base; @@ -253,11 +261,36 @@ static void twl4030_gpio_unmask_irqchip(unsigned int irq) wake_up_process(gpio_unmask_thread); } +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; + + trigger &= IRQ_TYPE_SENSE_MASK; + if (trigger & ~IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + if ((desc->status & IRQ_TYPE_SENSE_MASK) == trigger) + return 0; + + 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... + */ + gpio_pending_trigger |= (1 << gpio); + if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING) + wake_up_process(gpio_unmask_thread); + + return 0; +} + static struct irq_chip twl4030_gpio_irq_chip = { - .name = "twl4030", - .ack = twl4030_gpio_mask_and_ack_irqchip, - .mask = twl4030_gpio_mask_irqchip, - .unmask = twl4030_gpio_unmask_irqchip, + .name = "twl4030", + .ack = twl4030_gpio_irq_mask_and_ack, + .mask = twl4030_gpio_irq_mask, + .unmask = twl4030_gpio_irq_unmask, + .set_type = twl4030_gpio_irq_set_type, }; /* @@ -478,8 +511,6 @@ int twl4030_set_gpio_pull(int gpio, int pull_dircn) /* * Configure Edge control for a GPIO pin on TWL4030 - * - * FIXME this should just be the irq_chip.set_type() method */ int twl4030_set_gpio_edge_ctrl(int gpio, int edge) { @@ -601,10 +632,14 @@ static int twl4030_gpio_unmask_thread(void *data) while (!kthread_should_stop()) { int irq; unsigned int gpio_unmask; + unsigned int gpio_trigger; local_irq_disable(); gpio_unmask = gpio_pending_unmask; gpio_pending_unmask = 0; + + gpio_trigger = gpio_pending_trigger; + gpio_pending_trigger = 0; local_irq_enable(); for (irq = twl4030_gpio_irq_base; 0 != gpio_unmask; @@ -613,8 +648,37 @@ static int twl4030_gpio_unmask_thread(void *data) twl4030_gpio_unmask(irq); } + for (irq = twl4030_gpio_irq_base; + gpio_trigger; + gpio_trigger >>= 1, irq++) { + struct irq_desc *desc; + unsigned type, edge; + + if (!(gpio_trigger & 0x1)) + continue; + + desc = irq_desc + irq; + spin_lock_irq(&desc->lock); + type = desc->status & IRQ_TYPE_SENSE_MASK; + spin_unlock_irq(&desc->lock); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + edge = TWL4030_GPIO_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + edge = TWL4030_GPIO_EDGE_FALLING; + break; + default: + edge = TWL4030_GPIO_EDGE_RISING + | TWL4030_GPIO_EDGE_FALLING; + break; + } + twl4030_set_gpio_edge_ctrl(irq, edge); + } + local_irq_disable(); - if (!gpio_pending_unmask) + if (!gpio_pending_unmask && !gpio_pending_trigger) set_current_state(TASK_INTERRUPTIBLE); local_irq_enable(); -- 1.6.0.1.196.g01914 -- 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