summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMulin Chao <mlchao@nuvoton.com>2016-02-16 14:17:42 +0800
committerchrome-bot <chrome-bot@chromium.org>2016-05-11 21:24:32 -0700
commit3ccb91fe10271f8af6f097483efac9a177d11a31 (patch)
treefa3e6ecf1d4bf8dd53c8a12dce4ff0389ff02367
parentb6b6430daa81e44519e92e37fe57ccd8312cfcf1 (diff)
downloadchrome-ec-3ccb91fe10271f8af6f097483efac9a177d11a31.tar.gz
npcx: Fixed bug that unexpected value of timer which source clock is 32K
In rare case, FW read the unexpected counter value of timer which source clock is 32K (Watchdog timer and ITIM16/32 which use 32K source clock). The root cause is the clocks between reading registers and timer's are asynchronous. It has a chance to get invalid counter value when timer is under transaction edge. The solution is using two consecutive equal readings to make sure the counter value is valid. Beside different source clocks of timer, we also found there's chip's bug which causes unexpected value of timer. If an interrupt that occurs very shortly before entering deep idle with instant wakeup, it might result in disruptive execution (such as skipping some instructions or hard fault) after "wfi". The workaround is adding the same bypass for idle in deep idle section. Modified sources: 1. clock.c: Add bypass for instant wakeup from deep sleep. 2. hwtimer.c: Add consecutive reading function for event timer. 3. watchdog.c: Add consecutive reading function for watchdog timer. BUG=chrome-os-partner:34346 TEST=make buildall -j; test nuvoton IC specific drivers BRANCH=none Change-Id: I7c9f1fb9618a3c29826d8f4599864a8dac4203bf Signed-off-by: Mulin Chao <mlchao@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/327356 Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--chip/npcx/clock.c17
-rw-r--r--chip/npcx/hwtimer.c10
-rw-r--r--chip/npcx/watchdog.c13
3 files changed, 35 insertions, 5 deletions
diff --git a/chip/npcx/clock.c b/chip/npcx/clock.c
index 9cda94ddc6..05d49ae751 100644
--- a/chip/npcx/clock.c
+++ b/chip/npcx/clock.c
@@ -298,8 +298,21 @@ void __idle(void)
/* Get current counter value of event timer */
evt_count = __hw_clock_event_count();
- /* Enter deep idle */
- asm("wfi");
+
+ /*
+ * TODO (ML): We found the same symptom of idle occurs
+ * after wake-up from deep idle. Please see task.c for
+ * more detail.
+ * Workaround: Apply the same bypass of idle.
+ */
+ asm ("push {r0-r5}\n"
+ "ldr r0, =0x100A8000\n"
+ "wfi\n"
+ "ldm r0, {r0-r5}\n"
+ "pop {r0-r5}\n"
+ "isb\n"
+ );
+
/* Get time delay cause of deep idle */
next_evt_us = __hw_clock_get_sleep_time(evt_count);
diff --git a/chip/npcx/hwtimer.c b/chip/npcx/hwtimer.c
index 5a7fa8b38c..196fcf985c 100644
--- a/chip/npcx/hwtimer.c
+++ b/chip/npcx/hwtimer.c
@@ -117,7 +117,13 @@ uint32_t __hw_clock_event_get(void)
/* Get current counter value of event timer */
uint16_t __hw_clock_event_count(void)
{
- return NPCX_ITCNT16(ITIM_EVENT_NO);
+ uint16_t cnt;
+ /* Wait for two consecutive equal values are read */
+ do {
+ cnt = NPCX_ITCNT16(ITIM_EVENT_NO);
+ } while (cnt != NPCX_ITCNT16(ITIM_EVENT_NO));
+
+ return cnt;
}
/* Returns time delay cause of deep idle */
@@ -125,7 +131,7 @@ uint32_t __hw_clock_get_sleep_time(uint16_t pre_evt_cnt)
{
fp_t evt_tick = FLOAT_TO_FP(SECOND/(float)INT_32K_CLOCK);
uint32_t sleep_time;
- uint16_t cnt = NPCX_ITCNT16(ITIM_EVENT_NO);
+ uint16_t cnt = __hw_clock_event_count();
/* Event has been triggered but timer ISR dosen't handle it */
if (IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_TO_STS))
diff --git a/chip/npcx/watchdog.c b/chip/npcx/watchdog.c
index c228eec708..3c62cbc3f3 100644
--- a/chip/npcx/watchdog.c
+++ b/chip/npcx/watchdog.c
@@ -45,6 +45,17 @@ void watchdog_init_warning_timer(void)
task_enable_irq(ITIM16_INT(ITIM_WDG_NO));
}
+static uint8_t watchdog_count(void)
+{
+ uint8_t cnt;
+ /* Wait for two consecutive equal values are read */
+ do {
+ cnt = NPCX_TWMWD;
+ } while (cnt != NPCX_TWMWD);
+
+ return cnt;
+}
+
void __keep watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
{
int wd_cnt;
@@ -52,7 +63,7 @@ void __keep watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
SET_BIT(NPCX_ITCTS(ITIM_WDG_NO), NPCX_ITCTS_TO_STS);
/* Read watchdog counter from TWMWD */
- wd_cnt = NPCX_TWMWD;
+ wd_cnt = watchdog_count();
#if DEBUG_WDG
panic_printf("WD (%d)\r\n", wd_cnt);
#endif