diff options
Diffstat (limited to 'chip/ish')
-rw-r--r-- | chip/ish/config_chip.h | 3 | ||||
-rw-r--r-- | chip/ish/hwtimer.c | 142 |
2 files changed, 27 insertions, 118 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 */ |