blob: 1bbc3a87039a0466f62a1af74e3a4d062ba1791a [file] [log] [blame]
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "common.h"
#include "hooks.h"
#include "hwtimer.h"
#include "init_chip.h"
#include "registers.h"
#include "task.h"
#include "util.h"
/* The frequency of timerls is 256k so there are about 4usec/tick */
#define USEC_PER_TICK 4
/*
* Scale the maximum number of ticks so that it will only count up to the
* equivalent of 0xffffffff usecs.
*/
#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 ticks * USEC_PER_TICK;
}
static inline uint32_t usec_to_ticks(uint32_t next_evt_us)
{
return next_evt_us / USEC_PER_TICK;
}
uint32_t __hw_clock_event_get(void)
{
/* At what time will the next event fire? */
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 */
GWRITE_FIELD(TIMELS, EVENT(CONTROL), ENABLE, 0);
/* Disable interrupts */
GWRITE(TIMELS, EVENT(IER), 0);
/* Clear any pending interrupts */
GWRITE(TIMELS, EVENT(WAKEUP_ACK), 1);
GWRITE(TIMELS, EVENT(IAR), 1);
}
void __hw_clock_event_set(uint32_t deadline)
{
uint32_t event_time;
__hw_clock_event_clear();
/* How long from the current time to the deadline? */
event_time = (deadline - __hw_clock_source_read());
/* 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);
}
/*
* Handle event matches. It's priority matches the HW rollover irq to prevent
* a race condition that could lead to a watchdog timeout if preempted after
* the get_time() call in process_timers().
*/
void __hw_clock_event_irq(void)
{
__hw_clock_event_clear();
process_timers(0);
}
DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT1, __hw_clock_event_irq, 1);
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(TIMELS_MAX - GREG32(TIMELS, SOURCE(VALUE)));
}
void __hw_clock_source_set(uint32_t ts)
{
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 */
GWRITE(TIMELS, SOURCE(WAKEUP_ACK), 1);
GWRITE(TIMELS, SOURCE(IAR), 1);
/* Reset the load value */
GREG32(TIMELS, SOURCE(LOAD)) = TIMELS_MAX;
process_timers(1);
}
DECLARE_IRQ(GC_IRQNUM_TIMELS0_TIMINT0, __hw_clock_source_irq, 1);
int __hw_clock_source_init(uint32_t start_t)
{
if (runlevel_is_high()) {
/* 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();
/* Clear any pending interrupts */
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_TIMELS0_TIMINT0);
task_enable_irq(GC_IRQNUM_TIMELS0_TIMINT1);
/* Return the Event timer IRQ number (NOT the HW timer IRQ) */
return GC_IRQNUM_TIMELS0_TIMINT1;
}