From 843da234cfc0e7014f9e2da82786a485e0820665 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 13 Mar 2014 15:32:05 +0100 Subject: [PATCH] irq: Add a new IRQCHIP_EOI_THREADED flag This flag must be used in combination with handle_fasteoi_irq, when set handle_fasteoi_irq will delay the calling of chip->irq_eoi until the threaded handler has run. Reviewed-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Hans de Goede --- include/linux/irq.h | 3 +++ kernel/irq/chip.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- kernel/irq/internals.h | 1 + kernel/irq/manage.c | 2 +- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/include/linux/irq.h b/include/linux/irq.h index 7dc1003..0f036fb 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -349,6 +349,8 @@ struct irq_chip { * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks * when irq enabled * IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip + * IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask + * IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode */ enum { IRQCHIP_SET_TYPE_MASKED = (1 << 0), @@ -357,6 +359,7 @@ enum { IRQCHIP_ONOFFLINE_ENABLED = (1 << 3), IRQCHIP_SKIP_SET_WAKE = (1 << 4), IRQCHIP_ONESHOT_SAFE = (1 << 5), + IRQCHIP_EOI_THREADED = (1 << 6), }; /* This include will go away once we isolated irq_desc usage to core code */ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index dc04c16..6397df2 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -281,6 +281,19 @@ void unmask_irq(struct irq_desc *desc) } } +void unmask_threaded_irq(struct irq_desc *desc) +{ + struct irq_chip *chip = desc->irq_data.chip; + + if (chip->flags & IRQCHIP_EOI_THREADED) + chip->irq_eoi(&desc->irq_data); + + if (chip->irq_unmask) { + chip->irq_unmask(&desc->irq_data); + irq_state_clr_masked(desc); + } +} + /* * handle_nested_irq - Handle a nested irq from a irq thread * @irq: the interrupt number @@ -435,6 +448,27 @@ static inline void preflow_handler(struct irq_desc *desc) static inline void preflow_handler(struct irq_desc *desc) { } #endif +static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip) +{ + if (!(desc->istate & IRQS_ONESHOT)) { + chip->irq_eoi(&desc->irq_data); + return; + } + /* + * We need to unmask in the following cases: + * - Oneshot irq which did not wake the thread (caused by a + * spurious interrupt or a primary handler handling it + * completely). + */ + if (!irqd_irq_disabled(&desc->irq_data) && + irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) { + chip->irq_eoi(&desc->irq_data); + unmask_irq(desc); + } else if (!(chip->flags & IRQCHIP_EOI_THREADED)) { + chip->irq_eoi(&desc->irq_data); + } +} + /** * handle_fasteoi_irq - irq handler for transparent controllers * @irq: the interrupt number @@ -448,6 +482,8 @@ static inline void preflow_handler(struct irq_desc *desc) { } void handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) { + struct irq_chip *chip = desc->irq_data.chip; + raw_spin_lock(&desc->lock); if (unlikely(irqd_irq_inprogress(&desc->irq_data))) @@ -473,18 +509,14 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) preflow_handler(desc); handle_irq_event(desc); - if (desc->istate & IRQS_ONESHOT) - cond_unmask_irq(desc); + cond_unmask_eoi_irq(desc, chip); -out_eoi: - desc->irq_data.chip->irq_eoi(&desc->irq_data); -out_unlock: raw_spin_unlock(&desc->lock); return; out: - if (!(desc->irq_data.chip->flags & IRQCHIP_EOI_IF_HANDLED)) - goto out_eoi; - goto out_unlock; + if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED)) + chip->irq_eoi(&desc->irq_data); + raw_spin_unlock(&desc->lock); } /** diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 001fa5b..e98bb56 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -73,6 +73,7 @@ extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu); extern void mask_irq(struct irq_desc *desc); extern void unmask_irq(struct irq_desc *desc); +extern void unmask_threaded_irq(struct irq_desc *desc); extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index d3bf660..7593958 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -718,7 +718,7 @@ static void irq_finalize_oneshot(struct irq_desc *desc, if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) && irqd_irq_masked(&desc->irq_data)) - unmask_irq(desc); + unmask_threaded_irq(desc); out_unlock: raw_spin_unlock_irq(&desc->lock); -- 2.0.3 From d000f9a5348e6d6c8b620a9c2d0b97c69d6d6153 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Mar 2014 16:47:46 +0100 Subject: [PATCH] irqchip: sun4i: Fix irq 0 not working SUN4I_IRQ_VECTOR_REG containing 0 can mean one of 3 things: 1) no more irqs pending 2) irq 0 pending 3) spurious irq So if we immediately get a reading of 0, check the irq-pending reg to differentiate between 2 and 3. We only do this once to avoid the extra check in the common case of 1) hapening after having read the vector-reg once. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard --- drivers/irqchip/irq-sun4i.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index a5438d8..5c25048 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -140,10 +140,24 @@ static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *re { u32 irq, hwirq; + /* + * hwirq == 0 can mean one of 3 things: + * 1) no more irqs pending + * 2) irq 0 pending + * 3) spurious irq + * So if we immediately get a reading of 0, check the irq-pending reg + * to differentiate between 2 and 3. We only do this once to avoid + * the extra check in the common case of 1 hapening after having + * read the vector-reg once. + */ hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; - while (hwirq != 0) { + if (hwirq == 0 && + !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0))) + return; + + do { irq = irq_find_mapping(sun4i_irq_domain, hwirq); handle_IRQ(irq, regs); hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; - } + } while (hwirq != 0); } -- 2.0.3 From b37587009473582d9fc080e8b8b99b67b0077a90 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Mar 2014 16:53:23 +0100 Subject: [PATCH] irqchip: sun4i: Fix a comment about mask register initialization The comment was claiming that we were masking all irqs, while the code actually *un*masks all of them. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard --- drivers/irqchip/irq-sun4i.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 5c25048..8a2fbee 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -109,7 +109,7 @@ static int __init sun4i_of_init(struct device_node *node, writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); - /* Mask all the interrupts */ + /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */ writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); -- 2.0.3 From c8865ee82b74b2d95339370972a0d9bfdbac09cf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Mar 2014 17:43:45 +0100 Subject: [PATCH] irqchip: sun4i: Don't ack IRQs != 0, fix acking of IRQ 0 All IRQs except for IRQ 0 seem to not need acking, so drop acking for them. The ENMI needs to have the ack done *after* clearing the interrupt source, otherwise we will get a spurious interrupt for each real interrupt. So use the new IRQCHIP_EOI_THREADED flag for this in combination with handle_fasteoi_irq. This uses a separate irq_chip struct for IRQ 0, since we only want this behavior for IRQ 0. Signed-off-by: Hans de Goede --- drivers/irqchip/irq-sun4i.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 8a2fbee..a0ed1ea 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -76,16 +76,29 @@ static void sun4i_irq_unmask(struct irq_data *irqd) static struct irq_chip sun4i_irq_chip = { .name = "sun4i_irq", - .irq_ack = sun4i_irq_ack, .irq_mask = sun4i_irq_mask, .irq_unmask = sun4i_irq_unmask, }; +/* IRQ 0 / the ENMI needs a late eoi call */ +static struct irq_chip sun4i_irq_chip_enmi = { + .name = "sun4i_irq", + .irq_eoi = sun4i_irq_ack, + .irq_mask = sun4i_irq_mask, + .irq_unmask = sun4i_irq_unmask, + .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED, +}; + static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { - irq_set_chip_and_handler(virq, &sun4i_irq_chip, - handle_level_irq); + if (hw == 0) + irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi, + handle_fasteoi_irq); + else + irq_set_chip_and_handler(virq, &sun4i_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); return 0; -- 2.0.3 From f8b4347aa12d7a30aa1d3e5bfcdccece52d17af3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 13 Mar 2014 19:38:26 +0100 Subject: [PATCH] irqchip: sun4i: Use handle_fasteoi_irq for all interrupts Since the sun4i irq chip does not require any action and clears the interrupt when the level goes back to inactive, we don't need to mask / unmask for non oneshot IRQs, to achieve this we make sun4i_irq_ack a nop for all irqs except irq 0 and use handle_fasteoi_irq for all interrupts. Now there might be a case when the device reactivates the interrupt before the RETI. But that does not matter as we run the primary interrupt handlers with interrupts disabled. This also allows us to get rid of needing to use 2 irq_chip structs, this means that the IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED will now influence all interrupts rather then just irq 0, but that does not matter as the eoi is now a nop anyways for all interrupts but irq 0. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard --- drivers/irqchip/irq-sun4i.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index a0ed1ea..6a8c88d 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -45,6 +45,9 @@ static void sun4i_irq_ack(struct irq_data *irqd) int reg = irq / 32; u32 val; + if (irq != 0) + return; /* Only IRQ 0 / the ENMI needs to be acked */ + val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); writel(val | (1 << irq_off), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); @@ -76,13 +79,6 @@ static void sun4i_irq_unmask(struct irq_data *irqd) static struct irq_chip sun4i_irq_chip = { .name = "sun4i_irq", - .irq_mask = sun4i_irq_mask, - .irq_unmask = sun4i_irq_unmask, -}; - -/* IRQ 0 / the ENMI needs a late eoi call */ -static struct irq_chip sun4i_irq_chip_enmi = { - .name = "sun4i_irq", .irq_eoi = sun4i_irq_ack, .irq_mask = sun4i_irq_mask, .irq_unmask = sun4i_irq_unmask, @@ -92,13 +88,7 @@ static struct irq_chip sun4i_irq_chip_enmi = { static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { - if (hw == 0) - irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi, - handle_fasteoi_irq); - else - irq_set_chip_and_handler(virq, &sun4i_irq_chip, - handle_level_irq); - + irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq); set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); return 0; -- 2.0.3 From de39bc31eaa554bd044e6adefacd3da6da5bf6e3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 13 Mar 2014 20:41:20 +0100 Subject: [PATCH] irqchip: sun4i: simplify sun4i_irq_ack Now that we only ack irq 0 the code can be simplified a lot. Also switch from read / modify / write to a simple write clear: 1) This is what the android code does (it has a hack for acking irq 0 in its unmask code doing this) 2) read / modify / write simply does not make sense for an irq status register like this, if the other bits are writeable (and the data sheet says they are not) they should be write 1 to clear, since otherwise a read / modify / write can race with a device raising an interrupt and then clear the pending bit unintentionally Signed-off-by: Hans de Goede Acked-by: Maxime Ripard --- drivers/irqchip/irq-sun4i.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 6a8c88d..75615b5 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -41,16 +41,11 @@ static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *re static void sun4i_irq_ack(struct irq_data *irqd) { unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; if (irq != 0) return; /* Only IRQ 0 / the ENMI needs to be acked */ - val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); - writel(val | (1 << irq_off), - sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); + writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); } static void sun4i_irq_mask(struct irq_data *irqd) -- 2.0.3