ac745d9709387d7aec07b45ba3cc0d5019487822
[fw/altos] / src / stm32l0 / ao_serial_stm.c
1 /*
2  * Copyright © 2020 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include <ao.h>
16 #include <ao_exti.h>
17
18 static int
19 _ao_usart_tx_start(struct ao_stm_usart *usart)
20 {
21         if (!ao_fifo_empty(usart->tx_fifo)) {
22 #if HAS_SERIAL_SW_FLOW
23                 if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts) == 1) {
24                         ao_exti_enable(usart->gpio_cts, usart->pin_cts);
25                         return 0;
26                 }
27 #endif
28                 if (usart->reg->isr & (1 << STM_USART_ISR_TXE))
29                 {
30                         usart->tx_running = 1;
31                         usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
32                         ao_fifo_remove(usart->tx_fifo, usart->reg->tdr);
33                         ao_wakeup(&usart->tx_fifo);
34                         return 1;
35                 }
36         }
37         return 0;
38 }
39
40 #if HAS_SERIAL_SW_FLOW
41 static void
42 _ao_usart_cts(struct ao_stm_usart *usart)
43 {
44         if (_ao_usart_tx_start(usart))
45                 ao_exti_disable(usart->gpio_cts, usart->pin_cts);
46 }
47 #endif
48
49 static void
50 _ao_usart_rx(struct ao_stm_usart *usart, int is_stdin)
51 {
52         if (usart->reg->isr & (1 << STM_USART_ISR_RXNE)) {
53                 usart->reg->icr = (1 << STM_USART_ICR_ORECF);
54                 if (!ao_fifo_full(usart->rx_fifo)) {
55                         ao_fifo_insert(usart->rx_fifo, usart->reg->rdr);
56                         ao_wakeup(&usart->rx_fifo);
57                         if (is_stdin)
58                                 ao_wakeup(&ao_stdin_ready);
59 #if HAS_SERIAL_SW_FLOW
60                         /* If the fifo is nearly full, turn off RTS and wait
61                          * for it to drain a bunch
62                          */
63                         if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
64                                 ao_gpio_set(usart->gpio_rts, usart->pin_rts, 1);
65                                 usart->rts = 0;
66                         }
67 #endif
68                 } else {
69                         usart->reg->cr1 &= ~(1 << STM_USART_CR1_RXNEIE);
70                 }
71         }
72 }
73
74 static void
75 ao_usart_isr(struct ao_stm_usart *usart, int is_stdin)
76 {
77         _ao_usart_rx(usart, is_stdin);
78
79         if (!_ao_usart_tx_start(usart))
80                 usart->reg->cr1 &= ~(1<< STM_USART_CR1_TXEIE);
81
82         if (usart->reg->isr & (1 << STM_USART_ISR_TC)) {
83                 usart->tx_running = 0;
84                 usart->reg->cr1 &= ~(1 << STM_USART_CR1_TCIE);
85                 if (usart->draining) {
86                         usart->draining = 0;
87                         ao_wakeup(&usart->tx_fifo);
88                 }
89         }
90 }
91
92 static int
93 _ao_usart_pollchar(struct ao_stm_usart *usart)
94 {
95         int     c;
96
97         if (ao_fifo_empty(usart->rx_fifo))
98                 c = AO_READ_AGAIN;
99         else {
100                 uint8_t u;
101                 ao_fifo_remove(usart->rx_fifo,u);
102                 if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
103                         if (ao_fifo_barely(usart->rx_fifo))
104                                 usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
105                 }
106 #if HAS_SERIAL_SW_FLOW
107                 /* If we've cleared RTS, check if there's space now and turn it back on */
108                 if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
109                         ao_gpio_set(usart->gpio_rts, usart->pin_rts, 0);
110                         usart->rts = 1;
111                 }
112 #endif
113                 c = u;
114         }
115         return c;
116 }
117
118 static char
119 ao_usart_getchar(struct ao_stm_usart *usart)
120 {
121         int c;
122         ao_arch_block_interrupts();
123         while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
124                 ao_sleep(&usart->rx_fifo);
125         ao_arch_release_interrupts();
126         return (char) c;
127 }
128
129 #if 0
130 static inline uint8_t
131 _ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
132 {
133         return ao_sleep_for(&usart->rx_fifo, timeout);
134 }
135 #endif
136
137 static void
138 ao_usart_putchar(struct ao_stm_usart *usart, char c)
139 {
140         ao_arch_block_interrupts();
141         while (ao_fifo_full(usart->tx_fifo))
142                 ao_sleep(&usart->tx_fifo);
143         ao_fifo_insert(usart->tx_fifo, c);
144         _ao_usart_tx_start(usart);
145         ao_arch_release_interrupts();
146 }
147
148 #if 0
149 static void
150 ao_usart_drain(struct ao_stm_usart *usart)
151 {
152         ao_arch_block_interrupts();
153         while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
154                 usart->draining = 1;
155                 ao_sleep(&usart->tx_fifo);
156         }
157         ao_arch_release_interrupts();
158 }
159 #endif
160
161 const uint32_t ao_usart_speeds[] = {
162         [AO_SERIAL_SPEED_4800] = 4800,
163         [AO_SERIAL_SPEED_9600] = 9600,
164         [AO_SERIAL_SPEED_19200] = 19200,
165         [AO_SERIAL_SPEED_57600] = 57600,
166         [AO_SERIAL_SPEED_115200] = 115200,
167 };
168
169 static void
170 ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
171 {
172         if (speed > AO_SERIAL_SPEED_115200)
173                 return;
174         usart->reg->brr = AO_PCLK2 / ao_usart_speeds[speed];
175 }
176
177 static void
178 ao_usart_init(struct ao_stm_usart *usart, int hw_flow)
179 {
180         usart->reg->cr1 = ((0 << STM_USART_CR1_M1) |
181                            (0 << STM_USART_CR1_EOBIE) |
182                            (0 << STM_USART_CR1_RTOIE) |
183                            (0 << STM_USART_CR1_DEAT) |
184                            (0 << STM_USART_CR1_DEDT) |
185                            (0 << STM_USART_CR1_OVER8) |
186                            (0 << STM_USART_CR1_CMIE) |
187                            (0 << STM_USART_CR1_MME) |
188                            (0 << STM_USART_CR1_M0) |
189                            (0 << STM_USART_CR1_WAKE) |
190                            (0 << STM_USART_CR1_PCE) |
191                            (0 << STM_USART_CR1_PS) |
192                            (0 << STM_USART_CR1_PEIE) |
193                            (0 << STM_USART_CR1_TXEIE) |
194                            (0 << STM_USART_CR1_TCIE) |
195                            (1 << STM_USART_CR1_RXNEIE) |
196                            (0 << STM_USART_CR1_IDLEIE) |
197                            (1 << STM_USART_CR1_TE) |
198                            (1 << STM_USART_CR1_RE) |
199                            (0 << STM_USART_CR1_UESM) |
200                            (0 << STM_USART_CR1_UE));
201
202         usart->reg->cr2 = ((0 << STM_USART_CR2_ADD) |
203                            (0 << STM_USART_CR2_RTOEN) |
204                            (0 << STM_USART_CR2_ABRMOD) |
205                            (0 << STM_USART_CR2_ABREN) |
206                            (0 << STM_USART_CR2_MSBFIRST) |
207                            (0 << STM_USART_CR2_DATAINV) |
208                            (0 << STM_USART_CR2_TXINV) |
209                            (0 << STM_USART_CR2_RXINV) |
210                            (0 << STM_USART_CR2_SWAP) |
211                            (0 << STM_USART_CR2_LINEN) |
212                            (0 << STM_USART_CR2_STOP) |
213                            (0 << STM_USART_CR2_CLKEN) |
214                            (0 << STM_USART_CR2_CPOL) |
215                            (0 << STM_USART_CR2_CHPA) |
216                            (0 << STM_USART_CR2_LBCL) |
217                            (0 << STM_USART_CR2_LBDIE) |
218                            (0 << STM_USART_CR2_LBDL) |
219                            (0 << STM_USART_CR2_ADDM7));
220
221         uint32_t cr3 = ((0 << STM_USART_CR3_WUFIE) |
222                         (0 << STM_USART_CR3_WUS) |
223                         (0 << STM_USART_CR3_SCARCNT) |
224                         (0 << STM_USART_CR3_DEP) |
225                         (0 << STM_USART_CR3_DEM) |
226                         (0 << STM_USART_CR3_DDRE) |
227                         (0 << STM_USART_CR3_OVRDIS) |
228                         (0 << STM_USART_CR3_ONEBIT) |
229                         (0 << STM_USART_CR3_CTIIE) |
230                         (0 << STM_USART_CR3_CTSE) |
231                         (0 << STM_USART_CR3_RTSE) |
232                         (0 << STM_USART_CR3_DMAT) |
233                         (0 << STM_USART_CR3_DMAR) |
234                         (0 << STM_USART_CR3_SCEN) |
235                         (0 << STM_USART_CR3_NACK) |
236                         (0 << STM_USART_CR3_HDSEL) |
237                         (0 << STM_USART_CR3_IRLP) |
238                         (0 << STM_USART_CR3_IREN) |
239                         (0 << STM_USART_CR3_EIE));
240
241         if (hw_flow)
242                 cr3 |= ((1 << STM_USART_CR3_CTSE) |
243                         (1 << STM_USART_CR3_RTSE));
244
245         usart->reg->cr3 = cr3;
246
247         /* Pick a 9600 baud rate */
248         ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
249
250         /* Enable the usart */
251         usart->reg->cr1 |= (1 << STM_USART_CR1_UE);
252 }
253
254 #if HAS_SERIAL_1
255
256 struct ao_stm_usart ao_stm_usart1;
257
258 void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
259
260 char
261 ao_serial1_getchar(void)
262 {
263         return ao_usart_getchar(&ao_stm_usart1);
264 }
265
266 void
267 ao_serial1_putchar(char c)
268 {
269         ao_usart_putchar(&ao_stm_usart1, c);
270 }
271
272 int
273 _ao_serial1_pollchar(void)
274 {
275         return _ao_usart_pollchar(&ao_stm_usart1);
276 }
277
278 #if 0
279 uint8_t
280 _ao_serial1_sleep_for(uint16_t timeout)
281 {
282         return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
283 }
284 #endif
285
286 #if 0
287 void
288 ao_serial1_drain(void)
289 {
290         ao_usart_drain(&ao_stm_usart1);
291 }
292
293 void
294 ao_serial1_set_speed(uint8_t speed)
295 {
296         ao_usart_drain(&ao_stm_usart1);
297         ao_usart_set_speed(&ao_stm_usart1, speed);
298 }
299 #endif
300 #endif  /* HAS_SERIAL_1 */
301
302 #if HAS_SERIAL_2
303
304 struct ao_stm_usart ao_stm_usart2;
305
306 void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
307
308 char
309 ao_serial2_getchar(void)
310 {
311         return ao_usart_getchar(&ao_stm_usart2);
312 }
313
314 void
315 ao_serial2_putchar(char c)
316 {
317         ao_usart_putchar(&ao_stm_usart2, c);
318 }
319
320 int
321 _ao_serial2_pollchar(void)
322 {
323         return _ao_usart_pollchar(&ao_stm_usart2);
324 }
325
326 #if 0
327 uint8_t
328 _ao_serial2_sleep_for(uint16_t timeout)
329 {
330         return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
331 }
332
333 void
334 ao_serial2_drain(void)
335 {
336         ao_usart_drain(&ao_stm_usart2);
337 }
338
339 void
340 ao_serial2_set_speed(uint8_t speed)
341 {
342         ao_usart_drain(&ao_stm_usart2);
343         ao_usart_set_speed(&ao_stm_usart2, speed);
344 }
345 #endif
346
347 #if USE_SERIAL_2_FLOW && USE_SERIAL_2_SW_FLOW
348 void
349 ao_serial2_cts(void)
350 {
351         _ao_usart_cts(&ao_stm_usart2);
352 }
353 #endif
354
355 #endif  /* HAS_SERIAL_2 */
356
357 #if HAS_SERIAL_SW_FLOW
358 static void
359 ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
360                          void (*isr)(void),
361                          struct stm_gpio *port_rts,
362                          int pin_rts,
363                          struct stm_gpio *port_cts,
364                          int pin_cts)
365 {
366         /* Pull RTS low to note that there's space in the FIFO
367          */
368         ao_enable_output(port_rts, pin_rts, 0);
369         usart->gpio_rts = port_rts;
370         usart->pin_rts = pin_rts;
371         usart->rts = 1;
372
373         ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
374         usart->gpio_cts = port_cts;
375         usart->pin_cts = pin_cts;
376 }
377 #endif
378
379 #if 0
380 void
381 ao_serial_shutdown(void)
382 {
383 # if SERIAL_2_PA2_PA3
384         stm_moder_set(&stm_gpioa, 2, STM_MODER_INPUT);
385         stm_moder_set(&stm_gpioa, 3, STM_MODER_INPUT);
386 # elif SERIAL_2_PA9_PA10
387         stm_moder_set(&stm_gpioa, 9, STM_MODER_INPUT);
388         stm_moder_set(&stm_gpioa, 10, STM_MODER_INPUT);
389 # elif SERIAL_2_PA14_PA15
390         stm_moder_set(&stm_gpioa, 14, STM_MODER_INPUT);
391         stm_moder_set(&stm_gpioa, 15, STM_MODER_INPUT);
392 # elif SERIAL_2_PB6_PB7
393         stm_moder_set(&stm_gpiob, 6, STM_MODER_INPUT);
394         stm_moder_set(&stm_gpiob, 7, STM_MODER_INPUT);
395 #endif
396 #if HAS_SERIAL_1
397         stm_rcc.apb2enr &= ~(1 << STM_RCC_APB2ENR_USART1EN);
398 #endif
399 #if HAS_SERIAL_2
400         stm_nvic_set_disable(STM_ISR_USART2_POS);
401         stm_rcc.apb1enr &= ~(1 << STM_RCC_APB1ENR_USART2EN);
402 #endif
403 }
404 #endif
405
406 void
407 ao_serial_init(void)
408 {
409 #if HAS_SERIAL_1
410 #endif
411
412 #if HAS_SERIAL_2
413         /*
414          *      TX      RX
415          *      PA2     PA3
416          *      PA9     PA10
417          *      PA14    PA15
418          *      PB6     PB7
419          */
420
421 # if SERIAL_2_PA2_PA3
422         ao_enable_port(&stm_gpioa);
423         stm_afr_set(&stm_gpioa, 2, STM_AFR_AF4);
424         stm_afr_set(&stm_gpioa, 3, STM_AFR_AF4);
425 # elif SERIAL_2_PA9_PA10
426         ao_enable_port(&stm_gpioa);
427         stm_afr_set(&stm_gpioa, 9, STM_AFR_AF4);
428         stm_afr_set(&stm_gpioa, 10, STM_AFR_AF4);
429 # elif SERIAL_2_PA14_PA15
430         ao_enable_port(&stm_gpioa);
431         stm_afr_set(&stm_gpioa, 14, STM_AFR_AF4);
432         stm_afr_set(&stm_gpioa, 15, STM_AFR_AF4);
433 # elif SERIAL_2_PB6_PB7
434         ao_enable_port(&stm_gpiob);
435         stm_afr_set(&stm_gpiob, 6, STM_AFR_AF0);
436         stm_afr_set(&stm_gpiob, 7, STM_AFR_AF0);
437 # else
438 #  error "No SERIAL_2 port configuration specified"
439 # endif
440         /* Enable USART */
441         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
442
443         ao_stm_usart2.reg = &stm_usart2;
444         ao_usart_init(&ao_stm_usart2, USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW);
445
446         stm_nvic_set_enable(STM_ISR_USART2_POS);
447         stm_nvic_set_priority(STM_ISR_USART2_POS, 4);
448 # if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
449         ao_add_stdio(_ao_serial2_pollchar,
450                      ao_serial2_putchar,
451                      NULL);
452 # endif
453 #endif
454 }