I have done a similar modification. the key is...
in imx_start_tx(), enable TCEN (in UCR4).
in imx_stop_tx(), check TXDC (in USR2).
this is my full patch.
====================================================
---
drivers/tty/serial/imx.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 69 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index a512a76..66081e1 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -49,6 +49,7 @@
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/uaccess.h>
#include <mach/dma.h>
#include <mach/hardware.h>
#include <mach/imx-uart.h>
@@ -213,6 +214,8 @@ struct imx_port {
unsigned int dma_tx_nents;
bool dma_is_rxing;
wait_queue_head_t dma_wait;
+
+ struct serial_rs485 rs485;
};
struct imx_port_ucrs {
@@ -227,6 +230,25 @@ struct imx_port_ucrs {
#define USE_IRDA(sport) (0)
#endif
+static inline void imx_rs485_switch_to_tx(struct imx_port *sport) {
+ writel(readl(sport->port.membase + UCR2) & ~UCR2_CTS,
+ sport->port.membase + UCR2);
+}
+
+static inline void imx_rs485_switch_to_rx(struct imx_port *sport) {
+ writel(readl(sport->port.membase + UCR2) | UCR2_CTS,
+ sport->port.membase + UCR2);
+}
+
+static inline void imx_rs485_config(struct imx_port *sport) {
+ if (sport->have_rtscts) {
+ writel(readl(sport->port.membase + UCR2) & ~UCR2_CTSC,
+ sport->port.membase + UCR2);
+ imx_rs485_switch_to_rx(sport);
+ } else
+ sport->rs485.flags &= ~SER_RS485_ENABLED;
+}
+
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
@@ -301,6 +323,13 @@ static void imx_stop_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ if (readl(sport->port.membase + USR2) & USR2_TXDC) {
+ if (sport->rs485.flags & SER_RS485_ENABLED)
+ imx_rs485_switch_to_rx(sport);
+ writel(readl(sport->port.membase + UCR4) & ~UCR4_TCEN,
+ sport->port.membase + UCR4);
+ }
+
if (USE_IRDA(sport)) {
/* half duplex - wait for end of transmission */
int n = 256;
@@ -508,6 +537,12 @@ static void imx_start_tx(struct uart_port *port)
return;
}
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ writel(readl(sport->port.membase + UCR4) | UCR4_TCEN,
+ sport->port.membase + UCR4);
+ imx_rs485_switch_to_tx(sport);
+ }
+
if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
imx_transmit_buffer(sport);
}
@@ -651,6 +686,11 @@ static irqreturn_t imx_int(int irq, void *dev_id)
struct imx_port *sport = dev_id;
unsigned int sts;
+ if (readl(sport->port.membase + USR2) & USR2_TXDC &&
+ readl(sport->port.membase + UCR4) & UCR4_TCEN) {
+ imx_txint(irq, dev_id);
+ }
+
sts = readl(sport->port.membase + USR1);
if (sts & USR1_RRDY) {
@@ -1361,6 +1401,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
/* set the parity, stop bits and data size */
writel(ucr2 | old_txrxen, sport->port.membase + UCR2);
+ if (sport->rs485.flags & SER_RS485_ENABLED)
+ imx_rs485_config(sport);
+
spin_unlock_irqrestore(&sport->port.lock, flags);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
@@ -1510,6 +1553,31 @@ static void imx_poll_put_char(struct uart_port *port, unsigned char c)
}
#endif
+static int imx_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&(sport->rs485), (struct serial_rs485 *) arg,
+ sizeof(struct serial_rs485)))
+ return -EFAULT;
+ if (sport->rs485.flags & SER_RS485_ENABLED)
+ imx_rs485_config(sport);
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg, &(sport->rs485),
+ sizeof(struct serial_rs485)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
static struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
@@ -1521,6 +1589,7 @@ static struct uart_ops imx_pops = {
.break_ctl = imx_break_ctl,
.startup = imx_startup,
.shutdown = imx_shutdown,
+ .ioctl = imx_ioctl,
.set_termios = imx_set_termios,
.type = imx_type,
.release_port = imx_release_port,
--
1.7.4.1