Files
nuttx/drivers/timers/goldfish_timer.c
ouyangxiangzhen 6f17f1ce81 driver/timers: Simplify Goldfish timer driver.
This commit simplified goldfish timer driver for better performance.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
2025-11-24 19:43:07 +08:00

221 lines
7.9 KiB
C

/****************************************************************************
* drivers/timers/goldfish_timer.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/timers/oneshot.h>
#include <nuttx/spinlock.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef getreg32
#define getreg32(a) (*(volatile uint32_t *)(a))
#endif
#ifndef putreg32
#define putreg32(v,a) (*(volatile uint32_t *)(a) = (v))
#endif
#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. */
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure provides the private representation of the "lower-half"
* driver state structure. This structure must be cast-compatible with the
* oneshot_lowerhalf_s structure.
*/
struct goldfish_timer_lowerhalf_s
{
struct oneshot_lowerhalf_s lh; /* Lower half operations */
uintptr_t base; /* Base address of registers */
spinlock_t lock; /* Lock for setting timer */
spinlock_t read_lock; /* Lock for reading time */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static clkcnt_t goldfish_timer_max_delay(struct oneshot_lowerhalf_s *lower);
static clkcnt_t goldfish_timer_current(struct oneshot_lowerhalf_s *lower_);
static void goldfish_timer_start_absolute(struct oneshot_lowerhalf_s *lower_,
clkcnt_t expected);
static void goldfish_timer_start(struct oneshot_lowerhalf_s *lower_,
clkcnt_t delta);
static void goldfish_timer_cancel(struct oneshot_lowerhalf_s *lower_);
/****************************************************************************
* 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
};
/****************************************************************************
* Inline Functions
****************************************************************************/
static inline_function uint64_t goldfish_timer_get_nsec(uintptr_t base)
{
uint32_t l32 = getreg32(base + GOLDFISH_TIMER_TIME_LOW);
uint32_t 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(expected >> 32, base + GOLDFISH_TIMER_ALARM_HIGH);
putreg32(expected, base + GOLDFISH_TIMER_ALARM_LOW);
}
/****************************************************************************
* Private Functions
****************************************************************************/
static int goldfish_timer_interrupt(int irq, FAR void *context,
FAR void *arg)
{
FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
/* Only one core can handle the IRQ, so lock is not required. */
putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_INTERRUPT);
/* Then perform the callback */
oneshot_process_callback(&lower->lh);
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_)
{
/* Do not remove the read lock here, because the access to the
* GOLDFISH_TIMER_TIME_LOW will update the whole GOLDFISH_TIMER_TIME.
*/
FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
irqstate_t flags = spin_lock_irqsave(&lower->read_lock);
uint64_t nsec = goldfish_timer_get_nsec(lower->base);
spin_unlock_irqrestore(&lower->read_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 = &g_goldfish_timer_lowerhalf;
irqstate_t 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 = &g_goldfish_timer_lowerhalf;
irqstate_t 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_)
{
FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
irqstate_t flags = spin_lock_irqsave(&lower->lock);
/* Disable the timer and clear the interrupt. */
putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_ALARM);
spin_unlock_irqrestore(&lower->lock, flags);
}
/****************************************************************************
* Public Functions
****************************************************************************/
FAR struct oneshot_lowerhalf_s *
goldfish_timer_initialize(uintptr_t base, int irq)
{
FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
lower->base = base;
spin_lock_init(&lower->lock);
spin_lock_init(&lower->read_lock);
oneshot_count_init(&lower->lh, NSEC_PER_SEC);
/* Reset the timer registers. */
putreg32(1, base + GOLDFISH_TIMER_CLEAR_ALARM);
putreg32(1, base + GOLDFISH_TIMER_CLEAR_INTERRUPT);
putreg32(1, base + GOLDFISH_TIMER_IRQ_ENABLED);
irq_attach(irq, goldfish_timer_interrupt, lower);
up_enable_irq(irq);
return (struct oneshot_lowerhalf_s *)lower;
}