From efbd5518f6677feae5b454f1a6f4e93f0dbec5f2 Mon Sep 17 00:00:00 2001
From: Kaspar Schleiser <kaspar@schleiser.de>
Date: Sat, 5 Sep 2015 01:18:36 +0200
Subject: [PATCH] cpu: stm32f1: timer: fix race when combining two 16bit timer
 values

---
 cpu/stm32f1/periph/timer.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/cpu/stm32f1/periph/timer.c b/cpu/stm32f1/periph/timer.c
index d2ce653bd2..0513d0350c 100644
--- a/cpu/stm32f1/periph/timer.c
+++ b/cpu/stm32f1/periph/timer.c
@@ -220,16 +220,33 @@ int timer_clear(tim_t dev, int channel)
 
 unsigned int timer_read(tim_t dev)
 {
+    unsigned a, b;
     switch (dev) {
 #if TIMER_0_EN
         case TIMER_0:
-            return (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) | (TIMER_0_DEV_1->CNT<<16));
-            break;
+            /* do OR'ing two times and only use value if results are equal.
+             * otherwise, the lower 16bit counter could overflow while the
+             * upper counter is read, leading to an incorrect result. */
+            do {
+                a = (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) |
+                        (TIMER_0_DEV_1->CNT<<16));
+                b = (((unsigned int)(0xffff & TIMER_0_DEV_0->CNT)) |
+                        (TIMER_0_DEV_1->CNT<<16));
+            } while (a!=b);
+
+            return a;
 #endif
 #if TIMER_1_EN
         case TIMER_1:
-            return (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) | (TIMER_1_DEV_1->CNT<<16));
-            break;
+             /* see above about why loop is needed */
+            do {
+                a = (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) |
+                        (TIMER_1_DEV_1->CNT<<16));
+                b = (((unsigned int)(0xffff & TIMER_1_DEV_0->CNT)) |
+                        (TIMER_1_DEV_1->CNT<<16));
+            } while (a!=b);
+
+            return a;
 #endif
         case TIMER_UNDEFINED:
         default:
-- 
GitLab