Configure UART for 2-wire RS485

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

Configure UART for 2-wire RS485

Jump to solution
3,612 Views
okwaj
Contributor III

Hi All,

I want to use UART7 and UART8 on iMX6UL for RS485.

The schematic of RS485 is as below. I want to use DE/RE_485_2 to control the transmit or receive direction.

okwaj_1-1602927862558.png

I used this post to configure my dts.

My dts is :

 

		pinctrl_uart7: uart7grp //Interface RS485
		{
			fsl,pins = <
					MX6UL_PAD_LCD_DATA17__UART7_DCE_RX 0x1b0b1 //485_RX_1
					MX6UL_PAD_LCD_DATA16__UART7_DCE_TX 0x1b0b1 //485_TX_1
					MX6UL_PAD_GPIO1_IO05__GPIO1_IO05	0x1b0b1 //DE/RE_485_1
			>;
		};

		pinctrl_uart8: uart8grp //Interface RS485
		{
			fsl,pins = <
				MX6UL_PAD_LCD_DATA20__UART8_DCE_TX 0x1b0b1 //485_TX_2
				MX6UL_PAD_LCD_DATA21__UART8_DCE_RX 0x1b0b1 //485_RX_2
				MX6UL_PAD_GPIO1_IO04__GPIO1_IO04   0x1b0b1 //DE/RE_485_2
			>;
		};

&uart7
{
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart7>;
	status = "okay";
	rts-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>;
	rs485-rts-active-high;
	rs485-rx-during-tx;
	rs485-rts-delay = <1 1>;
	linux,rs485-enabled-at-boot-time;
};

&uart8
{
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart8>;
	status = "okay";
	rts-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>;
	rs485-rts-active-high;
	rs485-rx-during-tx;
	rs485-rts-delay = <1 1>;
	linux,rs485-enabled-at-boot-time;
};

 

When I "echo "something" > /dev/ttymxc<6_7>", I see the data but the DE/RE doesn't change its state.

I enabled the "Debug GPIO calls" in the make menuconfig and I don't see the DE/RE pins configured during startup and their initial state is not as declared in my dts...

 

Do you see any issue in my dts ?

0 Kudos
1 Solution
3,572 Views
okwaj
Contributor III

I don't have any driver developpement knowledge and I didn't have time so I fixed it in an ugly way.

I modified imx.c under drivers/tty/serial like that (this is ugly and only working for my specific case but It can help so I share it ...):

 


index c9bd603d395b..7cf00d3f1c4b 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -39,6 +39,7 @@
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>

#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
@@ -330,6 +331,11 @@ static void imx_port_ucrs_restore(struct uart_port *port,
}
#endif

+static void rs485_init(void);
+static void rs485_free(void);
+static void rs485_start_tx(struct imx_port *sport);
+static void rs485_stop_tx(struct imx_port *sport);
+
/*
* Handle any change of modem status signal since we were last called.
*/
@@ -382,7 +388,7 @@ static void imx_stop_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
-
+ // rs485_stop_tx(sport);
/*
* We are maybe in the SMP context, so if the DMA TX thread is running
* on other cpu, we have to wait for it to finish.
@@ -595,6 +601,7 @@ static void imx_start_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;

+ rs485_start_tx(sport);
if (port->rs485.flags & SER_RS485_ENABLED) {
/* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR2);
@@ -732,9 +739,16 @@ static irqreturn_t imx_int(int irq, void *dev_id)
struct imx_port *sport = dev_id;
unsigned int sts;
unsigned int sts2;
+ unsigned int cr4;

sts = readl(sport->port.membase + USR1);
sts2 = readl(sport->port.membase + USR2);
+ cr4 = readl(sport->port.membase + UCR4);
+
+ if ((cr4 & UCR4_TCEN) && (sts2 & USR2_TXDC))
+ {
+ rs485_stop_tx(sport);
+ }

if ((sts & USR1_RRDY || sts & USR1_AGTIM) &&
!sport->dma_is_enabled) {
@@ -746,7 +760,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
if ((sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
(sts2 & USR2_TXDC &&
- readl(sport->port.membase + UCR4) & UCR4_TCEN))
+ cr4 & UCR4_TCEN))
imx_txint(irq, dev_id);

if (sts & USR1_RTSD)
@@ -1122,6 +1136,7 @@ static int imx_startup(struct uart_port *port)
return retval;
}

+ rs485_init();
imx_setup_ufcr(sport, 0);

/* disable the DREN bit (Data Ready interrupt enable) before
@@ -1222,6 +1237,7 @@ static void imx_shutdown(struct uart_port *port)
imx_disable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
imx_uart_dma_exit(sport);
+ rs485_free();
}

spin_lock_irqsave(&sport->port.lock, flags);
@@ -1966,6 +1982,78 @@ static void serial_imx_probe_pdata(struct imx_port *sport,
sport->have_rtscts = 1;
}

+/*
+ Init the GPIO for DE/RE communication for UART7 and UART8
+*/
+static void rs485_init(void)
+{
+ static int done;
+ if (done == 0)
+ {
+ gpio_request(4, "DERE_485_2");
+ gpio_request(5, "DERE_485_1");
+ gpio_direction_output(4, 1);
+ gpio_direction_output(5, 1);
+ done = 1;
+ }
+}
+
+/*
+ Free the GPIOs used for DE/RE com on UART7/8
+*/
+static void rs485_free(void)
+{
+ static int done;
+ if (done == 0)
+ {
+ gpio_free(4);
+ gpio_free(5);
+ done = 1;
+ }
+}
+
+/*
+ Activate the corresponding gpio for UART7/8
+*/
+static void rs485_start_tx(struct imx_port *sport)
+{
+ if (sport == imx_ports[6])
+ {
+ unsigned long temp;
+ gpio_set_value(5, 1);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp | UCR4_TCEN, sport->port.membase + UCR4);
+ }
+ else if (sport == imx_ports[7])
+ {
+ unsigned long temp;
+ gpio_set_value(4, 1);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp | UCR4_TCEN, sport->port.membase + UCR4);
+ }
+}
+
+/*
+ Deactivate the corresponding gpio for UART7/8
+*/
+static void rs485_stop_tx(struct imx_port *sport)
+{
+ if (sport == imx_ports[6])
+ {
+ unsigned long temp;
+ gpio_set_value(5, 0);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp & ~UCR4_TCEN, sport->port.membase + UCR4);
+ }
+ else if (sport == imx_ports[7])
+ {
+ unsigned long temp;
+ gpio_set_value(4, 0);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp & ~UCR4_TCEN, sport->port.membase + UCR4);
+ }
+}
+
static int serial_imx_probe(struct platform_device *pdev)
{
struct imx_port *sport;

View solution in original post

0 Kudos
3 Replies
3,573 Views
okwaj
Contributor III

I don't have any driver developpement knowledge and I didn't have time so I fixed it in an ugly way.

I modified imx.c under drivers/tty/serial like that (this is ugly and only working for my specific case but It can help so I share it ...):

 


index c9bd603d395b..7cf00d3f1c4b 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -39,6 +39,7 @@
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>

#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
@@ -330,6 +331,11 @@ static void imx_port_ucrs_restore(struct uart_port *port,
}
#endif

+static void rs485_init(void);
+static void rs485_free(void);
+static void rs485_start_tx(struct imx_port *sport);
+static void rs485_stop_tx(struct imx_port *sport);
+
/*
* Handle any change of modem status signal since we were last called.
*/
@@ -382,7 +388,7 @@ static void imx_stop_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
-
+ // rs485_stop_tx(sport);
/*
* We are maybe in the SMP context, so if the DMA TX thread is running
* on other cpu, we have to wait for it to finish.
@@ -595,6 +601,7 @@ static void imx_start_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;

+ rs485_start_tx(sport);
if (port->rs485.flags & SER_RS485_ENABLED) {
/* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR2);
@@ -732,9 +739,16 @@ static irqreturn_t imx_int(int irq, void *dev_id)
struct imx_port *sport = dev_id;
unsigned int sts;
unsigned int sts2;
+ unsigned int cr4;

sts = readl(sport->port.membase + USR1);
sts2 = readl(sport->port.membase + USR2);
+ cr4 = readl(sport->port.membase + UCR4);
+
+ if ((cr4 & UCR4_TCEN) && (sts2 & USR2_TXDC))
+ {
+ rs485_stop_tx(sport);
+ }

if ((sts & USR1_RRDY || sts & USR1_AGTIM) &&
!sport->dma_is_enabled) {
@@ -746,7 +760,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
if ((sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
(sts2 & USR2_TXDC &&
- readl(sport->port.membase + UCR4) & UCR4_TCEN))
+ cr4 & UCR4_TCEN))
imx_txint(irq, dev_id);

if (sts & USR1_RTSD)
@@ -1122,6 +1136,7 @@ static int imx_startup(struct uart_port *port)
return retval;
}

+ rs485_init();
imx_setup_ufcr(sport, 0);

/* disable the DREN bit (Data Ready interrupt enable) before
@@ -1222,6 +1237,7 @@ static void imx_shutdown(struct uart_port *port)
imx_disable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
imx_uart_dma_exit(sport);
+ rs485_free();
}

spin_lock_irqsave(&sport->port.lock, flags);
@@ -1966,6 +1982,78 @@ static void serial_imx_probe_pdata(struct imx_port *sport,
sport->have_rtscts = 1;
}

+/*
+ Init the GPIO for DE/RE communication for UART7 and UART8
+*/
+static void rs485_init(void)
+{
+ static int done;
+ if (done == 0)
+ {
+ gpio_request(4, "DERE_485_2");
+ gpio_request(5, "DERE_485_1");
+ gpio_direction_output(4, 1);
+ gpio_direction_output(5, 1);
+ done = 1;
+ }
+}
+
+/*
+ Free the GPIOs used for DE/RE com on UART7/8
+*/
+static void rs485_free(void)
+{
+ static int done;
+ if (done == 0)
+ {
+ gpio_free(4);
+ gpio_free(5);
+ done = 1;
+ }
+}
+
+/*
+ Activate the corresponding gpio for UART7/8
+*/
+static void rs485_start_tx(struct imx_port *sport)
+{
+ if (sport == imx_ports[6])
+ {
+ unsigned long temp;
+ gpio_set_value(5, 1);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp | UCR4_TCEN, sport->port.membase + UCR4);
+ }
+ else if (sport == imx_ports[7])
+ {
+ unsigned long temp;
+ gpio_set_value(4, 1);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp | UCR4_TCEN, sport->port.membase + UCR4);
+ }
+}
+
+/*
+ Deactivate the corresponding gpio for UART7/8
+*/
+static void rs485_stop_tx(struct imx_port *sport)
+{
+ if (sport == imx_ports[6])
+ {
+ unsigned long temp;
+ gpio_set_value(5, 0);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp & ~UCR4_TCEN, sport->port.membase + UCR4);
+ }
+ else if (sport == imx_ports[7])
+ {
+ unsigned long temp;
+ gpio_set_value(4, 0);
+ temp = readl(sport->port.membase + UCR4);
+ writel(temp & ~UCR4_TCEN, sport->port.membase + UCR4);
+ }
+}
+
static int serial_imx_probe(struct platform_device *pdev)
{
struct imx_port *sport;

0 Kudos
3,589 Views
okwaj
Contributor III

Thank you for your answer,

I tried the same thing as uart4 and 5 of https://github.com/boundarydevices/linux-imx6/blob/boundary-imx_4.14.x_2.0.0_ga/arch/arm/boot/dts/im...

But still nothing on the DE/RE...

I noticed that I use a GPIO for this signal whereas in the example, it is an RTS/CTS GPIO, does it change anything ?

I also saw this post but there is no example provided.

0 Kudos