blob: b39e186e24b97c49bf524bf6000bdd6f1ff500a9 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014-2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <reg.h>
24#include <stdio.h>
25#include <trace.h>
26#include <lib/cbuf.h>
27#include <kernel/thread.h>
28#include <platform/interrupts.h>
29#include <platform/debug.h>
30#include <platform/qemu-virt.h>
31
32/* PL011 implementation */
33#define UART_DR (0x00)
34#define UART_RSR (0x04)
35#define UART_TFR (0x18)
36#define UART_ILPR (0x20)
37#define UART_IBRD (0x24)
38#define UART_FBRD (0x28)
39#define UART_LCRH (0x2c)
40#define UART_CR (0x30)
41#define UART_IFLS (0x34)
42#define UART_IMSC (0x38)
43#define UART_TRIS (0x3c)
44#define UART_TMIS (0x40)
45#define UART_ICR (0x44)
46#define UART_DMACR (0x48)
47
48#define UARTREG(base, reg) (*REG32((base) + (reg)))
49
50#define RXBUF_SIZE 16
51#define NUM_UART 1
52
53static cbuf_t uart_rx_buf[NUM_UART];
54
55static inline uintptr_t uart_to_ptr(unsigned int n)
56{
57 switch (n) {
58 default:
59 case 0:
60 return UART_BASE;
61 }
62}
63
64static enum handler_return uart_irq(void *arg)
65{
66 bool resched = false;
67 uint port = (uintptr_t)arg;
68 uintptr_t base = uart_to_ptr(port);
69
70 /* read interrupt status and mask */
71 uint32_t isr = UARTREG(base, UART_TMIS);
72
73 if (isr & (1<<4)) { // rxmis
74 cbuf_t *rxbuf = &uart_rx_buf[port];
75
76 /* while fifo is not empty, read chars out of it */
77 while ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
78 /* if we're out of rx buffer, mask the irq instead of handling it */
79 if (cbuf_space_avail(rxbuf) == 0) {
80 UARTREG(base, UART_IMSC) &= ~(1<<4); // !rxim
81 break;
82 }
83
84 char c = UARTREG(base, UART_DR);
85 cbuf_write_char(rxbuf, c, false);
86
87 resched = true;
88 }
89 }
90
91 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
92}
93
94void uart_init(void)
95{
96 for (size_t i = 0; i < NUM_UART; i++) {
97 uintptr_t base = uart_to_ptr(i);
98
99 // create circular buffer to hold received data
100 cbuf_initialize(&uart_rx_buf[i], RXBUF_SIZE);
101
102 // assumes interrupts are contiguous
103 register_int_handler(UART0_INT + i, &uart_irq, (void *)i);
104
105 // clear all irqs
106 UARTREG(base, UART_ICR) = 0x3ff;
107
108 // set fifo trigger level
109 UARTREG(base, UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
110
111 // enable rx interrupt
112 UARTREG(base, UART_IMSC) = (1<<4); // rxim
113
114 // enable receive
115 UARTREG(base, UART_CR) |= (1<<9); // rxen
116
117 // enable interrupt
118 unmask_interrupt(UART0_INT + i);
119 }
120}
121
122void uart_init_early(void)
123{
124 for (size_t i = 0; i < NUM_UART; i++) {
125 UARTREG(uart_to_ptr(i), UART_CR) = (1<<8)|(1<<0); // tx_enable, uarten
126 }
127}
128
129int uart_putc(int port, char c)
130{
131 uintptr_t base = uart_to_ptr(port);
132
133 /* spin while fifo is full */
134 while (UARTREG(base, UART_TFR) & (1<<5))
135 ;
136 UARTREG(base, UART_DR) = c;
137
138 return 1;
139}
140
141int uart_getc(int port, bool wait)
142{
143 cbuf_t *rxbuf = &uart_rx_buf[port];
144
145 char c;
146 if (cbuf_read_char(rxbuf, &c, wait) == 1) {
147 UARTREG(uart_to_ptr(port), UART_IMSC) = (1<<4); // rxim
148 return c;
149 }
150
151 return -1;
152}
153
154/* panic-time getc/putc */
155int uart_pputc(int port, char c)
156{
157 uintptr_t base = uart_to_ptr(port);
158
159 /* spin while fifo is full */
160 while (UARTREG(base, UART_TFR) & (1<<5))
161 ;
162 UARTREG(base, UART_DR) = c;
163
164 return 1;
165}
166
167int uart_pgetc(int port, bool wait)
168{
169 uintptr_t base = uart_to_ptr(port);
170
171 if ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
172 return UARTREG(base, UART_DR);
173 } else {
174 return -1;
175 }
176}
177
178
179void uart_flush_tx(int port)
180{
181}
182
183void uart_flush_rx(int port)
184{
185}
186
187void uart_init_port(int port, uint baud)
188{
189}
190