1 // Copyright 2017 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <arch/arm64/periphmap.h>
8 #include <dev/interrupt.h>
9 #include <dev/uart.h>
10 #include <kernel/thread.h>
11 #include <lib/cbuf.h>
12 #include <lib/debuglog.h>
13 #include <reg.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <trace.h>
17
18 #include <pdev/driver.h>
19 #include <pdev/uart.h>
20 #include <zircon/boot/driver-config.h>
21
22 // clang-format off
23
24 #define S905_UART_WFIFO (0x0)
25 #define S905_UART_RFIFO (0x4)
26 #define S905_UART_CONTROL (0x8)
27 #define S905_UART_STATUS (0xc)
28 #define S905_UART_IRQ_CONTROL (0x10)
29 #define S905_UART_REG5 (0x14)
30
31
32 #define S905_UART_CONTROL_INVRTS (1 << 31)
33 #define S905_UART_CONTROL_MASKERR (1 << 30)
34 #define S905_UART_CONTROL_INVCTS (1 << 29)
35 #define S905_UART_CONTROL_TXINTEN (1 << 28)
36 #define S905_UART_CONTROL_RXINTEN (1 << 27)
37 #define S905_UART_CONTROL_INVTX (1 << 26)
38 #define S905_UART_CONTROL_INVRX (1 << 25)
39 #define S905_UART_CONTROL_CLRERR (1 << 24)
40 #define S905_UART_CONTROL_RSTRX (1 << 23)
41 #define S905_UART_CONTROL_RSTTX (1 << 22)
42 #define S905_UART_CONTROL_XMITLEN (1 << 20)
43 #define S905_UART_CONTROL_XMITLEN_MASK (0x3 << 20)
44 #define S905_UART_CONTROL_PAREN (1 << 19)
45 #define S905_UART_CONTROL_PARTYPE (1 << 18)
46 #define S905_UART_CONTROL_STOPLEN (1 << 16)
47 #define S905_UART_CONTROL_STOPLEN_MASK (0x3 << 16)
48 #define S905_UART_CONTROL_TWOWIRE (1 << 15)
49 #define S905_UART_CONTROL_RXEN (1 << 13)
50 #define S905_UART_CONTROL_TXEN (1 << 12)
51 #define S905_UART_CONTROL_BAUD0 (1 << 0)
52 #define S905_UART_CONTROL_BAUD0_MASK (0xfff << 0)
53
54 #define S905_UART_STATUS_RXBUSY (1 << 26)
55 #define S905_UART_STATUS_TXBUSY (1 << 25)
56 #define S905_UART_STATUS_RXOVRFLW (1 << 24)
57 #define S905_UART_STATUS_CTSLEVEL (1 << 23)
58 #define S905_UART_STATUS_TXEMPTY (1 << 22)
59 #define S905_UART_STATUS_TXFULL (1 << 21)
60 #define S905_UART_STATUS_RXEMPTY (1 << 20)
61 #define S905_UART_STATUS_RXFULL (1 << 19)
62 #define S905_UART_STATUS_TXOVRFLW (1 << 18)
63 #define S905_UART_STATUS_FRAMEERR (1 << 17)
64 #define S905_UART_STATUS_PARERR (1 << 16)
65 #define S905_UART_STATUS_TXCOUNT_POS (8)
66 #define S905_UART_STATUS_TXCOUNT_MASK (0x7f << S905_UART_STATUS_TXCOUNT_POS)
67 #define S905_UART_STATUS_RXCOUNT_POS (0)
68 #define S905_UART_STATUS_RXCOUNT_MASK (0x7f << S905_UART_STATUS_RXCOUNT_POS)
69
70 #define UARTREG(base, reg) (*(volatile uint32_t*)((base) + (reg)))
71
72 #define RXBUF_SIZE 128
73 #define NUM_UART 5
74
75 #define S905_UART0_OFFSET (0x011084c0)
76 #define S905_UART1_OFFSET (0x011084dc)
77 #define S905_UART2_OFFSET (0x01108700)
78 #define S905_UART0_AO_OFFSET (0x081004c0)
79 #define S905_UART1_AO_OFFSET (0x081004e0)
80
81 // clang-format on
82
83 static cbuf_t uart_rx_buf;
84 static bool initialized = false;
85 static vaddr_t s905_uart_base = 0;
86 static uint32_t s905_uart_irq = 0;
87
88 /*
89 * Tx driven irq:
90 * According to the meson s905 UART spec
91 * https://dn.odroid.com/S905/DataSheet/S905_Public_Datasheet_V1.1.4.pdf
92 * 1) Tx Fifo depth is 64 bytes
93 * 2) The Misc register (aka irq control), by default will
94 * interrupt when the # of bytes in the fifo falls below 32
95 * but this can be changed if necessary (XMIT_IRQ_CNT).
96 * But no need to change this right now.
97 * 3) UART status register (TXCOUNT_MASK) holds the # of bytes
98 * in the Tx FIFO. More usefully, the TXFULL bit tells us when
99 * the Tx FIFO is full. We can use this to continue shoving
100 * data into the FIFO.
101 * 4) Setting TXINTEN will generate an interrupt each time a byte is
102 * read from the Tx FIFO. So we can leave the interrupt unmasked.
103 */
104 static bool uart_tx_irq_enabled = false;
105 static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
106 true,
107 EVENT_FLAG_AUTOUNSIGNAL);
108
109 static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
110
uart_irq(void * arg)111 static interrupt_eoi uart_irq(void* arg) {
112 uintptr_t base = (uintptr_t)arg;
113
114 /* read interrupt status and mask */
115 while ((UARTREG(base, S905_UART_STATUS) & S905_UART_STATUS_RXCOUNT_MASK) > 0) {
116 if (cbuf_space_avail(&uart_rx_buf) == 0) {
117 break;
118 }
119 char c = static_cast<char>(UARTREG(base, S905_UART_RFIFO));
120 cbuf_write_char(&uart_rx_buf, c);
121 }
122 if (UARTREG(s905_uart_base, S905_UART_CONTROL) & S905_UART_CONTROL_TXINTEN) {
123 spin_lock(&uart_spinlock);
124 if (!(UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_TXFULL))
125 /* Signal any waiting Tx */
126 {
127 event_signal(&uart_dputc_event, true);
128 }
129 spin_unlock(&uart_spinlock);
130 }
131
132 return IRQ_EOI_DEACTIVATE;
133 }
134
s905_uart_init(const void * driver_data,uint32_t length)135 static void s905_uart_init(const void* driver_data, uint32_t length) {
136 assert(s905_uart_base);
137 assert(s905_uart_irq);
138
139 // create circular buffer to hold received data
140 cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
141
142 //reset the port
143 UARTREG(s905_uart_base, S905_UART_CONTROL) |= S905_UART_CONTROL_RSTRX |
144 S905_UART_CONTROL_RSTTX |
145 S905_UART_CONTROL_CLRERR;
146 UARTREG(s905_uart_base, S905_UART_CONTROL) &= ~(S905_UART_CONTROL_RSTRX |
147 S905_UART_CONTROL_RSTTX |
148 S905_UART_CONTROL_CLRERR);
149 // enable rx and tx
150 UARTREG(s905_uart_base, S905_UART_CONTROL) |= S905_UART_CONTROL_TXEN |
151 S905_UART_CONTROL_RXEN;
152
153 uint32_t val;
154 val = S905_UART_CONTROL_INVRTS | S905_UART_CONTROL_RXINTEN |
155 S905_UART_CONTROL_TWOWIRE;
156 if (dlog_bypass() == false) {
157 val |= S905_UART_CONTROL_TXINTEN;
158 }
159 UARTREG(s905_uart_base, S905_UART_CONTROL) |= val;
160
161 // Set to interrupt every 1 rx byte
162 uint32_t temp2 = UARTREG(s905_uart_base, S905_UART_IRQ_CONTROL);
163 temp2 &= 0xffff0000;
164 temp2 |= (1 << 8) | (1);
165 UARTREG(s905_uart_base, S905_UART_IRQ_CONTROL) = temp2;
166
167 zx_status_t status = register_int_handler(s905_uart_irq, &uart_irq, (void*)s905_uart_base);
168 DEBUG_ASSERT(status == ZX_OK);
169
170 initialized = true;
171
172 if (dlog_bypass() == true) {
173 uart_tx_irq_enabled = false;
174 } else {
175 /* start up tx driven output */
176 printf("UART: started IRQ driven TX\n");
177 uart_tx_irq_enabled = true;
178 }
179
180 // enable interrupt
181 unmask_interrupt(s905_uart_irq);
182 }
183
184 /* panic-time getc/putc */
s905_uart_pputc(char c)185 static int s905_uart_pputc(char c) {
186 if (!s905_uart_base) {
187 return 0;
188 }
189
190 /* spin while fifo is full */
191 while (UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_TXFULL)
192 ;
193 UARTREG(s905_uart_base, S905_UART_WFIFO) = c;
194
195 return 1;
196 }
197
s905_uart_pgetc()198 static int s905_uart_pgetc() {
199 if (!s905_uart_base) {
200 return 0;
201 }
202
203 if ((UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_RXEMPTY) == 0) {
204 return UARTREG(s905_uart_base, S905_UART_RFIFO);
205 } else {
206 return ZX_ERR_INTERNAL;
207 }
208 }
209
s905_uart_getc(bool wait)210 static int s905_uart_getc(bool wait) {
211 if (!s905_uart_base) {
212 return -1;
213 }
214
215 if (initialized) {
216 // do cbuf stuff here
217 char c;
218 if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) {
219 return c;
220 }
221 return ZX_ERR_INTERNAL;
222
223 } else {
224 //Interrupts not online yet, use the panic calls for now.
225 return s905_uart_pgetc();
226 }
227 }
228
229 /*
230 * Keeping this simple for now, we try to write 1 byte at a time
231 * to the Tx FIFO. Blocking or spinning if the Tx FIFO is full.
232 * The event is signaled up from the interrupt handler, when a
233 * byte is read from the Tx FIFO.
234 * (setting TXINTEN results in the generation of an interrupt
235 * each time a byte is read from the Tx FIFO).
236 */
s905_dputs(const char * str,size_t len,bool block,bool map_NL)237 static void s905_dputs(const char* str, size_t len,
238 bool block, bool map_NL) {
239 spin_lock_saved_state_t state;
240 bool copied_CR = false;
241
242 if (!s905_uart_base) {
243 return;
244 }
245 if (!uart_tx_irq_enabled) {
246 block = false;
247 }
248 spin_lock_irqsave(&uart_spinlock, state);
249 while (len > 0) {
250 /* Is FIFO Full ? */
251 while (UARTREG(s905_uart_base, S905_UART_STATUS) & S905_UART_STATUS_TXFULL) {
252 spin_unlock_irqrestore(&uart_spinlock, state);
253 if (block) {
254 event_wait(&uart_dputc_event);
255 } else {
256 arch_spinloop_pause();
257 }
258 spin_lock_irqsave(&uart_spinlock, state);
259 }
260
261 if (*str == '\n' && map_NL && !copied_CR) {
262 copied_CR = true;
263 UARTREG(s905_uart_base, S905_UART_WFIFO) = '\r';
264 } else {
265 copied_CR = false;
266 UARTREG(s905_uart_base, S905_UART_WFIFO) = *str++;
267 len--;
268 }
269 }
270 spin_unlock_irqrestore(&uart_spinlock, state);
271 }
272
s905_uart_start_panic()273 static void s905_uart_start_panic() {
274 uart_tx_irq_enabled = false;
275 }
276
277 static const struct pdev_uart_ops s905_uart_ops = {
278 .getc = s905_uart_getc,
279 .pputc = s905_uart_pputc,
280 .pgetc = s905_uart_pgetc,
281 .start_panic = s905_uart_start_panic,
282 .dputs = s905_dputs,
283 };
284
s905_uart_init_early(const void * driver_data,uint32_t length)285 static void s905_uart_init_early(const void* driver_data, uint32_t length) {
286 ASSERT(length >= sizeof(dcfg_simple_t));
287 auto driver = static_cast<const dcfg_simple_t*>(driver_data);
288 ASSERT(driver->mmio_phys && driver->irq);
289
290 s905_uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
291 ASSERT(s905_uart_base);
292 s905_uart_irq = driver->irq;
293
294 pdev_register_uart(&s905_uart_ops);
295 }
296
297 LK_PDEV_INIT(s905_uart_init_early, KDRV_AMLOGIC_UART, s905_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
298 LK_PDEV_INIT(s905_uart_init, KDRV_AMLOGIC_UART, s905_uart_init, LK_INIT_LEVEL_PLATFORM);
299