From: Maciej Szmigiero <mhej@o2.pl>
To: linux-kernel@vger.kernel.org
Cc: linux-arch@vger.kernel.org,
Anton Vorontsov <avorontsov@ru.mvista.com>,
Greg Kroah-Hartman <gregkh@suse.de>,
Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>,
Grant Likely <grant.likely@secretlab.ca>,
Andrew Morton <akpm@linux-foundation.org>,
Arnd Bergmann <arnd@arndb.de>, Jonathan Cameron <jic23@cam.ac.uk>,
Ben Nizette <bn@niasdigital.com>
Subject: [GPIO]implement sleeping GPIO chip removal
Date: Sun, 07 Nov 2010 19:30:33 +0100 [thread overview]
Message-ID: <4CD6F049.10102@o2.pl> (raw)
[GPIO]implement sleeping GPIO chip removal
Existing GPIO chip removal code is only of "non-blocking" type: if the chip is currently
requested it just returns -EBUSY.
This is bad for devices which disappear and reappear, like those on hot pluggable buses,
because it forces the driver to call gpiochip_remove() in loop until it returns 0.
This patch implements a new function which sleeps until device is free instead of
returning -EBUSY like gpiochip_remove().
Signed-off-by: Maciej Szmigiero <mhej@o2.pl>
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 21da9c1..a41f614 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -11,7 +11,7 @@
#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
-
+#include <linux/sched.h>
/* Optional implementation infrastructure for GPIO interfaces.
*
@@ -95,6 +95,10 @@ static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset)
const struct gpio_chip *chip = desc->chip;
const int gpio = chip->base + offset;
+ /* no new requests if chip is being deregistered */
+ if ((chip->dead) && (test_bit(FLAG_REQUESTED, &desc->flags) == 0))
+ return -ENODEV;
+
if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0,
"autorequest GPIO-%d\n", gpio)) {
if (!try_module_get(chip->owner)) {
@@ -1041,6 +1045,11 @@ int gpiochip_add(struct gpio_chip *chip)
goto fail;
}
+ /* make sure is not registered as already dead */
+ chip->dead = 0;
+
+ chip->removing_task = NULL;
+
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) {
@@ -1134,6 +1143,75 @@ int gpiochip_remove(struct gpio_chip *chip)
EXPORT_SYMBOL_GPL(gpiochip_remove);
/**
+ * gpiochip_remove_sleeping() - unregister a gpio_chip sleeping when needed
+ * @chip: the chip to unregister
+ * @interruptible: should the sleep be interruptible?
+ *
+ * If any of GPIOs are still requested this function will wait for them
+ * to be freed.
+ */
+int gpiochip_remove_sleeping(struct gpio_chip *chip, int interruptible)
+{
+ unsigned id;
+ unsigned long flags;
+
+ /* prevent new requests */
+ chip->dead = 1;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ while (1) {
+ int busy = 0;
+
+ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
+ if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
+ /* printk("ID %u is still requested\n", id); */
+ busy = 1;
+ break;
+ }
+ }
+
+ if (!busy)
+ break;
+
+ set_current_state(interruptible ?
+ TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ chip->removing_task = current;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ schedule();
+
+ /* printk("GPIO remove woken up\n"); */
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (interruptible && (signal_pending(current))) {
+ /* printk("GPIO remove signal pending\n"); */
+ /* mark chip alive again */
+ chip->dead = 0;
+ chip->removing_task = NULL;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return -EINTR;
+ }
+ }
+
+ of_gpiochip_remove(chip);
+
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ gpio_desc[id].chip = NULL;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ gpiochip_unexport(chip);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_remove_sleeping);
+
+/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
* @callback: Callback function to check gpio_chip
@@ -1186,6 +1264,12 @@ int gpio_request(unsigned gpio, const char *label)
if (chip == NULL)
goto done;
+ /* chip is being deregistered, prohibit new requests */
+ if (chip->dead) {
+ status = -ENODEV;
+ goto done;
+ }
+
if (!try_module_get(chip->owner))
goto done;
@@ -1254,6 +1338,9 @@ void gpio_free(unsigned gpio)
module_put(desc->chip->owner);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
+
+ if (chip->removing_task != NULL)
+ wake_up_process(chip->removing_task);
} else
WARN_ON(extra_checks);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index ff5c660..8576732 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -119,6 +119,9 @@ struct gpio_chip {
const char *const *names;
unsigned can_sleep:1;
unsigned exported:1;
+ unsigned dead:1;
+
+ struct task_struct *removing_task;
#if defined(CONFIG_OF_GPIO)
/*
@@ -139,6 +142,7 @@ extern int __must_check gpiochip_reserve(int start, int ngpio);
/* add/remove chips */
extern int gpiochip_add(struct gpio_chip *chip);
extern int __must_check gpiochip_remove(struct gpio_chip *chip);
+extern int gpiochip_remove_sleeping(struct gpio_chip *chip, int interruptible);
extern struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *chip,
void *data));
next reply other threads:[~2010-11-07 18:30 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-11-07 18:30 Maciej Szmigiero [this message]
2010-11-10 5:09 ` [GPIO]implement sleeping GPIO chip removal Grant Likely
2010-11-10 9:49 ` Thomas Gleixner
2010-11-10 15:28 ` Maciej Szmigiero
2010-11-10 20:42 ` Thomas Gleixner
2010-11-10 21:01 ` Maciej Szmigiero
2010-11-10 21:07 ` Thomas Gleixner
2010-11-10 21:15 ` Grant Likely
2010-11-10 22:45 ` Greg KH
2010-11-10 22:47 ` Thomas Gleixner
2010-11-10 23:15 ` Paul Mundt
2010-11-10 22:14 ` Grant Likely
2010-11-12 20:46 ` Maciej Szmigiero
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=4CD6F049.10102@o2.pl \
--to=mhej@o2.pl \
--cc=akpm@linux-foundation.org \
--cc=arnd@arndb.de \
--cc=avorontsov@ru.mvista.com \
--cc=bn@niasdigital.com \
--cc=grant.likely@secretlab.ca \
--cc=gregkh@suse.de \
--cc=jic23@cam.ac.uk \
--cc=linux-arch@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=u.kleine-koenig@pengutronix.de \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.