Merge branch '2021-03-03-gpio-improvements' into next

- GPIO uclass improvements
diff --git a/arch/sandbox/include/asm/gpio.h b/arch/sandbox/include/asm/gpio.h
index df4ba4f..9e10052 100644
--- a/arch/sandbox/include/asm/gpio.h
+++ b/arch/sandbox/include/asm/gpio.h
@@ -23,6 +23,15 @@
  */
 #include <asm-generic/gpio.h>
 
+/* Our own private GPIO flags, which musn't conflict with GPIOD_... */
+#define GPIOD_EXT_HIGH		BIT(31)	/* external source is high (else low) */
+#define GPIOD_EXT_DRIVEN	BIT(30)	/* external source is driven */
+#define GPIOD_EXT_PULL_UP	BIT(29)	/* GPIO has external pull-up */
+#define GPIOD_EXT_PULL_DOWN	BIT(28)	/* GPIO has external pull-down */
+
+#define GPIOD_EXT_PULL		(BIT(28) | BIT(29))
+#define GPIOD_SANDBOX_MASK	GENMASK(31, 28)
+
 /**
  * Return the simulated value of a GPIO (used only in sandbox test code)
  *
@@ -69,17 +78,17 @@
  * @param offset	GPIO offset within bank
  * @return dir_flags: bitfield accesses by GPIOD_ defines
  */
-ulong sandbox_gpio_get_dir_flags(struct udevice *dev, unsigned int offset);
+ulong sandbox_gpio_get_flags(struct udevice *dev, unsigned int offset);
 
 /**
  * Set the simulated flags of a GPIO (used only in sandbox test code)
  *
  * @param dev		device to use
  * @param offset	GPIO offset within bank
- * @param flags		dir_flags: bitfield accesses by GPIOD_ defines
+ * @param flags		bitfield accesses by GPIOD_ defines
  * @return -1 on error, 0 if ok
  */
-int sandbox_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
-			       ulong flags);
+int sandbox_gpio_set_flags(struct udevice *dev, unsigned int offset,
+			   ulong flags);
 
 #endif
diff --git a/arch/x86/include/asm/intel_pinctrl_defs.h b/arch/x86/include/asm/intel_pinctrl_defs.h
index 1ea141f..5d83d24 100644
--- a/arch/x86/include/asm/intel_pinctrl_defs.h
+++ b/arch/x86/include/asm/intel_pinctrl_defs.h
@@ -11,6 +11,11 @@
 
 /* This file is included by device trees, so avoid BIT() macros */
 
+#define GPIO_DW_SIZE(x)			(sizeof(u32) * (x))
+#define PAD_CFG_OFFSET(x, dw_num)	((x) + GPIO_DW_SIZE(dw_num))
+#define PAD_CFG0_OFFSET(x)		PAD_CFG_OFFSET(x, 0)
+#define PAD_CFG1_OFFSET(x)		PAD_CFG_OFFSET(x, 1)
+
 #define PAD_CFG0_TX_STATE_BIT		0
 #define PAD_CFG0_TX_STATE		(1 << PAD_CFG0_TX_STATE_BIT)
 #define PAD_CFG0_RX_STATE_BIT		1
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 4a9b74e..f24db87 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2013 Google, Inc
  */
 
+#define LOG_CATEGORY	UCLASS_GPIO
+
 #include <common.h>
 #include <dm.h>
 #include <log.h>
@@ -21,6 +23,7 @@
 #include <dm/device_compat.h>
 #include <linux/bug.h>
 #include <linux/ctype.h>
+#include <linux/delay.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -220,7 +223,7 @@
 static int gpio_find_and_xlate(struct gpio_desc *desc,
 			       struct ofnode_phandle_args *args)
 {
-	struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
+	const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
 
 	if (ops->xlate)
 		return ops->xlate(desc->dev, desc, args);
@@ -353,6 +356,7 @@
 
 int dm_gpio_request(struct gpio_desc *desc, const char *label)
 {
+	const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
 	struct udevice *dev = desc->dev;
 	struct gpio_dev_priv *uc_priv;
 	char *str;
@@ -364,8 +368,8 @@
 	str = strdup(label);
 	if (!str)
 		return -ENOMEM;
-	if (gpio_get_ops(dev)->request) {
-		ret = gpio_get_ops(dev)->request(dev, desc->offset, label);
+	if (ops->request) {
+		ret = ops->request(dev, desc->offset, label);
 		if (ret) {
 			free(str);
 			return ret;
@@ -442,14 +446,15 @@
 
 int _dm_gpio_free(struct udevice *dev, uint offset)
 {
+	const struct dm_gpio_ops *ops = gpio_get_ops(dev);
 	struct gpio_dev_priv *uc_priv;
 	int ret;
 
 	uc_priv = dev_get_uclass_priv(dev);
 	if (!uc_priv->name[offset])
 		return -ENXIO;
-	if (gpio_get_ops(dev)->rfree) {
-		ret = gpio_get_ops(dev)->rfree(dev, offset);
+	if (ops->rfree) {
+		ret = ops->rfree(dev, offset);
 		if (ret)
 			return ret;
 	}
@@ -515,11 +520,8 @@
 	ret = gpio_to_device(gpio, &desc);
 	if (ret)
 		return ret;
-	ret = check_reserved(&desc, "dir_input");
-	if (ret)
-		return ret;
 
-	return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset);
+	return dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN);
 }
 
 /**
@@ -534,24 +536,25 @@
 int gpio_direction_output(unsigned gpio, int value)
 {
 	struct gpio_desc desc;
+	ulong flags;
 	int ret;
 
 	ret = gpio_to_device(gpio, &desc);
 	if (ret)
 		return ret;
-	ret = check_reserved(&desc, "dir_output");
-	if (ret)
-		return ret;
 
-	return gpio_get_ops(desc.dev)->direction_output(desc.dev,
-							desc.offset, value);
+	flags = GPIOD_IS_OUT;
+	if (value)
+		flags |= GPIOD_IS_OUT_ACTIVE;
+	return dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, flags);
 }
 
 static int _gpio_get_value(const struct gpio_desc *desc)
 {
+	const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
 	int value;
 
-	value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
+	value = ops->get_value(desc->dev, desc->offset);
 
 	return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
 }
@@ -569,6 +572,7 @@
 
 int dm_gpio_set_value(const struct gpio_desc *desc, int value)
 {
+	const struct dm_gpio_ops *ops;
 	int ret;
 
 	ret = check_reserved(desc, "set_value");
@@ -578,21 +582,33 @@
 	if (desc->flags & GPIOD_ACTIVE_LOW)
 		value = !value;
 
+	/* GPIOD_ are directly managed by driver in set_flags */
+	ops = gpio_get_ops(desc->dev);
+	if (ops->set_flags) {
+		ulong flags = desc->flags;
+
+		if (value)
+			flags |= GPIOD_IS_OUT_ACTIVE;
+		else
+			flags &= ~GPIOD_IS_OUT_ACTIVE;
+		return ops->set_flags(desc->dev, desc->offset, flags);
+	}
+
 	/*
 	 * Emulate open drain by not actively driving the line high or
 	 * Emulate open source by not actively driving the line low
 	 */
 	if ((desc->flags & GPIOD_OPEN_DRAIN && value) ||
 	    (desc->flags & GPIOD_OPEN_SOURCE && !value))
-		return gpio_get_ops(desc->dev)->direction_input(desc->dev,
-								desc->offset);
+		return ops->direction_input(desc->dev, desc->offset);
 	else if (desc->flags & GPIOD_OPEN_DRAIN ||
 		 desc->flags & GPIOD_OPEN_SOURCE)
-		return gpio_get_ops(desc->dev)->direction_output(desc->dev,
-								desc->offset,
-								value);
+		return ops->direction_output(desc->dev, desc->offset, value);
 
-	gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value);
+	ret = ops->set_value(desc->dev, desc->offset, value);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -620,10 +636,21 @@
 	return 0;
 }
 
-static int _dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
+/**
+ * _dm_gpio_set_flags() - Send flags to the driver
+ *
+ * This uses the best available method to send the given flags to the driver.
+ * Note that if flags & GPIOD_ACTIVE_LOW, the driver sees the opposite value
+ * of GPIOD_IS_OUT_ACTIVE.
+ *
+ * @desc:	GPIO description
+ * @flags:	flags value to set
+ * @return 0 if OK, -ve on error
+ */
+static int _dm_gpio_set_flags(struct gpio_desc *desc, ulong flags)
 {
 	struct udevice *dev = desc->dev;
-	struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	const struct dm_gpio_ops *ops = gpio_get_ops(dev);
 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 	int ret = 0;
 
@@ -638,38 +665,52 @@
 		return ret;
 	}
 
-	/* GPIOD_ are directly managed by driver in set_dir_flags*/
-	if (ops->set_dir_flags) {
-		ret = ops->set_dir_flags(dev, desc->offset, flags);
+	/* If active low, invert the output state */
+	if ((flags & (GPIOD_IS_OUT | GPIOD_ACTIVE_LOW)) ==
+		(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW))
+		flags ^= GPIOD_IS_OUT_ACTIVE;
+
+	/* GPIOD_ are directly managed by driver in set_flags */
+	if (ops->set_flags) {
+		ret = ops->set_flags(dev, desc->offset, flags);
 	} else {
 		if (flags & GPIOD_IS_OUT) {
-			ret = ops->direction_output(dev, desc->offset,
-						    GPIOD_FLAGS_OUTPUT(flags));
+			bool value = flags & GPIOD_IS_OUT_ACTIVE;
+
+			ret = ops->direction_output(dev, desc->offset, value);
 		} else if (flags & GPIOD_IS_IN) {
 			ret = ops->direction_input(dev, desc->offset);
 		}
 	}
 
-	/* save the flags also in descriptor */
-	if (!ret)
-		desc->flags = flags;
-
 	return ret;
 }
 
-int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
+int dm_gpio_clrset_flags(struct gpio_desc *desc, ulong clr, ulong set)
 {
+	ulong flags;
 	int ret;
 
 	ret = check_reserved(desc, "set_dir_flags");
 	if (ret)
 		return ret;
 
-	/* combine the requested flags (for IN/OUT) and the descriptor flags */
-	flags |= desc->flags;
-	ret = _dm_gpio_set_dir_flags(desc, flags);
+	flags = (desc->flags & ~clr) | set;
 
-	return ret;
+	ret = _dm_gpio_set_flags(desc, flags);
+	if (ret)
+		return ret;
+
+	/* save the flags also in descriptor */
+	desc->flags = flags;
+
+	return 0;
+}
+
+int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
+{
+	/* combine the requested flags (for IN/OUT) and the descriptor flags */
+	return dm_gpio_clrset_flags(desc, GPIOD_MASK_DIR, flags);
 }
 
 int dm_gpio_set_dir(struct gpio_desc *desc)
@@ -680,42 +721,57 @@
 	if (ret)
 		return ret;
 
-	return _dm_gpio_set_dir_flags(desc, desc->flags);
+	return _dm_gpio_set_flags(desc, desc->flags);
 }
 
-int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags)
+int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr,
+			  ulong set)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < count; i++) {
+		ret = dm_gpio_clrset_flags(&desc[i], clr, set);
+		if (ret)
+			return log_ret(ret);
+	}
+
+	return 0;
+}
+
+int dm_gpio_get_flags(struct gpio_desc *desc, ulong *flagsp)
 {
 	struct udevice *dev = desc->dev;
 	int ret, value;
-	struct dm_gpio_ops *ops = gpio_get_ops(dev);
-	ulong dir_flags;
+	const struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	ulong flags;
 
-	ret = check_reserved(desc, "get_dir_flags");
+	ret = check_reserved(desc, "get_flags");
 	if (ret)
 		return ret;
 
 	/* GPIOD_ are directly provided by driver except GPIOD_ACTIVE_LOW */
-	if (ops->get_dir_flags) {
-		ret = ops->get_dir_flags(dev, desc->offset, &dir_flags);
+	if (ops->get_flags) {
+		ret = ops->get_flags(dev, desc->offset, &flags);
 		if (ret)
 			return ret;
 
 		/* GPIOD_ACTIVE_LOW is saved in desc->flags */
-		value = dir_flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
+		value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
 		if (desc->flags & GPIOD_ACTIVE_LOW)
 			value = !value;
-		dir_flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
-		dir_flags |= (desc->flags & GPIOD_ACTIVE_LOW);
+		flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
+		flags |= (desc->flags & GPIOD_ACTIVE_LOW);
 		if (value)
-			dir_flags |= GPIOD_IS_OUT_ACTIVE;
+			flags |= GPIOD_IS_OUT_ACTIVE;
 	} else {
-		dir_flags = desc->flags;
+		flags = desc->flags;
 		/* only GPIOD_IS_OUT_ACTIVE is provided by uclass */
-		dir_flags &= ~GPIOD_IS_OUT_ACTIVE;
+		flags &= ~GPIOD_IS_OUT_ACTIVE;
 		if ((desc->flags & GPIOD_IS_OUT) && _gpio_get_value(desc))
-			dir_flags |= GPIOD_IS_OUT_ACTIVE;
+			flags |= GPIOD_IS_OUT_ACTIVE;
 	}
-	*flags = dir_flags;
+	*flagsp = flags;
 
 	return 0;
 }
@@ -785,7 +841,7 @@
 			const char **namep)
 {
 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
-	struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	const struct dm_gpio_ops *ops = gpio_get_ops(dev);
 
 	BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
 	if (!device_active(dev))
@@ -822,7 +878,7 @@
 
 int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
 {
-	struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	const struct dm_gpio_ops *ops = gpio_get_ops(dev);
 	struct gpio_dev_priv *priv;
 	char *str = buf;
 	int func;
@@ -862,7 +918,7 @@
 #if CONFIG_IS_ENABLED(ACPIGEN)
 int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
 {
-	struct dm_gpio_ops *ops;
+	const struct dm_gpio_ops *ops;
 
 	memset(gpio, '\0', sizeof(*gpio));
 	if (!dm_gpio_is_valid(desc)) {
@@ -949,6 +1005,71 @@
 	return vector;
 }
 
+int dm_gpio_get_values_as_int_base3(struct gpio_desc *desc_list,
+				    int count)
+{
+	static const char tristate[] = "01z";
+	enum {
+		PULLUP,
+		PULLDOWN,
+
+		NUM_OPTIONS,
+	};
+	int vals[NUM_OPTIONS];
+	uint mask;
+	uint vector = 0;
+	int ret, i;
+
+	/*
+	 * Limit to 19 digits which should be plenty. This avoids overflow of a
+	 * 32-bit int
+	 */
+	assert(count < 20);
+
+	for (i = 0; i < NUM_OPTIONS; i++) {
+		uint flags = GPIOD_IS_IN;
+
+		flags |= (i == PULLDOWN) ? GPIOD_PULL_DOWN : GPIOD_PULL_UP;
+		ret = dm_gpios_clrset_flags(desc_list, count, GPIOD_MASK_PULL,
+					    flags);
+		if (ret)
+			return log_msg_ret("pu", ret);
+
+		/* Give the lines time to settle */
+		udelay(10);
+
+		ret = dm_gpio_get_values_as_int(desc_list, count);
+		if (ret < 0)
+			return log_msg_ret("get1", ret);
+		vals[i] = ret;
+	}
+
+	log_debug("values: %x %x, count = %d\n", vals[0], vals[1], count);
+	for (i = count - 1, mask = 1 << i; i >= 0; i--, mask >>= 1) {
+		uint pd = vals[PULLDOWN] & mask ? 1 : 0;
+		uint pu = vals[PULLUP] & mask ? 1 : 0;
+		uint digit;
+
+		/*
+		 * Get value with internal pulldown active. If this is 1 then
+		 * there is a stronger external pullup, which we call 1. If not
+		 * then call it 0.
+		 *
+		 * If the values differ then the pin is floating so we call
+		 * this a 2.
+		 */
+		if (pu == pd)
+			digit = pd;
+		else
+			digit = 2;
+		log_debug("%c ", tristate[digit]);
+		vector = 3 * vector + digit;
+	}
+	log_debug("vector=%d\n", vector);
+
+	return vector;
+}
+
 /**
  * gpio_request_tail: common work for requesting a gpio.
  *
@@ -1011,7 +1132,10 @@
 		debug("%s: dm_gpio_requestf failed\n", __func__);
 		goto err;
 	}
-	ret = dm_gpio_set_dir_flags(desc, flags);
+
+	/* Keep any direction flags provided by the devicetree */
+	ret = dm_gpio_set_dir_flags(desc,
+				    flags | (desc->flags & GPIOD_MASK_DIR));
 	if (ret) {
 		debug("%s: dm_gpio_set_dir failed\n", __func__);
 		goto err;
@@ -1024,6 +1148,7 @@
 	return ret;
 }
 
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 static int _gpio_request_by_name_nodev(ofnode node, const char *list_name,
 				       int index, struct gpio_desc *desc,
 				       int flags, bool add_index)
@@ -1110,6 +1235,7 @@
 
 	return ret;
 }
+#endif /* OF_PLATDATA */
 
 int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc)
 {
@@ -1306,10 +1432,10 @@
 			ops->get_function += gd->reloc_off;
 		if (ops->xlate)
 			ops->xlate += gd->reloc_off;
-		if (ops->set_dir_flags)
-			ops->set_dir_flags += gd->reloc_off;
-		if (ops->get_dir_flags)
-			ops->get_dir_flags += gd->reloc_off;
+		if (ops->set_flags)
+			ops->set_flags += gd->reloc_off;
+		if (ops->get_flags)
+			ops->get_flags += gd->reloc_off;
 
 		reloc_done++;
 	}
diff --git a/drivers/gpio/intel_gpio.c b/drivers/gpio/intel_gpio.c
index eda9548..ab46a94 100644
--- a/drivers/gpio/intel_gpio.c
+++ b/drivers/gpio/intel_gpio.c
@@ -3,6 +3,8 @@
  * Copyright 2019 Google LLC
  */
 
+#define LOG_CATEGORY	UCLASS_GPIO
+
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
@@ -23,38 +25,6 @@
 #include <dm/acpi.h>
 #include <dt-bindings/gpio/x86-gpio.h>
 
-static int intel_gpio_direction_input(struct udevice *dev, uint offset)
-{
-	struct udevice *pinctrl = dev_get_parent(dev);
-	uint config_offset;
-
-	config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
-
-	pcr_clrsetbits32(pinctrl, config_offset,
-			 PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
-				  PAD_CFG0_RX_DISABLE,
-			 PAD_CFG0_MODE_GPIO | PAD_CFG0_TX_DISABLE);
-
-	return 0;
-}
-
-static int intel_gpio_direction_output(struct udevice *dev, uint offset,
-				       int value)
-{
-	struct udevice *pinctrl = dev_get_parent(dev);
-	uint config_offset;
-
-	config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
-
-	pcr_clrsetbits32(pinctrl, config_offset,
-			 PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
-				  PAD_CFG0_TX_DISABLE | PAD_CFG0_TX_STATE,
-			 PAD_CFG0_MODE_GPIO | PAD_CFG0_RX_DISABLE |
-				  (value ? PAD_CFG0_TX_STATE : 0));
-
-	return 0;
-}
-
 static int intel_gpio_get_value(struct udevice *dev, uint offset)
 {
 	struct udevice *pinctrl = dev_get_parent(dev);
@@ -130,6 +100,41 @@
 	return 0;
 }
 
+static int intel_gpio_set_flags(struct udevice *dev, unsigned int offset,
+				ulong flags)
+{
+	struct udevice *pinctrl = dev_get_parent(dev);
+	u32 bic0 = 0, bic1 = 0;
+	u32 or0, or1;
+	uint config_offset;
+
+	config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
+
+	if (flags & GPIOD_IS_OUT) {
+		bic0 |= PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
+			PAD_CFG0_TX_DISABLE;
+		or0 |= PAD_CFG0_MODE_GPIO | PAD_CFG0_RX_DISABLE;
+	} else if (flags & GPIOD_IS_IN) {
+		bic0 |= PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
+			PAD_CFG0_RX_DISABLE;
+		or0 |= PAD_CFG0_MODE_GPIO | PAD_CFG0_TX_DISABLE;
+	}
+	if (flags & GPIOD_PULL_UP) {
+		bic1 |= PAD_CFG1_PULL_MASK;
+		or1 |= PAD_CFG1_PULL_UP_20K;
+	} else if (flags & GPIOD_PULL_DOWN) {
+		bic1 |= PAD_CFG1_PULL_MASK;
+		or1 |= PAD_CFG1_PULL_DN_20K;
+	}
+
+	pcr_clrsetbits32(pinctrl, PAD_CFG0_OFFSET(config_offset), bic0, or0);
+	pcr_clrsetbits32(pinctrl, PAD_CFG1_OFFSET(config_offset), bic1, or1);
+	log_debug("%s: flags=%lx, offset=%x, config_offset=%x, %x/%x %x/%x\n",
+		  dev->name, flags, offset, config_offset, bic0, or0, bic1, or1);
+
+	return 0;
+}
+
 #if CONFIG_IS_ENABLED(ACPIGEN)
 static int intel_gpio_get_acpi(const struct gpio_desc *desc,
 			       struct acpi_gpio *gpio)
@@ -177,12 +182,11 @@
 }
 
 static const struct dm_gpio_ops gpio_intel_ops = {
-	.direction_input	= intel_gpio_direction_input,
-	.direction_output	= intel_gpio_direction_output,
 	.get_value		= intel_gpio_get_value,
 	.set_value		= intel_gpio_set_value,
 	.get_function		= intel_gpio_get_function,
 	.xlate			= intel_gpio_xlate,
+	.set_flags		= intel_gpio_set_flags,
 #if CONFIG_IS_ENABLED(ACPIGEN)
 	.get_acpi		= intel_gpio_get_acpi,
 #endif
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index dc8d506..d008fdd 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -19,42 +19,51 @@
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/gpio/sandbox-gpio.h>
 
-
 struct gpio_state {
 	const char *label;	/* label given by requester */
-	ulong dir_flags;	/* dir_flags (GPIOD_...) */
+	ulong flags;		/* flags (GPIOD_...) */
 };
 
-/* Access routines for GPIO dir flags */
-static ulong *get_gpio_dir_flags(struct udevice *dev, unsigned int offset)
+/* Access routines for GPIO info */
+static struct gpio_state *get_gpio_state(struct udevice *dev, uint offset)
 {
 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 	struct gpio_state *state = dev_get_priv(dev);
 
 	if (offset >= uc_priv->gpio_count) {
-		static ulong invalid_dir_flags;
 		printf("sandbox_gpio: error: invalid gpio %u\n", offset);
-		return &invalid_dir_flags;
+		return NULL;
 	}
 
-	return &state[offset].dir_flags;
+	return &state[offset];
+}
+
+/* Access routines for GPIO flags */
+static ulong *get_gpio_flags(struct udevice *dev, unsigned int offset)
+{
+	struct gpio_state *state = get_gpio_state(dev, offset);
+
+	if (!state)
+		return NULL;
+
+	return &state->flags;
 
 }
 
 static int get_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag)
 {
-	return (*get_gpio_dir_flags(dev, offset) & flag) != 0;
+	return (*get_gpio_flags(dev, offset) & flag) != 0;
 }
 
 static int set_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag,
 			 int value)
 {
-	ulong *gpio = get_gpio_dir_flags(dev, offset);
+	struct gpio_state *state = get_gpio_state(dev, offset);
 
 	if (value)
-		*gpio |= flag;
+		state->flags |= flag;
 	else
-		*gpio &= ~flag;
+		state->flags &= ~flag;
 
 	return 0;
 }
@@ -65,14 +74,31 @@
 
 int sandbox_gpio_get_value(struct udevice *dev, unsigned offset)
 {
+	struct gpio_state *state = get_gpio_state(dev, offset);
+	bool val;
+
 	if (get_gpio_flag(dev, offset, GPIOD_IS_OUT))
 		debug("sandbox_gpio: get_value on output gpio %u\n", offset);
-	return get_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE);
+
+	if (state->flags & GPIOD_EXT_DRIVEN) {
+		val = state->flags & GPIOD_EXT_HIGH;
+	} else {
+		if (state->flags & GPIOD_EXT_PULL_UP)
+			val = true;
+		else if (state->flags & GPIOD_EXT_PULL_DOWN)
+			val = false;
+		else
+			val = state->flags & GPIOD_PULL_UP;
+	}
+
+	return val;
 }
 
 int sandbox_gpio_set_value(struct udevice *dev, unsigned offset, int value)
 {
-	return set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE, value);
+	set_gpio_flag(dev, offset, GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
+
+	return 0;
 }
 
 int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset)
@@ -83,20 +109,23 @@
 int sandbox_gpio_set_direction(struct udevice *dev, unsigned offset, int output)
 {
 	set_gpio_flag(dev, offset, GPIOD_IS_OUT, output);
-	set_gpio_flag(dev, offset, GPIOD_IS_IN, !(output));
+	set_gpio_flag(dev, offset, GPIOD_IS_IN, !output);
 
 	return 0;
 }
 
-ulong sandbox_gpio_get_dir_flags(struct udevice *dev, unsigned int offset)
+ulong sandbox_gpio_get_flags(struct udevice *dev, uint offset)
 {
-	return *get_gpio_dir_flags(dev, offset);
+	ulong flags = *get_gpio_flags(dev, offset);
+
+	return flags & ~GPIOD_SANDBOX_MASK;
 }
 
-int sandbox_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
-			       ulong flags)
+int sandbox_gpio_set_flags(struct udevice *dev, uint offset, ulong flags)
 {
-	*get_gpio_dir_flags(dev, offset) = flags;
+	struct gpio_state *state = get_gpio_state(dev, offset);
+
+	state->flags = flags;
 
 	return 0;
 }
@@ -117,10 +146,19 @@
 static int sb_gpio_direction_output(struct udevice *dev, unsigned offset,
 				    int value)
 {
+	int ret;
+
 	debug("%s: offset:%u, value = %d\n", __func__, offset, value);
 
-	return sandbox_gpio_set_direction(dev, offset, 1) |
-		sandbox_gpio_set_value(dev, offset, value);
+	ret = sandbox_gpio_set_direction(dev, offset, 1);
+	if (ret)
+		return ret;
+	ret = set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE |
+			    GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
+	if (ret)
+		return ret;
+
+	return 0;
 }
 
 /* read GPIO IN value of port 'offset' */
@@ -134,6 +172,8 @@
 /* write GPIO OUT value to port 'offset' */
 static int sb_gpio_set_value(struct udevice *dev, unsigned offset, int value)
 {
+	int ret;
+
 	debug("%s: offset:%u, value = %d\n", __func__, offset, value);
 
 	if (!sandbox_gpio_get_direction(dev, offset)) {
@@ -142,7 +182,12 @@
 		return -1;
 	}
 
-	return sandbox_gpio_set_value(dev, offset, value);
+	ret = set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE |
+			    GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
+	if (ret)
+		return ret;
+
+	return 0;
 }
 
 static int sb_gpio_get_function(struct udevice *dev, unsigned offset)
@@ -177,33 +222,30 @@
 	return 0;
 }
 
-static int sb_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
-				 ulong flags)
+static int sb_gpio_set_flags(struct udevice *dev, unsigned int offset,
+			     ulong flags)
 {
-	ulong *dir_flags;
+	debug("%s: offset:%u, flags = %lx\n", __func__, offset, flags);
+	struct gpio_state *state = get_gpio_state(dev, offset);
 
-	debug("%s: offset:%u, dir_flags = %lx\n", __func__, offset, flags);
-
-	dir_flags = get_gpio_dir_flags(dev, offset);
-
-	/*
-	 * For testing purposes keep the output value when switching to input.
-	 * This allows us to manipulate the input value via the gpio command.
-	 */
-	if (flags & GPIOD_IS_IN)
-		*dir_flags = (flags & ~GPIOD_IS_OUT_ACTIVE) |
-			     (*dir_flags & GPIOD_IS_OUT_ACTIVE);
-	else
-		*dir_flags = flags;
+	if (flags & GPIOD_IS_OUT) {
+		flags |= GPIOD_EXT_DRIVEN;
+		if (flags & GPIOD_IS_OUT_ACTIVE)
+			flags |= GPIOD_EXT_HIGH;
+		else
+			flags &= ~GPIOD_EXT_HIGH;
+	} else {
+		flags |= state->flags & GPIOD_SANDBOX_MASK;
+	}
+	state->flags = flags;
 
 	return 0;
 }
 
-static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
-				 ulong *flags)
+static int sb_gpio_get_flags(struct udevice *dev, uint offset, ulong *flagsp)
 {
 	debug("%s: offset:%u\n", __func__, offset);
-	*flags = *get_gpio_dir_flags(dev, offset);
+	*flagsp = *get_gpio_flags(dev, offset) & ~GPIOD_SANDBOX_MASK;
 
 	return 0;
 }
@@ -272,8 +314,8 @@
 	.set_value		= sb_gpio_set_value,
 	.get_function		= sb_gpio_get_function,
 	.xlate			= sb_gpio_xlate,
-	.set_dir_flags		= sb_gpio_set_dir_flags,
-	.get_dir_flags		= sb_gpio_get_dir_flags,
+	.set_flags		= sb_gpio_set_flags,
+	.get_flags		= sb_gpio_get_flags,
 #if CONFIG_IS_ENABLED(ACPIGEN)
 	.get_acpi		= sb_gpio_get_acpi,
 #endif
@@ -456,7 +498,7 @@
 	return pin_name;
 }
 
-static char *get_dir_flags_string(ulong flags)
+static char *get_flags_string(ulong flags)
 {
 	if (flags & GPIOD_OPEN_DRAIN)
 		return "drive-open-drain";
@@ -475,7 +517,7 @@
 {
 	struct udevice *gpio_dev;
 	unsigned int gpio_idx;
-	ulong dir_flags;
+	ulong flags;
 	int function;
 
 	/* look up for the bank which owns the requested pin */
@@ -484,11 +526,11 @@
 		snprintf(buf, size, "Error");
 	} else {
 		function = sb_gpio_get_function(gpio_dev, gpio_idx);
-		dir_flags = *get_gpio_dir_flags(gpio_dev, gpio_idx);
+		flags = *get_gpio_flags(gpio_dev, gpio_idx);
 
 		snprintf(buf, size, "gpio %s %s",
 			 function == GPIOF_OUTPUT ? "output" : "input",
-			 get_dir_flags_string(dir_flags));
+			 get_flags_string(flags));
 	}
 
 	return 0;
diff --git a/drivers/gpio/stm32_gpio.c b/drivers/gpio/stm32_gpio.c
index 7184db3..125c237 100644
--- a/drivers/gpio/stm32_gpio.c
+++ b/drivers/gpio/stm32_gpio.c
@@ -191,8 +191,8 @@
 	return GPIOF_FUNC;
 }
 
-static int stm32_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
-				    ulong flags)
+static int stm32_gpio_set_flags(struct udevice *dev, unsigned int offset,
+				ulong flags)
 {
 	struct stm32_gpio_priv *priv = dev_get_priv(dev);
 	struct stm32_gpio_regs *regs = priv->regs;
@@ -203,12 +203,13 @@
 		return idx;
 
 	if (flags & GPIOD_IS_OUT) {
-		int value = GPIOD_FLAGS_OUTPUT(flags);
+		bool value = flags & GPIOD_IS_OUT_ACTIVE;
 
 		if (flags & GPIOD_OPEN_DRAIN)
 			stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_OD);
 		else
 			stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_PP);
+
 		stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_OUT);
 		writel(BSRR_BIT(idx, value), &regs->bsrr);
 
@@ -223,8 +224,8 @@
 	return 0;
 }
 
-static int stm32_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
-				    ulong *flags)
+static int stm32_gpio_get_flags(struct udevice *dev, unsigned int offset,
+				ulong *flagsp)
 {
 	struct stm32_gpio_priv *priv = dev_get_priv(dev);
 	struct stm32_gpio_regs *regs = priv->regs;
@@ -259,7 +260,7 @@
 	default:
 		break;
 	}
-	*flags = dir_flags;
+	*flagsp = dir_flags;
 
 	return 0;
 }
@@ -270,8 +271,8 @@
 	.get_value		= stm32_gpio_get_value,
 	.set_value		= stm32_gpio_set_value,
 	.get_function		= stm32_gpio_get_function,
-	.set_dir_flags		= stm32_gpio_set_dir_flags,
-	.get_dir_flags		= stm32_gpio_get_dir_flags,
+	.set_flags		= stm32_gpio_set_flags,
+	.get_flags		= stm32_gpio_get_flags,
 };
 
 static int gpio_stm32_probe(struct udevice *dev)
diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c
index 1a8d0a3..fe7a59d 100644
--- a/drivers/pinctrl/pinctrl-stmfx.c
+++ b/drivers/pinctrl/pinctrl-stmfx.c
@@ -163,12 +163,14 @@
 	return stmfx_write_reg(dev, STMFX_REG_GPIO_DIR, offset, 1);
 }
 
-static int stmfx_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
-				    ulong flags)
+static int stmfx_gpio_set_flags(struct udevice *dev, unsigned int offset,
+				ulong flags)
 {
 	int ret = -ENOTSUPP;
 
 	if (flags & GPIOD_IS_OUT) {
+		bool value = flags & GPIOD_IS_OUT_ACTIVE;
+
 		if (flags & GPIOD_OPEN_SOURCE)
 			return -ENOTSUPP;
 		if (flags & GPIOD_OPEN_DRAIN)
@@ -177,8 +179,7 @@
 			ret = stmfx_conf_set_type(dev, offset, 1);
 		if (ret)
 			return ret;
-		ret = stmfx_gpio_direction_output(dev, offset,
-						  GPIOD_FLAGS_OUTPUT(flags));
+		ret = stmfx_gpio_direction_output(dev, offset, value);
 	} else if (flags & GPIOD_IS_IN) {
 		ret = stmfx_gpio_direction_input(dev, offset);
 		if (ret)
@@ -199,8 +200,8 @@
 	return ret;
 }
 
-static int stmfx_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
-				    ulong *flags)
+static int stmfx_gpio_get_flags(struct udevice *dev, unsigned int offset,
+				ulong *flagsp)
 {
 	ulong dir_flags = 0;
 	int ret;
@@ -233,7 +234,7 @@
 				dir_flags |= GPIOD_PULL_DOWN;
 		}
 	}
-	*flags = dir_flags;
+	*flagsp = dir_flags;
 
 	return 0;
 }
@@ -266,8 +267,8 @@
 	.get_function = stmfx_gpio_get_function,
 	.direction_input = stmfx_gpio_direction_input,
 	.direction_output = stmfx_gpio_direction_output,
-	.set_dir_flags = stmfx_gpio_set_dir_flags,
-	.get_dir_flags = stmfx_gpio_get_dir_flags,
+	.set_flags = stmfx_gpio_set_flags,
+	.get_flags = stmfx_gpio_get_flags,
 };
 
 U_BOOT_DRIVER(stmfx_gpio) = {
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 82294cb..2cb0500 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -128,6 +128,12 @@
 #define GPIOD_PULL_UP		BIT(7)	/* GPIO has pull-up enabled */
 #define GPIOD_PULL_DOWN		BIT(8)	/* GPIO has pull-down enabled */
 
+/* Flags for updating the above */
+#define GPIOD_MASK_DIR		(GPIOD_IS_OUT | GPIOD_IS_IN | \
+					GPIOD_IS_OUT_ACTIVE)
+#define GPIOD_MASK_DSTYPE	(GPIOD_OPEN_DRAIN | GPIOD_OPEN_SOURCE)
+#define GPIOD_MASK_PULL		(GPIOD_PULL_UP | GPIOD_PULL_DOWN)
+
 	uint offset;		/* GPIO offset within the device */
 	/*
 	 * We could consider adding the GPIO label in here. Possibly we could
@@ -135,12 +141,6 @@
 	 */
 };
 
-/* helper to compute the value of the gpio output */
-#define GPIOD_FLAGS_OUTPUT_MASK (GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE)
-#define GPIOD_FLAGS_OUTPUT(flags) \
-	(((((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_IS_OUT_ACTIVE) || \
-	  (((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_ACTIVE_LOW)))
-
 /**
  * dm_gpio_is_valid() - Check if a GPIO is valid
  *
@@ -260,10 +260,32 @@
 struct dm_gpio_ops {
 	int (*request)(struct udevice *dev, unsigned offset, const char *label);
 	int (*rfree)(struct udevice *dev, unsigned int offset);
+
+	/**
+	 * direction_input() - deprecated
+	 *
+	 * Equivalent to set_flags(...GPIOD_IS_IN)
+	 */
 	int (*direction_input)(struct udevice *dev, unsigned offset);
+
+	/**
+	 * direction_output() - deprecated
+	 *
+	 * Equivalent to set_flags(...GPIOD_IS_OUT) with GPIOD_IS_OUT_ACTIVE
+	 * also set if @value
+	 */
 	int (*direction_output)(struct udevice *dev, unsigned offset,
 				int value);
+
 	int (*get_value)(struct udevice *dev, unsigned offset);
+
+	/**
+	 * set_value() - Sets the GPIO value of an output
+	 *
+	 * If the driver provides an @set_flags() method then that is used
+	 * in preference to this, with GPIOD_IS_OUT_ACTIVE set according to
+	 * @value.
+	 */
 	int (*set_value)(struct udevice *dev, unsigned offset, int value);
 	/**
 	 * get_function() Get the GPIO function
@@ -301,35 +323,54 @@
 		     struct ofnode_phandle_args *args);
 
 	/**
-	 * set_dir_flags() - Set GPIO dir flags
+	 * set_flags() - Adjust GPIO flags
 	 *
 	 * This function should set up the GPIO configuration according to the
-	 * information provide by the direction flags bitfield.
+	 * information provided by @flags.
 	 *
-	 * This method is optional.
+	 * If any flags cannot be set (e.g. the driver or hardware does not
+	 * support them or this particular GPIO does not have the requested
+	 * feature), the driver should return -EINVAL.
+	 *
+	 * The uclass checks that flags do not obviously conflict (e.g. input
+	 * and output). If the driver finds other conflicts it should return
+	 * -ERECALLCONFLICT
+	 *
+	 * Note that GPIOD_ACTIVE_LOW should be ignored, since the uclass
+	 * adjusts for it automatically. For example, for an output GPIO,
+	 * GPIOD_ACTIVE_LOW causes GPIOD_IS_OUT_ACTIVE to be inverted by the
+	 * uclass, so the driver always sees the value that should be set at the
+	 * pin (1=high, 0=low).
+	 *
+	 * This method is required and should be implemented by new drivers. At
+	 * some point, it will supersede direction_input() and
+	 * direction_output(), which wil be removed.
 	 *
 	 * @dev:	GPIO device
 	 * @offset:	GPIO offset within that device
-	 * @flags:	GPIO configuration to use
-	 * @return 0 if OK, -ve on error
+	 * @flags:	New flags value (GPIOD_...)
+	 *
+	 * @return 0 if OK, -EINVAL if unsupported, -ERECALLCONFLICT if flags
+	 *	conflict in some *	non-obvious way and were not applied,
+	 *	other -ve on error
 	 */
-	int (*set_dir_flags)(struct udevice *dev, unsigned int offset,
-			     ulong flags);
+	int (*set_flags)(struct udevice *dev, unsigned int offset, ulong flags);
 
 	/**
-	 * get_dir_flags() - Get GPIO dir flags
+	 * get_flags() - Get GPIO flags
 	 *
-	 * This function return the GPIO direction flags used.
+	 * This function return the GPIO flags used. It should read this from
+	 * the hardware directly.
 	 *
 	 * This method is optional.
 	 *
 	 * @dev:	GPIO device
 	 * @offset:	GPIO offset within that device
-	 * @flags:	place to put the used direction flags by GPIO
+	 * @flagsp:	place to put the current flags value
 	 * @return 0 if OK, -ve on error
 	 */
-	int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
-			     ulong *flags);
+	int (*get_flags)(struct udevice *dev, unsigned int offset,
+			 ulong *flagsp);
 
 #if CONFIG_IS_ENABLED(ACPIGEN)
 	/**
@@ -457,6 +498,31 @@
 int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count);
 
 /**
+ * dm_gpio_get_values_as_int_base3() - Create a base-3 int from a list of GPIOs
+ *
+ * This uses pull-ups/pull-downs to figure out whether a GPIO line is externally
+ * pulled down, pulled up or floating. This allows three different strap values
+ * for each pin:
+ *    0 : external pull-down
+ *    1 : external pull-up
+ *    2 : floating
+ *
+ * With this it is possible to obtain more combinations from the same number of
+ * strapping pins, when compared to dm_gpio_get_values_as_int(). The external
+ * pull resistors should be made stronger that the internal SoC pull resistors,
+ * for this to work.
+ *
+ * With 2 pins, 6 combinations are possible, compared with 4
+ * With 3 pins, 27 are possible, compared with 8
+ *
+ * @desc_list: List of GPIOs to collect
+ * @count: Number of GPIOs
+ * @return resulting integer value, or -ve on error
+ */
+int dm_gpio_get_values_as_int_base3(struct gpio_desc *desc_list,
+				    int count);
+
+/**
  * gpio_claim_vector() - claim a number of GPIOs for input
  *
  * @gpio_num_array:	array of gpios to claim, terminated by -1
@@ -652,6 +718,25 @@
 int dm_gpio_set_dir(struct gpio_desc *desc);
 
 /**
+ * dm_gpio_clrset_flags() - Update flags
+ *
+ * This updates the flags as directled. Note that desc->flags is updated by this
+ * function on success. If any changes cannot be made, best efforts are made.
+ *
+ * By use of @clr and @set any of flags can be individually updated, or left
+ * alone
+ *
+ * @desc:	GPIO description containing device, offset and flags,
+ *		previously returned by gpio_request_by_name()
+ * @clr:	Flags to clear (GPIOD_...)
+ * @set:	Flags to set (GPIOD_...)
+ * @return 0 if OK, -EINVAL if the flags had obvious conflicts,
+ * -ERECALLCONFLICT if there was a non-obvious hardware conflict when attempting
+ * to set the flags
+ */
+int dm_gpio_clrset_flags(struct gpio_desc *desc, ulong clr, ulong set);
+
+/**
  * dm_gpio_set_dir_flags() - Set direction using description and added flags
  *
  * This sets up the direction according to the provided flags and the GPIO
@@ -666,16 +751,31 @@
 int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);
 
 /**
- * dm_gpio_get_dir_flags() - Get direction flags
+ * dm_gpios_clrset_flags() - Sets flags for a set of GPIOs
  *
- * read the current direction flags
+ * This clears and sets flags individually for each GPIO.
+ *
+ * @desc:	List of GPIOs to update
+ * @count:	Number of GPIOs in the list
+ * @clr:	Flags to clear (GPIOD_...), e.g. GPIOD_MASK_DIR if you are
+ *		changing the direction
+ * @set:	Flags to set (GPIOD_...)
+ * @return 0 if OK, -ve on error
+ */
+int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr,
+			  ulong set);
+
+/**
+ * dm_gpio_get_flags() - Get flags
+ *
+ * Read the current flags
  *
  * @desc:	GPIO description containing device, offset and flags,
  *		previously returned by gpio_request_by_name()
  * @flags:	place to put the used flags
  * @return 0 if OK, -ve on error, in which case desc->flags is not updated
  */
-int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
+int dm_gpio_get_flags(struct gpio_desc *desc, ulong *flags);
 
 /**
  * gpio_get_number() - Get the global GPIO number of a GPIO
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
index d7b85e7..33ae987 100644
--- a/test/dm/gpio.c
+++ b/test/dm/gpio.c
@@ -80,15 +80,15 @@
 
 	/* Make it an open drain output, and reset it */
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
-		    sandbox_gpio_get_dir_flags(dev, offset));
-	ut_assertok(ops->set_dir_flags(dev, offset,
-				       GPIOD_IS_OUT | GPIOD_OPEN_DRAIN));
+		    sandbox_gpio_get_flags(dev, offset));
+	ut_assertok(ops->set_flags(dev, offset,
+				   GPIOD_IS_OUT | GPIOD_OPEN_DRAIN));
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
-		    sandbox_gpio_get_dir_flags(dev, offset));
-	ut_assertok(ops->set_dir_flags(dev, offset,
-				       GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
+		    sandbox_gpio_get_flags(dev, offset));
+	ut_assertok(ops->set_flags(dev, offset,
+				   GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
-		    sandbox_gpio_get_dir_flags(dev, offset));
+		    sandbox_gpio_get_flags(dev, offset));
 
 	/* Make it an input */
 	ut_assertok(ops->direction_input(dev, offset));
@@ -176,54 +176,64 @@
 
 	/* GPIO 0 is (GPIO_OUT|GPIO_OPEN_DRAIN) */
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
-		    sandbox_gpio_get_dir_flags(gpio_c, 0));
+		    sandbox_gpio_get_flags(gpio_c, 0));
 
-	/* Set it as output high, should become an input */
+	/* Set it as output high */
 	ut_assertok(dm_gpio_set_value(&desc_list[0], 1));
-	ut_assertok(gpio_get_status(gpio_c, 0, buf, sizeof(buf)));
-	ut_asserteq_str("c0: input: 0 [x] a-test.test3-gpios0", buf);
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN | GPIOD_IS_OUT_ACTIVE,
+		    sandbox_gpio_get_flags(gpio_c, 0));
 
-	/* Set it as output low, should become output low */
+	/* Set it as output low */
 	ut_assertok(dm_gpio_set_value(&desc_list[0], 0));
-	ut_assertok(gpio_get_status(gpio_c, 0, buf, sizeof(buf)));
-	ut_asserteq_str("c0: output: 0 [x] a-test.test3-gpios0", buf);
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
+		    sandbox_gpio_get_flags(gpio_c, 0));
 
 	/* GPIO 1 is (GPIO_OUT|GPIO_OPEN_SOURCE) */
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
-		    sandbox_gpio_get_dir_flags(gpio_c, 1));
+		    sandbox_gpio_get_flags(gpio_c, 1));
 
 	/* Set it as output high, should become output high */
 	ut_assertok(dm_gpio_set_value(&desc_list[1], 1));
 	ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
 	ut_asserteq_str("c1: output: 1 [x] a-test.test3-gpios1", buf);
 
-	/* Set it as output low, should become an input */
+	/* Set it as output low */
 	ut_assertok(dm_gpio_set_value(&desc_list[1], 0));
-	ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
-	ut_asserteq_str("c1: input: 1 [x] a-test.test3-gpios1", buf);
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
+		    sandbox_gpio_get_flags(gpio_c, 1));
 
-	/* GPIO 6 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN) */
-	ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
-		    sandbox_gpio_get_dir_flags(gpio_c, 6));
+	ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
+	ut_asserteq_str("c1: output: 0 [x] a-test.test3-gpios1", buf);
+
+	/*
+	 * GPIO 6 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN). Looking at it
+	 * directlt from the driver, we get GPIOD_IS_OUT_ACTIVE also, since it
+	 * is active low
+	 */
+	ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN |
+		    GPIOD_IS_OUT_ACTIVE,
+		    sandbox_gpio_get_flags(gpio_c, 6));
 
 	/* Set it as output high, should become output low */
 	ut_assertok(dm_gpio_set_value(&desc_list[6], 1));
 	ut_assertok(gpio_get_status(gpio_c, 6, buf, sizeof(buf)));
 	ut_asserteq_str("c6: output: 0 [x] a-test.test3-gpios6", buf);
 
-	/* Set it as output low, should become an input */
+	/* Set it as output low */
 	ut_assertok(dm_gpio_set_value(&desc_list[6], 0));
-	ut_assertok(gpio_get_status(gpio_c, 6, buf, sizeof(buf)));
-	ut_asserteq_str("c6: input: 0 [x] a-test.test3-gpios6", buf);
+	ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN |
+		    GPIOD_IS_OUT_ACTIVE,
+		    sandbox_gpio_get_flags(gpio_c, 6));
 
 	/* GPIO 7 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_SOURCE) */
-	ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
-		    sandbox_gpio_get_dir_flags(gpio_c, 7));
+	ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE |
+		    GPIOD_IS_OUT_ACTIVE,
+		    sandbox_gpio_get_flags(gpio_c, 7));
 
-	/* Set it as output high, should become an input */
+	/* Set it as output high */
 	ut_assertok(dm_gpio_set_value(&desc_list[7], 1));
-	ut_assertok(gpio_get_status(gpio_c, 7, buf, sizeof(buf)));
-	ut_asserteq_str("c7: input: 0 [x] a-test.test3-gpios7", buf);
+	ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
+		    sandbox_gpio_get_flags(gpio_c, 7));
 
 	/* Set it as output low, should become output high */
 	ut_assertok(dm_gpio_set_value(&desc_list[7], 0));
@@ -363,12 +373,12 @@
 	ut_assertok(gpio_free_list(dev, desc_list, 3));
 
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
-		    sandbox_gpio_get_dir_flags(gpio_a, 1));
+		    sandbox_gpio_get_flags(gpio_a, 1));
 	ut_asserteq(6, gpio_request_list_by_name(dev, "test2-gpios", desc_list,
 						 ARRAY_SIZE(desc_list), 0));
 
 	/* This was set to output previously but flags resetted to 0 = INPUT */
-	ut_asserteq(0, sandbox_gpio_get_dir_flags(gpio_a, 1));
+	ut_asserteq(0, sandbox_gpio_get_flags(gpio_a, 1));
 	ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 1, NULL));
 
 	/* Active low should invert the input value */
@@ -397,22 +407,22 @@
 	ut_asserteq(6, gpio_request_list_by_name(dev, "test3-gpios", desc_list,
 						 ARRAY_SIZE(desc_list), 0));
 
-	ut_assertok(dm_gpio_get_dir_flags(&desc_list[0], &flags));
+	ut_assertok(dm_gpio_get_flags(&desc_list[0], &flags));
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, flags);
 
-	ut_assertok(dm_gpio_get_dir_flags(&desc_list[1], &flags));
+	ut_assertok(dm_gpio_get_flags(&desc_list[1], &flags));
 	ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, flags);
 
-	ut_assertok(dm_gpio_get_dir_flags(&desc_list[2], &flags));
+	ut_assertok(dm_gpio_get_flags(&desc_list[2], &flags));
 	ut_asserteq(GPIOD_IS_OUT, flags);
 
-	ut_assertok(dm_gpio_get_dir_flags(&desc_list[3], &flags));
+	ut_assertok(dm_gpio_get_flags(&desc_list[3], &flags));
 	ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags);
 
-	ut_assertok(dm_gpio_get_dir_flags(&desc_list[4], &flags));
+	ut_assertok(dm_gpio_get_flags(&desc_list[4], &flags));
 	ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_DOWN, flags);
 
-	ut_assertok(dm_gpio_get_dir_flags(&desc_list[5], &flags));
+	ut_assertok(dm_gpio_get_flags(&desc_list[5], &flags));
 	ut_asserteq(GPIOD_IS_IN, flags);
 
 	ut_assertok(gpio_free_list(dev, desc_list, 6));
@@ -582,3 +592,189 @@
 	return 0;
 }
 DM_TEST(dm_test_gpio_devm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_clrset_flags(struct unit_test_state *uts)
+{
+	struct gpio_desc desc;
+	struct udevice *dev;
+	ulong flags;
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+	ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+	ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_OUT));
+	ut_assertok(dm_gpio_get_flags(&desc, &flags));
+	ut_asserteq(GPIOD_IS_OUT, flags);
+	ut_asserteq(GPIOD_IS_OUT, desc.flags);
+	ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+	ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE));
+	ut_assertok(dm_gpio_get_flags(&desc, &flags));
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, flags);
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, desc.flags);
+	ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
+	ut_asserteq(1, dm_gpio_get_value(&desc));
+
+	ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN));
+	ut_assertok(dm_gpio_get_flags(&desc, &flags));
+	ut_asserteq(GPIOD_IS_IN, flags & GPIOD_MASK_DIR);
+	ut_asserteq(GPIOD_IS_IN, desc.flags & GPIOD_MASK_DIR);
+
+	ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_PULL,
+					 GPIOD_PULL_UP));
+	ut_assertok(dm_gpio_get_flags(&desc, &flags));
+	ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags);
+	ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, desc.flags);
+
+	/* Check we cannot set both PULL_UP and PULL_DOWN */
+	ut_asserteq(-EINVAL, dm_gpio_clrset_flags(&desc, 0, GPIOD_PULL_DOWN));
+
+	return 0;
+}
+DM_TEST(dm_test_clrset_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check that an active-low GPIO works as expected */
+static int dm_test_clrset_flags_invert(struct unit_test_state *uts)
+{
+	struct gpio_desc desc;
+	struct udevice *dev;
+	ulong flags;
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+	ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc,
+					 GPIOD_IS_OUT | GPIOD_ACTIVE_LOW));
+
+	/*
+	 * From this size we see it as 0 (active low), but the sandbox driver
+	 * sees the pin value high
+	 */
+	ut_asserteq(0, dm_gpio_get_value(&desc));
+	ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+	ut_assertok(dm_gpio_set_value(&desc, 1));
+	ut_asserteq(1, dm_gpio_get_value(&desc));
+	ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+	/* Do the same with dm_gpio_clrset_flags() */
+	ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0));
+	ut_asserteq(0, dm_gpio_get_value(&desc));
+	ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+	ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE));
+	ut_asserteq(1, dm_gpio_get_value(&desc));
+	ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+	ut_assertok(dm_gpio_get_flags(&desc, &flags));
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE,
+		    flags);
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE,
+		    desc.flags);
+
+	ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0));
+	ut_assertok(dm_gpio_get_flags(&desc, &flags));
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, flags);
+	ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, desc.flags);
+
+	return 0;
+}
+DM_TEST(dm_test_clrset_flags_invert, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int set_gpios(struct unit_test_state *uts, struct gpio_desc *desc,
+		     int count, uint value)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		const uint mask = 1 << i;
+
+		ut_assertok(sandbox_gpio_set_value(desc[i].dev, desc[i].offset,
+						   value & mask));
+	}
+
+	return 0;
+}
+
+/* Check that an active-low GPIO works as expected */
+static int dm_test_gpio_get_values_as_int(struct unit_test_state *uts)
+{
+	const int gpio_count = 3;
+	struct gpio_desc desc[gpio_count];
+	struct udevice *dev;
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+
+	ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc,
+						 gpio_count, GPIOD_IS_IN));
+	ut_assertok(set_gpios(uts, desc, gpio_count, 0));
+	ut_asserteq(0, dm_gpio_get_values_as_int(desc, gpio_count));
+
+	ut_assertok(set_gpios(uts, desc, gpio_count, 5));
+	ut_asserteq(5, dm_gpio_get_values_as_int(desc, gpio_count));
+
+	ut_assertok(set_gpios(uts, desc, gpio_count, 7));
+	ut_asserteq(7, dm_gpio_get_values_as_int(desc, gpio_count));
+
+	return 0;
+}
+DM_TEST(dm_test_gpio_get_values_as_int,
+	UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check that an active-low GPIO works as expected */
+static int dm_test_gpio_get_values_as_int_base3(struct unit_test_state *uts)
+{
+	const int gpio_count = 3;
+	struct gpio_desc desc[gpio_count];
+	struct udevice *dev;
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+
+	ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc,
+						 gpio_count, GPIOD_IS_IN));
+
+	/*
+	 * First test the sandbox GPIO driver works as expected. The external
+	 * pull resistor should be stronger than the internal one.
+	 */
+	sandbox_gpio_set_flags(desc[0].dev, desc[0].offset,
+			       GPIOD_IS_IN | GPIOD_EXT_PULL_UP | GPIOD_PULL_UP);
+	ut_asserteq(1, dm_gpio_get_value(desc));
+
+	sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_IS_IN |
+			       GPIOD_EXT_PULL_DOWN | GPIOD_PULL_UP);
+	ut_asserteq(0, dm_gpio_get_value(desc));
+
+	sandbox_gpio_set_flags(desc[0].dev, desc[0].offset,
+			       GPIOD_IS_IN | GPIOD_PULL_UP);
+	ut_asserteq(1, dm_gpio_get_value(desc));
+
+	sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_PULL_DOWN);
+	ut_asserteq(0, dm_gpio_get_value(desc));
+
+	/*
+	 * Set up pins: pull-up (1), pull-down (0) and floating (2). This should
+	 * result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19
+	 */
+	sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_EXT_PULL_UP);
+	sandbox_gpio_set_flags(desc[1].dev, desc[1].offset,
+			       GPIOD_EXT_PULL_DOWN);
+	sandbox_gpio_set_flags(desc[2].dev, desc[2].offset, 0);
+	ut_asserteq(19, dm_gpio_get_values_as_int_base3(desc, gpio_count));
+
+	/*
+	 * Set up pins: floating (2), pull-up (1) and pull-down (0). This should
+	 * result in digits 0 1 2, i.e. 1 * 3 + 2 = 5
+	 */
+	sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, 0);
+	sandbox_gpio_set_flags(desc[1].dev, desc[1].offset, GPIOD_EXT_PULL_UP);
+	sandbox_gpio_set_flags(desc[2].dev, desc[2].offset,
+			       GPIOD_EXT_PULL_DOWN);
+	ut_asserteq(5, dm_gpio_get_values_as_int_base3(desc, gpio_count));
+
+	return 0;
+}
+DM_TEST(dm_test_gpio_get_values_as_int_base3,
+	UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);