Merge tag 'gpio-v3.19-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[cascardo/linux.git] / drivers / gpio / gpiolib.c
index 56b7c5d..487afe6 100644 (file)
@@ -1688,14 +1688,36 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
                                        unsigned int idx,
                                        enum gpio_lookup_flags *flags)
 {
+       static const char * const suffixes[] = { "gpios", "gpio" };
+       struct acpi_device *adev = ACPI_COMPANION(dev);
        struct acpi_gpio_info info;
        struct gpio_desc *desc;
+       char propname[32];
+       int i;
 
-       desc = acpi_get_gpiod_by_index(dev, idx, &info);
-       if (IS_ERR(desc))
-               return desc;
+       /* Try first from _DSD */
+       for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
+               if (con_id && strcmp(con_id, "gpios")) {
+                       snprintf(propname, sizeof(propname), "%s-%s",
+                                con_id, suffixes[i]);
+               } else {
+                       snprintf(propname, sizeof(propname), "%s",
+                                suffixes[i]);
+               }
+
+               desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
+               if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+                       break;
+       }
 
-       if (info.gpioint && info.active_low)
+       /* Then from plain _CRS GPIOs */
+       if (IS_ERR(desc)) {
+               desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
+               if (IS_ERR(desc))
+                       return desc;
+       }
+
+       if (info.active_low)
                *flags |= GPIO_ACTIVE_LOW;
 
        return desc;
@@ -1895,6 +1917,61 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(__gpiod_get_index);
 
+/**
+ * fwnode_get_named_gpiod - obtain a GPIO from firmware node
+ * @fwnode:    handle of the firmware node
+ * @propname:  name of the firmware property representing the GPIO
+ *
+ * This function can be used for drivers that get their configuration
+ * from firmware.
+ *
+ * Function properly finds the corresponding GPIO using whatever is the
+ * underlying firmware interface and then makes sure that the GPIO
+ * descriptor is requested before it is returned to the caller.
+ *
+ * In case of error an ERR_PTR() is returned.
+ */
+struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
+                                        const char *propname)
+{
+       struct gpio_desc *desc = ERR_PTR(-ENODEV);
+       bool active_low = false;
+       int ret;
+
+       if (!fwnode)
+               return ERR_PTR(-EINVAL);
+
+       if (is_of_node(fwnode)) {
+               enum of_gpio_flags flags;
+
+               desc = of_get_named_gpiod_flags(of_node(fwnode), propname, 0,
+                                               &flags);
+               if (!IS_ERR(desc))
+                       active_low = flags & OF_GPIO_ACTIVE_LOW;
+       } else if (is_acpi_node(fwnode)) {
+               struct acpi_gpio_info info;
+
+               desc = acpi_get_gpiod_by_index(acpi_node(fwnode), propname, 0,
+                                              &info);
+               if (!IS_ERR(desc))
+                       active_low = info.active_low;
+       }
+
+       if (IS_ERR(desc))
+               return desc;
+
+       ret = gpiod_request(desc, NULL);
+       if (ret)
+               return ERR_PTR(ret);
+
+       /* Only value flag can be set from both DT and ACPI is active_low */
+       if (active_low)
+               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+
+       return desc;
+}
+EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
+
 /**
  * gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO
  *                            function