diff packages/io/serial/current/src/mips/tx3904_serial.c @ 2:443894e2e912 ecos-v1_2_1-release

Block commit of eCos version 1.2.1
author jlarmour
date Tue, 11 May 1999 12:24:34 +0000
parents
children 7a6ac9edc838
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/io/serial/current/src/mips/tx3904_serial.c
@@ -0,0 +1,729 @@
+//==========================================================================
+//
+//      tx3904_serial.c
+//
+//      Serial device driver for TX3904 on-chip serial devices
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//
+// -------------------------------------------
+// The contents of this file are subject to the Cygnus eCos Public License
+// Version 1.0 (the "License"); you may not use this file except in
+// compliance with the License.  You may obtain a copy of the License at
+// http://sourceware.cygnus.com/ecos
+// 
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
+// License for the specific language governing rights and limitations under
+// the License.
+// 
+// The Original Code is eCos - Embedded Cygnus Operating System, released
+// September 30, 1998.
+// 
+// The Initial Developer of the Original Code is Cygnus.  Portions created
+// by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions.  All Rights Reserved.
+// -------------------------------------------
+//
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   nickg
+// Contributors: nickg
+// Date:        1999-03-3
+// Purpose:     TX3904 serial device driver
+// Description: TX3904 serial device driver
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/hal.h>
+#include <pkgconf/io_serial.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/serial.h>
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904
+
+cyg_bool cyg_hal_is_break(char *buf, int size);
+void cyg_hal_user_break( CYG_ADDRWORD *regs );
+
+//-------------------------------------------------------------------------
+
+extern void diag_printf(const char *fmt, ...);
+
+//-------------------------------------------------------------------------
+// Forward definitions
+
+static bool tx3904_serial_init(struct cyg_devtab_entry *tab);
+static bool tx3904_serial_putc(serial_channel *chan, unsigned char c);
+static Cyg_ErrNo tx3904_serial_lookup(struct cyg_devtab_entry **tab, 
+                                   struct cyg_devtab_entry *sub_tab,
+                                   const char *name);
+static unsigned char tx3904_serial_getc(serial_channel *chan);
+static bool tx3904_serial_set_config(serial_channel *chan, cyg_serial_info_t *config);
+static void tx3904_serial_start_xmit(serial_channel *chan);
+static void tx3904_serial_stop_xmit(serial_channel *chan);
+
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
+static cyg_uint32 tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs);
+static void       tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
+#endif
+
+
+//-------------------------------------------------------------------------
+// TX3904 serial line control register values:
+
+// Offsets to serial control registers from base
+#define SERIAL_CR       0x00
+#define SERIAL_SR       0x04
+#define SERIAL_ICR      0x08
+#define SERIAL_ISR      0x0C
+#define SERIAL_FCR      0x10
+#define SERIAL_BRG      0x14
+#define SERIAL_TXB      0x20
+#define SERIAL_RXB      0x30
+
+// Status register bits
+#define ISR_RXRDY       0x01
+#define ISR_TXRDY       0x02
+#define ISR_ERROR       0x04
+
+// Control register bits
+#define LCR_SB1         0x0000
+#define LCR_SB1_5       0x0000
+#define LCR_SB2         0x0004
+#define LCR_PN          0x0000    // Parity mode - none
+#define LCR_PS          0x0000    // Forced "space" parity
+#define LCR_PM          0x0000    // Forced "mark" parity
+#define LCR_PE          0x0018    // Parity mode - even
+#define LCR_PO          0x0010    // Parity mode - odd
+#define LCR_WL5         0x0001    // not supported - use 7bit
+#define LCR_WL6         0x0001    // not supported - use 7bit
+#define LCR_WL7         0x0001    // 7 bit chars
+#define LCR_WL8         0x0000    // 8 bit chars
+
+#define LCR_BRG         0x0020    // Select baud rate generator
+
+#define ICR_RXE         0x0001  // receive enable
+#define ICR_TXE         0x0002  // transmit enable
+
+//-------------------------------------------------------------------------
+// Tables to map input values to hardware settings
+
+static unsigned char select_word_length[] = {
+    LCR_WL5,    // 5 bits / word (char)
+    LCR_WL6,
+    LCR_WL7,
+    LCR_WL8
+};
+
+static unsigned char select_stop_bits[] = {
+    0,
+    LCR_SB1,    // 1 stop bit
+    LCR_SB1_5,  // 1.5 stop bit
+    LCR_SB2     // 2 stop bits
+};
+
+static unsigned char select_parity[] = {
+    LCR_PN,     // No parity
+    LCR_PE,     // Even parity
+    LCR_PO,     // Odd parity
+    LCR_PM,     // Mark parity
+    LCR_PS,     // Space parity
+};
+
+// The values in this table plug straight into the BRG register
+// in the serial driver hardware. They comprise a baud rate divisor
+// in the bottom 8 bits and a clock selector in the top 8 bits.
+// These figures all come from Toshiba.
+
+#if (CYGHWR_HAL_MIPS_CPU_FREQ == 50)
+
+static unsigned short select_baud[] = {
+    0,          // Unused
+    0,          // 50
+    0,          // 75
+    0,          // 110
+    0,          // 134.5
+    0,          // 150
+    0,          // 200
+    0,          // 300
+    0x0300|20,  // 600
+    0x0300|10,  // 1200
+    0,          // 1800
+    0x0300|05,  // 2400
+    0,          // 3600
+    0x0300|10,  // 4800
+    0,          // 7200
+    0x0200|05,  // 9600
+    0,          // 14400
+    0x0100|10,  // 19200
+    0x0100|05,  // 38400
+    0,          // 57600
+    0,          // 115200
+    0,          // 230400
+};
+
+#elif (CYGHWR_HAL_MIPS_CPU_FREQ == 66)
+
+static unsigned short select_baud[] = {
+    0,          // Unused
+    0,          // 50
+    0,          // 75
+    0,          // 110
+    0,          // 134.5
+    0,          // 150
+    0,          // 200
+    0,          // 300
+    0x0300|27,  // 600
+    0x0200|54,  // 1200
+    0,          // 1800
+    0x0200|27,  // 2400
+    0,          // 3600
+    0x0100|54,  // 4800
+    0,          // 7200
+    0x0100|27,  // 9600
+    0,          // 14400
+    0x0000|54,  // 19200
+    0x0000|27,  // 38400
+    0,          // 57600
+    0,          // 115200
+    0,          // 230400
+};
+
+#else
+
+#error Unsupported CPU frequency
+
+#endif
+
+//-------------------------------------------------------------------------
+// Info for each serial device controlled
+
+typedef struct tx3904_serial_info {
+    CYG_ADDRWORD   base;
+    CYG_WORD       int_num;
+    cyg_interrupt  interrupt;
+    cyg_handle_t   interrupt_handle;
+    cyg_uint8      input_char;
+    cyg_bool       input_char_valid;
+    cyg_bool       output_ready;
+    cyg_uint16     cur_baud;
+} tx3904_serial_info;
+
+//-------------------------------------------------------------------------
+// Callback functions exported by this driver
+
+static SERIAL_FUNS(tx3904_serial_funs, 
+                   tx3904_serial_putc, 
+                   tx3904_serial_getc,
+                   tx3904_serial_set_config,
+                   tx3904_serial_start_xmit,
+                   tx3904_serial_stop_xmit
+    );
+
+//-------------------------------------------------------------------------
+// Hardware info for each serial line
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+static tx3904_serial_info tx3904_serial_info0 = {
+    0xFFFFF300,
+    CYGNUM_HAL_INTERRUPT_SIO_0
+};
+#if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE > 0
+static unsigned char tx3904_serial_out_buf0[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE];
+static unsigned char tx3904_serial_in_buf0[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE];
+#endif
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
+static tx3904_serial_info tx3904_serial_info1 = {
+    0xFFFFF400,
+    CYGNUM_HAL_INTERRUPT_SIO_1
+};
+#if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE > 0
+static unsigned char tx3904_serial_out_buf1[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE];
+static unsigned char tx3904_serial_in_buf1[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE];
+#endif
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
+
+//-------------------------------------------------------------------------
+// Channel descriptions:
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
+#define SIZEOF_BUF(_x_) 0
+#else
+#define SIZEOF_BUF(_x_) sizeof(_x_)
+#endif
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+#if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE > 0
+static SERIAL_CHANNEL_USING_INTERRUPTS(tx3904_serial_channel0,
+                                       tx3904_serial_funs, 
+                                       tx3904_serial_info0,
+                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BAUD),
+                                       CYG_SERIAL_STOP_DEFAULT,
+                                       CYG_SERIAL_PARITY_DEFAULT,
+                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
+                                       CYG_SERIAL_FLAGS_DEFAULT,
+                                       &tx3904_serial_out_buf0[0],
+                                       SIZEOF_BUF(tx3904_serial_out_buf0),
+                                       &tx3904_serial_in_buf0[0],
+                                       SIZEOF_BUF(tx3904_serial_in_buf0)
+    );
+#else
+static SERIAL_CHANNEL(tx3904_serial_channel0,
+                      tx3904_serial_funs, 
+                      tx3904_serial_info0,
+                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BAUD),
+                      CYG_SERIAL_STOP_DEFAULT,
+                      CYG_SERIAL_PARITY_DEFAULT,
+                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
+                      CYG_SERIAL_FLAGS_DEFAULT
+    );
+#endif
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
+#if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE > 0
+static SERIAL_CHANNEL_USING_INTERRUPTS(tx3904_serial_channel1,
+                                       tx3904_serial_funs, 
+                                       tx3904_serial_info1,
+                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BAUD),
+                                       CYG_SERIAL_STOP_DEFAULT,
+                                       CYG_SERIAL_PARITY_DEFAULT,
+                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
+                                       CYG_SERIAL_FLAGS_DEFAULT,
+                                       &tx3904_serial_out_buf1[0],
+                                       SIZEOF_BUF(tx3904_serial_out_buf1),
+                                       &tx3904_serial_in_buf1[0],
+                                       SIZEOF_BUF(tx3904_serial_in_buf1)
+    );
+#else
+static SERIAL_CHANNEL(tx3904_serial_channel1,
+                      tx3904_serial_funs, 
+                      tx3904_serial_info1,
+                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BAUD),
+                      CYG_SERIAL_STOP_DEFAULT,
+                      CYG_SERIAL_PARITY_DEFAULT,
+                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
+                      CYG_SERIAL_FLAGS_DEFAULT
+    );
+#endif
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
+
+//-------------------------------------------------------------------------
+// And finally, the device table entries:
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+DEVTAB_ENTRY(tx3904_serial_io0, 
+             CYGDAT_IO_SERIAL_TX39_JMR3904_SERIAL0_NAME,
+             0,                     // Does not depend on a lower level interface
+             &serial_devio, 
+             tx3904_serial_init, 
+             tx3904_serial_lookup,     // Serial driver may need initializing
+             &tx3904_serial_channel0
+    );
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
+DEVTAB_ENTRY(tx3904_serial_io1, 
+             CYGDAT_IO_SERIAL_TX39_JMR3904_SERIAL1_NAME,
+             0,                     // Does not depend on a lower level interface
+             &serial_devio, 
+             tx3904_serial_init, 
+             tx3904_serial_lookup,     // Serial driver may need initializing
+             &tx3904_serial_channel1
+    );
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
+
+// ------------------------------------------------------------------------
+// Delay for some number of character times. This is based on the baud
+// rate currently set. We use the numbers that plug in to the BRG
+// clock select and divider to control two loops. The innermost delay
+// loop uses a count that is derived from dividing the CPU frequency
+// by the BRG granularity (and we then add 1 to compensate for any
+// rounding).  This gives the number of cycles that the innermost loop
+// must consume.  For the sake of simplicity we assume that this loop
+// will take 1 cycle per loop, which is roughly true in optimized
+// code.
+
+void delay_char_time(tx3904_serial_info *tx3904_chan, int n)
+{
+    static cyg_uint16 clock_val[4] = { 4, 16, 64, 256 };
+    cyg_uint16 baud_val = select_baud[tx3904_chan->cur_baud];
+    cyg_count32 clock_loop = clock_val[baud_val>>8];
+    cyg_count32 div_loop = baud_val & 0xFF;
+    cyg_count32 bit_time = ((CYGHWR_HAL_MIPS_CPU_FREQ_ACTUAL)/(2457600)) + 1;
+    
+    n *= 11;    // allow for start and stop bits and 8 data bits
+    
+    while( n-- )
+    {
+        cyg_count32 i,j,k;
+
+        for( i = 0; i < clock_loop; i++ )
+            for( j = 0; j < div_loop; j++ )
+                for( k = 0; k < bit_time; k++ )
+                    continue;
+    }
+}
+
+//-------------------------------------------------------------------------
+
+static bool
+tx3904_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
+{
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint16 cr = 0;
+    cyg_uint16 icr = 0;
+    cyg_uint16 baud_divisor = select_baud[new_config->baud];
+
+    if (baud_divisor == 0)
+        return false;  // Invalid baud rate selected
+
+    // set up other config values:
+
+    cr |= select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5];
+    cr |= select_stop_bits[new_config->stop];
+    cr |= select_parity[new_config->parity];
+
+    // Source transfer clock from BRG
+    cr |= LCR_BRG;
+
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
+    // Enable RX interrupts only at present
+#ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
+    if ((chan->out_cbuf.len != 0) || (chan == &tx3904_serial_channel0)) {
+#else
+    if (chan->out_cbuf.len != 0) {
+#endif
+        icr |= ICR_RXE;
+    }
+#endif
+
+    // Avoid any interrupts while we are fiddling with the line parameters.
+    cyg_drv_interrupt_mask(tx3904_chan->int_num);
+
+    
+    // In theory we should wait here for the transmitter to drain the
+    // FIFO so we dont change the line parameters with characters
+    // unsent. Unfortunately the TX39 serial devices do not allow us
+    // to discover when the FIFO is empty.
+
+    delay_char_time(tx3904_chan, 8);
+    
+    // Disable device entirely.
+//    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, 0);
+//    HAL_WRITE_UINT8(tx3904_chan->base+SERIAL_ICR, 0);
+
+    // Reset the FIFOs
+
+    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 7);
+    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 0);
+    
+    // Set up baud rate
+
+    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_BRG, baud_divisor );
+
+    // Write CR into hardware
+    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, cr);    
+
+    // Write ICR into hardware
+    HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_ICR, icr);    
+
+    // Re-enable interrupts.
+    cyg_drv_interrupt_unmask(tx3904_chan->int_num);
+
+    // Save current baud rate
+    tx3904_chan->cur_baud = new_config->baud;
+
+    if (new_config != &chan->config) {
+        chan->config = *new_config;
+    }
+    return true;
+}
+
+//-------------------------------------------------------------------------
+// Function to initialize the device.  Called at bootstrap time.
+
+bool tx3904_serial_init(struct cyg_devtab_entry *tab)
+{
+    serial_channel *chan = (serial_channel *)tab->priv;
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+
+    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
+
+    tx3904_chan->cur_baud = CYGNUM_SERIAL_BAUD_38400;
+        
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
+    if (chan->out_cbuf.len != 0) {
+        // Install and enable the interrupt
+        cyg_drv_interrupt_create(tx3904_chan->int_num,
+                                 4,                      // Priority
+                                 (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
+                                 (cyg_ISR_t *)tx3904_serial_ISR,
+                                 tx3904_serial_DSR,
+                                 &tx3904_chan->interrupt_handle,
+                                 &tx3904_chan->interrupt);
+        cyg_drv_interrupt_attach(tx3904_chan->interrupt_handle);
+        cyg_drv_interrupt_unmask(tx3904_chan->int_num);
+    }
+#endif
+    
+    tx3904_serial_config_port(chan, &chan->config, true);
+    
+    return true;
+}
+
+//-------------------------------------------------------------------------
+// This routine is called when the device is "looked" up (i.e. attached)
+
+static Cyg_ErrNo 
+tx3904_serial_lookup(struct cyg_devtab_entry **tab, 
+                  struct cyg_devtab_entry *sub_tab,
+                  const char *name)
+{
+    serial_channel *chan = (serial_channel *)(*tab)->priv;
+    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
+    return ENOERR;
+}
+
+//-------------------------------------------------------------------------
+// Return 'true' if character is sent to device
+
+bool
+tx3904_serial_putc(serial_channel *chan, unsigned char c)
+{
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint16 isr;
+
+    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+
+    if( isr & ISR_TXRDY )
+    {
+        HAL_WRITE_UINT8( tx3904_chan->base+SERIAL_TXB, c );
+
+        isr &= ~ISR_TXRDY;
+        
+        HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+
+        return true;
+    }
+    else return false;
+}
+
+//-------------------------------------------------------------------------
+
+unsigned char 
+tx3904_serial_getc(serial_channel *chan)
+{
+    unsigned char c;
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint16 isr;
+
+    do
+    {
+        HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+
+        // Eliminate any RX errors
+        if( isr & ISR_ERROR )
+        {
+            cyg_uint16 sr = 0;
+            
+            isr &= ISR_ERROR;
+
+//            HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
+
+            HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, sr );            
+            HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+        }
+        
+    } while( (isr & ISR_RXRDY) != ISR_RXRDY );
+    
+    HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, c );
+
+    isr &= ~ISR_RXRDY;
+        
+    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+
+    return c;
+}
+
+//-------------------------------------------------------------------------
+
+bool 
+tx3904_serial_set_config(serial_channel *chan, cyg_serial_info_t *config)
+{
+    return tx3904_serial_config_port(chan, config, false);
+}
+
+//-------------------------------------------------------------------------
+// Enable the transmitter on the device
+
+static void
+tx3904_serial_start_xmit(serial_channel *chan)
+{
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint16 icr;
+
+    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
+
+    icr |= ICR_TXE;
+
+    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
+#endif    
+}
+
+//-------------------------------------------------------------------------
+// Disable the transmitter on the device
+
+static void 
+tx3904_serial_stop_xmit(serial_channel *chan)
+{
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE    
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint16 icr;
+
+    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
+
+    icr &= ~ICR_TXE;
+
+    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
+#endif    
+}
+
+//-------------------------------------------------------------------------
+// Serial I/O - low level interrupt handlers (ISR)
+
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
+
+static cyg_uint32 
+tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs)
+{
+    serial_channel *chan = (serial_channel *)data;
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint8 isr;
+    cyg_uint32 result = 0;
+    
+    cyg_drv_interrupt_mask(tx3904_chan->int_num);
+    cyg_drv_interrupt_acknowledge(tx3904_chan->int_num);
+
+    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+
+    // Eliminate any RX errors
+    if( isr & ISR_ERROR )
+    {
+        cyg_uint16 sr = 0;
+            
+        isr &= ~ISR_ERROR;
+
+        HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
+        
+        HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, 0 );            
+    }
+
+    // Check for a TX interrupt and set the flag if so.
+    if( isr & ISR_TXRDY )
+    {
+        isr &= ~ISR_TXRDY;
+
+        tx3904_chan->output_ready = true;
+
+        result |= CYG_ISR_CALL_DSR;  // Cause DSR to be run
+    }
+    
+    
+    // Check here for an RX interrupt and fetch the character. If it
+    // is a ^C then call into GDB stub to handle it.
+    
+    if( isr & ISR_RXRDY )
+    {
+        cyg_uint8 rxb;
+        HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );
+
+        isr &= ~ISR_RXRDY;
+
+        if( cyg_hal_is_break( &rxb , 1 ) )
+            cyg_hal_user_break( regs );
+        else
+        {
+            tx3904_chan->input_char = rxb;
+            tx3904_chan->input_char_valid = true;
+            result |= CYG_ISR_CALL_DSR;  // Cause DSR to be run
+        }
+
+    }
+
+    HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+    
+    return result;
+}
+
+
+#endif
+
+//-------------------------------------------------------------------------
+// Serial I/O - high level interrupt handler (DSR)
+
+#ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
+
+static void       
+tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+    serial_channel *chan = (serial_channel *)data;
+    tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
+    cyg_uint8 isr;
+
+    HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+
+    if( tx3904_chan->input_char_valid )
+    {
+        (chan->callbacks->rcv_char)(chan, tx3904_chan->input_char);
+
+        tx3904_chan->input_char_valid = false;
+
+#if 0        
+        // And while we are here, pull any further characters out of the
+        // FIFO. This should help to reduce the interrupt rate.
+
+        HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+    
+        while( isr & ISR_RXRDY )
+        {
+            cyg_uint8 rxb;
+            HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );
+
+            (chan->callbacks->rcv_char)(chan, rxb);
+            
+            isr &= ~ISR_RXRDY;
+        
+            HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
+            HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );        
+        }
+#endif
+        
+    }
+
+    if( tx3904_chan->output_ready )
+    {
+        (chan->callbacks->xmt_char)(chan);
+
+        tx3904_chan->output_ready = false;
+    }
+    
+    cyg_drv_interrupt_unmask(tx3904_chan->int_num);
+}
+
+#endif
+#endif // CYGPKG_IO_SERIAL_TX39_JMR3904
+
+//-------------------------------------------------------------------------
+// EOF tx3904_serial.c