blob: c99324d9d499652afc1610fe19f34888da900dac [file] [log] [blame]
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <stdio.h>
#include <assert.h>
#include <utils/util.h>
#include <utils/time.h>
#include <platsupport/ltimer.h>
#include <platsupport/plat/sp804.h>
#include <platsupport/io.h>
#include "../../ltimer.h"
/*
* We use two sp804 timers: one to keep track of an absolute time, the other for timeouts.
*/
typedef struct {
/* fvp sp804 have 2 timers per frame, we just use one per each */
sp804_t sp804_timeout;
sp804_t sp804_timestamp;
ps_io_ops_t ops;
} fvp_ltimer_t;
static int get_time(void *data, uint64_t *time)
{
fvp_ltimer_t *fvp_ltimer = data;
assert(data != NULL);
assert(time != NULL);
*time = sp804_get_time(&fvp_ltimer->sp804_timestamp);
return 0;
}
int set_timeout(void *data, uint64_t ns, timeout_type_t type)
{
if (type == TIMEOUT_ABSOLUTE) {
uint64_t time;
int error = get_time(data, &time);
if (error) {
return error;
}
if (time > ns) {
return ETIME;
}
ns -= time;
}
fvp_ltimer_t *fvp_ltimer = data;
return sp804_set_timeout(&fvp_ltimer->sp804_timeout, ns, type == TIMEOUT_PERIODIC, true);
}
static int reset(void *data)
{
fvp_ltimer_t *fvp_ltimer = data;
/* restart the rtc */
sp804_stop(&fvp_ltimer->sp804_timeout);
sp804_start(&fvp_ltimer->sp804_timeout);
return 0;
}
static void destroy(void *data)
{
assert(data != NULL);
fvp_ltimer_t *fvp_ltimer = data;
sp804_destroy(&fvp_ltimer->sp804_timeout);
sp804_destroy(&fvp_ltimer->sp804_timestamp);
ps_free(&fvp_ltimer->ops.malloc_ops, sizeof(fvp_ltimer_t), fvp_ltimer);
}
int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token)
{
int error;
if (ltimer == NULL) {
ZF_LOGE("ltimer cannot be NULL");
return EINVAL;
}
error = create_ltimer_simple(
ltimer, ops, sizeof(fvp_ltimer_t),
get_time, set_timeout, reset, destroy
);
if (error) {
ZF_LOGE("Failed to create ltimer simple");
return error;
}
fvp_ltimer_t *fvp_ltimer = ltimer->data;
fvp_ltimer->ops = ops;
/* set up an SP804 for timeouts */
sp804_config_t sp804_config = {
.fdt_path = SP804_TIMER1_PATH,
.user_cb_fn = callback,
.user_cb_token = callback_token,
.user_cb_event = LTIMER_TIMEOUT_EVENT
};
error = sp804_init(&fvp_ltimer->sp804_timeout, ops, sp804_config);
if (error) {
ZF_LOGE("Failed to init timeout timer");
destroy(&fvp_ltimer);
return error;
}
error = sp804_start(&fvp_ltimer->sp804_timeout);
if (error) {
ZF_LOGE("Failed to start timeout timer");
destroy(&fvp_ltimer);
return error;
}
/* another for timestamps */
sp804_config.fdt_path = SP804_TIMER2_PATH;
sp804_config.user_cb_event = LTIMER_OVERFLOW_EVENT;
error = sp804_init(&fvp_ltimer->sp804_timestamp, ops, sp804_config);
if (error) {
ZF_LOGE("Failed to init timestamp timer");
destroy(&fvp_ltimer);
return error;
}
error = sp804_start(&fvp_ltimer->sp804_timestamp);
if (error) {
ZF_LOGE("Failed to start timestamp timer");
destroy(&fvp_ltimer);
return error;
}
error = sp804_set_timeout_ticks(&fvp_ltimer->sp804_timestamp, UINT32_MAX, true, true);
if (error) {
ZF_LOGE("Failed to set timeout ticks for timer");
destroy(&fvp_ltimer);
return error;
}
return 0;
}
/* This function is intended to be deleted,
* this is just left here for now so that stuff can compile */
int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops)
{
ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid");
return EINVAL;
}