1 /*
2  * Copyright (c) 2014 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/bits.h>
10 #include <stdio.h>
11 #include <lk/trace.h>
12 #include <lib/cbuf.h>
13 #include <dev/uart.h>
14 #include <kernel/thread.h>
15 #include <kernel/spinlock.h>
16 #include <platform/interrupts.h>
17 #include <platform/debug.h>
18 #include <platform/alterasoc.h>
19 
20 #define UART_THR (0x00)
21 #define UART_RBR (0x00)
22 #define UART_DLL (0x00)
23 #define UART_IER (0x04)
24 #define UART_DLH (0x04)
25 #define UART_IIR (0x08)
26 #define UART_FCR (0x08)
27 #define UART_LCR (0x0c)
28 #define UART_MCR (0x10)
29 #define UART_LSR (0x14)
30 #define UART_MSR (0x18)
31 #define UART_SCR (0x1c)
32 #define UART_USR (0x7c)
33 #define UART_TFL (0x80)
34 #define UART_RFL (0x84)
35 #define UART_SRR (0x88)
36 
37 #define UARTREG(base, reg)  (*REG32((base)  + (reg)))
38 
39 #define RXBUF_SIZE 16
40 
41 static cbuf_t uart0_rx_buf;
42 static cbuf_t uart1_rx_buf;
43 
uart_to_ptr(unsigned int n)44 static inline uintptr_t uart_to_ptr(unsigned int n) { return (n == 0) ? UART0_BASE : UART1_BASE; }
uart_to_rxbuf(unsigned int n)45 static inline cbuf_t *uart_to_rxbuf(unsigned int n) { return (n == 0) ? &uart0_rx_buf : &uart1_rx_buf; }
46 
47 static spin_lock_t lock = SPIN_LOCK_INITIAL_VALUE;
48 
uart_irq(void * arg)49 static enum handler_return uart_irq(void *arg) {
50     bool resched = false;
51     uint port = (uint)arg;
52     uintptr_t base = uart_to_ptr(port);
53 
54     /* read interrupt identity */
55     uint32_t iir = UARTREG(base, UART_IIR);
56 
57     /* receive data available */
58     if (BITS(iir, 3, 0) == 0x4) {
59         cbuf_t *rxbuf = uart_to_rxbuf(port);
60 
61         /* while receive fifo not empty, read a char */
62         while ((UARTREG(base, UART_USR) & (1<<3))) {
63             char c = UARTREG(base, UART_RBR);
64             cbuf_write_char(rxbuf, c, false);
65 
66             resched = true;
67         }
68     }
69 
70     return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
71 }
72 
uart_init(void)73 void uart_init(void) {
74     cbuf_initialize(&uart0_rx_buf, RXBUF_SIZE);
75     cbuf_initialize(&uart1_rx_buf, RXBUF_SIZE);
76 
77     register_int_handler(UART0_INT, &uart_irq, (void *)0);
78     register_int_handler(UART1_INT, &uart_irq, (void *)1);
79 
80     // enable the fifo
81     UARTREG(uart_to_ptr(0), UART_FCR) = (1<<0); // enable rx fifo, set tx trigger to 0, set rx trigger to 0
82     UARTREG(uart_to_ptr(1), UART_FCR) = (1<<0); // enable rx fifo, set tx trigger to 0, set rx trigger to 0
83 
84     // enable rx interrupt
85     UARTREG(uart_to_ptr(0), UART_IER) = (1<<0); // receive data interrupt
86     UARTREG(uart_to_ptr(1), UART_IER) = (1<<0); // receive data interrupt
87 
88     unmask_interrupt(UART0_INT);
89     unmask_interrupt(UART1_INT);
90 }
91 
uart_init_early(void)92 void uart_init_early(void) {
93 #if 0
94     UARTREG(uart_to_ptr(0), UART_CR) = (1<<4); // txen
95     UARTREG(uart_to_ptr(1), UART_CR) = (1<<4); // txen
96 #endif
97 }
98 
uart_putc(int port,char c)99 int uart_putc(int port, char c) {
100     uintptr_t base = uart_to_ptr(port);
101 
102     spin_lock_saved_state_t state;
103     spin_lock_irqsave(&lock, state);
104 
105     /* spin while fifo is full */
106     while ((UARTREG(base, UART_USR) & (1<<1)) == 0) {
107     }
108     UARTREG(base, UART_THR) = c;
109 
110     spin_unlock_irqrestore(&lock, state);
111 
112     return 1;
113 }
114 
uart_getc(int port,bool wait)115 int uart_getc(int port, bool wait) {
116     cbuf_t *rxbuf = uart_to_rxbuf(port);
117 
118     char c;
119     if (cbuf_read_char(rxbuf, &c, wait) == 1)
120         return c;
121 
122     return -1;
123 }
124 
uart_flush_tx(int port)125 void uart_flush_tx(int port) {
126 }
127 
uart_flush_rx(int port)128 void uart_flush_rx(int port) {
129 }
130 
uart_init_port(int port,uint baud)131 void uart_init_port(int port, uint baud) {
132 }
133 
134