1 /*
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 <lk/reg.h>
9 #include <stdio.h>
10 #include <lk/trace.h>
11 #include <lib/cbuf.h>
12 #include <dev/uart.h>
13 #include <kernel/thread.h>
14 #include <platform/interrupts.h>
15 #include <platform/debug.h>
16 #include <platform/qemu-virt.h>
17 #include <target/debugconfig.h>
18 
19 /* PL011 implementation */
20 #define UART_DR    (0x00)
21 #define UART_RSR   (0x04)
22 #define UART_TFR   (0x18)
23 #define UART_ILPR  (0x20)
24 #define UART_IBRD  (0x24)
25 #define UART_FBRD  (0x28)
26 #define UART_LCRH  (0x2c)
27 #define UART_CR    (0x30)
28 #define UART_IFLS  (0x34)
29 #define UART_IMSC  (0x38)
30 #define UART_TRIS  (0x3c)
31 #define UART_TMIS  (0x40)
32 #define UART_ICR   (0x44)
33 #define UART_DMACR (0x48)
34 
35 #define UARTREG(base, reg)  (*REG32((base)  + (reg)))
36 
37 #define RXBUF_SIZE 16
38 #define NUM_UART 1
39 
40 static cbuf_t uart_rx_buf[NUM_UART];
41 
uart_to_ptr(unsigned int n)42 static inline uintptr_t uart_to_ptr(unsigned int n) {
43     switch (n) {
44         default:
45         case 0:
46             return UART_BASE;
47     }
48 }
49 
uart_irq(void * arg)50 static enum handler_return uart_irq(void *arg) {
51     bool resched = false;
52     uint port = (uintptr_t)arg;
53     uintptr_t base = uart_to_ptr(port);
54 
55     /* read interrupt status and mask */
56     uint32_t isr = UARTREG(base, UART_TMIS);
57 
58     if (isr & (1<<4)) { // rxmis
59         cbuf_t *rxbuf = &uart_rx_buf[port];
60 
61         /* while fifo is not empty, read chars out of it */
62         while ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
63 #if CONSOLE_HAS_INPUT_BUFFER
64             if (port == DEBUG_UART) {
65                 char c = UARTREG(base, UART_DR);
66                 cbuf_write_char(&console_input_cbuf, c, false);
67             } else
68 #endif
69             {
70                 /* if we're out of rx buffer, mask the irq instead of handling it */
71                 if (cbuf_space_avail(rxbuf) == 0) {
72                     UARTREG(base, UART_IMSC) &= ~(1<<4); // !rxim
73                     break;
74                 }
75 
76                 char c = UARTREG(base, UART_DR);
77                 cbuf_write_char(rxbuf, c, false);
78             }
79 
80             resched = true;
81         }
82     }
83 
84     return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
85 }
86 
uart_init(void)87 void uart_init(void) {
88     for (size_t i = 0; i < NUM_UART; i++) {
89         uintptr_t base = uart_to_ptr(i);
90 
91         // create circular buffer to hold received data
92         cbuf_initialize(&uart_rx_buf[i], RXBUF_SIZE);
93 
94         // assumes interrupts are contiguous
95         register_int_handler(UART0_INT + i, &uart_irq, (void *)i);
96 
97         // clear all irqs
98         UARTREG(base, UART_ICR) = 0x3ff;
99 
100         // set fifo trigger level
101         UARTREG(base, UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
102 
103         // enable rx interrupt
104         UARTREG(base, UART_IMSC) = (1<<4); // rxim
105 
106         // enable receive
107         UARTREG(base, UART_CR) |= (1<<9); // rxen
108 
109         // enable interrupt
110         unmask_interrupt(UART0_INT + i);
111     }
112 }
113 
uart_init_early(void)114 void uart_init_early(void) {
115     for (size_t i = 0; i < NUM_UART; i++) {
116         UARTREG(uart_to_ptr(i), UART_CR) = (1<<8)|(1<<0); // tx_enable, uarten
117     }
118 }
119 
uart_putc(int port,char c)120 int uart_putc(int port, char c) {
121     uintptr_t base = uart_to_ptr(port);
122 
123     /* spin while fifo is full */
124     while (UARTREG(base, UART_TFR) & (1<<5))
125         ;
126     UARTREG(base, UART_DR) = c;
127 
128     return 1;
129 }
130 
uart_getc(int port,bool wait)131 int uart_getc(int port, bool wait) {
132     cbuf_t *rxbuf = &uart_rx_buf[port];
133 
134     char c;
135     if (cbuf_read_char(rxbuf, &c, wait) == 1) {
136         UARTREG(uart_to_ptr(port), UART_IMSC) = (1<<4); // rxim
137         return c;
138     }
139 
140     return -1;
141 }
142 
143 /* panic-time getc/putc */
uart_pputc(int port,char c)144 int uart_pputc(int port, char c) {
145     uintptr_t base = uart_to_ptr(port);
146 
147     /* spin while fifo is full */
148     while (UARTREG(base, UART_TFR) & (1<<5))
149         ;
150     UARTREG(base, UART_DR) = c;
151 
152     return 1;
153 }
154 
uart_pgetc(int port)155 int uart_pgetc(int port) {
156     uintptr_t base = uart_to_ptr(port);
157 
158     if ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
159         return UARTREG(base, UART_DR);
160     } else {
161         return -1;
162     }
163 }
164 
165 
uart_flush_tx(int port)166 void uart_flush_tx(int port) {
167 }
168 
uart_flush_rx(int port)169 void uart_flush_rx(int port) {
170 }
171 
uart_init_port(int port,uint baud)172 void uart_init_port(int port, uint baud) {
173 }
174 
175