Consolidating GIC per-cpu interrupts
Subsystems (5)
v9 cover Consolidating GIC per-cpu interrupts
The current GIC per-cpu interrupts (aka PPIs) suffer from a number of problems:
- They use a completely separate scheme to handle the interrupts, mostly because the PPI concept doesn't really match the kernel view of an interrupt.
- PPIs can only be used by the timer code, unless we add more low-level assembly code.
- The local timer code can only be used by devices generating PPIs, and not SPIs.
- At least one platform (msm) has started implementing its own alternative scheme.
- Some low-level code gets duplicated, as usual...
As the previous solution which tried to map PPIs to normal interrupts has been proved to be buggy, I've opted to a much simpler scheme (based on Russell's input).
The proposed solution is to handle the interrupt using the same path as SPIs, with a common handler for all PPIs. Each PPI can be requested using gic_request_ppi(), similar to request_irq(). The local timer code is updated to reflect these changes.
Patches against next-20110721, tested on PB11MP. As this patch series is quite different from the previous one, I've dropped all previous acks from platform maintainers.
Marc Zyngier (4):
ARM: gic: consolidate PPI handling
ARM: gic: Add PPI registration interface
ARM: local timers: drop local_timer_ack()
ARM: gic: add compute_irqnr macro for exynos4
arch/arm/common/gic.c | 150 +++++++++++++++++++-- arch/arm/include/asm/entry-macro-multi.S | 7 - arch/arm/include/asm/hardirq.h | 3 - arch/arm/include/asm/hardware/entry-macro-gic.S | 22 +--- arch/arm/include/asm/hardware/gic.h | 5 +- arch/arm/include/asm/localtimer.h | 14 +- arch/arm/include/asm/smp.h | 5 - arch/arm/include/asm/smp_twd.h | 2 +- arch/arm/kernel/irq.c | 3 - arch/arm/kernel/smp.c | 33 +----- arch/arm/kernel/smp_twd.c | 32 +++-- arch/arm/mach-exynos4/include/mach/entry-macro.S | 61 +-------- arch/arm/mach-exynos4/mct.c | 6 - arch/arm/mach-msm/board-msm8x60.c | 11 -- arch/arm/mach-msm/include/mach/entry-macro-qgic.S | 73 +---------- arch/arm/mach-msm/timer.c | 52 +++---- arch/arm/mach-omap2/include/mach/entry-macro.S | 14 +-- arch/arm/mach-shmobile/entry-intc.S | 3 - arch/arm/mach-shmobile/include/mach/entry-macro.S | 3 - 19 files changed, 208 insertions(+), 291 deletions(-)
RFC PATCH v9 1/4 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.
This also allows the removal of some duplicated code.
--- arch/arm/common/gic.c | 72 +++++++++++++++++++- arch/arm/include/asm/entry-macro-multi.S | 7 -- arch/arm/include/asm/hardirq.h | 3 - arch/arm/include/asm/hardware/entry-macro-gic.S | 19 +----- arch/arm/include/asm/localtimer.h | 6 +- arch/arm/include/asm/smp.h | 5 -- arch/arm/kernel/irq.c | 3 - arch/arm/kernel/smp.c | 27 ++------ arch/arm/mach-exynos4/include/mach/entry-macro.S | 6 +-- arch/arm/mach-msm/board-msm8x60.c | 11 --- arch/arm/mach-msm/include/mach/entry-macro-qgic.S | 73 +-------------------- arch/arm/mach-omap2/include/mach/entry-macro.S | 14 +---- arch/arm/mach-shmobile/entry-intc.S | 3 - arch/arm/mach-shmobile/include/mach/entry-macro.S | 3 - 14 files changed, 83 insertions(+), 169 deletions(-)
arch/arm/common/gic.c +69 -3
@@ -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);
@@ -256,12 +260,33 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)irq_set_chained_handler (irq, gic_handle_cascade_irq); } +static unsigned int gic_nr_ppis, gic_ppi_base; + +#define PPI_IRQACT (nr) \ + { \ + .handler = percpu_timer_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) {unsigned int gic_irqs, irq_limit, i;void __iomem *base = gic->dist_base;u32 cpumask =1 <<smp_processor_id (); +u32 dist_ctr, nrcpus; +u32 nrppis =0 , ppi_base =0 ; cpumask |= cpumask <<8 ; cpumask |= cpumask <<16 ;
@@ -272,11 +297,38 @@ static void __init gic_dist_init(struct gic_chip_data *gic,* Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources. */ - gic_irqs =readl_relaxed (base +GIC_DIST_CTR ) &0x1f ; - gic_irqs = (gic_irqs +1 ) *32 ; + dist_ctr =readl_relaxed (base +GIC_DIST_CTR ); + gic_irqs = ((dist_ctr &0x1f ) +1 ) *32 ;if (gic_irqs >1020 ) gic_irqs =1020 ; +/* Find out how many CPUs are supported (8 max). */ + nrcpus = ((dist_ctr >>5 ) &7 ) +1 ; + +/* +* Nobody would be insane enough to use PPIs on a secondary +* GIC, right? +*/ +if (gic == &gic_data[0 ]) { + nrppis =16 - (irq_start &15 ); + ppi_base = gic->irq_offset +32 - nrppis; + ppi_irqaction =kzalloc (sizeof (*ppi_irqaction) * nrppis, +GFP_KERNEL ); +if (!ppi_irqaction) { +pr_err ("GIC: Can't allocate PPI memory" ); + nrppis =0 ; + ppi_base =0 ; + } + +for (i =0 ; i < nrppis; i++) + ppi_irqaction[i] = ppi_irqaction_template[i + (ppi_base &15 )]; + gic_nr_ppis = nrppis; + gic_ppi_base = ppi_base; + } + +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. */
@@ -312,7 +364,21 @@ 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_chip_and_handler (ppi, &gic_chip, handle_percpu_irq); +irq_set_chip_data (ppi, gic); +irq_set_status_flags (ppi,IRQ_NOAUTOEN |IRQ_NOPROBE ); + + err =setup_irq (ppi, &ppi_irqaction[i]); +if (err) +pr_err ("GIC: can't setup IRQ%d PPI%d (%d)\n" , + i, 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 );
arch/arm/include/asm/entry-macro-multi.S +0 -7
@@ -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
arch/arm/include/asm/hardirq.h +0 -3
@@ -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
arch/arm/include/asm/hardware/entry-macro-gic.S +2 -17
@@ -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
arch/arm/include/asm/localtimer.h +4 -2
@@ -10,6 +10,8 @@#ifndef __ASM_ARM_LOCALTIMER_H#define __ASM_ARM_LOCALTIMER_H +#include <linux/interrupt.h> +struct clock_event_device ;/*
@@ -18,9 +20,9 @@ struct clock_event_device;void percpu_timer_setup (void );/* -* Called from assembly, this is the local timer IRQ handler +* Per-cpu timer IRQ handler */ -asmlinkage void do_local_timer(struct pt_regs *); +irqreturn_t percpu_timer_handler (int irq,void *dev_id);#ifdef CONFIG_LOCAL_TIMERS
arch/arm/include/asm/smp.h +0 -5
@@ -88,9 +88,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 */
arch/arm/kernel/irq.c +0 -3
@@ -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 ; }
arch/arm/kernel/smp.c +5 -22
@@ -437,10 +437,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; }
@@ -458,29 +454,16 @@ static void ipi_timer(void) }#ifdef CONFIG_LOCAL_TIMERS -asmlinkage void __exception_irq_entry do_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
arch/arm/mach-exynos4/include/mach/entry-macro.S +1 -5
@@ -55,7 +55,7 @@ bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr
@@ -77,7 +77,3 @@ cmpcs \irqnr, \irqnr .endm - /* As above, this assumes that irqstat and base are preserved.. */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm
arch/arm/mach-msm/board-msm8x60.c +0 -11
@@ -42,8 +42,6 @@ static struct platform_device *devices[] __initdata = {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 );
@@ -55,15 +53,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 )
arch/arm/mach-msm/include/mach/entry-macro-qgic.S +1 -72
82 lines not on this page · raw message
arch/arm/mach-omap2/include/mach/entry-macro.S +1 -13
@@ -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 */
arch/arm/mach-shmobile/entry-intc.S +0 -3
@@ -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
arch/arm/mach-shmobile/include/mach/entry-macro.S +0 -3
@@ -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
Signature · 1 line
1.7.0.4
RFC PATCH v9 1/4 ARM: gic: consolidate PPI handling
arch/arm/common/gic.c void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) · line 267
Quoted 9 lines · Marc Zyngier
arch/arm/common/gic.c +5
-0@@ -256,12 +260,33 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)irq_set_chained_handler (irq, gic_handle_cascade_irq); } +static unsigned int gic_nr_ppis, gic_ppi_base; + +#define PPI_IRQACT (nr) \ + { \ + .handler = percpu_timer_handler, \
Won't this break on non-SMP non-localtimer builds?
Quoted 11 lines
arch/arm/common/gic.c +11
-0+ .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 ), +};
arch/arm/common/gic.c void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) · line 288
Quoted 10 lines
arch/arm/common/gic.c +4
-0+ +static struct irqaction *ppi_irqaction; +static void __init gic_dist_init (struct gic_chip_data *gic,unsigned int irq_start) {unsigned int gic_irqs, irq_limit, i;void __iomem *base = gic->dist_base;u32 cpumask =1 <<smp_processor_id (); +u32 dist_ctr, nrcpus;
nrcpus doesn't seem to be used. With that eliminated, dist_ctr doesn't seem to have much purpose.
arch/arm/common/gic.c void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) · line 289
Quoted 1 line
arch/arm/common/gic.c +1
-0+u32 nrppis =0 , ppi_base =0 ;
Might be better to move this inside the "if (gic == &gic_data[0]) {" block, along with the printk too.
Quoted 6 lines
arch/arm/common/gic.c +0
-0cpumask |= cpumask <<8 ; cpumask |= cpumask <<16 ;@@ -272,11 +297,38 @@ static void __init gic_dist_init(struct gic_chip_data *gic,* Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources.
arch/arm/common/gic.c static void __init gic_dist_init(struct gic_chip_data *gic, · line 306
Quoted 10 lines
arch/arm/common/gic.c +4
-2*/ - gic_irqs =readl_relaxed (base +GIC_DIST_CTR ) &0x1f ; - gic_irqs = (gic_irqs +1 ) *32 ; + dist_ctr =readl_relaxed (base +GIC_DIST_CTR ); + gic_irqs = ((dist_ctr &0x1f ) +1 ) *32 ;if (gic_irqs >1020 ) gic_irqs =1020 ; +/* Find out how many CPUs are supported (8 max). */ + nrcpus = ((dist_ctr >>5 ) &7 ) +1 ;
As mentioned above, the above change can be killed because it doesn't alter anything which is used.
Quoted 10 lines
arch/arm/common/gic.c +10
-0+ +/* +* Nobody would be insane enough to use PPIs on a secondary +* GIC, right? +*/ +if (gic == &gic_data[0 ]) { + nrppis =16 - (irq_start &15 ); + ppi_base = gic->irq_offset +32 - nrppis; + ppi_irqaction =kzalloc (sizeof (*ppi_irqaction) * nrppis, +GFP_KERNEL );
arch/arm/common/gic.c static void __init gic_dist_init(struct gic_chip_data *gic, · line 326
Quoted 10 lines
arch/arm/common/gic.c +10
-0+if (!ppi_irqaction) { +pr_err ("GIC: Can't allocate PPI memory" ); + nrppis =0 ; + ppi_base =0 ; + } + +for (i =0 ; i < nrppis; i++) + ppi_irqaction[i] = ppi_irqaction_template[i + (ppi_base &15 )]; + gic_nr_ppis = nrppis; + gic_ppi_base = ppi_base;
Would:
ppi_irqaction = kmemdup(ppi_irqaction_template,
sizeof(*ppi_irqaction) * nrppis,
GFP_KERNEL);
if (ppi_irqaction) {
gic_nr_ppis = nrppis;
gic_ppi_base = ppi_base;
}be a shorter way to write what you have above?
RFC PATCH v9 1/4 ARM: gic: consolidate PPI handling
Quoted 5 lines · Russell King - ARM Linux
Quoted 4 lines · Marc Zyngier
arch/arm/common/gic.c +0
-0@@ -256,12 +260,33 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)irq_set_chained_handler (irq, gic_handle_cascade_irq); }
Quoted 7 lines
arch/arm/common/gic.c void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) · line 267
Quoted 5 lines
arch/arm/common/gic.c +5
-0+static unsigned int gic_nr_ppis, gic_ppi_base; + +#define PPI_IRQACT (nr) \ + { \ + .handler = percpu_timer_handler, \Won't this break on non-SMP non-localtimer builds?
Good point. I'd probably need to #ifdef that (as it was #ifdef'ed in the original code), and remove it in patch #2.
Quoted 44 lines
Quoted 11 lines
arch/arm/common/gic.c +11
-0+ .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 ), +};arch/arm/common/gic.c void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) · line 288
Quoted 10 lines
arch/arm/common/gic.c +4
-0+ +static struct irqaction *ppi_irqaction; +static void __init gic_dist_init (struct gic_chip_data *gic,unsigned int irq_start) {unsigned int gic_irqs, irq_limit, i;void __iomem *base = gic->dist_base;u32 cpumask =1 <<smp_processor_id (); +u32 dist_ctr, nrcpus;nrcpus doesn't seem to be used. With that eliminated,
dist_ctrdoesn't seem to have much purpose.arch/arm/common/gic.c void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) · line 289
Quoted 1 line
arch/arm/common/gic.c +1
-0+u32 nrppis =0 , ppi_base =0 ;Might be better to move this inside the "if (gic == &gic_data[0]) {" block, along with the printk too.
Quoted 14 lines
arch/arm/common/gic.c +2
-2cpumask |= cpumask <<8 ; cpumask |= cpumask <<16 ;@@ -272,11 +297,38 @@ static void __init gic_dist_init(struct gic_chip_data *gic,* Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources. */ - gic_irqs =readl_relaxed (base +GIC_DIST_CTR ) &0x1f ; - gic_irqs = (gic_irqs +1 ) *32 ; + dist_ctr =readl_relaxed (base +GIC_DIST_CTR ); + gic_irqs = ((dist_ctr &0x1f ) +1 ) *32 ;if (gic_irqs >1020 ) gic_irqs =1020 ;
Quoted 5 lines
arch/arm/common/gic.c static void __init gic_dist_init(struct gic_chip_data *gic, · line 306
Quoted 2 lines
arch/arm/common/gic.c +2
-0+/* Find out how many CPUs are supported (8 max). */ + nrcpus = ((dist_ctr >>5 ) &7 ) +1 ;As mentioned above, the above change can be killed because it doesn't alter anything which is used.
Actually, all this really belongs to patch #2. Will move it there.
Quoted 30 lines
Quoted 10 lines
arch/arm/common/gic.c +10
-0+ +/* +* Nobody would be insane enough to use PPIs on a secondary +* GIC, right? +*/ +if (gic == &gic_data[0 ]) { + nrppis =16 - (irq_start &15 ); + ppi_base = gic->irq_offset +32 - nrppis; + ppi_irqaction =kzalloc (sizeof (*ppi_irqaction) * nrppis, +GFP_KERNEL );arch/arm/common/gic.c static void __init gic_dist_init(struct gic_chip_data *gic, · line 326
Quoted 10 lines
arch/arm/common/gic.c +10
-0+if (!ppi_irqaction) { +pr_err ("GIC: Can't allocate PPI memory" ); + nrppis =0 ; + ppi_base =0 ; + } + +for (i =0 ; i < nrppis; i++) + ppi_irqaction[i] = ppi_irqaction_template[i + (ppi_base &15 )]; + gic_nr_ppis = nrppis; + gic_ppi_base = ppi_base;Would:
ppi_irqaction = kmemdup(ppi_irqaction_template, sizeof(*ppi_irqaction) * nrppis, GFP_KERNEL); if (ppi_irqaction) { gic_nr_ppis = nrppis; gic_ppi_base = ppi_base; }
Quoted 1 line
be a shorter way to write what you have above?
Indeed. Will fix that in the next version.
Thanks for reviewing.
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
RFC PATCH v9 2/4 ARM: gic: Add PPI registration interface
In reply to Marc ZyngierThis patch makes it possible to request (and free) a PPI. It thus makes it useable for more than just the local timers.
Update TWD and MSM timers to use that functionnality.
Based on an earlier patch by Russell King.
--- arch/arm/common/gic.c | 82 +++++++++++++++++++++++++++++----- arch/arm/include/asm/hardware/gic.h | 5 ++- arch/arm/include/asm/localtimer.h | 7 +++- arch/arm/include/asm/smp_twd.h | 1 + arch/arm/kernel/smp.c | 4 +- arch/arm/kernel/smp_twd.c | 17 ++++++- arch/arm/mach-msm/timer.c | 52 +++++++++++----------- 7 files changed, 124 insertions(+), 44 deletions(-)
arch/arm/common/gic.c +70 -12
@@ -35,7 +35,6 @@#include <asm/irq.h> #include <asm/mach/irq.h> #include <asm/hardware/gic.h> -#include <asm/localtimer.h> static DEFINE_SPINLOCK (irq_controller_lock);
@@ -260,11 +259,29 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)irq_set_chained_handler (irq, gic_handle_cascade_irq); } +struct gic_action { +irq_handler_t handler; +void *data; +}; + +static DEFINE_PER_CPU(struct gic_action *, gic_ppi_action);static unsigned int gic_nr_ppis, gic_ppi_base; +static irqreturn_t gic_ppi_handler (int irq,void *dev_id) +{ +unsigned int ppi = irq - gic_ppi_base; +struct gic_action *act; + + act = &__get_cpu_var (gic_ppi_action)[ppi]; +if (likely (act->handler)) +return act->handler (irq, act->data); + +return IRQ_NONE ; +} +#define PPI_IRQACT (nr) \ { \ - .handler = percpu_timer_handler, \ + .handler = gic_ppi_handler, \ .flags = IRQF_PERCPU | IRQF_TIMER, \ .irq = nr, \ .name = "PPI-" # nr, \
@@ -322,6 +339,14 @@ static void __init gic_dist_init(struct gic_chip_data *gic,for (i =0 ; i < nrppis; i++) ppi_irqaction[i] = ppi_irqaction_template[i + (ppi_base &15 )]; +for (i =0 ; i < nrcpus; i++) { +struct gic_action **ppiacts; + + ppiacts = &per_cpu (gic_ppi_action, i); + *ppiacts =kzalloc (sizeof (*gic_ppi_action) * nrppis,GFP_KERNEL ); +if (!*ppiacts) +pr_err ("GIC: Can't allocate PPI CPU%d memory" , i); + } gic_nr_ppis = nrppis; gic_ppi_base = ppi_base; }
@@ -387,6 +412,49 @@ static void __init gic_dist_init(struct gic_chip_data *gic,writel_relaxed (1 , base +GIC_DIST_CTRL ); } +int gic_request_ppi (unsigned int irq,irq_handler_t handler,void *data) +{ +struct gic_action *act; +unsigned long flags; +unsigned int ppi = irq - gic_ppi_base; +int ret = -EBUSY ; + +if (ppi >= gic_nr_ppis) +return -EINVAL ; + +local_irq_save (flags); + act = &__get_cpu_var (gic_ppi_action)[ppi]; +if (!act->handler) { + act->handler = handler; + act->data = data; + +gic_unmask_irq (irq_get_irq_data (irq)); + ret =0 ; + } +local_irq_restore (flags); + +return ret; +} + +void gic_free_ppi (unsigned int irq,void *data) +{ +struct gic_action *act; +unsigned long flags; +unsigned int ppi = irq - gic_ppi_base; + +if (ppi >= gic_nr_ppis) +return ; + +local_irq_save (flags); + act = &__get_cpu_var (gic_ppi_action)[ppi]; +if (act->data == data) { +gic_mask_irq (irq_get_irq_data (irq)); + act->handler =NULL ; + act->data =NULL ; + } +local_irq_restore (flags); +} +static void __cpuinit gic_cpu_init (struct gic_chip_data *gic) {void __iomem *dist_base = gic->dist_base;
@@ -436,16 +504,6 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr)gic_cpu_init (&gic_data[gic_nr]); } -void __cpuinit gic_enable_ppi (unsigned int irq) -{ -unsigned long flags; - -local_irq_save (flags); -irq_set_status_flags (irq,IRQ_NOPROBE ); -gic_unmask_irq (irq_get_irq_data (irq)); -local_irq_restore (flags); -} -#ifdef CONFIG_SMP void gic_raise_softirq (const struct cpumask *mask,unsigned int irq) {
arch/arm/include/asm/hardware/gic.h +4 -1
@@ -33,6 +33,8 @@#define GIC_DIST_SOFTINT 0xf00#ifndef __ASSEMBLY__ +#include <linux/interrupt.h> +extern void __iomem *gic_cpu_base_addr;extern struct irq_chip gic_arch_extn;
@@ -40,7 +42,8 @@ void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);void gic_secondary_init (unsigned int );void gic_cascade_irq (unsigned int gic_nr,unsigned int irq);void gic_raise_softirq (const struct cpumask *mask,unsigned int irq); -void gic_enable_ppi (unsigned int ); +int gic_request_ppi (unsigned int irq,irq_handler_t handler,void *data); +void gic_free_ppi (unsigned int irq,void *data);struct gic_chip_data {unsigned int irq_offset;
arch/arm/include/asm/localtimer.h +6 -1
@@ -24,7 +24,6 @@ void percpu_timer_setup(void);*/ irqreturn_t percpu_timer_handler (int irq,void *dev_id); -#ifdef CONFIG_LOCAL_TIMERS #ifdef CONFIG_HAVE_ARM_TWD
@@ -32,6 +31,7 @@ irqreturn_t percpu_timer_handler(int irq, void *dev_id);#include "smp_twd.h" #define local_timer_ack () twd_timer_ack() +#define local_timer_stop (c) twd_timer_stop((c))#else
@@ -41,6 +41,11 @@ irqreturn_t percpu_timer_handler(int irq, void *dev_id);*/ int local_timer_ack (void ); +/* +* Stop the local timer +*/ +void local_timer_stop (struct clock_event_device *); +#endif /*
arch/arm/include/asm/smp_twd.h +1 -0
@@ -24,5 +24,6 @@ extern void __iomem *twd_base;int twd_timer_ack (void );void twd_timer_setup (struct clock_event_device *); +void twd_timer_stop (struct clock_event_device *);#endif
arch/arm/kernel/smp.c +2 -2
@@ -456,7 +456,7 @@ static void ipi_timer(void)#ifdef CONFIG_LOCAL_TIMERS irqreturn_t percpu_timer_handler (int irq,void *dev_id) { -struct clock_event_device *evt = &__get_cpu_var (percpu_clockevent); +struct clock_event_device *evt = dev_id;if (local_timer_ack ()) { evt->event_handler (evt);
@@ -517,7 +517,7 @@ static void percpu_timer_stop(void)unsigned int cpu =smp_processor_id ();struct clock_event_device *evt = &per_cpu (percpu_clockevent, cpu); - evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); + local_timer_stop(evt); }#endif
arch/arm/kernel/smp_twd.c +15 -2
@@ -19,6 +19,7 @@#include <linux/io.h> #include <asm/smp_twd.h> +#include <asm/localtimer.h> #include <asm/hardware/gic.h> /* set up by the platform code */
@@ -80,6 +81,12 @@ int twd_timer_ack(void)return 0 ; } +void twd_timer_stop (struct clock_event_device *clk) +{ +twd_set_mode (CLOCK_EVT_MODE_UNUSED , clk); +gic_free_ppi (clk->irq, clk); +} +static void __cpuinit twd_calibrate_rate (void ) {unsigned long count;
@@ -124,6 +131,8 @@ static void __cpuinit twd_calibrate_rate(void)*/ void __cpuinit twd_timer_setup (struct clock_event_device *clk) { +int err; +twd_calibrate_rate (); clk->name ="local_timer" ;
@@ -137,8 +146,12 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) clk->max_delta_ns =clockevent_delta2ns (0xffffffff , clk); clk->min_delta_ns =clockevent_delta2ns (0xf , clk); -/* Make sure our local interrupt controller has this enabled */ -gic_enable_ppi (clk->irq); + err =gic_request_ppi (clk->irq, percpu_timer_handler, clk); +if (err) { +pr_err ("%s: can't register interrupt %d on cpu %d (%d)\n" , + clk->name, clk->irq,smp_processor_id (), err); +return ; + }clockevents_register_device (clk); }
arch/arm/mach-msm/timer.c +26 -26
115 lines not on this page · raw message
Signature · 1 line
1.7.0.4
RFC PATCH v9 2/4 ARM: gic: Add PPI registration interface
arch/arm/mach-msm/timer.c int __cpuinit local_timer_setup(struct clock_event_device *evt) · line 292
Quoted 8 lines · Marc Zyngier
arch/arm/mach-msm/timer.c +1
-3@@ -298,14 +289,23 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)clockevent_delta2ns (0xf0000000 >> clock->shift, evt); evt->min_delta_ns =clockevent_delta2ns (4 , evt); - local_clock_event = evt; - -gic_enable_ppi (clock->irq.irq); + res =git_request_ppi (evt->irq, msm_timer_interrupt, evt);
Typo, should be gic_request_ppi.
Jeff
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
RFC PATCH v9 2/4 ARM: gic: Add PPI registration interface
Quoted 11 lines · Jeff Ohlstein
arch/arm/mach-msm/timer.c int __cpuinit local_timer_setup(struct clock_event_device *evt) · line 292
Quoted 8 lines · Marc Zyngier
arch/arm/mach-msm/timer.c +1
-3@@ -298,14 +289,23 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)clockevent_delta2ns (0xf0000000 >> clock->shift, evt); evt->min_delta_ns =clockevent_delta2ns (4 , evt); - local_clock_event = evt; - -gic_enable_ppi (clock->irq.irq); + res =git_request_ppi (evt->irq, msm_timer_interrupt, evt);Typo, should be
gic_request_ppi.
Doh... Thanks for noticing this.
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
RFC PATCH v9 3/4 ARM: local timers: drop local_timer_ack()
In reply to Marc ZyngierOnly TWD uses local_timer_ack() to perform something useful. Make it the real (private) interrupt handler, remove the common interrupt handler as well as the useless stubs from MCT and MSM.
--- arch/arm/include/asm/localtimer.h | 7 ------- arch/arm/include/asm/smp_twd.h | 1 - arch/arm/kernel/smp.c | 14 -------------- arch/arm/kernel/smp_twd.c | 17 +++++++---------- arch/arm/mach-exynos4/mct.c | 6 ------ arch/arm/mach-msm/timer.c | 6 ------ 6 files changed, 7 insertions(+), 44 deletions(-)
arch/arm/include/asm/localtimer.h +0 -7
@@ -30,18 +30,11 @@ irqreturn_t percpu_timer_handler(int irq, void *dev_id);#include "smp_twd.h" -#define local_timer_ack () twd_timer_ack()#define local_timer_stop (c) twd_timer_stop((c))#else /* -* Platform provides this to acknowledge a local timer IRQ. -* Returns true if the local timer IRQ is to be processed. -*/ -int local_timer_ack (void ); - -/* * Stop the local timer */ void local_timer_stop (struct clock_event_device *);
arch/arm/include/asm/smp_twd.h +0 -1
@@ -22,7 +22,6 @@ struct clock_event_device;extern void __iomem *twd_base; -int twd_timer_ack (void );void twd_timer_setup (struct clock_event_device *);void twd_timer_stop (struct clock_event_device *);
arch/arm/kernel/smp.c +0 -14
@@ -453,20 +453,6 @@ static void ipi_timer(void)irq_exit (); } -#ifdef CONFIG_LOCAL_TIMERS -irqreturn_t percpu_timer_handler (int irq,void *dev_id) -{ -struct clock_event_device *evt = dev_id; - -if (local_timer_ack ()) { - evt->event_handler (evt); -return IRQ_HANDLED ; - } - -return IRQ_NONE ; -} -#endif -#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST static void smp_timer_broadcast (const struct cpumask *mask) {
arch/arm/kernel/smp_twd.c +7 -10
@@ -65,20 +65,17 @@ static int twd_set_next_event(unsigned long evt,return 0 ; } -/* -* local_timer_ack: checks for a local timer interrupt. -* -* If a local timer interrupt has occurred, acknowledge and return 1. -* Otherwise, return 0. -*/ -int twd_timer_ack (void ) +static irqreturn_t twd_timer_handler (int irq,void *dev_id) { +struct clock_event_device *clk = dev_id; +if (__raw_readl (twd_base +TWD_TIMER_INTSTAT )) {__raw_writel (1 , twd_base +TWD_TIMER_INTSTAT ); -return 1 ; + clk->event_handler (clk); +return IRQ_HANDLED ; } -return 0 ; +return IRQ_NONE ; }void twd_timer_stop (struct clock_event_device *clk)
@@ -146,7 +143,7 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) clk->max_delta_ns =clockevent_delta2ns (0xffffffff , clk); clk->min_delta_ns =clockevent_delta2ns (0xf , clk); - err =gic_request_ppi (clk->irq, percpu_timer_handler, clk); + err =gic_request_ppi (clk->irq, twd_timer_handler, clk);if (err) {pr_err ("%s: can't register interrupt %d on cpu %d (%d)\n" , clk->name, clk->irq,smp_processor_id (), err);
arch/arm/mach-exynos4/mct.c +0 -6
@@ -393,12 +393,6 @@ void __cpuinit local_timer_setup(struct clock_event_device *evt) {exynos4_mct_tick_init (evt); } - -int local_timer_ack (void ) -{ -return 0 ; -} -#endif /* CONFIG_LOCAL_TIMERS */ static void __init exynos4_timer_resources (void )
arch/arm/mach-msm/timer.c +0 -6
@@ -305,12 +305,6 @@ void local_timer_stop(struct clock_event_device *evt) evt->set_mode (CLOCK_EVT_MODE_UNUSED , evt);gic_free_ppi (evt->irq, evt); } - -inline int local_timer_ack (void ) -{ -return 1 ; -} -#endif struct sys_timer msm_timer = {
Signature · 1 line
1.7.0.4
RFC PATCH v9 4/4 ARM: gic: add compute_irqnr macro for exynos4
In reply to Marc Zyngierexynos4 has a full copy of entry-macro-gic.S, just for the sake of an offset added to the IRQ number read from the GIC.
Add a compute_irqnr macro to entry-macro-gic.S so that any platform can add it's own hook without having to copy the whole file again.
--- arch/arm/include/asm/hardware/entry-macro-gic.S | 3 + arch/arm/mach-exynos4/include/mach/entry-macro.S | 57 +++------------------- 2 files changed, 10 insertions(+), 50 deletions(-)
arch/arm/include/asm/hardware/entry-macro-gic.S +3 -0
@@ -43,6 +43,9 @@ cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr +#ifdef HAVE_GIC_BASE_OFFSET + compute_irqnr \irqnr, \tmp +#endif .endm /* We assume that irqstat (the raw value of the IRQ acknowledge
arch/arm/mach-exynos4/include/mach/entry-macro.S +7 -50
75 lines not on this page · raw message
Signature · 1 line
1.7.0.4
v9 cover Consolidating GIC per-cpu interrupts
In reply to Marc ZyngierQuoted 3 lines · Marc Zyngier
The current GIC per-cpu interrupts (aka PPIs) suffer from a number of problems:
[snip]
Quoted 4 lines
Patches against next-20110721, tested on PB11MP. As this patch series is quite different from the previous one, I've dropped all previous acks from platform maintainers.
I tried this out the other day and it didn't work. The setup_irq() calls in msm timer code fails and then we calibrate delay forever. I haven't looked further.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
v9 cover Consolidating GIC per-cpu interrupts
Quoted 6 lines · Stephen Boyd
Quoted 3 lines · Marc Zyngier
The current GIC per-cpu interrupts (aka PPIs) suffer from a number of problems:
[snip]
Quoted 1 line
Quoted 7 lines
Quoted 3 lines
Patches against next-20110721, tested on PB11MP. As this patch series is quite different from the previous one, I've dropped all previous acks from platform maintainers.
I tried this out the other day and it didn't work. The
setup_irq()calls in msm timer code fails and then we calibrate delay forever. I haven't looked further.
Ah, of course. Need to do a gic_request_ppi() on CPU0's timer instead of request_irq(), but only on msm8x60/msm8960. Not very nice.
I'll update the patches and repost them later today.
Cheers,
M.
Signature · 1 line
Jazz is not dead. It just smells funny...