lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <198a9a1fad3830f89aa033f38c7600ec512d1947.1196848533.git.jesper.nilsson@axis.com>
Date:	Thu, 29 Nov 2007 17:23:14 +0100
From:	Jesper Nilsson <jesper.nilsson@...s.com>
To:	Andrew Morton <akpm@...ux-foundation.org>,
	Mikael Starvik <mikael.starvik@...s.com>,
	Jesper Nilsson <jesper.nilsson@...s.com>,
	linux-kernel@...r.kernel.org, linux-serial@...r.kernel.org
Subject: [PATCH 06/47] Add serial driver for CRISv32.

Common driver for EtraxFS and Artpec-3.

Signed-off-by: Jesper Nilsson <jesper.nilsson@...s.com>
---
 drivers/serial/crisv32.c | 2404 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2404 insertions(+), 0 deletions(-)
 create mode 100644 drivers/serial/crisv32.c

diff --git a/drivers/serial/crisv32.c b/drivers/serial/crisv32.c
new file mode 100644
index 0000000..db5d62a
--- /dev/null
+++ b/drivers/serial/crisv32.c
@@ -0,0 +1,2404 @@
+/*
+ * Serial port driver for the ETRAX FS chip
+ *
+ *    Copyright (C) 1998-2007  Axis Communications AB
+ *
+ *    Many, many authors. Based once upon a time on serial.c for 16x50.
+ *
+ *    Johan Adolfsson - port to ETRAX FS
+ *    Mikael Starvik - port to serial_core framework
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/serial_core.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <dma.h>
+#include <asm/arch/system.h>
+#include <asm/arch/mach/pinmux.h>
+#include <hwregs/dma.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/ser_defs.h>
+#include <hwregs/dma_defs.h>
+#include <hwregs/gio_defs.h>
+#include <hwregs/intr_vect_defs.h>
+#include <hwregs/reg_map.h>
+
+#define UART_NR CONFIG_ETRAX_SERIAL_PORTS + 1	/* Ports + dummy port */
+#define SERIAL_RECV_DESCRIPTORS 8
+
+/* We only buffer 255 characters here, no need for more tx descriptors. */
+#define SERIAL_TX_DESCRIPTORS 4
+
+/* Kept for experimental purposes. */
+#define ETRAX_SER_FIFO_SIZE 1
+#define SERIAL_DESCR_BUF_SIZE 256
+#define regi_NULL 0
+#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 __DMA(ch) regi_dma##ch
+#define DMA(ch) __DMA(ch)
+#define DMA_IRQ(ch) (DMA0_INTR_VECT + (ch))
+
+/* Macro to set up control lines for a port. */
+#define SETUP_PINS(port) \
+	if (serial_cris_ports[port].used) { \
+		if (strcmp(CONFIG_ETRAX_SER##port##_DTR_BIT, "")) \
+			crisv32_io_get_name(&serial_cris_ports[port].dtr_pin, \
+				CONFIG_ETRAX_SER##port##_DTR_BIT); \
+		else \
+			serial_cris_ports[port].dtr_pin = dummy_pin; \
+		if (strcmp(CONFIG_ETRAX_SER##port##_DSR_BIT, "")) \
+			crisv32_io_get_name(&serial_cris_ports[port].dsr_pin, \
+				CONFIG_ETRAX_SER##port##_DSR_BIT); \
+		else \
+			serial_cris_ports[port].dsr_pin = dummy_pin; \
+		if (strcmp(CONFIG_ETRAX_SER##port##_RI_BIT, "")) \
+			crisv32_io_get_name(&serial_cris_ports[port].ri_pin, \
+				CONFIG_ETRAX_SER##port##_RI_BIT); \
+		else \
+			serial_cris_ports[port].ri_pin = dummy_pin; \
+		if (strcmp(CONFIG_ETRAX_SER##port##_CD_BIT, "")) \
+			crisv32_io_get_name(&serial_cris_ports[port].cd_pin, \
+				CONFIG_ETRAX_SER##port##_CD_BIT); \
+		else \
+			serial_cris_ports[port].cd_pin = dummy_pin; \
+	}
+
+/* Set a serial port register if anything has changed. */
+#define MODIFY_REG(instance, reg, var) \
+	do { \
+		if (REG_RD_INT(ser, instance, reg) \
+				!= REG_TYPE_CONV(int, reg_ser_##reg, var)) \
+			REG_WR(ser, instance, reg, var); \
+	} while (0)
+
+/*
+ * Regarding RS485 operation in crisv32 serial driver.
+ * ---------------------------------------------------
+ * RS485 can be run in two modes, full duplex using four wires (485FD) and
+ * half duplex using two wires (485HD). The default mode of each serial port
+ * is configured in the kernel configuration. The available modes are:
+ * RS-232, RS-485 half duplex, and RS-485 full duplex.
+ *
+ * In the 485HD mode the direction of the data bus must be able to switch.
+ * The direction of the transceiver is controlled by the RTS signal. Hence
+ * the auto_rts function in the ETRAX FS chip is enabled in this mode, which
+ * automatically toggle RTS when transmitting. The initial direction of the
+ * port is receiving.
+ *
+ * In the 485FD mode two transceivers will be used, one in each direction.
+ * Usually the hardware can handle both 485HD and 485FD, which implies that
+ * one of the transceivers can change direction. Consequently that transceiver
+ * must be tied to operate in the opposite direction of the other one, setting
+ * and keeping RTS to a fixed value do this.
+ *
+ * There are two special "ioctl" that can configure the ports. These two are
+ * left for backward compatible with older applications. The effects of using
+ * them are described below:
+ * The TIOCSERSETRS485:
+ * This ioctl sets a serial port in 232 mode to 485HD mode or vise versa. The
+ * state of the port is kept when closing the port. Note that this ioctl has no
+ * effect on a serial port in the 485FD mode.
+ * The TIOCSERWRRS485:
+ * This ioctl set a serial port in 232 mode to 485HD mode and writes the data
+ * "included" in the ioctl to the port. The port will then stay in 485HD mode.
+ * Using this ioctl on a serial port in the 485HD mode will transmit the data
+ * without changing the mode. Using this ioctl on a serial port in 485FD mode
+ * will not change the mode and simply send the data using the 485FD mode.
+ */
+
+#define TYPE_232 0
+#define TYPE_485HD 1
+#define TYPE_485FD 2
+
+struct etrax_recv_buffer {
+	struct etrax_recv_buffer *next;
+	unsigned short length;
+	unsigned char error;
+	unsigned char pad;
+
+	unsigned char buffer[0];
+};
+
+struct uart_cris_port {
+	struct uart_port port;
+
+	int initialized;
+	int used;
+	int irq;
+
+	/* Used to check if port enabled as well by testing for zero. */
+	reg_scope_instances regi_ser;
+	reg_scope_instances regi_dmain;
+	reg_scope_instances regi_dmaout;
+
+	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)));
+
+	/* This is the first one in the list the HW is working on now. */
+	struct dma_descr_data *first_tx_descr;
+
+	/* This is the last one in the list the HW is working on now. */
+	struct dma_descr_data *last_tx_descr;
+
+	/* This is how many characters the HW is working on now. */
+	unsigned int tx_pending_chars;
+
+	int tx_started;
+	unsigned int cur_rec_descr;
+	struct etrax_recv_buffer *first_recv_buffer;
+	struct etrax_recv_buffer *last_recv_buffer;
+
+	unsigned int recv_cnt;
+	unsigned int max_recv_cnt;
+
+	/* The time for 1 char, in usecs. */
+	unsigned long char_time_usec;
+
+	/* Last tx usec in the jiffies. */
+	unsigned long last_tx_active_usec;
+
+	/* Last tx time in jiffies. */
+	unsigned long last_tx_active;
+
+	/* Last rx usec in the jiffies. */
+	unsigned long last_rx_active_usec;
+
+	/* Last rx time in jiffies. */
+	unsigned long last_rx_active;
+
+#ifdef CONFIG_ETRAX_RS485
+	/* RS-485 support, duh. */
+	struct rs485_control rs485;
+#endif
+	int port_type;
+};
+
+extern struct uart_driver serial_cris_driver;
+static struct uart_port *console_port;
+static int console_baud = 115200;
+static struct uart_cris_port serial_cris_ports[UART_NR] = {
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+		.used = 1,
+		.irq = SER0_INTR_VECT,
+		.regi_ser = regi_ser0,
+		/*
+		 * We initialize 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,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT0_DMA1_IN)
+			regi_dma1,
+#  elif defined CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN
+			regi_NULL,
+#  endif
+
+		.regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+			regi_dma6,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT0_DMA7_OUT)
+			regi_dma7,
+#  else
+			regi_NULL,
+#  endif
+
+#  ifdef CONFIG_ETRAX_RS485
+#    ifdef CONFIG_ETRAX_SERIAL_PORT0_TYPE_485HD
+		.port_type = TYPE_485HD,
+#    endif
+#    ifdef CONFIG_ETRAX_SERIAL_PORT0_TYPE_485FD
+		.port_type = TYPE_485FD,
+#    endif
+#  endif
+#else
+		.regi_ser = regi_NULL,
+		.regi_dmain = regi_NULL,
+		.regi_dmaout = regi_NULL,
+#endif
+	},			/* ttyS0 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+		.used = 1,
+		.irq = SER1_INTR_VECT,
+		.regi_ser = regi_ser1,
+		.regi_dmain =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN
+			regi_dma5,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_IN)
+			regi_NULL,
+#  endif
+
+		.regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA4_OUT
+			regi_dma4,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_OUT)
+			regi_NULL,
+#  endif
+
+#  ifdef CONFIG_ETRAX_RS485
+#    ifdef CONFIG_ETRAX_SERIAL_PORT1_TYPE_485HD
+		.port_type = TYPE_485HD,
+#    endif
+#    ifdef CONFIG_ETRAX_SERIAL_PORT1_TYPE_485FD
+		.port_type = TYPE_485FD,
+#    endif
+#  endif
+#else
+		.regi_ser = regi_NULL,
+		.regi_dmain = regi_NULL,
+		.regi_dmaout = regi_NULL,
+#endif
+	},			/* ttyS1 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+		.used = 1,
+		.irq = SER2_INTR_VECT,
+		.regi_ser = regi_ser2,
+		.regi_dmain =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+			regi_dma3,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT2_DMA7_IN)
+			regi_dma7,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN)
+			regi_NULL,
+#  endif
+
+		.regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+			regi_dma2,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT2_DMA6_OUT)
+			regi_dma6,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT)
+			regi_NULL,
+#  endif
+
+#  ifdef CONFIG_ETRAX_RS485
+#    ifdef CONFIG_ETRAX_SERIAL_PORT2_TYPE_485HD
+		.port_type = TYPE_485HD,
+#    endif
+#    ifdef CONFIG_ETRAX_SERIAL_PORT2_TYPE_485FD
+		.port_type = TYPE_485FD,
+#    endif
+#  endif
+#else
+		.regi_ser = regi_NULL,
+		.regi_dmain = regi_NULL,
+		.regi_dmaout = regi_NULL,
+#endif
+	},			/* ttyS2 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+		.used = 1,
+		.irq = SER3_INTR_VECT,
+		.regi_ser = regi_ser3,
+		.regi_dmain =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN
+			regi_dma9,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT3_DMA4_IN)
+			regi_dma3,
+#  else
+			regi_NULL,
+#  endif
+
+		.regi_dmaout =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA8_OUT
+			regi_dma8,
+#  elif defined(CONFIG_ETRAX_SERIAL_PORT3_DMA2_OUT)
+			regi_dma2,
+#  else
+			regi_NULL,
+#  endif
+#  ifdef CONFIG_ETRAX_RS485
+#    ifdef CONFIG_ETRAX_SERIAL_PORT3_TYPE_485HD
+		.port_type = TYPE_485HD,
+#    endif
+#    ifdef CONFIG_ETRAX_SERIAL_PORT3_TYPE_485FD
+		.port_type = TYPE_485FD,
+#    endif
+#  endif
+#else
+		.regi_ser = regi_NULL,
+		.regi_dmain = regi_NULL,
+		.regi_dmaout = regi_NULL,
+#endif
+	},			/* ttyS3 */
+#if CONFIG_ETRAX_SERIAL_PORTS == 5
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT4
+		.used = 1,
+		.irq = SER4_INTR_VECT,
+		.regi_ser = regi_ser4,
+		.regi_dmain =
+#  ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA9_IN
+			regi_dma9,
+#  else
+			regi_NULL,
+#  endif
+
+		.regi_dmaout = regi_NULL,
+#  ifdef CONFIG_ETRAX_RS485
+#    ifdef CONFIG_ETRAX_SERIAL_PORT4_TYPE_485HD
+		.port_type = TYPE_485HD,
+#    endif
+#    ifdef CONFIG_ETRAX_SERIAL_PORT4_TYPE_485FD
+		.port_type = TYPE_485FD,
+#    endif
+#  endif
+#else
+		.regi_ser = regi_NULL,
+		.regi_dmain = regi_NULL,
+		.regi_dmaout = regi_NULL,
+#endif
+	},	/* ttyS4 */
+#endif
+	{
+#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
+		.used = 1,
+#endif
+		.regi_ser = regi_NULL
+	}	/* Dummy console port */
+
+};
+
+/* 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,
+	32
+};
+static struct crisv32_iopin dummy_pin = {
+	&dummy_port,
+	0
+};
+
+static int selected_console =
+#if defined(CONFIG_ETRAX_DEBUG_PORT0)
+    0;
+#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
+    1;
+#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
+    2;
+#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
+    3;
+#else /* CONFIG_ETRAX_DEBUG_PORT_NULL */
+    4;
+#endif
+
+extern void reset_watchdog(void);
+
+/*
+ * Interrupts are disabled on entering
+ */
+#ifndef CONFIG_ETRAX_VCS_SIM
+static void
+cris_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_cris_port *up;
+	int i;
+	reg_ser_r_stat_din stat;
+	reg_ser_rw_tr_dma_en tr_dma_en, old;
+
+	up = &serial_cris_ports[selected_console];
+
+	/*
+	 * This function isn't covered by the struct uart_ops, so we
+	 * have to check manually that the port really is there,
+	 * configured and live.
+	 */
+	if (!up->regi_ser)
+		return;
+
+	/* Switch to manual mode. */
+	tr_dma_en = old = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
+	if (tr_dma_en.en == regk_ser_yes) {
+		tr_dma_en.en = regk_ser_no;
+		REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
+	}
+
+	/* Send data. */
+	for (i = 0; i < count; i++) {
+		/* LF -> CRLF */
+		if (s[i] == '\n') {
+			do {
+				stat = REG_RD(ser, up->regi_ser, r_stat_din);
+			} while (!stat.tr_rdy);
+			REG_WR_INT(ser, up->regi_ser, rw_dout, '\r');
+		}
+		/* Wait until transmitter is ready and send. */
+		do {
+			stat = REG_RD(ser, up->regi_ser, r_stat_din);
+		} while (!stat.tr_rdy);
+		REG_WR_INT(ser, up->regi_ser, rw_dout, s[i]);
+
+		/* Feed watchdog, because this may take looong time. */
+		reset_watchdog();
+	}
+
+	/* Restore mode. */
+	if (tr_dma_en.en != old.en)
+		REG_WR(ser, up->regi_ser, rw_tr_dma_en, old);
+}
+#else
+
+extern void print_str(const char *str);
+static char buffer[1024];
+static char msg[] = "Debug: ";
+static int buffer_pos = sizeof(msg) - 1;
+
+static void
+cris_console_write(struct console *co, const char *buf, unsigned int len)
+{
+	char *pos;
+	pos = memchr(buf, '\n', len);
+	if (pos) {
+		int l = ++pos - buf;
+		memcpy(buffer + buffer_pos, buf, l);
+		memcpy(buffer, msg, sizeof(msg) - 1);
+		buffer[buffer_pos + l] = '\0';
+		print_str(buffer);
+		buffer_pos = sizeof(msg) - 1;
+		if (pos - buf != len) {
+			memcpy(buffer + buffer_pos, pos, len - l);
+			buffer_pos += len - l;
+		}
+	} else {
+		memcpy(buffer + buffer_pos, buf, len);
+		buffer_pos += len;
+	}
+}
+#endif
+
+static void cris_serial_port_init(struct uart_port *port, int line);
+static int __init cris_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index >= UART_NR)
+		co->index = 0;
+	if (options)
+		selected_console = co->index;
+	port = &serial_cris_ports[selected_console].port;
+	console_port = port;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	console_baud = baud;
+	cris_serial_port_init(port, selected_console);
+	co->index = port->line;
+	uart_set_options(port, co, baud, parity, bits, flow);
+
+	return 0;
+}
+
+static struct tty_driver *cris_console_device(struct console *co, int *index)
+{
+	struct uart_driver *p = co->data;
+	*index = selected_console;
+	return p->tty_driver;
+}
+
+static struct console cris_console = {
+	.name = "ttyS",
+	.write = cris_console_write,
+	.device = cris_console_device,
+	.setup = cris_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &serial_cris_driver,
+};
+
+#define SERIAL_CRIS_CONSOLE	&cris_console
+
+struct uart_driver serial_cris_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "serial",
+	.dev_name = "ttyS",
+	.major = TTY_MAJOR,
+	.minor = 64,
+	.nr = UART_NR,
+	.cons = SERIAL_CRIS_CONSOLE,
+};
+
+static inline int crisv32_serial_get_rts(struct uart_cris_port *up)
+{
+	reg_scope_instances regi_ser = up->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 = REG_RD(ser, regi_ser, r_stat_din);
+	return !(rstat.rts_n == regk_ser_active);
+}
+
+/*
+ * A set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
+ *                                            0=0V    , 1=3.3V
+ */
+static inline void crisv32_serial_set_rts(struct uart_cris_port *up, int set)
+{
+	reg_scope_instances regi_ser = up->regi_ser;
+
+#ifdef CONFIG_ETRAX_RS485
+	/* Never toggle RTS if port is in 485 mode. If port is in 485FD mode we
+	 * do not want to send with the reciever and for 485HD mode auto_rts
+	 * take care of the RTS for us.
+	 */
+	if (!up->rs485.enabled) {
+#else
+	{
+#endif
+		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);
+	}
+}
+
+/* Input */
+static inline int crisv32_serial_get_cts(struct uart_cris_port *up)
+{
+	reg_scope_instances regi_ser = up->regi_ser;
+	reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
+	return (rstat.cts_n == regk_ser_active);
+}
+
+/*
+ * Send a single character for XON/XOFF purposes.  We do it in this separate
+ * function instead of the alternative support port.x_char, in the ...start_tx
+ * function, so we don't mix up this case with possibly enabling transmission
+ * of queued-up data (in case that's disabled after *receiving* an XOFF or
+ * negative CTS).  This function is used for both DMA and non-DMA case; see HW
+ * docs specifically blessing sending characters manually when DMA for
+ * transmission is enabled and running.  We may be asked to transmit despite
+ * the transmitter being disabled by a ..._stop_tx call so we need to enable
+ * it temporarily but restore the state afterwards.
+ *
+ * Beware: I'm not sure how the RS-485 stuff is supposed to work.  Using
+ * XON/XOFF seems problematic if there are several controllers, but if it's
+ * actually RS-422 (multi-drop; one sender and multiple receivers), it might
+ * Just Work, so don't bail out just because it looks a little suspicious.
+ */
+
+void serial_cris_send_xchar(struct uart_port *port, char ch)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	reg_ser_rw_dout dout = {.data = ch };
+	reg_ser_rw_ack_intr ack_intr = {.tr_rdy = regk_ser_yes };
+	reg_ser_r_stat_din rstat;
+	reg_ser_rw_tr_ctrl prev_tr_ctrl, tr_ctrl;
+	reg_scope_instances regi_ser = up->regi_ser;
+	unsigned long flags;
+
+	/*
+	 * Wait for tr_rdy in case a character is already being output.  Make
+	 * sure we have integrity between the register reads and the writes
+	 * below, but don't busy-wait with interrupts off and the port lock
+	 * taken.
+	 */
+	spin_lock_irqsave(&port->lock, flags);
+	do {
+		spin_unlock_irqrestore(&port->lock, flags);
+		spin_lock_irqsave(&port->lock, flags);
+		prev_tr_ctrl = tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+		rstat = REG_RD(ser, regi_ser, r_stat_din);
+	} while (!rstat.tr_rdy);
+
+	/*
+	 * Ack an interrupt if one was just issued for the previous character
+	 * that was output.  This is required for non-DMA as the interrupt is
+	 * used as the only indicator that the transmitter is ready and it
+	 * isn't while this x_char is being transmitted.
+	 */
+	REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
+
+	/* Enable the transmitter in case it was disabled. */
+	tr_ctrl.stop = 0;
+	REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+
+	/*
+	 * Finally, send the blessed character; nothing should stop it now,
+	 * except for an xoff-detected state, which we'll handle below.
+	 */
+	REG_WR(ser, regi_ser, rw_dout, dout);
+	up->port.icount.tx++;
+
+	/* There might be an xoff state to clear. */
+	rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+
+	/*
+	 * Clear any xoff state that *may* have been there to
+	 * inhibit transmission of the character.
+	 */
+	if (rstat.xoff_detect) {
+		reg_ser_rw_xoff_clr xoff_clr = {.clr = 1 };
+		REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
+		reg_ser_rw_tr_dma_en tr_dma_en
+		    = REG_RD(ser, regi_ser, rw_tr_dma_en);
+
+		/*
+		 * If we had an xoff state but cleared it, instead sneak in a
+		 * disabled state for the transmitter, after the character we
+		 * sent.  Thus we keep the port disabled, just as if the xoff
+		 * state was still in effect (or actually, as if stop_tx had
+		 * been called, as we stop DMA too).
+		 */
+		prev_tr_ctrl.stop = 1;
+
+		tr_dma_en.en = 0;
+		REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
+	}
+
+	/* Restore "previous" enabled/disabled state of the transmitter. */
+	REG_WR(ser, regi_ser, rw_tr_ctrl, prev_tr_ctrl);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void transmit_chars_dma(struct uart_cris_port *up);
+
+/*
+ * Do not spin_lock_irqsave or disable interrupts by other means here; it's
+ * already done by the caller.
+ */
+
+static void serial_cris_start_tx(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	reg_scope_instances regi_ser = up->regi_ser;
+	reg_ser_rw_tr_ctrl tr_ctrl;
+
+	tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+	tr_ctrl.stop = regk_ser_no;
+	REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+	if (!up->regi_dmaout) {
+		reg_ser_rw_intr_mask 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);
+	} else {
+		/*
+		 * We're called possibly to re-enable transmission after it
+		 * has been disabled.  If so, DMA needs to be re-enabled.
+		 */
+		reg_ser_rw_tr_dma_en tr_dma_en = {.en = 1 };
+		REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
+		transmit_chars_dma(up);
+	}
+}
+
+/*
+ * This function handles both the DMA and non-DMA case by ordering the
+ * transmitter to stop of after the current character.  We don't need to wait
+ * for any such character to be completely transmitted; we do that where it
+ * matters, like in serial_cris_set_termios.  Don't busy-wait here; see
+ * Documentation/serial/driver: this function is called within
+ * spin_lock_irq{,save} and thus separate ones would be disastrous (when SMP).
+ * There's no documented need to set the txd pin to any particular value;
+ * break setting is controlled solely by serial_cris_break_ctl.
+ */
+
+static void serial_cris_stop_tx(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	reg_scope_instances regi_ser = up->regi_ser;
+	reg_ser_rw_tr_ctrl tr_ctrl;
+	reg_ser_rw_intr_mask intr_mask;
+	reg_ser_rw_tr_dma_en tr_dma_en = { 0 };
+	reg_ser_rw_xoff_clr xoff_clr = { 0 };
+
+	/*
+	 * For the non-DMA case, we'd get a tr_rdy interrupt that we're not
+	 * interested in as we're not transmitting any characters.  For the
+	 * DMA case, that interrupt is already turned off, but no reason to
+	 * waste code on conditionals here.
+	 */
+	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);
+
+	tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+	tr_ctrl.stop = 1;
+	REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+
+	/*
+	 * Always clear possible hardware xoff-detected state here, no need to
+	 * unnecessary consider mctrl settings and when they change.  We clear
+	 * it here rather than in start_tx: both functions are called as the
+	 * effect of XOFF processing, but start_tx is also called when upper
+	 * levels tell the driver that there are more characters to send, so
+	 * avoid adding code there.
+	 */
+	xoff_clr.clr = 1;
+	REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
+
+	/*
+	 * Disable transmitter DMA, so that if we're in XON/XOFF, we can send
+	 * those single characters without also giving go-ahead for queued up
+	 * DMA data.
+	 */
+	tr_dma_en.en = 0;
+	REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
+}
+
+static void serial_cris_stop_rx(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	reg_scope_instances regi_ser = up->regi_ser;
+	reg_ser_rw_rec_ctrl 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);
+}
+
+static void serial_cris_enable_ms(struct uart_port *port)
+{
+}
+
+static void check_modem_status(struct uart_cris_port *up)
+{
+}
+
+static unsigned int serial_cris_tx_empty(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	unsigned long flags;
+	unsigned int ret;
+	reg_ser_r_stat_din rstat = { 0 };
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->regi_dmaout) {
+		/*
+		 * For DMA, before looking at r_stat, we need to check that we
+		 * either haven't actually started or that end-of-list is
+		 * reached, else a tr_empty indication is just an internal
+		 * state.  The caller qualifies, if needed, that the
+		 * port->info.xmit buffer is empty, so we don't need to
+		 * check that.
+		 */
+		reg_dma_rw_stat status = REG_RD(dma, up->regi_dmaout, rw_stat);
+
+		if (!up->tx_started) {
+			ret = 1;
+			goto done;
+		}
+
+		if (status.list_state != regk_dma_data_at_eol) {
+			ret = 0;
+			goto done;
+		}
+	}
+
+	rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+	ret = rstat.tr_empty ? TIOCSER_TEMT : 0;
+
+done:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	return ret;
+}
+static unsigned int serial_cris_get_mctrl(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	unsigned int ret;
+
+	ret = 0;
+	if (crisv32_serial_get_rts(up))
+		ret |= TIOCM_RTS;
+	if (crisv32_io_rd(&up->dtr_pin))
+		ret |= TIOCM_DTR;
+	if (crisv32_io_rd(&up->cd_pin))
+		ret |= TIOCM_CD;
+	if (crisv32_io_rd(&up->ri_pin))
+		ret |= TIOCM_RI;
+	if (!crisv32_io_rd(&up->dsr_pin))
+		ret |= TIOCM_DSR;
+	if (crisv32_serial_get_cts(up))
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void serial_cris_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+	crisv32_serial_set_rts(up, mctrl & TIOCM_RTS ? 1 : 0);
+	crisv32_io_set(&up->dtr_pin, mctrl & TIOCM_DTR ? 1 : 0);
+	crisv32_io_set(&up->ri_pin, mctrl & TIOCM_RNG ? 1 : 0);
+	crisv32_io_set(&up->cd_pin, mctrl & TIOCM_CD ? 1 : 0);
+}
+
+static void serial_cris_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	unsigned long flags;
+	reg_ser_rw_tr_ctrl tr_ctrl;
+	reg_ser_rw_tr_dma_en tr_dma_en;
+	reg_ser_rw_intr_mask intr_mask;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	tr_ctrl = REG_RD(ser, up->regi_ser, rw_tr_ctrl);
+	tr_dma_en = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
+	intr_mask = REG_RD(ser, up->regi_ser, rw_intr_mask);
+
+	if (break_state != 0) {
+		/* Send break */
+		/*
+		 * We need to disable DMA (if used) or tr_rdy interrupts if no
+		 * DMA.  No need to make this conditional on use of DMA;
+		 * disabling will be a no-op for the other mode.
+		 */
+		intr_mask.tr_rdy = regk_ser_no;
+		tr_dma_en.en = 0;
+
+		/*
+		 * Stop transmission and set the txd pin to 0 after the
+		 * current character.  The txd setting will take effect after
+		 * any current transmission has completed.
+		 */
+		tr_ctrl.stop = 1;
+		tr_ctrl.txd = 0;
+	} else {
+		/* Re-enable either transmit DMA or the serial interrupt. */
+		if (up->regi_dmaout)
+			tr_dma_en.en = 1;
+		else
+			intr_mask.tr_rdy = regk_ser_yes;
+
+		tr_ctrl.stop = 0;
+		tr_ctrl.txd = 1;
+	}
+	REG_WR(ser, up->regi_ser, rw_tr_ctrl, tr_ctrl);
+	REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
+	REG_WR(ser, up->regi_ser, rw_intr_mask, intr_mask);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * The output DMA channel is free - use it to send as many chars as
+ * possible.
+ */
+
+static void transmit_chars_dma(struct uart_cris_port *up)
+{
+	struct dma_descr_data *descr, *pending_descr, *dmapos;
+	struct dma_descr_data *last_tx_descr;
+	struct circ_buf *xmit = &up->port.info->xmit;
+	unsigned int sentl = 0;
+	reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes };
+	reg_dma_rw_stat status;
+	reg_scope_instances regi_dmaout = up->regi_dmaout;
+	unsigned int chars_in_q;
+	unsigned int chars_to_send;
+
+	/* Acknowledge dma data descriptor irq, if there was one. */
+	REG_WR(dma, regi_dmaout, rw_ack_intr, ack_intr);
+
+	/*
+	 * 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 || !up->tx_started)
+		dmapos = phys_to_virt((int)up->last_tx_descr->next);
+	else
+		dmapos = phys_to_virt(REG_RD_INT(dma, regi_dmaout, rw_data));
+
+	pending_descr = up->first_tx_descr;
+	while (pending_descr != dmapos) {
+		sentl += pending_descr->after - pending_descr->buf;
+		pending_descr->after = pending_descr->buf = NULL;
+		pending_descr = phys_to_virt((int)pending_descr->next);
+	}
+
+	up->first_tx_descr = pending_descr;
+	last_tx_descr = up->last_tx_descr;
+
+	/* Update stats. */
+	up->port.icount.tx += sentl;
+
+	up->tx_pending_chars -= sentl;
+
+	/* Update xmit buffer. */
+	xmit->tail = (xmit->tail + sentl) & (UART_XMIT_SIZE - 1);
+
+	/*
+	 * Find out the largest amount of consecutive bytes we want to send
+	 * now.
+	 */
+	chars_in_q = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+	if (chars_in_q == 0)
+		/* Tell upper layers that we're now idle. */
+		goto done;
+
+	/* Some of those characters are actually pending output. */
+	chars_to_send = chars_in_q - up->tx_pending_chars;
+
+	/*
+	 * Clamp the new number of pending chars to the advertised
+	 * one.
+	 */
+	if (chars_to_send + up->tx_pending_chars > up->port.fifosize)
+		chars_to_send = up->port.fifosize - up->tx_pending_chars;
+
+	/* If we don't want to send any, we're done. */
+	if (chars_to_send == 0)
+		goto done;
+
+	descr = phys_to_virt((int)last_tx_descr->next);
+
+	/*
+	 * We can't send anything if we could make the condition in
+	 * the while-loop above (reaping finished descriptors) be met
+	 * immediately before the first iteration.  However, don't
+	 * mistake the full state for the empty state.
+	 */
+	if ((descr == up->first_tx_descr && up->tx_pending_chars != 0)
+	    || descr->next == up->first_tx_descr)
+		goto done;
+
+	/* Set up the descriptor for output. */
+	descr->buf = (void *)virt_to_phys(xmit->buf + xmit->tail
+					  + up->tx_pending_chars);
+	descr->after = descr->buf + chars_to_send;
+	descr->eol = 1;
+	descr->out_eop = 0;
+	descr->intr = 1;
+	descr->wait = 0;
+	descr->in_eop = 0;
+	descr->md = 0;
+	/*
+	 * Make sure GCC doesn't move this eol clear before the eol set
+	 * above.
+	 */
+	barrier();
+	last_tx_descr->eol = 0;
+
+	up->last_tx_descr = descr;
+	up->tx_pending_chars += chars_to_send;
+
+	if (!up->tx_started) {
+		up->tx_started = 1;
+		up->tr_context_descr.next = 0;
+		up->tr_context_descr.saved_data
+		    = (dma_descr_data *) virt_to_phys(descr);
+		up->tr_context_descr.saved_data_buf = descr->buf;
+		DMA_START_CONTEXT(regi_dmaout,
+				  virt_to_phys(&up->tr_context_descr));
+	} else
+		DMA_CONTINUE_DATA(regi_dmaout);
+
+	/* DMA is now running (hopefully). */
+
+done:
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+}
+
+static void transmit_chars_no_dma(struct uart_cris_port *up)
+{
+	int count;
+	struct circ_buf *xmit = &up->port.info->xmit;
+
+	reg_scope_instances regi_ser = up->regi_ser;
+	reg_ser_r_stat_din rstat;
+	reg_ser_rw_ack_intr ack_intr = {.tr_rdy = regk_ser_yes };
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		/* No more to send, so disable the interrupt. */
+		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);
+		return;
+	}
+
+	count = ETRAX_SER_FIFO_SIZE;
+	do {
+		reg_ser_rw_dout dout = {.data = xmit->buf[xmit->tail] };
+		REG_WR(ser, regi_ser, rw_dout, dout);
+		REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (xmit->head == xmit->tail)
+			break;
+		rstat = REG_RD(ser, regi_ser, r_stat_din);
+	} while ((--count > 0) && rstat.tr_rdy);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+}				/* transmit_chars_no_dma */
+
+static struct etrax_recv_buffer *alloc_recv_buffer(unsigned int size)
+{
+	struct etrax_recv_buffer *buffer;
+
+	buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC);
+	if (!buffer)
+		panic(KERN_ERR "%s: Could not allocate %d bytes buffer\n",
+		      __FUNCTION__, size);
+
+	buffer->next = NULL;
+	buffer->length = 0;
+	buffer->error = TTY_NORMAL;
+
+	return buffer;
+}
+
+static void
+append_recv_buffer(struct uart_cris_port *up, struct etrax_recv_buffer *buffer)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	if (!up->first_recv_buffer)
+		up->first_recv_buffer = buffer;
+	else
+		up->last_recv_buffer->next = buffer;
+
+	up->last_recv_buffer = buffer;
+
+	up->recv_cnt += buffer->length;
+	if (up->recv_cnt > up->max_recv_cnt)
+		up->max_recv_cnt = up->recv_cnt;
+
+	local_irq_restore(flags);
+}
+
+static int
+add_char_and_flag(struct uart_cris_port *up, unsigned char data,
+		  unsigned char flag)
+{
+	struct etrax_recv_buffer *buffer;
+
+	buffer = alloc_recv_buffer(4);
+	buffer->length = 1;
+	buffer->error = flag;
+	buffer->buffer[0] = data;
+
+	append_recv_buffer(up, buffer);
+
+	up->port.icount.rx++;
+
+	return 1;
+}
+
+static void flush_to_flip_buffer(struct uart_cris_port *up)
+{
+	struct tty_struct *tty;
+	struct etrax_recv_buffer *buffer;
+
+	tty = up->port.info->tty;
+	if (!up->first_recv_buffer || !tty)
+		return;
+
+	while ((buffer = up->first_recv_buffer)) {
+		unsigned int count = (unsigned int)
+		    tty_insert_flip_string(tty, buffer->buffer,
+					   buffer->length);
+
+		up->recv_cnt -= count;
+
+		if (count == buffer->length) {
+			up->first_recv_buffer = buffer->next;
+			kfree(buffer);
+		} else {
+			buffer->length -= count;
+			memmove(buffer->buffer, buffer->buffer + count,
+				buffer->length);
+			buffer->error = TTY_NORMAL;
+		}
+	}
+
+	if (!up->first_recv_buffer)
+		up->last_recv_buffer = NULL;
+
+	/* This call includes a check for low-latency. */
+	tty_flip_buffer_push(tty);
+}
+
+static unsigned int
+handle_descr_data(struct uart_cris_port *up, struct dma_descr_data *descr,
+		  unsigned int recvl)
+{
+	struct etrax_recv_buffer *buffer
+	    = phys_to_virt((unsigned long)descr->buf) - sizeof *buffer;
+
+	if (up->recv_cnt + recvl > 65536) {
+		printk(KERN_ERR "Too much pending incoming data on %s!"
+		       " Dropping %u bytes.\n", up->port.info->tty->name,
+		       recvl);
+		return 0;
+	}
+
+	buffer->length = recvl;
+
+	append_recv_buffer(up, buffer);
+
+	flush_to_flip_buffer(up);
+
+	buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE);
+	descr->buf = (void *)virt_to_phys(buffer->buffer);
+	descr->after = descr->buf + SERIAL_DESCR_BUF_SIZE;
+
+	return recvl;
+}
+
+static unsigned int handle_all_descr_data(struct uart_cris_port *up)
+{
+	struct dma_descr_data *descr = &up->rec_descr[(up->cur_rec_descr - 1) %
+		SERIAL_RECV_DESCRIPTORS];
+	struct dma_descr_data *prev_descr;
+	unsigned int recvl;
+	unsigned int ret = 0;
+	reg_scope_instances regi_dmain = up->regi_dmain;
+
+	while (1) {
+		prev_descr = descr;
+		descr = &up->rec_descr[up->cur_rec_descr];
+
+		if (descr == phys_to_virt(REG_RD(dma, regi_dmain, rw_data)))
+			break;
+
+		if (++up->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+			up->cur_rec_descr = 0;
+
+		/* Find out how many bytes were read. */
+		recvl = descr->after - descr->buf;
+
+		/* Update stats. */
+		up->port.icount.rx += recvl;
+
+		ret += handle_descr_data(up, descr, recvl);
+		descr->eol = 1;
+		/*
+		 * Make sure GCC doesn't move this eol clear before the
+		 * eol set above.
+		 */
+		barrier();
+		prev_descr->eol = 0;
+		flush_dma_descr(descr, 1);	/* Cache bug workaround */
+		flush_dma_descr(prev_descr, 0);	/* Cache bug workaround */
+	}
+
+	return ret;
+}
+
+static void receive_chars_dma(struct uart_cris_port *up)
+{
+	reg_ser_r_stat_din rstat;
+	reg_dma_rw_ack_intr ack_intr = { 0 };
+
+	/* Acknowledge both dma_descr and dma_eop irq. */
+	ack_intr.data = 1;
+	ack_intr.in_eop = 1;
+	REG_WR(dma, up->regi_dmain, rw_ack_intr, ack_intr);
+
+	handle_all_descr_data(up);
+
+	/* Read the status register to detect errors. */
+	rstat = REG_RD(ser, up->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, up->regi_ser, rs_stat_din);
+
+		if (stat_din.par_err)
+			add_char_and_flag(up, stat_din.data, TTY_PARITY);
+		else if (stat_din.orun)
+			add_char_and_flag(up, stat_din.data, TTY_OVERRUN);
+		else if (stat_din.framing_err)
+			add_char_and_flag(up, stat_din.data, TTY_FRAME);
+	}
+
+	/* Restart the receiving DMA, in case it got stuck on an EOL. */
+	DMA_CONTINUE_DATA(up->regi_dmain);
+}
+
+void receive_chars_no_dma(struct uart_cris_port *up)
+{
+	reg_ser_rs_stat_din stat_din;
+	reg_ser_r_stat_din rstat;
+	struct tty_struct *tty;
+	struct uart_icount *icount;
+	int max_count = 16;
+	char flag;
+	reg_ser_rw_ack_intr ack_intr = { 0 };
+
+	rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+	up->last_rx_active_usec = GET_JIFFIES_USEC();
+	up->last_rx_active = jiffies;
+	icount = &up->port.icount;
+	tty = up->port.info->tty;
+
+	do {
+		stat_din = REG_RD(ser, up->regi_ser, rs_stat_din);
+
+		flag = TTY_NORMAL;
+		ack_intr.dav = 1;
+		REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
+		icount->rx++;
+
+		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. */
+				flag = TTY_BREAK;
+				icount->brk++;
+			} else if (stat_din.par_err) {
+				flag = TTY_PARITY;
+				icount->parity++;
+			} else if (stat_din.orun) {
+				flag = TTY_OVERRUN;
+				icount->overrun++;
+			} else if (stat_din.framing_err) {
+				flag = TTY_FRAME;
+				icount->frame++;
+			}
+		}
+
+		/*
+		 * If this becomes important, we probably *could* handle this
+		 * gracefully by keeping track of the unhandled character.
+		 */
+		if (!tty_insert_flip_char(tty, stat_din.data, flag))
+			panic("%s: No tty buffer space", __FUNCTION__);
+		rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+	} while (rstat.dav && (max_count-- > 0));
+	spin_unlock(&up->port.lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&up->port.lock);
+}				/* receive_chars_no_dma */
+
+/*
+ * DMA output channel interrupt handler.
+ * this interrupt is called from DMA2(ser2), DMA8(ser3), DMA6(ser0) or
+ * DMA4(ser1) when they have finished a descriptor with the intr flag set.
+ */
+
+static irqreturn_t dma_tr_interrupt(int irq, void *dev_id)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)dev_id;
+	reg_dma_r_masked_intr masked_intr;
+	reg_scope_instances regi_dmaout;
+	int handled = 0;
+
+	spin_lock(&up->port.lock);
+	regi_dmaout = up->regi_dmaout;
+	if (!regi_dmaout) {
+		spin_unlock(&up->port.lock);
+		return IRQ_NONE;
+	}
+
+	/*
+	 * 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. */
+
+		/*
+		 * Read jiffies_usec first.
+		 * We want this time to be as late as possible.
+		 */
+		up->last_tx_active_usec = GET_JIFFIES_USEC();
+		up->last_tx_active = jiffies;
+		transmit_chars_dma(up);
+		handled = 1;
+	}
+	check_modem_status(up);
+	spin_unlock(&up->port.lock);
+	return IRQ_RETVAL(handled);
+}
+
+/* DMA input channel interrupt handler. */
+
+static irqreturn_t dma_rec_interrupt(int irq, void *dev_id)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)dev_id;
+	reg_dma_r_masked_intr masked_intr;
+	reg_scope_instances regi_dmain;
+	int handled = 0;
+
+	spin_lock(&up->port.lock);
+	regi_dmain = up->regi_dmain;
+	if (!regi_dmain) {
+		spin_unlock(&up->port.lock);
+		return IRQ_NONE;
+	}
+
+	/* 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(up);
+		handled = 1;
+	}
+	check_modem_status(up);
+	spin_unlock(&up->port.lock);
+	return IRQ_RETVAL(handled);
+}
+
+/* "Normal" serial port interrupt handler - both rx and tx. */
+
+static irqreturn_t ser_interrupt(int irq, void *dev_id)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)dev_id;
+	reg_scope_instances regi_ser;
+	int handled = 0;
+
+	spin_lock(&up->port.lock);
+	if (up->regi_dmain && up->regi_dmaout) {
+		spin_unlock(&up->port.lock);
+		return IRQ_NONE;
+	}
+
+	regi_ser = up->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(up);
+			handled = 1;
+		}
+		check_modem_status(up);
+
+		if (masked_intr.tr_rdy) {
+			transmit_chars_no_dma(up);
+			handled = 1;
+		}
+	}
+	spin_unlock(&up->port.lock);
+	return IRQ_RETVAL(handled);
+}				/* ser_interrupt */
+
+static int start_recv_dma(struct uart_cris_port *up)
+{
+	struct dma_descr_data *descr = up->rec_descr;
+	struct etrax_recv_buffer *buffer;
+	int i;
+
+	/* Set up the receiving descriptors. */
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+		buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE);
+		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]);
+
+	/* And mark it as end of list. */
+	descr[i - 1].eol = 1;
+
+	/* Start with the first descriptor in the list. */
+	up->cur_rec_descr = 0;
+	up->rec_context_descr.next = 0;
+	up->rec_context_descr.saved_data
+	    = (dma_descr_data *) virt_to_phys(&descr[up->cur_rec_descr]);
+	up->rec_context_descr.saved_data_buf = descr[up->cur_rec_descr].buf;
+
+	/* Start the DMA. */
+	DMA_START_CONTEXT(up->regi_dmain, virt_to_phys(&up->rec_context_descr));
+
+	/* Input DMA should be running now. */
+	return 1;
+}
+
+static void start_receive(struct uart_cris_port *up)
+{
+	reg_scope_instances regi_dmain = up->regi_dmain;
+	if (regi_dmain)
+		start_recv_dma(up);
+}
+
+static void start_transmitter(struct uart_cris_port *up)
+{
+	int i;
+	reg_scope_instances regi_dmaout = up->regi_dmaout;
+	if (regi_dmaout) {
+		for (i = 0; i < SERIAL_TX_DESCRIPTORS; i++) {
+			memset(&up->tr_descr[i], 0, sizeof(up->tr_descr[i]));
+			up->tr_descr[i].eol = 1;
+			up->tr_descr[i].intr = 1;
+			up->tr_descr[i].next = (dma_descr_data *)
+			    virt_to_phys(&up->tr_descr[i + 1]);
+		}
+		up->tr_descr[i - 1].next = (dma_descr_data *)
+		    virt_to_phys(&up->tr_descr[0]);
+		up->first_tx_descr = &up->tr_descr[0];
+
+		/*
+		 * We'll be counting up to up->last_tx_descr->next from
+		 * up->first_tx_descr when starting DMA, so we should make
+		 * them the same for the very first round.  If instead we'd
+		 * set last_tx_descr = first_tx_descr, we'd rely on
+		 * accidentally working code and data as we'd take a pass over
+		 * the first, unused, descriptor.
+		 */
+		up->last_tx_descr = &up->tr_descr[i - 1];
+		up->tx_started = 0;
+		up->tx_pending_chars = 0;
+	}
+}
+
+static int serial_cris_startup(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	unsigned long flags;
+	reg_ser_rw_intr_mask ser_intr_mask = { 0 };
+	reg_dma_rw_intr_mask dmain_intr_mask = { 0 };
+	reg_dma_rw_intr_mask dmaout_intr_mask = { 0 };
+	reg_dma_rw_cfg cfg = {.en = 1 };
+	reg_scope_instances regi_dma;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	dmain_intr_mask.data = dmain_intr_mask.in_eop = regk_dma_yes;
+	dmaout_intr_mask.data = regk_dma_yes;
+	if (!up->regi_dmain)
+		ser_intr_mask.dav = regk_ser_yes;
+
+	if (port->line == 0) {
+		if (request_irq(SER0_INTR_VECT, ser_interrupt,
+				IRQF_SHARED | IRQF_DISABLED, "ser0",
+				&serial_cris_ports[0]))
+			panic("irq ser0");
+		/* Port 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,
+				IRQF_DISABLED, "serial 0 dma tr",
+				&serial_cris_ports[0]))
+			panic("irq ser0txdma");
+		crisv32_request_dma(6, "ser0", DMA_PANIC_ON_ERROR, 0, dma_ser0);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+		if (request_irq(DMA7_INTR_VECT, dma_rec_interrupt,
+				IRQF_DISABLED, "serial 0 dma rec",
+				&serial_cris_ports[0]))
+			panic("irq ser0rxdma");
+		crisv32_request_dma(7, "ser0", DMA_PANIC_ON_ERROR, 0, dma_ser0);
+#endif
+	} else if (port->line == 1) {
+		if (request_irq(SER1_INTR_VECT, ser_interrupt,
+				IRQF_SHARED | IRQF_DISABLED, "ser1",
+				&serial_cris_ports[1]))
+			panic("irq ser1");
+
+		/* Port 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,
+				IRQF_DISABLED, "serial 1 dma tr",
+				&serial_cris_ports[1]))
+			panic("irq ser1txdma");
+		crisv32_request_dma(4, "ser1", DMA_PANIC_ON_ERROR, 0, dma_ser1);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN
+		if (request_irq(DMA5_INTR_VECT, dma_rec_interrupt,
+				IRQF_DISABLED, "serial 1 dma rec",
+				&serial_cris_ports[1]))
+			panic("irq ser1rxdma");
+		crisv32_request_dma(5, "ser1", DMA_PANIC_ON_ERROR, 0, dma_ser1);
+		crisv32_request_dma(ASYNC_SER1_RX_DMA_NBR, "ser1",
+				    DMA_PANIC_ON_ERROR, 0, dma_ser1);
+#endif
+	} else if (port->line == 2) {
+		if (request_irq(SER2_INTR_VECT, ser_interrupt,
+				IRQF_SHARED | IRQF_DISABLED, "ser2",
+				&serial_cris_ports[2]))
+			panic("irq ser2");
+
+		/* Port 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,
+				IRQF_DISABLED, "serial 2 dma tr",
+				&serial_cris_ports[2]))
+			panic("irq ser2txdma");
+		crisv32_request_dma(2, "ser2", DMA_PANIC_ON_ERROR, 0, dma_ser2);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+		if (request_irq(DMA3_INTR_VECT, dma_rec_interrupt,
+				IRQF_DISABLED, "serial 2 dma rec",
+				&serial_cris_ports[2]))
+			panic("irq ser2rxdma");
+		crisv32_request_dma(3, "ser2", DMA_PANIC_ON_ERROR, 0, dma_ser2);
+#endif
+	} else if (port->line == 3) {
+		if (request_irq(SER3_INTR_VECT, ser_interrupt,
+				IRQF_SHARED | IRQF_DISABLED, "ser3",
+				&serial_cris_ports[3]))
+			panic("irq ser3");
+
+		/* Port 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,
+				IRQF_DISABLED, "serial 3 dma tr",
+				&serial_cris_ports[3]))
+			panic("irq ser3txdma");
+		crisv32_request_dma(8, "ser3", DMA_PANIC_ON_ERROR, 0, dma_ser3);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN
+		if (request_irq(DMA9_INTR_VECT, dma_rec_interrupt,
+				IRQF_DISABLED, "serial 3 dma rec",
+				&serial_cris_ports[3]))
+			panic("irq ser3rxdma");
+		crisv32_request_dma(9, "ser3", DMA_PANIC_ON_ERROR, 0, dma_ser3);
+#endif
+	}
+#if CONFIG_ETRAX_SERIAL_PORTS == 5
+	else if (port->line == 4) {
+		if (request_irq(SER4_INTR_VECT, ser_interrupt,
+				IRQF_SHARED | IRQF_DISABLED, "ser4",
+				&serial_cris_ports[4]))
+			panic("irq ser4");
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA_OUT
+		if (request_irq(DMA4_INTR_VECT, dma_tr_interrupt,
+				IRQF_DISABLED, "serial 4 dma tr",
+				&serial_cris_ports[4]))
+			panic("irq ser4txdma");
+		crisv32_request_dma(5, "ser4", DMA_PANIC_ON_ERROR, 0, dma_ser4);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA_IN
+		if (request_irq(DMA5_INTR_VECT, dma_rec_interrupt,
+				IRQF_DISABLED, "serial 4 dma rec",
+				&serial_cris_ports[4]))
+			panic("irq ser4rxdma");
+		crisv32_request_dma(5, "ser4", DMA_PANIC_ON_ERROR, 0, dma_ser4);
+#endif
+	}
+#endif
+
+	/*
+	 * Reset the DMA channels and make sure their interrupts are cleared.
+	 */
+
+	regi_dma = up->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 = up->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);
+	}
+
+	REG_WR(ser, up->regi_ser, rw_intr_mask, ser_intr_mask);
+	if (up->regi_dmain)
+		REG_WR(dma, up->regi_dmain, rw_intr_mask, dmain_intr_mask);
+	if (up->regi_dmaout)
+		REG_WR(dma, up->regi_dmaout, rw_intr_mask, dmaout_intr_mask);
+
+	start_receive(up);
+	start_transmitter(up);
+
+	serial_cris_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return 0;
+}
+
+static void serial_cris_shutdown(struct uart_port *port)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	serial_cris_stop_tx(port);
+	serial_cris_stop_rx(port);
+
+	if (port->line == 0) {
+		free_irq(SER0_INTR_VECT, &serial_cris_ports[0]);
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+		crisv32_free_dma(6);
+		free_irq(DMA6_INTR_VECT, &serial_cris_ports[0]);
+#elif defined(CONFIG_ETRAX_SERIAL_PORT0_DMA0_OUT)
+		crisv32_free_dma(0);
+		free_irq(DMA0_INTR_VECT, &serial_cris_ports[0]);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+		crisv32_free_dma(7);
+		free_irq(DMA7_INTR_VECT, &serial_cris_ports[0]);
+#elif defined(CONFIG_ETRAX_SERIAL_PORT0_DMA1_IN)
+		crisv32_free_dma(1);
+		free_irq(DMA1_INTR_VECT, &serial_cris_ports[0]);
+#endif
+	} else if (port->line == 1) {
+		free_irq(SER1_INTR_VECT, &serial_cris_ports[1]);
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA4_OUT
+		crisv32_free_dma(4);
+		free_irq(DMA4_INTR_VECT, &serial_cris_ports[1]);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN
+		crisv32_free_dma(5);
+		free_irq(DMA5_INTR_VECT, &serial_cris_ports[1]);
+#endif
+	} else if (port->line == 2) {
+		free_irq(SER2_INTR_VECT, &serial_cris_ports[2]);
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+		crisv32_free_dma(2);
+		free_irq(DMA2_INTR_VECT, &serial_cris_ports[2]);
+#elif defined(CONFIG_ETRAX_SERIAL_PORT2_DMA6_OUT)
+		crisv32_free_dma(6);
+		free_irq(DMA6_INTR_VECT, &serial_cris_ports[2]);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+		crisv32_free_dma(3);
+		free_irq(DMA3_INTR_VECT, &serial_cris_ports[2]);
+#elif defined(CONFIG_ETRAX_SERIAL_PORT2_DMA7_IN)
+		crisv32_free_dma(7);
+		free_irq(DMA7_INTR_VECT, &serial_cris_ports[2]);
+#endif
+	} else if (port->line == 3) {
+		free_irq(SER3_INTR_VECT, &serial_cris_ports[3]);
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA8_OUT
+		crisv32_free_dma(8);
+		free_irq(DMA8_INTR_VECT, &serial_cris_ports[3]);
+#elif defined(CONFIG_ETRAX_SERIAL_PORT3_DMA2_OUT)
+		crisv32_free_dma(2);
+		free_irq(DMA2_INTR_VECT, &serial_cris_ports[3]);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN
+		crisv32_free_dma(9);
+		free_irq(DMA9_INTR_VECT, &serial_cris_ports[3]);
+#elif defined(CONFIG_ETRAX_SERIAL_PORT3_DMA3_IN)
+		crisv32_free_dma(3);
+		free_irq(DMA3_INTR_VECT, &serial_cris_ports[3]);
+#endif
+	}
+#if CONFIG_ETRAX_SERIAL_PORTS == 5
+	else if (port->line == 4) {
+		free_irq(SER4_INTR_VECT, &serial_cris_ports[4]);
+#ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA9_IN
+		crisv32_free_dma(9);
+		free_irq(DMA9_INTR_VECT, &serial_cris_ports[4]);
+#endif
+	}
+#endif
+
+	serial_cris_set_mctrl(&up->port, up->port.mctrl);
+
+	if (up->regi_dmain) {
+		struct etrax_recv_buffer *rb;
+		struct etrax_recv_buffer *rb_next;
+		int i;
+		struct dma_descr_data *descr;
+
+		/*
+		 * In case of DMA and receive errors, there might be pending
+		 * receive buffers still linked here and not flushed upwards.
+		 * Release them.
+		 */
+		for (rb = up->first_recv_buffer; rb != NULL; rb = rb_next) {
+			rb_next = rb->next;
+			kfree(rb);
+		}
+		up->first_recv_buffer = NULL;
+		up->last_recv_buffer = NULL;
+
+		/*
+		 * Also release buffers that were attached to the DMA
+		 * before we shut down the hardware above.
+		 */
+		for (i = 0, descr = up->rec_descr;
+		     i < SERIAL_RECV_DESCRIPTORS; i++)
+			if (descr[i].buf) {
+				rb = phys_to_virt((u32) descr[i].buf)
+				    - sizeof *rb;
+				kfree(rb);
+				descr[i].buf = NULL;
+			}
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+}
+
+static void
+serial_cris_set_termios(struct uart_port *port, struct termios *termios,
+			struct termios *old)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	unsigned long flags;
+	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 };
+	reg_ser_r_stat_din rstat;
+	int baud;
+
+	if (old &&
+	    termios->c_cflag == old->c_cflag &&
+	    termios->c_iflag == old->c_iflag)
+		return;
+
+	/* 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;
+	tx_ctrl.stop = 0;
+#ifdef CONFIG_ETRAX_RS485
+	if (up->rs485.enabled && (up->port_type != TYPE_485FD))
+		tx_ctrl.auto_rts = regk_ser_yes;
+	else
+#endif
+		tx_ctrl.auto_rts = regk_ser_no;
+	tx_ctrl.txd = 1;
+	tx_ctrl.auto_cts = 0;
+	/* Rx: 8 bit, no/even parity. */
+	if (up->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;
+
+#ifdef CONFIG_ETRAX_RS485
+	if (up->rs485.enabled && (up->port_type != TYPE_485FD)) {
+#  ifdef CONFIG_ETRAX_RS485_DISABLE_RECEIVER
+		rx_ctrl.half_duplex = regk_ser_yes;
+#  endif
+		rx_ctrl.rts_n = up->rs485.rts_after_sent ?
+		    regk_ser_active : regk_ser_inactive;
+	} else if (up->port_type == TYPE_485FD) {
+		rx_ctrl.rts_n = regk_ser_active;
+	} else
+#endif
+		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. */
+
+	/* 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
+	 */
+
+	/*
+	 * For the console port we keep the original baudrate here.  Not very
+	 * beautiful.
+	 */
+	if ((port != console_port) || old)
+		baud = uart_get_baud_rate(port, termios, old, 0,
+					  port->uartclk / 8);
+	else
+		baud = console_baud;
+
+	tx_baud_div.div = 29493000 / (8 * baud);
+	/* Rx uses same as tx. */
+	rx_baud_div.div = tx_baud_div.div;
+	rx_ctrl.base_freq = tx_ctrl.base_freq;
+
+	if ((termios->c_cflag & CSIZE) == CS7) {
+		/* Set 7 bit mode. */
+		tx_ctrl.data_bits = regk_ser_bits7;
+		rx_ctrl.data_bits = regk_ser_bits7;
+	}
+
+	if (termios->c_cflag & CSTOPB) {
+		/* Set 2 stop bit mode. */
+		tx_ctrl.stop_bits = regk_ser_bits2;
+	}
+
+	if (termios->c_cflag & PARENB) {
+		/* Enable parity. */
+		tx_ctrl.par_en = regk_ser_yes;
+		rx_ctrl.par_en = regk_ser_yes;
+	}
+
+	if (termios->c_cflag & CMSPAR) {
+		if (termios->c_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 (termios->c_cflag & PARODD) {
+			/* Set odd parity. */
+			tx_ctrl.par = regk_ser_odd;
+			rx_ctrl.par = regk_ser_odd;
+		}
+	}
+
+	if (termios->c_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;
+
+	/*
+	 * Wait for tr_idle in case a character is being output, so it won't
+	 * be damaged by the changes we do below.  It seems the termios
+	 * changes "sometimes" (we can't see e.g. a tcsetattr TCSANOW
+	 * parameter here) should take place no matter what state.  However,
+	 * in case we should wait, we may have a non-empty transmitter state
+	 * as we tell the upper layers that we're all done when we've passed
+	 * characters to the hardware, but we don't wait for them being
+	 * actually shifted out.
+	 */
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * None of our interrupts re-enable DMA, so it's thankfully ok to
+	 * disable it once, outside the loop.
+	 */
+	tx_dma_en.en = 0;
+	REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
+	do {
+		/*
+		 * Make sure we have integrity between the read r_stat status
+		 * and us writing the registers below, but don't busy-wait
+		 * with interrupts off.  We need to keep the port lock though
+		 * (if we go SMP), so nobody else writes characters.
+		 */
+		local_irq_restore(flags);
+		local_irq_save(flags);
+		rstat = REG_RD(ser, up->regi_ser, r_stat_din);
+	} while (!rstat.tr_idle);
+
+	/* Actually write the control regs (if modified) to the hardware. */
+
+	uart_update_timeout(port, termios->c_cflag, port->uartclk / 8);
+	MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div);
+	MODIFY_REG(up->regi_ser, rw_rec_ctrl, rx_ctrl);
+
+	MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div);
+	MODIFY_REG(up->regi_ser, rw_tr_ctrl, tx_ctrl);
+
+	tx_dma_en.en = up->regi_dmaout != 0;
+	REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
+
+	xoff = REG_RD(ser, up->regi_ser, rw_xoff);
+
+	if (up->port.info && (up->port.info->tty->termios->c_iflag & IXON)) {
+		xoff.chr = STOP_CHAR(up->port.info->tty);
+		xoff.automatic = regk_ser_yes;
+	} else
+		xoff.automatic = regk_ser_no;
+
+	MODIFY_REG(up->regi_ser, rw_xoff, xoff);
+
+	/*
+	 * Make sure we don't start in an automatically shut-off state due to
+	 * a previous early exit.
+	 */
+	xoff_clr.clr = 1;
+	REG_WR(ser, up->regi_ser, rw_xoff_clr, xoff_clr);
+
+	serial_cris_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static const char *serial_cris_type(struct uart_port *port)
+{
+	return "CRISv32";
+}
+
+static void serial_cris_release_port(struct uart_port *port)
+{
+}
+
+static int serial_cris_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_cris_config_port(struct uart_port *port, int flags)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	up->port.type = PORT_CRIS;
+}
+
+#if defined(CONFIG_ETRAX_RS485)
+
+static void cris_set_rs485_mode(struct uart_cris_port *up)
+{
+	reg_ser_rw_tr_ctrl tr_ctrl;
+	reg_ser_rw_rec_ctrl rec_ctrl;
+	reg_scope_instances regi_ser = up->regi_ser;
+
+	if (up->port_type == TYPE_485FD)
+		/* We do not want to change anything if we are in 485FD mode */
+		return;
+
+	tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+	rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+
+	/* Set port in RS-485 mode */
+	if (up->rs485.enabled) {
+		tr_ctrl.auto_rts = regk_ser_yes;
+		rec_ctrl.rts_n = up->rs485.rts_after_sent ?
+		    regk_ser_active : regk_ser_inactive;
+#ifdef CONFIG_ETRAX_RS485_DISABLE_RECEIVER
+		rec_ctrl.half_duplex = regk_ser_yes;
+#endif
+	}
+	/* Set port to RS-232 mode */
+	else {
+		rec_ctrl.rts_n = regk_ser_inactive;
+		tr_ctrl.auto_rts = regk_ser_no;
+		rec_ctrl.half_duplex = regk_ser_no;
+	}
+
+	REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+	REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+}
+
+/* Enable/disable RS-485 mode on selected port. */
+static int cris_enable_rs485(struct uart_cris_port *up, struct rs485_control *r)
+{
+	if (up->port_type == TYPE_485FD)
+		/* Port in 485FD mode can not chage mode */
+		goto out;
+
+	up->rs485.enabled = 0x1 & r->enabled;
+	up->rs485.rts_on_send = 0x01 & r->rts_on_send;
+	up->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
+	up->rs485.delay_rts_before_send = r->delay_rts_before_send;
+
+	cris_set_rs485_mode(up);
+out:
+	return 0;
+}
+
+/* Enable RS485 mode on port and send the data. Port will stay
+ * in 485 mode after the data has been sent.
+ */
+static int
+cris_write_rs485(struct uart_cris_port *up, const unsigned char *buf, int count)
+{
+	up->rs485.enabled = 1;
+
+	/* Set the port in RS485 mode */
+	cris_set_rs485_mode(up);
+
+	/* Send the data */
+	count =
+	    serial_cris_driver.tty_driver->write(up->port.info->tty, buf,
+						 count);
+
+	return count;
+}
+
+#endif /* CONFIG_ETRAX_RS485 */
+
+static int serial_cris_ioctl(struct uart_port *port, unsigned int cmd,
+			     unsigned long arg)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+
+	switch (cmd) {
+#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 cris_enable_rs485(up, &rs485ctrl);
+		}
+
+	case TIOCSERWRRS485:{
+			struct rs485_write rs485wr;
+			if (copy_from_user(&rs485wr, (struct rs485_write *)arg,
+					   sizeof(rs485wr)))
+				return -EFAULT;
+
+			return cris_write_rs485(up, rs485wr.outc,
+						rs485wr.outc_size);
+		}
+#endif
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static const struct uart_ops serial_cris_pops = {
+	.tx_empty = serial_cris_tx_empty,
+	.set_mctrl = serial_cris_set_mctrl,
+	.get_mctrl = serial_cris_get_mctrl,
+	.stop_tx = serial_cris_stop_tx,
+	.start_tx = serial_cris_start_tx,
+	.send_xchar = serial_cris_send_xchar,
+	.stop_rx = serial_cris_stop_rx,
+	.enable_ms = serial_cris_enable_ms,
+	.break_ctl = serial_cris_break_ctl,
+	.startup = serial_cris_startup,
+	.shutdown = serial_cris_shutdown,
+	.set_termios = serial_cris_set_termios,
+	.type = serial_cris_type,
+	.release_port = serial_cris_release_port,
+	.request_port = serial_cris_request_port,
+	.config_port = serial_cris_config_port,
+	.ioctl = serial_cris_ioctl,
+};
+
+/*
+ * It's too easy to break CONFIG_ETRAX_DEBUG_PORT_NULL and the
+ * no-config choices by adding and moving code to before a necessary
+ * early exit in all functions for the special case of
+ * up->regi_ser == 0.  This collection of dummy functions lets us
+ * avoid that.  Maybe there should be a generic table of dummy serial
+ * functions?
+ */
+
+static unsigned int serial_cris_tx_empty_dummy(struct uart_port *port)
+{
+	return TIOCSER_TEMT;
+}
+
+static void serial_cris_set_mctrl_dummy(struct uart_port *port,
+					unsigned int mctrl)
+{
+}
+
+static unsigned int serial_cris_get_mctrl_dummy(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_cris_stop_tx_dummy(struct uart_port *port)
+{
+}
+
+static void serial_cris_start_tx_dummy(struct uart_port *port)
+{
+	/* Discard outbound characters. */
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	struct circ_buf *xmit = &up->port.info->xmit;
+	xmit->tail = xmit->head;
+	uart_write_wakeup(port);
+}
+
+#define serial_cris_stop_rx_dummy serial_cris_stop_tx_dummy
+
+#define serial_cris_enable_ms_dummy serial_cris_stop_tx_dummy
+
+static void serial_cris_break_ctl_dummy(struct uart_port *port, int break_state)
+{
+}
+
+static int serial_cris_startup_dummy(struct uart_port *port)
+{
+	return 0;
+}
+
+#define serial_cris_shutdown_dummy serial_cris_stop_tx_dummy
+
+static void
+serial_cris_set_termios_dummy(struct uart_port *port, struct termios *termios,
+			      struct termios *old)
+{
+}
+
+#define serial_cris_release_port_dummy serial_cris_stop_tx_dummy
+#define serial_cris_request_port_dummy serial_cris_startup_dummy
+
+static const struct uart_ops serial_cris_dummy_pops = {
+	/*
+	 * We *could* save one or two of those with different
+	 * signature by casting and knowledge of the ABI, but it's
+	 * just not worth the maintenance headache.
+	 * For the ones we don't define here, the default (usually meaning
+	 * "unimplemented") makes sense.
+	 */
+	.tx_empty = serial_cris_tx_empty_dummy,
+	.set_mctrl = serial_cris_set_mctrl_dummy,
+	.get_mctrl = serial_cris_get_mctrl_dummy,
+	.stop_tx = serial_cris_stop_tx_dummy,
+	.start_tx = serial_cris_start_tx_dummy,
+	.stop_rx = serial_cris_stop_rx_dummy,
+	.enable_ms = serial_cris_enable_ms_dummy,
+	.break_ctl = serial_cris_break_ctl_dummy,
+	.startup = serial_cris_startup_dummy,
+	.shutdown = serial_cris_shutdown_dummy,
+	.set_termios = serial_cris_set_termios_dummy,
+
+	/* This one we keep the same. */
+	.type = serial_cris_type,
+
+	.release_port = serial_cris_release_port_dummy,
+	.request_port = serial_cris_request_port_dummy,
+
+	/*
+	 * This one we keep the same too, as long as it doesn't do
+	 * anything else but to set the type.
+	 */
+	.config_port = serial_cris_config_port,
+};
+
+static void cris_serial_port_init(struct uart_port *port, int line)
+{
+	struct uart_cris_port *up = (struct uart_cris_port *)port;
+	static int first = 1;
+
+	if (up->initialized)
+		return;
+	up->initialized = 1;
+	port->line = line;
+	spin_lock_init(&port->lock);
+	port->ops =
+	    up->regi_ser == 0 ? &serial_cris_dummy_pops : &serial_cris_pops;
+	port->irq = up->irq;
+	port->iobase = up->regi_ser ? up->regi_ser : 1;
+	port->uartclk = 29493000;
+
+	/*
+	 * We can't fit any more than 255 here (unsigned char), though
+	 * actually UART_XMIT_SIZE characters could be pending output (if it
+	 * wasn't for the single test in transmit_chars_dma).  At time of this
+	 * writing, the definition of "fifosize" is here the amount of
+	 * characters that can be pending output after a start_tx call until
+	 * tx_empty returns 1: see serial_core.c:uart_wait_until_sent.  This
+	 * matters for timeout calculations unfortunately, but keeping larger
+	 * amounts at the DMA wouldn't win much so let's just play nice.
+	 */
+	port->fifosize = 255;
+	port->flags = UPF_BOOT_AUTOCONF;
+
+#ifdef CONFIG_ETRAX_RS485
+	/* Set sane defaults. */
+	up->rs485.rts_on_send = 0;
+	up->rs485.rts_after_sent = 1;
+	up->rs485.delay_rts_before_send = 0;
+	if (up->port_type > TYPE_232)
+		up->rs485.enabled = 1;
+	else
+		up->rs485.enabled = 0;
+#endif
+
+	if (first) {
+		first = 0;
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+		SETUP_PINS(0);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+		SETUP_PINS(1);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+		SETUP_PINS(2);
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+		SETUP_PINS(3);
+#endif
+	}
+}
+
+static int __init serial_cris_init(void)
+{
+	int ret, i;
+	printk(KERN_INFO "Serial: CRISv32 driver $Revision: 1.86 $ ");
+
+	ret = uart_register_driver(&serial_cris_driver);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < UART_NR; i++) {
+		if (serial_cris_ports[i].used) {
+#ifdef CONFIG_ETRAX_RS485
+			reg_ser_rw_rec_ctrl rec_ctrl;
+
+			/* Make sure that the RTS pin stays low when allocating
+			 * pins for a port in 485 mode.
+			 */
+			if (serial_cris_ports[i].port_type > TYPE_232) {
+				rec_ctrl =
+				    REG_RD(ser, serial_cris_ports[i].regi_ser,
+					   rw_rec_ctrl);
+				rec_ctrl.rts_n = regk_ser_active;
+				REG_WR(ser, serial_cris_ports[i].regi_ser,
+				       rw_rec_ctrl, rec_ctrl);
+			}
+#endif
+			switch (serial_cris_ports[i].regi_ser) {
+			case regi_ser1:
+				if (crisv32_pinmux_alloc_fixed(pinmux_ser1)) {
+					printk(KERN_ERR
+						"Failed to allocate pins for "
+						"ser1, disable port\n");
+					serial_cris_ports[i].used = 0;
+					continue;
+				}
+				break;
+			case regi_ser2:
+				if (crisv32_pinmux_alloc_fixed(pinmux_ser2)) {
+					printk(KERN_ERR
+						"Failed to allocate pins for "
+						"ser2, disable port\n");
+					serial_cris_ports[i].used = 0;
+					continue;
+				}
+				break;
+			case regi_ser3:
+				if (crisv32_pinmux_alloc_fixed(pinmux_ser3)) {
+					printk(KERN_ERR
+						"Failed to allocate pins for "
+						"ser3, disable port\n");
+					serial_cris_ports[i].used = 0;
+					continue;
+				}
+				break;
+#if CONFIG_ETRAX_SERIAL_PORTS == 5
+			case regi_ser4:
+				if (crisv32_pinmux_alloc_fixed(pinmux_ser4)) {
+					printk(KERN_ERR
+						"Failed to allocate pins for "
+						"ser4, disable port\n");
+					serial_cris_ports[i].used = 0;
+					continue;
+				}
+				break;
+#endif
+			}
+
+			struct uart_port *port = &serial_cris_ports[i].port;
+			cris_console.index = i;
+			cris_serial_port_init(port, i);
+			uart_add_one_port(&serial_cris_driver, port);
+		}
+	}
+
+out:
+	return ret;
+}
+
+static void __exit serial_cris_exit(void)
+{
+	int i;
+	for (i = 0; i < UART_NR; i++)
+		if (serial_cris_ports[i].used) {
+			switch (serial_cris_ports[i].regi_ser) {
+			case regi_ser1:
+				crisv32_pinmux_dealloc_fixed(pinmux_ser1);
+				break;
+			case regi_ser2:
+				crisv32_pinmux_dealloc_fixed(pinmux_ser2);
+				break;
+			case regi_ser3:
+				crisv32_pinmux_dealloc_fixed(pinmux_ser3);
+				break;
+#if CONFIG_ETRAX_SERIAL_PORTS == 5
+			case regi_ser4:
+				crisv32_pinmux_dealloc_fixed(pinmux_ser4);
+				break;
+#endif
+			}
+			uart_remove_one_port(&serial_cris_driver,
+					     &serial_cris_ports[i].port);
+		}
+	uart_unregister_driver(&serial_cris_driver);
+}
+
+module_init(serial_cris_init);
+module_exit(serial_cris_exit);
-- 
1.5.3.6.970.gd25430

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ