|
@@ -3,6 +3,7 @@
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/irq.h>
|
|
|
#include <linux/spinlock.h>
|
|
|
+#include <linux/list.h>
|
|
|
#include <linux/device.h>
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/debugfs.h>
|
|
@@ -52,14 +53,13 @@ struct gpio_desc {
|
|
|
/* flag symbols are bit numbers */
|
|
|
#define FLAG_REQUESTED 0
|
|
|
#define FLAG_IS_OUT 1
|
|
|
-#define FLAG_RESERVED 2
|
|
|
-#define FLAG_EXPORT 3 /* protected by sysfs_lock */
|
|
|
-#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
|
|
|
-#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
|
|
|
-#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
|
|
|
-#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
|
|
|
-#define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */
|
|
|
-#define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */
|
|
|
+#define FLAG_EXPORT 2 /* protected by sysfs_lock */
|
|
|
+#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
|
|
|
+#define FLAG_TRIG_FALL 4 /* trigger on falling edge */
|
|
|
+#define FLAG_TRIG_RISE 5 /* trigger on rising edge */
|
|
|
+#define FLAG_ACTIVE_LOW 6 /* sysfs value has active low */
|
|
|
+#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
|
|
|
+#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
|
|
|
|
|
|
#define ID_SHIFT 16 /* add new flags before this one */
|
|
|
|
|
@@ -72,10 +72,36 @@ struct gpio_desc {
|
|
|
};
|
|
|
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
|
|
|
|
|
|
+#define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio)
|
|
|
+
|
|
|
+static LIST_HEAD(gpio_chips);
|
|
|
+
|
|
|
#ifdef CONFIG_GPIO_SYSFS
|
|
|
static DEFINE_IDR(dirent_idr);
|
|
|
#endif
|
|
|
|
|
|
+/*
|
|
|
+ * Internal gpiod_* API using descriptors instead of the integer namespace.
|
|
|
+ * Most of this should eventually go public.
|
|
|
+ */
|
|
|
+static int gpiod_request(struct gpio_desc *desc, const char *label);
|
|
|
+static void gpiod_free(struct gpio_desc *desc);
|
|
|
+static int gpiod_direction_input(struct gpio_desc *desc);
|
|
|
+static int gpiod_direction_output(struct gpio_desc *desc, int value);
|
|
|
+static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
|
|
+static int gpiod_get_value_cansleep(struct gpio_desc *desc);
|
|
|
+static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
|
|
|
+static int gpiod_get_value(struct gpio_desc *desc);
|
|
|
+static void gpiod_set_value(struct gpio_desc *desc, int value);
|
|
|
+static int gpiod_cansleep(struct gpio_desc *desc);
|
|
|
+static int gpiod_to_irq(struct gpio_desc *desc);
|
|
|
+static int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
|
|
|
+static int gpiod_export_link(struct device *dev, const char *name,
|
|
|
+ struct gpio_desc *desc);
|
|
|
+static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value);
|
|
|
+static void gpiod_unexport(struct gpio_desc *desc);
|
|
|
+
|
|
|
+
|
|
|
static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
|
{
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
@@ -83,6 +109,36 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Return the GPIO number of the passed descriptor relative to its chip
|
|
|
+ */
|
|
|
+static int gpio_chip_hwgpio(const struct gpio_desc *desc)
|
|
|
+{
|
|
|
+ return desc - &desc->chip->desc[0];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Convert a GPIO number to its descriptor
|
|
|
+ */
|
|
|
+static struct gpio_desc *gpio_to_desc(unsigned gpio)
|
|
|
+{
|
|
|
+ if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio))
|
|
|
+ return NULL;
|
|
|
+ else
|
|
|
+ return &gpio_desc[gpio];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Convert a GPIO descriptor to the integer namespace.
|
|
|
+ * This should disappear in the future but is needed since we still
|
|
|
+ * use GPIO numbers for error messages and sysfs nodes
|
|
|
+ */
|
|
|
+static int desc_to_gpio(const struct gpio_desc *desc)
|
|
|
+{
|
|
|
+ return desc->chip->base + gpio_chip_hwgpio(desc);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* 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
|
|
@@ -94,10 +150,10 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
|
* only "legal" in the sense that (old) code using it won't break yet,
|
|
|
* but instead only triggers a WARN() stack dump.
|
|
|
*/
|
|
|
-static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset)
|
|
|
+static int gpio_ensure_requested(struct gpio_desc *desc)
|
|
|
{
|
|
|
const struct gpio_chip *chip = desc->chip;
|
|
|
- const int gpio = chip->base + offset;
|
|
|
+ const int gpio = desc_to_gpio(desc);
|
|
|
|
|
|
if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0,
|
|
|
"autorequest GPIO-%d\n", gpio)) {
|
|
@@ -116,95 +172,54 @@ static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset)
|
|
|
}
|
|
|
|
|
|
/* caller holds gpio_lock *OR* gpio is marked as requested */
|
|
|
+static struct gpio_chip *gpiod_to_chip(struct gpio_desc *desc)
|
|
|
+{
|
|
|
+ return desc->chip;
|
|
|
+}
|
|
|
+
|
|
|
struct gpio_chip *gpio_to_chip(unsigned gpio)
|
|
|
{
|
|
|
- return gpio_desc[gpio].chip;
|
|
|
+ return gpiod_to_chip(gpio_to_desc(gpio));
|
|
|
}
|
|
|
|
|
|
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
|
|
|
static int gpiochip_find_base(int ngpio)
|
|
|
{
|
|
|
- int i;
|
|
|
- int spare = 0;
|
|
|
- int base = -ENOSPC;
|
|
|
-
|
|
|
- for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) {
|
|
|
- struct gpio_desc *desc = &gpio_desc[i];
|
|
|
- struct gpio_chip *chip = desc->chip;
|
|
|
+ struct gpio_chip *chip;
|
|
|
+ int base = ARCH_NR_GPIOS - ngpio;
|
|
|
|
|
|
- if (!chip && !test_bit(FLAG_RESERVED, &desc->flags)) {
|
|
|
- spare++;
|
|
|
- if (spare == ngpio) {
|
|
|
- base = i;
|
|
|
- break;
|
|
|
- }
|
|
|
- } else {
|
|
|
- spare = 0;
|
|
|
- if (chip)
|
|
|
- i -= chip->ngpio - 1;
|
|
|
- }
|
|
|
+ list_for_each_entry_reverse(chip, &gpio_chips, list) {
|
|
|
+ /* found a free space? */
|
|
|
+ if (chip->base + chip->ngpio <= base)
|
|
|
+ break;
|
|
|
+ else
|
|
|
+ /* nope, check the space right before the chip */
|
|
|
+ base = chip->base - ngpio;
|
|
|
}
|
|
|
|
|
|
- if (gpio_is_valid(base))
|
|
|
+ if (gpio_is_valid(base)) {
|
|
|
pr_debug("%s: found new base at %d\n", __func__, base);
|
|
|
- return base;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * gpiochip_reserve() - reserve range of gpios to use with platform code only
|
|
|
- * @start: starting gpio number
|
|
|
- * @ngpio: number of gpios to reserve
|
|
|
- * Context: platform init, potentially before irqs or kmalloc will work
|
|
|
- *
|
|
|
- * Returns a negative errno if any gpio within the range is already reserved
|
|
|
- * or registered, else returns zero as a success code. Use this function
|
|
|
- * to mark a range of gpios as unavailable for dynamic gpio number allocation,
|
|
|
- * for example because its driver support is not yet loaded.
|
|
|
- */
|
|
|
-int __init gpiochip_reserve(int start, int ngpio)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
- unsigned long flags;
|
|
|
- int i;
|
|
|
-
|
|
|
- if (!gpio_is_valid(start) || !gpio_is_valid(start + ngpio - 1))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- spin_lock_irqsave(&gpio_lock, flags);
|
|
|
-
|
|
|
- for (i = start; i < start + ngpio; i++) {
|
|
|
- struct gpio_desc *desc = &gpio_desc[i];
|
|
|
-
|
|
|
- if (desc->chip || test_bit(FLAG_RESERVED, &desc->flags)) {
|
|
|
- ret = -EBUSY;
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- set_bit(FLAG_RESERVED, &desc->flags);
|
|
|
+ return base;
|
|
|
+ } else {
|
|
|
+ pr_err("%s: cannot find free range\n", __func__);
|
|
|
+ return -ENOSPC;
|
|
|
}
|
|
|
-
|
|
|
- pr_debug("%s: reserved gpios from %d to %d\n",
|
|
|
- __func__, start, start + ngpio - 1);
|
|
|
-err:
|
|
|
- spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
-
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
/* caller ensures gpio is valid and requested, chip->get_direction may sleep */
|
|
|
-static int gpio_get_direction(unsigned gpio)
|
|
|
+static int gpiod_get_direction(struct gpio_desc *desc)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
- struct gpio_desc *desc = &gpio_desc[gpio];
|
|
|
+ unsigned offset;
|
|
|
int status = -EINVAL;
|
|
|
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
- gpio -= chip->base;
|
|
|
+ chip = gpiod_to_chip(desc);
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
|
|
|
if (!chip->get_direction)
|
|
|
return status;
|
|
|
|
|
|
- status = chip->get_direction(chip, gpio);
|
|
|
+ status = chip->get_direction(chip, offset);
|
|
|
if (status > 0) {
|
|
|
/* GPIOF_DIR_IN, or other positive */
|
|
|
status = 1;
|
|
@@ -248,19 +263,19 @@ static DEFINE_MUTEX(sysfs_lock);
|
|
|
static ssize_t gpio_direction_show(struct device *dev,
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
{
|
|
|
- const struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
- unsigned gpio = desc - gpio_desc;
|
|
|
+ struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
ssize_t status;
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
|
|
|
|
- if (!test_bit(FLAG_EXPORT, &desc->flags))
|
|
|
+ if (!test_bit(FLAG_EXPORT, &desc->flags)) {
|
|
|
status = -EIO;
|
|
|
- else
|
|
|
- gpio_get_direction(gpio);
|
|
|
+ } else {
|
|
|
+ gpiod_get_direction(desc);
|
|
|
status = sprintf(buf, "%s\n",
|
|
|
test_bit(FLAG_IS_OUT, &desc->flags)
|
|
|
? "out" : "in");
|
|
|
+ }
|
|
|
|
|
|
mutex_unlock(&sysfs_lock);
|
|
|
return status;
|
|
@@ -269,8 +284,7 @@ static ssize_t gpio_direction_show(struct device *dev,
|
|
|
static ssize_t gpio_direction_store(struct device *dev,
|
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
|
{
|
|
|
- const struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
- unsigned gpio = desc - gpio_desc;
|
|
|
+ struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
ssize_t status;
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
@@ -278,11 +292,11 @@ static ssize_t gpio_direction_store(struct device *dev,
|
|
|
if (!test_bit(FLAG_EXPORT, &desc->flags))
|
|
|
status = -EIO;
|
|
|
else if (sysfs_streq(buf, "high"))
|
|
|
- status = gpio_direction_output(gpio, 1);
|
|
|
+ status = gpiod_direction_output(desc, 1);
|
|
|
else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
|
|
|
- status = gpio_direction_output(gpio, 0);
|
|
|
+ status = gpiod_direction_output(desc, 0);
|
|
|
else if (sysfs_streq(buf, "in"))
|
|
|
- status = gpio_direction_input(gpio);
|
|
|
+ status = gpiod_direction_input(desc);
|
|
|
else
|
|
|
status = -EINVAL;
|
|
|
|
|
@@ -296,8 +310,7 @@ static /* const */ DEVICE_ATTR(direction, 0644,
|
|
|
static ssize_t gpio_value_show(struct device *dev,
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
{
|
|
|
- const struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
- unsigned gpio = desc - gpio_desc;
|
|
|
+ struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
ssize_t status;
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
@@ -307,7 +320,7 @@ static ssize_t gpio_value_show(struct device *dev,
|
|
|
} else {
|
|
|
int value;
|
|
|
|
|
|
- value = !!gpio_get_value_cansleep(gpio);
|
|
|
+ value = !!gpiod_get_value_cansleep(desc);
|
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
|
value = !value;
|
|
|
|
|
@@ -321,8 +334,7 @@ static ssize_t gpio_value_show(struct device *dev,
|
|
|
static ssize_t gpio_value_store(struct device *dev,
|
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
|
{
|
|
|
- const struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
- unsigned gpio = desc - gpio_desc;
|
|
|
+ struct gpio_desc *desc = dev_get_drvdata(dev);
|
|
|
ssize_t status;
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
@@ -338,7 +350,7 @@ static ssize_t gpio_value_store(struct device *dev,
|
|
|
if (status == 0) {
|
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
|
value = !value;
|
|
|
- gpio_set_value_cansleep(gpio, value != 0);
|
|
|
+ gpiod_set_value_cansleep(desc, value != 0);
|
|
|
status = size;
|
|
|
}
|
|
|
}
|
|
@@ -368,7 +380,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
|
|
|
if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
|
|
|
return 0;
|
|
|
|
|
|
- irq = gpio_to_irq(desc - gpio_desc);
|
|
|
+ irq = gpiod_to_irq(desc);
|
|
|
if (irq < 0)
|
|
|
return -EIO;
|
|
|
|
|
@@ -638,29 +650,32 @@ static ssize_t export_store(struct class *class,
|
|
|
struct class_attribute *attr,
|
|
|
const char *buf, size_t len)
|
|
|
{
|
|
|
- long gpio;
|
|
|
- int status;
|
|
|
+ long gpio;
|
|
|
+ struct gpio_desc *desc;
|
|
|
+ int status;
|
|
|
|
|
|
status = strict_strtol(buf, 0, &gpio);
|
|
|
if (status < 0)
|
|
|
goto done;
|
|
|
|
|
|
+ desc = gpio_to_desc(gpio);
|
|
|
+
|
|
|
/* No extra locking here; FLAG_SYSFS just signifies that the
|
|
|
* request and export were done by on behalf of userspace, so
|
|
|
* they may be undone on its behalf too.
|
|
|
*/
|
|
|
|
|
|
- status = gpio_request(gpio, "sysfs");
|
|
|
+ status = gpiod_request(desc, "sysfs");
|
|
|
if (status < 0) {
|
|
|
if (status == -EPROBE_DEFER)
|
|
|
status = -ENODEV;
|
|
|
goto done;
|
|
|
}
|
|
|
- status = gpio_export(gpio, true);
|
|
|
+ status = gpiod_export(desc, true);
|
|
|
if (status < 0)
|
|
|
- gpio_free(gpio);
|
|
|
+ gpiod_free(desc);
|
|
|
else
|
|
|
- set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
|
|
|
+ set_bit(FLAG_SYSFS, &desc->flags);
|
|
|
|
|
|
done:
|
|
|
if (status)
|
|
@@ -672,8 +687,9 @@ static ssize_t unexport_store(struct class *class,
|
|
|
struct class_attribute *attr,
|
|
|
const char *buf, size_t len)
|
|
|
{
|
|
|
- long gpio;
|
|
|
- int status;
|
|
|
+ long gpio;
|
|
|
+ struct gpio_desc *desc;
|
|
|
+ int status;
|
|
|
|
|
|
status = strict_strtol(buf, 0, &gpio);
|
|
|
if (status < 0)
|
|
@@ -681,17 +697,18 @@ static ssize_t unexport_store(struct class *class,
|
|
|
|
|
|
status = -EINVAL;
|
|
|
|
|
|
+ desc = gpio_to_desc(gpio);
|
|
|
/* reject bogus commands (gpio_unexport ignores them) */
|
|
|
- if (!gpio_is_valid(gpio))
|
|
|
+ if (!desc)
|
|
|
goto done;
|
|
|
|
|
|
/* No extra locking here; FLAG_SYSFS just signifies that the
|
|
|
* request and export were done by on behalf of userspace, so
|
|
|
* they may be undone on its behalf too.
|
|
|
*/
|
|
|
- if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
|
|
|
+ if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) {
|
|
|
status = 0;
|
|
|
- gpio_free(gpio);
|
|
|
+ gpiod_free(desc);
|
|
|
}
|
|
|
done:
|
|
|
if (status)
|
|
@@ -728,13 +745,13 @@ static struct class gpio_class = {
|
|
|
*
|
|
|
* Returns zero on success, else an error.
|
|
|
*/
|
|
|
-int gpio_export(unsigned gpio, bool direction_may_change)
|
|
|
+static int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- struct gpio_desc *desc;
|
|
|
int status;
|
|
|
const char *ioname = NULL;
|
|
|
struct device *dev;
|
|
|
+ int offset;
|
|
|
|
|
|
/* can't export until sysfs is available ... */
|
|
|
if (!gpio_class.p) {
|
|
@@ -742,20 +759,19 @@ int gpio_export(unsigned gpio, bool direction_may_change)
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
|
|
|
- if (!gpio_is_valid(gpio)) {
|
|
|
- pr_debug("%s: gpio %d is not valid\n", __func__, gpio);
|
|
|
+ if (!desc) {
|
|
|
+ pr_debug("%s: invalid gpio descriptor\n", __func__);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- desc = &gpio_desc[gpio];
|
|
|
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
|
|
|
test_bit(FLAG_EXPORT, &desc->flags)) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
pr_debug("%s: gpio %d unavailable (requested=%d, exported=%d)\n",
|
|
|
- __func__, gpio,
|
|
|
+ __func__, desc_to_gpio(desc),
|
|
|
test_bit(FLAG_REQUESTED, &desc->flags),
|
|
|
test_bit(FLAG_EXPORT, &desc->flags));
|
|
|
status = -EPERM;
|
|
@@ -766,11 +782,13 @@ int gpio_export(unsigned gpio, bool direction_may_change)
|
|
|
direction_may_change = false;
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
- if (desc->chip->names && desc->chip->names[gpio - desc->chip->base])
|
|
|
- ioname = desc->chip->names[gpio - desc->chip->base];
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
+ if (desc->chip->names && desc->chip->names[offset])
|
|
|
+ ioname = desc->chip->names[offset];
|
|
|
|
|
|
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
|
|
|
- desc, ioname ? ioname : "gpio%u", gpio);
|
|
|
+ desc, ioname ? ioname : "gpio%u",
|
|
|
+ desc_to_gpio(desc));
|
|
|
if (IS_ERR(dev)) {
|
|
|
status = PTR_ERR(dev);
|
|
|
goto fail_unlock;
|
|
@@ -786,7 +804,7 @@ int gpio_export(unsigned gpio, bool direction_may_change)
|
|
|
goto fail_unregister_device;
|
|
|
}
|
|
|
|
|
|
- if (gpio_to_irq(gpio) >= 0 && (direction_may_change ||
|
|
|
+ if (gpiod_to_irq(desc) >= 0 && (direction_may_change ||
|
|
|
!test_bit(FLAG_IS_OUT, &desc->flags))) {
|
|
|
status = device_create_file(dev, &dev_attr_edge);
|
|
|
if (status)
|
|
@@ -801,9 +819,15 @@ fail_unregister_device:
|
|
|
device_unregister(dev);
|
|
|
fail_unlock:
|
|
|
mutex_unlock(&sysfs_lock);
|
|
|
- pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
|
|
+ pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
|
|
|
+ status);
|
|
|
return status;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_export(unsigned gpio, bool direction_may_change)
|
|
|
+{
|
|
|
+ return gpiod_export(gpio_to_desc(gpio), direction_may_change);
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_export);
|
|
|
|
|
|
static int match_export(struct device *dev, const void *data)
|
|
@@ -822,18 +846,16 @@ static int match_export(struct device *dev, const void *data)
|
|
|
*
|
|
|
* Returns zero on success, else an error.
|
|
|
*/
|
|
|
-int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
|
|
|
+static int gpiod_export_link(struct device *dev, const char *name,
|
|
|
+ struct gpio_desc *desc)
|
|
|
{
|
|
|
- struct gpio_desc *desc;
|
|
|
int status = -EINVAL;
|
|
|
|
|
|
- if (!gpio_is_valid(gpio))
|
|
|
+ if (!desc)
|
|
|
goto done;
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
|
|
|
|
- desc = &gpio_desc[gpio];
|
|
|
-
|
|
|
if (test_bit(FLAG_EXPORT, &desc->flags)) {
|
|
|
struct device *tdev;
|
|
|
|
|
@@ -850,12 +872,17 @@ int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
|
|
|
|
|
|
done:
|
|
|
if (status)
|
|
|
- pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
|
|
+ pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
|
|
|
+ status);
|
|
|
|
|
|
return status;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(gpio_export_link);
|
|
|
|
|
|
+int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
|
|
|
+{
|
|
|
+ return gpiod_export_link(dev, name, gpio_to_desc(gpio));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(gpio_export_link);
|
|
|
|
|
|
/**
|
|
|
* gpio_sysfs_set_active_low - set the polarity of gpio sysfs value
|
|
@@ -869,19 +896,16 @@ EXPORT_SYMBOL_GPL(gpio_export_link);
|
|
|
*
|
|
|
* Returns zero on success, else an error.
|
|
|
*/
|
|
|
-int gpio_sysfs_set_active_low(unsigned gpio, int value)
|
|
|
+static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
- struct gpio_desc *desc;
|
|
|
struct device *dev = NULL;
|
|
|
int status = -EINVAL;
|
|
|
|
|
|
- if (!gpio_is_valid(gpio))
|
|
|
+ if (!desc)
|
|
|
goto done;
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
|
|
|
|
- desc = &gpio_desc[gpio];
|
|
|
-
|
|
|
if (test_bit(FLAG_EXPORT, &desc->flags)) {
|
|
|
dev = class_find_device(&gpio_class, NULL, desc, match_export);
|
|
|
if (dev == NULL) {
|
|
@@ -897,10 +921,16 @@ unlock:
|
|
|
|
|
|
done:
|
|
|
if (status)
|
|
|
- pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
|
|
+ pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
|
|
|
+ status);
|
|
|
|
|
|
return status;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_sysfs_set_active_low(unsigned gpio, int value)
|
|
|
+{
|
|
|
+ return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value);
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low);
|
|
|
|
|
|
/**
|
|
@@ -909,21 +939,18 @@ EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low);
|
|
|
*
|
|
|
* This is implicit on gpio_free().
|
|
|
*/
|
|
|
-void gpio_unexport(unsigned gpio)
|
|
|
+static void gpiod_unexport(struct gpio_desc *desc)
|
|
|
{
|
|
|
- struct gpio_desc *desc;
|
|
|
int status = 0;
|
|
|
struct device *dev = NULL;
|
|
|
|
|
|
- if (!gpio_is_valid(gpio)) {
|
|
|
+ if (!desc) {
|
|
|
status = -EINVAL;
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
|
|
|
|
- desc = &gpio_desc[gpio];
|
|
|
-
|
|
|
if (test_bit(FLAG_EXPORT, &desc->flags)) {
|
|
|
|
|
|
dev = class_find_device(&gpio_class, NULL, desc, match_export);
|
|
@@ -935,13 +962,20 @@ void gpio_unexport(unsigned gpio)
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&sysfs_lock);
|
|
|
+
|
|
|
if (dev) {
|
|
|
device_unregister(dev);
|
|
|
put_device(dev);
|
|
|
}
|
|
|
done:
|
|
|
if (status)
|
|
|
- pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
|
|
+ pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc),
|
|
|
+ status);
|
|
|
+}
|
|
|
+
|
|
|
+void gpio_unexport(unsigned gpio)
|
|
|
+{
|
|
|
+ gpiod_unexport(gpio_to_desc(gpio));
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpio_unexport);
|
|
|
|
|
@@ -975,9 +1009,9 @@ static int gpiochip_export(struct gpio_chip *chip)
|
|
|
unsigned gpio;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- gpio = chip->base;
|
|
|
- while (gpio_desc[gpio].chip == chip)
|
|
|
- gpio_desc[gpio++].chip = NULL;
|
|
|
+ gpio = 0;
|
|
|
+ while (gpio < chip->ngpio)
|
|
|
+ chip->desc[gpio++].chip = NULL;
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
pr_debug("%s: chip %s status %d\n", __func__,
|
|
@@ -1012,7 +1046,7 @@ static int __init gpiolib_sysfs_init(void)
|
|
|
{
|
|
|
int status;
|
|
|
unsigned long flags;
|
|
|
- unsigned gpio;
|
|
|
+ struct gpio_chip *chip;
|
|
|
|
|
|
status = class_register(&gpio_class);
|
|
|
if (status < 0)
|
|
@@ -1025,10 +1059,7 @@ static int __init gpiolib_sysfs_init(void)
|
|
|
* registered, and so arch_initcall() can always gpio_export().
|
|
|
*/
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
|
|
|
- struct gpio_chip *chip;
|
|
|
-
|
|
|
- chip = gpio_desc[gpio].chip;
|
|
|
+ list_for_each_entry(chip, &gpio_chips, list) {
|
|
|
if (!chip || chip->exported)
|
|
|
continue;
|
|
|
|
|
@@ -1053,8 +1084,66 @@ static inline void gpiochip_unexport(struct gpio_chip *chip)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
+static inline int gpiod_export(struct gpio_desc *desc,
|
|
|
+ bool direction_may_change)
|
|
|
+{
|
|
|
+ return -ENOSYS;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int gpiod_export_link(struct device *dev, const char *name,
|
|
|
+ struct gpio_desc *desc)
|
|
|
+{
|
|
|
+ return -ENOSYS;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
|
|
|
+{
|
|
|
+ return -ENOSYS;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void gpiod_unexport(struct gpio_desc *desc)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
#endif /* CONFIG_GPIO_SYSFS */
|
|
|
|
|
|
+/*
|
|
|
+ * Add a new chip to the global chips list, keeping the list of chips sorted
|
|
|
+ * by base order.
|
|
|
+ *
|
|
|
+ * Return -EBUSY if the new chip overlaps with some other chip's integer
|
|
|
+ * space.
|
|
|
+ */
|
|
|
+static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
+{
|
|
|
+ struct list_head *pos = &gpio_chips;
|
|
|
+ struct gpio_chip *_chip;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ /* find where to insert our chip */
|
|
|
+ list_for_each(pos, &gpio_chips) {
|
|
|
+ _chip = list_entry(pos, struct gpio_chip, list);
|
|
|
+ /* shall we insert before _chip? */
|
|
|
+ if (_chip->base >= chip->base + chip->ngpio)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* are we stepping on the chip right before? */
|
|
|
+ if (pos != &gpio_chips && pos->prev != &gpio_chips) {
|
|
|
+ _chip = list_entry(pos->prev, struct gpio_chip, list);
|
|
|
+ if (_chip->base + _chip->ngpio > chip->base) {
|
|
|
+ dev_err(chip->dev,
|
|
|
+ "GPIO integer space overlap, cannot add chip\n");
|
|
|
+ err = -EBUSY;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!err)
|
|
|
+ list_add_tail(&chip->list, pos);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* gpiochip_add() - register a gpio_chip
|
|
|
* @chip: the chip to register, with chip->base initialized
|
|
@@ -1096,16 +1185,14 @@ int gpiochip_add(struct gpio_chip *chip)
|
|
|
chip->base = base;
|
|
|
}
|
|
|
|
|
|
- /* these GPIO numbers must not be managed by another gpio_chip */
|
|
|
- for (id = base; id < base + chip->ngpio; id++) {
|
|
|
- if (gpio_desc[id].chip != NULL) {
|
|
|
- status = -EBUSY;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ status = gpiochip_add_to_list(chip);
|
|
|
+
|
|
|
if (status == 0) {
|
|
|
- for (id = base; id < base + chip->ngpio; id++) {
|
|
|
- gpio_desc[id].chip = chip;
|
|
|
+ chip->desc = &gpio_desc[chip->base];
|
|
|
+
|
|
|
+ for (id = 0; id < chip->ngpio; id++) {
|
|
|
+ struct gpio_desc *desc = &chip->desc[id];
|
|
|
+ desc->chip = chip;
|
|
|
|
|
|
/* REVISIT: most hardware initializes GPIOs as
|
|
|
* inputs (often with pullups enabled) so power
|
|
@@ -1114,7 +1201,7 @@ int gpiochip_add(struct gpio_chip *chip)
|
|
|
* and in case chip->get_direction is not set,
|
|
|
* we may expose the wrong direction in sysfs.
|
|
|
*/
|
|
|
- gpio_desc[id].flags = !chip->direction_input
|
|
|
+ desc->flags = !chip->direction_input
|
|
|
? (1 << FLAG_IS_OUT)
|
|
|
: 0;
|
|
|
}
|
|
@@ -1167,15 +1254,17 @@ int gpiochip_remove(struct gpio_chip *chip)
|
|
|
gpiochip_remove_pin_ranges(chip);
|
|
|
of_gpiochip_remove(chip);
|
|
|
|
|
|
- for (id = chip->base; id < chip->base + chip->ngpio; id++) {
|
|
|
- if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
|
|
|
+ for (id = 0; id < chip->ngpio; id++) {
|
|
|
+ if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) {
|
|
|
status = -EBUSY;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (status == 0) {
|
|
|
- for (id = chip->base; id < chip->base + chip->ngpio; id++)
|
|
|
- gpio_desc[id].chip = NULL;
|
|
|
+ for (id = 0; id < chip->ngpio; id++)
|
|
|
+ chip->desc[id].chip = NULL;
|
|
|
+
|
|
|
+ list_del(&chip->list);
|
|
|
}
|
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
@@ -1202,20 +1291,17 @@ struct gpio_chip *gpiochip_find(void *data,
|
|
|
int (*match)(struct gpio_chip *chip,
|
|
|
void *data))
|
|
|
{
|
|
|
- struct gpio_chip *chip = NULL;
|
|
|
+ struct gpio_chip *chip;
|
|
|
unsigned long flags;
|
|
|
- int i;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- for (i = 0; i < ARCH_NR_GPIOS; i++) {
|
|
|
- if (!gpio_desc[i].chip)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (match(gpio_desc[i].chip, data)) {
|
|
|
- chip = gpio_desc[i].chip;
|
|
|
+ list_for_each_entry(chip, &gpio_chips, list)
|
|
|
+ if (match(chip, data))
|
|
|
break;
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ /* No match? */
|
|
|
+ if (&chip->list == &gpio_chips)
|
|
|
+ chip = NULL;
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
return chip;
|
|
@@ -1297,20 +1383,18 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
|
|
|
* on each other, and help provide better diagnostics in debugfs.
|
|
|
* They're called even less than the "set direction" calls.
|
|
|
*/
|
|
|
-int gpio_request(unsigned gpio, const char *label)
|
|
|
+static int gpiod_request(struct gpio_desc *desc, const char *label)
|
|
|
{
|
|
|
- struct gpio_desc *desc;
|
|
|
struct gpio_chip *chip;
|
|
|
int status = -EPROBE_DEFER;
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- if (!gpio_is_valid(gpio)) {
|
|
|
+ if (!desc) {
|
|
|
status = -EINVAL;
|
|
|
goto done;
|
|
|
}
|
|
|
- desc = &gpio_desc[gpio];
|
|
|
chip = desc->chip;
|
|
|
if (chip == NULL)
|
|
|
goto done;
|
|
@@ -1334,7 +1418,7 @@ int gpio_request(unsigned gpio, const char *label)
|
|
|
if (chip->request) {
|
|
|
/* chip->request may sleep */
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- status = chip->request(chip, gpio - chip->base);
|
|
|
+ status = chip->request(chip, gpio_chip_hwgpio(desc));
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
if (status < 0) {
|
|
@@ -1347,42 +1431,46 @@ int gpio_request(unsigned gpio, const char *label)
|
|
|
if (chip->get_direction) {
|
|
|
/* chip->get_direction may sleep */
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- gpio_get_direction(gpio);
|
|
|
+ gpiod_get_direction(desc);
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
}
|
|
|
done:
|
|
|
if (status)
|
|
|
- pr_debug("gpio_request: gpio-%d (%s) status %d\n",
|
|
|
- gpio, label ? : "?", status);
|
|
|
+ pr_debug("_gpio_request: gpio-%d (%s) status %d\n",
|
|
|
+ desc ? desc_to_gpio(desc) : -1,
|
|
|
+ label ? : "?", status);
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
return status;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_request(unsigned gpio, const char *label)
|
|
|
+{
|
|
|
+ return gpiod_request(gpio_to_desc(gpio), label);
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_request);
|
|
|
|
|
|
-void gpio_free(unsigned gpio)
|
|
|
+static void gpiod_free(struct gpio_desc *desc)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- struct gpio_desc *desc;
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
|
might_sleep();
|
|
|
|
|
|
- if (!gpio_is_valid(gpio)) {
|
|
|
+ if (!desc) {
|
|
|
WARN_ON(extra_checks);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- gpio_unexport(gpio);
|
|
|
+ gpiod_unexport(desc);
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- desc = &gpio_desc[gpio];
|
|
|
chip = desc->chip;
|
|
|
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
|
|
|
if (chip->free) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
might_sleep_if(chip->can_sleep);
|
|
|
- chip->free(chip, gpio - chip->base);
|
|
|
+ chip->free(chip, gpio_chip_hwgpio(desc));
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
}
|
|
|
desc_set_label(desc, NULL);
|
|
@@ -1396,6 +1484,11 @@ void gpio_free(unsigned gpio)
|
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
}
|
|
|
+
|
|
|
+void gpio_free(unsigned gpio)
|
|
|
+{
|
|
|
+ gpiod_free(gpio_to_desc(gpio));
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_free);
|
|
|
|
|
|
/**
|
|
@@ -1406,29 +1499,32 @@ EXPORT_SYMBOL_GPL(gpio_free);
|
|
|
*/
|
|
|
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
|
|
|
{
|
|
|
+ struct gpio_desc *desc;
|
|
|
int err;
|
|
|
|
|
|
- err = gpio_request(gpio, label);
|
|
|
+ desc = gpio_to_desc(gpio);
|
|
|
+
|
|
|
+ err = gpiod_request(desc, label);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
if (flags & GPIOF_OPEN_DRAIN)
|
|
|
- set_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags);
|
|
|
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
|
|
|
|
|
if (flags & GPIOF_OPEN_SOURCE)
|
|
|
- set_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags);
|
|
|
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
|
|
|
|
|
if (flags & GPIOF_DIR_IN)
|
|
|
- err = gpio_direction_input(gpio);
|
|
|
+ err = gpiod_direction_input(desc);
|
|
|
else
|
|
|
- err = gpio_direction_output(gpio,
|
|
|
+ err = gpiod_direction_output(desc,
|
|
|
(flags & GPIOF_INIT_HIGH) ? 1 : 0);
|
|
|
|
|
|
if (err)
|
|
|
goto free_gpio;
|
|
|
|
|
|
if (flags & GPIOF_EXPORT) {
|
|
|
- err = gpio_export(gpio, flags & GPIOF_EXPORT_CHANGEABLE);
|
|
|
+ err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE);
|
|
|
if (err)
|
|
|
goto free_gpio;
|
|
|
}
|
|
@@ -1436,7 +1532,7 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
|
|
|
return 0;
|
|
|
|
|
|
free_gpio:
|
|
|
- gpio_free(gpio);
|
|
|
+ gpiod_free(desc);
|
|
|
return err;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpio_request_one);
|
|
@@ -1491,14 +1587,17 @@ EXPORT_SYMBOL_GPL(gpio_free_array);
|
|
|
*/
|
|
|
const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
|
|
|
{
|
|
|
- unsigned gpio = chip->base + offset;
|
|
|
+ struct gpio_desc *desc;
|
|
|
|
|
|
- if (!gpio_is_valid(gpio) || gpio_desc[gpio].chip != chip)
|
|
|
+ if (!GPIO_OFFSET_VALID(chip, offset))
|
|
|
return NULL;
|
|
|
- if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
|
|
|
+
|
|
|
+ desc = &chip->desc[offset];
|
|
|
+
|
|
|
+ if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
|
|
|
return NULL;
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
- return gpio_desc[gpio].label;
|
|
|
+ return desc->label;
|
|
|
#else
|
|
|
return "?";
|
|
|
#endif
|
|
@@ -1515,24 +1614,21 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
|
|
|
* rely on gpio_request() having been called beforehand.
|
|
|
*/
|
|
|
|
|
|
-int gpio_direction_input(unsigned gpio)
|
|
|
+static int gpiod_direction_input(struct gpio_desc *desc)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
struct gpio_chip *chip;
|
|
|
- struct gpio_desc *desc = &gpio_desc[gpio];
|
|
|
int status = -EINVAL;
|
|
|
+ int offset;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- if (!gpio_is_valid(gpio))
|
|
|
+ if (!desc)
|
|
|
goto fail;
|
|
|
chip = desc->chip;
|
|
|
if (!chip || !chip->get || !chip->direction_input)
|
|
|
goto fail;
|
|
|
- gpio -= chip->base;
|
|
|
- if (gpio >= chip->ngpio)
|
|
|
- goto fail;
|
|
|
- status = gpio_ensure_requested(desc, gpio);
|
|
|
+ status = gpio_ensure_requested(desc);
|
|
|
if (status < 0)
|
|
|
goto fail;
|
|
|
|
|
@@ -1542,11 +1638,12 @@ int gpio_direction_input(unsigned gpio)
|
|
|
|
|
|
might_sleep_if(chip->can_sleep);
|
|
|
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
if (status) {
|
|
|
- status = chip->request(chip, gpio);
|
|
|
+ status = chip->request(chip, offset);
|
|
|
if (status < 0) {
|
|
|
pr_debug("GPIO-%d: chip request fail, %d\n",
|
|
|
- chip->base + gpio, status);
|
|
|
+ desc_to_gpio(desc), status);
|
|
|
/* and it's not available to anyone else ...
|
|
|
* gpio_request() is the fully clean solution.
|
|
|
*/
|
|
@@ -1554,48 +1651,54 @@ int gpio_direction_input(unsigned gpio)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- status = chip->direction_input(chip, gpio);
|
|
|
+ status = chip->direction_input(chip, offset);
|
|
|
if (status == 0)
|
|
|
clear_bit(FLAG_IS_OUT, &desc->flags);
|
|
|
|
|
|
- trace_gpio_direction(chip->base + gpio, 1, status);
|
|
|
+ trace_gpio_direction(desc_to_gpio(desc), 1, status);
|
|
|
lose:
|
|
|
return status;
|
|
|
fail:
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- if (status)
|
|
|
+ if (status) {
|
|
|
+ int gpio = -1;
|
|
|
+ if (desc)
|
|
|
+ gpio = desc_to_gpio(desc);
|
|
|
pr_debug("%s: gpio-%d status %d\n",
|
|
|
__func__, gpio, status);
|
|
|
+ }
|
|
|
return status;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_direction_input(unsigned gpio)
|
|
|
+{
|
|
|
+ return gpiod_direction_input(gpio_to_desc(gpio));
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_direction_input);
|
|
|
|
|
|
-int gpio_direction_output(unsigned gpio, int value)
|
|
|
+static int gpiod_direction_output(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
struct gpio_chip *chip;
|
|
|
- struct gpio_desc *desc = &gpio_desc[gpio];
|
|
|
int status = -EINVAL;
|
|
|
+ int offset;
|
|
|
|
|
|
/* Open drain pin should not be driven to 1 */
|
|
|
if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
|
- return gpio_direction_input(gpio);
|
|
|
+ return gpiod_direction_input(desc);
|
|
|
|
|
|
/* Open source pin should not be driven to 0 */
|
|
|
if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
|
- return gpio_direction_input(gpio);
|
|
|
+ return gpiod_direction_input(desc);
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- if (!gpio_is_valid(gpio))
|
|
|
+ if (!desc)
|
|
|
goto fail;
|
|
|
chip = desc->chip;
|
|
|
if (!chip || !chip->set || !chip->direction_output)
|
|
|
goto fail;
|
|
|
- gpio -= chip->base;
|
|
|
- if (gpio >= chip->ngpio)
|
|
|
- goto fail;
|
|
|
- status = gpio_ensure_requested(desc, gpio);
|
|
|
+ status = gpio_ensure_requested(desc);
|
|
|
if (status < 0)
|
|
|
goto fail;
|
|
|
|
|
@@ -1605,11 +1708,12 @@ int gpio_direction_output(unsigned gpio, int value)
|
|
|
|
|
|
might_sleep_if(chip->can_sleep);
|
|
|
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
if (status) {
|
|
|
- status = chip->request(chip, gpio);
|
|
|
+ status = chip->request(chip, offset);
|
|
|
if (status < 0) {
|
|
|
pr_debug("GPIO-%d: chip request fail, %d\n",
|
|
|
- chip->base + gpio, status);
|
|
|
+ desc_to_gpio(desc), status);
|
|
|
/* and it's not available to anyone else ...
|
|
|
* gpio_request() is the fully clean solution.
|
|
|
*/
|
|
@@ -1617,20 +1721,29 @@ int gpio_direction_output(unsigned gpio, int value)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- status = chip->direction_output(chip, gpio, value);
|
|
|
+ status = chip->direction_output(chip, offset, value);
|
|
|
if (status == 0)
|
|
|
set_bit(FLAG_IS_OUT, &desc->flags);
|
|
|
- trace_gpio_value(chip->base + gpio, 0, value);
|
|
|
- trace_gpio_direction(chip->base + gpio, 0, status);
|
|
|
+ trace_gpio_value(desc_to_gpio(desc), 0, value);
|
|
|
+ trace_gpio_direction(desc_to_gpio(desc), 0, status);
|
|
|
lose:
|
|
|
return status;
|
|
|
fail:
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- if (status)
|
|
|
+ if (status) {
|
|
|
+ int gpio = -1;
|
|
|
+ if (desc)
|
|
|
+ gpio = desc_to_gpio(desc);
|
|
|
pr_debug("%s: gpio-%d status %d\n",
|
|
|
__func__, gpio, status);
|
|
|
+ }
|
|
|
return status;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_direction_output(unsigned gpio, int value)
|
|
|
+{
|
|
|
+ return gpiod_direction_output(gpio_to_desc(gpio), value);
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_direction_output);
|
|
|
|
|
|
/**
|
|
@@ -1638,24 +1751,22 @@ EXPORT_SYMBOL_GPL(gpio_direction_output);
|
|
|
* @gpio: the gpio to set debounce time
|
|
|
* @debounce: debounce time is microseconds
|
|
|
*/
|
|
|
-int gpio_set_debounce(unsigned gpio, unsigned debounce)
|
|
|
+static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
struct gpio_chip *chip;
|
|
|
- struct gpio_desc *desc = &gpio_desc[gpio];
|
|
|
int status = -EINVAL;
|
|
|
+ int offset;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- if (!gpio_is_valid(gpio))
|
|
|
+ if (!desc)
|
|
|
goto fail;
|
|
|
chip = desc->chip;
|
|
|
if (!chip || !chip->set || !chip->set_debounce)
|
|
|
goto fail;
|
|
|
- gpio -= chip->base;
|
|
|
- if (gpio >= chip->ngpio)
|
|
|
- goto fail;
|
|
|
- status = gpio_ensure_requested(desc, gpio);
|
|
|
+
|
|
|
+ status = gpio_ensure_requested(desc);
|
|
|
if (status < 0)
|
|
|
goto fail;
|
|
|
|
|
@@ -1665,16 +1776,26 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce)
|
|
|
|
|
|
might_sleep_if(chip->can_sleep);
|
|
|
|
|
|
- return chip->set_debounce(chip, gpio, debounce);
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
+ return chip->set_debounce(chip, offset, debounce);
|
|
|
|
|
|
fail:
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- if (status)
|
|
|
+ if (status) {
|
|
|
+ int gpio = -1;
|
|
|
+ if (desc)
|
|
|
+ gpio = desc_to_gpio(desc);
|
|
|
pr_debug("%s: gpio-%d status %d\n",
|
|
|
__func__, gpio, status);
|
|
|
+ }
|
|
|
|
|
|
return status;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_set_debounce(unsigned gpio, unsigned debounce)
|
|
|
+{
|
|
|
+ return gpiod_set_debounce(gpio_to_desc(gpio), debounce);
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_set_debounce);
|
|
|
|
|
|
/* I/O calls are only valid after configuration completed; the relevant
|
|
@@ -1708,18 +1829,25 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce);
|
|
|
* It returns the zero or nonzero value provided by the associated
|
|
|
* gpio_chip.get() method; or zero if no such method is provided.
|
|
|
*/
|
|
|
-int __gpio_get_value(unsigned gpio)
|
|
|
+static int gpiod_get_value(struct gpio_desc *desc)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
int value;
|
|
|
+ int offset;
|
|
|
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
+ chip = desc->chip;
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
/* Should be using gpio_get_value_cansleep() */
|
|
|
WARN_ON(chip->can_sleep);
|
|
|
- value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
|
|
|
- trace_gpio_value(gpio, 1, value);
|
|
|
+ value = chip->get ? chip->get(chip, offset) : 0;
|
|
|
+ trace_gpio_value(desc_to_gpio(desc), 1, value);
|
|
|
return value;
|
|
|
}
|
|
|
+
|
|
|
+int __gpio_get_value(unsigned gpio)
|
|
|
+{
|
|
|
+ return gpiod_get_value(gpio_to_desc(gpio));
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(__gpio_get_value);
|
|
|
|
|
|
/*
|
|
@@ -1728,23 +1856,25 @@ EXPORT_SYMBOL_GPL(__gpio_get_value);
|
|
|
* @chip: Gpio chip.
|
|
|
* @value: Non-zero for setting it HIGH otherise it will set to LOW.
|
|
|
*/
|
|
|
-static void _gpio_set_open_drain_value(unsigned gpio,
|
|
|
- struct gpio_chip *chip, int value)
|
|
|
+static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
int err = 0;
|
|
|
+ struct gpio_chip *chip = desc->chip;
|
|
|
+ int offset = gpio_chip_hwgpio(desc);
|
|
|
+
|
|
|
if (value) {
|
|
|
- err = chip->direction_input(chip, gpio - chip->base);
|
|
|
+ err = chip->direction_input(chip, offset);
|
|
|
if (!err)
|
|
|
- clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
|
|
|
+ clear_bit(FLAG_IS_OUT, &desc->flags);
|
|
|
} else {
|
|
|
- err = chip->direction_output(chip, gpio - chip->base, 0);
|
|
|
+ err = chip->direction_output(chip, offset, 0);
|
|
|
if (!err)
|
|
|
- set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
|
|
|
+ set_bit(FLAG_IS_OUT, &desc->flags);
|
|
|
}
|
|
|
- trace_gpio_direction(gpio, value, err);
|
|
|
+ trace_gpio_direction(desc_to_gpio(desc), value, err);
|
|
|
if (err < 0)
|
|
|
pr_err("%s: Error in set_value for open drain gpio%d err %d\n",
|
|
|
- __func__, gpio, err);
|
|
|
+ __func__, desc_to_gpio(desc), err);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1753,26 +1883,27 @@ static void _gpio_set_open_drain_value(unsigned gpio,
|
|
|
* @chip: Gpio chip.
|
|
|
* @value: Non-zero for setting it HIGH otherise it will set to LOW.
|
|
|
*/
|
|
|
-static void _gpio_set_open_source_value(unsigned gpio,
|
|
|
- struct gpio_chip *chip, int value)
|
|
|
+static void _gpio_set_open_source_value(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
int err = 0;
|
|
|
+ struct gpio_chip *chip = desc->chip;
|
|
|
+ int offset = gpio_chip_hwgpio(desc);
|
|
|
+
|
|
|
if (value) {
|
|
|
- err = chip->direction_output(chip, gpio - chip->base, 1);
|
|
|
+ err = chip->direction_output(chip, offset, 1);
|
|
|
if (!err)
|
|
|
- set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
|
|
|
+ set_bit(FLAG_IS_OUT, &desc->flags);
|
|
|
} else {
|
|
|
- err = chip->direction_input(chip, gpio - chip->base);
|
|
|
+ err = chip->direction_input(chip, offset);
|
|
|
if (!err)
|
|
|
- clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
|
|
|
+ clear_bit(FLAG_IS_OUT, &desc->flags);
|
|
|
}
|
|
|
- trace_gpio_direction(gpio, !value, err);
|
|
|
+ trace_gpio_direction(desc_to_gpio(desc), !value, err);
|
|
|
if (err < 0)
|
|
|
pr_err("%s: Error in set_value for open source gpio%d err %d\n",
|
|
|
- __func__, gpio, err);
|
|
|
+ __func__, desc_to_gpio(desc), err);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* __gpio_set_value() - assign a gpio's value
|
|
|
* @gpio: gpio whose value will be assigned
|
|
@@ -1782,20 +1913,25 @@ static void _gpio_set_open_source_value(unsigned gpio,
|
|
|
* This is used directly or indirectly to implement gpio_set_value().
|
|
|
* It invokes the associated gpio_chip.set() method.
|
|
|
*/
|
|
|
-void __gpio_set_value(unsigned gpio, int value)
|
|
|
+static void gpiod_set_value(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
+ chip = desc->chip;
|
|
|
/* Should be using gpio_set_value_cansleep() */
|
|
|
WARN_ON(chip->can_sleep);
|
|
|
- trace_gpio_value(gpio, 0, value);
|
|
|
- if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags))
|
|
|
- _gpio_set_open_drain_value(gpio, chip, value);
|
|
|
- else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags))
|
|
|
- _gpio_set_open_source_value(gpio, chip, value);
|
|
|
+ trace_gpio_value(desc_to_gpio(desc), 0, value);
|
|
|
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
|
+ _gpio_set_open_drain_value(desc, value);
|
|
|
+ else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
|
+ _gpio_set_open_source_value(desc, value);
|
|
|
else
|
|
|
- chip->set(chip, gpio - chip->base, value);
|
|
|
+ chip->set(chip, gpio_chip_hwgpio(desc), value);
|
|
|
+}
|
|
|
+
|
|
|
+void __gpio_set_value(unsigned gpio, int value)
|
|
|
+{
|
|
|
+ return gpiod_set_value(gpio_to_desc(gpio), value);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__gpio_set_value);
|
|
|
|
|
@@ -1807,14 +1943,15 @@ EXPORT_SYMBOL_GPL(__gpio_set_value);
|
|
|
* This is used directly or indirectly to implement gpio_cansleep(). It
|
|
|
* returns nonzero if access reading or writing the GPIO value can sleep.
|
|
|
*/
|
|
|
-int __gpio_cansleep(unsigned gpio)
|
|
|
+static int gpiod_cansleep(struct gpio_desc *desc)
|
|
|
{
|
|
|
- struct gpio_chip *chip;
|
|
|
-
|
|
|
/* only call this on GPIOs that are valid! */
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
+ return desc->chip->can_sleep;
|
|
|
+}
|
|
|
|
|
|
- return chip->can_sleep;
|
|
|
+int __gpio_cansleep(unsigned gpio)
|
|
|
+{
|
|
|
+ return gpiod_cansleep(gpio_to_desc(gpio));
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__gpio_cansleep);
|
|
|
|
|
@@ -1827,50 +1964,67 @@ EXPORT_SYMBOL_GPL(__gpio_cansleep);
|
|
|
* It returns the number of the IRQ signaled by this (input) GPIO,
|
|
|
* or a negative errno.
|
|
|
*/
|
|
|
-int __gpio_to_irq(unsigned gpio)
|
|
|
+static int gpiod_to_irq(struct gpio_desc *desc)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
+ int offset;
|
|
|
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
- return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO;
|
|
|
+ chip = desc->chip;
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
+ return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(__gpio_to_irq);
|
|
|
|
|
|
+int __gpio_to_irq(unsigned gpio)
|
|
|
+{
|
|
|
+ return gpiod_to_irq(gpio_to_desc(gpio));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(__gpio_to_irq);
|
|
|
|
|
|
|
|
|
/* There's no value in making it easy to inline GPIO calls that may sleep.
|
|
|
* Common examples include ones connected to I2C or SPI chips.
|
|
|
*/
|
|
|
|
|
|
-int gpio_get_value_cansleep(unsigned gpio)
|
|
|
+static int gpiod_get_value_cansleep(struct gpio_desc *desc)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
int value;
|
|
|
+ int offset;
|
|
|
|
|
|
might_sleep_if(extra_checks);
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
- value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
|
|
|
- trace_gpio_value(gpio, 1, value);
|
|
|
+ chip = desc->chip;
|
|
|
+ offset = gpio_chip_hwgpio(desc);
|
|
|
+ value = chip->get ? chip->get(chip, offset) : 0;
|
|
|
+ trace_gpio_value(desc_to_gpio(desc), 1, value);
|
|
|
return value;
|
|
|
}
|
|
|
+
|
|
|
+int gpio_get_value_cansleep(unsigned gpio)
|
|
|
+{
|
|
|
+ return gpiod_get_value_cansleep(gpio_to_desc(gpio));
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
|
|
|
|
|
|
-void gpio_set_value_cansleep(unsigned gpio, int value)
|
|
|
+static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
|
might_sleep_if(extra_checks);
|
|
|
- chip = gpio_to_chip(gpio);
|
|
|
- trace_gpio_value(gpio, 0, value);
|
|
|
- if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags))
|
|
|
- _gpio_set_open_drain_value(gpio, chip, value);
|
|
|
- else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags))
|
|
|
- _gpio_set_open_source_value(gpio, chip, value);
|
|
|
+ chip = desc->chip;
|
|
|
+ trace_gpio_value(desc_to_gpio(desc), 0, value);
|
|
|
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
|
+ _gpio_set_open_drain_value(desc, value);
|
|
|
+ else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
|
+ _gpio_set_open_source_value(desc, value);
|
|
|
else
|
|
|
- chip->set(chip, gpio - chip->base, value);
|
|
|
+ chip->set(chip, gpio_chip_hwgpio(desc), value);
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
|
|
|
|
|
|
+void gpio_set_value_cansleep(unsigned gpio, int value)
|
|
|
+{
|
|
|
+ return gpiod_set_value_cansleep(gpio_to_desc(gpio), value);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
@@ -1878,14 +2032,14 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
|
{
|
|
|
unsigned i;
|
|
|
unsigned gpio = chip->base;
|
|
|
- struct gpio_desc *gdesc = &gpio_desc[gpio];
|
|
|
+ struct gpio_desc *gdesc = &chip->desc[0];
|
|
|
int is_out;
|
|
|
|
|
|
for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
|
|
|
if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
|
|
|
continue;
|
|
|
|
|
|
- gpio_get_direction(gpio);
|
|
|
+ gpiod_get_direction(gdesc);
|
|
|
is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
|
|
|
seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
|
|
|
gpio, gdesc->label,
|
|
@@ -1899,46 +2053,35 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
|
|
|
|
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
{
|
|
|
+ unsigned long flags;
|
|
|
struct gpio_chip *chip = NULL;
|
|
|
- unsigned int gpio;
|
|
|
- void *ret = NULL;
|
|
|
- loff_t index = 0;
|
|
|
-
|
|
|
- /* REVISIT this isn't locked against gpio_chip removal ... */
|
|
|
+ loff_t index = *pos;
|
|
|
|
|
|
- for (gpio = 0; gpio_is_valid(gpio); gpio++) {
|
|
|
- if (gpio_desc[gpio].chip == chip)
|
|
|
- continue;
|
|
|
-
|
|
|
- chip = gpio_desc[gpio].chip;
|
|
|
- if (!chip)
|
|
|
- continue;
|
|
|
+ s->private = "";
|
|
|
|
|
|
- if (index++ >= *pos) {
|
|
|
- ret = chip;
|
|
|
- break;
|
|
|
+ spin_lock_irqsave(&gpio_lock, flags);
|
|
|
+ list_for_each_entry(chip, &gpio_chips, list)
|
|
|
+ if (index-- == 0) {
|
|
|
+ spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
+ return chip;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- s->private = "";
|
|
|
+ spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
- return ret;
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
{
|
|
|
+ unsigned long flags;
|
|
|
struct gpio_chip *chip = v;
|
|
|
- unsigned int gpio;
|
|
|
void *ret = NULL;
|
|
|
|
|
|
- /* skip GPIOs provided by the current chip */
|
|
|
- for (gpio = chip->base + chip->ngpio; gpio_is_valid(gpio); gpio++) {
|
|
|
- chip = gpio_desc[gpio].chip;
|
|
|
- if (chip) {
|
|
|
- ret = chip;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ spin_lock_irqsave(&gpio_lock, flags);
|
|
|
+ if (list_is_last(&chip->list, &gpio_chips))
|
|
|
+ ret = NULL;
|
|
|
+ else
|
|
|
+ ret = list_entry(chip->list.next, struct gpio_chip, list);
|
|
|
+ spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
s->private = "\n";
|
|
|
++*pos;
|