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>
This commit is contained in:
Родитель
88b6fc8c57
Коммит
292b293cee
|
@ -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());
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
* Per-cpu timer IRQ handler
|
||||
*/
|
||||
asmlinkage void do_local_timer(struct pt_regs *);
|
||||
|
||||
/*
|
||||
* Called from C code
|
||||
*/
|
||||
void handle_local_timer(struct pt_regs *);
|
||||
irqreturn_t percpu_timer_handler(int irq, void *dev_id);
|
||||
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
irqreturn_t percpu_timer_handler(int irq, void *dev_id)
|
||||
{
|
||||
handle_local_timer(regs);
|
||||
}
|
||||
|
||||
void handle_local_timer(struct pt_regs *regs)
|
||||
{
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче