[lkml]   [2010]   [Oct]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
Messages in this thread
    Patch in this message
    Subject[PATCHv2 3/3] ARM: Implement a timer based __delay() loop
    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-by: Saravana Kannan <>
    Signed-off-by: Stephen Boyd <>
    Reviewed-by: Saravana Kannan <>
    arch/arm/include/asm/delay.h | 1 +
    arch/arm/lib/delay.c | 18 ++++++++++++++++++
    2 files changed, 19 insertions(+), 0 deletions(-)

    diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h
    index 7c732b5..5c6b9a3 100644
    --- a/arch/arm/include/asm/delay.h
    +++ b/arch/arm/include/asm/delay.h
    @@ -41,6 +41,7 @@ extern void __const_udelay(unsigned long);

    extern void set_delay_fn(void (*fn)(unsigned long));
    +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 116a853..c92ae49 100644
    --- a/arch/arm/lib/delay.c
    +++ b/arch/arm/lib/delay.c
    @@ -5,6 +5,7 @@
    * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
    * Copyright (C) 1993 Linus Torvalds
    * Copyright (C) 1997 Martin Mares <>
    + * Copyright (C) 2005-2006 Atmel Corporation
    * This program is free software; you can redistribute it and/or modify
    * it under the terms of the GNU General Public License version 2 as
    @@ -12,6 +13,7 @@
    #include <linux/module.h>
    #include <linux/delay.h>
    +#include <linux/timex.h>

    * Oh, if only we had a cycle counter...
    @@ -26,6 +28,22 @@ 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);
    static void (*delay_fn)(unsigned long) = delay_loop;

    void set_delay_fn(void (*fn)(unsigned long))
    Sent by an employee of the Qualcomm Innovation Center, Inc.
    The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

     \ /
      Last update: 2010-10-28 23:23    [W:0.024 / U:12.700 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site