diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-01-19 05:03:13 +1200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-01-19 05:03:13 +1200 |
commit | b62a9c2058ee4c60677422fb7a8d6129320a4251 (patch) | |
tree | cb3d3f19f6a4d7d7664be4437ebf6876f44f5bfe | |
parent | 66893885bbf95b6c9030d97804cb678a70804edf (diff) | |
parent | 7b8792bbdffdff3abda704f89c6a45ea97afdc62 (diff) | |
download | linux-b62a9c2058ee4c60677422fb7a8d6129320a4251.tar.bz2 |
Merge tag 'gpio-v3.19-4' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO fixes from Linus Walleij:
"Here is a set of fixes that mainly appeared when Johan Hovold started
exercising the removal path of the GPIO library, dealing with
hotplugging of GPIO controllers. Details from tag:
A slew of fixes dealing with some irritating bugs (non-regressions)
that have been around forever in the GPIO subsystem, most of them also
tagged for stable:
- A large slew of fixes from Johan Hovold who is finally testing and
reviewing the removal path of the GPIO drivers.
- Fix of_get_named_gpiod_flags() so it works as expected.
- Fix an IRQ handling bug in the crystalcove driver"
* tag 'gpio-v3.19-4' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
gpiolib: of: Correct error handling in of_get_named_gpiod_flags
gpio: sysfs: fix gpio attribute-creation race
gpio: sysfs: fix gpio device-attribute leak
gpio: sysfs: fix gpio-chip device-attribute leak
gpio: unregister gpiochip device before removing it
gpio: fix sleep-while-atomic in gpiochip_remove
gpio: fix memory leak and sleep-while-atomic
gpio: clean up gpiochip_add error handling
gpio: fix gpio-chip list corruption
gpio: fix memory and reference leaks in gpiochip_add error path
gpio: crystalcove: use handle_nested_irq
-rw-r--r-- | drivers/gpio/gpio-crystalcove.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 10 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 92 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 58 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.h | 1 |
5 files changed, 92 insertions, 71 deletions
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 55d4803d71b0..3d9e08f7e823 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -272,7 +272,7 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) { if (pending & BIT(gpio)) { virq = irq_find_mapping(cg->chip.irqdomain, gpio); - generic_handle_irq(virq); + handle_nested_irq(virq); } } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 604dbe60bdee..08261f2b3a82 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -45,8 +45,14 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) return false; ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); - if (ret < 0) - return false; + if (ret < 0) { + /* We've found the gpio chip, but the translation failed. + * Return true to stop looking and return the translation + * error via out_gpio + */ + gg_data->out_gpio = ERR_PTR(ret); + return true; + } gg_data->out_gpio = gpiochip_get_desc(gc, ret); return true; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 2ac1800b58bb..f62aa115d79a 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -128,7 +128,7 @@ static ssize_t gpio_value_store(struct device *dev, return status; } -static const DEVICE_ATTR(value, 0644, +static DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) @@ -353,17 +353,46 @@ static ssize_t gpio_active_low_store(struct device *dev, return status ? : size; } -static const DEVICE_ATTR(active_low, 0644, +static DEVICE_ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store); -static const struct attribute *gpio_attrs[] = { +static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gpio_desc *desc = dev_get_drvdata(dev); + umode_t mode = attr->mode; + bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags); + + if (attr == &dev_attr_direction.attr) { + if (!show_direction) + mode = 0; + } else if (attr == &dev_attr_edge.attr) { + if (gpiod_to_irq(desc) < 0) + mode = 0; + if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) + mode = 0; + } + + return mode; +} + +static struct attribute *gpio_attrs[] = { + &dev_attr_direction.attr, + &dev_attr_edge.attr, &dev_attr_value.attr, &dev_attr_active_low.attr, NULL, }; -static const struct attribute_group gpio_attr_group = { - .attrs = (struct attribute **) gpio_attrs, +static const struct attribute_group gpio_group = { + .attrs = gpio_attrs, + .is_visible = gpio_is_visible, +}; + +static const struct attribute_group *gpio_groups[] = { + &gpio_group, + NULL }; /* @@ -400,16 +429,13 @@ static ssize_t chip_ngpio_show(struct device *dev, } static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); -static const struct attribute *gpiochip_attrs[] = { +static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, &dev_attr_ngpio.attr, NULL, }; - -static const struct attribute_group gpiochip_attr_group = { - .attrs = (struct attribute **) gpiochip_attrs, -}; +ATTRIBUTE_GROUPS(gpiochip); /* * /sys/class/gpio/export ... write-only @@ -556,45 +582,30 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto fail_unlock; } - if (!desc->chip->direction_input || !desc->chip->direction_output) - direction_may_change = false; + if (desc->chip->direction_input && desc->chip->direction_output && + direction_may_change) { + set_bit(FLAG_SYSFS_DIR, &desc->flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); if (desc->chip->names && desc->chip->names[offset]) ioname = desc->chip->names[offset]; - dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), - desc, ioname ? ioname : "gpio%u", - desc_to_gpio(desc)); + dev = device_create_with_groups(&gpio_class, desc->chip->dev, + MKDEV(0, 0), desc, gpio_groups, + ioname ? ioname : "gpio%u", + desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto fail_unlock; } - status = sysfs_create_group(&dev->kobj, &gpio_attr_group); - if (status) - goto fail_unregister_device; - - if (direction_may_change) { - status = device_create_file(dev, &dev_attr_direction); - if (status) - goto fail_unregister_device; - } - - if (gpiod_to_irq(desc) >= 0 && (direction_may_change || - !test_bit(FLAG_IS_OUT, &desc->flags))) { - status = device_create_file(dev, &dev_attr_edge); - if (status) - goto fail_unregister_device; - } - set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; -fail_unregister_device: - device_unregister(dev); fail_unlock: mutex_unlock(&sysfs_lock); gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -718,6 +729,7 @@ void gpiod_unexport(struct gpio_desc *desc) dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev) { gpio_setup_irq(desc, dev, 0); + clear_bit(FLAG_SYSFS_DIR, &desc->flags); clear_bit(FLAG_EXPORT, &desc->flags); } else status = -ENODEV; @@ -750,13 +762,13 @@ int gpiochip_export(struct gpio_chip *chip) /* use chip->base for the ID; it's already known to be unique */ mutex_lock(&sysfs_lock); - dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, - "gpiochip%d", chip->base); - if (!IS_ERR(dev)) { - status = sysfs_create_group(&dev->kobj, - &gpiochip_attr_group); - } else + dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0), + chip, gpiochip_groups, + "gpiochip%d", chip->base); + if (IS_ERR(dev)) status = PTR_ERR(dev); + else + status = 0; chip->exported = (status == 0); mutex_unlock(&sysfs_lock); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 487afe6f22fc..568aa2b6bdb0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -248,29 +248,30 @@ int gpiochip_add(struct gpio_chip *chip) base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; - goto unlock; + spin_unlock_irqrestore(&gpio_lock, flags); + goto err_free_descs; } chip->base = base; } status = gpiochip_add_to_list(chip); + if (status) { + spin_unlock_irqrestore(&gpio_lock, flags); + goto err_free_descs; + } - if (status == 0) { - for (id = 0; id < chip->ngpio; id++) { - struct gpio_desc *desc = &descs[id]; - desc->chip = chip; - - /* REVISIT: most hardware initializes GPIOs as - * inputs (often with pullups enabled) so power - * usage is minimized. Linux code should set the - * gpio direction first thing; but until it does, - * and in case chip->get_direction is not set, - * we may expose the wrong direction in sysfs. - */ - desc->flags = !chip->direction_input - ? (1 << FLAG_IS_OUT) - : 0; - } + for (id = 0; id < chip->ngpio; id++) { + struct gpio_desc *desc = &descs[id]; + + desc->chip = chip; + + /* REVISIT: most hardware initializes GPIOs as inputs (often + * with pullups enabled) so power usage is minimized. Linux + * code should set the gpio direction first thing; but until + * it does, and in case chip->get_direction is not set, we may + * expose the wrong direction in sysfs. + */ + desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } chip->desc = descs; @@ -284,12 +285,9 @@ int gpiochip_add(struct gpio_chip *chip) of_gpiochip_add(chip); acpi_gpiochip_add(chip); - if (status) - goto fail; - status = gpiochip_export(chip); if (status) - goto fail; + goto err_remove_chip; pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__, chip->base, chip->base + chip->ngpio - 1, @@ -297,11 +295,15 @@ int gpiochip_add(struct gpio_chip *chip) return 0; -unlock: +err_remove_chip: + acpi_gpiochip_remove(chip); + of_gpiochip_remove(chip); + spin_lock_irqsave(&gpio_lock, flags); + list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); -fail: - kfree(descs); chip->desc = NULL; +err_free_descs: + kfree(descs); /* failures here can mean systems won't boot... */ pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__, @@ -325,14 +327,15 @@ void gpiochip_remove(struct gpio_chip *chip) unsigned long flags; unsigned id; - acpi_gpiochip_remove(chip); - - spin_lock_irqsave(&gpio_lock, flags); + gpiochip_unexport(chip); gpiochip_irqchip_remove(chip); + + acpi_gpiochip_remove(chip); gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); + spin_lock_irqsave(&gpio_lock, flags); for (id = 0; id < chip->ngpio; id++) { if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); @@ -342,7 +345,6 @@ void gpiochip_remove(struct gpio_chip *chip) list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); - gpiochip_unexport(chip); kfree(chip->desc); chip->desc = NULL; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index e3a52113a541..550a5eafbd38 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -77,6 +77,7 @@ struct gpio_desc { #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ +#define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */ #define ID_SHIFT 16 /* add new flags before this one */ |