2012-10-28 20:47:32

by Roland Stigge

[permalink] [raw]
Subject: [PATCH RESEND 0/5 v6] gpio: Add block GPIO

This set of patches adds:

* Block GPIO API to gpiolib
* Sysfs support for GPIO API, to provide userland access
* Devicetree support to instantiate GPIO blocks via DT
* Example implementations in several gpio drivers since they need
special accessor functions for block wise GPIO access
* Fix for race condition in gpiolib on device creation

Signed-off-by: Roland Stigge <[email protected]>
--

Changes since v5:
* Documented sysfs: elaborated on "exported" and "values" attributes
* Documented sysfs: gpiochip is a separate class now
* Aggregated driver support patches for block gpio into one single patch
* Added gpio block driver support for twl6040 and pch

Changes since v4:
* Documented word width
* Bugfix: export/unexport on register/unregister
* Using default dev_attrs for gpio_block_class
* Fix gpiolib: race condition on device creation
* Added driver support for ucb14500, vt8500, xilinx

Changes since v3:
* Added driver support for pca953x, em, pl061, max732x, pcf857x
* Coding style improvements
* Fixed krealloc memory leak in error case
* sysfs: values in hex
* Register blocks in a list
* Narrowing lock scope
* Use S_IWUSR and S_IRUGO instead of direct octal values
* Use for_each_set_bit()
* Change from unsigned to unsigned long for masks and values

Changes since v2:
* Added sysfs support
* Added devicetree support
* Added support for lpc32xx, generic
* Added functions for GPIO block registration
* Added more error checking
* Bit remapping bugfix

Changes since v1:
* API change to 32/64 bit word, bit masks

Thanks to Ryan Mallon, Linus Walleij, Stijn Devriendt, Jean-Christophe
Plagniol-Villard, Mark Brown and Greg Kroah-Hartman for reviewing!

Roland Stigge (5):
gpio: Add a block GPIO API to gpiolib
gpio: Add sysfs support to block GPIO API
gpiolib: Fix default attributes for class
gpio: Add device tree support to block GPIO API
gpio: Add block gpio to several gpio drivers

Documentation/ABI/testing/sysfs-gpio | 29 +
Documentation/devicetree/bindings/gpio/gpio-block.txt | 36 +
Documentation/gpio.txt | 47 +
drivers/gpio/Makefile | 1
drivers/gpio/gpio-em.c | 23
drivers/gpio/gpio-generic.c | 56 +
drivers/gpio/gpio-lpc32xx.c | 82 ++
drivers/gpio/gpio-max730x.c | 61 ++
drivers/gpio/gpio-max732x.c | 59 ++
drivers/gpio/gpio-pca953x.c | 64 ++
drivers/gpio/gpio-pcf857x.c | 24
drivers/gpio/gpio-pch.c | 27
drivers/gpio/gpio-pl061.c | 17
drivers/gpio/gpio-twl6040.c | 32 +
drivers/gpio/gpio-ucb1400.c | 23
drivers/gpio/gpio-vt8500.c | 24
drivers/gpio/gpio-xilinx.c | 44 +
drivers/gpio/gpioblock-of.c | 84 ++
drivers/gpio/gpiolib.c | 510 ++++++++++++++++--
include/asm-generic/gpio.h | 26
include/linux/gpio.h | 87 +++
21 files changed, 1311 insertions(+), 45 deletions(-)


2012-10-28 20:47:14

by Roland Stigge

[permalink] [raw]
Subject: [PATCH RESEND 2/5 v6] gpio: Add sysfs support to block GPIO API

This patch adds sysfs support to the block GPIO API.

Signed-off-by: Roland Stigge <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

---
Documentation/ABI/testing/sysfs-gpio | 18 ++
drivers/gpio/gpiolib.c | 214 +++++++++++++++++++++++++++++++++++
include/asm-generic/gpio.h | 11 +
include/linux/gpio.h | 13 ++
4 files changed, 256 insertions(+)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,21 @@ 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 <[email protected]>
+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)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -972,6 +972,214 @@ 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));
+}
+
+static bool gpio_block_is_output(struct gpio_block *block)
+{
+ int i;
+
+ for (i = 0; i < block->ngpio; i++)
+ if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+ return false;
+ return true;
+}
+
+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, 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 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 out;
+ }
+
+ dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+ if (!dev) {
+ status = -ENODEV;
+ goto out;
+ }
+
+ status = device_create_file(dev, &dev_attr_block_value);
+ if (status)
+ goto out;
+
+ mutex_unlock(&sysfs_lock);
+ return 0;
+
+out:
+ 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);
+
+ 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;
@@ -982,6 +1190,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).
*
@@ -1876,6 +2088,7 @@ int gpio_block_register(struct gpio_bloc
return -EBUSY;

list_add(&block->list, &gpio_block_list);
+ gpio_block_export(block);

return 0;
}
@@ -1888,6 +2101,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
@@ -211,6 +211,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 */

@@ -270,6 +272,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 */

#endif /* _ASM_GENERIC_GPIO_H */
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -291,6 +291,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 */

2012-10-28 20:47:20

by Roland Stigge

[permalink] [raw]
Subject: [PATCH RESEND 4/5 v6] gpio: Add device tree support to block GPIO API

This patch adds device tree support to the block GPIO API.

Signed-off-by: Roland Stigge <[email protected]>

---
Documentation/devicetree/bindings/gpio/gpio-block.txt | 36 +++++++
drivers/gpio/Makefile | 1
drivers/gpio/gpioblock-of.c | 84 ++++++++++++++++++
3 files changed, 121 insertions(+)

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
@@ -0,0 +1,36 @@
+Block GPIO definition
+=====================
+
+This binding specifies arbitrary blocks of gpios, combining gpios from one or
+more GPIO controllers together, to form a word for r/w access.
+
+Required property:
+- compatible: must be "linux,gpio-block"
+
+Required subnodes:
+- the name will be the registered name of the block
+- property "gpios" is a list of gpios for the respective block
+
+Example:
+
+ blockgpio {
+ compatible = "linux,gpio-block";
+
+ block0 {
+ gpios = <&gpio 3 0 0>,
+ <&gpio 3 1 0>;
+ };
+ block1 {
+ gpios = <&gpio 4 1 0>,
+ <&gpio 4 3 0>,
+ <&gpio 4 2 0>,
+ <&gpio 4 4 0>,
+ <&gpio 4 5 0>,
+ <&gpio 4 6 0>,
+ <&gpio 4 7 0>,
+ <&gpio 4 8 0>,
+ <&gpio 4 9 0>,
+ <&gpio 4 10 0>,
+ <&gpio 4 19 0>;
+ };
+ };
--- linux-2.6.orig/drivers/gpio/Makefile
+++ linux-2.6/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG

obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
+obj-$(CONFIG_OF_GPIO) += gpioblock-of.o

# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
--- /dev/null
+++ linux-2.6/drivers/gpio/gpioblock-of.c
@@ -0,0 +1,84 @@
+/*
+ * OF implementation for Block GPIO
+ *
+ * Copyright (C) 2012 Roland Stigge <[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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static int __devinit gpioblock_of_probe(struct platform_device *pdev)
+{
+ struct device_node *block;
+ unsigned *gpios;
+ int ngpio;
+ int ret;
+ struct gpio_block *gb;
+
+ for_each_available_child_of_node(pdev->dev.of_node, block) {
+ int i;
+
+ ngpio = of_gpio_count(block);
+ gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
+ if (!gpios)
+ return -ENOMEM;
+ for (i = 0; i < ngpio; i++) {
+ ret = of_get_gpio(block, i);
+ if (ret < 0)
+ return ret; /* expect -EPROBE_DEFER */
+ gpios[i] = ret;
+ }
+ gb = gpio_block_create(gpios, ngpio, block->name);
+ if (IS_ERR(gb)) {
+ dev_err(&pdev->dev,
+ "Error creating GPIO block from device tree\n");
+ return PTR_ERR(gb);
+ }
+ ret = gpio_block_register(gb);
+ if (ret < 0) {
+ gpio_block_free(gb);
+ return ret;
+ }
+ kfree(gpios);
+ dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
+ block->name, ngpio);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpioblock_of_match[] __devinitdata = {
+ { .compatible = "linux,gpio-block", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, gpioblock_of_match);
+#endif
+
+static struct platform_driver gpioblock_of_driver = {
+ .driver = {
+ .name = "gpio-block",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gpioblock_of_match),
+
+ },
+ .probe = gpioblock_of_probe,
+};
+
+module_platform_driver(gpioblock_of_driver);
+
+MODULE_DESCRIPTION("GPIO Block driver");
+MODULE_AUTHOR("Roland Stigge <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpioblock-of");

2012-10-28 20:47:26

by Roland Stigge

[permalink] [raw]
Subject: [PATCH RESEND 5/5 v6] gpio: Add block gpio to several gpio drivers

This patch adds block GPIO support to several gpio drivers.

Signed-off-by: Roland Stigge <[email protected]>

---
drivers/gpio/gpio-em.c | 23 ++++++++++++
drivers/gpio/gpio-generic.c | 56 ++++++++++++++++++++++++++++++
drivers/gpio/gpio-lpc32xx.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
drivers/gpio/gpio-max730x.c | 61 ++++++++++++++++++++++++++++++++
drivers/gpio/gpio-max732x.c | 59 +++++++++++++++++++++++++++++++
drivers/gpio/gpio-pca953x.c | 64 ++++++++++++++++++++++++++++++++++
drivers/gpio/gpio-pcf857x.c | 24 ++++++++++++
drivers/gpio/gpio-pch.c | 27 ++++++++++++++
drivers/gpio/gpio-pl061.c | 17 +++++++++
drivers/gpio/gpio-twl6040.c | 32 +++++++++++++++++
drivers/gpio/gpio-ucb1400.c | 23 ++++++++++++
drivers/gpio/gpio-vt8500.c | 24 ++++++++++++
drivers/gpio/gpio-xilinx.c | 44 +++++++++++++++++++++++
13 files changed, 536 insertions(+)

--- linux-2.6.orig/drivers/gpio/gpio-em.c
+++ linux-2.6/drivers/gpio/gpio-em.c
@@ -203,6 +203,27 @@ static void em_gio_set(struct gpio_chip
__em_gio_set(chip, GIO_OH, offset - 16, value);
}

+static unsigned long em_gio_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & mask);
+}
+
+static void em_gio_set_block(struct gpio_chip *chip, unsigned long mask,
+ unsigned long values)
+{
+ unsigned long mask_ol = mask & 0xFFFF;
+ unsigned long mask_oh = mask >> 16;
+
+ unsigned long values_ol = values & mask_ol;
+ unsigned long values_oh = (values >> 16) & mask_oh;
+
+ em_gio_write(gpio_to_priv(chip), GIO_OL,
+ mask_ol << 16 | values_ol);
+ em_gio_write(gpio_to_priv(chip), GIO_OH,
+ mask_oh << 16 | values_oh);
+}
+
static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
@@ -317,8 +338,10 @@ static int __devinit em_gio_probe(struct
gpio_chip = &p->gpio_chip;
gpio_chip->direction_input = em_gio_direction_input;
gpio_chip->get = em_gio_get;
+ gpio_chip->get_block = em_gio_get_block;
gpio_chip->direction_output = em_gio_direction_output;
gpio_chip->set = em_gio_set;
+ gpio_chip->set_block = em_gio_set_block;
gpio_chip->to_irq = em_gio_to_irq;
gpio_chip->label = name;
gpio_chip->owner = THIS_MODULE;
--- linux-2.6.orig/drivers/gpio/gpio-generic.c
+++ linux-2.6/drivers/gpio/gpio-generic.c
@@ -122,6 +122,13 @@ static int bgpio_get(struct gpio_chip *g
return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
}

+static unsigned long bgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return bgc->read_reg(bgc->reg_dat) & mask;
+}
+
static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -170,6 +177,51 @@ static void bgpio_set_set(struct gpio_ch
spin_unlock_irqrestore(&bgc->lock, flags);
}

+static void bgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->data &= ~mask;
+ bgc->data |= values & mask;
+
+ bgc->write_reg(bgc->reg_dat, bgc->data);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_with_clear_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long set_bits = values & mask;
+ unsigned long clr_bits = ~values & mask;
+
+ if (set_bits)
+ bgc->write_reg(bgc->reg_set, set_bits);
+ if (clr_bits)
+ bgc->write_reg(bgc->reg_set, clr_bits);
+}
+
+static void bgpio_set_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->data &= ~mask;
+ bgc->data |= values & mask;
+
+ bgc->write_reg(bgc->reg_set, bgc->data);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
return 0;
@@ -317,14 +369,18 @@ static int bgpio_setup_io(struct bgpio_c
bgc->reg_set = set;
bgc->reg_clr = clr;
bgc->gc.set = bgpio_set_with_clear;
+ bgc->gc.set_block = bgpio_set_with_clear_block;
} else if (set && !clr) {
bgc->reg_set = set;
bgc->gc.set = bgpio_set_set;
+ bgc->gc.set_block = bgpio_set_set_block;
} else {
bgc->gc.set = bgpio_set;
+ bgc->gc.set_block = bgpio_set_block;
}

bgc->gc.get = bgpio_get;
+ bgc->gc.get_block = bgpio_get_block;

return 0;
}
--- linux-2.6.orig/drivers/gpio/gpio-lpc32xx.c
+++ linux-2.6/drivers/gpio/gpio-lpc32xx.c
@@ -297,6 +297,22 @@ static int lpc32xx_gpio_get_value_p3(str
return __get_gpio_state_p3(group, pin);
}

+static unsigned long lpc32xx_gpio_get_block_p3(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+ /* In P3_INP_STATE, the 6 GPIOs are scattered over the register,
+ * rearranging to bits 0-5
+ */
+ bits >>= 10;
+ bits &= 0x401F;
+ bits |= (bits & 0x4000) >> 9;
+
+ return bits & mask & 0x3F;
+}
+
static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
@@ -304,6 +320,15 @@ static int lpc32xx_gpi_get_value(struct
return __get_gpi_state_p3(group, pin);
}

+static unsigned long lpc32xx_gpi_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+ return bits & mask;
+}
+
static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
int value)
{
@@ -351,6 +376,27 @@ static void lpc32xx_gpio_set_value_p3(st
__set_gpio_level_p3(group, pin, value);
}

+static void lpc32xx_gpio_set_block_p3(struct gpio_chip *chip,
+ unsigned long mask,
+ unsigned long values)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ u32 set_bits = values & mask;
+ u32 clr_bits = ~values & mask;
+
+ /* States of GPIO 0-5 start at bit 25 */
+ set_bits <<= 25;
+ clr_bits <<= 25;
+
+ /* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+ * but not set and cleared at once
+ */
+ if (set_bits)
+ __raw_writel(set_bits, group->gpio_grp->outp_set);
+ if (clr_bits)
+ __raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
int value)
{
@@ -366,6 +412,31 @@ static int lpc32xx_gpo_get_value(struct
return __get_gpo_state_p3(group, pin);
}

+static void lpc32xx_gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+ unsigned long values)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ u32 set_bits = values & mask;
+ u32 clr_bits = ~values & mask;
+
+ /* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+ * but not set and cleared at once
+ */
+ if (set_bits)
+ __raw_writel(set_bits, group->gpio_grp->outp_set);
+ if (clr_bits)
+ __raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
+static unsigned long lpc32xx_gpo_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ u32 bits = __raw_readl(group->gpio_grp->outp_state);
+
+ return bits & mask;
+}
+
static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
{
if (pin < chip->ngpio)
@@ -440,8 +511,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
.label = "gpio_p0",
.direction_input = lpc32xx_gpio_dir_input_p012,
.get = lpc32xx_gpio_get_value_p012,
+ .get_block = lpc32xx_gpi_get_block,
.direction_output = lpc32xx_gpio_dir_output_p012,
.set = lpc32xx_gpio_set_value_p012,
+ .set_block = lpc32xx_gpo_set_block,
.request = lpc32xx_gpio_request,
.to_irq = lpc32xx_gpio_to_irq_p01,
.base = LPC32XX_GPIO_P0_GRP,
@@ -456,8 +529,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
.label = "gpio_p1",
.direction_input = lpc32xx_gpio_dir_input_p012,
.get = lpc32xx_gpio_get_value_p012,
+ .get_block = lpc32xx_gpi_get_block,
.direction_output = lpc32xx_gpio_dir_output_p012,
.set = lpc32xx_gpio_set_value_p012,
+ .set_block = lpc32xx_gpo_set_block,
.request = lpc32xx_gpio_request,
.to_irq = lpc32xx_gpio_to_irq_p01,
.base = LPC32XX_GPIO_P1_GRP,
@@ -472,8 +547,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
.label = "gpio_p2",
.direction_input = lpc32xx_gpio_dir_input_p012,
.get = lpc32xx_gpio_get_value_p012,
+ .get_block = lpc32xx_gpi_get_block,
.direction_output = lpc32xx_gpio_dir_output_p012,
.set = lpc32xx_gpio_set_value_p012,
+ .set_block = lpc32xx_gpo_set_block,
.request = lpc32xx_gpio_request,
.base = LPC32XX_GPIO_P2_GRP,
.ngpio = LPC32XX_GPIO_P2_MAX,
@@ -487,8 +564,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
.label = "gpio_p3",
.direction_input = lpc32xx_gpio_dir_input_p3,
.get = lpc32xx_gpio_get_value_p3,
+ .get_block = lpc32xx_gpio_get_block_p3,
.direction_output = lpc32xx_gpio_dir_output_p3,
.set = lpc32xx_gpio_set_value_p3,
+ .set_block = lpc32xx_gpio_set_block_p3,
.request = lpc32xx_gpio_request,
.to_irq = lpc32xx_gpio_to_irq_gpio_p3,
.base = LPC32XX_GPIO_P3_GRP,
@@ -503,6 +582,7 @@ static struct lpc32xx_gpio_chip lpc32xx_
.label = "gpi_p3",
.direction_input = lpc32xx_gpio_dir_in_always,
.get = lpc32xx_gpi_get_value,
+ .get_block = lpc32xx_gpi_get_block,
.request = lpc32xx_gpio_request,
.to_irq = lpc32xx_gpio_to_irq_gpi_p3,
.base = LPC32XX_GPI_P3_GRP,
@@ -517,7 +597,9 @@ static struct lpc32xx_gpio_chip lpc32xx_
.label = "gpo_p3",
.direction_output = lpc32xx_gpio_dir_out_always,
.set = lpc32xx_gpo_set_value,
+ .set_block = lpc32xx_gpo_set_block,
.get = lpc32xx_gpo_get_value,
+ .get_block = lpc32xx_gpo_get_block,
.request = lpc32xx_gpio_request,
.base = LPC32XX_GPO_P3_GRP,
.ngpio = LPC32XX_GPO_P3_MAX,
--- linux-2.6.orig/drivers/gpio/gpio-max730x.c
+++ linux-2.6/drivers/gpio/gpio-max730x.c
@@ -146,6 +146,44 @@ static int max7301_get(struct gpio_chip
return level;
}

+static unsigned long max7301_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
+ int i, j;
+ unsigned long result = 0;
+
+ for (i = 0; i < 4; i++) {
+ if ((mask >> (i * 8)) & 0xFF) { /* i/o only if necessary */
+ u8 in_level = ts->read(ts->dev, 0x44 + i * 8);
+ u8 in_mask = 0;
+ u8 out_level = (ts->out_level >> (i * 8 + 4)) & 0xFF;
+ u8 out_mask = 0;
+
+ for (j = 0; j < 8; j++) {
+ int offset = 4 + i * 8 + j;
+ int config = (ts->port_config[offset >> 2] >>
+ ((offset & 3) << 1)) &
+ PIN_CONFIG_MASK;
+
+ switch (config) {
+ case PIN_CONFIG_OUT:
+ out_mask |= BIT(j);
+ break;
+ case PIN_CONFIG_IN_WO_PULLUP:
+ case PIN_CONFIG_IN_PULLUP:
+ in_mask |= BIT(j);
+ }
+ }
+
+ result |= ((unsigned long)(in_level & in_mask) |
+ (out_level & out_mask)) << (i * 8);
+ }
+ }
+
+ return result & mask;
+}
+
static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct max7301 *ts = container_of(chip, struct max7301, chip);
@@ -160,6 +198,27 @@ static void max7301_set(struct gpio_chip
mutex_unlock(&ts->lock);
}

+static void max7301_set_block(struct gpio_chip *chip, unsigned long mask,
+ unsigned long values)
+{
+ struct max7301 *ts = container_of(chip, struct max7301, chip);
+ unsigned long changes;
+ int i;
+
+ mutex_lock(&ts->lock);
+
+ changes = (ts->out_level ^ (values << 4)) & (mask << 4);
+ ts->out_level ^= changes;
+
+ for (i = 0; i < 4; i++) {
+ if ((changes >> (i * 8 + 4)) & 0xFF) /* i/o only on change */
+ ts->write(ts->dev, 0x44 + i * 8,
+ (ts->out_level >> (i * 8 + 4)) & 0xFF);
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
int __devinit __max730x_probe(struct max7301 *ts)
{
struct device *dev = ts->dev;
@@ -183,8 +242,10 @@ int __devinit __max730x_probe(struct max

ts->chip.direction_input = max7301_direction_input;
ts->chip.get = max7301_get;
+ ts->chip.get_block = max7301_get_block;
ts->chip.direction_output = max7301_direction_output;
ts->chip.set = max7301_set;
+ ts->chip.set_block = max7301_set_block;

ts->chip.base = pdata->base;
ts->chip.ngpio = PIN_NUMBER;
--- linux-2.6.orig/drivers/gpio/gpio-max732x.c
+++ linux-2.6/drivers/gpio/gpio-max732x.c
@@ -219,6 +219,63 @@ out:
mutex_unlock(&chip->lock);
}

+static unsigned long max732x_gpio_get_block(struct gpio_chip *gc,
+ unsigned long mask)
+{
+ struct max732x_chip *chip;
+ uint8_t lo = 0;
+ uint8_t hi = 0;
+ int ret;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ if (mask & 0xFF) {
+ ret = max732x_readb(chip, is_group_a(chip, 0), &lo);
+ if (ret < 0)
+ return 0;
+ }
+ if (mask & 0xFF00) {
+ ret = max732x_readb(chip, is_group_a(chip, 8), &hi);
+ if (ret < 0)
+ return 0;
+ }
+
+ return (((unsigned long)hi << 8) | lo) & mask;
+}
+
+static void max732x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct max732x_chip *chip;
+ uint8_t reg_out;
+ uint8_t lo_mask = mask & 0xFF;
+ uint8_t hi_mask = (mask >> 8) & 0xFF;
+ int ret;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ mutex_lock(&chip->lock);
+
+ if (lo_mask) {
+ reg_out = (chip->reg_out[0] & ~lo_mask) | (values & lo_mask);
+ ret = max732x_writeb(chip, is_group_a(chip, 0), reg_out);
+ if (ret < 0)
+ goto out;
+ chip->reg_out[0] = reg_out;
+ }
+ if (hi_mask) {
+ reg_out = (chip->reg_out[1] & ~hi_mask) |
+ ((values >> 8) & hi_mask);
+ ret = max732x_writeb(chip, is_group_a(chip, 8), reg_out);
+ if (ret < 0)
+ goto out;
+ chip->reg_out[1] = reg_out;
+ }
+
+out:
+ mutex_unlock(&chip->lock);
+}
+
static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
struct max732x_chip *chip;
@@ -562,8 +619,10 @@ static int __devinit max732x_setup_gpio(
if (chip->dir_output) {
gc->direction_output = max732x_gpio_direction_output;
gc->set = max732x_gpio_set_value;
+ gc->set_block = max732x_gpio_set_block;
}
gc->get = max732x_gpio_get_value;
+ gc->get_block = max732x_gpio_get_block;
gc->can_sleep = 1;

gc->base = gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pca953x.c
+++ linux-2.6/drivers/gpio/gpio-pca953x.c
@@ -300,6 +300,68 @@ exit:
mutex_unlock(&chip->i2c_lock);
}

+static unsigned long pca953x_gpio_get_block(struct gpio_chip *gc,
+ unsigned long mask)
+{
+ struct pca953x_chip *chip;
+ u32 reg_val;
+ int ret, offset = 0;
+
+ chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+ mutex_lock(&chip->i2c_lock);
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_INPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_IN;
+ break;
+ }
+ ret = pca953x_read_reg(chip, offset, &reg_val);
+ mutex_unlock(&chip->i2c_lock);
+ if (ret < 0) {
+ /* NOTE: diagnostic already emitted; that's all we should
+ * do unless gpio_*_value_cansleep() calls become different
+ * from their nonsleeping siblings (and report faults).
+ */
+ return 0;
+ }
+
+ return reg_val & mask;
+}
+
+static void pca953x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct pca953x_chip *chip;
+ u32 reg_val;
+ int ret, offset = 0;
+
+ chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+ mutex_lock(&chip->i2c_lock);
+
+ reg_val = chip->reg_output & ~mask;
+ reg_val |= values & mask;
+
+ switch (chip->chip_type) {
+ case PCA953X_TYPE:
+ offset = PCA953X_OUTPUT;
+ break;
+ case PCA957X_TYPE:
+ offset = PCA957X_OUT;
+ break;
+ }
+ ret = pca953x_write_reg(chip, offset, reg_val);
+ if (ret)
+ goto exit;
+
+ chip->reg_output = reg_val;
+exit:
+ mutex_unlock(&chip->i2c_lock);
+}
+
static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
{
struct gpio_chip *gc;
@@ -310,6 +372,8 @@ static void pca953x_setup_gpio(struct pc
gc->direction_output = pca953x_gpio_direction_output;
gc->get = pca953x_gpio_get_value;
gc->set = pca953x_gpio_set_value;
+ gc->get_block = pca953x_gpio_get_block;
+ gc->set_block = pca953x_gpio_set_block;
gc->can_sleep = 1;

gc->base = chip->gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pcf857x.c
+++ linux-2.6/drivers/gpio/gpio-pcf857x.c
@@ -158,6 +158,28 @@ static void pcf857x_set(struct gpio_chip
pcf857x_output(chip, offset, value);
}

+static unsigned long pcf857x_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ int value;
+
+ value = gpio->read(gpio->client);
+ return (value < 0) ? 0 : (value & mask);
+}
+
+static void pcf857x_set_block(struct gpio_chip *chip, unsigned long mask,
+ unsigned long values)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+
+ mutex_lock(&gpio->lock);
+ gpio->out &= ~mask;
+ gpio->out |= values & mask;
+ gpio->write(gpio->client, gpio->out);
+ mutex_unlock(&gpio->lock);
+}
+
/*-------------------------------------------------------------------------*/

static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -280,6 +302,8 @@ static int pcf857x_probe(struct i2c_clie
gpio->chip.owner = THIS_MODULE;
gpio->chip.get = pcf857x_get;
gpio->chip.set = pcf857x_set;
+ gpio->chip.get_block = pcf857x_get_block;
+ gpio->chip.set_block = pcf857x_set_block;
gpio->chip.direction_input = pcf857x_input;
gpio->chip.direction_output = pcf857x_output;
gpio->chip.ngpio = id->driver_data;
--- linux-2.6.orig/drivers/gpio/gpio-pch.c
+++ linux-2.6/drivers/gpio/gpio-pch.c
@@ -122,6 +122,23 @@ static void pch_gpio_set(struct gpio_chi
spin_unlock_irqrestore(&chip->spinlock, flags);
}

+static void pch_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+ unsigned long values)
+{
+ u32 reg_val;
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ reg_val = ioread32(&chip->reg->po);
+
+ reg_val &= ~mask;
+ reg_val |= values & mask;
+
+ iowrite32(reg_val, &chip->reg->po);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
{
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
@@ -129,6 +146,14 @@ static int pch_gpio_get(struct gpio_chip
return ioread32(&chip->reg->pi) & (1 << nr);
}

+static unsigned long pch_gpio_get_block(struct gpio_chip *gpio,
+ unsigned long mask)
+{
+ struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
+
+ return ioread32(&chip->reg->pi) & mask;
+}
+
static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
int val)
{
@@ -218,8 +243,10 @@ static void pch_gpio_setup(struct pch_gp
gpio->owner = THIS_MODULE;
gpio->direction_input = pch_gpio_direction_input;
gpio->get = pch_gpio_get;
+ gpio->get_block = pch_gpio_get_block;
gpio->direction_output = pch_gpio_direction_output;
gpio->set = pch_gpio_set;
+ gpio->set_block = pch_gpio_set_block;
gpio->dbg_show = NULL;
gpio->base = -1;
gpio->ngpio = gpio_pins[chip->ioh];
--- linux-2.6.orig/drivers/gpio/gpio-pl061.c
+++ linux-2.6/drivers/gpio/gpio-pl061.c
@@ -123,6 +123,21 @@ static void pl061_set_value(struct gpio_
writeb(!!value << offset, chip->base + (1 << (offset + 2)));
}

+static unsigned long pl061_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+ return !!readb(chip->base + (mask << 2));
+}
+
+static void pl061_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+ writeb(values & mask, chip->base + (mask << 2));
+}
+
static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -256,6 +271,8 @@ static int pl061_probe(struct amba_devic
chip->gc.direction_output = pl061_direction_output;
chip->gc.get = pl061_get_value;
chip->gc.set = pl061_set_value;
+ chip->gc.get_block = pl061_get_block;
+ chip->gc.set_block = pl061_set_block;
chip->gc.to_irq = pl061_to_irq;
chip->gc.ngpio = PL061_GPIO_NR;
chip->gc.label = dev_name(&dev->dev);
--- linux-2.6.orig/drivers/gpio/gpio-twl6040.c
+++ linux-2.6/drivers/gpio/gpio-twl6040.c
@@ -46,6 +46,19 @@ static int twl6040gpo_get(struct gpio_ch
return (ret >> offset) & 1;
}

+static unsigned long twl6040gpo_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+ int ret = 0;
+
+ ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+ if (ret < 0)
+ return 0;
+
+ return ret & mask;
+}
+
static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
int value)
{
@@ -71,12 +84,31 @@ static void twl6040gpo_set(struct gpio_c
twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
}

+static void twl6040gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+ unsigned long values)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+ int ret;
+ u8 gpoctl;
+
+ ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+ if (ret < 0)
+ return;
+
+ gpoctl = ret & ~mask;
+ gpoctl |= values & mask;
+
+ twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
static struct gpio_chip twl6040gpo_chip = {
.label = "twl6040",
.owner = THIS_MODULE,
.get = twl6040gpo_get,
+ .get_block = twl6040gpo_get_block,
.direction_output = twl6040gpo_direction_out,
.set = twl6040gpo_set,
+ .set_block = twl6040gpo_set_block,
.can_sleep = 1,
};

--- linux-2.6.orig/drivers/gpio/gpio-ucb1400.c
+++ linux-2.6/drivers/gpio/gpio-ucb1400.c
@@ -45,6 +45,27 @@ static void ucb1400_gpio_set(struct gpio
ucb1400_gpio_set_value(gpio->ac97, off, val);
}

+static unsigned long ucb1400_gpio_get_block(struct gpio_chip *gc,
+ unsigned long mask)
+{
+ struct ucb1400_gpio *gpio;
+ gpio = container_of(gc, struct ucb1400_gpio, gc);
+ return ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & mask;
+}
+
+static void ucb1400_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ struct ucb1400_gpio *gpio;
+ u16 tmp;
+ gpio = container_of(gc, struct ucb1400_gpio, gc);
+
+ tmp = ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & ~mask;
+ tmp |= values & mask;
+
+ ucb1400_reg_write(gpio->ac97, UCB_IO_DATA, tmp);
+}
+
static int ucb1400_gpio_probe(struct platform_device *dev)
{
struct ucb1400_gpio *ucb = dev->dev.platform_data;
@@ -66,6 +87,8 @@ static int ucb1400_gpio_probe(struct pla
ucb->gc.direction_output = ucb1400_gpio_dir_out;
ucb->gc.get = ucb1400_gpio_get;
ucb->gc.set = ucb1400_gpio_set;
+ ucb->gc.get_block = ucb1400_gpio_get_block;
+ ucb->gc.set_block = ucb1400_gpio_set_block;
ucb->gc.can_sleep = 1;

err = gpiochip_add(&ucb->gc);
--- linux-2.6.orig/drivers/gpio/gpio-vt8500.c
+++ linux-2.6/drivers/gpio/gpio-vt8500.c
@@ -209,6 +209,28 @@ static void vt8500_gpio_set_value(struct
writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
}

+static unsigned long vt8500_gpio_get_block(struct gpio_chip *chip,
+ unsigned long mask)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ return readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) &
+ mask;
+}
+
+static void vt8500_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+ unsigned long values)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ val &= ~mask;
+ val |= values & mask;
+
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
static int vt8500_of_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags)
{
@@ -251,6 +273,8 @@ static int vt8500_add_chips(struct platf
chip->direction_output = vt8500_gpio_direction_output;
chip->get = vt8500_gpio_get_value;
chip->set = vt8500_gpio_set_value;
+ chip->get_block = vt8500_gpio_get_block;
+ chip->set_block = vt8500_gpio_set_block;
chip->can_sleep = 0;
chip->base = pin_cnt;
chip->ngpio = data->banks[i].ngpio;
--- linux-2.6.orig/drivers/gpio/gpio-xilinx.c
+++ linux-2.6/drivers/gpio/gpio-xilinx.c
@@ -49,6 +49,21 @@ static int xgpio_get(struct gpio_chip *g
}

/**
+ * xgpio_get_block - Read a block of specified signals of the GPIO device.
+ * @gc: Pointer to gpio_chip device structure.
+ * @mask: Bit mask (bit 0 == 0th GPIO) for GPIOs to get
+ *
+ * This function reads a block of specified signal of the GPIO device, returned
+ * as a bit mask, each bit representing a GPIO
+ */
+static unsigned long xgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+ return in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) & mask;
+}
+
+/**
* xgpio_set - Write the specified signal of the GPIO device.
* @gc: Pointer to gpio_chip device structure.
* @gpio: GPIO signal number.
@@ -77,6 +92,33 @@ static void xgpio_set(struct gpio_chip *
}

/**
+ * xgpio_set_block - Write a block of specified signals of the GPIO device.
+ * @gc: Pointer to gpio_chip device structure.
+ * @mask: Bit mask (bit 0 == 0th GPIO) for GPIOs to set
+ * @values: Bit mapped values
+ *
+ * This function writes the specified values in to the specified signals of the
+ * GPIO device.
+ */
+static void xgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+ unsigned long values)
+{
+ unsigned long flags;
+ struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+ struct xgpio_instance *chip =
+ container_of(mm_gc, struct xgpio_instance, mmchip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ chip->gpio_state &= ~mask;
+ chip->gpio_state |= mask & values;
+
+ out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
* xgpio_dir_in - Set the direction of the specified GPIO signal as input.
* @gc: Pointer to gpio_chip device structure.
* @gpio: GPIO signal number.
@@ -195,6 +237,8 @@ static int __devinit xgpio_of_probe(stru
chip->mmchip.gc.direction_output = xgpio_dir_out;
chip->mmchip.gc.get = xgpio_get;
chip->mmchip.gc.set = xgpio_set;
+ chip->mmchip.gc.get_block = xgpio_get_block;
+ chip->mmchip.gc.set_block = xgpio_set_block;

chip->mmchip.save_regs = xgpio_save_regs;

2012-10-28 20:47:39

by Roland Stigge

[permalink] [raw]
Subject: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

The recurring task of providing simultaneous access to GPIO lines (especially
for bit banging protocols) needs an appropriate API.

This patch adds a kernel internal "Block GPIO" API that enables simultaneous
access to several GPIOs. This is done by abstracting GPIOs to an n-bit word:
Once requested, it provides access to a group of GPIOs which can range over
multiple GPIO chips.

Signed-off-by: Roland Stigge <[email protected]>
---

Documentation/gpio.txt | 47 +++++++++
drivers/gpio/gpiolib.c | 217 +++++++++++++++++++++++++++++++++++++++++++++
include/asm-generic/gpio.h | 15 +++
include/linux/gpio.h | 74 +++++++++++++++
4 files changed, 353 insertions(+)

--- linux-2.6.orig/Documentation/gpio.txt
+++ linux-2.6/Documentation/gpio.txt
@@ -439,6 +439,53 @@ slower clock delays the rising edge of S
signaling rate accordingly.


+Block GPIO
+----------
+
+The above described interface concentrates on handling single GPIOs. However,
+in applications where it is critical to set several GPIOs at once, this
+interface doesn't work well, e.g. bit-banging protocols via grouped GPIO lines.
+Consider a GPIO controller that is connected via a slow I2C line. When
+switching two or more GPIOs one after another, there can be considerable time
+between those events. This is solved by an interface called Block GPIO:
+
+struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size);
+
+This creates a new block of GPIOs as a list of GPIO numbers with the specified
+size which are accessible via the returned struct gpio_block and the accessor
+functions described below. Please note that you need to request the GPIOs
+separately via gpio_request(). An arbitrary list of globally valid GPIOs can be
+specified, even ranging over several gpio_chips. Actual handling of I/O
+operations will be done on a best effort base, i.e. simultaneous I/O only where
+possible by hardware and implemented in the respective GPIO driver. The number
+of GPIOs in one block is limited to the number of bits in an unsigned long, or
+BITS_PER_LONG, of the respective platform, i.e. typically at least 32 on a 32
+bit system, and at least 64 on a 64 bit system. However, several blocks can be
+defined at once.
+
+unsigned gpio_block_get(struct gpio_block *block);
+void gpio_block_set(struct gpio_block *block, unsigned value);
+
+With those accessor functions, setting and getting the GPIO values is possible,
+analogous to gpio_get_value() and gpio_set_value(). Each bit in the return
+value of gpio_block_get() and in the value argument of gpio_block_set()
+corresponds to a bit specified on gpio_block_create(). Block operations in
+hardware are only possible where the respective GPIO driver implements it,
+falling back to using single GPIO operations where the driver only implements
+single GPIO access.
+
+void gpio_block_free(struct gpio_block *block);
+
+After the GPIO block isn't used anymore, it should be free'd via
+gpio_block_free().
+
+int gpio_block_register(struct gpio_block *block);
+void gpio_block_unregister(struct gpio_block *block);
+
+These functions can be used to register a GPIO block. Blocks registered this
+way will be available via sysfs.
+
+
What do these conventions omit?
===============================
One of the biggest things these conventions omit is pin multiplexing, since
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -83,6 +83,8 @@ static inline void desc_set_label(struct
#endif
}

+static LIST_HEAD(gpio_block_list);
+
/* Warn when drivers omit gpio_request() calls -- legal but ill-advised
* when setting direction, and otherwise illegal. Until board setup code
* and drivers use explicit requests everywhere (which won't happen when
@@ -1676,6 +1678,221 @@ void __gpio_set_value(unsigned gpio, int
}
EXPORT_SYMBOL_GPL(__gpio_set_value);

+static int gpio_block_chip_index(struct gpio_block *block, struct gpio_chip *gc)
+{
+ int i;
+
+ for (i = 0; i < block->nchip; i++) {
+ if (block->gbc[i].gc == gc)
+ return i;
+ }
+ return -1;
+}
+
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+ const char *name)
+{
+ struct gpio_block *block;
+ struct gpio_block_chip *gbc;
+ struct gpio_remap *remap;
+ void *tmp;
+ int i;
+
+ if (size < 1 || size > sizeof(unsigned long) * 8)
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < size; i++)
+ if (!gpio_is_valid(gpios[i]))
+ return ERR_PTR(-EINVAL);
+
+ block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
+ if (!block)
+ return ERR_PTR(-ENOMEM);
+
+ block->name = name;
+ block->ngpio = size;
+ block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
+ if (!block->gpio)
+ goto err1;
+
+ memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
+
+ for (i = 0; i < size; i++) {
+ struct gpio_chip *gc = gpio_to_chip(gpios[i]);
+ int bit = gpios[i] - gc->base;
+ int index = gpio_block_chip_index(block, gc);
+
+ if (index < 0) {
+ block->nchip++;
+ tmp = krealloc(block->gbc,
+ sizeof(struct gpio_block_chip) *
+ block->nchip, GFP_KERNEL);
+ if (!tmp) {
+ kfree(block->gbc);
+ goto err2;
+ }
+ block->gbc = tmp;
+ gbc = &block->gbc[block->nchip - 1];
+ gbc->gc = gc;
+ gbc->remap = NULL;
+ gbc->nremap = 0;
+ gbc->mask = 0;
+ } else {
+ gbc = &block->gbc[index];
+ }
+ /* represents the mask necessary on calls to the driver's
+ * .get_block() and .set_block()
+ */
+ gbc->mask |= BIT(bit);
+
+ /* collect gpios that are specified together, represented by
+ * neighboring bits
+ *
+ * Note that even though in setting remap is given a negative
+ * index, the next lines guard that the potential out-of-bounds
+ * pointer is not dereferenced when out of bounds.
+ */
+ remap = &gbc->remap[gbc->nremap - 1];
+ if (!gbc->nremap || (bit - i != remap->offset)) {
+ gbc->nremap++;
+ tmp = krealloc(gbc->remap,
+ sizeof(struct gpio_remap) *
+ gbc->nremap, GFP_KERNEL);
+ if (!tmp) {
+ kfree(gbc->remap);
+ goto err3;
+ }
+ gbc->remap = tmp;
+ remap = &gbc->remap[gbc->nremap - 1];
+ remap->offset = bit - i;
+ remap->mask = 0;
+ }
+
+ /* represents the mask necessary for bit reordering between
+ * gpio_block (i.e. as specified on gpio_block_get() and
+ * gpio_block_set()) and gpio_chip domain (i.e. as specified on
+ * the driver's .set_block() and .get_block())
+ */
+ remap->mask |= BIT(i);
+ }
+
+ return block;
+err3:
+ for (i = 0; i < block->nchip - 1; i++)
+ kfree(block->gbc[i].remap);
+ kfree(block->gbc);
+err2:
+ kfree(block->gpio);
+err1:
+ kfree(block);
+ return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(gpio_block_create);
+
+void gpio_block_free(struct gpio_block *block)
+{
+ int i;
+
+ for (i = 0; i < block->nchip; i++)
+ kfree(block->gbc[i].remap);
+ kfree(block->gpio);
+ kfree(block->gbc);
+ kfree(block);
+}
+EXPORT_SYMBOL_GPL(gpio_block_free);
+
+unsigned long gpio_block_get(const struct gpio_block *block)
+{
+ struct gpio_block_chip *gbc;
+ int i, j;
+ unsigned long values = 0;
+
+ for (i = 0; i < block->nchip; i++) {
+ unsigned long remapped = 0;
+
+ gbc = &block->gbc[i];
+
+ if (gbc->gc->get_block) {
+ remapped = gbc->gc->get_block(gbc->gc, gbc->mask);
+ } else {
+ /* emulate */
+ for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
+ remapped |= gbc->gc->get(gbc->gc,
+ gbc->gc->base + j) << j;
+ }
+
+ for (j = 0; j < gbc->nremap; j++) {
+ struct gpio_remap *gr = &gbc->remap[j];
+
+ values |= (remapped >> gr->offset) & gr->mask;
+ }
+ }
+
+ return values;
+}
+EXPORT_SYMBOL_GPL(gpio_block_get);
+
+void gpio_block_set(struct gpio_block *block, unsigned long values)
+{
+ struct gpio_block_chip *gbc;
+ int i, j;
+
+ for (i = 0; i < block->nchip; i++) {
+ unsigned long remapped = 0;
+
+ gbc = &block->gbc[i];
+
+ for (j = 0; j < gbc->nremap; j++) {
+ struct gpio_remap *gr = &gbc->remap[j];
+
+ remapped |= (values & gr->mask) << gr->offset;
+ }
+ if (gbc->gc->set_block) {
+ gbc->gc->set_block(gbc->gc, gbc->mask, remapped);
+ } else {
+ /* emulate */
+ for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
+ gbc->gc->set(gbc->gc, gbc->gc->base + j,
+ (remapped >> j) & 1);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(gpio_block_set);
+
+struct gpio_block *gpio_block_find_by_name(const char *name)
+{
+ struct gpio_block *i;
+
+ list_for_each_entry(i, &gpio_block_list, list)
+ if (!strcmp(i->name, name))
+ return i;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
+
+int gpio_block_register(struct gpio_block *block)
+{
+ if (gpio_block_find_by_name(block->name))
+ return -EBUSY;
+
+ list_add(&block->list, &gpio_block_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_block_register);
+
+void gpio_block_unregister(struct gpio_block *block)
+{
+ struct gpio_block *i;
+
+ list_for_each_entry(i, &gpio_block_list, list)
+ if (i == block) {
+ list_del(&i->list);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(gpio_block_unregister);
+
/**
* __gpio_cansleep() - report whether gpio value access will sleep
* @gpio: gpio in question
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -43,6 +43,7 @@ static inline bool gpio_is_valid(int num

struct device;
struct gpio;
+struct gpio_block;
struct seq_file;
struct module;
struct device_node;
@@ -105,6 +106,8 @@ struct gpio_chip {
unsigned offset);
int (*get)(struct gpio_chip *chip,
unsigned offset);
+ unsigned long (*get_block)(struct gpio_chip *chip,
+ unsigned long mask);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
int (*set_debounce)(struct gpio_chip *chip,
@@ -112,6 +115,9 @@ struct gpio_chip {

void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
+ void (*set_block)(struct gpio_chip *chip,
+ unsigned long mask,
+ unsigned long values);

int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
@@ -171,6 +177,15 @@ extern void gpio_set_value_cansleep(unsi
extern int __gpio_get_value(unsigned gpio);
extern void __gpio_set_value(unsigned gpio, int value);

+extern struct gpio_block *gpio_block_create(unsigned *gpio, size_t size,
+ const char *name);
+extern void gpio_block_free(struct gpio_block *block);
+extern unsigned long gpio_block_get(const struct gpio_block *block);
+extern void gpio_block_set(struct gpio_block *block, unsigned long values);
+extern struct gpio_block *gpio_block_find_by_name(const char *name);
+extern int gpio_block_register(struct gpio_block *block);
+extern void gpio_block_unregister(struct gpio_block *block);
+
extern int __gpio_cansleep(unsigned gpio);

extern int __gpio_to_irq(unsigned gpio);
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -2,6 +2,8 @@
#define __LINUX_GPIO_H

#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>

/* see Documentation/gpio.txt */

@@ -39,6 +41,43 @@ struct gpio {
const char *label;
};

+/*
+ * struct gpio_remap - a structure for describing a bit mapping
+ * @mask: a bit mask
+ * @offset: how many bits to shift to the left (negative: to the right)
+ *
+ * When we are mapping bit values from one word to another (here: from GPIO
+ * block domain to GPIO driver domain) we first mask them out with mask and
+ * shift them as specified with offset. More complicated mappings are done by
+ * grouping several of those structs and adding the results together.
+ */
+struct gpio_remap {
+ unsigned long mask;
+ int offset;
+};
+
+struct gpio_block_chip {
+ struct gpio_chip *gc;
+ struct gpio_remap *remap;
+ int nremap;
+ unsigned long mask;
+};
+
+/**
+ * struct gpio_block - a structure describing a list of GPIOs for simultaneous
+ * operations
+ */
+struct gpio_block {
+ struct gpio_block_chip *gbc;
+ size_t nchip;
+ const char *name;
+
+ int ngpio;
+ unsigned *gpio;
+
+ struct list_head list;
+};
+
#ifdef CONFIG_GENERIC_GPIO

#ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H
@@ -169,6 +208,41 @@ static inline void gpio_set_value(unsign
WARN_ON(1);
}

+static inline
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+ const char *name)
+{
+ WARN_ON(1);
+ return NULL;
+}
+
+static inline void gpio_block_free(struct gpio_block *block)
+{
+ WARN_ON(1);
+}
+
+static inline unsigned long gpio_block_get(const struct gpio_block *block)
+{
+ WARN_ON(1);
+ return 0;
+}
+
+static inline void gpio_block_set(struct gpio_block *block, unsigned long value)
+{
+ WARN_ON(1);
+}
+
+static inline int gpio_block_register(struct gpio_block *block)
+{
+ WARN_ON(1);
+ return 0;
+}
+
+static inline void gpio_block_unregister(struct gpio_block *block)
+{
+ WARN_ON(1);
+}
+
static inline int gpio_cansleep(unsigned gpio)
{
/* GPIO can never have been requested or set as {in,out}put */

2012-10-28 20:48:12

by Roland Stigge

[permalink] [raw]
Subject: [PATCH RESEND 3/5 v6] gpiolib: Fix default attributes for class

There is a race condition between creating a gpio or gpiochip device and adding
default attributes. This patch fixes this by defining the default attributes as
dev_attrs of the class. For this, it was necessary to create a separate
gpiochip_class besides gpio_class.

Signed-off-by: Roland Stigge <[email protected]>
---
Documentation/ABI/testing/sysfs-gpio | 11 ++++
drivers/gpio/gpiolib.c | 79 +++++++++++++++--------------------
2 files changed, 45 insertions(+), 45 deletions(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -20,6 +20,17 @@ Description:
/value ... always readable, writes fail for input GPIOs
/direction ... r/w as: in, out (default low); write: high, low
/edge ... r/w as: none, falling, rising, both
+
+What: /sys/class/gpiochip/
+Date: October 2012
+KernelVersion: 3.7
+Contact: Roland Stigge <[email protected]>
+Description:
+
+ Each gpiochip is represented by a separate device having the following
+ attributes:
+
+ /sys/class/gpiochip
/gpiochipN ... for each gpiochip; #N is its first GPIO
/base ... (r/o) same as N
/label ... (r/o) descriptive, not necessarily unique
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -321,9 +321,6 @@ static ssize_t gpio_value_store(struct d
return status;
}

-static const DEVICE_ATTR(value, 0644,
- gpio_value_show, gpio_value_store);
-
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
struct sysfs_dirent *value_sd = priv;
@@ -544,19 +541,6 @@ static ssize_t gpio_active_low_store(str
return status ? : size;
}

-static const DEVICE_ATTR(active_low, 0644,
- gpio_active_low_show, gpio_active_low_store);
-
-static const struct attribute *gpio_attrs[] = {
- &dev_attr_value.attr,
- &dev_attr_active_low.attr,
- NULL,
-};
-
-static const struct attribute_group gpio_attr_group = {
- .attrs = (struct attribute **) gpio_attrs,
-};
-
/*
* /sys/class/gpio/gpiochipN/
* /base ... matching gpio_chip.base (N)
@@ -571,7 +555,6 @@ static ssize_t chip_base_show(struct dev

return sprintf(buf, "%d\n", chip->base);
}
-static DEVICE_ATTR(base, 0444, chip_base_show, NULL);

static ssize_t chip_label_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -580,7 +563,6 @@ static ssize_t chip_label_show(struct de

return sprintf(buf, "%s\n", chip->label ? : "");
}
-static DEVICE_ATTR(label, 0444, chip_label_show, NULL);

static ssize_t chip_ngpio_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -589,18 +571,6 @@ static ssize_t chip_ngpio_show(struct de

return sprintf(buf, "%u\n", chip->ngpio);
}
-static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
-
-static const struct attribute *gpiochip_attrs[] = {
- &dev_attr_base.attr,
- &dev_attr_label.attr,
- &dev_attr_ngpio.attr,
- NULL,
-};
-
-static const struct attribute_group gpiochip_attr_group = {
- .attrs = (struct attribute **) gpiochip_attrs,
-};

/*
* /sys/class/gpio/export ... write-only
@@ -677,11 +647,32 @@ static struct class_attribute gpio_class
__ATTR_NULL,
};

+static struct device_attribute gpio_attrs[] = {
+ __ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store),
+ __ATTR(value, 0644, gpio_value_show, gpio_value_store),
+ __ATTR_NULL,
+};
+
static struct class gpio_class = {
.name = "gpio",
.owner = THIS_MODULE,

- .class_attrs = gpio_class_attrs,
+ .class_attrs = gpio_class_attrs,
+ .dev_attrs = gpio_attrs,
+};
+
+static struct device_attribute gpiochip_attrs[] = {
+ __ATTR(label, 0444, chip_label_show, NULL),
+ __ATTR(base, 0444, chip_base_show, NULL),
+ __ATTR(ngpio, 0444, chip_ngpio_show, NULL),
+ __ATTR_NULL,
+};
+
+static struct class gpiochip_class = {
+ .name = "gpiochip",
+ .owner = THIS_MODULE,
+
+ .dev_attrs = gpiochip_attrs,
};


@@ -738,10 +729,7 @@ int gpio_export(unsigned gpio, bool dire
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
desc, ioname ? ioname : "gpio%u", gpio);
if (!IS_ERR(dev)) {
- status = sysfs_create_group(&dev->kobj,
- &gpio_attr_group);
-
- if (!status && direction_may_change)
+ if (direction_may_change)
status = device_create_file(dev,
&dev_attr_direction);

@@ -911,25 +899,22 @@ EXPORT_SYMBOL_GPL(gpio_unexport);

static int gpiochip_export(struct gpio_chip *chip)
{
- int status;
+ int status = 0;
struct device *dev;

/* Many systems register gpio chips for SOC support very early,
* before driver model support is available. In those cases we
* export this later, in gpiolib_sysfs_init() ... here we just
- * verify that _some_ field of gpio_class got initialized.
+ * verify that _some_ field of gpiochip_class got initialized.
*/
- if (!gpio_class.p)
+ if (!gpiochip_class.p)
return 0;

/* use chip->base for the ID; it's already known to be unique */
mutex_lock(&sysfs_lock);
- dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
- "gpiochip%d", chip->base);
- if (!IS_ERR(dev)) {
- status = sysfs_create_group(&dev->kobj,
- &gpiochip_attr_group);
- } else
+ dev = device_create(&gpiochip_class, chip->dev, MKDEV(0, 0), chip,
+ "gpiochip%d", chip->base);
+ if (IS_ERR(dev))
status = PTR_ERR(dev);
chip->exported = (status == 0);
mutex_unlock(&sysfs_lock);
@@ -957,7 +942,7 @@ static void gpiochip_unexport(struct gpi
struct device *dev;

mutex_lock(&sysfs_lock);
- dev = class_find_device(&gpio_class, NULL, chip, match_export);
+ dev = class_find_device(&gpiochip_class, NULL, chip, match_export);
if (dev) {
put_device(dev);
device_unregister(dev);
@@ -1190,6 +1175,10 @@ static int __init gpiolib_sysfs_init(voi
if (status < 0)
return status;

+ status = class_register(&gpiochip_class);
+ if (status < 0)
+ return status;
+
status = class_register(&gpio_block_class);
if (status < 0)
return status;

2012-10-31 14:06:53

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

On Sun, Oct 28, 2012 at 9:46 PM, Roland Stigge <[email protected]> wrote:

Just a quick few things I spotted:

> +struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
> + const char *name)
> +{
> + struct gpio_block *block;
> + struct gpio_block_chip *gbc;
> + struct gpio_remap *remap;
> + void *tmp;
> + int i;
> +
> + if (size < 1 || size > sizeof(unsigned long) * 8)
> + return ERR_PTR(-EINVAL);

If the most GPIOs in a block is BITS_PER_LONG, why is the
latter clause not size > BITS_PER_LONG?

Maybe there was some discussion I missed, anyway it deserves
a comment because this looks completely arbitrary.

> + for (i = 0; i < size; i++)
> + if (!gpio_is_valid(gpios[i]))
> + return ERR_PTR(-EINVAL);
> +
> + block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
> + if (!block)
> + return ERR_PTR(-ENOMEM);

If these were instead glued to a struct device you could do
devm_kzalloc() here and have it garbage collected.

This is why
https://blueprints.launchpad.net/linux-linaro/+spec/gpiochip-to-dev
needs to happen. (Sorry for hyperlinking.)

> + block->name = name;
> + block->ngpio = size;
> + block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
> + if (!block->gpio)
> + goto err1;
> +
> + memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
> +
> + for (i = 0; i < size; i++) {
> + struct gpio_chip *gc = gpio_to_chip(gpios[i]);
> + int bit = gpios[i] - gc->base;
> + int index = gpio_block_chip_index(block, gc);
> +
> + if (index < 0) {
> + block->nchip++;
> + tmp = krealloc(block->gbc,
> + sizeof(struct gpio_block_chip) *
> + block->nchip, GFP_KERNEL);

Don't do this dynamic array business. Use a linked list instead.

> + if (!tmp) {
> + kfree(block->gbc);
> + goto err2;
> + }
> + block->gbc = tmp;
> + gbc = &block->gbc[block->nchip - 1];

So this becomes a list traversal and lookup instead.

> + gbc->gc = gc;
> + gbc->remap = NULL;
> + gbc->nremap = 0;
> + gbc->mask = 0;
> + } else {
> + gbc = &block->gbc[index];

So find it in the list instead.

> + }
> + /* represents the mask necessary on calls to the driver's
> + * .get_block() and .set_block()
> + */
> + gbc->mask |= BIT(bit);
> +
> + /* collect gpios that are specified together, represented by
> + * neighboring bits
> + *
> + * Note that even though in setting remap is given a negative
> + * index, the next lines guard that the potential out-of-bounds
> + * pointer is not dereferenced when out of bounds.
> + */


/*
* Maybe I'm a bit picky on comment style but I prefer
* that the first line of a multi-line comment is blank.
* Applies everywhere.
*/

> + remap = &gbc->remap[gbc->nremap - 1];
> + if (!gbc->nremap || (bit - i != remap->offset)) {
> + gbc->nremap++;
> + tmp = krealloc(gbc->remap,
> + sizeof(struct gpio_remap) *
> + gbc->nremap, GFP_KERNEL);

I don't like this dynamic array either.
Basic pattern: wherever there is a krealloc, use a list.

If lists aren't efficient enough, use some other datastructure, rbtree or
whatever.

> + if (!tmp) {
> + kfree(gbc->remap);
> + goto err3;
> + }
> + gbc->remap = tmp;
> + remap = &gbc->remap[gbc->nremap - 1];
> + remap->offset = bit - i;
> + remap->mask = 0;
> + }
> +
> + /* represents the mask necessary for bit reordering between
> + * gpio_block (i.e. as specified on gpio_block_get() and
> + * gpio_block_set()) and gpio_chip domain (i.e. as specified on
> + * the driver's .set_block() and .get_block())
> + */
> + remap->mask |= BIT(i);
> + }
> +
> + return block;
> +err3:
> + for (i = 0; i < block->nchip - 1; i++)
> + kfree(block->gbc[i].remap);
> + kfree(block->gbc);
> +err2:
> + kfree(block->gpio);
> +err1:
> + kfree(block);
> + return ERR_PTR(-ENOMEM);
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_create);
> +
> +void gpio_block_free(struct gpio_block *block)
> +{
> + int i;
> +
> + for (i = 0; i < block->nchip; i++)
> + kfree(block->gbc[i].remap);
> + kfree(block->gpio);
> + kfree(block->gbc);
> + kfree(block);
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_free);

So if we only had a real struct device inside the gpiochip all
this boilerplate would go away, as devm_* take care of releasing
the resources.

> +unsigned long gpio_block_get(const struct gpio_block *block)
> +{
> + struct gpio_block_chip *gbc;
> + int i, j;
> + unsigned long values = 0;
> +
> + for (i = 0; i < block->nchip; i++) {
> + unsigned long remapped = 0;
> +
> + gbc = &block->gbc[i];
> +
> + if (gbc->gc->get_block) {
> + remapped = gbc->gc->get_block(gbc->gc, gbc->mask);
> + } else {
> + /* emulate */
> + for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
> + remapped |= gbc->gc->get(gbc->gc,
> + gbc->gc->base + j) << j;
> + }
> +
> + for (j = 0; j < gbc->nremap; j++) {
> + struct gpio_remap *gr = &gbc->remap[j];
> +
> + values |= (remapped >> gr->offset) & gr->mask;
> + }
> + }
> +
> + return values;
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_get);
> +
> +void gpio_block_set(struct gpio_block *block, unsigned long values)
> +{
> + struct gpio_block_chip *gbc;
> + int i, j;
> +
> + for (i = 0; i < block->nchip; i++) {
> + unsigned long remapped = 0;
> +
> + gbc = &block->gbc[i];
> +
> + for (j = 0; j < gbc->nremap; j++) {
> + struct gpio_remap *gr = &gbc->remap[j];
> +
> + remapped |= (values & gr->mask) << gr->offset;
> + }
> + if (gbc->gc->set_block) {
> + gbc->gc->set_block(gbc->gc, gbc->mask, remapped);
> + } else {
> + /* emulate */
> + for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
> + gbc->gc->set(gbc->gc, gbc->gc->base + j,
> + (remapped >> j) & 1);
> + }
> + }
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_set);
> +
> +struct gpio_block *gpio_block_find_by_name(const char *name)
> +{
> + struct gpio_block *i;
> +
> + list_for_each_entry(i, &gpio_block_list, list)
> + if (!strcmp(i->name, name))
> + return i;

And here is a list anyway, I'm getting confused of this partitioning between
using dynamic arrays and lists. Just use a list please. This part looks
good.

Yours,
Linus Walleij

2012-10-31 15:00:41

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

On Sun, Oct 28, 2012 at 9:46 PM, Roland Stigge <[email protected]> wrote:
> The recurring task of providing simultaneous access to GPIO lines (especially
> for bit banging protocols) needs an appropriate API.
>
> This patch adds a kernel internal "Block GPIO" API that enables simultaneous
> access to several GPIOs. This is done by abstracting GPIOs to an n-bit word:
> Once requested, it provides access to a group of GPIOs which can range over
> multiple GPIO chips.
>
> Signed-off-by: Roland Stigge <[email protected]>

Hey Roland,

Linus and I just sat down and talked about your changes. I think I
understand what you need to do, but I've got concerns about the
approach. I'm already not a big fan of the sysfs gpio interface
design*, so you can understand that I'm not keen to extend the
interface further. At the very least, I want to be really careful
about the form that the extension takes.

First off, thank you for writing good documentation. That makes it a
lot easier to understand how the series is intended to be used, and I
really appreciate it.

For the API, I don't think it is a good idea at all to try and
abstract away gpios on multiple controllers. I understand that it
makes life a lot easier for userspace to abstract those details away,
but the problem is that it hides very important information about how
the system is actually constructed that is important to actually get
things to work. For example, say you have a gpio-connected device with
the constraint that GPIOA must change either before or at the same
time as GPIOB, but never after. If those GPIOs are on separate
controllers, then the order is completely undefined, and the user has
no way to control that other than to fall back to manipulating GPIOs
one at a time again (and losing all the performance benefits). Either
controller affinity needs to be explicit in the API, or the API needs
to be constraint oriented (ie. a stream of commands and individual
commands can be coalesced if they meet the constraints**). Also, the
API requires remapping the GPIO numbers which forces the code to be a
lot more complex than it needs to be.

I would rather see new attribute(s) added to the gpiochip's directory
to allow modifying all the pins on a given controller. It's
considerably less complex, and I'm a lot happier about extending the
sysfs ABI in that way than committing to the remapping block approach.

Second, the API appears a little naive in the way it approaches
changing values. It makes the assumption that every gpio in the block
will be written at the same time, which doesn't take into account that
even within a block it is highly likely that only a subset of the
gpios need to be manipulated. A lot of GPIO controllers implement
separate 'set' and 'clear' registers for exactly this reason. The API
needs to allow users to choose a subset for manipulation. The ABI
needs to either have separate 'set' and 'clear' operations, or
operations need to have both mask and value arguments. Similarly, how
do users manipulate pin direction with this ABI?

*The big problem with the sysfs interface is that each operation is
performed on a different file descriptor. While it is convenient for
manipulating gpios from shell scripts, it doesn't provide any good
mechanism to restrict gpios to a specific process or keep track of
which gpios are "opened' by userspace. Ultimately what I think what is
really needed is a proper character device interface that can keep
track of multiple users and who can flip which bits, but that is
slightly out of scope for this discussion.
** Actually, the command stream model is a very interesting idea. It
is worth exploring. It would be a more natural ABI than the multiple
files-and-directories model currently used. It would also eliminate
the problems I have with the sysfs abi while still being usable from
shell script. :-)

g.

2012-10-31 15:05:32

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH RESEND 3/5 v6] gpiolib: Fix default attributes for class

On Sun, Oct 28, 2012 at 9:46 PM, Roland Stigge <[email protected]> wrote:
> There is a race condition between creating a gpio or gpiochip device and adding
> default attributes. This patch fixes this by defining the default attributes as
> dev_attrs of the class. For this, it was necessary to create a separate
> gpiochip_class besides gpio_class.
>
> Signed-off-by: Roland Stigge <[email protected]>

This looks right, but I need to test it....

g.

2012-10-31 15:06:10

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH RESEND 4/5 v6] gpio: Add device tree support to block GPIO API

On Sun, Oct 28, 2012 at 9:46 PM, Roland Stigge <[email protected]> wrote:
> This patch adds device tree support to the block GPIO API.
>
> Signed-off-by: Roland Stigge <[email protected]>

I'm deferring review on this binding since the API issues need to be
resolved first.

g.

>
> ---
> Documentation/devicetree/bindings/gpio/gpio-block.txt | 36 +++++++
> drivers/gpio/Makefile | 1
> drivers/gpio/gpioblock-of.c | 84 ++++++++++++++++++
> 3 files changed, 121 insertions(+)
>
> --- /dev/null
> +++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
> @@ -0,0 +1,36 @@
> +Block GPIO definition
> +=====================
> +
> +This binding specifies arbitrary blocks of gpios, combining gpios from one or
> +more GPIO controllers together, to form a word for r/w access.
> +
> +Required property:
> +- compatible: must be "linux,gpio-block"
> +
> +Required subnodes:
> +- the name will be the registered name of the block
> +- property "gpios" is a list of gpios for the respective block
> +
> +Example:
> +
> + blockgpio {
> + compatible = "linux,gpio-block";
> +
> + block0 {
> + gpios = <&gpio 3 0 0>,
> + <&gpio 3 1 0>;
> + };
> + block1 {
> + gpios = <&gpio 4 1 0>,
> + <&gpio 4 3 0>,
> + <&gpio 4 2 0>,
> + <&gpio 4 4 0>,
> + <&gpio 4 5 0>,
> + <&gpio 4 6 0>,
> + <&gpio 4 7 0>,
> + <&gpio 4 8 0>,
> + <&gpio 4 9 0>,
> + <&gpio 4 10 0>,
> + <&gpio 4 19 0>;
> + };
> + };
> --- linux-2.6.orig/drivers/gpio/Makefile
> +++ linux-2.6/drivers/gpio/Makefile
> @@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
>
> obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o
> obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
> +obj-$(CONFIG_OF_GPIO) += gpioblock-of.o
>
> # Device drivers. Generally keep list sorted alphabetically
> obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
> --- /dev/null
> +++ linux-2.6/drivers/gpio/gpioblock-of.c
> @@ -0,0 +1,84 @@
> +/*
> + * OF implementation for Block GPIO
> + *
> + * Copyright (C) 2012 Roland Stigge <[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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +
> +static int __devinit gpioblock_of_probe(struct platform_device *pdev)
> +{
> + struct device_node *block;
> + unsigned *gpios;
> + int ngpio;
> + int ret;
> + struct gpio_block *gb;
> +
> + for_each_available_child_of_node(pdev->dev.of_node, block) {
> + int i;
> +
> + ngpio = of_gpio_count(block);
> + gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
> + if (!gpios)
> + return -ENOMEM;
> + for (i = 0; i < ngpio; i++) {
> + ret = of_get_gpio(block, i);
> + if (ret < 0)
> + return ret; /* expect -EPROBE_DEFER */
> + gpios[i] = ret;
> + }
> + gb = gpio_block_create(gpios, ngpio, block->name);
> + if (IS_ERR(gb)) {
> + dev_err(&pdev->dev,
> + "Error creating GPIO block from device tree\n");
> + return PTR_ERR(gb);
> + }
> + ret = gpio_block_register(gb);
> + if (ret < 0) {
> + gpio_block_free(gb);
> + return ret;
> + }
> + kfree(gpios);
> + dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
> + block->name, ngpio);
> + }
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id gpioblock_of_match[] __devinitdata = {
> + { .compatible = "linux,gpio-block", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, gpioblock_of_match);
> +#endif
> +
> +static struct platform_driver gpioblock_of_driver = {
> + .driver = {
> + .name = "gpio-block",
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(gpioblock_of_match),
> +
> + },
> + .probe = gpioblock_of_probe,
> +};
> +
> +module_platform_driver(gpioblock_of_driver);
> +
> +MODULE_DESCRIPTION("GPIO Block driver");
> +MODULE_AUTHOR("Roland Stigge <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:gpioblock-of");



--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

2012-10-31 17:20:03

by Roland Stigge

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

Hi Grant,

thank you for your feedback!

Notes below.

On 10/31/2012 04:00 PM, Grant Likely wrote:
> Linus and I just sat down and talked about your changes. I think I
> understand what you need to do, but I've got concerns about the
> approach. I'm already not a big fan of the sysfs gpio interface
> design*, so you can understand that I'm not keen to extend the
> interface further. At the very least, I want to be really careful
> about the form that the extension takes.
>
> First off, thank you for writing good documentation. That makes it a
> lot easier to understand how the series is intended to be used, and I
> really appreciate it.
>
> For the API, I don't think it is a good idea at all to try and
> abstract away gpios on multiple controllers. I understand that it
> makes life a lot easier for userspace to abstract those details away,
> but the problem is that it hides very important information about how
> the system is actually constructed that is important to actually get
> things to work. For example, say you have a gpio-connected device with
> the constraint that GPIOA must change either before or at the same
> time as GPIOB, but never after. If those GPIOs are on separate
> controllers, then the order is completely undefined

It is correct that it's not (yet) well documented and the API is also
not very explicit about it, but the actual approach of the manipulation
order is to let drivers handle gpios "as simultaneous as possible" and
when not possible, do it in the _order of bits specified_ (either
defined at the device tree level, or when created via
block_gpio_create() directly).

I consider it a good thing to abstract things away if possible here, if
it is well documented what actually happens, which info should be
available from the definition and the possibilities of the drivers and
hardware actually used (the optional block gpio interface must be well
implemented in the respective driver, and when combining multiple gpio
controller chips, it should be clear that certain realtime timings on a
resulting virtual n-bit bus are not possible.)

> Second, the API appears a little naive in the way it approaches
> changing values. It makes the assumption that every gpio in the block
> will be written at the same time, which doesn't take into account that
> even within a block it is highly likely that only a subset of the
> gpios need to be manipulated. A lot of GPIO controllers implement
> separate 'set' and 'clear' registers for exactly this reason. The API
> needs to allow users to choose a subset for manipulation. The ABI
> needs to either have separate 'set' and 'clear' operations, or
> operations need to have both mask and value arguments. Similarly, how
> do users manipulate pin direction with this ABI?

I'm not sure how far you tested the API in depth: You can already define
a block that maps onto a subset of gpios on a controller and internally
of course maps onto those set and clear operations. Whenever you need to
manipulate a different subset (whether disjoint or overlapping), you can
easily define _additional_ blocks. From my experience, this solves most
of the real world problems when n-bit busses are bit banged over GPIOs.
Doesn't this already solve this (in a different way, though)?

Pin direction currently needs to be set up separately, analogous to
requesting gpios. Need to document this better, right. The assumption is
that I/O needs to be efficient primarily, before bloating the API with
direction functions. Or should I add functions for this?

As long as there is no consensus about mainlining this API, I will
maintain it further at

git://git.antcom.de/linux-2.6 blockgpio

because I need it in projects anyway. Will post updates that go in the
direction that you proposed.

Thanks!

Roland

2012-10-31 17:47:21

by Roland Stigge

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

Hi Linus,

thanks for your notes, comments below:

On 10/31/2012 03:06 PM, Linus Walleij wrote:
>> +struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
>> + const char *name)
>> +{
>> + struct gpio_block *block;
>> + struct gpio_block_chip *gbc;
>> + struct gpio_remap *remap;
>> + void *tmp;
>> + int i;
>> +
>> + if (size < 1 || size > sizeof(unsigned long) * 8)
>> + return ERR_PTR(-EINVAL);
>
> If the most GPIOs in a block is BITS_PER_LONG, why is the
> latter clause not size > BITS_PER_LONG?

Good catch, thanks! :-)

>> + for (i = 0; i < size; i++)
>> + if (!gpio_is_valid(gpios[i]))
>> + return ERR_PTR(-EINVAL);
>> +
>> + block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
>> + if (!block)
>> + return ERR_PTR(-ENOMEM);
>
> If these were instead glued to a struct device you could do
> devm_kzalloc() here and have it garbage collected.

Good, will do.

> This is why
> https://blueprints.launchpad.net/linux-linaro/+spec/gpiochip-to-dev
> needs to happen. (Sorry for hyperlinking.)
>
>> + block->name = name;
>> + block->ngpio = size;
>> + block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
>> + if (!block->gpio)
>> + goto err1;
>> +
>> + memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
>> +
>> + for (i = 0; i < size; i++) {
>> + struct gpio_chip *gc = gpio_to_chip(gpios[i]);
>> + int bit = gpios[i] - gc->base;
>> + int index = gpio_block_chip_index(block, gc);
>> +
>> + if (index < 0) {
>> + block->nchip++;
>> + tmp = krealloc(block->gbc,
>> + sizeof(struct gpio_block_chip) *
>> + block->nchip, GFP_KERNEL);
>
> Don't do this dynamic array business. Use a linked list instead.

OK, I checked the important spots where access to the two lists (gbc and
remap) happen (set and get functions), and the access is always
sequential, monotonic. So will use lists. Thanks.

> [...]
> /*
> * Maybe I'm a bit picky on comment style but I prefer
> * that the first line of a multi-line comment is blank.
> * Applies everywhere.
> */

As noted earlier in the discussion, current gpiolib.c's convention is
done first-line-not-blank. So I sticked to this (un)convention. But can
change this - you are not the first one pointing this out. :-)

>> + if (!tmp) {
>> + kfree(gbc->remap);
>> + goto err3;
>> + }
>> + gbc->remap = tmp;
>> + remap = &gbc->remap[gbc->nremap - 1];
>> + remap->offset = bit - i;
>> + remap->mask = 0;
>> + }
>> +
>> + /* represents the mask necessary for bit reordering between
>> + * gpio_block (i.e. as specified on gpio_block_get() and
>> + * gpio_block_set()) and gpio_chip domain (i.e. as specified on
>> + * the driver's .set_block() and .get_block())
>> + */
>> + remap->mask |= BIT(i);
>> + }
>> +
>> + return block;
>> +err3:
>> + for (i = 0; i < block->nchip - 1; i++)
>> + kfree(block->gbc[i].remap);
>> + kfree(block->gbc);
>> +err2:
>> + kfree(block->gpio);
>> +err1:
>> + kfree(block);
>> + return ERR_PTR(-ENOMEM);
>> +}
>> +EXPORT_SYMBOL_GPL(gpio_block_create);
>> +
>> +void gpio_block_free(struct gpio_block *block)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < block->nchip; i++)
>> + kfree(block->gbc[i].remap);
>> + kfree(block->gpio);
>> + kfree(block->gbc);
>> + kfree(block);
>> +}
>> +EXPORT_SYMBOL_GPL(gpio_block_free);
>
> So if we only had a real struct device inside the gpiochip all
> this boilerplate would go away, as devm_* take care of releasing
> the resources.

Right. I guess you mean struct gpio_block here which should have a dev?
(Having gpiochip as a dev also would be best, though.) :-)

We need a separate dev for struct gpio_block, since those can be defined
dynamically (i.e. often) during the lifespan of gpio and gpiochip, so
garbage collection would be deferred for too long.

Thanks,

Roland

2012-10-31 18:59:28

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

Hi Roland

On Wed, Oct 31, 2012 at 6:19 PM, Roland Stigge <[email protected]> wrote:
> On 10/31/2012 04:00 PM, Grant Likely wrote:
>> For the API, I don't think it is a good idea at all to try and
>> abstract away gpios on multiple controllers. I understand that it
>> makes life a lot easier for userspace to abstract those details away,
>> but the problem is that it hides very important information about how
>> the system is actually constructed that is important to actually get
>> things to work. For example, say you have a gpio-connected device with
>> the constraint that GPIOA must change either before or at the same
>> time as GPIOB, but never after. If those GPIOs are on separate
>> controllers, then the order is completely undefined
>
> It is correct that it's not (yet) well documented and the API is also
> not very explicit about it, but the actual approach of the manipulation
> order is to let drivers handle gpios "as simultaneous as possible" and
> when not possible, do it in the _order of bits specified_ (either
> defined at the device tree level, or when created via
> block_gpio_create() directly).

The documentation is actually fine. I do understand that the intent is
"as simultaneous as possible", but I accept the point that the order
of specification affects the behaviour*. However, it still remains
that the method used by the ABI abstracts at the wrong level and that
blocking arbitrary GPIO pins into a single virtual GPIO register is a
bad idea.

*note that the current code doesn't implement that intended behaviour
either since the gpios are processed in the order of the controllers,
not the order of the bits.

> I'm not sure how far you tested the API in depth: You can already define
> a block that maps onto a subset of gpios on a controller and internally
> of course maps onto those set and clear operations. Whenever you need to
> manipulate a different subset (whether disjoint or overlapping), you can
> easily define _additional_ blocks. From my experience, this solves most
> of the real world problems when n-bit busses are bit banged over GPIOs.
> Doesn't this already solve this (in a different way, though)?

Blech! Requiring a new block for each possible combination of
write-at-once bits is a horrible ABI. That just strengthens my opinion
that the abstraction isn't right yet.

> Pin direction currently needs to be set up separately, analogous to
> requesting gpios. Need to document this better, right. The assumption is
> that I/O needs to be efficient primarily, before bloating the API with
> direction functions. Or should I add functions for this?

Since this is a userspace facing ABI, once it is merged it cannot be
changed in an incompatible way. I cannot merge it until there is at
least a plan for how to handle all of the reasonable use cases. That
means it must support set/clear or mask operations. Also, if it sticks
with the design of grouping pins from multiple controllers, then it
needs to handle explicitly constraining what order operations are
performed in at the time of the operation. At the time of setup
doesn't work since constraints between pins may not always be in the
same order.

I really think you should consider implementing a command stream type
of interface.

g.

2012-10-31 19:30:37

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

On Wed, Oct 31, 2012 at 04:00:17PM +0100, Grant Likely wrote:

> For the API, I don't think it is a good idea at all to try and
> abstract away gpios on multiple controllers. I understand that it
> makes life a lot easier for userspace to abstract those details away,
> but the problem is that it hides very important information about how
> the system is actually constructed that is important to actually get
> things to work. For example, say you have a gpio-connected device with
> the constraint that GPIOA must change either before or at the same
> time as GPIOB, but never after. If those GPIOs are on separate
> controllers, then the order is completely undefined, and the user has
> no way to control that other than to fall back to manipulating GPIOs
> one at a time again (and losing all the performance benefits). Either
> controller affinity needs to be explicit in the API, or the API needs
> to be constraint oriented (ie. a stream of commands and individual
> commands can be coalesced if they meet the constraints**). Also, the
> API requires remapping the GPIO numbers which forces the code to be a
> lot more complex than it needs to be.

It feels like I'm missing something here but can we not simply say that
if the user cares about the ordering of the signal changes within an
update then they should be doing two separate updates? Most of the
cases I'm aware of do things as an update with a strobe or clock that
latches the values.

The big advantage of grouping things together is that it means that we
centralise the fallback code.

> I would rather see new attribute(s) added to the gpiochip's directory
> to allow modifying all the pins on a given controller. It's
> considerably less complex, and I'm a lot happier about extending the
> sysfs ABI in that way than committing to the remapping block approach.

When I've looked at this stuff I've only looked at and thought about in
kernel users. The gpiolib sysfs ABI is already fun enough :)

Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

On 19:59 Wed 31 Oct , Grant Likely wrote:
> Hi Roland
>
> On Wed, Oct 31, 2012 at 6:19 PM, Roland Stigge <[email protected]> wrote:
> > On 10/31/2012 04:00 PM, Grant Likely wrote:
> >> For the API, I don't think it is a good idea at all to try and
> >> abstract away gpios on multiple controllers. I understand that it
> >> makes life a lot easier for userspace to abstract those details away,
> >> but the problem is that it hides very important information about how
> >> the system is actually constructed that is important to actually get
> >> things to work. For example, say you have a gpio-connected device with
> >> the constraint that GPIOA must change either before or at the same
> >> time as GPIOB, but never after. If those GPIOs are on separate
> >> controllers, then the order is completely undefined
> >
> > It is correct that it's not (yet) well documented and the API is also
> > not very explicit about it, but the actual approach of the manipulation
> > order is to let drivers handle gpios "as simultaneous as possible" and
> > when not possible, do it in the _order of bits specified_ (either
> > defined at the device tree level, or when created via
> > block_gpio_create() directly).
>
> The documentation is actually fine. I do understand that the intent is
> "as simultaneous as possible", but I accept the point that the order
> of specification affects the behaviour*. However, it still remains
> that the method used by the ABI abstracts at the wrong level and that
> blocking arbitrary GPIO pins into a single virtual GPIO register is a
> bad idea.
>
> *note that the current code doesn't implement that intended behaviour
> either since the gpios are processed in the order of the controllers,
> not the order of the bits.
>
> > I'm not sure how far you tested the API in depth: You can already define
> > a block that maps onto a subset of gpios on a controller and internally
> > of course maps onto those set and clear operations. Whenever you need to
> > manipulate a different subset (whether disjoint or overlapping), you can
> > easily define _additional_ blocks. From my experience, this solves most
> > of the real world problems when n-bit busses are bit banged over GPIOs.
> > Doesn't this already solve this (in a different way, though)?
>
> Blech! Requiring a new block for each possible combination of
> write-at-once bits is a horrible ABI. That just strengthens my opinion
> that the abstraction isn't right yet.
>
> > Pin direction currently needs to be set up separately, analogous to
> > requesting gpios. Need to document this better, right. The assumption is
> > that I/O needs to be efficient primarily, before bloating the API with
> > direction functions. Or should I add functions for this?
>
> Since this is a userspace facing ABI, once it is merged it cannot be
> changed in an incompatible way. I cannot merge it until there is at
> least a plan for how to handle all of the reasonable use cases. That
> means it must support set/clear or mask operations. Also, if it sticks
> with the design of grouping pins from multiple controllers, then it
> needs to handle explicitly constraining what order operations are
> performed in at the time of the operation. At the time of setup
> doesn't work since constraints between pins may not always be in the
> same order.
>
> I really think you should consider implementing a command stream type
> of interface.
I agreed with Grant and I'm not also a fan of the sysfs ABI

as I already point out in the previous version and Linus too

Best Regards,
J.
>
> g.

2012-11-01 15:22:35

by Grant Likely

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

On Wed, Oct 31, 2012 at 8:30 PM, Mark Brown
<[email protected]> wrote:
> On Wed, Oct 31, 2012 at 04:00:17PM +0100, Grant Likely wrote:
>
>> For the API, I don't think it is a good idea at all to try and
>> abstract away gpios on multiple controllers. I understand that it
>> makes life a lot easier for userspace to abstract those details away,
>> but the problem is that it hides very important information about how
>> the system is actually constructed that is important to actually get
>> things to work. For example, say you have a gpio-connected device with
>> the constraint that GPIOA must change either before or at the same
>> time as GPIOB, but never after. If those GPIOs are on separate
>> controllers, then the order is completely undefined, and the user has
>> no way to control that other than to fall back to manipulating GPIOs
>> one at a time again (and losing all the performance benefits). Either
>> controller affinity needs to be explicit in the API, or the API needs
>> to be constraint oriented (ie. a stream of commands and individual
>> commands can be coalesced if they meet the constraints**). Also, the
>> API requires remapping the GPIO numbers which forces the code to be a
>> lot more complex than it needs to be.
>
> It feels like I'm missing something here but can we not simply say that
> if the user cares about the ordering of the signal changes within an
> update then they should be doing two separate updates? Most of the
> cases I'm aware of do things as an update with a strobe or clock that
> latches the values.
>
> The big advantage of grouping things together is that it means that we
> centralise the fallback code.

The internal ABI is less of an issue because it is a whole lot easier
to change compared to a userspace ABI (though I think we can do a lot
better before deciding to merge it). Userspace also appears to be the
intended usage, so I've focused my review on that use case.

g.

2012-11-02 09:22:28

by Roland Stigge

[permalink] [raw]
Subject: Re: [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib

On 10/31/2012 07:59 PM, Grant Likely wrote:
>> Pin direction currently needs to be set up separately, analogous to
>> requesting gpios. Need to document this better, right. The assumption is
>> that I/O needs to be efficient primarily, before bloating the API with
>> direction functions. Or should I add functions for this?
>
> Since this is a userspace facing ABI, once it is merged it cannot be
> changed in an incompatible way. I cannot merge it until there is at
> least a plan for how to handle all of the reasonable use cases. That
> means it must support set/clear or mask operations. Also, if it sticks
> with the design of grouping pins from multiple controllers, then it
> needs to handle explicitly constraining what order operations are
> performed in at the time of the operation. At the time of setup
> doesn't work since constraints between pins may not always be in the
> same order.
>
> I really think you should consider implementing a command stream type
> of interface.

Yes, understand. What do you consider a good example of a command stream
type interface? Link or hint appreciated!

There was already mentioned the idea of a device node which can be
interfaced to via ioctl() for the various operations. Can it be a
"struct miscdevice" or do you require sth. more sophisticated?

Thanks in advance,

Roland