Loading Documentation/ABI/testing/sysfs-gpio→Documentation/ABI/obsolete/sysfs-gpio +3 −1 Original line number Diff line number Diff line What: /sys/class/gpio/ Date: July 2008 KernelVersion: 2.6.27 Contact: David Brownell <dbrownell@users.sourceforge.net> Contact: Linus Walleij <linusw@kernel.org> Description: As a Kconfig option, individual GPIO signals may be accessed from Loading @@ -26,3 +26,5 @@ Description: /label ... (r/o) descriptive, not necessarily unique /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) This ABI is deprecated and will be removed after 2020. It is replaced with the GPIO character device. Documentation/ABI/testing/gpio-cdev 0 → 100644 +26 −0 Original line number Diff line number Diff line What: /dev/gpiochip[0-9]+ Date: November 2015 KernelVersion: 4.4 Contact: linux-gpio@vger.kernel.org Description: The character device files /dev/gpiochip* are the interface between GPIO chips and userspace. The ioctl(2)-based ABI is defined and documented in [include/uapi]<linux/gpio.h>. The following file operations are supported: open(2) Currently the only useful flags are O_RDWR. ioctl(2) Initiate various actions. See the inline documentation in [include/uapi]<linux/gpio.h> for descriptions of all ioctls. close(2) Stops and free up the I/O contexts that was associated with the file descriptor. Users: TBD MAINTAINERS +4 −0 Original line number Diff line number Diff line Loading @@ -4821,10 +4821,14 @@ L: linux-gpio@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git S: Maintained F: Documentation/gpio/ F: Documentation/ABI/testing/gpio-cdev F: Documentation/ABI/obsolete/sysfs-gpio F: drivers/gpio/ F: include/linux/gpio/ F: include/linux/gpio.h F: include/asm-generic/gpio.h F: include/uapi/linux/gpio.h F: tools/gpio/ GRE DEMULTIPLEXER DRIVER M: Dmitry Kozlov <xeb@mail.ru> Loading drivers/gpio/gpiolib-sysfs.c +7 −5 Original line number Diff line number Diff line Loading @@ -547,6 +547,7 @@ static struct class gpio_class = { int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpio_chip *chip; struct gpio_device *gdev; struct gpiod_data *data; unsigned long flags; int status; Loading @@ -566,6 +567,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) } chip = desc->chip; gdev = chip->gpiodev; mutex_lock(&sysfs_lock); Loading Loading @@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) if (chip->names && chip->names[offset]) ioname = chip->names[offset]; dev = device_create_with_groups(&gpio_class, chip->parent, dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc)); Loading Loading @@ -771,7 +773,7 @@ static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; struct gpio_chip *chip; struct gpio_device *gdev; status = class_register(&gpio_class); if (status < 0) Loading @@ -784,8 +786,8 @@ static int __init gpiolib_sysfs_init(void) * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(chip, &gpio_chips, list) { if (chip->cdev) list_for_each_entry(gdev, &gpio_devices, list) { if (gdev->chip->cdev) continue; /* Loading @@ -798,7 +800,7 @@ static int __init gpiolib_sysfs_init(void) * gpio_lock prevents us from doing this. */ spin_unlock_irqrestore(&gpio_lock, flags); status = gpiochip_sysfs_register(chip); status = gpiochip_sysfs_register(gdev->chip); spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); Loading drivers/gpio/gpiolib.c +290 −71 Original line number Diff line number Diff line Loading @@ -16,6 +16,11 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> #include <linux/pinctrl/consumer.h> #include <linux/idr.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" Loading @@ -42,6 +47,14 @@ #define extra_checks 0 #endif /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); static dev_t gpio_devt; #define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */ static struct bus_type gpio_bus_type = { .name = "gpio", }; /* 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. Loading @@ -50,8 +63,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); Loading @@ -67,15 +79,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]; } } Loading Loading @@ -125,16 +138,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)) { Loading Loading @@ -187,18 +200,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 Loading @@ -211,8 +234,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; } } Loading @@ -225,18 +248,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; } Loading @@ -245,16 +268,16 @@ static int gpiochip_add_to_list(struct gpio_chip *chip) */ 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; Loading Loading @@ -290,8 +313,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) gpio = gpio_name_to_desc(gc->names[i]); if (gpio) dev_warn(gc->parent, "Detected name collision for " "GPIO name '%s'\n", dev_warn(&gc->gpiodev->dev, "Detected name collision for GPIO name '%s'\n", gc->names[i]); } Loading @@ -302,6 +325,88 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) return 0; } /** * gpio_ioctl() - ioctl handler for the GPIO chardev */ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct gpio_device *gdev = filp->private_data; struct gpio_chip *chip = gdev->chip; int __user *ip = (int __user *)arg; struct gpiochip_info chipinfo; /* We fail any subsequent ioctl():s when the chip is gone */ if (!chip) return -ENODEV; if (cmd == GPIO_GET_CHIPINFO_IOCTL) { /* Fill in the struct and pass to userspace */ strncpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name)); chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; chipinfo.lines = chip->ngpio; if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) return -EFAULT; return 0; } return -EINVAL; } /** * gpio_chrdev_open() - open the chardev for ioctl operations * @inode: inode for this chardev * @filp: file struct for storing private data * Returns 0 on success */ static int gpio_chrdev_open(struct inode *inode, struct file *filp) { struct gpio_device *gdev = container_of(inode->i_cdev, struct gpio_device, chrdev); /* Fail on open if the backing gpiochip is gone */ if (!gdev || !gdev->chip) return -ENODEV; get_device(&gdev->dev); filp->private_data = gdev; return 0; } /** * gpio_chrdev_release() - close chardev after ioctl operations * @inode: inode for this chardev * @filp: file struct for storing private data * Returns 0 on success */ static int gpio_chrdev_release(struct inode *inode, struct file *filp) { struct gpio_device *gdev = container_of(inode->i_cdev, struct gpio_device, chrdev); if (!gdev) return -ENODEV; put_device(&gdev->dev); return 0; } static const struct file_operations gpio_fileops = { .release = gpio_chrdev_release, .open = gpio_chrdev_open, .owner = THIS_MODULE, .llseek = noop_llseek, .unlocked_ioctl = gpio_ioctl, .compat_ioctl = gpio_ioctl, }; static void gpiodevice_release(struct device *dev) { struct gpio_device *gdev = dev_get_drvdata(dev); cdev_del(&gdev->chrdev); 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 Loading @@ -323,19 +428,61 @@ 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->dev.bus = &gpio_bus_type; 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); Loading @@ -350,15 +497,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 Loading @@ -369,18 +517,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; Loading @@ -391,28 +536,58 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) acpi_gpiochip_add(chip); /* * By first adding the chardev, and then adding the device, * we get a device node entry in sysfs under * /sys/bus/gpio/devices/gpiochipN/dev that can be used for * coldplug of device nodes and other udev business. */ cdev_init(&gdev->chrdev, &gpio_fileops); gdev->chrdev.owner = THIS_MODULE; gdev->chrdev.kobj.parent = &gdev->dev.kobj; gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1); if (status < 0) chip_warn(chip, "failed to add char device %d:%d\n", MAJOR(gpio_devt), gdev->id); else chip_dbg(chip, "added GPIO chardev (%d:%d)\n", MAJOR(gpio_devt), gdev->id); status = device_add(&gdev->dev); if (status) goto err_remove_chardev; status = gpiochip_sysfs_register(chip); if (status) goto err_remove_chip; 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_chardev: cdev_del(&gdev->chrdev); 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, Loading @@ -429,15 +604,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; gpiochip_sysfs_unregister(chip); /* 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); Loading @@ -450,15 +628,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, dev_crit(&chip->gpiodev->dev, "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); Loading @@ -477,17 +663,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; Loading Loading @@ -617,14 +807,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; Loading @@ -635,7 +825,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) Loading Loading @@ -985,10 +1175,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: Loading Loading @@ -1034,7 +1224,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); } Loading Loading @@ -2456,6 +2646,26 @@ void gpiod_put_array(struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(gpiod_put_array); static int __init gpiolib_dev_init(void) { int ret; /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { pr_err("gpiolib: could not register GPIO bus type\n"); return ret; } ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); if (ret < 0) { pr_err("gpiolib: failed to allocate char dev region\n"); bus_unregister(&gpio_bus_type); } return ret; } core_initcall(gpiolib_dev_init); #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) Loading Loading @@ -2492,16 +2702,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); Loading @@ -2511,14 +2721,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"; Loading @@ -2533,15 +2743,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; if (!chip) { seq_printf(s, "%s%s: (dangling chip)", (char *)s->private, dev_name(&gdev->dev)); return 0; } seq_printf(s, "%sGPIOs %d-%d", (char *)s->private, seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private, dev_name(&gdev->dev), 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)); 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) Loading Loading
Documentation/ABI/testing/sysfs-gpio→Documentation/ABI/obsolete/sysfs-gpio +3 −1 Original line number Diff line number Diff line What: /sys/class/gpio/ Date: July 2008 KernelVersion: 2.6.27 Contact: David Brownell <dbrownell@users.sourceforge.net> Contact: Linus Walleij <linusw@kernel.org> Description: As a Kconfig option, individual GPIO signals may be accessed from Loading @@ -26,3 +26,5 @@ Description: /label ... (r/o) descriptive, not necessarily unique /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) This ABI is deprecated and will be removed after 2020. It is replaced with the GPIO character device.
Documentation/ABI/testing/gpio-cdev 0 → 100644 +26 −0 Original line number Diff line number Diff line What: /dev/gpiochip[0-9]+ Date: November 2015 KernelVersion: 4.4 Contact: linux-gpio@vger.kernel.org Description: The character device files /dev/gpiochip* are the interface between GPIO chips and userspace. The ioctl(2)-based ABI is defined and documented in [include/uapi]<linux/gpio.h>. The following file operations are supported: open(2) Currently the only useful flags are O_RDWR. ioctl(2) Initiate various actions. See the inline documentation in [include/uapi]<linux/gpio.h> for descriptions of all ioctls. close(2) Stops and free up the I/O contexts that was associated with the file descriptor. Users: TBD
MAINTAINERS +4 −0 Original line number Diff line number Diff line Loading @@ -4821,10 +4821,14 @@ L: linux-gpio@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git S: Maintained F: Documentation/gpio/ F: Documentation/ABI/testing/gpio-cdev F: Documentation/ABI/obsolete/sysfs-gpio F: drivers/gpio/ F: include/linux/gpio/ F: include/linux/gpio.h F: include/asm-generic/gpio.h F: include/uapi/linux/gpio.h F: tools/gpio/ GRE DEMULTIPLEXER DRIVER M: Dmitry Kozlov <xeb@mail.ru> Loading
drivers/gpio/gpiolib-sysfs.c +7 −5 Original line number Diff line number Diff line Loading @@ -547,6 +547,7 @@ static struct class gpio_class = { int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpio_chip *chip; struct gpio_device *gdev; struct gpiod_data *data; unsigned long flags; int status; Loading @@ -566,6 +567,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) } chip = desc->chip; gdev = chip->gpiodev; mutex_lock(&sysfs_lock); Loading Loading @@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) if (chip->names && chip->names[offset]) ioname = chip->names[offset]; dev = device_create_with_groups(&gpio_class, chip->parent, dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc)); Loading Loading @@ -771,7 +773,7 @@ static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; struct gpio_chip *chip; struct gpio_device *gdev; status = class_register(&gpio_class); if (status < 0) Loading @@ -784,8 +786,8 @@ static int __init gpiolib_sysfs_init(void) * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(chip, &gpio_chips, list) { if (chip->cdev) list_for_each_entry(gdev, &gpio_devices, list) { if (gdev->chip->cdev) continue; /* Loading @@ -798,7 +800,7 @@ static int __init gpiolib_sysfs_init(void) * gpio_lock prevents us from doing this. */ spin_unlock_irqrestore(&gpio_lock, flags); status = gpiochip_sysfs_register(chip); status = gpiochip_sysfs_register(gdev->chip); spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); Loading
drivers/gpio/gpiolib.c +290 −71 Original line number Diff line number Diff line Loading @@ -16,6 +16,11 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> #include <linux/pinctrl/consumer.h> #include <linux/idr.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" Loading @@ -42,6 +47,14 @@ #define extra_checks 0 #endif /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); static dev_t gpio_devt; #define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */ static struct bus_type gpio_bus_type = { .name = "gpio", }; /* 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. Loading @@ -50,8 +63,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); Loading @@ -67,15 +79,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]; } } Loading Loading @@ -125,16 +138,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)) { Loading Loading @@ -187,18 +200,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 Loading @@ -211,8 +234,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; } } Loading @@ -225,18 +248,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; } Loading @@ -245,16 +268,16 @@ static int gpiochip_add_to_list(struct gpio_chip *chip) */ 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; Loading Loading @@ -290,8 +313,8 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) gpio = gpio_name_to_desc(gc->names[i]); if (gpio) dev_warn(gc->parent, "Detected name collision for " "GPIO name '%s'\n", dev_warn(&gc->gpiodev->dev, "Detected name collision for GPIO name '%s'\n", gc->names[i]); } Loading @@ -302,6 +325,88 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) return 0; } /** * gpio_ioctl() - ioctl handler for the GPIO chardev */ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct gpio_device *gdev = filp->private_data; struct gpio_chip *chip = gdev->chip; int __user *ip = (int __user *)arg; struct gpiochip_info chipinfo; /* We fail any subsequent ioctl():s when the chip is gone */ if (!chip) return -ENODEV; if (cmd == GPIO_GET_CHIPINFO_IOCTL) { /* Fill in the struct and pass to userspace */ strncpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name)); chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; chipinfo.lines = chip->ngpio; if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) return -EFAULT; return 0; } return -EINVAL; } /** * gpio_chrdev_open() - open the chardev for ioctl operations * @inode: inode for this chardev * @filp: file struct for storing private data * Returns 0 on success */ static int gpio_chrdev_open(struct inode *inode, struct file *filp) { struct gpio_device *gdev = container_of(inode->i_cdev, struct gpio_device, chrdev); /* Fail on open if the backing gpiochip is gone */ if (!gdev || !gdev->chip) return -ENODEV; get_device(&gdev->dev); filp->private_data = gdev; return 0; } /** * gpio_chrdev_release() - close chardev after ioctl operations * @inode: inode for this chardev * @filp: file struct for storing private data * Returns 0 on success */ static int gpio_chrdev_release(struct inode *inode, struct file *filp) { struct gpio_device *gdev = container_of(inode->i_cdev, struct gpio_device, chrdev); if (!gdev) return -ENODEV; put_device(&gdev->dev); return 0; } static const struct file_operations gpio_fileops = { .release = gpio_chrdev_release, .open = gpio_chrdev_open, .owner = THIS_MODULE, .llseek = noop_llseek, .unlocked_ioctl = gpio_ioctl, .compat_ioctl = gpio_ioctl, }; static void gpiodevice_release(struct device *dev) { struct gpio_device *gdev = dev_get_drvdata(dev); cdev_del(&gdev->chrdev); 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 Loading @@ -323,19 +428,61 @@ 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->dev.bus = &gpio_bus_type; 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); Loading @@ -350,15 +497,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 Loading @@ -369,18 +517,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; Loading @@ -391,28 +536,58 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) acpi_gpiochip_add(chip); /* * By first adding the chardev, and then adding the device, * we get a device node entry in sysfs under * /sys/bus/gpio/devices/gpiochipN/dev that can be used for * coldplug of device nodes and other udev business. */ cdev_init(&gdev->chrdev, &gpio_fileops); gdev->chrdev.owner = THIS_MODULE; gdev->chrdev.kobj.parent = &gdev->dev.kobj; gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1); if (status < 0) chip_warn(chip, "failed to add char device %d:%d\n", MAJOR(gpio_devt), gdev->id); else chip_dbg(chip, "added GPIO chardev (%d:%d)\n", MAJOR(gpio_devt), gdev->id); status = device_add(&gdev->dev); if (status) goto err_remove_chardev; status = gpiochip_sysfs_register(chip); if (status) goto err_remove_chip; 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_chardev: cdev_del(&gdev->chrdev); 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, Loading @@ -429,15 +604,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; gpiochip_sysfs_unregister(chip); /* 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); Loading @@ -450,15 +628,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, dev_crit(&chip->gpiodev->dev, "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); Loading @@ -477,17 +663,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; Loading Loading @@ -617,14 +807,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; Loading @@ -635,7 +825,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) Loading Loading @@ -985,10 +1175,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: Loading Loading @@ -1034,7 +1224,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); } Loading Loading @@ -2456,6 +2646,26 @@ void gpiod_put_array(struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(gpiod_put_array); static int __init gpiolib_dev_init(void) { int ret; /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret < 0) { pr_err("gpiolib: could not register GPIO bus type\n"); return ret; } ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); if (ret < 0) { pr_err("gpiolib: failed to allocate char dev region\n"); bus_unregister(&gpio_bus_type); } return ret; } core_initcall(gpiolib_dev_init); #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) Loading Loading @@ -2492,16 +2702,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); Loading @@ -2511,14 +2721,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"; Loading @@ -2533,15 +2743,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; if (!chip) { seq_printf(s, "%s%s: (dangling chip)", (char *)s->private, dev_name(&gdev->dev)); return 0; } seq_printf(s, "%sGPIOs %d-%d", (char *)s->private, seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private, dev_name(&gdev->dev), 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)); 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) Loading