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 "include/dev/uart/pl011.h"
9
10 #include <assert.h>
11 #include <lk/trace.h>
12 #include <lib/cbuf.h>
13 #include <lib/io.h>
14 #include <dev/uart.h>
15 #include <kernel/thread.h>
16 #include <platform/interrupts.h>
17
18 // PL011 UART driver
19
20 // PL011 registers
21 enum pl011_regs {
22 UART_DR = 0x00,
23 UART_RSR = 0x04,
24 UART_TFR = 0x18,
25 UART_ILPR = 0x20,
26 UART_IBRD = 0x24,
27 UART_FBRD = 0x28,
28 UART_LCRH = 0x2c,
29 UART_CR = 0x30,
30 UART_IFLS = 0x34,
31 UART_IMSC = 0x38,
32 UART_TRIS = 0x3c,
33 UART_TMIS = 0x40,
34 UART_ICR = 0x44,
35 UART_DMACR = 0x48
36 };
37
38 // Control register bits that are used in the driver
39 #define UART_TFR_RXFE (1<<4)
40 #define UART_TFR_TXFF (1<<5)
41 #define UART_TFR_RXFF (1<<6)
42 #define UART_TFR_TXFE (1<<7)
43
44 #define UART_IMSC_RXIM (1<<4)
45
46 #define UART_TMIS_RXMIS (1<<4)
47
48 #define UART_CR_UARTEN (1<<0)
49 #define UART_CR_TXEN (1<<8)
50 #define UART_CR_RXEN (1<<9)
51
52 // TODO: have these be configurable
53 #define RXBUF_SIZE 32
54 #define NUM_UART 1
55
56 struct pl011_struct {
57 struct pl011_config config;
58
59 cbuf_t uart_rx_buf;
60 };
61
62 static struct pl011_struct uart[NUM_UART];
63
write_uart_reg(uintptr_t base,size_t offset,uint32_t val)64 static inline void write_uart_reg(uintptr_t base, size_t offset, uint32_t val) {
65 mmio_write32((uint32_t *)(base + offset), val);
66 }
67
read_uart_reg(uintptr_t base,size_t offset)68 static inline uint32_t read_uart_reg(uintptr_t base, size_t offset) {
69 return mmio_read32((uint32_t *)(base + offset));
70 }
71
set_uart_reg_bits(uintptr_t base,size_t offset,uint32_t bits)72 static inline void set_uart_reg_bits(uintptr_t base, size_t offset, uint32_t bits) {
73 write_uart_reg(base, offset, read_uart_reg(base, offset) | bits);
74 }
75
clear_uart_reg_bits(uintptr_t base,size_t offset,uint32_t bits)76 static inline void clear_uart_reg_bits(uintptr_t base, size_t offset, uint32_t bits) {
77 write_uart_reg(base, offset, read_uart_reg(base, offset) & ~bits);
78 }
79
uart_to_ptr(unsigned int n)80 static inline uintptr_t uart_to_ptr(unsigned int n) {
81 DEBUG_ASSERT(n < NUM_UART);
82 return uart[n].config.base;
83 }
84
uart_irq(void * arg)85 static enum handler_return uart_irq(void *arg) {
86 struct pl011_struct *u = (struct pl011_struct *)arg;
87
88 /* read interrupt status and mask */
89 uint32_t isr = read_uart_reg(u->config.base, UART_TMIS);
90
91 bool resched = false;
92 if (isr & UART_TMIS_RXMIS) { // rxmis
93 cbuf_t *rxbuf = &uart->uart_rx_buf;
94
95 /* while fifo is not empty, read chars out of it */
96 while ((read_uart_reg(u->config.base, UART_TFR) & UART_TFR_RXFE) == 0) {
97 #if CONSOLE_HAS_INPUT_BUFFER
98 if (u->config.flag & PL011_FLAG_DEBUG_UART) {
99 char c = read_uart_reg(u->config.base, UART_DR);
100 cbuf_write_char(&console_input_cbuf, c, false);
101 } else
102 #endif
103 {
104 /* if we're out of rx buffer, mask the irq instead of handling it */
105 if (cbuf_space_avail(rxbuf) == 0) {
106 clear_uart_reg_bits(u->config.base, UART_IMSC, UART_IMSC_RXIM); // !rxim
107 break;
108 }
109
110 char c = read_uart_reg(u->config.base, UART_DR);
111 cbuf_write_char(rxbuf, c, false);
112 }
113
114 resched = true;
115 }
116 }
117
118 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
119 }
120
pl011_init(int port)121 void pl011_init(int port) {
122 uintptr_t base = uart_to_ptr(port);
123
124 // create circular buffer to hold received data
125 cbuf_initialize(&uart[port].uart_rx_buf, RXBUF_SIZE);
126
127 // assumes interrupts are contiguous
128 register_int_handler(uart[port].config.irq, &uart_irq, (void *)&uart[port]);
129
130 // clear all irqs
131 write_uart_reg(base, UART_ICR, 0x3ff);
132
133 // set fifo trigger level
134 write_uart_reg(base, UART_IFLS, 0); // 1/8 rxfifo, 1/8 txfifo
135
136 // enable rx interrupt
137 write_uart_reg(base, UART_IMSC, UART_IMSC_RXIM); // rxim
138
139 // enable receive
140 set_uart_reg_bits(base, UART_CR, UART_CR_RXEN); // rxen
141
142 // enable interrupt
143 unmask_interrupt(uart[port].config.irq);
144 }
145
pl011_init_early(int port,const struct pl011_config * config)146 void pl011_init_early(int port, const struct pl011_config *config) {
147 if (port >= NUM_UART) {
148 return;
149 }
150
151 // TODO: validate config
152 uart[port].config = *config;
153
154 write_uart_reg(uart[port].config.base, UART_CR, UART_CR_TXEN | UART_CR_UARTEN); // tx_enable, uarten
155 }
156
uart_putc(int port,char c)157 int uart_putc(int port, char c) {
158 uintptr_t base = uart_to_ptr(port);
159
160 /* spin while fifo is full */
161 while (read_uart_reg(base, UART_TFR) & UART_TFR_TXFF)
162 ;
163 write_uart_reg(base, UART_DR, c);
164
165 return 1;
166 }
167
uart_getc(int port,bool wait)168 int uart_getc(int port, bool wait) {
169 cbuf_t *rxbuf = &uart[port].uart_rx_buf;
170
171 char c;
172 if (cbuf_read_char(rxbuf, &c, wait) == 1) {
173 set_uart_reg_bits(uart[port].config.base, UART_IMSC, UART_IMSC_RXIM); // rxim
174 return c;
175 }
176
177 return -1;
178 }
179
180 /* panic-time getc/putc */
uart_pputc(int port,char c)181 int uart_pputc(int port, char c) {
182 uintptr_t base = uart_to_ptr(port);
183
184 /* spin while fifo is full */
185 while (read_uart_reg(base, UART_TFR) & UART_TFR_TXFF)
186 ;
187 write_uart_reg(base, UART_DR, c);
188
189 return 1;
190 }
191
uart_pgetc(int port)192 int uart_pgetc(int port) {
193 uintptr_t base = uart_to_ptr(port);
194
195 if ((read_uart_reg(base, UART_TFR) & UART_TFR_RXFE) == 0) {
196 unsigned char c = read_uart_reg(base, UART_DR);
197 return c;
198 } else {
199 return -1;
200 }
201 }
202
uart_flush_tx(int port)203 void uart_flush_tx(int port) {
204 }
205
uart_flush_rx(int port)206 void uart_flush_rx(int port) {
207 }
208
209 // TODO collapse this into the early/regular init routines
uart_init_port(int port,uint baud)210 void uart_init_port(int port, uint baud) {
211 }
212
213