patch-2.4.22 linux-2.4.22/arch/cris/drivers/serial.c
Next file: linux-2.4.22/arch/cris/drivers/serial.h
Previous file: linux-2.4.22/arch/cris/drivers/pcf8563.c
Back to the patch index
Back to the overall index
- Lines: 2690
- Date:
2003-08-25 04:44:39.000000000 -0700
- Orig file:
linux-2.4.21/arch/cris/drivers/serial.c
- Orig date:
2003-06-13 07:51:29.000000000 -0700
diff -urN linux-2.4.21/arch/cris/drivers/serial.c linux-2.4.22/arch/cris/drivers/serial.c
@@ -1,12 +1,92 @@
-/* $Id: serial.c,v 1.44 2002/11/21 04:28:06 hp Exp $
+/* $Id: serial.c,v 1.54 2003/07/08 12:42:19 johana Exp $
*
* Serial port driver for the ETRAX 100LX chip
*
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Axis Communications AB
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Axis Communications AB
*
- * Many, many authors. Based once upon a time on serial.c for 16x50.
+ * Many, many authors. Based once upon a time on serial.c for 16x50.
*
* $Log: serial.c,v $
+ * Revision 1.54 2003/07/08 12:42:19 johana
+ * Removed some test defines within #if 0.
+ * Moved a comment to correct place.
+ *
+ * Revision 1.53 2003/07/08 09:48:28 starvik
+ * Removed test code
+ *
+ * Revision 1.52 2003/07/08 07:24:47 pkj
+ * Corrected spelling mistakes originally found in 2.5.x
+ *
+ * Revision 1.51 2003/07/07 09:17:12 johana
+ * Config.in:
+ * Don't consider ETRAX_FAST_TIMER experimental.
+ * Add CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+ * Add CONFIG_ETRAX_EXTERN_PB6CLK_FREQ
+ * Add CONFIG_ETRAX_SERIAL_PORTx_DMAy_OUT/IN
+ *
+ * serial.h:
+ * Support for with and without DMA.
+ * Added baud_base and custom_divisor for flexible baudrate.
+ * Added flush_time_usec.
+ * Removed unused DMA register pointers.
+ *
+ * serial.c:
+ * Use C99 style initialisation of rs_table, some fields added, some removed.
+ * Always disable interrupts when fiddling with shadow variables.
+ * Support for flexible baudrate using prescaler or external clock.
+ * Only disable the DMA channel if we own it.
+ * Support for either using DMA or char-by-char interrupt.
+ *
+ * DMA changes:
+ * o When transmitting with DMA, don't put all in one descriptor, split
+ * it so that we can wake up the application before everything is sent.
+ * o When starting a fast timer to flip later, use a longer, fixed time.
+ * o Use flush_time_usec and have a minimum value to prevent excessive load
+ * on high baudrates.
+ * o Do force_eop_if_needed() after processing to reduce load.
+ * o All DMA interrupts uses SA_SHIRQ | SA_INTERRUPT
+ *
+ * Only have interrupts disabled while processing rx interrupts,
+ * unmask just tx interrupts and unblock serial interrupt and keep
+ * global interrupts enabled while processing tx interrupts to make
+ * char-by-char interrupts usable.
+ *
+ * Flow control:
+ * o Use normal tx interrupt when sending x_char.
+ * o Use throttling within the driver when using DMA.
+ *
+ * Improved debugging with DEBUG_LOG macro.
+ *
+ * Revision 1.50 2003/06/13 10:04:12 johana
+ * Help the user to avoid trouble by:
+ * Preventing FAST_TIMER and FAST_DMA_FLUSH at the same time.
+ * Forcing mixed mode for status/control lines if not all pins are used.
+ *
+ * Som minor changes to reduce diff to os/lx25 version.
+ *
+ * Revision 1.49 2003/05/30 11:31:54 johana
+ * Merged in change-branch--serial9bit that adds CMSPAR support for sticky
+ * parity (mark/space)
+ *
+ * Revision 1.48 2003/05/30 11:03:57 johana
+ * Implemented rs_send_xchar() by disabling the DMA and writing manually.
+ * Added e100_disable_txdma_channel() and e100_enable_txdma_channel().
+ * Fixed rs_throttle() and rs_unthrottle() to properly call rs_send_xchar
+ * instead of setting info->x_char and check the CRTSCTS flag before
+ * controlling the rts pin.
+ *
+ * Revision 1.47 2003/04/09 08:31:14 pkj
+ * Typo correction (taken from Linux 2.5).
+ *
+ * Revision 1.46 2003/04/01 14:12:06 starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.45 2003/01/22 06:54:46 starvik
+ * Fixed warnings issued by GCC 3.2.1
+ *
+ * Revision 1.44.4.1 2003/01/08 06:47:12 starvik
+ * Added mark/space parity (for 9-bit serial)
+ *
* Revision 1.44 2002/11/21 04:28:06 hp
* Change static inline to extern inline where otherwise outlined with gcc-3.2
*
@@ -206,7 +286,7 @@
* Items worth noticing:
*
* No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now)
- * RS485 is not ported (why cant it be done in userspace as on x86 ?)
+ * RS485 is not ported (why can't it be done in userspace as on x86 ?)
* Statistics done through async_icount - if any more stats are needed,
* that's the place to put them or in an arch-dep version of it.
* timeout_interrupt and the other fast timeout stuff not ported yet
@@ -357,7 +437,7 @@
*
*/
-static char *serial_version = "$Revision: 1.44 $";
+static char *serial_version = "$Revision: 1.54 $";
#include <linux/config.h>
#include <linux/version.h>
@@ -392,7 +472,7 @@
#include <asm/svinto.h>
-/* non-arch dependant serial structures are in linux/serial.h */
+/* 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"
@@ -406,6 +486,11 @@
#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER"
#endif
#endif
+
+#if defined(CONFIG_ETRAX_FAST_TIMER) && defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)
+#error "Disable either ETRAX_SERIAL_FLUSH_DMA_FAST or ETRAX_FAST_TIMER"
+#endif
+
/*
* All of the compatibilty code so we can compile serial.c against
* older kernels is hidden in serial_compat.h
@@ -414,8 +499,11 @@
#include "serial_compat.h"
#endif
+#if 1 /* Set to 0 to avoid inlining - easier to see how size of parts */
#define _INLINE_ inline
-
+#else
+#define _INLINE_
+#endif
static DECLARE_TASK_QUEUE(tq_serial);
struct tty_driver serial_driver, callout_driver;
@@ -436,7 +524,7 @@
//#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 SERIAL_DEBUG_LINE 3 /* What serport we want to debug */
/* Enable this to use serial interrupts to handle when you
expect the first received event on the serial port to
@@ -444,14 +532,78 @@
from eLinux */
#define SERIAL_HANDLE_EARLY_ERRORS
-#define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
+/* Defined and used in n_tty.c, but we need it here as well */
+#define TTY_THRESHOLD_THROTTLE 128
+/* Due to buffersizes and threshold values, our SERIAL_DESCR_BUF_SIZE
+ * must not be to high or flow control won't work if we leave it to the tty
+ * layer so we have our own throttling in flush_to_flip
+ * TTY_FLIPBUF_SIZE=512,
+ * TTY_THRESHOLD_THROTTLE/UNTHROTTLE=128
+ * BUF_SIZE can't be > 128
+ */
+/* Currently 16 descriptors x 128 bytes = 2048 bytes */
#define SERIAL_DESCR_BUF_SIZE 256
+#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */
+#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE
+
+/* We don't want to load the system with massive fast timer interrupt
+ * on high baudrates so limit it to 250 us (4kHz) */
+#define MIN_FLUSH_TIME_USEC 250
+
/* Add an x here to log a lot of timer stuff */
#define TIMERD(x)
+/* Debug details of interrupt handling */
+#define DINTR1(x) /* irq on/off, errors */
+#define DINTR2(x) /* tx and rx */
+/* Debug flip buffer stuff */
+#define DFLIP(x)
+/* Debug flow control and overview of data flow */
+#define DFLOW(x)
+#define DBAUD(x)
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+#define DLOG_INT_TRIG(x)
+#else
+#define DLOG_INT_TRIG(x)
+#endif
+//#define DEBUG_LOG_INCLUDED
+#ifndef DEBUG_LOG_INCLUDED
#define DEBUG_LOG(line, string, value)
+#else
+struct debug_log_info
+{
+ unsigned long time;
+ unsigned long timer_data;
+// int line;
+ const char *string;
+ int value;
+};
+#define DEBUG_LOG_SIZE 4096
+
+struct debug_log_info debug_log[DEBUG_LOG_SIZE];
+int debug_log_pos = 0;
+
+#define DEBUG_LOG(_line, _string, _value) do { \
+ if ((_line) == SERIAL_DEBUG_LINE) {\
+ debug_log_func(_line, _string, _value); \
+ }\
+}while(0)
+
+void debug_log_func(int line, const char *string, int value)
+{
+ if (debug_log_pos < DEBUG_LOG_SIZE) {
+ debug_log[debug_log_pos].time = jiffies;
+ debug_log[debug_log_pos].timer_data = *R_TIMER_DATA;
+// debug_log[debug_log_pos].line = line;
+ debug_log[debug_log_pos].string = string;
+ debug_log[debug_log_pos].value = value;
+ debug_log_pos++;
+ }
+ /*printk(string, value);*/
+}
+#endif
#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
/* Default number of timer ticks before flushing rx fifo
@@ -461,21 +613,23 @@
#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
#endif
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
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,
const unsigned char *buf, int count);
-extern inline int raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int rs_raw_write(struct tty_struct * tty, int from_user,
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);
#endif
-static int
-get_lsr_info(struct e100_serial * info, unsigned int *value);
+static int get_lsr_info(struct e100_serial * info, unsigned int *value);
-#define DEF_BAUD 0x99 /* 115.2 kbit/s */
+#define DEF_BAUD 115200 /* 115.2 kbit/s */
#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */
/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
@@ -484,6 +638,7 @@
/* offsets from R_SERIALx_CTRL */
#define REG_DATA 0
+#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */
#define REG_TR_DATA 0
#define REG_STATUS 1
#define REG_TR_CTRL 1
@@ -519,60 +674,162 @@
*/
+/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */
+static const unsigned long e100_ser_int_mask = 0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready)
+#endif
+;
+unsigned long r_alt_ser_baudrate_shadow = 0;
+
/* this is the data for the four serial ports in the etrax100 */
/* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
static struct e100_serial rs_table[] = {
- { DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
- R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
- R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
- R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
- R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
- STD_FLAGS, DEF_RX, DEF_TX, 2,
+ { .baud = DEF_BAUD,
+ .port = (unsigned char *)R_SERIAL0_CTRL,
+ .irq = 1U << 12, /* uses DMA 6 and 7 */
+ .oclrintradr = R_DMA_CH6_CLR_INTR,
+ .ofirstadr = R_DMA_CH6_FIRST,
+ .ocmdadr = R_DMA_CH6_CMD,
+ .ostatusadr = R_DMA_CH6_STATUS,
+ .iclrintradr = R_DMA_CH7_CLR_INTR,
+ .ifirstadr = R_DMA_CH7_FIRST,
+ .icmdadr = R_DMA_CH7_CMD,
+ .idescradr = R_DMA_CH7_DESCR,
+ .flags = STD_FLAGS,
+ .rx_ctrl = DEF_RX,
+ .tx_ctrl = DEF_TX,
+ .iseteop = 2,
#ifdef CONFIG_ETRAX_SERIAL_PORT0
- 1
+ .enabled = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+ .dma_out_enabled = 1,
+#else
+ .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+ .dma_in_enabled = 1,
+#else
+ .dma_in_enabled = 0
+#endif
#else
- 0
+ .enabled = 0,
+ .dma_out_enabled = 0,
+ .dma_in_enabled = 0
#endif
+
}, /* ttyS0 */
#ifndef CONFIG_SVINTO_SIM
- { DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
- R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
- R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
- R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
- R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
- STD_FLAGS, DEF_RX, DEF_TX, 3 ,
+ { .baud = DEF_BAUD,
+ .port = (unsigned char *)R_SERIAL1_CTRL,
+ .irq = 1U << 16, /* uses DMA 8 and 9 */
+ .oclrintradr = R_DMA_CH8_CLR_INTR,
+ .ofirstadr = R_DMA_CH8_FIRST,
+ .ocmdadr = R_DMA_CH8_CMD,
+ .ostatusadr = R_DMA_CH8_STATUS,
+ .iclrintradr = R_DMA_CH9_CLR_INTR,
+ .ifirstadr = R_DMA_CH9_FIRST,
+ .icmdadr = R_DMA_CH9_CMD,
+ .idescradr = R_DMA_CH9_DESCR,
+ .flags = STD_FLAGS,
+ .rx_ctrl = DEF_RX,
+ .tx_ctrl = DEF_TX,
+ .iseteop = 3,
#ifdef CONFIG_ETRAX_SERIAL_PORT1
- 1
+ .enabled = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+ .dma_out_enabled = 1,
+#else
+ .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+ .dma_in_enabled = 1,
#else
- 0
+ .dma_in_enabled = 0
+#endif
+#else
+ .enabled = 0,
+ .dma_out_enabled = 0,
+ .dma_in_enabled = 0
#endif
}, /* ttyS1 */
- { DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4, /* uses DMA 2 and 3 */
- R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
- R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
- R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
- R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
- STD_FLAGS, DEF_RX, DEF_TX, 0,
+ { .baud = DEF_BAUD,
+ .port = (unsigned char *)R_SERIAL2_CTRL,
+ .irq = 1U << 4, /* uses DMA 2 and 3 */
+ .oclrintradr = R_DMA_CH2_CLR_INTR,
+ .ofirstadr = R_DMA_CH2_FIRST,
+ .ocmdadr = R_DMA_CH2_CMD,
+ .ostatusadr = R_DMA_CH2_STATUS,
+ .iclrintradr = R_DMA_CH3_CLR_INTR,
+ .ifirstadr = R_DMA_CH3_FIRST,
+ .icmdadr = R_DMA_CH3_CMD,
+ .idescradr = R_DMA_CH3_DESCR,
+ .flags = STD_FLAGS,
+ .rx_ctrl = DEF_RX,
+ .tx_ctrl = DEF_TX,
+ .iseteop = 0,
#ifdef CONFIG_ETRAX_SERIAL_PORT2
- 1
+ .enabled = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+ .dma_out_enabled = 1,
+#else
+ .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+ .dma_in_enabled = 1,
#else
- 0
+ .dma_in_enabled = 0
+#endif
+#else
+ .enabled = 0,
+ .dma_out_enabled = 0,
+ .dma_in_enabled = 0
#endif
}, /* ttyS2 */
- { DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8, /* uses DMA 4 and 5 */
- R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
- R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
- R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
- R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
- STD_FLAGS, DEF_RX, DEF_TX, 1,
+ { .baud = DEF_BAUD,
+ .port = (unsigned char *)R_SERIAL3_CTRL,
+ .irq = 1U << 8, /* uses DMA 4 and 5 */
+ .oclrintradr = R_DMA_CH4_CLR_INTR,
+ .ofirstadr = R_DMA_CH4_FIRST,
+ .ocmdadr = R_DMA_CH4_CMD,
+ .ostatusadr = R_DMA_CH4_STATUS,
+ .iclrintradr = R_DMA_CH5_CLR_INTR,
+ .ifirstadr = R_DMA_CH5_FIRST,
+ .icmdadr = R_DMA_CH5_CMD,
+ .idescradr = R_DMA_CH5_DESCR,
+ .flags = STD_FLAGS,
+ .rx_ctrl = DEF_RX,
+ .tx_ctrl = DEF_TX,
+ .iseteop = 1,
#ifdef CONFIG_ETRAX_SERIAL_PORT3
- 1
+ .enabled = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+ .dma_out_enabled = 1,
+#else
+ .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+ .dma_in_enabled = 1,
#else
- 0
+ .dma_in_enabled = 0
+#endif
+#else
+ .enabled = 0,
+ .dma_out_enabled = 0,
+ .dma_in_enabled = 0
#endif
} /* ttyS3 */
#endif
@@ -627,7 +884,7 @@
((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
(R_PORT_PA_DATA): ( \
(CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
- (R_PORT_PB_DATA):&dummy_ser[##line##]))
+ (R_PORT_PB_DATA):&dummy_ser[line]))
#define E100_STRUCT_SHADOW(line, pinname) \
((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
@@ -646,6 +903,225 @@
#define DUMMY_CD_MASK 8
static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF};
+/* If not all status pins are used or disabled, use mixed mode */
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+
+#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT)
+
+#if SER0_PA_BITSUM != -4
+# if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT)
+
+#if SER0_PB_BITSUM != -4
+# if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#endif /* PORT0 */
+
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+
+#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT)
+
+#if SER1_PA_BITSUM != -4
+# if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT)
+
+#if SER1_PB_BITSUM != -4
+# if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#endif /* PORT1 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+
+#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT)
+
+#if SER2_PA_BITSUM != -4
+# if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT)
+
+#if SER2_PB_BITSUM != -4
+# if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#endif /* PORT2 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+
+#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT)
+
+#if SER3_PA_BITSUM != -4
+# if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT)
+
+#if SER3_PB_BITSUM != -4
+# if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+# if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1
+# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+# endif
+# endif
+#endif
+
+#endif /* PORT3 */
+
+
#if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \
defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \
defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \
@@ -653,6 +1129,7 @@
#define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
#endif
+
#ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
/* The pins can be mixed on PA and PB */
#define CONTROL_PINS_PORT_NOT_USED(line) \
@@ -838,7 +1315,6 @@
#define E100_RTS_MASK 0x20
#define E100_CTS_MASK 0x40
-
/* 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
@@ -937,6 +1413,9 @@
/* calc timeout */
info->char_time_usec = ((bits * 1000000) / info->baud) + 1;
+ info->flush_time_usec = 4*info->char_time_usec;
+ if (info->flush_time_usec < MIN_FLUSH_TIME_USEC)
+ info->flush_time_usec = MIN_FLUSH_TIME_USEC;
}
/*
@@ -980,7 +1459,7 @@
retval = baud_table[cflag & CBAUD];
if (retval < 0) {
- printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+ printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag);
retval = 5; /* choose default 9600 instead */
}
@@ -1036,9 +1515,13 @@
e100_rts(struct e100_serial *info, int set)
{
#ifndef CONFIG_SVINTO_SIM
+ unsigned long flags;
+ save_flags(flags);
+ cli();
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);
#ifdef SERIAL_DEBUG_IO
printk("ser%i rts %i\n", info->line, set);
#endif
@@ -1112,6 +1595,7 @@
#ifdef SERIAL_DEBUG_INTR
printk("rxdma_irq(%d): 0\n",info->line);
#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line));
*R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
}
@@ -1121,6 +1605,7 @@
#ifdef SERIAL_DEBUG_INTR
printk("rxdma_irq(%d): 1\n",info->line);
#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line));
*R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
}
@@ -1132,6 +1617,7 @@
#ifdef SERIAL_DEBUG_INTR
printk("txdma_irq(%d): 0\n",info->line);
#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line));
*R_IRQ_MASK2_CLR = info->irq;
}
@@ -1141,9 +1627,143 @@
#ifdef SERIAL_DEBUG_INTR
printk("txdma_irq(%d): 1\n",info->line);
#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line));
*R_IRQ_MASK2_SET = info->irq;
}
+static _INLINE_ void
+e100_disable_txdma_channel(struct e100_serial *info)
+{
+ unsigned long flags;
+
+ /* Disable output DMA channel for the serial port in question
+ * ( set to something other then serialX)
+ */
+ save_flags(flags);
+ cli();
+ 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)) ==
+ IO_STATE(R_GEN_CONFIG, dma6, serial0)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+ }
+ } else if (info->line == 1) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) ==
+ IO_STATE(R_GEN_CONFIG, dma8, serial1)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+ }
+ } else if (info->line == 2) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) ==
+ IO_STATE(R_GEN_CONFIG, dma2, serial2)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+ }
+ } else if (info->line == 3) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) ==
+ IO_STATE(R_GEN_CONFIG, dma4, serial3)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
+ }
+ }
+ *R_GEN_CONFIG = genconfig_shadow;
+ restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_txdma_channel(struct e100_serial *info)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ 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) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0);
+ } else if (info->line == 1) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1);
+ } else if (info->line == 2) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2);
+ } else if (info->line == 3) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
+ }
+ *R_GEN_CONFIG = genconfig_shadow;
+ restore_flags(flags);
+}
+
+static _INLINE_ void
+e100_disable_rxdma_channel(struct e100_serial *info)
+{
+ unsigned long flags;
+
+ /* Disable input DMA channel for the serial port in question
+ * ( set to something other then serialX)
+ */
+ save_flags(flags);
+ cli();
+ if (info->line == 0) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
+ IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused);
+ }
+ } else if (info->line == 1) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) ==
+ IO_STATE(R_GEN_CONFIG, dma9, serial1)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb);
+ }
+ } else if (info->line == 2) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) ==
+ IO_STATE(R_GEN_CONFIG, dma3, serial2)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
+ }
+ } else if (info->line == 3) {
+ if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) ==
+ IO_STATE(R_GEN_CONFIG, dma5, serial3)) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
+ }
+ }
+ *R_GEN_CONFIG = genconfig_shadow;
+ restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_rxdma_channel(struct e100_serial *info)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /* Enable input DMA channel for the serial port in question */
+ if (info->line == 0) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0);
+ } else if (info->line == 1) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1);
+ } else if (info->line == 2) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2);
+ } else if (info->line == 3) {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
+ }
+ *R_GEN_CONFIG = genconfig_shadow;
+ restore_flags(flags);
+}
+
+
#ifdef SERIAL_HANDLE_EARLY_ERRORS
/* in order to detect and fix errors on the first byte
we have to use the serial interrupts as well. */
@@ -1154,6 +1774,7 @@
#ifdef SERIAL_DEBUG_INTR
printk("ser_irq(%d): 0\n",info->line);
#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line));
*R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
}
@@ -1166,10 +1787,50 @@
(8+2*info->line),
(1U << (8+2*info->line)));
#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line));
*R_IRQ_MASK1_SET = (1U << (8+2*info->line));
}
#endif
+static inline void
+e100_disable_serial_tx_ready_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_tx_irq(%d): 0\n",info->line);
+#endif
+ DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line));
+ *R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line));
+}
+
+static inline void
+e100_enable_serial_tx_ready_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_tx_irq(%d): 1\n",info->line);
+ printk("**** %d = %d\n",
+ (8+1+2*info->line),
+ (1U << (8+1+2*info->line)));
+#endif
+ DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line));
+ *R_IRQ_MASK1_SET = (1U << (8+1+2*info->line));
+}
+
+static inline void e100_enable_rx_irq(struct e100_serial *info)
+{
+ if (info->uses_dma_in)
+ e100_enable_rxdma_irq(info);
+ else
+ e100_enable_serial_data_irq(info);
+}
+static inline void e100_disable_rx_irq(struct e100_serial *info)
+{
+ if (info->uses_dma_in)
+ e100_disable_rxdma_irq(info);
+ else
+ e100_disable_serial_data_irq(info);
+}
+
+
#if defined(CONFIG_ETRAX_RS485)
/* Enable RS-485 mode on selected port. This is UGLY. */
static int
@@ -1183,7 +1844,10 @@
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;
+ if (r->delay_rts_before_send >= 1000)
+ info->rs485.delay_rts_before_send = 1000;
+ else
+ info->rs485.delay_rts_before_send = r->delay_rts_before_send;
info->rs485.enabled = r->enabled;
/* printk("rts: on send = %i, after = %i, enabled = %i",
info->rs485.rts_on_send,
@@ -1223,7 +1887,7 @@
e100_rts(info, info->rs485.rts_after_sent);
#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
e100_enable_rx(info);
- e100_enable_rxdma_irq(info);
+ e100_enable_rx_irq(info);
#endif
}
#endif
@@ -1245,8 +1909,12 @@
if (info) {
unsigned long flags;
unsigned long xoff;
-
+
save_flags(flags); cli();
+ DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
+ CIRC_CNT(info->xmit.head,
+ info->xmit.tail,SERIAL_XMIT_SIZE)));
+
xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
if (tty->termios->c_iflag & IXON ) {
@@ -1267,6 +1935,9 @@
unsigned long xoff;
save_flags(flags); cli();
+ DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
+ CIRC_CNT(info->xmit.head,
+ info->xmit.tail,SERIAL_XMIT_SIZE)));
xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
if (tty->termios->c_iflag & IXON ) {
@@ -1274,6 +1945,9 @@
}
*((unsigned long *)&info->port[REG_XOFF]) = xoff;
+ if (!info->uses_dma_out &&
+ info->xmit.head != info->xmit.tail && info->xmit.buf)
+ e100_enable_serial_tx_ready_irq(info);
restore_flags(flags);
}
@@ -1308,6 +1982,8 @@
rs_sched_event(struct e100_serial *info,
int event)
{
+ if (info->event & (1 << event))
+ return;
info->event |= 1 << event;
queue_task(&info->tqueue, &tq_serial);
mark_bh(SERIAL_BH);
@@ -1325,7 +2001,7 @@
*/
static void
-transmit_chars(struct e100_serial *info)
+transmit_chars_dma(struct e100_serial *info)
{
unsigned int c, sentl;
struct etrax_dma_descr *descr;
@@ -1333,11 +2009,11 @@
#ifdef CONFIG_SVINTO_SIM
/* This will output too little if tail is not 0 always since
* we don't reloop to send the other part. Anyway this SHOULD be a
- * no-op - transmit_chars would never really be called during sim
+ * no-op - transmit_chars_dma would never really be called during sim
* since rs_write does not write into the xmit buffer then.
*/
if (info->xmit.tail)
- printk("Error in serial.c:transmit_chars(), tail!=0\n");
+ printk("Error in serial.c:transmit_chars_dma(), tail!=0\n");
if (info->xmit.head != info->xmit.tail) {
SIMCOUT(info->xmit.buf + info->xmit.tail,
CIRC_CNT(info->xmit.head,
@@ -1359,7 +2035,7 @@
#endif
if (!info->tr_running) {
/* weirdo... we shouldn't get here! */
- printk("Achtung: transmit_chars with !tr_running\n");
+ printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n");
return;
}
@@ -1375,6 +2051,8 @@
/* otherwise we find the amount of data sent here */
sentl = descr->hw_len;
+ DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl));
+
/* update stats */
info->icount.tx += sentl;
@@ -1392,6 +2070,13 @@
c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+ /* Don't send all in one DMA transfer - divide it so we wake up
+ * application before all is sent
+ */
+
+ if (c >= 4*WAKEUP_CHARS)
+ c = c/2;
+
if (c <= 0) {
/* our job here is done, don't schedule any new DMA transfer */
info->tr_running = 0;
@@ -1411,17 +2096,17 @@
/* ok we can schedule a dma send of c chars starting at info->xmit.tail */
/* set up the descriptor correctly for output */
-
+ DFLOW(DEBUG_LOG(info->line, "TX %i\n", c));
descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
descr->sw_len = c;
descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
descr->status = 0;
*info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
- *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */
+ *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); /* dma command start -> R_DMAx_CMD */
/* DMA is now running (hopefully) */
-} /* transmit_chars */
+} /* transmit_chars_dma */
static void
start_transmit(struct e100_serial *info)
@@ -1435,15 +2120,17 @@
info->tr_descr.hw_len = 0;
info->tr_descr.status = 0;
info->tr_running = 1;
-
- transmit_chars(info);
+ if (info->uses_dma_out)
+ transmit_chars_dma(info);
+ else
+ e100_enable_serial_tx_ready_irq(info);
} /* start_transmit */
#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
static int serial_fast_timer_started = 0;
static int serial_fast_timer_expired = 0;
static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
unsigned long timer_flags; \
save_flags(timer_flags); \
cli(); \
@@ -1454,7 +2141,7 @@
start_one_shot_timer(&fast_timers[info->line], \
flush_timeout_function, \
(unsigned long)info, \
- info->char_time_usec*4, \
+ (usec), \
string); \
} \
else { \
@@ -1462,8 +2149,10 @@
} \
restore_flags(timer_flags); \
}
+#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
#else
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec)
#define START_FLUSH_FAST_TIMER(info, string)
#endif
@@ -1508,18 +2197,27 @@
add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
{
struct etrax_recv_buffer *buffer;
+ if (info->uses_dma_in) {
+ if (!(buffer = alloc_recv_buffer(4)))
+ return 0;
- if (!(buffer = alloc_recv_buffer(4)))
- return 0;
-
- buffer->length = 1;
- buffer->error = flag;
- buffer->buffer[0] = data;
+ buffer->length = 1;
+ buffer->error = flag;
+ buffer->buffer[0] = data;
- append_recv_buffer(info, buffer);
-
- info->icount.rx++;
+ append_recv_buffer(info, buffer);
+ info->icount.rx++;
+ } else {
+ struct tty_struct *tty = info->tty;
+ *tty->flip.char_buf_ptr = data;
+ *tty->flip.flag_buf_ptr = flag;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ info->icount.rx++;
+ }
+
return 1;
}
@@ -1529,7 +2227,8 @@
struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
if (info->recv_cnt + recvl > 65536) {
- printk(__FUNCTION__ ": Too much pending incoming serial data! Dropping %u bytes.\n", recvl);
+ printk(KERN_CRIT
+ "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl);
return 0;
}
@@ -1542,7 +2241,7 @@
append_recv_buffer(info, buffer);
if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
- panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+ panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
descr->buf = virt_to_phys(buffer->buffer);
@@ -1579,8 +2278,16 @@
/* Reset the status information */
descr->status = 0;
- DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
+ DFLOW( DEBUG_LOG(info->line, "RX %lu\n", recvl);
+ if (info->tty->stopped) {
+ unsigned char *buf = phys_to_virt(descr->buf);
+ DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]);
+ DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]);
+ DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]);
+ }
+ );
+
+
/* update stats */
info->icount.rx += recvl;
@@ -1591,7 +2298,7 @@
}
static _INLINE_ void
-receive_chars(struct e100_serial *info)
+receive_chars_dma(struct e100_serial *info)
{
struct tty_struct *tty;
unsigned char rstat;
@@ -1623,7 +2330,10 @@
/* Read the status register to detect errors */
rstat = info->port[REG_STATUS];
-
+ if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+ DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat));
+ }
+
if (rstat & SER_ERROR_MASK) {
/* If we got an error, we must reset it by reading the
* data_in field
@@ -1658,7 +2368,7 @@
/* Set up the receiving descriptors */
for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
- panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+ panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
descr[i].ctrl = d_int;
descr[i].buf = virt_to_phys(buffer->buffer);
@@ -1691,20 +2401,20 @@
*/
return;
#endif
+ info->tty->flip.count = 0;
+ if (info->uses_dma_in) {
+ /* reset the input dma channel to be sure it works */
+
+ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+ IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
- /* reset the input dma channel to be sure it works */
-
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
- info->tty->flip.count = 0;
-
- start_recv_dma(info);
+ start_recv_dma(info);
#ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
- start_flush_timer();
+ start_flush_timer();
#endif
+ }
}
@@ -1749,25 +2459,25 @@
for (i = 0; i < NR_PORTS; i++) {
info = rs_table + i;
- if (!info->enabled || !info->uses_dma)
+ if (!info->enabled || !info->uses_dma_out)
continue;
/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
if (ireg & info->irq) {
/* we can send a new dma bunch. make it so. */
- DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+ DINTR2(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(info);
+ transmit_chars_dma(info);
}
/* FIXME: here we should really check for a change in the
status lines and if so call status_handle(info) */
}
-}
+} /* tr_interrupt */
/* dma input channel interrupt handler */
@@ -1795,18 +2505,18 @@
for (i = 0; i < NR_PORTS; i++) {
info = rs_table + i;
- if (!info->enabled || !info->uses_dma)
+ if (!info->enabled || !info->uses_dma_in)
continue;
/* check for both dma_eop and dma_descr for the input dma channel */
if (ireg & ((info->irq << 2) | (info->irq << 3))) {
/* we have received something */
- receive_chars(info);
+ receive_chars_dma(info);
}
/* FIXME: here we should really check for a change in the
status lines and if so call status_handle(info) */
}
-}
+} /* rec_interrupt */
static _INLINE_ int
force_eop_if_needed(struct e100_serial *info)
@@ -1846,7 +2556,7 @@
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);
+ TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line));
FORCE_EOP(info);
}
@@ -1860,7 +2570,8 @@
struct etrax_recv_buffer *buffer;
unsigned int length;
unsigned long flags;
-
+ int max_flip_size;
+
if (!info->first_recv_buffer)
return;
@@ -1873,12 +2584,46 @@
}
length = tty->flip.count;
+ /* Don't flip more than the ldisc has room for.
+ * The return value from ldisc.receive_room(tty) - might not be up to
+ * date, the previous flip of up to TTY_FLIPBUF_SIZE might be on the
+ * processed and not accounted for yet.
+ * Since we use DMA, 1 SERIAL_DESCR_BUF_SIZE could be on the way.
+ * Lets buffer data here and let flow control take care of it.
+ * Since we normally flip large chunks, the ldisc don't react
+ * with throttle until too late if we flip to much.
+ */
+ max_flip_size = tty->ldisc.receive_room(tty);
+ if (max_flip_size < 0)
+ max_flip_size = 0;
+ if (max_flip_size <= (TTY_FLIPBUF_SIZE + /* Maybe not accounted for */
+ length + info->recv_cnt + /* We have this queued */
+ 2*SERIAL_DESCR_BUF_SIZE + /* This could be on the way */
+ TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+ /* check TTY_THROTTLED first so it indicates our state */
+ if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+ DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles room %lu\n", max_flip_size));
+ rs_throttle(tty);
+ }
+#if 0
+ else if (max_flip_size <= (TTY_FLIPBUF_SIZE + /* Maybe not accounted for */
+ length + info->recv_cnt + /* We have this queued */
+ SERIAL_DESCR_BUF_SIZE + /* This could be on the way */
+ TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+ DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles again! %lu\n", max_flip_size));
+ rs_throttle(tty);
+ }
+#endif
+ }
- while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) {
+ if (max_flip_size > TTY_FLIPBUF_SIZE)
+ max_flip_size = TTY_FLIPBUF_SIZE;
+
+ while ((buffer = info->first_recv_buffer) && length < max_flip_size) {
unsigned int count = buffer->length;
- if (length + count > TTY_FLIPBUF_SIZE)
- count = TTY_FLIPBUF_SIZE - length;
+ if (length + count > max_flip_size)
+ count = max_flip_size - length;
memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count);
memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count);
@@ -1886,7 +2631,8 @@
length += count;
info->recv_cnt -= count;
-
+ DFLIP(DEBUG_LOG(info->line,"flip: %i\n", length));
+
if (count == buffer->length) {
info->first_recv_buffer = buffer->next;
kfree(buffer);
@@ -1901,10 +2647,31 @@
info->last_recv_buffer = NULL;
tty->flip.count = length;
-
+ DFLIP(if (tty->ldisc.chars_in_buffer(tty) > 3500) {
+ DEBUG_LOG(info->line, "ldisc %lu\n",
+ tty->ldisc.chars_in_buffer(tty));
+ DEBUG_LOG(info->line, "flip.count %lu\n",
+ tty->flip.count);
+ }
+ );
restore_flags(flags);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+ DFLIP(
+ if (1) {
+
+ if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+ DEBUG_LOG(info->line, "*** TTY_DONT_FLIP set flip.count %i ***\n", tty->flip.count);
+ DEBUG_LOG(info->line, "*** recv_cnt %i\n", info->recv_cnt);
+ } else {
+ }
+ DEBUG_LOG(info->line, "*** rxtot %i\n", info->icount.rx);
+ DEBUG_LOG(info->line, "ldisc %lu\n", tty->ldisc.chars_in_buffer(tty));
+ DEBUG_LOG(info->line, "room %lu\n", tty->ldisc.receive_room(tty));
+ }
+
+ );
+
/* this includes a check for low-latency */
tty_flip_buffer_push(tty);
#else
@@ -1915,12 +2682,19 @@
static _INLINE_ void
check_flush_timeout(struct e100_serial *info)
{
- force_eop_if_needed(info);
-
+ /* Flip what we've got (if we can) */
flush_to_flip_buffer(info);
- if (info->first_recv_buffer)
- START_FLUSH_FAST_TIMER(info, "flip");
+ /* We might need to flip later, but not to fast
+ * since the system is busy processing input... */
+ if (info->first_recv_buffer)
+ START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000);
+
+ /* Force eop last, since data might have come while we're processing
+ * and if we started the slow timer above, we won't start a fast
+ * below.
+ */
+ force_eop_if_needed(info);
}
#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
@@ -1960,7 +2734,7 @@
for (i = 0; i < NR_PORTS; i++) {
info = rs_table + i;
- if (info->enabled && info->uses_dma)
+ if (info->enabled && info->uses_dma_in)
check_flush_timeout(info);
}
} /* timeout_interrupt */
@@ -1988,7 +2762,7 @@
for (i = 0; i < NR_PORTS; i++) {
info = rs_table + i;
- if (info->uses_dma)
+ if (info->uses_dma_in)
check_flush_timeout(info);
}
@@ -2017,7 +2791,7 @@
B= Break character (0x00) with framing error.
E= Error byte with parity error received after B characters.
-F= "Faked" valid byte received immediatly after B characters.
+F= "Faked" valid byte received immediately after B characters.
V= Valid byte
1.
@@ -2054,7 +2828,7 @@
To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp
of the last faulty char (B) and compares it with the current time:
-If the time elapsed time is less then 2*char_time_usec we will assume
+If the time elapsed time is less than 2*char_time_usec we will assume
it's a faked F char and not a Valid char and set
info->errorcode = ERRCODE_SET_BREAK.
@@ -2066,15 +2840,159 @@
TODO: The break will be delayed until an F or V character is received.
*/
+
+extern _INLINE_
+struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
+{
+ unsigned long data_read;
+ struct tty_struct *tty = info->tty;
+
+ if (!tty) {
+ printk("!NO TTY!\n");
+ return info;
+ }
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE - TTY_THRESHOLD_THROTTLE) {
+ /* check TTY_THROTTLED first so it indicates our state */
+ if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+ DFLOW(DEBUG_LOG(info->line, "rs_throttle flip.count: %i\n", tty->flip.count));
+ rs_throttle(tty);
+ }
+ }
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ DEBUG_LOG(info->line, "force FLIP! %i\n", tty->flip.count);
+ tty->flip.tqueue.routine((void *) tty);
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ DEBUG_LOG(info->line, "FLIP FULL! %i\n", tty->flip.count);
+ return info; /* if TTY_DONT_FLIP is set */
+ }
+ }
+ /* Read data and status at the same time */
+ data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+more_data:
+ if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) {
+ DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+ }
+ DINTR2(DEBUG_LOG(info->line, "ser_rx %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)));
+
+ if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) |
+ IO_MASK(R_SERIAL0_READ, par_err) |
+ IO_MASK(R_SERIAL0_READ, overrun) )) {
+ /* An error */
+ info->last_rx_active_usec = GET_JIFFIES_USEC();
+ info->last_rx_active = jiffies;
+ DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read));
+ DLOG_INT_TRIG(
+ if (!log_int_trig1_pos) {
+ log_int_trig1_pos = log_int_pos;
+ log_int(rdpc(), 0, 0);
+ }
+ );
+
+
+ if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) &&
+ (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) {
+ /* Most likely a break, but we get interrupts over and
+ * over again.
+ */
-extern void _INLINE_ handle_ser_interrupt(struct e100_serial *info)
+ if (!info->break_detected_cnt) {
+ DEBUG_LOG(info->line, "#BRK start\n", 0);
+ }
+ if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) {
+ /* The RX pin is high now, so the break
+ * must be over, but....
+ * we can't really know if we will get another
+ * last byte ending the break or not.
+ * And we don't know if the byte (if any) will
+ * have an error or look valid.
+ */
+ DEBUG_LOG(info->line, "# BL BRK\n", 0);
+ info->errorcode = ERRCODE_INSERT_BREAK;
+ }
+ info->break_detected_cnt++;
+ } else {
+ /* The error does not look like a break, but could be
+ * the end of one
+ */
+ if (info->break_detected_cnt) {
+ DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+ info->errorcode = ERRCODE_INSERT_BREAK;
+ } else {
+ if (info->errorcode == ERRCODE_INSERT_BREAK) {
+ info->icount.brk++;
+ *tty->flip.char_buf_ptr = 0;
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ info->icount.rx++;
+ }
+ *tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+
+ if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) {
+ info->icount.parity++;
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ } else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) {
+ info->icount.overrun++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ } else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) {
+ info->icount.frame++;
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ }
+ info->errorcode = 0;
+ }
+ info->break_detected_cnt = 0;
+ }
+ } else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+ /* No error */
+ DLOG_INT_TRIG(
+ if (!log_int_trig1_pos) {
+ if (log_int_pos >= log_int_size) {
+ log_int_pos = 0;
+ }
+ log_int_trig0_pos = log_int_pos;
+ log_int(rdpc(), 0, 0);
+ }
+ );
+ *tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+ *tty->flip.flag_buf_ptr = 0;
+ } else {
+ DEBUG_LOG(info->line, "ser_rx int but no data_avail %08lX\n", data_read);
+ }
+
+
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ info->icount.rx++;
+ data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+ if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+ DEBUG_LOG(info->line, "ser_rx %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read));
+ goto more_data;
+ }
+
+ tty_flip_buffer_push(info->tty);
+ return info;
+}
+
+extern _INLINE_
+struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
{
- unsigned char rstat = info->port[REG_STATUS];
+ unsigned char rstat;
#ifdef SERIAL_DEBUG_INTR
printk("Interrupt from serport %d\n", i);
#endif
/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
+ if (!info->uses_dma_in) {
+ return handle_ser_rx_interrupt_no_dma(info);
+ }
+ /* DMA is used */
+ rstat = info->port[REG_STATUS];
+ if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+ DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+ }
+
if (rstat & SER_ERROR_MASK) {
unsigned char data;
@@ -2084,7 +3002,8 @@
* data_in field
*/
data = info->port[REG_DATA];
-
+ DINTR1(DEBUG_LOG(info->line, "ser_rx! %c\n", data));
+ DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat));
if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
/* Most likely a break, but we get interrupts over and
* over again.
@@ -2113,15 +3032,22 @@
DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
info->errorcode = ERRCODE_INSERT_BREAK;
} else {
- if (info->errorcode == ERRCODE_INSERT_BREAK)
+ if (info->errorcode == ERRCODE_INSERT_BREAK) {
+ info->icount.brk++;
add_char_and_flag(info, '\0', TTY_BREAK);
+ }
- if (rstat & SER_PAR_ERR_MASK)
+ if (rstat & SER_PAR_ERR_MASK) {
+ info->icount.parity++;
add_char_and_flag(info, data, TTY_PARITY);
- else if (rstat & SER_OVERRUN_MASK)
+ } else if (rstat & SER_OVERRUN_MASK) {
+ info->icount.overrun++;
add_char_and_flag(info, data, TTY_OVERRUN);
- else if (rstat & SER_FRAMING_ERR_MASK)
+ } else if (rstat & SER_FRAMING_ERR_MASK) {
+ info->icount.frame++;
add_char_and_flag(info, data, TTY_FRAME);
+ }
+
info->errorcode = 0;
}
info->break_detected_cnt = 0;
@@ -2145,7 +3071,7 @@
if (elapsed_usec < 2*info->char_time_usec) {
DEBUG_LOG(info->line, "FBRK %i\n", info->line);
/* Report as BREAK (error) and let
- * receive_chars() handle it
+ * receive_chars_dma() handle it
*/
info->errorcode = ERRCODE_SET_BREAK;
} else {
@@ -2155,38 +3081,195 @@
}
#ifdef SERIAL_DEBUG_INTR
- printk("** OK, disabling ser_interupts\n");
+ printk("** OK, disabling ser_interrupts\n");
#endif
e100_disable_serial_data_irq(info);
-
+ DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line));
info->break_detected_cnt = 0;
PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
- DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
}
-
/* Restarting the DMA never hurts */
*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
START_FLUSH_FAST_TIMER(info, "ser_int");
-} /* handle_ser_interrupt */
+ return info;
+} /* handle_ser_rx_interrupt */
+
+extern _INLINE_ void handle_ser_tx_interrupt(struct e100_serial *info)
+{
+ unsigned long flags;
+
+ 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();
+ rstat = info->port[REG_STATUS];
+ DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+
+ info->port[REG_TR_DATA] = info->x_char;
+ info->icount.tx++;
+ info->x_char = 0;
+ /* We must enable since it is disabled in ser_interrupt */
+ e100_enable_serial_tx_ready_irq(info);
+ restore_flags(flags);
+ return;
+ }
+ if (info->uses_dma_out) {
+ unsigned char rstat;
+ 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();
+ rstat = info->port[REG_STATUS];
+ DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+ e100_disable_serial_tx_ready_irq(info);
+ if (info->tty->stopped)
+ rs_stop(info->tty);
+ /* Enable the DMA channel and tell it to continue */
+ e100_enable_txdma_channel(info);
+ /* Wait 12 cycles before doing the DMA command */
+ for(i = 6; i > 0; i--)
+ nop();
+
+ *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue);
+ restore_flags(flags);
+ return;
+ }
+ /* Normal char-by-char interrupt */
+ if (info->xmit.head == info->xmit.tail
+ || info->tty->stopped
+ || info->tty->hw_stopped) {
+ DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->tty->stopped));
+ e100_disable_serial_tx_ready_irq(info);
+ info->tr_running = 0;
+ return;
+ }
+ 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();
+ 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++;
+ if (info->xmit.head == info->xmit.tail) {
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+ if (info->rs485.enabled) {
+ /* Set a short timer to toggle RTS */
+ start_one_shot_timer(&fast_timers_rs485[info->line],
+ rs485_toggle_rts_timer_function,
+ (unsigned long)info,
+ info->char_time_usec*2,
+ "RS-485");
+ }
+#endif /* RS485 */
+ info->last_tx_active_usec = GET_JIFFIES_USEC();
+ info->last_tx_active = jiffies;
+ e100_disable_serial_tx_ready_irq(info);
+ DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0));
+ } else {
+ /* We must enable since it is disabled in ser_interrupt */
+ e100_enable_serial_tx_ready_irq(info);
+ }
+ restore_flags(flags);
+
+ if (CIRC_CNT(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+} /* handle_ser_tx_interrupt */
+
+/* result of time measurements:
+ * RX duration 54-60 us when doing something, otherwise 6-9 us
+ * ser_int duration: just sending: 8-15 us normally, up to 73 us
+ */
static void
ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
+ static volatile int tx_started = 0;
struct e100_serial *info;
int i;
-
+ unsigned long flags;
+ unsigned long irq_mask1_rd;
+ unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */
+ static volatile unsigned long reentered_ready_mask = 0;
+
+ save_flags(flags); cli();
+ irq_mask1_rd = *R_IRQ_MASK1_RD;
+ /* First handle all rx interrupts with ints disabled */
+ info = rs_table;
+ irq_mask1_rd &= e100_ser_int_mask;
for (i = 0; i < NR_PORTS; i++) {
- info = rs_table + i;
-
- if (!info->enabled || !info->uses_dma)
- continue;
-
- /* Which line caused the irq? */
- if (*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) {
- handle_ser_interrupt(info);
+ /* Which line caused the data irq? */
+ if (irq_mask1_rd & data_mask) {
+ handle_ser_rx_interrupt(info);
}
+ info += 1;
+ data_mask <<= 2;
}
+ /* Handle tx interrupts with interrupts enabled so we
+ * can take care of new data interrupts while transmitting
+ * We protect the tx part with the tx_started flag.
+ * We disable the tr_ready interrupts we are about to handle and
+ * unblock the serial interrupt so new serial interrupts may come.
+ *
+ * If we get a new interrupt:
+ * - it migth be due to synchronous serial ports.
+ * - serial irq will be blocked by general irq handler.
+ * - async data will be handled above (sync will be ignored).
+ * - tx_started flag will prevent us from trying to send again and
+ * we will exit fast - no need to unblock serial irq.
+ * - Next (sync) serial interrupt handler will be runned with
+ * disabled interrupt due to restore_flags() at end of function,
+ * so sync handler will not be preempted or reentered.
+ */
+ if (!tx_started) {
+ unsigned long ready_mask;
+ unsigned long
+ tx_started = 1;
+ /* Only the tr_ready interrupts left */
+ irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+ IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+ IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+ IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+ while (irq_mask1_rd) {
+ /* Disable those we are about to handle */
+ *R_IRQ_MASK1_CLR = irq_mask1_rd;
+ /* Unblock the serial interrupt */
+ *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+
+ sti();
+ ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
+ info = rs_table;
+ for (i = 0; i < NR_PORTS; i++) {
+ /* Which line caused the ready irq? */
+ if (irq_mask1_rd & ready_mask) {
+ handle_ser_tx_interrupt(info);
+ }
+ info += 1;
+ ready_mask <<= 2;
+ }
+ /* handle_ser_tx_interrupt enables tr_ready interrupts */
+ cli();
+ /* Handle reentered TX interrupt */
+ irq_mask1_rd = reentered_ready_mask;
+ }
+ cli();
+ tx_started = 0;
+ } else {
+ unsigned long ready_mask;
+ ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+ IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+ IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+ IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+ if (ready_mask) {
+ reentered_ready_mask |= ready_mask;
+ /* Disable those we are about to handle */
+ *R_IRQ_MASK1_CLR = ready_mask;
+ DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask));
+ }
+ }
+
+ restore_flags(flags);
} /* ser_interrupt */
#endif
@@ -2288,25 +3371,40 @@
* Reset the DMA channels and make sure their interrupts are cleared
*/
- info->uses_dma = 1;
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-
- /* Wait until reset cycle is complete */
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+ if (info->dma_in_enabled) {
+ info->uses_dma_in = 1;
+ e100_enable_rxdma_channel(info);
+
+ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- /* Make sure the irqs are cleared */
- *info->iclrintradr =
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
- *info->oclrintradr =
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+ /* Wait until reset cycle is complete */
+ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+ IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+ /* Make sure the irqs are cleared */
+ *info->iclrintradr =
+ IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+ IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+ } else {
+ e100_disable_rxdma_channel(info);
+ }
+
+ if (info->dma_out_enabled) {
+ info->uses_dma_out = 1;
+ e100_enable_txdma_channel(info);
+ *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+
+ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
+ IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+ /* Make sure the irqs are cleared */
+ *info->oclrintradr =
+ IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+ IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+ } else {
+ e100_disable_txdma_channel(info);
+ }
+
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -2321,8 +3419,10 @@
* and set the speed and other flags of the serial port
* this will start the rx/tx as well
*/
+
#ifdef SERIAL_HANDLE_EARLY_ERRORS
- e100_enable_serial_data_irq(info);
+ if (info->uses_dma_in)
+ e100_enable_serial_data_irq(info);
#endif
change_speed(info);
@@ -2331,9 +3431,10 @@
(void)info->port[REG_DATA];
/* enable the interrupts */
+ if (info->uses_dma_out)
+ e100_enable_txdma_irq(info);
- e100_enable_txdma_irq(info);
- e100_enable_rxdma_irq(info);
+ e100_enable_rx_irq(info);
info->tr_running = 0; /* to be sure we don't lock up the transmitter */
@@ -2342,10 +3443,10 @@
start_receive(info);
/* for safety, make sure the descriptors last result is 0 bytes written */
-
info->tr_descr.sw_len = 0;
info->tr_descr.hw_len = 0;
info->tr_descr.status = 0;
+
/* enable RTS/DTR last */
@@ -2374,20 +3475,28 @@
#ifndef CONFIG_SVINTO_SIM
/* shut down the transmitter and receiver */
-
+ DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
e100_disable_rx(info);
info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
- e100_disable_rxdma_irq(info);
- e100_disable_txdma_irq(info);
-
- info->tr_running = 0;
-
- /* reset both dma channels */
-
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- info->uses_dma = 0;
+ /* disable interrupts, reset dma channels */
+ if (info->uses_dma_in) {
+ e100_disable_rxdma_irq(info);
+ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+ info->uses_dma_in = 0;
+ } else {
+ e100_disable_serial_data_irq(info);
+ }
+
+ if (info->uses_dma_out) {
+ e100_disable_txdma_irq(info);
+ info->tr_running = 0;
+ *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+ info->uses_dma_out = 0;
+ } else {
+ e100_disable_serial_tx_ready_irq(info);
+ info->tr_running = 0;
+ }
#endif /* CONFIG_SVINTO_SIM */
@@ -2435,7 +3544,7 @@
{
unsigned int cflag;
unsigned long xoff;
-
+ unsigned long flags;
/* first some safety checks */
if (!info->tty || !info->tty->termios)
@@ -2444,17 +3553,80 @@
return;
cflag = info->tty->termios->c_cflag;
-
+
/* possibly, the tx/rx should be disabled first to do this safely */
/* change baud-rate and write it to the hardware */
-
- info->baud = cflag_to_baud(cflag);
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+ /* Special baudrate */
+ u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+ unsigned long alt_source =
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+ /* R_ALT_SER_BAUDRATE selects the source */
+ DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n",
+ (unsigned long)info->baud_base, info->custom_divisor));
+ if (info->baud_base == SERIAL_PRESCALE_BASE) {
+ /* 0, 2-65535 (0=65536) */
+ u16 divisor = info->custom_divisor;
+ /* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */
+ /* baudrate is 3.125MHz/custom_divisor */
+ alt_source =
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) |
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale);
+ alt_source = 0x11;
+ DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor));
+ *R_SERIAL_PRESCALE = divisor;
+ info->baud = SERIAL_PRESCALE_BASE/divisor;
+ }
+#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+ else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 &&
+ info->custom_divisor == 1) ||
+ (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ &&
+ info->custom_divisor == 8)) {
+ /* ext_clk selected */
+ alt_source =
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) |
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern);
+ DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8));
+ info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8;
+ }
+ }
+#endif
+ else
+ {
+ /* Bad baudbase, we don't support using timer0
+ * for baudrate.
+ */
+ printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n",
+ (unsigned long)info->baud_base, info->custom_divisor);
+ }
+ r_alt_ser_baudrate_shadow &= ~mask;
+ r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+ *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+ } else {
+ /* Normal baudrate */
+ /* Make sure we use normal baudrate */
+ u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+ unsigned long alt_source =
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+ IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+ r_alt_ser_baudrate_shadow &= ~mask;
+ r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+#ifndef CONFIG_SVINTO_SIM
+ *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+#endif /* CONFIG_SVINTO_SIM */
+
+ info->baud = cflag_to_baud(cflag);
+#ifndef CONFIG_SVINTO_SIM
+ info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
+#endif /* CONFIG_SVINTO_SIM */
+ }
#ifndef CONFIG_SVINTO_SIM
- info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
/* start with default settings and then fill in changes */
-
+ save_flags(flags);
+ cli();
/* 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) |
@@ -2484,14 +3656,26 @@
info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
}
- if (cflag & PARODD) {
- /* set odd parity */
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
- info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+ if (cflag & CMSPAR) {
+ /* enable stick parity */
+ info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick);
+ info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick);
+ if (!(cflag & PARODD)) {
+ /* set mark parity */
+ info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+ info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+ }
+ } else {
+ if (cflag & PARODD) {
+ /* set odd parity */
+ info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+ info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+ }
}
if (cflag & CRTSCTS) {
/* enable automatic CTS handling */
+ DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0));
info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active);
}
@@ -2507,10 +3691,12 @@
xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
if (info->tty->termios->c_iflag & IXON ) {
+ DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", STOP_CHAR(info->tty)));
xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
}
*((unsigned long *)&info->port[REG_XOFF]) = xoff;
+ restore_flags(flags);
#endif /* !CONFIG_SVINTO_SIM */
update_char_time(info);
@@ -2543,8 +3729,8 @@
restore_flags(flags);
}
-extern inline int
-raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int
+rs_raw_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
int c, ret = 0;
@@ -2558,7 +3744,7 @@
#ifdef SERIAL_DEBUG_DATA
if (info->line == SERIAL_DEBUG_LINE)
- printk("raw_write (%d), status %d\n",
+ printk("rs_raw_write (%d), status %d\n",
count, info->port[REG_STATUS]);
#endif
@@ -2568,6 +3754,9 @@
return count;
#endif
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
* DMA interrupt handler moves the info->xmit values. the memcpy
@@ -2635,6 +3824,7 @@
* this does not need IRQ protection since if tr_running == 0
* the IRQ's are not running anyway for this port.
*/
+ DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret));
if (info->xmit.head != info->xmit.tail &&
!tty->stopped &&
@@ -2644,7 +3834,7 @@
}
return ret;
-} /* raw_write() */
+} /* rs_raw_write() */
static int
rs_write(struct tty_struct * tty, int from_user,
@@ -2666,7 +3856,7 @@
e100_rts(info, info->rs485.rts_on_send);
#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
e100_disable_rx(info);
- e100_disable_rxdma_irq(info);
+ e100_disable_rx_irq(info);
#endif
if (info->rs485.delay_rts_before_send > 0) {
@@ -2676,7 +3866,7 @@
}
#endif /* CONFIG_ETRAX_RS485 */
- count = raw_write(tty, from_user, buf, count);
+ count = rs_raw_write(tty, from_user, buf, count);
#if defined(CONFIG_ETRAX_RS485)
if (info->rs485.enabled)
@@ -2704,7 +3894,7 @@
#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
e100_enable_rx(info);
- e100_enable_rxdma_irq(info);
+ e100_enable_rx_irq(info);
#endif
}
#endif /* CONFIG_ETRAX_RS485 */
@@ -2760,20 +3950,33 @@
* 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.
+ * Since we use DMA we don't check for info->x_char in transmit_chars_dma(),
+ * but we do it in handle_ser_tx_interrupt().
+ * We disable DMA channel and enable tx ready interrupt and write the
+ * character when possible.
*/
static void rs_send_xchar(struct tty_struct *tty, char ch)
{
struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+ save_flags(flags); cli();
+ if (info->uses_dma_out) {
+ /* Put the DMA on hold and disable the channel */
+ *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold);
+ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) !=
+ IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold));
+ e100_disable_txdma_channel(info);
+ }
+
+ /* Must make sure transmitter is not stopped before we can transmit */
+ if (tty->stopped)
+ rs_start(tty);
- printk("serial.c:rs_send_xchar not implemented!\n");
-
+ /* Enable manual transmit interrupt and send from there */
+ DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
info->x_char = ch;
- if (ch) {
- /* Make sure transmit interrupts are on */
- /* TODO. */
- }
+ e100_enable_serial_tx_ready_irq(info);
+ restore_flags(flags);
}
/*
@@ -2788,50 +3991,49 @@
rs_throttle(struct tty_struct * tty)
{
struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
- printk("throttle %s: %d....\n", _tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
+ printk("throttle %s: %lu....\n", tty_name(tty, buf),
+ (unsigned long)tty->ldisc.chars_in_buffer(tty));
#endif
-
+ DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
+
+ /* Do RTS before XOFF since XOFF might take some time */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ /* Turn off RTS line */
+ e100_rts(info, 0);
+ }
if (I_IXOFF(tty))
- info->x_char = STOP_CHAR(tty);
+ rs_send_xchar(tty, STOP_CHAR(tty));
- /* Turn off RTS line (do this atomic) should here be an else ?? */
-
- save_flags(flags);
- cli();
- e100_rts(info, 0);
- restore_flags(flags);
}
static void
rs_unthrottle(struct tty_struct * tty)
{
struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
- printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
+ printk("unthrottle %s: %lu....\n", tty_name(tty, buf),
+ (unsigned long)tty->ldisc.chars_in_buffer(tty));
#endif
+ DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
+ DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
+ /* Do RTS before XOFF since XOFF might take some time */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ /* Assert RTS line */
+ e100_rts(info, 1);
+ }
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
- info->x_char = START_CHAR(tty);
+ rs_send_xchar(tty, START_CHAR(tty));
}
- /* Assert RTS line (do this atomic) */
-
- save_flags(flags);
- cli();
- e100_rts(info, 1);
- restore_flags(flags);
}
/*
@@ -2859,8 +4061,10 @@
tmp.port = (int)info->port;
tmp.irq = info->irq;
tmp.flags = info->flags;
+ tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
@@ -2897,9 +4101,11 @@
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
-
+
+ info->baud_base = new_serial.baud_base;
info->flags = ((info->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
+ info->custom_divisor = new_serial.custom_divisor;
info->type = new_serial.type;
info->close_delay = new_serial.close_delay;
info->closing_wait = new_serial.closing_wait;
@@ -3098,18 +4304,20 @@
info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
info->port[REG_TR_CTRL] = info->tx_ctrl;
-
+ restore_flags(flags);
/* wait for "duration" jiffies */
schedule();
+ cli();
info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
info->port[REG_TR_CTRL] = info->tx_ctrl;
/* the DMA gets awfully confused if we toggle the tranceiver like this
* so we need to reset it
*/
- *info->ocmdadr = 4;
+ if (info->uses_dma_out)
+ *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
restore_flags(flags);
}
@@ -3252,6 +4460,7 @@
change_speed(info);
+ /* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
@@ -3301,12 +4510,13 @@
* 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, "
+ printk(KERN_CRIT
+ "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",
+ printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n",
info->line, info->count);
info->count = 0;
}
@@ -3340,7 +4550,7 @@
#ifndef CONFIG_SVINTO_SIM
e100_disable_rx(info);
- e100_disable_rxdma_irq(info);
+ e100_disable_rx_irq(info);
if (info->flags & ASYNC_INITIALIZED) {
/*
@@ -3676,6 +4886,12 @@
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open ttyS%d successful...\n", info->line);
#endif
+ DLOG_INT_TRIG( log_int_pos = 0);
+
+ DFLIP( if (info->line == SERIAL_DEBUG_LINE) {
+ info->icount.rx = 0;
+ } );
+
return 0;
}
@@ -3683,11 +4899,12 @@
* /proc fs routines....
*/
-extern inline int line_info(char *buf, struct e100_serial *info)
+extern _INLINE_ int line_info(char *buf, struct e100_serial *info)
{
char stat_buf[30];
int ret;
-
+ unsigned long tmp;
+
ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
info->line, (unsigned long)info->port, info->irq);
@@ -3716,11 +4933,39 @@
ret += sprintf(buf+ret, " tx:%lu rx:%lu",
(unsigned long)info->icount.tx,
(unsigned long)info->icount.rx);
-
+ tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+ if (tmp) {
+ ret += sprintf(buf+ret, " tx_pend:%lu/%lu",
+ (unsigned long)tmp,
+ (unsigned long)SERIAL_XMIT_SIZE);
+ }
+
ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
(unsigned long)info->recv_cnt,
(unsigned long)info->max_recv_cnt);
+#if 1
+ if (info->tty) {
+
+ if (info->tty->stopped)
+ ret += sprintf(buf+ret, " stopped:%i",
+ (int)info->tty->stopped);
+ if (info->tty->hw_stopped)
+ ret += sprintf(buf+ret, " hw_stopped:%i",
+ (int)info->tty->hw_stopped);
+ }
+
+ {
+ unsigned char rstat = info->port[REG_STATUS];
+ if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) )
+ ret += sprintf(buf+ret, " xoff_detect:1");
+ }
+
+#endif
+
+
+
+
if (info->icount.frame)
ret += sprintf(buf+ret, " fe:%lu",
(unsigned long)info->icount.frame);
@@ -3744,12 +4989,14 @@
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++) {
@@ -3764,6 +5011,22 @@
len = 0;
}
}
+#ifdef DEBUG_LOG_INCLUDED
+ for (i = 0; i < debug_log_pos; i++) {
+ len += sprintf(page + len, "%-4i %lu.%lu ", i, debug_log[i].time, timer_data_to_ns(debug_log[i].timer_data));
+ len += sprintf(page + len, debug_log[i].string, debug_log[i].value);
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ len += sprintf(page + len, "debug_log %i/%i %li bytes\n",
+ i, DEBUG_LOG_SIZE, begin+len);
+ debug_log_pos = 0;
+#endif
+
*eof = 1;
done:
if (off >= len+begin)
@@ -3777,7 +5040,8 @@
static void
show_serial_version(void)
{
- printk("ETRAX 100LX serial-driver %s, (c) 2000-2002 Axis Communications AB\r\n",
+ printk(KERN_INFO
+ "ETRAX 100LX serial-driver %s, (c) 2000-2003 Axis Communications AB\r\n",
&serial_version[11]); /* "$Revision: x.yy" */
}
@@ -3794,7 +5058,7 @@
init_bh(SERIAL_BH, do_serial_bh);
/* Setup the timed flush handler system */
-
+
#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) && !defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)
init_timer(&flush_timer);
flush_timer.function = timed_flush_handler;
@@ -3868,12 +5132,15 @@
/* do some initializing for the separate ports */
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- info->uses_dma = 0;
+ info->uses_dma_in = 0;
+ info->uses_dma_out = 0;
info->line = i;
info->tty = 0;
info->type = PORT_ETRAX;
info->tr_running = 0;
info->forced_eop = 0;
+ info->baud_base = DEF_BAUD_BASE;
+ info->custom_divisor = 0;
info->flags = 0;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
@@ -3920,41 +5187,58 @@
#ifndef CONFIG_SVINTO_SIM
/* 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))
+ 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
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
- if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
- panic("irq8");
#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) */
- if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 2 dma tr", NULL))
+#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");
- if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 2 dma rec", NULL))
+#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) */
- if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 3 dma tr", NULL))
+#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");
- if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 3 dma rec", NULL))
+#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("err: timer1 irq\n");
+ printk(KERN_CRIT "err: timer1 irq\n");
}
#endif
#endif /* CONFIG_SVINTO_SIM */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)