GOLDFISH TIMER
Introduction
This device is used to return the current host time as a high-precision signed 64-bit nanosecond value to the kernel, with the starting point being an arbitrary loose reference point. The value should correspond to the QEMU “vm_clock”; that is, it should not be updated when the emulated system is not running, hence it cannot be directly based on the host clock.
Timer Registers
#define GOLDFISH_TIMER_TIME_LOW 0x0 /* Get current time, then return low-order 32-bits. */
#define GOLDFISH_TIMER_TIME_HIGH 0x4 /* Return high 32-bits from previous TIME_LOW read. */
#define GOLDFISH_TIMER_ALARM_LOW 0x8 /* Set low 32-bit value of alarm, then arm it. */
#define GOLDFISH_TIMER_ALARM_HIGH 0xc /* Set high 32-bit value of alarm. */
#define GOLDFISH_TIMER_IRQ_ENABLED 0x10 /* Enable interrupts. */
#define GOLDFISH_TIMER_CLEAR_ALARM 0x14 /* Clear alarm. */
#define GOLDFISH_TIMER_ALARM_STATUS 0x18 /* Return 1 if alarm is armed, 0 if not. */
#define GOLDFISH_TIMER_CLEAR_INTERRUPT 0x1c /* Clear interrupt. */
Timer Read
To read the current time, the kernel must execute an IO_READ(TIME_LOW), which returns an unsigned 32-bit value, followed by an IO_READ(TIME_HIGH), which returns a signed 32-bit value corresponding to the high half of the complete value.
static int goldfish_timer_current(FAR struct oneshot_lowerhalf_s *lower_,
FAR struct timespec *ts)
{
FAR struct goldfish_timer_lowerhalf_s *lower =
(FAR struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint32_t l32;
uint32_t h32;
uint64_t nsec;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
l32 = getreg32(lower->base + GOLDFISH_TIMER_TIME_LOW);
h32 = getreg32(lower->base + GOLDFISH_TIMER_TIME_HIGH);
nsec = ((uint64_t)h32 << 32) | l32;
ts->tv_sec = nsec / NSEC_PER_SEC;
ts->tv_nsec = nsec % NSEC_PER_SEC;
spin_unlock_irqrestore(&lower->lock, flags);
return 0;
}
Timer Set Alarm
This device can also be used to set an alarm, as follows:
IO_WRITE(ALARM_HIGH, <high-value>) // Must be executed first.
IO_WRITE(ALARM_LOW, <low-value>) // Must be executed second.
When the corresponding value is reached, the device will raise its IRQ. Note that if the alarm value is already older than the current time, the IRQ will be triggered immediately after the second IO_WRITE().
static int goldfish_timer_start(FAR struct oneshot_lowerhalf_s *lower_,
FAR oneshot_callback_t callback,
FAR void *arg,
FAR const struct timespec *ts)
{
FAR struct goldfish_timer_lowerhalf_s *lower =
(FAR struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint64_t nsec;
uint32_t l32;
uint32_t h32;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
lower->callback = callback;
lower->arg = arg;
nsec = ts->tv_sec * 1000000000 + ts->tv_nsec;
l32 = getreg32(lower->base + GOLDFISH_TIMER_TIME_LOW);
h32 = getreg32(lower->base + GOLDFISH_TIMER_TIME_HIGH);
nsec += ((uint64_t)h32 << 32) | l32;
putreg32(1, lower->base + GOLDFISH_TIMER_IRQ_ENABLED);
putreg32(nsec >> 32, lower->base + GOLDFISH_TIMER_ALARM_HIGH);
putreg32(nsec, lower->base + GOLDFISH_TIMER_ALARM_LOW);
spin_unlock_irqrestore(&lower->lock, flags);
return 0;
}
Timer Interrupt
IO_WRITE(CLEAR_INTERRUPT, <any>) can be used to lower the IRQ level after the kernel has processed the alarm. IO_WRITE(CLEAR_ALARM, <any>) can be used to disarm the current alarm (if one exists).
Note: Currently, the alarm is used only on ARM-based systems. On MIPS-based systems, only TIME_LOW / TIME_HIGH are used.
static int goldfish_timer_interrupt(int irq,
FAR void *context,
FAR void *arg)
{
FAR struct goldfish_timer_lowerhalf_s *lower = arg;
oneshot_callback_t callback = NULL;
irqstate_t flags;
void *cbarg;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_ALARM);
if (lower->callback != NULL)
{
callback = lower->callback;
cbarg = lower->arg;
lower->callback = NULL;
lower->arg = NULL;
}
spin_unlock_irqrestore(&lower->lock, flags);
/* Then perform the callback */
if (callback)
{
callback(&lower->lh, cbarg);
}
return 0;
}