/* * Copyright 2017, Data61 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) * ABN 41 687 119 230. * * This software may be distributed and modified according to the terms of * the BSD 2-Clause license. Note that NO WARRANTY is provided. * See "LICENSE_BSD2.txt" for details. * * @TAG(DATA61_BSD) */ #include #include #include #include #include #include "../../chardev.h" #define AXI_UARTLITE_CR_RST_TX_FIFO BIT(0) #define AXI_UARTLITE_CR_RST_RX_FIFO BIT(1) #define AXI_UARTLITE_CR_ENABLE_INTR BIT(4) #define AXI_UARTLITE_SR_RX_FIFO_VALID BIT(0) #define AXI_UARTLITE_SR_RX_FIFO_FULL BIT(1) #define AXI_UARTLITE_SR_TX_FIFO_EMPTY BIT(2) #define AXI_UARTLITE_SR_TX_FIFO_FULL BIT(3) #define AXI_UARTLITE_SR_INTR_ENABLED BIT(4) #define AXI_UARTLITE_SR_OVERRUN_ERROR BIT(5) #define AXI_UARTLITE_SR_FRAME_ERROR BIT(6) #define AXI_UARTLITE_SR_PARITY_ERROR BIT(7) struct zynq_axi_uartlite_regs { uint32_t rx_fifo; /* 0x0 Receive FIFO */ uint32_t tx_fifo; /* 0x4 Transmit FIFO */ uint32_t sr; /* 0x8 Status Register */ uint32_t cr; /* 0xC Control Register */ }; typedef volatile struct zynq_axi_uartlite_regs zynq_axi_uartlite_regs_t; static inline zynq_axi_uartlite_regs_t* zynq_axi_uartlite_get_priv(ps_chardevice_t *d) { return (zynq_axi_uartlite_regs_t*)d->vaddr; } static int axi_uartlite_getchar(ps_chardevice_t *d) { zynq_axi_uartlite_regs_t *regs = zynq_axi_uartlite_get_priv(d); int c = -1; /* check if there is at least one byte in the fifo */ if (regs->sr & AXI_UARTLITE_SR_RX_FIFO_VALID) { c = regs->rx_fifo; } return c; } static int axi_uartlite_putchar(ps_chardevice_t *d, int c) { static int needs_newline = 0; zynq_axi_uartlite_regs_t *regs = zynq_axi_uartlite_get_priv(d); /* check if fifo is full */ if (regs->sr & AXI_UARTLITE_SR_TX_FIFO_FULL) { return -1; } else { if (needs_newline) { /* if the last putchar was a '\n' and the fifo filled after * only the '\r' was sent, send the remaining '\n' here */ regs->tx_fifo = '\n'; needs_newline = 0; if (regs->sr & AXI_UARTLITE_SR_TX_FIFO_FULL) { return -1; } } if (c == '\n') { regs->tx_fifo = '\r'; /* the fifo may have filled after sending the '\r' */ if (regs->sr & AXI_UARTLITE_SR_TX_FIFO_FULL) { needs_newline = 1; /* even if the '\n' didn't get sent on this call, still * return '\n', as it will still eventually be sent */ } else { regs->tx_fifo = '\n'; } } else { regs->tx_fifo = c; } return c; } } static ssize_t axi_uartlite_write(ps_chardevice_t* d, const void* vdata, size_t count, chardev_callback_t rcb UNUSED, void* token UNUSED) { const char *data = (const char*)vdata; for (int i = 0; i < count; i++) { if (axi_uartlite_putchar(d, *data++) < 0) { return i; } } return count; } static ssize_t axi_uartlite_read(ps_chardevice_t* d, void* vdata, size_t count, chardev_callback_t rcb UNUSED, void* token UNUSED) { char *data = (char*)vdata; for (int i = 0; i < count; i++) { int ch = axi_uartlite_getchar(d); if (ch != EOF) { *data++ = ch; } else { return i; } } return count; } int axi_uartlite_init(void* vaddr, ps_chardevice_t* dev) { memset(dev, 0, sizeof(*dev)); dev->vaddr = vaddr; dev->read = &axi_uartlite_read; dev->write = &axi_uartlite_write; zynq_axi_uartlite_regs_t *regs = zynq_axi_uartlite_get_priv(dev); // clear the fifos regs->cr |= (AXI_UARTLITE_CR_RST_TX_FIFO | AXI_UARTLITE_CR_RST_RX_FIFO); // disable interrupts regs->cr &= ~AXI_UARTLITE_CR_ENABLE_INTR; return 0; } int axi_uartlite_init_defn(const struct dev_defn* defn, const ps_io_ops_t* ops, ps_chardevice_t* dev) { void *vaddr = chardev_map(defn, ops); if (vaddr == NULL) { return -1; } axi_uartlite_init(vaddr, dev); dev->id = defn->id; dev->irqs = defn->irqs; dev->ioops = *ops; return 0; }