Skip to content

Commit

Permalink
ARM: gic: consolidate PPI handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marc Zyngier committed Oct 23, 2011
1 parent 88b6fc8 commit 292b293
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 178 deletions.
75 changes: 74 additions & 1 deletion arch/arm/common/gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -255,13 +259,40 @@ 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)
{
unsigned int gic_irqs, irq_limit, i;
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());
Expand All @@ -282,6 +313,33 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
if (gic_irqs > 1020)
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.
*/
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 0 additions & 7 deletions arch/arm/include/asm/entry-macro-multi.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions arch/arm/include/asm/hardirq.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 2 additions & 17 deletions arch/arm/include/asm/hardware/entry-macro-gic.S
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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
Expand All @@ -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
11 changes: 4 additions & 7 deletions arch/arm/include/asm/localtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#ifndef __ASM_ARM_LOCALTIMER_H
#define __ASM_ARM_LOCALTIMER_H

#include <linux/interrupt.h>

struct clock_event_device;

/*
Expand All @@ -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

Expand Down
5 changes: 0 additions & 5 deletions arch/arm/include/asm/smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
3 changes: 0 additions & 3 deletions arch/arm/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)
#endif
#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;
Expand Down
32 changes: 5 additions & 27 deletions arch/arm/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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

Expand Down
7 changes: 1 addition & 6 deletions arch/arm/mach-exynos4/include/mach/entry-macro.S
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

bic \irqnr, \irqstat, #0x1c00

cmp \irqnr, #29
cmp \irqnr, #15
cmpcc \irqnr, \irqnr
cmpne \irqnr, \tmp
cmpcs \irqnr, \irqnr
Expand All @@ -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
11 changes: 0 additions & 11 deletions arch/arm/mach-msm/board-msm8x60.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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)
Expand Down
Loading

0 comments on commit 292b293

Please sign in to comment.