]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/mips/jmr3904/v2_0/src/tx3904_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / mips / jmr3904 / v2_0 / src / tx3904_serial.c
1 //==========================================================================
2 //
3 //      tx3904_serial.c
4 //
5 //      Serial device driver for TX3904 on-chip serial devices
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):   nickg
44 // Contributors: nickg
45 // Date:        1999-03-3
46 // Purpose:     TX3904 serial device driver
47 // Description: TX3904 serial device driver
48 //
49 //####DESCRIPTIONEND####
50 //
51 //==========================================================================
52
53 #include <pkgconf/hal.h>
54 #include <pkgconf/io_serial.h>
55 #include <cyg/hal/hal_io.h>
56 #include <cyg/hal/hal_intr.h>
57
58 #include <cyg/io/io.h>
59 #include <cyg/io/devtab.h>
60 #include <cyg/io/serial.h>
61
62 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904
63
64 cyg_bool cyg_hal_is_break(char *buf, int size);
65 void cyg_hal_user_break( CYG_ADDRWORD *regs );
66
67 //-------------------------------------------------------------------------
68
69 extern void diag_printf(const char *fmt, ...);
70
71 //-------------------------------------------------------------------------
72 // Forward definitions
73
74 static bool tx3904_serial_init(struct cyg_devtab_entry *tab);
75 static bool tx3904_serial_putc(serial_channel *chan, unsigned char c);
76 static Cyg_ErrNo tx3904_serial_lookup(struct cyg_devtab_entry **tab, 
77                                    struct cyg_devtab_entry *sub_tab,
78                                    const char *name);
79 static unsigned char tx3904_serial_getc(serial_channel *chan);
80 static Cyg_ErrNo tx3904_serial_set_config(serial_channel *chan, cyg_uint32 key,
81                                           const void *xbuf, cyg_uint32 *len);
82 static void tx3904_serial_start_xmit(serial_channel *chan);
83 static void tx3904_serial_stop_xmit(serial_channel *chan);
84
85 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
86 static cyg_uint32 tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs);
87 static void       tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
88 #endif
89
90
91 //-------------------------------------------------------------------------
92 // TX3904 serial line control register values:
93
94 // Offsets to serial control registers from base
95 #define SERIAL_CR       0x00
96 #define SERIAL_SR       0x04
97 #define SERIAL_ICR      0x08
98 #define SERIAL_ISR      0x0C
99 #define SERIAL_FCR      0x10
100 #define SERIAL_BRG      0x14
101 #define SERIAL_TXB      0x20
102 #define SERIAL_RXB      0x30
103
104 // Status register bits
105 #define ISR_RXRDY       0x01
106 #define ISR_TXRDY       0x02
107 #define ISR_ERROR       0x04
108
109 // Control register bits
110 #define LCR_SB1         0x0000
111 #define LCR_SB1_5       0x0000
112 #define LCR_SB2         0x0004
113 #define LCR_PN          0x0000    // Parity mode - none
114 #define LCR_PS          0x0000    // Forced "space" parity
115 #define LCR_PM          0x0000    // Forced "mark" parity
116 #define LCR_PE          0x0018    // Parity mode - even
117 #define LCR_PO          0x0010    // Parity mode - odd
118 #define LCR_WL5         0x0001    // not supported - use 7bit
119 #define LCR_WL6         0x0001    // not supported - use 7bit
120 #define LCR_WL7         0x0001    // 7 bit chars
121 #define LCR_WL8         0x0000    // 8 bit chars
122
123 #define LCR_BRG         0x0020    // Select baud rate generator
124
125 #define ICR_RXE         0x0001  // receive enable
126 #define ICR_TXE         0x0002  // transmit enable
127
128 //-------------------------------------------------------------------------
129 // Tables to map input values to hardware settings
130
131 static unsigned char select_word_length[] = {
132     LCR_WL5,    // 5 bits / word (char)
133     LCR_WL6,
134     LCR_WL7,
135     LCR_WL8
136 };
137
138 static unsigned char select_stop_bits[] = {
139     0,
140     LCR_SB1,    // 1 stop bit
141     LCR_SB1_5,  // 1.5 stop bit
142     LCR_SB2     // 2 stop bits
143 };
144
145 static unsigned char select_parity[] = {
146     LCR_PN,     // No parity
147     LCR_PE,     // Even parity
148     LCR_PO,     // Odd parity
149     LCR_PM,     // Mark parity
150     LCR_PS,     // Space parity
151 };
152
153 // The values in this table plug straight into the BRG register
154 // in the serial driver hardware. They comprise a baud rate divisor
155 // in the bottom 8 bits and a clock selector in the top 8 bits.
156 // These figures all come from Toshiba.
157
158 #if (CYGHWR_HAL_MIPS_CPU_FREQ == 50)
159
160 static unsigned short select_baud[] = {
161     0,          // Unused
162     0,          // 50
163     0,          // 75
164     0,          // 110
165     0,          // 134.5
166     0,          // 150
167     0,          // 200
168     0,          // 300
169     0x0300|20,  // 600
170     0x0300|10,  // 1200
171     0,          // 1800
172     0x0300|05,  // 2400
173     0,          // 3600
174     0x0300|10,  // 4800
175     0,          // 7200
176     0x0200|05,  // 9600
177     0,          // 14400
178     0x0100|10,  // 19200
179     0x0100|05,  // 38400
180     0,          // 57600
181     0,          // 115200
182     0,          // 230400
183 };
184
185 #elif (CYGHWR_HAL_MIPS_CPU_FREQ == 66)
186
187 static unsigned short select_baud[] = {
188     0,          // Unused
189     0,          // 50
190     0,          // 75
191     0,          // 110
192     0,          // 134.5
193     0,          // 150
194     0,          // 200
195     0,          // 300
196     0x0300|27,  // 600
197     0x0200|54,  // 1200
198     0,          // 1800
199     0x0200|27,  // 2400
200     0,          // 3600
201     0x0100|54,  // 4800
202     0,          // 7200
203     0x0100|27,  // 9600
204     0,          // 14400
205     0x0000|54,  // 19200
206     0x0000|27,  // 38400
207     0,          // 57600
208     0,          // 115200
209     0,          // 230400
210 };
211
212 #else
213
214 #error Unsupported CPU frequency
215
216 #endif
217
218 //-------------------------------------------------------------------------
219 // Info for each serial device controlled
220
221 typedef struct tx3904_serial_info {
222     CYG_ADDRWORD   base;
223     CYG_WORD       int_num;
224     cyg_interrupt  interrupt;
225     cyg_handle_t   interrupt_handle;
226     cyg_uint8      input_char;
227     cyg_bool       input_char_valid;
228     cyg_bool       output_ready;
229     cyg_uint16     cur_baud;
230 } tx3904_serial_info;
231
232 //-------------------------------------------------------------------------
233 // Callback functions exported by this driver
234
235 static SERIAL_FUNS(tx3904_serial_funs, 
236                    tx3904_serial_putc, 
237                    tx3904_serial_getc,
238                    tx3904_serial_set_config,
239                    tx3904_serial_start_xmit,
240                    tx3904_serial_stop_xmit
241     );
242
243 //-------------------------------------------------------------------------
244 // Hardware info for each serial line
245
246 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
247 static tx3904_serial_info tx3904_serial_info0 = {
248     0xFFFFF300,
249     CYGNUM_HAL_INTERRUPT_SIO_0
250 };
251 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE > 0
252 static unsigned char tx3904_serial_out_buf0[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE];
253 static unsigned char tx3904_serial_in_buf0[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE];
254 #endif
255 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
256
257 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
258 static tx3904_serial_info tx3904_serial_info1 = {
259     0xFFFFF400,
260     CYGNUM_HAL_INTERRUPT_SIO_1
261 };
262 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE > 0
263 static unsigned char tx3904_serial_out_buf1[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE];
264 static unsigned char tx3904_serial_in_buf1[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE];
265 #endif
266 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
267
268 //-------------------------------------------------------------------------
269 // Channel descriptions:
270
271 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
272 #define SIZEOF_BUF(_x_) 0
273 #else
274 #define SIZEOF_BUF(_x_) sizeof(_x_)
275 #endif
276
277 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
278 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE > 0
279 static SERIAL_CHANNEL_USING_INTERRUPTS(tx3904_serial_channel0,
280                                        tx3904_serial_funs, 
281                                        tx3904_serial_info0,
282                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BAUD),
283                                        CYG_SERIAL_STOP_DEFAULT,
284                                        CYG_SERIAL_PARITY_DEFAULT,
285                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
286                                        CYG_SERIAL_FLAGS_DEFAULT,
287                                        &tx3904_serial_out_buf0[0],
288                                        SIZEOF_BUF(tx3904_serial_out_buf0),
289                                        &tx3904_serial_in_buf0[0],
290                                        SIZEOF_BUF(tx3904_serial_in_buf0)
291     );
292 #else
293 static SERIAL_CHANNEL(tx3904_serial_channel0,
294                       tx3904_serial_funs, 
295                       tx3904_serial_info0,
296                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BAUD),
297                       CYG_SERIAL_STOP_DEFAULT,
298                       CYG_SERIAL_PARITY_DEFAULT,
299                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
300                       CYG_SERIAL_FLAGS_DEFAULT
301     );
302 #endif
303 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
304
305 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
306 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE > 0
307 static SERIAL_CHANNEL_USING_INTERRUPTS(tx3904_serial_channel1,
308                                        tx3904_serial_funs, 
309                                        tx3904_serial_info1,
310                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BAUD),
311                                        CYG_SERIAL_STOP_DEFAULT,
312                                        CYG_SERIAL_PARITY_DEFAULT,
313                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
314                                        CYG_SERIAL_FLAGS_DEFAULT,
315                                        &tx3904_serial_out_buf1[0],
316                                        SIZEOF_BUF(tx3904_serial_out_buf1),
317                                        &tx3904_serial_in_buf1[0],
318                                        SIZEOF_BUF(tx3904_serial_in_buf1)
319     );
320 #else
321 static SERIAL_CHANNEL(tx3904_serial_channel1,
322                       tx3904_serial_funs, 
323                       tx3904_serial_info1,
324                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BAUD),
325                       CYG_SERIAL_STOP_DEFAULT,
326                       CYG_SERIAL_PARITY_DEFAULT,
327                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
328                       CYG_SERIAL_FLAGS_DEFAULT
329     );
330 #endif
331 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
332
333 //-------------------------------------------------------------------------
334 // And finally, the device table entries:
335
336 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
337 DEVTAB_ENTRY(tx3904_serial_io0, 
338              CYGDAT_IO_SERIAL_TX39_JMR3904_SERIAL0_NAME,
339              0,                     // Does not depend on a lower level interface
340              &cyg_io_serial_devio, 
341              tx3904_serial_init, 
342              tx3904_serial_lookup,     // Serial driver may need initializing
343              &tx3904_serial_channel0
344     );
345 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
346
347 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
348 DEVTAB_ENTRY(tx3904_serial_io1, 
349              CYGDAT_IO_SERIAL_TX39_JMR3904_SERIAL1_NAME,
350              0,                     // Does not depend on a lower level interface
351              &cyg_io_serial_devio, 
352              tx3904_serial_init, 
353              tx3904_serial_lookup,     // Serial driver may need initializing
354              &tx3904_serial_channel1
355     );
356 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
357
358 // ------------------------------------------------------------------------
359 // Delay for some number of character times. This is based on the baud
360 // rate currently set. We use the numbers that plug in to the BRG
361 // clock select and divider to control two loops. The innermost delay
362 // loop uses a count that is derived from dividing the CPU frequency
363 // by the BRG granularity (and we then add 1 to compensate for any
364 // rounding).  This gives the number of cycles that the innermost loop
365 // must consume.  For the sake of simplicity we assume that this loop
366 // will take 1 cycle per loop, which is roughly true in optimized
367 // code.
368
369 void delay_char_time(tx3904_serial_info *tx3904_chan, int n)
370 {
371     static cyg_uint16 clock_val[4] = { 4, 16, 64, 256 };
372     cyg_uint16 baud_val = select_baud[tx3904_chan->cur_baud];
373     cyg_count32 clock_loop = clock_val[baud_val>>8];
374     cyg_count32 div_loop = baud_val & 0xFF;
375     cyg_count32 bit_time = ((CYGHWR_HAL_MIPS_CPU_FREQ_ACTUAL)/(2457600)) + 1;
376     
377     n *= 11;    // allow for start and stop bits and 8 data bits
378     
379     while( n-- )
380     {
381         cyg_count32 i,j,k;
382
383         for( i = 0; i < clock_loop; i++ )
384             for( j = 0; j < div_loop; j++ )
385                 for( k = 0; k < bit_time; k++ )
386                     continue;
387     }
388 }
389
390 //-------------------------------------------------------------------------
391
392 static bool
393 tx3904_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
394 {
395     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
396     cyg_uint16 cr = 0;
397     cyg_uint16 icr = 0;
398     cyg_uint16 baud_divisor = select_baud[new_config->baud];
399
400     if (baud_divisor == 0)
401         return false;  // Invalid baud rate selected
402
403     // set up other config values:
404
405     cr |= select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5];
406     cr |= select_stop_bits[new_config->stop];
407     cr |= select_parity[new_config->parity];
408
409     // Source transfer clock from BRG
410     cr |= LCR_BRG;
411
412 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
413     // Enable RX interrupts only at present
414 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
415     if ((chan->out_cbuf.len != 0) || (chan == &tx3904_serial_channel0)) {
416 #else
417     if (chan->out_cbuf.len != 0) {
418 #endif
419         icr |= ICR_RXE;
420     }
421 #endif
422
423     // Avoid any interrupts while we are fiddling with the line parameters.
424     cyg_drv_interrupt_mask(tx3904_chan->int_num);
425
426     
427     // In theory we should wait here for the transmitter to drain the
428     // FIFO so we dont change the line parameters with characters
429     // unsent. Unfortunately the TX39 serial devices do not allow us
430     // to discover when the FIFO is empty.
431
432     delay_char_time(tx3904_chan, 8);
433     
434     // Disable device entirely.
435 //    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, 0);
436 //    HAL_WRITE_UINT8(tx3904_chan->base+SERIAL_ICR, 0);
437
438     // Reset the FIFOs
439
440     HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 7);
441     HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 0);
442     
443     // Set up baud rate
444
445     HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_BRG, baud_divisor );
446
447     // Write CR into hardware
448     HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, cr);    
449
450     // Write ICR into hardware
451     HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_ICR, icr);    
452
453     // Re-enable interrupts.
454     cyg_drv_interrupt_unmask(tx3904_chan->int_num);
455
456     // Save current baud rate
457     tx3904_chan->cur_baud = new_config->baud;
458
459     if (new_config != &chan->config) {
460         chan->config = *new_config;
461     }
462     return true;
463 }
464
465 //-------------------------------------------------------------------------
466 // Function to initialize the device.  Called at bootstrap time.
467
468 bool tx3904_serial_init(struct cyg_devtab_entry *tab)
469 {
470     serial_channel *chan = (serial_channel *)tab->priv;
471     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
472
473     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
474
475     tx3904_chan->cur_baud = CYGNUM_SERIAL_BAUD_38400;
476         
477 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
478     if (chan->out_cbuf.len != 0) {
479         // Install and enable the interrupt
480         cyg_drv_interrupt_create(tx3904_chan->int_num,
481                                  4,                      // Priority
482                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
483                                  (cyg_ISR_t *)tx3904_serial_ISR,
484                                  tx3904_serial_DSR,
485                                  &tx3904_chan->interrupt_handle,
486                                  &tx3904_chan->interrupt);
487         cyg_drv_interrupt_attach(tx3904_chan->interrupt_handle);
488         cyg_drv_interrupt_unmask(tx3904_chan->int_num);
489     }
490 #endif
491     
492     tx3904_serial_config_port(chan, &chan->config, true);
493     
494     return true;
495 }
496
497 //-------------------------------------------------------------------------
498 // This routine is called when the device is "looked" up (i.e. attached)
499
500 static Cyg_ErrNo 
501 tx3904_serial_lookup(struct cyg_devtab_entry **tab, 
502                   struct cyg_devtab_entry *sub_tab,
503                   const char *name)
504 {
505     serial_channel *chan = (serial_channel *)(*tab)->priv;
506     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
507     return ENOERR;
508 }
509
510 //-------------------------------------------------------------------------
511 // Return 'true' if character is sent to device
512
513 bool
514 tx3904_serial_putc(serial_channel *chan, unsigned char c)
515 {
516     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
517     cyg_uint16 isr;
518
519     HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
520
521     if( isr & ISR_TXRDY )
522     {
523         HAL_WRITE_UINT8( tx3904_chan->base+SERIAL_TXB, c );
524
525         isr &= ~ISR_TXRDY;
526         
527         HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
528
529         return true;
530     }
531     else return false;
532 }
533
534 //-------------------------------------------------------------------------
535
536 unsigned char 
537 tx3904_serial_getc(serial_channel *chan)
538 {
539     unsigned char c;
540     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
541     cyg_uint16 isr;
542
543     do
544     {
545         HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
546
547         // Eliminate any RX errors
548         if( isr & ISR_ERROR )
549         {
550             cyg_uint16 sr = 0;
551             
552             isr &= ISR_ERROR;
553
554 //            HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
555
556             HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, sr );            
557             HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
558         }
559         
560     } while( (isr & ISR_RXRDY) != ISR_RXRDY );
561     
562     HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, c );
563
564     isr &= ~ISR_RXRDY;
565         
566     HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
567
568     return c;
569 }
570
571 //-------------------------------------------------------------------------
572
573 static Cyg_ErrNo
574 tx3904_serial_set_config(serial_channel *chan, cyg_uint32 key,
575                          const void *xbuf, cyg_uint32 *len)
576 {
577     switch (key) {
578     case CYG_IO_SET_CONFIG_SERIAL_INFO:
579       {
580         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
581         if ( *len < sizeof(cyg_serial_info_t) ) {
582             return -EINVAL;
583         }
584         *len = sizeof(cyg_serial_info_t);
585         if ( true != tx3904_serial_config_port(chan, config, false) )
586             return -EINVAL;
587       }
588       break;
589     default:
590         return -EINVAL;
591     }
592     return ENOERR;
593 }
594
595 //-------------------------------------------------------------------------
596 // Enable the transmitter on the device
597
598 static void
599 tx3904_serial_start_xmit(serial_channel *chan)
600 {
601 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
602     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
603     cyg_uint16 icr;
604
605     HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
606
607     icr |= ICR_TXE;
608
609     HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
610 #endif    
611 }
612
613 //-------------------------------------------------------------------------
614 // Disable the transmitter on the device
615
616 static void 
617 tx3904_serial_stop_xmit(serial_channel *chan)
618 {
619 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
620     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
621     cyg_uint16 icr;
622
623     HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
624
625     icr &= ~ICR_TXE;
626
627     HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
628 #endif    
629 }
630
631 //-------------------------------------------------------------------------
632 // Serial I/O - low level interrupt handlers (ISR)
633
634 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
635
636 static cyg_uint32 
637 tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs)
638 {
639     serial_channel *chan = (serial_channel *)data;
640     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
641     cyg_uint8 isr;
642     cyg_uint32 result = 0;
643     
644     cyg_drv_interrupt_mask(tx3904_chan->int_num);
645     cyg_drv_interrupt_acknowledge(tx3904_chan->int_num);
646
647     HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
648
649     // Eliminate any RX errors
650     if( isr & ISR_ERROR )
651     {
652         cyg_uint16 sr = 0;
653             
654         isr &= ~ISR_ERROR;
655
656         HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
657         
658         HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, 0 );            
659     }
660
661     // Check for a TX interrupt and set the flag if so.
662     if( isr & ISR_TXRDY )
663     {
664         isr &= ~ISR_TXRDY;
665
666         tx3904_chan->output_ready = true;
667
668         result |= CYG_ISR_CALL_DSR;  // Cause DSR to be run
669     }
670     
671     
672     // Check here for an RX interrupt and fetch the character. If it
673     // is a ^C then call into GDB stub to handle it.
674     
675     if( isr & ISR_RXRDY )
676     {
677         cyg_uint8 rxb;
678         HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );
679
680         isr &= ~ISR_RXRDY;
681
682         if( cyg_hal_is_break( &rxb , 1 ) )
683             cyg_hal_user_break( regs );
684         else
685         {
686             tx3904_chan->input_char = rxb;
687             tx3904_chan->input_char_valid = true;
688             result |= CYG_ISR_CALL_DSR;  // Cause DSR to be run
689         }
690
691     }
692
693     HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
694     
695     return result;
696 }
697
698
699 #endif
700
701 //-------------------------------------------------------------------------
702 // Serial I/O - high level interrupt handler (DSR)
703
704 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
705
706 static void       
707 tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
708 {
709     serial_channel *chan = (serial_channel *)data;
710     tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
711     cyg_uint8 isr;
712
713     HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
714
715     if( tx3904_chan->input_char_valid )
716     {
717         (chan->callbacks->rcv_char)(chan, tx3904_chan->input_char);
718
719         tx3904_chan->input_char_valid = false;
720
721 #if 0        
722         // And while we are here, pull any further characters out of the
723         // FIFO. This should help to reduce the interrupt rate.
724
725         HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
726     
727         while( isr & ISR_RXRDY )
728         {
729             cyg_uint8 rxb;
730             HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );
731
732             (chan->callbacks->rcv_char)(chan, rxb);
733             
734             isr &= ~ISR_RXRDY;
735         
736             HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
737             HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );        
738         }
739 #endif
740         
741     }
742
743     if( tx3904_chan->output_ready )
744     {
745         (chan->callbacks->xmt_char)(chan);
746
747         tx3904_chan->output_ready = false;
748     }
749     
750     cyg_drv_interrupt_unmask(tx3904_chan->int_num);
751 }
752
753 #endif
754 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904
755
756 //-------------------------------------------------------------------------
757 // EOF tx3904_serial.c