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