AnsweredAssumed Answered

How to receive space parity data after sending in RS-485 mode?

Question asked by Maik Brenke on Apr 2, 2019
Latest reply on Sep 25, 2019 by Raimund Wagner



I need to handle Mark and Space Parity on a UART from a IMX6SX chip. I checked also different posts under [1].

The use case for me is to transmit a address byte with mark parity on UART_5 followed by some data bytes with space parity to request a response from a serial bus client with only space parity data bytes.

Therefore, I check [2] and noticed that in the my linux kernel (4.1.5) and also the current kernel there is no support for the "CMSPAR" option, but the hardware is supporting RS485 with a 9th bit option.


I checked the reference manual (i.MX 6SoloX Applications Processor Reference Manual) and implemented the undocumented "CMSPAR" option from the Linux Kernel into my kernel. With this changed the parity can be defined as mark or space via PREN=1, WS=1, MDEN=1 and TXB8 can be used to define the parity bit (see chapter 65.7.2 Transmit 9-bit RS-485 frames).

For transmitting everything is working fine (see patch in [3]). I can send a address byte (mark parity) followed from some data bytes (space parity) and can see that the serial bus client is receiving the command and is processing the data.


The problem is that I am unable to receive anything. The UART only receive data in the RS-485 mode when a address byte (mark parity) was send (Slave Address Normal/Automatic Detect Mode). But the response here is only send with space parity so that the UART is ignoring all bytes.



How can I receive 8 bit data from the UART after sending an address and some data in RS-485 mode?



Some experiences with iMX.6 9-bit UART mode 

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

Linux and MARK/SPACE Parity

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index c9bd603..661417d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -62,6 +62,7 @@
#define IMX21_ONEMS 0xb0 /* One Millisecond register */
#define IMX1_UTS 0xd0 /* UART Test Register on i.mx1 */
#define IMX21_UTS 0xb4 /* UART Test Register on all other*/
+#define UMCR 0xb8 /* UART RS-485 Mode Control Register */

/* UART Control Register Bit Fields.*/
#define URXD_DUMMY_READ (1<<16)
@@ -162,6 +163,10 @@
#define UTS_TXFULL (1<<4) /* TxFIFO full */
#define UTS_RXFULL (1<<3) /* RxFIFO full */
#define UTS_SOFTRST (1<<0) /* Software reset */
+#define UMCR_SADEN (1<<3) /* RS-485 Slave Address Detected Interrupt Enable */
+#define UMCR_TXB8 (1<<2) /* Transmit RS-485 bit 8 (the ninth bit or 9th bit) */
+#define UMCR_SLAM (1<<1) /* RS-485 Slave Address Detect Mode Selection */
+#define UMCR_MDEN (1<<0) /* 9-bit data or Multidrop Mode (RS-485) Enable */

/* We've been assigned a range on the "Low-density serial ports" major */
#define SERIAL_IMX_MAJOR 207
@@ -1301,7 +1306,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
+ u32 umcr, ucr2, old_ucr1, old_txrxen, baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int div, ufcr;
unsigned long num, denom;
@@ -1346,10 +1351,18 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
ucr2 |= UCR2_CTS;

+ /* stop bits */
if (termios->c_cflag & CSTOPB)
ucr2 |= UCR2_STPB;
+ /* parity */
if (termios->c_cflag & PARENB) {
ucr2 |= UCR2_PREN;
+ if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */
+ if (termios->c_cflag & PARODD)
+ umcr |= UMCR_TXB8;
+ }
if (termios->c_cflag & PARODD)
ucr2 |= UCR2_PROE;
@@ -1451,6 +1464,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);

+ /* set RS-485 mode and ninth bit */
+ writel(umcr, sport->port.membase + UMCR);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))