diff options
Diffstat (limited to 'drivers/gpio/gpiolib-sysfs.c')
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 182 |
1 files changed, 79 insertions, 103 deletions
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 17ed229412af..1acfa43bf1ab 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -21,6 +21,8 @@ #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> +#include <uapi/linux/gpio.h> + #include "gpiolib.h" #include "gpiolib-sysfs.h" @@ -77,12 +79,10 @@ static ssize_t direction_show(struct device *dev, struct gpio_desc *desc = data->desc; int value; - mutex_lock(&data->mutex); - - gpiod_get_direction(desc); - value = !!test_bit(FLAG_IS_OUT, &desc->flags); - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) { + gpiod_get_direction(desc); + value = !!test_bit(FLAG_IS_OUT, &desc->flags); + } return sysfs_emit(buf, "%s\n", value ? "out" : "in"); } @@ -94,7 +94,7 @@ static ssize_t direction_store(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); @@ -105,8 +105,6 @@ static ssize_t direction_store(struct device *dev, else status = -EINVAL; - mutex_unlock(&data->mutex); - return status ? : size; } static DEVICE_ATTR_RW(direction); @@ -118,11 +116,8 @@ static ssize_t value_show(struct device *dev, struct gpio_desc *desc = data->desc; ssize_t status; - mutex_lock(&data->mutex); - - status = gpiod_get_value_cansleep(desc); - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) + status = gpiod_get_value_cansleep(desc); if (status < 0) return status; @@ -140,18 +135,17 @@ static ssize_t value_store(struct device *dev, status = kstrtol(buf, 0, &value); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); - if (!test_bit(FLAG_IS_OUT, &desc->flags)) { - status = -EPERM; - } else if (status == 0) { - gpiod_set_value_cansleep(desc, value); - status = size; - } + if (!test_bit(FLAG_IS_OUT, &desc->flags)) + return -EPERM; + + if (status) + return status; - mutex_unlock(&data->mutex); + gpiod_set_value_cansleep(desc, value); - return status; + return size; } static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); @@ -185,12 +179,16 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) return -ENODEV; irq_flags = IRQF_SHARED; - if (flags & GPIO_IRQF_TRIGGER_FALLING) + if (flags & GPIO_IRQF_TRIGGER_FALLING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; - if (flags & GPIO_IRQF_TRIGGER_RISING) + set_bit(FLAG_EDGE_FALLING, &desc->flags); + } + if (flags & GPIO_IRQF_TRIGGER_RISING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + set_bit(FLAG_EDGE_RISING, &desc->flags); + } /* * FIXME: This should be done in the irq_request_resources callback @@ -216,6 +214,8 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) err_unlock: gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); err_put_kn: + clear_bit(FLAG_EDGE_RISING, &desc->flags); + clear_bit(FLAG_EDGE_FALLING, &desc->flags); sysfs_put(data->value_kn); return ret; @@ -237,6 +237,8 @@ static void gpio_sysfs_free_irq(struct device *dev) data->irq_flags = 0; free_irq(data->irq, data); gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); + clear_bit(FLAG_EDGE_RISING, &desc->flags); + clear_bit(FLAG_EDGE_FALLING, &desc->flags); sysfs_put(data->value_kn); } @@ -253,11 +255,8 @@ static ssize_t edge_show(struct device *dev, struct gpiod_data *data = dev_get_drvdata(dev); int flags; - mutex_lock(&data->mutex); - - flags = data->irq_flags; - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) + flags = data->irq_flags; if (flags >= ARRAY_SIZE(trigger_names)) return 0; @@ -276,26 +275,24 @@ static ssize_t edge_store(struct device *dev, if (flags < 0) return flags; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); - if (flags == data->irq_flags) { - status = size; - goto out_unlock; - } + if (flags == data->irq_flags) + return size; if (data->irq_flags) gpio_sysfs_free_irq(dev); - if (flags) { - status = gpio_sysfs_request_irq(dev, flags); - if (!status) - status = size; - } + if (!flags) + return size; -out_unlock: - mutex_unlock(&data->mutex); + status = gpio_sysfs_request_irq(dev, flags); + if (status) + return status; - return status; + gpiod_line_state_notify(data->desc, GPIO_V2_LINE_CHANGED_CONFIG); + + return size; } static DEVICE_ATTR_RW(edge); @@ -320,6 +317,8 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) status = gpio_sysfs_request_irq(dev, flags); } + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); + return status; } @@ -330,11 +329,8 @@ static ssize_t active_low_show(struct device *dev, struct gpio_desc *desc = data->desc; int value; - mutex_lock(&data->mutex); - - value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags); - - mutex_unlock(&data->mutex); + scoped_guard(mutex, &data->mutex) + value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags); return sysfs_emit(buf, "%d\n", value); } @@ -350,13 +346,9 @@ static ssize_t active_low_store(struct device *dev, if (status) return status; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); - status = gpio_sysfs_set_active_low(dev, value); - - mutex_unlock(&data->mutex); - - return status ? : size; + return gpio_sysfs_set_active_low(dev, value) ?: size; } static DEVICE_ATTR_RW(active_low); @@ -463,7 +455,7 @@ static ssize_t export_store(const struct class *class, desc = gpio_to_desc(gpio); /* reject invalid GPIOs */ if (!desc) { - pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); + pr_debug_ratelimited("%s: invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } @@ -473,7 +465,7 @@ static ssize_t export_store(const struct class *class, offset = gpio_chip_hwgpio(desc); if (!gpiochip_line_is_valid(guard.gc, offset)) { - pr_warn("%s: GPIO %ld masked\n", __func__, gpio); + pr_debug_ratelimited("%s: GPIO %ld masked\n", __func__, gpio); return -EINVAL; } @@ -493,10 +485,12 @@ static ssize_t export_store(const struct class *class, } status = gpiod_export(desc, true); - if (status < 0) + if (status < 0) { gpiod_free(desc); - else + } else { set_bit(FLAG_SYSFS, &desc->flags); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); + } done: if (status) @@ -520,7 +514,7 @@ static ssize_t unexport_store(const struct class *class, desc = gpio_to_desc(gpio); /* reject bogus commands (gpiod_unexport() ignores them) */ if (!desc) { - pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); + pr_debug_ratelimited("%s: invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } @@ -549,12 +543,11 @@ static struct attribute *gpio_class_attrs[] = { }; ATTRIBUTE_GROUPS(gpio_class); -static struct class gpio_class = { +static const struct class gpio_class = { .name = "gpio", - .class_groups = gpio_class_groups, + .class_groups = gpio_class_groups, }; - /** * gpiod_export - export a GPIO through sysfs * @desc: GPIO to make available, already requested @@ -573,11 +566,10 @@ static struct class gpio_class = { */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { - const char *ioname = NULL; struct gpio_device *gdev; struct gpiod_data *data; struct device *dev; - int status, offset; + int status; /* can't export until sysfs is available ... */ if (!class_is_registered(&gpio_class)) { @@ -599,24 +591,24 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) gdev = desc->gdev; - mutex_lock(&sysfs_lock); + guard(mutex)(&sysfs_lock); /* check if chip is being removed */ if (!gdev->mockdev) { status = -ENODEV; - goto err_unlock; + goto err_clear_bit; } if (!test_bit(FLAG_REQUESTED, &desc->flags)) { gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__); status = -EPERM; - goto err_unlock; + goto err_clear_bit; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { status = -ENOMEM; - goto err_unlock; + goto err_clear_bit; } data->desc = desc; @@ -626,26 +618,19 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) else data->direction_can_change = false; - offset = gpio_chip_hwgpio(desc); - if (guard.gc->names && guard.gc->names[offset]) - ioname = guard.gc->names[offset]; - dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, - ioname ? ioname : "gpio%u", - desc_to_gpio(desc)); + "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto err_free_data; } - mutex_unlock(&sysfs_lock); return 0; err_free_data: kfree(data); -err_unlock: - mutex_unlock(&sysfs_lock); +err_clear_bit: clear_bit(FLAG_EXPORT, &desc->flags); gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; @@ -709,36 +694,28 @@ void gpiod_unexport(struct gpio_desc *desc) return; } - mutex_lock(&sysfs_lock); - - if (!test_bit(FLAG_EXPORT, &desc->flags)) - goto err_unlock; - - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (!dev) - goto err_unlock; - - data = dev_get_drvdata(dev); + scoped_guard(mutex, &sysfs_lock) { + if (!test_bit(FLAG_EXPORT, &desc->flags)) + return; - clear_bit(FLAG_EXPORT, &desc->flags); + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (!dev) + return; - device_unregister(dev); + data = dev_get_drvdata(dev); + clear_bit(FLAG_EXPORT, &desc->flags); + device_unregister(dev); - /* - * Release irq after deregistration to prevent race with edge_store. - */ - if (data->irq_flags) - gpio_sysfs_free_irq(dev); - - mutex_unlock(&sysfs_lock); + /* + * Release irq after deregistration to prevent race with + * edge_store. + */ + if (data->irq_flags) + gpio_sysfs_free_irq(dev); + } put_device(dev); kfree(data); - - return; - -err_unlock: - mutex_unlock(&sysfs_lock); } EXPORT_SYMBOL_GPL(gpiod_unexport); @@ -779,9 +756,8 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) if (IS_ERR(dev)) return PTR_ERR(dev); - mutex_lock(&sysfs_lock); + guard(mutex)(&sysfs_lock); gdev->mockdev = dev; - mutex_unlock(&sysfs_lock); return 0; } |