LKML Archive on
help / color / mirror / Atom feed
From: Stephen Boyd <>
To: Russell King <>
	Saravana Kannan <>,
	Nicolas Pitre <>,
	Andrew Morton <>,
	Mattias Wallin <>,
	Linus Walleij <>
Subject: [PATCHv5 3/3] ARM: Implement a timer based __delay() loop
Date: Tue,  5 Apr 2011 16:56:40 -0700	[thread overview]
Message-ID: <> (raw)
In-Reply-To: <>

udelay() can be incorrect on SMP machines that scale their CPU
frequencies independently of one another (as pointed out here The delay
loop can either be too fast or too slow depending on which CPU the
loops_per_jiffy counter is calibrated on and which CPU the delay
loop is running on. udelay() can also be incorrect if the
CPU frequency switches during the __delay() loop, causing the loop
to either terminate too early, or too late.

Forcing udelay() to run on one CPU is unreasonable and taking the
penalty of a rather large loops_per_jiffy in udelay() when the
CPU is actually running slower is bad for performance. Solve the
problem by adding a timer based__delay() loop unaffected by CPU
frequency scaling. Machines should set this loop as their
__delay() implementation by calling set_timer_fn() during their
timer initialization.

The kernel is already prepared for a timer based approach
(evident by the read_current_timer() function). If an arch
implements read_current_timer(), calibrate_delay() will use
calibrate_delay_direct() to calculate loops_per_jiffy (in which
case loops_per_jiffy should really be renamed to
timer_ticks_per_jiffy). Since the loops_per_jiffy will be based
on timer ticks, __delay() should be implemented as a loop around

Doing this makes the expensive loops_per_jiffy calculation go
away (saving ~150ms on boot time on my machine) and fixes
udelay() by making it safe in the face of independently scaling
CPUs. The only prerequisite is that read_current_timer() is
monotonically increasing across calls (and doesn't overflow
within ~2000us).

There is a downside to this approach though. BogoMIPS is no
longer "accurate" in that it reflects the BogoMIPS of the timer
and not the CPU. On most SoC's the timer isn't running anywhere
near as fast as the CPU so BogoMIPS will be ridiculously low (my
timer runs at 4.8 MHz and thus my BogoMIPS is 9.6 compared to my
CPU's 800). This shouldn't be too much of a concern though since
BogoMIPS are bogus anyway (hence the name).

This loop is pretty much a copy of AVR's version.

Reported-and-reviewed-by: Saravana Kannan <>
Signed-off-by: Stephen Boyd <>
 arch/arm/include/asm/delay.h |    2 ++
 arch/arm/lib/delay.c         |   17 +++++++++++++++++
 2 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h
index 82ef82a..91063a3 100644
--- a/arch/arm/include/asm/delay.h
+++ b/arch/arm/include/asm/delay.h
@@ -47,5 +47,7 @@ static inline void set_delay_fn(void (*fn)(unsigned long))
 	delay_fn = fn;
+extern void read_current_timer_delay_loop(unsigned long loops);
 #endif /* defined(_ARM_DELAY_H) */
diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c
index 42cda15..b8825e9 100644
--- a/arch/arm/lib/delay.c
+++ b/arch/arm/lib/delay.c
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/timex.h>
  * Oh, if only we had a cycle counter...
@@ -23,6 +24,22 @@ static void delay_loop(unsigned long loops)
+ * Assumes read_current_timer() is monotonically increasing
+ * across calls and wraps at most once within MAX_UDELAY_MS.
+ */
+void read_current_timer_delay_loop(unsigned long loops)
+	unsigned long bclock, now;
+	read_current_timer(&bclock);
+	do {
+		read_current_timer(&now);
+	} while ((now - bclock) < loops);
 void (*delay_fn)(unsigned long) = delay_loop;
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

  parent reply	other threads:[~2011-04-05 23:56 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-04-05 23:56 [PATCHv5 0/3] Constant udelay() for SMP and non-SMP systems Stephen Boyd
2011-04-05 23:56 ` [PATCHv5 1/3] ARM: Translate delay.S into (mostly) C Stephen Boyd
2011-04-06  8:49   ` Mattias Wallin
2011-04-06 17:34     ` Stephen Boyd
2011-04-07  1:27     ` Saravana Kannan
2011-04-07  7:27       ` Mattias Wallin
2011-04-07  7:29   ` Mattias Wallin
2011-04-05 23:56 ` [PATCHv5 2/3] ARM: Allow machines to override __delay() Stephen Boyd
2011-04-07  7:30   ` Mattias Wallin
2011-04-05 23:56 ` Stephen Boyd [this message]
2011-04-07  7:30   ` [PATCHv5 3/3] ARM: Implement a timer based __delay() loop Mattias Wallin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \ \ \ \ \ \ \ \ \ \ \ \
    --subject='Re: [PATCHv5 3/3] ARM: Implement a timer based __delay() loop' \

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

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).