gpiolib: tighten up ACPI legacy gpio lookups
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 11 Nov 2015 19:45:30 +0000 (11:45 -0800)
committerLinus Walleij <linus.walleij@linaro.org>
Thu, 19 Nov 2015 08:24:37 +0000 (09:24 +0100)
We should not fall back to the legacy unnamed gpio lookup style if the
driver requests gpios with different names, because we'll give out the same
gpio twice. Let's keep track of the names that were used for the device and
only do the fallback for the first name used.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h

index e4620e14457f5860272bb12fe72dacbbf7883b76..07e571a1a3775f27aa421667b6dfcb101e44f6ad 100644 (file)
@@ -925,3 +925,46 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
        }
        return count;
 }
+
+struct acpi_crs_lookup {
+       struct list_head node;
+       struct acpi_device *adev;
+       const char *con_id;
+};
+
+static DEFINE_MUTEX(acpi_crs_lookup_lock);
+static LIST_HEAD(acpi_crs_lookup_list);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
+{
+       struct acpi_crs_lookup *l, *lookup = NULL;
+
+       /* Never allow fallback if the device has properties */
+       if (adev->data.properties || adev->driver_gpios)
+               return false;
+
+       mutex_lock(&acpi_crs_lookup_lock);
+
+       list_for_each_entry(l, &acpi_crs_lookup_list, node) {
+               if (l->adev == adev) {
+                       lookup = l;
+                       break;
+               }
+       }
+
+       if (!lookup) {
+               lookup = kmalloc(sizeof(*lookup), GFP_KERNEL);
+               if (lookup) {
+                       lookup->adev = adev;
+                       lookup->con_id = con_id;
+                       list_add_tail(&lookup->node, &acpi_crs_lookup_list);
+               }
+       }
+
+       mutex_unlock(&acpi_crs_lookup_lock);
+
+       return lookup &&
+               ((!lookup->con_id && !con_id) ||
+                (lookup->con_id && con_id &&
+                 strcmp(lookup->con_id, con_id) == 0));
+}
index 8b35457013dac2160af0b08da3619bff6fbccdc1..dfe09c06df49a26444a845fdda3d7835807dc902 100644 (file)
@@ -1869,6 +1869,9 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
 
        /* Then from plain _CRS GPIOs */
        if (IS_ERR(desc)) {
+               if (!acpi_can_fallback_to_crs(adev, con_id))
+                       return ERR_PTR(-ENOENT);
+
                desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
                if (IS_ERR(desc))
                        return desc;
index 98ab08c0aa2d2d13344b6af003e6696129193907..2b87cbd68478ca0526676d1382acf80f70b22d3f 100644 (file)
@@ -47,6 +47,8 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
                                      struct acpi_gpio_info *info);
 
 int acpi_gpio_count(struct device *dev, const char *con_id);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
 #else
 static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
 static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
@@ -73,6 +75,12 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id)
 {
        return -ENODEV;
 }
+
+static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
+                                           const char *con_id)
+{
+       return false;
+}
 #endif
 
 struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,