mirror of
https://github.com/apache/nuttx.git
synced 2025-12-12 22:05:54 +08:00
This commit simplified goldfish timer driver for better performance. Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
221 lines
7.9 KiB
C
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;
|
|
}
|