Commit e2aec918 authored by Julius Werner's avatar Julius Werner
Browse files

delay_timer: Guarantee that delay time can never be undershot



Delay functions like udelay() are often used to ensure that the
necessary time passed to allow some asynchronous event to finish, such
as the stabilization delay for a power rail. For these use cases it is
not very problematic if the delay is slightly longer than requested,
but it is critical that the delay must never be shorter.

The current udelay() implementation contains two hazards that may cause
the delay to be slightly shorter than intended: Firstly, the amount of
ticks to wait is calculated with an integer division, which may cut off
the last fraction of ticks needed. Secondly, the delay may be short by a
fraction of a tick because we do not know whether the initial ("start")
sample of the timer was near the start or near the end of the current
tick. Thus, if the code intends to wait for one tick, it might read the
timer value close to the end of the current tick and then read it again
right after the start of the next tick, concluding that the duration of
a full tick has passed when it in fact was just a fraction of it.

This patch rounds up the division and always adds one extra tick to
counteract both problems and ensure that delays will always be larger
but never smaller than requested.

Change-Id: Ic5fe5f858b5cdf3c0dbf3e488d4d5702d9569433
Signed-off-by: default avatarJulius Werner <jwerner@chromium.org>
parent 7baa7bca
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <assert.h> #include <assert.h>
#include <delay_timer.h> #include <delay_timer.h>
#include <platform_def.h> #include <platform_def.h>
#include <utils_def.h>
/*********************************************************** /***********************************************************
* The delay timer implementation * The delay timer implementation
...@@ -30,7 +31,8 @@ void udelay(uint32_t usec) ...@@ -30,7 +31,8 @@ void udelay(uint32_t usec)
start = ops->get_timer_value(); start = ops->get_timer_value();
total_delta = (usec * ops->clk_div) / ops->clk_mult; /* Add an extra tick to avoid delaying less than requested. */
total_delta = div_round_up(usec * ops->clk_div, ops->clk_mult) + 1;
do { do {
/* /*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment