altos/lpc: Get USART running
[fw/altos] / src / lpc / ao_serial_lpc.c
1 /*
2  * Copyright © 2013 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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_serial.h>
20
21 struct ao_fifo  ao_usart_rx_fifo;
22 struct ao_fifo  ao_usart_tx_fifo;
23 uint8_t         ao_usart_tx_started;
24
25 void
26 ao_debug_out(char c)
27 {
28         if (c == '\n')
29                 ao_debug_out('\r');
30         while (!(lpc_usart.lsr & (1 << LPC_USART_LSR_TEMT)))
31                 ;
32         lpc_usart.rbr_thr = c;
33 }
34
35 static void
36 _ao_serial_tx_start(void)
37 {
38         if (!ao_fifo_empty(ao_usart_tx_fifo) & !ao_usart_tx_started)
39         {
40                 ao_usart_tx_started = 1;
41                 ao_fifo_remove(ao_usart_tx_fifo, lpc_usart.rbr_thr);
42         }
43 }
44
45 void
46 lpc_usart_isr(void)
47 {
48         (void) lpc_usart.iir_fcr;
49
50         while (lpc_usart.lsr & (1 << LPC_USART_LSR_RDR)) {
51                 char c = lpc_usart.rbr_thr;
52                 if (!ao_fifo_full(ao_usart_rx_fifo))
53                         ao_fifo_insert(ao_usart_rx_fifo, c);
54                 ao_wakeup(&ao_usart_rx_fifo);
55                 if (stdin)
56                         ao_wakeup(&ao_stdin_ready);
57         }
58         if (lpc_usart.lsr & (1 << LPC_USART_LSR_THRE)) {
59                 ao_usart_tx_started = 0;
60                 _ao_serial_tx_start();
61                 ao_wakeup(&ao_usart_tx_fifo);
62         }
63 }
64
65 int
66 _ao_serial_pollchar(void)
67 {
68         int     c;
69         
70         if (ao_fifo_empty(ao_usart_rx_fifo))
71                 c = AO_READ_AGAIN;
72         else {
73                 uint8_t u;
74                 ao_fifo_remove(ao_usart_rx_fifo,u);
75                 c = u;
76         }
77         return c;
78 }
79
80 char
81 ao_serial_getchar(void)
82 {
83         int c;
84         ao_arch_block_interrupts();
85         while ((c = _ao_serial_pollchar()) == AO_READ_AGAIN)
86                 ao_sleep(&ao_usart_rx_fifo);
87         ao_arch_release_interrupts();
88         return (char) c;
89 }
90
91 void
92 ao_serial_putchar(char c)
93 {
94         ao_arch_block_interrupts();
95         while (ao_fifo_full(ao_usart_tx_fifo))
96                 ao_sleep(&ao_usart_tx_fifo);
97         ao_fifo_insert(ao_usart_tx_fifo, c);
98         _ao_serial_tx_start();
99         ao_arch_release_interrupts();
100 }
101
102 void
103 ao_serial_drain(void)
104 {
105         ao_arch_block_interrupts();
106         while (!ao_fifo_empty(ao_usart_tx_fifo))
107                 ao_sleep(&ao_usart_tx_fifo);
108         ao_arch_release_interrupts();
109 }
110
111 #include "ao_serial_lpc.h"
112
113 void
114 ao_serial_set_speed(uint8_t speed)
115 {
116         if (speed > AO_SERIAL_SPEED_115200)
117                 return;
118
119         /* Flip to allow access to divisor latches */
120         lpc_usart.lcr |= (1 << LPC_USART_LCR_DLAB);
121
122         /* DL LSB */
123         lpc_usart.rbr_thr = ao_usart_speeds[speed].dl & 0xff;
124         
125         /* DL MSB */
126         lpc_usart.ier = (ao_usart_speeds[speed].dl >> 8) & 0xff;
127
128         lpc_usart.fdr = ((ao_usart_speeds[speed].divaddval << LPC_USART_FDR_DIVADDVAL) |
129                          (ao_usart_speeds[speed].mulval << LPC_USART_FDR_MULVAL));
130
131         /* Turn access to divisor latches back off */
132         lpc_usart.lcr &= ~(1 << LPC_USART_LCR_DLAB);
133 }
134
135 void
136 ao_serial_init(void)
137 {
138 #if SERIAL_0_18_19
139         lpc_ioconf.pio0_18 = ((LPC_IOCONF_FUNC_PIO0_18_RXD << LPC_IOCONF_FUNC) |
140                               (LPC_IOCONF_MODE_INACTIVE << LPC_IOCONF_MODE) |
141                               (0 << LPC_IOCONF_HYS) |
142                               (0 << LPC_IOCONF_INV) |
143                               (0 << LPC_IOCONF_OD));
144         lpc_ioconf.pio0_19 = ((LPC_IOCONF_FUNC_PIO0_19_TXD << LPC_IOCONF_FUNC) |
145                               (LPC_IOCONF_MODE_INACTIVE << LPC_IOCONF_MODE) |
146                               (0 << LPC_IOCONF_HYS) |
147                               (0 << LPC_IOCONF_INV) |
148                               (0 << LPC_IOCONF_OD));
149 #endif
150
151         /* Turn on the USART */
152         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_USART);
153
154         /* Turn on the USART clock */
155         lpc_scb.uartclkdiv = AO_LPC_CLKOUT / AO_LPC_USARTCLK;
156
157         /* Configure USART */
158
159         /* Enable FIFOs, reset fifo contents, interrupt on 1 received char */
160         lpc_usart.iir_fcr = ((1 << LPC_USART_FCR_FIFOEN) |
161                          (1 << LPC_USART_FCR_RXFIFORES) |
162                          (1 << LPC_USART_FCR_TXFIFORES) |
163                          (LPC_USART_FCR_RXTL_1 << LPC_USART_FCR_RXTL));
164
165         /* 8 n 1 */
166         lpc_usart.lcr = ((LPC_USART_LCR_WLS_8 << LPC_USART_LCR_WLS) |
167                          (LPC_USART_LCR_SBS_1 << LPC_USART_LCR_SBS) |
168                          (0 << LPC_USART_LCR_PE) |
169                          (LPC_USART_LCR_PS_ODD << LPC_USART_LCR_PS) |
170                          (0 << LPC_USART_LCR_BC) |
171                          (0 << LPC_USART_LCR_DLAB));
172
173         /* Disable flow control */
174         lpc_usart.mcr = ((0 << LPC_USART_MCR_DTRCTRL) |
175                          (0 << LPC_USART_MCR_RTSCTRL) |
176                          (0 << LPC_USART_MCR_LMS) |
177                          (0 << LPC_USART_MCR_RTSEN) |
178                          (0 << LPC_USART_MCR_CTSEN));
179
180         /* 16x oversampling */
181         lpc_usart.osr = ((0 << LPC_USART_OSR_OSFRAC) |
182                          ((16 - 1) << LPC_USART_OSR_OSINT) |
183                          (0 << LPC_USART_OSR_FDINT));
184
185         /* Full duplex */
186         lpc_usart.hden = ((0 << LPC_USART_HDEN_HDEN));
187
188         /* Set baud rate */
189         ao_serial_set_speed(AO_SERIAL_SPEED_9600);
190
191         /* Enable interrupts */
192         lpc_usart.ier = ((1 << LPC_USART_IER_RBRINTEN) |
193                          (1 << LPC_USART_IER_THREINTEN));
194
195         lpc_nvic_set_enable(LPC_ISR_USART_POS);
196         lpc_nvic_set_priority(LPC_ISR_USART_POS, 0);
197 #if USE_SERIAL_0_STDIN
198         ao_add_stdio(_ao_serial_pollchar,
199                      ao_serial_putchar,
200                      NULL);
201 #endif
202 }