driver allowing to define GPIO pin groups and control them via sysfs

Started by January 24, 2018
Hi,

I'm developing a system based on Xilinx MPSoC chip, where multiple functionalities
are controlled by AXI GPIO blocks.
All those GPIO pins are accessible via sysfs interface.
However, it is difficult to find the gpio chip and pin numbers from running Linux
system.
In the HDL design, the GPIO blocks are described by their names, but unfortunately
those names are not propagated to the device tree.

For single pins, I have created a solution, that defines the virtual node in the
device tree:
https://github.com/wzab/Z-turn-examples/blob/master/axi_dma_prj2/software/example/dts/ksgpio.dtsi
and provides the kernel space driver that allows to control single pins via
functions exported to other kernel drivers:
https://github.com/wzab/Z-turn-examples/blob/master/axi_dma_prj2/software/example/axi4s2dmov/ksgpio.c

What I need now is something that supports multiple GPIOs and allows to define the
group of pins like below. (for example assuming that we have a simple bus with 8-bit
address, 8-bit data, read and write strobe, the example shuffles the order of pins
to show the required flexibility)
In the device tree i can handle it like below:

&amba_pl {
            multi-gpio {
                    compatible = "wzab,multi-gpio";
                    data-gpios = <&axi_gpio_ctl 0 0>, 
                                  <&axi_gpio_ctl 17 0>, 
                                  <&axi_gpio_ctl 11 0>, 
                                  <&axi_gpio_ctl 2 0>, 
                                  <&axi_gpio_ctl 4 0>, 
                                  <&axi_gpio_ctl 6 0>, 
                                  <&axi_gpio_ctl 3 0>, 
                                  <&axi_gpio_ctl 7 0>; 
                    address-gpios = <&axi_gpio_ctl 1 0>, 
                                  <&axi_gpio_ctl 5 0>, 
                                  <&axi_gpio_ctl 14 0>, 
                                  <&axi_gpio_ctl 18 0>, 
                                  <&axi_gpio_ctl 19 0>, 
                                  <&axi_gpio_ctl 8 0>,
                                  <&axi_gpio_ctl 10 0>, 
                                  <&axi_gpio_ctl 12 0>, 
                    read-gpios = <&axi_gpio_ctl 21 0>, 
                    write-gpios = <&axi_gpio_ctl 24 0>, 
            };
};
Of course it would be nice if I could describe the continuous range of GPIOs in a
simple way like "data-gpios = <&axi_pio_ctl 7-0 0>". 

But the real problem is how to efficently represent it in the driver?
It would be perfect, if I could create the node in the sysfs, like
/sys/class/multi-gpio/data, then control its direction and set its value like a
single gpio:

echo out > /sys/class/multi-gpio/data/direction
echo 12 >  /sys/class/multi-gpio/data/value
echo 1 >   /sys/class/multi-gpio/write/value
echo 0 >   /sys/class/multi-gpio/write/value
echo in > /sys/class/multi-gpio/data/direction
echo 1 >   /sys/class/multi-gpio/read/value
val=`cat   /sys/class/multi-gpio/data/value`
echo 0 >   /sys/class/multi-gpio/read/value

Is the above a reasonable approach? Are there any existing solutions that can be
used for such purposes?

Thank you very much in advance,
With best regards,
Wojtek
It seems, that the functions devm_gpiod_get_array, gpiod_set_array_value and similar
may be useful...

Wojtek
Well, I have implemented a quick&dirty, proof of the concept implementation of such
driver. The sources are available at
https://groups.google.com/forum/#!topic/alt.sources/qp88RCer95s
I'll appreciate any error corrections or suggestions of improvements.

Regards,
Wojtek
The disadvantage of the solution published at
https://groups.google.com/forum/#!topic/alt.sources/qp88RCer95s is that it requires
creating "attributes" representing the groups of gpios manually in the code.
It would be good, to have a code that could autamatically scan the device tree for
lists of gpios, and create appropriate attributes.
Unfortunately, the standard way of creating the attributes uses macros that are
processed at the compile time (DEVICE_ATTR_RO and similar).
It seems, that the attributes may be created dynamically at the runtime, using the 
"device_create_file" function, but the "show" and "store" functions should be
created in advance.
Well, probably it is possible to use the same "show" and "store" function for all
attributes, and then access the appropriate array of GPIOs after checking the
attribute name passed to the function via the "attr" argument (see
http://elixir.free-electrons.com/linux/v4.14.14/source/include/linux/device.h#L553
).
So in fact the only thing that is lacking is the possibility to find all GPIOs list
in the device tree node.
Unfortunately, the gpiolib does not contain any iterator like "for_each_con_id" that
I could run on a platform device...
Are there any other ways to do it?

TIA & Regards,
Wojtek
At the moment I have a version of the driver that allows to define the groups of the
GPIOs in a single simple table in the driver's code:

static my_attr_t my_attrs[]={
  {"dout1",NULL},
  {"dout2",NULL},
  {"din",NULL},
  {NULL,NULL}
};

The user is responsible to make sure that the names in the table agree with the DT
definition (e.g. in this case it should contain "dout1-gpios", "dout2-gpios and
"din-gpios").
The sources of the driver are available at
https://groups.google.com/d/msg/alt.sources/qp88RCer95s/xilfmezIAQAJ

Regards,
Wojtek