Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[fw/altos] / src / cc1111 / ao_serial.c
1 /*
2  * Copyright © 2009 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
20 const __code struct ao_serial_speed ao_serial_speeds[] = {
21         /* [AO_SERIAL_SPEED_4800] = */ {
22                 /* .baud = */ 163,
23                 /* .gcr  = */ (7 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
24         },
25         /* [AO_SERIAL_SPEED_9600] = */ {
26                 /* .baud = */ 163,
27                 /* .gcr  = */ (8 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
28         },
29         /* [AO_SERIAL_SPEED_19200] = */ {
30                 /* .baud = */ 163,
31                 /* .gcr  = */ (9 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
32         },
33         /* [AO_SERIAL_SPEED_57600] = */ {
34                 /* .baud = */ 59,
35                 /* .gcr =  */ (11 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
36         },
37         /* [AO_SERIAL_SPEED_115200] = */ {
38                 /* .baud = */ 59,
39                 /* .gcr =  */ (12 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
40         },
41 };
42
43 #define AO_SERIAL_SPEED_MAX     AO_SERIAL_SPEED_115200
44
45 #if HAS_SERIAL_1_ALT_1
46 #define SERIAL_1_RTS P0_3
47 #else
48 #define SERIAL_1_RTS P1_5
49 #endif
50
51 #if HAS_SERIAL_0_ALT_1
52 #define SERIAL_0_RTS P0_5
53 #else
54 #define SERIAL_0_RTS P1_3
55 #endif
56
57 #if HAS_SERIAL_0
58
59 volatile __xdata struct ao_fifo ao_serial0_rx_fifo;
60 volatile __xdata struct ao_fifo ao_serial0_tx_fifo;
61
62 void
63 ao_serial0_rx_isr(void) __interrupt 2
64 {
65         if (!ao_fifo_full(ao_serial0_rx_fifo))
66                 ao_fifo_insert(ao_serial0_rx_fifo, U0DBUF);
67         ao_wakeup(&ao_serial0_rx_fifo);
68 #if USE_SERIAL_0_STDIN
69         ao_wakeup(&ao_stdin_ready);
70 #endif
71 #if HAS_SERIAL_0_HW_FLOW
72         if (ao_fifo_mostly(ao_serial0_rx_fifo))
73                 SERIAL_0_RTS = 1;
74 #endif
75 }
76
77 static __xdata uint8_t ao_serial0_tx_started;
78
79 static void
80 ao_serial0_tx_start(void)
81 {
82         if (!ao_fifo_empty(ao_serial0_tx_fifo) &&
83             !ao_serial0_tx_started)
84         {
85                 ao_serial0_tx_started = 1;
86                 ao_fifo_remove(ao_serial0_tx_fifo, U0DBUF);
87         }
88 }
89
90 void
91 ao_serial0_tx_isr(void) __interrupt 7
92 {
93         UTX0IF = 0;
94         ao_serial0_tx_started = 0;
95         ao_serial0_tx_start();
96         ao_wakeup(&ao_serial0_tx_fifo);
97 }
98
99 char
100 ao_serial0_getchar(void) __critical
101 {
102         char    c;
103         while (ao_fifo_empty(ao_serial0_rx_fifo))
104                 ao_sleep(&ao_serial0_rx_fifo);
105         ao_fifo_remove(ao_serial0_rx_fifo, c);
106 #if HAS_SERIAL_0_HW_FLOW
107         if (ao_fifo_barely(ao_serial0_rx_fifo))
108                 SERIAL_0_RTS = 0;
109 #endif
110         return c;
111 }
112
113 #if USE_SERIAL_0_STDIN
114 int
115 _ao_serial0_pollchar(void)
116 {
117         uint8_t c;
118         if (ao_fifo_empty(ao_serial0_rx_fifo))
119                 return AO_READ_AGAIN;
120         ao_fifo_remove(ao_serial0_rx_fifo,c);
121 #if HAS_SERIAL_0_HW_FLOW
122         if (ao_fifo_barely(ao_serial0_rx_fifo))
123                 SERIAL_0_RTS = 0;
124 #endif
125         return c;
126 }
127 #endif
128
129 void
130 ao_serial0_putchar(char c) __critical
131 {
132         while (ao_fifo_full(ao_serial0_tx_fifo))
133                 ao_sleep(&ao_serial0_tx_fifo);
134         ao_fifo_insert(ao_serial0_tx_fifo, c);
135         ao_serial0_tx_start();
136 }
137
138 void
139 ao_serial0_drain(void) __critical
140 {
141         while (!ao_fifo_empty(ao_serial0_tx_fifo))
142                 ao_sleep(&ao_serial0_tx_fifo);
143 }
144
145 void
146 ao_serial0_set_speed(uint8_t speed)
147 {
148         ao_serial0_drain();
149         if (speed > AO_SERIAL_SPEED_MAX)
150                 return;
151         U0UCR |= UxUCR_FLUSH;
152         U0BAUD = ao_serial_speeds[speed].baud;
153         U0GCR = ao_serial_speeds[speed].gcr;
154 }
155 #endif /* HAS_SERIAL_0 */
156
157 #if HAS_SERIAL_1
158
159 volatile __xdata struct ao_fifo ao_serial1_rx_fifo;
160 volatile __xdata struct ao_fifo ao_serial1_tx_fifo;
161
162 void
163 ao_serial1_rx_isr(void) __interrupt 3
164 {
165         if (!ao_fifo_full(ao_serial1_rx_fifo))
166                 ao_fifo_insert(ao_serial1_rx_fifo, U1DBUF);
167         ao_wakeup(&ao_serial1_rx_fifo);
168 #if USE_SERIAL_1_STDIN
169         ao_wakeup(&ao_stdin_ready);
170 #endif
171 #if HAS_SERIAL_1_HW_FLOW
172         if (ao_fifo_mostly(ao_serial1_rx_fifo))
173                 SERIAL_1_RTS = 1;
174 #endif
175 }
176
177 static __xdata uint8_t ao_serial1_tx_started;
178
179 static void
180 ao_serial1_tx_start(void)
181 {
182         if (!ao_fifo_empty(ao_serial1_tx_fifo) &&
183             !ao_serial1_tx_started)
184         {
185                 ao_serial1_tx_started = 1;
186                 ao_fifo_remove(ao_serial1_tx_fifo, U1DBUF);
187         }
188 }
189
190 void
191 ao_serial1_tx_isr(void) __interrupt 14
192 {
193         UTX1IF = 0;
194         ao_serial1_tx_started = 0;
195         ao_serial1_tx_start();
196         ao_wakeup(&ao_serial1_tx_fifo);
197 }
198
199 char
200 ao_serial1_getchar(void) __critical
201 {
202         char    c;
203         while (ao_fifo_empty(ao_serial1_rx_fifo))
204                 ao_sleep(&ao_serial1_rx_fifo);
205         ao_fifo_remove(ao_serial1_rx_fifo, c);
206 #if HAS_SERIAL_1_HW_FLOW
207         if (ao_fifo_barely(ao_serial1_rx_fifo))
208                 SERIAL_1_RTS = 0;
209 #endif
210         return c;
211 }
212
213 #if USE_SERIAL_1_STDIN
214 int
215 _ao_serial1_pollchar(void)
216 {
217         uint8_t c;
218         if (ao_fifo_empty(ao_serial1_rx_fifo))
219                 return AO_READ_AGAIN;
220         ao_fifo_remove(ao_serial1_rx_fifo,c);
221 #if HAS_SERIAL_1_HW_FLOW
222         if (ao_fifo_barely(ao_serial1_rx_fifo))
223                 SERIAL_1_RTS = 0;
224 #endif
225         return c;
226 }
227 #endif
228
229 void
230 ao_serial1_putchar(char c) __critical
231 {
232         while (ao_fifo_full(ao_serial1_tx_fifo))
233                 ao_sleep(&ao_serial1_tx_fifo);
234         ao_fifo_insert(ao_serial1_tx_fifo, c);
235         ao_serial1_tx_start();
236 }
237
238 void
239 ao_serial1_drain(void) __critical
240 {
241         while (!ao_fifo_empty(ao_serial1_tx_fifo))
242                 ao_sleep(&ao_serial1_tx_fifo);
243 }
244
245 void
246 ao_serial1_set_speed(uint8_t speed)
247 {
248         ao_serial1_drain();
249         if (speed > AO_SERIAL_SPEED_MAX)
250                 return;
251         U1UCR |= UxUCR_FLUSH;
252         U1BAUD = ao_serial_speeds[speed].baud;
253         U1GCR = ao_serial_speeds[speed].gcr;
254 }
255
256 #endif /* HAS_SERIAL_1 */
257
258 void
259 ao_serial_init(void)
260 {
261 #if HAS_SERIAL_0
262 #if HAS_SERIAL_0_ALT_1
263         /* Set up the USART pin assignment */
264         PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_1;
265
266         P2DIR = (P2DIR & ~P2DIR_PRIP0_MASK) | P2DIR_PRIP0_USART0_USART1;
267
268         /* Make the USART pins be controlled by the USART */
269         P0SEL |= (1 << 2) | (1 << 3);
270 #if HAS_SERIAL_0_HW_FLOW
271         SERIAL_0_RTS = 0;
272         P0DIR |= (1 << 5);
273
274         P0SEL |= (1 << 4);
275         P0INP |= (1 << 4);
276 #endif
277 #else
278         /* Set up the USART pin assignment */
279         PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;
280
281         P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI0P1_MASK)) |
282                 (P2SEL_PRI3P1_USART0 | P2SEL_PRI0P1_USART0);
283
284         /* Make the USART pins be controlled by the USART */
285         P1SEL |= (1 << 5) | (1 << 4);
286 #if HAS_SERIAL_0_HW_FLOW
287         SERIAL_0_RTS = 0;
288         P1DIR |= (1 << 3);
289
290         P1SEL |= (1 << 2);
291         P1INP |= (1 << 2);
292 #endif
293 #endif
294
295         /* UART mode with receiver enabled */
296         U0CSR = (UxCSR_MODE_UART | UxCSR_RE);
297
298         /* Pick a 9600 baud rate */
299         ao_serial0_set_speed(AO_SERIAL_SPEED_9600);
300
301         /* Reasonable serial parameters */
302         U0UCR = (UxUCR_FLUSH |
303 #if HAS_SERIAL_0_HW_FLOW
304                  UxUCR_FLOW_ENABLE |
305 #else
306                  UxUCR_FLOW_DISABLE |
307 #endif
308                  UxUCR_D9_EVEN_PARITY |
309                  UxUCR_BIT9_8_BITS |
310                  UxUCR_PARITY_DISABLE |
311                  UxUCR_SPB_1_STOP_BIT |
312                  UxUCR_STOP_HIGH |
313                  UxUCR_START_LOW);
314
315         IEN0 |= IEN0_URX0IE;
316         IEN2 |= IEN2_UTX0IE;
317 #if USE_SERIAL_0_STDIN && !DELAY_SERIAL_0_STDIN
318         ao_add_stdio(_ao_serial0_pollchar,
319                      ao_serial0_putchar,
320                      NULL);
321 #endif
322 #endif /* HAS_SERIAL_0 */
323
324 #if HAS_SERIAL_1
325 #if HAS_SERIAL_1_ALT_1
326         /* Set up the USART pin assignment */
327         PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_1;
328
329         P2DIR = (P2DIR & ~P2DIR_PRIP0_MASK) | P2DIR_PRIP0_USART1_USART0;
330
331         /* Make the USART pins be controlled by the USART */
332         P0SEL |= (1 << 5) | (1 << 4);
333 #if HAS_SERIAL_1_HW_FLOW
334         /* SW RTS control (hw doesn't work) */
335         SERIAL_1_RTS = 0;
336         P0DIR |= 1 << 3;
337
338         /* HW CTS. Maybe this works? */
339         P0SEL |= 1 << 2;
340         P0INP |= 1 << 2;
341 #endif
342 #else
343         /* Set up the USART pin assignment */
344         PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_2;
345
346         P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI2P1_MASK)) |
347                 (P2SEL_PRI3P1_USART1 | P2SEL_PRI2P1_USART1);
348
349         /* Make the USART pins be controlled by the USART */
350         P1SEL |= (1 << 6) | (1 << 7);
351 #if HAS_SERIAL_1_HW_FLOW
352         /* SW RTS control (hw doesn't work) */
353         SERIAL_1_RTS = 0;
354         P1DIR |= (1 << 5);
355
356         /* HW CTS. Maybe this works? */
357         P1SEL |= (1 << 4);
358         P1INP |= (1 << 4);
359 #endif
360 #endif
361
362         /* UART mode with receiver enabled */
363         U1CSR = (UxCSR_MODE_UART | UxCSR_RE);
364
365         /* Pick a 4800 baud rate */
366         ao_serial1_set_speed(AO_SERIAL_SPEED_4800);
367
368         /* Reasonable serial parameters */
369         U1UCR = (UxUCR_FLUSH |
370 #if HAS_SERIAL_1_HW_FLOW
371                  UxUCR_FLOW_ENABLE |
372 #else
373                  UxUCR_FLOW_DISABLE |
374 #endif
375                  UxUCR_D9_EVEN_PARITY |
376                  UxUCR_BIT9_8_BITS |
377                  UxUCR_PARITY_DISABLE |
378                  UxUCR_SPB_1_STOP_BIT |
379                  UxUCR_STOP_HIGH |
380                  UxUCR_START_LOW);
381
382         IEN0 |= IEN0_URX1IE;
383         IEN2 |= IEN2_UTX1IE;
384
385 #if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
386         ao_add_stdio(_ao_serial1_pollchar,
387                      ao_serial1_putchar,
388                      NULL);
389 #endif
390 #endif /* HAS_SERIAL_1 */
391 }