diff options
author | Mike J. Chen <mjchen@sta.samsung.com> | 2010-09-22 16:27:13 -0700 |
---|---|---|
committer | Arve Hjønnevåg <arve@android.com> | 2011-11-17 17:46:15 -0800 |
commit | a2c7132085c9f9ed93877302a7db61cc2c4fb1b8 (patch) | |
tree | 079d13c52da96810ff9f74df30e879b11213c992 /arch/arm/plat-s5p | |
parent | 97fc31cc93aaef3aa7428ba50cf0d7f0ea281bd5 (diff) | |
download | kernel_samsung_crespo-a2c7132085c9f9ed93877302a7db61cc2c4fb1b8.zip kernel_samsung_crespo-a2c7132085c9f9ed93877302a7db61cc2c4fb1b8.tar.gz kernel_samsung_crespo-a2c7132085c9f9ed93877302a7db61cc2c4fb1b8.tar.bz2 |
S5PC11X: IRQ: Add ack() call to unmask() for level triggered interrupts.
In Linux, the general sequence for irq handling is:
mask_ack()
irq_handler()
irq_thread_handler() if one exists
unmask()
The irq_handler() or irq_thread_handler() does what is needed to
clear the interrupt. The problem is that level triggered interrupts
will immediately repend after the mask_ack() before the handlers can
clear the interrupt cause. We add a ack() to unmask() for level
triggered to handle this case and prevent the second false interrupt
from occurring.
Change-Id: I1e8376300da44fa6bf685ad2b4094693f64dff4a
Signed-off-by: Mike J. Chen <mjchen@sta.samsung.com>
Diffstat (limited to 'arch/arm/plat-s5p')
-rw-r--r-- | arch/arm/plat-s5p/irq-eint-group.c | 28 | ||||
-rw-r--r-- | arch/arm/plat-s5p/irq-eint.c | 22 |
2 files changed, 35 insertions, 15 deletions
diff --git a/arch/arm/plat-s5p/irq-eint-group.c b/arch/arm/plat-s5p/irq-eint-group.c index 944cc64..b343b61 100644 --- a/arch/arm/plat-s5p/irq-eint-group.c +++ b/arch/arm/plat-s5p/irq-eint-group.c @@ -375,33 +375,43 @@ static inline void s5pv210_irq_eint_group_mask(struct irq_data *d) __raw_writel(mask, group->mask_reg); } -static void s5pv210_irq_eint_group_unmask(struct irq_data *d) +static inline void s5pv210_irq_eint_group_ack(struct irq_data *d) { struct s5pv210_eint_group_t *group; int grp; - u32 mask; + u32 pend; grp = to_group_number(d->irq); group = &eint_groups[grp]; - mask = __raw_readl(group->mask_reg); - mask &= ~(1 << (group->mask_ofs + to_irq_number(grp, d->irq))); + pend = (1 << (group->pend_ofs + to_irq_number(grp, d->irq))); - __raw_writel(mask, group->mask_reg); + __raw_writel(pend, group->pend_reg); } -static inline void s5pv210_irq_eint_group_ack(struct irq_data *d) +static void s5pv210_irq_eint_group_unmask(struct irq_data *d) { struct s5pv210_eint_group_t *group; int grp; - u32 pend; + u32 mask; grp = to_group_number(d->irq); group = &eint_groups[grp]; - pend = (1 << (group->pend_ofs + to_irq_number(grp, d->irq))); + /* for level triggered interrupts, masking doesn't prevent + * the interrupt from becoming pending again. by the time + * the handler (either irq or thread) can do its thing to clear + * the interrupt, it's too late because it could be pending + * already. we have to ack it here, after the handler runs, + * or else we get a false interrupt. + */ + if (irqd_is_level_type(d)) + s5pv210_irq_eint_group_ack(d); - __raw_writel(pend, group->pend_reg); + mask = __raw_readl(group->mask_reg); + mask &= ~(1 << (group->mask_ofs + to_irq_number(grp, d->irq))); + + __raw_writel(mask, group->mask_reg); } static void s5pv210_irq_eint_group_maskack(struct irq_data *d) diff --git a/arch/arm/plat-s5p/irq-eint.c b/arch/arm/plat-s5p/irq-eint.c index 1597063..8380951 100644 --- a/arch/arm/plat-s5p/irq-eint.c +++ b/arch/arm/plat-s5p/irq-eint.c @@ -37,21 +37,31 @@ static inline void s5p_irq_eint_mask(struct irq_data *data) __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq))); } +static inline void s5p_irq_eint_ack(struct irq_data *data) +{ + __raw_writel(eint_irq_to_bit(data->irq), + S5P_EINT_PEND(EINT_REG_NR(data->irq))); +} + static void s5p_irq_eint_unmask(struct irq_data *data) { u32 mask; + /* for level triggered interrupts, masking doesn't prevent + * the interrupt from becoming pending again. by the time + * the handler (either irq or thread) can do its thing to clear + * the interrupt, it's too late because it could be pending + * already. we have to ack it here, after the handler runs, + * or else we get a false interrupt. + */ + if (irqd_is_level_type(data)) + s5p_irq_eint_ack(data); + mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq))); mask &= ~(eint_irq_to_bit(data->irq)); __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq))); } -static inline void s5p_irq_eint_ack(struct irq_data *data) -{ - __raw_writel(eint_irq_to_bit(data->irq), - S5P_EINT_PEND(EINT_REG_NR(data->irq))); -} - static void s5p_irq_eint_maskack(struct irq_data *data) { /* compiler should in-line these */ |