From patchwork Fri May 20 00:24:51 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Colin Cross X-Patchwork-Id: 801472 Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p4K0Rbti026419 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 20 May 2011 00:27:58 GMT Received: from canuck.infradead.org ([2001:4978:20e::1]) by bombadil.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QNDXD-0002Kz-E6; Fri, 20 May 2011 00:25:43 +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 1QNDXB-0002Ms-Pb; Fri, 20 May 2011 00:25:41 +0000 Received: from smtp-out.google.com ([216.239.44.51]) by canuck.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QNDX8-0002Ma-2V for linux-arm-kernel@lists.infradead.org; Fri, 20 May 2011 00:25:39 +0000 Received: from hpaq2.eem.corp.google.com (hpaq2.eem.corp.google.com [172.25.149.2]) by smtp-out.google.com with ESMTP id p4K0P7Dl026720; Thu, 19 May 2011 17:25:07 -0700 Received: from walnut.mtv.corp.google.com (walnut.mtv.corp.google.com [172.18.102.62]) by hpaq2.eem.corp.google.com with ESMTP id p4K0P48W032233; Thu, 19 May 2011 17:25:05 -0700 Received: by walnut.mtv.corp.google.com (Postfix, from userid 99897) id BF1CC2578EC; Thu, 19 May 2011 17:25:03 -0700 (PDT) From: Colin Cross To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v2] ARM: smp_twd: Reconfigure clockevents after cpufreq change Date: Thu, 19 May 2011 17:24:51 -0700 Message-Id: <1305851091-10096-1-git-send-email-ccross@android.com> X-Mailer: git-send-email 1.7.4.1 X-System-Of-Record: true X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110519_202538_242501_446D1E4B X-CRM114-Status: GOOD ( 19.88 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [216.239.44.51 listed in list.dnswl.org] -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain Cc: Russell King , linux-kernel@vger.kernel.org, Santosh Shilimkar , Colin Cross , Thomas Gleixner 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]); Fri, 20 May 2011 00:27:58 +0000 (UTC) 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. Change-Id: I413b5cd07fb2b79bc9fad8b4bf257332d1da7bcd Signed-off-by: Colin Cross Cc: Thomas Gleixner Cc: Russell King Cc: Santosh Shilimkar Cc: Rob Herring Tested-by: Linus Walleij --- arch/arm/kernel/smp_twd.c | 86 +++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 80 insertions(+), 6 deletions(-) This patch depends on Thomas Gleixner's patch to add clockevents_update_freq v2: Squash in changes from Linus Wallej, enable smp_twd clock during init diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 60636f4..b5bd303 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,48 @@ int twd_timer_ack(void) return 0; } +/* + * 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); + static void __cpuinit twd_calibrate_rate(void) { unsigned long count; @@ -119,12 +167,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 +207,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); }