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>
Sun, 13 Dec 2015 16:36:02 +0000 (17:36 +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 16a7b68167444bba93e24d3d59769f0a08d55440..7ab7bc3c93003e0734426da4ec677258b81216d6 100644 (file)
@@ -922,3 +922,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 a18f00fc1bb87544cc59182e6f4a5515464a2f29..3ca6c9f4391e8d4cd809a765ae3c7614dae2ad1f 100644 (file)
@@ -1868,6 +1868,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,