Enable UART cts-gpios and rts-gpios on Verdin iMX8MM

I’ve managed to repurpose UART2 pins (on dev board they are connected to RS232 interface) to SPI0. I’ve successfully connected MCP2517FD and got CAN interface (SPI1 is already occupied).

But I still want to have that UART2 interface available, with flow controll enabled. I managed to configure Verdins GPIO_3 and GPIO_4 and use them as RX and TX. There is no other UART3_CTS and UART3_RTS pin available I had to recompile imx8mm-verdin-wifi-dev.dts to disable fsl,uart-has-rtscts property. But i still havent successfully enabled GPIO_6 and GPIO_7 for flow control.

This are UART parts (in order to avoid posting huge code) of my overlay im using:

...
fragment@5{
		/* Move UART3 to another pins */
		target = <&pinctrl_uart3>;
		__overlay__ {
			fsl,pins = <
				/*MX8MM_IOMUXC_ECSPI1_SCLK_UART3_DCE_RX		0x1c4	/* SODIMM 137 */
				/*MX8MM_IOMUXC_ECSPI1_MOSI_UART3_DCE_TX		0x1c4	/* SODIMM 139 */
				/*MX8MM_IOMUXC_ECSPI1_MISO_UART3_DCE_CTS_B	0x1c4	/* SODIMM 141 */
				/*MX8MM_IOMUXC_ECSPI1_SS0_UART3_DCE_RTS_B		0x1c4	/* SODIMM 143 */
				/* new pinout */
				MX8MM_IOMUXC_UART3_RXD_UART3_DCE_RX	0x1c4	/* SODIMM 210 -> GPIO_3 */
				MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX	0x1c4	/* SODIMM 212 -> GPIO_4 */
				MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11	0x184   /* SODIMM 218 -> GPIO_6 CTS*/
				MX8MM_IOMUXC_GPIO1_IO08_GPIO1_IO8	0x184	/* SODIMM 220 -> GPIO_7 RTS*/
			>;
		};
	};

...

fragment@7{
		target = <&uart3>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_uart3>;
			cts-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
			rts-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;	
		};
	};

Im able to send and receive data using new RX and TX pins but CTS and RTS are still disabled.

Greetings @spasoye,

On quick glance everything seems okay with your device tree changes. I did some research and I have a couple of ideas.

I see you’re using SODIMM 220 as the RTS line. In the device tree by default I believe SODIMM 220 is being used as a system GPIO here: imx8mm-verdin-v1.1.dtsi « freescale « dts « boot « arm64 « arch - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

SODIMM 220 is in the pinctrl_gpio7 pinmux group. I’m not sure if this might conflict with using SODIMM 220 as a RTS GPIO, but I suppose it wouldn’t hurt check.

Next you might need additional parameters in the UART node. Via the kernel documentation here: fsl-imx-uart.txt « serial « bindings « devicetree « Documentation - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

It seems you need this linux,rs485-enabled-at-boot-time for RTS/CTS signals to be used at boot time. At least or RS485 purposes.

Give these a try and let me know how it works out. By the way, when you say flow control hasn’t been working so far, specifically do you mean there’s no signals on this line or?

Best Regards,
Jeremias

Hello @jeremias.tx,

this is my overlay that moves UART to another pins and repurposes UART pins to ecspi1 pins for mcp:

/dts-v1/;
/plugin/;

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/imx8mm-clock.h>
#include <imx8mm-pinfunc.h>

/ {
    compatible = "toradex,verdin-imx8mm";

	fragment@0 {
		target-path = "/";
		__overlay__{
			clk40m: oscillator-1 {
				compatible = "fixed-clock";
				#clock-cells = <0>;
				clock-frequency = <40000000>;
			};
		};
	};

	fragment@1 {
		target = <&iomuxc>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_gpio8>, <&pinctrl_gpio_hog1>,
					<&pinctrl_gpio_hog2>, <&pinctrl_gpio_hog3>,
					<&pinctrl_sai5>, <&pinctrl_pmic_tpm_ena>;
		};
	};

	fragment@2{
		target = <&pinctrl_gpio5>;
		__overlay__ {
			fsl,pins = <
				MX8MM_IOMUXC_GPIO1_IO00_GPIO1_IO0		0x1c4	/* SODIMM 216 */
			>;
		};
	};

	fragment@3{
		target = <&spidev20>;
		__overlay__ {
			status = "disabled";
		};
	};

	fragment@4{
		target = <&iomuxc>;
		__overlay__ {
			pinctrl_ecspi1: ecspi1grp {
				fsl,pins = <
					MX8MM_IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK		0x4	/* SODIMM 137 */
					MX8MM_IOMUXC_ECSPI1_MOSI_ECSPI1_MOSI		0x4	/* SODIMM 139 */
					MX8MM_IOMUXC_ECSPI1_MISO_ECSPI1_MISO		0x1c4 /* SODIMM 141 */
					MX8MM_IOMUXC_ECSPI1_SS0_ECSPI1_SS0    	    0x1c4 /* SODIMM 143 */
				>;
			};
		};
	};

	fragment@5{
		/* Move UART3 to another pins */
		target = <&pinctrl_uart3>;
		__overlay__ {
			fsl,pins = <
				/* new pinout */
				MX8MM_IOMUXC_UART3_RXD_UART3_DCE_RX			0x140	/* SODIMM 210 -> GPIO_3 */
				MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX			0x140	/* SODIMM 212 -> GPIO_4 */
				MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11			0x140	/* SODIMM 218 -> GPIO_6 */
				MX8MM_IOMUXC_GPIO1_IO08_GPIO1_IO8			0x140	/* SODIMM 220 -> GPIO_7 */
			>;
		};
	};

	fragment@6{
		target = <&ecspi1>;
		__overlay__ {
			#address-cells = <1>;
			#size-cells = <0>;
			#cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
			/* This property is required, even if marked as obsolete in the doku */
			fsl,spi-num-chipselects = <1>;
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_ecspi1>;
			status = "okay";

			can3: can@0 {
				compatible = "microchip,mcp2517fd";
				clocks = <&clk40m>;
				gpio-controller;
				/* dev board gpio5 pin -> gpio1_0 */
				interrupt-parent = <&gpio1>;
				interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
				microchip,clock-allways-on;
				microchip,clock-out-div = <1>;
				pinctrl-names = "default";
				pinctrl-0 = <&pinctrl_gpio5>;
				reg = <0>;
				spi-max-frequency = <1000000>;
				status = "okay";
			};
		};
	};

	fragment@7{
		target = <&uart3>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_uart3>;
			cts-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
			rts-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;	
		};
	};
};

As you can see fragment@1 (line 23) I modified iomuxc otherwise I get an error when booting linux. Is that legit ?

My current UART test consists of connecting RX to Tx and CTS to RTS right there on development board. This test seemed reasonable because I did it before applying the overlay. I send data in minicom and started to receive back the data only if CTS and RTS are coupled.
But after applying overlay when I connect SODIMM_210 to SODIMM_212 and send data in minicom I start to receive back data no matter if pins 218 and 220 are connected or not.

hey @jeremias.tx,

I tried using linux,rs485-enabled-at-boot-time property:

fragment@7{
		target = <&uart3>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_uart3>;
			// linux,rs485-enabled-at-boot-time;
			cts-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
			rts-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;	
		};
	};

It doesn change a thing.

I did some digging into the driver code and I’m not all entirely sure if the NXP driver the i.MX8MM uses supports the cts-gpios property.

For reference the driver here: imx.c « serial « tty « drivers - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

I can find and see references to the rts-gpios but not the cts-gpios property.

So I did even more research to see if I could find other device trees that use these properties. The i.MX6 SoC uses the same driver as the i.MX8MM here. After searching device trees I found several i.MX6 based device trees that use the rts-gpios property but none of them use the cts-gpios property.

I may be wrong and this may require more research but I just wanted to keep you updated.

Best Regards,
Jeremias

Hey @jeremias.tx , tnx for your time.

I’ll keep you posted if I find something useful.

I’va also take a look to IMX serial driver implementation and found that it does support cts-gpio as well as dsr-, dcd-, rng-, rts- and dtr-. It’s done by serial_mctrl_gpio.c. During a probe phase IMX driver calls mctrl_gpio_init() which read all listed properties. Then IMX driver invokes mctrl_gpio_set() at the end of imx_uart_set_mctrl() . So if *-gpio property was listed in a Device Tree corresponded GPIO pin should be set accordingly. However decisions about how to set modem control line are done on upper levels and conveyed to IMX driver from serial_core.c .

@jeremias.tx @alex.tx

I’ve been fiddling around with flow control and I noticed that after applying above overlay RTS line works as it should.

cts-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
rts-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;

For example when running minicom RTS line will go low until I exit the minicom. However UART will send data no matter the state of CTS pin. Apparently @jeremias.tx was right about
cts-gpios (part of imx_uart_probe_dt function):

...
if (of_get_property(np, "uart-has-rtscts", NULL) ||
	    of_get_property(np, "fsl,uart-has-rtscts", NULL) /* deprecated */)
		sport->have_rtscts = 1;

if (of_get_property(np, "fsl,dte-mode", NULL))
		sport->dte_mode = 1;

if (of_get_property(np, "rts-gpios", NULL))
		sport->have_rtsgpio = 1;

return 0;

Well thank you for confirming that at least the RTS line is working as it should when a GPIO is used. But this does seem to give evidence that perhaps cts-gpios isn’t supported/doesn’t work. Or it requires some kind of configuration that isn’t very obvious.

Best Regards,
Jeremias