diff mbox

[RESEND,2/6,v13] gpio: Add sysfs support to block GPIO API

Message ID 1358250716-21986-3-git-send-email-stigge@antcom.de (mailing list archive)
State New, archived
Headers show

Commit Message

Roland Stigge Jan. 15, 2013, 11:51 a.m. UTC
This patch adds sysfs support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/ABI/testing/sysfs-gpio |   20 ++
 drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
 include/asm-generic/gpio.h           |   11 +
 include/linux/gpio.h                 |   15 ++
 4 files changed, 297 insertions(+), 1 deletion(-)

Comments

Grant Likely Feb. 15, 2013, 9:50 p.m. UTC | #1
On Tue, 15 Jan 2013 12:51:52 +0100, Roland Stigge <stigge@antcom.de> wrote:
> This patch adds sysfs support to the block GPIO API.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Hi Roland.

I'm going to ignore this patch for the time being because I think my
comments on the first patch basically obsolete what is being done here.
If the a gpio sequence parser is used instead of a global block gpio api
is used, then a sysfs interface for blocks of GPIOs could be implemented
merely as a separate device driver that consumes a bunch of standard
GPIO references.

I would also suggest that an API that accepts a stream of commands
instead of a fixed u32 would be better for the userspace interface. When
I originally suggested doing a char device interface, I was expecting
the sysfs interface to be dropped entirely.

g.

> 
> ---
>  Documentation/ABI/testing/sysfs-gpio |   20 ++
>  drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
>  include/asm-generic/gpio.h           |   11 +
>  include/linux/gpio.h                 |   15 ++
>  4 files changed, 297 insertions(+), 1 deletion(-)
> 
> --- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
> +++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
> @@ -25,3 +25,23 @@ Description:
>  	    /label ... (r/o) descriptive, not necessarily unique
>  	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
>  
> +What:		/sys/class/gpioblock/
> +Date:		October 2012
> +KernelVersion:	3.7
> +Contact:	Roland Stigge <stigge@antcom.de>
> +Description:
> +
> +  Block GPIO devices are visible in sysfs as soon as they are registered
> +  (e.g. via devicetree definition). For actual I/O use, their "exported"
> +  boolean attribute must be set to "1". Then, the attribute "values" is
> +  created and at the same time, the GPIOs in the block are requested for
> +  exclusive use by sysfs.
> +
> +    /sys/class/gpioblock
> +	/BLOCKNAME ... for each GPIO block name
> +	    /ngpio ... (r/o) number of GPIOs in this group
> +	    /exported ... sysfs export state of this group (0, 1)
> +	    /value ... current value as 32 or 64 bit integer in hex
> +                       (only available if /exported is 1)
> +	    /mask ... current mask as 32 or 64 bit integer in hex
> +                       (only available if /exported is 1)
> --- linux-2.6.orig/drivers/gpio/gpiolib.c
> +++ linux-2.6/drivers/gpio/gpiolib.c
> @@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
>  	int i;
>  
>  	for (i = 0; i < block->ngpio; i++)
> -		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
> +		if ((block->cur_mask & BIT(i)) &&
> +		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
>  			return false;
>  	return true;
>  }
> @@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
>  				chip->label, status);
>  }
>  
> +static ssize_t gpio_block_ngpio_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block	*block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", block->ngpio);
> +}
> +
> +static ssize_t gpio_block_value_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block *block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> +		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
> +}
> +
> +static ssize_t gpio_block_value_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t size)
> +{
> +	ssize_t			status;
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +	unsigned long		value;
> +
> +	status = kstrtoul(buf, 0, &value);
> +	if (status == 0) {
> +		mutex_lock(&sysfs_lock);
> +		if (gpio_block_is_output(block)) {
> +			gpio_block_set(block, block->cur_mask, value);
> +			status = size;
> +		} else {
> +			status = -EPERM;
> +		}
> +		mutex_unlock(&sysfs_lock);
> +	}
> +	return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
> +			      gpio_block_value_store);
> +
> +static ssize_t gpio_block_mask_show(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block *block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> +		       "0x%016lx\n", block->cur_mask);
> +}
> +
> +static ssize_t gpio_block_mask_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t size)
> +{
> +	ssize_t			status;
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +	unsigned long		mask;
> +
> +	status = kstrtoul(buf, 0, &mask);
> +	if (status == 0) {
> +		block->cur_mask = mask;
> +		status = size;
> +	}
> +	return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
> +			     gpio_block_mask_store);
> +
> +static struct class gpio_block_class;
> +
> +static int gpio_block_value_is_exported(struct gpio_block *block)
> +{
> +	struct device		*dev;
> +	struct sysfs_dirent	*sd = NULL;
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev)
> +		goto out;
> +
> +	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return !!sd;
> +}
> +
> +static ssize_t gpio_block_exported_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
> +}
> +
> +static int gpio_block_value_export(struct gpio_block *block)
> +{
> +	struct device	*dev;
> +	int		status;
> +	int		i;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	for (i = 0; i < block->ngpio; i++) {
> +		status = gpio_request(block->gpio[i], "sysfs");
> +		if (status)
> +			goto out1;
> +	}
> +
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev) {
> +		status = -ENODEV;
> +		goto out1;
> +	}
> +
> +	status = device_create_file(dev, &dev_attr_block_value);
> +	if (status)
> +		goto out1;
> +
> +	status = device_create_file(dev, &dev_attr_block_mask);
> +	if (status)
> +		goto out2;
> +
> +	mutex_unlock(&sysfs_lock);
> +	return 0;
> +
> +out2:
> +	device_remove_file(dev, &dev_attr_block_value);
> +out1:
> +	while (--i >= 0)
> +		gpio_free(block->gpio[i]);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static int gpio_block_value_unexport(struct gpio_block *block)
> +{
> +	struct device	*dev;
> +	int		i;
> +
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev)
> +		return -ENODEV;
> +
> +	for (i = 0; i < block->ngpio; i++)
> +		gpio_free(block->gpio[i]);
> +
> +	device_remove_file(dev, &dev_attr_block_value);
> +	device_remove_file(dev, &dev_attr_block_mask);
> +
> +	return 0;
> +}
> +
> +static ssize_t gpio_block_exported_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t size)
> +{
> +	long	value;
> +	int	status;
> +	struct	gpio_block *block = dev_get_drvdata(dev);
> +	int	exported = gpio_block_value_is_exported(block);
> +
> +	status = kstrtoul(buf, 0, &value);
> +	if (status < 0)
> +		goto err;
> +
> +	if (value != exported) {
> +		if (value)
> +			status = gpio_block_value_export(block);
> +		else
> +			status = gpio_block_value_unexport(block);
> +		if (!status)
> +			status = size;
> +	} else {
> +		status = size;
> +	}
> +err:
> +	return status;
> +}
> +
> +static struct device_attribute gpio_block_attrs[] = {
> +	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
> +	       gpio_block_exported_store),
> +	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
> +	__ATTR_NULL,
> +};
> +
> +static struct class gpio_block_class = {
> +	.name =         "gpioblock",
> +	.owner =        THIS_MODULE,
> +
> +	.dev_attrs =  gpio_block_attrs,
> +};
> +
> +int gpio_block_export(struct gpio_block *block)
> +{
> +	int		status = 0;
> +	struct device	*dev;
> +
> +	/* can't export until sysfs is available ... */
> +	if (!gpio_block_class.p) {
> +		pr_debug("%s: called too early!\n", __func__);
> +		return -ENOENT;
> +	}
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
> +			    block->name);
> +	if (IS_ERR(dev))
> +		status = PTR_ERR(dev);
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_export);
> +
> +void gpio_block_unexport(struct gpio_block *block)
> +{
> +	struct device *dev;
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (dev)
> +		device_unregister(dev);
> +	mutex_unlock(&sysfs_lock);
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_unexport);
> +
>  static int __init gpiolib_sysfs_init(void)
>  {
>  	int		status;
> @@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
>  	if (status < 0)
>  		return status;
>  
> +	status = class_register(&gpio_block_class);
> +	if (status < 0)
> +		return status;
> +
>  	/* Scan and register the gpio_chips which registered very
>  	 * early (e.g. before the class_register above was called).
>  	 *
> @@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
>  
>  	block->name = name;
>  	block->ngpio = size;
> +	block->cur_mask = ~0;
>  	INIT_LIST_HEAD(&block->gbc_list);
>  	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
>  	if (!block->gpio)
> @@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
>  
>  int gpio_block_register(struct gpio_block *block)
>  {
> +	int ret;
> +
>  	if (gpio_block_find_by_name(block->name))
>  		return -EBUSY;
>  
>  	list_add(&block->list, &gpio_block_list);
>  
> +	ret = gpio_block_export(block);
> +	if (ret)
> +		goto err1;
> +
>  	return 0;
> +err1:
> +	list_del(&block->list);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(gpio_block_register);
>  
> @@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
>  	list_for_each_entry(i, &gpio_block_list, list)
>  		if (i == block) {
>  			list_del(&i->list);
> +			gpio_block_unexport(block);
>  			break;
>  		}
>  }
> --- linux-2.6.orig/include/asm-generic/gpio.h
> +++ linux-2.6/include/asm-generic/gpio.h
> @@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
>  			unsigned gpio);
>  extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
>  extern void gpio_unexport(unsigned gpio);
> +extern int gpio_block_export(struct gpio_block *block);
> +extern void gpio_block_unexport(struct gpio_block *block);
>  
>  #endif	/* CONFIG_GPIO_SYSFS */
>  
> @@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
>  static inline void gpio_unexport(unsigned gpio)
>  {
>  }
> +
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> +	return -ENOSYS;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +}
>  #endif	/* CONFIG_GPIO_SYSFS */
>  
>  #ifdef CONFIG_PINCTRL
> --- linux-2.6.orig/include/linux/gpio.h
> +++ linux-2.6/include/linux/gpio.h
> @@ -88,6 +88,7 @@ struct gpio_block_chip {
>   * @ngpio:      number of gpios in this block
>   * @gpio:       list of gpios in this block
>   * @list:       global list of blocks, maintained by gpiolib
> + * @cur_mask:   currently used gpio mask used by userspace API
>   */
>  struct gpio_block {
>  	struct list_head	gbc_list;
> @@ -97,6 +98,7 @@ struct gpio_block {
>  	unsigned		*gpio;
>  
>  	struct list_head	list;
> +	unsigned long		cur_mask;
>  };
>  
>  #ifdef CONFIG_GENERIC_GPIO
> @@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
>  	WARN_ON(1);
>  }
>  
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> +	/* GPIO block can never have been requested or set as {in,out}put */
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +	/* GPIO block can never have been exported */
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_to_irq(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as input */
diff mbox

Patch

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,23 @@  Description:
 	    /label ... (r/o) descriptive, not necessarily unique
 	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
 
+What:		/sys/class/gpioblock/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Block GPIO devices are visible in sysfs as soon as they are registered
+  (e.g. via devicetree definition). For actual I/O use, their "exported"
+  boolean attribute must be set to "1". Then, the attribute "values" is
+  created and at the same time, the GPIOs in the block are requested for
+  exclusive use by sysfs.
+
+    /sys/class/gpioblock
+	/BLOCKNAME ... for each GPIO block name
+	    /ngpio ... (r/o) number of GPIOs in this group
+	    /exported ... sysfs export state of this group (0, 1)
+	    /value ... current value as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
+	    /mask ... current mask as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -224,7 +224,8 @@  static bool gpio_block_is_output(struct
 	int i;
 
 	for (i = 0; i < block->ngpio; i++)
-		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+		if ((block->cur_mask & BIT(i)) &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
 			return false;
 	return true;
 }
@@ -1020,6 +1021,240 @@  static void gpiochip_unexport(struct gpi
 				chip->label, status);
 }
 
+static ssize_t gpio_block_ngpio_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", block->ngpio);
+}
+
+static ssize_t gpio_block_value_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
+}
+
+static ssize_t gpio_block_value_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		value;
+
+	status = kstrtoul(buf, 0, &value);
+	if (status == 0) {
+		mutex_lock(&sysfs_lock);
+		if (gpio_block_is_output(block)) {
+			gpio_block_set(block, block->cur_mask, value);
+			status = size;
+		} else {
+			status = -EPERM;
+		}
+		mutex_unlock(&sysfs_lock);
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
+			      gpio_block_value_store);
+
+static ssize_t gpio_block_mask_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", block->cur_mask);
+}
+
+static ssize_t gpio_block_mask_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		mask;
+
+	status = kstrtoul(buf, 0, &mask);
+	if (status == 0) {
+		block->cur_mask = mask;
+		status = size;
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
+			     gpio_block_mask_store);
+
+static struct class gpio_block_class;
+
+static int gpio_block_value_is_exported(struct gpio_block *block)
+{
+	struct device		*dev;
+	struct sysfs_dirent	*sd = NULL;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		goto out;
+
+	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return !!sd;
+}
+
+static ssize_t gpio_block_exported_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
+}
+
+static int gpio_block_value_export(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		status;
+	int		i;
+
+	mutex_lock(&sysfs_lock);
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], "sysfs");
+		if (status)
+			goto out1;
+	}
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev) {
+		status = -ENODEV;
+		goto out1;
+	}
+
+	status = device_create_file(dev, &dev_attr_block_value);
+	if (status)
+		goto out1;
+
+	status = device_create_file(dev, &dev_attr_block_mask);
+	if (status)
+		goto out2;
+
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+out2:
+	device_remove_file(dev, &dev_attr_block_value);
+out1:
+	while (--i >= 0)
+		gpio_free(block->gpio[i]);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static int gpio_block_value_unexport(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		i;
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		return -ENODEV;
+
+	for (i = 0; i < block->ngpio; i++)
+		gpio_free(block->gpio[i]);
+
+	device_remove_file(dev, &dev_attr_block_value);
+	device_remove_file(dev, &dev_attr_block_mask);
+
+	return 0;
+}
+
+static ssize_t gpio_block_exported_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t size)
+{
+	long	value;
+	int	status;
+	struct	gpio_block *block = dev_get_drvdata(dev);
+	int	exported = gpio_block_value_is_exported(block);
+
+	status = kstrtoul(buf, 0, &value);
+	if (status < 0)
+		goto err;
+
+	if (value != exported) {
+		if (value)
+			status = gpio_block_value_export(block);
+		else
+			status = gpio_block_value_unexport(block);
+		if (!status)
+			status = size;
+	} else {
+		status = size;
+	}
+err:
+	return status;
+}
+
+static struct device_attribute gpio_block_attrs[] = {
+	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
+	       gpio_block_exported_store),
+	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpio_block_class = {
+	.name =         "gpioblock",
+	.owner =        THIS_MODULE,
+
+	.dev_attrs =  gpio_block_attrs,
+};
+
+int gpio_block_export(struct gpio_block *block)
+{
+	int		status = 0;
+	struct device	*dev;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_block_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
+			    block->name);
+	if (IS_ERR(dev))
+		status = PTR_ERR(dev);
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_block_export);
+
+void gpio_block_unexport(struct gpio_block *block)
+{
+	struct device *dev;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (dev)
+		device_unregister(dev);
+	mutex_unlock(&sysfs_lock);
+}
+EXPORT_SYMBOL_GPL(gpio_block_unexport);
+
 static int __init gpiolib_sysfs_init(void)
 {
 	int		status;
@@ -1030,6 +1265,10 @@  static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpio_block_class);
+	if (status < 0)
+		return status;
+
 	/* Scan and register the gpio_chips which registered very
 	 * early (e.g. before the class_register above was called).
 	 *
@@ -1843,6 +2082,7 @@  struct gpio_block *gpio_block_create(uns
 
 	block->name = name;
 	block->ngpio = size;
+	block->cur_mask = ~0;
 	INIT_LIST_HEAD(&block->gbc_list);
 	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
 	if (!block->gpio)
@@ -2005,12 +2245,21 @@  EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
 
 int gpio_block_register(struct gpio_block *block)
 {
+	int ret;
+
 	if (gpio_block_find_by_name(block->name))
 		return -EBUSY;
 
 	list_add(&block->list, &gpio_block_list);
 
+	ret = gpio_block_export(block);
+	if (ret)
+		goto err1;
+
 	return 0;
+err1:
+	list_del(&block->list);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
 
@@ -2021,6 +2270,7 @@  void gpio_block_unregister(struct gpio_b
 	list_for_each_entry(i, &gpio_block_list, list)
 		if (i == block) {
 			list_del(&i->list);
+			gpio_block_unexport(block);
 			break;
 		}
 }
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -226,6 +226,8 @@  extern int gpio_export_link(struct devic
 			unsigned gpio);
 extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
 extern void gpio_unexport(unsigned gpio);
+extern int gpio_block_export(struct gpio_block *block);
+extern void gpio_block_unexport(struct gpio_block *block);
 
 #endif	/* CONFIG_GPIO_SYSFS */
 
@@ -285,6 +287,15 @@  static inline int gpio_sysfs_set_active_
 static inline void gpio_unexport(unsigned gpio)
 {
 }
+
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+}
 #endif	/* CONFIG_GPIO_SYSFS */
 
 #ifdef CONFIG_PINCTRL
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -88,6 +88,7 @@  struct gpio_block_chip {
  * @ngpio:      number of gpios in this block
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
+ * @cur_mask:   currently used gpio mask used by userspace API
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -97,6 +98,7 @@  struct gpio_block {
 	unsigned		*gpio;
 
 	struct list_head	list;
+	unsigned long		cur_mask;
 };
 
 #ifdef CONFIG_GENERIC_GPIO
@@ -314,6 +316,19 @@  static inline void gpio_unexport(unsigne
 	WARN_ON(1);
 }
 
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	/* GPIO block can never have been requested or set as {in,out}put */
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+	/* GPIO block can never have been exported */
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */