i.MX6 UART Driver and RS485 Support

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

i.MX6 UART Driver and RS485 Support

21,532 Views
PaulDeMetrotion
Senior Contributor I

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,

10 Replies

4,092 Views
PaulDeMetrotion
Senior Contributor I

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.

0 Kudos

4,092 Views
Yuri
NXP Employee
NXP Employee

Hello,

  the following may be helpful 

Does UART in RS485 mode support only 9 Bit mode for i.MX6 ? 

(suggest to use a recent kernel such as 4.13.2 for rs485 evaluation.)

Regards,

Yuri.

0 Kudos

4,092 Views
bobbywu
Contributor I

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

4,092 Views
gavinbao
Contributor I

Do you have Slove the problem?I have the same question,please help。

0 Kudos

4,096 Views
PaulDeMetrotion
Senior Contributor I

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,

0 Kudos

4,095 Views
YixingKong
Senior Contributor IV

Paul, if your issue does not exists, please click Correct Answer/Helpful Answer.

Thansk,

Yixing

0 Kudos

4,096 Views
felipezimmerle
Contributor II

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.

0 Kudos

4,096 Views
YixingKong
Senior Contributor IV

Paul, could you please reply to Pushi's inquery? If your issue does not exists, please click Correct Answer/Helpful Answer.

Thanks,

Yixing

0 Kudos

4,096 Views
fushi_peng
NXP Employee
NXP Employee

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.

0 Kudos

4,096 Views
方圆张
Contributor I

Hello,anyway,do you have saved the problem?However,I encountered the same problem in my project even though I use the RS485 mode by enabling the MDEN bit int the UMCR register.Can you help me?

0 Kudos