diff options
author | Lennert Buytenhek <buytenh@wantstofly.org> | 2008-10-20 01:51:03 +0200 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2008-12-20 12:24:05 -0500 |
commit | 07332318f33da6acd88abb762a8b6febdfc560a3 (patch) | |
tree | 911c34bb215427f1f8771a52e7c3d4433397ab83 /arch | |
parent | 9569dae75f6f6987e79fa26cf6da3fc24006c996 (diff) | |
download | op-kernel-dev-07332318f33da6acd88abb762a8b6febdfc560a3.zip op-kernel-dev-07332318f33da6acd88abb762a8b6febdfc560a3.tar.gz |
[ARM] Orion: share GPIO IRQ handling code
Split off Orion GPIO IRQ handling code into plat-orion/.
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: Nicolas Pitre <nico@marvell.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-orion5x/include/mach/gpio.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/include/mach/orion5x.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-orion5x/irq.c | 183 | ||||
-rw-r--r-- | arch/arm/plat-orion/gpio.c | 176 | ||||
-rw-r--r-- | arch/arm/plat-orion/include/plat/gpio.h | 7 |
5 files changed, 200 insertions, 172 deletions
diff --git a/arch/arm/mach-orion5x/include/mach/gpio.h b/arch/arm/mach-orion5x/include/mach/gpio.h index a1a387b..d8182e8 100644 --- a/arch/arm/mach-orion5x/include/mach/gpio.h +++ b/arch/arm/mach-orion5x/include/mach/gpio.h @@ -19,6 +19,9 @@ #define GPIO_BLINK_EN(pin) ORION5X_DEV_BUS_REG(0x108) #define GPIO_IN_POL(pin) ORION5X_DEV_BUS_REG(0x10c) #define GPIO_DATA_IN(pin) ORION5X_DEV_BUS_REG(0x110) +#define GPIO_EDGE_CAUSE(pin) ORION5X_DEV_BUS_REG(0x114) +#define GPIO_EDGE_MASK(pin) ORION5X_DEV_BUS_REG(0x118) +#define GPIO_LEVEL_MASK(pin) ORION5X_DEV_BUS_REG(0x11c) static inline int gpio_to_irq(int pin) { diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h index a891508..67bda31 100644 --- a/arch/arm/mach-orion5x/include/mach/orion5x.h +++ b/arch/arm/mach-orion5x/include/mach/orion5x.h @@ -134,9 +134,6 @@ #define MPP_16_19_CTRL ORION5X_DEV_BUS_REG(0x050) #define MPP_DEV_CTRL ORION5X_DEV_BUS_REG(0x008) #define MPP_RESET_SAMPLE ORION5X_DEV_BUS_REG(0x010) -#define GPIO_EDGE_CAUSE ORION5X_DEV_BUS_REG(0x114) -#define GPIO_EDGE_MASK ORION5X_DEV_BUS_REG(0x118) -#define GPIO_LEVEL_MASK ORION5X_DEV_BUS_REG(0x11c) #define DEV_BANK_0_PARAM ORION5X_DEV_BUS_REG(0x45c) #define DEV_BANK_1_PARAM ORION5X_DEV_BUS_REG(0x460) #define DEV_BANK_2_PARAM ORION5X_DEV_BUS_REG(0x464) diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c index 6b2f1353..0caae43 100644 --- a/arch/arm/mach-orion5x/irq.c +++ b/arch/arm/mach-orion5x/irq.c @@ -19,193 +19,38 @@ #include <plat/irq.h> #include "common.h" -/***************************************************************************** - * Orion GPIO IRQ - * - * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same - * value of the line or the opposite value. - * - * Level IRQ handlers: DATA_IN is used directly as cause register. - * Interrupt are masked by LEVEL_MASK registers. - * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. - * Interrupt are masked by EDGE_MASK registers. - * Both-edge handlers: Similar to regular Edge handlers, but also swaps - * the polarity to catch the next line transaction. - * This is a race condition that might not perfectly - * work on some use cases. - * - * Every eight GPIO lines are grouped (OR'ed) before going up to main - * cause register. - * - * EDGE cause mask - * data-in /--------| |-----| |----\ - * -----| |----- ---- to main cause reg - * X \----------------| |----/ - * polarity LEVEL mask - * - ****************************************************************************/ -static void orion5x_gpio_irq_ack(u32 irq) -{ - int pin = irq_to_gpio(irq); - if (irq_desc[irq].status & IRQ_LEVEL) - /* - * Mask bit for level interrupt - */ - orion5x_clrbits(GPIO_LEVEL_MASK, 1 << pin); - else - /* - * Clear casue bit for egde interrupt - */ - orion5x_clrbits(GPIO_EDGE_CAUSE, 1 << pin); -} - -static void orion5x_gpio_irq_mask(u32 irq) -{ - int pin = irq_to_gpio(irq); - if (irq_desc[irq].status & IRQ_LEVEL) - orion5x_clrbits(GPIO_LEVEL_MASK, 1 << pin); - else - orion5x_clrbits(GPIO_EDGE_MASK, 1 << pin); -} - -static void orion5x_gpio_irq_unmask(u32 irq) +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { - int pin = irq_to_gpio(irq); - if (irq_desc[irq].status & IRQ_LEVEL) - orion5x_setbits(GPIO_LEVEL_MASK, 1 << pin); - else - orion5x_setbits(GPIO_EDGE_MASK, 1 << pin); -} - -static int orion5x_gpio_set_irq_type(u32 irq, u32 type) -{ - int pin = irq_to_gpio(irq); - struct irq_desc *desc; - - if ((readl(GPIO_IO_CONF(pin)) & (1 << pin)) == 0) { - printk(KERN_ERR "orion5x_gpio_set_irq_type failed " - "(irq %d, pin %d).\n", irq, pin); - return -EINVAL; - } - - desc = irq_desc + irq; - - switch (type) { - case IRQ_TYPE_LEVEL_HIGH: - desc->handle_irq = handle_level_irq; - desc->status |= IRQ_LEVEL; - orion5x_clrbits(GPIO_IN_POL(pin), (1 << pin)); - break; - case IRQ_TYPE_LEVEL_LOW: - desc->handle_irq = handle_level_irq; - desc->status |= IRQ_LEVEL; - orion5x_setbits(GPIO_IN_POL(pin), (1 << pin)); - break; - case IRQ_TYPE_EDGE_RISING: - desc->handle_irq = handle_edge_irq; - desc->status &= ~IRQ_LEVEL; - orion5x_clrbits(GPIO_IN_POL(pin), (1 << pin)); - break; - case IRQ_TYPE_EDGE_FALLING: - desc->handle_irq = handle_edge_irq; - desc->status &= ~IRQ_LEVEL; - orion5x_setbits(GPIO_IN_POL(pin), (1 << pin)); - break; - case IRQ_TYPE_EDGE_BOTH: - desc->handle_irq = handle_edge_irq; - desc->status &= ~IRQ_LEVEL; - /* - * set initial polarity based on current input level - */ - if ((readl(GPIO_IN_POL(pin)) ^ readl(GPIO_DATA_IN(pin))) - & (1 << pin)) - orion5x_setbits(GPIO_IN_POL(pin), (1 << pin)); /* falling */ - else - orion5x_clrbits(GPIO_IN_POL(pin), (1 << pin)); /* rising */ - - break; - default: - printk(KERN_ERR "failed to set irq=%d (type=%d)\n", irq, type); - return -EINVAL; - } - - desc->status &= ~IRQ_TYPE_SENSE_MASK; - desc->status |= type & IRQ_TYPE_SENSE_MASK; - - return 0; -} - -static struct irq_chip orion5x_gpio_irq_chip = { - .name = "Orion-IRQ-GPIO", - .ack = orion5x_gpio_irq_ack, - .mask = orion5x_gpio_irq_mask, - .unmask = orion5x_gpio_irq_unmask, - .set_type = orion5x_gpio_set_irq_type, -}; - -static void orion5x_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - u32 cause, offs, pin; - BUG_ON(irq < IRQ_ORION5X_GPIO_0_7 || irq > IRQ_ORION5X_GPIO_24_31); - offs = (irq - IRQ_ORION5X_GPIO_0_7) * 8; - cause = (readl(GPIO_DATA_IN(offs)) & readl(GPIO_LEVEL_MASK)) | - (readl(GPIO_EDGE_CAUSE) & readl(GPIO_EDGE_MASK)); - for (pin = offs; pin < offs + 8; pin++) { - if (cause & (1 << pin)) { - irq = gpio_to_irq(pin); - desc = irq_desc + irq; - if ((desc->status & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { - /* Swap polarity (race with GPIO line) */ - u32 polarity = readl(GPIO_IN_POL(pin)); - polarity ^= 1 << pin; - writel(polarity, GPIO_IN_POL(pin)); - } - generic_handle_irq(irq); - } - } + orion_gpio_irq_handler((irq - IRQ_ORION5X_GPIO_0_7) << 3); } -static void __init orion5x_init_gpio_irq(void) +void __init orion5x_init_irq(void) { int i; - struct irq_desc *desc; + + orion_irq_init(0, (void __iomem *)MAIN_IRQ_MASK); /* * Mask and clear GPIO IRQ interrupts */ - writel(0x0, GPIO_LEVEL_MASK); - writel(0x0, GPIO_EDGE_MASK); - writel(0x0, GPIO_EDGE_CAUSE); + writel(0x0, GPIO_LEVEL_MASK(0)); + writel(0x0, GPIO_EDGE_MASK(0)); + writel(0x0, GPIO_EDGE_CAUSE(0)); /* * Register chained level handlers for GPIO IRQs by default. * User can use set_type() if he wants to use edge types handlers. */ for (i = IRQ_ORION5X_GPIO_START; i < NR_IRQS; i++) { - set_irq_chip(i, &orion5x_gpio_irq_chip); + set_irq_chip(i, &orion_gpio_irq_level_chip); set_irq_handler(i, handle_level_irq); - desc = irq_desc + i; - desc->status |= IRQ_LEVEL; + irq_desc[i].status |= IRQ_LEVEL; set_irq_flags(i, IRQF_VALID); } - set_irq_chained_handler(IRQ_ORION5X_GPIO_0_7, orion5x_gpio_irq_handler); - set_irq_chained_handler(IRQ_ORION5X_GPIO_8_15, orion5x_gpio_irq_handler); - set_irq_chained_handler(IRQ_ORION5X_GPIO_16_23, orion5x_gpio_irq_handler); - set_irq_chained_handler(IRQ_ORION5X_GPIO_24_31, orion5x_gpio_irq_handler); -} - -/***************************************************************************** - * Orion Main IRQ - ****************************************************************************/ -static void __init orion5x_init_main_irq(void) -{ - orion_irq_init(0, (void __iomem *)MAIN_IRQ_MASK); -} - -void __init orion5x_init_irq(void) -{ - orion5x_init_main_irq(); - orion5x_init_gpio_irq(); + set_irq_chained_handler(IRQ_ORION5X_GPIO_0_7, gpio_irq_handler); + set_irq_chained_handler(IRQ_ORION5X_GPIO_8_15, gpio_irq_handler); + set_irq_chained_handler(IRQ_ORION5X_GPIO_16_23, gpio_irq_handler); + set_irq_chained_handler(IRQ_ORION5X_GPIO_24_31, gpio_irq_handler); } diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c index d86fc08..9671864 100644 --- a/arch/arm/plat-orion/gpio.c +++ b/arch/arm/plat-orion/gpio.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/irq.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/bitops.h> @@ -237,3 +238,178 @@ void orion_gpio_set_blink(unsigned pin, int blink) spin_unlock_irqrestore(&gpio_lock, flags); } EXPORT_SYMBOL(orion_gpio_set_blink); + + +/***************************************************************************** + * Orion GPIO IRQ + * + * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same + * value of the line or the opposite value. + * + * Level IRQ handlers: DATA_IN is used directly as cause register. + * Interrupt are masked by LEVEL_MASK registers. + * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. + * Interrupt are masked by EDGE_MASK registers. + * Both-edge handlers: Similar to regular Edge handlers, but also swaps + * the polarity to catch the next line transaction. + * This is a race condition that might not perfectly + * work on some use cases. + * + * Every eight GPIO lines are grouped (OR'ed) before going up to main + * cause register. + * + * EDGE cause mask + * data-in /--------| |-----| |----\ + * -----| |----- ---- to main cause reg + * X \----------------| |----/ + * polarity LEVEL mask + * + ****************************************************************************/ +static void gpio_irq_edge_ack(u32 irq) +{ + int pin = irq_to_gpio(irq); + + writel(~(1 << (pin & 31)), GPIO_EDGE_CAUSE(pin)); +} + +static void gpio_irq_edge_mask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_EDGE_MASK(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_EDGE_MASK(pin)); +} + +static void gpio_irq_edge_unmask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_EDGE_MASK(pin)); + u |= 1 << (pin & 31); + writel(u, GPIO_EDGE_MASK(pin)); +} + +static void gpio_irq_level_mask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_LEVEL_MASK(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_LEVEL_MASK(pin)); +} + +static void gpio_irq_level_unmask(u32 irq) +{ + int pin = irq_to_gpio(irq); + u32 u; + + u = readl(GPIO_LEVEL_MASK(pin)); + u |= 1 << (pin & 31); + writel(u, GPIO_LEVEL_MASK(pin)); +} + +static int gpio_irq_set_type(u32 irq, u32 type) +{ + int pin = irq_to_gpio(irq); + struct irq_desc *desc; + u32 u; + + u = readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31)); + if (!u) { + printk(KERN_ERR "orion gpio_irq_set_type failed " + "(irq %d, pin %d).\n", irq, pin); + return -EINVAL; + } + + desc = irq_desc + irq; + + /* + * Set edge/level type. + */ + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + desc->chip = &orion_gpio_irq_edge_chip; + } else if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + desc->chip = &orion_gpio_irq_level_chip; + } else { + printk(KERN_ERR "failed to set irq=%d (type=%d)\n", irq, type); + return -EINVAL; + } + + /* + * Configure interrupt polarity. + */ + if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) { + u = readl(GPIO_IN_POL(pin)); + u &= ~(1 << (pin & 31)); + writel(u, GPIO_IN_POL(pin)); + } else if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW) { + u = readl(GPIO_IN_POL(pin)); + u |= 1 << (pin & 31); + writel(u, GPIO_IN_POL(pin)); + } else if (type == IRQ_TYPE_EDGE_BOTH) { + u32 v; + + v = readl(GPIO_IN_POL(pin)) ^ readl(GPIO_DATA_IN(pin)); + + /* + * set initial polarity based on current input level + */ + u = readl(GPIO_IN_POL(pin)); + if (v & (1 << (pin & 31))) + u |= 1 << (pin & 31); /* falling */ + else + u &= ~(1 << (pin & 31)); /* rising */ + writel(u, GPIO_IN_POL(pin)); + } + + desc->status = (desc->status & ~IRQ_TYPE_SENSE_MASK) | type; + + return 0; +} + +struct irq_chip orion_gpio_irq_edge_chip = { + .name = "orion_gpio_irq_edge", + .ack = gpio_irq_edge_ack, + .mask = gpio_irq_edge_mask, + .unmask = gpio_irq_edge_unmask, + .set_type = gpio_irq_set_type, +}; + +struct irq_chip orion_gpio_irq_level_chip = { + .name = "orion_gpio_irq_level", + .mask = gpio_irq_level_mask, + .mask_ack = gpio_irq_level_mask, + .unmask = gpio_irq_level_unmask, + .set_type = gpio_irq_set_type, +}; + +void orion_gpio_irq_handler(int pinoff) +{ + u32 cause; + int pin; + + cause = readl(GPIO_DATA_IN(pinoff)) & readl(GPIO_LEVEL_MASK(pinoff)); + cause |= readl(GPIO_EDGE_CAUSE(pinoff)) & readl(GPIO_EDGE_MASK(pinoff)); + + for (pin = pinoff; pin < pinoff + 8; pin++) { + int irq = gpio_to_irq(pin); + struct irq_desc *desc = irq_desc + irq; + + if (!(cause & (1 << (pin & 31)))) + continue; + + if ((desc->status & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { + /* Swap polarity (race with GPIO line) */ + u32 polarity; + + polarity = readl(GPIO_IN_POL(pin)); + polarity ^= 1 << (pin & 31); + writel(polarity, GPIO_IN_POL(pin)); + } + desc_handle_irq(irq, desc); + } +} diff --git a/arch/arm/plat-orion/include/plat/gpio.h b/arch/arm/plat-orion/include/plat/gpio.h index 956658d..54deaf2 100644 --- a/arch/arm/plat-orion/include/plat/gpio.h +++ b/arch/arm/plat-orion/include/plat/gpio.h @@ -28,5 +28,12 @@ void orion_gpio_set_unused(unsigned pin); void orion_gpio_set_valid(unsigned pin, int valid); void orion_gpio_set_blink(unsigned pin, int blink); +/* + * GPIO interrupt handling. + */ +extern struct irq_chip orion_gpio_irq_edge_chip; +extern struct irq_chip orion_gpio_irq_level_chip; +void orion_gpio_irq_handler(int irqoff); + #endif |