All of lore.kernel.org
 help / color / mirror / Atom feed
From: marc.zyngier@arm.com (Marc Zyngier)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH v9 2/4] ARM: gic: Add PPI registration interface
Date: Thu, 21 Jul 2011 17:57:26 +0100	[thread overview]
Message-ID: <1311267448-14652-3-git-send-email-marc.zyngier@arm.com> (raw)
In-Reply-To: <1311267448-14652-1-git-send-email-marc.zyngier@arm.com>

This 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.

Cc: David Brown <davidb@codeaurora.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 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(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 6a54c28..30ee245 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -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)
 {
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 435d3f8..b37780f 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -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;
diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h
index e3663f7..9b0dfbb 100644
--- a/arch/arm/include/asm/localtimer.h
+++ b/arch/arm/include/asm/localtimer.h
@@ -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
 
 /*
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
index fed9981..6923037 100644
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -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
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index c213f6a..9bcdbba 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -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
 
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 2c277d4..b2c4543 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -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);
 }
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 63621f1..0f49b5c 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -71,7 +71,7 @@ enum timer_location {
 struct msm_clock {
 	struct clock_event_device   clockevent;
 	struct clocksource          clocksource;
-	struct irqaction            irq;
+	unsigned int		    irq;
 	void __iomem                *regbase;
 	uint32_t                    freq;
 	uint32_t                    shift;
@@ -87,13 +87,10 @@ enum {
 
 
 static struct msm_clock msm_clocks[];
-static struct clock_event_device *local_clock_event;
 
 static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
 {
 	struct clock_event_device *evt = dev_id;
-	if (smp_processor_id() != 0)
-		evt = local_clock_event;
 	if (evt->event_handler == NULL)
 		return IRQ_HANDLED;
 	evt->event_handler(evt);
@@ -171,13 +168,7 @@ static struct msm_clock msm_clocks[] = {
 			.mask           = CLOCKSOURCE_MASK(32),
 			.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 		},
-		.irq = {
-			.name    = "gp_timer",
-			.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
-			.handler = msm_timer_interrupt,
-			.dev_id  = &msm_clocks[0].clockevent,
-			.irq     = INT_GP_TIMER_EXP
-		},
+		.irq = INT_GP_TIMER_EXP,
 		.freq = GPT_HZ,
 	},
 	[MSM_CLOCK_DGT] = {
@@ -196,13 +187,7 @@ static struct msm_clock msm_clocks[] = {
 			.mask           = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
 			.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 		},
-		.irq = {
-			.name    = "dg_timer",
-			.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
-			.handler = msm_timer_interrupt,
-			.dev_id  = &msm_clocks[1].clockevent,
-			.irq     = INT_DEBUG_TIMER_EXP
-		},
+		.irq = INT_DEBUG_TIMER_EXP,
 		.freq = DGT_HZ >> MSM_DGT_SHIFT,
 		.shift = MSM_DGT_SHIFT,
 	}
@@ -261,10 +246,13 @@ static void __init msm_timer_init(void)
 			printk(KERN_ERR "msm_timer_init: clocksource_register "
 			       "failed for %s\n", cs->name);
 
-		res = setup_irq(clock->irq.irq, &clock->irq);
+		ce->irq = clock->irq;
+		res = request_irq(ce->irq, msm_timer_interrupt,
+				  IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
+				  ce->name, ce);
 		if (res)
-			printk(KERN_ERR "msm_timer_init: setup_irq "
-			       "failed for %s\n", cs->name);
+			pr_err("msm_timer_init: request_irq failed for %s\n",
+			       ce->name);
 
 		clockevents_register_device(ce);
 	}
@@ -273,7 +261,9 @@ static void __init msm_timer_init(void)
 #ifdef CONFIG_SMP
 int __cpuinit local_timer_setup(struct clock_event_device *evt)
 {
+	static bool local_timer_inited;
 	struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
+	int res;
 
 	/* Use existing clock_event for cpu 0 */
 	if (!smp_processor_id())
@@ -281,12 +271,13 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)
 
 	writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 
-	if (!local_clock_event) {
+	if (!local_timer_inited) {
 		writel(0, clock->regbase  + TIMER_ENABLE);
 		writel(0, clock->regbase + TIMER_CLEAR);
 		writel(~0, clock->regbase + TIMER_MATCH_VAL);
+		local_timer_inited = true;
 	}
-	evt->irq = clock->irq.irq;
+	evt->irq = clock->irq;
 	evt->name = "local_timer";
 	evt->features = CLOCK_EVT_FEAT_ONESHOT;
 	evt->rating = clock->clockevent.rating;
@@ -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);
+	if (res) {
+		pr_err("local_timer_setup: request_irq failed for %s\n",
+			       clock->clockevent.name);
+		return res;
+	}
 
 	clockevents_register_device(evt);
 	return 0;
 }
 
+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;
-- 
1.7.0.4

  parent reply	other threads:[~2011-07-21 16:57 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-21 16:57 [RFC PATCH v9 0/4] Consolidating GIC per-cpu interrupts Marc Zyngier
2011-07-21 16:57 ` [RFC PATCH v9 1/4] ARM: gic: consolidate PPI handling Marc Zyngier
2011-07-22  9:42   ` Russell King - ARM Linux
2011-07-22 10:01     ` Marc Zyngier
2011-07-21 16:57 ` Marc Zyngier [this message]
2011-07-22  0:21   ` [RFC PATCH v9 2/4] ARM: gic: Add PPI registration interface Jeff Ohlstein
2011-07-22  9:30     ` Marc Zyngier
2011-07-21 16:57 ` [RFC PATCH v9 3/4] ARM: local timers: drop local_timer_ack() Marc Zyngier
2011-07-21 16:57 ` [RFC PATCH v9 4/4] ARM: gic: add compute_irqnr macro for exynos4 Marc Zyngier
2011-07-28  6:34 ` [RFC PATCH v9 0/4] Consolidating GIC per-cpu interrupts Stephen Boyd
2011-08-01  8:20   ` Marc Zyngier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1311267448-14652-3-git-send-email-marc.zyngier@arm.com \
    --to=marc.zyngier@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.