diff options
Diffstat (limited to 'target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch')
-rw-r--r-- | target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch b/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch new file mode 100644 index 0000000000..5a8c83c5e1 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch @@ -0,0 +1,415 @@ +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 + |