1 /*
2 * Copyright (c) 2012 Kent Ryhorchuk
3 * Copyright (c) 2016 Erik Gilling
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9
10 #include <arch/arm/cm.h>
11 #include <assert.h>
12 #include <dev/uart.h>
13 #include <lib/cbuf.h>
14 #include <lib/io.h>
15 #include <platform/rcc.h>
16 #include <stdint.h>
17 #include <stm32f0xx.h>
18 #include <target/debugconfig.h>
19
20 typedef USART_TypeDef stm32_usart_t;
21
22 #define RXBUF_SIZE 16
23
24 #ifdef ENABLE_UART1
25 cbuf_t uart1_rx_buf;
26 #ifndef UART1_FLOWCONTROL
27 #define UART1_FLOWCONTROL 0x0
28 #endif
29 #endif
30
31 #ifdef ENABLE_UART2
32 cbuf_t uart2_rx_buf;
33 #ifndef UART2_FLOWCONTROL
34 #define UART2_FLOWCONTROL 0x0
35 #endif
36 #endif
37
38 #ifdef ENABLE_UART3
39 cbuf_t uart3_rx_buf;
40 #ifndef UART3_FLOWCONTROL
41 #define UART3_FLOWCONTROL 0x0
42 #endif
43 #endif
44
45 #ifdef ENABLE_UART4
46 cbuf_t uart4_rx_buf;
47 #ifndef UART4_FLOWCONTROL
48 #define UART4_FLOWCONTROL 0x0
49 #endif
50 #endif
51
stm32_usart_init1_early(stm32_usart_t * usart,uint16_t flow_control,int irqn)52 static void stm32_usart_init1_early(stm32_usart_t *usart,
53 uint16_t flow_control, int irqn) {
54 uint32_t baud_rate = 115200;
55
56 // Ensure USART is disabled before configuring it.
57 usart->CR1 = 0;
58
59 // Set stop bits to 1 (CR2[13:12] = 00b)
60 usart->CR2 = 0;
61
62
63 // Go with the defaults of:
64 // word length: 8 bits
65 // parity: disabled
66 // Enable TX and RX.
67 usart->CR1 = USART_CR1_TE | USART_CR1_RE;
68
69 usart->CR3 = flow_control;
70
71 // TODO(konkers): Add rcc API for querying clock freq.
72 uint32_t apb_clock = 48000000;
73
74 usart->BRR = (apb_clock + baud_rate / 2) / baud_rate;
75
76 // Leave IRQs disabled until init.
77 NVIC_DisableIRQ(irqn);
78
79 // Enable UART.
80 usart->CR1 |= USART_CR1_UE;
81 }
82
stm32_usart_init1(stm32_usart_t * usart,int irqn,cbuf_t * rxbuf)83 static void stm32_usart_init1(stm32_usart_t *usart, int irqn, cbuf_t *rxbuf) {
84 cbuf_initialize(rxbuf, RXBUF_SIZE);
85 // Enable RX not empty interrupt.
86 usart->CR1 |= USART_CR1_RXNEIE;
87 NVIC_EnableIRQ(irqn);
88 }
89
90 #if defined(ENABLE_UART3) && defined(ENABLE_UART4)
91 #error UART3 and 4 share an interrupt; not supported
92 #endif
93
uart_init_early(void)94 void uart_init_early(void) {
95 #ifdef ENABLE_UART1
96 stm32_rcc_set_enable(STM32_RCC_CLK_USART1, true);
97 #endif
98 #ifdef ENABLE_UART2
99 stm32_rcc_set_enable(STM32_RCC_CLK_USART2, true);
100 #endif
101 #ifdef ENABLE_UART3
102 stm32_rcc_set_enable(STM32_RCC_CLK_USART3, true);
103 #endif
104 #ifdef ENABLE_UART4
105 stm32_rcc_set_enable(STM32_RCC_CLK_USART4, true);
106 #endif
107
108 #ifdef ENABLE_UART1
109 stm32_usart_init1_early(USART1, UART1_FLOWCONTROL, USART1_IRQn);
110 #endif
111 #ifdef ENABLE_UART2
112 stm32_usart_init1_early(USART2, UART2_FLOWCONTROL, USART2_IRQn);
113 #endif
114 #ifdef ENABLE_UART3
115 stm32_usart_init1_early(USART3, UART3_FLOWCONTROL, USART3_4_IRQn);
116 #endif
117 #ifdef ENABLE_UART4
118 stm32_usart_init1_early(USART4, UART4_FLOWCONTROL, USART3_4_IRQn);
119 #endif
120 }
121
uart_init(void)122 void uart_init(void) {
123 #ifdef ENABLE_UART1
124 stm32_usart_init1(USART1, USART1_IRQn, &uart1_rx_buf);
125 #endif
126 #ifdef ENABLE_UART2
127 stm32_usart_init1(USART2, USART2_IRQn, &uart2_rx_buf);
128 #endif
129 #ifdef ENABLE_UART3
130 stm32_usart_init1(USART3, USART3_4_IRQn, &uart3_rx_buf);
131 #endif
132 #ifdef ENABLE_UART4
133 stm32_usart_init1(USART4, USART3_4_IRQn, &uart4_rx_buf);
134 #endif
135 }
136
137 // I'm seeing a weird issue with my nucleo-f072rb board where rx interrupts
138 // don't fire right after a reset by the on board programmer. If I hit
139 // the reset button, rx works fine. I can live with this. YMMV.
stm32_uart_rx_irq(stm32_usart_t * usart,cbuf_t * rxbuf)140 static void stm32_uart_rx_irq(stm32_usart_t *usart, cbuf_t *rxbuf) {
141 arm_cm_irq_entry();
142
143 bool resched = false;
144 while (usart->ISR & USART_ISR_RXNE) {
145 if (!cbuf_space_avail(rxbuf)) {
146 // Overflow - let flow control do its thing by not
147 // reading the from the FIFO.
148 usart->CR1 &= ~USART_CR1_RXNEIE;
149 break;
150 }
151
152 char c = usart->RDR;
153 cbuf_write_char(rxbuf, c, false);
154 resched = true;
155 }
156
157 arm_cm_irq_exit(resched);
158 }
159
stm32_get_rxbuf(int port)160 static cbuf_t *stm32_get_rxbuf(int port) {
161 #if CONSOLE_HAS_INPUT_BUFFER
162 if (DEBUG_UART == port) {
163 return &console_input_cbuf;
164 }
165 #endif
166 switch (port) {
167 #ifdef ENABLE_UART1
168 case 1:
169 return &uart1_rx_buf;
170 #endif
171 #ifdef ENABLE_UART2
172 case 2:
173 return &uart2_rx_buf;
174 #endif
175 #ifdef ENABLE_UART3
176 case 3:
177 return &uart3_rx_buf;
178 #endif
179 #ifdef ENABLE_UART4
180 case 4:
181 return &uart4_rx_buf;
182 #endif
183 default:
184 ASSERT(false);
185 return 0;
186 }
187 }
188
189 #ifdef ENABLE_UART1
stm32_USART1_IRQ(void)190 void stm32_USART1_IRQ(void) {
191 stm32_uart_rx_irq(USART1, stm32_get_rxbuf(1));
192 }
193 #endif
194
195 #ifdef ENABLE_UART2
stm32_USART2_IRQ(void)196 void stm32_USART2_IRQ(void) {
197 stm32_uart_rx_irq(USART2, stm32_get_rxbuf(2));
198 }
199 #endif
200
201 #ifdef ENABLE_UART3
stm32_USART3_4_IRQ(void)202 void stm32_USART3_4_IRQ(void) {
203 stm32_uart_rx_irq(USART3, stm32_get_rxbuf(3));
204 }
205 #endif
206
207 #ifdef ENABLE_UART4
stm32_USART3_4_IRQ(void)208 void stm32_USART3_4_IRQ(void) {
209 stm32_uart_rx_irq(USART4, stm32_get_rxbuf(4));
210 }
211 #endif
212
213
stm32_usart_putc(stm32_usart_t * usart,char c)214 static void stm32_usart_putc(stm32_usart_t *usart, char c) {
215 while ((usart->ISR & USART_ISR_TXE) == 0);
216 usart->TDR = c;
217 while ((usart->ISR & USART_ISR_TC) == 0);
218 }
219
stm32_usart_getc(stm32_usart_t * usart,cbuf_t * rxbuf,bool wait)220 static int stm32_usart_getc(stm32_usart_t *usart, cbuf_t *rxbuf, bool wait) {
221 char c;
222 cbuf_read_char(rxbuf, &c, wait);
223 if (cbuf_space_avail(rxbuf) > RXBUF_SIZE/2)
224 usart->CR1 |= USART_CR1_RXNEIE;
225
226 return c;
227 }
228
stm32_get_usart(int port)229 static stm32_usart_t *stm32_get_usart(int port) {
230 switch (port) {
231 #ifdef ENABLE_UART1
232 case 1:
233 return USART1;
234 #endif
235 #ifdef ENABLE_UART2
236 case 2:
237 return USART2;
238 #endif
239 #ifdef ENABLE_UART3
240 case 3:
241 return USART3;
242 #endif
243 #ifdef ENABLE_UART4
244 case 4:
245 return USART4;
246 #endif
247 default:
248 ASSERT(false);
249 return 0;
250 }
251
252 }
253
uart_putc(int port,char c)254 int uart_putc(int port, char c) {
255 stm32_usart_t *usart = stm32_get_usart(port);
256 stm32_usart_putc(usart, c);
257 return 1;
258 }
259
uart_getc(int port,bool wait)260 int uart_getc(int port, bool wait) {
261 cbuf_t *rxbuf = stm32_get_rxbuf(port);
262 stm32_usart_t *usart = stm32_get_usart(port);
263
264 return stm32_usart_getc(usart, rxbuf, wait);
265 }
266
uart_flush_tx(int port)267 void uart_flush_tx(int port) {}
268
uart_flush_rx(int port)269 void uart_flush_rx(int port) {}
270
uart_init_port(int port,uint baud)271 void uart_init_port(int port, uint baud) {
272 // TODO - later
273 PANIC_UNIMPLEMENTED;
274 }
275