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