|
|
|
@ -16,6 +16,7 @@
|
|
|
|
|
#include <linux/gpio/driver.h>
|
|
|
|
|
#include <linux/gpio/machine.h>
|
|
|
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
|
#include <linux/idr.h>
|
|
|
|
|
|
|
|
|
|
#include "gpiolib.h"
|
|
|
|
|
|
|
|
|
@ -42,6 +43,9 @@
|
|
|
|
|
#define extra_checks 0
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Device and char device-related information */
|
|
|
|
|
static DEFINE_IDA(gpio_ida);
|
|
|
|
|
|
|
|
|
|
/* gpio_lock prevents conflicts during gpio_desc[] table updates.
|
|
|
|
|
* While any GPIO is requested, its gpio_chip is not removable;
|
|
|
|
|
* each GPIO's "requested" flag serves as a lock and refcount.
|
|
|
|
@ -50,8 +54,7 @@ DEFINE_SPINLOCK(gpio_lock);
|
|
|
|
|
|
|
|
|
|
static DEFINE_MUTEX(gpio_lookup_lock);
|
|
|
|
|
static LIST_HEAD(gpio_lookup_list);
|
|
|
|
|
LIST_HEAD(gpio_chips);
|
|
|
|
|
|
|
|
|
|
LIST_HEAD(gpio_devices);
|
|
|
|
|
|
|
|
|
|
static void gpiochip_free_hogs(struct gpio_chip *chip);
|
|
|
|
|
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
|
|
|
|
@ -67,15 +70,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
|
|
|
*/
|
|
|
|
|
struct gpio_desc *gpio_to_desc(unsigned gpio)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
struct gpio_device *gdev;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(chip, &gpio_chips, list) {
|
|
|
|
|
if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
|
|
|
|
|
list_for_each_entry(gdev, &gpio_devices, list) {
|
|
|
|
|
if (gdev->chip->base <= gpio &&
|
|
|
|
|
gdev->chip->base + gdev->chip->ngpio > gpio) {
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
return &chip->desc[gpio - chip->base];
|
|
|
|
|
return &gdev->chip->desc[gpio - gdev->chip->base];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -125,16 +129,16 @@ EXPORT_SYMBOL_GPL(gpiod_to_chip);
|
|
|
|
|
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
|
|
|
|
|
static int gpiochip_find_base(int ngpio)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
struct gpio_device *gdev;
|
|
|
|
|
int base = ARCH_NR_GPIOS - ngpio;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry_reverse(chip, &gpio_chips, list) {
|
|
|
|
|
list_for_each_entry_reverse(gdev, &gpio_devices, list) {
|
|
|
|
|
/* found a free space? */
|
|
|
|
|
if (chip->base + chip->ngpio <= base)
|
|
|
|
|
if (gdev->chip->base + gdev->chip->ngpio <= base)
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
/* nope, check the space right before the chip */
|
|
|
|
|
base = chip->base - ngpio;
|
|
|
|
|
base = gdev->chip->base - ngpio;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gpio_is_valid(base)) {
|
|
|
|
@ -187,18 +191,28 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
|
|
|
|
|
* Return -EBUSY if the new chip overlaps with some other chip's integer
|
|
|
|
|
* space.
|
|
|
|
|
*/
|
|
|
|
|
static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
|
|
static int gpiodev_add_to_list(struct gpio_device *gdev)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_chip *iterator;
|
|
|
|
|
struct gpio_chip *previous = NULL;
|
|
|
|
|
struct gpio_device *iterator;
|
|
|
|
|
struct gpio_device *previous = NULL;
|
|
|
|
|
|
|
|
|
|
if (list_empty(&gpio_chips)) {
|
|
|
|
|
list_add_tail(&chip->list, &gpio_chips);
|
|
|
|
|
if (!gdev->chip)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
if (list_empty(&gpio_devices)) {
|
|
|
|
|
list_add_tail(&gdev->list, &gpio_devices);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(iterator, &gpio_chips, list) {
|
|
|
|
|
if (iterator->base >= chip->base + chip->ngpio) {
|
|
|
|
|
list_for_each_entry(iterator, &gpio_devices, list) {
|
|
|
|
|
/*
|
|
|
|
|
* The list may contain dangling GPIO devices with no
|
|
|
|
|
* live chip assigned.
|
|
|
|
|
*/
|
|
|
|
|
if (!iterator->chip)
|
|
|
|
|
continue;
|
|
|
|
|
if (iterator->chip->base >=
|
|
|
|
|
gdev->chip->base + gdev->chip->ngpio) {
|
|
|
|
|
/*
|
|
|
|
|
* Iterator is the first GPIO chip so there is no
|
|
|
|
|
* previous one
|
|
|
|
@ -211,8 +225,8 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
|
|
* [base, base + ngpio - 1]) between previous
|
|
|
|
|
* and iterator chip.
|
|
|
|
|
*/
|
|
|
|
|
if (previous->base + previous->ngpio
|
|
|
|
|
<= chip->base)
|
|
|
|
|
if (previous->chip->base + previous->chip->ngpio
|
|
|
|
|
<= gdev->chip->base)
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -225,18 +239,18 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
|
|
* Let iterator point to the last chip in the list.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
iterator = list_last_entry(&gpio_chips, struct gpio_chip, list);
|
|
|
|
|
if (iterator->base + iterator->ngpio <= chip->base) {
|
|
|
|
|
list_add(&chip->list, &iterator->list);
|
|
|
|
|
iterator = list_last_entry(&gpio_devices, struct gpio_device, list);
|
|
|
|
|
if (iterator->chip->base + iterator->chip->ngpio <= gdev->chip->base) {
|
|
|
|
|
list_add(&gdev->list, &iterator->list);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev_err(chip->parent,
|
|
|
|
|
dev_err(&gdev->dev,
|
|
|
|
|
"GPIO integer space overlap, cannot add chip\n");
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
|
|
found:
|
|
|
|
|
list_add_tail(&chip->list, &iterator->list);
|
|
|
|
|
list_add_tail(&gdev->list, &iterator->list);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -245,16 +259,16 @@ found:
|
|
|
|
|
*/
|
|
|
|
|
static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
struct gpio_device *gdev;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(chip, &gpio_chips, list) {
|
|
|
|
|
list_for_each_entry(gdev, &gpio_devices, list) {
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i != chip->ngpio; ++i) {
|
|
|
|
|
struct gpio_desc *gpio = &chip->desc[i];
|
|
|
|
|
for (i = 0; i != gdev->chip->ngpio; ++i) {
|
|
|
|
|
struct gpio_desc *gpio = &gdev->chip->desc[i];
|
|
|
|
|
|
|
|
|
|
if (!gpio->name || !name)
|
|
|
|
|
continue;
|
|
|
|
@ -302,6 +316,14 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void gpiodevice_release(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_device *gdev = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
|
|
list_del(&gdev->list);
|
|
|
|
|
ida_simple_remove(&gpio_ida, gdev->id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gpiochip_add_data() - register a gpio_chip
|
|
|
|
|
* @chip: the chip to register, with chip->base initialized
|
|
|
|
@ -323,19 +345,60 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int status = 0;
|
|
|
|
|
unsigned id;
|
|
|
|
|
unsigned i;
|
|
|
|
|
int base = chip->base;
|
|
|
|
|
struct gpio_desc *descs;
|
|
|
|
|
struct gpio_device *gdev;
|
|
|
|
|
|
|
|
|
|
descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
|
|
|
|
|
if (!descs)
|
|
|
|
|
/*
|
|
|
|
|
* First: allocate and populate the internal stat container, and
|
|
|
|
|
* set up the struct device.
|
|
|
|
|
*/
|
|
|
|
|
gdev = kmalloc(sizeof(*gdev), GFP_KERNEL);
|
|
|
|
|
if (!gdev)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
gdev->chip = chip;
|
|
|
|
|
chip->gpiodev = gdev;
|
|
|
|
|
if (chip->parent) {
|
|
|
|
|
gdev->dev.parent = chip->parent;
|
|
|
|
|
gdev->dev.of_node = chip->parent->of_node;
|
|
|
|
|
} else {
|
|
|
|
|
#ifdef CONFIG_OF_GPIO
|
|
|
|
|
/* If the gpiochip has an assigned OF node this takes precedence */
|
|
|
|
|
if (chip->of_node)
|
|
|
|
|
gdev->dev.of_node = chip->of_node;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
|
|
|
|
|
if (gdev->id < 0) {
|
|
|
|
|
status = gdev->id;
|
|
|
|
|
goto err_free_gdev;
|
|
|
|
|
}
|
|
|
|
|
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
|
|
|
|
|
device_initialize(&gdev->dev);
|
|
|
|
|
dev_set_drvdata(&gdev->dev, gdev);
|
|
|
|
|
if (chip->parent && chip->parent->driver)
|
|
|
|
|
gdev->owner = chip->parent->driver->owner;
|
|
|
|
|
else if (chip->owner)
|
|
|
|
|
/* TODO: remove chip->owner */
|
|
|
|
|
gdev->owner = chip->owner;
|
|
|
|
|
else
|
|
|
|
|
gdev->owner = THIS_MODULE;
|
|
|
|
|
|
|
|
|
|
/* FIXME: devm_kcalloc() these and move to gpio_device */
|
|
|
|
|
descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
|
|
|
|
|
if (!descs) {
|
|
|
|
|
status = -ENOMEM;
|
|
|
|
|
goto err_free_gdev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FIXME: move driver data into gpio_device dev_set_drvdata() */
|
|
|
|
|
chip->data = data;
|
|
|
|
|
|
|
|
|
|
if (chip->ngpio == 0) {
|
|
|
|
|
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
status = -EINVAL;
|
|
|
|
|
goto err_free_descs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
@ -350,15 +413,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
|
|
chip->base = base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = gpiochip_add_to_list(chip);
|
|
|
|
|
status = gpiodev_add_to_list(gdev);
|
|
|
|
|
if (status) {
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
goto err_free_descs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (id = 0; id < chip->ngpio; id++) {
|
|
|
|
|
struct gpio_desc *desc = &descs[id];
|
|
|
|
|
for (i = 0; i < chip->ngpio; i++) {
|
|
|
|
|
struct gpio_desc *desc = &descs[i];
|
|
|
|
|
|
|
|
|
|
/* REVISIT: maybe a pointer to gpio_device is better */
|
|
|
|
|
desc->chip = chip;
|
|
|
|
|
|
|
|
|
|
/* REVISIT: most hardware initializes GPIOs as inputs (often
|
|
|
|
@ -369,18 +433,15 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
|
|
*/
|
|
|
|
|
desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chip->desc = descs;
|
|
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_PINCTRL
|
|
|
|
|
/* FIXME: move pin ranges to gpio_device */
|
|
|
|
|
INIT_LIST_HEAD(&chip->pin_ranges);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!chip->owner && chip->parent && chip->parent->driver)
|
|
|
|
|
chip->owner = chip->parent->driver->owner;
|
|
|
|
|
|
|
|
|
|
status = gpiochip_set_desc_names(chip);
|
|
|
|
|
if (status)
|
|
|
|
|
goto err_remove_from_list;
|
|
|
|
@ -391,28 +452,39 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
|
|
|
|
|
|
|
acpi_gpiochip_add(chip);
|
|
|
|
|
|
|
|
|
|
status = gpiochip_sysfs_register(chip);
|
|
|
|
|
status = device_add(&gdev->dev);
|
|
|
|
|
if (status)
|
|
|
|
|
goto err_remove_chip;
|
|
|
|
|
|
|
|
|
|
status = gpiochip_sysfs_register(chip);
|
|
|
|
|
if (status)
|
|
|
|
|
goto err_remove_device;
|
|
|
|
|
|
|
|
|
|
/* From this point, the .release() function cleans up gpio_device */
|
|
|
|
|
gdev->dev.release = gpiodevice_release;
|
|
|
|
|
get_device(&gdev->dev);
|
|
|
|
|
pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
|
|
|
|
|
chip->base, chip->base + chip->ngpio - 1,
|
|
|
|
|
chip->label ? : "generic");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_remove_device:
|
|
|
|
|
device_del(&gdev->dev);
|
|
|
|
|
err_remove_chip:
|
|
|
|
|
acpi_gpiochip_remove(chip);
|
|
|
|
|
gpiochip_free_hogs(chip);
|
|
|
|
|
of_gpiochip_remove(chip);
|
|
|
|
|
err_remove_from_list:
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
list_del(&chip->list);
|
|
|
|
|
list_del(&gdev->list);
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
chip->desc = NULL;
|
|
|
|
|
err_free_descs:
|
|
|
|
|
kfree(descs);
|
|
|
|
|
|
|
|
|
|
err_free_gdev:
|
|
|
|
|
ida_simple_remove(&gpio_ida, gdev->id);
|
|
|
|
|
kfree(gdev);
|
|
|
|
|
/* failures here can mean systems won't boot... */
|
|
|
|
|
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
|
|
|
|
|
chip->base, chip->base + chip->ngpio - 1,
|
|
|
|
@ -429,15 +501,18 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data);
|
|
|
|
|
*/
|
|
|
|
|
void gpiochip_remove(struct gpio_chip *chip)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_device *gdev = chip->gpiodev;
|
|
|
|
|
struct gpio_desc *desc;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
unsigned id;
|
|
|
|
|
bool requested = false;
|
|
|
|
|
|
|
|
|
|
/* Numb the device, cancelling all outstanding operations */
|
|
|
|
|
gdev->chip = NULL;
|
|
|
|
|
|
|
|
|
|
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
|
|
|
|
|
gpiochip_sysfs_unregister(chip);
|
|
|
|
|
|
|
|
|
|
gpiochip_irqchip_remove(chip);
|
|
|
|
|
|
|
|
|
|
acpi_gpiochip_remove(chip);
|
|
|
|
|
gpiochip_remove_pin_ranges(chip);
|
|
|
|
|
gpiochip_free_hogs(chip);
|
|
|
|
@ -450,15 +525,23 @@ void gpiochip_remove(struct gpio_chip *chip)
|
|
|
|
|
if (test_bit(FLAG_REQUESTED, &desc->flags))
|
|
|
|
|
requested = true;
|
|
|
|
|
}
|
|
|
|
|
list_del(&chip->list);
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
|
if (requested)
|
|
|
|
|
dev_crit(chip->parent,
|
|
|
|
|
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
|
|
|
|
|
|
|
|
|
|
/* FIXME: need to be moved to gpio_device and held there */
|
|
|
|
|
kfree(chip->desc);
|
|
|
|
|
chip->desc = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The gpiochip side puts its use of the device to rest here:
|
|
|
|
|
* if there are no userspace clients, the chardev and device will
|
|
|
|
|
* be removed, else it will be dangling until the last user is
|
|
|
|
|
* gone.
|
|
|
|
|
*/
|
|
|
|
|
put_device(&gdev->dev);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_remove);
|
|
|
|
|
|
|
|
|
@ -477,17 +560,21 @@ struct gpio_chip *gpiochip_find(void *data,
|
|
|
|
|
int (*match)(struct gpio_chip *chip,
|
|
|
|
|
void *data))
|
|
|
|
|
{
|
|
|
|
|
struct gpio_device *gdev;
|
|
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
list_for_each_entry(chip, &gpio_chips, list)
|
|
|
|
|
if (match(chip, data))
|
|
|
|
|
list_for_each_entry(gdev, &gpio_devices, list)
|
|
|
|
|
if (match(gdev->chip, data))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* No match? */
|
|
|
|
|
if (&chip->list == &gpio_chips)
|
|
|
|
|
if (&gdev->list == &gpio_devices)
|
|
|
|
|
chip = NULL;
|
|
|
|
|
else
|
|
|
|
|
chip = gdev->chip;
|
|
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
|
return chip;
|
|
|
|
@ -617,14 +704,14 @@ static int gpiochip_irq_reqres(struct irq_data *d)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
|
|
|
|
|
|
if (!try_module_get(chip->owner))
|
|
|
|
|
if (!try_module_get(chip->gpiodev->owner))
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
if (gpiochip_lock_as_irq(chip, d->hwirq)) {
|
|
|
|
|
chip_err(chip,
|
|
|
|
|
"unable to lock HW IRQ %lu for IRQ\n",
|
|
|
|
|
d->hwirq);
|
|
|
|
|
module_put(chip->owner);
|
|
|
|
|
module_put(chip->gpiodev->owner);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
@ -635,7 +722,7 @@ static void gpiochip_irq_relres(struct irq_data *d)
|
|
|
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
|
|
|
|
|
|
gpiochip_unlock_as_irq(chip, d->hwirq);
|
|
|
|
|
module_put(chip->owner);
|
|
|
|
|
module_put(chip->gpiodev->owner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
|
|
|
|
@ -985,10 +1072,10 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
|
|
|
|
|
if (!chip)
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
if (try_module_get(chip->owner)) {
|
|
|
|
|
if (try_module_get(chip->gpiodev->owner)) {
|
|
|
|
|
status = __gpiod_request(desc, label);
|
|
|
|
|
if (status < 0)
|
|
|
|
|
module_put(chip->owner);
|
|
|
|
|
module_put(chip->gpiodev->owner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
@ -1034,7 +1121,7 @@ static bool __gpiod_free(struct gpio_desc *desc)
|
|
|
|
|
void gpiod_free(struct gpio_desc *desc)
|
|
|
|
|
{
|
|
|
|
|
if (desc && __gpiod_free(desc))
|
|
|
|
|
module_put(desc->chip->owner);
|
|
|
|
|
module_put(desc->chip->gpiodev->owner);
|
|
|
|
|
else
|
|
|
|
|
WARN_ON(extra_checks);
|
|
|
|
|
}
|
|
|
|
@ -2492,16 +2579,16 @@ 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;
|
|
|
|
|
struct gpio_device *gdev = NULL;
|
|
|
|
|
loff_t index = *pos;
|
|
|
|
|
|
|
|
|
|
s->private = "";
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
list_for_each_entry(chip, &gpio_chips, list)
|
|
|
|
|
list_for_each_entry(gdev, &gpio_devices, list)
|
|
|
|
|
if (index-- == 0) {
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
return chip;
|
|
|
|
|
return gdev;
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
@ -2511,14 +2598,14 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
|
|
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
struct gpio_chip *chip = v;
|
|
|
|
|
struct gpio_device *gdev = v;
|
|
|
|
|
void *ret = NULL;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
if (list_is_last(&chip->list, &gpio_chips))
|
|
|
|
|
if (list_is_last(&gdev->list, &gpio_devices))
|
|
|
|
|
ret = NULL;
|
|
|
|
|
else
|
|
|
|
|
ret = list_entry(chip->list.next, struct gpio_chip, list);
|
|
|
|
|
ret = list_entry(gdev->list.next, struct gpio_device, list);
|
|
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
|
|
|
|
s->private = "\n";
|
|
|
|
@ -2533,15 +2620,24 @@ static void gpiolib_seq_stop(struct seq_file *s, void *v)
|
|
|
|
|
|
|
|
|
|
static int gpiolib_seq_show(struct seq_file *s, void *v)
|
|
|
|
|
{
|
|
|
|
|
struct gpio_chip *chip = v;
|
|
|
|
|
struct device *dev;
|
|
|
|
|
struct gpio_device *gdev = v;
|
|
|
|
|
struct gpio_chip *chip = gdev->chip;
|
|
|
|
|
struct device *parent;
|
|
|
|
|
|
|
|
|
|
seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
|
|
|
|
|
chip->base, chip->base + chip->ngpio - 1);
|
|
|
|
|
dev = chip->parent;
|
|
|
|
|
if (dev)
|
|
|
|
|
seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
|
|
|
|
|
dev_name(dev));
|
|
|
|
|
if (!chip) {
|
|
|
|
|
seq_printf(s, "%s%s: (dangling chip)", (char *)s->private,
|
|
|
|
|
dev_name(&gdev->dev));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private,
|
|
|
|
|
dev_name(&gdev->dev),
|
|
|
|
|
chip->base, chip->base + chip->ngpio - 1);
|
|
|
|
|
parent = chip->parent;
|
|
|
|
|
if (parent)
|
|
|
|
|
seq_printf(s, ", parent: %s/%s",
|
|
|
|
|
parent->bus ? parent->bus->name : "no-bus",
|
|
|
|
|
dev_name(parent));
|
|
|
|
|
if (chip->label)
|
|
|
|
|
seq_printf(s, ", %s", chip->label);
|
|
|
|
|
if (chip->can_sleep)
|
|
|
|
|