Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for SiFive Premier P550 #190

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions libplatsupport/plat_include/p550/platsupport/plat/serial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2023, UNSW
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once
#include <autoconf.h>

/* The ESWIN EIC7700X SoC contains five 8250 compatible UARTs. */

enum chardev_id {
UART0,
UART1,
UART2,
UART3,
UART4,
PS_SERIAL_DEFAULT = UART0
};

#define UART0_PADDR 0x50900000
#define UART1_PADDR 0x50910000
#define UART2_PADDR 0x50920000
#define UART3_PADDR 0x50930000
#define UART4_PADDR 0x50940000

#define UART0_IRQ 100
#define UART1_IRQ 101
#define UART2_IRQ 102
#define UART3_IRQ 103
#define UART4_IRQ 104

#define DEFAULT_SERIAL_PADDR UART0_PADDR
#define DEFAULT_SERIAL_INTERRUPT UART0_IRQ
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);
47 changes: 47 additions & 0 deletions libplatsupport/src/plat/p550/chardev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2023, UNSW
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include "../../chardev.h"
#include "../../common.h"
#include <utils/util.h>

static const int uart0_irqs[] = {UART0_IRQ, -1};
static const int uart1_irqs[] = {UART1_IRQ, -1};
static const int uart2_irqs[] = {UART2_IRQ, -1};
static const int uart3_irqs[] = {UART3_IRQ, -1};
static const int uart4_irqs[] = {UART4_IRQ, -1};

/*
* Despite each UART being 0x10000 in size (according to the device tree) we
* only need to map in the first page for the driver to functon.
*/
#define UART_DEFN(devid) { \
.id = UART##devid, \
.paddr = UART##devid##_PADDR, \
.size = BIT(12), \
.irqs = uart##devid##_irqs, \
.init_fn = &uart_init \
}

const struct dev_defn dev_defn[] = {
UART_DEFN(0),
UART_DEFN(1),
UART_DEFN(2),
UART_DEFN(3),
UART_DEFN(4),
};

struct ps_chardevice *
ps_cdev_init(enum chardev_id id, const ps_io_ops_t *o, struct ps_chardevice *d)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(dev_defn); i++) {
if (dev_defn[i].id == id) {
return (dev_defn[i].init_fn(dev_defn + i, o, d)) ? NULL : d;
}
}
return NULL;
}
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