summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpiolib-shared.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpiolib-shared.c')
-rw-r--r--drivers/gpio/gpiolib-shared.c63
1 files changed, 28 insertions, 35 deletions
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index cd4dd6fc76ab..8bdd107b1ad1 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -49,6 +49,7 @@ struct gpio_shared_entry {
unsigned int offset;
/* Index in the property value array. */
size_t index;
+ struct mutex lock;
struct gpio_shared_desc *shared_desc;
struct kref ref;
struct list_head refs;
@@ -170,6 +171,7 @@ static int gpio_shared_of_traverse(struct device_node *curr)
entry->offset = offset;
entry->index = count;
INIT_LIST_HEAD(&entry->refs);
+ mutex_init(&entry->lock);
list_add_tail(&entry->list, &gpio_shared_list);
}
@@ -246,6 +248,7 @@ static void gpio_shared_adev_release(struct device *dev)
}
static int gpio_shared_make_adev(struct gpio_device *gdev,
+ struct gpio_shared_entry *entry,
struct gpio_shared_ref *ref)
{
struct auxiliary_device *adev = &ref->adev;
@@ -258,6 +261,7 @@ static int gpio_shared_make_adev(struct gpio_device *gdev,
adev->id = ref->dev_id;
adev->name = "proxy";
adev->dev.parent = gdev->dev.parent;
+ adev->dev.platform_data = entry;
adev->dev.release = gpio_shared_adev_release;
ret = auxiliary_device_init(adev);
@@ -461,7 +465,7 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
pr_debug("Setting up a shared GPIO entry for %s\n",
fwnode_get_name(ref->fwnode));
- ret = gpio_shared_make_adev(gdev, ref);
+ ret = gpio_shared_make_adev(gdev, entry, ref);
if (ret)
return ret;
}
@@ -495,10 +499,11 @@ static void gpio_shared_release(struct kref *kref)
{
struct gpio_shared_entry *entry =
container_of(kref, struct gpio_shared_entry, ref);
- struct gpio_shared_desc *shared_desc = entry->shared_desc;
+ struct gpio_shared_desc *shared_desc;
- guard(mutex)(&gpio_shared_lock);
+ guard(mutex)(&entry->lock);
+ shared_desc = entry->shared_desc;
gpio_device_put(shared_desc->desc->gdev);
if (shared_desc->can_sleep)
mutex_destroy(&shared_desc->mutex);
@@ -521,6 +526,8 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
struct gpio_shared_desc *shared_desc;
struct gpio_device *gdev;
+ lockdep_assert_held(&entry->lock);
+
shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
if (!shared_desc)
return ERR_PTR(-ENOMEM);
@@ -541,57 +548,42 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
return shared_desc;
}
-static struct gpio_shared_entry *gpiod_shared_find(struct auxiliary_device *adev)
+struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
{
struct gpio_shared_desc *shared_desc;
struct gpio_shared_entry *entry;
- struct gpio_shared_ref *ref;
-
- guard(mutex)(&gpio_shared_lock);
+ int ret;
- list_for_each_entry(entry, &gpio_shared_list, list) {
- list_for_each_entry(ref, &entry->refs, list) {
- if (adev != &ref->adev)
- continue;
+ lockdep_assert_not_held(&gpio_shared_lock);
- if (entry->shared_desc) {
- kref_get(&entry->ref);
- return entry;
- }
+ entry = dev_get_platdata(dev);
+ if (WARN_ON(!entry))
+ /* Programmer bug */
+ return ERR_PTR(-ENOENT);
+ scoped_guard(mutex, &entry->lock) {
+ if (entry->shared_desc) {
+ kref_get(&entry->ref);
+ shared_desc = entry->shared_desc;
+ } else {
shared_desc = gpiod_shared_desc_create(entry);
if (IS_ERR(shared_desc))
return ERR_CAST(shared_desc);
kref_init(&entry->ref);
entry->shared_desc = shared_desc;
-
- pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
- dev_name(&adev->dev), gpiod_hwgpio(shared_desc->desc),
- gpio_device_get_label(shared_desc->desc->gdev));
-
-
- return entry;
}
- }
- return ERR_PTR(-ENOENT);
-}
-
-struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
-{
- struct gpio_shared_entry *entry;
- int ret;
-
- entry = gpiod_shared_find(to_auxiliary_dev(dev));
- if (IS_ERR(entry))
- return ERR_CAST(entry);
+ pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
+ dev_name(dev), gpiod_hwgpio(shared_desc->desc),
+ gpio_device_get_label(shared_desc->desc->gdev));
+ }
ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
if (ret)
return ERR_PTR(ret);
- return entry->shared_desc;
+ return shared_desc;
}
EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
@@ -607,6 +599,7 @@ static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
static void gpio_shared_drop_entry(struct gpio_shared_entry *entry)
{
list_del(&entry->list);
+ mutex_destroy(&entry->lock);
fwnode_handle_put(entry->fwnode);
kfree(entry);
}