From patchwork Wed Jun 1 09:15:11 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 1704 Return-Path: Delivered-To: unknown Received: from imap.gmail.com (74.125.159.109) by localhost6.localdomain6 with IMAP4-SSL; 08 Jun 2011 14:54:31 -0000 Delivered-To: patches@linaro.org Received: by 10.52.181.10 with SMTP id ds10cs296202vdc; Wed, 1 Jun 2011 02:15:57 -0700 (PDT) Received: by 10.14.16.100 with SMTP id g76mr2731924eeg.168.1306919756692; Wed, 01 Jun 2011 02:15:56 -0700 (PDT) Received: from eu1sys200aog101.obsmtp.com (eu1sys200aog101.obsmtp.com [207.126.144.111]) by mx.google.com with SMTP id y3si2398281eeh.59.2011.06.01.02.15.50 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 01 Jun 2011 02:15:56 -0700 (PDT) Received-SPF: neutral (google.com: 207.126.144.111 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) client-ip=207.126.144.111; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.111 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 eu1sys200aob101.postini.com ([207.126.147.11]) with SMTP ID DSNKTeYDQGl6UIpvYP4+cDBLP8pG+5D1Mb9a@postini.com; Wed, 01 Jun 2011 09:15:56 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 EC16951; Wed, 1 Jun 2011 09:15:17 +0000 (GMT) Received: from relay2.stm.gmessaging.net (unknown [10.230.100.18]) by zeta.dmz-us.st.com (STMicroelectronics) with ESMTP id 346C64D; Wed, 1 Jun 2011 09:15:17 +0000 (GMT) Received: from exdcvycastm003.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm003", Issuer "exdcvycastm003" (not verified)) by relay2.stm.gmessaging.net (Postfix) with ESMTPS id D4417A8093; Wed, 1 Jun 2011 11:15:12 +0200 (CEST) Received: from localhost.localdomain (10.230.100.153) by smtp.stericsson.com (10.230.100.1) with Microsoft SMTP Server (TLS) id 8.3.83.0; Wed, 1 Jun 2011 11:15:16 +0200 From: Linus Walleij To: Cc: Lee Jones , Colin Cross , Thomas Gleixner , Russell King , Linus Walleij Subject: [PATCH] ARM: smp_twd: Reconfigure clockevents after cpufreq change Date: Wed, 1 Jun 2011 11:15:11 +0200 Message-ID: <1306919711-30965-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. 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 --- I'm sending this as a pull request to Russells in this form, this nasty cpufreq bug which is making the system timeline jump back and forth on ux500 is *certainly* -rc material to me, but I'm leaving it for Russell to decide. Tested with and without CPUfreq support. --- 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 60636f4..5c8775d 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); }