[PATCH 07/11] gpiolib: add support for OF GPIO configuration binding

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Previous commits laid the groundwork:

  - Flags in the DT are saved into GPIO descriptors
  - GPIO drivers can implement a set_config operation

Let's wire them together, so we call the set_config operation when
requesting a GPIO as necessary.

For this to be usable:

  - The GPIO consumer must request the GPIO with gpiod_get
  - The GPIO provider must implement set_config

This is not yet the case for the overwhelming majority of barebox GPIO
consumers and providers, so we allow disabling this functionality via a
Kconfig option.

Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
---
 drivers/gpio/gpiolib.c | 94 +++++++++++++++++++++++++++++++++++++++++-
 drivers/of/Kconfig     | 15 +++++++
 include/of_gpio.h      |  5 +++
 3 files changed, 112 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 539fd3c7c25f..4a1792a8df1f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -8,6 +8,7 @@
 #include <gpio.h>
 #include <of_gpio.h>
 #include <linux/gpio/consumer.h>
+#include <linux/pinctrl/pinconf-generic.h>
 #include <linux/overflow.h>
 #include <errno.h>
 #include <malloc.h>
@@ -437,6 +438,61 @@ int gpio_direction_output(unsigned gpio, int value)
 }
 EXPORT_SYMBOL(gpio_direction_output);
 
+static int gpio_set_config_with_argument(struct gpio_desc *desc,
+					 enum pin_config_param mode,
+					 u32 argument)
+{
+	unsigned long config;
+
+	config = pinconf_to_config_packed(mode, argument);
+	return gpio_do_set_config(desc->chip, gpiodesc_chip_offset(desc), config);
+}
+
+static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
+						  enum pin_config_param mode,
+						  u32 argument)
+{
+	int ret;
+
+	ret = gpio_set_config_with_argument(desc, mode, argument);
+	if (ret != -ENOTSUPP)
+		return ret;
+	return 0;
+}
+
+static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode)
+{
+	return gpio_set_config_with_argument(desc, mode, 0);
+}
+
+static int gpio_set_bias(struct gpio_desc *desc)
+{
+	enum pin_config_param bias;
+	unsigned int arg;
+
+	if (desc->flags & OF_GPIO_PULL_DISABLE)
+		bias = PIN_CONFIG_BIAS_DISABLE;
+	else if (desc->flags & OF_GPIO_PULL_UP)
+		bias = PIN_CONFIG_BIAS_PULL_UP;
+	else if (desc->flags & OF_GPIO_PULL_DOWN)
+		bias = PIN_CONFIG_BIAS_PULL_DOWN;
+	else
+		return 0;
+
+	switch (bias) {
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+	case PIN_CONFIG_BIAS_PULL_UP:
+		arg = 1;
+		break;
+
+	default:
+		arg = 0;
+		break;
+	}
+
+	return gpio_set_config_with_argument_optional(desc, bias, arg);
+}
+
 /**
  * gpiod_direction_output - set the GPIO direction to output
  * @desc:	GPIO to set to output
@@ -451,8 +507,36 @@ EXPORT_SYMBOL(gpio_direction_output);
  */
 int gpiod_direction_output(struct gpio_desc *desc, int value)
 {
+	int ret;
+
 	VALIDATE_DESC(desc);
 
+	if (IS_ENABLED(CONFIG_GPIO_PINCONF)) {
+		if (desc->flags & (OF_GPIO_OPEN_DRAIN | OF_GPIO_SINGLE_ENDED)) {
+			/* First see if we can enable open drain in hardware */
+			ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_DRAIN);
+			if (!ret)
+				goto set_output_value;
+			/* Emulate open drain by not actively driving the line high */
+			if (value)
+				return gpiod_direction_input(desc);
+		} else if (desc->flags & OF_GPIO_SINGLE_ENDED) {
+			ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE);
+			if (!ret)
+				goto set_output_value;
+			/* Emulate open source by not actively driving the line low */
+			if (!value)
+				return gpiod_direction_input(desc);
+		} else {
+			gpio_set_config(desc, PIN_CONFIG_DRIVE_PUSH_PULL);
+		}
+
+set_output_value:
+		ret = gpio_set_bias(desc);
+		if (ret)
+			return ret;
+	}
+
 	return gpiod_direction_output_raw(desc, gpio_adjust_value(desc, value));
 }
 
@@ -478,13 +562,19 @@ EXPORT_SYMBOL(gpio_direction_active);
  */
 int gpiod_direction_input(struct gpio_desc *desc)
 {
+	int ret;
+
 	VALIDATE_DESC(desc);
 
 	if (!desc->chip->ops->direction_input)
 		return -ENOSYS;
 
-	return desc->chip->ops->direction_input(desc->chip,
-					      gpiodesc_chip_offset(desc));
+	ret = desc->chip->ops->direction_input(desc->chip,
+					       gpiodesc_chip_offset(desc));
+	if (ret == 0 && IS_ENABLED(CONFIG_GPIO_PINCONF))
+		ret = gpio_set_bias(desc);
+
+	return ret;
 }
 EXPORT_SYMBOL(gpiod_direction_input);
 
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 2791100a2d9c..6c9aedf355b4 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -40,6 +40,21 @@ config OF_GPIO
 	depends on OFDEVICE
 	def_bool y
 
+config OF_GPIO_PINCONF
+	depends on OF_GPIO && HAVE_GPIO_PINCONF
+	select GPIO_PINCONF
+	bool "Enable support for extra GPIO pin configuration binding"
+	default y
+	help
+	  In addition to the normal pinctrl-names/pinctrl-X binding, there's
+	  also a binding to add flags like GPIO_OPEN_DRAIN or GPIO_PULL_UP
+	  OR-ed into the cell of the gpios property used for
+	  GPIO_ACTIVE_HIGH/LOW. This latter binding is optional and many
+	  drivers don't support it.
+
+	  If unsure and not size conscious, say y here to enable the
+	  extra binding.
+
 config OF_PCI
 	bool
 	depends on PCI
diff --git a/include/of_gpio.h b/include/of_gpio.h
index a7a3493473c8..4ab5de6ed580 100644
--- a/include/of_gpio.h
+++ b/include/of_gpio.h
@@ -17,6 +17,11 @@
  */
 enum of_gpio_flags {
 	OF_GPIO_ACTIVE_LOW = 0x1,
+	OF_GPIO_SINGLE_ENDED = 0x2,
+	OF_GPIO_OPEN_DRAIN = 0x4,
+	OF_GPIO_PULL_UP = 0x10,
+	OF_GPIO_PULL_DOWN = 0x20,
+	OF_GPIO_PULL_DISABLE = 0x40,
 	OF_GPIO_REQUESTED = 0x80000000, /* internal use */
 };
 
-- 
2.39.2





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux