blob: 83f132d1309d53b202f8064814b7a5ff749c82dc [file] [log] [blame]
/*
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
* Copyright (C) 2021, Hensoldt Cyber GmbH
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <errno.h>
#include <stdlib.h>
#include <utils/util.h>
#include "../../arch/arm/clock.h"
#include <platsupport/timer.h>
#include <platsupport/plat/system_timer.h>
/*
* The system timer on the BCM2711 does not differ from the system timer
* on older BCM283x platforms. Thus, system timer settings were copied from
* the bcm2837 platform port.
* The system timer on the BCM283[5-7] is fairly simple in its nature.
* It has a 64-bit free-running counter and 4 compare registers. When
* the lower 32 bits of the free-running counter match one of the
* compare registers, an associated IRQ is generated and an associated
* control bit is set in the control register.
*/
int system_timer_init(system_timer_t *timer, system_timer_config_t config)
{
if (timer == NULL || config.vaddr == NULL) {
return EINVAL;
}
timer->regs = config.vaddr;
return 0;
}
uint64_t system_timer_get_time(system_timer_t *timer)
{
if (timer == NULL) {
return EINVAL;
}
uint64_t initial_high = timer->regs->counter_high;
uint64_t low = timer->regs->counter_low;
uint64_t high = timer->regs->counter_high;
if (high != initial_high) {
/* get low again if high has ticked over. */
low = timer->regs->counter_low;
}
uint64_t ticks = (high << 32) | low;
uint64_t time = ticks * SYSTEM_TIMER_NS_PER_TICK;
return time;
}
int system_timer_set_timeout(system_timer_t *timer, uint64_t ns)
{
if (timer == NULL) {
return EINVAL;
}
/* Can only set a timeout within the next 2^32 microseconds. */
uint64_t time = system_timer_get_time(timer);
uint64_t ticks = time / SYSTEM_TIMER_NS_PER_TICK;
uint64_t timeout_ticks = ns / SYSTEM_TIMER_NS_PER_TICK;
if (timeout_ticks < ticks) {
ZF_LOGE("Timeout in the past\n");
return ETIME;
} else if ((timeout_ticks - ticks) > UINT32_MAX) {
ZF_LOGE("Timeout too far in the future\n");
return ETIME;
}
/* Clear any existing interrupt. */
timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
uint32_t timeout = timeout_ticks & MASK(32);
timer->regs->compare[SYSTEM_TIMER_MATCH] = timeout;
time = system_timer_get_time(timer);
if (time >= ns && !(timer->regs->ctrl & BIT(SYSTEM_TIMER_MATCH))) {
timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
ZF_LOGE("Timeout missed\n");
return ETIME;
}
return 0;
}
int system_timer_handle_irq(system_timer_t *timer)
{
if (timer == NULL) {
return EINVAL;
}
timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
return 0;
}
int system_timer_reset(system_timer_t *timer)
{
if (timer == NULL) {
return EINVAL;
}
/* Just clear the one timer that is used. */
timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
return 0;
}