diff options
author | Bill Richardson <wfrichar@chromium.org> | 2014-11-13 14:02:30 -0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-11-14 03:03:32 +0000 |
commit | 9157dd93daf0500c4f8f0768b00dafb324d633ec (patch) | |
tree | 5cf26a3379168fbf9f8a0e7f4ff294104f73d548 /chip | |
parent | 3ddd906b92deff11b29934bd0e5fdc49b3fd3921 (diff) | |
download | chrome-ec-9157dd93daf0500c4f8f0768b00dafb324d633ec.tar.gz |
cr50: Add support for hwtimer
Implement the API expected by common/timer.c
BUG=chrome-os-partner:33699
BRANCH=none
TEST=manual
Run the "gettime" and "timerinfo" and "taskinfo" and "waitms"
commands. Compare the elapsed time with the real world. They seem
to match.
Change-Id: Ie5acae76780ee09e7dfb6cc0282de25f8063e96f
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/229642
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/g/hwtimer.c | 145 | ||||
-rw-r--r-- | chip/g/registers.h | 45 |
2 files changed, 186 insertions, 4 deletions
diff --git a/chip/g/hwtimer.c b/chip/g/hwtimer.c index ec9750db0a..f6bd9f67d5 100644 --- a/chip/g/hwtimer.c +++ b/chip/g/hwtimer.c @@ -4,21 +4,158 @@ */ #include "common.h" +#include "hooks.h" #include "hwtimer.h" +#include "registers.h" +#include "task.h" +#include "util.h" -/* TODO(crosbug.com/p/33432): Implement these functions */ +/* + * 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. + */ +static uint32_t clock_mul_factor; +static uint32_t clock_div_factor; +static uint32_t hw_rollover_count; + +static inline uint32_t ticks_to_usecs(uint32_t ticks) +{ + return hw_rollover_count * clock_div_factor + ticks / clock_mul_factor; +} + +static inline uint32_t usecs_to_ticks(uint32_t usecs) +{ + /* Really large counts will just be scheduled more than once */ + if (usecs >= clock_div_factor) + return 0xffffffff; + + return usecs * clock_mul_factor; +} + +static void update_prescaler(void) +{ + /* + * 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_MASK(G_TIMEHS_CONTROL(0, 1), 0x0c, 0x00, 2); + REG_WRITE_MASK(G_TIMEHS_CONTROL(0, 2), 0x0c, 0x00, 2); + + /* + * We're not yet doing anything to detect the current frequency, we're + * just hard-coding it. We're also assuming the clock rate is an + * integer multiple of MHz. + */ + clock_mul_factor = 26; /* NOTE: prototype board */ + clock_div_factor = 0xffffffff / clock_mul_factor; +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT); uint32_t __hw_clock_event_get(void) { - return 0; + /* At what time will the next event fire? */ + uint32_t time_now_in_ticks; + time_now_in_ticks = (0xffffffff - G_TIMEHS_VALUE(0, 1)); + return ticks_to_usecs(time_now_in_ticks + G_TIMEHS_VALUE(0, 2)); +} + +void __hw_clock_event_clear(void) +{ + /* one-shot, 32-bit, timer & interrupts disabled, 1:1 prescale */ + G_TIMEHS_CONTROL(0, 2) = 0x3; + /* Clear any pending interrupts */ + G_TIMEHS_INTCLR(0, 2) = 0x1; +} + +void __hw_clock_event_set(uint32_t deadline) +{ + uint32_t time_now_in_ticks; + + __hw_clock_event_clear(); + + /* How long from the current time to the deadline? */ + time_now_in_ticks = (0xffffffff - G_TIMEHS_VALUE(0, 1)); + G_TIMEHS_LOAD(0, 2) = usecs_to_ticks(deadline) - time_now_in_ticks; + + /* timer & interrupts enabled */ + G_TIMEHS_CONTROL(0, 2) = 0xa3; +} + +/* + * Handle event matches. It's lower priority than the HW rollover irq, so it + * will always be either before or after a rollover exception. + */ +void __hw_clock_event_irq(void) +{ + __hw_clock_event_clear(); + process_timers(0); } +DECLARE_IRQ(G_IRQNUM_TIMEHS0_TIMINT2, __hw_clock_event_irq, 2); uint32_t __hw_clock_source_read(void) { - return 0; + /* + * Return the current time in usecs. Since the counter counts down, + * we have to invert the value. + */ + return ticks_to_usecs(0xffffffff - G_TIMEHS_VALUE(0, 1)); } +void __hw_clock_source_set(uint32_t ts) +{ + G_TIMEHS_LOAD(0, 1) = 0xffffffff - usecs_to_ticks(ts); +} + +/* This handles rollover in the HW timer */ +void __hw_clock_source_irq(void) +{ + /* Clear the interrupt */ + G_TIMEHS_INTCLR(0, 1) = 0x1; + + /* 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); + } +} +DECLARE_IRQ(G_IRQNUM_TIMEHS0_TIMINT1, __hw_clock_source_irq, 1); + int __hw_clock_source_init(uint32_t start_t) { - return 0; + /* Set the reload and current value. */ + G_TIMEHS_BGLOAD(0, 1) = 0xffffffff; + G_TIMEHS_LOAD(0, 1) = 0xffffffff; + + /* HW Timer enabled, periodic, interrupt enabled, 32-bit, wrapping */ + G_TIMEHS_CONTROL(0, 1) = 0xe2; + /* Event timer disabled */ + __hw_clock_event_clear(); + + /* Account for the clock speed. */ + update_prescaler(); + + /* Clear any pending interrupts */ + G_TIMEHS_INTCLR(0, 1) = 0x1; + + /* Force the time to whatever we're told it is */ + __hw_clock_source_set(start_t); + + /* Here we go... */ + task_enable_irq(G_IRQNUM_TIMEHS0_TIMINT1); + task_enable_irq(G_IRQNUM_TIMEHS0_TIMINT2); + + /* Return the Event timer IRQ number (NOT the HW timer IRQ) */ + return G_IRQNUM_TIMEHS0_TIMINT2; } diff --git a/chip/g/registers.h b/chip/g/registers.h index a45d94914d..9a705f99b1 100644 --- a/chip/g/registers.h +++ b/chip/g/registers.h @@ -80,6 +80,51 @@ static inline int g_uart_addr(int ch, int offset) #define G_UART_FIFO(ch) G_UARTREG(ch, 0x0024) #define G_UART_RFIFO(ch) G_UARTREG(ch, 0x0028) + +/* + * High-speed timers. Two modules with two timers each; four timers total. + */ +#define G_TIMEHS0_BASE_ADDR 0x40570000 +#define G_TIMEHS1_BASE_ADDR 0x40580000 +#define G_TIMEHS_BASE_ADDR_SEP 0x00010000 +#define G_TIMEHSX_TIMER1_OFS 0x00 +#define G_TIMEHSX_TIMER2_OFS 0x20 +#define G_TIMEHSX_TIMER_OFS_SEP 0x20 +/* NOTE: module is 0-1, timer is 1-2 */ +static inline int g_timehs_addr(unsigned int module, unsigned int timer, + int offset) +{ + return G_TIMEHS0_BASE_ADDR + G_TIMEHS_BASE_ADDR_SEP * module + + G_TIMEHSX_TIMER1_OFS + G_TIMEHSX_TIMER_OFS_SEP * (timer - 1) + + offset; +} +/* Per-timer registers */ +#define G_TIMEHSREG(m, t, ofs) REG32(g_timehs_addr(m, t, ofs)) +#define G_TIMEHS_LOAD(m, t) G_TIMEHSREG(m, t, 0x0000) +#define G_TIMEHS_VALUE(m, t) G_TIMEHSREG(m, t, 0x0004) +#define G_TIMEHS_CONTROL(m, t) G_TIMEHSREG(m, t, 0x0008) +#define G_TIMEHS_INTCLR(m, t) G_TIMEHSREG(m, t, 0x000c) +#define G_TIMEHS_RIS(m, t) G_TIMEHSREG(m, t, 0x0010) +#define G_TIMEHS_MIS(m, t) G_TIMEHSREG(m, t, 0x0014) +#define G_TIMEHS_BGLOAD(m, t) G_TIMEHSREG(m, t, 0x0018) +/* These are only per-module */ +#define G_TIMEHS_ITCR(m) G_TIMEHSREG(m, 1, 0x0f00) +#define G_TIMEHS_ITOP(m) G_TIMEHSREG(m, 1, 0x0f04) +#define G_TIMEHS_PERIPHID4(m) G_TIMEHSREG(m, 1, 0x0fd0) +#define G_TIMEHS_PERIPHID5(m) G_TIMEHSREG(m, 1, 0x0fd4) +#define G_TIMEHS_PERIPHID6(m) G_TIMEHSREG(m, 1, 0x0fd8) +#define G_TIMEHS_PERIPHID7(m) G_TIMEHSREG(m, 1, 0x0fdc) +#define G_TIMEHS_PERIPHID0(m) G_TIMEHSREG(m, 1, 0x0fe0) +#define G_TIMEHS_PERIPHID1(m) G_TIMEHSREG(m, 1, 0x0fe4) +#define G_TIMEHS_PERIPHID2(m) G_TIMEHSREG(m, 1, 0x0fe8) +#define G_TIMEHS_PERIPHID3(m) G_TIMEHSREG(m, 1, 0x0fec) +#define G_TIMEHS_PCELLID0(m) G_TIMEHSREG(m, 1, 0x0ff0) +#define G_TIMEHS_PCELLID1(m) G_TIMEHSREG(m, 1, 0x0ff4) +#define G_TIMEHS_PCELLID2(m) G_TIMEHSREG(m, 1, 0x0ff8) +#define G_TIMEHS_PCELLID3(m) G_TIMEHSREG(m, 1, 0x0ffc) + + +/* Oscillator */ #define G_XO0_BASE_ADDR 0x40420000 #define G_XO_OSC_RC_CAL_RSTB REG32(G_XO0_BASE_ADDR + 0x0014) #define G_XO_OSC_RC_CAL_LOAD REG32(G_XO0_BASE_ADDR + 0x0018) |