aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c150
1 files changed, 84 insertions, 66 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4c93cf73a826..75be4a3ca7f8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2,7 +2,6 @@
#include <linux/acpi.h>
#include <linux/bitmap.h>
-#include <linux/cleanup.h>
#include <linux/compat.h>
#include <linux/debugfs.h>
#include <linux/device.h>
@@ -16,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/seq_file.h>
@@ -83,9 +81,7 @@ DEFINE_SPINLOCK(gpio_lock);
static DEFINE_MUTEX(gpio_lookup_lock);
static LIST_HEAD(gpio_lookup_list);
-
LIST_HEAD(gpio_devices);
-DECLARE_RWSEM(gpio_devices_sem);
static DEFINE_MUTEX(gpio_machine_hogs_mutex);
static LIST_HEAD(gpio_machine_hogs);
@@ -117,15 +113,20 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
struct gpio_device *gdev;
+ unsigned long flags;
- scoped_guard(rwsem_read, &gpio_devices_sem) {
- list_for_each_entry(gdev, &gpio_devices, list) {
- if (gdev->base <= gpio &&
- gdev->base + gdev->ngpio > gpio)
- return &gdev->descs[gpio - gdev->base];
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ list_for_each_entry(gdev, &gpio_devices, list) {
+ if (gdev->base <= gpio &&
+ gdev->base + gdev->ngpio > gpio) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return &gdev->descs[gpio - gdev->base];
}
}
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
if (!gpio_is_valid(gpio))
pr_warn("invalid GPIO %d\n", gpio);
@@ -398,21 +399,26 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
static struct gpio_desc *gpio_name_to_desc(const char * const name)
{
struct gpio_device *gdev;
+ unsigned long flags;
if (!name)
return NULL;
- guard(rwsem_read)(&gpio_devices_sem);
+ spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(gdev, &gpio_devices, list) {
struct gpio_desc *desc;
for_each_gpio_desc(gdev->chip, desc) {
- if (desc->name && !strcmp(desc->name, name))
+ if (desc->name && !strcmp(desc->name, name)) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
return desc;
+ }
}
}
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
return NULL;
}
@@ -807,6 +813,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct lock_class_key *request_key)
{
struct gpio_device *gdev;
+ unsigned long flags;
unsigned int i;
int base = 0;
int ret = 0;
@@ -871,46 +878,49 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gdev->ngpio = gc->ngpio;
- scoped_guard(rwsem_write, &gpio_devices_sem) {
- /*
- * TODO: this allocates a Linux GPIO number base in the global
- * GPIO numberspace for this chip. In the long run we want to
- * get *rid* of this numberspace and use only descriptors, but
- * it may be a pipe dream. It will not happen before we get rid
- * of the sysfs interface anyways.
- */
- base = gc->base;
+ spin_lock_irqsave(&gpio_lock, flags);
+ /*
+ * TODO: this allocates a Linux GPIO number base in the global
+ * GPIO numberspace for this chip. In the long run we want to
+ * get *rid* of this numberspace and use only descriptors, but
+ * it may be a pipe dream. It will not happen before we get rid
+ * of the sysfs interface anyways.
+ */
+ base = gc->base;
+ if (base < 0) {
+ base = gpiochip_find_base_unlocked(gc->ngpio);
if (base < 0) {
- base = gpiochip_find_base_unlocked(gc->ngpio);
- if (base < 0) {
- ret = base;
- base = 0;
- goto err_free_label;
- }
- /*
- * TODO: it should not be necessary to reflect the assigned
- * base outside of the GPIO subsystem. Go over drivers and
- * see if anyone makes use of this, else drop this and assign
- * a poison instead.
- */
- gc->base = base;
- } else {
- dev_warn(&gdev->dev,
- "Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
- }
- gdev->base = base;
-
- ret = gpiodev_add_to_list_unlocked(gdev);
- if (ret) {
- chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ ret = base;
+ base = 0;
goto err_free_label;
}
+ /*
+ * TODO: it should not be necessary to reflect the assigned
+ * base outside of the GPIO subsystem. Go over drivers and
+ * see if anyone makes use of this, else drop this and assign
+ * a poison instead.
+ */
+ gc->base = base;
+ } else {
+ dev_warn(&gdev->dev,
+ "Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
+ }
+ gdev->base = base;
- for (i = 0; i < gc->ngpio; i++)
- gdev->descs[i].gdev = gdev;
+ ret = gpiodev_add_to_list_unlocked(gdev);
+ if (ret) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
+ goto err_free_label;
}
+ for (i = 0; i < gc->ngpio; i++)
+ gdev->descs[i].gdev = gdev;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
init_rwsem(&gdev->sem);
@@ -958,11 +968,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiochip_irqchip_init_valid_mask(gc);
if (ret)
- goto err_remove_acpi_chip;
+ goto err_free_hogs;
ret = gpiochip_irqchip_init_hw(gc);
if (ret)
- goto err_remove_acpi_chip;
+ goto err_remove_irqchip_mask;
ret = gpiochip_add_irqchip(gc, lock_key, request_key);
if (ret)
@@ -987,22 +997,23 @@ err_remove_irqchip:
gpiochip_irqchip_remove(gc);
err_remove_irqchip_mask:
gpiochip_irqchip_free_valid_mask(gc);
-err_remove_acpi_chip:
+err_free_hogs:
+ gpiochip_free_hogs(gc);
acpi_gpiochip_remove(gc);
+ gpiochip_remove_pin_ranges(gc);
err_remove_of_chip:
- gpiochip_free_hogs(gc);
of_gpiochip_remove(gc);
err_free_gpiochip_mask:
- gpiochip_remove_pin_ranges(gc);
gpiochip_free_valid_mask(gc);
+err_remove_from_list:
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_del(&gdev->list);
+ spin_unlock_irqrestore(&gpio_lock, flags);
if (gdev->dev.release) {
/* release() has been registered by gpiochip_setup_dev() */
gpio_device_put(gdev);
goto err_print_message;
}
-err_remove_from_list:
- scoped_guard(rwsem_write, &gpio_devices_sem)
- list_del(&gdev->list);
err_free_label:
kfree_const(gdev->label);
err_free_descs:
@@ -1065,7 +1076,7 @@ void gpiochip_remove(struct gpio_chip *gc)
dev_crit(&gdev->dev,
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
- scoped_guard(rwsem_write, &gpio_devices_sem)
+ scoped_guard(spinlock_irqsave, &gpio_lock)
list_del(&gdev->list);
/*
@@ -1114,7 +1125,7 @@ struct gpio_device *gpio_device_find(void *data,
*/
might_sleep();
- guard(rwsem_read)(&gpio_devices_sem);
+ guard(spinlock_irqsave)(&gpio_lock);
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->chip && match(gdev->chip, data))
@@ -2031,6 +2042,11 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free);
int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset,
unsigned long config)
{
+#ifdef CONFIG_PINCTRL
+ if (list_empty(&gc->gpiodev->pin_ranges))
+ return -ENOTSUPP;
+#endif
+
return pinctrl_gpio_set_config(gc, offset, config);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_config);
@@ -4725,33 +4741,35 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
{
+ unsigned long flags;
struct gpio_device *gdev = NULL;
loff_t index = *pos;
s->private = "";
- guard(rwsem_read)(&gpio_devices_sem);
-
- list_for_each_entry(gdev, &gpio_devices, list) {
- if (index-- == 0)
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_for_each_entry(gdev, &gpio_devices, list)
+ if (index-- == 0) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
return gdev;
- }
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
return NULL;
}
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
+ unsigned long flags;
struct gpio_device *gdev = v;
void *ret = NULL;
- scoped_guard(rwsem_read, &gpio_devices_sem) {
- if (list_is_last(&gdev->list, &gpio_devices))
- ret = NULL;
- else
- ret = list_first_entry(&gdev->list, struct gpio_device,
- list);
- }
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (list_is_last(&gdev->list, &gpio_devices))
+ ret = NULL;
+ else
+ ret = list_first_entry(&gdev->list, struct gpio_device, list);
+ spin_unlock_irqrestore(&gpio_lock, flags);
s->private = "\n";
++*pos;