aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s5p/irq-gpioint.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-s5p/irq-gpioint.c')
-rw-r--r--arch/arm/plat-s5p/irq-gpioint.c97
1 files changed, 72 insertions, 25 deletions
diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c
index 327ab9f..06c6bfb 100644
--- a/arch/arm/plat-s5p/irq-gpioint.c
+++ b/arch/arm/plat-s5p/irq-gpioint.c
@@ -22,6 +22,10 @@
#include <mach/map.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
+#include <plat/cpu.h>
+
+#include <asm/mach/irq.h>
+#include <mach/regs-gpio.h>
#define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u)
@@ -46,22 +50,25 @@ static int s5p_gpioint_set_type(struct irq_data *d, unsigned int type)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = gc->chip_types;
unsigned int shift = (d->irq - gc->irq_base) << 2;
+ struct irq_desc *desc = irq_to_desc(d->irq);
+ unsigned int type_s5p = 0;
+ struct s3c_gpio_chip *chip = gc->private;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
- type = S5P_IRQ_TYPE_EDGE_RISING;
+ type_s5p = S5P_IRQ_TYPE_EDGE_RISING;
break;
case IRQ_TYPE_EDGE_FALLING:
- type = S5P_IRQ_TYPE_EDGE_FALLING;
+ type_s5p = S5P_IRQ_TYPE_EDGE_FALLING;
break;
case IRQ_TYPE_EDGE_BOTH:
- type = S5P_IRQ_TYPE_EDGE_BOTH;
+ type_s5p = S5P_IRQ_TYPE_EDGE_BOTH;
break;
case IRQ_TYPE_LEVEL_HIGH:
- type = S5P_IRQ_TYPE_LEVEL_HIGH;
+ type_s5p = S5P_IRQ_TYPE_LEVEL_HIGH;
break;
case IRQ_TYPE_LEVEL_LOW:
- type = S5P_IRQ_TYPE_LEVEL_LOW;
+ type_s5p = S5P_IRQ_TYPE_LEVEL_LOW;
break;
case IRQ_TYPE_NONE:
default:
@@ -70,29 +77,45 @@ static int s5p_gpioint_set_type(struct irq_data *d, unsigned int type)
}
gc->type_cache &= ~(0x7 << shift);
- gc->type_cache |= type << shift;
+ gc->type_cache |= type_s5p << shift;
writel(gc->type_cache, gc->reg_base + ct->regs.type);
+
+ pr_info("%s irq:%d is at %s(%d)\n", __func__, d->irq, chip->chip.label,
+ (d->irq - chip->irq_base));
+
+ s3c_gpio_cfgpin(chip->chip.base + (d->irq - chip->irq_base), EINT_MODE);
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ desc->handle_irq = handle_edge_irq;
+ else
+ desc->handle_irq = handle_level_irq;
+
return 0;
}
static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc)
{
struct s5p_gpioint_bank *bank = irq_get_handler_data(irq);
- int group, pend_offset, mask_offset;
- unsigned int pend, mask;
+ int group, eint_offset;
+ unsigned int pend, mask, action = 0;
+
+ struct irq_chip *chip = irq_get_chip(irq);
+ chained_irq_enter(chip, desc);
for (group = 0; group < bank->nr_groups; group++) {
struct s3c_gpio_chip *chip = bank->chips[group];
if (!chip)
continue;
-
- pend_offset = REG_OFFSET(group);
- pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
+ if (soc_is_exynos4210() || soc_is_exynos4212()
+ || soc_is_exynos4412()
+ || soc_is_exynos5250())
+ eint_offset = chip->eint_offset;
+ else
+ eint_offset = REG_OFFSET(group);
+ pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + eint_offset);
if (!pend)
continue;
-
- mask_offset = REG_OFFSET(group);
- mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
+ mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + eint_offset);
pend &= ~mask;
while (pend) {
@@ -100,28 +123,38 @@ static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc)
int real_irq = chip->irq_base + offset;
generic_handle_irq(real_irq);
pend &= ~BIT(offset);
+ ++action;
}
}
+ chained_irq_exit(chip, desc);
+
+ if (!action)
+ do_bad_IRQ(irq, desc);
}
static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip)
{
static int used_gpioint_groups = 0;
int group = chip->group;
- struct s5p_gpioint_bank *bank = NULL;
+ struct s5p_gpioint_bank *b, *bank = NULL;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
- if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT)
+ if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) {
+ WARN(1, "used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT\n");
return -ENOMEM;
+ }
- list_for_each_entry(bank, &banks, list) {
- if (group >= bank->start &&
- group < bank->start + bank->nr_groups)
+ list_for_each_entry(b, &banks, list) {
+ if (group >= b->start && group < b->start + b->nr_groups) {
+ bank = b;
break;
+ }
}
- if (!bank)
+ if (!bank) {
+ WARN(1, "bank not found\n");
return -EINVAL;
+ }
if (!bank->handler) {
bank->chips = kzalloc(sizeof(struct s3c_gpio_chip *) *
@@ -149,16 +182,29 @@ static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip)
gc = irq_alloc_generic_chip("s5p_gpioint", 1, chip->irq_base,
(void __iomem *)GPIO_BASE(chip),
handle_level_irq);
- if (!gc)
+ if (!gc) {
+ WARN(1, "irq_alloc_generic_chip failed\n");
return -ENOMEM;
+ }
+
+ gc->private = chip;
+
ct = gc->chip_types;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_set_bit;
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
- ct->chip.irq_set_type = s5p_gpioint_set_type,
- ct->regs.ack = PEND_OFFSET + REG_OFFSET(chip->group);
- ct->regs.mask = MASK_OFFSET + REG_OFFSET(chip->group);
- ct->regs.type = CON_OFFSET + REG_OFFSET(chip->group);
+ ct->chip.irq_disable = irq_gc_mask_and_ack_set;
+ ct->chip.irq_set_type = s5p_gpioint_set_type;
+ if (soc_is_exynos4210() || soc_is_exynos4212() || soc_is_exynos4412()
+ || soc_is_exynos5250()) {
+ ct->regs.ack = PEND_OFFSET + chip->eint_offset;
+ ct->regs.mask = MASK_OFFSET + chip->eint_offset;
+ ct->regs.type = CON_OFFSET + chip->eint_offset;
+ } else {
+ ct->regs.ack = PEND_OFFSET + REG_OFFSET(chip->group);
+ ct->regs.mask = MASK_OFFSET + REG_OFFSET(chip->group);
+ ct->regs.type = CON_OFFSET + REG_OFFSET(chip->group);
+ }
irq_setup_generic_chip(gc, IRQ_MSK(chip->chip.ngpio),
IRQ_GC_INIT_MASK_CACHE,
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
@@ -189,6 +235,7 @@ int __init s5p_register_gpio_interrupt(int pin)
group);
return my_chip->irq_base + offset;
}
+
return ret;
}