summaryrefslogtreecommitdiff
path: root/chip/ish/hwtimer.c
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2019-02-06 16:27:46 -0700
committerJett Rink <jettrink@chromium.org>2019-02-26 14:21:48 +0000
commit101a82ebc5b2be19fd3f73b9324b4fd05dd6fa1f (patch)
treeac7109143903a179d2765a544e51ca7472d7f4c9 /chip/ish/hwtimer.c
parent5acb32bfbd528277dadb6967b03d25d7ffe2433f (diff)
downloadchrome-ec-101a82ebc5b2be19fd3f73b9324b4fd05dd6fa1f.tar.gz
ish: adjust timers for ISH 4/5 32kHz clock
The main timer for ISH 4/5 is a 32kHz clock so we need to adjust the current hardware timer interface to scale it correctly. Also, the timer has problems staying armed without 1) ensuring the timer is at least 25 ticks in the future 2) waiting for the status register to specify that the timer1 updates have actually been written to the hardware BRANCH=none BUG=b:123762212 TEST=events are firing correctly on arcacda_ish in motion sense task with testing CL. Otherwise polling would stall completely Change-Id: I5d3a32df96e0290dc0381738689cc3f2d5fd430f Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/1457597 Reviewed-by: Edward Hill <ecgh@chromium.org> Commit-Queue: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Diffstat (limited to 'chip/ish/hwtimer.c')
-rw-r--r--chip/ish/hwtimer.c234
1 files changed, 151 insertions, 83 deletions
diff --git a/chip/ish/hwtimer.c b/chip/ish/hwtimer.c
index 97a2a4f848..63c9dcde0a 100644
--- a/chip/ish/hwtimer.c
+++ b/chip/ish/hwtimer.c
@@ -3,7 +3,7 @@
* found in the LICENSE file.
*/
-/* Hardware timers driver - HPET */
+/* Hardware timers driver for ISH High Precision Event Timers (HPET) */
#include "console.h"
#include "hpet.h"
@@ -13,44 +13,108 @@
#include "task.h"
#include "util.h"
-#if defined(CHIP_FAMILY_ISH3)
-#define CLOCK_FACTOR 12
-#endif
-
#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_CLOCK, format, ## args)
static uint32_t last_deadline;
-/* TODO: Conform to EC API
- * ISH supports 32KHz and 12MHz clock sources.
- * EC expects timer value in 1MHz.
- * Scale the values and support it.
+/*
+ * Number of ticks for timer0 comparator representing 2^32 us seconds. SECONDS
+ * represents the expected clock frequency of the OS (i.e. 1 Mhz).
*/
+#define ROLLOVER_CMP_VAL (((uint64_t)ISH_HPET_CLK_FREQ << 32) / SECOND)
-void __hw_clock_event_set(uint32_t deadline)
+/*
+ * The ISH hardware needs at least 25 ticks of leeway to arms the timer.
+ * ISH4/5 are the slowest with 32kHz timers, so we wait at least 800us when
+ * scheduling events in the future
+ */
+#define MINIMUM_EVENT_DELAY_US 800
+
+/* Scaling helper methods for different ISH chip variants */
+#ifdef CHIP_FAMILY_ISH3
+#define CLOCK_FACTOR 12
+BUILD_ASSERT(CLOCK_FACTOR * SECOND == ISH_HPET_CLK_FREQ);
+
+static inline uint64_t scale_us2ticks(uint32_t us)
{
- last_deadline = deadline;
-#if defined(CHIP_FAMILY_ISH3)
- HPET_TIMER_COMP(1) = deadline * CLOCK_FACTOR;
-#else
- HPET_TIMER_COMP(1) = deadline;
-#endif
- HPET_TIMER_CONF_CAP(1) |= HPET_Tn_INT_ENB_CNF;
+ return (uint64_t)us * CLOCK_FACTOR;
}
-uint32_t __hw_clock_event_get(void)
+static inline uint32_t scale_ticks2us(uint64_t ticks)
{
- return last_deadline;
+ /*
+ * We drop into asm here since this is on the critical path of reading
+ * the hardware timer for clock result. This needs to be efficient.
+ *
+ * Modulating hi first ensures that the quotient fits in 32-bits due to
+ * the follow math:
+ * Let ticks = (hi << 32) + lo;
+ * Let hi = N*CLOCK_FACTOR + R; where R is hi % CLOCK_FACTOR
+ *
+ * ticks = (N*CLOCK_FACTOR << 32) + (R << 32) + lo
+ *
+ * ticks / CLOCK_FACTOR = ((N*CLOCK_FACTOR << 32) + (R << 32) + lo) /
+ * CLOCK_FACTOR
+ * ticks / CLOCK_FACTOR = (N*CLOCK_FACTOR << 32) / CLOCK_FACTOR +
+ * (R << 32) / CLOCK_FACTOR +
+ * lo / CLOCK_FACTOR
+ * ticks / 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.
+ * (ticks / CLOCK_FACTOR) & 0xFFFFFFFF = ((R << 32) + lo) / CLOCK_FACTOR
+ */
+ const uint32_t divisor = CLOCK_FACTOR;
+ const uint32_t hi = ((uint32_t)(ticks >> 32)) % divisor;
+ const uint32_t lo = ticks;
+ uint32_t quotient;
+
+ asm("divl %3" : "=a"(quotient) : "d"(hi), "a"(lo), "rm"(divisor));
+ return quotient;
}
-void __hw_clock_event_clear(void)
+#elif defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5)
+#define CLOCK_SCALE_BITS 15
+BUILD_ASSERT((1 << CLOCK_SCALE_BITS) == ISH_HPET_CLK_FREQ);
+
+static inline uint32_t scale_us2ticks(uint32_t us)
{
- HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF;
+ /*
+ * ticks = us * ISH_HPET_CLK_FREQ / SECOND;
+ *
+ * First multiple us by ISH_HPET_CLK_FREQ via bit shift, then use
+ * 64-bit div into 32-bit result.
+ *
+ * We use asm directly to maintain full 32-bit precision without using
+ * an iterative divide (i.e. 64-bit / 64-bit => 64-bit). We use the
+ * 64-bit / 32-bit => 32-bit asm instruction directly since there is no
+ * way to emitted that instruction via the compiler.
+ *
+ * The intermediate result of (us * ISH_HPET_CLK_FREQ) needs 64-bits of
+ * precision to maintain full 32-bit precision for the end result.
+ */
+ const uint32_t hi = us >> (32 - CLOCK_SCALE_BITS);
+ const uint32_t lo = us << CLOCK_SCALE_BITS;
+ const uint32_t divisor = SECOND;
+ uint32_t ticks;
+
+ asm("divl %3" : "=a"(ticks) : "d"(hi), "a"(lo), "rm"(divisor));
+ return ticks;
}
-#ifdef CHIP_FAMILY_ISH3
+static inline uint32_t scale_ticks2us(uint64_t ticks)
+{
+ /*
+ * us = ticks / ISH_HPET_CLK_FREQ * SECOND;
+ */
+ const uint64_t intermediate = (uint64_t)ticks * SECOND;
+
+ return intermediate >> CLOCK_SCALE_BITS;
+}
+#endif /* CHIP_FAMILY_ISH4 || CHIP_FAMILY_ISH5 */
+
/*
* 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.
@@ -68,51 +132,72 @@ static inline uint64_t read_main_timer(void)
return t.val;
}
-#endif
-uint32_t __hw_clock_source_read(void)
+void __hw_clock_event_set(uint32_t deadline)
{
-#if defined(CHIP_FAMILY_ISH3)
- const uint64_t tmp = read_main_timer();
- const uint32_t divisor = CLOCK_FACTOR;
+ uint32_t remaining_us;
+
+ last_deadline = deadline;
+
+ remaining_us = deadline - __hw_clock_source_read();
+
+ /* Ensure HW has enough time to react to new timer value */
+ remaining_us = MAX(remaining_us, MINIMUM_EVENT_DELAY_US);
+
/*
- * 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
+ * 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.
*/
- const uint32_t hi = ((uint32_t)(tmp >> 32)) % divisor;
- const uint32_t lo = tmp;
+ HPET_TIMER_COMP(1) = read_main_timer() + scale_us2ticks(remaining_us);
- register uint32_t quotient;
- asm("divl %3" : "=a"(quotient) : "d"(hi), "a"(lo), "rm"(divisor));
- return quotient;
-#else
- return HPET_MAIN_COUNTER;
+ do {
+ /* Arm timer */
+ HPET_TIMER_CONF_CAP(1) |= HPET_Tn_INT_ENB_CNF;
+
+#if defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5)
+ /* Wait for timer settings to settle ~ 150us */
+ while (HPET_CTRL_STATUS & HPET_T1_SETTLING)
+ continue;
#endif
+ /*
+ * TODO(b/124890290): Remove or update while loop that ensures timer is
+ * armed once we have a better hardware understanding.
+ */
+ } while (!(HPET_TIMER_CONF_CAP(1) & HPET_Tn_INT_ENB_CNF));
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+ return last_deadline;
+}
+
+void __hw_clock_event_clear(void)
+{
+ HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF;
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+ return scale_ticks2us(read_main_timer());
}
void __hw_clock_source_set(uint32_t ts)
{
+ /* Reset both clock and overflow comparators */
+
HPET_GENERAL_CONFIG &= ~HPET_ENABLE_CNF;
-#if defined(CHIP_FAMILY_ISH3)
- HPET_MAIN_COUNTER_64 = (uint64_t)ts * CLOCK_FACTOR;
-#else
- HPET_MAIN_COUNTER = ts;
+
+ HPET_MAIN_COUNTER_64 = scale_us2ticks(ts);
+ HPET_TIMER0_COMP_64 = ROLLOVER_CMP_VAL;
+
+#if defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5)
+ /* Wait for timer to settle. required for ISH 4 */
+ while (HPET_CTRL_STATUS & HPET_MAIN_COUNTER_SETTLING)
+ continue;
#endif
+
HPET_GENERAL_CONFIG |= HPET_ENABLE_CNF;
}
@@ -121,7 +206,10 @@ static void __hw_clock_source_irq(int timer_id)
/* Clear interrupt */
HPET_INTR_CLEAR = (1 << timer_id);
- /* If IRQ is from timer 0, 32-bit timer overflowed */
+ /*
+ * If IRQ is from timer 0, 2^32 us have elapsed (i.e. OS timer
+ * overflowed).
+ */
process_timers(timer_id == 0);
}
@@ -152,31 +240,11 @@ int __hw_clock_source_init(uint32_t start_t)
/* Disable HPET */
HPET_GENERAL_CONFIG &= ~HPET_ENABLE_CNF;
-#if defined(CHIP_FAMILY_ISH3)
- HPET_MAIN_COUNTER_64 = (uint64_t)start_t * CLOCK_FACTOR;
-#else
- HPET_MAIN_COUNTER = start_t;
-#endif
+ HPET_MAIN_COUNTER_64 = scale_us2ticks(start_t);
-#if defined(CHIP_FAMILY_ISH3)
- /*
- * Set comparator value. HMC will operate in 64 bit mode.
- * HMC is 12MHz, Hence set COMP to 12x of 1MHz.
- */
- HPET_TIMER_COMP_64(0) = (uint64_t)CLOCK_FACTOR << 32; /*0xC00000000ULL;*/
-#else
- /* Set comparator value */
- HPET_TIMER_COMP(0) = 0XFFFFFFFF;
-#endif
- /* Timer 0 - enable periodic mode */
+ /* Set comparator value for Timer 0 and enable periodic mode */
+ HPET_TIMER0_COMP_64 = ROLLOVER_CMP_VAL;
timer0_config |= HPET_Tn_TYPE_CNF;
-#if defined(CHIP_FAMILY_ISH3)
- /* TIMER0 in 64-bit mode */
- timer0_config &= ~HPET_Tn_32MODE_CNF;
-#else
- /*TIMER0 in 32-bit mode*/
- timer0_config |= HPET_Tn_32MODE_CNF;
-#endif
/* Timer 0 - IRQ routing, no need IRQ set for HPET0 */
timer0_config &= ~HPET_Tn_INT_ROUTE_CNF_MASK;
@@ -192,9 +260,8 @@ int __hw_clock_source_init(uint32_t start_t)
/* Enable interrupt */
timer0_config |= HPET_Tn_INT_ENB_CNF;
- timer1_config |= HPET_Tn_INT_ENB_CNF;
- /* Unask HPET IRQ in IOAPIC */
+ /* Unmask HPET IRQ in IOAPIC */
task_enable_irq(ISH_HPET_TIMER0_IRQ);
task_enable_irq(ISH_HPET_TIMER1_IRQ);
@@ -204,8 +271,8 @@ int __hw_clock_source_init(uint32_t start_t)
#if defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5)
/* Wait for timer to settle. required for ISH 4 */
- while (HPET_CTRL_STATUS & HPET_T_CONF_CAP_BIT)
- ;
+ while (HPET_CTRL_STATUS & HPET_MAIN_COUNTER_SETTLING)
+ continue;
#endif
/*
@@ -214,5 +281,6 @@ int __hw_clock_source_init(uint32_t start_t)
*/
HPET_GENERAL_CONFIG |= (HPET_ENABLE_CNF | HPET_LEGACY_RT_CNF);
+ /* Return IRQ value for OS event timer */
return ISH_HPET_TIMER1_IRQ;
}