blob: 2dc791adc3140ada090838c7b6794bf0cfd88e8a [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <autoconf.h>
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <sel4/sel4.h>
#include <sel4/arch/constants.h>
#include <camkes.h>
#include <camkes/irq.h>
#include <platsupport/time_manager.h>
#include <platsupport/local_time_manager.h>
#include <platsupport/irq.h>
#include <utils/util.h>
#include <sel4utils/sel4_zf_logif.h>
#include <simple/simple.h>
#include <camkes/io.h>
#include "plat.h"
/* ltimer for accessing timer devices */
static ltimer_t ltimer;
/* time manager for timeout multiplexing */
static time_manager_t time_manager;
/* declare the memory needed for the clients
* this field tracks which timeouts have triggered
* for a specific client */
uint32_t *client_state = NULL;
/* Prototype for this function is not generated by the camkes templates yet */
seL4_Word the_timer_get_sender_id();
void the_timer_emit(unsigned int);
int the_timer_largest_badge(void);
static ps_io_ops_t io_ops;
static inline uint64_t current_time_ns()
{
uint64_t time;
int error = ltimer_get_time(&ltimer, &time);
ZF_LOGF_IF(error, "Failed to get time");
return time;
}
static inline unsigned int get_time_token(int cid, int tid)
{
return (unsigned int) cid * timers_per_client + tid;
}
static int signal_client(uintptr_t token)
{
int cid = ((int) token) / timers_per_client;
int tid = ((int) token) % timers_per_client;
assert(client_state != NULL);
client_state[cid] |= BIT(tid);
the_timer_emit(cid + 1);
return 0;
}
void time_server_ltimer_handle(UNUSED void *empty_token, ltimer_event_t ltimer_event)
{
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock time server");
error = tm_update(&time_manager);
ZF_LOGF_IF(error, "Failed to update time manager");
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock time server");
}
static int _oneshot_relative(int cid, int tid, uint64_t ns)
{
if (tid >= timers_per_client || tid < 0) {
ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
return -1;
}
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock time server");
unsigned int id = get_time_token(cid, tid);
error = tm_register_rel_cb(&time_manager, ns, id, signal_client, (uintptr_t) id);
ZF_LOGF_IF(error, "Failed to set timeout");
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock time server");
return 0;
}
static int _oneshot_absolute(int cid, int tid, uint64_t ns)
{
if (tid >= timers_per_client || tid < 0) {
ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
return -1;
}
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock time server");
unsigned int token = get_time_token(cid, tid);
error = tm_register_abs_cb(&time_manager, ns, token, signal_client, (uintptr_t) token);
if (error == ETIME) {
signal_client(token);
error = 0;
}
ZF_LOGF_IF(error, "Failed to set timeout");
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock time server");
return 0;
}
static int _periodic(int cid, int tid, uint64_t ns)
{
if (tid >= timers_per_client || tid < 0) {
ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
return -1;
}
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock time server");
unsigned int token = get_time_token(cid, tid);
error = tm_register_periodic_cb(&time_manager, ns, 0, token, signal_client, (uintptr_t) token);
ZF_LOGF_IF(error, "Failed to set timeout");
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock time server");
return 0;
}
static int _stop(int cid, int tid)
{
if (tid >= timers_per_client || tid < 0) {
ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
return -1;
}
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock time server");
error = tm_deregister_cb(&time_manager, get_time_token(cid, tid));
ZF_LOGF_IF(error, "Failed to deregister callback");
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock time server");
return 0;
}
static unsigned int _completed(int cid)
{
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock time server");
assert(client_state != NULL);
unsigned int ret = client_state[cid];
client_state[cid] = 0;
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock time server");
return ret;
}
static uint64_t _time(int cid)
{
return current_time_ns();
}
/* substract 1 from the badge as we started counting badges at 1
* to avoid using the 0 badge */
int the_timer_oneshot_relative(int id, uint64_t ns)
{
return _oneshot_relative(the_timer_get_sender_id() - 1, id, ns);
}
int the_timer_oneshot_absolute(int id, uint64_t ns)
{
return _oneshot_absolute(the_timer_get_sender_id() - 1, id, ns);
}
int the_timer_periodic(int id, uint64_t ns)
{
return _periodic(the_timer_get_sender_id() - 1, id, ns);
}
int the_timer_stop(int id)
{
return _stop(the_timer_get_sender_id() - 1, id);
}
unsigned int the_timer_completed()
{
return _completed(the_timer_get_sender_id() - 1);
}
uint64_t the_timer_time()
{
return _time(the_timer_get_sender_id() - 1);
}
void post_init()
{
int error = time_server_lock();
ZF_LOGF_IF(error, "Failed to lock timer server");
error = camkes_io_ops(&io_ops);
ZF_LOGF_IF(error, "Failed to get camkes_io_ops");
error = ps_calloc(&(io_ops.malloc_ops), the_timer_largest_badge(), sizeof(*client_state), (void **) &client_state);
ZF_LOGF_IF(error, "Failed to allocate client state");
if (plat_pre_init) {
plat_pre_init();
}
error = ltimer_default_init(&ltimer, io_ops, time_server_ltimer_handle, NULL);
ZF_LOGF_IF(error, "Failed to init timer");
if (plat_post_init) {
plat_post_init(&ltimer);
}
int num_timers = timers_per_client * the_timer_largest_badge();
tm_init(&time_manager, &ltimer, &io_ops, num_timers);
for (unsigned int i = 0; i < num_timers; i++) {
error = tm_alloc_id_at(&time_manager, i);
ZF_LOGF_IF(error, "Failed to alloc id at %u\n", i);
}
error = time_server_unlock();
ZF_LOGF_IF(error, "Failed to unlock timer server");
set_putchar(putchar_putchar);
}