diff options
author | Herbert Valerio Riedel <hvr@gnu.org> | 2007-11-26 18:49:08 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-01-26 14:37:31 +0000 |
commit | 3c9a071d77e007464952a2d41b6686d98eb3f0b8 (patch) | |
tree | 7e346e17e81ed78adaeee94741c2ec68fd8fac28 /arch/arm/mach-ep93xx | |
parent | 7ca722533979d47563e75a40c86c405153818b83 (diff) | |
download | lwn-3c9a071d77e007464952a2d41b6686d98eb3f0b8.tar.gz lwn-3c9a071d77e007464952a2d41b6686d98eb3f0b8.zip |
[ARM] 4670/1: ep93xx: implement IRQT_BOTHEDGE gpio irq sense type
Having a both-edge sensitive irq trigger type is required for the
generic gpio-keys input driver; alas the ep93xx does not support
both-edge gpio triggers in hardware, so this patch implements them by
switching edge polarity on each triggered interrupt. This is the same
approach as taken by the Orion SoC both-edge gpio irq support
implementation.
Signed-off-by: Herbert Valerio Riedel <hvr@gnu.org>
Acked-by: Lennert Buytenhek <buytenh@wantstofly.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-ep93xx')
-rw-r--r-- | arch/arm/mach-ep93xx/core.c | 64 |
1 files changed, 53 insertions, 11 deletions
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 58957d7a5763..2788da06417c 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -3,6 +3,7 @@ * Core routines for Cirrus EP93xx chips. * * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> + * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> * * Thanks go to Michael Burian and Ray Lehtiniemi for their key * role in the ep93xx linux community. @@ -315,12 +316,29 @@ static void ep93xx_gpio_f_irq_handler(unsigned int irq, struct irq_desc *desc) desc_handle_irq(gpio_irq, irq_desc + gpio_irq); } +static void ep93xx_gpio_irq_ack(unsigned int irq) +{ + int line = irq_to_gpio(irq); + int port = line >> 3; + int port_mask = 1 << (line & 7); + + if ((irq_desc[irq].status & IRQ_TYPE_SENSE_MASK) == IRQT_BOTHEDGE) { + gpio_int_type2[port] ^= port_mask; /* switch edge direction */ + update_gpio_int_params(port); + } + + __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); +} + static void ep93xx_gpio_irq_mask_ack(unsigned int irq) { int line = irq_to_gpio(irq); int port = line >> 3; int port_mask = 1 << (line & 7); + if ((irq_desc[irq].status & IRQ_TYPE_SENSE_MASK) == IRQT_BOTHEDGE) + gpio_int_type2[port] ^= port_mask; /* switch edge direction */ + gpio_int_unmasked[port] &= ~port_mask; update_gpio_int_params(port); @@ -353,31 +371,54 @@ static void ep93xx_gpio_irq_unmask(unsigned int irq) */ static int ep93xx_gpio_irq_type(unsigned int irq, unsigned int type) { + struct irq_desc *desc = irq_desc + irq; const int gpio = irq_to_gpio(irq); const int port = gpio >> 3; const int port_mask = 1 << (gpio & 7); ep93xx_gpio_set_direction(gpio, GPIO_IN); - if (type & IRQT_RISING) { - gpio_int_enabled[port] |= port_mask; + switch (type) { + case IRQT_RISING: gpio_int_type1[port] |= port_mask; gpio_int_type2[port] |= port_mask; - } else if (type & IRQT_FALLING) { - gpio_int_enabled[port] |= port_mask; + desc->handle_irq = handle_edge_irq; + break; + case IRQT_FALLING: gpio_int_type1[port] |= port_mask; gpio_int_type2[port] &= ~port_mask; - } else if (type & IRQT_HIGH) { - gpio_int_enabled[port] |= port_mask; + desc->handle_irq = handle_edge_irq; + break; + case IRQT_HIGH: gpio_int_type1[port] &= ~port_mask; gpio_int_type2[port] |= port_mask; - } else if (type & IRQT_LOW) { - gpio_int_enabled[port] |= port_mask; + desc->handle_irq = handle_level_irq; + break; + case IRQT_LOW: gpio_int_type1[port] &= ~port_mask; gpio_int_type2[port] &= ~port_mask; - } else { - gpio_int_enabled[port] &= ~port_mask; + desc->handle_irq = handle_level_irq; + break; + case IRQT_BOTHEDGE: + gpio_int_type1[port] |= port_mask; + /* set initial polarity based on current input level */ + if (gpio_get_value(gpio)) + gpio_int_type2[port] &= ~port_mask; /* falling */ + else + gpio_int_type2[port] |= port_mask; /* rising */ + desc->handle_irq = handle_edge_irq; + break; + default: + pr_err("ep93xx: failed to set irq type %d for gpio %d\n", + type, gpio); + return -EINVAL; } + + gpio_int_enabled[port] |= port_mask; + + desc->status &= ~IRQ_TYPE_SENSE_MASK; + desc->status |= type & IRQ_TYPE_SENSE_MASK; + update_gpio_int_params(port); return 0; @@ -385,7 +426,8 @@ static int ep93xx_gpio_irq_type(unsigned int irq, unsigned int type) static struct irq_chip ep93xx_gpio_irq_chip = { .name = "GPIO", - .ack = ep93xx_gpio_irq_mask_ack, + .ack = ep93xx_gpio_irq_ack, + .mask_ack = ep93xx_gpio_irq_mask_ack, .mask = ep93xx_gpio_irq_mask, .unmask = ep93xx_gpio_irq_unmask, .set_type = ep93xx_gpio_irq_type, |