diff options
author | Erik Gilling <konkers@android.com> | 2011-01-04 17:51:22 -0800 |
---|---|---|
committer | Erik Gilling <konkers@android.com> | 2011-01-04 17:51:27 -0800 |
commit | 00756cc5246293c02669eb9c13f2245f3cfa2706 (patch) | |
tree | 7a126bb8e717cad0271d13d29eab067e930d3483 /arch/arm/kernel | |
parent | 4d86495ef99f6a8de7154794eeb765001658e44c (diff) | |
parent | b4c92f36c0368fe9bc7a780bc6ca9fe9ef199c55 (diff) |
Merge branch linux-tegra-2.6.36 into android-tegra-2.6.36
Change-Id: Iec42f1fa234c03c6267f91ba0553d1c651b9ba94
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 110 |
1 files changed, 76 insertions, 34 deletions
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 014862ae170c..3dc62ee45639 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -17,6 +17,7 @@ #include <linux/clockchips.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/cpufreq.h> #include <asm/smp_twd.h> #include <asm/hardware/gic.h> @@ -26,7 +27,7 @@ void __iomem *twd_base; static unsigned long twd_timer_rate; static unsigned long twd_periphclk_prescaler; -static unsigned long twd_target_rate; +static unsigned long twd_cpu_rate; static void twd_set_mode(enum clock_event_mode mode, struct clock_event_device *clk) @@ -82,7 +83,13 @@ int twd_timer_ack(void) return 0; } -void twd_recalc_prescaler(unsigned long new_rate) +/* + * Recalculate the twd prescaler value when the cpu frequency changes. To + * prevent early timer interrupts, must be called before changing the cpu + * frequency if the frequency is increasing, or after if the frequency is + * decreasing. + */ +static void twd_update_prescaler(void *data) { u32 ctrl; int prescaler; @@ -90,7 +97,7 @@ void twd_recalc_prescaler(unsigned long new_rate) BUG_ON(twd_periphclk_prescaler == 0 || twd_timer_rate == 0); - periphclk_rate = new_rate / twd_periphclk_prescaler; + periphclk_rate = twd_cpu_rate / twd_periphclk_prescaler; prescaler = DIV_ROUND_UP(periphclk_rate, twd_timer_rate); prescaler = clamp(prescaler - 1, 0, 0xFF); @@ -101,60 +108,95 @@ void twd_recalc_prescaler(unsigned long new_rate) __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); } +static int twd_cpufreq_transition(struct notifier_block *nb, + unsigned long state, void *data) +{ + struct cpufreq_freqs *freqs = data; + if (((freqs->new > freqs->old) && state == CPUFREQ_PRECHANGE) || + ((freqs->old > freqs->new) && state == CPUFREQ_POSTCHANGE)) { + /* freqs->new is in kHz, twd_cpu_rate is in Hz */ + twd_cpu_rate = freqs->new * 1000; + + smp_call_function_single(freqs->cpu, twd_update_prescaler, + NULL, 1); + } + + return NOTIFY_OK; +} + +static struct notifier_block twd_cpufreq_nb = { + .notifier_call = twd_cpufreq_transition, +}; + +static int twd_cpufreq_init(void) +{ + if (twd_cpu_rate) + return cpufreq_register_notifier(&twd_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + + return 0; +} +core_initcall(twd_cpufreq_init); + static void __cpuinit twd_calibrate_rate(unsigned long target_rate, unsigned int periphclk_prescaler) { unsigned long load, count; u64 waitjiffies; - unsigned long cpu_rate; /* * If this is the first time round, we need to work out how fast * the timer ticks */ - printk(KERN_INFO "Calibrating local timer... "); + if (twd_timer_rate == 0) { + printk(KERN_INFO "Calibrating local timer... "); - /* Wait for a tick to start */ - waitjiffies = get_jiffies_64() + 1; + /* Wait for a tick to start */ + waitjiffies = get_jiffies_64() + 1; - while (get_jiffies_64() < waitjiffies) - udelay(10); + while (get_jiffies_64() < waitjiffies) + udelay(10); - /* OK, now the tick has started, let's get the timer going */ - waitjiffies += 5; + /* OK, now the tick has started, let's get the timer going */ + waitjiffies += 5; - /* enable, no interrupt or reload */ - __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); + /* enable, no interrupt or reload */ + __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); - /* maximum value */ - __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); + /* maximum value */ + __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); - while (get_jiffies_64() < waitjiffies) - udelay(10); + while (get_jiffies_64() < waitjiffies) + udelay(10); - count = __raw_readl(twd_base + TWD_TIMER_COUNTER); + count = __raw_readl(twd_base + TWD_TIMER_COUNTER); - twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); + twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); - /* - * If a target rate has been requested, adjust the TWD prescaler - * to get the closest lower frequency. - */ - if (target_rate) { - twd_periphclk_prescaler = periphclk_prescaler; - twd_target_rate = target_rate; + /* + * If a target rate has been requested, adjust the TWD prescaler + * to get the closest lower frequency. + */ + if (target_rate) { + twd_periphclk_prescaler = periphclk_prescaler; - printk("%lu.%02luMHz, setting to ", - twd_timer_rate / 1000000, + printk("%lu.%02luMHz, setting to ", + twd_timer_rate / 1000000, + (twd_timer_rate / 10000) % 100); + twd_cpu_rate = twd_timer_rate * periphclk_prescaler; + twd_timer_rate = target_rate; + twd_update_prescaler(NULL); + } + + printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, (twd_timer_rate / 10000) % 100); - cpu_rate = twd_timer_rate * periphclk_prescaler; - twd_timer_rate = twd_target_rate; - twd_recalc_prescaler(cpu_rate); + } else { + if (target_rate) { + BUG_ON(target_rate != twd_timer_rate); + twd_update_prescaler(NULL); + } } - printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, - (twd_timer_rate / 10000) % 100); - load = twd_timer_rate / HZ; __raw_writel(load, twd_base + TWD_TIMER_LOAD); |