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