From 7baa7bcaf58b7fe89aab6e5e7c0f46cc4adc2b93 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Mon, 22 Jan 2018 13:56:13 -0800 Subject: [PATCH 1/2] Make div_round_up() correct for divisors that are not a power of 2 The current div_round_up() implementation relies on round_up() which only works correctly for boundaries that are a power of 2. It is documented as such, but this still seems dangerously easy to overlook, especially since many other environments (e.g. the Linux kernel) have a similar macro without these limitations. There is a different way to calculate this that can deal with all kinds of divisors without other drawbacks, so let's just use that instead. Change-Id: Id382736683f5d4e880ef00c53cfa23a2f9208440 Signed-off-by: Julius Werner --- include/lib/utils_def.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index 185a1c1297..145e1967e7 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -24,6 +24,11 @@ */ #define DIV_ROUND_UP_2EVAL(n, d) (((n) + (d) - 1) / (d)) +#define div_round_up(val, div) __extension__ ({ \ + __typeof__(div) _div = (div); \ + ((val) + _div - 1) / _div; \ +}) + #define MIN(x, y) __extension__ ({ \ __typeof__(x) _x = (x); \ __typeof__(y) _y = (y); \ @@ -55,11 +60,6 @@ #define round_down(value, boundary) \ ((value) & ~round_boundary(value, boundary)) -#define div_round_up(val, div) __extension__ ({ \ - __typeof__(div) _div = (div); \ - round_up((val), _div)/_div; \ -}) - /* * Evaluates to 1 if (ptr + inc) overflows, 0 otherwise. * Both arguments must be unsigned pointer values (i.e. uintptr_t). From e2aec918d07237e4a6aa185ad2d267750e8f2883 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Mon, 22 Jan 2018 14:02:03 -0800 Subject: [PATCH 2/2] 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: Julius Werner --- drivers/delay_timer/delay_timer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/delay_timer/delay_timer.c b/drivers/delay_timer/delay_timer.c index 43f5af7b39..c9f84d7773 100644 --- a/drivers/delay_timer/delay_timer.c +++ b/drivers/delay_timer/delay_timer.c @@ -7,6 +7,7 @@ #include #include #include +#include /*********************************************************** * The delay timer implementation @@ -30,7 +31,8 @@ void udelay(uint32_t usec) 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 { /*