mirror of
https://github.com/apache/nuttx.git
synced 2025-12-13 15:12:38 +08:00
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:
committed by
Xiang Xiao
parent
8cb0e654a3
commit
8f2f51e145
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user