[PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API
Grant Likely
grant.likely at secretlab.ca
Fri Feb 15 16:50:33 EST 2013
On Tue, 15 Jan 2013 12:51:52 +0100, Roland Stigge <stigge at antcom.de> wrote:
> This patch adds sysfs support to the block GPIO API.
>
> Signed-off-by: Roland Stigge <stigge at antcom.de>
Hi Roland.
I'm going to ignore this patch for the time being because I think my
comments on the first patch basically obsolete what is being done here.
If the a gpio sequence parser is used instead of a global block gpio api
is used, then a sysfs interface for blocks of GPIOs could be implemented
merely as a separate device driver that consumes a bunch of standard
GPIO references.
I would also suggest that an API that accepts a stream of commands
instead of a fixed u32 would be better for the userspace interface. When
I originally suggested doing a char device interface, I was expecting
the sysfs interface to be dropped entirely.
g.
>
> ---
> Documentation/ABI/testing/sysfs-gpio | 20 ++
> drivers/gpio/gpiolib.c | 252 ++++++++++++++++++++++++++++++++++-
> include/asm-generic/gpio.h | 11 +
> include/linux/gpio.h | 15 ++
> 4 files changed, 297 insertions(+), 1 deletion(-)
>
> --- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
> +++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
> @@ -25,3 +25,23 @@ Description:
> /label ... (r/o) descriptive, not necessarily unique
> /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
>
> +What: /sys/class/gpioblock/
> +Date: October 2012
> +KernelVersion: 3.7
> +Contact: Roland Stigge <stigge at antcom.de>
> +Description:
> +
> + Block GPIO devices are visible in sysfs as soon as they are registered
> + (e.g. via devicetree definition). For actual I/O use, their "exported"
> + boolean attribute must be set to "1". Then, the attribute "values" is
> + created and at the same time, the GPIOs in the block are requested for
> + exclusive use by sysfs.
> +
> + /sys/class/gpioblock
> + /BLOCKNAME ... for each GPIO block name
> + /ngpio ... (r/o) number of GPIOs in this group
> + /exported ... sysfs export state of this group (0, 1)
> + /value ... current value as 32 or 64 bit integer in hex
> + (only available if /exported is 1)
> + /mask ... current mask as 32 or 64 bit integer in hex
> + (only available if /exported is 1)
> --- linux-2.6.orig/drivers/gpio/gpiolib.c
> +++ linux-2.6/drivers/gpio/gpiolib.c
> @@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
> int i;
>
> for (i = 0; i < block->ngpio; i++)
> - if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
> + if ((block->cur_mask & BIT(i)) &&
> + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
> return false;
> return true;
> }
> @@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
> chip->label, status);
> }
>
> +static ssize_t gpio_block_ngpio_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct gpio_block *block = dev_get_drvdata(dev);
> +
> + return sprintf(buf, "%u\n", block->ngpio);
> +}
> +
> +static ssize_t gpio_block_value_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct gpio_block *block = dev_get_drvdata(dev);
> +
> + return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> + "0x%016lx\n", gpio_block_get(block, block->cur_mask));
> +}
> +
> +static ssize_t gpio_block_value_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t size)
> +{
> + ssize_t status;
> + struct gpio_block *block = dev_get_drvdata(dev);
> + unsigned long value;
> +
> + status = kstrtoul(buf, 0, &value);
> + if (status == 0) {
> + mutex_lock(&sysfs_lock);
> + if (gpio_block_is_output(block)) {
> + gpio_block_set(block, block->cur_mask, value);
> + status = size;
> + } else {
> + status = -EPERM;
> + }
> + mutex_unlock(&sysfs_lock);
> + }
> + return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
> + gpio_block_value_store);
> +
> +static ssize_t gpio_block_mask_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + const struct gpio_block *block = dev_get_drvdata(dev);
> +
> + return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> + "0x%016lx\n", block->cur_mask);
> +}
> +
> +static ssize_t gpio_block_mask_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t size)
> +{
> + ssize_t status;
> + struct gpio_block *block = dev_get_drvdata(dev);
> + unsigned long mask;
> +
> + status = kstrtoul(buf, 0, &mask);
> + if (status == 0) {
> + block->cur_mask = mask;
> + status = size;
> + }
> + return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
> + gpio_block_mask_store);
> +
> +static struct class gpio_block_class;
> +
> +static int gpio_block_value_is_exported(struct gpio_block *block)
> +{
> + struct device *dev;
> + struct sysfs_dirent *sd = NULL;
> +
> + mutex_lock(&sysfs_lock);
> + dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> + if (!dev)
> + goto out;
> +
> + sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
> +
> +out:
> + mutex_unlock(&sysfs_lock);
> + return !!sd;
> +}
> +
> +static ssize_t gpio_block_exported_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct gpio_block *block = dev_get_drvdata(dev);
> +
> + return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
> +}
> +
> +static int gpio_block_value_export(struct gpio_block *block)
> +{
> + struct device *dev;
> + int status;
> + int i;
> +
> + mutex_lock(&sysfs_lock);
> +
> + for (i = 0; i < block->ngpio; i++) {
> + status = gpio_request(block->gpio[i], "sysfs");
> + if (status)
> + goto out1;
> + }
> +
> + dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> + if (!dev) {
> + status = -ENODEV;
> + goto out1;
> + }
> +
> + status = device_create_file(dev, &dev_attr_block_value);
> + if (status)
> + goto out1;
> +
> + status = device_create_file(dev, &dev_attr_block_mask);
> + if (status)
> + goto out2;
> +
> + mutex_unlock(&sysfs_lock);
> + return 0;
> +
> +out2:
> + device_remove_file(dev, &dev_attr_block_value);
> +out1:
> + while (--i >= 0)
> + gpio_free(block->gpio[i]);
> +
> + mutex_unlock(&sysfs_lock);
> + return status;
> +}
> +
> +static int gpio_block_value_unexport(struct gpio_block *block)
> +{
> + struct device *dev;
> + int i;
> +
> + dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> + if (!dev)
> + return -ENODEV;
> +
> + for (i = 0; i < block->ngpio; i++)
> + gpio_free(block->gpio[i]);
> +
> + device_remove_file(dev, &dev_attr_block_value);
> + device_remove_file(dev, &dev_attr_block_mask);
> +
> + return 0;
> +}
> +
> +static ssize_t gpio_block_exported_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t size)
> +{
> + long value;
> + int status;
> + struct gpio_block *block = dev_get_drvdata(dev);
> + int exported = gpio_block_value_is_exported(block);
> +
> + status = kstrtoul(buf, 0, &value);
> + if (status < 0)
> + goto err;
> +
> + if (value != exported) {
> + if (value)
> + status = gpio_block_value_export(block);
> + else
> + status = gpio_block_value_unexport(block);
> + if (!status)
> + status = size;
> + } else {
> + status = size;
> + }
> +err:
> + return status;
> +}
> +
> +static struct device_attribute gpio_block_attrs[] = {
> + __ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
> + gpio_block_exported_store),
> + __ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
> + __ATTR_NULL,
> +};
> +
> +static struct class gpio_block_class = {
> + .name = "gpioblock",
> + .owner = THIS_MODULE,
> +
> + .dev_attrs = gpio_block_attrs,
> +};
> +
> +int gpio_block_export(struct gpio_block *block)
> +{
> + int status = 0;
> + struct device *dev;
> +
> + /* can't export until sysfs is available ... */
> + if (!gpio_block_class.p) {
> + pr_debug("%s: called too early!\n", __func__);
> + return -ENOENT;
> + }
> +
> + mutex_lock(&sysfs_lock);
> + dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
> + block->name);
> + if (IS_ERR(dev))
> + status = PTR_ERR(dev);
> + mutex_unlock(&sysfs_lock);
> +
> + return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_export);
> +
> +void gpio_block_unexport(struct gpio_block *block)
> +{
> + struct device *dev;
> +
> + mutex_lock(&sysfs_lock);
> + dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> + if (dev)
> + device_unregister(dev);
> + mutex_unlock(&sysfs_lock);
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_unexport);
> +
> static int __init gpiolib_sysfs_init(void)
> {
> int status;
> @@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
> if (status < 0)
> return status;
>
> + status = class_register(&gpio_block_class);
> + if (status < 0)
> + return status;
> +
> /* Scan and register the gpio_chips which registered very
> * early (e.g. before the class_register above was called).
> *
> @@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
>
> block->name = name;
> block->ngpio = size;
> + block->cur_mask = ~0;
> INIT_LIST_HEAD(&block->gbc_list);
> block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
> if (!block->gpio)
> @@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
>
> int gpio_block_register(struct gpio_block *block)
> {
> + int ret;
> +
> if (gpio_block_find_by_name(block->name))
> return -EBUSY;
>
> list_add(&block->list, &gpio_block_list);
>
> + ret = gpio_block_export(block);
> + if (ret)
> + goto err1;
> +
> return 0;
> +err1:
> + list_del(&block->list);
> + return ret;
> }
> EXPORT_SYMBOL_GPL(gpio_block_register);
>
> @@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
> list_for_each_entry(i, &gpio_block_list, list)
> if (i == block) {
> list_del(&i->list);
> + gpio_block_unexport(block);
> break;
> }
> }
> --- linux-2.6.orig/include/asm-generic/gpio.h
> +++ linux-2.6/include/asm-generic/gpio.h
> @@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
> unsigned gpio);
> extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
> extern void gpio_unexport(unsigned gpio);
> +extern int gpio_block_export(struct gpio_block *block);
> +extern void gpio_block_unexport(struct gpio_block *block);
>
> #endif /* CONFIG_GPIO_SYSFS */
>
> @@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
> static inline void gpio_unexport(unsigned gpio)
> {
> }
> +
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +}
> #endif /* CONFIG_GPIO_SYSFS */
>
> #ifdef CONFIG_PINCTRL
> --- linux-2.6.orig/include/linux/gpio.h
> +++ linux-2.6/include/linux/gpio.h
> @@ -88,6 +88,7 @@ struct gpio_block_chip {
> * @ngpio: number of gpios in this block
> * @gpio: list of gpios in this block
> * @list: global list of blocks, maintained by gpiolib
> + * @cur_mask: currently used gpio mask used by userspace API
> */
> struct gpio_block {
> struct list_head gbc_list;
> @@ -97,6 +98,7 @@ struct gpio_block {
> unsigned *gpio;
>
> struct list_head list;
> + unsigned long cur_mask;
> };
>
> #ifdef CONFIG_GENERIC_GPIO
> @@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
> WARN_ON(1);
> }
>
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> + /* GPIO block can never have been requested or set as {in,out}put */
> + WARN_ON(1);
> + return -EINVAL;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> + /* GPIO block can never have been exported */
> + WARN_ON(1);
> +}
> +
> static inline int gpio_to_irq(unsigned gpio)
> {
> /* GPIO can never have been requested or set as input */
--
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies, Ltd.
More information about the linux-arm-kernel
mailing list