1 /*
2 * Copyright (c) 2018 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 <lk/trace.h>
10 #include <lib/cbuf.h>
11 #include <kernel/thread.h>
12 #include <platform.h>
13 #include <platform/interrupts.h>
14 #include <platform/debug.h>
15 #include <platform/sifive.h>
16 #include <sys/types.h>
17
18 #include "platform_p.h"
19
20 #define LOCAL_TRACE 0
21 #define POLLING_RX 0
22
23 static volatile unsigned int *const uart_base = (unsigned int *)UART0_BASE;
24
25 #define UART_TXDATA 0
26 #define UART_RXDATA 1
27 #define UART_TXCTRL 2
28 #define UART_RXCTRL 3
29 #define UART_IE 4
30 #define UART_IP 5
31 #define UART_DIV 6
32
33 #define RXBUF_SIZE 128
34 static char uart_rx_buf_data[RXBUF_SIZE];
35 static struct cbuf uart_rx_buf;
36 static spin_lock_t uart_tx_spin_lock = 0;
37
sifive_uart_write(int c)38 void sifive_uart_write(int c) {
39 spin_lock_saved_state_t state;
40 spin_lock_irqsave(&uart_tx_spin_lock, state);
41
42 // wait for tx fifo to clear
43 while (uart_base[UART_TXDATA] & (1<<31))
44 ;
45 uart_base[UART_TXDATA] = (c & 0xff);
46
47 spin_unlock_irqrestore(&uart_tx_spin_lock, state);
48 }
49
sifive_uart_read(char * c,bool wait)50 int sifive_uart_read(char *c, bool wait) {
51 #if !POLLING_RX
52 if (cbuf_read_char(&uart_rx_buf, c, wait) == 1) {
53 return 0;
54 }
55 return -1;
56 #else
57 // full polling mode for reads
58 int i = uart_base[UART_RXDATA];
59 if (i & (1<<31))
60 return -1;
61 *c = i & 0xff;
62 return 0;
63 #endif
64 }
65
sifive_uart_irq(void * unused)66 static enum handler_return sifive_uart_irq(void *unused) {
67 LTRACE;
68
69 enum handler_return ret = INT_NO_RESCHEDULE;
70 for (;;) {
71 int c = uart_base[UART_RXDATA];
72 if (c & (1<<31))
73 break; // nothing in the fifo
74
75 // stuff this char in the cbuf and try again
76 cbuf_write_char(&uart_rx_buf, c & 0xff, false);
77 ret = INT_RESCHEDULE;
78 }
79
80 return ret;
81 }
82
sifive_uart_early_init(void)83 void sifive_uart_early_init(void) {
84 uart_base[UART_DIV] = SIFIVE_FREQ / 115200;
85 uart_base[UART_TXCTRL] = 1; // txen
86 }
87
sifive_uart_init(void)88 void sifive_uart_init(void) {
89 cbuf_initialize_etc(&uart_rx_buf, RXBUF_SIZE, uart_rx_buf_data);
90
91 register_int_handler(SIFIVE_IRQ_UART0, sifive_uart_irq, NULL);
92
93 uart_base[UART_RXCTRL] = 1; // rxen, rxcnt = 0
94 uart_base[UART_IE] |= (1<<1); // rxwvm
95
96 #if !POLLING_RX
97 unmask_interrupt(SIFIVE_IRQ_UART0);
98 #endif
99 }
100
101