diff options
-rw-r--r-- | chip/ish/hpet.h | 71 | ||||
-rw-r--r-- | chip/ish/hwtimer.c | 234 |
2 files changed, 178 insertions, 127 deletions
diff --git a/chip/ish/hpet.h b/chip/ish/hpet.h index 72acf3eaa6..463d3b38a6 100644 --- a/chip/ish/hpet.h +++ b/chip/ish/hpet.h @@ -9,72 +9,55 @@ #include "common.h" /* ISH HPET config and timer registers */ -#define GENERAL_CAPS_ID_REG 0x00 -#define GENERAL_CONFIG_REG 0x10 -#define GENERAL_INT_STAT_REG 0x20 -#define MAIN_COUNTER_REG 0xF0 #define TIMER0_CONF_CAP_REG 0x100 #define TIMER0_COMP_VAL_REG 0x108 -#define TIMER0_FSB_IR_REG 0x110 -#define TIMER1_CONF_CAP_REG 0x120 -#define TIMER1_COMP_VAL_REG 0x128 -#define TIMER1_FSB_IR_REG 0x130 - -#define TIMER2_CONF_CAP_REG 0x140 -#define TIMER2_COMP_VAL_REG 0x148 -#define TIMER2_FSB_IR_REG 0x150 - -/* ISH 4: Special status register - * Use this register to see HPET timer are settled after a write. - */ -#define CONTROL_AND_STATUS_REG 0x160 -#define HPET_T_CONF_CAP_BIT 0x4 +/* HPET_GENERAL_CONFIG settings */ +#define HPET_GENERAL_CONFIG REG32(ISH_HPET_BASE + 0x10) #define HPET_ENABLE_CNF (1<<0) #define HPET_LEGACY_RT_CNF (1<<1) +/* Interrupt status acknowledge register */ +#define HPET_INTR_CLEAR REG32(ISH_HPET_BASE + 0x20) + +/* Main counter register. 64-bit */ +#define HPET_MAIN_COUNTER_64 REG64(ISH_HPET_BASE + 0xF0) +#define HPET_MAIN_COUNTER_64_LO REG32(ISH_HPET_BASE + 0xF0) +#define HPET_MAIN_COUNTER_64_HI REG32(ISH_HPET_BASE + 0xF4) + +/* HPET Timer 0/1/2 configuration*/ +#define HPET_TIMER_CONF_CAP(x) REG32(ISH_HPET_BASE + 0x100 + ((x) * 0x20)) #define HPET_Tn_INT_TYPE_CNF (1<<1) #define HPET_Tn_INT_ENB_CNF (1<<2) #define HPET_Tn_TYPE_CNF (1<<3) #define HPET_Tn_VAL_SET_CNF (1<<6) #define HPET_Tn_32MODE_CNF (1<<8) - #define HPET_Tn_INT_ROUTE_CNF_SHIFT 0x9 #define HPET_Tn_INT_ROUTE_CNF_MASK (0x1f << 9) -#define HPET_GENERAL_CONFIG REG32(ISH_HPET_BASE + GENERAL_CONFIG_REG) -#ifdef CHIP_FAMILY_ISH3 -#define HPET_MAIN_COUNTER_64 REG64(ISH_HPET_BASE + MAIN_COUNTER_REG) -#define HPET_MAIN_COUNTER_64_LO REG32(ISH_HPET_BASE + MAIN_COUNTER_REG) -#define HPET_MAIN_COUNTER_64_HI REG32(ISH_HPET_BASE + MAIN_COUNTER_REG + 0x04) -#else -#define HPET_MAIN_COUNTER REG32(ISH_HPET_BASE + MAIN_COUNTER_REG) -#endif -#define HPET_INTR_CLEAR REG32(ISH_HPET_BASE + GENERAL_INT_STAT_REG) -#define HPET_CTRL_STATUS REG32(ISH_HPET_BASE + CONTROL_AND_STATUS_REG) +/* + * HPET Timer 0/1/2 comparator values. 1/2 are always 32-bit. 0 can be + * configured as 64-bit. + */ +#define HPET_TIMER_COMP(x) REG32(ISH_HPET_BASE + 0x108 + ((x) * 0x20)) +#define HPET_TIMER0_COMP_64 REG64(ISH_HPET_BASE + 0x108) -#define HPET_TIMER_CONF_CAP(x) \ - REG32(ISH_HPET_BASE + TIMER0_CONF_CAP_REG + ((x) * 0x20)) -/* HPET1/2 are 32 bit only. HPET0 is 32bit/64bit from configuration register - * HPET_TIMER_CONFIG_CAP(0) */ -#define HPET_TIMER_COMP(x) \ - REG32(ISH_HPET_BASE + TIMER0_COMP_VAL_REG + ((x) * 0x20)) -#ifdef CHIP_FAMILY_ISH3 -#define HPET_TIMER_COMP_64(x) \ - REG64(ISH_HPET_BASE + TIMER0_COMP_VAL_REG + ((x) * 0x20)) -#endif +/* ISH 4/5: Special status register + * Use this register to see HPET timer are settled after a write. + */ +#define HPET_CTRL_STATUS REG32(ISH_HPET_BASE + 0x160) +#define HPET_T1_CMP_SETTLING (1 << 8) +#define HPET_T1_CAP_SETTLING (1 << 5) +#define HPET_MAIN_COUNTER_SETTLING (1 << 2) +#define HPET_T1_SETTLING (HPET_T1_CAP_SETTLING | \ + HPET_T1_CMP_SETTLING) #if defined(CHIP_FAMILY_ISH3) #define ISH_HPET_CLK_FREQ 12000000 /* 12 MHz clock */ #elif defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5) #define ISH_HPET_CLK_FREQ 32768 /* 32.768 KHz clock */ -#else -#define ISH_HPET_CLK_FREQ 1000000 /* 1 MHz clock */ #endif -/* HPET timer 0 period of 10ms (100 ticks per second) */ -#define ISH_TICKS_PER_SEC 100 - #endif /* __CROS_EC_HPET_H */ 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; } |