summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2019-01-03 12:38:11 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-01-16 01:47:02 -0800
commite3b446951d6001b5c6cb0e3019e908205d75f31a (patch)
treeced3c1b6fbe9fa886a283c27227f5dbcb2bf649e
parente0ec876dac4a89f2d0924b2323ebf6cb3d981723 (diff)
downloadchrome-ec-e3b446951d6001b5c6cb0e3019e908205d75f31a.tar.gz
ish: fix 32-bit quotient overflow
Restructure the hw_clock_source_read to perform a divide by 12 operation on a 64-bit number without causing a 32-bit overflow for the quotient by performing the operation in stages. Also ensure that the 64-bit read doesn't tear (by rolling over) while we are reading it. BRANCH=atlas BUG=b:122314133,b:121454497 TEST=use fortime on atlas to put time next to rollover point and verify that we no longer get a divide expection (#DE) Change-Id: I0b5fe59bec9a703fddac657986106537d4e9e203 Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1394753 Reviewed-by: Aaron Durbin <adurbin@chromium.org>
-rw-r--r--chip/ish/hpet.h2
-rw-r--r--chip/ish/hwtimer.c55
2 files changed, 50 insertions, 7 deletions
diff --git a/chip/ish/hpet.h b/chip/ish/hpet.h
index 0f30bfda41..4359fe0748 100644
--- a/chip/ish/hpet.h
+++ b/chip/ish/hpet.h
@@ -47,6 +47,8 @@
#define HPET_GENERAL_CONFIG REG32(ISH_HPET_BASE + GENERAL_CONFIG_REG)
#ifdef CHIP_FAMILY_ISH3
#define HPET_MAIN_COUNTER_64 REG64(ISH_HPET_BASE + MAIN_COUNTER_REG)
+#define HPET_MAIN_COUNTER_64_LO REG32(ISH_HPET_BASE + MAIN_COUNTER_REG)
+#define HPET_MAIN_COUNTER_64_HI REG32(ISH_HPET_BASE + MAIN_COUNTER_REG + 0x04)
#else
#define HPET_MAIN_COUNTER REG32(ISH_HPET_BASE + MAIN_COUNTER_REG)
#endif
diff --git a/chip/ish/hwtimer.c b/chip/ish/hwtimer.c
index 960a4218ff..9367590348 100644
--- a/chip/ish/hwtimer.c
+++ b/chip/ish/hwtimer.c
@@ -8,6 +8,7 @@
#include "console.h"
#include "hpet.h"
#include "hwtimer.h"
+#include "timer.h"
#include "registers.h"
#include "task.h"
#include "util.h"
@@ -49,16 +50,56 @@ void __hw_clock_event_clear(void)
HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF;
}
+#ifdef CHIP_FAMILY_ISH3
+/*
+ * The 64-bit read on a 32-bit chip can tear during the read. Ensure that the
+ * value returned for 64-bit didn't rollover while we were reading it.
+ */
+static inline uint64_t read_main_timer(void)
+{
+ timestamp_t t;
+ uint32_t hi;
+
+ do {
+ t.le.hi = HPET_MAIN_COUNTER_64_HI;
+ t.le.lo = HPET_MAIN_COUNTER_64_LO;
+ hi = HPET_MAIN_COUNTER_64_HI;
+ } while (t.le.hi != hi);
+
+ return t.val;
+}
+#endif
+
uint32_t __hw_clock_source_read(void)
{
#if defined(CHIP_FAMILY_ISH3)
- uint64_t tmp = HPET_MAIN_COUNTER_64;
- uint32_t hi = tmp >> 32;
- uint32_t lo = tmp;
- uint32_t q, r;
- const uint32_t d = CLOCK_FACTOR;
- asm ("divl %4" : "=d" (r), "=a" (q) : "0" (hi), "1" (lo), "rm" (d) : "cc");
- return q;
+ const uint64_t tmp = read_main_timer();
+ const uint32_t divisor = CLOCK_FACTOR;
+ /*
+ * Modulating hi first ensures that the quotient fits in 32-bits due to
+ * the follow math:
+ * Let tmp = (hi << 32) + lo;
+ * Let hi = N*CLOCK_FACTOR + R; where R is hi % CLOCK_FACTOR
+ *
+ * tmp = (N*CLOCK_FACTOR << 32) + (R << 32) + lo
+ *
+ * tmp / CLOCK_FACTOR = ((N*CLOCK_FACTOR << 32) + (R << 32) + lo) /
+ * CLOCK_FACTOR
+ * tmp / CLOCK_FACTOR = (N*CLOCK_FACTOR << 32) / CLOCK_FACTOR +
+ * (R << 32) / CLOCK_FACTOR +
+ * lo / CLOCK_FACTOR
+ * tmp / CLOCK_FACTOR = (N << 32) +
+ * (R << 32) / CLOCK_FACTOR +
+ * lo / CLOCK_FACTOR
+ * If we want to truncate to 32 bits, then the N << 32 can be dropped.
+ * (tmp / CLOCK_FACTOR) & 0xFFFFFFFF = ((R << 32) + lo) / CLOCK_FACTOR
+ */
+ const uint32_t hi = ((uint32_t)(tmp >> 32)) % divisor;
+ const uint32_t lo = tmp;
+
+ register uint32_t quotient;
+ asm("divl %3" : "=a"(quotient) : "d"(hi), "a"(lo), "rm"(divisor));
+ return quotient;
#else
return HPET_MAIN_COUNTER;
#endif