1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <stdlib.h>
14#include <platsupport/serial.h>
15#include <platsupport/plat/serial.h>
16#include <string.h>
17#include <stdio.h>
18#include "../../chardev.h"
19
20#define AXI_UARTLITE_CR_RST_TX_FIFO     BIT(0)
21#define AXI_UARTLITE_CR_RST_RX_FIFO     BIT(1)
22#define AXI_UARTLITE_CR_ENABLE_INTR     BIT(4)
23
24#define AXI_UARTLITE_SR_RX_FIFO_VALID   BIT(0)
25#define AXI_UARTLITE_SR_RX_FIFO_FULL    BIT(1)
26#define AXI_UARTLITE_SR_TX_FIFO_EMPTY   BIT(2)
27#define AXI_UARTLITE_SR_TX_FIFO_FULL    BIT(3)
28#define AXI_UARTLITE_SR_INTR_ENABLED    BIT(4)
29#define AXI_UARTLITE_SR_OVERRUN_ERROR   BIT(5)
30#define AXI_UARTLITE_SR_FRAME_ERROR     BIT(6)
31#define AXI_UARTLITE_SR_PARITY_ERROR    BIT(7)
32
33struct zynq_axi_uartlite_regs {
34    uint32_t rx_fifo;   /* 0x0 Receive FIFO */
35    uint32_t tx_fifo;   /* 0x4 Transmit FIFO */
36    uint32_t sr;        /* 0x8 Status Register */
37    uint32_t cr;        /* 0xC Control Register */
38};
39typedef volatile struct zynq_axi_uartlite_regs zynq_axi_uartlite_regs_t;
40
41static inline zynq_axi_uartlite_regs_t*
42zynq_axi_uartlite_get_priv(ps_chardevice_t *d)
43{
44    return (zynq_axi_uartlite_regs_t*)d->vaddr;
45}
46
47static int axi_uartlite_getchar(ps_chardevice_t *d)
48{
49    zynq_axi_uartlite_regs_t *regs =
50        zynq_axi_uartlite_get_priv(d);
51
52    int c = -1;
53
54    /* check if there is at least one byte in the fifo */
55    if (regs->sr & AXI_UARTLITE_SR_RX_FIFO_VALID) {
56        c = regs->rx_fifo;
57    }
58
59    return c;
60}
61
62static int axi_uartlite_putchar(ps_chardevice_t *d, int c)
63{
64
65    static int needs_newline = 0;
66
67    zynq_axi_uartlite_regs_t *regs =
68        zynq_axi_uartlite_get_priv(d);
69
70    /* check if fifo is full */
71    if (regs->sr & AXI_UARTLITE_SR_TX_FIFO_FULL) {
72        return -1;
73    } else {
74        if (needs_newline) {
75            /* if the last putchar was a '\n' and the fifo filled after
76             * only the '\r' was sent, send the remaining '\n' here */
77            regs->tx_fifo = '\n';
78            needs_newline = 0;
79            if (regs->sr & AXI_UARTLITE_SR_TX_FIFO_FULL) {
80                return -1;
81            }
82        }
83        if (c == '\n') {
84            regs->tx_fifo = '\r';
85            /* the fifo may have filled after sending the '\r' */
86            if (regs->sr & AXI_UARTLITE_SR_TX_FIFO_FULL) {
87                needs_newline = 1;
88                /* even if the '\n' didn't get sent on this call, still
89                 * return '\n', as it will still eventually be sent */
90            } else {
91                regs->tx_fifo = '\n';
92            }
93        } else {
94            regs->tx_fifo = c;
95        }
96        return c;
97    }
98}
99
100static ssize_t axi_uartlite_write(ps_chardevice_t* d, const void* vdata,
101                                  size_t count, chardev_callback_t rcb UNUSED,
102                                  void* token UNUSED)
103{
104    const char *data = (const char*)vdata;
105    for (int i = 0; i < count; i++) {
106        if (axi_uartlite_putchar(d, *data++) < 0) {
107            return i;
108        }
109    }
110    return count;
111}
112
113static ssize_t axi_uartlite_read(ps_chardevice_t* d, void* vdata,
114                                 size_t count, chardev_callback_t rcb UNUSED,
115                                 void* token UNUSED)
116{
117    char *data = (char*)vdata;
118    for (int i = 0; i < count; i++) {
119        int ch = axi_uartlite_getchar(d);
120        if (ch != EOF) {
121            *data++ = ch;
122        } else {
123            return i;
124        }
125    }
126    return count;
127}
128
129int axi_uartlite_init(void* vaddr, ps_chardevice_t* dev)
130{
131
132    memset(dev, 0, sizeof(*dev));
133
134    dev->vaddr = vaddr;
135    dev->read  = &axi_uartlite_read;
136    dev->write = &axi_uartlite_write;
137
138    zynq_axi_uartlite_regs_t *regs = zynq_axi_uartlite_get_priv(dev);
139
140    // clear the fifos
141    regs->cr |= (AXI_UARTLITE_CR_RST_TX_FIFO | AXI_UARTLITE_CR_RST_RX_FIFO);
142
143    // disable interrupts
144    regs->cr &= ~AXI_UARTLITE_CR_ENABLE_INTR;
145
146    return 0;
147}
148
149int axi_uartlite_init_defn(const struct dev_defn* defn,
150                           const ps_io_ops_t* ops,
151                           ps_chardevice_t* dev)
152{
153
154    void *vaddr = chardev_map(defn, ops);
155    if (vaddr == NULL) {
156        return -1;
157    }
158
159    axi_uartlite_init(vaddr, dev);
160
161    dev->id    = defn->id;
162    dev->irqs  = defn->irqs;
163    dev->ioops = *ops;
164
165    return 0;
166}
167