简体   繁体   中英

How to use I2C GPIO expander's pin instead of RTS to control the RS485 direction, in Linux AUART kernel driver?

I'm creating an embedded system based on i.MX287 processor from NXP(Freescale). I'm using a core processing board which is connected to my evaluation board via a mini PCIe connector.

UARTs 0,3,4 are used as RS232 and UARTs 1,2 as RS485. The core board does not provide the RTS signals in its pinout, so I have to use pins from an I2C GPIO expander to control the RS485 direction. The GPIO expander module is also used for controlling some other devices on the board.

In user-space, I can control the direction pin using libi2c, but my client asked me to put the direction pin control in the UART driver.

- how can I interact with an i2c device inside the auart driver? 如何与auart驱动程序中的i2c设备进行交互? (is it possible)

- if it is possible, then how to prevent the i2c-0 bus from being blocked by the kernel? 如果可能,那么如何防止i2c-0总线被​​内核阻塞? (I also need the userspace calls to the libi2c to work properly)

I googled a lot, but most cases are about how to use the I2C driver or how to activate GPIO pins in the sysfs, and I was able to do all of those.

The libi2c is for userspace so I cannot call it here. I also know that opening a file(/dev/i2c-0) in kernel and reading or writing to it is not a good idea. I am trying to understand what is the best way to handle this problem, without causing any concurrent access issues.

I would appreciate any ideas

PS - I don't have a deep understanding of how Linux kernel works, so sorry if my question is a little vague.

Edit 1: based on @0andriy 's suggestion, I edited the DTS file and added the following to /arch/arm/boot/dts/my_dts_file.dts :

/dts-v1/;
#include "imx28.dtsi"

/ {

// some definitions

apbx@80040000 {    
    i2c0: i2c@80058000 {
        pca8575: gpio@20 {
            compatible = "nxp,pca8575";
            reg = <0x20>;   // PCA8575PW Address -0-0-0
            gpio-controller;
            #gpio-cells = <2>;
        };
    };

    auart1: serial@8006c000 {
        pinctrl-names = "default";
        pinctrl-0 = <&auart1_2pins_a>;
        linux,rs485-enabled-at-boot-time;
        rs485-rts-delay = <0 0>;        // in milliseconds
        rts-gpios = <&pca8575 4 GPIO_ACTIVE_LOW>;
        rs485-rts-active-low;
        status = "okay";
    };

    auart2: serial@8006e000 {
        pinctrl-names = "default";
        pinctrl-0 = <&auart2_2pins_b>;
        linux,rs485-enabled-at-boot-time;
        rs485-rts-delay = <0 0>;        // in milliseconds
        rts-gpios = <&pca8575 5 GPIO_ACTIVE_LOW>;
        rs485-rts-active-low;
        status = "okay";
    };
};

// some definitions
};

and then rebuilt the kernel. I also edited the mxs_auart_init_gpios function in the mxs-auart.c driver to print out the pin description of all the auart GPIOs at boot time. but gpiod = mctrl_gpio_to_gpiod(s->gpios, i) is always NULL. the pca8575 GPIO controller is not added under /sys/class/gpio/

root# ls /sys/class/gpio
export       gpiochip128  gpiochip64   unexport
gpiochip0    gpiochip32   gpiochip96

Edit 2:

auart1_2pins_a and auart2_2pins_b from the imx28.dtsi file :

auart2_2pins_b: auart2-2pins@1 {
reg = <1>;
fsl,pinmux-ids = <
        MX28_PAD_AUART2_RX__AUART2_RX
        MX28_PAD_AUART2_TX__AUART2_TX
    >;
    fsl,drive-strength = <MXS_DRIVE_4mA>;
    fsl,voltage = <MXS_VOLTAGE_HIGH>;
    fsl,pull-up = <MXS_PULL_DISABLE>;
};

auart1_2pins_a: auart1-2pins@0 {
    reg = <0>;
    fsl,pinmux-ids = <
            MX28_PAD_AUART1_RX__AUART1_RX
            MX28_PAD_AUART1_TX__AUART1_TX
        >;
    fsl,drive-strength = <MXS_DRIVE_4mA>;
    fsl,voltage = <MXS_VOLTAGE_HIGH>;
    fsl,pull-up = <MXS_PULL_DISABLE>;
};

I'm using kernel 4.14.13

the figure below demonstrates what I'm trying to achieve : 在此处输入图片说明

I'm not familiar at all with your board so take this answer with a pinch of salt, but I've noticed some funny things on your files.

First off, you need to define the I2C pin you want to use for toggling the direction inside the UART pinmux:

auart2_2pins_b: auart2-2pins@1 {
reg = <1>;
fsl,pinmux-ids = <
        MX28_PAD_AUART2_RX__AUART2_RX
        MX28_PAD_AUART2_TX__AUART2_TX
        MX28_PAD_I2C0_SCL__I2C0_SCL
    >;
    fsl,drive-strength = <MXS_DRIVE_4mA>;
    fsl,voltage = <MXS_VOLTAGE_HIGH>;
    fsl,pull-up = <MXS_PULL_DISABLE>;
};

Make sure you double-check the pin name you want to use, I cannot be sure that is the right one.

Then, you seem to be missing the pinctrl for the I2C controller:

i2c0: i2c@80058000 {
        pinctrl-names = "default";
        pinctrl-0 = <&i2c0_pins_a>;
        status = "okay";

        pca8575: gpio@20 {
            compatible = "nxp,pca8575";
            reg = <0x20>;   // PCA8575PW Address -0-0-0
            gpio-controller;
            #gpio-cells = <2>;
        };
    };

I could not confirm your reg and your pin numbers but I'm assuming you took it from your board's documentation. If you didn't, make sure you find a reliable source for your hardware.

Finally, I'm not sure why you want to have the RTS line active low, most transceivers have a DE/~RE input, which means you need to have the line active high to drive the bus. Maybe your driver is different...

What you are trying to do is documented to be working for other boards so I guess unless there is a bug you should be able to make it work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM