1// Copyright 2018 The Fuchsia Authors
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file or at
4// https://opensource.org/licenses/MIT
5
6#include <reg.h>
7#include <stdio.h>
8#include <trace.h>
9#include <arch/arm64/periphmap.h>
10#include <lib/cbuf.h>
11#include <lib/debuglog.h>
12#include <kernel/thread.h>
13#include <dev/interrupt.h>
14#include <dev/uart.h>
15#include <platform/debug.h>
16#include <pdev/driver.h>
17#include <pdev/uart.h>
18#include <zircon/boot/driver-config.h>
19
20/* Registers */
21#define MX8_URXD                    (0x00)
22#define MX8_UTXD                    (0x40)
23#define MX8_UCR1                    (0x80)
24#define MX8_UCR2                    (0x84)
25#define MX8_UCR3                    (0x88)
26#define MX8_UCR4                    (0x8C)
27#define MX8_UFCR                    (0x90)
28#define MX8_USR1                    (0x94)
29#define MX8_USR2                    (0x98)
30#define MX8_UTS                     (0xB4)
31
32/* UCR1 Bit Definition */
33#define UCR1_TRDYEN                 (1 << 13)
34#define UCR1_RRDYEN                 (1 << 9)
35#define UCR1_UARTEN                 (1 << 0)
36
37/* UCR2 Bit Definition */
38#define UCR2_TXEN                   (1 << 2)
39#define UCR2_RXEN                   (1 << 1)
40#define UCR2_SRST                   (1 << 0)
41
42/* UFCR Bit Definition */
43#define UFCR_TXTL(x)                (x << 10)
44#define UFCR_RXTL(x)                (x << 0)
45#define UFCR_MASK                   (0x3f)
46
47/* USR1 Bit Definition */
48#define USR1_TRDY                   (1 << 13)
49#define USR1_RRDY                   (1 << 9)
50
51/* USR2 Bit Definition */
52#define USR2_TXFE                   (1 << 14)
53
54/* UTS Bit Definition */
55#define UTS_TXEMPTY                 (1 << 6)
56#define UTS_RXEMPTY                 (1 << 5)
57#define UTS_TXFULL                  (1 << 4)
58#define UTS_RXFULL                  (1 << 3)
59
60#define RXBUF_SIZE 32
61
62
63// values read from zbi
64static bool initialized = false;
65static vaddr_t uart_base = 0;
66static uint32_t uart_irq = 0;
67static cbuf_t uart_rx_buf;
68// static cbuf_t uart_tx_buf;
69
70static bool uart_tx_irq_enabled = false;
71static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
72                                                      true,
73                                                      EVENT_FLAG_AUTOUNSIGNAL);
74
75static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
76
77#define UARTREG(reg)          (*(volatile uint32_t*)((uart_base)  + (reg)))
78
79static void uart_irq_handler(void *arg)
80{
81    /* read interrupt status and mask */
82    while ((UARTREG(MX8_USR1) & USR1_RRDY)) {
83        if (cbuf_space_avail(&uart_rx_buf) == 0) {
84                break;
85        }
86        char c = UARTREG(MX8_URXD) & 0xFF;
87        cbuf_write_char(&uart_rx_buf, c);
88    }
89
90    /* Signal if anyone is waiting to TX */
91    if (UARTREG(MX8_UCR1) & UCR1_TRDYEN) {
92        spin_lock(&uart_spinlock);
93        if (!(UARTREG(MX8_USR2) & UTS_TXFULL)) {
94            // signal
95            event_signal(&uart_dputc_event, true);
96        }
97        spin_unlock(&uart_spinlock);
98    }
99}
100
101
102
103/* panic-time getc/putc */
104static int imx_uart_pputc(char c)
105{
106    if (!uart_base) {
107        return -1;
108    }
109
110    /* spin while fifo is full */
111    while (UARTREG(MX8_UTS) & UTS_TXFULL)
112        ;
113    UARTREG(MX8_UTXD) = c;
114
115    return 1;
116}
117
118static int imx_uart_pgetc(void)
119{
120    if (!uart_base) {
121        return ZX_ERR_NOT_SUPPORTED;
122    }
123
124    if ((UARTREG(MX8_UTS) & UTS_RXEMPTY)) {
125        return ZX_ERR_INTERNAL;
126    }
127
128   return UARTREG(MX8_URXD);
129}
130
131static int imx_uart_getc(bool wait)
132{
133    if (!uart_base) {
134        return ZX_ERR_NOT_SUPPORTED;
135    }
136
137    if (initialized) {
138        char c;
139        if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) {
140            return c;
141        }
142        return ZX_ERR_INTERNAL;
143    } else {
144        // Interrupts are not enabled yet. Use panic calls for now
145        return imx_uart_pgetc();
146    }
147
148}
149
150static void imx_dputs(const char* str, size_t len,
151                        bool block, bool map_NL)
152{
153    spin_lock_saved_state_t state;
154    bool copied_CR = false;
155
156    if (!uart_base) {
157        return;
158    }
159    if (!uart_tx_irq_enabled) {
160        block = false;
161    }
162    spin_lock_irqsave(&uart_spinlock, state);
163
164    while (len > 0) {
165        // is FIFO full?
166        while ((UARTREG(MX8_UTS) & UTS_TXFULL)) {
167            spin_unlock_irqrestore(&uart_spinlock, state);
168            if (block) {
169                event_wait(&uart_dputc_event);
170            } else {
171                arch_spinloop_pause();
172            }
173            spin_lock_irqsave(&uart_spinlock, state);
174        }
175        if (*str == '\n' && map_NL && !copied_CR) {
176            copied_CR = true;
177            imx_uart_pputc('\r');
178        } else {
179            copied_CR = false;
180            imx_uart_pputc(*str++);
181            len--;
182        }
183    }
184    spin_unlock_irqrestore(&uart_spinlock, state);
185}
186
187static void imx_start_panic(void)
188{
189    uart_tx_irq_enabled = false;
190}
191
192static const struct pdev_uart_ops uart_ops = {
193    .getc = imx_uart_getc,
194    .pputc = imx_uart_pputc,
195    .pgetc = imx_uart_pgetc,
196    .start_panic = imx_start_panic,
197    .dputs = imx_dputs,
198};
199
200static void imx_uart_init(const void* driver_data, uint32_t length)
201{
202    uint32_t regVal;
203
204    // create circular buffer to hold received data
205    cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
206
207    // register uart irq
208    register_int_handler(uart_irq, &uart_irq_handler, NULL);
209
210    // set rx fifo threshold to 1 character
211    regVal = UARTREG(MX8_UFCR);
212    regVal &= ~UFCR_RXTL(UFCR_MASK);
213    regVal &= ~UFCR_TXTL(UFCR_MASK);
214    regVal |= UFCR_RXTL(1);
215    regVal |= UFCR_TXTL(0x2);
216    UARTREG(MX8_UFCR) = regVal;
217
218    // enable rx interrupt
219    regVal = UARTREG(MX8_UCR1);
220    regVal |= UCR1_RRDYEN;
221    if (dlog_bypass() == false) {
222        // enable tx interrupt
223        regVal |= UCR1_TRDYEN;
224    }
225    UARTREG(MX8_UCR1) = regVal;
226
227    // enable rx and tx transmisster
228    regVal = UARTREG(MX8_UCR2);
229    regVal |= UCR2_RXEN | UCR2_TXEN;
230    UARTREG(MX8_UCR2) = regVal;
231
232    if (dlog_bypass() == true)
233        uart_tx_irq_enabled = false;
234    else {
235        /* start up tx driven output */
236        printf("UART: started IRQ driven TX\n");
237        uart_tx_irq_enabled = true;
238    }
239
240    initialized = true;
241
242    // enable interrupts
243    unmask_interrupt(uart_irq);
244}
245
246static void imx_uart_init_early(const void* driver_data, uint32_t length) {
247    ASSERT(length >= sizeof(dcfg_simple_t));
248    const dcfg_simple_t* driver = driver_data;
249    ASSERT(driver->mmio_phys && driver->irq);
250
251    uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
252    ASSERT(uart_base);
253    uart_irq = driver->irq;
254
255    pdev_register_uart(&uart_ops);
256}
257
258LK_PDEV_INIT(imx_uart_init_early, KDRV_NXP_IMX_UART, imx_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
259LK_PDEV_INIT(imx_uart_init, KDRV_NXP_IMX_UART, imx_uart_init, LK_INIT_LEVEL_PLATFORM);
260