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 <string.h>
14#include <stdlib.h>
15#include <platsupport/serial.h>
16#include "../../chardev.h"
17
18#define RHR_MASK                MASK(8)
19#define UARTDR                  0x000
20#define UARTFR                  0x018
21#define UARTIMSC                0x038
22#define UARTICR                 0x044
23#define PL011_UARTFR_TXFF       BIT(5)
24#define PL011_UARTFR_RXFE       BIT(4)
25
26#define REG_PTR(base, off)     ((volatile uint32_t *)((base) + (off)))
27
28int uart_getchar(ps_chardevice_t *d)
29{
30    int ch = EOF;
31
32    if ((*REG_PTR(d->vaddr, UARTFR) & PL011_UARTFR_RXFE) == 0) {
33        ch = *REG_PTR(d->vaddr, UARTDR) & RHR_MASK;
34    }
35    return ch;
36}
37
38int uart_putchar(ps_chardevice_t* d, int c)
39{
40    while ((*REG_PTR(d->vaddr, UARTFR) & PL011_UARTFR_TXFF) != 0);
41
42    *REG_PTR(d->vaddr, UARTDR) = c;
43    if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) {
44        uart_putchar(d, '\r');
45    }
46
47    return c;
48}
49
50static void
51uart_handle_irq(ps_chardevice_t* dev)
52{
53    *REG_PTR(dev->vaddr, UARTICR) = 0x7f0;
54}
55
56int uart_init(const struct dev_defn* defn,
57              const ps_io_ops_t* ops,
58              ps_chardevice_t* dev)
59{
60    memset(dev, 0, sizeof(*dev));
61    void* vaddr = chardev_map(defn, ops);
62    if (vaddr == NULL) {
63        return -1;
64    }
65
66    /* Set up all the  device properties. */
67    dev->id         = defn->id;
68    dev->vaddr      = (void*)vaddr;
69    dev->read       = &uart_read;
70    dev->write      = &uart_write;
71    dev->handle_irq = &uart_handle_irq;
72    dev->irqs       = defn->irqs;
73    dev->ioops      = *ops;
74    dev->flags      = SERIAL_AUTO_CR;
75
76    *REG_PTR(dev->vaddr, UARTIMSC) = 0x50;
77    return 0;
78}
79