Merge branch 'micropeak-1.1'
[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_0
46
47 volatile __xdata struct ao_fifo ao_serial0_rx_fifo;
48 volatile __xdata struct ao_fifo ao_serial0_tx_fifo;
49
50 void
51 ao_serial0_rx_isr(void) __interrupt 2
52 {
53         if (!ao_fifo_full(ao_serial0_rx_fifo))
54                 ao_fifo_insert(ao_serial0_rx_fifo, U0DBUF);
55         ao_wakeup(&ao_serial0_rx_fifo);
56 #if USE_SERIAL_0_STDIN
57         ao_wakeup(&ao_stdin_ready);
58 #endif
59 }
60
61 static __xdata uint8_t ao_serial0_tx_started;
62
63 static void
64 ao_serial0_tx_start(void)
65 {
66         if (!ao_fifo_empty(ao_serial0_tx_fifo) &&
67             !ao_serial0_tx_started)
68         {
69                 ao_serial0_tx_started = 1;
70                 ao_fifo_remove(ao_serial0_tx_fifo, U0DBUF);
71         }
72 }
73
74 void
75 ao_serial0_tx_isr(void) __interrupt 7
76 {
77         UTX0IF = 0;
78         ao_serial0_tx_started = 0;
79         ao_serial0_tx_start();
80         ao_wakeup(&ao_serial0_tx_fifo);
81 }
82
83 char
84 ao_serial0_getchar(void) __critical
85 {
86         char    c;
87         while (ao_fifo_empty(ao_serial0_rx_fifo))
88                 ao_sleep(&ao_serial0_rx_fifo);
89         ao_fifo_remove(ao_serial0_rx_fifo, c);
90         return c;
91 }
92
93 #if USE_SERIAL_0_STDIN
94 int
95 ao_serial0_pollchar(void) __critical
96 {
97         uint8_t c;
98         if (ao_fifo_empty(ao_serial0_rx_fifo))
99                 return AO_READ_AGAIN;
100         ao_fifo_remove(ao_serial0_rx_fifo,c);
101         return c;
102 }
103 #endif
104
105 void
106 ao_serial0_putchar(char c) __critical
107 {
108         while (ao_fifo_full(ao_serial0_tx_fifo))
109                 ao_sleep(&ao_serial0_tx_fifo);
110         ao_fifo_insert(ao_serial0_tx_fifo, c);
111         ao_serial0_tx_start();
112 }
113
114 void
115 ao_serial0_drain(void) __critical
116 {
117         while (!ao_fifo_empty(ao_serial0_tx_fifo))
118                 ao_sleep(&ao_serial0_tx_fifo);
119 }
120
121 void
122 ao_serial0_set_speed(uint8_t speed)
123 {
124         ao_serial0_drain();
125         if (speed > AO_SERIAL_SPEED_MAX)
126                 return;
127         U0UCR |= UxUCR_FLUSH;
128         U0BAUD = ao_serial_speeds[speed].baud;
129         U0GCR = ao_serial_speeds[speed].gcr;
130 }
131 #endif /* HAS_SERIAL_0 */
132
133 #if HAS_SERIAL_1
134
135 volatile __xdata struct ao_fifo ao_serial1_rx_fifo;
136 volatile __xdata struct ao_fifo ao_serial1_tx_fifo;
137
138 void
139 ao_serial1_rx_isr(void) __interrupt 3
140 {
141         if (!ao_fifo_full(ao_serial1_rx_fifo))
142                 ao_fifo_insert(ao_serial1_rx_fifo, U1DBUF);
143         ao_wakeup(&ao_serial1_rx_fifo);
144 #if USE_SERIAL_1_STDIN
145         ao_wakeup(&ao_stdin_ready);
146 #endif
147 }
148
149 static __xdata uint8_t ao_serial1_tx_started;
150
151 static void
152 ao_serial1_tx_start(void)
153 {
154         if (!ao_fifo_empty(ao_serial1_tx_fifo) &&
155             !ao_serial1_tx_started)
156         {
157                 ao_serial1_tx_started = 1;
158                 ao_fifo_remove(ao_serial1_tx_fifo, U1DBUF);
159         }
160 }
161
162 void
163 ao_serial1_tx_isr(void) __interrupt 14
164 {
165         UTX1IF = 0;
166         ao_serial1_tx_started = 0;
167         ao_serial1_tx_start();
168         ao_wakeup(&ao_serial1_tx_fifo);
169 }
170
171 char
172 ao_serial1_getchar(void) __critical
173 {
174         char    c;
175         while (ao_fifo_empty(ao_serial1_rx_fifo))
176                 ao_sleep(&ao_serial1_rx_fifo);
177         ao_fifo_remove(ao_serial1_rx_fifo, c);
178         return c;
179 }
180
181 #if USE_SERIAL_1_STDIN
182 int
183 ao_serial1_pollchar(void) __critical
184 {
185         uint8_t c;
186         if (ao_fifo_empty(ao_serial1_rx_fifo))
187                 return AO_READ_AGAIN;
188         ao_fifo_remove(ao_serial1_rx_fifo,c);
189         return c;
190 }
191 #endif
192
193 void
194 ao_serial1_putchar(char c) __critical
195 {
196         while (ao_fifo_full(ao_serial1_tx_fifo))
197                 ao_sleep(&ao_serial1_tx_fifo);
198         ao_fifo_insert(ao_serial1_tx_fifo, c);
199         ao_serial1_tx_start();
200 }
201
202 void
203 ao_serial1_drain(void) __critical
204 {
205         while (!ao_fifo_empty(ao_serial1_tx_fifo))
206                 ao_sleep(&ao_serial1_tx_fifo);
207 }
208
209 void
210 ao_serial1_set_speed(uint8_t speed)
211 {
212         ao_serial1_drain();
213         if (speed > AO_SERIAL_SPEED_MAX)
214                 return;
215         U1UCR |= UxUCR_FLUSH;
216         U1BAUD = ao_serial_speeds[speed].baud;
217         U1GCR = ao_serial_speeds[speed].gcr;
218 }
219
220 #endif /* HAS_SERIAL_1 */
221
222 void
223 ao_serial_init(void)
224 {
225 #if HAS_SERIAL_0
226 #if HAS_SERIAL_0_ALT_1
227         /* Set up the USART pin assignment */
228         PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_1;
229
230         P2DIR = (P2DIR & ~P2DIR_PRIP0_MASK) | P2DIR_PRIP0_USART0_USART1;
231
232         /* Make the USART pins be controlled by the USART */
233         P0SEL |= (1 << 2) | (1 << 3);
234 #if HAS_SERIAL_0_HW_FLOW
235         P0SEL |= (1 << 4) | (1 << 5);
236 #endif
237 #else
238         /* Set up the USART pin assignment */
239         PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;
240
241         P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI0P1_MASK)) |
242                 (P2SEL_PRI3P1_USART0 | P2SEL_PRI0P1_USART0);
243
244         /* Make the USART pins be controlled by the USART */
245         P1SEL |= (1 << 5) | (1 << 4);
246 #if HAS_SERIAL_0_HW_FLOW
247         P1SEL |= (1 << 3) | (1 << 2);
248 #endif
249 #endif
250
251         /* UART mode with receiver enabled */
252         U0CSR = (UxCSR_MODE_UART | UxCSR_RE);
253
254         /* Pick a 9600 baud rate */
255         ao_serial0_set_speed(AO_SERIAL_SPEED_9600);
256
257         /* Reasonable serial parameters */
258         U0UCR = (UxUCR_FLUSH |
259 #if HAS_SERIAL_0_HW_FLOW
260                  UxUCR_FLOW_ENABLE |
261 #else
262                  UxUCR_FLOW_DISABLE |
263 #endif
264                  UxUCR_D9_EVEN_PARITY |
265                  UxUCR_BIT9_8_BITS |
266                  UxUCR_PARITY_DISABLE |
267                  UxUCR_SPB_1_STOP_BIT |
268                  UxUCR_STOP_HIGH |
269                  UxUCR_START_LOW);
270
271         IEN0 |= IEN0_URX0IE;
272         IEN2 |= IEN2_UTX0IE;
273 #if USE_SERIAL_0_STDIN && !DELAY_SERIAL_0_STDIN
274         ao_add_stdio(ao_serial0_pollchar,
275                      ao_serial0_putchar,
276                      NULL);
277 #endif
278 #endif /* HAS_SERIAL_0 */
279
280 #if HAS_SERIAL_1
281 #if HAS_SERIAL_1_ALT_1
282         /* Set up the USART pin assignment */
283         PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_1;
284
285         P2DIR = (P2DIR & ~P2DIR_PRIP0_MASK) | P2DIR_PRIP0_USART1_USART0;
286
287         /* Make the USART pins be controlled by the USART */
288         P0SEL |= (1 << 5) | (1 << 4);
289 #if HAS_SERIAL_1_HW_FLOW
290         P0SEL |= (1 << 3) | (1 << 2);
291 #endif
292 #else
293         /* Set up the USART pin assignment */
294         PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_2;
295
296         P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI2P1_MASK)) |
297                 (P2SEL_PRI3P1_USART1 | P2SEL_PRI2P1_USART1);
298
299         /* Make the USART pins be controlled by the USART */
300         P1SEL |= (1 << 6) | (1 << 7);
301 #if HAS_SERIAL_1_HW_FLOW
302         P1SEL |= (1 << 5) | (1 << 4);
303 #endif
304 #endif
305
306         /* UART mode with receiver enabled */
307         U1CSR = (UxCSR_MODE_UART | UxCSR_RE);
308
309         /* Pick a 4800 baud rate */
310         ao_serial1_set_speed(AO_SERIAL_SPEED_4800);
311
312         /* Reasonable serial parameters */
313         U1UCR = (UxUCR_FLUSH |
314 #if HAS_SERIAL_1_HW_FLOW
315                  UxUCR_FLOW_ENABLE |
316 #else
317                  UxUCR_FLOW_DISABLE |
318 #endif
319                  UxUCR_D9_EVEN_PARITY |
320                  UxUCR_BIT9_8_BITS |
321                  UxUCR_PARITY_DISABLE |
322                  UxUCR_SPB_1_STOP_BIT |
323                  UxUCR_STOP_HIGH |
324                  UxUCR_START_LOW);
325
326         IEN0 |= IEN0_URX1IE;
327         IEN2 |= IEN2_UTX1IE;
328
329 #if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
330         ao_add_stdio(ao_serial1_pollchar,
331                      ao_serial1_putchar,
332                      NULL);
333 #endif
334 #endif /* HAS_SERIAL_1 */
335 }