Message ID | 070fa558-6e20-0fbf-d3e4-0a0eca4fe82c@enneenne.com |
---|---|
State | New |
Headers | show |
Series | [RFC] GPIO lines [was: GPIO User I/O] | expand |
On Thu, Jul 9, 2020 at 4:11 PM Rodolfo Giometti <giometti@enneenne.com> wrote: > gpio_lines { > compatible = "gpio-line"; > > bypass0 { > gpios = <&gpionb 10 GPIO_ACTIVE_HIGH>; > mode = "out-low"; > }; > > bypass1 { > gpios = <&gpiosb 11 GPIO_ACTIVE_HIGH>; > mode = "out-low"; > }; > > key { > gpios = <&gpionb 4 GPIO_ACTIVE_HIGH>; > mode = "input"; > }; > > motor { > gpios = <&gpionb 8 GPIO_ACTIVE_HIGH>; > mode = "out-high-open-drain"; > }; These mode = ... strings are really just a big confusion for me since they reinvent several of the flags you can already give to the gpios, see include/dt-bindings/gpio/gpio.h: /* Bit 0 express polarity */ #define GPIO_ACTIVE_HIGH 0 #define GPIO_ACTIVE_LOW 1 /* Bit 1 express single-endedness */ #define GPIO_PUSH_PULL 0 #define GPIO_SINGLE_ENDED 2 /* Bit 2 express Open drain or open source */ #define GPIO_LINE_OPEN_SOURCE 0 #define GPIO_LINE_OPEN_DRAIN 4 /* * Open Drain/Collector is the combination of single-ended open drain interface. * Open Source/Emitter is the combination of single-ended open source interface. */ #define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) #define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE) Yours, Linus Walleij
On 11/07/2020 17:21, Linus Walleij wrote: > On Thu, Jul 9, 2020 at 4:11 PM Rodolfo Giometti <giometti@enneenne.com> wrote: > >> gpio_lines { >> compatible = "gpio-line"; >> >> bypass0 { >> gpios = <&gpionb 10 GPIO_ACTIVE_HIGH>; >> mode = "out-low"; >> }; >> >> bypass1 { >> gpios = <&gpiosb 11 GPIO_ACTIVE_HIGH>; >> mode = "out-low"; >> }; >> >> key { >> gpios = <&gpionb 4 GPIO_ACTIVE_HIGH>; >> mode = "input"; >> }; >> >> motor { >> gpios = <&gpionb 8 GPIO_ACTIVE_HIGH>; >> mode = "out-high-open-drain"; >> }; > > These mode = ... strings are really just a big confusion for me since > they reinvent several of the flags you can already give to the gpios, > see include/dt-bindings/gpio/gpio.h: > > /* Bit 0 express polarity */ > #define GPIO_ACTIVE_HIGH 0 > #define GPIO_ACTIVE_LOW 1 > > /* Bit 1 express single-endedness */ > #define GPIO_PUSH_PULL 0 > #define GPIO_SINGLE_ENDED 2 > > /* Bit 2 express Open drain or open source */ > #define GPIO_LINE_OPEN_SOURCE 0 > #define GPIO_LINE_OPEN_DRAIN 4 > > /* > * Open Drain/Collector is the combination of single-ended open drain interface. > * Open Source/Emitter is the combination of single-ended open source interface. > */ > #define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) > #define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE) These modes came from include/linux/gpio/consumer.h: /** * Optional flags that can be passed to one of gpiod_* to configure direction * and output value. These values cannot be OR'd. */ enum gpiod_flags { GPIOD_ASIS = 0, GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET, GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT, GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | GPIOD_FLAGS_BIT_DIR_VAL, GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_OPEN_DRAIN, GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_OPEN_DRAIN, }; So a setting like the following should set at boot gpio10 as output to 0: bypass0 { gpios = <&gpionb 10 GPIO_ACTIVE_HIGH>; mode = "out-low"; }; While the next one should set gpio10 as output to 1: bypass0 { gpios = <&gpionb 10 GPIO_ACTIVE_LOW>; mode = "out-low"; }; Maybe I can do something similar to hog-gpio as below, if you prefer... bypass0 { gpios = <&gpionb 10 GPIO_ACTIVE_LOW>; output-low; }; Ciao, Rodolfo
On Mon, Jul 13, 2020 at 4:20 PM Rodolfo Giometti <giometti@enneenne.com> wrote: > >> motor { > >> gpios = <&gpionb 8 GPIO_ACTIVE_HIGH>; > >> mode = "out-high-open-drain"; > >> }; (...) > > /* Bit 2 express Open drain or open source */ > > #define GPIO_LINE_OPEN_SOURCE 0 > > #define GPIO_LINE_OPEN_DRAIN 4 (...) > These modes came from include/linux/gpio/consumer.h: OK I was a bit unclear. Mainly open drain should be a flag on the line, not a mode. > Maybe I can do something similar to hog-gpio as below, if you prefer... > > bypass0 { > gpios = <&gpionb 10 GPIO_ACTIVE_LOW>; > output-low; Yes this is better, just boolean flags is not natural than strings for this. However it addresses in a way an issue we have had popping up from time to time which is assignment of default values to lines before they are used overall. I think that would be a bit of thing that should be proper to solve as part of this. The discussion has often stopped short due to different opinions on the device tree bindings for that. Yours, Linus Walleij
On 13/07/2020 23:26, Linus Walleij wrote: > On Mon, Jul 13, 2020 at 4:20 PM Rodolfo Giometti <giometti@enneenne.com> wrote: >> Maybe I can do something similar to hog-gpio as below, if you prefer... >> >> bypass0 { >> gpios = <&gpionb 10 GPIO_ACTIVE_LOW>; >> output-low; > > Yes this is better, just boolean flags is not natural than strings > for this. OK, changed. > However it addresses in a way an issue we have had popping > up from time to time which is assignment of default values to > lines before they are used overall. > > I think that would be a bit of thing that should be proper to > solve as part of this. > > The discussion has often stopped short due to different > opinions on the device tree bindings for that. I see... however attached is a new version of my proposal patch with the following changelog: - GPIO line modes are now decoded as boolean properties (as for gpio-hogs). Allowed values are" "input", "output-low" and "output-high". If nothing is specified defaults to "as-is"; - At boot a more descriptive message for each line is displayed as below: [ 1.834901] line bypass0: GPIO486 added as output-low Ciao, Rodolfo
On Tue, Jul 14, 2020 at 4:01 PM Rodolfo Giometti <giometti@enneenne.com> wrote:
> I see... however attached is a new version of my proposal patch
I looked a bit at this!
IIUC the idea is a "new" sysfs interface that does not require the exporting
etc used by the current "old" sysfs interface. Instead of poking around in
sysfs to export lines we do that from the device tree.
It also does not use any global GPIO numbers which would be my other
main concern.
I must admit that it has some elegance to it. Especially when it comes
to scripting.
The problem I see is that lines are left in whatever state they were in
if a script crashes, so there is no "return to the initial value" that was
there when the GPIOs were picked from the device tree. This makes
this a bit fragile.
Also users regularly need to listen to events. This interface can and
should never support that, for this one must use the character device,
which will of course not work in parallel with using this sysfs ABI.
And the day someone wants that we simply have to say no. There
is no way to hold states for event handling in a stateless ABI.
Well of course they can poll for a line to change, but that is not
proper event handling that reacts to an interrupt.
So while this is much more elegant the old sysfs ABI, and certainly
better for scripting, it still suffers from some conflicts with
the character device, and there is a risk to make users dissatisfied
when they want to e.g. script event handlers.
What are your thoughts on this?
Yours,
Linus Walleij
On 16/07/2020 15:38, Linus Walleij wrote: > On Tue, Jul 14, 2020 at 4:01 PM Rodolfo Giometti <giometti@enneenne.com> wrote: > >> I see... however attached is a new version of my proposal patch > > I looked a bit at this! > > IIUC the idea is a "new" sysfs interface that does not require the exporting > etc used by the current "old" sysfs interface. Instead of poking around in > sysfs to export lines we do that from the device tree. Yes. > It also does not use any global GPIO numbers which would be my other > main concern. Exactly, the idea is to have "names" that describe the IO lines to the userspace and a way to fix their usage in each board in the device tree. If a board has a relay line is a non-sense allow users in the userland to use it as an input line. > I must admit that it has some elegance to it. Especially when it comes > to scripting. :) > The problem I see is that lines are left in whatever state they were in > if a script crashes, so there is no "return to the initial value" that was > there when the GPIOs were picked from the device tree. This makes > this a bit fragile. I see but this interface is not designed for such complex usage nor to compete with the current character interface! It is designed to allow boards manufactures to "describe" some I/O lines that are not used by any driver in the device tree, and that users may desire to manage in a very simple manner. Let's thing about relay lines, or just a locked/unlocked input line which can be easily polled. > Also users regularly need to listen to events. This interface can and > should never support that, for this one must use the character device, > which will of course not work in parallel with using this sysfs ABI. > And the day someone wants that we simply have to say no. There > is no way to hold states for event handling in a stateless ABI. > > Well of course they can poll for a line to change, but that is not > proper event handling that reacts to an interrupt. Again, the basic idea is not to compete with the current character interface nor to manage interrupts lines, but just to have a really simple way to describe those I/O lines which at the moment has no name nor references within the device tree. > So while this is much more elegant the old sysfs ABI, and certainly > better for scripting, it still suffers from some conflicts with > the character device, and there is a risk to make users dissatisfied > when they want to e.g. script event handlers. > > What are your thoughts on this? Regarding the event handlers we can add irq supports... however I prefer to keep this interface really simple just to not be confused with the character interface. Ciao, Rodolfo
On Thu, Jul 16, 2020 at 6:17 PM Rodolfo Giometti <giometti@enneenne.com> wrote: > On 16/07/2020 15:38, Linus Walleij wrote: ... > I see but this interface is not designed for such complex usage nor to compete > with the current character interface! It is designed to allow boards > manufactures to "describe" some I/O lines that are not used by any driver in the > device tree, Why are they not in firmware tables? Platform is a set of hardware that makes it so. If something is not in DT, then there is no possible way to know what is that line? Or in other words how does the OS know that the certain line is connected to a relay? > and that users may desire to manage in a very simple manner. Let's > thing about relay lines, or just a locked/unlocked input line which can be > easily polled.
On 19/07/2020 20:35, Andy Shevchenko wrote: > On Thu, Jul 16, 2020 at 6:17 PM Rodolfo Giometti <giometti@enneenne.com> wrote: >> On 16/07/2020 15:38, Linus Walleij wrote: > > ... > >> I see but this interface is not designed for such complex usage nor to compete >> with the current character interface! It is designed to allow boards >> manufactures to "describe" some I/O lines that are not used by any driver in the >> device tree, > > Why are they not in firmware tables? Platform is a set of hardware > that makes it so. > If something is not in DT, then there is no possible way to know what > is that line? > > Or in other words how does the OS know that the certain line is > connected to a relay? I'm sorry but I'm not sure to understand you. I think that within the DT the board developer should describe his/her hardware in the most detailed manner for drivers and, as last step, for the userspace. The OS should only knows such IO lines whose are driver related while other ones (such as a relay or a generic digital input such as a lock/unlock signal) should be described for the userspace. At the moment the only way to "describe" a digital output/input not related to any driver is by using the led or uinput interface that are not designed for such purposes! My suggestion is to give a proper/dedicated description of such IO lines. Ciao, Rodolfo
On Sun, Jul 19, 2020 at 8:36 PM Andy Shevchenko <andy.shevchenko@gmail.com> wrote: > On Thu, Jul 16, 2020 at 6:17 PM Rodolfo Giometti <giometti@enneenne.com> wrote: > > On 16/07/2020 15:38, Linus Walleij wrote: > > > I see but this interface is not designed for such complex usage nor to compete > > with the current character interface! It is designed to allow boards > > manufactures to "describe" some I/O lines that are not used by any driver in the > > device tree, > > Why are they not in firmware tables? Platform is a set of hardware > that makes it so. > If something is not in DT, then there is no possible way to know what > is that line? > > Or in other words how does the OS know that the certain line is > connected to a relay? IIUC Rodolfo's idea is to provide this with a DT compatible. The use case will be industrial automation-ish tasks from userspace. Currently the only mechanism we have in the device tree to assign a use for a line is the "gpio-line-names" property, which creates a name that is reported upward to the character device. Rodolfo's patch is for scripting use cases, assigning some lines for some cases to be handled by scripts, not the character device. What I am a bit worried about is if this would be a Linuxism, as DT should be OS neutral. Yours, Linus Walleij
On 20/07/20 10:17, Linus Walleij wrote: > IIUC Rodolfo's idea is to provide this with a DT compatible. > The use case will be industrial automation-ish tasks from userspace. > > Currently the only mechanism we have in the device tree to > assign a use for a line is the "gpio-line-names" property, > which creates a name that is reported upward to the character > device. > > Rodolfo's patch is for scripting use cases, assigning some lines > for some cases to be handled by scripts, not the character device. > > What I am a bit worried about is if this would be a Linuxism, as DT > should be OS neutral. Hello Linus, I'm currently using my solution for GPIOs management as input or output lines described in the DT on several systems. Are you re-thinking about this topic? Can we go further for inclusion or should I continue doing out-of-tree? :( Ciao, Rodolfo
On Mon, Apr 26, 2021 at 11:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: > > On 20/07/20 10:17, Linus Walleij wrote: > > IIUC Rodolfo's idea is to provide this with a DT compatible. > > The use case will be industrial automation-ish tasks from userspace. > > > > Currently the only mechanism we have in the device tree to > > assign a use for a line is the "gpio-line-names" property, > > which creates a name that is reported upward to the character > > device. > > > > Rodolfo's patch is for scripting use cases, assigning some lines > > for some cases to be handled by scripts, not the character device. > > > > What I am a bit worried about is if this would be a Linuxism, as DT > > should be OS neutral. Not only neutral but be software'isms free! It's only about hardware. What I understand here is that we have missed the intermediate layer (let's call it 'platform abstraction') where it's related to the platform and neither strictly speaking hardware, nor software per se. > I'm currently using my solution for GPIOs management as input or output lines > described in the DT on several systems. > > Are you re-thinking about this topic? Can we go further for inclusion or should > I continue doing out-of-tree? :(
On 26/04/21 10:48, Andy Shevchenko wrote: > On Mon, Apr 26, 2021 at 11:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: >> >> On 20/07/20 10:17, Linus Walleij wrote: >>> IIUC Rodolfo's idea is to provide this with a DT compatible. >>> The use case will be industrial automation-ish tasks from userspace. >>> >>> Currently the only mechanism we have in the device tree to >>> assign a use for a line is the "gpio-line-names" property, >>> which creates a name that is reported upward to the character >>> device. >>> >>> Rodolfo's patch is for scripting use cases, assigning some lines >>> for some cases to be handled by scripts, not the character device. >>> >>> What I am a bit worried about is if this would be a Linuxism, as DT >>> should be OS neutral. > > Not only neutral but be software'isms free! > It's only about hardware. > > What I understand here is that we have missed the intermediate layer > (let's call it 'platform abstraction') where it's related to the > platform and neither strictly speaking hardware, nor software per se. Maybe I don't well understand the-problem(TM), but why people are currently using led and uinput layers to describes their output or input lines? Why don't providing a dedicated layer for this special scope? My two cents, Rodolfo
On Mon, Apr 26, 2021 at 10:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: > > On 20/07/20 10:17, Linus Walleij wrote: > > IIUC Rodolfo's idea is to provide this with a DT compatible. > > The use case will be industrial automation-ish tasks from userspace. > > > > Currently the only mechanism we have in the device tree to > > assign a use for a line is the "gpio-line-names" property, > > which creates a name that is reported upward to the character > > device. > > > > Rodolfo's patch is for scripting use cases, assigning some lines > > for some cases to be handled by scripts, not the character device. > > > > What I am a bit worried about is if this would be a Linuxism, as DT > > should be OS neutral. > > Hello Linus, > > I'm currently using my solution for GPIOs management as input or output lines > described in the DT on several systems. > > Are you re-thinking about this topic? Can we go further for inclusion or should > I continue doing out-of-tree? :( It is a system description question, and I think you will need to discuss it on the devicetree@vger.kernel.org mailing list. The people there are working with things like the system device tree which is intended to describe aspects of a system apart from what Linux or any operating system is doing with the hardware. I would send an RFC of the proposed DT bindings there. They have been Cced in the past but not addressed directly. Yours, Linus Walleij
On 26/04/21 11:31, Linus Walleij wrote: > On Mon, Apr 26, 2021 at 10:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: >> >> On 20/07/20 10:17, Linus Walleij wrote: >>> IIUC Rodolfo's idea is to provide this with a DT compatible. >>> The use case will be industrial automation-ish tasks from userspace. >>> >>> Currently the only mechanism we have in the device tree to >>> assign a use for a line is the "gpio-line-names" property, >>> which creates a name that is reported upward to the character >>> device. >>> >>> Rodolfo's patch is for scripting use cases, assigning some lines >>> for some cases to be handled by scripts, not the character device. >>> >>> What I am a bit worried about is if this would be a Linuxism, as DT >>> should be OS neutral. >> >> Hello Linus, >> >> I'm currently using my solution for GPIOs management as input or output lines >> described in the DT on several systems. >> >> Are you re-thinking about this topic? Can we go further for inclusion or should >> I continue doing out-of-tree? :( > > It is a system description question, and I think you will need to discuss it > on the devicetree@vger.kernel.org mailing list. > > The people there are working with things like the system device tree which > is intended to describe aspects of a system apart from what Linux or > any operating system is doing with the hardware. That sounds great to me! :-) > I would send an RFC of the proposed DT bindings there. I agree. Please, let me know if you need some help. Ciao, Rodolfo
On Mon, Apr 26, 2021 at 11:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: > On 26/04/21 11:31, Linus Walleij wrote: > > On Mon, Apr 26, 2021 at 10:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: > > I would send an RFC of the proposed DT bindings there. > > I agree. Please, let me know if you need some help. I [general tense] would, implied [if I were you]. What I mean is that whoever wants to drive this should send proposed DT bindings there. I am not volunteering to drive the project. It needs to be driven by those who have the need and can verify that it is fulfils the needs they have. Yours, Linus Walleij
On 26/04/21 12:12, Linus Walleij wrote: > On Mon, Apr 26, 2021 at 11:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: >> On 26/04/21 11:31, Linus Walleij wrote: >>> On Mon, Apr 26, 2021 at 10:44 AM Rodolfo Giometti <giometti@enneenne.com> wrote: > >>> I would send an RFC of the proposed DT bindings there. >> >> I agree. Please, let me know if you need some help. > > I [general tense] would, implied [if I were you]. > > What I mean is that whoever wants to drive this should send > proposed DT bindings there. :) > I am not volunteering to drive the project. It needs to be driven > by those who have the need and can verify that it is fulfils the > needs they have. OK, I'm going to re-propose my patch to the devicetree@vger.kernel.org mailing list putting you on Cc. Thanks a lot for your suggestions. Ciao, Rodolfo
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4f52c3a8ec99..f117b0b9d33e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -73,6 +73,16 @@ config GPIO_SYSFS Kernel drivers may also request that a particular GPIO be exported to userspace; this can be useful when debugging. +config GPIO_LINE + bool "/sys/class/line/... (GPIO lines interface)" + depends on SYSFS + help + Say Y here to add a sysfs interface to manage system's GPIO lines. + + Instead of the GPIO_SYSFS support, by using this support, you'll be + able to use GPIOs from userspace as stated in the device-tree + for well defined pourposes and by using proper names. + config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c256aff66a65..033a6b836dec 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o +obj-$(CONFIG_GPIO_LINE) += gpiolib-line.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o # Device drivers. Generally keep list sorted alphabetically diff --git a/drivers/gpio/gpiolib-line.c b/drivers/gpio/gpiolib-line.c new file mode 100644 index 000000000000..8abd08c1a5e3 --- /dev/null +++ b/drivers/gpio/gpiolib-line.c @@ -0,0 +1,256 @@ +/* + * GPIOlib - userspace I/O line interface + * + * + * Copyright (C) 2020 Rodolfo Giometti <giometti@enneenne.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/idr.h> +#include <linux/kdev_t.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> + +#define GPIO_LINE_MAX_SOURCES 128 /* should be enough... */ + +/* + * Local variables + */ + +static dev_t gpio_line_devt; +static struct class *gpio_line_class; + +static DEFINE_MUTEX(gpio_line_idr_lock); +static DEFINE_IDR(gpio_line_idr); + +struct gpio_line_device { + struct gpio_desc *gpiod; + const char *name; + unsigned int id; + struct device *dev; +}; + +/* + * sysfs methods + */ + +static ssize_t state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_line_device *gpio_line = dev_get_drvdata(dev); + int status, ret; + + ret = sscanf(buf, "%d", &status); + if (ret != 1 && status != 0 && status != 1) + return -EINVAL; + + gpiod_set_value_cansleep(gpio_line->gpiod, status); + + return count; +} + +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_line_device *gpio_line = dev_get_drvdata(dev); + int status = gpiod_get_value_cansleep(gpio_line->gpiod); + + return sprintf(buf, "%d\n", status); +} +static DEVICE_ATTR_RW(state); + +/* + * Class attributes + */ + +static struct attribute *gpio_line_attrs[] = { + &dev_attr_state.attr, + NULL, +}; + +static const struct attribute_group gpio_line_group = { + .attrs = gpio_line_attrs, +}; + +static const struct attribute_group *gpio_line_groups[] = { + &gpio_line_group, + NULL, +}; + +/* + * Driver stuff + */ + +static int gpio_line_create_entry(const char *name, + struct gpio_desc *gpiod, + struct device *parent) +{ + struct gpio_line_device *gpio_line; + dev_t devt; + int ret; + + /* First allocate a new gpio_line device */ + gpio_line = kmalloc(sizeof(struct gpio_line_device), GFP_KERNEL); + if (!gpio_line) + return -ENOMEM; + + mutex_lock(&gpio_line_idr_lock); + /* + * Get new ID for the new gpio_line source. After idr_alloc() calling + * the new source will be freely available into the kernel. + */ + ret = idr_alloc(&gpio_line_idr, gpio_line, 0, + GPIO_LINE_MAX_SOURCES, GFP_KERNEL); + if (ret < 0) { + if (ret == -ENOSPC) { + pr_err("%s: too many GPIO lines in the system\n", + name); + ret = -EBUSY; + } + goto error_device_create; + } + gpio_line->id = ret; + mutex_unlock(&gpio_line_idr_lock); + + /* Create the device and init the device's data */ + devt = MKDEV(MAJOR(gpio_line_devt), gpio_line->id); + gpio_line->dev = device_create(gpio_line_class, parent, devt, gpio_line, + "%s", name); + if (IS_ERR(gpio_line->dev)) { + dev_err(gpio_line->dev, "unable to create device %s\n", name); + ret = PTR_ERR(gpio_line->dev); + goto error_idr_remove; + } + dev_set_drvdata(gpio_line->dev, gpio_line); + + /* Init the gpio_line data */ + gpio_line->gpiod = gpiod; + gpio_line->name = name; + + dev_info(gpio_line->dev, "added\n"); + + return 0; + +error_idr_remove: + mutex_lock(&gpio_line_idr_lock); + idr_remove(&gpio_line_idr, gpio_line->id); + +error_device_create: + mutex_unlock(&gpio_line_idr_lock); + kfree(gpio_line); + + return ret; +} + +static int gpio_line_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fwnode_handle *child; + int ret; + + device_for_each_child_node(dev, child) { + struct device_node *np = to_of_node(child); + const char *label; + enum gpiod_flags flags = GPIOD_ASIS; + const char *mode = "as-is"; + struct gpio_desc *gpiod; + + ret = fwnode_property_read_string(child, "label", &label); + if (ret && IS_ENABLED(CONFIG_OF) && np) + label = np->name; + if (!label) { + dev_err(dev, + "label property not defined or invalid!\n"); + goto skip; + } + + ret = fwnode_property_read_string(child, "mode", &mode); + if ((ret == 0) && mode) { + if (strcmp("as-is", mode) == 0) + flags = GPIOD_ASIS; + else if (strcmp("input", mode) == 0) + flags = GPIOD_IN; + else if (strcmp("out-low", mode) == 0) + flags = GPIOD_OUT_LOW; + else if (strcmp("out-high", mode) == 0) + flags = GPIOD_OUT_HIGH; + else if (strcmp("out-low-open-drain", mode) == 0) + flags = GPIOD_OUT_LOW_OPEN_DRAIN; + else if (strcmp("out-high-open-drain", mode) == 0) + flags = GPIOD_OUT_HIGH_OPEN_DRAIN; + } + + gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child, + flags, label); + if (IS_ERR(gpiod)) { + dev_err(dev, "gpios property not defined!\n"); + goto skip; + } + + ret = gpio_line_create_entry(label, gpiod, dev); + if (ret) + goto skip; + + /* Success, now go to the next child */ + continue; + +skip: /* Error, skip the child */ + fwnode_handle_put(child); + dev_err(dev, "failed to register GPIO lines interface\n"); + } + + return 0; +} + +static const struct of_device_id of_gpio_gpio_line_match[] = { + { .compatible = "gpio-line", }, + { /* sentinel */ } +}; + +static struct platform_driver gpio_line_gpio_driver = { + .driver = { + .name = "gpio-line", + .of_match_table = of_gpio_gpio_line_match, + }, +}; + +builtin_platform_driver_probe(gpio_line_gpio_driver, gpio_line_gpio_probe); + +/* + * Module stuff + */ + +static int __init gpiolib_line_init(void) +{ + /* Create the new class */ + gpio_line_class = class_create(THIS_MODULE, "line"); + if (!gpio_line_class) { + printk(KERN_ERR "gpio_line: failed to create class\n"); + return -ENOMEM; + } + gpio_line_class->dev_groups = gpio_line_groups; + + return 0; +} + +postcore_initcall(gpiolib_line_init);