blob: b9f8562f58330e8634c802ae7b013d207afccc7e [file] [log] [blame]
/*
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <utils/util.h>
#include <platsupport/timer.h>
#include <platsupport/fdt.h>
#include <platsupport/plat/timer.h>
#include <utils/frequency.h>
#include "../../ltimer.h"
#define USER_MODE BIT(1)
#define UNMASKED_INT BIT(2)
#define TCLR_STARTTIMER BIT(0)
#define TISR_IRQ_CLEAR BIT(0)
//debug method
static void print_regs(rk_t *rk)
{
printf("load_count0 >> 0x%08x\n", rk->hw->load_count0);
printf("load_count1 >> 0x%08x\n", rk->hw->load_count1);
printf("current_cnt_lowbits >> 0x%08x\n", rk->hw->current_value0);
printf("current_cnt_highbits >> 0x%08x\n", rk->hw->current_value1);
printf("load_count2 >> 0x%08x\n", rk->hw->load_count2);
printf("load_count3 >> 0x%08x\n", rk->hw->load_count3);
printf("interrupt_status >> 0x%08x\n", rk->hw->interrupt_status);
printf("control_register >> 0x%08x\n", rk->hw->control_register);
}
int rk_stop(rk_t *rk)
{
if (rk == NULL) {
return EINVAL;
}
rk->hw->control_register = 0;
return 0;
}
uint64_t rk_get_time(rk_t *rk)
{
if (rk == NULL) {
return EINVAL;
}
uint32_t val1 = rk->hw->current_value1;
uint32_t val2 = rk->hw->current_value0;
if (val1 != rk->hw->current_value1) {
val1 = rk->hw->current_value1;
val2 = rk->hw->current_value0;
}
uint64_t time = 0;
time = val1;
time <<= 32;
time |= val2;
return ((uint64_t)((time) * NS_IN_S) / 24000000ull);
}
int rk_start_timestamp_timer(rk_t *rk)
{
assert(rk != NULL);
assert(rk->user_cb_event == LTIMER_OVERFLOW_EVENT);
rk->hw->control_register = 0;
//set timer to count up monotonically
rk->hw->load_count0 = 0xffffffff;
rk->hw->load_count1 = 0xffffffff;
rk->hw->control_register |= UNMASKED_INT | TCLR_STARTTIMER;
return 0;
}
int rk_set_timeout(rk_t *rk, uint64_t ns, bool periodic)
{
if (rk == NULL) {
return EINVAL;
}
/* disable timer */
rk->hw->control_register = 0;
/* timer mode */
uint32_t tclrFlags = periodic ? 0 : USER_MODE;
/* load timer count */
uint64_t ticks = freq_ns_and_hz_to_cycles(ns, 24000000ull);
rk->hw->load_count0 = (uint32_t)(ticks & 0xffffffff);
rk->hw->load_count1 = (ticks >> 32);
/* enable timer with configs */
rk->hw->control_register |= TCLR_STARTTIMER | UNMASKED_INT | tclrFlags;
return 0;
}
static void rk_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
{
assert(data != NULL);
rk_t *rk = data;
/* ack any pending irqs */
rk->hw->interrupt_status = 1;
ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the timer's interrupts");
if (rk->user_cb_fn) {
rk->user_cb_fn(rk->user_cb_token, rk->user_cb_event);
}
}
bool rk_pending_match(rk_t *rk)
{
return rk->hw->interrupt_status & TISR_IRQ_CLEAR;
}
void rk_destroy(rk_t *rk)
{
int error;
if (rk->irq_id != PS_INVALID_IRQ_ID) {
error = ps_irq_unregister(&rk->ops.irq_ops, rk->irq_id);
ZF_LOGF_IF(error, "Failed to unregister IRQ");
}
if (rk->hw != NULL) {
rk_stop(rk);
}
if (rk->rk_map_base != NULL) {
/* use base because rk_map is adjusted based on whether secondary */
ps_pmem_unmap(&rk->ops, rk->pmem, (void *) rk->rk_map_base);
}
}
static int irq_index_walker(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token)
{
rk_t *rk = token;
if (RK_IRQ_CHOICE == curr_num) {
irq_id_t registered_id = ps_irq_register(&rk->ops.irq_ops, irq, rk_handle_irq, rk);
if (registered_id >= 0) {
rk->irq_id = registered_id;
rk->irq = irq;
} else {
/* Bail on error */
return registered_id;
}
}
return 0;
}
int rk_init(rk_t *rk, ps_io_ops_t ops, rk_config_t config)
{
int error;
if (rk == NULL) {
ZF_LOGE("rk cannot be null");
return EINVAL;
}
rk->ops = ops;
rk->user_cb_fn = config.user_cb_fn;
rk->user_cb_token = config.user_cb_token;
rk->user_cb_event = config.user_cb_event;
ps_fdt_cookie_t *cookie = NULL;
error = ps_fdt_read_path(&ops.io_fdt, &ops.malloc_ops, config.fdt_path, &cookie);
if (error) {
ZF_LOGE("rockpro64 timer failed to read path (%d, %s)", error, config.fdt_path);
return error;
}
rk->rk_map_base = ps_fdt_index_map_register(&ops, cookie, RK_REG_CHOICE, &rk->pmem);
if (rk->rk_map_base == NULL) {
ZF_LOGE("rockpro64 timer failed to map registers");
return ENODEV;
}
error = ps_fdt_walk_irqs(&ops.io_fdt, cookie, irq_index_walker, rk);
if (error) {
ZF_LOGE("rockpro64 timer failed to register irqs (%d)", error);
return error;
}
rk->irq_id = ps_fdt_cleanup_cookie(&ops.malloc_ops, cookie);
if (rk->irq_id) {
ZF_LOGE("rockpro64 timer to clean up cookie (%d)", error);
return rk->irq_id;
}
rk->hw = rk->rk_map_base;
return 0;
}
/* initialise rk using the base address of rkp, so that we do not attempt to map
* that base address again but instead re-use it. */
int rk_init_secondary(rk_t *rk, rk_t *rkp, ps_io_ops_t ops, rk_config_t config)
{
int error;
if (rk == NULL || rkp == NULL) {
ZF_LOGE("rk or rkp cannot be null");
return EINVAL;
}
rk->ops = ops;
rk->user_cb_fn = config.user_cb_fn;
rk->user_cb_token = config.user_cb_token;
rk->user_cb_event = config.user_cb_event;
/* so that destroy does not try to unmap twice */
rk->rk_map_base = NULL;
/* just like dmt, rockpro64 has another timer in the same page at 0x20 offset */
rk->hw = (void *)((uintptr_t) rkp->rk_map_base) + 0x20;
/* similarly, the IRQ for this secondary timer is offset by 1 */
rk->irq_id = PS_INVALID_IRQ_ID;
ps_irq_t irq2 = { .type = PS_INTERRUPT, .irq.number = rkp->irq.irq.number + 1 };
irq_id_t irq2_id = ps_irq_register(&ops.irq_ops, irq2, rk_handle_irq, rk);
if (irq2_id < 0) {
ZF_LOGE("Failed to register secondary irq for rk timer");
return irq2_id;
}
rk->irq_id = irq2_id;
return 0;
}