From: stigge@antcom.de (Roland Stigge)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/6 v14] gpio: Add sysfs support to block GPIO API
Date: Tue, 22 Jan 2013 13:06:40 +0100 [thread overview]
Message-ID: <1358856404-8975-3-git-send-email-stigge@antcom.de> (raw)
In-Reply-To: <1358856404-8975-1-git-send-email-stigge@antcom.de>
This patch adds sysfs support to the block GPIO API.
Signed-off-by: Roland Stigge <stigge@antcom.de>
---
Documentation/ABI/testing/sysfs-gpio | 20 ++
drivers/gpio/gpiolib.c | 252 ++++++++++++++++++++++++++++++++++-
include/asm-generic/gpio.h | 11 +
include/linux/gpio.h | 15 ++
4 files changed, 297 insertions(+), 1 deletion(-)
--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,23 @@ Description:
/label ... (r/o) descriptive, not necessarily unique
/ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
+What: /sys/class/gpioblock/
+Date: October 2012
+KernelVersion: 3.7
+Contact: Roland Stigge <stigge@antcom.de>
+Description:
+
+ Block GPIO devices are visible in sysfs as soon as they are registered
+ (e.g. via devicetree definition). For actual I/O use, their "exported"
+ boolean attribute must be set to "1". Then, the attribute "values" is
+ created and at the same time, the GPIOs in the block are requested for
+ exclusive use by sysfs.
+
+ /sys/class/gpioblock
+ /BLOCKNAME ... for each GPIO block name
+ /ngpio ... (r/o) number of GPIOs in this group
+ /exported ... sysfs export state of this group (0, 1)
+ /value ... current value as 32 or 64 bit integer in hex
+ (only available if /exported is 1)
+ /mask ... current mask as 32 or 64 bit integer in hex
+ (only available if /exported is 1)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
int i;
for (i = 0; i < block->ngpio; i++)
- if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+ if ((block->cur_mask & BIT(i)) &&
+ !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
return false;
return true;
}
@@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
chip->label, status);
}
+static ssize_t gpio_block_ngpio_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_block *block = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", block->ngpio);
+}
+
+static ssize_t gpio_block_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_block *block = dev_get_drvdata(dev);
+
+ return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+ "0x%016lx\n", gpio_block_get(block, block->cur_mask));
+}
+
+static ssize_t gpio_block_value_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ ssize_t status;
+ struct gpio_block *block = dev_get_drvdata(dev);
+ unsigned long value;
+
+ status = kstrtoul(buf, 0, &value);
+ if (status == 0) {
+ mutex_lock(&sysfs_lock);
+ if (gpio_block_is_output(block)) {
+ gpio_block_set(block, block->cur_mask, value);
+ status = size;
+ } else {
+ status = -EPERM;
+ }
+ mutex_unlock(&sysfs_lock);
+ }
+ return status;
+}
+
+static struct device_attribute
+dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
+ gpio_block_value_store);
+
+static ssize_t gpio_block_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_block *block = dev_get_drvdata(dev);
+
+ return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+ "0x%016lx\n", block->cur_mask);
+}
+
+static ssize_t gpio_block_mask_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ ssize_t status;
+ struct gpio_block *block = dev_get_drvdata(dev);
+ unsigned long mask;
+
+ status = kstrtoul(buf, 0, &mask);
+ if (status == 0) {
+ block->cur_mask = mask;
+ status = size;
+ }
+ return status;
+}
+
+static struct device_attribute
+dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
+ gpio_block_mask_store);
+
+static struct class gpio_block_class;
+
+static int gpio_block_value_is_exported(struct gpio_block *block)
+{
+ struct device *dev;
+ struct sysfs_dirent *sd = NULL;
+
+ mutex_lock(&sysfs_lock);
+ dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+ if (!dev)
+ goto out;
+
+ sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+
+out:
+ mutex_unlock(&sysfs_lock);
+ return !!sd;
+}
+
+static ssize_t gpio_block_exported_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gpio_block *block = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
+}
+
+static int gpio_block_value_export(struct gpio_block *block)
+{
+ struct device *dev;
+ int status;
+ int i;
+
+ mutex_lock(&sysfs_lock);
+
+ for (i = 0; i < block->ngpio; i++) {
+ status = gpio_request(block->gpio[i], "sysfs");
+ if (status)
+ goto out1;
+ }
+
+ dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+ if (!dev) {
+ status = -ENODEV;
+ goto out1;
+ }
+
+ status = device_create_file(dev, &dev_attr_block_value);
+ if (status)
+ goto out1;
+
+ status = device_create_file(dev, &dev_attr_block_mask);
+ if (status)
+ goto out2;
+
+ mutex_unlock(&sysfs_lock);
+ return 0;
+
+out2:
+ device_remove_file(dev, &dev_attr_block_value);
+out1:
+ while (--i >= 0)
+ gpio_free(block->gpio[i]);
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static int gpio_block_value_unexport(struct gpio_block *block)
+{
+ struct device *dev;
+ int i;
+
+ dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+ if (!dev)
+ return -ENODEV;
+
+ for (i = 0; i < block->ngpio; i++)
+ gpio_free(block->gpio[i]);
+
+ device_remove_file(dev, &dev_attr_block_value);
+ device_remove_file(dev, &dev_attr_block_mask);
+
+ return 0;
+}
+
+static ssize_t gpio_block_exported_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ long value;
+ int status;
+ struct gpio_block *block = dev_get_drvdata(dev);
+ int exported = gpio_block_value_is_exported(block);
+
+ status = kstrtoul(buf, 0, &value);
+ if (status < 0)
+ goto err;
+
+ if (value != exported) {
+ if (value)
+ status = gpio_block_value_export(block);
+ else
+ status = gpio_block_value_unexport(block);
+ if (!status)
+ status = size;
+ } else {
+ status = size;
+ }
+err:
+ return status;
+}
+
+static struct device_attribute gpio_block_attrs[] = {
+ __ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
+ gpio_block_exported_store),
+ __ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
+ __ATTR_NULL,
+};
+
+static struct class gpio_block_class = {
+ .name = "gpioblock",
+ .owner = THIS_MODULE,
+
+ .dev_attrs = gpio_block_attrs,
+};
+
+int gpio_block_export(struct gpio_block *block)
+{
+ int status = 0;
+ struct device *dev;
+
+ /* can't export until sysfs is available ... */
+ if (!gpio_block_class.p) {
+ pr_debug("%s: called too early!\n", __func__);
+ return -ENOENT;
+ }
+
+ mutex_lock(&sysfs_lock);
+ dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
+ block->name);
+ if (IS_ERR(dev))
+ status = PTR_ERR(dev);
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_block_export);
+
+void gpio_block_unexport(struct gpio_block *block)
+{
+ struct device *dev;
+
+ mutex_lock(&sysfs_lock);
+ dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+ if (dev)
+ device_unregister(dev);
+ mutex_unlock(&sysfs_lock);
+}
+EXPORT_SYMBOL_GPL(gpio_block_unexport);
+
static int __init gpiolib_sysfs_init(void)
{
int status;
@@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
if (status < 0)
return status;
+ status = class_register(&gpio_block_class);
+ if (status < 0)
+ return status;
+
/* Scan and register the gpio_chips which registered very
* early (e.g. before the class_register above was called).
*
@@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
block->name = name;
block->ngpio = size;
+ block->cur_mask = ~0;
INIT_LIST_HEAD(&block->gbc_list);
block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
if (!block->gpio)
@@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
int gpio_block_register(struct gpio_block *block)
{
+ int ret;
+
if (gpio_block_find_by_name(block->name))
return -EBUSY;
list_add(&block->list, &gpio_block_list);
+ ret = gpio_block_export(block);
+ if (ret)
+ goto err1;
+
return 0;
+err1:
+ list_del(&block->list);
+ return ret;
}
EXPORT_SYMBOL_GPL(gpio_block_register);
@@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
list_for_each_entry(i, &gpio_block_list, list)
if (i == block) {
list_del(&i->list);
+ gpio_block_unexport(block);
break;
}
}
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
unsigned gpio);
extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
extern void gpio_unexport(unsigned gpio);
+extern int gpio_block_export(struct gpio_block *block);
+extern void gpio_block_unexport(struct gpio_block *block);
#endif /* CONFIG_GPIO_SYSFS */
@@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
static inline void gpio_unexport(unsigned gpio)
{
}
+
+static inline int gpio_block_export(struct gpio_block *block)
+{
+ return -ENOSYS;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+}
#endif /* CONFIG_GPIO_SYSFS */
#ifdef CONFIG_PINCTRL
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -88,6 +88,7 @@ struct gpio_block_chip {
* @ngpio: number of gpios in this block
* @gpio: list of gpios in this block
* @list: global list of blocks, maintained by gpiolib
+ * @cur_mask: currently used gpio mask used by userspace API
*/
struct gpio_block {
struct list_head gbc_list;
@@ -97,6 +98,7 @@ struct gpio_block {
unsigned *gpio;
struct list_head list;
+ unsigned long cur_mask;
};
#ifdef CONFIG_GENERIC_GPIO
@@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
WARN_ON(1);
}
+static inline int gpio_block_export(struct gpio_block *block)
+{
+ /* GPIO block can never have been requested or set as {in,out}put */
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+ /* GPIO block can never have been exported */
+ WARN_ON(1);
+}
+
static inline int gpio_to_irq(unsigned gpio)
{
/* GPIO can never have been requested or set as input */
next prev parent reply other threads:[~2013-01-22 12:06 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-01-22 12:06 [PATCH 0/6 v14] gpio: Add block GPIO Roland Stigge
2013-01-22 12:06 ` [PATCH 1/6 v14] gpio: Add a block GPIO API to gpiolib Roland Stigge
2013-01-27 13:19 ` Stijn Devriendt
2013-01-27 13:58 ` Roland Stigge
2013-01-28 11:30 ` Stijn Devriendt
2013-01-31 20:56 ` Linus Walleij
2013-01-22 12:06 ` Roland Stigge [this message]
2013-01-22 12:06 ` [PATCH 3/6 v14] gpio: Add userland device interface to block GPIO Roland Stigge
2013-01-23 1:03 ` Jonathan Corbet
2013-01-23 18:47 ` Roland Stigge
2013-01-24 9:13 ` Alexander Stein
2013-01-22 12:06 ` [PATCH 4/6 v14] gpiolib: Fix default attributes for class Roland Stigge
2013-01-22 12:06 ` [PATCH 5/6 v14] gpio: Add device tree support to block GPIO API Roland Stigge
2013-01-27 13:07 ` Stijn Devriendt
2013-01-27 14:29 ` Roland Stigge
2013-01-28 11:39 ` Stijn Devriendt
2013-01-28 12:29 ` Roland Stigge
2013-01-22 12:06 ` [PATCH 6/6 v14] gpio: Add block gpio to several gpio drivers Roland Stigge
2013-01-24 12:02 ` Stijn Devriendt
2013-01-24 12:17 ` Mark Brown
2013-01-24 12:19 ` Roland Stigge
2013-01-27 12:18 ` Stijn Devriendt
2013-01-27 13:48 ` Roland Stigge
2013-01-28 11:27 ` Stijn Devriendt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1358856404-8975-3-git-send-email-stigge@antcom.de \
--to=stigge@antcom.de \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).