linux/named-gpio-export: add support for OPEN_DRAIN and OPEN_SOURCE flag
authorMartin Schiller <ms@dev.tdt.de>
Fri, 4 Aug 2023 10:59:03 +0000 (12:59 +0200)
committerHauke Mehrtens <hauke@hauke-m.de>
Tue, 15 Aug 2023 14:07:04 +0000 (16:07 +0200)
This change makes it possible to use the GPIO_OPEN_DRAIN /
GPIO_OPEN_SOURCE Flags when exporting GPIOs via dts.

We need to emulate the open-source or open-drain functionalities for the
initial value, because the used functions (gpiod_direction_output_raw)
do not take this into account.

Signed-off-by: Martin Schiller <ms@dev.tdt.de>
target/linux/generic/hack-5.15/800-GPIO-add-named-gpio-exports.patch
target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch

index 0a2c82cacbc11f4ac0d1b5481e634ca40057f48a..f27b7ef9de592641b335884314df42c18244db35 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
  #include "gpiolib.h"
  #include "gpiolib-of.h"
-@@ -1059,3 +1061,72 @@ void of_gpio_dev_init(struct gpio_chip *
+@@ -1059,3 +1061,100 @@ void of_gpio_dev_init(struct gpio_chip *
        else
                gc->of_node = gdev->dev.of_node;
  }
@@ -47,6 +47,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +                      max_gpio = of_gpio_count(cnp);
 +
 +              for (i = 0; i < max_gpio; i++) {
++                      struct gpio_desc *desc;
 +                      unsigned flags = 0;
 +                      enum of_gpio_flags of_flags;
 +
@@ -54,17 +55,44 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +                      if (!gpio_is_valid(gpio))
 +                              return gpio;
 +
-+                      if (of_flags == OF_GPIO_ACTIVE_LOW)
++                      if (of_flags & OF_GPIO_ACTIVE_LOW)
 +                              flags |= GPIOF_ACTIVE_LOW;
 +
-+                      if (!of_property_read_u32(cnp, "gpio-export,output", &val))
-+                              flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
-+                      else
++                      if (!of_property_read_u32(cnp, "gpio-export,output", &val)) {
++                              if (of_flags & OF_GPIO_SINGLE_ENDED) {
++                                      /*
++                                       * As gpiod_direction_output_raw() is used, we
++                                       * need to emulate open drain or open source here.
++                                       */
++                                      if (of_flags & OF_GPIO_OPEN_DRAIN) {
++                                              flags |= GPIOF_OPEN_DRAIN;
++                                              flags |= val ? GPIOF_IN : GPIOF_OUT_INIT_LOW;
++                                      } else {
++                                              flags |= GPIOF_OPEN_SOURCE;
++                                              flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_IN;
++                                      }
++                              } else {
++                                      flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++                              }
++                      } else {
 +                              flags |= GPIOF_IN;
++                      }
 +
 +                      if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
 +                              continue;
 +
++                      /*
++                       * When emulating open-source or open-drain functionalities by not
++                       * actively driving the line (setting mode to input) we still need to
++                       * set the IS_OUT flag or otherwise we won't be able to set the line
++                       * value anymore.
++                       */
++                      if ((flags & GPIOF_IN) &&
++                          ((flags & GPIOF_OPEN_DRAIN) || (flags & GPIOF_OPEN_SOURCE))) {
++                              desc = gpio_to_desc(gpio);
++                              set_bit(FLAG_IS_OUT, &desc->flags);
++                      }
++
 +                      dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
 +                      gpio_export_with_name(gpio, dmc, name);
 +                      nb++;
index 658d9c38069178f62336f1f1a2526a0ace9889c4..ba5351275dd0b506f2551bbe68f31ab331e1a47a 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  
  #include "gpiolib.h"
  #include "gpiolib-of.h"
-@@ -1030,3 +1032,72 @@ void of_gpio_dev_init(struct gpio_chip *
+@@ -1030,3 +1032,100 @@ void of_gpio_dev_init(struct gpio_chip *
        else
                gc->of_node = gdev->dev.of_node;
  }
@@ -47,6 +47,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +                      max_gpio = of_gpio_count(cnp);
 +
 +              for (i = 0; i < max_gpio; i++) {
++                      struct gpio_desc *desc;
 +                      unsigned flags = 0;
 +                      enum of_gpio_flags of_flags;
 +
@@ -54,17 +55,44 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +                      if (!gpio_is_valid(gpio))
 +                              return gpio;
 +
-+                      if (of_flags == OF_GPIO_ACTIVE_LOW)
++                      if (of_flags & OF_GPIO_ACTIVE_LOW)
 +                              flags |= GPIOF_ACTIVE_LOW;
 +
-+                      if (!of_property_read_u32(cnp, "gpio-export,output", &val))
-+                              flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
-+                      else
++                      if (!of_property_read_u32(cnp, "gpio-export,output", &val)) {
++                              if (of_flags & OF_GPIO_SINGLE_ENDED) {
++                                      /*
++                                       * As gpiod_direction_output_raw() is used, we
++                                       * need to emulate open drain or open source here.
++                                       */
++                                      if (of_flags & OF_GPIO_OPEN_DRAIN) {
++                                              flags |= GPIOF_OPEN_DRAIN;
++                                              flags |= val ? GPIOF_IN : GPIOF_OUT_INIT_LOW;
++                                      } else {
++                                              flags |= GPIOF_OPEN_SOURCE;
++                                              flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_IN;
++                                      }
++                              } else {
++                                      flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++                              }
++                      } else {
 +                              flags |= GPIOF_IN;
++                      }
 +
 +                      if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
 +                              continue;
 +
++                      /*
++                       * When emulating open-source or open-drain functionalities by not
++                       * actively driving the line (setting mode to input) we still need to
++                       * set the IS_OUT flag or otherwise we won't be able to set the line
++                       * value anymore.
++                       */
++                      if ((flags & GPIOF_IN) &&
++                          ((flags & GPIOF_OPEN_DRAIN) || (flags & GPIOF_OPEN_SOURCE))) {
++                              desc = gpio_to_desc(gpio);
++                              set_bit(FLAG_IS_OUT, &desc->flags);
++                      }
++
 +                      dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
 +                      gpio_export_with_name(gpio, dmc, name);
 +                      nb++;