* [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
@ 2010-09-02 3:04 Colin Cross
2010-11-12 12:39 ` Linus Walleij
0 siblings, 1 reply; 6+ messages in thread
From: Colin Cross @ 2010-09-02 3:04 UTC (permalink / raw)
To: linux-arm-kernel
The clock to the ARM TWD local timer scales with the cpu
frequency. To allow the cpu frequency to change while
maintaining a constant TWD frequency, pick a lower target
frequency for the TWD and use the prescaler to divide down
to the closest lower frequency.
This patch provides a new initialization function that takes
a target TWD frequency and the relation between the cpu
clock and the TWD clock, required to be an integer divider
>= 2 by the ARM spec. It also provides a function to be
called from cpufreq drivers to set the prescaler whenever
the cpu frequency changes.
Also fixes a typo in the printk of the calibrated frequency.
Change-Id: I45c76ae1ed501c40b94709c15834c8a0b6116c84
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/include/asm/smp_twd.h | 19 +++++++++++++
arch/arm/kernel/smp_twd.c | 57 +++++++++++++++++++++++++++++++++++++---
2 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
index 634f357..79aebe2 100644
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -17,6 +17,7 @@
#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;
@@ -26,4 +27,22 @@ void twd_timer_stop(void);
int twd_timer_ack(void);
void twd_timer_setup(struct clock_event_device *);
+/*
+ * Use this setup function on systems where the cpu clock frequency may
+ * change. 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.
+ */
+void twd_timer_setup_scalable(struct clock_event_device *,
+ unsigned long target_rate, unsigned int periphclk_prescaler);
+
+/*
+ * 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.
+ */
+void twd_recalc_prescaler(unsigned long new_rate);
+
#endif
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 35882fb..2b4f927 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -25,6 +25,8 @@
void __iomem *twd_base;
static unsigned long twd_timer_rate;
+static unsigned long twd_periphclk_prescaler;
+static unsigned long twd_target_rate;
static void twd_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
@@ -79,10 +81,31 @@ int twd_timer_ack(void)
return 0;
}
-static void __cpuinit twd_calibrate_rate(void)
+void twd_recalc_prescaler(unsigned long new_rate)
+{
+ u32 ctrl;
+ int prescaler;
+ unsigned long periphclk_rate;
+
+ BUG_ON(twd_periphclk_prescaler == 0 || twd_timer_rate == 0);
+
+ periphclk_rate = new_rate / twd_periphclk_prescaler;
+
+ prescaler = DIV_ROUND_UP(periphclk_rate, twd_timer_rate);
+ prescaler = clamp(prescaler - 1, 0, 0xFF);
+
+ ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
+ ctrl &= ~TWD_TIMER_CONTROL_PRESCALE_MASK;
+ ctrl |= prescaler << 8;
+ __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
+}
+
+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
@@ -113,8 +136,22 @@ static void __cpuinit twd_calibrate_rate(void)
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;
+
+ cpu_rate = twd_timer_rate * periphclk_prescaler;
+ twd_recalc_prescaler(cpu_rate);
+
+ twd_timer_rate = twd_target_rate;
+ }
+
printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
- (twd_timer_rate / 100000) % 100);
+ (twd_timer_rate / 10000) % 100);
}
load = twd_timer_rate / HZ;
@@ -125,11 +162,12 @@ static void __cpuinit twd_calibrate_rate(void)
/*
* Setup the local clock events for a CPU.
*/
-void __cpuinit twd_timer_setup(struct clock_event_device *clk)
+static void __cpuinit __twd_timer_setup(struct clock_event_device *clk,
+ unsigned long target_rate, unsigned int periphclk_prescaler)
{
unsigned long flags;
- twd_calibrate_rate();
+ twd_calibrate_rate(target_rate, periphclk_prescaler);
clk->name = "local_timer";
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
@@ -151,6 +189,17 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
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);
+}
+
#ifdef CONFIG_HOTPLUG_CPU
/*
* take a local timer down
--
1.7.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
2010-09-02 3:04 [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency Colin Cross
@ 2010-11-12 12:39 ` Linus Walleij
2010-11-12 12:49 ` Harald Gustafsson
0 siblings, 1 reply; 6+ messages in thread
From: Linus Walleij @ 2010-11-12 12:39 UTC (permalink / raw)
To: linux-arm-kernel
Hi Colin,
2010/9/2 Colin Cross <ccross@android.com>:
> The clock to the ARM TWD local timer scales with the cpu
> frequency. ?To allow the cpu frequency to change while
> maintaining a constant TWD frequency, pick a lower target
> frequency for the TWD and use the prescaler to divide down
> to the closest lower frequency.
>
> This patch provides a new initialization function that takes
> a target TWD frequency and the relation between the cpu
> clock and the TWD clock, required to be an integer divider
>>= 2 by the ARM spec. ?It also provides a function to be
> called from cpufreq drivers to set the prescaler whenever
> the cpu frequency changes.
What's the status of this patch?
We have basically the same problem so we'd really like
a generic solution to this problem.
Our only comment is that we cannot see the reason for
not using the CPUfreq notifiers as most scaling code
is, but I assume there is some specific reason for this?
Unless something holds this back, could you post it to
Russells patch tracker, we might have an add-on patch
for the U8500 later.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
2010-11-12 12:39 ` Linus Walleij
@ 2010-11-12 12:49 ` Harald Gustafsson
2010-11-12 12:59 ` Russell King - ARM Linux
0 siblings, 1 reply; 6+ messages in thread
From: Harald Gustafsson @ 2010-11-12 12:49 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
Don't forget the other patches to make this work:
[ARM] twd: Always calibrate local timer
[ARM] twd: Fix updating control register in set_mode
[ARM] twd: Fix prescaler getting cleared by set_mode
/Harald Gustafsson
________________________________________
From: Linus Walleij [linus.ml.walleij at gmail.com]
Sent: Friday, November 12, 2010 1:39 PM
To: Colin Cross
Cc: linux-kernel at vger.kernel.org; linux-arm-kernel at lists.infradead.org; Russell King; srinidhi kasagar; Varun Swara; Catalin Marinas; Harald Gustafsson; Rickard ANDERSSON
Subject: Re: [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
Hi Colin,
2010/9/2 Colin Cross <ccross@android.com>:
> The clock to the ARM TWD local timer scales with the cpu
> frequency. To allow the cpu frequency to change while
> maintaining a constant TWD frequency, pick a lower target
> frequency for the TWD and use the prescaler to divide down
> to the closest lower frequency.
>
> This patch provides a new initialization function that takes
> a target TWD frequency and the relation between the cpu
> clock and the TWD clock, required to be an integer divider
>>= 2 by the ARM spec. It also provides a function to be
> called from cpufreq drivers to set the prescaler whenever
> the cpu frequency changes.
What's the status of this patch?
We have basically the same problem so we'd really like
a generic solution to this problem.
Our only comment is that we cannot see the reason for
not using the CPUfreq notifiers as most scaling code
is, but I assume there is some specific reason for this?
Unless something holds this back, could you post it to
Russells patch tracker, we might have an add-on patch
for the U8500 later.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
2010-11-12 12:49 ` Harald Gustafsson
@ 2010-11-12 12:59 ` Russell King - ARM Linux
2010-11-12 13:05 ` Harald Gustafsson
0 siblings, 1 reply; 6+ messages in thread
From: Russell King - ARM Linux @ 2010-11-12 12:59 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Nov 12, 2010 at 01:49:50PM +0100, Harald Gustafsson wrote:
> Hi,
>
> Don't forget the other patches to make this work:
>
> [ARM] twd: Always calibrate local timer
> [ARM] twd: Fix updating control register in set_mode
> [ARM] twd: Fix prescaler getting cleared by set_mode
... which have never been posted to this mailing list, so no one here
knows of them.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
2010-11-12 12:59 ` Russell King - ARM Linux
@ 2010-11-12 13:05 ` Harald Gustafsson
2010-11-17 3:55 ` Colin Cross
0 siblings, 1 reply; 6+ messages in thread
From: Harald Gustafsson @ 2010-11-12 13:05 UTC (permalink / raw)
To: linux-arm-kernel
> > Don't forget the other patches to make this work:
> >
> > [ARM] twd: Always calibrate local timer [ARM] twd: Fix updating
> > control register in set_mode [ARM] twd: Fix prescaler
> getting cleared
> > by set_mode
>
> ... which have never been posted to this mailing list, so no
> one here knows of them.
They are on the tegra branch git also authored by Colin. So I would suggest that Colin make all the changes into a new patch.
http://android.git.kernel.org/?p=kernel%2Ftegra.git&a=search&h=refs%2Fheads%2Flinux-tegra-2.6.35&st=commit&s=twd
/Harald
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency
2010-11-12 13:05 ` Harald Gustafsson
@ 2010-11-17 3:55 ` Colin Cross
0 siblings, 0 replies; 6+ messages in thread
From: Colin Cross @ 2010-11-17 3:55 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Nov 12, 2010 at 5:05 AM, Harald Gustafsson
<harald.gustafsson@ericsson.com> wrote:
>> > Don't forget the other patches to make this work:
>> >
>> > [ARM] twd: Always calibrate local timer [ARM] twd: Fix updating
>> > control register in set_mode [ARM] twd: Fix prescaler
>> getting cleared
>> > by set_mode
>>
>> ... which have never been posted to this mailing list, so no
>> one here knows of them.
>
> They are on the tegra branch git also authored by Colin. So I would suggest that Colin make all the changes into a new patch.
>
> http://android.git.kernel.org/?p=kernel%2Ftegra.git&a=search&h=refs%2Fheads%2Flinux-tegra-2.6.35&st=commit&s=twd
>
> /Harald
I'm not happy with the current implementation, which is why it hasn't
been posted. I'm planning to rewrite it with cpufreq notifiers, but
that brings us back to the periphclk divider problem discussed in
http://comments.gmane.org/gmane.linux.ports.arm.kernel/91827
If the frequency is going to change, using clk_get_rate on periphclk
is not sufficient, because the clock will not have been updated when
the prechange cpufreq notifier is called. If the cpu frequency is
increasing, the twd divider must be recalculated before the frequency
changes to avoid timers expiring early. Either the clock used by the
twd driver must be updated before prechange notifiers and after
postchange notifiers, which is difficult to fit in to all the
different implementations of the clock api, or the twd driver needs to
know the ratio between the cpu frequency and the periphclk frequency.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-11-17 3:55 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-02 3:04 [PATCH] [ARM] twd: Allow twd rescaling to match cpu frequency Colin Cross
2010-11-12 12:39 ` Linus Walleij
2010-11-12 12:49 ` Harald Gustafsson
2010-11-12 12:59 ` Russell King - ARM Linux
2010-11-12 13:05 ` Harald Gustafsson
2010-11-17 3:55 ` Colin Cross
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).