diff options
author | Mulin Chao <mlchao@nuvoton.com> | 2016-02-16 14:17:42 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-05-11 21:24:32 -0700 |
commit | 3ccb91fe10271f8af6f097483efac9a177d11a31 (patch) | |
tree | fa3e6ecf1d4bf8dd53c8a12dce4ff0389ff02367 | |
parent | b6b6430daa81e44519e92e37fe57ccd8312cfcf1 (diff) | |
download | chrome-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.c | 17 | ||||
-rw-r--r-- | chip/npcx/hwtimer.c | 10 | ||||
-rw-r--r-- | chip/npcx/watchdog.c | 13 |
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 |