drivers/timers: Add clkdev drivers for goldfish.

This commit added clkdev drivers for goldfish.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
ouyangxiangzhen
2025-09-18 11:15:20 +08:00
committed by Xiang Xiao
parent 8cb0e654a3
commit 8f2f51e145
2 changed files with 117 additions and 111 deletions

View File

@@ -545,6 +545,7 @@ endif # TIMERS_CS2100CP
config GOLDFISH_TIMER
bool "Enable GOLDFISH_TIMER"
default n
select ONESHOT_COUNT
---help---
Goldfish timer is a virtual timer device that is used in the Android
emulator. It is used to provide a virtual timer to the guest OS.

View File

@@ -69,116 +69,32 @@ struct goldfish_timer_lowerhalf_s
};
/****************************************************************************
* Private Function Prototypes
* Inline Functions
****************************************************************************/
static int goldfish_timer_maxdelay(FAR struct oneshot_lowerhalf_s *lower,
FAR struct timespec *ts);
static int goldfish_timer_start(FAR struct oneshot_lowerhalf_s *lower,
FAR const struct timespec *ts);
static int goldfish_timer_cancel(FAR struct oneshot_lowerhalf_s *lower,
FAR struct timespec *ts);
static int goldfish_timer_current(FAR struct oneshot_lowerhalf_s *lower,
FAR struct timespec *ts);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct oneshot_operations_s g_goldfish_timer_ops =
static inline_function uint64_t goldfish_timer_get_nsec(uintptr_t base)
{
.max_delay = goldfish_timer_maxdelay,
.start = goldfish_timer_start,
.cancel = goldfish_timer_cancel,
.current = goldfish_timer_current,
};
uint32_t l32;
uint32_t h32;
l32 = getreg32(base + GOLDFISH_TIMER_TIME_LOW);
h32 = getreg32(base + GOLDFISH_TIMER_TIME_HIGH);
return ((uint64_t)h32 << 32) | l32;
}
static inline_function
void goldfish_timer_set_nsec(uintptr_t base, uint64_t expected)
{
putreg32(1, base + GOLDFISH_TIMER_IRQ_ENABLED);
putreg32(expected >> 32, base + GOLDFISH_TIMER_ALARM_HIGH);
putreg32(expected, base + GOLDFISH_TIMER_ALARM_LOW);
}
/****************************************************************************
* Private Functions
****************************************************************************/
static int goldfish_timer_maxdelay(FAR struct oneshot_lowerhalf_s *lower_,
FAR struct timespec *ts)
{
ts->tv_sec = UINT32_MAX;
ts->tv_nsec = UINT32_MAX;
return 0;
}
static int goldfish_timer_start(FAR struct oneshot_lowerhalf_s *lower_,
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);
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;
}
static int goldfish_timer_cancel(FAR struct oneshot_lowerhalf_s *lower_,
FAR struct timespec *ts)
{
struct goldfish_timer_lowerhalf_s *lower =
(struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
putreg32(0, lower->base + GOLDFISH_TIMER_IRQ_ENABLED);
putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_ALARM);
spin_unlock_irqrestore(&lower->lock, flags);
return 0;
}
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;
}
static int goldfish_timer_interrupt(int irq,
FAR void *context,
FAR void *arg)
@@ -202,6 +118,97 @@ static int goldfish_timer_interrupt(int irq,
return 0;
}
static clkcnt_t goldfish_timer_max_delay(struct oneshot_lowerhalf_s *lower)
{
return UINT64_MAX;
}
static clkcnt_t goldfish_timer_current(struct oneshot_lowerhalf_s *lower_)
{
FAR struct goldfish_timer_lowerhalf_s *lower =
(FAR struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
uint64_t nsec;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
nsec = goldfish_timer_get_nsec(lower->base);
spin_unlock_irqrestore(&lower->lock, flags);
return nsec;
}
static void goldfish_timer_start_absolute(struct oneshot_lowerhalf_s *lower_,
clkcnt_t expected)
{
FAR struct goldfish_timer_lowerhalf_s *lower =
(FAR struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
goldfish_timer_set_nsec(lower->base, expected);
spin_unlock_irqrestore(&lower->lock, flags);
}
static void goldfish_timer_start(struct oneshot_lowerhalf_s *lower_,
clkcnt_t delta)
{
FAR struct goldfish_timer_lowerhalf_s *lower =
(FAR struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
delta += goldfish_timer_get_nsec(lower->base);
goldfish_timer_set_nsec(lower->base, delta);
spin_unlock_irqrestore(&lower->lock, flags);
}
static void goldfish_timer_cancel(struct oneshot_lowerhalf_s *lower_)
{
struct goldfish_timer_lowerhalf_s *lower =
(struct goldfish_timer_lowerhalf_s *)lower_;
irqstate_t flags;
DEBUGASSERT(lower != NULL);
flags = spin_lock_irqsave(&lower->lock);
putreg32(0, lower->base + GOLDFISH_TIMER_IRQ_ENABLED);
putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_ALARM);
spin_unlock_irqrestore(&lower->lock, flags);
}
/****************************************************************************
* Private Data
****************************************************************************/
static const struct oneshot_operations_s g_goldfish_timer_ops =
{
.current = goldfish_timer_current,
.start = goldfish_timer_start,
.start_absolute = goldfish_timer_start_absolute,
.cancel = goldfish_timer_cancel,
.max_delay = goldfish_timer_max_delay,
};
static struct goldfish_timer_lowerhalf_s g_goldfish_timer_lowerhalf =
{
.lh.ops = &g_goldfish_timer_ops
};
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -209,24 +216,22 @@ static int goldfish_timer_interrupt(int irq,
FAR struct oneshot_lowerhalf_s *
goldfish_timer_initialize(uintptr_t base, int irq)
{
FAR struct goldfish_timer_lowerhalf_s *lower;
FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
lower = kmm_zalloc(sizeof(*lower));
if (lower == NULL)
{
return NULL;
}
lower->lh.ops = &g_goldfish_timer_ops;
lower->base = base;
spin_lock_init(&lower->lock);
/* Enable timer, but disable interrupt */
oneshot_count_init(&lower->lh, NSEC_PER_SEC);
putreg32(1, base + GOLDFISH_TIMER_CLEAR_ALARM);
putreg32(1, base + GOLDFISH_TIMER_CLEAR_INTERRUPT);
putreg32(0, base + GOLDFISH_TIMER_IRQ_ENABLED);
irq_attach(irq, goldfish_timer_interrupt, lower);
up_enable_irq(irq);
putreg32(0, base + GOLDFISH_TIMER_IRQ_ENABLED);
return (struct oneshot_lowerhalf_s *)lower;
}