aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s5p
diff options
context:
space:
mode:
authorMike J. Chen <mjchen@sta.samsung.com>2010-09-22 16:27:13 -0700
committerArve Hjønnevåg <arve@android.com>2011-11-17 17:46:15 -0800
commita2c7132085c9f9ed93877302a7db61cc2c4fb1b8 (patch)
tree079d13c52da96810ff9f74df30e879b11213c992 /arch/arm/plat-s5p
parent97fc31cc93aaef3aa7428ba50cf0d7f0ea281bd5 (diff)
downloadkernel_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.c28
-rw-r--r--arch/arm/plat-s5p/irq-eint.c22
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 */