I am currently attempting to use RS-485 mode on a custom device based on the i.MX6Q. I am altering the imx.c driver found in ltib/rpm/BUILD/Linux/drivers/tty/serial. Using the assistance found in the 'RS485 Serial Communications' documentation, I have made the following changes to the driver. Unfortunately this has not helped. The signal CTS which I am using to control the direction of the RS-485 driver is never enabled so the device can never transmit. Anybody have any experience with RS-485 and this part? Should I be using the RS-485 mode by enabling the MDEN bit in the UMCR register?
Index: imx.c
===================================================================
--- imx.c (revision 34)
+++ imx.c (working copy)
@@ -46,6 +46,7 @@
#include <linux/rational.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -213,6 +214,9 @@
unsigned int dma_tx_nents;
bool dma_is_rxing;
wait_queue_head_t dma_wait;
+
+ // RS485 Support
+ struct serial_rs485 rs485;
};
struct imx_port_ucrs {
@@ -227,6 +231,33 @@
#define USE_IRDA(sport) (0)
#endif
+static inline struct imx_port *
+to_imx_uart_port(struct uart_port *uart)
+{
+ return container_of(uart, struct imx_port, port);
+}
+
+/* Enable or disable the rs485 support */
+void imx_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct imx_port *imx_port = to_imx_uart_port(port);
+ //unsigned int mode;
+ unsigned long flags;
+ //unsigned long temp;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ imx_port->rs485 = *rs485conf;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
@@ -345,6 +376,13 @@
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
+
+ // disable CTS if rs485
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_CTS);
+ writel(temp, sport->port.membase + UCR2);
+ }
}
/*
@@ -477,6 +515,13 @@
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ // enable CTS if rs485
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ temp = readl(sport->port.membase + UCR2);
+ temp |= UCR2_CTS;
+ writel(temp, sport->port.membase + UCR2);
+ }
+
if (USE_IRDA(sport)) {
/* half duplex in IrDA mode; have to disable receive mode */
temp = readl(sport->port.membase + UCR4);
@@ -1446,6 +1491,36 @@
return ret;
}
+static int imx_ioctl(struct uart_port *port,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf,
+ (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ imx_config_rs485(port, &rs485conf);
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ &(to_imx_uart_port(port)->rs485),
+ sizeof(rs485conf)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
#if defined(CONFIG_CONSOLE_POLL)
static int imx_poll_get_char(struct uart_port *port)
{
@@ -1527,6 +1602,8 @@
.request_port = imx_request_port,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
+ // rs485 support
+ .ioctl = imx_ioctl,
#if defined(CONFIG_CONSOLE_POLL)
.poll_get_char = imx_poll_get_char,
.poll_put_char = imx_poll_put_char,
Does anybody have an update on this? I have not looked at this for a while and thought I had it working. But now I cannot transfer large files due to transmit collisions. I have been attempting to review my imx.c driver changes but cannot find any fixes that work. I am using rz and sz to transfer a file between two on-board UART ports.
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
I believe I have made progress through additional changes to the imx.c serial driver. I still cannot transfer a file but I do see the CTS signal being enabled properly when I attempt to transmit. When I attempt to send or receive, I get the following errors. I have not found much on the internet about these errors. Anybody familiar with them?
root@winsys-ubuntu-desktop:~# rz -vv -b < /dev/ttymxc3 > /dev/ttymxc3
rz waiting to receive.got ZRINIT
Transfer incomplete
root@winsys-ubuntu-desktop:/usr/src/rs485# sz -vv -b rs485.c > /dev/ttymxc2 < /dev/ttymxc2
Retry 0: Got ZNAK
Retry 0: Got ZNAK
Retry 0: Got ZNAK
Retry 0: Got ZNAK
Retry 0: Got ZNAK
Retry 0: Got ZNAK
Transfer incomplete
Patch file for imx.c:
Index: imx.c
===================================================================
--- imx.c (revision 34)
+++ imx.c (working copy)
@@ -46,6 +46,7 @@
#include <linux/rational.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -213,6 +214,9 @@
unsigned int dma_tx_nents;
bool dma_is_rxing;
wait_queue_head_t dma_wait;
+
+ // RS485 Support
+ struct serial_rs485 rs485;
};
struct imx_port_ucrs {
@@ -227,6 +231,39 @@
#define USE_IRDA(sport) (0)
#endif
+static inline struct imx_port *
+to_imx_uart_port(struct uart_port *uart)
+{
+ return container_of(uart, struct imx_port, port);
+}
+
+/* Enable or disable the rs485 support */
+void imx_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct imx_port *imx_port = to_imx_uart_port(port);
+ struct imx_port *sport = (struct imx_port *)port;
+ unsigned long flags;
+ unsigned long temp;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ // copy rs485 structure
+ imx_port->rs485 = *rs485conf;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+
+ // disable CTS - it is set by mctrl
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_CTS);
+ writel(temp, sport->port.membase + UCR2);
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
@@ -345,6 +382,21 @@
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
+
+ // disable CTS if rs485
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ // wait for transmit to complete
+ n = 256;
+ while ((--n > 0) && !(readl(sport->port.membase + USR2) & USR2_TXDC)) {
+ udelay(5);
+ //barrier();
+ }
+
+ // disable CTS
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_CTS);
+ writel(temp, sport->port.membase + UCR2);
+ }
}
/*
@@ -477,6 +529,13 @@
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ // enable CTS if rs485
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ temp = readl(sport->port.membase + UCR2);
+ temp |= UCR2_CTS;
+ writel(temp, sport->port.membase + UCR2);
+ }
+
if (USE_IRDA(sport)) {
/* half duplex in IrDA mode; have to disable receive mode */
temp = readl(sport->port.membase + UCR4);
@@ -710,8 +769,9 @@
temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
- if (mctrl & TIOCM_RTS)
- temp |= UCR2_CTS;
+ if (!(sport->rs485.flags & SER_RS485_ENABLED))
+ if (mctrl & TIOCM_RTS)
+ temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
@@ -1446,6 +1506,36 @@
return ret;
}
+static int imx_ioctl(struct uart_port *port,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf,
+ (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ imx_config_rs485(port, &rs485conf);
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ &(to_imx_uart_port(port)->rs485),
+ sizeof(rs485conf)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
#if defined(CONFIG_CONSOLE_POLL)
static int imx_poll_get_char(struct uart_port *port)
{
@@ -1527,6 +1617,8 @@
.request_port = imx_request_port,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
+ // rs485 support
+ .ioctl = imx_ioctl,
#if defined(CONFIG_CONSOLE_POLL)
.poll_get_char = imx_poll_get_char,
.poll_put_char = imx_poll_put_char,
Paul, if your issue does not exists, please click Correct Answer/Helpful Answer.
Thansk,
Yixing
Hi PaulDeMetrotion,
Did you managed to solve that issue? whats it your current progress. I will follow your steps and i am afraid that i will end up in the same situation.
Thanks,
F.
Paul, could you please reply to Pushi's inquery? If your issue does not exists, please click Correct Answer/Helpful Answer.
Thanks,
Yixing
I am not sure do you change code of receive and transfer RS-485 data follow the RM section 64.7.2 Transmit 9-bit RS-485 frames and 64.7.3 Receive 9-bit RS-485 frames. And please check RM section 64.13.2 Programming the UART in 9-bit RS-485 mode.