diff --git a/board/cr50/board.c b/board/cr50/board.c index fec01fc506..2ea8f74318 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -95,9 +95,11 @@ static void init_pmu(void) void pmu_wakeup_interrupt(void) { - int exiten; + int exiten, wakeup_src; - delay_sleep_by(1000); + delay_sleep_by(1 * MSEC); + + wakeup_src = GR_PMU_EXITPD_SRC; /* Clear interrupt state */ GWRITE_FIELD(PMU, INT_STATE, INTR_WAKEUP, 1); @@ -105,7 +107,7 @@ void pmu_wakeup_interrupt(void) /* Clear pmu reset */ GWRITE(PMU, CLRRST, 1); - if (GR_PMU_EXITPD_SRC & GC_PMU_EXITPD_SRC_PIN_PD_EXIT_MASK) { + if (wakeup_src & GC_PMU_EXITPD_SRC_PIN_PD_EXIT_MASK) { /* * If any wake pins are edge triggered, the pad logic latches * the wakeup. Clear EXITEN0 to reset the wakeup logic. @@ -123,22 +125,26 @@ void pmu_wakeup_interrupt(void) if (!gpio_get_level(GPIO_SYS_RST_L_IN)) sys_rst_asserted(GPIO_SYS_RST_L_IN); } + + /* Trigger timer0 interrupt */ + if (wakeup_src & GC_PMU_EXITPD_SRC_TIMELS0_PD_EXIT_TIMER0_MASK) + task_trigger_irq(GC_IRQNUM_TIMELS0_TIMINT0); + + /* Trigger timer1 interrupt */ + if (wakeup_src & GC_PMU_EXITPD_SRC_TIMELS0_PD_EXIT_TIMER1_MASK) + task_trigger_irq(GC_IRQNUM_TIMELS0_TIMINT1); + } DECLARE_IRQ(GC_IRQNUM_PMU_INTR_WAKEUP_INT, pmu_wakeup_interrupt, 1); -static void init_timers(void) -{ - /* Cancel low speed timers that may have - * been initialized prior to soft reset. */ - GREG32(TIMELS, TIMER0_CONTROL) = 0; - GREG32(TIMELS, TIMER0_LOAD) = 0; - GREG32(TIMELS, TIMER1_CONTROL) = 0; - GREG32(TIMELS, TIMER1_LOAD) = 0; -} - static void init_interrupts(void) { int i; + uint32_t exiten = GREG32(PINMUX, EXITEN0); + + /* Clear wake pin interrupts */ + GREG32(PINMUX, EXITEN0) = 0; + GREG32(PINMUX, EXITEN0) = exiten; /* Enable all GPIO interrupts */ for (i = 0; i < gpio_ih_count; i++) @@ -191,7 +197,6 @@ static void init_runlevel(const enum permission_level desired_level) static void board_init(void) { init_pmu(); - init_timers(); init_interrupts(); init_trng(); init_jittery_clock(1); diff --git a/chip/g/hwtimer.c b/chip/g/hwtimer.c index 46a3a7769a..22d43775c4 100644 --- a/chip/g/hwtimer.c +++ b/chip/g/hwtimer.c @@ -10,76 +10,63 @@ #include "task.h" #include "util.h" +/* The frequency of timerls is 256k so there are about 4usec/tick */ +#define USEC_PER_TICK 4 /* - * Other chips can interrupt at arbitrary match points. We can only interrupt - * at zero, so we'll have to use a separate timer for events. We'll use module - * 0, timer 1 for the current time and module 0, timer 2 for event timers. - * - * Oh, and we can't control the rate at which the timers tick. We expect to - * have all counter values in microseconds, but instead they'll be some factor - * faster than that. 1 usec / tick == 1 MHz, so if PCLK is 30 MHz, we'll have - * to divide the hardware counter by 30 to get the values expected outside of - * this file. + * Scale the maximum number of ticks so that it will only count up to the + * equivalent of 0xffffffff usecs. */ -static uint32_t clock_mul_factor; -static uint32_t hw_rollover_count; +#define TIMELS_MAX (0xffffffff / USEC_PER_TICK) + +#define SOURCE(field) TIMER0_##field +#define EVENT(field) TIMER1_##field static inline uint32_t ticks_to_usecs(uint32_t ticks) { - return ((uint64_t)hw_rollover_count * 0xffffffff + ticks) - / clock_mul_factor; + return ticks * USEC_PER_TICK; } -static void update_prescaler(void) +static inline uint32_t usec_to_ticks(uint32_t next_evt_us) { - /* - * We want the timer to tick every microsecond, but we can only divide - * PCLK by 1, 16, or 256. We're targeting 30MHz, so we'll just let it - * run at 1:1. - */ - REG_WRITE_MLV(GR_TIMEHS_CONTROL(0, 1), - GC_TIMEHS_TIMER1CONTROL_PRE_MASK, - GC_TIMEHS_TIMER1CONTROL_PRE_LSB, 0); - REG_WRITE_MLV(GR_TIMEHS_CONTROL(0, 2), - GC_TIMEHS_TIMER1CONTROL_PRE_MASK, - GC_TIMEHS_TIMER1CONTROL_PRE_LSB, 0); - - /* - * Assume the clock rate is an integer multiple of MHz. - */ - clock_mul_factor = PCLK_FREQ / 1000000; + return next_evt_us / USEC_PER_TICK; } -DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT); uint32_t __hw_clock_event_get(void) { /* At what time will the next event fire? */ - uint32_t time_now_in_ticks; - time_now_in_ticks = (0xffffffff - GR_TIMEHS_VALUE(0, 1)); - return ticks_to_usecs(time_now_in_ticks + GR_TIMEHS_VALUE(0, 2)); + return __hw_clock_source_read() + + ticks_to_usecs(GREG32(TIMELS, EVENT(VALUE))); } void __hw_clock_event_clear(void) { /* one-shot, 32-bit, timer & interrupts disabled, 1:1 prescale */ - GR_TIMEHS_CONTROL(0, 2) = 0x3; + GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 0); + + /* Disable interrupts */ + GWRITE(TIMELS, EVENT(IER), 0); + /* Clear any pending interrupts */ - GR_TIMEHS_INTCLR(0, 2) = 0x1; + GWRITE(TIMELS, EVENT(WAKEUP_ACK), 1); + GWRITE(TIMELS, EVENT(IAR), 1); } void __hw_clock_event_set(uint32_t deadline) { - uint32_t time_now_in_ticks; + uint32_t event_time; __hw_clock_event_clear(); /* How long from the current time to the deadline? */ - time_now_in_ticks = (0xffffffff - GR_TIMEHS_VALUE(0, 1)); - GR_TIMEHS_LOAD(0, 2) = (deadline - time_now_in_ticks / clock_mul_factor) - * clock_mul_factor; + event_time = (deadline - __hw_clock_source_read()); - /* timer & interrupts enabled */ - GR_TIMEHS_CONTROL(0, 2) = 0xa3; + /* Convert event_time to ticks rounding up */ + GREG32(TIMELS, EVENT(LOAD)) = + ((uint64_t)(event_time + USEC_PER_TICK - 1) / USEC_PER_TICK); + + /* Enable the timer & interrupts */ + GWRITE(TIMELS, EVENT(IER), 1); + GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 1); } /* @@ -92,7 +79,7 @@ void __hw_clock_event_irq(void) __hw_clock_event_clear(); process_timers(0); } -DECLARE_IRQ(GC_IRQNUM_TIMEHS0_TIMINT2, __hw_clock_event_irq, 1); +DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT1, __hw_clock_event_irq, 1); uint32_t __hw_clock_source_read(void) { @@ -100,58 +87,69 @@ uint32_t __hw_clock_source_read(void) * Return the current time in usecs. Since the counter counts down, * we have to invert the value. */ - return ticks_to_usecs(0xffffffff - GR_TIMEHS_VALUE(0, 1)); + return ticks_to_usecs(TIMELS_MAX - GREG32(TIMELS, SOURCE(VALUE))); } void __hw_clock_source_set(uint32_t ts) { - hw_rollover_count = ((uint64_t)ts * clock_mul_factor) >> 32; - GR_TIMEHS_LOAD(0, 1) = 0xffffffff - ts * clock_mul_factor; - GR_TIMEHS_BGLOAD(0, 1) = 0xffffffff; + GREG32(TIMELS, SOURCE(LOAD)) = (0xffffffff - ts) / USEC_PER_TICK; } /* This handles rollover in the HW timer */ void __hw_clock_source_irq(void) { /* Clear the interrupt */ - GR_TIMEHS_INTCLR(0, 1) = 0x1; + GWRITE(TIMELS, SOURCE(WAKEUP_ACK), 1); + GWRITE(TIMELS, SOURCE(IAR), 1); - /* The one-tick-per-clock HW counter has rolled over. */ - hw_rollover_count++; - /* Has the system's usec counter rolled over? */ - if (hw_rollover_count >= clock_mul_factor) { - hw_rollover_count = 0; - process_timers(1); - } else { - process_timers(0); - } + /* Reset the load value */ + GREG32(TIMELS, SOURCE(LOAD)) = TIMELS_MAX; + + process_timers(1); } -DECLARE_IRQ(GC_IRQNUM_TIMEHS0_TIMINT1, __hw_clock_source_irq, 1); +DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT0, __hw_clock_source_irq, 1); int __hw_clock_source_init(uint32_t start_t) { - /* Set the reload and current value. */ - GR_TIMEHS_BGLOAD(0, 1) = 0xffffffff; - GR_TIMEHS_LOAD(0, 1) = 0xffffffff; - /* HW Timer enabled, periodic, interrupt enabled, 32-bit, wrapping */ - GR_TIMEHS_CONTROL(0, 1) = 0xe2; + /* Verify the contents of CC_TRIM are valid */ + ASSERT(GR_FUSE(RC_RTC_OSC256K_CC_EN) == 0x5); + + /* Initialize RTC to 256kHz */ + GWRITE_FIELD(RTC, CTRL, X_RTC_RC_CTRL, + GR_FUSE(RC_RTC_OSC256K_CC_TRIM)); + + /* Configure timer1 */ + GREG32(TIMELS, EVENT(LOAD)) = TIMELS_MAX; + GREG32(TIMELS, EVENT(RELOADVAL)) = TIMELS_MAX; + GWRITE_FIELD(TIMELS, EVENT(CONTROL), WRAP, 1); + GWRITE_FIELD(TIMELS, EVENT(CONTROL), RELOAD, 0); + GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 0); + + /* Configure timer0 */ + GREG32(TIMELS, SOURCE(RELOADVAL)) = TIMELS_MAX; + GWRITE_FIELD(TIMELS, SOURCE(CONTROL), WRAP, 1); + GWRITE_FIELD(TIMELS, SOURCE(CONTROL), RELOAD, 1); + /* Event timer disabled */ __hw_clock_event_clear(); - /* Account for the clock speed. */ - update_prescaler(); - /* Clear any pending interrupts */ - GR_TIMEHS_INTCLR(0, 1) = 0x1; + GWRITE(TIMELS, SOURCE(WAKEUP_ACK), 1); /* Force the time to whatever we're told it is */ __hw_clock_source_set(start_t); + /* HW Timer enabled, periodic, interrupt enabled, 32-bit, wrapping */ + GWRITE_FIELD(TIMELS, SOURCE(CONTROL), ENABLE, 1); + + /* Enable source timer interrupts */ + GWRITE(TIMELS, SOURCE(IER), 1); + /* Here we go... */ - task_enable_irq(GC_IRQNUM_TIMEHS0_TIMINT1); - task_enable_irq(GC_IRQNUM_TIMEHS0_TIMINT2); + task_enable_irq(GC_IRQNUM_TIMELS0_TIMINT0); + task_enable_irq(GC_IRQNUM_TIMELS0_TIMINT1); /* Return the Event timer IRQ number (NOT the HW timer IRQ) */ - return GC_IRQNUM_TIMEHS0_TIMINT2; + return GC_IRQNUM_TIMELS0_TIMINT1; } diff --git a/chip/g/idle.c b/chip/g/idle.c index 359902d94b..d1250115f2 100644 --- a/chip/g/idle.c +++ b/chip/g/idle.c @@ -4,12 +4,13 @@ */ #include "common.h" -#include "timer.h" #include "console.h" +#include "hwtimer.h" #include "rdd.h" #include "registers.h" #include "system.h" #include "task.h" +#include "timer.h" #include "util.h" /* What to do when we're just waiting */ @@ -22,6 +23,7 @@ static enum { } idle_action; #define IDLE_DEFAULT IDLE_SLEEP +#define EVENT_MIN 500 static const char const *idle_name[] = { "invalid", @@ -96,6 +98,9 @@ static void prepare_to_sleep(void) * down. Currently deep sleep is only enabled through the console. */ if (idle_action == IDLE_DEEP_SLEEP) { + /* Clear upcoming events. They don't matter in deep sleep */ + __hw_clock_event_clear(); + /* * Disable the i2c and spi slave wake sources since the TPM is * not being used and reenable them in their init functions on @@ -164,7 +169,7 @@ void clock_refresh_console_in_use(void) /* Custom idle task, executed when no tasks are ready to be scheduled. */ void __idle(void) { - int sleep_ok, sleep_delay_passed; + int sleep_ok, sleep_delay_passed, next_evt_us; /* * This register is preserved across soft reboots, but not hard. It @@ -189,12 +194,15 @@ void __idle(void) /* Wait a bit, just in case */ sleep_delay_passed = timestamp_expired(next_sleep_time, 0); + /* Don't enable sleep if there is about to be an event */ + next_evt_us = __hw_clock_event_get() - __hw_clock_source_read(); + /* If it hasn't yet been long enough, check again when it is */ if (!sleep_delay_passed) timer_arm(next_sleep_time, TASK_ID_IDLE); /* We're allowed to sleep now, so set it up. */ - if (sleep_ok && sleep_delay_passed) + if (sleep_ok && sleep_delay_passed && next_evt_us > EVENT_MIN) if (idle_action != IDLE_WFI) prepare_to_sleep();