diff --git a/libplatsupport/plat_include/cheshire/platsupport/plat/apb_timer.h b/libplatsupport/plat_include/cheshire/platsupport/plat/apb_timer.h new file mode 100644 index 000000000..26382aecc --- /dev/null +++ b/libplatsupport/plat_include/cheshire/platsupport/plat/apb_timer.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#include +#include +#include + +/* The input frequence is the CPU frequency which is 50MHz by default */ +#define APB_TIMER_INPUT_FREQ (50*1000*1000) +#define APB_TIMER_PADDR 0x18000000 + +/* Multiple timers */ +#define APB_TIMER_DIST 0x10 +#define APB_TIMER_NUM 2 +#define APB_TIMER_BASE(n) APB_TIMER_DIST * n + +#define CMP_WIDTH 32 +#define APB_TIMER_CTRL_ENABLE BIT(0); +#define CMP_MASK MASK(CMP_WIDTH) + +/* Timer IRQs */ +#define APB_TIMER_PLIC_BASE 4 +#define APB_TIMER_IRQ_OVF(n) APB_TIMER_PLIC_BASE + 2*n + 0x0 +#define APB_TIMER_IRQ_CMP(n) APB_TIMER_PLIC_BASE + 2*n + 0x1 + +typedef struct { + /* vaddr apb_timer is mapped to */ + void *vaddr; +} apb_timer_config_t; + +typedef struct apb_timer { + volatile struct apb_timer_map *apb_timer_map; + uint64_t time_h; +} apb_timer_t; + +struct apb_timer_map { + uint32_t time; + uint32_t ctrl; + uint32_t cmp; +}; + +int apb_timer_start(apb_timer_t *apb_timer); +int apb_timer_stop(apb_timer_t *apb_timer); +uint64_t apb_timer_get_time(apb_timer_t *apb_timer); +int apb_timer_set_timeout(apb_timer_t *apb_timer, uint64_t ns); +int apb_timer_init(apb_timer_t *apb_timer, apb_timer_config_t config); diff --git a/libplatsupport/plat_include/cheshire/platsupport/plat/serial.h b/libplatsupport/plat_include/cheshire/platsupport/plat/serial.h new file mode 100644 index 000000000..6656a5f3a --- /dev/null +++ b/libplatsupport/plat_include/cheshire/platsupport/plat/serial.h @@ -0,0 +1,19 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once +#include + +enum chardev_id { + PS_SERIAL0, + /* defaults */ + PS_SERIAL_DEFAULT = PS_SERIAL0 +}; + +#define PS_SERIAL_DEFAULT 0 + +#define DEFAULT_SERIAL_PADDR NULL +#define DEFAULT_SERIAL_INTERRUPT 0 diff --git a/libplatsupport/src/plat/cheshire/apb_timer.c b/libplatsupport/src/plat/cheshire/apb_timer.c new file mode 100644 index 000000000..5e1664268 --- /dev/null +++ b/libplatsupport/src/plat/cheshire/apb_timer.c @@ -0,0 +1,60 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +#include + +#include +#include + +/* Largest timeout that can be programmed */ +#define MAX_TIMEOUT_NS (BIT(31) * (NS_IN_S / APB_TIMER_INPUT_FREQ)) + +int apb_timer_start(apb_timer_t *apb_timer) +{ + apb_timer->apb_timer_map->ctrl |= APB_TIMER_CTRL_ENABLE; + return 0; +} + +int apb_timer_stop(apb_timer_t *apb_timer) +{ + /* Disable timer. */ + apb_timer->apb_timer_map->cmp = CMP_MASK; + apb_timer->apb_timer_map->ctrl &= ~APB_TIMER_CTRL_ENABLE; + apb_timer->apb_timer_map->time = 0; + + return 0; +} + +int apb_timer_set_timeout(apb_timer_t *apb_timer, uint64_t ns) +{ + if (ns > MAX_TIMEOUT_NS) { + ZF_LOGE("Cannot program a timeout larget than %ld ns", MAX_TIMEOUT_NS); + return -1; + } + + apb_timer->apb_timer_map->cmp = ns / (NS_IN_S / APB_TIMER_INPUT_FREQ); + apb_timer_start(apb_timer); + + return 0; +} + +uint64_t apb_timer_get_time(apb_timer_t *apb_timer) +{ + uint64_t ticks = apb_timer->apb_timer_map->time + apb_timer->time_h; + return ticks * (NS_IN_S / APB_TIMER_INPUT_FREQ); +} + +int apb_timer_init(apb_timer_t *apb_timer, apb_timer_config_t config) +{ + apb_timer->apb_timer_map = (volatile struct apb_timer_map *) config.vaddr; + apb_timer->time_h = 0; + return 0; +} diff --git a/libplatsupport/src/plat/cheshire/chardev.c b/libplatsupport/src/plat/cheshire/chardev.c new file mode 100644 index 000000000..0ad021ddc --- /dev/null +++ b/libplatsupport/src/plat/cheshire/chardev.c @@ -0,0 +1,16 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "../../chardev.h" +#include "../../common.h" +#include + +struct ps_chardevice * +ps_cdev_init(enum chardev_id id, const ps_io_ops_t *o, struct ps_chardevice *d) +{ + return NULL; +} + diff --git a/libplatsupport/src/plat/cheshire/ltimer.c b/libplatsupport/src/plat/cheshire/ltimer.c new file mode 100644 index 000000000..dc174fe75 --- /dev/null +++ b/libplatsupport/src/plat/cheshire/ltimer.c @@ -0,0 +1,263 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ +/* Implementation of a logical timer for Ariane SoC platform. + * + */ +#include +#include +#include +#include +#include + +#include "../../ltimer.h" + +typedef struct { + apb_timer_t apb_timer; + void *vaddr; +} apb_timer_ltimer_t; + +typedef struct { + apb_timer_ltimer_t apb_timer_ltimer; + //ps_irq_t irq; + ps_io_ops_t ops; + irq_id_t irq_id; + ltimer_callback_fn_t user_callback; + timer_callback_data_t callback_data; + void *user_callback_token; + uint64_t period; +} ariane_ltimer_t; + +static pmem_region_t pmems[] = { + { + .type = PMEM_TYPE_DEVICE, + .base_addr = APB_TIMER_PADDR, + .length = PAGE_SIZE_4K + } +}; + +static ps_irq_t irqs[] = { + { + .type = PS_INTERRUPT, + .irq.number = APB_TIMER_IRQ_CMP(0) + + } +}; + +size_t get_num_irqs(void *data) +{ + return 1; +} + +static int get_nth_irq(void *data, size_t n, ps_irq_t *irq) +{ + assert(data != NULL); + assert(irq != NULL); + assert(n == 0); + + irq->irq.number = APB_TIMER_IRQ_CMP(0); + irq->type = PS_INTERRUPT; + return 0; +} + +static size_t get_num_pmems(void *data) +{ + return sizeof(pmems) / sizeof(pmems[0]); +} + +static int get_nth_pmem(void *data, size_t n, pmem_region_t *paddr) +{ + assert(n < get_num_pmems(data)); + *paddr = pmems[n]; + return 0; +} + +static int get_time(void *data, uint64_t *time) +{ + assert(data != NULL); + assert(time != NULL); + ariane_ltimer_t *timer = data; + + *time = apb_timer_get_time(&timer->apb_timer_ltimer.apb_timer); + return 0; +} + +static int get_resolution(void *data, uint64_t *resolution) +{ + return ENOSYS; +} + +static int set_timeout(void *data, uint64_t ns, timeout_type_t type) +{ + assert(data != NULL); + ariane_ltimer_t *timer = data; + timer->period = 0; + + switch (type) { + case TIMEOUT_ABSOLUTE: { + uint64_t time = apb_timer_get_time(&timer->apb_timer_ltimer.apb_timer); + if (time >= ns) { + return ETIME; + } + apb_timer_set_timeout(&timer->apb_timer_ltimer.apb_timer, ns - time); + return 0; + } + case TIMEOUT_PERIODIC: { + timer->period = ns; + /* fall through */ + } + case TIMEOUT_RELATIVE: { + apb_timer_set_timeout(&timer->apb_timer_ltimer.apb_timer, ns); + return 0; + } + } + + return EINVAL; +} + +static int reset(void *data) +{ + assert(data != NULL); + ariane_ltimer_t *timer = data; + apb_timer_stop(&timer->apb_timer_ltimer.apb_timer); + apb_timer_start(&timer->apb_timer_ltimer.apb_timer); + return 0; +} + +static void destroy(void *data) +{ + assert(data); + ariane_ltimer_t *timer = data; + if (timer->apb_timer_ltimer.vaddr) { + apb_timer_stop(&timer->apb_timer_ltimer.apb_timer); + ps_pmem_unmap(&timer->ops, pmems[0], timer->apb_timer_ltimer.vaddr); + } + if (timer->irq_id > PS_INVALID_IRQ_ID) { + int error = ps_irq_unregister(&timer->ops.irq_ops, timer->irq_id); + ZF_LOGF_IF(error, "Failed to unregister IRQ"); + } + ps_free(&timer->ops.malloc_ops, sizeof(timer), timer); +} + +static int create_ltimer(ltimer_t *ltimer, ps_io_ops_t ops) +{ + assert(ltimer != NULL); + ltimer->get_time = get_time; + ltimer->get_resolution = get_resolution; + ltimer->set_timeout = set_timeout; + ltimer->reset = reset; + ltimer->destroy = destroy; + + int error = ps_calloc(&ops.malloc_ops, 1, sizeof(ariane_ltimer_t), <imer->data); + if (error) { + return error; + } + assert(ltimer->data != NULL); + + return 0; +} + +static int init_ltimer(ltimer_t *ltimer) +{ + assert(ltimer != NULL); + ariane_ltimer_t *timer = ltimer->data; + + /* setup apb_timer */ + apb_timer_config_t config_counter = { + .vaddr = timer->apb_timer_ltimer.vaddr + }; + + apb_timer_init(&timer->apb_timer_ltimer.apb_timer, config_counter); + apb_timer_start(&timer->apb_timer_ltimer.apb_timer); + return 0; +} + +static int handle_irq(void *data, ps_irq_t *irq) +{ + assert(data != NULL); + ariane_ltimer_t *timer = data; + + timer->apb_timer_ltimer.apb_timer.time_h += timer->apb_timer_ltimer.apb_timer.apb_timer_map->cmp; + apb_timer_stop(&timer->apb_timer_ltimer.apb_timer); + + if (timer->period > 0) { + apb_timer_set_timeout(&timer->apb_timer_ltimer.apb_timer, timer->period); + } + + ltimer_event_t event = LTIMER_TIMEOUT_EVENT; + if (timer->user_callback) { + timer->user_callback(timer->user_callback_token, event); + } + apb_timer_start(&timer->apb_timer_ltimer.apb_timer); +} + +int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token) +{ + + int error = ltimer_default_describe(ltimer, ops); + if (error) { + return error; + } + + error = create_ltimer(ltimer, ops); + if (error) { + return error; + } + + ariane_ltimer_t *timer = ltimer->data; + timer->ops = ops; + timer->user_callback = callback; + timer->user_callback_token = callback_token; + timer->irq_id = 0;//PS_INVALID_IRQ_ID; + + /* map the registers we need */ + timer->apb_timer_ltimer.vaddr = ps_pmem_map(&ops, pmems[0], false, PS_MEM_NORMAL); + if (timer->apb_timer_ltimer.vaddr == NULL) { + destroy(ltimer->data); + return ENOMEM; + } + + /* register the IRQs we need */ + error = ps_calloc(&ops.malloc_ops, 1, sizeof(ps_irq_t), (void **) &timer->callback_data.irq); + if (error) { + destroy(ltimer->data); + return error; + } + timer->callback_data.ltimer = ltimer; + timer->callback_data.irq_handler = handle_irq; + error = get_nth_irq(ltimer->data, 0, timer->callback_data.irq); + if (error) { + destroy(ltimer->data); + return error; + } + timer->irq_id = ps_irq_register(&ops.irq_ops, *timer->callback_data.irq, handle_irq_wrapper, + &timer->callback_data); + if (timer->irq_id < 0) { + destroy(ltimer->data); + return EIO; + } + + error = init_ltimer(ltimer); + if (error) { + destroy(ltimer->data); + return error; + } + /* success! */ + return 0; +} + +int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops) +{ + if (ltimer == NULL) { + ZF_LOGE("Timer is NULL!"); + return EINVAL; + } + + 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; + return 0; +} diff --git a/libplatsupport/src/plat/cheshire/serial.c b/libplatsupport/src/plat/cheshire/serial.c new file mode 100644 index 000000000..921b15139 --- /dev/null +++ b/libplatsupport/src/plat/cheshire/serial.c @@ -0,0 +1,17 @@ +/* + * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +int uart_init(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev) +{ + return 0; +}