From: "Mikael Starvik" <mikael.starvik@axis.com>

Changes to serial port driver.

* Use I/O and DMA allocator.
* Replaced calls to restore_flags etc.
* Added CRISv32 driver.
* Remove the console shortcut hack.

Signed-off-by: Mikael Starvik <starvik@axis.com>

rmk:

I'm going to say no outright to this because it introduces another driver
which doesn't use the serial core _and_ the comments do not explain why.

Is cris SMP?  If yes, converting save_flags()+cli() to local_irq_save()
doesn't provide the required locking to ensure correct operation.
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 drivers/serial/Makefile  |    1 
 drivers/serial/crisv10.c |  540 +++----
 drivers/serial/crisv10.h |   15 
 drivers/serial/crisv32.c | 3568 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/serial/crisv32.h |  126 +
 5 files changed, 4001 insertions(+), 249 deletions(-)

diff -puN drivers/serial/crisv10.c~cris-update-12-17-serial-port-driver drivers/serial/crisv10.c
--- 25/drivers/serial/crisv10.c~cris-update-12-17-serial-port-driver	2005-06-27 18:32:02.000000000 -0700
+++ 25-akpm/drivers/serial/crisv10.c	2005-06-27 18:32:02.000000000 -0700
@@ -2,7 +2,7 @@
  *
  * Serial port driver for the ETRAX 100LX chip
  *
- *    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Axis Communications AB
+ *    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004  Axis Communications AB
  *
  *    Many, many authors. Based once upon a time on serial.c for 16x50.
  *
@@ -447,6 +447,7 @@ static char *serial_version = "$Revision
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
@@ -457,8 +458,9 @@ static char *serial_version = "$Revision
 /* non-arch dependent serial structures are in linux/serial.h */
 #include <linux/serial.h>
 /* while we keep our own stuff (struct e100_serial) in a local .h file */
-#include "serial.h"
+#include "crisv10.h"
 #include <asm/fasttimer.h>
+#include <asm/arch/io_interface_mux.h>
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 #ifndef CONFIG_ETRAX_FAST_TIMER
@@ -591,13 +593,12 @@ unsigned long timer_data_to_ns(unsigned 
 static void change_speed(struct e100_serial *info);
 static void rs_throttle(struct tty_struct * tty);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
-static int rs_write(struct tty_struct * tty, int from_user,
+static int rs_write(struct tty_struct * tty,
                     const unsigned char *buf, int count);
-extern _INLINE_ int rs_raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int rs_raw_write(struct tty_struct * tty,
                             const unsigned char *buf, int count);
 #ifdef CONFIG_ETRAX_RS485
-static int e100_write_rs485(struct tty_struct * tty, int from_user,
-                            const unsigned char *buf, int count);
+static int e100_write_rs485(struct tty_struct * tty, const unsigned char *buf, int count);
 #endif
 static int get_lsr_info(struct e100_serial * info, unsigned int *value);
 
@@ -684,20 +685,39 @@ static struct e100_serial rs_table[] = {
 	  .rx_ctrl     = DEF_RX,
 	  .tx_ctrl     = DEF_TX,
 	  .iseteop     = 2,
+	  .dma_owner   = dma_ser0,
+	  .io_if       = if_serial_0,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
           .enabled  = 1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
 	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER0_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER0_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = SA_INTERRUPT,
+	  .dma_out_irq_description = "serial 0 dma tr",
 #else
 	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
 #endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
 	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER0_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER0_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = SA_INTERRUPT,
+	  .dma_in_irq_description = "serial 0 dma rec",
 #else
-	  .dma_in_enabled = 0
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
 #endif
 #else
           .enabled  = 0,
+	  .io_if_description = NULL,
 	  .dma_out_enabled = 0,
 	  .dma_in_enabled = 0
 #endif
@@ -719,20 +739,42 @@ static struct e100_serial rs_table[] = {
 	  .rx_ctrl     = DEF_RX,
 	  .tx_ctrl     = DEF_TX,
 	  .iseteop     = 3,
+	  .dma_owner   = dma_ser1,
+	  .io_if       = if_serial_1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
           .enabled  = 1,
+	  .io_if_description = "ser1",
 #ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
 	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER1_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER1_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = SA_INTERRUPT,
+	  .dma_out_irq_description = "serial 1 dma tr",
 #else
 	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
 #endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
 	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER1_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER1_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = SA_INTERRUPT,
+	  .dma_in_irq_description = "serial 1 dma rec",
 #else
-	  .dma_in_enabled = 0
+	  .dma_in_enabled = 0,
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
 #endif
 #else
           .enabled  = 0,
+	  .io_if_description = NULL,
+	  .dma_in_irq_nbr = 0,
 	  .dma_out_enabled = 0,
 	  .dma_in_enabled = 0
 #endif
@@ -753,20 +795,40 @@ static struct e100_serial rs_table[] = {
 	  .rx_ctrl     = DEF_RX,
 	  .tx_ctrl     = DEF_TX,
 	  .iseteop     = 0,
+	  .dma_owner   = dma_ser2,
+	  .io_if       = if_serial_2,
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
           .enabled  = 1,
+	  .io_if_description = "ser2",
 #ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
 	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER2_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER2_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = SA_INTERRUPT,
+	  .dma_out_irq_description = "serial 2 dma tr",
 #else
 	  .dma_out_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
 #endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
 	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER2_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER2_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = SA_INTERRUPT,
+	  .dma_in_irq_description = "serial 2 dma rec",
 #else
-	  .dma_in_enabled = 0
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
 #endif
 #else
           .enabled  = 0,
+	  .io_if_description = NULL,
 	  .dma_out_enabled = 0,
 	  .dma_in_enabled = 0
 #endif
@@ -787,20 +849,40 @@ static struct e100_serial rs_table[] = {
 	  .rx_ctrl     = DEF_RX,
 	  .tx_ctrl     = DEF_TX,
 	  .iseteop     = 1,
+	  .dma_owner   = dma_ser3,
+	  .io_if       = if_serial_3,
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
           .enabled  = 1,
+	  .io_if_description = "ser3",
 #ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
 	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER3_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER3_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = SA_INTERRUPT,
+	  .dma_out_irq_description = "serial 3 dma tr",
 #else
 	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
 #endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
 	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER3_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER3_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = SA_INTERRUPT,
+	  .dma_in_irq_description = "serial 3 dma rec",
 #else
-	  .dma_in_enabled = 0
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL
 #endif
 #else
           .enabled  = 0,
+	  .io_if_description = NULL,
 	  .dma_out_enabled = 0,
 	  .dma_in_enabled = 0
 #endif
@@ -1425,12 +1507,11 @@ e100_dtr(struct e100_serial *info, int s
 	{
 		unsigned long flags;
 
-		save_flags(flags);
-		cli();
+		local_irq_save(flags);
 		*e100_modem_pins[info->line].dtr_shadow &= ~mask;
 		*e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask);
 		*e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow;
-		restore_flags(flags);
+		local_irq_restore(flags);
 	}
 
 #ifdef SERIAL_DEBUG_IO
@@ -1449,12 +1530,11 @@ e100_rts(struct e100_serial *info, int s
 {
 #ifndef CONFIG_SVINTO_SIM
 	unsigned long flags;
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	info->rx_ctrl &= ~E100_RTS_MASK;
 	info->rx_ctrl |= (set ? 0 : E100_RTS_MASK);  /* RTS is active low */
 	info->port[REG_REC_CTRL] = info->rx_ctrl;
-	restore_flags(flags);
+	local_irq_restore(flags);
 #ifdef SERIAL_DEBUG_IO
 	printk("ser%i rts %i\n", info->line, set);
 #endif
@@ -1472,12 +1552,11 @@ e100_ri_out(struct e100_serial *info, in
 		unsigned char mask = e100_modem_pins[info->line].ri_mask;
 		unsigned long flags;
 
-		save_flags(flags);
-		cli();
+		local_irq_save(flags);
 		*e100_modem_pins[info->line].ri_shadow &= ~mask;
 		*e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask);
 		*e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow;
-		restore_flags(flags);
+		local_irq_restore(flags);
 	}
 #endif
 }
@@ -1490,12 +1569,11 @@ e100_cd_out(struct e100_serial *info, in
 		unsigned char mask = e100_modem_pins[info->line].cd_mask;
 		unsigned long flags;
 
-		save_flags(flags);
-		cli();
+		local_irq_save(flags);
 		*e100_modem_pins[info->line].cd_shadow &= ~mask;
 		*e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask);
 		*e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow;
-		restore_flags(flags);
+		local_irq_restore(flags);
 	}
 #endif
 }
@@ -1572,8 +1650,7 @@ e100_disable_txdma_channel(struct e100_s
 	/* Disable output DMA channel for the serial port in question
 	 * ( set to something other then serialX)
 	 */
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line));
 	if (info->line == 0) {
 		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) ==
@@ -1601,7 +1678,7 @@ e100_disable_txdma_channel(struct e100_s
 		}
 	}
 	*R_GEN_CONFIG = genconfig_shadow;
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 
@@ -1610,8 +1687,7 @@ e100_enable_txdma_channel(struct e100_se
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line));
 	/* Enable output DMA channel for the serial port in question */
 	if (info->line == 0) {
@@ -1628,7 +1704,7 @@ e100_enable_txdma_channel(struct e100_se
 		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
 	}
 	*R_GEN_CONFIG = genconfig_shadow;
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 static _INLINE_ void
@@ -1639,8 +1715,7 @@ e100_disable_rxdma_channel(struct e100_s
 	/* Disable input DMA channel for the serial port in question
 	 * ( set to something other then serialX)
 	 */
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	if (info->line == 0) {
 		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
 		    IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
@@ -1667,7 +1742,7 @@ e100_disable_rxdma_channel(struct e100_s
 		}
 	}
 	*R_GEN_CONFIG = genconfig_shadow;
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 
@@ -1676,8 +1751,7 @@ e100_enable_rxdma_channel(struct e100_se
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	/* Enable input DMA channel for the serial port in question */
 	if (info->line == 0) {
 		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
@@ -1693,7 +1767,7 @@ e100_enable_rxdma_channel(struct e100_se
 		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
 	}
 	*R_GEN_CONFIG = genconfig_shadow;
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 #ifdef SERIAL_HANDLE_EARLY_ERRORS
@@ -1800,7 +1874,7 @@ e100_enable_rs485(struct tty_struct *tty
 }
 
 static int
-e100_write_rs485(struct tty_struct *tty, int from_user,
+e100_write_rs485(struct tty_struct *tty,
                  const unsigned char *buf, int count)
 {
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
@@ -1813,7 +1887,7 @@ e100_write_rs485(struct tty_struct *tty,
 	 */
 	info->rs485.enabled = 1;
 	/* rs_write now deals with RS485 if enabled */
-	count = rs_write(tty, from_user, buf, count);
+	count = rs_write(tty, buf, count);
 	info->rs485.enabled = old_enabled;
 	return count;
 }
@@ -1851,7 +1925,7 @@ rs_stop(struct tty_struct *tty)
 		unsigned long flags;
 		unsigned long xoff;
 
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
 				CIRC_CNT(info->xmit.head,
 					 info->xmit.tail,SERIAL_XMIT_SIZE)));
@@ -1863,7 +1937,7 @@ rs_stop(struct tty_struct *tty)
 		}
 
 		*((unsigned long *)&info->port[REG_XOFF]) = xoff;
-		restore_flags(flags);
+		local_irq_restore(flags);
 	}
 }
 
@@ -1875,7 +1949,7 @@ rs_start(struct tty_struct *tty)
 		unsigned long flags;
 		unsigned long xoff;
 
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
 				CIRC_CNT(info->xmit.head,
 					 info->xmit.tail,SERIAL_XMIT_SIZE)));
@@ -1890,7 +1964,7 @@ rs_start(struct tty_struct *tty)
 		    info->xmit.head != info->xmit.tail && info->xmit.buf)
 			e100_enable_serial_tx_ready_irq(info);
 
-		restore_flags(flags);
+		local_irq_restore(flags);
 	}
 }
 
@@ -2072,8 +2146,7 @@ static int serial_fast_timer_expired = 0
 static void flush_timeout_function(unsigned long data);
 #define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
   unsigned long timer_flags; \
-  save_flags(timer_flags); \
-  cli(); \
+  local_irq_save(timer_flags); \
   if (fast_timers[info->line].function == NULL) { \
     serial_fast_timer_started++; \
     TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
@@ -2087,7 +2160,7 @@ static void flush_timeout_function(unsig
   else { \
     TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
   } \
-  restore_flags(timer_flags); \
+  local_irq_restore(timer_flags); \
 }
 #define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
 
@@ -2116,8 +2189,7 @@ append_recv_buffer(struct e100_serial *i
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 
 	if (!info->first_recv_buffer)
 		info->first_recv_buffer = buffer;
@@ -2130,7 +2202,7 @@ append_recv_buffer(struct e100_serial *i
 	if (info->recv_cnt > info->max_recv_cnt)
 		info->max_recv_cnt = info->recv_cnt;
 
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 static int
@@ -2517,11 +2589,10 @@ flush_to_flip_buffer(struct e100_serial 
 	if (!info->first_recv_buffer)
 		return;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 
 	if (!(tty = info->tty)) {
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return;
 	}
 
@@ -2596,7 +2667,7 @@ flush_to_flip_buffer(struct e100_serial 
 			  tty->flip.count);
 	      }
 	      );
-	restore_flags(flags);
+	local_irq_restore(flags);
 
 	DFLIP(
 	  if (1) {
@@ -3008,7 +3079,7 @@ extern _INLINE_ void handle_ser_tx_inter
 	if (info->x_char) {
 		unsigned char rstat;
 		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char));
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		rstat = info->port[REG_STATUS];
 		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
 
@@ -3017,7 +3088,7 @@ extern _INLINE_ void handle_ser_tx_inter
 		info->x_char = 0;
 		/* We must enable since it is disabled in ser_interrupt */
 		e100_enable_serial_tx_ready_irq(info);
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return;
 	}
 	if (info->uses_dma_out) {
@@ -3025,7 +3096,7 @@ extern _INLINE_ void handle_ser_tx_inter
 		int i;
 		/* We only use normal tx interrupt when sending x_char */
 		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0));
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		rstat = info->port[REG_STATUS];
 		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
 		e100_disable_serial_tx_ready_irq(info);
@@ -3038,7 +3109,7 @@ extern _INLINE_ void handle_ser_tx_inter
 			nop();
 
 		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue);
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return;
 	}
 	/* Normal char-by-char interrupt */
@@ -3052,7 +3123,7 @@ extern _INLINE_ void handle_ser_tx_inter
 	}
 	DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail]));
 	/* Send a byte, rs485 timing is critical so turn of ints */
-	save_flags(flags); cli();
+	local_irq_save(flags);
 	info->port[REG_TR_DATA] = info->xmit.buf[info->xmit.tail];
 	info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
 	info->icount.tx++;
@@ -3076,7 +3147,7 @@ extern _INLINE_ void handle_ser_tx_inter
 		/* We must enable since it is disabled in ser_interrupt */
 		e100_enable_serial_tx_ready_irq(info);
 	}
-	restore_flags(flags);
+	local_irq_restore(flags);
 
 	if (CIRC_CNT(info->xmit.head,
 		     info->xmit.tail,
@@ -3101,7 +3172,7 @@ ser_interrupt(int irq, void *dev_id, str
 	int handled = 0;
 	static volatile unsigned long reentered_ready_mask = 0;
 
-	save_flags(flags); cli();
+	local_irq_save(flags);
 	irq_mask1_rd = *R_IRQ_MASK1_RD;
 	/* First handle all rx interrupts with ints disabled */
 	info = rs_table;
@@ -3146,7 +3217,7 @@ ser_interrupt(int irq, void *dev_id, str
 			/* Unblock the serial interrupt */
 			*R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
 
-			sti();
+			local_irq_enable();
 			ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
 			info = rs_table;
 			for (i = 0; i < NR_PORTS; i++) {
@@ -3159,11 +3230,11 @@ ser_interrupt(int irq, void *dev_id, str
 				ready_mask <<= 2;
 			}
 			/* handle_ser_tx_interrupt enables tr_ready interrupts */
-			cli();
+			local_irq_disable();
 			/* Handle reentered TX interrupt */
 			irq_mask1_rd = reentered_ready_mask;
 		}
-		cli();
+		local_irq_disable();
 		tx_started = 0;
 	} else {
 		unsigned long ready_mask;
@@ -3179,7 +3250,7 @@ ser_interrupt(int irq, void *dev_id, str
 		}
 	}
 
-	restore_flags(flags);
+	local_irq_restore(flags);
 	return IRQ_RETVAL(handled);
 } /* ser_interrupt */
 #endif
@@ -3228,13 +3299,12 @@ startup(struct e100_serial * info)
 	if (!xmit_page)
 		return -ENOMEM;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 
 	/* if it was already initialized, skip this */
 
 	if (info->flags & ASYNC_INITIALIZED) {
-		restore_flags(flags);
+		local_irq_restore(flags);
 		free_page(xmit_page);
 		return 0;
 	}
@@ -3360,7 +3430,7 @@ startup(struct e100_serial * info)
 
 	info->flags |= ASYNC_INITIALIZED;
 
-	restore_flags(flags);
+	local_irq_restore(flags);
 	return 0;
 }
 
@@ -3411,8 +3481,7 @@ shutdown(struct e100_serial * info)
 	       info->irq);
 #endif
 
-	save_flags(flags);
-	cli(); /* Disable interrupts */
+	local_irq_save(flags);
 
 	if (info->xmit.buf) {
 		free_page((unsigned long)info->xmit.buf);
@@ -3436,7 +3505,7 @@ shutdown(struct e100_serial * info)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
 
 	info->flags &= ~ASYNC_INITIALIZED;
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 
@@ -3528,8 +3597,7 @@ change_speed(struct e100_serial *info)
 
 #ifndef CONFIG_SVINTO_SIM
 	/* start with default settings and then fill in changes */
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	/* 8 bit, no/even parity */
 	info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) |
 			   IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) |
@@ -3593,7 +3661,7 @@ change_speed(struct e100_serial *info)
 	}
 
 	*((unsigned long *)&info->port[REG_XOFF]) = xoff;
-	restore_flags(flags);
+	local_irq_restore(flags);
 #endif /* !CONFIG_SVINTO_SIM */
 
 	update_char_time(info);
@@ -3621,14 +3689,13 @@ rs_flush_chars(struct tty_struct *tty)
 
 	/* this protection might not exactly be necessary here */
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	start_transmit(info);
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 extern _INLINE_ int
-rs_raw_write(struct tty_struct * tty, int from_user,
+rs_raw_write(struct tty_struct * tty,
 	  const unsigned char *buf, int count)
 {
 	int	c, ret = 0;
@@ -3651,72 +3718,37 @@ rs_raw_write(struct tty_struct * tty, in
 	SIMCOUT(buf, count);
 	return count;
 #endif
-	save_flags(flags);
+	local_save_flags(flags);
 	DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
 	DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty)));
 
 
-	/* the cli/restore_flags pairs below are needed because the
+	/* the local_irq_disable/restore_flags pairs below are needed because the
 	 * DMA interrupt handler moves the info->xmit values. the memcpy
 	 * needs to be in the critical region unfortunately, because we
 	 * need to read xmit values, memcpy, write xmit values in one
 	 * atomic operation... this could perhaps be avoided by more clever
 	 * design.
 	 */
-	if (from_user) {
-		down(&tmp_buf_sem);
-		while (1) {
-			int c1;
-			c = CIRC_SPACE_TO_END(info->xmit.head,
-					      info->xmit.tail,
-					      SERIAL_XMIT_SIZE);
-			if (count < c)
-				c = count;
-			if (c <= 0)
-				break;
-
-			c -= copy_from_user(tmp_buf, buf, c);
-			if (!c) {
-				if (!ret)
-					ret = -EFAULT;
-				break;
-			}
-			cli();
-			c1 = CIRC_SPACE_TO_END(info->xmit.head,
-					       info->xmit.tail,
-					       SERIAL_XMIT_SIZE);
-			if (c1 < c)
-				c = c1;
-			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
-			info->xmit.head = ((info->xmit.head + c) &
-					   (SERIAL_XMIT_SIZE-1));
-			restore_flags(flags);
-			buf += c;
-			count -= c;
-			ret += c;
-		}
-		up(&tmp_buf_sem);
-	} else {
-		cli();
-		while (count) {
-			c = CIRC_SPACE_TO_END(info->xmit.head,
-					      info->xmit.tail,
-					      SERIAL_XMIT_SIZE);
-
-			if (count < c)
-				c = count;
-			if (c <= 0)
-				break;
-
-			memcpy(info->xmit.buf + info->xmit.head, buf, c);
-			info->xmit.head = (info->xmit.head + c) &
-				(SERIAL_XMIT_SIZE-1);
-			buf += c;
-			count -= c;
-			ret += c;
-		}
-		restore_flags(flags);
+	local_irq_disable();
+	while (count) {
+		c = CIRC_SPACE_TO_END(info->xmit.head,
+				      info->xmit.tail,
+				      SERIAL_XMIT_SIZE);
+
+		if (count < c)
+			c = count;
+		if (c <= 0)
+			break;
+
+		memcpy(info->xmit.buf + info->xmit.head, buf, c);
+		info->xmit.head = (info->xmit.head + c) &
+			(SERIAL_XMIT_SIZE-1);
+		buf += c;
+		count -= c;
+		ret += c;
 	}
+	local_irq_restore(flags);
 
 	/* enable transmitter if not running, unless the tty is stopped
 	 * this does not need IRQ protection since if tr_running == 0
@@ -3735,7 +3767,7 @@ rs_raw_write(struct tty_struct * tty, in
 } /* raw_raw_write() */
 
 static int
-rs_write(struct tty_struct * tty, int from_user,
+rs_write(struct tty_struct * tty,
 	 const unsigned char *buf, int count)
 {
 #if defined(CONFIG_ETRAX_RS485)
@@ -3762,7 +3794,7 @@ rs_write(struct tty_struct * tty, int fr
 	}
 #endif /* CONFIG_ETRAX_RS485 */
 
-	count = rs_raw_write(tty, from_user, buf, count);
+	count = rs_raw_write(tty, buf, count);
 
 #if defined(CONFIG_ETRAX_RS485)
 	if (info->rs485.enabled)
@@ -3830,10 +3862,9 @@ rs_flush_buffer(struct tty_struct *tty)
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	info->xmit.head = info->xmit.tail = 0;
-	restore_flags(flags);
+	local_irq_restore(flags);
 
 	wake_up_interruptible(&tty->write_wait);
 
@@ -3855,7 +3886,7 @@ static void rs_send_xchar(struct tty_str
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
 	unsigned long flags;
-	save_flags(flags); cli();
+	local_irq_save(flags);
 	if (info->uses_dma_out) {
 		/* Put the DMA on hold and disable the channel */
 		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold);
@@ -3872,7 +3903,7 @@ static void rs_send_xchar(struct tty_str
 	DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
 	info->x_char = ch;
 	e100_enable_serial_tx_ready_irq(info);
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 /*
@@ -4186,8 +4217,7 @@ rs_break(struct tty_struct *tty, int bre
 	if (!info->port)
 		return;
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	if (break_state == -1) {
 		/* Go to manual mode and set the txd pin to 0 */
 		info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
@@ -4195,7 +4225,7 @@ rs_break(struct tty_struct *tty, int bre
 		info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
 	}
 	info->port[REG_TR_CTRL] = info->tx_ctrl;
-	restore_flags(flags);
+	local_irq_restore(flags);
 }
 
 static int
@@ -4249,7 +4279,7 @@ rs_ioctl(struct tty_struct *tty, struct 
 			if (copy_from_user(&rs485wr, (struct rs485_write*)arg, sizeof(rs485wr)))
 				return -EFAULT;
 
-			return e100_write_rs485(tty, 1, rs485wr.outc, rs485wr.outc_size);
+			return e100_write_rs485(tty, rs485wr.outc, rs485wr.outc_size);
 		}
 #endif
 
@@ -4279,46 +4309,6 @@ rs_set_termios(struct tty_struct *tty, s
 
 }
 
-/* In debugport.c - register a console write function that uses the normal
- * serial driver
- */
-typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
-
-extern debugport_write_function debug_write_function;
-
-static int rs_debug_write_function(int i, const char *buf, unsigned int len)
-{
-	int cnt;
-	int written = 0;
-        struct tty_struct *tty;
-        static int recurse_cnt = 0;
-
-        tty = rs_table[i].tty;
-        if (tty)  {
-		unsigned long flags;
-		if (recurse_cnt > 5) /* We skip this debug output */
-			return 1;
-
-		local_irq_save(flags);
-		recurse_cnt++;
-		local_irq_restore(flags);
-                do {
-                        cnt = rs_write(tty, 0, buf + written, len);
-                        if (cnt >= 0) {
-				written += cnt;
-                                buf += cnt;
-                                len -= cnt;
-                        } else
-                                len = cnt;
-                } while(len > 0);
-		local_irq_save(flags);
-		recurse_cnt--;
-		local_irq_restore(flags);
-                return 1;
-        }
-        return 0;
-}
-
 /*
  * ------------------------------------------------------------
  * rs_close()
@@ -4340,11 +4330,10 @@ rs_close(struct tty_struct *tty, struct 
 
 	/* interrupts are disabled for this entire function */
 
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 
 	if (tty_hung_up_p(filp)) {
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return;
 	}
 
@@ -4371,7 +4360,7 @@ rs_close(struct tty_struct *tty, struct 
 		info->count = 0;
 	}
 	if (info->count) {
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return;
 	}
 	info->flags |= ASYNC_CLOSING;
@@ -4427,7 +4416,7 @@ rs_close(struct tty_struct *tty, struct 
 	}
 	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
 	wake_up_interruptible(&info->close_wait);
-	restore_flags(flags);
+	local_irq_restore(flags);
 
 	/* port closed */
 
@@ -4525,7 +4514,7 @@ block_til_ready(struct tty_struct *tty, 
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 		if (info->flags & ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
+			wait_event_interruptible(info->close_wait, 0);
 #ifdef SERIAL_DO_RESTART
 		if (info->flags & ASYNC_HUP_NOTIFY)
 			return -EAGAIN;
@@ -4563,21 +4552,19 @@ block_til_ready(struct tty_struct *tty, 
 	printk("block_til_ready before block: ttyS%d, count = %d\n",
 	       info->line, info->count);
 #endif
-	save_flags(flags);
-	cli();
+	local_irq_save(flags);
 	if (!tty_hung_up_p(filp)) {
 		extra_count++;
 		info->count--;
 	}
-	restore_flags(flags);
+	local_irq_restore(flags);
 	info->blocked_open++;
 	while (1) {
-		save_flags(flags);
-		cli();
+		local_irq_save(flags);
 		/* assert RTS and DTR */
 		e100_rts(info, 1);
 		e100_dtr(info, 1);
-		restore_flags(flags);
+		local_irq_restore(flags);
 		set_current_state(TASK_INTERRUPTIBLE);
 		if (tty_hung_up_p(filp) ||
 		    !(info->flags & ASYNC_INITIALIZED)) {
@@ -4629,9 +4616,9 @@ rs_open(struct tty_struct *tty, struct f
 	struct e100_serial	*info;
 	int 			retval, line;
 	unsigned long           page;
+	int                     allocated_resources = 0;
 
 	/* find which port we want to open */
-
 	line = tty->index;
 
 	if (line < 0 || line >= NR_PORTS)
@@ -4672,7 +4659,7 @@ rs_open(struct tty_struct *tty, struct f
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 		if (info->flags & ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
+			wait_event_interruptible(info->close_wait, 0);
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
@@ -4682,12 +4669,79 @@ rs_open(struct tty_struct *tty, struct f
 	}
 
 	/*
+	 * If DMA is enabled try to allocate the irq's.
+	 */
+	if (info->count == 1) {
+		allocated_resources = 1;
+		if (info->dma_in_enabled) {
+			if (request_irq(info->dma_in_irq_nbr,
+					rec_interrupt,
+					info->dma_in_irq_flags,
+					info->dma_in_irq_description,
+					info)) {
+				printk(KERN_WARNING "DMA irq '%s' busy; falling back to non-DMA mode\n", info->dma_in_irq_description);
+				/* Make sure we never try to use DMA in for the port again. */
+				info->dma_in_enabled = 0;
+			} else if (cris_request_dma(info->dma_in_nbr,
+						    info->dma_in_irq_description,
+						    DMA_VERBOSE_ON_ERROR,
+						    info->dma_owner)) {
+				free_irq(info->dma_in_irq_nbr, info);
+				printk(KERN_WARNING "DMA '%s' busy; falling back to non-DMA mode\n", info->dma_in_irq_description);
+				/* Make sure we never try to use DMA in for the port again. */
+				info->dma_in_enabled = 0;
+			}
+#ifdef SERIAL_DEBUG_OPEN
+			else printk("DMA irq '%s' allocated\n", info->dma_in_irq_description);
+#endif
+		}
+		if (info->dma_out_enabled) {
+			if (request_irq(info->dma_out_irq_nbr,
+					       tr_interrupt,
+					       info->dma_out_irq_flags,
+					       info->dma_out_irq_description,
+					       info)) {
+				printk(KERN_WARNING "DMA irq '%s' busy; falling back to non-DMA mode\n", info->dma_out_irq_description);
+				/* Make sure we never try to use DMA out for the port again. */
+				info->dma_out_enabled = 0;
+			} else if (cris_request_dma(info->dma_out_nbr,
+					     info->dma_out_irq_description,
+					     DMA_VERBOSE_ON_ERROR,
+					     info->dma_owner)) {
+				free_irq(info->dma_out_irq_nbr, info);
+				printk(KERN_WARNING "DMA '%s' busy; falling back to non-DMA mode\n", info->dma_out_irq_description);
+				/* Make sure we never try to use DMA in for the port again. */
+				info->dma_out_enabled = 0;
+			}
+#ifdef SERIAL_DEBUG_OPEN
+			else printk("DMA irq '%s' allocated\n", info->dma_out_irq_description);
+#endif
+		}
+	}
+
+	/*
 	 * Start up the serial port
 	 */
 
 	retval = startup(info);
-	if (retval)
-		return retval;
+	if (retval) {
+		if (allocated_resources) {
+			if (info->dma_out_enabled) {
+				cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description);
+				free_irq(info->dma_out_irq_nbr,
+					 info);
+			}
+			if (info->dma_in_enabled) {
+				cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description);
+				free_irq(info->dma_in_irq_nbr,
+					 info);
+			}
+		}
+		/* FIXME Decrease count info->count here too? */
+ 		return retval;
+
+	}
+
 
 	retval = block_til_ready(tty, filp, info);
 	if (retval) {
@@ -4695,6 +4749,19 @@ rs_open(struct tty_struct *tty, struct f
 		printk("rs_open returning after block_til_ready with %d\n",
 		       retval);
 #endif
+		if (allocated_resources) {
+			if (info->dma_out_enabled) {
+				cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description);
+				free_irq(info->dma_out_irq_nbr,
+					 info);
+			}
+			if (info->dma_in_enabled) {
+				cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description);
+				free_irq(info->dma_in_irq_nbr,
+					 info);
+			}
+		}
+
 		return retval;
 	}
 
@@ -4903,7 +4970,22 @@ rs_init(void)
 #if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER)
 	init_timer(&flush_timer);
 	flush_timer.function = timed_flush_handler;
-	mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS);
+	mod_timer(&flush_timer, jiffies + 5);
+#endif
+
+#if defined(CONFIG_ETRAX_RS485)
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+	if (cris_io_interface_allocate_pins(if_ser0, 'a', rs485_pa_bit, rs485_pa_bit)) {
+ 		printk(KERN_CRIT "ETRAX100LX serial: Could not allocate RS485 pin\n");
+		return -EBUSY;
+	}
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+	if (cris_io_interface_allocate_pins(if_ser0, 'g', rs485_pa_bit, rs485_port_g_bit)) {
+ 		printk(KERN_CRIT "ETRAX100LX serial: Could not allocate RS485 pin\n");
+		return -EBUSY;
+	}
+#endif
 #endif
 
 	/* Initialize the tty_driver structure */
@@ -4928,6 +5010,14 @@ rs_init(void)
 	/* do some initializing for the separate ports */
 
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		if (info->enabled) {
+			if (cris_request_io_interface(info->io_if, info->io_if_description)) {
+				printk(KERN_CRIT "ETRAX100LX async serial: Could not allocate IO pins for %s, port %d\n",
+				       info->io_if_description,
+				       i);
+				info->enabled = 0;
+			}
+		}
 		info->uses_dma_in = 0;
 		info->uses_dma_out = 0;
 		info->line = i;
@@ -4979,64 +5069,16 @@ rs_init(void)
 #endif
 
 #ifndef CONFIG_SVINTO_SIM
+#ifndef CONFIG_ETRAX_KGDB
 	/* Not needed in simulator.  May only complicate stuff. */
 	/* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
 
-	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
+	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", rs_table))
 		panic("irq8");
 
-#ifdef CONFIG_ETRAX_SERIAL_PORT0
-#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
-	if (request_irq(SER0_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL))
-		panic("irq22");
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
-	if (request_irq(SER0_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL))
-		panic("irq23");
-#endif
-#endif
-
-#ifdef CONFIG_ETRAX_SERIAL_PORT1
-#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
-	if (request_irq(SER1_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL))
-		panic("irq24");
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
-	if (request_irq(SER1_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL))
-		panic("irq25");
-#endif
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
-	/* DMA Shared with par0 (and SCSI0 and ATA) */
-#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
-	if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma tr", NULL))
-		panic("irq18");
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
-	if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma rec", NULL))
-		panic("irq19");
-#endif
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT3
-	/* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */
-#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
-	if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma tr", NULL))
-		panic("irq20");
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
-	if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma rec", NULL))
-		panic("irq21");
-#endif
-#endif
-
-#ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
-	if (request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ | SA_INTERRUPT,
-		       "fast serial dma timeout", NULL)) {
-		printk(KERN_CRIT "err: timer1 irq\n");
-	}
 #endif
 #endif /* CONFIG_SVINTO_SIM */
-	debug_write_function = rs_debug_write_function;
+
 	return 0;
 }
 
diff -puN drivers/serial/crisv10.h~cris-update-12-17-serial-port-driver drivers/serial/crisv10.h
--- 25/drivers/serial/crisv10.h~cris-update-12-17-serial-port-driver	2005-06-27 18:32:02.000000000 -0700
+++ 25-akpm/drivers/serial/crisv10.h	2005-06-27 18:32:02.000000000 -0700
@@ -10,6 +10,8 @@
 #include <linux/config.h>
 #include <linux/circ_buf.h>
 #include <asm/termios.h>
+#include <asm/dma.h>
+#include <asm/arch/io_interface_mux.h>
 
 /* Software state per channel */
 
@@ -62,6 +64,19 @@ struct e100_serial {
 	u8		dma_in_enabled:1;  /* Set to 1 if DMA should be used */
 
 	/* end of fields defined in rs_table[] in .c-file */
+	int		dma_owner;
+	unsigned int	dma_in_nbr;
+	unsigned int	dma_out_nbr;
+	unsigned int	dma_in_irq_nbr;
+	unsigned int	dma_out_irq_nbr;
+	unsigned long	dma_in_irq_flags;
+	unsigned long	dma_out_irq_flags;
+	char		*dma_in_irq_description;
+	char		*dma_out_irq_description;
+
+	enum cris_io_interface io_if;
+	char            *io_if_description;
+
 	u8		uses_dma_in;  /* Set to 1 if DMA is used */
 	u8		uses_dma_out; /* Set to 1 if DMA is used */
 	u8		forced_eop;   /* a fifo eop has been forced */
diff -puN /dev/null drivers/serial/crisv32.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/serial/crisv32.c	2005-06-27 18:32:02.000000000 -0700
@@ -0,0 +1,3568 @@
+/* $Id: crisv32.c,v 1.50 2005/06/19 17:09:11 starvik Exp $
+ *
+ * Serial port driver for the ETRAX FS chip
+ *
+ *    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Axis Communications AB
+ *
+ *    Many, many authors. Based once upon a time on serial.c for 16x50.
+ *
+ *    Johan Adolfsson - port to ETRAX FS
+ * $Log: crisv32.c,v $
+ * Revision 1.50  2005/06/19 17:09:11  starvik
+ * Merge of Linux 2.6.12.
+ *
+ * Revision 1.49  2005/06/10 10:22:04  starvik
+ * The debug_write_function hack is not needed anymore.
+ *
+ * Revision 1.48  2005/05/03 09:36:03  starvik
+ * Mark transmitter as stopped in rs_close. This makes it possbile send
+ * data after port has been closed and reopened.
+ *
+ * Revision 1.47  2005/04/26 09:37:55  starvik
+ * Make it compile again.
+ *
+ * Revision 1.46  2005/04/24 18:43:16  starvik
+ * Updated with latest register headers.
+ *
+ * Revision 1.45  2005/04/15 09:30:43  starvik
+ * Corrected DMA receiver path.
+ * Removed warnings.
+ *
+ * Revision 1.44  2005/04/15 08:35:53  starvik
+ * Use a small list of transmit descriptors because there is no way to tell
+ * the DMA to issue the descriptor interrupt after the characters have been
+ * drained from the FIFO.
+ *
+ * Revision 1.43  2005/04/14 12:11:22  starvik
+ * Removed garbage on serial console:
+ *   Only set registers in change_speed if value has been modified.
+ *   Don't disable serial port in change_speed.
+ *
+ * Revision 1.42  2005/04/08 11:03:11  starvik
+ * Removed missleading comment.
+ *
+ * Revision 1.41  2005/04/08 10:51:26  starvik
+ * Some corrections in DMA transmit code to make it work on real silicon.
+ *
+ * Revision 1.40  2005/04/04 13:55:54  orjanf
+ * * Added check for missing tty in info struct in receive_chars_no_dma
+ *   (corresponding check already exists in other functions).
+ *
+ * Revision 1.39  2005/01/26 07:14:19  starvik
+ * Applied diff from kernel janitors (Nish Aravamudan).
+ *
+ * Revision 1.38  2005/01/05 06:13:03  starvik
+ * local_save_flags -> local_irq_save
+ *
+ * Revision 1.37  2005/01/04 12:10:13  starvik
+ * Serial driver moved here. Modified include.
+ *
+ * Revision 1.36  2005/01/04 11:15:18  starvik
+ * Modified arguments to rs_write and friends.
+ *
+ * Revision 1.35  2004/11/16 10:17:54  starvik
+ * Use the new GPIO helper functions to reduce configuration complexity.
+ *
+ * Revision 1.34  2004/09/29 10:33:51  starvik
+ * Resolved a dealock when printing debug from kernel.
+ *
+ * Revision 1.33  2004/09/27 07:41:45  starvik
+ * Let the DMA allocator set up the strmux
+ *
+ * Revision 1.32  2004/09/27 06:18:43  starvik
+ * Use pin allocator
+ *
+ * Revision 1.31  2004/08/27 23:46:31  johana
+ * rs_set_termios() must call change_speed() if c_iflag has changed or
+ * automatic XOFF handling will not be configured correctly.
+ *
+ * Revision 1.30  2004/06/08 08:05:36  starvik
+ * ETRAX 200 -> ETRAX FS
+ *
+ * Revision 1.28  2004/05/14 10:54:21  orjanf
+ * * Fixed a couple of typos.
+ *
+ * Revision 1.27  2004/05/14 07:58:02  starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.26  2004/01/13 12:14:12  starvik
+ * Don't set EOP in out channel
+ *
+ * Revision 1.25  2004/01/09 12:48:09  starvik
+ * The previous change was completely bogus. The IRQ must be acked after
+ * the write and there is no race condition (both because the tr_ready
+ * IRQ is issued continously while the transmitter is ready).
+ *
+ * Revision 1.24  2004/01/07 14:49:50  starvik
+ * Ack IRQ before sending data to avoid potential race
+ *
+ * Revision 1.23  2003/10/07 08:19:12  starvik
+ * Enable tx data IRQ when waiting for data to be sent.
+ * Report IRQ as handled for tr_ready IRQ.
+ * Implemented modem_pins for ser2 and ser3.
+ *
+ * Revision 1.22  2003/09/30 13:23:33  johana
+ * Added options argument to crisv32_request_dma() to be verbose and
+ * possibly panic.
+ * Store user in dma.c and assign variables when it is safe.
+ *
+ * Revision 1.21  2003/09/30 12:11:21  johana
+ * Removed PORT0_DMAs that doesn't exist anymore.
+ * Corrected and added missing crisv32_request_dma().
+ *
+ * Revision 1.20  2003/09/30 11:49:06  starvik
+ * Allocated DMA channels
+ *
+ * Revision 1.19  2003/09/30 09:29:33  johana
+ * Serial DMA channels moved and not that flexible anymore.
+ * Added DMA channels for ser2 and ser3.
+ * Fixed typo in etraxfs_write_rs485.
+ * Added rs_debug_write_function and register it with debugport.c
+ *
+ * Revision 1.18  2003/09/25 14:10:34  starvik
+ * Updated for latest serial port implementation
+ *
+ * Revision 1.17  2003/09/22 08:49:24  johana
+ * Corrected mark/space parity. PARODD gives mark.
+ *
+ * Revision 1.16  2003/09/22 08:43:26  johana
+ * Compile with debug enabled.
+ *
+ * Revision 1.15  2003/09/11 13:44:03  starvik
+ * Don't reset DMAs after they have been started (reset clears all settings).
+ * Flush received characters to flip buffer.
+ *
+ * Revision 1.14  2003/09/08 05:28:56  starvik
+ * Corrected DMA context handling.
+ * Enable DMA after each reset.
+ *
+ * Revision 1.13  2003/09/01 07:05:56  starvik
+ * Updated for latest DMA
+ *
+ * Revision 1.12  2003/08/18 08:12:05  johana
+ * Clear tr_running when stopping transmission.
+ *
+ * Revision 1.11  2003/07/10 13:25:46  starvik
+ * Compiles for 2.5.74
+ * Lindented ethernet.c
+ *
+ * Revision 1.10  2003/07/04 08:27:46  starvik
+ * Merge of Linux 2.5.74
+ *
+ * Revision 1.9  2003/06/10 12:38:09  johana
+ * Adapted to new register description.
+ * r_status -> r_status_data,
+ * rs_data_in -> rs_status_data.
+ * Unified variable naming to rstat.
+ * Removed errorcode.
+ *
+ * Revision 1.8  2003/06/10 08:56:10  johana
+ * Use stop field to stop/start transmission instead of enable/disable port.
+ *
+ * Revision 1.7  2003/06/10 08:28:38  johana
+ * Added ack_intr.
+ * Added missing status read.
+ * Read masked_intr before using it.
+ *
+ * Revision 1.6  2003/06/06 12:48:11  johana
+ * Check masked_intr instead of status register in ser_interrupt.
+ * Disable/enable tr_ready interrupt in rs_break if dmaout isn't used.
+ *
+ * Revision 1.5  2003/06/06 12:03:21  johana
+ * local_irq_disable() not needed after local_irq_save(flags).
+ * Implemented enable/disable of dma interrupt.
+ * Corrected rs_break() so it handles if transmission is in progress.
+ *
+ * Revision 1.4  2003/06/05 14:52:08  johana
+ * Support for CONFIG_ETRAX_RS485_DISABLE_RECEIVER
+ *
+ * Revision 1.3  2003/06/04 12:45:27  johana
+ * Corrected DMA channel usage for serial ports.
+ * Make them configurable.
+ *
+ * Revision 1.2  2003/06/04 10:36:39  johana
+ * Initial CRIS v32 version.
+ * Timer stuff removed (DMA has timeout).
+ * Support separate input baudrate using CIBAUD flag.
+ * Removed support for RS485_ON_PA.
+ * Using hardware auto_rts support for RS485.
+ * DMA usage is configurable for in and out seperatly.
+ * save_flags(flags) -> local_irq_save(flags),
+ * cli() -> local_irq_disable(),
+ * restore_flags(flags) -> local_irq_restore(flags).
+ *
+ * Revision 1.1  2003/06/04 08:08:17  johana
+ * Copy of the arch-v10 driver version 1.14.
+ */
+
+static char *serial_version = "$Revision: 1.50 $";
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <linux/delay.h>
+
+/* non-arch dependent serial structures are in linux/serial.h */
+#include <linux/serial.h>
+
+/* while we keep our own stuff (struct etraxfs_serial) in a local .h file */
+#include "crisv32.h"
+#include <asm/arch/dma.h>
+#include <asm/arch/system.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/ser_defs.h>
+#include <asm/arch/hwregs/gio_defs.h>
+#include <asm/arch/hwregs/intr_vect_defs.h>
+#include <asm/arch/hwregs/reg_map.h>
+
+
+
+/*
+ * All of the compatibilty code so we can compile serial.c against
+ * older kernels is hidden in serial_compat.h
+ */
+#if defined(LOCAL_HEADERS)
+#include "serial_compat.h"
+#endif
+
+#define DMA_WAIT_UNTIL_RESET(inst) do {  reg_dma_rw_stat r; do { r = REG_RD(dma, (inst), rw_stat); }while(r.mode != regk_dma_rst); }while(0)
+
+#define _INLINE_ inline
+
+struct tty_driver *serial_driver;
+
+/* serial subtype definitions */
+#ifndef SERIAL_TYPE_NORMAL
+#define SERIAL_TYPE_NORMAL	1
+#endif
+
+#define ETRAX_SER_FIFO_SIZE 1 /* Or perhaps 2 */
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+//#define SERIAL_DEBUG_INTR
+//#define SERIAL_DEBUG_OPEN
+//#define SERIAL_DEBUG_FLOW
+//#define SERIAL_DEBUG_DATA
+//#define SERIAL_DEBUG_THROTTLE
+//#define SERIAL_DEBUG_IO  /* Debug for Extra control and status pins */
+#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
+
+#define DEBUG_BAUD(x) do { x; }while(0)
+
+#define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
+
+#define SERIAL_DESCR_BUF_SIZE 256
+
+
+#if 0
+#define DEBUG_LOG(line, string, value) printk(string, value)
+#else
+#define DEBUG_LOG(line, string, value)
+#endif
+
+/* Macro to set up control lines for a port. */
+#define SETUP_PINS(port) \
+	if (strcmp(CONFIG_ETRAX_SER##port##_DTR_BIT, "")) \
+		crisv32_io_get_name(&rs_table[port].dtr_pin, CONFIG_ETRAX_SER##port##_DTR_BIT); \
+	else \
+		rs_table[port].dtr_pin = dummy_pin; \
+	if (strcmp(CONFIG_ETRAX_SER##port##_DSR_BIT, "")) \
+		crisv32_io_get_name(&rs_table[port].dsr_pin, CONFIG_ETRAX_SER##port##_DSR_BIT); \
+	else \
+		rs_table[port].dsr_pin = dummy_pin; \
+	if (strcmp(CONFIG_ETRAX_SER##port##_RI_BIT, "")) \
+		crisv32_io_get_name(&rs_table[port].ri_pin, CONFIG_ETRAX_SER##port##_RI_BIT); \
+	else \
+		rs_table[port].ri_pin = dummy_pin; \
+	if (strcmp(CONFIG_ETRAX_SER##port##_CD_BIT, "")) \
+		crisv32_io_get_name(&rs_table[port].cd_pin, CONFIG_ETRAX_SER##port##_CD_BIT); \
+	else \
+		rs_table[port].cd_pin = dummy_pin;
+
+static void change_speed(struct etraxfs_serial *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+static int rs_write(struct tty_struct * tty,
+                    const unsigned char *buf, int count);
+static inline int raw_write(struct tty_struct * tty,
+                            const unsigned char *buf, int count);
+#ifdef CONFIG_ETRAX_RS485
+static int etraxfs_write_rs485(struct tty_struct * tty,
+                            const unsigned char *buf, int count);
+#endif
+static int get_lsr_info(struct etraxfs_serial * info, unsigned int *value);
+static void flush_to_flip_buffer(struct etraxfs_serial *info);
+
+#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+
+/* Dummy pin used for unused CD, DSR, DTR and RI signals */
+static unsigned long io_dummy;
+static struct crisv32_ioport dummy_port =
+{
+	&io_dummy,
+	&io_dummy,
+	&io_dummy,
+	18
+};
+static struct crisv32_iopin dummy_pin =
+{
+	&dummy_port,
+	0
+};
+
+/* this is the data for the two fixed serial ports in the ETRAX FS */
+/* ser0 can use dma4 or dma6 for tx and dma5 or dma7 for rx */
+/* ser1 can use dma2 for tx and dma3 for rx */
+
+#define regi_NULL 0
+
+static struct etraxfs_serial rs_table[] = {
+{
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+	.regi_ser    = regi_ser0,
+	/* We initilise the dma stuff like this to get a compiler error
+	 * if a CONFIG is missing
+	 */
+	.regi_dmain  =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+	               regi_dma7,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN
+	               regi_NULL,
+#  endif
+
+	.regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+	               regi_dma6,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT
+	               regi_NULL,
+#  endif
+#else
+	.regi_ser    = regi_NULL,
+	.regi_dmain  = regi_NULL,
+	.regi_dmaout = regi_NULL,
+#endif
+
+	.flags = STD_FLAGS,
+},  /* ttyS0 */
+{
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+	.regi_ser    = regi_ser1,
+	.regi_dmain  =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN
+	               regi_dma5,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_IN
+	               regi_NULL,
+#  endif
+
+	 .regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA4_OUT
+	               regi_dma4,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_OUT
+	               regi_NULL,
+#  endif
+#else
+	.regi_ser    = regi_NULL,
+	.regi_dmain  = regi_NULL,
+	.regi_dmaout = regi_NULL,
+#endif
+	.flags = STD_FLAGS,
+},  /* ttyS1 */
+{
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+	.regi_ser    = regi_ser2,
+	.regi_dmain  =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	               regi_dma3,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN
+	               regi_NULL,
+#  endif
+
+	 .regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	               regi_dma2,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT
+	               regi_NULL,
+#  endif
+#else
+	.regi_ser    = regi_NULL,
+	.regi_dmain  = regi_NULL,
+	.regi_dmaout = regi_NULL,
+#endif
+	.flags = STD_FLAGS,
+},  /* ttyS2 */
+{
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+	.regi_ser    = regi_ser3,
+	.regi_dmain  =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN
+	               regi_dma9,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_IN
+	               regi_NULL,
+#  endif
+
+	 .regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA8_OUT
+	               regi_dma8,
+#  endif
+#  ifdef CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_OUT
+	               regi_NULL,
+#  endif
+#else
+	.regi_ser    = regi_NULL,
+	.regi_dmain  = regi_NULL,
+	.regi_dmaout = regi_NULL,
+#endif
+	.flags = STD_FLAGS,
+},  /* ttyS3 */
+};
+
+
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct etraxfs_serial))
+
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
+#define PROCSTAT(x) x
+struct ser_statistics_type {
+	int overrun_cnt;
+	int early_errors_cnt;
+	int ser_ints_ok_cnt;
+	int errors_cnt;
+	unsigned long int processing_flip;
+	unsigned long processing_flip_still_room;
+	unsigned long int timeout_flush_cnt;
+	int rx_dma_ints;
+	int tx_dma_ints;
+	int rx_tot;
+	int tx_tot;
+};
+
+static struct ser_statistics_type ser_stat[NR_PORTS];
+
+#else
+
+#define PROCSTAT(x)
+
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
+
+/* All serial port signals are active low:
+ * active   = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
+ * inactive = 1 -> 0V   to RS-232 driver -> +12V on RS-232 level
+ *
+ * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip
+ */
+
+/* Output */
+#define ETRAXFS_RTS_GET(info) ser_hw_rts_get(info)
+static int inline ser_hw_rts_get(struct etraxfs_serial * info)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+	if (regi_ser) {
+		/* Return what the user has controlled rts to or
+		 * what the pin is? (if auto_rts is used it differs during tx)
+		 */
+		reg_ser_r_stat_din rstat;
+		rstat = REG_RD(ser, regi_ser, r_stat_din);
+		return !(rstat.rts_n == regk_ser_active);
+	}
+	return 1;
+}
+
+/* Input */
+#define ETRAXFS_CTS_GET(info) ser_hw_cts_get(info)
+static int inline ser_hw_cts_get(struct etraxfs_serial * info)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+	if (regi_ser) {
+		reg_ser_r_stat_din rstat;
+		rstat = REG_RD(ser, regi_ser, r_stat_din);
+		return !(rstat.cts_n == regk_ser_active);
+	}
+	return 1;
+}
+
+/* 0 means 0V, 1 means 3.3V */
+/* Is an output - read input anyway */
+#define ETRAXFS_DTR_GET(info) crisv32_io_rd(&info->dtr_pin)
+
+/* Normally inputs */
+#define ETRAXFS_RI_GET(info) crisv32_io_rd(&info->ri_pin)
+#define ETRAXFS_CD_GET(info) crisv32_io_rd(&info->cd_pin)
+
+/* Input */
+#define ETRAXFS_DSR_GET(info) crisv32_io_rd(&info->dsr_pin)
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+#ifdef DECLARE_MUTEX
+static DECLARE_MUTEX(tmp_buf_sem);
+#else
+static struct semaphore tmp_buf_sem = MUTEX;
+#endif
+
+/* Calculate the chartime depending on baudrate, numbor of bits etc. */
+static void update_char_time(struct etraxfs_serial * info)
+{
+	tcflag_t cflags = info->tty->termios->c_cflag;
+	int bits;
+
+	/* calc. number of bits / data byte */
+	/* databits + startbit and 1 stopbit */
+	if ((cflags & CSIZE) == CS7)
+		bits = 9;
+	else
+		bits = 10;
+
+	if (cflags & CSTOPB)     /* 2 stopbits ? */
+		bits++;
+
+	if (cflags & PARENB)     /* parity bit ? */
+		bits++;
+
+	/* calc timeout */
+	info->char_time_usec = ((bits * 1000000) / info->obaud) + 1;
+}
+
+/*
+ * This function maps from the Bxxxx defines in asm/termbits.h into real
+ * baud rates.
+ */
+
+static int
+cflag_to_baud(unsigned int cflag)
+{
+	static const int baud_table[] = {
+		0, 0, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
+		4800, 9600, 19200, 38400 };
+
+	static const int ext_baud_table[] = {
+		0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000,
+		12500000, 0, 0, 0, 0, 0, 0, 0 };
+
+	if (cflag & CBAUDEX)
+		return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+	else
+		return baud_table[cflag & CBAUD];
+}
+
+/* and this maps to an ETRAX FS hardware baud divisor */
+#define F29_493 29493000
+#define BAUD_DIV(baud) ((F29_493/8)/(baud))
+#define BAUD_DIV100(baud) ((100000000/8)/(baud))
+
+
+static const unsigned short baud_div_table[] = {
+	0, 0, BAUD_DIV(75), BAUD_DIV(110),
+	BAUD_DIV(134), BAUD_DIV(150), BAUD_DIV(200), BAUD_DIV(300),
+	BAUD_DIV(600), BAUD_DIV(1200), BAUD_DIV(1800), BAUD_DIV(2400),
+	BAUD_DIV(4800), BAUD_DIV(9600), BAUD_DIV(19200), BAUD_DIV(38400)
+};
+
+static const unsigned short ext_baud_div_table[] = {
+	0, BAUD_DIV(57600), BAUD_DIV(115200), BAUD_DIV(230400),
+	BAUD_DIV(460800), BAUD_DIV(921600), BAUD_DIV(1843200), BAUD_DIV100(6250000),
+	BAUD_DIV100(12500000), 0, 0, 0,
+	0, 0, 0, 0,
+};
+
+static unsigned short
+cflag_to_etrax_divisor(unsigned int cflag, int *base_freq_p)
+{
+	unsigned short retval;
+
+	*base_freq_p = regk_ser_f29_493;
+	if (cflag & CBAUDEX) {
+		int ix = (cflag & CBAUD) & ~CBAUDEX;
+
+		retval = ext_baud_div_table[ix];
+		if (ix == 7 || ix == 8)
+			*base_freq_p = regk_ser_f100;
+	} else {
+		retval = baud_div_table[cflag & CBAUD];
+	}
+
+
+	if (retval == 0) {
+		printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+		retval = BAUD_DIV(9600); /* choose default 9600 instead */
+	}
+	return retval;
+}
+
+#if 0
+/* returns best divisor and base_freq (in regk_ser_fxxx) for an
+   input baudrate */
+static int
+special_baud_to_etrax_registers(struct etraxfs_serial *info,
+				int baud,
+				int *base_freq_p)
+{
+	int base_freq;
+	unsigned int baud_res;
+	int baud_diff, best_baud_diff;
+	int divisor, best_divisor;
+
+
+
+	/* baud_clock = base_freq / (divisor*8)
+	 * divisor = base_freq / (baud_clock * 8)
+	 * base_freq is either:
+	 * off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz
+	 * 29.493MHz is used for standard baudrates
+	 */
+	if (baud > 32768000/8) {
+		divisor = (100000000/8)/baud;
+		baud_res = (100000000/8)/divisor;
+		DEBUG_BAUD(printk("100.000MHz baud %i divisor %i baud_res %i diff %i\n", baud, divisor, baud_res, baud_res-baud));
+		*base_freq_p = regk_ser_f100;
+		return divisor;
+	} else {
+
+		divisor = (29493000/8)/baud;
+		baud_res = (29493000/8)/divisor;
+		DEBUG_BAUD(printk(" 29.493MHz baud %i divisor %i baud_res %i diff %i\n", baud, divisor, baud_res, baud_res-baud));
+		if (divisor >65535) {
+			DEBUG_BAUD(printk("baudrate to low!\n"));
+		}
+
+		baud_diff = baud_res-baud;
+		if (baud_diff == 0) {
+			*base_freq_p = regk_ser_f29_493;
+			return divisor;
+		}
+		best_divisor = divisor;
+		best_baud_diff = baud_diff;
+		base_freq = regk_ser_f29_493;
+
+		divisor = (32000000/8)/baud;
+		baud_res = (32000000/8)/divisor;
+		DEBUG_BAUD(printk(" 32.000MHz baud %i divisor %i baud_res %i diff %i\n", baud, divisor, baud_res, baud_res-baud));
+		if (baud_diff == 0) {
+			*base_freq_p = regk_ser_f32;
+			return divisor;
+		}
+		if (baud_diff < best_baud_diff) {
+			best_divisor = divisor;
+			best_baud_diff = baud_diff;
+			base_freq = regk_ser_f32;
+		}
+
+		divisor = (32768000/8)/baud;
+		baud_res = (32768000/8)/divisor;
+		DEBUG_BAUD(printk(" 32.768MHz baud %i divisor %i baud_res %i diff %i\n", baud, divisor, baud_res, baud_res-baud));
+		if (baud_diff == 0) {
+			*base_freq_p = regk_ser_f32_768;
+			return divisor;
+		}
+		if (baud_diff < best_baud_diff) {
+			best_divisor = divisor;
+			best_baud_diff = baud_diff;
+			base_freq = regk_ser_f32_768;
+		}
+
+		divisor = (100000000/8)/baud;
+		baud_res = (100000000/8)/divisor;
+		DEBUG_BAUD(printk("100.000MHz baud %i divisor %i baud_res %i diff %i\n", baud, divisor, baud_res, baud_res-baud));
+		if (baud_diff == 0) {
+			*base_freq_p = regk_ser_f100;
+			return divisor;
+		}
+		if (baud_diff < best_baud_diff) {
+			best_divisor = divisor;
+			best_baud_diff = baud_diff;
+			base_freq = regk_ser_f100;
+		}
+		*base_freq_p = base_freq;
+		return best_divisor;
+	}
+}
+#endif
+
+/* Various static support functions */
+
+/* Functions to set or clear DTR/RTS on the requested line */
+/* It is complicated by the fact that RTS is a serial port register, while
+ * DTR might not be implemented in the HW at all, and if it is, it can be on
+ * any general port.
+ */
+
+
+static inline void
+etraxfs_dtr(struct etraxfs_serial *info, int set)
+{
+#ifdef SERIAL_DEBUG_IO
+	printk("ser%i dtr %i\n", info->line, set);
+#endif
+	/* DTR is active low */
+	{
+		unsigned long flags;
+
+		local_irq_save(flags);
+		crisv32_io_set(&info->dtr_pin, set ? 0 : 1);
+		local_irq_restore(flags);
+	}
+}
+
+/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
+ *                                          0=0V    , 1=3.3V
+ */
+static inline void
+etraxfs_rts(struct etraxfs_serial *info, int set)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_rec_ctrl rec_ctrl;
+		local_irq_save(flags);
+		rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+		if (set)
+			rec_ctrl.rts_n = regk_ser_active;
+		else
+			rec_ctrl.rts_n = regk_ser_inactive;
+		REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+		local_irq_restore(flags);
+	}
+
+#ifdef SERIAL_DEBUG_IO
+	printk("ser%i rts %i\n", info->line, set);
+#endif
+}
+
+
+/* If this behaves as a modem, RI and CD is an output */
+static inline void
+etraxfs_ri_out(struct etraxfs_serial *info, int set)
+{
+	/* RI is active low */
+	{
+		unsigned long flags;
+
+		local_irq_save(flags);
+		crisv32_io_set(&info->ri_pin, set ? 0 : 1);
+		local_irq_restore(flags);
+	}
+}
+static inline void
+etraxfs_cd_out(struct etraxfs_serial *info, int set)
+{
+	/* CD is active low */
+	{
+		unsigned long flags;
+
+		local_irq_save(flags);
+		crisv32_io_set(&info->cd_pin, set ? 0 : 1);
+		local_irq_restore(flags);
+	}
+}
+
+static inline void
+etraxfs_disable_rx(struct etraxfs_serial *info)
+{
+	/* disable the receiver */
+	reg_scope_instances regi_ser = info->regi_ser;
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_rec_ctrl rec_ctrl;
+		local_irq_save(flags);
+		rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+		rec_ctrl.en = regk_ser_no;
+		REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+		local_irq_restore(flags);
+	}
+}
+
+static inline void
+etraxfs_enable_rx(struct etraxfs_serial *info)
+{
+	/* enable the receiver */
+	reg_scope_instances regi_ser = info->regi_ser;
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_rec_ctrl rec_ctrl;
+		local_irq_save(flags);
+		rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+		rec_ctrl.en = regk_ser_yes;
+		REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+		local_irq_restore(flags);
+	}
+}
+
+/* the rx DMA uses both the dma_descr and the dma_eop interrupts */
+
+static inline void
+etraxfs_disable_rxdma_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_dmain = info->regi_dmain;
+#ifdef SERIAL_DEBUG_INTR
+	printk("rxdma_irq(%d): 0\n",info->line);
+#endif
+	if (regi_dmain) {
+		reg_dma_rw_intr_mask intr_mask = { 0 };
+		REG_WR(dma, regi_dmain, rw_intr_mask, intr_mask);
+	}
+}
+
+static inline void
+etraxfs_enable_rxdma_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_dmain = info->regi_dmain;
+#ifdef SERIAL_DEBUG_INTR
+	printk("rxdma_irq(%d): 1\n",info->line);
+#endif
+	if (regi_dmain) {
+		reg_dma_rw_intr_mask intr_mask = { 0 };
+		intr_mask.data = regk_dma_yes;
+		intr_mask.in_eop = regk_dma_yes;
+		REG_WR(dma, regi_dmain, rw_intr_mask, intr_mask);
+	}
+}
+
+/* the tx DMA uses only dma_descr interrupt */
+
+static inline void
+etraxfs_disable_txdma_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_dmaout = info->regi_dmaout;
+#ifdef SERIAL_DEBUG_INTR
+	printk("txdma_irq(%d): 0\n",info->line);
+#endif
+	if (regi_dmaout) {
+		reg_dma_rw_intr_mask intr_mask = { 0 };
+		REG_WR(dma, regi_dmaout, rw_intr_mask, intr_mask);
+	}
+}
+
+static inline void
+etraxfs_enable_txdma_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_dmaout = info->regi_dmaout;
+#ifdef SERIAL_DEBUG_INTR
+	printk("txdma_irq(%d): 1\n",info->line);
+#endif
+	if (regi_dmaout) {
+		reg_dma_rw_intr_mask intr_mask = { 0 };
+		intr_mask.data = regk_dma_yes;
+		REG_WR(dma, regi_dmaout, rw_intr_mask, intr_mask);
+	}
+}
+
+
+static inline void
+etraxfs_disable_serial_data_rx_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_irq(%d): 0\n",info->line);
+#endif
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_intr_mask intr_mask;
+		local_irq_save(flags);
+		intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+		intr_mask.dav = regk_ser_no;
+		REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+		local_irq_restore(flags);
+	}
+}
+
+static inline void
+etraxfs_enable_serial_data_rx_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_irq(%d): 1\n",info->line);
+#endif
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_intr_mask intr_mask;
+		local_irq_save(flags);
+		intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+		intr_mask.dav= regk_ser_yes;
+		REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+		local_irq_restore(flags);
+	}
+}
+
+
+static inline void
+etraxfs_disable_serial_data_tx_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_irq(%d): 0\n",info->line);
+#endif
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_intr_mask intr_mask;
+		local_irq_save(flags);
+		intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+		intr_mask.tr_rdy = regk_ser_no;
+		REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+		local_irq_restore(flags);
+	}
+}
+
+static inline void
+etraxfs_enable_serial_data_tx_irq(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_ser = info->regi_ser;
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_irq(%d): 1\n",info->line);
+#endif
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_intr_mask intr_mask;
+		local_irq_save(flags);
+		intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+		intr_mask.tr_rdy = regk_ser_yes;
+		REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+		local_irq_restore(flags);
+	}
+}
+
+
+
+#if defined(CONFIG_ETRAX_RS485)
+/* Enable RS-485 mode on selected port. This is UGLY. */
+static int
+etraxfs_enable_rs485(struct tty_struct *tty,struct rs485_control *r)
+{
+	struct etraxfs_serial * info = (struct etraxfs_serial *)tty->driver_data;
+	reg_scope_instances regi_ser = info->regi_ser;
+
+	info->rs485.rts_on_send = 0x01 & r->rts_on_send;
+	info->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
+	info->rs485.delay_rts_before_send = r->delay_rts_before_send;
+	info->rs485.enabled = r->enabled;
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_tr_ctrl tr_ctrl;
+                reg_ser_rw_rec_ctrl rec_ctrl;
+
+		local_irq_save(flags);
+		tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+                rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+		if (r->enabled) {
+			tr_ctrl.auto_rts = regk_ser_yes;
+			rec_ctrl.rts_n = r->rts_after_sent ?
+				regk_ser_active : regk_ser_inactive;
+
+		} else {
+			tr_ctrl.auto_rts = regk_ser_no;
+			rec_ctrl.rts_n = r->rts_after_sent ?
+				regk_ser_active : regk_ser_inactive;
+		}
+
+		REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+                REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+#ifdef CONFIG_ETRAX_RS485_DISABLE_RECEIVER
+		{
+			reg_ser_rw_rec_ctrl rec_ctrl;
+			rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+			rec_ctrl.half_duplex = r->enabled;
+			REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+		}
+#endif /* CONFIG_ETRAX_RS485_DISABLE_RECEIVER */
+		local_irq_restore(flags);
+	}
+/*	printk("rts: on send = %i, after = %i, enabled = %i",
+		    info->rs485.rts_on_send,
+		    info->rs485.rts_after_sent,
+		    info->rs485.enabled
+	);
+*/
+	return 0;
+}
+
+static int
+etraxfs_write_rs485(struct tty_struct *tty,
+                 const unsigned char *buf, int count)
+{
+	struct etraxfs_serial * info = (struct etraxfs_serial *)tty->driver_data;
+	int old_enabled = info->rs485.enabled;
+	reg_scope_instances regi_ser = info->regi_ser;
+
+	/* rs485 is always implicitly enabled if we're using the ioctl()
+	 * but it doesn't have to be set in the rs485_control
+	 * (to be backward compatible with old apps)
+	 * So we store, set and restore it.
+	 */
+	info->rs485.enabled = 1;
+	/* rs_write now deals with RS485 if enabled */
+	if (regi_ser) {
+		unsigned long flags;
+		reg_ser_rw_tr_ctrl tr_ctrl;
+                reg_ser_rw_rec_ctrl rec_ctrl;
+
+		local_irq_save(flags);
+		tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+                rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+		tr_ctrl.auto_rts = regk_ser_yes;
+		rec_ctrl.rts_n = info->rs485.rts_after_sent ?
+			regk_ser_active : regk_ser_inactive;
+
+#ifdef CONFIG_ETRAX_RS485_DISABLE_RECEIVER
+		rec_ctrl.half_duplex = regk_ser_yes;
+#endif /* CONFIG_ETRAX_RS485_DISABLE_RECEIVER */
+		REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+                REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+		local_irq_restore(flags);
+	}
+	count = rs_write(tty, buf, count);
+	info->rs485.enabled = old_enabled;
+	if (regi_ser && !old_enabled) {
+		unsigned long flags;
+		reg_ser_rw_tr_ctrl tr_ctrl;
+		local_irq_save(flags);
+		tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+		tr_ctrl.auto_rts = regk_ser_no;
+		REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+#ifdef CONFIG_ETRAX_RS485_DISABLE_RECEIVER
+		{
+			reg_ser_rw_rec_ctrl rec_ctrl;
+			rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+			rec_ctrl.half_duplex = regk_ser_no;
+			REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+		}
+#endif /* CONFIG_ETRAX_RS485_DISABLE_RECEIVER */
+		local_irq_restore(flags);
+	}
+	return count;
+}
+
+#endif /* CONFIG_ETRAX_RS485 */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter using rw_tr_ctrl register.
+ * ------------------------------------------------------------
+ */
+
+static void
+rs_stop(struct tty_struct *tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+	if (info) {
+		unsigned long flags;
+		reg_scope_instances regi_ser = info->regi_ser;
+		reg_ser_rw_tr_ctrl tr_ctrl;
+		if (regi_ser) {
+			local_irq_save(flags);
+			tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+			tr_ctrl.en = regk_ser_no;
+			REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+			local_irq_restore(flags);
+		}
+	}
+}
+
+static void
+rs_start(struct tty_struct *tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+	if (info) {
+		unsigned long flags;
+		reg_scope_instances regi_ser = info->regi_ser;
+		reg_ser_rw_tr_ctrl tr_ctrl;
+		if (regi_ser) {
+			local_irq_save(flags);
+			tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+			tr_ctrl.en = regk_ser_yes;
+			REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+			local_irq_restore(flags);
+		}
+	}
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void
+rs_sched_event(struct etraxfs_serial *info,
+				    int event)
+{
+	info->event |= 1 << event;
+	schedule_work(&info->work);
+}
+
+/* The output DMA channel is free - use it to send as many chars as possible
+ * NOTES:
+ *   We don't pay attention to info->x_char, which means if the TTY wants to
+ *   use XON/XOFF it will set info->x_char but we won't send any X char!
+ *
+ *   To implement this, we'd just start a DMA send of 1 byte pointing at a
+ *   buffer containing the X char, and skip updating xmit. We'd also have to
+ *   check if the last sent char was the X char when we enter this function
+ *   the next time, to avoid updating xmit with the sent X value.
+ */
+
+
+static void inline
+transmit_chars_dma(struct etraxfs_serial *info,
+		   reg_scope_instances regi_dmaout)
+{
+	struct dma_descr_data *descr, *prev, *dmapos;
+	unsigned int c, sentl = 0;
+	reg_dma_rw_ack_intr ack_intr = { 0 };
+	reg_dma_rw_stat status;
+	ack_intr.data = regk_dma_yes;
+	/* acknowledge dma data descriptor irq */
+	REG_WR(dma, regi_dmaout, rw_ack_intr, ack_intr);
+	if (!info->tr_running) {
+		/* weirdo... we shouldn't get here! */
+		printk("Achtung: transmit_chars with !tr_running\n");
+		return;
+	}
+
+	/* first get the amount of bytes sent during the last DMA transfer,
+	   and update xmit accordingly */
+	status = REG_RD(dma, regi_dmaout, rw_stat);
+	if (status.list_state == regk_dma_data_at_eol || !info->tx_started)
+		dmapos = phys_to_virt((int)info->last_tx_descr->next);
+	else
+		dmapos = phys_to_virt(REG_RD_INT(dma, regi_dmaout, rw_data));
+
+	descr = info->first_tx_descr;
+	while (descr != dmapos) {
+		sentl += descr->after - descr->buf;
+		descr->after = descr->buf = NULL;
+		descr = phys_to_virt((int)descr->next);
+        }
+
+	info->first_tx_descr = descr;
+	descr = info->last_tx_descr;
+
+	/* update stats */
+	info->icount.tx += sentl;
+
+	/* update xmit buffer */
+	info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1);
+
+	/* if there is only a few chars left in the buf, wake up the blocked
+	   write if any */
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+	/* find out the largest amount of consecutive bytes we want to send now */
+
+	c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+
+	if (c <= 0) {
+		/* our job here is done, don't schedule any new DMA transfer */
+		info->tr_running = 0;
+		return;
+	}
+
+	/* ok we can schedule a dma send of c chars starting at info->xmit.tail */
+	/* set up the descriptor correctly for output */
+	prev = descr;
+	descr = phys_to_virt((int)descr->next);
+	info->last_tx_descr = descr;
+	descr->buf = (void*)virt_to_phys(info->xmit.buf + info->xmit.tail);
+	descr->after = descr->buf + c;
+	descr->eol  = 1;
+	descr->out_eop = 0;
+	descr->intr = 1;
+	descr->wait = 0;
+	descr->in_eop = 0;
+	descr->md = 0;
+	prev->eol = 0;
+	if (!info->tx_started)
+	{
+		info->tx_started = 1;
+		info->tr_context_descr.next = 0;
+		info->tr_context_descr.saved_data = (dma_descr_data*)virt_to_phys(descr);
+		info->tr_context_descr.saved_data_buf = descr->buf;
+		DMA_START_CONTEXT(regi_dmaout, virt_to_phys(&info->tr_context_descr));
+        }
+        else
+        {
+		reg_dma_rw_cmd cmd = {.cont_data = regk_dma_yes};
+		REG_WR(dma, regi_dmaout, rw_cmd, cmd);
+        }
+
+	/* DMA is now running (hopefully) */
+}
+
+static void inline
+transmit_chars_no_dma(struct etraxfs_serial *info,
+		      reg_scope_instances regi_ser)
+{
+	int count;
+
+	reg_ser_r_stat_din rstat;
+	reg_ser_rw_ack_intr ack_intr = { 0 };
+
+	ack_intr.tr_rdy = 1;
+
+	if (info->x_char) {
+		reg_ser_rw_dout dout = { .data = info->x_char };
+		REG_WR(ser, regi_ser, rw_dout, dout);
+		REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
+		info->icount.tx++;
+		info->x_char = 0;
+		return;
+	}
+	if (info->xmit.head == info->xmit.tail
+	    || info->tty->stopped
+	    || info->tty->hw_stopped) {
+		/* No more to send, disable int */
+		reg_ser_rw_intr_mask intr_mask;
+		intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
+		intr_mask.tr_rdy = 0;
+		intr_mask.tr_empty = 0;
+		REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
+		info->tr_running = 0;
+		return;
+	}
+
+	count = ETRAX_SER_FIFO_SIZE;
+	do {
+		reg_ser_rw_dout dout = { .data = info->xmit.buf[info->xmit.tail] };
+		REG_WR(ser, regi_ser, rw_dout, dout);
+		REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
+		info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
+		info->icount.tx++;
+		if (info->xmit.head == info->xmit.tail)
+			break;
+		rstat = REG_RD(ser, regi_ser, r_stat_din);
+	} while ((--count > 0) && rstat.tr_rdy);
+
+	etraxfs_enable_serial_data_tx_irq(info);
+
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+} /* transmit_chars_no_dma */
+
+static void
+transmit_chars(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_dmaout = info->regi_dmaout;
+#ifdef SERIAL_DEBUG_INTR
+	if (info->line == SERIAL_DEBUG_LINE)
+		printk("tc\n");
+#endif
+	if (regi_dmaout) {
+		/* DMA is used for transmit */
+		transmit_chars_dma(info, regi_dmaout);
+	} else {
+		/* Use char-by-char transmit */
+		reg_scope_instances regi_ser = info->regi_ser;
+		if (regi_ser) {
+			transmit_chars_no_dma(info, regi_ser);
+		}
+	}
+} /* transmit_chars */
+
+static void
+start_transmit(struct etraxfs_serial *info)
+{
+	info->tr_running = 1;
+	transmit_chars(info);
+} /* start_transmit */
+
+
+static struct etrax_recv_buffer *
+alloc_recv_buffer(unsigned int size)
+{
+	struct etrax_recv_buffer *buffer;
+
+	if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC)))
+		return NULL;
+
+	buffer->next = NULL;
+	buffer->length = 0;
+	buffer->error = TTY_NORMAL;
+
+	return buffer;
+}
+
+static void
+append_recv_buffer(struct etraxfs_serial *info, struct etrax_recv_buffer *buffer)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	if (!info->first_recv_buffer)
+		info->first_recv_buffer = buffer;
+	else
+		info->last_recv_buffer->next = buffer;
+
+	info->last_recv_buffer = buffer;
+
+	info->recv_cnt += buffer->length;
+	if (info->recv_cnt > info->max_recv_cnt)
+		info->max_recv_cnt = info->recv_cnt;
+
+	local_irq_restore(flags);
+}
+
+static int
+add_char_and_flag(struct etraxfs_serial *info, unsigned char data, unsigned char flag)
+{
+	struct etrax_recv_buffer *buffer;
+
+	if (!(buffer = alloc_recv_buffer(4)))
+		return 0;
+
+	buffer->length = 1;
+	buffer->error = flag;
+	buffer->buffer[0] = data;
+
+	append_recv_buffer(info, buffer);
+
+	info->icount.rx++;
+
+	return 1;
+}
+
+extern _INLINE_ unsigned int
+handle_descr_data(struct etraxfs_serial *info, struct dma_descr_data *descr, unsigned int recvl)
+{
+	struct etrax_recv_buffer *buffer = phys_to_virt((unsigned long)descr->buf) - sizeof *buffer;
+
+	if (info->recv_cnt + recvl > 65536) {
+		printk("%s Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl);
+		return 0;
+	}
+
+	buffer->length = recvl;
+
+	append_recv_buffer(info, buffer);
+	flush_to_flip_buffer(info);
+
+	if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+		panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
+
+	descr->buf = (void*)virt_to_phys(buffer->buffer);
+
+	return recvl;
+}
+
+static _INLINE_ unsigned int
+handle_all_descr_data(struct etraxfs_serial *info)
+{
+	struct dma_descr_data *descr;
+	unsigned int recvl;
+	unsigned int ret = 0;
+	reg_scope_instances regi_dmain = info->regi_dmain;
+
+	while (1)
+	{
+		descr = &info->rec_descr[info->cur_rec_descr];
+
+		if (descr == phys_to_virt(REG_RD(dma, regi_dmain, rw_data)))
+			break;
+
+		if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+			info->cur_rec_descr = 0;
+
+		/* find out how many bytes were read */
+		recvl = descr->after - descr->buf;
+
+		DEBUG_LOG(info->line, "recvl %d\n", recvl);
+
+		/* update stats */
+		info->icount.rx += recvl;
+
+		ret += handle_descr_data(info, descr, recvl);
+	}
+
+	return ret;
+}
+
+static _INLINE_ void
+receive_chars_dma(struct etraxfs_serial *info)
+{
+	struct tty_struct *tty;
+	reg_ser_r_stat_din rstat;
+	reg_dma_rw_ack_intr ack_intr;
+
+	/* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
+	ack_intr.data = 1;
+	ack_intr.in_eop = 1;
+	REG_WR(dma, info->regi_dmain, rw_ack_intr, ack_intr);
+
+	tty = info->tty;
+	if (!tty) /* Something wrong... */
+		return;
+
+	handle_all_descr_data(info);
+
+	/* Read the status register to detect errors */
+	rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+
+	if (rstat.framing_err | rstat.par_err | rstat.orun) {
+		/* If we got an error, we must reset it by reading the
+		 * rs_stat_din register and put the data in buffer manually.
+		 */
+		reg_ser_rs_stat_din stat_din;
+                stat_din = REG_RD(ser, info->regi_ser, rs_stat_din);
+
+		PROCSTAT(ser_stat[info->line].errors_cnt++);
+		DEBUG_LOG(info->line, "#dERR: s d 0x%05X\n",
+			  ((stat_din.framing_err << 16 |
+			    stat_din.par_err << 12 |
+			    stat_din.orun << 8) |
+			   stat_din.data));
+
+		if (stat_din.par_err)
+			add_char_and_flag(info, stat_din.data, TTY_PARITY);
+		else if (stat_din.orun)
+			add_char_and_flag(info, stat_din.data, TTY_OVERRUN);
+		else if (stat_din.framing_err)
+			add_char_and_flag(info, stat_din.data, TTY_FRAME);
+	}
+
+
+	/* Restart the receiving DMA */
+	DMA_CONTINUE_DATA(info->regi_dmain); // CHECK
+}
+
+static _INLINE_ int
+start_recv_dma(struct etraxfs_serial *info)
+{
+	struct dma_descr_data *descr = info->rec_descr;
+	struct etrax_recv_buffer *buffer;
+	int i;
+
+	/* Set up the receiving descriptors */
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+		if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+			panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__ );
+		descr[i].next = (void*)virt_to_phys(&descr[i+1]);
+		descr[i].buf = (void*)virt_to_phys(buffer->buffer);
+		descr[i].after = descr[i].buf + SERIAL_DESCR_BUF_SIZE;
+		descr[i].eol = 0;
+		descr[i].out_eop = 0;
+		descr[i].intr = 1;
+		descr[i].wait = 0;
+		descr[i].in_eop = 0;
+		descr[i].md = 0;
+
+	}
+
+	/* Link the last descriptor to the first */
+	descr[i-1].next = (void*)virt_to_phys(&descr[0]);
+
+	/* Start with the first descriptor in the list */
+	info->cur_rec_descr = 0;
+	info->rec_context_descr.next = 0;
+	info->rec_context_descr.saved_data = (dma_descr_data *)virt_to_phys(&descr[info->cur_rec_descr]);
+	info->rec_context_descr.saved_data_buf = descr[info->cur_rec_descr].buf;
+
+	/* Start the DMA */
+	DMA_START_CONTEXT(info->regi_dmain, virt_to_phys(&info->rec_context_descr));
+
+	/* Input DMA should be running now */
+	return 1;
+}
+
+
+static void
+start_receive(struct etraxfs_serial *info)
+{
+	reg_scope_instances regi_dmain = info->regi_dmain;
+	if (regi_dmain) {
+		info->tty->flip.count = 0;
+		start_recv_dma(info);
+	}
+}
+
+
+static void
+start_transmitter(struct etraxfs_serial *info)
+{
+	int i;
+	reg_scope_instances regi_dmaout = info->regi_dmaout;
+	if (regi_dmaout) {
+		for (i = 0; i < SERIAL_TX_DESCRIPTORS; i++)
+		{
+			memset(&info->tr_descr[i], 0, sizeof(info->tr_descr[i]));
+			info->tr_descr[i].eol = 1;
+			info->tr_descr[i].intr = 1;
+			info->tr_descr[i].next = (dma_descr_data*)virt_to_phys(&info->tr_descr[i+1]);
+		}
+		info->tr_descr[i-1].next = (dma_descr_data*)virt_to_phys(&info->tr_descr[0]);
+		info->first_tx_descr = info->last_tx_descr = &info->tr_descr[0];
+	}
+}
+
+
+static _INLINE_ void
+status_handle(struct etraxfs_serial *info, unsigned short status)
+{
+}
+
+static _INLINE_ void check_modem_status(struct etraxfs_serial *info)
+{
+	/* TODO: Check modem status change */
+}
+
+/* dma output channel interrupt handler
+   this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or
+   DMA8(ser1) when they have finished a descriptor with the intr flag set.
+*/
+
+static irqreturn_t
+dma_tr_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct etraxfs_serial *info;
+	int i;
+	int handled = 0;
+	reg_scope_instances regi_dmaout;
+	/* find out the line that caused this irq and get it from rs_table */
+
+	for (i = 0; i < NR_PORTS; i++) {
+		reg_dma_r_masked_intr masked_intr;
+		info = rs_table + i;
+		regi_dmaout = info->regi_dmaout;
+		if (!regi_dmaout)
+			continue;
+		/* check for dma_descr (don't need to check for dma_eop in
+		   output dma for serial */
+		masked_intr = REG_RD(dma, regi_dmaout, r_masked_intr);
+
+		if (masked_intr.data) {
+			/* we can send a new dma bunch. make it so. */
+			DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+			/* Read jiffies_usec first,
+			 * we want this time to be as late as possible
+			 */
+ 			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+			info->last_tx_active_usec = GET_JIFFIES_USEC();
+			info->last_tx_active = jiffies;
+			transmit_chars_dma(info, regi_dmaout);
+			handled = 1;
+		}
+		check_modem_status(info);
+	}
+	return IRQ_RETVAL(handled);
+}
+
+/* dma input channel interrupt handler */
+
+static irqreturn_t
+dma_rec_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct etraxfs_serial *info;
+	int i;
+	reg_scope_instances regi_dmain;
+	int handled = 0;
+	/* find out the line that caused this irq and get it from rs_table */
+
+	/* TODO: use irq argument to get correct info? */
+	for (i = 0; i < NR_PORTS; i++) {
+		reg_dma_r_masked_intr masked_intr;
+		info = rs_table + i;
+		regi_dmain = info->regi_dmain;
+		if (!regi_dmain)
+			continue;
+		/* check for both dma_eop and dma_descr for the input dma channel */
+		masked_intr = REG_RD(dma, regi_dmain, r_masked_intr);
+		if (masked_intr.data || masked_intr.in_eop) {
+			/* we have received something */
+			receive_chars_dma(info);
+			handled = 1;
+		}
+		check_modem_status(info);
+	}
+	return IRQ_RETVAL(handled);
+}
+
+#if 0
+static _INLINE_ int
+force_eop_if_needed(struct etraxfs_serial *info)
+{
+	/* We check dav bit to determine if data has
+	 * arrived since last time
+	 */
+	unsigned char rstat = info->port[REG_STATUS];
+
+	/* error or datavail? */
+	if (rstat & SER_ERROR_MASK) {
+		/* Some error has occurred. If there has been valid data, an
+		 * EOP interrupt will be made automatically. If no data, the
+		 * normal ser_interrupt should be enabled and handle it.
+		 * So do nothing!
+		 */
+		DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
+		          rstat | (info->line << 8));
+		return 0;
+	}
+
+	if (rstat & SER_DATA_AVAIL_MASK) {
+		/* Ok data, no error, count it */
+		TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
+		          rstat | (info->line << 8)));
+		/* Read data to clear status flags */
+		(void)info->port[REG_DATA];
+
+		info->forced_eop = 0;
+		START_FLUSH_FAST_TIMER(info, "magic");
+		return 0;
+	}
+
+	/* hit the timeout, force an EOP for the input
+	 * dma channel if we haven't already
+	 */
+	if (!info->forced_eop) {
+		info->forced_eop = 1;
+		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+		DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+		FORCE_EOP(info);
+	}
+
+	return 1;
+}
+#endif
+
+static void
+flush_to_flip_buffer(struct etraxfs_serial *info)
+{
+	struct tty_struct *tty;
+	struct etrax_recv_buffer *buffer;
+	unsigned int length;
+	unsigned long flags;
+
+	if (!info->first_recv_buffer)
+		return;
+
+	local_irq_save(flags);
+
+	if (!(tty = info->tty)) {
+		local_irq_restore(flags);
+		return;
+	}
+
+	length = tty->flip.count;
+
+	while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) {
+		unsigned int count = buffer->length;
+
+		if (length + count > TTY_FLIPBUF_SIZE)
+			count = TTY_FLIPBUF_SIZE - length;
+
+		memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count);
+		memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count);
+		tty->flip.flag_buf_ptr[length] = buffer->error;
+
+		length += count;
+		info->recv_cnt -= count;
+
+		if (count == buffer->length) {
+			info->first_recv_buffer = buffer->next;
+			kfree(buffer);
+		} else {
+			buffer->length -= count;
+			memmove(buffer->buffer, buffer->buffer + count, buffer->length);
+			buffer->error = TTY_NORMAL;
+		}
+	}
+
+	if (!info->first_recv_buffer)
+		info->last_recv_buffer = NULL;
+
+	tty->flip.count = length;
+
+	local_irq_restore(flags);
+
+	/* this includes a check for low-latency */
+	tty_flip_buffer_push(tty);
+}
+
+#if 0
+static _INLINE_ void
+check_flush_timeout(struct etraxfs_serial *info)
+{
+	force_eop_if_needed(info);
+
+	flush_to_flip_buffer(info);
+
+	if (info->first_recv_buffer)
+		START_FLUSH_FAST_TIMER(info, "flip");
+}
+#endif
+
+extern void _INLINE_ receive_chars_no_dma(struct etraxfs_serial *info)
+
+{
+	reg_ser_rs_stat_din stat_din;
+	reg_ser_r_stat_din rstat;
+	struct tty_struct *tty;
+	struct	async_icount *icount;
+	int max_count = 16;
+	reg_ser_rw_ack_intr ack_intr = { 0 };
+
+	rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+	info->last_rx_active_usec = GET_JIFFIES_USEC();
+	info->last_rx_active = jiffies;
+	tty = info->tty;
+	if (!tty) {
+		/* Something wrong... */
+		/* One specific example where this happens is when the KGDB port
+		   is in interrupt mode and we send lots of ctrl-C. */
+		return;
+	}
+	icount = &info->icount;
+#ifdef SERIAL_DEBUG_INTR
+	printk("Int from ser%d\n", info->line);
+#endif
+/*DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
+	do {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+#if 0
+			tty->flip.tqueue.routine((void *) tty);
+#endif
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				return;		// if TTY_DONT_FLIP is set
+		}
+		stat_din = REG_RD(ser, info->regi_ser, rs_stat_din);
+
+		ack_intr.dav = 1;
+		REG_WR(ser, info->regi_ser, rw_ack_intr, ack_intr);
+
+		*tty->flip.char_buf_ptr = stat_din.data;
+		icount->rx++;
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x fe%i pe%i oe%i", stat_din.data,
+                       stat_din.framing_err, stat_din.par_err, stat_din.orun);
+#endif
+
+
+
+		if (stat_din.framing_err |
+                    stat_din.par_err |
+                    stat_din.orun) {
+			if (stat_din.data == 0x00 &&
+                            stat_din.framing_err) {
+				/* Most likely a break. */
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+				icount->brk++;
+				DEBUG_LOG(info->line, "# BL BRK %i\n", icount->brk);
+			} else if (stat_din.par_err) {
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+				icount->parity++;
+			} else if (stat_din.orun) {
+				*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				icount->overrun++;
+			} else if (stat_din.framing_err) {
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+				icount->frame++;
+			}
+
+			DEBUG_LOG(info->line, "#iERR s d %04X\n",
+				  ((stat_din.framing_err << 16 |
+				    stat_din.par_err << 12 |
+				    stat_din.orun << 8) |
+				   stat_din.data));
+		} else {
+			*tty->flip.flag_buf_ptr = 0;
+		}
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+		rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+	} while (rstat.dav && (max_count-- > 0));
+	tty_flip_buffer_push(tty);
+} /* receive_chars_no_dma */
+
+
+/* "Normal" serial port interrupt handler - both rx and tx */
+static irqreturn_t
+ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct etraxfs_serial *info;
+	reg_scope_instances regi_ser;
+	int i;
+	int handled = 0;
+
+	for (i = 0; i < NR_PORTS; i++) {
+		info = rs_table + i;
+		if (info->regi_dmain && info->regi_dmaout)
+			continue; /* we use DMA for both in and out - skip */
+
+		regi_ser = info->regi_ser;
+
+		if (regi_ser) {
+			reg_ser_r_masked_intr masked_intr;
+			masked_intr = REG_RD(ser, regi_ser, r_masked_intr);
+			/* Check what interrupts are active before taking
+			 * actions. If DMA is used the interrupt shouldn't
+			 * be enabled.
+			 */
+			if (masked_intr.dav) {
+				receive_chars_no_dma(info);
+				handled = 1;
+			}
+			check_modem_status(info);
+
+			if (masked_intr.tr_rdy) {
+				transmit_chars_no_dma(info, regi_ser);
+				handled = 1;
+			}
+		}
+	}
+	return IRQ_RETVAL(handled);
+} /* ser_interrupt */
+
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void
+do_softint(void *private_)
+{
+	struct etraxfs_serial	*info = (struct etraxfs_serial *) private_;
+	struct tty_struct	*tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+static int
+startup(struct etraxfs_serial * info)
+{
+	unsigned long flags;
+	unsigned long xmit_page;
+	reg_scope_instances regi_dma;
+	reg_scope_instances regi_ser;
+	reg_dma_rw_cfg cfg = {.en = 1};
+	int i;
+
+	xmit_page = get_zeroed_page(GFP_KERNEL);
+	if (!xmit_page)
+		return -ENOMEM;
+
+	local_irq_save(flags);
+
+	/* if it was already initialized, skip this */
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		local_irq_restore(flags);
+		free_page(xmit_page);
+		return 0;
+	}
+
+	if (info->xmit.buf)
+		free_page(xmit_page);
+	else
+		info->xmit.buf = (unsigned char *) xmit_page;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d (xmit_buf 0x%p)\n", info->line, info->xmit.buf);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+
+	/*
+	 * Reset the DMA channels and make sure their interrupts are cleared
+	 */
+
+	regi_dma = info->regi_dmain;
+	if (regi_dma) {
+		reg_dma_rw_ack_intr ack_intr = { 0 };
+		DMA_RESET(regi_dma);
+		/* Wait until reset cycle is complete */
+		DMA_WAIT_UNTIL_RESET(regi_dma);
+		REG_WR(dma, regi_dma, rw_cfg, cfg);
+		/* Make sure the irqs are cleared */
+		ack_intr.group = 1;
+		ack_intr.ctxt = 1;
+		ack_intr.data = 1;
+		ack_intr.in_eop = 1;
+		ack_intr.stream_cmd = 1;
+		REG_WR(dma, regi_dma, rw_ack_intr, ack_intr);
+	}
+	regi_dma = info->regi_dmaout;
+	if (regi_dma) {
+		reg_dma_rw_ack_intr ack_intr = { 0 };
+		DMA_RESET(regi_dma);
+		/* Wait until reset cycle is complete */
+		DMA_WAIT_UNTIL_RESET(regi_dma);
+		REG_WR(dma, regi_dma, rw_cfg, cfg);
+		/* Make sure the irqs are cleared */
+		ack_intr.group = 1;
+		ack_intr.ctxt = 1;
+		ack_intr.data = 1;
+		ack_intr.in_eop = 1;
+		ack_intr.stream_cmd = 1;
+		REG_WR(dma, regi_dma, rw_ack_intr, ack_intr);
+	}
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->xmit.head = info->xmit.tail = 0;
+	info->first_recv_buffer = info->last_recv_buffer = NULL;
+	info->recv_cnt = info->max_recv_cnt = 0;
+
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		info->rec_descr[i].buf = 0;
+
+	/*
+	 * and set the speed and other flags of the serial port
+	 * this will start the rx/tx as well
+	 */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	etraxfs_enable_serial_data_irq(info);
+#endif
+	change_speed(info);
+
+	/* dummy read to reset any serial errors */
+
+	/* dummy read to reset any serial errors */
+	regi_ser = info->regi_ser;
+	if (regi_ser) {
+		(void)REG_RD(ser, regi_ser, rs_stat_din);
+	}
+
+	/* enable the interrupts */
+	if (info->regi_dmain)
+	{
+		etraxfs_enable_rxdma_irq(info);
+	} else {
+		/* No DMA used */
+		/* enable the interrupts */
+		etraxfs_enable_serial_data_rx_irq(info);
+	}
+	if (info->regi_dmaout)
+	{
+		etraxfs_enable_txdma_irq(info);
+	}
+
+	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
+
+	/* setup the dma input descriptor and start dma */
+
+	start_receive(info);
+	start_transmitter(info);
+
+	/* enable RTS/DTR last */
+
+	etraxfs_rts(info, 1);
+	etraxfs_dtr(info, 1);
+
+	info->flags |= ASYNC_INITIALIZED;
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct etraxfs_serial * info)
+{
+	unsigned long flags;
+	struct dma_descr_data *descr = info->rec_descr;
+	struct etrax_recv_buffer *buffer;
+	int i;
+	reg_ser_rw_tr_ctrl tr_ctrl;
+
+	/* shut down the transmitter and receiver */
+
+	etraxfs_disable_rx(info);
+	tr_ctrl = REG_RD(ser, info->regi_ser, rw_tr_ctrl);
+	tr_ctrl.en = 0;
+	REG_WR(ser, info->regi_ser, rw_tr_ctrl, tr_ctrl);
+
+	info->tr_running = 0;
+
+	/* reset both dma channels */
+	/* reset both dma channels */
+	if (info->regi_dmain) {
+		etraxfs_disable_rxdma_irq(info);
+		DMA_RESET(info->regi_dmain);
+	}
+	if (info->regi_dmaout) {
+		etraxfs_disable_txdma_irq(info);
+		DMA_RESET(info->regi_dmaout);
+	}
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....\n", info->line,
+	       info->irq);
+#endif
+
+	local_irq_save(flags); /* Disable interrupts */
+
+	if (info->xmit.buf) {
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = NULL;
+	}
+
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		if (descr[i].buf) {
+			buffer = phys_to_virt((unsigned long)descr[i].buf) - sizeof *buffer;
+			kfree(buffer);
+			descr[i].buf = 0;
+		}
+
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		/* hang up DTR and RTS if HUPCL is enabled */
+		etraxfs_dtr(info, 0);
+		etraxfs_rts(info, 0); /* could check CRTSCTS before doing this */
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	local_irq_restore(flags);
+}
+
+/* Set a serial port register if anything has changed. */
+#define MODIFY_REG(instance, reg, var) \
+  if (REG_RD_INT(ser, instance, reg) != REG_TYPE_CONV(int, reg_ser_##reg, var)) \
+      REG_WR(ser, instance, reg, var);
+
+
+/* change baud rate and other assorted parameters */
+
+static void
+change_speed(struct etraxfs_serial *info)
+{
+	unsigned int cflag;
+	reg_ser_rw_xoff xoff;
+	reg_ser_rw_xoff_clr xoff_clr = {0};
+	reg_ser_rw_tr_ctrl tx_ctrl = {0};
+	reg_ser_rw_tr_dma_en tx_dma_en = {0};
+	reg_ser_rw_rec_ctrl rx_ctrl = {0};
+	reg_ser_rw_tr_baud_div tx_baud_div = {0};
+	reg_ser_rw_rec_baud_div rx_baud_div = {0};
+	int base_freq;
+
+	/* first some safety checks */
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	if (!info->regi_ser)
+		return;
+
+	cflag = info->tty->termios->c_cflag;
+
+	/* start with default settings and then fill in changes */
+
+	/* tx: 8 bit, no/even parity, 1 stop bit, no cts */
+	tx_ctrl.base_freq = regk_ser_f29_493;
+	tx_ctrl.en = 0;
+	if (info->regi_dmaout)
+		tx_dma_en.en = 1;
+	tx_ctrl.stop = 0;
+	tx_ctrl.auto_rts = 0;
+	tx_ctrl.txd = 1;
+	tx_ctrl.auto_cts = 0;
+	/* rx: 8 bit, no/even parity */
+	if (info->regi_dmain) {
+		rx_ctrl.dma_mode = 1;
+		rx_ctrl.auto_eop = 1;
+	}
+	rx_ctrl.dma_err = regk_ser_stop;
+	rx_ctrl.sampling = regk_ser_majority;
+	rx_ctrl.timeout = 1;
+	rx_ctrl.rts_n = regk_ser_inactive;
+
+	/* Common for tx and rx: 8N1 */
+	tx_ctrl.data_bits = regk_ser_bits8;
+	rx_ctrl.data_bits = regk_ser_bits8;
+	tx_ctrl.par = regk_ser_even;
+	rx_ctrl.par = regk_ser_even;
+	tx_ctrl.par_en = regk_ser_no;
+	rx_ctrl.par_en = regk_ser_no;
+
+	tx_ctrl.stop_bits = regk_ser_bits1;
+
+
+	/* change baud-rate and write it to the hardware */
+
+	info->obaud = cflag_to_baud(cflag);
+	info->ibaud = info->obaud;
+	/* baud_clock = base_freq / (divisor*8)
+	 * divisor = base_freq / (baud_clock * 8)
+	 * base_freq is either:
+	 * off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz
+	 * 20.493MHz is used for standard baudrates
+	 */
+
+
+	tx_baud_div.div = cflag_to_etrax_divisor(cflag, &base_freq);
+	tx_ctrl.base_freq = base_freq;
+	/* ETRAX supports different baudrates on in and out */
+	if (cflag & CIBAUD) {
+		info->ibaud = cflag_to_baud(cflag >> IBSHIFT);
+		rx_baud_div.div = cflag_to_etrax_divisor(cflag >> IBSHIFT, &base_freq);
+		rx_ctrl.base_freq = base_freq;
+	} else {
+		/* RX uses same as TX */
+		rx_baud_div.div = tx_baud_div.div;
+		rx_ctrl.base_freq = tx_ctrl.base_freq;
+	}
+
+	if ((cflag & CSIZE) == CS7) {
+		/* set 7 bit mode */
+		tx_ctrl.data_bits = regk_ser_bits7;
+		rx_ctrl.data_bits = regk_ser_bits7;
+	}
+
+	if (cflag & CSTOPB) {
+		/* set 2 stop bit mode */
+		tx_ctrl.stop_bits = regk_ser_bits2;
+	}
+
+	if (cflag & PARENB) {
+		/* enable parity */
+		tx_ctrl.par_en = regk_ser_yes;
+		rx_ctrl.par_en = regk_ser_yes;
+	}
+
+	if (cflag & CMSPAR) {
+		if (cflag & PARODD) {
+			/* set mark parity if PARODD and CMSPAR */
+			tx_ctrl.par = regk_ser_mark;
+			rx_ctrl.par = regk_ser_mark;
+		} else {
+			tx_ctrl.par = regk_ser_space;
+			rx_ctrl.par = regk_ser_space;
+		}
+	} else {
+		if (cflag & PARODD) {
+			/* set odd parity */
+		       tx_ctrl.par = regk_ser_odd;
+		       rx_ctrl.par = regk_ser_odd;
+		}
+	}
+
+	if (cflag & CRTSCTS) {
+		/* enable automatic CTS handling */
+		tx_ctrl.auto_cts = regk_ser_yes;
+	}
+
+	/* make sure the tx and rx are enabled */
+	tx_ctrl.en = regk_ser_yes;
+	rx_ctrl.en = regk_ser_yes;
+
+	/* actually write the control regs (if modified) to the hardware */
+
+	MODIFY_REG(info->regi_ser, rw_rec_baud_div, rx_baud_div);
+	MODIFY_REG(info->regi_ser, rw_rec_ctrl, rx_ctrl);
+
+	MODIFY_REG(info->regi_ser, rw_tr_baud_div, tx_baud_div);
+	MODIFY_REG(info->regi_ser, rw_tr_ctrl, tx_ctrl);
+	MODIFY_REG(info->regi_ser, rw_tr_dma_en, tx_dma_en);
+
+	xoff = REG_RD(ser, info->regi_ser, rw_xoff);
+	xoff.chr = STOP_CHAR(info->tty);
+
+	xoff_clr.clr = regk_ser_yes;
+	if (info->tty->termios->c_iflag & IXON ) {
+		xoff.automatic = regk_ser_yes;
+	} else {
+		xoff.automatic = regk_ser_no;
+	}
+	MODIFY_REG(info->regi_ser, rw_xoff_clr, xoff_clr);
+	MODIFY_REG(info->regi_ser, rw_xoff, xoff);
+
+	update_char_time(info);
+} /* change_speed */
+
+/* start transmitting chars NOW */
+
+static void
+rs_flush_chars(struct tty_struct *tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (info->tr_running ||
+	    info->xmit.head == info->xmit.tail ||
+	    tty->stopped ||
+	    tty->hw_stopped ||
+	    !info->xmit.buf)
+		return;
+
+#ifdef SERIAL_DEBUG_FLOW
+	printk("rs_flush_chars\n");
+#endif
+
+	/* this protection might not exactly be necessary here */
+
+	local_irq_save(flags);
+	start_transmit(info);
+	local_irq_restore(flags);
+}
+
+extern inline int
+raw_write(struct tty_struct * tty,
+	  const unsigned char *buf, int count)
+{
+	int	c, ret = 0;
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+	unsigned long flags;
+
+	/* first some sanity checks */
+
+	if (!tty || !info->xmit.buf || !tmp_buf)
+		return 0;
+
+#ifdef SERIAL_DEBUG_DATA
+	if (info->line == SERIAL_DEBUG_LINE)
+		printk("raw_write (%d)\n", count);
+#endif
+
+	local_irq_save(flags);
+
+	/* the loca_irq_save/local_irq_restore pairs below are needed because
+	 * the DMA interrupt handler moves the info->xmit values. the memcpy
+	 * needs to be in the critical region unfortunately, because we
+	 * need to read xmit values, memcpy, write xmit values in one
+	 * atomic operation... this could perhaps be avoided by more clever
+	 * design.
+	 */
+	while (count) {
+		c = CIRC_SPACE_TO_END(info->xmit.head,
+				      info->xmit.tail,
+				      SERIAL_XMIT_SIZE);
+
+		if (count < c)
+			c = count;
+		if (c <= 0)
+			break;
+
+		memcpy(info->xmit.buf + info->xmit.head, buf, c);
+		info->xmit.head = (info->xmit.head + c) &
+			(SERIAL_XMIT_SIZE-1);
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+	local_irq_restore(flags);
+
+	/* enable transmitter if not running, unless the tty is stopped
+	 * this does not need IRQ protection since if tr_running == 0
+	 * the IRQ's are not running anyway for this port.
+	 */
+
+	if (info->xmit.head != info->xmit.tail &&
+	    !tty->stopped &&
+	    !tty->hw_stopped &&
+	    !info->tr_running) {
+		start_transmit(info);
+	}
+
+	return ret;
+} /* raw_write() */
+
+static int
+rs_write(struct tty_struct * tty,
+	 const unsigned char *buf, int count)
+{
+	/* RS-485 is taken care of by hardware.
+	 * but we don't support waiting before we transmit.
+	 */
+	count = raw_write(tty, buf, count);
+	return count;
+} /* rs_write */
+
+
+/* how much space is available in the xmit buffer? */
+
+static int
+rs_write_room(struct tty_struct *tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+
+	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* How many chars are in the xmit buffer?
+ * This does not include any chars in the transmitter FIFO.
+ * Use wait_until_sent for waiting for FIFO drain.
+ */
+
+static int
+rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+
+	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* discard everything in the xmit buffer */
+
+static void
+rs_flush_buffer(struct tty_struct *tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	info->xmit.head = info->xmit.tail = 0;
+	local_irq_restore(flags);
+
+	wake_up_interruptible(&tty->write_wait);
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ *
+ * Since we don't bother to check for info->x_char in transmit_chars yet,
+ * we don't really implement this function yet.
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+
+	info->x_char = ch;
+	if (ch) {
+		if (info->regi_dmaout) {
+			/* DMA is used, just write the char manually
+			 * and it will be sent after any ongoing char
+			 */
+			reg_ser_rw_dout dout = { .data = info->x_char };
+			REG_WR(ser, info->regi_ser, rw_dout, dout);
+			info->icount.tx++;
+			info->x_char = 0;
+		} else {
+			/* No DMA - Make sure transmit interrupts are on */
+			etraxfs_enable_serial_data_tx_irq(info);
+		}
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+rs_throttle(struct tty_struct * tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("throttle %s: %d....\n", tty_name(tty, buf),
+	       (int)tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (I_IXOFF(tty))
+		rs_send_xchar(tty, STOP_CHAR(tty));
+
+	/* Turn off RTS line */
+	if (tty->termios->c_cflag & CRTSCTS)
+		etraxfs_rts(info, 0);
+}
+
+static void
+rs_unthrottle(struct tty_struct * tty)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("unthrottle %s: %d....\n", tty_name(tty, buf),
+	       (int)tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			rs_send_xchar(tty, START_CHAR(tty));
+	}
+
+	/* Assert RTS line (do this atomic) */
+	if (tty->termios->c_cflag & CRTSCTS)
+		etraxfs_rts(info, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+get_serial_info(struct etraxfs_serial * info,
+		struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+
+	/* this is all probably wrong, there are a lot of fields
+	 * here that we don't have in etraxfs_serial and maybe we
+	 * should set them to something else than 0.
+	 */
+
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = (int)info->regi_ser;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int
+set_serial_info(struct etraxfs_serial *info,
+		struct serial_struct *new_info)
+{
+	struct serial_struct new_serial;
+	struct etraxfs_serial old_info;
+	int retval = 0;
+
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+		return -EFAULT;
+
+	old_info = *info;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
+		     (info->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			       (new_serial.flags & ASYNC_USR_MASK));
+		goto check_and_exit;
+	}
+
+	if (info->count > 1)
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+		       (new_serial.flags & ASYNC_FLAGS));
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ check_and_exit:
+	if (info->flags & ASYNC_INITIALIZED) {
+		change_speed(info);
+	} else
+		retval = startup(info);
+	return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ */
+static int
+get_lsr_info(struct etraxfs_serial * info, unsigned int *value)
+{
+	unsigned int result = TIOCSER_TEMT;
+	reg_ser_r_stat_din rstat;
+	rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+	if (info->xmit.head != info->xmit.tail  || !rstat.tr_empty)
+		result = 0;
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+#ifdef SERIAL_DEBUG_IO
+struct state_str
+{
+	int state;
+	const char *str;
+};
+
+const struct state_str control_state_str[] = {
+	{TIOCM_DTR, "DTR" },
+	{TIOCM_RTS, "RTS"},
+	{TIOCM_ST, "ST?" },
+	{TIOCM_SR, "SR?" },
+	{TIOCM_CTS, "CTS" },
+	{TIOCM_CD, "CD" },
+	{TIOCM_RI, "RI" },
+	{TIOCM_DSR, "DSR" },
+	{0, NULL }
+};
+
+char *get_control_state_str(int MLines, char *s)
+{
+	int i = 0;
+
+	s[0]='\0';
+	while (control_state_str[i].str != NULL) {
+		if (MLines & control_state_str[i].state) {
+			if (s[0] != '\0') {
+				strcat(s, ", ");
+			}
+			strcat(s, control_state_str[i].str);
+		}
+		i++;
+	}
+	return s;
+}
+#endif
+
+static int
+get_modem_info(struct etraxfs_serial * info, unsigned int *value)
+{
+	unsigned int result;
+	/* Polarity isn't verified */
+#if 0 /*def SERIAL_DEBUG_IO  */
+
+	printk("get_modem_info: RTS: %i DTR: %i CD: %i RI: %i DSR: %i CTS: %i\n",
+	       ETRAXFS_RTS_GET(info),
+	       ETRAXFS_DTR_GET(info),
+	       ETRAXFS_CD_GET(info),
+	       ETRAXFS_RI_GET(info),
+	       ETRAXFS_DSR_GET(info),
+	       ETRAXFS_CTS_GET(info));
+#endif
+
+	result =
+		(!ETRAXFS_RTS_GET(info) ? TIOCM_RTS : 0)
+		| (!ETRAXFS_DTR_GET(info) ? TIOCM_DTR : 0)
+		| (!ETRAXFS_RI_GET(info) ? TIOCM_RNG : 0)
+		| (!ETRAXFS_DSR_GET(info) ? TIOCM_DSR : 0)
+		| (!ETRAXFS_CD_GET(info) ? TIOCM_CAR : 0)
+		| (!ETRAXFS_CTS_GET(info) ? TIOCM_CTS : 0);
+
+#ifdef SERIAL_DEBUG_IO
+	printk("etraxfsser: modem state: %i 0x%08X\n", result, result);
+	{
+		char s[100];
+
+		get_control_state_str(result, s);
+		printk("state: %s\n", s);
+	}
+#endif
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+
+static int
+set_modem_info(struct etraxfs_serial * info, unsigned int cmd,
+	       unsigned int *value)
+{
+	unsigned int arg;
+
+	if (copy_from_user(&arg, value, sizeof(int)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS) {
+			etraxfs_rts(info, 1);
+		}
+		if (arg & TIOCM_DTR) {
+			etraxfs_dtr(info, 1);
+		}
+		/* Handle FEMALE behaviour */
+		if (arg & TIOCM_RI) {
+			etraxfs_ri_out(info, 1);
+		}
+		if (arg & TIOCM_CD) {
+			etraxfs_cd_out(info, 1);
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			etraxfs_rts(info, 0);
+		}
+		if (arg & TIOCM_DTR) {
+			etraxfs_dtr(info, 0);
+		}
+		/* Handle FEMALE behaviour */
+		if (arg & TIOCM_RI) {
+			etraxfs_ri_out(info, 0);
+		}
+		if (arg & TIOCM_CD) {
+			etraxfs_cd_out(info, 0);
+		}
+		break;
+	case TIOCMSET:
+		etraxfs_rts(info, arg & TIOCM_RTS);
+		etraxfs_dtr(info, arg & TIOCM_DTR);
+		/* Handle FEMALE behaviour */
+		etraxfs_ri_out(info, arg & TIOCM_RI);
+		etraxfs_cd_out(info, arg & TIOCM_CD);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+static void
+rs_break(struct tty_struct *tty, int break_state)
+{
+	struct etraxfs_serial * info = (struct etraxfs_serial *)tty->driver_data;
+	unsigned long flags;
+	reg_ser_rw_tr_ctrl tr_ctrl;
+	reg_ser_rw_tr_dma_en tr_dma_en;
+	reg_ser_r_stat_din rstat;
+
+	if (!info->regi_ser)
+		return;
+
+	if (break_state == -1) { /* Send break */
+		/* Stop transmission */
+		local_irq_save(flags);
+		tr_ctrl = REG_RD(ser, info->regi_ser, rw_tr_ctrl);
+		tr_dma_en = REG_RD(ser, info->regi_ser, rw_tr_dma_en);
+		tr_ctrl.stop = 1;
+		/* Disable DMA if used or tr_rdy interrupts if no DMA */
+		if (info->regi_dmaout) {
+			tr_dma_en.en = 0;
+		} else{
+			/* Disable tr_rdy interrupt so we don't have
+			 * to wait until all sent
+			 */
+			etraxfs_disable_serial_data_tx_irq(info);
+		}
+		REG_WR(ser, info->regi_ser, rw_tr_ctrl, tr_ctrl);
+		REG_WR(ser, info->regi_ser, rw_tr_dma_en, tr_dma_en);
+		local_irq_restore(flags);
+
+		/* Wait for current data sent so we don't cripple it */
+		do {
+			rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+		}while(!rstat.tr_empty);
+
+		/* Go to manual mode and set the txd pin to 0 */
+		local_irq_save(flags);
+		tr_ctrl = REG_RD(ser, info->regi_ser, rw_tr_ctrl);
+		tr_ctrl.stop = 1;
+		tr_ctrl.txd = 0;
+	} else {
+		local_irq_save(flags);
+		tr_ctrl = REG_RD(ser, info->regi_ser, rw_tr_ctrl);
+		tr_dma_en = REG_RD(ser, info->regi_ser, rw_tr_dma_en);
+		if (info->regi_dmaout) {
+			tr_dma_en.en = 1;
+		} else {
+			etraxfs_enable_serial_data_tx_irq(info);
+		}
+
+		tr_ctrl.stop = 0;
+		tr_ctrl.txd = 1;
+	}
+	REG_WR(ser, info->regi_ser, rw_tr_ctrl, tr_ctrl);
+	REG_WR(ser, info->regi_ser, rw_tr_dma_en, tr_dma_en);
+	local_irq_restore(flags);
+}
+
+static int
+rs_ioctl(struct tty_struct *tty, struct file * file,
+	 unsigned int cmd, unsigned long arg)
+{
+	struct etraxfs_serial * info = (struct etraxfs_serial *)tty->driver_data;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+			return -EIO;
+	}
+
+	switch (cmd) {
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case TIOCGSERIAL:
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERGETLSR: /* Get line status register */
+			return get_lsr_info(info, (unsigned int *) arg);
+
+		case TIOCSERGSTRUCT:
+			if (copy_to_user((struct etraxfs_serial *) arg,
+					 info, sizeof(struct etraxfs_serial)))
+				return -EFAULT;
+			return 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+		case TIOCSERSETRS485:
+		{
+			struct rs485_control rs485ctrl;
+			if (copy_from_user(&rs485ctrl, (struct rs485_control*)arg, sizeof(rs485ctrl)))
+				return -EFAULT;
+
+			return etraxfs_enable_rs485(tty, &rs485ctrl);
+		}
+
+		case TIOCSERWRRS485:
+		{
+			struct rs485_write rs485wr;
+			if (copy_from_user(&rs485wr, (struct rs485_write*)arg, sizeof(rs485wr)))
+				return -EFAULT;
+
+			return etraxfs_write_rs485(tty, 1, rs485wr.outc, rs485wr.outc_size);
+		}
+#endif
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void
+rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag &&
+	    tty->termios->c_iflag == old_termios->c_iflag)
+		return;
+
+	change_speed(info);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * S structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void
+rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct etraxfs_serial * info = (struct etraxfs_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info)
+		return;
+
+	/* interrupts are disabled for this entire function */
+
+	local_irq_save(flags);
+
+	if (tty_hung_up_p(filp)) {
+		local_irq_restore(flags);
+		return;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
+	       info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttyS%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		local_irq_restore(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the serial receiver and the DMA receive interrupt.
+	 */
+
+	etraxfs_disable_serial_data_rx_irq(info);
+	etraxfs_disable_rx(info);
+	etraxfs_disable_rxdma_irq(info);
+	info->tx_started = 0;
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important as we have a transmit FIFO!
+		 */
+		rs_wait_until_sent(tty, HZ);
+	}
+
+	shutdown(info);
+	if (tty->driver->flush_buffer)
+		tty->driver->flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(info->close_delay);
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	local_irq_restore(flags);
+
+	/* port closed */
+
+#if defined(CONFIG_ETRAX_RS485)
+	if (info->rs485.enabled) {
+		info->rs485.enabled = 0;
+	}
+#endif
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	unsigned long orig_jiffies;
+	struct etraxfs_serial *info = (struct etraxfs_serial *)tty->driver_data;
+	reg_ser_r_stat_din rstat;
+
+	orig_jiffies = jiffies;
+	rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+	while (info->xmit.head != info->xmit.tail || /* More in send queue */
+	       (!rstat.tr_empty)) {  /* transmitter not empty */
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
+			break;
+		rstat = REG_RD(ser, info->regi_ser, r_stat_din);
+	}
+	set_current_state(TASK_RUNNING);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+rs_hangup(struct tty_struct *tty)
+{
+	struct etraxfs_serial * info = (struct etraxfs_serial *)tty->driver_data;
+
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~ASYNC_NORMAL_ACTIVE;
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+		struct etraxfs_serial *info)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long	flags;
+	int		retval;
+	int		do_clocal = 0, extra_count = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			wait_event_interruptible(info->close_wait, 0);
+#ifdef SERIAL_DO_RESTART
+		if (info->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (tty->termios->c_cflag & CLOCAL)
+		do_clocal = 1;
+
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	local_irq_save(flags);
+	if (!tty_hung_up_p(filp)) {
+		extra_count++;
+		info->count--;
+	}
+	local_irq_restore(flags);
+	info->blocked_open++;
+	while (1) {
+		/* assert RTS and DTR */
+		etraxfs_rts(info, 1);
+		etraxfs_dtr(info, 1);
+		local_irq_restore(flags);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ASYNC_CLOSING) && do_clocal)
+			/* && (do_clocal || DCD_IS_ASSERTED) */
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttyS%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&info->open_wait, &wait);
+	if (extra_count)
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.
+ * It performs the serial-specific initialization for the tty structure.
+ */
+static int
+rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct etraxfs_serial	*info;
+	int 			retval, line;
+	unsigned long           page;
+
+	/* find which port we want to open */
+
+	line = tty->index;
+
+	if (line < 0 || line >= NR_PORTS)
+		return -ENODEV;
+
+	/* find the corresponding etraxfs_serial struct in the table */
+	info = rs_table + line;
+
+	/* don't allow the opening of ports that are not enabled in the HW config */
+	if (!info->regi_ser)
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name,
+ 	       info->count);
+#endif
+
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+	if (!tmp_buf) {
+		page = get_zeroed_page(GFP_KERNEL);
+		if (!page) {
+			return -ENOMEM;
+		}
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
+	/*
+	 * If the port is in the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			wait_event_interruptible(info->close_wait, 0);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * Start up the serial port
+	 */
+
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("rs_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open ttyS%d successful...\n", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+extern inline int line_info(char *buf, struct etraxfs_serial *info)
+{
+	char	stat_buf[30];
+	int	ret;
+
+	ret = sprintf(buf, "%d: uart:ETRAXFS port:%lX irq:%d",
+		      info->line, (unsigned long)info->regi_ser, info->irq);
+
+	if (!info->regi_ser || (info->type == PORT_UNKNOWN)) {
+		ret += sprintf(buf+ret, "\n");
+		return ret;
+	}
+
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (!ETRAXFS_RTS_GET(info))
+		strcat(stat_buf, "|RTS");
+	if (!ETRAXFS_CTS_GET(info))
+		strcat(stat_buf, "|CTS");
+	if (!ETRAXFS_DTR_GET(info))
+		strcat(stat_buf, "|DTR");
+	if (!ETRAXFS_DSR_GET(info))
+		strcat(stat_buf, "|DSR");
+	if (!ETRAXFS_CD_GET(info))
+		strcat(stat_buf, "|CD");
+	if (!ETRAXFS_RI_GET(info))
+		strcat(stat_buf, "|RI");
+
+	ret += sprintf(buf+ret, " baud:%d", info->obaud);
+	if (info->ibaud != info->obaud)
+		ret += sprintf(buf+ret, " ibaud:%d", info->ibaud);
+
+	ret += sprintf(buf+ret, " tx:%lu rx:%lu",
+		       (unsigned long)info->icount.tx,
+		       (unsigned long)info->icount.rx);
+
+	ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
+		       (unsigned long)info->recv_cnt,
+		       (unsigned long)info->max_recv_cnt);
+
+	if (info->icount.frame)
+		ret += sprintf(buf+ret, " fe:%lu",
+			       (unsigned long)info->icount.frame);
+
+	if (info->icount.parity)
+		ret += sprintf(buf+ret, " pe:%lu",
+			       (unsigned long)info->icount.parity);
+
+	if (info->icount.brk)
+		ret += sprintf(buf+ret, " brk:%lu",
+			       (unsigned long)info->icount.brk);
+
+	if (info->icount.overrun)
+		ret += sprintf(buf+ret, " oe:%lu",
+			       (unsigned long)info->icount.overrun);
+
+	/*
+	 * Last thing is the RS-232 status lines
+	 */
+	ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+	return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int i, len = 0, l;
+	off_t	begin = 0;
+
+	len += sprintf(page, "serinfo:1.0 driver:%s\n",
+		       serial_version);
+	for (i = 0; i < NR_PORTS && len < 4000; i++) {
+		if (!rs_table[i].regi_ser)
+			continue;
+		l = line_info(page + len, &rs_table[i]);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void
+show_serial_version(void)
+{
+	printk("ETRAX FS serial-driver %s, (c) 2003 Axis Communications AB\r\n",
+	       &serial_version[11]); /* "$Revision: x.yy" */
+}
+
+/* rs_init inits the driver at boot (using the module_init chain) */
+
+static struct tty_operations rs_ops = {
+	.open = rs_open,
+	.close = rs_close,
+	.write = rs_write,
+	.flush_chars = rs_flush_chars,
+	.write_room = rs_write_room,
+	.chars_in_buffer = rs_chars_in_buffer,
+	.flush_buffer = rs_flush_buffer,
+	.ioctl = rs_ioctl,
+	.throttle = rs_throttle,
+	.unthrottle = rs_unthrottle,
+	.set_termios = rs_set_termios,
+	.stop = rs_stop,
+	.start = rs_start,
+	.hangup = rs_hangup,
+	.break_ctl = rs_break,
+	.send_xchar = rs_send_xchar,
+	.wait_until_sent = rs_wait_until_sent,
+	.read_proc = rs_read_proc,
+};
+
+static int __init
+rs_init(void)
+{
+	int i;
+	struct etraxfs_serial *info;
+	struct tty_driver *driver = alloc_tty_driver(NR_PORTS);
+	reg_intr_vect_rw_mask intr_mask;
+	unsigned long flags;
+
+	if (!driver)
+		return -ENOMEM;
+
+	show_serial_version();
+
+	/* Setup the timed flush handler system */
+
+	/* Initialize the tty_driver structure */
+
+	driver->driver_name = "serial";
+	driver->name = "ttyS";
+	driver->major = TTY_MAJOR;
+	driver->minor_start = 64;
+	driver->type = TTY_DRIVER_TYPE_SERIAL;
+	driver->subtype = SERIAL_TYPE_NORMAL;
+	driver->init_termios = tty_std_termios;
+	driver->init_termios.c_cflag =
+		B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
+	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+	tty_set_operations(driver, &rs_ops);
+	if (tty_register_driver(driver))
+		panic("Couldn't register serial driver\n");
+	serial_driver = driver;
+
+	/* do some initializing for the separate ports */
+
+	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		info->line = i;
+		info->tty = 0;
+		info->type = PORT_ETRAX;
+		info->tr_running = 0;
+		info->forced_eop = 0;
+		info->flags = 0;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		info->xmit.buf = NULL;
+		info->xmit.tail = info->xmit.head = 0;
+		info->first_recv_buffer = info->last_recv_buffer = NULL;
+		info->recv_cnt = info->max_recv_cnt = 0;
+		info->last_tx_active_usec = 0;
+		info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+		/* Set sane defaults */
+		info->rs485.rts_on_send = 0;
+		info->rs485.rts_after_sent = 1;
+		info->rs485.delay_rts_before_send = 0;
+		info->rs485.enabled = 0;
+#endif
+		INIT_WORK(&info->work, do_softint, info);
+
+		if (info->regi_ser) {
+			printk(KERN_INFO "%s%d at 0x%x is a builtin UART %s %s\n",
+			       serial_driver->name, info->line,
+			       (unsigned int)info->regi_ser,
+			       info->regi_dmain?"DMA IN":"",
+			       info->regi_dmaout?"DMA OUT":"");
+		}
+	}
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+	if (request_irq(SER0_INTR_VECT, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "ser0", &rs_table[0]))
+		panic("irq ser0");
+	local_irq_save(flags);
+	/* enable the ser0 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.ser0 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+	SETUP_PINS(0);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+	if (crisv32_pinmux_alloc_fixed(pinmux_ser1))
+		panic("pinmux ser1");
+	if (request_irq(SER1_INTR_VECT, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "ser1", &rs_table[1]))
+		panic("irq ser1");
+	local_irq_save(flags);
+	/* enable the ser1 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.ser1 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+	SETUP_PINS(1);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+       if (crisv32_pinmux_alloc_fixed(pinmux_ser2))
+		panic("pinmux ser1");
+	if (request_irq(SER2_INTR_VECT, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "ser2", &rs_table[2]))
+		panic("irq ser2");
+	local_irq_save(flags);
+	/* enable the ser2 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.ser2 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+	SETUP_PINS(2);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+       if (crisv32_pinmux_alloc_fixed(pinmux_ser3))
+		panic("pinmux ser1");
+	if (request_irq(SER3_INTR_VECT, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "ser3", &rs_table[3]))
+		panic("irq ser3" );
+	local_irq_save(flags);
+	/* enable the ser3 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.ser3 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+	SETUP_PINS(3);
+#endif
+
+	/* ser0 can use dma6 for tx and dma7 for rx */
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+	if (request_irq(DMA6_INTR_VECT, dma_tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL))
+		panic("irq ser0txdma");
+	crisv32_request_dma(6, "ser0", DMA_PANIC_ON_ERROR, 0, dma_ser0);
+	local_irq_save(flags);
+	/* enable the dma6 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma6 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+	if (request_irq(DMA7_INTR_VECT, dma_rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL))
+		panic("irq ser0rxdma");
+	crisv32_request_dma(7, "ser0", DMA_PANIC_ON_ERROR, 0, dma_ser0);
+	local_irq_save(flags);
+	/* enable the dma7 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma7 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+
+	/* ser1 can use dma4 for tx and dma5 for rx */
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA4_OUT
+	if (request_irq(DMA4_INTR_VECT, dma_tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL))
+		panic("irq ser1txdma");
+	crisv32_request_dma(4, "ser1", DMA_PANIC_ON_ERROR, 0, dma_ser1);
+	local_irq_save(flags);
+	/* enable the dma4 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma4 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN
+	if (request_irq(DMA5_INTR_VECT, dma_rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL))
+		panic("irq ser1rxdma");
+	crisv32_request_dma(5, "ser1", DMA_PANIC_ON_ERROR, 0, dma_ser1);
+	local_irq_save(flags);
+	/* enable the dma5 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma5 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+	/* ser2 can use dma2 for tx and dma3 for rx */
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	if (request_irq(DMA2_INTR_VECT, dma_tr_interrupt, SA_INTERRUPT, "serial 2 dma tr", NULL))
+		panic("irq ser2txdma");
+	crisv32_request_dma(2, "ser2", DMA_PANIC_ON_ERROR, 0, dma_ser2);
+	local_irq_save(flags);
+	/* enable the dma2 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma2 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	if (request_irq(DMA3_INTR_VECT, dma_rec_interrupt, SA_INTERRUPT, "serial 2 dma rec", NULL))
+		panic("irq ser2rxdma");
+	crisv32_request_dma(3, "ser2", DMA_PANIC_ON_ERROR, 0, dma_ser2);
+	local_irq_save(flags);
+	/* enable the dma3 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma3 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+
+	/* ser3 can use dma8 for tx and dma9 for rx */
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA8_OUT
+	if (request_irq(DMA8_INTR_VECT, dma_tr_interrupt, SA_INTERRUPT, "serial 3 dma tr", NULL))
+		panic("irq ser3txdma");
+	crisv32_request_dma(8, "ser3", DMA_PANIC_ON_ERROR, 0, dma_ser3);
+	local_irq_save(flags);
+	/* enable the dma2 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma8 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN
+	if (request_irq(DMA9_INTR_VECT, dma_rec_interrupt, SA_INTERRUPT, "serial 3 dma rec", NULL))
+		panic("irq ser3rxdma");
+	crisv32_request_dma(9, "ser3", DMA_PANIC_ON_ERROR, 0, dma_ser3);
+	local_irq_save(flags);
+	/* enable the dma3 irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma9 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+	local_irq_restore(flags);
+#endif
+
+	return 0;
+}
+
+/* this makes sure that rs_init is called during kernel boot */
+
+module_init(rs_init);
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+int
+register_serial(struct serial_struct *req)
+{
+	return -1;
+}
+
+void unregister_serial(int line)
+{
+}
diff -puN /dev/null drivers/serial/crisv32.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/serial/crisv32.h	2005-06-27 18:32:02.000000000 -0700
@@ -0,0 +1,126 @@
+/*
+ * serial.h: Arch-dep definitions for the ETRAX FS serial driver.
+ *
+ * Copyright (C) 2003 Axis Communications AB
+ */
+
+#ifndef _ETRAX_SERIAL_H
+#define _ETRAX_SERIAL_H
+
+#include <linux/config.h>
+#include <linux/circ_buf.h>
+#include <asm/termios.h>
+
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/dma.h>
+#include <asm/arch/io.h>
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#define SERIAL_RECV_DESCRIPTORS 8
+#define SERIAL_TX_DESCRIPTORS 8
+
+struct etrax_recv_buffer {
+	struct etrax_recv_buffer *next;
+	unsigned short length;
+	unsigned char error;
+	unsigned char pad;
+
+	unsigned char buffer[0];
+};
+
+struct etraxfs_serial {
+	reg_scope_instances	regi_ser; /* Used to check if port enabled as well */
+	reg_scope_instances	regi_dmain;
+	reg_scope_instances	regi_dmaout;
+
+	int			flags;	/* defined in tty.h */
+	/* end of fields defined in rs_table[] in .c-file */
+	int                     forced_eop; /* a fifo eop has been forced */
+
+	struct crisv32_iopin    dtr_pin;
+	struct crisv32_iopin    dsr_pin;
+	struct crisv32_iopin    ri_pin;
+	struct crisv32_iopin    cd_pin;
+
+	struct dma_descr_context tr_context_descr __attribute__ ((__aligned__(32)));
+	struct dma_descr_data	 tr_descr[SERIAL_TX_DESCRIPTORS] __attribute__ ((__aligned__(32)));
+  	struct dma_descr_context rec_context_descr __attribute__ ((__aligned__(32)));
+	struct dma_descr_data	 rec_descr[SERIAL_RECV_DESCRIPTORS] __attribute__ ((__aligned__(32)));
+	struct dma_descr_data*   first_tx_descr;
+	struct dma_descr_data*   last_tx_descr;
+	int			tx_started;
+	int			cur_rec_descr;
+
+	volatile int		tr_running; /* 1 if output is running */
+
+	struct tty_struct	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			x_char;	/* xon/xoff character */
+	int		        obaud;
+	int		        ibaud;
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	u32			irq;
+	int			type;  /* PORT_ETRAX */
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	struct circ_buf		xmit;
+	struct etrax_recv_buffer *first_recv_buffer;
+	struct etrax_recv_buffer *last_recv_buffer;
+	unsigned int		recv_cnt;
+	unsigned int		max_recv_cnt;
+
+	struct work_struct	work;
+	struct async_icount	icount;   /* error-statistics etc.*/
+#ifdef DECLARE_WAITQUEUE
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+#else
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+#endif
+
+	unsigned long		char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long		last_tx_active_usec;  /* Last tx usec in the jiffies */
+	unsigned long		last_tx_active;       /* Last tx time in jiffies */
+	unsigned long		last_rx_active_usec;  /* Last rx usec in the jiffies */
+	unsigned long		last_rx_active;       /* Last rx time in jiffies */
+
+
+#ifdef CONFIG_ETRAX_RS485
+	struct rs485_control	rs485;  /* RS-485 support */
+#endif
+};
+
+/* this PORT is not in the standard serial.h. it's not actually used for
+ * anything since we only have one type of async serial-port anyway in this
+ * system.
+ */
+
+#define PORT_ETRAX 1
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+#endif /* __KERNEL__ */
+
+#endif /* !_ETRAX_SERIAL_H */
diff -puN drivers/serial/Makefile~cris-update-12-17-serial-port-driver drivers/serial/Makefile
--- 25/drivers/serial/Makefile~cris-update-12-17-serial-port-driver	2005-06-27 18:32:02.000000000 -0700
+++ 25-akpm/drivers/serial/Makefile	2005-06-27 18:32:02.000000000 -0700
@@ -53,6 +53,7 @@ obj-$(CONFIG_SERIAL_ICOM) += icom.o
 obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
 obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
+obj-$(CONFIG_ETRAXFS_SERIAL) += crisv32.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
 obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
_