Signed-off-by: Guenter Roeck <li...@roeck-us.net> --- arch/cris/arch-v32/drivers/Kconfig | 237 ++++ arch/cris/include/uapi/asm/ioctls.h | 4 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/crisv32.c | 2581 +++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 5 files changed, 2826 insertions(+) create mode 100644 drivers/tty/serial/crisv32.c
diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig index 15a9ed1..f5e3333 100644 --- a/arch/cris/arch-v32/drivers/Kconfig +++ b/arch/cris/arch-v32/drivers/Kconfig @@ -49,24 +49,261 @@ config ETRAX_SERIAL_PORT0 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 --git a/arch/cris/include/uapi/asm/ioctls.h b/arch/cris/include/uapi/asm/ioctls.h index 488fbb3..22b9a61 100644 --- a/arch/cris/include/uapi/asm/ioctls.h +++ b/arch/cris/include/uapi/asm/ioctls.h @@ -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 --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 0080cc3..79bae96 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o +obj-$(CONFIG_ETRAXFS_SERIAL) += crisv32.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ diff --git a/drivers/tty/serial/crisv32.c b/drivers/tty/serial/crisv32.c new file mode 100644 index 0000000..a69f131 --- /dev/null +++ b/drivers/tty/serial/crisv32.c @@ -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 we're in XON/XOFF, we can send + * those single characters without also giving go-ahead for queued up + * DMA data. + */ + tr_dma_en.en = 0; + REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en); +} + +static void serial_cris_stop_rx(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + reg_scope_instances regi_ser = up->regi_ser; + reg_ser_rw_rec_ctrl rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); + + rec_ctrl.en = regk_ser_no; + REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); +} + +static void serial_cris_enable_ms(struct uart_port *port) +{ +} + +static void check_modem_status(struct uart_cris_port *up) +{ +} + +static unsigned int serial_cris_tx_empty(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + unsigned int ret; + reg_ser_r_stat_din rstat = {0}; + + spin_lock_irqsave(&up->port.lock, flags); + if (up->regi_dmaout) { + /* + * For DMA, before looking at r_stat, we need to check that we + * either haven't actually started or that end-of-list is + * reached, else a tr_empty indication is just an internal + * state. The caller qualifies, if needed, that the + * port->info.xmit buffer is empty, so we don't need to + * check that. + */ + reg_dma_rw_stat status = REG_RD(dma, up->regi_dmaout, rw_stat); + + if (!up->tx_started) { + ret = 1; + goto done; + } + + if (status.list_state != regk_dma_data_at_eol) { + ret = 0; + goto done; + } + } + + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + ret = rstat.tr_empty ? TIOCSER_TEMT : 0; + + done: + spin_unlock_irqrestore(&up->port.lock, flags); + return ret; +} +static unsigned int serial_cris_get_mctrl(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned int ret; + + ret = 0; + if (crisv32_serial_get_rts(up)) + ret |= TIOCM_RTS; + /* DTR is active low */ + if (!crisv32_io_rd(&up->dtr_pin)) + ret |= TIOCM_DTR; + /* CD is active low */ + if (!crisv32_io_rd(&up->cd_pin)) + ret |= TIOCM_CD; + /* RI is active low */ + if (!crisv32_io_rd(&up->ri_pin)) + ret |= TIOCM_RI; + /* DSR is active low */ + if (!crisv32_io_rd(&up->dsr_pin)) + ret |= TIOCM_DSR; + if (crisv32_serial_get_cts(up)) + ret |= TIOCM_CTS; + return ret; +} + +static void serial_cris_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + + crisv32_serial_set_rts(up, mctrl & TIOCM_RTS ? 1 : 0, 0); + /* DTR is active low */ + crisv32_io_set(&up->dtr_pin, mctrl & TIOCM_DTR ? 0 : 1); + /* RI is active low */ + crisv32_io_set(&up->ri_pin, mctrl & TIOCM_RNG ? 0 : 1); + /* CD is active low */ + crisv32_io_set(&up->cd_pin, mctrl & TIOCM_CD ? 0 : 1); +} + +static void serial_cris_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + reg_ser_rw_tr_ctrl tr_ctrl; + reg_ser_rw_tr_dma_en tr_dma_en; + reg_ser_rw_intr_mask intr_mask; + + spin_lock_irqsave(&up->port.lock, flags); + tr_ctrl = REG_RD(ser, up->regi_ser, rw_tr_ctrl); + tr_dma_en = REG_RD(ser, up->regi_ser, rw_tr_dma_en); + intr_mask = REG_RD(ser, up->regi_ser, rw_intr_mask); + + if (break_state != 0) { /* Send break */ + /* + * We need to disable DMA (if used) or tr_rdy interrupts if no + * DMA. No need to make this conditional on use of DMA; + * disabling will be a no-op for the other mode. + */ + intr_mask.tr_rdy = regk_ser_no; + tr_dma_en.en = 0; + + /* + * Stop transmission and set the txd pin to 0 after the + * current character. The txd setting will take effect after + * any current transmission has completed. + */ + tr_ctrl.stop = 1; + tr_ctrl.txd = 0; + } else { + /* Re-enable either transmit DMA or the serial interrupt. */ + if (up->regi_dmaout) + tr_dma_en.en = 1; + else + intr_mask.tr_rdy = regk_ser_yes; + + + tr_ctrl.stop = 0; + tr_ctrl.txd = 1; + } + REG_WR(ser, up->regi_ser, rw_tr_ctrl, tr_ctrl); + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en); + REG_WR(ser, up->regi_ser, rw_intr_mask, intr_mask); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * The output DMA channel is free - use it to send as many chars as + * possible. + */ + +static void +transmit_chars_dma(struct uart_cris_port *up) +{ + struct dma_descr_data *descr, *pending_descr, *dmapos; + struct dma_descr_data *last_tx_descr; + struct circ_buf *xmit = &up->port.state->xmit; + unsigned int sentl = 0; + reg_dma_rw_ack_intr ack_intr = { .data = regk_dma_yes }; + reg_dma_rw_stat status; + reg_scope_instances regi_dmaout = up->regi_dmaout; + unsigned int chars_in_q; + unsigned int chars_to_send; + + /* Acknowledge dma data descriptor irq, if there was one. */ + REG_WR(dma, regi_dmaout, rw_ack_intr, ack_intr); + + /* + * First get the amount of bytes sent during the last DMA transfer, + * and update xmit accordingly. + */ + status = REG_RD(dma, regi_dmaout, rw_stat); + if (status.list_state == regk_dma_data_at_eol || !up->tx_started) + dmapos = phys_to_virt((int)up->last_tx_descr->next); + else + dmapos = phys_to_virt(REG_RD_INT(dma, regi_dmaout, rw_data)); + + pending_descr = up->first_tx_descr; + while (pending_descr != dmapos) { + sentl += pending_descr->after - pending_descr->buf; + pending_descr->after = pending_descr->buf = NULL; + pending_descr = phys_to_virt((int)pending_descr->next); + } + + up->first_tx_descr = pending_descr; + last_tx_descr = up->last_tx_descr; + + /* Update stats. */ + up->port.icount.tx += sentl; + + up->tx_pending_chars -= sentl; + + /* Update xmit buffer. */ + xmit->tail = (xmit->tail + sentl) & (UART_XMIT_SIZE - 1); + + /* + * Find out the largest amount of consecutive bytes we want to send + * now. + */ + chars_in_q = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + if (chars_in_q == 0) + /* Tell upper layers that we're now idle. */ + goto done; + + /* Some of those characters are actually pending output. */ + chars_to_send = chars_in_q - up->tx_pending_chars; + + /* + * Clamp the new number of pending chars to the advertised + * one. + */ + if (chars_to_send + up->tx_pending_chars > up->port.fifosize) + chars_to_send = up->port.fifosize - up->tx_pending_chars; + + /* If we don't want to send any, we're done. */ + if (chars_to_send == 0) + goto done; + + descr = phys_to_virt((int)last_tx_descr->next); + + /* + * We can't send anything if we could make the condition in + * the while-loop above (reaping finished descriptors) be met + * immediately before the first iteration. However, don't + * mistake the full state for the empty state. + */ + if ((descr == up->first_tx_descr && up->tx_pending_chars != 0) + || descr->next == up->first_tx_descr) + goto done; + + /* Set up the descriptor for output. */ + descr->buf = (void*)virt_to_phys(xmit->buf + xmit->tail + + up->tx_pending_chars); + descr->after = descr->buf + chars_to_send; + descr->eol = 1; + descr->out_eop = 0; + descr->intr = 1; + descr->wait = 0; + descr->in_eop = 0; + descr->md = 0; + /* + * Make sure GCC doesn't move this eol clear before the eol set + * above. + */ + barrier(); + last_tx_descr->eol = 0; + + up->last_tx_descr = descr; + up->tx_pending_chars += chars_to_send; + + if (!up->tx_started) { + up->tx_started = 1; + up->tr_context_descr.next = 0; + up->tr_context_descr.saved_data + = (dma_descr_data*)virt_to_phys(descr); + up->tr_context_descr.saved_data_buf = descr->buf; + DMA_START_CONTEXT(regi_dmaout, + virt_to_phys(&up->tr_context_descr)); + } else + DMA_CONTINUE_DATA(regi_dmaout); + + /* DMA is now running (hopefully). */ + + done: + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} + +static void +transmit_chars_no_dma(struct uart_cris_port *up) +{ + int max_count; + struct circ_buf *xmit = &up->port.state->xmit; + + reg_scope_instances regi_ser = up->regi_ser; + reg_ser_r_stat_din rstat; + reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes }; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + /* No more to send, so disable the interrupt. */ + reg_ser_rw_intr_mask intr_mask; + intr_mask = REG_RD(ser, regi_ser, rw_intr_mask); + intr_mask.tr_rdy = 0; + intr_mask.tr_empty = 0; + REG_WR(ser, regi_ser, rw_intr_mask, intr_mask); + up->write_ongoing=0; + return; + } + + /* If the serport is fast, we send up to max_count bytes before + exiting the loop. */ + max_count = 64; + do { + reg_ser_rw_dout dout = { .data = xmit->buf[xmit->tail] }; + REG_WR(ser, regi_ser, rw_dout, dout); + REG_WR(ser, regi_ser, rw_ack_intr, ack_intr); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + up->port.icount.tx++; + if (xmit->head == xmit->tail) + break; + rstat = REG_RD(ser, regi_ser, r_stat_din); + } while ((--max_count > 0) && rstat.tr_rdy); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} /* transmit_chars_no_dma */ + +static struct etrax_recv_buffer * +alloc_recv_buffer(unsigned int size) +{ + struct etrax_recv_buffer *buffer; + + if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC))) + panic("%s: Could not allocate %d bytes buffer\n", + __FUNCTION__, size); + + buffer->next = NULL; + buffer->length = 0; + buffer->error = TTY_NORMAL; + + return buffer; +} + +static void +append_recv_buffer(struct uart_cris_port *up, + struct etrax_recv_buffer *buffer) +{ + unsigned long flags; + + local_irq_save(flags); + + if (!up->first_recv_buffer) + up->first_recv_buffer = buffer; + else + up->last_recv_buffer->next = buffer; + + up->last_recv_buffer = buffer; + + up->recv_cnt += buffer->length; + if (up->recv_cnt > up->max_recv_cnt) + up->max_recv_cnt = up->recv_cnt; + + local_irq_restore(flags); +} + +static int +add_char_and_flag(struct uart_cris_port *up, unsigned char data, + unsigned char flag) +{ + struct etrax_recv_buffer *buffer; + + buffer = alloc_recv_buffer(4); + buffer->length = 1; + buffer->error = flag; + buffer->buffer[0] = data; + + append_recv_buffer(up, buffer); + + up->port.icount.rx++; + + return 1; +} + +static void +flush_to_flip_buffer(struct uart_cris_port *up) +{ + struct etrax_recv_buffer *buffer; + + if (!up->first_recv_buffer) + return; + + while ((buffer = up->first_recv_buffer)) { + unsigned int count = (unsigned int) + tty_insert_flip_string(&up->port.state->port, + buffer->buffer, + buffer->length); + + up->recv_cnt -= count; + + if (count == buffer->length) { + up->first_recv_buffer = buffer->next; + kfree(buffer); + } else { + buffer->length -= count; + memmove(buffer->buffer, buffer->buffer + count, + buffer->length); + buffer->error = TTY_NORMAL; + } + } + + if (!up->first_recv_buffer) + up->last_recv_buffer = NULL; + + /* This call includes a check for low-latency. */ + tty_flip_buffer_push(&up->port.state->port); +} + +static unsigned int +handle_descr_data(struct uart_cris_port *up, struct dma_descr_data *descr, + unsigned int recvl) +{ + struct etrax_recv_buffer *buffer + = phys_to_virt((unsigned long)descr->buf) - sizeof *buffer; + + if (up->recv_cnt + recvl > 65536) { + printk(KERN_ERR "Too much pending incoming data on %s!" + " Dropping %u bytes.\n", up->port.state->port.tty->name, + recvl); + return 0; + } + + buffer->length = recvl; + + append_recv_buffer(up, buffer); + + flush_to_flip_buffer(up); + + buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE); + descr->buf = (void*)virt_to_phys(buffer->buffer); + descr->after = descr->buf + SERIAL_DESCR_BUF_SIZE; + + return recvl; +} + +static unsigned int +handle_all_descr_data(struct uart_cris_port *up) +{ + struct dma_descr_data *descr + = &up->rec_descr[(up->cur_rec_descr - 1) + % SERIAL_RECV_DESCRIPTORS]; + struct dma_descr_data *prev_descr; + unsigned int recvl; + unsigned int ret = 0; + reg_scope_instances regi_dmain = up->regi_dmain; + + while (1) { + prev_descr = descr; + descr = &up->rec_descr[up->cur_rec_descr]; + + if (descr == phys_to_virt(REG_RD(dma, regi_dmain, rw_data))) + break; + + if (++up->cur_rec_descr == SERIAL_RECV_DESCRIPTORS) + up->cur_rec_descr = 0; + + /* Find out how many bytes were read. */ + recvl = descr->after - descr->buf; + + /* Update stats. */ + up->port.icount.rx += recvl; + + ret += handle_descr_data(up, descr, recvl); + descr->eol = 1; + /* + * Make sure GCC doesn't move this eol clear before the + * eol set above. + */ + barrier(); + prev_descr->eol = 0; + flush_dma_descr(descr,1); // Cache bug workaround + flush_dma_descr(prev_descr,0); // Cache bug workaround + } + + return ret; +} + +static void +receive_chars_dma(struct uart_cris_port *up) +{ + reg_ser_r_stat_din rstat; + reg_dma_rw_ack_intr ack_intr = {0}; + + /* Acknowledge both dma_descr and dma_eop irq. */ + ack_intr.data = 1; + ack_intr.in_eop = 1; + REG_WR(dma, up->regi_dmain, rw_ack_intr, ack_intr); + + handle_all_descr_data(up); + + /* Read the status register to detect errors. */ + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + + if (rstat.framing_err | rstat.par_err | rstat.orun) { + /* + * If we got an error, we must reset it by reading the + * rs_stat_din register and put the data in buffer manually. + */ + reg_ser_rs_stat_din stat_din; + stat_din = REG_RD(ser, up->regi_ser, rs_stat_din); + + if (stat_din.par_err) + add_char_and_flag(up, stat_din.data, TTY_PARITY); + else if (stat_din.orun) + add_char_and_flag(up, stat_din.data, TTY_OVERRUN); + else if (stat_din.framing_err) + add_char_and_flag(up, stat_din.data, TTY_FRAME); + } + + /* Restart the receiving DMA, in case it got stuck on an EOL. */ + DMA_CONTINUE_DATA(up->regi_dmain); +} + +void receive_chars_no_dma(struct uart_cris_port *up) +{ + reg_ser_rs_stat_din stat_din; + reg_ser_r_stat_din rstat; + struct uart_icount *icount; + int max_count = 16; + char flag; + reg_ser_rw_ack_intr ack_intr = { 0 }; + + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + up->last_rx_active_usec = GET_JIFFIES_USEC(); + up->last_rx_active = jiffies; + icount = &up->port.icount; + + do { + stat_din = REG_RD(ser, up->regi_ser, rs_stat_din); + + flag = TTY_NORMAL; + ack_intr.dav = 1; + REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr); + icount->rx++; + + if (stat_din.framing_err | stat_din.par_err | stat_din.orun) { + if (stat_din.data == 0x00 && + stat_din.framing_err) { + /* Most likely a break. */ + flag = TTY_BREAK; + icount->brk++; + } else if (stat_din.par_err) { + flag = TTY_PARITY; + icount->parity++; + } else if (stat_din.orun) { + flag = TTY_OVERRUN; + icount->overrun++; + } else if (stat_din.framing_err) { + flag = TTY_FRAME; + icount->frame++; + } + } + + /* + * If this becomes important, we probably *could* handle this + * gracefully by keeping track of the unhandled character. + */ + if (!tty_insert_flip_char(&up->port.state->port, stat_din.data, flag)) + panic("%s: No tty buffer space", __FUNCTION__); + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + } while (rstat.dav && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(&up->port.state->port); + spin_lock(&up->port.lock); +} /* receive_chars_no_dma */ + +/* + * DMA output channel interrupt handler. + * this interrupt is called from DMA2(ser2), DMA8(ser3), DMA6(ser0) or + * DMA4(ser1) when they have finished a descriptor with the intr flag set. + */ +static irqreturn_t dma_tr_interrupt(int irq, void *dev_id) +{ + struct uart_cris_port *up = (struct uart_cris_port *)dev_id; + reg_dma_r_masked_intr masked_intr; + reg_scope_instances regi_dmaout; + int handled = 0; + + spin_lock(&up->port.lock); + regi_dmaout = up->regi_dmaout; + if (!regi_dmaout) { + spin_unlock(&up->port.lock); + return IRQ_NONE; + } + + /* + * Check for dma_descr (don't need to check for dma_eop in + * output DMA for serial). + */ + masked_intr = REG_RD(dma, regi_dmaout, r_masked_intr); + + if (masked_intr.data) { + /* We can send a new dma bunch. make it so. */ + + /* + * Read jiffies_usec first. + * We want this time to be as late as possible. + */ + up->last_tx_active_usec = GET_JIFFIES_USEC(); + up->last_tx_active = jiffies; + transmit_chars_dma(up); + handled = 1; + } + check_modem_status(up); + spin_unlock(&up->port.lock); + return IRQ_RETVAL(handled); +} + +/* DMA input channel interrupt handler. */ + +static irqreturn_t +dma_rec_interrupt(int irq, void *dev_id) +{ + struct uart_cris_port *up = (struct uart_cris_port *)dev_id; + reg_dma_r_masked_intr masked_intr; + reg_scope_instances regi_dmain; + int handled = 0; + + spin_lock(&up->port.lock); + regi_dmain = up->regi_dmain; + if (!regi_dmain) { + spin_unlock(&up->port.lock); + return IRQ_NONE; + } + + /* Check for both dma_eop and dma_descr for the input dma channel. */ + masked_intr = REG_RD(dma, regi_dmain, r_masked_intr); + if (masked_intr.data || masked_intr.in_eop) { + /* We have received something. */ + receive_chars_dma(up); + handled = 1; + } + check_modem_status(up); + spin_unlock(&up->port.lock); + return IRQ_RETVAL(handled); +} + +/* "Normal" serial port interrupt handler - both rx and tx. */ + +static irqreturn_t +ser_interrupt(int irq, void *dev_id) +{ + struct uart_cris_port *up = (struct uart_cris_port *)dev_id; + reg_scope_instances regi_ser; + int handled = 0; + + spin_lock(&up->port.lock); + if (up->regi_dmain && up->regi_dmaout) { + spin_unlock(&up->port.lock); + return IRQ_NONE; + } + + regi_ser = up->regi_ser; + + if (regi_ser) { + reg_ser_r_masked_intr masked_intr; + masked_intr = REG_RD(ser, regi_ser, r_masked_intr); + /* + * Check what interrupts are active before taking + * actions. If DMA is used the interrupt shouldn't + * be enabled. + */ + if (masked_intr.dav) { + receive_chars_no_dma(up); + handled = 1; + } + check_modem_status(up); + + if (masked_intr.tr_rdy) { + transmit_chars_no_dma(up); + handled = 1; + } + } + spin_unlock(&up->port.lock); + return IRQ_RETVAL(handled); +} /* ser_interrupt */ + +static int start_recv_dma(struct uart_cris_port *up) +{ + struct dma_descr_data *descr = up->rec_descr; + struct etrax_recv_buffer *buffer; + int i; + + /* Set up the receiving descriptors. */ + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { + buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE); + descr[i].next = (void*)virt_to_phys(&descr[i+1]); + descr[i].buf = (void*)virt_to_phys(buffer->buffer); + descr[i].after = descr[i].buf + SERIAL_DESCR_BUF_SIZE; + descr[i].eol = 0; + descr[i].out_eop = 0; + descr[i].intr = 1; + descr[i].wait = 0; + descr[i].in_eop = 0; + descr[i].md = 0; + + } + + /* Link the last descriptor to the first. */ + descr[i-1].next = (void*)virt_to_phys(&descr[0]); + + /* And mark it as end of list. */ + descr[i-1].eol = 1; + + /* Start with the first descriptor in the list. */ + up->cur_rec_descr = 0; + up->rec_context_descr.next = 0; + up->rec_context_descr.saved_data + = (dma_descr_data *)virt_to_phys(&descr[up->cur_rec_descr]); + up->rec_context_descr.saved_data_buf = descr[up->cur_rec_descr].buf; + + /* Start the DMA. */ + DMA_START_CONTEXT(up->regi_dmain, + virt_to_phys(&up->rec_context_descr)); + + /* Input DMA should be running now. */ + return 1; +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial_artpec_get_poll_char(struct uart_port *port) +{ + reg_ser_rs_stat_din stat; + reg_ser_rw_ack_intr ack_intr = { 0 }; + struct uart_cris_port *up = (struct uart_cris_port *)port; + + do { + stat = REG_RD(ser, up->regi_ser, rs_stat_din); + } while (!stat.dav); + + /* Ack the data_avail interrupt. */ + ack_intr.dav = 1; + REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr); + + return stat.data; +} + +static void serial_artpec_put_poll_char(struct uart_port *port, + unsigned char c) +{ + reg_ser_r_stat_din stat; + struct uart_cris_port *up = (struct uart_cris_port *)port; + + do { + stat = REG_RD (ser, up->regi_ser, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT (ser, up->regi_ser, rw_dout, c); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static void start_receive(struct uart_cris_port *up) +{ + reg_scope_instances regi_dmain = up->regi_dmain; + if (regi_dmain) { + start_recv_dma(up); + } +} + + +static void start_transmitter(struct uart_cris_port *up) +{ + int i; + reg_scope_instances regi_dmaout = up->regi_dmaout; + if (regi_dmaout) { + for (i = 0; i < SERIAL_TX_DESCRIPTORS; i++) { + memset(&up->tr_descr[i], 0, sizeof(up->tr_descr[i])); + up->tr_descr[i].eol = 1; + up->tr_descr[i].intr = 1; + up->tr_descr[i].next = (dma_descr_data *) + virt_to_phys(&up->tr_descr[i+1]); + } + up->tr_descr[i-1].next = (dma_descr_data *) + virt_to_phys(&up->tr_descr[0]); + up->first_tx_descr = &up->tr_descr[0]; + + /* + * We'll be counting up to up->last_tx_descr->next from + * up->first_tx_descr when starting DMA, so we should make + * them the same for the very first round. If instead we'd + * set last_tx_descr = first_tx_descr, we'd rely on + * accidentally working code and data as we'd take a pass over + * the first, unused, descriptor. + */ + up->last_tx_descr = &up->tr_descr[i-1]; + up->tx_started = 0; + up->tx_pending_chars = 0; + } +} + +static int serial_cris_startup(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + reg_ser_rw_intr_mask ser_intr_mask = {0}; + reg_dma_rw_intr_mask dmain_intr_mask = {0}; + reg_dma_rw_intr_mask dmaout_intr_mask = {0}; + reg_dma_rw_cfg cfg = {.en = 1}; + reg_scope_instances regi_dma; + + /* We dont disable interrupts here because request_irq should + not be called with ints disabled. */ + spin_lock(&up->port.lock); + + dmain_intr_mask.data = dmain_intr_mask.in_eop = regk_dma_yes; + dmaout_intr_mask.data = regk_dma_yes; + if (!up->regi_dmain) + ser_intr_mask.dav = regk_ser_yes; + + if (port->line == 0) { + if (request_irq(SER0_INTR_VECT, ser_interrupt, + IRQF_DISABLED, "ser0", + &serial_cris_ports[0])) + panic("irq ser0"); + /* Port ser0 can use dma6 for tx and dma7 for rx. */ +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT + if (request_irq(DMA6_INTR_VECT, dma_tr_interrupt, + IRQF_DISABLED, "serial 0 dma tr", + &serial_cris_ports[0])) + panic("irq ser0txdma"); + crisv32_request_dma(6, "ser0", DMA_PANIC_ON_ERROR, 0, + dma_ser0); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN + if (request_irq(DMA7_INTR_VECT, dma_rec_interrupt, + IRQF_DISABLED, "serial 0 dma rec", + &serial_cris_ports[0])) + panic("irq ser0rxdma"); + crisv32_request_dma(7, "ser0", DMA_PANIC_ON_ERROR, 0, + dma_ser0); +#endif + } else if (port->line == 1) { + if (request_irq(SER1_INTR_VECT, ser_interrupt, + IRQF_DISABLED, "ser1", + &serial_cris_ports[1])) + panic("irq ser1"); + + /* Port ser1 can use dma4 for tx and dma5 for rx. */ +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA4_OUT + if (request_irq(DMA4_INTR_VECT, dma_tr_interrupt, + IRQF_DISABLED, "serial 1 dma tr", + &serial_cris_ports[1])) + panic("irq ser1txdma"); + crisv32_request_dma(4, "ser1", DMA_PANIC_ON_ERROR, 0, + dma_ser1); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN + if (request_irq(DMA5_INTR_VECT, dma_rec_interrupt, + IRQF_DISABLED, "serial 1 dma rec", + &serial_cris_ports[1])) + panic("irq ser1rxdma"); + crisv32_request_dma(5, "ser1", DMA_PANIC_ON_ERROR, 0, + dma_ser1); +#endif + } else if (port->line == 2) { + if (request_irq(SER2_INTR_VECT, ser_interrupt, + IRQF_DISABLED, "ser2", + &serial_cris_ports[2])) + panic("irq ser2"); + + /* Port ser2 can use dma2 for tx and dma3 for rx. */ +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + if (request_irq(DMA2_INTR_VECT, dma_tr_interrupt, + IRQF_DISABLED, "serial 2 dma tr", + &serial_cris_ports[2])) + panic("irq ser2txdma"); + crisv32_request_dma(2, "ser2", DMA_PANIC_ON_ERROR, 0, + dma_ser2); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN + if (request_irq(DMA3_INTR_VECT, dma_rec_interrupt, + IRQF_DISABLED, "serial 2 dma rec", + &serial_cris_ports[2])) + panic("irq ser2rxdma"); + crisv32_request_dma(3, "ser2", DMA_PANIC_ON_ERROR, 0, + dma_ser2); +#endif + } else if (port->line == 3) { + if (request_irq(SER3_INTR_VECT, ser_interrupt, + IRQF_DISABLED, "ser3", + &serial_cris_ports[3])) + panic("irq ser3" ); + + /* Port ser3 can use dma8 for tx and dma9 for rx. */ +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA8_OUT + if (request_irq(DMA8_INTR_VECT, dma_tr_interrupt, + IRQF_DISABLED, "serial 3 dma tr", + &serial_cris_ports[3])) + panic("irq ser3txdma"); + crisv32_request_dma(8, "ser3", DMA_PANIC_ON_ERROR, 0, + dma_ser3); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN + if (request_irq(DMA9_INTR_VECT, dma_rec_interrupt, + IRQF_DISABLED, "serial 3 dma rec", + &serial_cris_ports[3])) + panic("irq ser3rxdma"); + crisv32_request_dma(9, "ser3", DMA_PANIC_ON_ERROR, 0, + dma_ser3); +#endif + } +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + else if (port->line == 4) { + if (request_irq(SER4_INTR_VECT, ser_interrupt, + IRQF_DISABLED, "ser4", + &serial_cris_ports[4])) + panic("irq ser4" ); + +#ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA_OUT + if (request_irq(DMA4_INTR_VECT, dma_tr_interrupt, + IRQF_DISABLED, "serial 4 dma tr", + &serial_cris_ports[4])) + panic("irq ser4txdma"); + crisv32_request_dma(5, "ser4", DMA_PANIC_ON_ERROR, 0, + dma_ser4); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA_IN + if (request_irq(DMA5_INTR_VECT, dma_rec_interrupt, + IRQF_DISABLED, "serial 4 dma rec", + &serial_cris_ports[4])) + panic("irq ser4rxdma"); + crisv32_request_dma(5, "ser4", DMA_PANIC_ON_ERROR, 0, + dma_ser4); +#endif + } +#endif + + local_irq_save(flags); + + /* + * Reset the DMA channels and make sure their interrupts are cleared. + */ + + regi_dma = up->regi_dmain; + if (regi_dma) { + reg_dma_rw_ack_intr ack_intr = { 0 }; + DMA_RESET(regi_dma); + /* Wait until reset cycle is complete. */ + DMA_WAIT_UNTIL_RESET(regi_dma); + REG_WR(dma, regi_dma, rw_cfg, cfg); + /* Make sure the irqs are cleared. */ + ack_intr.group = 1; + ack_intr.ctxt = 1; + ack_intr.data = 1; + ack_intr.in_eop = 1; + ack_intr.stream_cmd = 1; + REG_WR(dma, regi_dma, rw_ack_intr, ack_intr); + } + regi_dma = up->regi_dmaout; + if (regi_dma) { + reg_dma_rw_ack_intr ack_intr = { 0 }; + DMA_RESET(regi_dma); + /* Wait until reset cycle is complete. */ + DMA_WAIT_UNTIL_RESET(regi_dma); + REG_WR(dma, regi_dma, rw_cfg, cfg); + /* Make sure the irqs are cleared. */ + ack_intr.group = 1; + ack_intr.ctxt = 1; + ack_intr.data = 1; + ack_intr.in_eop = 1; + ack_intr.stream_cmd = 1; + REG_WR(dma, regi_dma, rw_ack_intr, ack_intr); + } + + REG_WR(ser, up->regi_ser, rw_intr_mask, ser_intr_mask); + if (up->regi_dmain) + REG_WR(dma, up->regi_dmain, rw_intr_mask, dmain_intr_mask); + if (up->regi_dmaout) + REG_WR(dma, up->regi_dmaout, rw_intr_mask, dmaout_intr_mask); + + start_receive(up); + start_transmitter(up); + + serial_cris_set_mctrl(&up->port, up->port.mctrl); + + local_irq_restore(flags); + spin_unlock(&up->port.lock); + + return 0; +} + +static void serial_cris_shutdown(struct uart_port *port) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + serial_cris_stop_tx(port); + serial_cris_stop_rx(port); + + if (port->line == 0) { + free_irq(SER0_INTR_VECT, &serial_cris_ports[0]); +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT + crisv32_free_dma(6); + free_irq(DMA6_INTR_VECT, &serial_cris_ports[0]); +#elif defined(CONFIG_ETRAX_SERIAL_PORT0_DMA0_OUT) + crisv32_free_dma(0); + free_irq(DMA0_INTR_VECT, &serial_cris_ports[0]); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN + crisv32_free_dma(7); + free_irq(DMA7_INTR_VECT, &serial_cris_ports[0]); +#elif defined(CONFIG_ETRAX_SERIAL_PORT0_DMA1_IN) + crisv32_free_dma(1); + free_irq(DMA1_INTR_VECT, &serial_cris_ports[0]); +#endif + } else if (port->line == 1) { + free_irq(SER1_INTR_VECT, &serial_cris_ports[1]); +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA4_OUT + crisv32_free_dma(4); + free_irq(DMA4_INTR_VECT, &serial_cris_ports[1]); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA5_IN + crisv32_free_dma(5); + free_irq(DMA5_INTR_VECT, &serial_cris_ports[1]); +#endif + } else if (port->line == 2) { + free_irq(SER2_INTR_VECT, &serial_cris_ports[2]); +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + crisv32_free_dma(2); + free_irq(DMA2_INTR_VECT, &serial_cris_ports[2]); +#elif defined(CONFIG_ETRAX_SERIAL_PORT2_DMA6_OUT) + crisv32_free_dma(6); + free_irq(DMA6_INTR_VECT, &serial_cris_ports[2]); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN + crisv32_free_dma(3); + free_irq(DMA3_INTR_VECT, &serial_cris_ports[2]); +#elif defined(CONFIG_ETRAX_SERIAL_PORT2_DMA7_IN) + crisv32_free_dma(7); + free_irq(DMA7_INTR_VECT, &serial_cris_ports[2]); +#endif + } else if (port->line == 3) { + free_irq(SER3_INTR_VECT, &serial_cris_ports[3]); +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA8_OUT + crisv32_free_dma(8); + free_irq(DMA8_INTR_VECT, &serial_cris_ports[3]); +#elif defined(CONFIG_ETRAX_SERIAL_PORT3_DMA2_OUT) + crisv32_free_dma(2); + free_irq(DMA2_INTR_VECT, &serial_cris_ports[3]); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA9_IN + crisv32_free_dma(9); + free_irq(DMA9_INTR_VECT, &serial_cris_ports[3]); +#elif defined(CONFIG_ETRAX_SERIAL_PORT3_DMA3_IN) + crisv32_free_dma(3); + free_irq(DMA3_INTR_VECT, &serial_cris_ports[3]); +#endif + } +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + else if (port->line == 4) { + free_irq(SER4_INTR_VECT, &serial_cris_ports[4]); +#ifdef CONFIG_ETRAX_SERIAL_PORT4_DMA9_IN + crisv32_free_dma(9); + free_irq(DMA9_INTR_VECT, &serial_cris_ports[4]); +#endif + } +#endif + + serial_cris_set_mctrl(&up->port, up->port.mctrl); + + if (up->regi_dmain) { + struct etrax_recv_buffer *rb; + struct etrax_recv_buffer *rb_next; + int i; + struct dma_descr_data *descr; + + /* + * In case of DMA and receive errors, there might be pending + * receive buffers still linked here and not flushed upwards. + * Release them. + */ + for (rb = up->first_recv_buffer; rb != NULL; rb = rb_next) { + rb_next = rb->next; + kfree (rb); + } + up->first_recv_buffer = NULL; + up->last_recv_buffer = NULL; + + /* + * Also release buffers that were attached to the DMA + * before we shut down the hardware above. + */ + for (i = 0, descr = up->rec_descr; + i < SERIAL_RECV_DESCRIPTORS; + i++) + if (descr[i].buf) { + rb = phys_to_virt((u32) descr[i].buf) + - sizeof *rb; + kfree(rb); + descr[i].buf = NULL; + } + } + + spin_unlock_irqrestore(&up->port.lock, flags); + +} + +static void +serial_cris_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + unsigned long flags; + reg_ser_rw_xoff xoff; + reg_ser_rw_xoff_clr xoff_clr = {0}; + reg_ser_rw_tr_ctrl tx_ctrl = {0}; + reg_ser_rw_tr_dma_en tx_dma_en = {0}; + reg_ser_rw_rec_ctrl rx_ctrl = {0}; + reg_ser_rw_tr_baud_div tx_baud_div = {0}; + reg_ser_rw_rec_baud_div rx_baud_div = {0}; + reg_ser_r_stat_din rstat; + int baud; + + if (old && + termios->c_cflag == old->c_cflag && + termios->c_iflag == old->c_iflag) + return; + + /* Start with default settings and then fill in changes. */ + + /* Tx: 8 bit, no/even parity, 1 stop bit, no cts. */ + tx_ctrl.base_freq = regk_ser_f29_493; + tx_ctrl.en = 0; + tx_ctrl.stop = 0; +#ifdef CONFIG_ETRAX_RS485 + if (up->rs485.enabled && (up->port_type != TYPE_485FD)) { + tx_ctrl.auto_rts = regk_ser_yes; + tx_ctrl.rts_setup = regk_ser_bits2; + + if (termios->c_cflag & CSTOPB) { + /* 2 stop bits. */ + tx_ctrl.rts_delay = regk_ser_del2; + } + else { + /* 1 stop bits. */ + tx_ctrl.rts_delay = regk_ser_del1; + } + } else +#endif + tx_ctrl.auto_rts = regk_ser_no; + tx_ctrl.txd = 1; + tx_ctrl.auto_cts = 0; + /* Rx: 8 bit, no/even parity. */ + if (up->regi_dmain) { + rx_ctrl.dma_mode = 1; + rx_ctrl.auto_eop = 1; + } + rx_ctrl.dma_err = regk_ser_stop; + rx_ctrl.sampling = regk_ser_majority; + rx_ctrl.timeout = 1; + +#ifdef CONFIG_ETRAX_RS485 + if (up->rs485.enabled && (up->port_type != TYPE_485FD)) { +# ifdef CONFIG_ETRAX_RS485_DISABLE_RECEIVER + rx_ctrl.half_duplex = regk_ser_yes; +# endif + rx_ctrl.rts_n = up->rs485.rts_after_sent ? + regk_ser_active : regk_ser_inactive; + } else if (up->port_type == TYPE_485FD) { + rx_ctrl.rts_n = regk_ser_active; + } else +#endif + rx_ctrl.rts_n = regk_ser_inactive; + + /* Common for tx and rx: 8N1. */ + tx_ctrl.data_bits = regk_ser_bits8; + rx_ctrl.data_bits = regk_ser_bits8; + tx_ctrl.par = regk_ser_even; + rx_ctrl.par = regk_ser_even; + tx_ctrl.par_en = regk_ser_no; + rx_ctrl.par_en = regk_ser_no; + + tx_ctrl.stop_bits = regk_ser_bits1; + + + /* Change baud-rate and write it to the hardware. */ + + /* baud_clock = base_freq / (divisor*8) + * divisor = base_freq / (baud_clock * 8) + * base_freq is either: + * off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz + * 20.493MHz is used for standard baudrates + */ + + /* + * For the console port we keep the original baudrate here. Not very + * beautiful. + */ + if ((port != console_port) || old) + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / 8); + else + baud = console_baud; + + tx_baud_div.div = 29493000 / (8 * baud); + /* Rx uses same as tx. */ + rx_baud_div.div = tx_baud_div.div; + rx_ctrl.base_freq = tx_ctrl.base_freq; + + if ((termios->c_cflag & CSIZE) == CS7) { + /* Set 7 bit mode. */ + tx_ctrl.data_bits = regk_ser_bits7; + rx_ctrl.data_bits = regk_ser_bits7; + } + + if (termios->c_cflag & CSTOPB) { + /* Set 2 stop bit mode. */ + tx_ctrl.stop_bits = regk_ser_bits2; + } + + if (termios->c_cflag & PARENB) { + /* Enable parity. */ + tx_ctrl.par_en = regk_ser_yes; + rx_ctrl.par_en = regk_ser_yes; + } + + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) { + /* Set mark parity if PARODD and CMSPAR. */ + tx_ctrl.par = regk_ser_mark; + rx_ctrl.par = regk_ser_mark; + } else { + tx_ctrl.par = regk_ser_space; + rx_ctrl.par = regk_ser_space; + } + } else { + if (termios->c_cflag & PARODD) { + /* Set odd parity. */ + tx_ctrl.par = regk_ser_odd; + rx_ctrl.par = regk_ser_odd; + } + } + + if (termios->c_cflag & CRTSCTS) { + /* Enable automatic CTS handling. */ + tx_ctrl.auto_cts = regk_ser_yes; + } + + /* Make sure the tx and rx are enabled. */ + tx_ctrl.en = regk_ser_yes; + rx_ctrl.en = regk_ser_yes; + + /* + * Wait for tr_idle in case a character is being output, so it won't + * be damaged by the changes we do below. It seems the termios + * changes "sometimes" (we can't see e.g. a tcsetattr TCSANOW + * parameter here) should take place no matter what state. However, + * in case we should wait, we may have a non-empty transmitter state + * as we tell the upper layers that we're all done when we've passed + * characters to the hardware, but we don't wait for them being + * actually shifted out. + */ + spin_lock_irqsave(&port->lock, flags); + + /* + * None of our interrupts re-enable DMA, so it's thankfully ok to + * disable it once, outside the loop. + */ + tx_dma_en.en = 0; + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en); + do { + /* + * Make sure we have integrity between the read r_stat status + * and us writing the registers below, but don't busy-wait + * with interrupts off. We need to keep the port lock though + * (if we go SMP), so nobody else writes characters. + */ + local_irq_restore(flags); + local_irq_save(flags); + rstat = REG_RD(ser, up->regi_ser, r_stat_din); + } while (!rstat.tr_idle); + + /* Actually write the control regs (if modified) to the hardware. */ + + uart_update_timeout(port, termios->c_cflag, port->uartclk/8); + MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div); + MODIFY_REG(up->regi_ser, rw_rec_ctrl, rx_ctrl); + + MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div); + MODIFY_REG(up->regi_ser, rw_tr_ctrl, tx_ctrl); + + tx_dma_en.en = up->regi_dmaout != 0; + REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en); + + xoff = REG_RD(ser, up->regi_ser, rw_xoff); + + if (up->port.state && up->port.state->port.tty && + (termios->c_iflag & IXON)) { + xoff.chr = STOP_CHAR(up->port.state->port.tty); + xoff.automatic = regk_ser_yes; + } else + xoff.automatic = regk_ser_no; + + MODIFY_REG(up->regi_ser, rw_xoff, xoff); + + /* + * Make sure we don't start in an automatically shut-off state due to + * a previous early exit. + */ + xoff_clr.clr = 1; + REG_WR(ser, up->regi_ser, rw_xoff_clr, xoff_clr); + + serial_cris_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static const char * +serial_cris_type(struct uart_port *port) +{ + return "CRISv32"; +} + +static void serial_cris_release_port(struct uart_port *port) +{ +} + +static int serial_cris_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_cris_config_port(struct uart_port *port, int flags) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + up->port.type = PORT_CRIS; +} + +#if defined(CONFIG_ETRAX_RS485) + +static void cris_set_rs485_mode(struct uart_cris_port* up) { + reg_ser_rw_tr_ctrl tr_ctrl; + reg_ser_rw_rec_ctrl rec_ctrl; + reg_scope_instances regi_ser = up->regi_ser; + + if (up->port_type == TYPE_485FD) + /* We do not want to change anything if we are in 485FD mode */ + return; + + tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl); + rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); + + /* Set port in RS-485 mode */ + if (up->rs485.enabled) { + tr_ctrl.auto_rts = regk_ser_yes; + rec_ctrl.rts_n = up->rs485.rts_after_sent ? + regk_ser_active : regk_ser_inactive; + } + /* Set port to RS-232 mode */ + else { + rec_ctrl.rts_n = regk_ser_inactive; + tr_ctrl.auto_rts = regk_ser_no; + rec_ctrl.half_duplex = regk_ser_no; + } + + REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); + REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); +} + +/* Enable/disable RS-485 mode on selected port. */ +static int +cris_enable_rs485(struct uart_cris_port* up, struct rs485_control *r) +{ + if (up->port_type == TYPE_485FD) + /* Port in 485FD mode can not chage mode */ + goto out; + + up->rs485.enabled = 0x1 & r->enabled; + up->rs485.rts_on_send = 0x01 & r->rts_on_send; + up->rs485.rts_after_sent = 0x01 & r->rts_after_sent; + up->rs485.delay_rts_before_send = r->delay_rts_before_send; + + cris_set_rs485_mode(up); + out: + return 0; +} + + +/* Enable RS485 mode on port and send the data. Port will stay + * in 485 mode after the data has been sent. + */ +static int +cris_write_rs485(struct uart_cris_port *up, const unsigned char* buf, int count) +{ + up->rs485.enabled = 1; + + /* Set the port in RS485 mode */ + cris_set_rs485_mode(up); + + /* Send the data */ + count = serial_cris_driver.tty_driver->ops->write(up->port.state->port.tty, buf, count); + + return count; +} + +#endif /* CONFIG_ETRAX_RS485 */ + +static int serial_cris_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ +#if defined(CONFIG_ETRAX_RS485) + struct uart_cris_port *up = (struct uart_cris_port *)port; + + switch (cmd) { + case TIOCSRS485: { + struct serial_rs485 in; + struct rs485_control rs485ctrl; + if (copy_from_user(&in, (struct serial_rs485 *)cmd, + sizeof(rs485ctrl))) + return -EFAULT; + rs485ctrl.rts_on_send = in.flags & SER_RS485_RTS_ON_SEND; + rs485ctrl.rts_after_sent = in.flags & SER_RS485_RTS_AFTER_SEND; + rs485ctrl.enabled = in.flags & SER_RS485_ENABLED; + rs485ctrl.delay_rts_before_send = in.delay_rts_before_send; + + return cris_enable_rs485(up, &rs485ctrl); + } + + case TIOCSERSETRS485: { + struct rs485_control rs485ctrl; + if (copy_from_user(&rs485ctrl, (struct rs485_control*) arg, + sizeof(rs485ctrl))) + return -EFAULT; + + return cris_enable_rs485(up, &rs485ctrl); + } + + case TIOCSERWRRS485: { + struct rs485_write rs485wr; + if (copy_from_user(&rs485wr, (struct rs485_write*)arg, + sizeof(rs485wr))) + return -EFAULT; + + return cris_write_rs485(up, rs485wr.outc, rs485wr.outc_size); + } + + case TIOCSERSETRS485FD: { + reg_scope_instances regi_ser = up->regi_ser; + reg_ser_rw_rec_ctrl rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); + + if (arg) + rec_ctrl.half_duplex = regk_ser_no; + else + rec_ctrl.half_duplex = regk_ser_yes; + + REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); + return 0; + } + + case TIOCSERSETDIVISOR: { + reg_ser_rw_tr_baud_div tx_baud_div = {0}; + reg_ser_rw_rec_baud_div rx_baud_div = {0}; + + /* divisor must be >= 8 */ + if (arg < 8) + return -EINVAL; + + tx_baud_div.div = arg; + rx_baud_div.div = tx_baud_div.div; /* same as tx. */ + + MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div); + MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div); + + return 0; + } + + default: + return -ENOIOCTLCMD; + } + + return 0; +#else + return -ENOIOCTLCMD; +#endif +} + +static const struct uart_ops serial_cris_pops = { + .tx_empty = serial_cris_tx_empty, + .set_mctrl = serial_cris_set_mctrl, + .get_mctrl = serial_cris_get_mctrl, + .stop_tx = serial_cris_stop_tx, + .start_tx = serial_cris_start_tx, + .send_xchar = serial_cris_send_xchar, + .stop_rx = serial_cris_stop_rx, + .enable_ms = serial_cris_enable_ms, + .break_ctl = serial_cris_break_ctl, + .startup = serial_cris_startup, + .shutdown = serial_cris_shutdown, + .set_termios = serial_cris_set_termios, + .type = serial_cris_type, + .release_port = serial_cris_release_port, + .request_port = serial_cris_request_port, + .config_port = serial_cris_config_port, + .ioctl = serial_cris_ioctl, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_artpec_get_poll_char, + .poll_put_char = serial_artpec_put_poll_char, +#endif +}; + +/* + * It's too easy to break CONFIG_ETRAX_DEBUG_PORT_NULL and the + * no-config choices by adding and moving code to before a necessary + * early exit in all functions for the special case of + * up->regi_ser == 0. This collection of dummy functions lets us + * avoid that. Maybe there should be a generic table of dummy serial + * functions? + */ + +static unsigned int serial_cris_tx_empty_dummy(struct uart_port *port) +{ + return TIOCSER_TEMT; +} + +static void serial_cris_set_mctrl_dummy(struct uart_port *port, + unsigned int mctrl) +{ +} + +static unsigned int serial_cris_get_mctrl_dummy(struct uart_port *port) +{ + return 0; +} + +static void serial_cris_stop_tx_dummy(struct uart_port *port) +{ +} + +static void serial_cris_start_tx_dummy(struct uart_port *port) +{ + /* Discard outbound characters. */ + struct uart_cris_port *up = (struct uart_cris_port *)port; + struct circ_buf *xmit = &up->port.state->xmit; + xmit->tail = xmit->head; + uart_write_wakeup(port); +} + +#define serial_cris_stop_rx_dummy serial_cris_stop_tx_dummy + +#define serial_cris_enable_ms_dummy serial_cris_stop_tx_dummy + +static void serial_cris_break_ctl_dummy(struct uart_port *port, + int break_state) +{ +} + +static int serial_cris_startup_dummy(struct uart_port *port) +{ + return 0; +} + +#define serial_cris_shutdown_dummy serial_cris_stop_tx_dummy + +static void +serial_cris_set_termios_dummy(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ +} + +#define serial_cris_release_port_dummy serial_cris_stop_tx_dummy +#define serial_cris_request_port_dummy serial_cris_startup_dummy + +static const struct uart_ops serial_cris_dummy_pops = { + /* + * We *could* save one or two of those with different + * signature by casting and knowledge of the ABI, but it's + * just not worth the maintenance headache. + * For the ones we don't define here, the default (usually meaning + * "unimplemented") makes sense. + */ + .tx_empty = serial_cris_tx_empty_dummy, + .set_mctrl = serial_cris_set_mctrl_dummy, + .get_mctrl = serial_cris_get_mctrl_dummy, + .stop_tx = serial_cris_stop_tx_dummy, + .start_tx = serial_cris_start_tx_dummy, + .stop_rx = serial_cris_stop_rx_dummy, + .enable_ms = serial_cris_enable_ms_dummy, + .break_ctl = serial_cris_break_ctl_dummy, + .startup = serial_cris_startup_dummy, + .shutdown = serial_cris_shutdown_dummy, + .set_termios = serial_cris_set_termios_dummy, + + /* This one we keep the same. */ + .type = serial_cris_type, + + .release_port = serial_cris_release_port_dummy, + .request_port = serial_cris_request_port_dummy, + + /* + * This one we keep the same too, as long as it doesn't do + * anything else but to set the type. + */ + .config_port = serial_cris_config_port, +}; + +static void cris_serial_port_init(struct uart_port *port, int line) +{ + struct uart_cris_port *up = (struct uart_cris_port *)port; + static int first = 1; + + if (up->initialized) + return; + up->initialized = 1; + port->line = line; + spin_lock_init(&port->lock); + port->ops = + up->regi_ser == 0 ? &serial_cris_dummy_pops : + &serial_cris_pops; + port->irq = up->irq; + port->iobase = up->regi_ser ? up->regi_ser : 1; + port->uartclk = 29493000; + + /* + * We can't fit any more than 255 here (unsigned char), though + * actually UART_XMIT_SIZE characters could be pending output (if it + * wasn't for the single test in transmit_chars_dma). At time of this + * writing, the definition of "fifosize" is here the amount of + * characters that can be pending output after a start_tx call until + * tx_empty returns 1: see serial_core.c:uart_wait_until_sent. This + * matters for timeout calculations unfortunately, but keeping larger + * amounts at the DMA wouldn't win much so let's just play nice. + */ + port->fifosize = 255; + port->flags = UPF_BOOT_AUTOCONF; + +#ifdef CONFIG_ETRAX_RS485 + /* Set sane defaults. */ + up->rs485.rts_on_send = 0; + up->rs485.rts_after_sent = 1; + up->rs485.delay_rts_before_send = 0; + if (up->port_type > TYPE_232) + up->rs485.enabled = 1; + else + up->rs485.enabled = 0; +#endif + + if (first) { + first = 0; +#ifdef CONFIG_ETRAX_SERIAL_PORT0 + SETUP_PINS(0); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1 + SETUP_PINS(1); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + SETUP_PINS(2); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3 + SETUP_PINS(3); +#endif + } +} + +static int __init serial_cris_init(void) +{ + int ret, i; + printk(KERN_INFO "Serial: CRISv32 driver $Revision: 1.109 $ "); + + ret = uart_register_driver(&serial_cris_driver); + if (ret) + goto out; + + for (i = 0; i < UART_NR; i++) { + if (serial_cris_ports[i].used) { + struct uart_port *port; +#ifdef CONFIG_ETRAX_RS485 + reg_ser_rw_rec_ctrl rec_ctrl; + + /* Make sure that the RTS pin stays low when allocating + * pins for a port in 485 mode. + */ + if (serial_cris_ports[i].port_type > TYPE_232) { + rec_ctrl = REG_RD(ser, serial_cris_ports[i].regi_ser, rw_rec_ctrl); + rec_ctrl.rts_n = regk_ser_active; + REG_WR(ser, serial_cris_ports[i].regi_ser, rw_rec_ctrl, rec_ctrl); + } +#endif + switch (serial_cris_ports[i].regi_ser) { + case regi_ser0: + break; + case regi_ser1: + if (crisv32_pinmux_alloc_fixed(pinmux_ser1)) { + printk("Failed to allocate pins for ser1, disable port\n"); + serial_cris_ports[i].used = 0; + continue; + } + break; + case regi_ser2: + if (crisv32_pinmux_alloc_fixed(pinmux_ser2)) { + printk("Failed to allocate pins for ser2, disable port\n"); + serial_cris_ports[i].used = 0; + continue; + } + break; + case regi_ser3: + if (crisv32_pinmux_alloc_fixed(pinmux_ser3)) { + printk("Failed to allocate pins for ser3, disable port\n"); + serial_cris_ports[i].used = 0; + continue; + } + break; +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + case regi_ser4: + if (crisv32_pinmux_alloc_fixed(pinmux_ser4)) { + printk("Failed to allocate pins for ser4, disable port\n"); + serial_cris_ports[i].used = 0; + continue; + } + break; +#endif + default: + printk("Error: No such serial port (%d) \n", serial_cris_ports[i].regi_ser); + serial_cris_ports[i].used = 0; + break; + } + + port = &serial_cris_ports[i].port; + cris_console.index = i; + cris_serial_port_init(port, i); + uart_add_one_port(&serial_cris_driver, port); + } + } + +out: + return ret; +} + +static void __exit serial_cris_exit(void) +{ + int i; + for (i = 0; i < UART_NR; i++) + if (serial_cris_ports[i].used) { + switch (serial_cris_ports[i].regi_ser) { + case regi_ser1: + crisv32_pinmux_dealloc_fixed(pinmux_ser1); + break; + case regi_ser2: + crisv32_pinmux_dealloc_fixed(pinmux_ser2); + break; + case regi_ser3: + crisv32_pinmux_dealloc_fixed(pinmux_ser3); + break; +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + case regi_ser4: + crisv32_pinmux_dealloc_fixed(pinmux_ser4); + break; +#endif + default: + printk("Error: No such serial port (%d) \n", serial_cris_ports[i].regi_ser); + serial_cris_ports[i].used = 0; + break; + } + uart_remove_one_port(&serial_cris_driver, + &serial_cris_ports[i].port); + } + uart_unregister_driver(&serial_cris_driver); +} + +module_init(serial_cris_init); +module_exit(serial_cris_exit); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 5820269..dc1d379 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -244,4 +244,7 @@ /* SC16IS74xx */ #define PORT_SC16IS7XX 108 +/* Cris v10 / v32 SoC */ +#define PORT_CRIS 109 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/