@@ -36,11 +36,12 @@ struct gpio_shared_ref {
enum gpiod_flags flags;
char *con_id;
int dev_id;
- /* Protects the auxiliary device struct and the lookup table. */
+ /* Protects the auxiliary device struct and fwnode. */
struct mutex lock;
struct lock_class_key lock_key;
struct auxiliary_device proxy_dev;
- struct gpiod_lookup_table *lookup;
+ struct software_node proxy_swnode;
+ struct fwnode_handle *proxy_fwnode;
bool is_reset_gpio;
};
@@ -315,33 +316,51 @@ static int gpio_shared_make_proxy_dev(struct gpio_device *gdev,
struct gpio_shared_entry *entry,
struct gpio_shared_ref *ref)
{
+ struct software_node *swnode = &ref->proxy_swnode;
struct auxiliary_device *adev = &ref->proxy_dev;
+ struct device *dev = &adev->dev;
int ret;
guard(mutex)(&ref->lock);
memset(adev, 0, sizeof(*adev));
+ memset(swnode, 0, sizeof(*swnode));
+
+ ret = software_node_register(swnode);
+ if (ret)
+ return ret;
+
+ ref->proxy_fwnode = software_node_fwnode(swnode);
+ if (!ref->proxy_fwnode)
+ goto err_swnode_unreg;
adev->id = ref->dev_id;
adev->name = "proxy";
- adev->dev.parent = gdev->dev.parent;
- adev->dev.platform_data = entry;
- adev->dev.release = gpio_shared_proxy_dev_release;
+ dev->parent = gdev->dev.parent;
+ dev->platform_data = entry;
+ dev->release = gpio_shared_proxy_dev_release;
+
+ device_set_node(dev, software_node_fwnode(swnode));
ret = auxiliary_device_init(adev);
if (ret)
- return ret;
+ goto err_swnode_unreg;
ret = auxiliary_device_add(adev);
- if (ret) {
- auxiliary_device_uninit(adev);
- return ret;
- }
+ if (ret)
+ goto err_dev_uninit;
pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
dev_name(&adev->dev), gpio_device_get_label(gdev));
return 0;
+
+err_dev_uninit:
+ auxiliary_device_uninit(adev);
+err_swnode_unreg:
+ software_node_unregister(swnode);
+
+ return ret;
}
#if IS_ENABLED(CONFIG_RESET_GPIO)
@@ -446,23 +465,19 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
}
#endif /* CONFIG_RESET_GPIO */
-int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode,
- const char *con_id, unsigned long lflags)
+struct gpio_desc *
+gpio_shared_get_proxy(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id)
{
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
- const char *dev_id;
-
- if (!consumer)
- return -EOPNOTSUPP;
-
- dev_id = dev_name(consumer);
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
guard(mutex)(&ref->lock);
- if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) {
+ if (!ref->fwnode && consumer &&
+ device_is_compatible(consumer, "reset-gpio")) {
if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue;
} else if (fwnode != ref->fwnode) {
@@ -473,48 +488,22 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *
(con_id && ref->con_id && strcmp(con_id, ref->con_id) != 0))
continue;
- /* We've already done that on a previous request. */
- if (ref->lookup)
- return 0;
-
- char *key __free(kfree) =
- kasprintf(GFP_KERNEL,
- KBUILD_MODNAME ".proxy.%u",
- ref->proxy_dev.id);
- if (!key)
- return -ENOMEM;
-
- struct gpiod_lookup_table *lookup __free(kfree) =
- kzalloc_flex(*lookup, table, 2);
- if (!lookup)
- return -ENOMEM;
-
- pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n",
- dev_id, key, ref->con_id ?: "none");
-
- lookup->dev_id = kstrdup(dev_id, GFP_KERNEL);
- if (!lookup->dev_id)
- return -ENOMEM;
-
- lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
- ref->con_id, lflags);
-
- ref->lookup = no_free_ptr(lookup);
- gpiod_add_lookup_table(ref->lookup);
+ struct gpio_device *gdev __free(gpio_device_put) =
+ gpio_device_find_by_fwnode(ref->proxy_fwnode);
+ if (!gdev)
+ /*
+ * A matching entry is there but the associated
+ * controller has not been registered yet.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
- return 0;
+ return gpio_device_get_desc(gdev, 0);
}
}
/* We warn here because this can only happen if the programmer borked. */
WARN_ON(1);
- return -ENOENT;
-}
-
-static void gpio_shared_remove_proxy_dev(struct auxiliary_device *adev)
-{
- auxiliary_device_delete(adev);
- auxiliary_device_uninit(adev);
+ return ERR_PTR(-ENOENT);
}
int gpiochip_setup_shared(struct gpio_chip *gc)
@@ -616,15 +605,9 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
gpiod_free_commit(&gdev->descs[entry->offset]);
list_for_each_entry(ref, &entry->refs, list) {
- if (ref->lookup) {
- gpiod_remove_lookup_table(ref->lookup);
- kfree(ref->lookup->table[0].key);
- kfree(ref->lookup->dev_id);
- kfree(ref->lookup);
- ref->lookup = NULL;
- }
-
- gpio_shared_remove_proxy_dev(&ref->proxy_dev);
+ auxiliary_device_delete(&ref->proxy_dev);
+ auxiliary_device_uninit(&ref->proxy_dev);
+ software_node_unregister(&ref->proxy_swnode);
}
}
}
@@ -14,9 +14,9 @@ struct fwnode_handle;
int gpiochip_setup_shared(struct gpio_chip *gc);
void gpio_device_teardown_shared(struct gpio_device *gdev);
-int gpio_shared_add_proxy_lookup(struct device *consumer,
- struct fwnode_handle *fwnode,
- const char *con_id, unsigned long lflags);
+struct gpio_desc *
+gpio_shared_get_proxy(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id);
#else
@@ -27,12 +27,11 @@ static inline int gpiochip_setup_shared(struct gpio_chip *gc)
static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
-static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
- struct fwnode_handle *fwnode,
- const char *con_id,
- unsigned long lflags)
+static inline struct gpio_desc *
+gpio_shared_get_proxy(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id)
{
- return 0;
+ return NULL;
}
#endif /* CONFIG_GPIO_SHARED */
@@ -4824,24 +4824,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
scoped_guard(srcu, &gpio_devices_srcu) {
desc = gpiod_fwnode_lookup(fwnode, consumer, con_id, idx,
&flags, &lookupflags);
- if (!IS_ERR_OR_NULL(desc) &&
- test_bit(GPIOD_FLAG_SHARED, &desc->flags)) {
- /*
- * We're dealing with a GPIO shared by multiple
- * consumers. This is the moment to add the machine
- * lookup table for the proxy device as previously
- * we only knew the consumer's fwnode.
- */
- ret = gpio_shared_add_proxy_lookup(consumer, fwnode,
- con_id, lookupflags);
- if (ret)
- return ERR_PTR(ret);
-
- /* Trigger platform lookup for shared GPIO proxy. */
- desc = ERR_PTR(-ENOENT);
- /* Trigger it even for fwnode-only gpiod_get(). */
- platform_lookup_allowed = true;
- }
+ if (!IS_ERR_OR_NULL(desc) && test_bit(GPIOD_FLAG_SHARED, &desc->flags))
+ desc = gpio_shared_get_proxy(consumer, fwnode, con_id);
if (gpiod_not_found(desc) && platform_lookup_allowed) {
/*
Automated management of shared GPIOs only allows to request shared pins when they're assigned to a real struct device. Firmware node, no-device lookup currently isn't supported and can't be because we're using platform lookup tables to point the callers to the shared proxy GPIO controllers. Rework the GPIO sharing by using dummy software nodes assigned to the proxy devices and stored in the reference structures which allow us - once the correct struct gpio_shared_ref is found - to find the correct target GPIO device by its firmware node. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com> --- drivers/gpio/gpiolib-shared.c | 111 ++++++++++++++++++------------------------ drivers/gpio/gpiolib-shared.h | 15 +++--- drivers/gpio/gpiolib.c | 20 +------- 3 files changed, 56 insertions(+), 90 deletions(-)