From patchwork Tue Sep 6 07:06:15 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 3876 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 9D02D23EF8 for ; Tue, 6 Sep 2011 07:07:10 +0000 (UTC) Received: from mail-fx0-f52.google.com (mail-fx0-f52.google.com [209.85.161.52]) by fiordland.canonical.com (Postfix) with ESMTP id 832E8A18608 for ; Tue, 6 Sep 2011 07:07:10 +0000 (UTC) Received: by fxd18 with SMTP id 18so5926065fxd.11 for ; Tue, 06 Sep 2011 00:07:10 -0700 (PDT) Received: by 10.223.24.21 with SMTP id t21mr1256891fab.24.1315292830161; Tue, 06 Sep 2011 00:07:10 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.152.11.8 with SMTP id m8cs86333lab; Tue, 6 Sep 2011 00:07:09 -0700 (PDT) Received: by 10.213.28.17 with SMTP id k17mr1555768ebc.109.1315292829414; Tue, 06 Sep 2011 00:07:09 -0700 (PDT) Received: from eu1sys200aog109.obsmtp.com (eu1sys200aog109.obsmtp.com [207.126.144.127]) by mx.google.com with SMTP id e48si2431998eeb.42.2011.09.06.00.07.04 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 06 Sep 2011 00:07:09 -0700 (PDT) Received-SPF: neutral (google.com: 207.126.144.127 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) client-ip=207.126.144.127; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.127 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) smtp.mail=linus.walleij@stericsson.com Received: from beta.dmz-eu.st.com ([164.129.1.35]) (using TLSv1) by eu1sys200aob109.postini.com ([207.126.147.11]) with SMTP ID DSNKTmXGkmLpbiJ2jHX7HrjXlCQlNWUK802L@postini.com; Tue, 06 Sep 2011 07:07:09 UTC Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 4607B12A; Tue, 6 Sep 2011 07:06:22 +0000 (GMT) Received: from relay1.stm.gmessaging.net (unknown [10.230.100.17]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 8E940154F; Tue, 6 Sep 2011 07:06:21 +0000 (GMT) Received: from exdcvycastm004.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm004", Issuer "exdcvycastm004" (not verified)) by relay1.stm.gmessaging.net (Postfix) with ESMTPS id 0925524C080; Tue, 6 Sep 2011 09:06:17 +0200 (CEST) Received: from localhost.localdomain (10.230.100.153) by smtp.stericsson.com (10.230.100.2) with Microsoft SMTP Server (TLS) id 8.3.83.0; Tue, 6 Sep 2011 09:06:21 +0200 From: Linus Walleij To: Cc: Lee Jones , Colin Cross , Thomas Gleixner , Russell King , Linus Walleij Subject: [PATCH RESEND] ARM: smp_twd: Reconfigure clockevents after cpufreq change Date: Tue, 6 Sep 2011 09:06:15 +0200 Message-ID: <1315292775-4413-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.7.3.2 MIME-Version: 1.0 From: Colin Cross The localtimer's clock changes with the cpu clock. After a cpufreq transition, update the clockevent's frequency and reprogram the next clock event. Adds a clock called "smp_twd" that is used to determine the twd frequency, which can also be used at init time to avoid calibrating the twd frequency. Clock changes are based on Rob Herring's work. The necessary changes in the clockevents framework was done by Thomas Gleixner in kernel v3.0. Signed-off-by: Colin Cross Cc: Thomas Gleixner Cc: Russell King Acked-by: Rob Herring Acked-by: Santosh Shilimkar [ifdef:ed CPUfreq stuff for non-cpufreq configs] Signed-off-by: Linus Walleij --- Any further comments on this? It solves a real issue for ST-Ericsson Ux500, nVidia Tegra and ARM Versatile Express. Infrastructural changes required in clockchips.h were merged by tglx in kernel v3.0. Rob: if you think this is good could you please retire patch 6802/1 to lower the fuzz around this? Thanks, Linus Walleij arch/arm/kernel/smp_twd.c | 90 ++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 84 insertions(+), 6 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 2c277d4..36c12ef 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -10,13 +10,17 @@ */ #include #include +#include +#include #include #include +#include #include #include #include #include #include +#include #include #include @@ -24,7 +28,9 @@ /* set up by the platform code */ void __iomem *twd_base; +static struct clk *twd_clk; static unsigned long twd_timer_rate; +static DEFINE_PER_CPU(struct clock_event_device *, twd_ce); static void twd_set_mode(enum clock_event_mode mode, struct clock_event_device *clk) @@ -80,6 +86,52 @@ int twd_timer_ack(void) return 0; } +#ifdef CONFIG_CPU_FREQ + +/* + * Updates clockevent frequency when the cpu frequency changes. + * Called on the cpu that is changing frequency with interrupts disabled. + */ +static void twd_update_frequency(void *data) +{ + twd_timer_rate = clk_get_rate(twd_clk); + + clockevents_update_freq(__get_cpu_var(twd_ce), twd_timer_rate); +} + +static int twd_cpufreq_transition(struct notifier_block *nb, + unsigned long state, void *data) +{ + struct cpufreq_freqs *freqs = data; + + /* + * The twd clock events must be reprogrammed to account for the new + * frequency. The timer is local to a cpu, so cross-call to the + * changing cpu. + */ + if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) + smp_call_function_single(freqs->cpu, twd_update_frequency, + NULL, 1); + + return NOTIFY_OK; +} + +static struct notifier_block twd_cpufreq_nb = { + .notifier_call = twd_cpufreq_transition, +}; + +static int twd_cpufreq_init(void) +{ + if (!IS_ERR_OR_NULL(twd_clk)) + return cpufreq_register_notifier(&twd_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + + return 0; +} +core_initcall(twd_cpufreq_init); + +#endif + static void __cpuinit twd_calibrate_rate(void) { unsigned long count; @@ -119,12 +171,39 @@ static void __cpuinit twd_calibrate_rate(void) } } +static struct clk *twd_get_clock(void) +{ + struct clk *clk; + int err; + + clk = clk_get_sys("smp_twd", NULL); + if (IS_ERR(clk)) { + pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); + return clk; + } + + err = clk_enable(clk); + if (err) { + pr_err("smp_twd: clock failed to enable: %d\n", err); + clk_put(clk); + return ERR_PTR(err); + } + + return clk; +} + /* * Setup the local clock events for a CPU. */ void __cpuinit twd_timer_setup(struct clock_event_device *clk) { - twd_calibrate_rate(); + if (!twd_clk) + twd_clk = twd_get_clock(); + + if (!IS_ERR_OR_NULL(twd_clk)) + twd_timer_rate = clk_get_rate(twd_clk); + else + twd_calibrate_rate(); clk->name = "local_timer"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | @@ -132,13 +211,12 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) clk->rating = 350; clk->set_mode = twd_set_mode; clk->set_next_event = twd_set_next_event; - clk->shift = 20; - clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift); - 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); - clockevents_register_device(clk); + __get_cpu_var(twd_ce) = clk; + + clockevents_config_and_register(clk, twd_timer_rate, + 0xf, 0xffffffff); }