mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-11 02:15:14 +00:00
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:
committed by
chrome-internal-fetch
parent
3ddd906b92
commit
9157dd93da
145
chip/g/hwtimer.c
145
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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user