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>
This commit is contained in:
Bill Richardson
2014-11-13 14:02:30 -08:00
committed by chrome-internal-fetch
parent 3ddd906b92
commit 9157dd93da
2 changed files with 186 additions and 4 deletions

View File

@@ -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;
}

View File

@@ -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)