mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-22 17:01:14 +00:00
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUu4HCAAoJEEEQszewGV1zx9wP/3oREk/os8ZvWbbXnsLiJ2lf v7Oa0kZtIqS5zhE6MOmjEuMoKJBvIjv89n3xwydXfWN9SImhJzjYkhkVYMs8zysh li0AgmVzYHM3+/cuX/qtyV57WhP5WNW3B4j5WXdDQEDMvPhH3Rdbgke2cgVsuAfm Z+yddAwy18iqPaCy5BKc4JbHH5NWF7ntkGD5Loryt0VPZeJFiXvf6Y1qCpxRXe0k acj03QOBiwGJFCYGyXOJn8wjjuM/vLJ+XFchF144EUR6ECKYoMbnn+JjQURaWYrq ubc+B0A5fwsmWBwKcvRfPfgDwwnm3BNFK0I1R/ZvRJWRCwCxsuUmW2gnpi9PlD/G iQ0aCudrqBQfOLBRkDrtSMpVnD3A6pxRWx7cHAe24mx6ORNXs1EpmSgjA2mk08LL hr1w9tp0L4huGQ/5b9P7S6Oz+KTyH+OBQU6VKxBxJ24raDVigkSXe9YGXlbupruB B7JpgEdzvHp1YcBEYB8Bu+p89/NdFN4VdoSwnLKttFaPxIX+i1J4PyZaGNIhB8HI flIapWbbcQ4jJA9Ym7r7Vc/i3ATBoq+znq/VPNNlfPu767GLukyln3KvFHpBJVd4 JR3zCuWwlsgz9uDPSwl1h6gIeCFiXGvxk9LWBcbjTdDKZzE4i0rqpfPU36WSFeyP NKUck+bcPcqKuo0EoDU6 =ekXq -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
b62a9c2058
5 changed files with 91 additions and 70 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
for (id = 0; id < chip->ngpio; id++) {
|
||||
struct gpio_desc *desc = &descs[id];
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in a new issue