blob: 6525412ef03ae4d9160c2f6c6816ebc682d3fcb1 [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <platsupport/io.h>
#include <platsupport/pmem.h>
#include <platsupport/irq.h>
/**
* This file provides the interface for an OS independant consisent timer interface.
*
* Implementations of this interface vary per platform - for some platforms,
* a single timer driver will back the implementation, for others, there may be many.
*/
typedef enum {
TIMEOUT_PERIODIC,
TIMEOUT_ABSOLUTE,
TIMEOUT_RELATIVE
} timeout_type_t;
typedef enum {
LTIMER_TIMEOUT_EVENT,
LTIMER_OVERFLOW_EVENT
} ltimer_event_t;
/*
* Type signature of the callback function that can be accepted by the ltimer.
* The callbacks are invoked when an event occurs. These events are described
* by the ltimer_event_t type.
*
* The callbacks are expected to be reentrant with regards to the ltimer state.
* More specifically, the ltimer interface cannot guarantee that the internal
* state will be race-safe if two different callbacks invoke the ltimer API
* functions concurrently.
*/
typedef void (*ltimer_callback_fn_t)(void *token, ltimer_event_t event);
/* logical timers are the interface used by the timer manager to multiplex
* timer requests from clients - only one timeout can be registered at a time.
* logical timers may be backed by several timer drivers to implement the
* functionality
*/
typedef struct ltimer {
/*
* Get the number of irqs this ltimer requires to be handled to function.
*
* @param data for the logical timer to use
* @return the number of irqs this timer needs.
*/
size_t (*get_num_irqs)(void *data);
/*
* Get the nth irq number.
*
* @param data for the logical timer to use
* @param n index of the irq, < get_num_irqs
* @param[out] irq variable to read the irq number into
* @return 0 on success, errno on error.
*/
int (*get_nth_irq)(void *data, size_t n, ps_irq_t *irq);
/* Get the number of phyiscal memory regions needed by this ltimer
*
* @param data for the logical timer to use
* @return the number of pmem regions this timer needs.
*/
size_t (*get_num_pmems)(void *data);
/*
* Populate a region structure with details of the nth pmem region this timer requires.
*
* @param data for the logical timer to use
* @param n index of the pmem_region, < get_num_pmems
* @return 0 on success, errno on error.
*/
int (*get_nth_pmem)(void *data, size_t n, pmem_region_t *region);
/*
* Read the current time in nanoseconds. Precision depends on the implementation, but
* the value is guaranteed to be monotonically increasing and at least millisecond accurate.
*
* @param data for the logical timer to use
* @param[out] time variable to read the time value into
* @return 0 on success, errno on error.
*/
int (*get_time)(void *data, uint64_t *time);
/*
* Get the precision of this time returned by get_time. i.e if the timer is
* millisecond precise return NS_IN_US.
*
* @param data for the logical timer to use
* @param[out] resolution variable to read the resoltion value into
* @return 0 on success, errno on error.
*/
int (*get_resolution)(void *data, uint64_t *resolution);
/*
* Set an irq to come in at a specific time.
*
* IRQs may come in earlier than requested due to implementation details.
*
* @param data for the logical timer to use
* @param ns ns value (depends on timer type)
* @param type type of timeout
* @return 0 on success, errno on error.
*/
int (*set_timeout)(void *data, uint64_t ns, timeout_type_t type);
/*
* Reset the timer into a state similar to what it was when first initialized. This
* should cancel any outstanding timeouts etc. It may or may not cause the monotonic
* upcounter to change (by resetting to 0 or otherwise)
*
* @param data for the logical timer to use
* @return 0 on success, errno on error.
*/
int (*reset)(void *data);
/* Destroy an ltimer, freeing any resources and turning off devices */
void (*destroy)(void *data);
/* data for the implementation to use */
void *data;
} ltimer_t;
/* Logical timer helper functions */
static inline int ltimer_get_resolution(ltimer_t *timer, uint64_t *resolution)
{
if (!timer) {
ZF_LOGE("Logical timer invalid!");
return EINVAL;
}
if (!resolution) {
ZF_LOGE("time argument cannot be NULL");
return EINVAL;
}
if (timer->get_resolution == NULL) {
ZF_LOGE("not implemented");
return ENOSYS;
}
return timer->get_resolution(timer->data, resolution);
}
static inline int ltimer_set_timeout(ltimer_t *timer, uint64_t nanoseconds, timeout_type_t type)
{
if (!timer) {
ZF_LOGE("Logical timer invalid!");
return EINVAL;
}
if (timer->set_timeout == NULL) {
ZF_LOGE("not implemented");
return ENOSYS;
}
switch (type) {
case TIMEOUT_ABSOLUTE:
case TIMEOUT_PERIODIC:
case TIMEOUT_RELATIVE:
break;
default:
ZF_LOGE("Invalid timer type");
return EINVAL;
}
return timer->set_timeout(timer->data, nanoseconds, type);
}
static inline size_t ltimer_get_num_irqs(ltimer_t *timer)
{
if (timer->get_num_irqs == NULL) {
/* assume no irqs for this timer */
return 0;
}
return timer->get_num_irqs(timer->data);
}
static inline int ltimer_get_nth_irq(ltimer_t *timer, size_t n, ps_irq_t *irq)
{
if (!timer || !irq) {
ZF_LOGE("Arguments cannot be null");
return EINVAL;
}
if (!timer->get_nth_irq || !timer->get_num_irqs) {
ZF_LOGE("not implemented");
return ENOSYS;
}
size_t nirqs = ltimer_get_num_irqs(timer);
if (n >= nirqs) {
ZF_LOGD("n invalid");
return EINVAL;
}
return timer->get_nth_irq(timer->data, n, irq);
}
static inline size_t ltimer_get_num_pmems(ltimer_t *timer)
{
if (timer->get_num_pmems == NULL) {
/* assume no physical memory required for this ltimer */
return 0;
}
return timer->get_num_pmems(timer->data);
}
static inline int ltimer_get_nth_pmem(ltimer_t *timer, size_t n, pmem_region_t *pmem)
{
if (!timer || !pmem) {
ZF_LOGE("Arguments cannot be null");
return EINVAL;
}
if (!timer->get_nth_pmem || !timer->get_num_pmems) {
ZF_LOGE("not implemented");
return ENOSYS;
}
size_t npmems = ltimer_get_num_pmems(timer);
if (n >= npmems) {
ZF_LOGD("n invalid");
return EINVAL;
}
return timer->get_nth_pmem(timer->data, n, pmem);
}
static inline int ltimer_get_time(ltimer_t *timer, uint64_t *time)
{
if (!timer) {
ZF_LOGE("Logical timer invalid!");
return EINVAL;
}
if (!time) {
ZF_LOGE("time argument cannot be NULL");
return EINVAL;
}
if (timer->get_time == NULL) {
ZF_LOGE("get_time not implemented");
return ENOSYS;
}
return timer->get_time(timer->data, time);
}
static inline int ltimer_reset(ltimer_t *timer)
{
if (!timer) {
ZF_LOGE("Logical timer invalid!");
return EINVAL;
}
if (timer->reset == NULL) {
ZF_LOGE("not implemented");
return ENOSYS;
}
return timer->reset(timer->data);
}
static inline void ltimer_destroy(ltimer_t *timer)
{
if (!timer || !timer->destroy) {
ZF_LOGW("nothing to destroy");
return;
}
return timer->destroy(timer->data);
}
/* Spinning delay functions */
static inline void ltimer_ns_delay(ltimer_t *timer, uint64_t nanoseconds) {
uint64_t start, end;
int error = ltimer_get_time(timer, &start);
/* spin */
for (int i = 0; !error; i++) {
error = ltimer_get_time(timer, &end);
if (end - start >= nanoseconds) {
break;
} else if (i % 1000 == 0 && start == end) {
ZF_LOGD("Time doesn't appear to be changing");
}
}
}
static inline void ltimer_s_delay(ltimer_t *timer, uint64_t seconds) {
ltimer_ns_delay(timer, seconds * NS_IN_S);
}
static inline void ltimer_ms_delay(ltimer_t *timer, uint64_t milliseconds) {
ltimer_ns_delay(timer, milliseconds * NS_IN_MS);
}
static inline void ltimer_us_delay(ltimer_t *timer, uint64_t microseconds) {
ltimer_ns_delay(timer, microseconds * NS_IN_US);
}
/*
* default init function -> platforms may provide multiple ltimers, but each
* must have a default
*
* The callback functions will be invoked every single time an event described
* by the 'ltimer_event_t' type occurs. A reminder that care should be taken
* with calling the ltimer functions inside the callback, the ltimer interface
* functions are not reentrant.
*/
int ltimer_default_init(ltimer_t *timer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token);
/* initialise the subset of functions required to get
* the resources this ltimer needs without initialising the actual timer
* drivers*/
int ltimer_default_describe(ltimer_t *timer, ps_io_ops_t ops);