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 <arch/arm64/periphmap.h>
9 #include <dev/interrupt.h>
10 #include <dev/uart.h>
11 #include <kernel/thread.h>
12 #include <lib/cbuf.h>
13 #include <lib/debuglog.h>
14 #include <pdev/driver.h>
15 #include <pdev/uart.h>
16 #include <platform/debug.h>
17 #include <reg.h>
18 #include <stdio.h>
19 #include <trace.h>
20 #include <zircon/boot/driver-config.h>
21
22 // PL011 implementation
23
24 // clang-format off
25 #define UART_DR (0x00)
26 #define UART_RSR (0x04)
27 #define UART_FR (0x18)
28 #define UART_ILPR (0x20)
29 #define UART_IBRD (0x24)
30 #define UART_FBRD (0x28)
31 #define UART_LCRH (0x2c)
32 #define UART_CR (0x30)
33 #define UART_IFLS (0x34)
34 #define UART_IMSC (0x38)
35 #define UART_TRIS (0x3c)
36 #define UART_TMIS (0x40)
37 #define UART_ICR (0x44)
38 #define UART_DMACR (0x48)
39 // clang-format on
40
41 #define UARTREG(base, reg) (*REG32((base) + (reg)))
42
43 #define RXBUF_SIZE 16
44
45 // values read from zbi
46 static vaddr_t uart_base = 0;
47 static uint32_t uart_irq = 0;
48
49 static cbuf_t uart_rx_buf;
50
51 /*
52 * Tx driven irq:
53 * NOTE: For the pl011, txim is the "ready to transmit" interrupt. So we must
54 * mask it when we no longer care about it and unmask it when we start
55 * xmitting.
56 */
57 static bool uart_tx_irq_enabled = false;
58 static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
59 true,
60 EVENT_FLAG_AUTOUNSIGNAL);
61
62 static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
63
pl011_mask_tx()64 static inline void pl011_mask_tx() {
65 UARTREG(uart_base, UART_IMSC) &= ~(1 << 5);
66 }
67
pl011_unmask_tx()68 static inline void pl011_unmask_tx() {
69 UARTREG(uart_base, UART_IMSC) |= (1 << 5);
70 }
71
pl011_uart_irq(void * arg)72 static interrupt_eoi pl011_uart_irq(void* arg) {
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 = static_cast<char>(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 return IRQ_EOI_DEACTIVATE;
101 }
102
pl011_uart_init(const void * driver_data,uint32_t length)103 static void pl011_uart_init(const void* driver_data, uint32_t length) {
104 // create circular buffer to hold received data
105 cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
106
107 // assumes interrupts are contiguous
108 zx_status_t status = register_int_handler(uart_irq, &pl011_uart_irq, NULL);
109 DEBUG_ASSERT(status == ZX_OK);
110
111 // clear all irqs
112 UARTREG(uart_base, UART_ICR) = 0x3ff;
113
114 // set fifo trigger level
115 UARTREG(uart_base, UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
116
117 // enable rx interrupt
118 UARTREG(uart_base, UART_IMSC) = (1 << 4) | // rxim
119 (1 << 6); // rtim
120
121 // enable receive
122 UARTREG(uart_base, UART_CR) |= (1 << 9); // rxen
123
124 // enable interrupt
125 unmask_interrupt(uart_irq);
126
127 if (dlog_bypass() == true) {
128 uart_tx_irq_enabled = false;
129 } else {
130 /* start up tx driven output */
131 printf("UART: started IRQ driven TX\n");
132 uart_tx_irq_enabled = true;
133 }
134 }
135
pl011_uart_getc(bool wait)136 static int pl011_uart_getc(bool wait) {
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 */
pl011_uart_pputc(char c)147 static int pl011_uart_pputc(char c) {
148 /* spin while fifo is full */
149 while (UARTREG(uart_base, UART_FR) & (1 << 5))
150 ;
151 UARTREG(uart_base, UART_DR) = c;
152
153 return 1;
154 }
155
pl011_uart_pgetc()156 static int pl011_uart_pgetc() {
157 if ((UARTREG(uart_base, UART_FR) & (1 << 4)) == 0) {
158 return UARTREG(uart_base, UART_DR);
159 } else {
160 return -1;
161 }
162 }
163
pl011_dputs(const char * str,size_t len,bool block,bool map_NL)164 static void pl011_dputs(const char* str, size_t len,
165 bool block, bool map_NL) {
166 spin_lock_saved_state_t state;
167 bool copied_CR = false;
168
169 if (!uart_tx_irq_enabled) {
170 block = false;
171 }
172 spin_lock_irqsave(&uart_spinlock, state);
173 while (len > 0) {
174 // Is FIFO Full ?
175 while (UARTREG(uart_base, UART_FR) & (1 << 5)) {
176 if (block) {
177 /* Unmask Tx interrupts before we block on the event */
178 pl011_unmask_tx();
179 spin_unlock_irqrestore(&uart_spinlock, state);
180 event_wait(&uart_dputc_event);
181 } else {
182 spin_unlock_irqrestore(&uart_spinlock, state);
183 arch_spinloop_pause();
184 }
185 spin_lock_irqsave(&uart_spinlock, state);
186 }
187 if (!copied_CR && map_NL && *str == '\n') {
188 copied_CR = true;
189 UARTREG(uart_base, UART_DR) = '\r';
190 } else {
191 copied_CR = false;
192 UARTREG(uart_base, UART_DR) = *str++;
193 len--;
194 }
195 }
196 spin_unlock_irqrestore(&uart_spinlock, state);
197 }
198
pl011_start_panic()199 static void pl011_start_panic() {
200 uart_tx_irq_enabled = false;
201 }
202
203 static const struct pdev_uart_ops uart_ops = {
204 .getc = pl011_uart_getc,
205 .pputc = pl011_uart_pputc,
206 .pgetc = pl011_uart_pgetc,
207 .start_panic = pl011_start_panic,
208 .dputs = pl011_dputs,
209 };
210
pl011_uart_init_early(const void * driver_data,uint32_t length)211 static void pl011_uart_init_early(const void* driver_data, uint32_t length) {
212 ASSERT(length >= sizeof(dcfg_simple_t));
213 auto driver = static_cast<const dcfg_simple_t*>(driver_data);
214 ASSERT(driver->mmio_phys && driver->irq);
215
216 uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
217 ASSERT(uart_base);
218 uart_irq = driver->irq;
219
220 UARTREG(uart_base, UART_CR) = (1 << 8) | (1 << 0); // tx_enable, uarten
221
222 pdev_register_uart(&uart_ops);
223 }
224
225 LK_PDEV_INIT(pl011_uart_init_early, KDRV_PL011_UART, pl011_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
226 LK_PDEV_INIT(pl011_uart_init, KDRV_PL011_UART, pl011_uart_init, LK_INIT_LEVEL_PLATFORM);
227