From e733a2fb8cbcff0747108cb529ffb4e4a00465ac Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Mon, 12 Jan 2015 10:09:32 +0800 Subject: [PATCH 01/11] gpio: crystalcove: use handle_nested_irq The CrystalCove GPIO chip has can_sleep set so its demultiplexed irqs will have IRQ_NESTED_THREAD flag set, thus we should use the nested version handle_nested_irq in CrystalCove's irq handler instead of handle_generic_irq, or the following warning will be hit and the functionality is lost: [ 4089.639554] Hardware name: ASUSTeK COMPUTER INC. T100TA/T100TA, BIOS T100TA.313 08/13/2014 [ 4089.639564] 00000002 00000000 c24fbdf4 c16e0257 c24fbe38 c24fbe28 c105390c c18ec480 [ 4089.639596] c24fbe54 00000048 c18f8e3b 00000295 c10a60fc 00000295 c10a60fc f4464540 [ 4089.639626] f446459c c278ad40 c24fbe40 c1053974 00000009 c24fbe38 c18ec480 c24fbe54 [ 4089.639656] Call Trace: [ 4089.639685] [] dump_stack+0x41/0x52 [ 4089.639707] [] warn_slowpath_common+0x8c/0xc0 [ 4089.639727] [] ? irq_nested_primary_handler+0x2c/0x30 [ 4089.639744] [] ? irq_nested_primary_handler+0x2c/0x30 [ 4089.639763] [] warn_slowpath_fmt+0x34/0x40 [ 4089.639781] [] irq_nested_primary_handler+0x2c/0x30 [ 4089.639800] [] handle_irq_event_percpu+0x76/0x190 [ 4089.639818] [] ? regmap_format_10_14_write+0x30/0x30 [ 4089.639836] [] ? _regmap_bus_raw_write+0x4c/0x70 [ 4089.639854] [] handle_irq_event+0x31/0x50 [ 4089.639872] [] handle_simple_irq+0x4b/0x70 [ 4089.639889] [] generic_handle_irq+0x24/0x40 [ 4089.639908] [] crystalcove_gpio_irq_handler+0xa7/0xc0 [ 4089.639927] [] handle_nested_irq+0x77/0x190 [ 4089.639947] [] regmap_irq_thread+0x1b1/0x360 [ 4089.639966] [] irq_thread_fn+0x18/0x30 [ 4089.639983] [] irq_thread+0xf6/0x110 [ 4089.640001] [] ? irq_finalize_oneshot.part.30+0x1b0/0x1b0 [ 4089.640019] [] ? irq_forced_thread_fn+0x50/0x50 [ 4089.640037] [] ? irq_thread_check_affinity+0xc0/0xc0 [ 4089.640054] [] kthread+0xa9/0xc0 [ 4089.640074] [] ret_from_kernel_thread+0x21/0x30 [ 4089.640091] [] ? kthread_create_on_node+0x110/0x110 [ 4089.640105] ---[ end trace dca7946ad31eba7d ]--- Buglink: https://bugzilla.kernel.org/show_bug.cgi?id=90521 Reported-and-tested-by: Brian Loften Cc: Stable Signed-off-by: Aaron Lu Signed-off-by: Linus Walleij --- drivers/gpio/gpio-crystalcove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } } From 5539b3c938d64a60cb1fc442ac3ce9263d52de0c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 12 Jan 2015 17:12:24 +0100 Subject: [PATCH 02/11] gpio: fix memory and reference leaks in gpiochip_add error path Memory allocated and references taken by of_gpiochip_add and acpi_gpiochip_add were never released on errors in gpiochip_add (e.g. failure to find free gpio range). Fixes: 391c970c0dd1 ("of/gpio: add default of_xlate function if device has a node pointer") Fixes: 664e3e5ac64c ("gpio / ACPI: register to ACPI events automatically") Cc: stable Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 487afe6f22fc..89c59f5f1924 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -277,6 +277,9 @@ int gpiochip_add(struct gpio_chip *chip) spin_unlock_irqrestore(&gpio_lock, flags); + if (status) + goto fail; + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif @@ -284,12 +287,12 @@ 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) + if (status) { + acpi_gpiochip_remove(chip); + of_gpiochip_remove(chip); goto fail; + } pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__, chip->base, chip->base + chip->ngpio - 1, From 225fce83cb72e1bffb712a33ce47c210c770f8ab Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 12 Jan 2015 17:12:25 +0100 Subject: [PATCH 03/11] gpio: fix gpio-chip list corruption Fix potential corruption of gpio-chip list due to failure to remove the chip from the list before returning in gpiochip_add error path. The chip could be long gone when the global list is next traversed, something which could lead to a null-pointer dereference. In the best case (chip not deallocated) we are just leaking the gpio range. Fixes: 14e85c0e69d5 ("gpio: remove gpio_descs global array") Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 89c59f5f1924..ac5944b4e4d8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -248,7 +248,8 @@ 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; } @@ -288,11 +289,8 @@ int gpiochip_add(struct gpio_chip *chip) acpi_gpiochip_add(chip); status = gpiochip_export(chip); - if (status) { - acpi_gpiochip_remove(chip); - of_gpiochip_remove(chip); - goto fail; - } + if (status) + goto err_remove_chip; pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__, chip->base, chip->base + chip->ngpio - 1, @@ -300,9 +298,14 @@ 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: +err_free_descs: kfree(descs); chip->desc = NULL; From 05aa52033494a13178fb550660aea87cd8a99cfd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 12 Jan 2015 17:12:26 +0100 Subject: [PATCH 04/11] gpio: clean up gpiochip_add error handling Clean up gpiochip_add error handling. Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ac5944b4e4d8..4efb92ca3463 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -255,32 +255,29 @@ int gpiochip_add(struct gpio_chip *chip) } 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; spin_unlock_irqrestore(&gpio_lock, flags); - if (status) - goto fail; - #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif @@ -304,10 +301,9 @@ int gpiochip_add(struct gpio_chip *chip) spin_lock_irqsave(&gpio_lock, flags); list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); -fail: + chip->desc = NULL; err_free_descs: kfree(descs); - chip->desc = NULL; /* failures here can mean systems won't boot... */ pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__, From 00acc3dc248063f982cfacfbe5e78c0d6797ffef Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 12 Jan 2015 17:12:27 +0100 Subject: [PATCH 05/11] gpio: fix memory leak and sleep-while-atomic Fix memory leak and sleep-while-atomic in gpiochip_remove. The memory leak was introduced by afa82fab5e13 ("gpio / ACPI: Move event handling registration to gpiolib irqchip helpers") that moved the release of acpi interrupt resources to gpiochip_irqchip_remove, but by then the resources are no longer accessible as the acpi_gpio_chip has already been freed by acpi_gpiochip_remove. Note that this also fixes a few potential sleep-while-atomics, which has been around since 1425052097b5 ("gpio: add IRQ chip helpers in gpiolib") when the call to gpiochip_irqchip_remove while holding a spinlock was added (a couple of irq-domain paths can end up grabbing mutexes). Fixes: afa82fab5e13 ("gpio / ACPI: Move event handling registration to gpiolib irqchip helpers") Fixes: 1425052097b5 ("gpio: add IRQ chip helpers in gpiolib") Cc: stable Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4efb92ca3463..0f8173051edc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -327,11 +327,12 @@ void gpiochip_remove(struct gpio_chip *chip) unsigned long flags; unsigned id; + gpiochip_irqchip_remove(chip); + acpi_gpiochip_remove(chip); spin_lock_irqsave(&gpio_lock, flags); - gpiochip_irqchip_remove(chip); gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); From 6798acaa0138d8b12f1c54402ebcb66fea3deb03 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 12 Jan 2015 17:12:28 +0100 Subject: [PATCH 06/11] gpio: fix sleep-while-atomic in gpiochip_remove Move direct and indirect calls to gpiochip_remove_pin_ranges outside of spin lock as they can end up taking a mutex in pinctrl_remove_gpio_range. Note that the pin ranges are already added outside of the lock. Fixes: 9ef0d6f7628b ("gpiolib: call pin removal in chip removal function") Fixes: f23f1516b675 ("gpiolib: provide provision to register pin ranges") Cc: stable Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0f8173051edc..37f919dc2cb4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -330,12 +330,10 @@ void gpiochip_remove(struct gpio_chip *chip) gpiochip_irqchip_remove(chip); acpi_gpiochip_remove(chip); - - spin_lock_irqsave(&gpio_lock, flags); - 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"); From 01cca93a9491ed95992523ff7e79dd9bfcdea8e0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 12 Jan 2015 17:12:29 +0100 Subject: [PATCH 07/11] gpio: unregister gpiochip device before removing it Unregister gpiochip device (used to export information through sysfs) before removing it internally. This way removal will reverse addition. Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 37f919dc2cb4..568aa2b6bdb0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -327,6 +327,8 @@ void gpiochip_remove(struct gpio_chip *chip) unsigned long flags; unsigned id; + gpiochip_unexport(chip); + gpiochip_irqchip_remove(chip); acpi_gpiochip_remove(chip); @@ -343,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; From 121b6a79955a3a3fd7bbb9b8cb88d5b9dad6283d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Jan 2015 13:00:04 +0100 Subject: [PATCH 08/11] gpio: sysfs: fix gpio-chip device-attribute leak The gpio-chip device attributes were never destroyed when the device was removed. Fix by using device_create_with_groups() to create the device attributes of the chip class device. Note that this also fixes the attribute-creation race with userspace. Fixes: d8f388d8dc8d ("gpio: sysfs interface") Cc: stable # v2.6.27+ Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 2ac1800b58bb..33cf4bd0ed2d 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -400,16 +400,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 @@ -750,13 +747,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); From 0915e6feb38de8d3601819992a5bd050201a56fa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Jan 2015 13:00:05 +0100 Subject: [PATCH 09/11] gpio: sysfs: fix gpio device-attribute leak The gpio device attributes were never destroyed when the gpio was unexported (or on export failures). Use device_create_with_groups() to create the default device attributes of the gpio class device. Note that this also fixes the attribute-creation race with userspace for these attributes. Remove contingent attributes in export error path and on unexport. Fixes: d8f388d8dc8d ("gpio: sysfs interface") Cc: stable # v2.6.27+ Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 33cf4bd0ed2d..fd4d9423f6e6 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,18 +353,15 @@ 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 struct attribute *gpio_attrs[] = { &dev_attr_value.attr, &dev_attr_active_low.attr, NULL, }; - -static const struct attribute_group gpio_attr_group = { - .attrs = (struct attribute **) gpio_attrs, -}; +ATTRIBUTE_GROUPS(gpio); /* * /sys/class/gpio/gpiochipN/ @@ -561,18 +558,15 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) 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) @@ -583,13 +577,15 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) !test_bit(FLAG_IS_OUT, &desc->flags))) { status = device_create_file(dev, &dev_attr_edge); if (status) - goto fail_unregister_device; + goto fail_remove_attr_direction; } set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; +fail_remove_attr_direction: + device_remove_file(dev, &dev_attr_direction); fail_unregister_device: device_unregister(dev); fail_unlock: @@ -723,6 +719,8 @@ void gpiod_unexport(struct gpio_desc *desc) mutex_unlock(&sysfs_lock); if (dev) { + device_remove_file(dev, &dev_attr_edge); + device_remove_file(dev, &dev_attr_direction); device_unregister(dev); put_device(dev); } From ebbeba120ab2ec6ac5f3afc1425ec6ff0b77ad6f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Jan 2015 13:00:06 +0100 Subject: [PATCH 10/11] gpio: sysfs: fix gpio attribute-creation race Fix attribute-creation race with userspace by using the default group to create also the contingent gpio device attributes. Fixes: d8f388d8dc8d ("gpio: sysfs interface") Signed-off-by: Johan Hovold Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 61 +++++++++++++++++++++++------------- drivers/gpio/gpiolib.h | 1 + 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fd4d9423f6e6..f62aa115d79a 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -356,12 +356,44 @@ static ssize_t gpio_active_low_store(struct device *dev, static DEVICE_ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store); +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, }; -ATTRIBUTE_GROUPS(gpio); + +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 +}; /* * /sys/class/gpio/gpiochipN/ @@ -550,8 +582,11 @@ 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); @@ -567,27 +602,10 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto fail_unlock; } - 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_remove_attr_direction; - } - set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; -fail_remove_attr_direction: - device_remove_file(dev, &dev_attr_direction); -fail_unregister_device: - device_unregister(dev); fail_unlock: mutex_unlock(&sysfs_lock); gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -711,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; @@ -719,8 +738,6 @@ void gpiod_unexport(struct gpio_desc *desc) mutex_unlock(&sysfs_lock); if (dev) { - device_remove_file(dev, &dev_attr_edge); - device_remove_file(dev, &dev_attr_direction); device_unregister(dev); put_device(dev); } 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 */ From 7b8792bbdffdff3abda704f89c6a45ea97afdc62 Mon Sep 17 00:00:00 2001 From: Hans Holmberg Date: Fri, 9 Jan 2015 09:40:43 +0100 Subject: [PATCH 11/11] gpiolib: of: Correct error handling in of_get_named_gpiod_flags of_get_named_gpiod_flags fails with -EPROBE_DEFER in cases where the gpio chip is available and the GPIO translation fails. This causes drivers to be re-probed erroneusly, and hides the real problem(i.e. the GPIO number being out of range). Cc: Stable Signed-off-by: Hans Holmberg Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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;