From ff2b1359229927563addbf2f5ad480660c350903 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Oct 2015 11:10:38 +0200 Subject: gpio: make the gpiochip a real device GPIO chips have been around for years, but were never real devices, instead they were piggy-backing on a parent device (such as a platform_device or amba_device) but this was always optional. GPIO chips could also exist without any device at all, with its struct device *parent (ex *dev) pointer being set to null. When sysfs was in use, a mock device would be created, with the optional parent assigned, or just floating orphaned with NULL as parent. If sysfs is active, it will use this device as parent. We now create a gpio_device struct containing a real struct device and move the subsystem over to using that. The list of struct gpio_chip:s is augmented to hold struct gpio_device:s and we find gpio_chips:s by first looking up the struct gpio_device. The struct gpio_device is designed to stay around even if the gpio_chip is removed, so as to satisfy users in userspace that need a backing data structure to hold the state of the session initiated with e.g. a character device even if there is no physical chip anymore. From this point on, gpiochips are devices. Cc: Johan Hovold Cc: Michael Welling Cc: Markus Pargmann Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 82fda487453f..f3f1dbd43c9b 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -1,6 +1,7 @@ #ifndef __LINUX_GPIO_DRIVER_H #define __LINUX_GPIO_DRIVER_H +#include #include #include #include @@ -10,22 +11,22 @@ #include #include -struct device; struct gpio_desc; struct of_phandle_args; struct device_node; struct seq_file; +struct gpio_device; #ifdef CONFIG_GPIOLIB /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics + * @gpiodev: the internal state holder, opaque struct * @parent: optional parent device providing the GPIOs * @cdev: class device used by sysfs interface (may be NULL) * @owner: helps prevent removal of modules exporting active GPIOs * @data: per-instance data assigned by the driver - * @list: links gpio_chips together for traversal * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as @@ -107,11 +108,11 @@ struct seq_file; */ struct gpio_chip { const char *label; + struct gpio_device *gpiodev; struct device *parent; struct device *cdev; struct module *owner; void *data; - struct list_head list; int (*request)(struct gpio_chip *chip, unsigned offset); -- cgit v1.2.3 From afbc4f312b5e6e87fcd383eb6764e09f1324c78e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 9 Feb 2016 13:21:06 +0100 Subject: gpio: move sysfs mock device to the gpio_device Since gpio_device is the struct that survives if the backing gpio_chip is removed, move the sysfs mock device to this state container so it becomes part of the dangling state of the GPIO device on removal. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 23 ++++++++++++----------- drivers/gpio/gpiolib.c | 4 ++-- drivers/gpio/gpiolib.h | 11 +++++++---- include/linux/gpio/driver.h | 2 -- 4 files changed, 21 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 28d3bf2328aa..94ba4bb8b4f8 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -572,7 +572,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) mutex_lock(&sysfs_lock); /* check if chip is being removed */ - if (!chip || !chip->cdev) { + if (!chip || !gdev->mockdev) { status = -ENODEV; goto err_unlock; } @@ -718,9 +718,10 @@ err_unlock: } EXPORT_SYMBOL_GPL(gpiod_unexport); -int gpiochip_sysfs_register(struct gpio_chip *chip) +int gpiochip_sysfs_register(struct gpio_device *gdev) { struct device *dev; + struct gpio_chip *chip = gdev->chip; /* * Many systems add gpio chips for SOC support very early, @@ -732,7 +733,7 @@ int gpiochip_sysfs_register(struct gpio_chip *chip) return 0; /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, chip->parent, + dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), chip, gpiochip_groups, "gpiochip%d", chip->base); @@ -740,25 +741,26 @@ int gpiochip_sysfs_register(struct gpio_chip *chip) return PTR_ERR(dev); mutex_lock(&sysfs_lock); - chip->cdev = dev; + gdev->mockdev = dev; mutex_unlock(&sysfs_lock); return 0; } -void gpiochip_sysfs_unregister(struct gpio_chip *chip) +void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; + struct gpio_chip *chip = gdev->chip; unsigned int i; - if (!chip->cdev) + if (!gdev->mockdev) return; - device_unregister(chip->cdev); + device_unregister(gdev->mockdev); /* prevent further gpiod exports */ mutex_lock(&sysfs_lock); - chip->cdev = NULL; + gdev->mockdev = NULL; mutex_unlock(&sysfs_lock); /* unregister gpiod class devices owned by sysfs */ @@ -787,7 +789,7 @@ static int __init gpiolib_sysfs_init(void) */ spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->chip->cdev) + if (gdev->mockdev) continue; /* @@ -800,12 +802,11 @@ static int __init gpiolib_sysfs_init(void) * gpio_lock prevents us from doing this. */ spin_unlock_irqrestore(&gpio_lock, flags); - status = gpiochip_sysfs_register(gdev->chip); + status = gpiochip_sysfs_register(gdev); spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); - return status; } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 36f8be3f910b..5763290f777c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -558,7 +558,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) if (status) goto err_remove_chardev; - status = gpiochip_sysfs_register(chip); + status = gpiochip_sysfs_register(gdev); if (status) goto err_remove_device; @@ -615,7 +615,7 @@ void gpiochip_remove(struct gpio_chip *chip) gdev->chip = NULL; /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ - gpiochip_sysfs_unregister(chip); + gpiochip_sysfs_unregister(gdev); gpiochip_irqchip_remove(chip); acpi_gpiochip_remove(chip); gpiochip_remove_pin_ranges(chip); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 1524ba0ca99d..c5a5b57463c7 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -27,6 +27,8 @@ struct acpi_device; * @id: numerical ID number for the GPIO chip * @dev: the GPIO device struct * @chrdev: character device for the GPIO device + * @mockdev: class device used by the deprecated sysfs interface (may be + * NULL) * @owner: helps prevent removal of modules exporting active GPIOs * @chip: pointer to the corresponding gpiochip, holding static * data for this device @@ -41,6 +43,7 @@ struct gpio_device { int id; struct device dev; struct cdev chrdev; + struct device *mockdev; struct module *owner; struct gpio_chip *chip; struct list_head list; @@ -190,17 +193,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc) #ifdef CONFIG_GPIO_SYSFS -int gpiochip_sysfs_register(struct gpio_chip *chip); -void gpiochip_sysfs_unregister(struct gpio_chip *chip); +int gpiochip_sysfs_register(struct gpio_device *gdev); +void gpiochip_sysfs_unregister(struct gpio_device *gdev); #else -static inline int gpiochip_sysfs_register(struct gpio_chip *chip) +static inline int gpiochip_sysfs_register(struct gpio_device *gdev) { return 0; } -static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip) +static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) { } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index f3f1dbd43c9b..4db64ab534ef 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -24,7 +24,6 @@ struct gpio_device; * @label: for diagnostics * @gpiodev: the internal state holder, opaque struct * @parent: optional parent device providing the GPIOs - * @cdev: class device used by sysfs interface (may be NULL) * @owner: helps prevent removal of modules exporting active GPIOs * @data: per-instance data assigned by the driver * @request: optional hook for chip-specific activation, such as @@ -110,7 +109,6 @@ struct gpio_chip { const char *label; struct gpio_device *gpiodev; struct device *parent; - struct device *cdev; struct module *owner; void *data; -- cgit v1.2.3 From 1c3cdb186172ee3be24005a7ff0e849bc17b67b8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 9 Feb 2016 13:51:59 +0100 Subject: gpio: move descriptors into gpio_device We need gpio_device to hold the descriptors so that they can be lifecycled with the struct gpio_device held from userspace. Move the descriptor array into gpio_device. Also rename it from "desc" (singularis) to "descs" (pluralis) to reflect the fact that it is an array. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 2 +- drivers/gpio/gpiolib.c | 53 ++++++++++++++++++-------------------------- drivers/gpio/gpiolib.h | 4 +++- include/linux/gpio/driver.h | 2 -- 4 files changed, 26 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 94ba4bb8b4f8..de65633471af 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -765,7 +765,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) /* unregister gpiod class devices owned by sysfs */ for (i = 0; i < chip->ngpio; i++) { - desc = &chip->desc[i]; + desc = &chip->gpiodev->descs[i]; if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) gpiod_free(desc); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5763290f777c..f3fcd415a77b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -88,7 +88,7 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) if (gdev->chip->base <= gpio && gdev->chip->base + gdev->chip->ngpio > gpio) { spin_unlock_irqrestore(&gpio_lock, flags); - return &gdev->chip->desc[gpio - gdev->chip->base]; + return &gdev->descs[gpio - gdev->chip->base]; } } @@ -110,7 +110,7 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, if (hwnum >= chip->ngpio) return ERR_PTR(-EINVAL); - return &chip->desc[hwnum]; + return &chip->gpiodev->descs[hwnum]; } /** @@ -120,7 +120,7 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, */ int desc_to_gpio(const struct gpio_desc *desc) { - return desc->chip->base + (desc - &desc->chip->desc[0]); + return desc->chip->base + (desc - &desc->chip->gpiodev->descs[0]); } EXPORT_SYMBOL_GPL(desc_to_gpio); @@ -277,7 +277,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name) int i; for (i = 0; i != gdev->chip->ngpio; ++i) { - struct gpio_desc *gpio = &gdev->chip->desc[i]; + struct gpio_desc *gpio = &gdev->descs[i]; if (!gpio->name || !name) continue; @@ -320,7 +320,7 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) /* Then add all names to the GPIO descriptors */ for (i = 0; i != gc->ngpio; ++i) - gc->desc[i].name = gc->names[i]; + gc->gpiodev->descs[i].name = gc->names[i]; return 0; } @@ -431,7 +431,6 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) int status = 0; unsigned i; int base = chip->base; - struct gpio_desc *descs; struct gpio_device *gdev; /* @@ -470,9 +469,9 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) 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) { + gdev->descs = devm_kcalloc(&gdev->dev, chip->ngpio, + sizeof(gdev->descs[0]), GFP_KERNEL); + if (!gdev->descs) { status = -ENOMEM; goto err_free_gdev; } @@ -483,7 +482,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) if (chip->ngpio == 0) { chip_err(chip, "tried to insert a GPIO chip with zero lines\n"); status = -EINVAL; - goto err_free_descs; + goto err_free_gdev; } spin_lock_irqsave(&gpio_lock, flags); @@ -493,7 +492,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) if (base < 0) { status = base; spin_unlock_irqrestore(&gpio_lock, flags); - goto err_free_descs; + goto err_free_gdev; } chip->base = base; } @@ -501,11 +500,11 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) status = gpiodev_add_to_list(gdev); if (status) { spin_unlock_irqrestore(&gpio_lock, flags); - goto err_free_descs; + goto err_free_gdev; } for (i = 0; i < chip->ngpio; i++) { - struct gpio_desc *desc = &descs[i]; + struct gpio_desc *desc = &gdev->descs[i]; /* REVISIT: maybe a pointer to gpio_device is better */ desc->chip = chip; @@ -518,7 +517,6 @@ 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); @@ -583,9 +581,6 @@ err_remove_from_list: spin_lock_irqsave(&gpio_lock, flags); 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); @@ -608,7 +603,7 @@ void gpiochip_remove(struct gpio_chip *chip) struct gpio_device *gdev = chip->gpiodev; struct gpio_desc *desc; unsigned long flags; - unsigned id; + unsigned i; bool requested = false; /* Numb the device, cancelling all outstanding operations */ @@ -623,8 +618,8 @@ void gpiochip_remove(struct gpio_chip *chip) of_gpiochip_remove(chip); spin_lock_irqsave(&gpio_lock, flags); - for (id = 0; id < chip->ngpio; id++) { - desc = &chip->desc[id]; + for (i = 0; i < chip->ngpio; i++) { + desc = &gdev->descs[i]; desc->chip = NULL; if (test_bit(FLAG_REQUESTED, &desc->flags)) requested = true; @@ -635,10 +630,6 @@ void gpiochip_remove(struct gpio_chip *chip) 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 @@ -1250,7 +1241,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) if (offset >= chip->ngpio) return NULL; - desc = &chip->desc[offset]; + desc = &chip->gpiodev->descs[offset]; if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) return NULL; @@ -1837,14 +1828,14 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return -EINVAL; - if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) { + if (test_bit(FLAG_IS_OUT, &chip->gpiodev->descs[offset].flags)) { chip_err(chip, "%s: tried to flag a GPIO set as output for IRQ\n", __func__); return -EIO; } - set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + set_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); return 0; } EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq); @@ -1862,7 +1853,7 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return; - clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); } EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); @@ -2549,8 +2540,8 @@ static void gpiochip_free_hogs(struct gpio_chip *chip) int id; for (id = 0; id < chip->ngpio; id++) { - if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags)) - gpiochip_free_own_desc(&chip->desc[id]); + if (test_bit(FLAG_IS_HOGGED, &chip->gpiodev->descs[id].flags)) + gpiochip_free_own_desc(&chip->gpiodev->descs[id]); } } @@ -2673,7 +2664,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; unsigned gpio = chip->base; - struct gpio_desc *gdesc = &chip->desc[0]; + struct gpio_desc *gdesc = &chip->gpiodev->descs[0]; int is_out; int is_irq; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index c5a5b57463c7..39b8301c98b6 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -32,6 +32,7 @@ struct acpi_device; * @owner: helps prevent removal of modules exporting active GPIOs * @chip: pointer to the corresponding gpiochip, holding static * data for this device + * @descs: array of ngpio descriptors. * @list: links gpio_device:s together for traversal * * This state container holds most of the runtime variable data @@ -46,6 +47,7 @@ struct gpio_device { struct device *mockdev; struct module *owner; struct gpio_chip *chip; + struct gpio_desc *descs; struct list_head list; }; @@ -152,7 +154,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, */ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc) { - return desc - &desc->chip->desc[0]; + return desc - &desc->chip->gpiodev->descs[0]; } /* With descriptor prefix */ diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 4db64ab534ef..bfc842c2fc57 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -52,7 +52,6 @@ struct gpio_device; * get rid of the static GPIO number space in the long run. * @ngpio: the number of GPIOs handled by this controller; the last GPIO * handled is (base + ngpio - 1). - * @desc: array of ngpio descriptors. Private. * @names: if set, must be an array of strings to use as alternative * names for the GPIOs in this chip. Any entry in the array * may be NULL if there is no alias for the GPIO, however the @@ -140,7 +139,6 @@ struct gpio_chip { struct gpio_chip *chip); int base; u16 ngpio; - struct gpio_desc *desc; const char *const *names; bool can_sleep; bool irq_not_threaded; -- cgit v1.2.3 From 6cee3821e4e4bd6e6cdf0870b6c72d455460bd39 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 11 Feb 2016 20:16:45 +0100 Subject: gpio/pinctrl: sunxi: stop poking around in private vars This kind of hacks disturbs the refactoring of the gpiolib. The descriptor table belongs to the gpiolib, if we want to know something about something in it, use or define the proper accessor functions. Let's add this gpiochip_lins_is_irq() to do what the sunxi driver is trying at so we can privatize the descriptors properly. Cc: Maxime Ripard Cc: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 9 +++++++++ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 4 ++-- include/linux/gpio/driver.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f3fcd415a77b..ff8d55ad790c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1857,6 +1857,15 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); +bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return test_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_irq); + /** * gpiod_get_raw_value_cansleep() - return a gpio's raw value * @desc: gpio whose value will be returned diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 7a2465f5e71e..3e95bfe66a06 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -457,8 +457,8 @@ static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset) struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); u32 reg = sunxi_data_reg(offset); u8 index = sunxi_data_offset(offset); - u32 set_mux = pctl->desc->irq_read_needs_mux && - test_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + bool set_mux = pctl->desc->irq_read_needs_mux && + gpiochip_line_is_irq(chip, offset); u32 val; if (set_mux) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index bfc842c2fc57..41c6144c473b 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -208,6 +208,7 @@ extern struct gpio_chip *gpiochip_find(void *data, /* lock/unlock as IRQ */ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset); void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); +bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); /* get driver data */ static inline void *gpiochip_get_data(struct gpio_chip *chip) -- cgit v1.2.3 From 20ec3e39fc12cf3a331ee73eb01d52bddcdd2fa4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 11 Feb 2016 11:03:06 +0100 Subject: gpio: move the pin ranges into gpio_device Instead of keeping this reference to the pin ranges in the client driver-supplied gpio_chip, move it to the internal gpio_device as the drivers have no need to inspect this. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 18 +++++++++--------- drivers/gpio/gpiolib.c | 10 +++++----- drivers/gpio/gpiolib.h | 10 ++++++++++ include/linux/gpio/driver.h | 9 --------- 4 files changed, 24 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 540cbc88c7a2..682070d20f00 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -71,29 +71,29 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) * controller uses pin controller and the mapping is not contiguous the * offset might be different. */ -static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin) +static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_device *gdev, int pin) { struct gpio_pin_range *pin_range; /* If there are no ranges in this chip, use 1:1 mapping */ - if (list_empty(&chip->pin_ranges)) + if (list_empty(&gdev->pin_ranges)) return pin; - list_for_each_entry(pin_range, &chip->pin_ranges, node) { + list_for_each_entry(pin_range, &gdev->pin_ranges, node) { const struct pinctrl_gpio_range *range = &pin_range->range; int i; if (range->pins) { for (i = 0; i < range->npins; i++) { if (range->pins[i] == pin) - return range->base + i - chip->base; + return range->base + i - gdev->base; } } else { if (pin >= range->pin_base && pin < range->pin_base + range->npins) { unsigned gpio_base; - gpio_base = range->base - chip->base; + gpio_base = range->base - gdev->base; return gpio_base + pin - range->pin_base; } } @@ -102,7 +102,7 @@ static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin) return -EINVAL; } #else -static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, +static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_device *gdev, int pin) { return pin; @@ -134,7 +134,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin) if (!chip) return ERR_PTR(-EPROBE_DEFER); - offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + offset = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (offset < 0) return ERR_PTR(offset); @@ -202,7 +202,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, if (!handler) return AE_BAD_PARAMETER; - pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (pin < 0) return AE_BAD_PARAMETER; @@ -673,7 +673,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_desc *desc; bool found; - pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (pin < 0) { status = AE_BAD_PARAMETER; goto out; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 646dea4f96ff..28984bbc079c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -532,8 +532,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) spin_unlock_irqrestore(&gpio_lock, flags); #ifdef CONFIG_PINCTRL - /* FIXME: move pin ranges to gpio_device */ - INIT_LIST_HEAD(&chip->pin_ranges); + INIT_LIST_HEAD(&gdev->pin_ranges); #endif status = gpiochip_set_desc_names(chip); @@ -1036,7 +1035,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip, gpio_offset, gpio_offset + pin_range->range.npins - 1, pinctrl_dev_get_devname(pctldev), pin_group); - list_add_tail(&pin_range->node, &chip->pin_ranges); + list_add_tail(&pin_range->node, &gdev->pin_ranges); return 0; } @@ -1085,7 +1084,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, pinctl_name, pin_offset, pin_offset + npins - 1); - list_add_tail(&pin_range->node, &chip->pin_ranges); + list_add_tail(&pin_range->node, &gdev->pin_ranges); return 0; } @@ -1098,8 +1097,9 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); void gpiochip_remove_pin_ranges(struct gpio_chip *chip) { struct gpio_pin_range *pin_range, *tmp; + struct gpio_device *gdev = chip->gpiodev; - list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, &pin_range->range); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d154984c71d9..5a36908fd39d 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -55,6 +55,16 @@ struct gpio_device { int base; u16 ngpio; struct list_head list; + +#ifdef CONFIG_PINCTRL + /* + * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally + * describe the actual pin range which they serve in an SoC. This + * information would be used by pinctrl subsystem to configure + * corresponding pins for gpio usage. + */ + struct list_head pin_ranges; +#endif }; /** diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 41c6144c473b..e2a934ce3e64 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -181,15 +181,6 @@ struct gpio_chip { int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); #endif -#ifdef CONFIG_PINCTRL - /* - * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally - * describe the actual pin range which they serve in an SoC. This - * information would be used by pinctrl subsystem to configure - * corresponding pins for gpio usage. - */ - struct list_head pin_ranges; -#endif }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, -- cgit v1.2.3 From 43c54ecade400cf6ca8203f960b525fbe5b73a13 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 11 Feb 2016 11:37:48 +0100 Subject: gpio: move the subdriver data pointer into gpio_device We move to manage this pointer under gpiolib control rather than leave it in the subdevice's gpio_chip. We can not NULL it after gpiochip_remove so at to keep things tight. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 17 +++++++++++++++-- drivers/gpio/gpiolib.h | 2 ++ include/linux/gpio/driver.h | 7 +------ 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 28984bbc079c..aa4a60e19339 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -480,8 +480,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) goto err_free_gdev; } gdev->ngpio = chip->ngpio; - /* FIXME: move driver data into gpio_device dev_set_drvdata() */ - chip->data = data; + gdev->data = data; spin_lock_irqsave(&gpio_lock, flags); @@ -602,6 +601,15 @@ err_free_gdev: } EXPORT_SYMBOL_GPL(gpiochip_add_data); +/** + * gpiochip_get_data() - get per-subdriver data for the chip + */ +void *gpiochip_get_data(struct gpio_chip *chip) +{ + return chip->gpiodev->data; +} +EXPORT_SYMBOL_GPL(gpiochip_get_data); + /** * gpiochip_remove() - unregister a gpio_chip * @chip: the chip to unregister @@ -626,6 +634,11 @@ void gpiochip_remove(struct gpio_chip *chip) gpiochip_remove_pin_ranges(chip); gpiochip_free_hogs(chip); of_gpiochip_remove(chip); + /* + * We accept no more calls into the driver from this point, so + * NULL the driver data pointer + */ + gdev->data = NULL; spin_lock_irqsave(&gpio_lock, flags); for (i = 0; i < gdev->ngpio; i++) { diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 5a36908fd39d..ddbe409ad48f 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -37,6 +37,7 @@ struct acpi_device; * of the @descs array. * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned * at device creation time. + * @data: per-instance data assigned by the driver * @list: links gpio_device:s together for traversal * * This state container holds most of the runtime variable data @@ -54,6 +55,7 @@ struct gpio_device { struct gpio_desc *descs; int base; u16 ngpio; + void *data; struct list_head list; #ifdef CONFIG_PINCTRL diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index e2a934ce3e64..b92ab9efdb69 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -25,7 +25,6 @@ struct gpio_device; * @gpiodev: the internal state holder, opaque struct * @parent: optional parent device providing the GPIOs * @owner: helps prevent removal of modules exporting active GPIOs - * @data: per-instance data assigned by the driver * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as @@ -109,7 +108,6 @@ struct gpio_chip { struct gpio_device *gpiodev; struct device *parent; struct module *owner; - void *data; int (*request)(struct gpio_chip *chip, unsigned offset); @@ -202,10 +200,7 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); /* get driver data */ -static inline void *gpiochip_get_data(struct gpio_chip *chip) -{ - return chip->data; -} +void *gpiochip_get_data(struct gpio_chip *chip); struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); -- cgit v1.2.3 From 143b65d677a59764e438d457bf2510b3fa5b90f8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 16 Feb 2016 15:41:42 +0100 Subject: gpio: create an API to detect open drain/source on lines My left hand merges code to privatize the descriptor handling while my right hand merges drivers that poke around and disrespect with the same gpiolib internals. So let's expose the proper APIs for drivers to ask the gpiolib core if a line is marked as open drain or open source and get some order around things so this driver compiles again. Reported-by: Stephen Rothwell Cc: Nicolas Saenz Julienne Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tps65218.c | 9 ++++----- drivers/gpio/gpiolib.c | 18 ++++++++++++++++++ include/linux/gpio/driver.h | 4 ++++ 3 files changed, 26 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 7b02f7be9bc9..9eb1a5ab2d95 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -71,17 +71,16 @@ static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) { struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); struct tps65218 *tps65218 = tps65218_gpio->tps65218; - unsigned long flags = gc->desc[offset].flags; int ret; - if (flags & FLAG_OPEN_SOURCE) { + if (gpiochip_line_is_open_source(gc, offset)) { dev_err(gc->parent, "can't work as open source\n"); return -EINVAL; } switch (offset) { case 0: - if (!(flags & FLAG_OPEN_DRAIN)) { + if (!gpiochip_line_is_open_drain(gc, offset)) { dev_err(gc->parent, "GPO1 works only as open drain\n"); return -EINVAL; } @@ -103,7 +102,7 @@ static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) break; case 1: /* GP02 is push-pull by default, can be set as open drain. */ - if (flags & FLAG_OPEN_DRAIN) { + if (gpiochip_line_is_open_drain(gc, offset)) { ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1, TPS65218_CONFIG1_GPO2_BUF, @@ -122,7 +121,7 @@ static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) break; case 2: - if (!(flags & FLAG_OPEN_DRAIN)) { + if (!gpiochip_line_is_open_drain(gc, offset)) { dev_err(gc->parent, "GPO3 works only as open drain\n"); return -EINVAL; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index aa4a60e19339..d8511cd68e7b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1901,6 +1901,24 @@ bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_line_is_irq); +bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return test_bit(FLAG_OPEN_DRAIN, &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); + +bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return test_bit(FLAG_OPEN_SOURCE, &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); + /** * gpiod_get_raw_value_cansleep() - return a gpio's raw value * @desc: gpio whose value will be returned diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index b92ab9efdb69..ff96d0f9fceb 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -199,6 +199,10 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset); void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); +/* Line status inquiry for drivers */ +bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset); +bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset); + /* get driver data */ void *gpiochip_get_data(struct gpio_chip *chip); -- cgit v1.2.3 From ee2204a37957daed80418ea8ffc4f5c3146fb8e7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 Feb 2016 16:40:35 +0100 Subject: gpio: remove broken irq_to_gpio() interface gpiolib has removed the irq_to_gpio() API several years ago, but the global header still provided a non-working stub. To prevent new users of this broken function from showing up, let's remove the stubs as well. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij --- include/linux/gpio.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gpio.h b/include/linux/gpio.h index d12b5d566e4b..6fc1c9e74854 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -70,11 +70,6 @@ static inline int gpio_to_irq(unsigned int gpio) return __gpio_to_irq(gpio); } -static inline int irq_to_gpio(unsigned int irq) -{ - return -EINVAL; -} - #endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */ /* CONFIG_GPIOLIB: bindings for managed devices that want to request gpios */ @@ -222,13 +217,6 @@ static inline void gpiochip_unlock_as_irq(struct gpio_chip *chip, WARN_ON(1); } -static inline int irq_to_gpio(unsigned irq) -{ - /* irq can never have been returned from gpio_to_irq() */ - WARN_ON(1); - return -EINVAL; -} - static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int gpio_offset, unsigned int pin_offset, -- cgit v1.2.3 From df4878e969ccc047da45d2cd3af5d08031da1593 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 12 Feb 2016 14:48:23 +0100 Subject: gpio: store reflect the label to userspace The gpio_chip label is useful for userspace to understand what kind of GPIO chip it is dealing with. Let's store a copy of this label in the gpio_device, add it to the struct passed to userspace for GPIO_GET_CHIPINFO_IOCTL and modify lsgpio to show it. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 13 +++++++++++++ drivers/gpio/gpiolib.h | 3 +++ include/linux/gpio/driver.h | 3 ++- include/uapi/linux/gpio.h | 2 ++ tools/gpio/lsgpio.c | 4 ++-- 5 files changed, 22 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 59f0045c5950..797c790aa750 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -342,6 +342,9 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) strncpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name)); chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; + strncpy(chipinfo.label, gdev->label, + sizeof(chipinfo.label)); + chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; chipinfo.lines = gdev->ngpio; if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) return -EFAULT; @@ -479,6 +482,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) status = -EINVAL; goto err_free_gdev; } + + if (chip->label) + gdev->label = devm_kstrdup(&gdev->dev, chip->label, GFP_KERNEL); + else + gdev->label = devm_kstrdup(&gdev->dev, "unknown", GFP_KERNEL); + if (!gdev->label) { + status = -ENOMEM; + goto err_free_gdev; + } + gdev->ngpio = chip->ngpio; gdev->data = data; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index ddbe409ad48f..e30e5fdb1214 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -37,6 +37,8 @@ struct acpi_device; * of the @descs array. * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned * at device creation time. + * @label: a descriptive name for the GPIO device, such as the part number + * or name of the IP component in a System on Chip. * @data: per-instance data assigned by the driver * @list: links gpio_device:s together for traversal * @@ -55,6 +57,7 @@ struct gpio_device { struct gpio_desc *descs; int base; u16 ngpio; + char *label; void *data; struct list_head list; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index ff96d0f9fceb..639607658ed8 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -21,7 +21,8 @@ struct gpio_device; /** * struct gpio_chip - abstract a GPIO controller - * @label: for diagnostics + * @label: a functional name for the GPIO device, such as a part + * number or the name of the SoC IP-block implementing it. * @gpiodev: the internal state holder, opaque struct * @parent: optional parent device providing the GPIOs * @owner: helps prevent removal of modules exporting active GPIOs diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 3188a87bdaa0..3f93e1bcd3dd 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -16,10 +16,12 @@ /** * struct gpiochip_info - Information about a certain GPIO chip * @name: the name of this GPIO chip + * @label: a functional name for this GPIO chip * @lines: number of GPIO lines on this chip */ struct gpiochip_info { char name[32]; + char label[32]; __u32 lines; }; diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 4cfe29da279b..692233f561fb 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -54,8 +54,8 @@ int list_device(const char *device_name) goto free_chrdev_name; } - fprintf(stdout, "GPIO chip: %s, %u GPIO lines\n", - cinfo.name, cinfo.lines); + fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", + cinfo.name, cinfo.label, cinfo.lines); if (close(fd) == -1) { ret = -errno; -- cgit v1.2.3 From 2307f3aab887bc43e0f262d298977fd9c6b9eee3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 20 Feb 2016 12:53:31 +0100 Subject: Revert "gpio: remove broken irq_to_gpio() interface" This reverts commit ee2204a37957daed80418ea8ffc4f5c3146fb8e7. --- include/linux/gpio.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 6fc1c9e74854..d12b5d566e4b 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -70,6 +70,11 @@ static inline int gpio_to_irq(unsigned int gpio) return __gpio_to_irq(gpio); } +static inline int irq_to_gpio(unsigned int irq) +{ + return -EINVAL; +} + #endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */ /* CONFIG_GPIOLIB: bindings for managed devices that want to request gpios */ @@ -217,6 +222,13 @@ static inline void gpiochip_unlock_as_irq(struct gpio_chip *chip, WARN_ON(1); } +static inline int irq_to_gpio(unsigned irq) +{ + /* irq can never have been returned from gpio_to_irq() */ + WARN_ON(1); + return -EINVAL; +} + static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int gpio_offset, unsigned int pin_offset, -- cgit v1.2.3