diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2011-07-20 16:24:14 +0100 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2011-10-23 13:32:29 +0100 |
commit | 292b293ceef2eda1f96f0c90b96e954d7bdabd1c (patch) | |
tree | 931da77e74e1aee404ffb61f8ffe2f6acb73f80c | |
parent | 88b6fc8c57055590d8e9538f6faa85483db8c366 (diff) | |
download | linux-stable-292b293ceef2eda1f96f0c90b96e954d7bdabd1c.tar.gz linux-stable-292b293ceef2eda1f96f0c90b96e954d7bdabd1c.tar.bz2 linux-stable-292b293ceef2eda1f96f0c90b96e954d7bdabd1c.zip |
ARM: gic: consolidate PPI handling
PPI handling is a bit of an odd beast. It uses its own low level
handling code and is hardwired to the local timers (hence lacking
a registration interface).
Instead, switch the low handling to the normal SPI handling code.
PPIs are handled by the handle_percpu_devid_irq flow.
This also allows the removal of some duplicated code.
Cc: Kukjin Kim <kgene.kim@samsung.com>
Cc: David Brown <davidb@codeaurora.org>
Cc: Bryan Huntsman <bryanh@codeaurora.org>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Acked-by: David Brown <davidb@codeaurora.org>
Tested-by: David Brown <davidb@codeaurora.org>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r-- | arch/arm/common/gic.c | 75 | ||||
-rw-r--r-- | arch/arm/include/asm/entry-macro-multi.S | 7 | ||||
-rw-r--r-- | arch/arm/include/asm/hardirq.h | 3 | ||||
-rw-r--r-- | arch/arm/include/asm/hardware/entry-macro-gic.S | 19 | ||||
-rw-r--r-- | arch/arm/include/asm/localtimer.h | 11 | ||||
-rw-r--r-- | arch/arm/include/asm/smp.h | 5 | ||||
-rw-r--r-- | arch/arm/kernel/irq.c | 3 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 32 | ||||
-rw-r--r-- | arch/arm/mach-exynos4/include/mach/entry-macro.S | 7 | ||||
-rw-r--r-- | arch/arm/mach-msm/board-msm8x60.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-msm/include/mach/entry-macro-qgic.S | 73 | ||||
-rw-r--r-- | arch/arm/mach-omap2/include/mach/entry-macro.S | 14 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/entry-intc.S | 3 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/include/mach/entry-macro.S | 3 |
14 files changed, 88 insertions, 178 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 666b278e56d7..bbea0168779b 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -28,10 +28,14 @@ #include <linux/smp.h> #include <linux/cpumask.h> #include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/percpu.h> +#include <linux/slab.h> #include <asm/irq.h> #include <asm/mach/irq.h> #include <asm/hardware/gic.h> +#include <asm/localtimer.h> static DEFINE_SPINLOCK(irq_controller_lock); @@ -255,6 +259,32 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) irq_set_chained_handler(irq, gic_handle_cascade_irq); } +#ifdef CONFIG_LOCAL_TIMERS +#define gic_ppi_handler percpu_timer_handler +#else +static irqreturn_t gic_ppi_handler(int irq, void *dev_id) +{ + return IRQ_NONE; +} +#endif + +#define PPI_IRQACT(nr) \ + { \ + .handler = gic_ppi_handler, \ + .flags = IRQF_PERCPU | IRQF_TIMER, \ + .irq = nr, \ + .name = "PPI-" # nr, \ + } + +static struct irqaction ppi_irqaction_template[16] __initdata = { + PPI_IRQACT(0), PPI_IRQACT(1), PPI_IRQACT(2), PPI_IRQACT(3), + PPI_IRQACT(4), PPI_IRQACT(5), PPI_IRQACT(6), PPI_IRQACT(7), + PPI_IRQACT(8), PPI_IRQACT(9), PPI_IRQACT(10), PPI_IRQACT(11), + PPI_IRQACT(12), PPI_IRQACT(13), PPI_IRQACT(14), PPI_IRQACT(15), +}; + +static struct irqaction *ppi_irqaction; + static void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start) { @@ -262,6 +292,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic, u32 cpumask; void __iomem *base = gic->dist_base; u32 cpu = 0; + u32 nrppis = 0, ppi_base = 0; #ifdef CONFIG_SMP cpu = cpu_logical_map(smp_processor_id()); @@ -283,6 +314,33 @@ static void __init gic_dist_init(struct gic_chip_data *gic, gic_irqs = 1020; /* + * Nobody would be insane enough to use PPIs on a secondary + * GIC, right? + */ + if (gic == &gic_data[0]) { + nrppis = (32 - irq_start) & 31; + + /* The GIC only supports up to 16 PPIs. */ + if (nrppis > 16) + BUG(); + + ppi_base = gic->irq_offset + 32 - nrppis; + + ppi_irqaction = kmemdup(&ppi_irqaction_template[16 - nrppis], + sizeof(*ppi_irqaction) * nrppis, + GFP_KERNEL); + + if (nrppis && !ppi_irqaction) { + pr_err("GIC: Can't allocate PPI memory"); + nrppis = 0; + ppi_base = 0; + } + } + + pr_info("Configuring GIC with %d sources (%d PPIs)\n", + gic_irqs, (gic == &gic_data[0]) ? nrppis : 0); + + /* * Set all global interrupts to be level triggered, active low. */ for (i = 32; i < gic_irqs; i += 16) @@ -317,7 +375,22 @@ static void __init gic_dist_init(struct gic_chip_data *gic, /* * Setup the Linux IRQ subsystem. */ - for (i = irq_start; i < irq_limit; i++) { + for (i = 0; i < nrppis; i++) { + int ppi = i + ppi_base; + int err; + + irq_set_percpu_devid(ppi); + irq_set_chip_and_handler(ppi, &gic_chip, + handle_percpu_devid_irq); + irq_set_chip_data(ppi, gic); + set_irq_flags(ppi, IRQF_VALID | IRQF_NOAUTOEN); + + err = setup_percpu_irq(ppi, &ppi_irqaction[i]); + if (err) + pr_err("GIC: can't setup PPI%d (%d)\n", ppi, err); + } + + for (i = irq_start + nrppis; i < irq_limit; i++) { irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); irq_set_chip_data(i, gic); set_irq_flags(i, IRQF_VALID | IRQF_PROBE); diff --git a/arch/arm/include/asm/entry-macro-multi.S b/arch/arm/include/asm/entry-macro-multi.S index 2f1e2098dfe7..88d61815f0c0 100644 --- a/arch/arm/include/asm/entry-macro-multi.S +++ b/arch/arm/include/asm/entry-macro-multi.S @@ -25,13 +25,6 @@ movne r1, sp adrne lr, BSYM(1b) bne do_IPI - -#ifdef CONFIG_LOCAL_TIMERS - test_for_ltirq r0, r2, r6, lr - movne r0, sp - adrne lr, BSYM(1b) - bne do_local_timer -#endif #endif 9997: .endm diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index 89ad1805e579..ddf07a92a6c8 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -9,9 +9,6 @@ typedef struct { unsigned int __softirq_pending; -#ifdef CONFIG_LOCAL_TIMERS - unsigned int local_timer_irqs; -#endif #ifdef CONFIG_SMP unsigned int ipi_irqs[NR_IPI]; #endif diff --git a/arch/arm/include/asm/hardware/entry-macro-gic.S b/arch/arm/include/asm/hardware/entry-macro-gic.S index c115b82fe80a..74ebc803904d 100644 --- a/arch/arm/include/asm/hardware/entry-macro-gic.S +++ b/arch/arm/include/asm/hardware/entry-macro-gic.S @@ -22,15 +22,11 @@ * interrupt controller spec. To wit: * * Interrupts 0-15 are IPI - * 16-28 are reserved - * 29-31 are local. We allow 30 to be used for the watchdog. + * 16-31 are local. We allow 30 to be used for the watchdog. * 32-1020 are global * 1021-1022 are reserved * 1023 is "spurious" (no interrupt) * - * For now, we ignore all local interrupts so only return an interrupt if it's - * between 30 and 1020. The test_for_ipi routine below will pick up on IPIs. - * * A simple read from the controller will tell us the number of the highest * priority enabled interrupt. We then just need to check whether it is in the * valid range for an IRQ (30-1020 inclusive). @@ -43,7 +39,7 @@ ldr \tmp, =1021 bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr @@ -62,14 +58,3 @@ strcc \irqstat, [\base, #GIC_CPU_EOI] cmpcs \irqnr, \irqnr .endm - -/* As above, this assumes that irqstat and base are preserved.. */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - mov \tmp, #0 - cmp \irqnr, #29 - moveq \tmp, #1 - streq \irqstat, [\base, #GIC_CPU_EOI] - cmp \tmp, #0 - .endm diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h index 3306f281333c..5c8acb4c4040 100644 --- a/arch/arm/include/asm/localtimer.h +++ b/arch/arm/include/asm/localtimer.h @@ -10,6 +10,8 @@ #ifndef __ASM_ARM_LOCALTIMER_H #define __ASM_ARM_LOCALTIMER_H +#include <linux/interrupt.h> + struct clock_event_device; /* @@ -18,14 +20,9 @@ struct clock_event_device; void percpu_timer_setup(void); /* - * Called from assembly, this is the local timer IRQ handler - */ -asmlinkage void do_local_timer(struct pt_regs *); - -/* - * Called from C code + * Per-cpu timer IRQ handler */ -void handle_local_timer(struct pt_regs *); +irqreturn_t percpu_timer_handler(int irq, void *dev_id); #ifdef CONFIG_LOCAL_TIMERS diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 0a17b62538c2..1e5717afc4ac 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -99,9 +99,4 @@ extern void platform_cpu_enable(unsigned int cpu); extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); -/* - * show local interrupt info - */ -extern void show_local_irqs(struct seq_file *, int); - #endif /* ifndef __ASM_ARM_SMP_H */ diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 53919b230e8b..7cb29261249a 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -59,9 +59,6 @@ int arch_show_interrupts(struct seq_file *p, int prec) #ifdef CONFIG_SMP show_ipi_list(p, prec); #endif -#ifdef CONFIG_LOCAL_TIMERS - show_local_irqs(p, prec); -#endif seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count); return 0; } diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 35417d0fb8ab..917ed2fa4e4c 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -457,10 +457,6 @@ u64 smp_irq_stat_cpu(unsigned int cpu) for (i = 0; i < NR_IPI; i++) sum += __get_irq_stat(cpu, ipi_irqs[i]); -#ifdef CONFIG_LOCAL_TIMERS - sum += __get_irq_stat(cpu, local_timer_irqs); -#endif - return sum; } @@ -478,34 +474,16 @@ static void ipi_timer(void) } #ifdef CONFIG_LOCAL_TIMERS -asmlinkage void __exception_irq_entry do_local_timer(struct pt_regs *regs) -{ - handle_local_timer(regs); -} - -void handle_local_timer(struct pt_regs *regs) +irqreturn_t percpu_timer_handler(int irq, void *dev_id) { - struct pt_regs *old_regs = set_irq_regs(regs); - int cpu = smp_processor_id(); + struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent); if (local_timer_ack()) { - __inc_irq_stat(cpu, local_timer_irqs); - ipi_timer(); + evt->event_handler(evt); + return IRQ_HANDLED; } - set_irq_regs(old_regs); -} - -void show_local_irqs(struct seq_file *p, int prec) -{ - unsigned int cpu; - - seq_printf(p, "%*s: ", prec, "LOC"); - - for_each_present_cpu(cpu) - seq_printf(p, "%10u ", __get_irq_stat(cpu, local_timer_irqs)); - - seq_printf(p, " Local timer interrupts\n"); + return IRQ_NONE; } #endif diff --git a/arch/arm/mach-exynos4/include/mach/entry-macro.S b/arch/arm/mach-exynos4/include/mach/entry-macro.S index d7a1e281ce7a..006a4f4c65c6 100644 --- a/arch/arm/mach-exynos4/include/mach/entry-macro.S +++ b/arch/arm/mach-exynos4/include/mach/entry-macro.S @@ -55,7 +55,7 @@ bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr @@ -76,8 +76,3 @@ strcc \irqstat, [\base, #GIC_CPU_EOI] cmpcs \irqnr, \irqnr .endm - - /* As above, this assumes that irqstat and base are preserved.. */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c index 1163b6fd05d2..d70a2f643613 100644 --- a/arch/arm/mach-msm/board-msm8x60.c +++ b/arch/arm/mach-msm/board-msm8x60.c @@ -36,8 +36,6 @@ static void __init msm8x60_map_io(void) static void __init msm8x60_init_irq(void) { - unsigned int i; - gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, (void *)MSM_QGIC_CPU_BASE); @@ -49,15 +47,6 @@ static void __init msm8x60_init_irq(void) */ if (!machine_is_msm8x60_sim()) writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET); - - /* FIXME: Not installing AVS_SVICINT and AVS_SVICINTSWDONE yet - * as they are configured as level, which does not play nice with - * handle_percpu_irq. - */ - for (i = GIC_PPI_START; i < GIC_SPI_START; i++) { - if (i != AVS_SVICINT && i != AVS_SVICINTSWDONE) - irq_set_handler(i, handle_percpu_irq); - } } static void __init msm8x60_init(void) diff --git a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S index 12467157afb9..717076f3ca73 100644 --- a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S +++ b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S @@ -8,81 +8,10 @@ * warranty of any kind, whether express or implied. */ -#include <mach/hardware.h> -#include <asm/hardware/gic.h> +#include <asm/hardware/entry-macro-gic.S> .macro disable_fiq .endm - .macro get_irqnr_preamble, base, tmp - ldr \base, =gic_cpu_base_addr - ldr \base, [\base] - .endm - .macro arch_ret_to_user, tmp1, tmp2 .endm - - /* - * The interrupt numbering scheme is defined in the - * interrupt controller spec. To wit: - * - * Migrated the code from ARM MP port to be more consistent - * with interrupt processing , the following still holds true - * however, all interrupts are treated the same regardless of - * if they are local IPI or PPI - * - * Interrupts 0-15 are IPI - * 16-31 are PPI - * (16-18 are the timers) - * 32-1020 are global - * 1021-1022 are reserved - * 1023 is "spurious" (no interrupt) - * - * A simple read from the controller will tell us the number of the - * highest priority enabled interrupt. We then just need to check - * whether it is in the valid range for an IRQ (0-1020 inclusive). - * - * Base ARM code assumes that the local (private) peripheral interrupts - * are not valid, we treat them differently, in that the privates are - * handled like normal shared interrupts with the exception that only - * one processor can register the interrupt and the handler must be - * the same for all processors. - */ - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - - ldr \irqstat, [\base, #GIC_CPU_INTACK] /* bits 12-10 =srcCPU, - 9-0 =int # */ - - bic \irqnr, \irqstat, #0x1c00 @mask src - cmp \irqnr, #15 - ldr \tmp, =1021 - cmpcc \irqnr, \irqnr - cmpne \irqnr, \tmp - cmpcs \irqnr, \irqnr - - .endm - - /* We assume that irqstat (the raw value of the IRQ acknowledge - * register) is preserved from the macro above. - * If there is an IPI, we immediately signal end of interrupt on the - * controller, since this requires the original irqstat value which - * we won't easily be able to recreate later. - */ - .macro test_for_ipi, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #16 - strcc \irqstat, [\base, #GIC_CPU_EOI] - cmpcs \irqnr, \irqnr - .endm - - /* As above, this assumes that irqstat and base are preserved.. */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - mov \tmp, #0 - cmp \irqnr, #16 - moveq \tmp, #1 - streq \irqstat, [\base, #GIC_CPU_EOI] - cmp \tmp, #0 - .endm diff --git a/arch/arm/mach-omap2/include/mach/entry-macro.S b/arch/arm/mach-omap2/include/mach/entry-macro.S index ceb8b7e593d7..feb90a10945a 100644 --- a/arch/arm/mach-omap2/include/mach/entry-macro.S +++ b/arch/arm/mach-omap2/include/mach/entry-macro.S @@ -78,7 +78,7 @@ 4401: ldr \irqstat, [\base, #GIC_CPU_INTACK] ldr \tmp, =1021 bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr @@ -101,18 +101,6 @@ it cs cmpcs \irqnr, \irqnr .endm - - /* As above, this assumes that irqstat and base are preserved */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - mov \tmp, #0 - cmp \irqnr, #29 - itt eq - moveq \tmp, #1 - streq \irqstat, [\base, #GIC_CPU_EOI] - cmp \tmp, #0 - .endm #endif /* CONFIG_SMP */ #else /* MULTI_OMAP2 */ diff --git a/arch/arm/mach-shmobile/entry-intc.S b/arch/arm/mach-shmobile/entry-intc.S index cac0a7ae2084..1a1c00ca39a2 100644 --- a/arch/arm/mach-shmobile/entry-intc.S +++ b/arch/arm/mach-shmobile/entry-intc.S @@ -51,7 +51,4 @@ .macro test_for_ipi, irqnr, irqstat, base, tmp .endm - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm - arch_irq_handler shmobile_handle_irq_intc diff --git a/arch/arm/mach-shmobile/include/mach/entry-macro.S b/arch/arm/mach-shmobile/include/mach/entry-macro.S index d791f10eeac7..8d4a416d4285 100644 --- a/arch/arm/mach-shmobile/include/mach/entry-macro.S +++ b/arch/arm/mach-shmobile/include/mach/entry-macro.S @@ -27,8 +27,5 @@ .macro test_for_ipi, irqnr, irqstat, base, tmp .endm - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm - .macro arch_ret_to_user, tmp1, tmp2 .endm |