This patch adds new driver "gpio-of-helper", witch has possibility to export
gpios defined in dt. It exports them in defined name under
/sysfs/class/gpio/name.
It's rebased from Pantelis Antoniou patch to new kernel.
Usage example:
        gpio {
                compatible = "gpio-of-helper";
                status = "okay";
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_gpio>;

                gsm_on {
                        gpio-name = "gsm_on";
                        gpio = <&pioB 13 GPIO_ACTIVE_HIGH>;
                        output;
                        init-low;
                };
        };

This patch needs Alexey Ignatov [PATCH] gpiolib: allow exporting gpios with
custom names.

Signed-off-by: Jiri Prchal <[email protected]>
---
 drivers/gpio/Kconfig          |  14 ++
 drivers/gpio/Makefile         |   1 +
 drivers/gpio/gpio-of-helper.c | 423 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 438 insertions(+)

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9de1515..3ec127f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -85,6 +85,20 @@ config GPIO_SYSFS
          Kernel drivers may also request that a particular GPIO be
          exported to userspace; this can be useful when debugging.
 
+config GPIO_OF_HELPER
+       bool "GPIO OF helper device"
+       depends on OF_GPIO
+       help
+         Say Y here to add an GPIO OF helper driver
+
+         Allows you specify a GPIO helper based on OF
+         which allows simple export of GPIO functionality
+         in user-space.
+
+         Features include, value set/get, direction control,
+         interrupt/value change poll support, event counting
+         and others.
+
 config GPIO_GENERIC
        tristate
 
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 5d024e3..6708880 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_GPIOLIB)           += gpiolib-legacy.o
 obj-$(CONFIG_OF_GPIO)          += gpiolib-of.o
 obj-$(CONFIG_GPIO_SYSFS)       += gpiolib-sysfs.o
 obj-$(CONFIG_GPIO_ACPI)                += gpiolib-acpi.o
+obj-$(CONFIG_GPIO_OF_HELPER)   += gpio-of-helper.o
 
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)     += gpio-generic.o
diff --git a/drivers/gpio/gpio-of-helper.c b/drivers/gpio/gpio-of-helper.c
new file mode 100644
index 0000000..4e6f2e2
--- /dev/null
+++ b/drivers/gpio/gpio-of-helper.c
@@ -0,0 +1,423 @@
+/*
+ * GPIO OF based helper
+ *
+ * A simple DT based driver to provide access to GPIO functionality
+ * to user-space via sysfs.
+ *
+ * Copyright (C) 2013 Pantelis Antoniou <[email protected]>
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/atomic.h>
+#include <linux/idr.h>
+
+/* fwd decl. */
+struct gpio_of_helper_info;
+
+enum gpio_type {
+       GPIO_TYPE_INPUT = 0,
+       GPIO_TYPE_OUTPUT = 1,
+};
+
+struct gpio_of_entry {
+       int id;
+       struct gpio_of_helper_info *info;
+       struct device_node *node;
+       enum gpio_type type;
+       int gpio;
+       enum of_gpio_flags gpio_flags;
+       int irq;
+       const char *name;
+       atomic64_t counter;
+       unsigned int count_flags;
+#define COUNT_RISING_EDGE      (1 << 0)
+#define COUNT_FALLING_EDGE     (1 << 1)
+};
+
+struct gpio_of_helper_info {
+       struct platform_device *pdev;
+       struct idr idr;
+};
+
+static DEFINE_IDR(gpio_of_helper_idr);
+static DEFINE_SPINLOCK(gpio_of_helper_lock);
+
+static const struct of_device_id gpio_of_helper_of_match[] = {
+       { .compatible = "gpio-of-helper", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, gpio_of_helper_of_match);
+
+static ssize_t gpio_of_helper_show_status(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct gpio_of_helper_info *info = platform_get_drvdata(pdev);
+       struct gpio_of_entry *entry;
+       char *p, *e;
+       int id, n;
+
+       p = buf;
+       e = p + PAGE_SIZE;
+       n = 0;
+       idr_for_each_entry(&info->idr, entry, id) {
+               switch (entry->type) {
+               case GPIO_TYPE_INPUT:
+                       n = snprintf(p, e - p, "%2d %-24s %3d %-3s %llu\n",
+                               entry->id, entry->name, entry->gpio, "IN",
+                               (unsigned long long)
+                                       atomic64_read(&entry->counter));
+                       break;
+               case GPIO_TYPE_OUTPUT:
+                       n = snprintf(p, e - p, "%2d %-24s %3d %-3s\n",
+                               entry->id, entry->name, entry->gpio, "OUT");
+                       break;
+               }
+               p += n;
+       }
+
+       return p - buf;
+}
+
+static DEVICE_ATTR(status, S_IRUGO,
+               gpio_of_helper_show_status, NULL);
+
+static irqreturn_t gpio_of_helper_handler(int irq, void *ptr)
+{
+       struct gpio_of_entry *entry = ptr;
+
+       /* caution - low speed interfaces only! */
+       atomic64_inc(&entry->counter);
+
+       return IRQ_HANDLED;
+}
+
+static struct gpio_of_entry *
+gpio_of_entry_create(struct gpio_of_helper_info *info, struct device_node 
*node)
+{
+       struct platform_device *pdev = info->pdev;
+       struct device *dev = &pdev->dev;
+       struct gpio_of_entry *entry;
+       int err, gpio, irq;
+       unsigned int req_flags, count_flags, irq_flags;
+       enum gpio_type type;
+       enum of_gpio_flags gpio_flags;
+       const char *name;
+
+       /* get the type of the node first */
+       if (of_property_read_bool(node, "input"))
+               type = GPIO_TYPE_INPUT;
+       else if (of_property_read_bool(node, "output"))
+               type = GPIO_TYPE_OUTPUT;
+       else {
+               dev_err(dev, "Not valid gpio node type\n");
+               err = -EINVAL;
+               goto err_bad_node;
+       }
+
+       /* get the name */
+       err = of_property_read_string(node, "gpio-name", &name);
+       if (err != 0) {
+               dev_err(dev, "Failed to get name property\n");
+               goto err_bad_node;
+       }
+
+       err = of_get_named_gpio_flags(node, "gpio", 0, &gpio_flags);
+       if (IS_ERR_VALUE(err)) {
+               dev_err(dev, "Failed to get gpio property of '%s'\n", name);
+               goto err_bad_node;
+       }
+       gpio = err;
+
+       if (!gpio_is_valid(gpio)) {
+               dev_err(dev, "Skipping unavailable gpio %d (%s)\n",
+                        gpio, name);
+               goto err_bad_node;
+       }
+
+       req_flags = 0;
+       count_flags = 0;
+
+       /* set the request flags */
+       switch (type) {
+               case GPIO_TYPE_INPUT:
+                       req_flags = GPIOF_DIR_IN;
+                       if (of_property_read_bool(node, "count-falling-edge"))
+                               count_flags |= COUNT_FALLING_EDGE;
+                       if (of_property_read_bool(node, "count-rising-edge"))
+                               count_flags |= COUNT_RISING_EDGE;
+                       break;
+               case GPIO_TYPE_OUTPUT:
+                       req_flags = GPIOF_DIR_OUT;
+                       if (of_property_read_bool(node, "init-high"))
+                               req_flags |= GPIOF_OUT_INIT_HIGH;
+                       else if (of_property_read_bool(node, "init-low"))
+                               req_flags |= GPIOF_OUT_INIT_LOW;
+                       break;
+       }
+       if (gpio_flags)
+               req_flags |= GPIOF_ACTIVE_LOW;
+
+       /* request the gpio */
+       err = devm_gpio_request_one(dev, gpio, req_flags, name);
+       if (err != 0) {
+               dev_err(dev, "Failed to request gpio '%s'\n", name);
+               goto err_bad_node;
+       }
+
+       gpio_export_name(gpio, 0, name);
+
+       irq = -1;
+       irq_flags = 0;
+
+       /* counter mode requested - need an interrupt */
+       if (count_flags != 0) {
+               irq = gpio_to_irq(gpio);
+               if (IS_ERR_VALUE(irq)) {
+                       dev_err(dev, "Failed to request gpio '%s'\n", name);
+                       goto err_bad_node;
+               }
+
+               if (count_flags & COUNT_RISING_EDGE)
+                       irq_flags |= IRQF_TRIGGER_RISING;
+               if (count_flags & COUNT_FALLING_EDGE)
+                       irq_flags |= IRQF_TRIGGER_FALLING;
+       }
+
+       entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
+       if (entry == NULL) {
+               dev_err(dev, "Failed to allocate gpio entry of '%s'\n", name);
+               err = -ENOMEM;
+               goto err_no_mem;
+       }
+
+       entry->id = -1;
+       entry->info = info;
+       entry->node = of_node_get(node); /* get node reference */
+       entry->type = type;
+       entry->gpio = gpio;
+       entry->gpio_flags = gpio_flags;
+       entry->irq = irq;
+       entry->name = name;
+
+       idr_preload(GFP_KERNEL);
+       spin_lock(&gpio_of_helper_lock);
+       err = idr_alloc(&gpio_of_helper_idr, entry, 0, 0, GFP_NOWAIT);
+       if (err >= 0)
+               entry->id = err;
+       spin_unlock(&gpio_of_helper_lock);
+       idr_preload_end();
+       if (err < 0) {
+               dev_err(dev, "Failed to idr_get_new  of '%s'\n", name);
+               goto err_fail_idr;
+       }
+
+       /* interrupt enable is last thing done */
+       if (irq >= 0) {
+               atomic64_set(&entry->counter, 0);
+               entry->count_flags = count_flags;
+               err = devm_request_irq(dev, irq, gpio_of_helper_handler,
+                               irq_flags, name, entry);
+               if (err != 0) {
+                       dev_err(dev, "Failed to request irq of '%s'\n", name);
+                       goto err_no_irq;
+               }
+       }
+
+       dev_info(dev, "Allocated GPIO id=%d\n", entry->id);
+
+       return entry;
+
+err_fail_idr:
+       /* nothing to do */
+err_no_irq:
+       /* release node ref */
+       of_node_put(node);
+       /* nothing else needs to be done, devres handles it */
+err_no_mem:
+err_bad_node:
+       return ERR_PTR(err);
+}
+
+static int gpio_of_entry_destroy(struct gpio_of_entry *entry)
+{
+       struct gpio_of_helper_info *info = entry->info;
+       struct platform_device *pdev = info->pdev;
+       struct device *dev = &pdev->dev;
+
+       dev_info(dev, "Destroying GPIO id=%d\n", entry->id);
+
+       /* remove from the IDR */
+       idr_remove(&info->idr, entry->id);
+
+       /* remove node ref */
+       of_node_put(entry->node);
+
+       /* free gpio */
+       devm_gpio_free(dev, entry->gpio);
+
+       /* gree irq */
+       if (entry->irq >= 0)
+               devm_free_irq(dev, entry->irq, entry);
+
+       /* and free */
+       devm_kfree(dev, entry);
+
+       return 0;
+}
+
+static int gpio_of_helper_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct gpio_of_helper_info *info;
+       struct gpio_of_entry *entry;
+       struct device_node *pnode = pdev->dev.of_node;
+       struct device_node *cnode;
+       struct pinctrl *pinctrl;
+       int err;
+
+       /* we only support OF */
+       if (pnode == NULL) {
+               dev_err(&pdev->dev, "No platform of_node!\n");
+               return -ENODEV;
+       }
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               /* special handling for probe defer */
+               if (PTR_ERR(pinctrl) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               dev_warn(&pdev->dev,
+                       "pins are not configured from the driver\n");
+       }
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               dev_err(&pdev->dev, "Failed to allocate info\n");
+               err = -ENOMEM;
+               goto err_no_mem;
+       }
+       platform_set_drvdata(pdev, info);
+       info->pdev = pdev;
+
+       idr_init(&info->idr);
+
+       err = device_create_file(dev, &dev_attr_status);
+       if (err != 0) {
+               dev_err(dev, "Failed to create status sysfs attribute\n");
+               goto err_no_sysfs;
+       }
+
+       for_each_child_of_node(pnode, cnode) {
+
+               entry = gpio_of_entry_create(info, cnode);
+               if (IS_ERR_OR_NULL(entry)) {
+                       dev_err(dev, "Failed to create gpio entry\n");
+                       err = PTR_ERR(entry);
+                       goto err_fail_entry;
+               }
+       }
+
+       dev_info(&pdev->dev, "ready\n");
+
+       return 0;
+err_fail_entry:
+       device_remove_file(&pdev->dev, &dev_attr_status);
+err_no_sysfs:
+err_no_mem:
+       return err;
+}
+
+static int gpio_of_helper_remove(struct platform_device *pdev)
+{
+       struct gpio_of_helper_info *info = platform_get_drvdata(pdev);
+       struct gpio_of_entry *entry;
+       int id;
+
+       dev_info(&pdev->dev, "removing\n");
+
+       device_remove_file(&pdev->dev, &dev_attr_status);
+
+       id = 0;
+       idr_for_each_entry(&info->idr, entry, id) {
+               /* destroy each and every one */
+               gpio_of_entry_destroy(entry);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int gpio_of_helper_runtime_suspend(struct device *dev)
+{
+       /* place holder */
+       return 0;
+}
+
+static int gpio_of_helper_runtime_resume(struct device *dev)
+{
+       /* place holder */
+       return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static struct dev_pm_ops gpio_of_helper_pm_ops = {
+       SET_RUNTIME_PM_OPS(gpio_of_helper_runtime_suspend,
+                          gpio_of_helper_runtime_resume, NULL)
+};
+#define GPIO_OF_HELPER_PM_OPS (&gpio_of_helper_pm_ops)
+#else
+#define GPIO_OF_HELPER_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+struct platform_driver gpio_of_helper_driver = {
+       .probe          = gpio_of_helper_probe,
+       .remove         = gpio_of_helper_remove,
+       .driver = {
+               .name           = "gpio-of-helper",
+               .owner          = THIS_MODULE,
+               .pm             = GPIO_OF_HELPER_PM_OPS,
+               .of_match_table = gpio_of_helper_of_match,
+       },
+};
+
+module_platform_driver(gpio_of_helper_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou <[email protected]>");
+MODULE_DESCRIPTION("GPIO OF Helper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-of-helper");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to