lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 18 Jan 2013 13:13:01 +0100
From:	Stijn Devriendt <highguy@...il.com>
To:	Roland Stigge <stigge@...com.de>
Cc:	gregkh@...uxfoundation.org, grant.likely@...retlab.ca,
	linus.walleij@...aro.org, linux-kernel@...r.kernel.org,
	linux-arm-kernel@...ts.infradead.org, w.sang@...gutronix.de,
	jbe@...gutronix.de, plagnioj@...osoft.com,
	broonie@...nsource.wolfsonmicro.com, daniel-gl@....net,
	rmallon@...il.com, sr@...x.de, wg@...ndegger.com,
	tru@...k-microwave.de, mark.rutland@....com
Subject: Re: [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib

Hi Roland,

This mail has been long overdue due to issues with some internal
permission-tool.
Just to be clear, this is not a competing implementation, it's what we
currently use as-is. I'm just posting this as a reference to see if
perhaps more concepts could be reused. It's based on a 2.6.32 kernel.

It includes:
- labels in sysfs (to provide useful names to userspace)
- gpio group support
- exporting individual/groups of gpios dictated by platform-data or device-tree
- open-drain support (different from mainline)
- examplary support for multi-gpio to pcf8575 driver
- gpio_direction_output_keep() function that prevents toggling when
changing direction

Provided-as-is-by: Stijn Devriendt <sdevrien@...co.com>
---
 drivers/gpio/Kconfig        |    6 +
 drivers/gpio/Makefile       |    1 +
 drivers/gpio/gpio-export.c  |  327 ++++++++++++++
 drivers/gpio/gpiolib.c      | 1021 ++++++++++++++++++++++++++++++++++++++++---
 drivers/gpio/pcf857x.c      |  102 ++++-
 include/asm-generic/gpio.h  |   67 +++
 include/linux/gpio-export.h |   64 +++
 include/linux/gpio.h        |   83 ++++
 8 files changed, 1602 insertions(+), 69 deletions(-)
 create mode 100644 drivers/gpio/gpio-export.c
 create mode 100644 include/linux/gpio-export.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2ad0128..7daf6df 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -48,6 +48,12 @@ config DEBUG_GPIO
 	  slower.  The diagnostics help catch the type of setup errors
 	  that are most common when setting up new platforms or boards.

+config GPIO_EXPORT
+	bool "GPIO export driver"
+	depends on GPIO_SYSFS && GPIOLIB
+	help
+	  Say Y here to include the GPIO export driver.
+
 config GPIO_SYSFS
 	bool "/sys/class/gpio/... (sysfs interface)"
 	depends on SYSFS && EXPERIMENTAL
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 00a532c..40b96d7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG

 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o

+obj-$(CONFIG_GPIO_EXPORT)	+= gpio-export.o
 obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
 obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
 obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
diff --git a/drivers/gpio/gpio-export.c b/drivers/gpio/gpio-export.c
new file mode 100644
index 0000000..4ee4fe5
--- /dev/null
+++ b/drivers/gpio/gpio-export.c
@@ -0,0 +1,327 @@
+/* drivers/gpio/gpio-export.c
+ *
+ * Copyright (C) 2011 Stijn Devriendt, Cisco Systems Inc.
+ * Copyright (C) 2011 Eli Steenput, Cisco Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/gpio-export.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_OF
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#endif
+
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+struct gpio_export_priv
+{
+	int count;
+	int gpio_num;
+	struct gpio_group *group;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+static __devinit int common_gpio_probe(struct mp_gpio_platform_data
*pdata, struct device *dev)
+{
+	int gpio_count = pdata->gpio_count;
+	struct mp_gpio_line *gpio_line;
+	int i;
+	int err = 0;
+	struct gpio_export_priv *priv;
+
+	if (gpio_count <= 0 || gpio_count > 32)
+		return -ENODEV;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->count = gpio_count;
+	strncpy(priv->desc, pdata->desc, MAX_GPIO_LABEL_SIZE);
+	if (gpio_count > 1)
+	{
+		u32 gpio[32];
+		u32 value = pdata->initialvalue;
+		u32 opendrain = 0;
+		for (i = 0; i < gpio_count; ++i)
+		{
+			gpio_line = &pdata->gpio_data[i];
+			gpio[i] = gpio_line->gpio_num;
+			if (gpio_line->active_low)
+				value ^= (1 << i);
+			if (gpio_line->open_drain)
+				opendrain |= (1 << i);
+		}
+
+		priv->group = gpio_group_request(gpio, gpio_count, priv->desc);
+		if (IS_ERR(priv->group))
+		{
+			dev_err(dev, "Could not request gpio-group: %ld\n", PTR_ERR(priv->group));
+			err = PTR_ERR(priv->group);
+			goto out_mem;
+		}
+
+		if (opendrain)
+		{
+			err = gpio_group_set_opendrain(priv->group,
gpio_group_value_to_raw(priv->group, opendrain));
+			if (err)
+			{
+				dev_err(dev, "Could not set gpio-group open-drain: %d\n", err);
+				goto out_free_group;
+			}
+		}
+
+		switch (pdata->direction)
+		{
+			case GPIO_INPUT:
+				err = gpio_group_direction_input(priv->group);
+				break;
+			case GPIO_OUTPUT:
+				err = gpio_group_direction_output(priv->group,
gpio_group_value_to_raw(priv->group, value));
+				break;
+			case GPIO_OUTPUT_KEEP:
+				err = gpio_group_direction_output_keep(priv->group);
+				break;
+			default:
+        break;
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set gpio-group direction: %d\n", err);
+			goto out_free_group;
+		}
+
+		err = gpio_group_export(priv->group, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_err(dev, "Could not export gpio-group: %d\n", err);
+			goto out_free_group;
+		}
+	}
+	else
+	{
+		gpio_line = &pdata->gpio_data[0];
+		err = gpio_request(gpio_line->gpio_num, priv->desc);
+		if (err)
+		{
+			dev_err(dev, "Could not request gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		if (gpio_line->open_drain)
+		{
+			err = gpio_set_opendrain(gpio_line->gpio_num, 1);
+			if (err)
+			{
+				dev_warn(dev, "Could not set open-drain on gpio %d\n",
gpio_line->gpio_num);
+				goto out_free;
+			}
+		}
+
+		if (pdata->direction == GPIO_INPUT)
+		{
+			err = gpio_direction_input(gpio_line->gpio_num);
+		}
+		else if (pdata->direction == GPIO_OUTPUT)
+		{
+			int value = 0;
+			value = pdata->initialvalue;
+			if (gpio_line->active_low)
+				value = !value;
+			dev_dbg(dev, "Setting output on gpio %d with value %d\n",
gpio_line->gpio_num, value);
+			err = gpio_direction_output(gpio_line->gpio_num, value);
+		}
+		else if (pdata->direction == GPIO_OUTPUT_KEEP)
+		{
+			err = gpio_direction_output_keep(gpio_line->gpio_num);
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set direction: %d\n", err);
+			goto out_free;
+		}
+
+		err = gpio_export(gpio_line->gpio_num, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_warn(dev, "Could not export gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		priv->gpio_num = gpio_line->gpio_num;
+	}
+	dev_set_drvdata(dev, priv);
+	dev_info(dev, "%s: Exported %d GPIO pins\n", pdata->desc, pdata->gpio_count);
+out:
+	return err;
+out_free:
+	gpio_free(gpio_line->gpio_num);
+	kfree(priv);
+	goto out;
+out_free_group:
+	gpio_group_free(priv->group);
+	kfree(priv);
+	goto out;
+out_mem:
+	kfree(priv);
+	goto out;
+}
+
+int common_gpio_remove(struct device *dev)
+{
+	struct gpio_export_priv *priv = dev_get_drvdata(dev);
+	BUG_ON(!priv);
+
+	if (priv->count == 1)
+		gpio_free(priv->gpio_num);
+	else
+		gpio_group_free(priv->group);
+
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+static __devinit int of_gpio_probe(struct platform_device *of_dev,
+		const struct of_device_id *match)
+{
+	struct device_node *np = of_dev->dev.of_node;
+	int gpio_count = of_gpio_count(np);
+	const char* linuxname = of_get_property(np, "desc", NULL);
+
+	struct mp_gpio_platform_data pdata;
+	struct mp_gpio_line gpio_line[32];
+	int i;
+
+	pdata.gpio_data = gpio_line;
+	pdata.gpio_count = gpio_count;
+	strncpy(pdata.desc, linuxname, MAX_GPIO_LABEL_SIZE);
+	pdata.initialvalue = 0;
+
+	if (of_device_is_compatible(np, "gpio-input"))
+		pdata.direction = GPIO_INPUT;
+	else if (of_device_is_compatible(np, "gpio-output"))
+	{
+		const __be32 *value = of_get_property_u32(np, "initial");
+		if (!value)
+			pdata.direction = GPIO_OUTPUT_KEEP;
+		else
+		{
+			pdata.direction = GPIO_OUTPUT;
+			pdata.initialvalue = be32_to_cpup(value);
+			dev_dbg(&of_dev->dev, "initialvalue=%u, keep=%d\n",
pdata.initialvalue, pdata.direction == GPIO_OUTPUT_KEEP);
+		}
+	}
+	else
+	{
+		pdata.direction = GPIO_CHANGE;
+	}
+
+	for (i=0; i < gpio_count; ++i)
+	{
+		u32 flags;
+		gpio_line[i].gpio_num = of_get_gpio_flags(np, i, &flags);
+		gpio_line[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+		gpio_line[i].open_drain = flags & OF_GPIO_OPEN_DRAIN;
+	}
+	return common_gpio_probe(&pdata, &of_dev->dev);
+}
+
+static __devexit int of_gpio_remove(struct platform_device *of_dev)
+{
+	return common_gpio_remove(&of_dev->dev);
+}
+
+static const struct of_device_id __devinitconst of_gpio_match[] = {
+	{ .compatible = "gpio-input", },
+	{ .compatible = "gpio-output", },
+	{ .compatible = "gpio-user", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_gpio_match);
+
+static struct of_platform_driver of_gpio_driver = {
+	.probe = of_gpio_probe,
+	.remove = __devexit_p(of_gpio_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "of-gpio",
+		.of_match_table = of_gpio_match,
+	},
+};
+
+static int __init of_gpio_init(void)
+{
+	// Use i2c_bus_type to support I/O expanders?
+	return of_register_platform_driver(&of_gpio_driver);
+}
+
+static void __exit of_gpio_exit(void)
+{
+	of_unregister_driver(&of_gpio_driver);
+}
+
+late_initcall(of_gpio_init);
+module_exit(of_gpio_exit);
+
+#else
+
+static __devinit int mp_probe(struct platform_device *p_device)
+{
+	return common_gpio_probe(dev_get_platdata(&p_device->dev), &p_device->dev);
+}
+
+static __devexit int mp_remove(struct platform_device *p_device)
+{
+	return common_gpio_remove(&p_device->dev);
+}
+
+static struct platform_device_id mp_id_table[] = {
+	{
+		.name		= "gpio-export",
+	},
+};
+
+static struct platform_driver mp_gpio_driver = {
+	.probe = mp_probe,
+	.remove = mp_remove,
+	.id_table = mp_id_table,
+	.driver.name = "gpio-export",
+	.driver.bus = &platform_bus_type,
+	.driver.owner = THIS_MODULE,
+};
+
+static int __init mp_gpio_init(void)
+{
+	return platform_driver_register(&mp_gpio_driver);
+}
+
+static void __exit mp_gpio_exit(void)
+{
+	platform_driver_unregister(&mp_gpio_driver);
+}
+
+late_initcall(mp_gpio_init);
+module_exit(mp_gpio_exit);
+
+#endif
+
+MODULE_AUTHOR("Stijn Devriendt, Eli Steenput");
+MODULE_DESCRIPTION("Multi Purpose GPIO export driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50de0f5..1c9c426 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -8,7 +8,9 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/idr.h>
+#include <linux/ctype.h>


 /* Optional implementation infrastructure for GPIO interfaces.
@@ -53,15 +55,15 @@ struct gpio_desc {
 #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
 #define FLAG_TRIG_FALL	5	/* trigger on falling edge */
 #define FLAG_TRIG_RISE	6	/* trigger on rising edge */
+#define FLAG_OPEN_DRAIN 7       /* gpio is open drain */

 #define PDESC_ID_SHIFT	16	/* add new flags before this one */

 #define GPIO_FLAGS_MASK		((1 << PDESC_ID_SHIFT) - 1)
 #define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

-#ifdef CONFIG_DEBUG_FS
 	const char		*label;
-#endif
+	struct gpio_group *group;
 };
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

@@ -76,9 +78,7 @@ static struct idr pdesc_idr;

 static inline void desc_set_label(struct gpio_desc *d, const char *label)
 {
-#ifdef CONFIG_DEBUG_FS
 	d->label = label;
-#endif
 }

 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
@@ -119,6 +119,11 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
 	return gpio_desc[gpio].chip;
 }

+static inline struct gpio_group *gpio_to_group(unsigned gpio)
+{
+	return gpio_desc[gpio].group;
+}
+
 /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
 static int gpiochip_find_base(int ngpio)
 {
@@ -189,6 +194,110 @@ err:
 	return ret;
 }

+/* gpio_group_raw_to_value() - reorder bits according to the gpio group
+ * request
+ *
+ * @group: gpio_group
+ * @raw: raw value
+ *
+ * Returns a compact value representing the gpio_group value.
+ * e.g. consider gpio pins [1,0,2,3] have been requested and their
+ * value is [1,0,0,1] respectively. The raw readout will be 0xA [1,0,1,0]
+ * while the return value of this function will be 0x9, considering
+ * the order of the GPIOs in the group.
+ */
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw)
+{
+	int i = 0;
+	u32 ret = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if raw[offset] is set
+		// then set ret[i]
+		if (raw & rawbit)
+			ret |= (1 << i);
+
+		++i;
+	}
+	return ret;
+}
+
+/* gpio_group_value_to_raw() - Inverse of gpio_group_raw_to_value
+ *
+ * @group: gpio_group
+ * @raw: compact value
+ *
+ * Returns the raw value.
+ */
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value)
+{
+	int i = 0;
+	u32 raw = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if value[i] is set
+		// then set ret[offset]
+		if (value & (1 << i))
+			raw |= rawbit;
+
+		++i;
+	}
+	return raw;
+}
+
+int gpio_group_test_bit(unsigned long flag, const struct gpio_group *group)
+{
+	u32 set = 0;
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i) && test_bit(flag, &gpio_desc[base + i].flags))
+			set |= (1 << i);
+	}
+	if (set == 0)
+		return 0;
+	else if (set == group->mask)
+		return 1;
+	else
+		return -EIO;
+}
+
+void gpio_group_set_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			set_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+void gpio_group_clear_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			clear_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+
 #ifdef CONFIG_GPIO_SYSFS

 /* lock protects against unexport_gpio() being called while
@@ -231,6 +340,30 @@ static ssize_t gpio_direction_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_direction_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+
+	status = gpio_group_test_bit(FLAG_IS_OUT, group);
+	if (status >= 0)
+		status = sprintf(buf, "%s\n", status == 0 ? "in" : "out");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static ssize_t gpio_direction_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t size)
 {
@@ -255,9 +388,49 @@ static ssize_t gpio_direction_store(struct device *dev,
 	return status ? : size;
 }

-static const DEVICE_ATTR(direction, 0644,
+static ssize_t gpio_group_direction_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+  const struct gpio_group *group = dev_get_drvdata(dev);
+  ssize_t status;
+  unsigned long input;
+
+  mutex_lock(&sysfs_lock);
+
+  if (gpio_desc[group->gpios[0]].group != group
+   || gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+  {
+    status = -EIO;
+    goto out;
+  }
+
+  if (sysfs_streq(buf, "in"))
+    status = gpio_group_direction_input(group);
+  else if (sysfs_streq(buf, "out"))
+    status = gpio_group_direction_output(group, 0);
+  else
+  {
+    status = strict_strtoul(buf, 0, &input);
+    if (!status)
+    {
+      if ((input & group->mask) == input)
+        status = gpio_group_direction_output(group, input);
+      else
+        status = -EINVAL;
+    }
+  }
+
+out:
+  mutex_unlock(&sysfs_lock);
+  return status ? : size;
+}
+
+static DEVICE_ATTR(direction, 0644,
 		gpio_direction_show, gpio_direction_store);

+struct device_attribute dev_attr_direction_group =
+	__ATTR(direction, 0644, gpio_group_direction_show,
gpio_group_direction_store);
+
 static ssize_t gpio_value_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -276,6 +449,50 @@ static ssize_t gpio_value_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_raw_to_value(group,
+				gpio_group_get_raw_cansleep(group)));
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+
+static ssize_t gpio_group_raw_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_get_raw_cansleep(group));
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static ssize_t gpio_value_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t size)
 {
@@ -303,9 +520,96 @@ static ssize_t gpio_value_store(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group,
+				gpio_group_value_to_raw(group, value));
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_raw_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group, value);
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_mask_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", group->mask);
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static /*const*/ DEVICE_ATTR(value, 0644,
 		gpio_value_show, gpio_value_store);

+struct device_attribute dev_attr_value_group =
+	__ATTR(value, 0644, gpio_group_value_show, gpio_group_value_store);
+
+DEVICE_ATTR(raw, 0644, gpio_group_raw_show, gpio_group_raw_store);
+
+DEVICE_ATTR(mask, 0444, gpio_group_mask_show, NULL);
+
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct work_struct	*work = priv;
@@ -381,7 +685,7 @@ static int gpio_setup_irq(struct gpio_desc *desc,
struct device *dev,
 			goto free_id;
 		}

-		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
+		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
 		if (!pdesc->value_sd) {
 			ret = -ENODEV;
 			goto free_id;
@@ -475,9 +779,55 @@ found:

 static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);

+static ssize_t gpio_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc	*desc = dev_get_drvdata(dev);
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static ssize_t gpio_group_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	const struct gpio_desc	*desc = &gpio_desc[group->gpios[0]];
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static struct device_attribute dev_attr_gpio_label = __ATTR(label,
0444, gpio_label_show, NULL);
+static struct device_attribute dev_attr_gpio_group_label =
__ATTR(label, 0444, gpio_group_label_show, NULL);
+
 static const struct attribute *gpio_attrs[] = {
-	&dev_attr_direction.attr,
 	&dev_attr_value.attr,
+	&dev_attr_gpio_label.attr,
+	NULL,
+};
+
+static const struct attribute *gpio_group_attrs[] = {
+	&dev_attr_value_group.attr,
+	&dev_attr_raw.attr,
+	&dev_attr_mask.attr,
+	&dev_attr_gpio_group_label.attr,
 	NULL,
 };

@@ -485,6 +835,10 @@ static const struct attribute_group gpio_attr_group = {
 	.attrs = (struct attribute **) gpio_attrs,
 };

+static const struct attribute_group gpio_group_attr_group = {
+	.attrs = (struct attribute **) gpio_group_attrs,
+};
+
 /*
  * /sys/class/gpio/gpiochipN/
  *   /base ... matching gpio_chip.base (N)
@@ -530,6 +884,36 @@ static const struct attribute_group gpiochip_attr_group = {
 	.attrs = (struct attribute **) gpiochip_attrs,
 };

+int read_gpios(const char *buf, size_t len, unsigned *gpios)
+{
+	const char *startp = buf;
+	char *endp = (char*)buf;
+	int i;
+	for (i = 0; i < 32; ++i)
+	{
+		unsigned gpio;
+
+		gpio = simple_strtoul(startp, &endp, 0);
+
+		if (endp == startp)
+			return -EINVAL; // not a number, bail out
+
+		gpios[i] = gpio;
+
+		while (endp - buf < len && isspace(*endp)) // eat whitespace
+			endp++;
+
+
+		if (endp - buf > len) // buffer overrun, should never happen
+			return -EINVAL;
+		else if (endp - buf == len) // end of buffer, return number of read gpios
+			return i+1;
+
+		startp = endp;
+	}
+	return -EINVAL;
+}
+
 /*
  * /sys/class/gpio/export ... write-only
  *	integer N ... number of GPIO to export (full access)
@@ -538,10 +922,10 @@ static const struct attribute_group
gpiochip_attr_group = {
  */
 static ssize_t export_store(struct class *class, const char *buf, size_t len)
 {
-	long	gpio;
+	unsigned	gpio[32];
 	int	status;

-	status = strict_strtol(buf, 0, &gpio);
+	status = read_gpios(buf, len, gpio);
 	if (status < 0)
 		goto done;

@@ -550,16 +934,41 @@ static ssize_t export_store(struct class *class,
const char *buf, size_t len)
 	 * they may be undone on its behalf too.
 	 */

-	status = gpio_request(gpio, "sysfs");
-	if (status < 0)
-		goto done;
+	if (status == 1)
+	{
+		status = gpio_request(gpio[0], "sysfs");
+		if (status < 0)
+			goto done;

-	status = gpio_export(gpio, true);
-	if (status < 0)
-		gpio_free(gpio);
+		status = gpio_export(gpio[0], true);
+		if (status < 0)
+			gpio_free(gpio[0]);
+		else
+			set_bit(FLAG_SYSFS, &gpio_desc[gpio[0]].flags);
+	}
 	else
-		set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
+	{
+		struct gpio_group *group = gpio_group_request(gpio, status, "sysfs");
+		if (IS_ERR(group))
+		{
+			status = PTR_ERR(group);
+			goto done;
+		}

+		status = gpio_group_export(group, true);
+		if (status < 0)
+			gpio_group_free(group);
+		else
+    {
+      // Lock required to protect against unexport being called
+      // against when only parts of the group have the flag set.
+      // The other cases: all have the flag or none have the flag
+      // are handled correctly.
+      mutex_lock(&sysfs_lock);
+			gpio_group_set_bit(FLAG_SYSFS, group);
+      mutex_unlock(&sysfs_lock);
+    }
+	}
 done:
 	if (status)
 		pr_debug("%s: status %d\n", __func__, status);
@@ -570,6 +979,7 @@ static ssize_t unexport_store(struct class *class,
const char *buf, size_t len)
 {
 	long	gpio;
 	int	status;
+	struct gpio_group *group;

 	status = strict_strtol(buf, 0, &gpio);
 	if (status < 0)
@@ -581,13 +991,29 @@ static ssize_t unexport_store(struct class
*class, const char *buf, size_t len)
 	if (!gpio_is_valid(gpio))
 		goto done;

-	/* No extra locking here; FLAG_SYSFS just signifies that the
-	 * request and export were done by on behalf of userspace, so
-	 * they may be undone on its behalf too.
-	 */
-	if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
-		status = 0;
-		gpio_free(gpio);
+	group = gpio_to_group(gpio);
+	if (group)
+	{
+		mutex_lock(&sysfs_lock);
+		if (gpio_group_test_bit(FLAG_SYSFS, group))
+		{
+			status = 0;
+			gpio_group_clear_bit(FLAG_SYSFS, group);
+		}
+		mutex_unlock(&sysfs_lock);
+		if (!status)
+			gpio_group_free(group);
+	}
+	else
+	{
+		/* No extra locking here; FLAG_SYSFS just signifies that the
+		 * request and export were done by on behalf of userspace, so
+		 * they may be undone on its behalf too.
+		 */
+		if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
+			status = 0;
+			gpio_free(gpio);
+		}
 	}
 done:
 	if (status)
@@ -662,12 +1088,11 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
 				desc, ioname ? ioname : "gpio%d", gpio);
 		if (!IS_ERR(dev)) {
-			if (direction_may_change)
-				status = sysfs_create_group(&dev->kobj,
-						&gpio_attr_group);
-			else
-				status = device_create_file(dev,
-						&dev_attr_value);
+  		status = sysfs_create_group(&dev->kobj,
+  						&gpio_attr_group);
+
+			if (!status && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction);

 			if (!status && gpio_to_irq(gpio) >= 0
 					&& (direction_may_change
@@ -694,6 +1119,72 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export);

+int gpio_group_export(struct gpio_group *group, bool direction_may_change)
+{
+	unsigned long flags;
+	int status = -EINVAL;
+	struct gpio_chip *chip;
+	unsigned lowest = group->gpios[0];
+	int i;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(group->gpios[0]);
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) > 0
+			&& gpio_group_test_bit(FLAG_EXPORT, group) == 0)
+	{
+		status = 0;
+		if (!chip->direction_input_multi || !chip->direction_output_multi)
+		{
+			direction_may_change = false;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status == 0)
+	{
+		struct device *dev;
+
+		i=1;
+		while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+		{
+			if (group->gpios[i] < lowest)
+				lowest = group->gpios[i];
+			++i;
+		}
+
+		dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0),
+				group, "group%d", lowest);
+		if (!IS_ERR(dev)) {
+			status = sysfs_create_group(&dev->kobj, &gpio_group_attr_group);
+
+			if (status == 0 && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction_group);
+
+			if (status != 0)
+				device_unregister(dev);
+		}
+		else
+			status = PTR_ERR(dev);
+		if (status == 0)
+			gpio_group_set_bit(FLAG_EXPORT, group);
+	}
+	mutex_unlock(&sysfs_lock);
+
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export);
+
+
 static int match_export(struct device *dev, void *data)
 {
 	return dev_get_drvdata(dev) == data;
@@ -744,6 +1235,30 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export_link);

+int gpio_group_export_link(struct device *dev, const char *name,
struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *tdev;
+
+		tdev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (tdev != NULL) {
+			status = sysfs_create_link(&dev->kobj, &tdev->kobj, name);
+		}
+		else
+		{
+			status = -ENODEV;
+		}
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export_link);
+
 /**
  * gpio_unexport - reverse effect of gpio_export()
  * @gpio: gpio to make unavailable
@@ -783,6 +1298,31 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_unexport);

+void gpio_group_unexport(struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *dev = NULL;
+		dev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (dev)
+		{
+			gpio_group_clear_bit(FLAG_EXPORT, group);
+			put_device(dev);
+			device_unregister(dev);
+			status = 0;
+		}
+		else
+			status = -ENODEV;
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+}
+EXPORT_SYMBOL_GPL(gpio_group_unexport);
+
 static int gpiochip_export(struct gpio_chip *chip)
 {
 	int		status;
@@ -959,6 +1499,8 @@ int gpiochip_add(struct gpio_chip *chip)
 		}
 	}

+	of_gpiochip_add(chip);
+
 unlock:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	if (status == 0)
@@ -987,6 +1529,8 @@ int gpiochip_remove(struct gpio_chip *chip)

 	spin_lock_irqsave(&gpio_lock, flags);

+	of_gpiochip_remove(chip);
+
 	for (id = chip->base; id < chip->base + chip->ngpio; id++) {
 		if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
 			status = -EBUSY;
@@ -1007,6 +1551,38 @@ int gpiochip_remove(struct gpio_chip *chip)
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);

+/**
+ * gpiochip_find() - iterator for locating a specific gpio_chip
+ * @data: data to pass to match function
+ * @callback: Callback function to check gpio_chip
+ *
+ * Similar to bus_find_device.  It returns a reference to a gpio_chip as
+ * determined by a user supplied @match callback.  The callback should return
+ * 0 if the device doesn't match and non-zero if it does.  If the callback is
+ * non-zero, this function will return to the caller and not iterate over any
+ * more gpio_chips.
+ */
+struct gpio_chip *gpiochip_find(void *data,
+				int (*match)(struct gpio_chip *chip, void *data))
+{
+	struct gpio_chip *chip = NULL;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	for (i = 0; i < ARCH_NR_GPIOS; i++) {
+		if (!gpio_desc[i].chip)
+			continue;
+
+		if (match(gpio_desc[i].chip, data)) {
+			chip = gpio_desc[i].chip;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return chip;
+}

 /* These "optional" allocation calls help prevent drivers from stomping
  * on each other, and help provide better diagnostics in debugfs.
@@ -1066,6 +1642,111 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_request);

+struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
const char* label)
+{
+	int i;
+	unsigned long flags;
+	u32 mask = 0;
+	struct gpio_chip *chip;
+	int rc = 0;
+	struct gpio_group *group;
+
+	if (ngpios <= 0)
+		return ERR_PTR(-EINVAL);
+
+	group = kzalloc(sizeof(struct gpio_group), GFP_KERNEL);
+	if (!group)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	if (!gpio_is_valid(gpio[0]))
+	{
+		rc = -EINVAL;
+		goto out_free;
+	}
+	chip = gpio_to_chip(gpio[0]);
+	// if not (multi-input or multi-output capable)
+	if (!chip ||
+			!(   (chip->get_multi && chip->direction_input_multi)
+				|| (chip->set_multi && chip->direction_output_multi))
+	   )
+	{
+		rc = -ENODEV;
+		goto out_free;
+	}
+
+	mask |= (1 << (gpio[0] - chip->base));
+	for (i = 1; i < ngpios; ++i)
+	{
+		if (gpio[i] < chip->base || gpio[i] > chip->base + chip->ngpio)
+		{
+			rc = -EINVAL;
+			goto out_free;
+		}
+		mask |= (1 << (gpio[i] - chip->base));
+	}
+
+	if (!try_module_get(chip->owner))
+	{
+		rc = -ENOSYS;
+		goto out_free;
+	}
+
+	group->mask = mask;
+	for (i = 0; i < 32; ++i)
+	{
+		if (i < ngpios)
+			group->gpios[i] = gpio[i];
+		else
+			group->gpios[i] = ARCH_NR_GPIOS;
+	}
+
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) != 0)
+	{
+		rc = -EBUSY;
+		goto out_put;
+	}
+
+	gpio_group_set_bit(FLAG_REQUESTED, group);
+	for (i = 0; i < ngpios; ++i)
+	{
+		gpio_desc[gpio[i]].group = group;
+		desc_set_label(&gpio_desc[gpio[i]], label ? : "?");
+	}
+
+	if (chip->request_multi) {
+		spin_unlock_irqrestore(&gpio_lock, flags);
+		rc = chip->request_multi(chip, mask);
+		spin_lock_irqsave(&gpio_lock, flags);
+		if (rc)
+			goto out_label;
+	}
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return group;
+out_label:
+	for (i = 0; i < ngpios; ++i)
+	{
+		desc_set_label(&gpio_desc[gpio[i]], NULL);
+		gpio_desc[gpio[i]].group = NULL;
+	}
+out_put:
+	module_put(chip->owner);
+out_free:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+	pr_debug("%s: group%d (%s) status %d\n", __func__, *gpio, label ? : "?", rc);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(gpio_group_request);
+
+struct gpio_group * gpio_get_group(unsigned gpio)
+{
+	if (!gpio_is_valid(gpio))
+		return NULL;
+	return gpio_desc[gpio].group;
+}
+
 void gpio_free(unsigned gpio)
 {
 	unsigned long		flags;
@@ -1102,6 +1783,45 @@ void gpio_free(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_free);

+void gpio_group_free(struct gpio_group *group)
+{
+	unsigned long flags;
+	struct gpio_chip *chip;
+
+	might_sleep();
+
+	gpio_group_unexport(group);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(group->gpios[0]);
+	if (chip && gpio_group_test_bit(FLAG_REQUESTED, group))
+	{
+		int i;
+		if (chip->free_multi) {
+			spin_unlock_irqrestore(&gpio_lock, flags);
+			might_sleep_if(extra_checks && chip->can_sleep);
+			chip->free_multi(chip, group->mask);
+			spin_lock_irqsave(&gpio_lock, flags);
+		}
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				desc_set_label(&gpio_desc[chip->base + i], NULL);
+				gpio_desc[chip->base +i].group = NULL;
+			}
+			module_put(chip->owner);
+			gpio_group_clear_bit(FLAG_REQUESTED, group);
+		}
+	}
+	else
+		WARN_ON(extra_checks);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+}
+EXPORT_SYMBOL_GPL(gpio_group_free);

 /**
  * gpiochip_is_requested - return string iff signal was requested
@@ -1124,15 +1844,10 @@ const char *gpiochip_is_requested(struct
gpio_chip *chip, unsigned offset)
 		return NULL;
 	if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
 		return NULL;
-#ifdef CONFIG_DEBUG_FS
 	return gpio_desc[gpio].label;
-#else
-	return "?";
-#endif
 }
 EXPORT_SYMBOL_GPL(gpiochip_is_requested);

-
 /* Drivers MUST set GPIO direction before making get/set calls.  In
  * some cases this is done in early boot, before IRQs are enabled.
  *
@@ -1195,6 +1910,25 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_input);

+
+int gpio_group_direction_input(const struct gpio_group *group)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->get_multi || !chip->direction_input_multi)
+		return -EINVAL;
+
+	rc = chip->direction_input_multi(chip, group->mask);
+	if (rc == 0)
+	{
+		gpio_group_clear_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
 int gpio_direction_output(unsigned gpio, int value)
 {
 	unsigned long		flags;
@@ -1248,6 +1982,124 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_output);

+int gpio_group_direction_output(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+
+	rc = chip->direction_output_multi(chip, group->mask, value & group->mask);
+	if (rc == 0)
+	{
+		gpio_group_set_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
+int gpio_set_opendrain(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+	struct gpio_desc	*desc = &gpio_desc[gpio];
+	int			status = -EINVAL;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	if (!gpio_is_valid(gpio))
+		goto fail;
+	chip = desc->chip;
+	if (!chip || !chip->set || !chip->direction_output)
+		goto fail;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto fail;
+	status = gpio_ensure_requested(desc, gpio);
+	if (status < 0)
+		goto fail;
+
+	/* now we know the gpio is valid and chip won't vanish */
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	if (!chip->set_opendrain)
+		return -ENOSYS;
+
+	if (status) {
+		status = chip->request(chip, gpio);
+		if (status < 0) {
+			pr_debug("GPIO-%d: chip request fail, %d\n",
+				chip->base + gpio, status);
+			/* and it's not available to anyone else ...
+			 * gpio_request() is the fully clean solution.
+			 */
+			goto lose;
+		}
+	}
+
+	status = chip->set_opendrain(chip, gpio, value);
+	if (status == 0)
+	{
+		if (value)
+			set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+		else
+			clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+	}
+lose:
+	return status;
+fail:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	if (status)
+		pr_debug("%s: gpio-%d status %d\n",
+			__func__, gpio, status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_set_opendrain);
+
+int gpio_group_set_opendrain(struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int err;
+	if (!chip)
+		return -EINVAL;
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+	if (!chip->set_opendrain_multi)
+		return -ENOSYS;
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	err = chip->set_opendrain_multi(chip, group->mask, value & group->mask);
+	if (err == 0)
+	{
+		int i;
+		unsigned base = chip->base;
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				if (value & (1 << i))
+				{
+					set_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+				else
+				{
+					clear_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+			}
+		}
+	}
+	if (err)
+		pr_debug("%s: group%d status %d\n",
+				__func__, group->gpios[0], err);
+	return err;
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_opendrain);

 /* I/O calls are only valid after configuration completed; the relevant
  * "is this a valid GPIO" error checks should already have been done.
@@ -1290,6 +2142,14 @@ int __gpio_get_value(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_get_value);

+u32 gpio_group_get_raw(const struct gpio_group* group)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw);
+
 /**
  * __gpio_set_value() - assign a gpio's value
  * @gpio: gpio whose value will be assigned
@@ -1309,6 +2169,13 @@ void __gpio_set_value(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);

+void gpio_group_set_raw(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value | group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw);
 /**
  * __gpio_cansleep() - report whether gpio value access will sleep
  * @gpio: gpio in question
@@ -1328,6 +2195,11 @@ int __gpio_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_cansleep);

+int gpio_group_cansleep(const struct gpio_group *group)
+{
+	return __gpio_cansleep(group->gpios[0]);
+}
+
 /**
  * __gpio_to_irq() - return the IRQ corresponding to a GPIO
  * @gpio: gpio whose IRQ will be returned (already requested)
@@ -1362,6 +2234,15 @@ int gpio_get_value_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);

+u32 gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw_cansleep);
+
 void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	struct gpio_chip	*chip;
@@ -1372,6 +2253,14 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);

+void gpio_group_set_raw_cansleep(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value & group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw_cansleep);

 #ifdef CONFIG_DEBUG_FS

@@ -1381,18 +2270,24 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 	unsigned		gpio = chip->base;
 	struct gpio_desc	*gdesc = &gpio_desc[gpio];
 	int			is_out;
+	int			is_open;
+	int     is_group;

 	for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
 		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
 			continue;

 		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
-		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
-			gpio, gdesc->label,
-			is_out ? "out" : "in ",
-			chip->get
+		is_open = test_bit(FLAG_OPEN_DRAIN, &gdesc->flags);
+		is_group = (gdesc->group != NULL);
+		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
+				gpio, gdesc->label,
+				is_out ? "out" : "in ",
+				chip->get
 				? (chip->get(chip, i) ? "hi" : "lo")
-				: "?  ");
+				: "?  ",
+				is_open ? "open" : "act ",
+				is_group ? "grp" : "pin");

 		if (!is_out) {
 			int		irq = gpio_to_irq(gpio);
@@ -1408,32 +2303,32 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 				char *trigger;

 				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
-				case IRQ_TYPE_NONE:
-					trigger = "(default)";
-					break;
-				case IRQ_TYPE_EDGE_FALLING:
-					trigger = "edge-falling";
-					break;
-				case IRQ_TYPE_EDGE_RISING:
-					trigger = "edge-rising";
-					break;
-				case IRQ_TYPE_EDGE_BOTH:
-					trigger = "edge-both";
-					break;
-				case IRQ_TYPE_LEVEL_HIGH:
-					trigger = "level-high";
-					break;
-				case IRQ_TYPE_LEVEL_LOW:
-					trigger = "level-low";
-					break;
-				default:
-					trigger = "?trigger?";
-					break;
+					case IRQ_TYPE_NONE:
+						trigger = "(default)";
+						break;
+					case IRQ_TYPE_EDGE_FALLING:
+						trigger = "edge-falling";
+						break;
+					case IRQ_TYPE_EDGE_RISING:
+						trigger = "edge-rising";
+						break;
+					case IRQ_TYPE_EDGE_BOTH:
+						trigger = "edge-both";
+						break;
+					case IRQ_TYPE_LEVEL_HIGH:
+						trigger = "level-high";
+						break;
+					case IRQ_TYPE_LEVEL_LOW:
+						trigger = "level-low";
+						break;
+					default:
+						trigger = "?trigger?";
+						break;
 				}

 				seq_printf(s, " irq-%d %s%s",
-					irq, trigger,
-					(desc->status & IRQ_WAKEUP)
+						irq, trigger,
+						(desc->status & IRQ_WAKEUP)
 						? " wakeup" : "");
 			}
 		}
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index 29f19ce..a51d9ad 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -79,6 +79,20 @@ static int pcf857x_input8(struct gpio_chip *chip,
unsigned offset)
 	return status;
 }

+static int pcf857x_input8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= (u16)mask;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+
 static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -88,6 +102,15 @@ static int pcf857x_get8(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -105,6 +128,25 @@ static int pcf857x_output8(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+static void pcf857x_set8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output8_multi(chip, mask, value);
+}
+
 static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output8(chip, offset, value);
@@ -147,6 +189,19 @@ static int pcf857x_input16(struct gpio_chip
*chip, unsigned offset)
 	return status;
 }

+static int pcf857x_input16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= mask;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -156,6 +211,15 @@ static int pcf857x_get16(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_read_le16(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -173,11 +237,30 @@ static int pcf857x_output16(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output16(chip, offset, value);
 }

+static void pcf857x_set16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output16_multi(chip, mask, value);
+}
+
 /*-------------------------------------------------------------------------*/

 static int pcf857x_probe(struct i2c_client *client,
@@ -190,7 +273,6 @@ static int pcf857x_probe(struct i2c_client *client,
 	pdata = client->dev.platform_data;
 	if (!pdata) {
 		dev_dbg(&client->dev, "no platform data\n");
-		return -EINVAL;
 	}

 	/* Allocate, initialize, and register this gpio_chip. */
@@ -200,7 +282,7 @@ static int pcf857x_probe(struct i2c_client *client,

 	mutex_init(&gpio->lock);

-	gpio->chip.base = pdata->gpio_base;
+	gpio->chip.base = pdata ? pdata->gpio_base : -1;
 	gpio->chip.can_sleep = 1;
 	gpio->chip.dev = &client->dev;
 	gpio->chip.owner = THIS_MODULE;
@@ -218,10 +300,14 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	gpio->chip.ngpio = id->driver_data;
 	if (gpio->chip.ngpio == 8) {
+		gpio->chip.direction_input_multi = pcf857x_input8_multi;
 		gpio->chip.direction_input = pcf857x_input8;
 		gpio->chip.get = pcf857x_get8;
+		gpio->chip.get_multi = pcf857x_get8_multi;
 		gpio->chip.direction_output = pcf857x_output8;
+		gpio->chip.direction_output_multi = pcf857x_output8_multi;
 		gpio->chip.set = pcf857x_set8;
+		gpio->chip.set_multi = pcf857x_set8_multi;

 		if (!i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_BYTE))
@@ -239,9 +325,13 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	} else if (gpio->chip.ngpio == 16) {
 		gpio->chip.direction_input = pcf857x_input16;
+		gpio->chip.direction_input_multi = pcf857x_input16_multi;
 		gpio->chip.get = pcf857x_get16;
+		gpio->chip.get_multi = pcf857x_get16_multi;
 		gpio->chip.direction_output = pcf857x_output16;
+		gpio->chip.direction_output_multi = pcf857x_output16_multi;
 		gpio->chip.set = pcf857x_set16;
+		gpio->chip.set_multi = pcf857x_set16_multi;

 		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 			status = -EIO;
@@ -278,7 +368,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	 * to zero, our software copy of the "latch" then matches the chip's
 	 * all-ones reset state.  Otherwise it flags pins to be driven low.
 	 */
-	gpio->out = ~pdata->n_latch;
+	gpio->out = pdata ? ~pdata->n_latch : ~0;

 	status = gpiochip_add(&gpio->chip);
 	if (status < 0)
@@ -299,7 +389,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	/* Let platform code set up the GPIOs and their users.
 	 * Now is the first time anyone could use them.
 	 */
-	if (pdata->setup) {
+	if (pdata && pdata->setup) {
 		status = pdata->setup(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
@@ -310,7 +400,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	return 0;

 fail:
-	dev_dbg(&client->dev, "probe error %d for '%s'\n",
+	dev_err(&client->dev, "probe error %d for '%s'\n",
 			status, client->name);
 	kfree(gpio);
 	return status;
@@ -322,7 +412,7 @@ static int pcf857x_remove(struct i2c_client *client)
 	struct pcf857x			*gpio = i2c_get_clientdata(client);
 	int				status = 0;

-	if (pdata->teardown) {
+	if (pdata && pdata->teardown) {
 		status = pdata->teardown(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 66d6106..ed2407f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -30,6 +30,7 @@ static inline int gpio_is_valid(int number)

 struct seq_file;
 struct module;
+struct device_node;

 /**
  * struct gpio_chip - abstract a GPIO controller
@@ -89,6 +90,22 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	int			(*set_opendrain)(struct gpio_chip *chip,
+						unsigned offset, int value);
+	int			(*set_opendrain_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+
+	int			(*request_multi)(struct gpio_chip *chip,
+						u32 mask);
+	void			(*free_multi)(struct gpio_chip *chip,
+						u32 mask);
+
+	int			(*direction_input_multi)(struct gpio_chip *chip,
+						u32 mask);
+	int			(*direction_output_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+	void			(*set_multi)(struct gpio_chip *chip, u32 mask, u32 value);
+	u32			(*get_multi)(struct gpio_chip *chip, u32 mask);

 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -100,6 +117,23 @@ struct gpio_chip {
 	char			**names;
 	unsigned		can_sleep:1;
 	unsigned		exported:1;
+
+#if defined(CONFIG_OF_GPIO)
+	/*
+	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
+	 * device tree automatically may have an OF translation
+	 */
+	struct device_node *of_node;
+	int of_gpio_n_cells;
+	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
+		        const void *gpio_spec, u32 *flags);
+#endif
+};
+
+struct gpio_group
+{
+	unsigned gpios[32];
+	u32 mask;
 };

 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
@@ -109,6 +143,9 @@ extern int __must_check gpiochip_reserve(int
start, int ngpio);
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
 extern int __must_check gpiochip_remove(struct gpio_chip *chip);
+extern struct gpio_chip *gpiochip_find(void *data,
+					int (*match)(struct gpio_chip *chip,
+						     void *data));


 /* Always use the library code for GPIO management calls,
@@ -120,9 +157,28 @@ extern void gpio_free(unsigned gpio);
 extern int gpio_direction_input(unsigned gpio);
 extern int gpio_direction_output(unsigned gpio, int value);

+extern int gpio_set_opendrain(unsigned gpio, int value);
+
 extern int gpio_get_value_cansleep(unsigned gpio);
 extern void gpio_set_value_cansleep(unsigned gpio, int value);

+/*
+ * Handling of gpio groups
+ */
+extern struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
+    const char *label);
+extern void gpio_group_free(struct gpio_group* group);
+
+extern int gpio_group_direction_input(const struct gpio_group *group);
+extern int gpio_group_direction_output(const struct gpio_group
*group, u32 value);
+
+extern u32 gpio_group_get_raw_cansleep(const struct gpio_group *group);
+extern void gpio_group_set_raw_cansleep(const struct gpio_group
*group, u32 value);
+
+extern int gpio_group_set_opendrain(struct gpio_group* group, u32 value);
+
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw);
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value);

 /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
  * the GPIO is constant and refers to some always-present controller,
@@ -135,6 +191,11 @@ extern int __gpio_cansleep(unsigned gpio);

 extern int __gpio_to_irq(unsigned gpio);

+extern u32 gpio_group_get_raw(const struct gpio_group *group);
+extern void gpio_group_set_raw(const struct gpio_group *group, u32 value);
+
+extern int gpio_group_cansleep(const struct gpio_group *group);
+
 #ifdef CONFIG_GPIO_SYSFS

 /*
@@ -146,6 +207,12 @@ extern int gpio_export_link(struct device *dev,
const char *name,
 			unsigned gpio);
 extern void gpio_unexport(unsigned gpio);

+extern int gpio_group_export(struct gpio_group *group,
+    bool direction_may_change);
+extern int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group);
+extern void gpio_group_unexport(struct gpio_group *group);
+
 #endif	/* CONFIG_GPIO_SYSFS */

 #else	/* !CONFIG_HAVE_GPIO_LIB */
diff --git a/include/linux/gpio-export.h b/include/linux/gpio-export.h
new file mode 100644
index 0000000..712e9ff
--- /dev/null
+++ b/include/linux/gpio-export.h
@@ -0,0 +1,64 @@
+/*    Structures for passing gpio settings to drivers/gpio/gpio-export.c
+ *    201103 steene99
+ */
+#ifndef __LINUX_GPIO_EXPORT_H
+#define __LINUX_GPIO_EXPORT_H
+
+enum gpio_direction
+{
+	GPIO_INPUT,
+	GPIO_OUTPUT,
+	GPIO_CHANGE,
+	GPIO_OUTPUT_KEEP,
+};
+
+struct mp_gpio_line
+{
+	int gpio_num;
+	int active_low;
+	int open_drain;
+};
+
+#define GPIO_PIN(_nr, _active_low, _open_drain) \
+{ \
+	.gpio_num = _nr, \
+	.active_low = _active_low, \
+	.open_drain = _open_drain, \
+}
+
+#define SIMPLE_GPIO_PIN(_name, _nr) \
+static struct mp_gpio_line _name[] = \
+{ \
+	GPIO_PIN(_nr, 0, 0), \
+}
+
+#define MAX_GPIO_LABEL_SIZE 32
+
+struct mp_gpio_platform_data
+{
+	int gpio_count;
+	enum gpio_direction direction;
+	u32 initialvalue;  // value
+	struct mp_gpio_line *gpio_data;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+#define DEFINE_GPIO_GROUP(_name, _count, _direction, _initial, _pin, _desc) \
+	static struct mp_gpio_platform_data _name = { \
+		.gpio_count = _count, \
+		.direction = _direction, \
+		.initialvalue = _initial, \
+		.gpio_data = _pin, \
+		.desc=_desc, \
+	}
+
+#define GPIO_PDEV(_id, _pdata) \
+{ \
+	.name = "gpio-export", \
+	.id = _id, \
+	.dev = { \
+		.platform_data = &_pdata, \
+	}, \
+}
+
+#endif //__LINUX_GPIO_EXPORT_H
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 059bd18..f82edad 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -13,6 +13,7 @@
 #include <linux/errno.h>

 struct device;
+struct gpio_chip;

 /*
  * Some platforms don't support the GPIO programming interface.
@@ -33,6 +34,11 @@ static inline int gpio_request(unsigned gpio, const
char *label)
 	return -ENOSYS;
 }

+static inline int gpio_group_request(unsigned *gpios, int ngpios,
const char *label)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
 static inline void gpio_free(unsigned gpio)
 {
 	might_sleep();
@@ -41,16 +47,32 @@ static inline void gpio_free(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_free(struct gpio_group *group)
+{
+	might_sleep();
+	WARN_ON(1);
+}
+
 static inline int gpio_direction_input(unsigned gpio)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_input(const struct gpio_group *group)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_direction_output(unsigned gpio, int value)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_output(const struct gpio_group
*group, u32 value)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_get_value(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -58,12 +80,29 @@ static inline int gpio_get_value(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw(const struct gpio_group *group,
u32 value)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_set_opendrain(unsigned gpio, int value)
+{
+	WARN_ON(1);
+	return -ENOSYS;
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -71,6 +110,12 @@ static inline int gpio_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline int gpio_get_value_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -78,12 +123,23 @@ static inline int gpio_get_value_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw_cansleep(const struct
gpio_group *group, u32 value)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_export(unsigned gpio, bool direction_may_change)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -91,6 +147,12 @@ static inline int gpio_export(unsigned gpio, bool
direction_may_change)
 	return -EINVAL;
 }

+static inline int gpio_group_export(struct gpio_group *group, bool
direction_may_change)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
 				unsigned gpio)
 {
@@ -99,6 +161,12 @@ static inline int gpio_export_link(struct device
*dev, const char *name,
 	return -EINVAL;
 }

+static inline int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}

 static inline void gpio_unexport(unsigned gpio)
 {
@@ -106,6 +174,11 @@ static inline void gpio_unexport(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_unexport(struct gpio_group *group)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */
@@ -122,4 +195,14 @@ static inline int irq_to_gpio(unsigned irq)

 #endif

+static inline int gpio_direction_output_keep(int gpio)
+{
+	return gpio_direction_output(gpio, gpio_get_value_cansleep(gpio));
+}
+
+static inline int gpio_group_direction_output_keep(const struct
gpio_group *group)
+{
+	return gpio_group_direction_output(group, gpio_group_get_raw_cansleep(group));
+}
+
 #endif /* __LINUX_GPIO_H */
-- 
1.7.5.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ