Skip to content

Commit

Permalink
libplatsupport: add ltimer for ESWIN
Browse files Browse the repository at this point in the history
Signed-off-by: Ivan-Velickovic <[email protected]>
  • Loading branch information
Ivan-Velickovic committed Feb 17, 2025
1 parent 0d368ea commit aacc64b
Show file tree
Hide file tree
Showing 3 changed files with 420 additions and 0 deletions.
54 changes: 54 additions & 0 deletions libplatsupport/plat_include/p550/platsupport/plat/timer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2023, UNSW
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once

#include <stdint.h>
#include <stdbool.h>

/* These values correspond to the DTS node 'soc/timer@0x51840000' */
#define ESWIN_TIMER_BASE 0x51840000
/* The device has a size of 0x8000, but we only need 0x1000 for the driver. */
#define ESWIN_TIMER_SIZE 0x1000
#define ESWIN_TIMER_IRQ 0x159

/* This is 24MHz */
#define ESWIN_TIMER_TICKS_PER_SECOND 0x16e3600

#define ESWIN_TIMER_MAX_TICKS UINT32_MAX

#define ESWIN_NUM_TIMERS 8

#define ESWIN_TIMER_ENABLED 0x1
#define ESWIN_TIMER_DISABLED 0x0
#define ESWIN_TIMER_MODE_FREE_RUNNING 0x0
#define ESWIN_TIMER_MODE_USER_DEFINED (1 << 1)
#define ESWIN_TIMER_IRQ_UNMASK 0x0

typedef struct {
uint32_t load_count;
uint32_t value;
uint32_t ctrl;
uint32_t eoi;
uint32_t int_status;
} eswin_timer_regs_t;

typedef struct {
volatile eswin_timer_regs_t *regs;
/*
* Stores the number of times the continuous counter timer has elapsed and started over.
* This allows us to count to a higher number than allowed by the hardware.
*/
uint32_t value_h;
} eswin_timer_t;

void eswin_timer_enable(eswin_timer_t *timer);
void eswin_timer_disable(eswin_timer_t *timer);
void eswin_timer_handle_irq(eswin_timer_t *timer);
uint64_t eswin_timer_get_time(eswin_timer_t *timer);
void eswin_timer_reset(eswin_timer_t *timer);
int eswin_timer_set_timeout(eswin_timer_t *timer, uint64_t ns, bool is_periodic);
void eswin_timer_disable_all(void *vaddr);
void eswin_timer_init(eswin_timer_t *timer, void *vaddr, uint64_t channel);
239 changes: 239 additions & 0 deletions libplatsupport/src/plat/p550/ltimer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
* Copyright 2025, UNSW
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <platsupport/io.h>
#include <platsupport/ltimer.h>
#include <platsupport/plat/timer.h>
#include <platsupport/pmem.h>
#include <platsupport/timer.h>
#include <utils/io.h>
#include <utils/util.h>

#include "../../ltimer.h"

enum {
COUNTER_TIMER,
TIMEOUT_TIMER,
NUM_TIMERS
};

typedef struct {
void *vaddr;
eswin_timer_t timer[NUM_TIMERS];
irq_id_t irq_id[NUM_TIMERS];
timer_callback_data_t callback_data;
ltimer_callback_fn_t user_callback;
void *user_callback_token;
ps_io_ops_t ops;
} eswin_ltimer_t;

static ps_irq_t irqs[] = {
{
.type = PS_INTERRUPT,
.trigger.number = ESWIN_TIMER_IRQ,
},
};

static pmem_region_t pmems[] = {
{
.type = PMEM_TYPE_DEVICE,
.base_addr = ESWIN_TIMER_BASE,
.length = ESWIN_TIMER_SIZE,
},
};

#define NUM_IRQS ARRAY_SIZE(irqs)
#define NUM_PMEMS ARRAY_SIZE(pmems)

static size_t get_num_irqs(void *data)
{
return NUM_IRQS;
}

static int get_nth_irq(void *data, size_t n, ps_irq_t *irq)
{
assert(n < NUM_IRQS);
assert(irq);

*irq = irqs[n];
return 0;
}

static size_t get_num_pmems(void *data)
{
return NUM_PMEMS;
}

static int get_nth_pmem(void *data, size_t n, pmem_region_t *paddr)
{
assert(n < NUM_PMEMS);
assert(paddr);

*paddr = pmems[n];
return 0;
}

static int ltimer_handle_irq(void *data, ps_irq_t *irq)
{
assert(data);
assert(irq);

eswin_ltimer_t *timers = (eswin_ltimer_t *)data;
long irq_number = irq->irq.number;
ltimer_event_t event;

if (irq_number != irqs[0].irq.number) {
ZF_LOGE("Invalid IRQ number %ld received.", irq_number);
return EINVAL;
}

if (timers->timer[COUNTER_TIMER].regs->int_status & 0x1) {
eswin_timer_handle_irq(&timers->timer[COUNTER_TIMER]);
event = LTIMER_OVERFLOW_EVENT;
}

if (timers->user_callback) {
timers->user_callback(timers->user_callback_token, event);
}

if (timers->timer[TIMEOUT_TIMER].regs->int_status & 0x1) {
eswin_timer_handle_irq(&timers->timer[TIMEOUT_TIMER]);
event = LTIMER_TIMEOUT_EVENT;
}

if (timers->user_callback) {
timers->user_callback(timers->user_callback_token, event);
}

return 0;
}

static int get_time(void *data, uint64_t *time)
{
assert(data);
assert(time);

eswin_ltimer_t *timers = (eswin_ltimer_t *)data;
*time = eswin_timer_get_time(&timers->timer[COUNTER_TIMER]);

return 0;
}

static int set_timeout(void *data, uint64_t ns, timeout_type_t type)
{
assert(data);
eswin_ltimer_t *timers = (eswin_ltimer_t *)data;

switch (type) {
case TIMEOUT_ABSOLUTE: {
uint64_t time = eswin_timer_get_time(&timers->timer[COUNTER_TIMER]);
if (time >= ns) {
ZF_LOGE("Requested time %"PRIu64" earlier than current time %"PRIu64, ns, time);
return ETIME;
}
return eswin_timer_set_timeout(&timers->timer[TIMEOUT_TIMER], ns - time, false);
}
case TIMEOUT_RELATIVE:
return eswin_timer_set_timeout(&timers->timer[TIMEOUT_TIMER], ns, false);
case TIMEOUT_PERIODIC:
return eswin_timer_set_timeout(&timers->timer[TIMEOUT_TIMER], ns, true);
}

return EINVAL;
}

static int reset(void *data)
{
assert(data);
eswin_ltimer_t *timers = (eswin_ltimer_t *)data;

eswin_timer_disable(&timers->timer[COUNTER_TIMER]);
eswin_timer_reset(&timers->timer[COUNTER_TIMER]);
eswin_timer_enable(&timers->timer[COUNTER_TIMER]);

eswin_timer_disable(&timers->timer[TIMEOUT_TIMER]);
eswin_timer_reset(&timers->timer[TIMEOUT_TIMER]);

return 0;
}

static void destroy(void *data)
{
assert(data);
eswin_ltimer_t *timers = (eswin_ltimer_t *)data;
int error;

eswin_timer_disable(&timers->timer[COUNTER_TIMER]);
eswin_timer_disable(&timers->timer[TIMEOUT_TIMER]);

ps_pmem_unmap(&timers->ops, pmems[0], timers->vaddr);

error = ps_irq_unregister(&timers->ops.irq_ops, timers->irq_id[COUNTER_TIMER]);
ZF_LOGE_IF(error, "Failed to uregister counter timer IRQ");

error = ps_irq_unregister(&timers->ops.irq_ops, timers->irq_id[TIMEOUT_TIMER]);
ZF_LOGE_IF(error, "Failed to uregister timeout timer IRQ");

error = ps_free(&timers->ops.malloc_ops, sizeof(*timers), timers);
ZF_LOGE_IF(error, "Failed to free device struct memory");
}

int ltimer_default_init(ltimer_t *ltimer,
ps_io_ops_t ops,
ltimer_callback_fn_t callback,
void *callback_token)
{
assert(ltimer);

ltimer->get_num_irqs = get_num_irqs;
ltimer->get_nth_irq = get_nth_irq;
ltimer->get_num_pmems = get_num_pmems;
ltimer->get_nth_pmem = get_nth_pmem;
ltimer->get_time = get_time;
ltimer->set_timeout = set_timeout;
ltimer->reset = reset;
ltimer->destroy = destroy;

int error = ps_calloc(&ops.malloc_ops, 1, sizeof(eswin_ltimer_t), &ltimer->data);
if (error) {
ZF_LOGE("Memory allocation failed with error %d", error);
return error;
}

eswin_ltimer_t *timers = ltimer->data;

timers->ops = ops;
timers->user_callback = callback;
timers->user_callback_token = callback_token;

timers->callback_data.ltimer = ltimer;
timers->callback_data.irq = &irqs[0];
timers->callback_data.irq_handler = ltimer_handle_irq;

irq_id_t irq_id = ps_irq_register(&ops.irq_ops,
irqs[0],
handle_irq_wrapper,
&timers->callback_data);
assert(irq_id >= 0);

timers->vaddr = ps_pmem_map(&ops, pmems[0], false, PS_MEM_NORMAL);
if (timers->vaddr == NULL) {
ZF_LOGE("Unable to map physical memory at 0x%"PRIx64" length 0x%"PRIx64,
pmems[0].base_addr,
pmems[0].length);
destroy(ltimer->data);
return EINVAL;
}

eswin_timer_disable_all(timers->vaddr);

eswin_timer_init(&timers->timer[COUNTER_TIMER], timers->vaddr, COUNTER_TIMER);
eswin_timer_enable(&timers->timer[COUNTER_TIMER]);

eswin_timer_init(&timers->timer[TIMEOUT_TIMER], timers->vaddr, TIMEOUT_TIMER);

return 0;
}
Loading

0 comments on commit aacc64b

Please sign in to comment.