blob: 456930abf6c318c5320c845be6cf8a25c9106b45 [file] [log] [blame]
/****************************************************************************
* arch/risc-v/src/litex/litex_tickless.c
*
* 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 <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include "chip.h"
#include "riscv_internal.h"
#include "litex.h"
#include "litex_clockconfig.h"
#include "hardware/litex_timer.h"
#if defined(CONFIG_SCHED_TICKLESS)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define LITEX_TICK_PER_SEC (CONFIG_LITEX_SYS_CORE_FREQ_HZ)
#define LITEX_TICK_PER_USEC (LITEX_TICK_PER_SEC / USEC_PER_SEC)
#define SEC_2_LITEX_TICK(s) ((s) * LITEX_TICK_PER_SEC)
#define USEC_2_LITEX_TICK(us) ((us) * LITEX_TICK_PER_USEC)
#define NSEC_2_LITEX_TICK(nsec) (((nsec) * LITEX_TICK_PER_USEC) / NSEC_PER_USEC)
#define LITEX_TICK_2_SEC(tick) ((tick) / LITEX_TICK_PER_SEC)
#define LITEX_TICK_2_USEC(tick) ((tick) / LITEX_TICK_PER_USEC)
#define LITEX_TICK_2_NSEC(tick) ((tick) * 1000 / LITEX_TICK_PER_USEC)
static bool g_timer_started; /* Whether an interval timer is being started */
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: up_timer_expire
*
* Description:
* Called as the IRQ handler for timer expiration.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static int up_timer_expire(int irq, void *regs, void *arg)
{
g_timer_started = false;
putreg32(1 << LITEX_TIMER0_TIMEOUT_EV_OFFSET, LITEX_TIMER0_EV_PENDING);
nxsched_timer_expiration();
return OK;
}
/****************************************************************************
* Name: litex_get_uptime
*
* Description:
* Get the number of ticks remaining on the counter.
*
* Input Parameters:
* None
*
****************************************************************************/
void litex_get_uptime(uint64_t * value)
{
putreg32(1, LITEX_TIMER0_UPTIME_LATCH);
*value = (uint64_t)getreg32(LITEX_TIMER0_UPTIME_U) << 32;
*value |= getreg32(LITEX_TIMER0_UPTIME_L);
}
/****************************************************************************
* Name: litex_get_remaining
*
* Description:
* Get the number of ticks remaining on the counter.
*
* Input Parameters:
* None
*
* Returned Value:
* The count remaining on the timer.
*
****************************************************************************/
uint32_t litex_get_remaining()
{
putreg32(1, LITEX_TIMER0_UPDATE_VALUE);
return getreg32(LITEX_TIMER0_VALUE);
}
/****************************************************************************
* Name: litex_timer_cancel
*
* Input Parameters:
* None
*
* Description:
* Cancel any running one-shot timer.
*
****************************************************************************/
void litex_timer_cancel()
{
g_timer_started = false;
putreg32(0, LITEX_TIMER0_EN);
putreg32(1 << LITEX_TIMER0_TIMEOUT_EV_OFFSET, LITEX_TIMER0_EV_PENDING);
}
/****************************************************************************
* Name: litex_timer_oneshot
*
* Description:
* Start timer0 in one-shot countdown mode.
*
* Input Parameters:
* ticks - The tick count to use for the one-shot timer.
*
****************************************************************************/
void litex_timer_oneshot(const uint32_t ticks)
{
g_timer_started = true;
putreg32(ticks, LITEX_TIMER0_LOAD);
putreg32(LITEX_TIMER0_ENABLE_BIT, LITEX_TIMER0_EN);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_timer_gettime
*
* Description:
* Return the elapsed time since power-up (or, more correctly, since
* up_timer_initialize() was called). This function is functionally
* equivalent to:
*
* int clock_gettime(clockid_t clockid, struct timespec *ts);
*
* when clockid is CLOCK_MONOTONIC.
*
* This function provides the basis for reporting the current time and
* also is used to eliminate error build-up from small errors in interval
* time calculations.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* ts - Provides the location in which to return the up-time.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
* Assumptions:
* Called from the normal tasking context. The implementation must
* provide whatever mutual exclusion is necessary for correct operation.
* This can include disabling interrupts in order to assure atomic register
* operations.
*
****************************************************************************/
int up_timer_gettime(struct timespec *ts)
{
uint64_t ticks;
litex_get_uptime(&ticks);
ts->tv_sec = LITEX_TICK_2_SEC(ticks);
ts->tv_nsec = LITEX_TICK_2_NSEC(ticks % LITEX_TICK_PER_SEC);
return OK;
}
/****************************************************************************
* Name: up_timer_cancel
*
* Description:
* Cancel the interval timer and return the time remaining on the timer.
* These two steps need to be as nearly atomic as possible.
* nxsched_timer_expiration() will not be called unless the timer is
* restarted with up_timer_start().
*
* If, as a race condition, the timer has already expired when this
* function is called, then that pending interrupt must be cleared so
* that up_timer_start() and the remaining time of zero should be
* returned.
*
* NOTE: This function may execute at a high rate with no timer running (as
* when pre-emption is enabled and disabled).
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* ts - Location to return the remaining time. Zero should be returned
* if the timer is not active. ts may be zero in which case the
* time remaining is not returned.
*
* Returned Value:
* Zero (OK) is returned on success. A call to up_timer_cancel() when
* the timer is not active should also return success; a negated errno
* value is returned on any failure.
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
* level. Interrupts may need to be disabled internally to assure
* non-reentrancy.
*
****************************************************************************/
int up_timer_cancel(struct timespec *ts)
{
uint64_t alarm_value;
irqstate_t flags;
flags = enter_critical_section();
if (ts != NULL)
{
if (g_timer_started == false)
{
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
else
{
litex_timer_cancel();
alarm_value = litex_get_remaining();
ts->tv_sec = LITEX_TICK_2_SEC(alarm_value);
ts->tv_nsec = LITEX_TICK_2_NSEC(alarm_value % LITEX_TICK_PER_SEC);
}
}
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: up_timer_start
*
* Description:
* Start the interval timer. nxsched_timer_expiration() will be
* called at the completion of the timeout (unless up_timer_cancel
* is called to stop the timing.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* ts - Provides the time interval until nxsched_timer_expiration() is
* called.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
* level. Interrupts may need to be disabled internally to assure
* non-reentrancy.
*
****************************************************************************/
int up_timer_start(const struct timespec *ts)
{
uint64_t cpu_ticks;
irqstate_t flags;
flags = enter_critical_section();
litex_timer_cancel();
cpu_ticks = SEC_2_LITEX_TICK((uint64_t)ts->tv_sec) +
NSEC_2_LITEX_TICK((uint64_t)ts->tv_nsec);
DEBUGASSERT(cpu_ticks <= UINT32_MAX);
litex_timer_oneshot(cpu_ticks);
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Function: up_timer_initialize
*
* Description:
* This function is called during start-up to initialize
* the timer interrupt.
*
****************************************************************************/
void up_timer_initialize(void)
{
/* Cancel any configuration that has been done in the bios or openSBI */
litex_timer_cancel();
/* Write zero to reload to ensure one-shot mode */
putreg32(0, LITEX_TIMER0_RELOAD);
putreg32(1 << LITEX_TIMER0_TIMEOUT_EV_OFFSET, LITEX_TIMER0_EV_ENABLE);
irq_attach(LITEX_IRQ_TIMER0, up_timer_expire, NULL);
up_enable_irq(LITEX_IRQ_TIMER0);
}
#endif /* CONFIG_SCHED_TICKLESS */