@@ -27,12 +27,16 @@
#include <linux/list.h>
#include <linux/smp.h>
#include <linux/cpumask.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/hardware/gic.h>
+#define GIC_FIRST_PPI 16
+#define NR_PPI 16
+
static DEFINE_SPINLOCK(irq_controller_lock);
/* Address of GIC 0 CPU interface */
@@ -376,14 +380,74 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr)
gic_cpu_init(&gic_data[gic_nr]);
}
-void __cpuinit gic_enable_ppi(unsigned int irq)
+struct gic_action {
+ irq_handler_t handler;
+ void *data;
+};
+
+static DEFINE_PER_CPU(struct gic_action[NR_PPI], gic_ppi_action);
+
+asmlinkage void __exception_irq_entry gic_call_ppi(unsigned int irq,
+ struct pt_regs *regs)
+{
+ unsigned int ppi = irq - GIC_FIRST_PPI;
+ struct gic_action *act;
+
+ act = &__get_cpu_var(gic_ppi_action)[ppi];
+ if (act->handler) {
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ /* FIXME: need to deal with PPI IRQ stats better.. */
+ __inc_irq_stat(smp_processor_id(), local_timer_irqs);
+
+ irq_enter();
+ act->handler(irq, act->data);
+ irq_exit();
+
+ set_irq_regs(old_regs);
+ }
+}
+
+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_FIRST_PPI;
+ int ret = -EBUSY;
+
+ if (ppi >= NR_PPI)
+ return -EINVAL;
local_irq_save(flags);
- irq_set_status_flags(irq, IRQ_NOPROBE);
- gic_unmask_irq(irq_get_irq_data(irq));
+ act = &__get_cpu_var(gic_ppi_action)[ppi];
+ if (!act->handler) {
+ act->handler = handler;
+ act->data = data;
+
+ irq_set_status_flags(irq, IRQ_NOPROBE);
+ gic_unmask_irq(irq_get_irq_data(irq));
+ }
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_FIRST_PPI;
+
+ if (ppi < NR_PPI) {
+ 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);
+ }
}
#ifdef CONFIG_SMP
@@ -27,10 +27,7 @@
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
+ test_for_ppi r0, r2, r6, lr, sp, BSYM(1b)
#endif
#endif
9997:
@@ -63,13 +63,16 @@
cmpcs \irqnr, \irqnr
.endm
-/* As above, this assumes that irqstat and base are preserved.. */
+/*
+ * We will have checked for normal IRQs (32+) and IPIs (0-15) so only
+ * PPIs are left here.
+ */
- .macro test_for_ltirq, irqnr, irqstat, base, tmp
+ .macro test_for_ppi, irqnr, irqstat, base, tmp, regs, sym
bic \irqnr, \irqstat, #0x1c00
- mov \tmp, #0
- cmp \irqnr, #29
- moveq \tmp, #1
- streq \irqstat, [\base, #GIC_CPU_EOI]
- cmp \tmp, #0
+ cmp \irqnr, #32
+ strcc \irqstat, [\base, #GIC_CPU_EOI]
+ movcc r1, \regs
+ adrcc lr, \sym
+ bcc gic_call_ppi
.endm
@@ -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_handler_t, void *);
+void gic_free_ppi(unsigned int, void *);
#endif
#endif
@@ -17,27 +17,17 @@ 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 *);
-
-
#ifdef CONFIG_LOCAL_TIMERS
#ifdef CONFIG_HAVE_ARM_TWD
#include "smp_twd.h"
-#define local_timer_ack() twd_timer_ack()
+#define local_timer_stop twd_timer_stop
#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);
+int local_timer_stop(struct clock_event_device *);
#endif
@@ -22,7 +22,7 @@ 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 *);
#endif
@@ -458,19 +458,6 @@ static void ipi_timer(void)
}
#ifdef CONFIG_LOCAL_TIMERS
-asmlinkage void __exception_irq_entry do_local_timer(struct pt_regs *regs)
-{
- struct pt_regs *old_regs = set_irq_regs(regs);
- int cpu = smp_processor_id();
-
- if (local_timer_ack()) {
- __inc_irq_stat(cpu, local_timer_irqs);
- ipi_timer();
- }
-
- set_irq_regs(old_regs);
-}
-
void show_local_irqs(struct seq_file *p, int prec)
{
unsigned int cpu;
@@ -531,10 +518,7 @@ void __cpuinit percpu_timer_setup(void)
*/
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(&per_cpu(percpu_clockevent, smp_processor_id()));
}
#endif
@@ -26,6 +26,18 @@ void __iomem *twd_base;
static unsigned long twd_timer_rate;
+static irqreturn_t twd_irq(int irq, void *data)
+{
+ struct clock_event_device *evt = data;
+
+ if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
+ __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
+ evt->event_handler(evt);
+ }
+
+ return IRQ_HANDLED;
+}
+
static void twd_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
{
@@ -64,22 +76,6 @@ 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)
-{
- if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
- __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
- return 1;
- }
-
- return 0;
-}
-
static void __cpuinit twd_calibrate_rate(void)
{
unsigned long count;
@@ -124,6 +120,8 @@ static void __cpuinit twd_calibrate_rate(void)
*/
void __cpuinit twd_timer_setup(struct clock_event_device *clk)
{
+ int ret;
+
twd_calibrate_rate();
clk->name = "local_timer";
@@ -138,7 +136,16 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
/* Make sure our local interrupt controller has this enabled */
- gic_enable_ppi(clk->irq);
+ ret = gic_request_ppi(clk->irq, twd_irq, clk);
+ if (ret)
+ pr_err("CPU%u: unable to request TWD interrupt\n",
+ smp_processor_id());
clockevents_register_device(clk);
}
+
+void twd_timer_stop(struct clock_event_device *clk)
+{
+ clk->set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+ gic_free_ppi(clk->irq, clk);
+}
@@ -74,11 +74,11 @@
/* As above, this assumes that irqstat and base are preserved.. */
- .macro test_for_ltirq, irqnr, irqstat, base, tmp
+ .macro test_for_ppi, irqnr, irqstat, base, tmp, regs, sym
bic \irqnr, \irqstat, #0x1c00
- mov \tmp, #0
cmp \irqnr, #29
- moveq \tmp, #1
streq \irqstat, [\base, #GIC_CPU_EOI]
- cmp \tmp, #0
+ moveq r1, \regs
+ adreq lr, \sym
+ bcc gic_call_ppi
.endm
@@ -78,11 +78,11 @@
/* 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
+ .macro test_for_ppi, irqnr, irqstat, base, tmp, regs, sym
+ bic \irqnr, \irqstat, #0x1c00
+ cmp \irqnr, #16
+ streq \irqstat, [\base, #GIC_CPU_EOI]
+ moveq r1, \regs
+ adreq lr, \sym
+ beq gic_call_ppi
.endm
@@ -271,9 +271,19 @@ static void __init msm_timer_init(void)
}
#ifdef CONFIG_SMP
+static irqreturn_t local_timer_irq(int irq, void *data)
+{
+ struct clock_event_device *evt = data;
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
+ int ret;
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
@@ -300,15 +310,19 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)
local_clock_event = evt;
- gic_enable_ppi(clock->irq.irq);
+ ret = gic_request_ppi(evt->irq, local_timer_irq, evt);
+ if (ret)
+ pr_err("CPU%u: unable to request local timer interrupt\n",
+ smp_processor_id());
clockevents_register_device(evt);
return 0;
}
-inline int local_timer_ack(void)
+void local_timer_stop(struct clock_event_device *evt)
{
- return 1;
+ clk->set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+ gic_free_ppi(clk->irq, clk);
}
#endif
@@ -104,14 +104,13 @@
/* As above, this assumes that irqstat and base are preserved */
- .macro test_for_ltirq, irqnr, irqstat, base, tmp
+ .macro test_for_ppi, irqnr, irqstat, base, tmp, regs, sym
bic \irqnr, \irqstat, #0x1c00
- mov \tmp, #0
cmp \irqnr, #29
- itt eq
- moveq \tmp, #1
streq \irqstat, [\base, #GIC_CPU_EOI]
- cmp \tmp, #0
+ moveq r1, \regs
+ adreq lr, \sym
+ beq gic_call_ppi
.endm
#endif /* CONFIG_SMP */
@@ -51,7 +51,7 @@
.macro test_for_ipi, irqnr, irqstat, base, tmp
.endm
- .macro test_for_ltirq, irqnr, irqstat, base, tmp
+ .macro test_for_ppi, irqnr, irqstat, base, tmp, regs, sym
.endm
arch_irq_handler shmobile_handle_irq_intc
@@ -27,7 +27,7 @@
.macro test_for_ipi, irqnr, irqstat, base, tmp
.endm
- .macro test_for_ltirq, irqnr, irqstat, base, tmp
+ .macro test_for_ppi, irqnr, irqstat, base, tmp, regs, sym
.endm
.macro arch_ret_to_user, tmp1, tmp2