416 lines
14 KiB
Diff
416 lines
14 KiB
Diff
|
From 843da234cfc0e7014f9e2da82786a485e0820665 Mon Sep 17 00:00:00 2001
|
||
|
From: Thomas Gleixner <tglx@linutronix.de>
|
||
|
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 <hdegoede@redhat.com>
|
||
|
Tested-by: Hans de Goede <hdegoede@redhat.com>
|
||
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||
|
---
|
||
|
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 <hdegoede@redhat.com>
|
||
|
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 <hdegoede@redhat.com>
|
||
|
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
---
|
||
|
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 <hdegoede@redhat.com>
|
||
|
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 <hdegoede@redhat.com>
|
||
|
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
---
|
||
|
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 <hdegoede@redhat.com>
|
||
|
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 <hdegoede@redhat.com>
|
||
|
---
|
||
|
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 <hdegoede@redhat.com>
|
||
|
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 <hdegoede@redhat.com>
|
||
|
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
---
|
||
|
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 <hdegoede@redhat.com>
|
||
|
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 <hdegoede@redhat.com>
|
||
|
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
---
|
||
|
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
|
||
|
|