Add Tercel PHY reset synchronization
[microwatt.git] / lib / console.c
1 #include <stdint.h>
2 #include <stdbool.h>
3
4 #include "console.h"
5 #include "microwatt_soc.h"
6 #include "io.h"
7
8 #define UART_BAUDS 115200
9
10 /*
11 * Core UART functions to implement for a port
12 */
13
14 bool uart_is_std;
15
16 static uint64_t uart_base;
17
18 static unsigned long uart_divisor(unsigned long uart_freq, unsigned long bauds)
19 {
20 return uart_freq / (bauds * 16);
21 }
22
23 static uint64_t potato_uart_reg_read(int offset)
24 {
25 return readq(uart_base + offset);
26 }
27
28 static void potato_uart_reg_write(int offset, uint64_t val)
29 {
30 writeq(val, uart_base + offset);
31 }
32
33 static int potato_uart_rx_empty(void)
34 {
35 uint64_t val;
36
37 val = potato_uart_reg_read(POTATO_CONSOLE_STATUS);
38
39 if (val & POTATO_CONSOLE_STATUS_RX_EMPTY)
40 return 1;
41
42 return 0;
43 }
44
45 static int potato_uart_tx_full(void)
46 {
47 uint64_t val;
48
49 val = potato_uart_reg_read(POTATO_CONSOLE_STATUS);
50
51 if (val & POTATO_CONSOLE_STATUS_TX_FULL)
52 return 1;
53
54 return 0;
55 }
56
57 static char potato_uart_read(void)
58 {
59 uint64_t val;
60
61 val = potato_uart_reg_read(POTATO_CONSOLE_RX);
62
63 return (char)(val & 0x000000ff);
64 }
65
66 static void potato_uart_write(char c)
67 {
68 uint64_t val;
69
70 val = c;
71
72 potato_uart_reg_write(POTATO_CONSOLE_TX, val);
73 }
74
75 static void potato_uart_init(uint64_t uart_freq)
76 {
77 unsigned long div = uart_divisor(uart_freq, UART_BAUDS) - 1;
78 potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, div);
79 }
80
81 static void potato_uart_set_irq_en(bool rx_irq, bool tx_irq)
82 {
83 uint64_t en = 0;
84
85 if (rx_irq)
86 en |= POTATO_CONSOLE_IRQ_RX;
87 if (tx_irq)
88 en |= POTATO_CONSOLE_IRQ_TX;
89 potato_uart_reg_write(POTATO_CONSOLE_IRQ_EN, en);
90 }
91
92 static bool std_uart_rx_empty(void)
93 {
94 return !(readb(uart_base + UART_REG_LSR) & UART_REG_LSR_DR);
95 }
96
97 static uint8_t std_uart_read(void)
98 {
99 return readb(uart_base + UART_REG_RX);
100 }
101
102 static bool std_uart_tx_full(void)
103 {
104 return !(readb(uart_base + UART_REG_LSR) & UART_REG_LSR_THRE);
105 }
106
107 static void std_uart_write(uint8_t c)
108 {
109 writeb(c, uart_base + UART_REG_TX);
110 }
111
112 static void std_uart_set_irq_en(bool rx_irq, bool tx_irq)
113 {
114 uint8_t ier = 0;
115
116 if (tx_irq)
117 ier |= UART_REG_IER_THRI;
118 if (rx_irq)
119 ier |= UART_REG_IER_RDI;
120 writeb(ier, uart_base + UART_REG_IER);
121 }
122
123 static void std_uart_init(uint64_t uart_freq)
124 {
125 unsigned long div = uart_divisor(uart_freq, UART_BAUDS);
126
127 writeb(UART_REG_LCR_DLAB, uart_base + UART_REG_LCR);
128 writeb(div & 0xff, uart_base + UART_REG_DLL);
129 writeb(div >> 8, uart_base + UART_REG_DLM);
130 writeb(UART_REG_LCR_8BIT, uart_base + UART_REG_LCR);
131 writeb(UART_REG_MCR_DTR |
132 UART_REG_MCR_RTS, uart_base + UART_REG_MCR);
133 writeb(UART_REG_FCR_EN_FIFO |
134 UART_REG_FCR_CLR_RCVR |
135 UART_REG_FCR_CLR_XMIT, uart_base + UART_REG_FCR);
136 }
137
138 int getchar(void)
139 {
140 if (uart_is_std) {
141 while (std_uart_rx_empty())
142 /* Do nothing */ ;
143 return std_uart_read();
144 } else {
145 while (potato_uart_rx_empty())
146 /* Do nothing */ ;
147 return potato_uart_read();
148 }
149 }
150
151 int putchar(int c)
152 {
153 if (uart_is_std) {
154 while(std_uart_tx_full())
155 /* Do Nothing */;
156 std_uart_write(c);
157 } else {
158 while (potato_uart_tx_full())
159 /* Do Nothing */;
160 potato_uart_write(c);
161 }
162 return c;
163 }
164
165 int puts(const char *str)
166 {
167 unsigned int i;
168
169 for (i = 0; *str; i++) {
170 char c = *(str++);
171 if (c == 10)
172 putchar(13);
173 putchar(c);
174 }
175 return 0;
176 }
177
178 #ifndef __USE_LIBC
179 size_t strlen(const char *s)
180 {
181 size_t len = 0;
182
183 while (*s++)
184 len++;
185
186 return len;
187 }
188 #endif
189
190 void console_init(void)
191 {
192 uint64_t sys_info;
193 uint64_t proc_freq;
194 uint64_t uart_info = 0;
195 uint64_t uart_freq = 0;
196
197 proc_freq = readq(SYSCON_BASE + SYS_REG_CLKINFO) & SYS_REG_CLKINFO_FREQ_MASK;
198 sys_info = readq(SYSCON_BASE + SYS_REG_INFO);
199
200 if (sys_info & SYS_REG_INFO_HAS_LARGE_SYSCON) {
201 uart_info = readq(SYSCON_BASE + SYS_REG_UART0_INFO);
202 uart_freq = uart_info & 0xffffffff;
203 }
204 if (uart_freq == 0)
205 uart_freq = proc_freq;
206
207 uart_base = UART_BASE;
208 if (uart_info & SYS_REG_UART_IS_16550) {
209 uart_is_std = true;
210 std_uart_init(proc_freq);
211 } else {
212 uart_is_std = false;
213 potato_uart_init(proc_freq);
214 }
215 }
216
217 void console_set_irq_en(bool rx_irq, bool tx_irq)
218 {
219 if (uart_is_std)
220 std_uart_set_irq_en(rx_irq, tx_irq);
221 else
222 potato_uart_set_irq_en(rx_irq, tx_irq);
223 }