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