]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/arm/e7t/v2_0/src/e7t_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / arm / e7t / v2_0 / src / e7t_serial.c
1 //==========================================================================
2 //
3 //      io/serial/arm/e7t_serial.c
4 //
5 //      ARM AEB-2 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):    Lars.Lindqvist@combitechsystems.com
44 // Contributors: jlarmour
45 // Date:         2001-10-19
46 // Purpose:      ARM AEB-2 Serial I/O Interface Module (interrupt driven)
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>
58 #include <cyg/io/devtab.h>
59 #include <cyg/io/serial.h>
60 #include <cyg/io/serialio.h>
61 #include <cyg/infra/diag.h>
62
63 #ifdef CYGPKG_IO_SERIAL_ARM_E7T
64
65 #include "e7t_serial.h"
66
67 typedef struct e7t_serial_info {
68     CYG_ADDRWORD   base;
69     CYG_WORD       tx_int_num;
70     CYG_WORD       rx_int_num;
71     cyg_interrupt  serial_tx_interrupt;
72     cyg_interrupt  serial_rx_interrupt;
73     cyg_handle_t   serial_tx_interrupt_handle;
74     cyg_handle_t   serial_rx_interrupt_handle;
75     bool           tx_enabled;
76 } e7t_serial_info;
77
78 static bool e7t_serial_init(struct cyg_devtab_entry *tab);
79 static bool e7t_serial_putc(serial_channel *chan, unsigned char c);
80 static Cyg_ErrNo e7t_serial_lookup(struct cyg_devtab_entry **tab, 
81                                    struct cyg_devtab_entry *sub_tab,
82                                    const char *name);
83 static unsigned char e7t_serial_getc(serial_channel *chan);
84 static Cyg_ErrNo e7t_serial_set_config(serial_channel *chan, 
85                                        cyg_uint32 key,
86                                        const void *xbuf, cyg_uint32 *len);
87 static void e7t_serial_start_xmit(serial_channel *chan);
88 static void e7t_serial_stop_xmit(serial_channel *chan);
89
90 static cyg_uint32 e7t_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
91 static void       e7t_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
92 static cyg_uint32 e7t_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
93 static void       e7t_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
94
95 static SERIAL_FUNS(e7t_serial_funs, 
96                    e7t_serial_putc, 
97                    e7t_serial_getc,
98                    e7t_serial_set_config,
99                    e7t_serial_start_xmit,
100                    e7t_serial_stop_xmit
101     );
102
103 #ifdef CYGPKG_IO_SERIAL_ARM_E7T_SERIAL0
104 static e7t_serial_info e7t_serial_info0 = {0x07FFd000, 
105                                            CYGNUM_HAL_INTERRUPT_UART0_TX,
106                                            CYGNUM_HAL_INTERRUPT_UART0_RX};
107 #if CYGNUM_IO_SERIAL_ARM_E7T_SERIAL0_BUFSIZE > 0
108 static unsigned char e7t_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_E7T_SERIAL0_BUFSIZE];
109 static unsigned char e7t_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_E7T_SERIAL0_BUFSIZE];
110
111 static SERIAL_CHANNEL_USING_INTERRUPTS(e7t_serial_channel0,
112                                        e7t_serial_funs, 
113                                        e7t_serial_info0,
114                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_E7T_SERIAL0_BAUD),
115                                        CYG_SERIAL_STOP_DEFAULT,
116                                        CYG_SERIAL_PARITY_DEFAULT,
117                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
118                                        CYG_SERIAL_FLAGS_DEFAULT,
119                                        &e7t_serial_out_buf0[0], sizeof(e7t_serial_out_buf0),
120                                        &e7t_serial_in_buf0[0], sizeof(e7t_serial_in_buf0)
121     );
122 #else 
123 static SERIAL_CHANNEL(e7t_serial_channel0,
124                       e7t_serial_funs, 
125                       e7t_serial_info0,
126                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_E7T_SERIAL0_BAUD),
127                       CYG_SERIAL_STOP_DEFAULT,
128                       CYG_SERIAL_PARITY_DEFAULT,
129                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
130                       CYG_SERIAL_FLAGS_DEFAULT
131     );
132 #endif
133
134 DEVTAB_ENTRY(e7t_serial_io0, 
135              CYGDAT_IO_SERIAL_ARM_E7T_SERIAL0_NAME,
136              0,                     // Does not depend on a lower level interface
137              &cyg_io_serial_devio, 
138              e7t_serial_init, 
139              e7t_serial_lookup,     // Serial driver may need initializing
140              &e7t_serial_channel0
141     );
142 #endif //  CYGPKG_IO_SERIAL_ARM_E7T_SERIAL0
143
144 #ifdef CYGPKG_IO_SERIAL_ARM_E7T_SERIAL1
145 static e7t_serial_info e7t_serial_info1 = {0x07FFe000,
146                                            CYGNUM_HAL_INTERRUPT_UART1_TX,
147                                            CYGNUM_HAL_INTERRUPT_UART1_RX};
148 #if CYGNUM_IO_SERIAL_ARM_E7T_SERIAL1_BUFSIZE > 0
149 static unsigned char e7t_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_E7T_SERIAL1_BUFSIZE];
150 static unsigned char e7t_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_E7T_SERIAL1_BUFSIZE];
151
152 static SERIAL_CHANNEL_USING_INTERRUPTS(e7t_serial_channel1,
153                                        e7t_serial_funs, 
154                                        e7t_serial_info1,
155                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_E7T_SERIAL1_BAUD),
156                                        CYG_SERIAL_STOP_DEFAULT,
157                                        CYG_SERIAL_PARITY_DEFAULT,
158                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
159                                        CYG_SERIAL_FLAGS_DEFAULT,
160                                        &e7t_serial_out_buf1[0], sizeof(e7t_serial_out_buf1),
161                                        &e7t_serial_in_buf1[0], sizeof(e7t_serial_in_buf1)
162     );
163 #else
164 static SERIAL_CHANNEL(e7t_serial_channel1,
165                       e7t_serial_funs, 
166                       e7t_serial_info1,
167                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_E7T_SERIAL1_BAUD),
168                       CYG_SERIAL_STOP_DEFAULT,
169                       CYG_SERIAL_PARITY_DEFAULT,
170                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
171                       CYG_SERIAL_FLAGS_DEFAULT
172     );
173 #endif
174
175 DEVTAB_ENTRY(e7t_serial_io1, 
176              CYGDAT_IO_SERIAL_ARM_E7T_SERIAL1_NAME,
177              0,                     // Does not depend on a lower level interface
178              &cyg_io_serial_devio, 
179              e7t_serial_init, 
180              e7t_serial_lookup,     // Serial driver may need initializing
181              &e7t_serial_channel1
182     );
183 #endif //  CYGPKG_IO_SERIAL_ARM_E7T_SERIAL1
184
185 // Internal function to actually configure the hardware to desired baud rate, etc.
186 static bool
187 e7t_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
188 {
189   e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
190   volatile struct serial_port *port = (volatile struct serial_port *)e7t_chan->base;
191   cyg_uint32 word_length = select_word_length[(new_config->word_length)-CYGNUM_SERIAL_WORD_LENGTH_5];
192   cyg_uint32 stop_bits = select_stop_bits[(new_config->stop)-CYGNUM_SERIAL_STOP_1];
193   cyg_uint32 parity_mode = select_parity[(new_config->parity)-CYGNUM_SERIAL_PARITY_NONE];
194   cyg_uint32 baud_divisor = select_baud[(new_config->baud)-CYGNUM_SERIAL_BAUD_50];
195   cyg_uint32 res = word_length | stop_bits | parity_mode | ULCON_SCI | ULCON_IROFF;
196   if ((word_length|stop_bits|parity_mode|baud_divisor) == U_NOT_SUPP) {
197     return false;
198   };
199   port->REG_ULCON = res;
200   port->REG_UCON = UCON_RXMINT | UCON_RXSIOFF | UCON_TXMINT | UCON_DSROFF | UCON_SBKOFF | UCON_LPBOFF;
201   port->REG_UBRDIV = baud_divisor;
202   if (new_config != &chan->config) {
203     chan->config = *new_config;
204   };
205   return true;
206 }
207
208 // Function to initialize the device.  Called at bootstrap time.
209 static bool 
210 e7t_serial_init(struct cyg_devtab_entry *tab)
211 {
212     serial_channel *chan = (serial_channel *)tab->priv;
213     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
214 #ifdef CYGDBG_IO_INIT
215     diag_printf("E7T SERIAL init - dev: %x.%d.%d\n", e7t_chan->base, e7t_chan->tx_int_num, e7t_chan->rx_int_num);
216 #endif
217     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
218     if (chan->out_cbuf.len != 0) {         // If bufferlength > 0 then interrupts are used for tx
219         cyg_drv_interrupt_create(e7t_chan->tx_int_num,
220                                  99,                     // Priority - unused
221                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
222                                  e7t_serial_tx_ISR,
223                                  e7t_serial_tx_DSR,
224                                  &e7t_chan->serial_tx_interrupt_handle,
225                                  &e7t_chan->serial_tx_interrupt);
226         cyg_drv_interrupt_attach(e7t_chan->serial_tx_interrupt_handle);
227         cyg_drv_interrupt_mask(e7t_chan->tx_int_num);
228         e7t_chan->tx_enabled = false;
229     }
230     if (chan->in_cbuf.len != 0) {         // If bufferlength > 0 then interrupts are used for rx
231         cyg_drv_interrupt_create(e7t_chan->rx_int_num,
232                                  99,                     // Priority - unused
233                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
234                                  e7t_serial_rx_ISR,
235                                  e7t_serial_rx_DSR,
236                                  &e7t_chan->serial_rx_interrupt_handle,
237                                  &e7t_chan->serial_rx_interrupt);
238         cyg_drv_interrupt_attach(e7t_chan->serial_rx_interrupt_handle);
239         cyg_drv_interrupt_unmask(e7t_chan->rx_int_num);
240     }
241     e7t_serial_config_port(chan, &chan->config, true);
242     return true;
243 }
244
245 // This routine is called when the device is "looked" up (i.e. attached)
246 static Cyg_ErrNo 
247 e7t_serial_lookup(struct cyg_devtab_entry **tab, 
248                   struct cyg_devtab_entry *sub_tab,
249                   const char *name)
250 {
251     serial_channel *chan = (serial_channel *)(*tab)->priv;
252     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
253     return ENOERR;
254 }
255
256 // Send a character to the device output buffer.
257 // Return 'true' if character is sent to device
258 static bool
259 e7t_serial_putc(serial_channel *chan, unsigned char c)
260 {
261     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
262     volatile struct serial_port *port = (volatile struct serial_port *)e7t_chan->base;
263
264     if (port->REG_USTAT & USTAT_TBE) {
265       // Transmit buffer is empty
266       port->REG_UTXBUF = c;
267       return true;
268     } else {
269       // No space
270       return false;
271     }
272 }
273
274 // Fetch a character from the device input buffer, waiting if necessary
275 static unsigned char 
276 e7t_serial_getc(serial_channel *chan)
277 {
278     unsigned char c;
279     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
280     volatile struct serial_port *port = (volatile struct serial_port *)e7t_chan->base;
281     while ((port->REG_USTAT & USTAT_RDR) == 0) ;   // Wait for char
282     c = port->REG_URXBUF;
283     return c;
284 }
285
286 // Set up the device characteristics; baud rate, etc.
287 static Cyg_ErrNo
288 e7t_serial_set_config(serial_channel *chan, cyg_uint32 key,
289                       const void *xbuf, cyg_uint32 *len)
290 {
291     switch (key) {
292     case CYG_IO_SET_CONFIG_SERIAL_INFO:
293       {
294         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
295         if ( *len < sizeof(cyg_serial_info_t) ) {
296             return -EINVAL;
297         }
298         *len = sizeof(cyg_serial_info_t);
299         if ( true != e7t_serial_config_port(chan, config, false) )
300             return -EINVAL;
301       }
302       break;
303     default:
304         return -EINVAL;
305     }
306     return ENOERR;
307 }
308
309 // Enable the transmitter on the device
310 static void
311 e7t_serial_start_xmit(serial_channel *chan)
312 {
313     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
314     e7t_chan->tx_enabled = true;
315     (chan->callbacks->xmt_char)(chan);
316     if (e7t_chan->tx_enabled) {
317         cyg_drv_interrupt_unmask(e7t_chan->tx_int_num);
318     }
319 }
320
321 // Disable the transmitter on the device
322 static void 
323 e7t_serial_stop_xmit(serial_channel *chan)
324 {
325     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
326     cyg_drv_interrupt_mask(e7t_chan->tx_int_num);
327     e7t_chan->tx_enabled = false;
328 }
329
330 // Serial I/O - low level tx interrupt handler (ISR)
331 static cyg_uint32 
332 e7t_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
333 {
334     serial_channel *chan = (serial_channel *)data;
335     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
336     cyg_drv_interrupt_mask(e7t_chan->tx_int_num);
337     cyg_drv_interrupt_acknowledge(e7t_chan->tx_int_num);
338     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
339 }
340
341 // Serial I/O - high level tx interrupt handler (DSR)
342 static void       
343 e7t_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
344 {
345     serial_channel *chan = (serial_channel *)data;
346     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
347     (chan->callbacks->xmt_char)(chan);
348     if (e7t_chan->tx_enabled) {
349         cyg_drv_interrupt_unmask(e7t_chan->tx_int_num);
350     }
351 }
352
353 // Serial I/O - low level rx interrupt handler (ISR)
354 static cyg_uint32 
355 e7t_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
356 {
357     serial_channel *chan = (serial_channel *)data;
358     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
359     cyg_drv_interrupt_mask(e7t_chan->rx_int_num);
360     cyg_drv_interrupt_acknowledge(e7t_chan->rx_int_num);
361     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
362 }
363
364 // Serial I/O - high level rx interrupt handler (DSR)
365 static void       
366 e7t_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
367 {
368     serial_channel *chan = (serial_channel *)data;
369     e7t_serial_info *e7t_chan = (e7t_serial_info *)chan->dev_priv;
370     volatile struct serial_port *port = (volatile struct serial_port *)e7t_chan->base;
371     (chan->callbacks->rcv_char)(chan, port->REG_URXBUF);
372     cyg_drv_interrupt_unmask(e7t_chan->rx_int_num);
373 }
374
375 #endif // CYGPKG_IO_SERIAL_ARM_E7T
376
377 // EOF e7t_serial.c