summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2019-06-19 15:33:32 -0600
committerCommit Bot <commit-bot@chromium.org>2019-06-24 22:45:18 +0000
commit52e914fa8e674f61b9602f124e998a74adbc2b61 (patch)
tree4a3b18d5fab03b12e1443c10233f36e83663c356
parentc6aa7a384d179128339068531f79baed3a42ceef (diff)
downloadchrome-ec-52e914fa8e674f61b9602f124e998a74adbc2b61.tar.gz
ish: Use 64-bit hardware timer
ISH has native support for storing the hardware ticks in a 64-bit integer. With CONFIG_HWTIMER_64BIT, we can use this instead of relying on the periodic rollover interrupt. BUG=b:133190570,chromium:976804 BRANCH=none TEST=ran arcada_ish for more than 2³² μs, observed timer worked as normal Change-Id: I3b608c49081842f28d2ef8c16279992af1cb4fad Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1668056 Reviewed-by: Denis Brockus <dbrockus@chromium.org>
-rw-r--r--chip/ish/config_chip.h3
-rw-r--r--chip/ish/hwtimer.c142
-rw-r--r--core/minute-ia/interrupts.c1
3 files changed, 27 insertions, 119 deletions
diff --git a/chip/ish/config_chip.h b/chip/ish/config_chip.h
index e6b1b52050..148341db47 100644
--- a/chip/ish/config_chip.h
+++ b/chip/ish/config_chip.h
@@ -108,6 +108,9 @@
/* Customize the build */
/* Optional features present on this chip */
+/* ISH uses 64-bit hardware timer */
+#define CONFIG_HWTIMER_64BIT
+
/* Macro used with gpio.inc, ISH only has port 0 */
#define GPIO_PIN(index) 0, (1 << (index))
#define GPIO_PIN_MASK(m) .port = 0, .mask = (m)
diff --git a/chip/ish/hwtimer.c b/chip/ish/hwtimer.c
index 2906d79aff..4b6faa96f8 100644
--- a/chip/ish/hwtimer.c
+++ b/chip/ish/hwtimer.c
@@ -20,12 +20,6 @@
static uint32_t last_deadline;
/*
- * 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)
-
-/*
* 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
@@ -45,87 +39,35 @@ static uint32_t last_deadline;
#define CLOCK_FACTOR 12
BUILD_ASSERT(CLOCK_FACTOR * SECOND == ISH_HPET_CLK_FREQ);
-static inline uint64_t scale_us2ticks(uint32_t us)
+static inline uint64_t scale_us2ticks(uint64_t us)
{
- return (uint64_t)us * CLOCK_FACTOR;
+ return us * CLOCK_FACTOR;
}
-static inline uint32_t scale_ticks2us(uint64_t ticks)
+static inline uint64_t scale_ticks2us(uint64_t ticks)
{
- /*
- * 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;
+ return ticks / CLOCK_FACTOR;
}
static inline void wait_while_settling(uint32_t mask)
{
/* Do nothing on ISH3, only ISH4 and ISH5 need settling */
- (void) mask;
}
#elif defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5)
#define CLOCK_SCALE_BITS 15
BUILD_ASSERT(BIT(CLOCK_SCALE_BITS) == ISH_HPET_CLK_FREQ);
-static inline uint32_t scale_us2ticks(uint32_t us)
+static inline uint64_t scale_us2ticks(uint64_t us)
{
- /*
- * 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;
+ /* ticks = us * ISH_HPET_CLK_FREQ / SECOND */
- asm("divl %3" : "=a"(ticks) : "d"(hi), "a"(lo), "rm"(divisor));
- return ticks;
+ return (us << CLOCK_SCALE_BITS) / SECOND;
}
-static inline uint32_t scale_ticks2us(uint64_t ticks)
+static inline uint64_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;
+ return (ticks * SECOND) >> CLOCK_SCALE_BITS;
}
/*
@@ -218,77 +160,54 @@ uint32_t __hw_clock_event_get(void)
void __hw_clock_event_clear(void)
{
- /*
- * we get timer event at every new clksrc_high.
- * so when there's no event, last_dealine should be
- * the last value within current clksrc_high.
- */
last_deadline = 0xFFFFFFFF;
wait_while_settling(HPET_T1_SETTLING);
HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF;
}
-uint32_t __hw_clock_source_read(void)
+uint64_t __hw_clock_source_read64(void)
{
return scale_ticks2us(read_main_timer());
}
-void __hw_clock_source_set(uint32_t ts)
+void __hw_clock_source_set64(uint64_t timestamp)
{
/* Reset both clock and overflow comparators */
wait_while_settling(HPET_ANY_SETTLING);
HPET_GENERAL_CONFIG &= ~HPET_ENABLE_CNF;
- HPET_MAIN_COUNTER_64 = scale_us2ticks(ts);
- HPET_TIMER0_COMP_64 = ROLLOVER_CMP_VAL;
+ HPET_MAIN_COUNTER_64 = scale_us2ticks(timestamp);
wait_while_settling(HPET_ANY_SETTLING);
HPET_GENERAL_CONFIG |= HPET_ENABLE_CNF;
}
-static void __hw_clock_source_irq(int timer_id)
+static void hw_clock_event_isr(void)
{
/* Clear interrupt */
wait_while_settling(HPET_INT_STATUS_SETTLING);
- HPET_INTR_CLEAR = BIT(timer_id);
-
- /*
- * If IRQ is from timer 0, 2^32 us have elapsed (i.e. OS timer
- * overflowed).
- */
- process_timers(timer_id == 0);
-}
+ HPET_INTR_CLEAR = BIT(1);
-void __hw_clock_source_irq_0(void)
-{
- __hw_clock_source_irq(0);
+ process_timers(0);
}
-DECLARE_IRQ(ISH_HPET_TIMER0_IRQ, __hw_clock_source_irq_0);
+DECLARE_IRQ(ISH_HPET_TIMER1_IRQ, hw_clock_event_isr);
-void __hw_clock_source_irq_1(void)
-{
- __hw_clock_source_irq(1);
-}
-DECLARE_IRQ(ISH_HPET_TIMER1_IRQ, __hw_clock_source_irq_1);
-
-int __hw_clock_source_init(uint32_t start_t)
+int __hw_clock_source_init64(uint64_t start_t)
{
/*
- * The timer can only fire interrupt when its value reaches zero.
- * Therefore we need two timers:
- * - Timer 0 as free running timer
- * - Timer 1 as event timer
+ * Timer 1 is used as an event timer. Timer 0 is unused, as
+ * CONFIG_HWTIMER_64BIT is enabled.
*/
-
- uint32_t timer0_config = 0x00000000;
uint32_t timer1_config = 0x00000000;
/* Disable HPET */
wait_while_settling(HPET_ANY_SETTLING);
HPET_GENERAL_CONFIG &= ~HPET_ENABLE_CNF;
- /* Disable T0 and T1 until we get them set up (below) */
+ /* Disable T0 */
HPET_TIMER_CONF_CAP(0) &= ~HPET_Tn_INT_ENB_CNF;
+
+ /* Disable T1 until we get it set up (below) */
HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF;
/* Initialize main counter */
@@ -298,37 +217,24 @@ int __hw_clock_source_init(uint32_t start_t)
HPET_INTR_CLEAR = BIT(0);
HPET_INTR_CLEAR = BIT(1);
- /* Set comparator value for Timer 0 and enable periodic mode */
- HPET_TIMER0_COMP_64 = ROLLOVER_CMP_VAL;
- timer0_config |= HPET_Tn_TYPE_CNF;
-
- /* Timer 0 - IRQ routing, no need IRQ set for HPET0 */
- timer0_config &= ~HPET_Tn_INT_ROUTE_CNF_MASK;
-
/* Timer 1 - IRQ routing */
timer1_config &= ~HPET_Tn_INT_ROUTE_CNF_MASK;
timer1_config |= (ISH_HPET_TIMER1_IRQ <<
HPET_Tn_INT_ROUTE_CNF_SHIFT);
/* Level triggered interrupt */
- timer0_config |= HPET_Tn_INT_TYPE_CNF;
timer1_config |= HPET_Tn_INT_TYPE_CNF;
- /* no event until next timer 0 IRQ for clksrc_high++ */
+ /* Initialize last_deadline until an event is scheduled */
last_deadline = 0xFFFFFFFF;
- /* Enable interrupt */
- timer0_config |= HPET_Tn_INT_ENB_CNF;
-
/* Before enabling, previous values must have settled */
wait_while_settling(HPET_ANY_SETTLING);
/* Unmask HPET IRQ in IOAPIC */
- task_enable_irq(ISH_HPET_TIMER0_IRQ);
task_enable_irq(ISH_HPET_TIMER1_IRQ);
- /* Set timer 0/1 config */
- HPET_TIMER_CONF_CAP(0) |= timer0_config;
+ /* Copy timer config to hardware register */
HPET_TIMER_CONF_CAP(1) |= timer1_config;
/* Enable HPET */
diff --git a/core/minute-ia/interrupts.c b/core/minute-ia/interrupts.c
index 07c4c71ef0..58db2bee27 100644
--- a/core/minute-ia/interrupts.c
+++ b/core/minute-ia/interrupts.c
@@ -146,7 +146,6 @@ static const irq_desc_t system_irqs[] = {
LEVEL_INTR(ISH_GPIO_IRQ, ISH_GPIO_VEC),
LEVEL_INTR(ISH_IPC_HOST2ISH_IRQ, ISH_IPC_VEC),
LEVEL_INTR(ISH_IPC_ISH2HOST_CLR_IRQ, ISH_IPC_ISH2HOST_CLR_VEC),
- LEVEL_INTR(ISH_HPET_TIMER0_IRQ, ISH_HPET_TIMER0_VEC),
LEVEL_INTR(ISH_HPET_TIMER1_IRQ, ISH_HPET_TIMER1_VEC),
LEVEL_INTR(ISH_DEBUG_UART_IRQ, ISH_DEBUG_UART_VEC),
LEVEL_INTR(ISH_FABRIC_IRQ, ISH_FABRIC_VEC),