From patchwork Tue Aug 9 10:46:53 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marc Zyngier X-Patchwork-Id: 1048902 Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p79AlmKi013394 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 9 Aug 2011 10:48:10 GMT Received: from canuck.infradead.org ([134.117.69.58]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QqjqT-0004f2-Dk; Tue, 09 Aug 2011 10:47:38 +0000 Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1QqjqS-0004GH-NL; Tue, 09 Aug 2011 10:47:36 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by canuck.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Qqjq2-0004AA-UG for linux-arm-kernel@canuck.infradead.org; Tue, 09 Aug 2011 10:47:11 +0000 Received: from service87.mimecast.com ([94.185.240.25]) by casper.infradead.org with smtp (Exim 4.76 #1 (Red Hat Linux)) id 1Qqjpy-0002lp-Oe for linux-arm-kernel@lists.infradead.org; Tue, 09 Aug 2011 10:47:09 +0000 Received: from cam-owa2.Emea.Arm.com (fw-tnat.cambridge.arm.com [217.140.96.21]) by service87.mimecast.com; Tue, 09 Aug 2011 11:47:05 +0100 Received: from localhost.localdomain ([10.1.255.212]) by cam-owa2.Emea.Arm.com with Microsoft SMTPSVC(6.0.3790.3959); Tue, 9 Aug 2011 11:46:57 +0100 From: Marc Zyngier To: linux-arm-kernel@lists.infradead.org Subject: [RFC PATCH 11/12] ARM: local timers: make MCT timer standalone Date: Tue, 9 Aug 2011 11:46:53 +0100 Message-Id: <1312886814-15627-12-git-send-email-marc.zyngier@arm.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1312886814-15627-1-git-send-email-marc.zyngier@arm.com> References: <1312886814-15627-1-git-send-email-marc.zyngier@arm.com> X-OriginalArrivalTime: 09 Aug 2011 10:46:57.0088 (UTC) FILETIME=[A8DF5400:01CC5681] X-MC-Unique: 111080911470503501 X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110809_114706_966242_B6DEA395 X-CRM114-Status: GOOD ( 18.75 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on casper.infradead.org summary: Content analysis details: (-2.6 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [94.185.240.25 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Kukjin Kim X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Tue, 09 Aug 2011 10:48:11 +0000 (UTC) Following the same pattern used for smp_twd, allow the MCT timers to be built in standalone, without relying on the CONFIG_LOCAL_TIMER infrastructure and use a CPU notifier block to stop or start the timer on the right CPU. Cc: Kukjin Kim Signed-off-by: Marc Zyngier --- arch/arm/Kconfig | 2 +- arch/arm/mach-exynos4/mct.c | 75 ++++++++++++++++++++++++++++++++---------- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 13cdba4..ef67b64 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1436,7 +1436,7 @@ config HOTPLUG_CPU config LOCAL_TIMERS bool "Use local timer interrupts" - depends on SMP && !ARM_SMP_TWD && !ARCH_MSM + depends on SMP && !ARM_SMP_TWD && !ARCH_MSM && !EXYNOS4_MCT default y help Enable support for local timers on SMP platforms, rather then the diff --git a/arch/arm/mach-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c index 194fc6d..abe92cf 100644 --- a/arch/arm/mach-exynos4/mct.c +++ b/arch/arm/mach-exynos4/mct.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -28,11 +29,11 @@ static unsigned long clk_cnt_per_tick; static unsigned long clk_rate; struct mct_clock_event_device { - struct clock_event_device *evt; + struct clock_event_device evt; void __iomem *base; }; -struct mct_clock_event_device mct_tick[2]; +static struct mct_clock_event_device __percpu *mct_tick; static void exynos4_mct_write(unsigned int value, void *addr) { @@ -249,10 +250,10 @@ static void exynos4_clockevent_init(void) setup_irq(IRQ_MCT_G0, &mct_comp_event_irq); } -#ifdef CONFIG_LOCAL_TIMERS /* Clock event handling */ -static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt) +static void exynos4_mct_tick_stop(void *data) { + struct mct_clock_event_device *mevt = data; unsigned long tmp; unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START; void __iomem *addr = mevt->base + MCT_L_TCON_OFFSET; @@ -288,8 +289,9 @@ static void exynos4_mct_tick_start(unsigned long cycles, static int exynos4_tick_set_next_event(unsigned long cycles, struct clock_event_device *evt) { - struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()]; + struct mct_clock_event_device *mevt; + mevt = container_of(evt, struct mct_clock_event_device, evt); exynos4_mct_tick_start(cycles, mevt); return 0; @@ -298,8 +300,9 @@ static int exynos4_tick_set_next_event(unsigned long cycles, static inline void exynos4_tick_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { - struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()]; + struct mct_clock_event_device *mevt; + mevt = container_of(evt, struct mct_clock_event_device, evt); exynos4_mct_tick_stop(mevt); switch (mode) { @@ -318,7 +321,7 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode, static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) { struct mct_clock_event_device *mevt = dev_id; - struct clock_event_device *evt = mevt->evt; + struct clock_event_device *evt = &mevt->evt; /* * This is for supporting oneshot mode. @@ -348,17 +351,17 @@ static struct irqaction mct_tick1_event_irq = { .handler = exynos4_mct_tick_isr, }; -static void exynos4_mct_tick_init(struct clock_event_device *evt) +static void __cpuinit exynos4_mct_tick_init(void *data) { + struct mct_clock_event_device *mevt = data; + struct clock_event_device *evt = &mevt->evt; unsigned int cpu = smp_processor_id(); - mct_tick[cpu].evt = evt; - if (cpu == 0) { - mct_tick[cpu].base = EXYNOS4_MCT_L0_BASE; + mct_tick->base = EXYNOS4_MCT_L0_BASE; evt->name = "mct_tick0"; } else { - mct_tick[cpu].base = EXYNOS4_MCT_L1_BASE; + mct_tick->base = EXYNOS4_MCT_L1_BASE; evt->name = "mct_tick1"; } @@ -379,21 +382,56 @@ static void exynos4_mct_tick_init(struct clock_event_device *evt) exynos4_mct_write(0x1, mct_tick[cpu].base + MCT_L_TCNTB_OFFSET); if (cpu == 0) { - mct_tick0_event_irq.dev_id = &mct_tick[cpu]; + mct_tick0_event_irq.dev_id = mevt; setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq); } else { - mct_tick1_event_irq.dev_id = &mct_tick[cpu]; + mct_tick1_event_irq.dev_id = mevt; setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq); irq_set_affinity(IRQ_MCT_L1, cpumask_of(1)); } } -/* Setup the local clock events for a CPU */ -void __cpuinit local_timer_setup(struct clock_event_device *evt) +static int __cpuinit exynos4_mct_cpu_notify(struct notifier_block *self, + unsigned long action, void *data) { - exynos4_mct_tick_init(evt); + int cpu = (int)data; + struct mct_clock_event_device *mevt = per_cpu_ptr(mct_tick, cpu); + + switch (action) { + case CPU_STARTING: + case CPU_STARTING_FROZEN: + smp_call_function_single(cpu, exynos4_mct_tick_init, mevt, 1); + break; + + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + smp_call_function_single(cpu, exynos4_mct_tick_stop, mevt, 1); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata exynos4_mct_cpu_nb = { + .notifier_call = exynos4_mct_cpu_notify, +}; + +static void __init exynos4_mct_init(void) +{ + struct mct_clock_event_device *mevt; + + mct_tick = alloc_percpu(struct mct_clock_event_device); + if (!mct_tick) { + pr_err("exynos4_mct_init: can't allocate memory\n"); + return; + } + + /* Immediately configure the timer on the boot CPU */ + mevt = per_cpu_ptr(mct_tick, smp_processor_id()); + exynos4_mct_tick_init(mevt); + + register_cpu_notifier(&exynos4_mct_cpu_nb); } -#endif /* CONFIG_LOCAL_TIMERS */ static void __init exynos4_timer_resources(void) { @@ -408,6 +446,7 @@ static void __init exynos4_timer_init(void) exynos4_timer_resources(); exynos4_clocksource_init(); exynos4_clockevent_init(); + exynos4_mct_init(); } struct sys_timer exynos4_timer = {