comparison packages/devs/serial/arm/aaed2000/current/src/aaed2000_serial.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children d2c90368aeef
comparison
equal deleted inserted replaced
207:74c807ddde34 208:e0c0827131d1
1 //==========================================================================
2 //
3 // io/serial/arm/aaed2000_serial.c
4 //
5 // Agilent AAED2000 Serial I/O Interface Module (interrupt driven)
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s): gthomas, jskov
44 // Contributors:gthomas, jskov
45 // Date: 2001-11-12
46 // Purpose: AAED2000 Serial I/O module (interrupt driven version)
47 // Description:
48 //
49 //####DESCRIPTIONEND####
50 //
51 //==========================================================================
52
53 #include <pkgconf/system.h>
54 #include <pkgconf/io_serial.h>
55 #include <pkgconf/io.h>
56 #include <cyg/io/io.h>
57 #include <cyg/hal/hal_intr.h> // interrupt
58 #include <cyg/hal/hal_io.h> // register base
59 #include <cyg/io/devtab.h>
60 #include <cyg/io/serial.h>
61 #include <cyg/infra/diag.h>
62
63 #include "aaed2000_serial.h"
64
65 typedef struct aaed2000_serial_info {
66 CYG_ADDRWORD base;
67 CYG_WORD int_num;
68 cyg_interrupt serial_interrupt;
69 cyg_handle_t serial_interrupt_handle;
70 } aaed2000_serial_info;
71
72 static bool aaed2000_serial_init(struct cyg_devtab_entry *tab);
73 static bool aaed2000_serial_putc(serial_channel *chan, unsigned char c);
74 static Cyg_ErrNo aaed2000_serial_lookup(struct cyg_devtab_entry **tab,
75 struct cyg_devtab_entry *sub_tab,
76 const char *name);
77 static unsigned char aaed2000_serial_getc(serial_channel *chan);
78 static Cyg_ErrNo aaed2000_serial_set_config(serial_channel *chan,
79 cyg_uint32 key,
80 const void *xbuf, cyg_uint32 *len);
81 static void aaed2000_serial_start_xmit(serial_channel *chan);
82 static void aaed2000_serial_stop_xmit(serial_channel *chan);
83
84 static cyg_uint32 aaed2000_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
85 static void aaed2000_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
86
87 static SERIAL_FUNS(aaed2000_serial_funs,
88 aaed2000_serial_putc,
89 aaed2000_serial_getc,
90 aaed2000_serial_set_config,
91 aaed2000_serial_start_xmit,
92 aaed2000_serial_stop_xmit
93 );
94
95 #ifdef CYGPKG_IO_SERIAL_ARM_AAED2000_SERIAL0
96 static aaed2000_serial_info aaed2000_serial_info0 = {0x80000800,
97 CYGNUM_HAL_INTERRUPT_UART3INTR};
98 #if CYGNUM_IO_SERIAL_ARM_AAED2000_SERIAL0_BUFSIZE > 0
99 static unsigned char aaed2000_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_AAED2000_SERIAL0_BUFSIZE];
100 static unsigned char aaed2000_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_AAED2000_SERIAL0_BUFSIZE];
101
102 static SERIAL_CHANNEL_USING_INTERRUPTS(aaed2000_serial_channel0,
103 aaed2000_serial_funs,
104 aaed2000_serial_info0,
105 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AAED2000_SERIAL0_BAUD),
106 CYG_SERIAL_STOP_DEFAULT,
107 CYG_SERIAL_PARITY_DEFAULT,
108 CYG_SERIAL_WORD_LENGTH_DEFAULT,
109 CYG_SERIAL_FLAGS_DEFAULT,
110 &aaed2000_serial_out_buf0[0], sizeof(aaed2000_serial_out_buf0),
111 &aaed2000_serial_in_buf0[0], sizeof(aaed2000_serial_in_buf0)
112 );
113 #else
114 static SERIAL_CHANNEL(aaed2000_serial_channel0,
115 aaed2000_serial_funs,
116 aaed2000_serial_info0,
117 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AAED2000_SERIAL0_BAUD),
118 CYG_SERIAL_STOP_DEFAULT,
119 CYG_SERIAL_PARITY_DEFAULT,
120 CYG_SERIAL_WORD_LENGTH_DEFAULT,
121 CYG_SERIAL_FLAGS_DEFAULT
122 );
123 #endif
124
125 DEVTAB_ENTRY(aaed2000_serial_io0,
126 CYGDAT_IO_SERIAL_ARM_AAED2000_SERIAL0_NAME,
127 0, // Does not depend on a lower level interface
128 &cyg_io_serial_devio,
129 aaed2000_serial_init,
130 aaed2000_serial_lookup, // Serial driver may need initializing
131 &aaed2000_serial_channel0
132 );
133 #endif // CYGPKG_IO_SERIAL_ARM_AAED2000_SERIAL0
134
135 // Internal function to actually configure the hardware to desired baud rate, etc.
136 static bool
137 aaed2000_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
138 {
139 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
140 CYG_ADDRWORD base = aaed2000_chan->base;
141 unsigned short baud_divisor = select_baud[new_config->baud];
142 cyg_uint32 _lcr, _intm;
143 if (baud_divisor == 0) return false;
144
145 // Register writes don't take effect till the UART is enabled.
146 HAL_WRITE_UINT32(base+AAEC_UART_CTRL, AAEC_UART_CTRL_ENAB);
147
148 // Disable port interrupts while changing hardware
149 HAL_READ_UINT32(base+AAEC_UART_INTM, _intm);
150 HAL_WRITE_UINT32(base+AAEC_UART_INTM, 0);
151
152 _lcr = select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] |
153 select_stop_bits[new_config->stop] |
154 select_parity[new_config->parity];
155 HAL_WRITE_UINT32(base+AAEC_UART_BAUD, baud_divisor);
156 if (init) {
157 _lcr |= AAEC_UART_LCR_FIFO; // Enable FIFO
158 if (chan->out_cbuf.len != 0) {
159 _intm = AAEC_UART_INT_RIS|AAEC_UART_INT_RTIS;
160 } else {
161 _intm = 0;
162 }
163 }
164 if (new_config != &chan->config) {
165 chan->config = *new_config;
166 }
167
168 // Set line control and (re)enable interrupts
169 HAL_WRITE_UINT32(base+AAEC_UART_LCR, _lcr);
170 HAL_WRITE_UINT32(base+AAEC_UART_INTM, _intm);
171
172 return true;
173 }
174
175 // Function to initialize the device. Called at bootstrap time.
176 static bool
177 aaed2000_serial_init(struct cyg_devtab_entry *tab)
178 {
179 serial_channel *chan = (serial_channel *)tab->priv;
180 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
181 #ifdef CYGDBG_IO_INIT
182 diag_printf("AAED2000 SERIAL init - dev: 0x%08x.%d\n",
183 aaed2000_chan->base, aaed2000_chan->int_num);
184 #endif
185 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
186 if (chan->out_cbuf.len != 0) {
187 cyg_drv_interrupt_create(aaed2000_chan->int_num,
188 1, // Priority - unused
189 (cyg_addrword_t)chan, // Data item passed to interrupt handler
190 aaed2000_serial_ISR,
191 aaed2000_serial_DSR,
192 &aaed2000_chan->serial_interrupt_handle,
193 &aaed2000_chan->serial_interrupt);
194 cyg_drv_interrupt_attach(aaed2000_chan->serial_interrupt_handle);
195 cyg_drv_interrupt_unmask(aaed2000_chan->int_num);
196 }
197 aaed2000_serial_config_port(chan, &chan->config, true);
198 return true;
199 }
200
201 // This routine is called when the device is "looked" up (i.e. attached)
202 static Cyg_ErrNo
203 aaed2000_serial_lookup(struct cyg_devtab_entry **tab,
204 struct cyg_devtab_entry *sub_tab,
205 const char *name)
206 {
207 serial_channel *chan = (serial_channel *)(*tab)->priv;
208 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
209 return ENOERR;
210 }
211
212 // Send a character to the device output buffer.
213 // Return 'true' if character is sent to device
214 static bool
215 aaed2000_serial_putc(serial_channel *chan, unsigned char c)
216 {
217 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
218 CYG_ADDRWORD base = aaed2000_chan->base;
219 cyg_uint32 _status;
220
221 HAL_READ_UINT32(base+AAEC_UART_STATUS, _status);
222 if (_status & AAEC_UART_STATUS_TxFF) {
223 // No space
224 return false;
225 } else {
226 // Transmit buffer is empty
227 HAL_WRITE_UINT32(base+AAEC_UART_DATA, (cyg_uint32)c);
228 return true;
229 }
230 }
231
232 // Fetch a character from the device input buffer, waiting if necessary
233 static unsigned char
234 aaed2000_serial_getc(serial_channel *chan)
235 {
236 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
237 CYG_ADDRWORD base = aaed2000_chan->base;
238 cyg_uint32 _status;
239 cyg_uint32 _c;
240
241 do {
242 HAL_READ_UINT32(base+AAEC_UART_STATUS, _status);
243 } while (_status & AAEC_UART_STATUS_RxFE);
244
245 HAL_READ_UINT32(base+AAEC_UART_DATA, _c);
246 return (unsigned char)_c;
247 }
248
249 // Set up the device characteristics; baud rate, etc.
250 static Cyg_ErrNo
251 aaed2000_serial_set_config(serial_channel *chan, cyg_uint32 key,
252 const void *xbuf, cyg_uint32 *len)
253 {
254 switch (key) {
255 case CYG_IO_SET_CONFIG_SERIAL_INFO:
256 {
257 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
258 if ( *len < sizeof(cyg_serial_info_t) ) {
259 return -EINVAL;
260 }
261 *len = sizeof(cyg_serial_info_t);
262 if ( true != aaed2000_serial_config_port(chan, config, false) )
263 return -EINVAL;
264 }
265 break;
266 default:
267 return -EINVAL;
268 }
269 return ENOERR;
270 }
271
272 // Enable the transmitter on the device
273 static void
274 aaed2000_serial_start_xmit(serial_channel *chan)
275 {
276 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
277 CYG_ADDRWORD base = aaed2000_chan->base;
278 cyg_uint32 _intm;
279
280 HAL_READ_UINT32(base+AAEC_UART_INTM, _intm);
281 _intm |= AAEC_UART_INT_TIS;
282 HAL_WRITE_UINT32(base+AAEC_UART_INTM, _intm);
283 }
284
285 // Disable the transmitter on the device
286 static void
287 aaed2000_serial_stop_xmit(serial_channel *chan)
288 {
289 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
290 CYG_ADDRWORD base = aaed2000_chan->base;
291 cyg_uint32 _intm;
292
293 HAL_READ_UINT32(base+AAEC_UART_INTM, _intm);
294 _intm &= ~AAEC_UART_INT_TIS;
295 HAL_WRITE_UINT32(base+AAEC_UART_INTM, _intm);
296 }
297
298 // Serial I/O - low level interrupt handler (ISR)
299 static cyg_uint32
300 aaed2000_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
301 {
302 serial_channel *chan = (serial_channel *)data;
303 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
304 cyg_drv_interrupt_mask(aaed2000_chan->int_num);
305 cyg_drv_interrupt_acknowledge(aaed2000_chan->int_num);
306 return CYG_ISR_CALL_DSR; // Cause DSR to be run
307 }
308
309 // Serial I/O - high level interrupt handler (DSR)
310 static void
311 aaed2000_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
312 {
313 serial_channel *chan = (serial_channel *)data;
314 aaed2000_serial_info *aaed2000_chan = (aaed2000_serial_info *)chan->dev_priv;
315 CYG_ADDRWORD base = aaed2000_chan->base;
316 cyg_uint32 _intres, _c;
317
318 HAL_READ_UINT32(base+AAEC_UART_INTRES, _intres);
319 _intres &= (AAEC_UART_INT_TIS | AAEC_UART_INT_RIS | AAEC_UART_INT_RTIS);
320 while (_intres) {
321
322 // Empty Rx FIFO
323 if (_intres & (AAEC_UART_INT_RIS|AAEC_UART_INT_RTIS)) {
324 HAL_READ_UINT32(base+AAEC_UART_DATA, _c);
325 (chan->callbacks->rcv_char)(chan, (unsigned char)_c);
326 }
327
328 // Fill into Tx FIFO. xmt_char will mask the interrupt when it
329 // runs out of chars, so doing this in a loop is OK.
330 if (_intres & AAEC_UART_INT_TIS) {
331 (chan->callbacks->xmt_char)(chan);
332 }
333
334 HAL_READ_UINT32(base+AAEC_UART_INTRES, _intres);
335 _intres &= (AAEC_UART_INT_TIS | AAEC_UART_INT_RIS | AAEC_UART_INT_RTIS);
336 }
337
338 cyg_drv_interrupt_unmask(aaed2000_chan->int_num);
339 }