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