Re: [RFC v2 GPIO lines [was: GPIO User I/O]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 13/07/2020 23:26, Linus Walleij wrote:
> On Mon, Jul 13, 2020 at 4:20 PM Rodolfo Giometti <giometti@xxxxxxxxxxxx> 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

-- 
GNU/Linux Solutions                  e-mail: giometti@xxxxxxxxxxxx
Linux Device Driver                          giometti@xxxxxxxx
Embedded Systems                     phone:  +39 349 2432127
UNIX programming                     skype:  rodolfo.giometti
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..640c9971d97e
--- /dev/null
+++ b/drivers/gpio/gpiolib-line.c
@@ -0,0 +1,251 @@
+/*
+ * GPIOlib - userspace I/O line interface
+ *
+ *
+ * Copyright (C) 2020   Rodolfo Giometti <giometti@xxxxxxxxxxxx>
+ *
+ *   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 struct gpio_line_device *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 ERR_PTR(-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;
+
+	return gpio_line;
+
+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 ERR_PTR(ret);
+}
+
+static int gpio_line_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct fwnode_handle *child;
+	struct gpio_line_device *gpio_line;
+	int ret;
+
+	device_for_each_child_node(dev, child) {
+		struct device_node *np = to_of_node(child);
+		const char *name;
+		enum gpiod_flags flags;
+		struct gpio_desc *gpiod;
+
+		ret = fwnode_property_read_string(child, "line-name", &name);
+		if (ret && IS_ENABLED(CONFIG_OF) && np)
+			name = np->name;
+		if (!name) {
+			dev_err(dev,
+				"name property not defined or invalid!\n");
+			goto skip;
+		}
+
+		flags = GPIOD_ASIS;
+		if (of_property_read_bool(np, "input"))
+			flags = GPIOD_IN;
+		else if (of_property_read_bool(np, "output-low"))
+			flags = GPIOD_OUT_LOW;
+		else if (of_property_read_bool(np, "output-high"))
+			flags = GPIOD_OUT_HIGH;
+		gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
+							 flags, name);
+		if (IS_ERR(gpiod)) {
+			dev_err(dev, "gpios property not defined!\n");
+			goto skip;
+		}
+
+		gpio_line = gpio_line_create_entry(name, gpiod, dev);
+		if (IS_ERR(gpio_line))
+			goto skip;
+
+		/* Success, go to the next child */
+		dev_info(gpio_line->dev, "GPIO%d added as %s\n",
+			desc_to_gpio(gpiod),
+			flags == GPIOD_ASIS ? "as-is" :
+			  flags == GPIOD_OUT_HIGH ? "output-high" :
+			    flags == GPIOD_OUT_LOW ? "output-low" :
+			      flags == GPIOD_IN ? "input" : "unknow!");
+		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);

[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux