summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Aaberg <jonas.aberg@stericsson.com>2011-09-22 15:20:44 +0200
committerRobert Marklund <robert.marklund@stericsson.com>2011-10-05 13:01:12 +0200
commit8364f08ab5f8dd3217ee2f2d4b8393c186e4d507 (patch)
tree1fa8a23aa32f890bf776b1e087ead7d408cb99b7
parent6bbed941d76cd37f9319c8b8c08d8ea642cb9370 (diff)
Reset to linux-next patch:
commit 774959368b9b567685179682cebfcf63e540b17b Author: Colin Cross <ccross@android.com> Date: Thu May 19 17:24:51 2011 -0700 ARM: smp_twd: Reconfigure clockevents after cpufreq change Change-Id: If31593fe23c00df528af597e6fd2baaf0c102753 Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32132
-rw-r--r--arch/arm/include/asm/smp_twd.h12
-rw-r--r--arch/arm/kernel/smp_twd.c184
-rw-r--r--arch/arm/mach-ux500/localtimer.c11
3 files changed, 73 insertions, 134 deletions
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
index 51197634059..fed9981fba0 100644
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -17,24 +17,12 @@
#define TWD_TIMER_CONTROL_ONESHOT (0 << 1)
#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
-#define TWD_TIMER_CONTROL_PRESCALE_MASK (0xFF << 8)
struct clock_event_device;
extern void __iomem *twd_base;
-void twd_timer_stop(void);
int twd_timer_ack(void);
void twd_timer_setup(struct clock_event_device *);
-/*
- * Use this setup function on systems that support cpufreq.
- * periphclk_prescaler is the fixed divider value between the cpu
- * clock and the PERIPHCLK clock that feeds the TWD. target_rate should be
- * low enough that the prescaler can accurately reach the target rate from the
- * lowest cpu frequency, but high enough to give a reasonable timer accuracy.
- */
-void twd_timer_setup_scalable(struct clock_event_device *,
- unsigned long target_rate, unsigned int periphclk_prescaler);
-
#endif
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 70fa99d740e..36c12ef9499 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -10,14 +10,17 @@
*/
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/smp.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/io.h>
-#include <linux/cpufreq.h>
+#include <linux/percpu.h>
#include <asm/smp_twd.h>
#include <asm/hardware/gic.h>
@@ -25,27 +28,21 @@
/* set up by the platform code */
void __iomem *twd_base;
+static struct clk *twd_clk;
static unsigned long twd_timer_rate;
-static unsigned long twd_periphclk_prescaler;
-static unsigned long twd_target_rate;
-
-static DEFINE_PER_CPU(unsigned long, twd_ctrl);
-static DEFINE_PER_CPU(unsigned long, twd_load);
+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)
{
unsigned long ctrl;
- int this_cpu = smp_processor_id();
-
- __raw_writel(per_cpu(twd_load, this_cpu),
- twd_base + TWD_TIMER_LOAD);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
/* timer load already set up */
ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
| TWD_TIMER_CONTROL_PERIODIC;
+ __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD);
break;
case CLOCK_EVT_MODE_ONESHOT:
/* period set, and timer enabled in 'next_event' hook */
@@ -57,22 +54,18 @@ static void twd_set_mode(enum clock_event_mode mode,
ctrl = 0;
}
- ctrl |= per_cpu(twd_ctrl, this_cpu) & TWD_TIMER_CONTROL_PRESCALE_MASK;
-
__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
- per_cpu(twd_ctrl, this_cpu) = ctrl;
}
static int twd_set_next_event(unsigned long evt,
struct clock_event_device *unused)
{
- int this_cpu = smp_processor_id();
+ unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
- per_cpu(twd_ctrl, this_cpu) |= TWD_TIMER_CONTROL_ENABLE;
+ ctrl |= TWD_TIMER_CONTROL_ENABLE;
__raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
- __raw_writel(per_cpu(twd_ctrl, this_cpu),
- twd_base + TWD_TIMER_CONTROL);
+ __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
return 0;
}
@@ -93,50 +86,51 @@ int twd_timer_ack(void)
return 0;
}
+#ifdef CONFIG_CPU_FREQ
+
/*
- * must be called with interrupts disabled and on the cpu that is being changed
+ * Updates clockevent frequency when the cpu frequency changes.
+ * Called on the cpu that is changing frequency with interrupts disabled.
*/
-static void twd_update_cpu_frequency(unsigned long new_rate)
+static void twd_update_frequency(void *data)
{
- int prescaler;
- int this_cpu = smp_processor_id();
+ twd_timer_rate = clk_get_rate(twd_clk);
- BUG_ON(twd_periphclk_prescaler == 0 || twd_target_rate == 0);
-
- twd_timer_rate = new_rate / twd_periphclk_prescaler;
-
- prescaler = DIV_ROUND_UP(twd_timer_rate, twd_target_rate);
- prescaler = clamp(prescaler - 1, 0, 0xFF);
-
- per_cpu(twd_ctrl, this_cpu) &= ~TWD_TIMER_CONTROL_PRESCALE_MASK;
- per_cpu(twd_ctrl, this_cpu) |= prescaler << 8;
- __raw_writel(per_cpu(twd_ctrl, this_cpu),
- twd_base + TWD_TIMER_CONTROL);
+ clockevents_update_freq(__get_cpu_var(twd_ce), twd_timer_rate);
}
-static void twd_update_cpu_frequency_on_cpu(void *data)
+static int twd_cpufreq_transition(struct notifier_block *nb,
+ unsigned long state, void *data)
{
- struct cpufreq_freqs *freq = 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);
- twd_update_cpu_frequency(freq->new * 1000);
+ return NOTIFY_OK;
}
-static int twd_cpufreq_notifier(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct cpufreq_freqs *freq = data;
+static struct notifier_block twd_cpufreq_nb = {
+ .notifier_call = twd_cpufreq_transition,
+};
- if (event == CPUFREQ_RESUMECHANGE ||
- (event == CPUFREQ_PRECHANGE && freq->new > freq->old) ||
- (event == CPUFREQ_POSTCHANGE && freq->new < freq->old))
- on_each_cpu(twd_update_cpu_frequency_on_cpu, freq, 1);
+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 struct notifier_block twd_cpufreq_notifier_block = {
- .notifier_call = twd_cpufreq_notifier,
-};
+#endif
static void __cpuinit twd_calibrate_rate(void)
{
@@ -160,11 +154,7 @@ static void __cpuinit twd_calibrate_rate(void)
waitjiffies += 5;
/* enable, no interrupt or reload */
- __raw_writel(TWD_TIMER_CONTROL_ENABLE,
- twd_base + TWD_TIMER_CONTROL);
-
- per_cpu(twd_ctrl, smp_processor_id()) =
- TWD_TIMER_CONTROL_ENABLE;
+ __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
/* maximum value */
__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
@@ -181,82 +171,52 @@ 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.
*/
-static void __cpuinit __twd_timer_setup(struct clock_event_device *clk,
- unsigned long target_rate, unsigned int periphclk_prescaler)
+void __cpuinit twd_timer_setup(struct clock_event_device *clk)
{
- unsigned long cpu_rate;
- unsigned long twd_tick_rate;
-
- twd_calibrate_rate();
-
- if (target_rate && periphclk_prescaler) {
- cpu_rate = twd_timer_rate * periphclk_prescaler;
- twd_target_rate = target_rate;
- twd_periphclk_prescaler = periphclk_prescaler;
- twd_update_cpu_frequency(cpu_rate);
- twd_tick_rate = twd_target_rate;
- } else {
- twd_tick_rate = twd_timer_rate;
- }
+ if (!twd_clk)
+ twd_clk = twd_get_clock();
- per_cpu(twd_load, smp_processor_id()) = twd_tick_rate / HZ;
+ if (!IS_ERR_OR_NULL(twd_clk))
+ twd_timer_rate = clk_get_rate(twd_clk);
+ else
+ twd_calibrate_rate();
clk->name = "local_timer";
-#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && \
- defined(CONFIG_LOCAL_TIMERS)
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_C3STOP;
-#else
- clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
-#endif
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_tick_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);
-}
-
-void __cpuinit twd_timer_setup_scalable(struct clock_event_device *clk,
- unsigned long target_rate, unsigned int periphclk_prescaler)
-{
- __twd_timer_setup(clk, target_rate, periphclk_prescaler);
-}
-
-void __cpuinit twd_timer_setup(struct clock_event_device *clk)
-{
- __twd_timer_setup(clk, 0, 0);
-}
-
-static int twd_timer_setup_cpufreq(void)
-{
- if (twd_periphclk_prescaler)
- cpufreq_register_notifier(&twd_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
-
- return 0;
-}
-arch_initcall(twd_timer_setup_cpufreq);
+ __get_cpu_var(twd_ce) = clk;
-#ifdef CONFIG_HOTPLUG_CPU
-/*
- * take a local timer down
- */
-void twd_timer_stop(void)
-{
- int this_cpu = smp_processor_id();
- per_cpu(twd_ctrl, this_cpu) &= ~(TWD_TIMER_CONTROL_ENABLE |
- TWD_TIMER_CONTROL_IT_ENABLE);
- __raw_writel(per_cpu(twd_ctrl, this_cpu),
- twd_base + TWD_TIMER_CONTROL);
+ clockevents_config_and_register(clk, twd_timer_rate,
+ 0xf, 0xffffffff);
}
-#endif
diff --git a/arch/arm/mach-ux500/localtimer.c b/arch/arm/mach-ux500/localtimer.c
index 30d4f3e1557..5ba113309a0 100644
--- a/arch/arm/mach-ux500/localtimer.c
+++ b/arch/arm/mach-ux500/localtimer.c
@@ -18,21 +18,12 @@
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
-#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
-void smp_timer_broadcast(const struct cpumask *mask);
-#endif
-
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
evt->irq = IRQ_LOCALTIMER;
-
-#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
- evt->broadcast = smp_timer_broadcast;
-#endif
-
- twd_timer_setup_scalable(evt, 2500 * 1000, 2);
+ twd_timer_setup(evt);
return 0;
}