diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2015-05-25 17:52:32 +0200 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2015-05-25 17:52:50 +0200 |
commit | 67e2da6a84f50c0b0e43f4d4fb9eae91671e81ad (patch) | |
tree | 1406e2b59fe929d08261173bf21d886c07f04c06 /target/linux/patches | |
parent | cc0c042aafec40d93b06be3b6d7e94f38c5ac69f (diff) |
add serial driver patch for crisv32
Diffstat (limited to 'target/linux/patches')
-rw-r--r-- | target/linux/patches/4.0.3/cris32-serial.patch | 2886 |
1 files changed, 2886 insertions, 0 deletions
diff --git a/target/linux/patches/4.0.3/cris32-serial.patch b/target/linux/patches/4.0.3/cris32-serial.patch new file mode 100644 index 000000000..0f071cffb --- /dev/null +++ b/target/linux/patches/4.0.3/cris32-serial.patch @@ -0,0 +1,2886 @@ +diff -Nur linux-4.0.3.orig/arch/cris/arch-v32/drivers/Kconfig linux-4.0.3/arch/cris/arch-v32/drivers/Kconfig +--- linux-4.0.3.orig/arch/cris/arch-v32/drivers/Kconfig 2015-05-13 14:14:53.000000000 +0200 ++++ linux-4.0.3/arch/cris/arch-v32/drivers/Kconfig 2015-05-25 17:20:55.000000000 +0200 +@@ -49,24 +49,261 @@ + if you do not need DMA to something else. + ser0 can use dma4 or dma6 for output and dma5 or dma7 for input. + ++choice ++ prompt "Ser0 default port type " ++ depends on ETRAX_SERIAL_PORT0 ++ default ETRAX_SERIAL_PORT0_TYPE_232 ++ help ++ Type of serial port. ++ ++config ETRAX_SERIAL_PORT0_TYPE_232 ++ bool "Ser0 is a RS-232 port" ++ help ++ Configure serial port 0 to be a RS-232 port. ++ ++config ETRAX_SERIAL_PORT0_TYPE_485HD ++ bool "Ser0 is a half duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 0 to be a half duplex (two wires) RS-485 port. ++ ++config ETRAX_SERIAL_PORT0_TYPE_485FD ++ bool "Ser0 is a full duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 0 to be a full duplex (four wires) RS-485 port. ++endchoice ++ ++config ETRAX_SER0_DTR_BIT ++ string "Ser 0 DTR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT0 ++ ++config ETRAX_SER0_RI_BIT ++ string "Ser 0 RI bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT0 ++ ++config ETRAX_SER0_DSR_BIT ++ string "Ser 0 DSR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT0 ++ ++config ETRAX_SER0_CD_BIT ++ string "Ser 0 CD bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT0 ++ + config ETRAX_SERIAL_PORT1 + bool "Serial port 1 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser1 (ttyS1). + ++choice ++ prompt "Ser1 default port type" ++ depends on ETRAX_SERIAL_PORT1 ++ default ETRAX_SERIAL_PORT1_TYPE_232 ++ help ++ Type of serial port. ++ ++config ETRAX_SERIAL_PORT1_TYPE_232 ++ bool "Ser1 is a RS-232 port" ++ help ++ Configure serial port 1 to be a RS-232 port. ++ ++config ETRAX_SERIAL_PORT1_TYPE_485HD ++ bool "Ser1 is a half duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 1 to be a half duplex (two wires) RS-485 port. ++ ++config ETRAX_SERIAL_PORT1_TYPE_485FD ++ bool "Ser1 is a full duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 1 to be a full duplex (four wires) RS-485 port. ++endchoice ++ ++config ETRAX_SER1_DTR_BIT ++ string "Ser 1 DTR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT1 ++ ++config ETRAX_SER1_RI_BIT ++ string "Ser 1 RI bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT1 ++ ++config ETRAX_SER1_DSR_BIT ++ string "Ser 1 DSR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT1 ++ ++config ETRAX_SER1_CD_BIT ++ string "Ser 1 CD bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT1 ++ + config ETRAX_SERIAL_PORT2 + bool "Serial port 2 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser2 (ttyS2). + ++choice ++ prompt "Ser2 default port type" ++ depends on ETRAX_SERIAL_PORT2 ++ default ETRAX_SERIAL_PORT2_TYPE_232 ++ help ++ What DMA channel to use for ser2 ++ ++config ETRAX_SERIAL_PORT2_TYPE_232 ++ bool "Ser2 is a RS-232 port" ++ help ++ Configure serial port 2 to be a RS-232 port. ++ ++config ETRAX_SERIAL_PORT2_TYPE_485HD ++ bool "Ser2 is a half duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 2 to be a half duplex (two wires) RS-485 port. ++ ++config ETRAX_SERIAL_PORT2_TYPE_485FD ++ bool "Ser2 is a full duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 2 to be a full duplex (four wires) RS-485 port. ++endchoice ++ ++ ++config ETRAX_SER2_DTR_BIT ++ string "Ser 2 DTR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT2 ++ ++config ETRAX_SER2_RI_BIT ++ string "Ser 2 RI bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT2 ++ ++config ETRAX_SER2_DSR_BIT ++ string "Ser 2 DSR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT2 ++ ++config ETRAX_SER2_CD_BIT ++ string "Ser 2 CD bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT2 ++ + config ETRAX_SERIAL_PORT3 + bool "Serial port 3 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser3 (ttyS3). + ++choice ++ prompt "Ser3 default port type" ++ depends on ETRAX_SERIAL_PORT3 ++ default ETRAX_SERIAL_PORT3_TYPE_232 ++ help ++ What DMA channel to use for ser3. ++ ++config ETRAX_SERIAL_PORT3_TYPE_232 ++ bool "Ser3 is a RS-232 port" ++ help ++ Configure serial port 3 to be a RS-232 port. ++ ++config ETRAX_SERIAL_PORT3_TYPE_485HD ++ bool "Ser3 is a half duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 3 to be a half duplex (two wires) RS-485 port. ++ ++config ETRAX_SERIAL_PORT3_TYPE_485FD ++ bool "Ser3 is a full duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 3 to be a full duplex (four wires) RS-485 port. ++endchoice ++ ++config ETRAX_SER3_DTR_BIT ++ string "Ser 3 DTR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT3 ++ ++config ETRAX_SER3_RI_BIT ++ string "Ser 3 RI bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT3 ++ ++config ETRAX_SER3_DSR_BIT ++ string "Ser 3 DSR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT3 ++ ++config ETRAX_SER3_CD_BIT ++ string "Ser 3 CD bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT3 ++ ++config ETRAX_SERIAL_PORT4 ++ bool "Serial port 4 enabled" ++ depends on ETRAXFS_SERIAL && CRIS_MACH_ARTPEC3 ++ help ++ Enables the ETRAX FS serial driver for ser4 (ttyS4). ++ ++choice ++ prompt "Ser4 default port type" ++ depends on ETRAX_SERIAL_PORT4 ++ default ETRAX_SERIAL_PORT4_TYPE_232 ++ help ++ What DMA channel to use for ser4. ++ ++config ETRAX_SERIAL_PORT4_TYPE_232 ++ bool "Ser4 is a RS-232 port" ++ help ++ Configure serial port 4 to be a RS-232 port. ++ ++config ETRAX_SERIAL_PORT4_TYPE_485HD ++ bool "Ser4 is a half duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 4 to be a half duplex (two wires) RS-485 port. ++ ++config ETRAX_SERIAL_PORT4_TYPE_485FD ++ bool "Ser4 is a full duplex RS-485 port" ++ depends on ETRAX_RS485 ++ help ++ Configure serial port 4 to be a full duplex (four wires) RS-485 port. ++endchoice ++ ++choice ++ prompt "Ser4 DMA in channel " ++ depends on ETRAX_SERIAL_PORT4 ++ default ETRAX_SERIAL_PORT4_NO_DMA_IN ++ help ++ What DMA channel to use for ser4. ++ ++ ++config ETRAX_SERIAL_PORT4_NO_DMA_IN ++ bool "Ser4 uses no DMA for input" ++ help ++ Do not use DMA for ser4 input. ++ ++config ETRAX_SERIAL_PORT4_DMA9_IN ++ bool "Ser4 uses DMA9 for input" ++ depends on ETRAX_SERIAL_PORT4 ++ help ++ Enables the DMA9 input channel for ser4 (ttyS4). ++ If you do not enable DMA, an interrupt for each character will be ++ used when receiving data. ++ Normally you want to use DMA, unless you use the DMA channel for ++ something else. ++ ++endchoice ++ ++config ETRAX_SER4_DTR_BIT ++ string "Ser 4 DTR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT4 ++ ++config ETRAX_SER4_RI_BIT ++ string "Ser 4 RI bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT4 ++ ++config ETRAX_SER4_DSR_BIT ++ string "Ser 4 DSR bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT4 ++ ++config ETRAX_SER4_CD_BIT ++ string "Ser 4 CD bit (empty = not used)" ++ depends on ETRAX_SERIAL_PORT4 ++ + config ETRAX_SYNCHRONOUS_SERIAL + bool "Synchronous serial-port support" + depends on ETRAX_ARCH_V32 +diff -Nur linux-4.0.3.orig/arch/cris/include/uapi/asm/ioctls.h linux-4.0.3/arch/cris/include/uapi/asm/ioctls.h +--- linux-4.0.3.orig/arch/cris/include/uapi/asm/ioctls.h 2015-05-13 14:14:53.000000000 +0200 ++++ linux-4.0.3/arch/cris/include/uapi/asm/ioctls.h 2015-05-25 17:20:56.000000000 +0200 +@@ -5,6 +5,10 @@ + #define TIOCSERSETRS485 0x5461 /* enable rs-485 (deprecated) */ + #define TIOCSERWRRS485 0x5462 /* write rs-485 */ + #define TIOCSRS485 0x5463 /* enable rs-485 */ ++#define TIOCSERSETRS485FD 0x5464 /* set rs-485 full/half duplex mode */ ++ ++ ++#define TIOCSERSETDIVISOR 0x5465 /* set the divisor for non standard bauds */ + + #include <asm-generic/ioctls.h> + +diff -Nur linux-4.0.3.orig/drivers/tty/serial/crisv32.c linux-4.0.3/drivers/tty/serial/crisv32.c +--- linux-4.0.3.orig/drivers/tty/serial/crisv32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.3/drivers/tty/serial/crisv32.c 2015-05-25 17:20:56.000000000 +0200 +@@ -0,0 +1,2581 @@ ++/* $Id: crisv32.c,v 1.109 2010-07-09 15:00:44 jespern Exp $ ++ * ++ * Serial port driver for the ETRAX FS chip ++ * ++ * Copyright (C) 1998-2006 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 <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/tty_flip.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/uaccess.h> ++ ++#include <dma.h> ++#include <arch/system.h> ++#include <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 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) \ ++ if (REG_RD_INT(ser, instance, reg) \ ++ != REG_TYPE_CONV(int, reg_ser_##reg, var)) \ ++ REG_WR(ser, instance, reg, var); ++ ++/* ++ * 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; ++ int write_ongoing; ++}; ++ ++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 ++ .write_ongoing = 0 ++}, /* 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 ++ .write_ongoing = 0 ++}, /* 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 ++ .write_ongoing = 0 ++}, /* 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 ++ .write_ongoing = 0 ++}, /* 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 ++ .write_ongoing = 0 ++}, /* ttyS4 */ ++#endif ++{ ++#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL ++ .used = 1, ++#endif ++ .regi_ser = regi_NULL, ++ .write_ongoing = 0 ++} /* 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; ++#elif defined(CONFIG_ETRAX_DEBUG_PORT4) ++4; ++#else /* CONFIG_ETRAX_DEBUG_PORT_NULL */ ++#if CONFIG_ETRAX_SERIAL_PORTS == 5 ++5; ++#else ++4; ++#endif ++#endif ++ ++extern void reset_watchdog(void); ++ ++static void serial_cris_stop_rx(struct uart_port *port); ++ ++/* ++ * 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; ++ ++ co->flags |= CON_CONSDEV; ++ ++ 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 int inline 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, int force) ++{ ++ 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 (force || !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 int inline 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_ser_rw_tr_dma_en tr_dma_en; ++ REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr); ++ 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; ++ ++ /* we have already done below if a write is ongoing */ ++ if (!up->regi_dmaout && up->write_ongoing) ++ return; ++ ++#ifdef CONFIG_ETRAX_RS485 ++ if (up->rs485.enabled) ++ { ++ /* If we are in RS-485 mode, we need to toggle RTS and disable ++ * the receiver before initiating a DMA transfer ++ */ ++ ++ if (up->rs485.delay_rts_before_send > 0) { ++ reg_ser_rw_tr_ctrl tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl); ++ tr_ctrl.auto_rts = regk_ser_no; ++ REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); ++ crisv32_serial_set_rts(up, up->rs485.rts_on_send, 1); ++ msleep(up->rs485.delay_rts_before_send); ++ tr_ctrl.auto_rts = regk_ser_yes; ++ REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); ++ crisv32_serial_set_rts(up, !up->rs485.rts_on_send, 1); ++ } ++ } ++#endif ++ ++ 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); ++ up->write_ongoing = 1; ++ } 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 |