This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM335x GPIO based RTS for RS-485

Other Parts Discussed in Thread: ISO1176T

I am looking for help in enabling a gpio based RTS in omap-serial for RS485 communication. I am not using the 8250 driver as it does not yet support RS485. I have a GPIO pin assigned as rts-gpio. I have verified that the GPIO is working with manual manipulation via sysfs and a scope. To do this I cannot declare 'rs485-enabled-at-boot-time' in device tree.

When using a device tree with 'rs485-enabled-at-boot-time' in my pin definition, I do see omap-serial take this GPIO via /sys/kernel/debug/gpio. However it always low. The 485 transceiver we are using TI iso1176t has active-high driver enable and active low receiver enable. The port is configured via ioctl correctly upon open, but when any bidirectional app tries to use, I never see the state of rts-GPIO change. It seems once omap-serial grabs this GPIO pin, it is forever low. All my other GPIO muxing/assignments work just fine.

System Specifics:
part/component
description
notes
am335x cpu
iso1176t 485 transceiver
image build yocto fido/poky (1.8.1)
kernel (current) 3.14.64
serial driver omap-serial
port uart4
RTS-GPIO based GPIO22
  • There was some development work upstream in the 4.4 kernel where the 8250 driver will actually utilize the RTS pin itself for RS-485. So if possible, I recommend that you create your hardware such that you hook up the enable pin to the RTS pin. That gives you flexibility to use either driver, i.e. you can use it in GPIO mode with omap-serial or use the upcoming RS-485 capability through the RTS pin with the 8250 driver.

    What do you mean by "GPIO22"? The AM335x does not define a signal by that name. I recommend verifying that you have the proper pin-muxing configured by interrogating the corresponding pinmux register using devmem2 at run-time. Also, can you post some snippets from your dts showing your configuration?
  • Thanks for the speedy response.

    It appeared the 8250 driver wasn't quite ready for rs-485 support yet, so defaulted to omap-serial. I am porting code from existing product which is using this driver just fine, but with an older kernel and without devicetree. I can't be switching kernel versions this late in our schedule, so really need to make this work on 3.14.xx.

    I see in omap-serial.c where it is reading devicetree data and setting internal variables for when/how to set RTS with the defined rts-gpio. And I can see in /sys/kernel/debug/gpio where omap-serial has the correct GPIO and with our 485 app running, I never see this change. I also have a scope tapped on this signal.

    cat /sys/kernel/debug/gpio

    GPIOs 0-31, gpio:
     gpio-7   (vtt                     ) out hi    
     gpio-22  (omap-serial  ) out lo    

    GPIOs 32-63, gpio:
     gpio-60  (sysfs               ) out lo    
     gpio-63  (sysfs               ) in  lo    

    GPIOs 64-95, gpio:
     gpio-76  (sysfs               ) out lo    
     gpio-84  (sysfs               ) out lo    
     gpio-86  (sysfs               ) out lo    
     gpio-87  (sysfs               ) out hi    

    GPIOs 96-127, gpio:
     gpio-99  (wlan-en-regulator   ) out hi    
     gpio-117 (kim                            ) out lo    

    Below are code snippets from my latest .dts:

        uart4_pins: pinmux_uart4_pins {
            pinctrl-single,pins = <
                0x020 (PIN_OUTPUT_PULLDOWN | MUX_MODE7)    /* gpmc_ad8.gpio0_22, AM335X_GPIO0_22 */
                0x070 (PIN_INPUT_PULLUP    | MUX_MODE6)    /* gpmc_wait0.uart4_rxd, AM335X_UART4_RXD */
                0x074 (PIN_OUTPUT_PULLDOWN | MUX_MODE6)    /* gpmc_wpn.uart4_txd, AM335X_UART4_TXD */
            >;
        };

    &uart4 {
        pinctrl-names = "default";
        pinctrl-0 = <&uart4_pins>;
        rts-gpio =  <&gpio0 22 0>;
        rs485-rts-delay = <0 0>;
        enable-active-high;
        rs485-rts-active-high;
        linux,rs485-enabled-at-boot-time;
        status = "okay";
    };

    If I don't have 'enabled-at-boot-time' declared, I am able to toggle this signal manually, via sysfs /sys/class/gpio, and change the RTS to the transceiver, allowing the Tx/Rx to operate. But this just proves that I have the correct GPIO configured for this device.

    It's not like the rts-gpio signal is inverse from what transceiver needs, I never see omap-serial change the state. Even though it has 'grabbed' it.

  • It's probably not the issue, but I don't think "enable-active-high" should be in there at all.  The omap-serial driver looks for "rs485-rts-active-high", which you have.

    I suggest adding some instrumentation into the omap-serial driver.  I don't see anything wrong with your dts aside from the superfluous "enable-active-high" I mentioned.

  • Mark,

    The following is the Device Tree configuration that I have used successfully:

        uart4_pins: uart4_pins {
            pinctrl-single,pins = <
                0x4c (PIN_OUTPUT | MUX_MODE7)        /* gpmc_a3.gpio1[19] */
                0x70 (PIN_INPUT | MUX_MODE6)        /* gpmc_wait0.uart4_rxd */
                0x74 (PIN_OUTPUT | MUX_MODE6)        /* gpmc_wpn.uart4_txd */
            >;
        };

    &uart4 {
        pinctrl-names = "default";
        pinctrl-0 = <&uart4_pins>;
        status = "okay";
        rts-gpio = <&gpio1 19 GPIO_ACTIVE_HIGH>;
        rs485-rts-active-high;
        rs485-rts-delay = <0 0>;
        linux,rs485-enabled-at-boot-time;
    };

    The pull-downs on the output pins are not required, but should be fine.  The pull-up on the input should also be fine.

    I agree with the assessment that enable-active-high should be removed completely, and I've highlighted one difference in my configuration that upon a code review appears to me like it could be significant.  I would want to correct your device tree configuration before moving forward with further debugging.

    Brad is also correct that while the omap_serial.c driver supports RS-485 TX enable on any GPIO pin, the 8250 driver that we have moved to in our latest releases only supports this on the RTS line, so keep in mind for future designs that you will have less flexibility on the RS-485 TX enable GPIO.

    Thanks,

    Stuart

  • Thank you Stuart and Brad, I appreciate the feedback.

    With the above changes I still cannot see omap-serial picking up rts-gpio. I have a scope on this pin and its always low. However, when not using "rs485-enabled-at-boot-time" in the .dts, I can manually toggle the gpio value, via /sys/class/gpio/ functions, whereby I see the scope change. And, if I am running communication test apps on either side of this port I can both Tx and Rx. But, obviously I'm bypassing omap-serial and talking directly to the transceiver. This tells me hardware is OK and it is again something in the driver.

    This code base is so mature, I am surprised I am having issues. Hard to explain.

  • Mark,

    One additional thing occurred to me and that is depending on the version of the omap_serial.c driver, I seem to recall that an rs485-rts-delay = <1 1> was required at an absolute minimum in order to see any activity on the TX enable line. This was for an older version of the driver and seems to describe what you are seeing.

    There have been significant improvements in the omap_serial.c driver with respect to the RS-485 support all the way up into the latest 2.x Linux SDK release when we switched over to the standard 8250 driver. It might be worth trying the delay = <1 1> and if that drives the line appropriately, consider pulling in a patch from the latest TI linux SDK to be able to use delay = <0 0>, which I know first hand works in the latest omap_serial.c driver.

    Thanks,
    Stuart
  • Adding my notes onto this issue in case anyone else stumbles across it.

    It looks like the ioctl that handles the cases for TIOCSRS485 never runs. It’s always selecting the default case. That means that serial_omap_config_rs485 never gets called. The version of the omap-serial driver in 4.1 calls serial_omap_config_rs485 from omap_serial_probe, so that’s what I did and removed the call from the IOCTL. Like so:

    ret = serial_omap_probe_rs485(up, pdev->dev.of_node);
    if (ret < 0)
    goto err_rs485;

    sprintf(up->name, "OMAP UART%d", up->port.line);
    up->port.mapbase = mem->start;
    up->port.membase = base;
    up->port.flags = omap_up_info->flags;
    up->port.uartclk = omap_up_info->uartclk;
    serial_omap_config_rs485(&up->port, &up->rs485);

    The RTS line toggles appropriately after recompiling.
    Cheers.