1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2014-2015 Travis Geiselbrecht
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7
8#include <reg.h>
9#include <stdio.h>
10#include <trace.h>
11#include <arch/arm64/periphmap.h>
12#include <lib/cbuf.h>
13#include <lib/debuglog.h>
14#include <kernel/thread.h>
15#include <dev/interrupt.h>
16#include <dev/uart.h>
17#include <platform/debug.h>
18#include <pdev/driver.h>
19#include <pdev/uart.h>
20#include <zircon/boot/driver-config.h>
21
22/* PL011 implementation */
23#define UART_DR    (0x00)
24#define UART_RSR   (0x04)
25#define UART_FR    (0x18)
26#define UART_ILPR  (0x20)
27#define UART_IBRD  (0x24)
28#define UART_FBRD  (0x28)
29#define UART_LCRH  (0x2c)
30#define UART_CR    (0x30)
31#define UART_IFLS  (0x34)
32#define UART_IMSC  (0x38)
33#define UART_TRIS  (0x3c)
34#define UART_TMIS  (0x40)
35#define UART_ICR   (0x44)
36#define UART_DMACR (0x48)
37
38#define UARTREG(base, reg)  (*REG32((base)  + (reg)))
39
40#define RXBUF_SIZE 16
41
42// values read from zbi
43static vaddr_t uart_base = 0;
44static uint32_t uart_irq = 0;
45
46static cbuf_t uart_rx_buf;
47
48/*
49 * Tx driven irq:
50 * NOTE: For the pl011, txim is the "ready to transmit" interrupt. So we must
51 * mask it when we no longer care about it and unmask it when we start
52 * xmitting.
53 */
54static bool uart_tx_irq_enabled = false;
55static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
56                                                      true,
57                                                      EVENT_FLAG_AUTOUNSIGNAL);
58
59static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
60
61static inline void pl011_mask_tx(void)
62{
63    UARTREG(uart_base, UART_IMSC) &= ~(1<<5);
64}
65
66static inline void pl011_unmask_tx(void)
67{
68    UARTREG(uart_base, UART_IMSC) |= (1<<5);
69}
70
71static void pl011_uart_irq(void *arg)
72{
73    /* read interrupt status and mask */
74    uint32_t isr = UARTREG(uart_base, UART_TMIS);
75
76    if (isr & ((1<<4) | (1<<6))) { // rxmis
77        /* while fifo is not empty, read chars out of it */
78        while ((UARTREG(uart_base, UART_FR) & (1<<4)) == 0) {
79            /* if we're out of rx buffer, mask the irq instead of handling it */
80            if (cbuf_space_avail(&uart_rx_buf) == 0) {
81                UARTREG(uart_base, UART_IMSC) &= ~((1<<4)|(1<<6)); // !rxim
82                break;
83            }
84
85            char c = UARTREG(uart_base, UART_DR);
86            cbuf_write_char(&uart_rx_buf, c);
87        }
88    }
89    spin_lock(&uart_spinlock);
90    if (isr & (1<<5)) {
91        /*
92         * Signal any waiting Tx and mask Tx interrupts once we
93         * wakeup any blocked threads
94         */
95        event_signal(&uart_dputc_event, true);
96        pl011_mask_tx();
97    }
98    spin_unlock(&uart_spinlock);
99}
100
101static void pl011_uart_init(const void* driver_data, uint32_t length)
102{
103    // create circular buffer to hold received data
104    cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
105
106    // assumes interrupts are contiguous
107    zx_status_t status = register_int_handler(uart_irq, &pl011_uart_irq, NULL);
108    DEBUG_ASSERT(status == ZX_OK);
109
110    // clear all irqs
111    UARTREG(uart_base, UART_ICR) = 0x3ff;
112
113    // set fifo trigger level
114    UARTREG(uart_base, UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
115
116    // enable rx interrupt
117    UARTREG(uart_base, UART_IMSC) = (1 << 4 ) |  //  rxim
118                                    (1 << 6);    //  rtim
119
120    // enable receive
121    UARTREG(uart_base, UART_CR) |= (1<<9); // rxen
122
123    // enable interrupt
124    unmask_interrupt(uart_irq);
125
126    if (dlog_bypass() == true)
127        uart_tx_irq_enabled = false;
128    else {
129        /* start up tx driven output */
130        printf("UART: started IRQ driven TX\n");
131        uart_tx_irq_enabled = true;
132    }
133}
134
135static int pl011_uart_getc(bool wait)
136{
137    char c;
138    if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) {
139        UARTREG(uart_base, UART_IMSC) |= ((1<<4)|(1<<6)); // rxim
140        return c;
141    }
142
143    return ZX_ERR_INTERNAL;
144}
145
146/* panic-time getc/putc */
147static int pl011_uart_pputc(char c)
148{
149    /* spin while fifo is full */
150    while (UARTREG(uart_base, UART_FR) & (1<<5))
151        ;
152    UARTREG(uart_base, UART_DR) = c;
153
154    return 1;
155}
156
157static int pl011_uart_pgetc(void)
158{
159    if ((UARTREG(uart_base, UART_FR) & (1<<4)) == 0) {
160        return UARTREG(uart_base, UART_DR);
161    } else {
162        return -1;
163    }
164}
165
166static void pl011_dputs(const char* str, size_t len,
167                        bool block, bool map_NL)
168{
169    spin_lock_saved_state_t state;
170    bool copied_CR = false;
171
172    if (!uart_tx_irq_enabled)
173        block = false;
174    spin_lock_irqsave(&uart_spinlock, state);
175    while (len > 0) {
176        // Is FIFO Full ?
177        while (UARTREG(uart_base, UART_FR) & (1<<5)) {
178            if (block) {
179                /* Unmask Tx interrupts before we block on the event */
180                pl011_unmask_tx();
181                spin_unlock_irqrestore(&uart_spinlock, state);
182                event_wait(&uart_dputc_event);
183            } else {
184                spin_unlock_irqrestore(&uart_spinlock, state);
185                arch_spinloop_pause();
186            }
187            spin_lock_irqsave(&uart_spinlock, state);
188        }
189        if (!copied_CR && map_NL && *str == '\n') {
190            copied_CR = true;
191            UARTREG(uart_base, UART_DR) = '\r';
192        } else {
193            copied_CR = false;
194            UARTREG(uart_base, UART_DR) = *str++;
195            len--;
196        }
197    }
198    spin_unlock_irqrestore(&uart_spinlock, state);
199}
200
201static void pl011_start_panic(void)
202{
203    uart_tx_irq_enabled = false;
204}
205
206static const struct pdev_uart_ops uart_ops = {
207    .getc = pl011_uart_getc,
208    .pputc = pl011_uart_pputc,
209    .pgetc = pl011_uart_pgetc,
210    .start_panic = pl011_start_panic,
211    .dputs = pl011_dputs,
212};
213
214static void pl011_uart_init_early(const void* driver_data, uint32_t length) {
215    ASSERT(length >= sizeof(dcfg_simple_t));
216    const dcfg_simple_t* driver = driver_data;
217    ASSERT(driver->mmio_phys && driver->irq);
218
219    uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
220    ASSERT(uart_base);
221    uart_irq = driver->irq;
222
223    UARTREG(uart_base, UART_CR) = (1<<8)|(1<<0); // tx_enable, uarten
224
225    pdev_register_uart(&uart_ops);
226}
227
228LK_PDEV_INIT(pl011_uart_init_early, KDRV_PL011_UART, pl011_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
229LK_PDEV_INIT(pl011_uart_init, KDRV_PL011_UART, pl011_uart_init, LK_INIT_LEVEL_PLATFORM);
230