diff --git a/libplatsupport/plat_include/p550/platsupport/plat/serial.h b/libplatsupport/plat_include/p550/platsupport/plat/serial.h new file mode 100644 index 00000000..3785d566 --- /dev/null +++ b/libplatsupport/plat_include/p550/platsupport/plat/serial.h @@ -0,0 +1,34 @@ +/* + * Copyright 2023, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once +#include + +/* 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 diff --git a/libplatsupport/src/plat/p550/chardev.c b/libplatsupport/src/plat/p550/chardev.c new file mode 100644 index 00000000..4920f663 --- /dev/null +++ b/libplatsupport/src/plat/p550/chardev.c @@ -0,0 +1,47 @@ +/* + * Copyright 2023, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "../../chardev.h" +#include "../../common.h" +#include + +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; +} \ No newline at end of file diff --git a/libplatsupport/src/plat/p550/serial.c b/libplatsupport/src/plat/p550/serial.c new file mode 100644 index 00000000..be0d14c6 --- /dev/null +++ b/libplatsupport/src/plat/p550/serial.c @@ -0,0 +1,75 @@ +/* + * Copyright 2023, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include "../../chardev.h" + +#define UART_THR 0x00 /* UART Transmit Holding Register */ +#define UART_IER 0x04 /* UART Interrupt Enable Register */ +#define UART_IER_ERDAI BIT(0) /* Enable Received Data Available Interrupt */ +#define UART_LSR 0x14 /* UART Line Status Register */ +#define UART_LSR_THRE 0x20 /* Transmit Holding Register Empty */ + +#define REG_PTR(base, off) ((volatile uint32_t *)((base) + (off))) + +int uart_getchar(ps_chardevice_t *d) +{ + while ((*REG_PTR(d->vaddr, UART_LSR) & BIT(0))); + return *REG_PTR(d->vaddr, UART_THR); +} + +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, UART_LSR) & UART_LSR_THRE) == 0); + + /* Add character to the buffer. */ + *REG_PTR(d->vaddr, UART_THR) = c; + + return c; +} + +static void uart_handle_irq(ps_chardevice_t *dev) +{ + /* + * This is currently only called when received data is available on the device. + * The interrupt will be acked to the device on the next read to the device. + * There's nothing else we need to do here. + */ +} + +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) { + ZF_LOGE("Unable to map chardev"); + return -1; + } + + /* Set up all the device properties. */ + dev->id = defn->id; + dev->vaddr = 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, 0x8) = 1; + *REG_PTR(dev->vaddr, UART_IER) = UART_IER_ERDAI; + + return 0; +}