From patchwork Mon Jun 20 08:48:07 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 2069 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 A94E123F18 for ; Mon, 20 Jun 2011 08:49:07 +0000 (UTC) Received: from mail-vw0-f52.google.com (mail-vw0-f52.google.com [209.85.212.52]) by fiordland.canonical.com (Postfix) with ESMTP id 6FE62A18707 for ; Mon, 20 Jun 2011 08:49:07 +0000 (UTC) Received: by mail-vw0-f52.google.com with SMTP id 16so3286813vws.11 for ; Mon, 20 Jun 2011 01:49:07 -0700 (PDT) Received: by 10.52.175.197 with SMTP id cc5mr3104273vdc.287.1308559747054; Mon, 20 Jun 2011 01:49:07 -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.52.183.130 with SMTP id em2cs19393vdc; Mon, 20 Jun 2011 01:49:06 -0700 (PDT) Received: by 10.213.19.203 with SMTP id c11mr225590ebb.62.1308559746216; Mon, 20 Jun 2011 01:49:06 -0700 (PDT) Received: from eu1sys200aog120.obsmtp.com (eu1sys200aog120.obsmtp.com [207.126.144.149]) by mx.google.com with SMTP id y12si12275501eey.94.2011.06.20.01.48.57 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 20 Jun 2011 01:49:06 -0700 (PDT) Received-SPF: neutral (google.com: 207.126.144.149 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) client-ip=207.126.144.149; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.149 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-us.st.com ([167.4.1.35]) (using TLSv1) by eu1sys200aob120.postini.com ([207.126.147.11]) with SMTP ID DSNKTf8JblsrJzOXWFuTE6ttmIDd+2fr3s/a@postini.com; Mon, 20 Jun 2011 08:49:06 UTC Received: from zeta.dmz-us.st.com (ns4.st.com [167.4.16.71]) by beta.dmz-us.st.com (STMicroelectronics) with ESMTP id 19C8862; Mon, 20 Jun 2011 08:48:17 +0000 (GMT) Received: from relay2.stm.gmessaging.net (unknown [10.230.100.18]) by zeta.dmz-us.st.com (STMicroelectronics) with ESMTP id 44E296D; Mon, 20 Jun 2011 08:48:17 +0000 (GMT) Received: from exdcvycastm022.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm022", Issuer "exdcvycastm022" (not verified)) by relay2.stm.gmessaging.net (Postfix) with ESMTPS id EC355A807D; Mon, 20 Jun 2011 10:48:12 +0200 (CEST) Received: from localhost.localdomain (10.230.100.153) by smtp.stericsson.com (10.230.100.30) with Microsoft SMTP Server (TLS) id 8.3.83.0; Mon, 20 Jun 2011 10:48:16 +0200 From: Linus Walleij To: Cc: Lee Jones , Colin Cross , Thomas Gleixner , Russell King , Marc Zyngier , Arnd Bergmann , Linus Walleij Subject: [PATCH] clocksource/arm_smp_twd: handle frequency changes Date: Mon, 20 Jun 2011 10:48:07 +0200 Message-ID: <1308559687-30768-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.7.3.2 MIME-Version: 1.0 From: Colin Cross First add 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 since we already know it. Then the localtimer's clock changes with the cpu clock, since the block has only one clock input. After a cpufreq transition, update the clockevent's frequency and reprogram the next clock event so we stay tight on the scheduled timeline. Clock changes are based on Rob Herring's work. This patch depends on the whole localtimer rewrite and move shebang from Marc Zyngier, and that in turn depends on other stuff. Signed-off-by: Colin Cross Cc: Thomas Gleixner Cc: Russell King Cc: Marc Zyngier Cc: Arnd Bergmann Acked-by: Rob Herring Acked-by: Santosh Shilimkar [ifdef:ed CPUfreq stuff - rebased to Marc Z patches] Signed-off-by: Linus Walleij --- Marc, if you're pursuing this series, consider merging this on top, it is needed for the new ARM A9 small-form factor reference platform whatever it is called. --- drivers/clocksource/arm_smp_twd.c | 89 ++++++++++++++++++++++++++++++++++--- 1 files changed, 83 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/arm_smp_twd.c b/drivers/clocksource/arm_smp_twd.c index 5e2e8cc..a18ac0d 100644 --- a/drivers/clocksource/arm_smp_twd.c +++ b/drivers/clocksource/arm_smp_twd.c @@ -19,6 +19,10 @@ #include #include #include +#include +#include +#include #include @@ -35,6 +39,8 @@ static void __iomem *twd_base; static int twd_ppi; +static struct clk *twd_clk; +static DEFINE_PER_CPU(struct clock_event_device *, twd_ce); static unsigned long twd_timer_rate; static DEFINE_PER_CPU(bool, irq_reqd); static struct clock_event_device __percpu *twd_evt; @@ -90,6 +96,52 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } +#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; @@ -129,6 +181,27 @@ 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. */ @@ -137,7 +210,13 @@ static void __cpuinit twd_setup(void *data) struct clock_event_device *clk = data; int err; - 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 = "arm_smp_twd"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | @@ -145,10 +224,6 @@ static void __cpuinit twd_setup(void *data) clk->rating = 450; 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); clk->irq = gic_ppi_to_vppi(twd_ppi); clk->cpumask = cpumask_of(smp_processor_id()); @@ -163,7 +238,9 @@ static void __cpuinit twd_setup(void *data) return; } - clockevents_register_device(clk); + __get_cpu_var(twd_ce) = clk; + clockevents_config_and_register(clk, twd_timer_rate, + 0xf, 0xffffffff); } static void __cpuinit twd_teardown(void *data)