diff --git a/libplatsupport/plat_include/morello-fvp/platsupport/plat/clock.h b/libplatsupport/plat_include/morello-fvp/platsupport/plat/clock.h new file mode 100644 index 000000000..6cfcd14b2 --- /dev/null +++ b/libplatsupport/plat_include/morello-fvp/platsupport/plat/clock.h @@ -0,0 +1,19 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +enum clk_id { + CLK_MASTER, + /* ----- */ + NCLOCKS, + /* Custom clock */ + CLK_CUSTOM, +}; + +enum clock_gate { + NCLKGATES +}; diff --git a/libplatsupport/plat_include/morello-fvp/platsupport/plat/i2c.h b/libplatsupport/plat_include/morello-fvp/platsupport/plat/i2c.h new file mode 100644 index 000000000..39318e264 --- /dev/null +++ b/libplatsupport/plat_include/morello-fvp/platsupport/plat/i2c.h @@ -0,0 +1,11 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +enum i2c_id { + NI2C +}; diff --git a/libplatsupport/plat_include/morello-fvp/platsupport/plat/serial.h b/libplatsupport/plat_include/morello-fvp/platsupport/plat/serial.h new file mode 100644 index 000000000..bd44a5d2f --- /dev/null +++ b/libplatsupport/plat_include/morello-fvp/platsupport/plat/serial.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * Copyright (c) 2024, Capabilities Ltd + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#define UART0_PADDR 0x2a400000 +#define UART0_IRQ 95 + +enum chardev_id { + PL001_UART0, + /* Aliases */ + PS_SERIAL0 = PL001_UART0, + /* defaults */ + PS_SERIAL_DEFAULT = PL001_UART0 +}; + +#define DEFAULT_SERIAL_PADDR UART0_PADDR +#define DEFAULT_SERIAL_INTERRUPT UART0_IRQ diff --git a/libplatsupport/plat_include/morello-fvp/platsupport/plat/sp804.h b/libplatsupport/plat_include/morello-fvp/platsupport/plat/sp804.h new file mode 100644 index 000000000..342ea78ae --- /dev/null +++ b/libplatsupport/plat_include/morello-fvp/platsupport/plat/sp804.h @@ -0,0 +1,79 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * Copyright (c) 2024, Capabilities Ltd + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#pragma once + +#include +#include +#include + +/* Each SP804 has two timers, but we only use one timer on eace device page. + * This is because the two timers on the same page share the same interrupt, + * and using one timer on each page saves us from identifying the sources of + * interrupts. + * */ +#define SP804_TIMER1_PATH "/timer@1c110000" +#define SP804_TIMER2_PATH "/timer@1c120000" + +#define SP804_REG_CHOICE 0 +#define SP804_IRQ_CHOICE 0 + +static UNUSED timer_properties_t sp804_timer_props = { + .upcounter = false, + .timeouts = true, + .absolute_timeouts = false, + .relative_timeouts = true, + .periodic_timeouts = true, + .bit_width = 32, + .irqs = 1 +}; + +typedef volatile struct sp804_regs { + uint32_t load; + uint32_t value; + uint32_t control; + uint32_t intclr; + uint32_t ris; + uint32_t mis; + uint32_t bgload; +} sp804_regs_t; + +typedef struct { + /* set in init */ + ps_io_ops_t ops; + ltimer_callback_fn_t user_cb_fn; + void *user_cb_token; + ltimer_event_t user_cb_event; /* what are we being used for? */ + + /* set in fdt helper */ + volatile sp804_regs_t *sp804_map; + pmem_region_t pmem; + irq_id_t irq_id; + + /* set in setup */ + uint32_t time_h; +} sp804_t; + +typedef struct { + const char *fdt_path; + ltimer_callback_fn_t user_cb_fn; + void *user_cb_token; + ltimer_event_t user_cb_event; +} sp804_config_t; + +int sp804_init(sp804_t *sp804, ps_io_ops_t ops, sp804_config_t config); +/* convert between dmt ticks and ns */ +uint64_t sp804_ticks_to_ns(uint64_t ticks); +/* return true if an overflow irq is pending */ +bool sp804_is_irq_pending(sp804_t *sp804); +int sp804_set_timeout_ticks(sp804_t *timer, uint32_t ticks, bool periodic, bool irqs); +/* set a timeout in nano seconds */ +int sp804_set_timeout(sp804_t *timer, uint64_t ns, bool periodic, bool irqs); +int sp804_start(sp804_t *timer); +int sp804_stop(sp804_t *timer); +uint64_t sp804_get_time(sp804_t *timer); +uint64_t sp804_get_ticks(sp804_t *timer); +void sp804_destroy(sp804_t *sp804); diff --git a/libplatsupport/src/plat/morello-fvp/chardev.c b/libplatsupport/src/plat/morello-fvp/chardev.c new file mode 100644 index 000000000..04e5bbf66 --- /dev/null +++ b/libplatsupport/src/plat/morello-fvp/chardev.c @@ -0,0 +1,42 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/** + * Contains the definition for all character devices on this platform. + * Currently this is just a simple patch. + */ + +#include "../../chardev.h" +#include "../../common.h" +#include + +#include "../../chardev.h" + +static const int uart0_irqs[] = {UART0_IRQ, -1}; + +#define UART_DEFN(devid) { \ + .id = PL001_UART##devid, \ + .paddr = UART##devid##_PADDR, \ + .size = BIT(12), \ + .irqs = uart##devid##_irqs, \ + .init_fn = &uart_init \ +} + +static const struct dev_defn dev_defn[] = { + UART_DEFN(0) +}; + +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; +} diff --git a/libplatsupport/src/plat/morello-fvp/clock_mux.c b/libplatsupport/src/plat/morello-fvp/clock_mux.c new file mode 100644 index 000000000..ee307c007 --- /dev/null +++ b/libplatsupport/src/plat/morello-fvp/clock_mux.c @@ -0,0 +1,19 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +int clock_sys_init(ps_io_ops_t *io_ops, clock_sys_t *clk_sys) +{ + return 0; +} + +int mux_sys_init(ps_io_ops_t *io_ops, UNUSED void *dependencies, mux_sys_t *mux) +{ + return 0; +} diff --git a/libplatsupport/src/plat/morello-fvp/ltimer.c b/libplatsupport/src/plat/morello-fvp/ltimer.c new file mode 100644 index 000000000..6ec6cdbac --- /dev/null +++ b/libplatsupport/src/plat/morello-fvp/ltimer.c @@ -0,0 +1,152 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../ltimer.h" + +/* + * We use two sp804 timers: one to keep track of an absolute time, the other for timeouts. + */ + +typedef struct { + /* fvp sp804 have 2 timers per frame, we just use one per each */ + sp804_t sp804_timeout; + sp804_t sp804_timestamp; + ps_io_ops_t ops; +} fvp_ltimer_t; + +static int get_time(void *data, uint64_t *time) +{ + fvp_ltimer_t *fvp_ltimer = data; + assert(data != NULL); + assert(time != NULL); + + *time = sp804_get_time(&fvp_ltimer->sp804_timestamp); + return 0; +} + +int set_timeout(void *data, uint64_t ns, timeout_type_t type) +{ + if (type == TIMEOUT_ABSOLUTE) { + uint64_t time; + int error = get_time(data, &time); + if (error) { + return error; + } + if (time > ns) { + return ETIME; + } + ns -= time; + } + + fvp_ltimer_t *fvp_ltimer = data; + return sp804_set_timeout(&fvp_ltimer->sp804_timeout, ns, type == TIMEOUT_PERIODIC, true); +} + +static int reset(void *data) +{ + fvp_ltimer_t *fvp_ltimer = data; + /* restart the rtc */ + sp804_stop(&fvp_ltimer->sp804_timeout); + sp804_start(&fvp_ltimer->sp804_timeout); + return 0; +} + +static void destroy(void *data) +{ + assert(data != NULL); + fvp_ltimer_t *fvp_ltimer = data; + sp804_destroy(&fvp_ltimer->sp804_timeout); + sp804_destroy(&fvp_ltimer->sp804_timestamp); + ps_free(&fvp_ltimer->ops.malloc_ops, sizeof(fvp_ltimer_t), fvp_ltimer); +} + +int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token) +{ + int error; + + if (ltimer == NULL) { + ZF_LOGE("ltimer cannot be NULL"); + return EINVAL; + } + + error = create_ltimer_simple( + ltimer, ops, sizeof(fvp_ltimer_t), + get_time, set_timeout, reset, destroy + ); + if (error) { + ZF_LOGE("Failed to create ltimer simple"); + return error; + } + + fvp_ltimer_t *fvp_ltimer = ltimer->data; + fvp_ltimer->ops = ops; + + /* set up an SP804 for timeouts */ + sp804_config_t sp804_config = { + .fdt_path = SP804_TIMER1_PATH, + .user_cb_fn = callback, + .user_cb_token = callback_token, + .user_cb_event = LTIMER_TIMEOUT_EVENT + }; + + error = sp804_init(&fvp_ltimer->sp804_timeout, ops, sp804_config); + if (error) { + ZF_LOGE("Failed to init timeout timer"); + destroy(&fvp_ltimer); + return error; + } + + error = sp804_start(&fvp_ltimer->sp804_timeout); + if (error) { + ZF_LOGE("Failed to start timeout timer"); + destroy(&fvp_ltimer); + return error; + } + + /* another for timestamps */ + sp804_config.fdt_path = SP804_TIMER2_PATH; + sp804_config.user_cb_event = LTIMER_OVERFLOW_EVENT; + + error = sp804_init(&fvp_ltimer->sp804_timestamp, ops, sp804_config); + if (error) { + ZF_LOGE("Failed to init timestamp timer"); + destroy(&fvp_ltimer); + return error; + } + + error = sp804_start(&fvp_ltimer->sp804_timestamp); + if (error) { + ZF_LOGE("Failed to start timestamp timer"); + destroy(&fvp_ltimer); + return error; + } + + error = sp804_set_timeout_ticks(&fvp_ltimer->sp804_timestamp, UINT32_MAX, true, true); + if (error) { + ZF_LOGE("Failed to set timeout ticks for timer"); + destroy(&fvp_ltimer); + return error; + } + + return 0; +} + +/* This function is intended to be deleted, + * this is just left here for now so that stuff can compile */ +int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops) +{ + ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid"); + return EINVAL; +} diff --git a/libplatsupport/src/plat/morello-fvp/serial.c b/libplatsupport/src/plat/morello-fvp/serial.c new file mode 100644 index 000000000..791d5eb57 --- /dev/null +++ b/libplatsupport/src/plat/morello-fvp/serial.c @@ -0,0 +1,75 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* Mostly copy/paste from the HiKey plat. + * Should be moved to a common driver file for PL011 */ + +#include +#include +#include +#include "../../chardev.h" + +#define RHR_MASK MASK(8) +#define UARTDR 0x000 +#define UARTFR 0x018 +#define UARTIMSC 0x038 +#define UARTICR 0x044 +#define PL011_UARTFR_TXFF BIT(5) +#define PL011_UARTFR_RXFE BIT(4) + +#define REG_PTR(base, off) ((volatile uint32_t *)((base) + (off))) + +int uart_getchar(ps_chardevice_t *d) +{ + int ch = EOF; + + if ((*REG_PTR(d->vaddr, UARTFR) & PL011_UARTFR_RXFE) == 0) { + ch = *REG_PTR(d->vaddr, UARTDR) & RHR_MASK; + } + return ch; +} + +int uart_putchar(ps_chardevice_t *d, int c) +{ + if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) { + uart_putchar(d, '\r'); + } + + while ((*REG_PTR(d->vaddr, UARTFR) & PL011_UARTFR_TXFF) != 0); + + *REG_PTR(d->vaddr, UARTDR) = c; + + return c; +} + +static void uart_handle_irq(ps_chardevice_t *dev) +{ + *REG_PTR(dev->vaddr, UARTICR) = 0x7f0; +} + +int uart_init(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev) +{ + memset(dev, 0, sizeof(*dev)); + void *vaddr = chardev_map(defn, ops); + if (vaddr == NULL) { + return -1; + } + + /* Set up all the device properties. */ + dev->id = defn->id; + dev->vaddr = (void *)vaddr; + dev->read = &uart_read; + dev->write = &uart_write; + dev->handle_irq = &uart_handle_irq; + dev->irqs = defn->irqs; + dev->ioops = *ops; + dev->flags = SERIAL_AUTO_CR; + + *REG_PTR(dev->vaddr, UARTIMSC) = 0x50; + return 0; +} diff --git a/libplatsupport/src/plat/morello-fvp/sp804.c b/libplatsupport/src/plat/morello-fvp/sp804.c new file mode 100644 index 000000000..b5bb80743 --- /dev/null +++ b/libplatsupport/src/plat/morello-fvp/sp804.c @@ -0,0 +1,198 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include + +#include +#include + +#include + +#include "../../ltimer.h" + +/* This file is mostly the same as the dmt.c file for the hikey. + * Consider to merge the two files as a single driver file for + * SP804. + */ + +#define TCLR_ONESHOT BIT(0) +#define TCLR_VALUE_32 BIT(1) +#define TCLR_INTENABLE BIT(5) +#define TCLR_AUTORELOAD BIT(6) +#define TCLR_STARTTIMER BIT(7) +/* It looks like the FVP does not emulate time accurately. Thus, pick + * a small Hz that triggers interrupts in a reasonable time */ +#define TICKS_PER_SECOND 16000 +#define TICKS_PER_MS (TICKS_PER_SECOND / MS_IN_S) + +static void sp804_timer_reset(sp804_t *sp804) +{ + assert(sp804 != NULL && sp804->sp804_map != NULL); + sp804_regs_t *sp804_regs = sp804->sp804_map; + sp804_regs->control = 0; + + sp804->time_h = 0; +} + +int sp804_stop(sp804_t *sp804) +{ + if (sp804 == NULL) { + return EINVAL; + } + assert(sp804->sp804_map != NULL); + sp804_regs_t *sp804_regs = sp804->sp804_map; + sp804_regs->control = sp804_regs->control & ~TCLR_STARTTIMER; + return 0; +} + +int sp804_start(sp804_t *sp804) +{ + if (sp804 == NULL) { + return EINVAL; + } + assert(sp804->sp804_map != NULL); + sp804_regs_t *sp804_regs = sp804->sp804_map; + sp804_regs->control = sp804_regs->control | TCLR_STARTTIMER; + return 0; +} + +uint64_t sp804_ticks_to_ns(uint64_t ticks) +{ + return ticks / TICKS_PER_MS * NS_IN_MS; +} + +bool sp804_is_irq_pending(sp804_t *sp804) +{ + if (sp804) { + assert(sp804->sp804_map != NULL); + return !!sp804->sp804_map->ris; + } + return false; +} + +int sp804_set_timeout(sp804_t *sp804, uint64_t ns, bool periodic, bool irqs) +{ + uint64_t ticks64 = ns * TICKS_PER_MS / NS_IN_MS; + if (ticks64 > UINT32_MAX) { + return ETIME; + } + return sp804_set_timeout_ticks(sp804, ticks64, periodic, irqs); +} + +int sp804_set_timeout_ticks(sp804_t *sp804, uint32_t ticks, bool periodic, bool irqs) +{ + if (sp804 == NULL) { + return EINVAL; + } + int flags = periodic ? TCLR_AUTORELOAD : TCLR_ONESHOT; + flags |= irqs ? TCLR_INTENABLE : 0; + + assert(sp804->sp804_map != NULL); + sp804_regs_t *sp804_regs = sp804->sp804_map; + sp804_regs->control = 0; + + if (flags & TCLR_AUTORELOAD) { + sp804_regs->bgload = ticks; + } else { + sp804_regs->bgload = 0; + } + sp804_regs->load = ticks; + + /* The TIMERN_VALUE register is read-only. */ + sp804_regs->control = TCLR_STARTTIMER | TCLR_VALUE_32 + | flags; + + return 0; +} + +static void sp804_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data) +{ + assert(data != NULL); + sp804_t *sp804 = data; + + if (sp804->user_cb_event == LTIMER_OVERFLOW_EVENT) { + sp804->time_h++; + } + + sp804_regs_t *sp804_regs = sp804->sp804_map; + sp804_regs->intclr = 0x1; + + ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the timer's interrupts"); + if (sp804->user_cb_fn) { + sp804->user_cb_fn(sp804->user_cb_token, sp804->user_cb_event); + } +} + +uint64_t sp804_get_ticks(sp804_t *sp804) +{ + assert(sp804 != NULL && sp804->sp804_map != NULL); + sp804_regs_t *sp804_regs = sp804->sp804_map; + return sp804_regs->value; +} + +uint64_t sp804_get_time(sp804_t *sp804) +{ + uint32_t high, low; + + /* timer must be being used for timekeeping */ + assert(sp804->user_cb_event == LTIMER_OVERFLOW_EVENT); + + /* sp804 is a down counter, invert the result */ + high = sp804->time_h; + low = UINT32_MAX - sp804_get_ticks(sp804); + + /* check after fetching low to see if we've missed a high bit */ + if (sp804_is_irq_pending(sp804)) { + high += 1; + assert(high != 0); + } + + uint64_t ticks = (((uint64_t) high << 32llu) | low); + return sp804_ticks_to_ns(ticks); +} + +void sp804_destroy(sp804_t *sp804) +{ + int error; + if (sp804->irq_id != PS_INVALID_IRQ_ID) { + error = ps_irq_unregister(&sp804->ops.irq_ops, sp804->irq_id); + ZF_LOGF_IF(error, "Failed to unregister IRQ"); + } + if (sp804->sp804_map != NULL) { + sp804_stop(sp804); + ps_pmem_unmap(&sp804->ops, sp804->pmem, (void *) sp804->sp804_map); + } +} + +int sp804_init(sp804_t *sp804, ps_io_ops_t ops, sp804_config_t config) +{ + int error; + + if (sp804 == NULL) { + ZF_LOGE("sp804 cannot be null"); + return EINVAL; + } + + sp804->ops = ops; + sp804->user_cb_fn = config.user_cb_fn; + sp804->user_cb_token = config.user_cb_token; + sp804->user_cb_event = config.user_cb_event; + + error = helper_fdt_alloc_simple( + &ops, config.fdt_path, + SP804_REG_CHOICE, SP804_IRQ_CHOICE, + (void *) &sp804->sp804_map, &sp804->pmem, &sp804->irq_id, + sp804_handle_irq, sp804 + ); + if (error) { + ZF_LOGE("Simple fdt alloc helper failed"); + return error; + } + + sp804_timer_reset(sp804); + return 0; +}