summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/ish/hwtimer.c39
-rw-r--r--chip/ish/power_mgt.c11
2 files changed, 43 insertions, 7 deletions
diff --git a/chip/ish/hwtimer.c b/chip/ish/hwtimer.c
index d3d78cadd6..2906d79aff 100644
--- a/chip/ish/hwtimer.c
+++ b/chip/ish/hwtimer.c
@@ -32,6 +32,14 @@ static uint32_t last_deadline;
*/
#define MINIMUM_EVENT_DELAY_US 800
+/*
+ * ISH HPET timer HW has latency for interrupt, on ISH5, this latency is about
+ * 3 ticks, defined this configuration to calibrate the 'last_deadline' which is
+ * updated in event timer interrupt ISR. Without this calibration, we could
+ * get negative sleep time in idle task for low power sleep process.
+ */
+#define HPET_INT_LATENCY_TICKS 3
+
/* Scaling helper methods for different ISH chip variants */
#ifdef CHIP_FAMILY_ISH3
#define CLOCK_FACTOR 12
@@ -161,26 +169,45 @@ static inline uint64_t read_main_timer(void)
void __hw_clock_event_set(uint32_t deadline)
{
uint32_t remaining_us;
+ uint32_t current_us;
+ uint64_t current_ticks;
- last_deadline = deadline;
+ /* 'current_ticks' is the current absolute 64bit HW timer counter */
+ current_ticks = read_main_timer();
- remaining_us = deadline - __hw_clock_source_read();
+ /*
+ * 'current_us' is the low 32bit part of current time in 64bit micro
+ * seconds format, it's can express 2^32 micro seconds in maximum.
+ */
+ current_us = scale_ticks2us(current_ticks);
- /* Ensure HW has enough time to react to new timer value */
+ /*
+ * To ensure HW has enough time to react to the new timer value,
+ * we make remaining time not less than 'MINIMUM_EVENT_DELAY_US'
+ */
+ remaining_us = deadline - current_us;
remaining_us = MAX(remaining_us, MINIMUM_EVENT_DELAY_US);
/*
+ * Set new 64bit absolute timeout ticks to Timer 1 comparator
+ * register.
* For ISH3, this assumes that remaining_us is less than 360 seconds
* (2^32 us / 12Mhz), otherwise we would need to handle 32-bit rollover
* of 12Mhz timer comparator value. Watchdog refresh happens at least
* every 10 seconds.
*/
wait_while_settling(HPET_T1_CMP_SETTLING);
- HPET_TIMER_COMP(1) = read_main_timer() + scale_us2ticks(remaining_us);
+ HPET_TIMER_COMP(1) = current_ticks + scale_us2ticks(remaining_us);
- wait_while_settling(HPET_T1_SETTLING);
+ /*
+ * Update 'last_deadline' and add calibrate delta due to HPET timer
+ * interrupt latency.
+ */
+ last_deadline = current_us + remaining_us;
+ last_deadline += scale_ticks2us(HPET_INT_LATENCY_TICKS);
- /* Arm timer */
+ /* Enable timer interrupt */
+ wait_while_settling(HPET_T1_SETTLING);
HPET_TIMER_CONF_CAP(1) |= HPET_Tn_INT_ENB_CNF;
}
diff --git a/chip/ish/power_mgt.c b/chip/ish/power_mgt.c
index dea5b4cf1c..cd5d17034e 100644
--- a/chip/ish/power_mgt.c
+++ b/chip/ish/power_mgt.c
@@ -585,7 +585,16 @@ void __idle(void)
t0 = get_time();
next_delay = __hw_clock_event_get() - t0.le.lo;
- pm_process(t0, next_delay);
+ /*
+ * Most of the time, 'next_delay' will be positive. But, due to
+ * interrupt latency, it's possible that get_time() returns
+ * the value bigger than the one from __hw_clock_event_get()
+ * which is supposed to be updated by ISR before control reaches
+ * to the get_time().
+ *
+ * Here, we handle the delayed update by changing negative to 0.
+ */
+ pm_process(t0, MAX(0, next_delay));
}
}