3

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.

Questions:

1- how can I interact with an i2c device inside the auart driver? (is it possible)

2- if it is possible, then how to prevent the i2c-0 bus from being blocked by the kernel? (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

P.S. - 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 : enter image description here

embedded
  • 51
  • 7
  • 1
    You don't need too much. The GPIO expander has to have it's own driver (probably it does already). Then in DTS you define it, and define your serial device to use mctrl GPIO (I don't remember details, but AFAIK it will go whenever you have defined `rts-gpios` and / or other lines in the DTS). – 0andriy Aug 18 '19 at 14:52
  • @0andriy: thank you, I was not aware of rts-gpios option in DTS file. I will look into it and let you know if I could figure it out. – embedded Aug 19 '19 at 06:18
  • @0andriy: I updated my question with the new results. would appreciate hearing your thoughts. – embedded Aug 21 '19 at 14:23
  • Do you have a driver for GPIO expander? Btw, better to work with GPIOs using `libgpiod` https://github.com/brgl/libgpiod/blob/master/README – 0andriy Aug 21 '19 at 17:36
  • @0andriy: Yes, the driver for pcf857x is included in the kernel source tree, and I check in the menuconfig and it is selected to be built. I can communicate with the chip via i2c-tools commands. I check the kernel boot log and it seems the driver is loading but the `mctrl_gpio_to_gpiod` still returning NULL for rts-gpios. – embedded Aug 22 '19 at 16:43
  • It might be a bug (or not implemented feature) in the code, or you are doing something not correct. I guess better to ask in *linux-serial@* maling list. – 0andriy Aug 22 '19 at 20:03
  • Can you show your `auart1_2pins_a` and `auart2_2pins_b` DTS sections? The GPIO should have a pinctrl there too. What kernel version are you running? Can you also include the output of `cat /sys/kernel/debug/gpio`? – Marcos G. Aug 31 '19 at 08:30
  • @Marcos G. : I updated my question . please see (Edit 2). the `/sys/kernel/debug/` folder is empty – embedded Aug 31 '19 at 14:38

1 Answers1

1

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.

Marcos G.
  • 3,371
  • 2
  • 8
  • 16
  • Marcos G. : I apologize for giving misleading information. my dts is actually including the `imx28.dtsi` file in which the `pinctrl` is defined for the I2C controller. to my understanding, my dts descriptions should add some properties to the original definitions in the `dtsi` file, not to replace it. right? – embedded Sep 01 '19 at 12:55
  • Marcos G. : but I still don't understand the part about adding the `MX28_PAD_I2C0_SCL__I2C0_SCL` to pinmux for the uart controller. this pin is not supposed to be controlled directly by the uart driver. the i2c controller should command the GPIO expander module to drive its pin that is connected to the RS485 direction – embedded Sep 01 '19 at 12:59
  • Yes, I think you should include the `imx28.dtsi` and make whatever changes you need on your own device three file. My point was the I2C section in your dtsi should include at least the pinout, status and the driver section (yours only included the driver). See for instance [this board](https://github.com/torvalds/linux/blob/2f4c53349961c8ca480193e47da4d44fdb8335a8/arch/arm/boot/dts/imx28-m28.dtsi) – Marcos G. Sep 01 '19 at 13:43
  • 1
    And regarding the UART pinmux, I think the pin you want to use for the toggling should be included in the UART pinmux. Take a look at this [thread](https://e2e.ti.com/support/processors/f/791/t/527378?AM335x-GPIO-based-RTS-for-RS-485). I'm not completely sure it is mandatory but somehow it makes sense to refer to it there – Marcos G. Sep 01 '19 at 13:55
  • Looking at the sketch you added to your question I think I misunderstood what you were trying to do... – Marcos G. Sep 01 '19 at 14:08
  • You have a DIO chip hooked up to the processor's I2C (now the pca8595 makes more sense). I have no idea if you can do it at device three level. Don't you have any processor GPIOs exposed somewhere? If you are not able to do it you can always toggle the line from userspace, see for instance [this fork](https://github.com/dhruvvyas90/libmodbus) of libmodbus. – Marcos G. Sep 01 '19 at 14:26
  • Marcos G.: No, unfortunately, there is no processor GPIO's exposed and the I cannot change the board design. Yes I toggled it from userspace with no problem but it is the client that wants it to be done in the kernel :| – embedded Sep 02 '19 at 04:34
  • At the moment the only method that is working for me is using `kernel_read` and `kernel_write` functions to work with the `/dev/i2c-0` file directly in the uart driver, but I am not fond of this solution. I don't know if it is a good idea to add my solution as an answer or not – embedded Sep 02 '19 at 04:42
  • Do you see any messages related to the pca8575 with `dmesg`? – Marcos G. Sep 02 '19 at 05:57