diff mbox series

[U-Boot,v3] gpio: add gpio-hog support

Message ID 20190530032854.1338588-1-hs@denx.de
State Superseded
Delegated to: Tom Rini
Headers show
Series [U-Boot,v3] gpio: add gpio-hog support | expand

Commit Message

Heiko Schocher May 30, 2019, 3:28 a.m. UTC
add gpio-hog support. GPIO hogging is a mechanism
providing automatic GPIO request and configuration
as part of the gpio-controller's driver probe function.

for more infos see:
doc/device-tree-bindings/gpio/gpio.txt

Signed-off-by: Heiko Schocher <hs@denx.de>

---
clean travis build, see result:
https://travis-ci.org/hsdenx/u-boot-test/builds/538734780

Changes in v3:
- probe now all gpio devices with gpio-hogs
  from board_r before board_late_init() call
  or if someone wants to access gpio-hog
  based gpios with gpio_hog_lookup_name()
  This is needed, because we cannot be sure,
  if a gpio device which contains gpio-hogs
  is probed.
- add line-name property as Michal recommended
- renamed gpio_priv_one into gpio_priv_one_hog
  and protected through CONFIG_DM_GPIO_HOG

Changes in v2:
- move gpio_hog() call from post_probe() to post_bind()
  call. Check if current gpio device has gpio-hog
  definitions, if so, probe it.

 common/board_r.c                       |   6 +
 doc/device-tree-bindings/gpio/gpio.txt |  55 +++++++
 drivers/gpio/Kconfig                   |  10 ++
 drivers/gpio/gpio-uclass.c             | 217 ++++++++++++++++++++++---
 include/asm-generic/gpio.h             |  32 ++++
 5 files changed, 301 insertions(+), 19 deletions(-)

Comments

Patrick DELAUNAY June 4, 2019, 11:49 a.m. UTC | #1
Hi Heiko,

> 
> add gpio-hog support. GPIO hogging is a mechanism providing automatic GPIO
> request and configuration as part of the gpio-controller's driver probe function.
> 
> for more infos see:
> doc/device-tree-bindings/gpio/gpio.txt
> 
> Signed-off-by: Heiko Schocher <hs@denx.de>

It seens the hog function can be called several times, for gpio bank probed in pre-reloc phasis:
	
- before recocation : gpio_post_probe()  => gpio_hog() .... gpio_hogs_probed = 0
- after relocation : 
 	gpio_post_probe()  => gpio_hog()... gpio_hogs_probed = 0
 	gpio_hog_probe_all() => gpio_post_probe()  => gpio_hog()... gpio_hogs_probed = 1
	
I don't known if it is expected behavior:
	hog configuration 2 times in Uboot = before and after relocation


> ---
> clean travis build, see result:
> https://travis-ci.org/hsdenx/u-boot-test/builds/538734780
> 
> Changes in v3:
> - probe now all gpio devices with gpio-hogs
>   from board_r before board_late_init() call
>   or if someone wants to access gpio-hog
>   based gpios with gpio_hog_lookup_name()
>   This is needed, because we cannot be sure,
>   if a gpio device which contains gpio-hogs
>   is probed.
> - add line-name property as Michal recommended
> - renamed gpio_priv_one into gpio_priv_one_hog
>   and protected through CONFIG_DM_GPIO_HOG
> 
> Changes in v2:
> - move gpio_hog() call from post_probe() to post_bind()
>   call. Check if current gpio device has gpio-hog
>   definitions, if so, probe it.
> 
>  common/board_r.c                       |   6 +
>  doc/device-tree-bindings/gpio/gpio.txt |  55 +++++++
>  drivers/gpio/Kconfig                   |  10 ++
>  drivers/gpio/gpio-uclass.c             | 217 ++++++++++++++++++++++---
>  include/asm-generic/gpio.h             |  32 ++++
>  5 files changed, 301 insertions(+), 19 deletions(-)
> 
> diff --git a/common/board_r.c b/common/board_r.c index 150e8cd424..507da4c74a
> 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -49,6 +49,9 @@
>  #include <linux/err.h>
>  #include <efi_loader.h>
>  #include <wdt.h>
> +#if defined(CONFIG_DM_GPIO_HOG)
> +#include <asm/gpio.h>
> +#endif
> 
>  DECLARE_GLOBAL_DATA_PTR;
> 
> @@ -796,6 +799,9 @@ static init_fnc_t init_sequence_r[] = {  #ifdef
> CONFIG_CMD_NET
>  	initr_ethaddr,
>  #endif
> +#if defined(CONFIG_DM_GPIO_HOG)
> +	gpio_hog_probe_all,
> +#endif
>  #ifdef CONFIG_BOARD_LATE_INIT
>  	board_late_init,
>  #endif
> diff --git a/doc/device-tree-bindings/gpio/gpio.txt b/doc/device-tree-
> bindings/gpio/gpio.txt
> index f7a158d858..e774439369 100644
> --- a/doc/device-tree-bindings/gpio/gpio.txt
> +++ b/doc/device-tree-bindings/gpio/gpio.txt
> @@ -210,3 +210,58 @@ Example 2:
>  Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO
> ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2  are
> named "foo" and "bar".
> +
> +3) GPIO hog definitions
> +-----------------------
> +
> +The GPIO chip may contain GPIO hog definitions. GPIO hogging is a
> +mechanism providing automatic GPIO request and configuration as part of
> +the gpio-controller's driver probe function.
> +
> +Each GPIO hog definition is represented as a child node of the GPIO controller.
> +Required properties:
> +- gpio-hog:   A property specifying that this child node represents a GPIO hog.
> +- gpios:      Store the GPIO information (id, flags) for the GPIO to
> +	      affect.
> +
> +              ! Not yet support more than one gpio !
> +
> +Only one of the following properties scanned in the order shown below.
> +- input:      A property specifying to set the GPIO direction as input.
> +- output-low  A property specifying to set the GPIO direction as output with
> +	      the value low.
> +- output-high A property specifying to set the GPIO direction as output with
> +	      the value high.
> +
> +Optional properties:
> +- line-name:  The GPIO label name. If not present the node name is used.
> +
> +Example:
> +
> +        tca6416@20 {
> +                compatible = "ti,tca6416";
> +                reg = <0x20>;
> +                #gpio-cells = <2>;
> +                gpio-controller;
> +
> +                env_reset {
> +                        gpio-hog;
> +                        input;
> +                        gpios = <6 GPIO_ACTIVE_LOW>;
> +                };
> +                boot_rescue {
> +                        gpio-hog;
> +                        input;
> +                        gpios = <7 GPIO_ACTIVE_LOW>;
> +                };
> +        };
> +
> +For the above Example you can than access the gpio in your boardcode
> +with:
> +
> +        desc = gpio_hog_lookup_name("boot_rescue.gpio-hog");
> +        if (desc) {
> +                if (dm_gpio_get_value(desc))
> +                        printf("\nBooting into Rescue System\n");
> +		else
> +			printf("\nBoot normal\n");
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index
> e36a8abc42..fa1c99700f 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -14,6 +14,16 @@ config DM_GPIO
>  	  particular GPIOs that they provide. The uclass interface
>  	  is defined in include/asm-generic/gpio.h.
> 
> +config DM_GPIO_HOG
> +	bool "Enable GPIO hog support"
> +	depends on DM_GPIO
> +	default n
> +	help
> +	  Enable gpio hog support
> +	  The GPIO chip may contain GPIO hog definitions. GPIO hogging
> +	  is a mechanism providing automatic GPIO request and config-
> +	  uration as part of the gpio-controller's driver probe function.
> +
>  config ALTERA_PIO
>  	bool "Altera PIO driver"
>  	depends on DM_GPIO
> diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index
> da5e9ba6e5..4ecff115f1 100644
> --- a/drivers/gpio/gpio-uclass.c
> +++ b/drivers/gpio/gpio-uclass.c
> @@ -5,6 +5,7 @@
> 
>  #include <common.h>
>  #include <dm.h>
> +#include <dm/device-internal.h>
>  #include <dt-bindings/gpio/gpio.h>
>  #include <errno.h>
>  #include <fdtdec.h>
> @@ -15,6 +16,18 @@
> 
>  DECLARE_GLOBAL_DATA_PTR;
> 
> +#if defined(CONFIG_DM_GPIO_HOG)
> +struct gpio_priv_one_hog {
> +	struct list_head list;
> +	char *name;
> +	struct gpio_desc gpiod;
> +};
> +
> +static struct list_head hogs;
> +static int list_init;
> +static int gpio_hogs_probed;
> +#endif
> +
>  /**
>   * gpio_to_device() - Convert global GPIO number to device, number
>   *
> @@ -141,6 +154,147 @@ static int gpio_find_and_xlate(struct gpio_desc *desc,
>  		return gpio_xlate_offs_flags(desc->dev, desc, args);  }
> 
> +#if defined(CONFIG_DM_GPIO_HOG)
> +int gpio_hog_probe_all(void)
> +{
> +	struct udevice *dev;
> +	ofnode node;
> +	int ret;
> +
> +	/*
> +	 * We need to probe all gpio-hog devices
> +	 * at some point, as we cannot be sure,
> +	 * that all gpio devices, which contain a
> +	 * gpio-hog definition are probed.
> +	 *
> +	 */
> +	if (gpio_hogs_probed)
> +		return 0;
> +
> +	for (uclass_first_device(UCLASS_GPIO, &dev);
> +	     dev;
> +	     uclass_next_device(&dev)) {

uclass_next_device automatically call probe for the class (in uclass_get_device_tail)
in this context, I think that uclass_find_next_device is better

> +		dev_for_each_subnode(node, dev) {
> +			if (ofnode_read_bool(node, "gpio-hog")) {
> +				ret = device_probe(dev);
> +				if (ret)
> +					return ret;
> +				break;
> +			}
> +		}
> +	}
> +	gpio_hogs_probed = 1;
> +
> +	return 0;
> +}
> +
> +struct gpio_desc *gpio_hog_lookup_name(const char *name) {
> +	struct list_head *entry;
> +	struct gpio_priv_one_hog *cur;
> +
> +	/* be sure, all gpio devices with gpio-hogs are probed */
> +	gpio_hog_probe_all();
> +	list_for_each(entry, &hogs) {
> +		cur = list_entry(entry, struct gpio_priv_one_hog, list);
> +		if (strcmp(cur->name, name) == 0)
> +			return &cur->gpiod;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int gpio_hog(struct udevice *dev) {
> +	ofnode node;
> +	int found = 0;
> +	int ret;
> +	struct gpio_dev_priv *uc_priv = NULL;
> +
> +	if (!list_init) {
> +		INIT_LIST_HEAD(&hogs);
> +		list_init = 1;
> +	}
> +	dev_for_each_subnode(node, dev) {
> +		if (ofnode_read_bool(node, "gpio-hog")) {
> +			found = 1;
> +			break;
> +		}
> +	}

This check is really needed, as it is already done in the main loop ?
I think you can remove this first  'found' loop.

> +
> +	if (!found)
> +		return 0;
> +
> +	uc_priv = dev_get_uclass_priv(dev);
> +	if (!uc_priv) {
> +		printf("%s: missing private data.\n", __func__);
> +		return 0;
> +	}

It is prossible to have uc_priv = 0 ? 
by construction, is is not allowed in driver mode, so this test can be remove I think.

> +
> +	/* scan for gpio-hog subnodes */
> +	dev_for_each_subnode(node, dev) {
> +		u32 val[2];
> +		int value = 0;
> +		int gpiod_flags;
> +		struct gpio_priv_one_hog *new;
> +		const char *nodename;
> +
> +		if (!ofnode_read_bool(node, "gpio-hog"))
> +			continue;
> +
> +		if (ofnode_read_bool(node, "input")) {
> +			gpiod_flags = GPIOD_IS_IN;
> +		} else if (ofnode_read_bool(node, "output-high")) {
> +			value = 1;
> +			gpiod_flags = GPIOD_IS_OUT;
> +		} else if (ofnode_read_bool(node, "output-low")) {
> +			gpiod_flags = GPIOD_IS_OUT;
> +		} else {
> +			printf("%s: missing gpio-hog state.\n", __func__);
> +			return -EINVAL;
> +		}
> +
> +		ret = ofnode_read_u32_array(node, "gpios", val, 2);
> +		if (ret) {
> +			printf("%s: wrong gpios property, 2 values needed, ret:
> %d\n", __func__, ret);
> +			return ret;
> +		}
> +
> +		new = calloc(1, sizeof(struct gpio_priv_one_hog));
> +		nodename = ofnode_read_string(node, "line-name");
> +		if (!nodename)
> +			nodename = ofnode_get_name(node);
> +		ret = gpio_dev_request_index(dev, nodename, "gpio-hog",
> +					     val[0], gpiod_flags, val[1],
> +					     &new->gpiod);
> +		if (ret < 0) {
> +			debug("%s: node %s could not get gpio.\n", __func__,
> +			      ofnode_get_name(node));
> +			free(new);
> +			return ret;
> +		}
> +
> +		new->name = uc_priv->name[new->gpiod.offset];
> +		list_add_tail(&new->list, &hogs);
> +		dm_gpio_set_dir(&new->gpiod);
> +		if (gpiod_flags == GPIOD_IS_OUT)
> +			dm_gpio_set_value(&new->gpiod, value);
> +	}
> +
> +	return 0;
> +}
> +#else
> +static int gpio_hog(struct udevice *dev) {
> +	return 0;
> +}
> +
> +struct gpio_desc *gpio_hog_lookup_name(const char *name) {
> +	return NULL;
> +}
> +#endif
> +
>  int dm_gpio_request(struct gpio_desc *desc, const char *label)  {
>  	struct udevice *dev = desc->dev;
> @@ -149,8 +303,9 @@ int dm_gpio_request(struct gpio_desc *desc, const char
> *label)
>  	int ret;
> 
>  	uc_priv = dev_get_uclass_priv(dev);
> -	if (uc_priv->name[desc->offset])
> -		return -EBUSY;
> +	if (uc_priv)
> +		if (uc_priv->name[desc->offset])
> +			return -EBUSY;
>  	str = strdup(label);
>  	if (!str)
>  		return -ENOMEM;
> @@ -640,22 +795,25 @@ int dm_gpio_get_values_as_int(const struct gpio_desc
> *desc_list, int count)
>  	return vector;
>  }
> 
> -static int gpio_request_tail(int ret, ofnode node,
> +static int gpio_request_tail(int ret, const char *nodename,
>  			     struct ofnode_phandle_args *args,
>  			     const char *list_name, int index,
> -			     struct gpio_desc *desc, int flags, bool add_index)
> +			     struct gpio_desc *desc, int flags,
> +			     bool add_index, struct udevice *dev)
>  {
> -	desc->dev = NULL;
> +	desc->dev = dev;
>  	desc->offset = 0;
>  	desc->flags = 0;
>  	if (ret)
>  		goto err;
> 
> -	ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
> -					  &desc->dev);
> -	if (ret) {
> -		debug("%s: uclass_get_device_by_ofnode failed\n", __func__);
> -		goto err;
> +	if (!desc->dev) {
> +		ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
> +						  &desc->dev);
> +		if (ret) {
> +			debug("%s: uclass_get_device_by_ofnode failed\n",
> __func__);
> +			goto err;
> +		}
>  	}
>  	ret = gpio_find_and_xlate(desc, args);
>  	if (ret) {
> @@ -663,8 +821,7 @@ static int gpio_request_tail(int ret, ofnode node,
>  		goto err;
>  	}
>  	ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
> -			       ofnode_get_name(node),
> -			       list_name, index);
> +			       nodename, list_name, index);
>  	if (ret) {
>  		debug("%s: dm_gpio_requestf failed\n", __func__);
>  		goto err;
> @@ -678,7 +835,7 @@ static int gpio_request_tail(int ret, ofnode node,
>  	return 0;
>  err:
>  	debug("%s: Node '%s', property '%s', failed to request GPIO index %d:
> %d\n",
> -	      __func__, ofnode_get_name(node), list_name, index, ret);
> +	      __func__, nodename, list_name, index, ret);
>  	return ret;
>  }
> 
> @@ -692,8 +849,8 @@ static int _gpio_request_by_name_nodev(ofnode node,
> const char *list_name,
>  	ret = ofnode_parse_phandle_with_args(node, list_name, "#gpio-cells", 0,
>  					     index, &args);
> 
> -	return gpio_request_tail(ret, node, &args, list_name, index, desc,
> -				 flags, add_index);
> +	return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
> +				 index, desc, flags, add_index, NULL);
>  }
> 
>  int gpio_request_by_name_nodev(ofnode node, const char *list_name, int index,
> @@ -707,13 +864,14 @@ int gpio_request_by_name(struct udevice *dev, const
> char *list_name, int index,
>  			 struct gpio_desc *desc, int flags)
>  {
>  	struct ofnode_phandle_args args;
> +	ofnode node;
>  	int ret;
> 
>  	ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0,
>  					 index, &args);
> -
> -	return gpio_request_tail(ret, dev_ofnode(dev), &args, list_name,
> -				 index, desc, flags, index > 0);
> +	node = dev_ofnode(dev);
> +	return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
> +				 index, desc, flags, index > 0, NULL);
>  }
> 
>  int gpio_request_list_by_name_nodev(ofnode node, const char *list_name, @@ -
> 832,12 +990,17 @@ int gpio_get_number(const struct gpio_desc *desc)  static int
> gpio_post_probe(struct udevice *dev)  {
>  	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +	int ret;
> 
>  	uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
>  	if (!uc_priv->name)
>  		return -ENOMEM;
> 
> -	return gpio_renumber(NULL);
> +	ret = gpio_renumber(NULL);
> +	if (ret)
> +		return ret;
> +

It is strange for me to have many device tree parsing in probe function....

For me
=> hog detection + DT parsing in gpio_post_bind() .... info saved in platdata
=> gpio configuration for hog (request and set_dir) => probe function


> +	return gpio_hog(dev);
>  }


One other solution to do it is to manage a hog with uclass  (using the new UCLASS_NOP or misc for example)
http://patchwork.ozlabs.org/patch/1098913/
Ps: we can also create a new UCLASS_GPIOHOG

And we to bind this class during GPIO binding; the parsing of DT is done only one time 
and manage privdata and platdata as expected by driver model....

For example something like (not tested)

static int gpio_post_bind(struct udevice *dev)
{
	struct udevice *child;
	ofnode node;
.....

#if defined(CONFIG_DM_GPIO_HOG)
	dev_for_each_subnode(node, dev) {
		if (ofnode_read_bool(node, "gpio-hog")) {
			const char *name = fdt_get_name(fdt, node, NULL);
			device_bind_driver_to_node (dev, "gpio_hog", name,
						         dev_ofnode(dev), &child);
		}
	}
#endif
	return 0;
}

#if defined(CONFIG_DM_GPIO_HOG)

struct gpio_hog_priv{ 
	struct gpio_desc gpiod;
}

struct gpio_hog_data { 
	int gpiod_flags; 
	int value; 
	u32 val[2];
}

static int gpio_hog_ofdata_to_platdata(struct udevice *dev)
{ 
	struct gpio_hog_data *plat = dev_get_platdata(dev); 
	char *nodename;

	plat->value = 0;
	if (dev_read_bool(dev, "input")) {
		plat->gpiod_flags = GPIOD_IS_IN;
	} else if (dev_read_bool(dev, "output-high")) {
		plat->value = 1;
		gpiod_flags = GPIOD_IS_OUT;
	} else if (dev_read_bool(dev, "output-low")) {
		gpiod_flags = GPIOD_IS_OUT;
	} else {
		printf("%s: missing gpio-hog state.\n", __func__);
		return -EINVAL;
	} 
	ret = dev_read_u32_array(dev, "gpios", plat->val, 2);
	if (ret) {
		printf("%s: wrong gpios property, 2 values needed, ret: %d\n", __func__, ret);
		return ret;
	} 
	nodename = dev_read_string(node, "line-name");
	if (!nodename)
		nodename = dev_read_name(node);
	device_set_name(dev, nodename);
} 

static int gpio_hog_probe(struct udevice *dev)
{ 
	struct gpio_hog_data *plat = dev_get_platdata(dev); 
	struct gpio_hog_priv *priv = dev_get_privdata(dev);

	ret = gpio_dev_request_index(dev, dev->name, "gpio-hog",
				          plat->val[0], plat->gpiod_flags, plat->val[1],
				         &priv->gpiod); 
	if (ret < 0) {
		debug("%s: node %s could not get gpio.\n", __func__,
		      ofnode_get_name(node));
		return ret;
	}
	dm_gpio_set_dir_flags(&priv->gpiod);
	if (gpiod_flags == GPIOD_IS_OUT)
		dm_gpio_set_value(&priv->gpiod, plat->value);
}

int gpio_hog_probe_all(void)
{
	struct udevice *dev;
	ofnode node;

	for (uclass_first_device(UCLASS_NOP, &dev);
	     dev;
	     uclass_find_next_device (&dev)) {
		if (dev->driver == M_GET_DRIVER(gpio_hog)) {
			ret = device_probe(dev);
			if (ret) '
				return ret;
				break;
			}
		}
	}
}

struct gpio_desc *gpio_hog_lookup_name(const char *name)
{
	struct udevice *dev;

	/* be sure, all gpio devices with gpio-hogs are probed */
	gpio_hog_probe_all();
	if (!uclass_get_device_by_name(UCLASS_NOP, name, &dev)) '
		struct gpio_hog_priv *priv = dev_get_privdata(dev);
		return &priv->gpiod;
	}

	return NULL;
}

U_BOOT_DRIVER(gio_hog) = {
 	.name	= "gpio_hog",
	.id	= UCLASS_NOP,
 	.ofdata_to_platdata = gpio_hog_ofdata_to_platdata,
 	.probe = gpio_hog_probe,
	.priv_auto_alloc_size = sizeof(struct gpio_hog_priv),
	.platdata_auto_alloc_size = sizeof(struct gpio_hog_data),
}
#endif


> 
>  static int gpio_pre_remove(struct udevice *dev) @@ -854,6 +1017,21 @@ static
> int gpio_pre_remove(struct udevice *dev)
>  	return gpio_renumber(dev);
>  }
> 
> +int gpio_dev_request_index(struct udevice *dev, const char *nodename,
> +			   char *list_name, int index, int flags,
> +			   int dtflags, struct gpio_desc *desc) {
> +	struct ofnode_phandle_args args;
> +
> +	args.node =  ofnode_null();
> +	args.args_count = 2;
> +	args.args[0] = index;
> +	args.args[1] = dtflags;
> +
> +	return gpio_request_tail(0, nodename, &args, list_name, index, desc,
> +				 flags, 0, dev);
> +}
> +
>  static int gpio_post_bind(struct udevice *dev)  {  #if
> defined(CONFIG_NEEDS_MANUAL_RELOC)
> @@ -885,6 +1063,7 @@ static int gpio_post_bind(struct udevice *dev)
>  		reloc_done++;
>  	}
>  #endif
> +
>  	return 0;
>  }
> 
> diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index
> d03602696f..37f71e5708 100644
> --- a/include/asm-generic/gpio.h
> +++ b/include/asm-generic/gpio.h
> @@ -348,6 +348,22 @@ const char *gpio_get_bank_info(struct udevice *dev, int
> *offset_count);
>   */
>  int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc);
> 
> +/**
> + * gpio_hog_lookup_name() - Look up a named GPIO and return the gpio descr.
> + *
> + * @name:	Name to look up
> + * @return:	Returns gpio_desc for gpio
> + */
> +struct gpio_desc *gpio_hog_lookup_name(const char *name);
> +
> +/**
> + * gpio_hog_probe_all() - probe all gpio devices with
> + * gpio-hog subnodes.
> + *
> + * @return:	Returns return value from device_probe()
> + */
> +int gpio_hog_probe_all(void);
> +
>  /**
>   * gpio_lookup_name - Look up a GPIO name and return its details
>   *
> @@ -503,6 +519,22 @@ int gpio_request_list_by_name_nodev(ofnode node,
> const char *list_name,
>  				    struct gpio_desc *desc_list, int max_count,
>  				    int flags);
> 
> +/**
> + * gpio_dev_request_index() - request single GPIO from gpio device
> + *
> + * @dev:	GPIO device
> + * @nodename:	Name of node
> + * @list_name:	Name of GPIO list (e.g. "board-id-gpios")
> + * @index:	Index number of the GPIO in that list use request (0=first)
> + * @flags:	GPIOD_* flags
> + * @dtflags:	GPIO flags read from DT
> + * @desc:	GPIO descriotor filled from this function
> + * @return:	return value from gpio_request_tail()
> + */
> +int gpio_dev_request_index(struct udevice *dev, const char *nodename,
> +			   char *list_name, int index, int flags,
> +			   int dtflags, struct gpio_desc *desc);
> +
>  /**
>   * dm_gpio_free() - Free a single GPIO
>   *
> --
> 2.21.0

Regards

Patrick
Heiko Schocher June 4, 2019, 4:04 p.m. UTC | #2
Hello Patrick,

Am 04.06.2019 um 13:49 schrieb Patrick DELAUNAY:
> Hi Heiko,
> 
>>
>> add gpio-hog support. GPIO hogging is a mechanism providing automatic GPIO
>> request and configuration as part of the gpio-controller's driver probe function.
>>
>> for more infos see:
>> doc/device-tree-bindings/gpio/gpio.txt
>>
>> Signed-off-by: Heiko Schocher <hs@denx.de>
> 
> It seens the hog function can be called several times, for gpio bank probed in pre-reloc phasis:
> 	
> - before recocation : gpio_post_probe()  => gpio_hog() .... gpio_hogs_probed = 0
> - after relocation :
>   	gpio_post_probe()  => gpio_hog()... gpio_hogs_probed = 0
>   	gpio_hog_probe_all() => gpio_post_probe()  => gpio_hog()... gpio_hogs_probed = 1
> 	
> I don't known if it is expected behavior:
> 	hog configuration 2 times in Uboot = before and after relocation

No, not expected.

>> ---
>> clean travis build, see result:
>> https://travis-ci.org/hsdenx/u-boot-test/builds/538734780
>>
>> Changes in v3:
>> - probe now all gpio devices with gpio-hogs
>>    from board_r before board_late_init() call
>>    or if someone wants to access gpio-hog
>>    based gpios with gpio_hog_lookup_name()
>>    This is needed, because we cannot be sure,
>>    if a gpio device which contains gpio-hogs
>>    is probed.
>> - add line-name property as Michal recommended
>> - renamed gpio_priv_one into gpio_priv_one_hog
>>    and protected through CONFIG_DM_GPIO_HOG
>>
>> Changes in v2:
>> - move gpio_hog() call from post_probe() to post_bind()
>>    call. Check if current gpio device has gpio-hog
>>    definitions, if so, probe it.
>>
>>   common/board_r.c                       |   6 +
>>   doc/device-tree-bindings/gpio/gpio.txt |  55 +++++++
>>   drivers/gpio/Kconfig                   |  10 ++
>>   drivers/gpio/gpio-uclass.c             | 217 ++++++++++++++++++++++---
>>   include/asm-generic/gpio.h             |  32 ++++
>>   5 files changed, 301 insertions(+), 19 deletions(-)

[...]
>> diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index
>> da5e9ba6e5..4ecff115f1 100644
>> --- a/drivers/gpio/gpio-uclass.c
>> +++ b/drivers/gpio/gpio-uclass.c
>> @@ -5,6 +5,7 @@
>>
>>   #include <common.h>
>>   #include <dm.h>
>> +#include <dm/device-internal.h>
>>   #include <dt-bindings/gpio/gpio.h>
>>   #include <errno.h>
>>   #include <fdtdec.h>
>> @@ -15,6 +16,18 @@
>>
>>   DECLARE_GLOBAL_DATA_PTR;
>>
>> +#if defined(CONFIG_DM_GPIO_HOG)
>> +struct gpio_priv_one_hog {
>> +	struct list_head list;
>> +	char *name;
>> +	struct gpio_desc gpiod;
>> +};
>> +
>> +static struct list_head hogs;
>> +static int list_init;
>> +static int gpio_hogs_probed;
>> +#endif
>> +
>>   /**
>>    * gpio_to_device() - Convert global GPIO number to device, number
>>    *
>> @@ -141,6 +154,147 @@ static int gpio_find_and_xlate(struct gpio_desc *desc,
>>   		return gpio_xlate_offs_flags(desc->dev, desc, args);  }
>>
>> +#if defined(CONFIG_DM_GPIO_HOG)
>> +int gpio_hog_probe_all(void)
>> +{
>> +	struct udevice *dev;
>> +	ofnode node;
>> +	int ret;
>> +
>> +	/*
>> +	 * We need to probe all gpio-hog devices
>> +	 * at some point, as we cannot be sure,
>> +	 * that all gpio devices, which contain a
>> +	 * gpio-hog definition are probed.
>> +	 *
>> +	 */
>> +	if (gpio_hogs_probed)
>> +		return 0;
>> +
>> +	for (uclass_first_device(UCLASS_GPIO, &dev);
>> +	     dev;
>> +	     uclass_next_device(&dev)) {
> 
> uclass_next_device automatically call probe for the class (in uclass_get_device_tail)
> in this context, I think that uclass_find_next_device is better

The idea was to only probe gpio devices with gpio-hog subnodes...

>> +		dev_for_each_subnode(node, dev) {
>> +			if (ofnode_read_bool(node, "gpio-hog")) {
>> +				ret = device_probe(dev);
>> +				if (ret)
>> +					return ret;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +	gpio_hogs_probed = 1;
>> +
>> +	return 0;
>> +}
>> +
>> +struct gpio_desc *gpio_hog_lookup_name(const char *name) {
>> +	struct list_head *entry;
>> +	struct gpio_priv_one_hog *cur;
>> +
>> +	/* be sure, all gpio devices with gpio-hogs are probed */
>> +	gpio_hog_probe_all();
>> +	list_for_each(entry, &hogs) {
>> +		cur = list_entry(entry, struct gpio_priv_one_hog, list);
>> +		if (strcmp(cur->name, name) == 0)
>> +			return &cur->gpiod;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +static int gpio_hog(struct udevice *dev) {
>> +	ofnode node;
>> +	int found = 0;
>> +	int ret;
>> +	struct gpio_dev_priv *uc_priv = NULL;
>> +
>> +	if (!list_init) {
>> +		INIT_LIST_HEAD(&hogs);
>> +		list_init = 1;
>> +	}
>> +	dev_for_each_subnode(node, dev) {
>> +		if (ofnode_read_bool(node, "gpio-hog")) {
>> +			found = 1;
>> +			break;
>> +		}
>> +	}
> 
> This check is really needed, as it is already done in the main loop ?
> I think you can remove this first  'found' loop.

Yes if we only setup the gpio-hogs from main loop.

>> +
>> +	if (!found)
>> +		return 0;
>> +
>> +	uc_priv = dev_get_uclass_priv(dev);
>> +	if (!uc_priv) {
>> +		printf("%s: missing private data.\n", __func__);
>> +		return 0;
>> +	}
> 
> It is prossible to have uc_priv = 0 ?
> by construction, is is not allowed in driver mode, so this test can be remove I think.

Yes, just panic check. I remove this.

>> +
>> +	/* scan for gpio-hog subnodes */
>> +	dev_for_each_subnode(node, dev) {
>> +		u32 val[2];
>> +		int value = 0;
>> +		int gpiod_flags;
>> +		struct gpio_priv_one_hog *new;
>> +		const char *nodename;
>> +
>> +		if (!ofnode_read_bool(node, "gpio-hog"))
>> +			continue;
>> +
>> +		if (ofnode_read_bool(node, "input")) {
>> +			gpiod_flags = GPIOD_IS_IN;
>> +		} else if (ofnode_read_bool(node, "output-high")) {
>> +			value = 1;
>> +			gpiod_flags = GPIOD_IS_OUT;
>> +		} else if (ofnode_read_bool(node, "output-low")) {
>> +			gpiod_flags = GPIOD_IS_OUT;
>> +		} else {
>> +			printf("%s: missing gpio-hog state.\n", __func__);
>> +			return -EINVAL;
>> +		}
>> +
>> +		ret = ofnode_read_u32_array(node, "gpios", val, 2);
>> +		if (ret) {
>> +			printf("%s: wrong gpios property, 2 values needed, ret:
>> %d\n", __func__, ret);
>> +			return ret;
>> +		}
>> +
>> +		new = calloc(1, sizeof(struct gpio_priv_one_hog));
>> +		nodename = ofnode_read_string(node, "line-name");
>> +		if (!nodename)
>> +			nodename = ofnode_get_name(node);
>> +		ret = gpio_dev_request_index(dev, nodename, "gpio-hog",
>> +					     val[0], gpiod_flags, val[1],
>> +					     &new->gpiod);
>> +		if (ret < 0) {
>> +			debug("%s: node %s could not get gpio.\n", __func__,
>> +			      ofnode_get_name(node));
>> +			free(new);
>> +			return ret;
>> +		}
>> +
>> +		new->name = uc_priv->name[new->gpiod.offset];
>> +		list_add_tail(&new->list, &hogs);
>> +		dm_gpio_set_dir(&new->gpiod);
>> +		if (gpiod_flags == GPIOD_IS_OUT)
>> +			dm_gpio_set_value(&new->gpiod, value);
>> +	}
>> +
>> +	return 0;
>> +}
>> +#else
>> +static int gpio_hog(struct udevice *dev) {
>> +	return 0;
>> +}
>> +
>> +struct gpio_desc *gpio_hog_lookup_name(const char *name) {
>> +	return NULL;
>> +}
>> +#endif
>> +
>>   int dm_gpio_request(struct gpio_desc *desc, const char *label)  {
>>   	struct udevice *dev = desc->dev;
>> @@ -149,8 +303,9 @@ int dm_gpio_request(struct gpio_desc *desc, const char
>> *label)
>>   	int ret;
>>
>>   	uc_priv = dev_get_uclass_priv(dev);
>> -	if (uc_priv->name[desc->offset])
>> -		return -EBUSY;
>> +	if (uc_priv)
>> +		if (uc_priv->name[desc->offset])
>> +			return -EBUSY;
>>   	str = strdup(label);
>>   	if (!str)
>>   		return -ENOMEM;
>> @@ -640,22 +795,25 @@ int dm_gpio_get_values_as_int(const struct gpio_desc
>> *desc_list, int count)
>>   	return vector;
>>   }
>>
>> -static int gpio_request_tail(int ret, ofnode node,
>> +static int gpio_request_tail(int ret, const char *nodename,
>>   			     struct ofnode_phandle_args *args,
>>   			     const char *list_name, int index,
>> -			     struct gpio_desc *desc, int flags, bool add_index)
>> +			     struct gpio_desc *desc, int flags,
>> +			     bool add_index, struct udevice *dev)
>>   {
>> -	desc->dev = NULL;
>> +	desc->dev = dev;
>>   	desc->offset = 0;
>>   	desc->flags = 0;
>>   	if (ret)
>>   		goto err;
>>
>> -	ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
>> -					  &desc->dev);
>> -	if (ret) {
>> -		debug("%s: uclass_get_device_by_ofnode failed\n", __func__);
>> -		goto err;
>> +	if (!desc->dev) {
>> +		ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
>> +						  &desc->dev);
>> +		if (ret) {
>> +			debug("%s: uclass_get_device_by_ofnode failed\n",
>> __func__);
>> +			goto err;
>> +		}
>>   	}
>>   	ret = gpio_find_and_xlate(desc, args);
>>   	if (ret) {
>> @@ -663,8 +821,7 @@ static int gpio_request_tail(int ret, ofnode node,
>>   		goto err;
>>   	}
>>   	ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
>> -			       ofnode_get_name(node),
>> -			       list_name, index);
>> +			       nodename, list_name, index);
>>   	if (ret) {
>>   		debug("%s: dm_gpio_requestf failed\n", __func__);
>>   		goto err;
>> @@ -678,7 +835,7 @@ static int gpio_request_tail(int ret, ofnode node,
>>   	return 0;
>>   err:
>>   	debug("%s: Node '%s', property '%s', failed to request GPIO index %d:
>> %d\n",
>> -	      __func__, ofnode_get_name(node), list_name, index, ret);
>> +	      __func__, nodename, list_name, index, ret);
>>   	return ret;
>>   }
>>
>> @@ -692,8 +849,8 @@ static int _gpio_request_by_name_nodev(ofnode node,
>> const char *list_name,
>>   	ret = ofnode_parse_phandle_with_args(node, list_name, "#gpio-cells", 0,
>>   					     index, &args);
>>
>> -	return gpio_request_tail(ret, node, &args, list_name, index, desc,
>> -				 flags, add_index);
>> +	return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
>> +				 index, desc, flags, add_index, NULL);
>>   }
>>
>>   int gpio_request_by_name_nodev(ofnode node, const char *list_name, int index,
>> @@ -707,13 +864,14 @@ int gpio_request_by_name(struct udevice *dev, const
>> char *list_name, int index,
>>   			 struct gpio_desc *desc, int flags)
>>   {
>>   	struct ofnode_phandle_args args;
>> +	ofnode node;
>>   	int ret;
>>
>>   	ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0,
>>   					 index, &args);
>> -
>> -	return gpio_request_tail(ret, dev_ofnode(dev), &args, list_name,
>> -				 index, desc, flags, index > 0);
>> +	node = dev_ofnode(dev);
>> +	return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
>> +				 index, desc, flags, index > 0, NULL);
>>   }
>>
>>   int gpio_request_list_by_name_nodev(ofnode node, const char *list_name, @@ -
>> 832,12 +990,17 @@ int gpio_get_number(const struct gpio_desc *desc)  static int
>> gpio_post_probe(struct udevice *dev)  {
>>   	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
>> +	int ret;
>>
>>   	uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
>>   	if (!uc_priv->name)
>>   		return -ENOMEM;
>>
>> -	return gpio_renumber(NULL);
>> +	ret = gpio_renumber(NULL);
>> +	if (ret)
>> +		return ret;
>> +
> 
> It is strange for me to have many device tree parsing in probe function....
> 
> For me
> => hog detection + DT parsing in gpio_post_bind() .... info saved in platdata
> => gpio configuration for hog (request and set_dir) => probe function

Yes, this approach is much more cleaner.

>> +	return gpio_hog(dev);
>>   }
> 
> 
> One other solution to do it is to manage a hog with uclass  (using the new UCLASS_NOP or misc for example)
> http://patchwork.ozlabs.org/patch/1098913/

Ah, thanks for this hint, wasn;t aware of it.

> Ps: we can also create a new UCLASS_GPIOHOG

Heh, I had a version (never posted) with such an approach. Thought this
is to much overhead ... I try first with UCLASS_NOP.

> And we to bind this class during GPIO binding; the parsing of DT is done only one time
> and manage privdata and platdata as expected by driver model....

Yes, cleaner.

> For example something like (not tested)
[...]

Thanks for your input! I try to rework this soon.

bye,
Heiko
diff mbox series

Patch

diff --git a/common/board_r.c b/common/board_r.c
index 150e8cd424..507da4c74a 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -49,6 +49,9 @@ 
 #include <linux/err.h>
 #include <efi_loader.h>
 #include <wdt.h>
+#if defined(CONFIG_DM_GPIO_HOG)
+#include <asm/gpio.h>
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -796,6 +799,9 @@  static init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_CMD_NET
 	initr_ethaddr,
 #endif
+#if defined(CONFIG_DM_GPIO_HOG)
+	gpio_hog_probe_all,
+#endif
 #ifdef CONFIG_BOARD_LATE_INIT
 	board_late_init,
 #endif
diff --git a/doc/device-tree-bindings/gpio/gpio.txt b/doc/device-tree-bindings/gpio/gpio.txt
index f7a158d858..e774439369 100644
--- a/doc/device-tree-bindings/gpio/gpio.txt
+++ b/doc/device-tree-bindings/gpio/gpio.txt
@@ -210,3 +210,58 @@  Example 2:
 Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO
 ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2
 are named "foo" and "bar".
+
+3) GPIO hog definitions
+-----------------------
+
+The GPIO chip may contain GPIO hog definitions. GPIO hogging is a mechanism
+providing automatic GPIO request and configuration as part of the
+gpio-controller's driver probe function.
+
+Each GPIO hog definition is represented as a child node of the GPIO controller.
+Required properties:
+- gpio-hog:   A property specifying that this child node represents a GPIO hog.
+- gpios:      Store the GPIO information (id, flags) for the GPIO to
+	      affect.
+
+              ! Not yet support more than one gpio !
+
+Only one of the following properties scanned in the order shown below.
+- input:      A property specifying to set the GPIO direction as input.
+- output-low  A property specifying to set the GPIO direction as output with
+	      the value low.
+- output-high A property specifying to set the GPIO direction as output with
+	      the value high.
+
+Optional properties:
+- line-name:  The GPIO label name. If not present the node name is used.
+
+Example:
+
+        tca6416@20 {
+                compatible = "ti,tca6416";
+                reg = <0x20>;
+                #gpio-cells = <2>;
+                gpio-controller;
+
+                env_reset {
+                        gpio-hog;
+                        input;
+                        gpios = <6 GPIO_ACTIVE_LOW>;
+                };
+                boot_rescue {
+                        gpio-hog;
+                        input;
+                        gpios = <7 GPIO_ACTIVE_LOW>;
+                };
+        };
+
+For the above Example you can than access the gpio in your boardcode
+with:
+
+        desc = gpio_hog_lookup_name("boot_rescue.gpio-hog");
+        if (desc) {
+                if (dm_gpio_get_value(desc))
+                        printf("\nBooting into Rescue System\n");
+		else
+			printf("\nBoot normal\n");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e36a8abc42..fa1c99700f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -14,6 +14,16 @@  config DM_GPIO
 	  particular GPIOs that they provide. The uclass interface
 	  is defined in include/asm-generic/gpio.h.
 
+config DM_GPIO_HOG
+	bool "Enable GPIO hog support"
+	depends on DM_GPIO
+	default n
+	help
+	  Enable gpio hog support
+	  The GPIO chip may contain GPIO hog definitions. GPIO hogging
+	  is a mechanism providing automatic GPIO request and config-
+	  uration as part of the gpio-controller's driver probe function.
+
 config ALTERA_PIO
 	bool "Altera PIO driver"
 	depends on DM_GPIO
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index da5e9ba6e5..4ecff115f1 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -5,6 +5,7 @@ 
 
 #include <common.h>
 #include <dm.h>
+#include <dm/device-internal.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <errno.h>
 #include <fdtdec.h>
@@ -15,6 +16,18 @@ 
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#if defined(CONFIG_DM_GPIO_HOG)
+struct gpio_priv_one_hog {
+	struct list_head list;
+	char *name;
+	struct gpio_desc gpiod;
+};
+
+static struct list_head hogs;
+static int list_init;
+static int gpio_hogs_probed;
+#endif
+
 /**
  * gpio_to_device() - Convert global GPIO number to device, number
  *
@@ -141,6 +154,147 @@  static int gpio_find_and_xlate(struct gpio_desc *desc,
 		return gpio_xlate_offs_flags(desc->dev, desc, args);
 }
 
+#if defined(CONFIG_DM_GPIO_HOG)
+int gpio_hog_probe_all(void)
+{
+	struct udevice *dev;
+	ofnode node;
+	int ret;
+
+	/*
+	 * We need to probe all gpio-hog devices
+	 * at some point, as we cannot be sure,
+	 * that all gpio devices, which contain a
+	 * gpio-hog definition are probed.
+	 *
+	 */
+	if (gpio_hogs_probed)
+		return 0;
+
+	for (uclass_first_device(UCLASS_GPIO, &dev);
+	     dev;
+	     uclass_next_device(&dev)) {
+		dev_for_each_subnode(node, dev) {
+			if (ofnode_read_bool(node, "gpio-hog")) {
+				ret = device_probe(dev);
+				if (ret)
+					return ret;
+				break;
+			}
+		}
+	}
+	gpio_hogs_probed = 1;
+
+	return 0;
+}
+
+struct gpio_desc *gpio_hog_lookup_name(const char *name)
+{
+	struct list_head *entry;
+	struct gpio_priv_one_hog *cur;
+
+	/* be sure, all gpio devices with gpio-hogs are probed */
+	gpio_hog_probe_all();
+	list_for_each(entry, &hogs) {
+		cur = list_entry(entry, struct gpio_priv_one_hog, list);
+		if (strcmp(cur->name, name) == 0)
+			return &cur->gpiod;
+	}
+
+	return NULL;
+}
+
+static int gpio_hog(struct udevice *dev)
+{
+	ofnode node;
+	int found = 0;
+	int ret;
+	struct gpio_dev_priv *uc_priv = NULL;
+
+	if (!list_init) {
+		INIT_LIST_HEAD(&hogs);
+		list_init = 1;
+	}
+	dev_for_each_subnode(node, dev) {
+		if (ofnode_read_bool(node, "gpio-hog")) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return 0;
+
+	uc_priv = dev_get_uclass_priv(dev);
+	if (!uc_priv) {
+		printf("%s: missing private data.\n", __func__);
+		return 0;
+	}
+
+	/* scan for gpio-hog subnodes */
+	dev_for_each_subnode(node, dev) {
+		u32 val[2];
+		int value = 0;
+		int gpiod_flags;
+		struct gpio_priv_one_hog *new;
+		const char *nodename;
+
+		if (!ofnode_read_bool(node, "gpio-hog"))
+			continue;
+
+		if (ofnode_read_bool(node, "input")) {
+			gpiod_flags = GPIOD_IS_IN;
+		} else if (ofnode_read_bool(node, "output-high")) {
+			value = 1;
+			gpiod_flags = GPIOD_IS_OUT;
+		} else if (ofnode_read_bool(node, "output-low")) {
+			gpiod_flags = GPIOD_IS_OUT;
+		} else {
+			printf("%s: missing gpio-hog state.\n", __func__);
+			return -EINVAL;
+		}
+
+		ret = ofnode_read_u32_array(node, "gpios", val, 2);
+		if (ret) {
+			printf("%s: wrong gpios property, 2 values needed, ret: %d\n", __func__, ret);
+			return ret;
+		}
+
+		new = calloc(1, sizeof(struct gpio_priv_one_hog));
+		nodename = ofnode_read_string(node, "line-name");
+		if (!nodename)
+			nodename = ofnode_get_name(node);
+		ret = gpio_dev_request_index(dev, nodename, "gpio-hog",
+					     val[0], gpiod_flags, val[1],
+					     &new->gpiod);
+		if (ret < 0) {
+			debug("%s: node %s could not get gpio.\n", __func__,
+			      ofnode_get_name(node));
+			free(new);
+			return ret;
+		}
+
+		new->name = uc_priv->name[new->gpiod.offset];
+		list_add_tail(&new->list, &hogs);
+		dm_gpio_set_dir(&new->gpiod);
+		if (gpiod_flags == GPIOD_IS_OUT)
+			dm_gpio_set_value(&new->gpiod, value);
+	}
+
+	return 0;
+}
+#else
+static int gpio_hog(struct udevice *dev)
+{
+	return 0;
+}
+
+struct gpio_desc *gpio_hog_lookup_name(const char *name)
+{
+	return NULL;
+}
+#endif
+
 int dm_gpio_request(struct gpio_desc *desc, const char *label)
 {
 	struct udevice *dev = desc->dev;
@@ -149,8 +303,9 @@  int dm_gpio_request(struct gpio_desc *desc, const char *label)
 	int ret;
 
 	uc_priv = dev_get_uclass_priv(dev);
-	if (uc_priv->name[desc->offset])
-		return -EBUSY;
+	if (uc_priv)
+		if (uc_priv->name[desc->offset])
+			return -EBUSY;
 	str = strdup(label);
 	if (!str)
 		return -ENOMEM;
@@ -640,22 +795,25 @@  int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count)
 	return vector;
 }
 
-static int gpio_request_tail(int ret, ofnode node,
+static int gpio_request_tail(int ret, const char *nodename,
 			     struct ofnode_phandle_args *args,
 			     const char *list_name, int index,
-			     struct gpio_desc *desc, int flags, bool add_index)
+			     struct gpio_desc *desc, int flags,
+			     bool add_index, struct udevice *dev)
 {
-	desc->dev = NULL;
+	desc->dev = dev;
 	desc->offset = 0;
 	desc->flags = 0;
 	if (ret)
 		goto err;
 
-	ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
-					  &desc->dev);
-	if (ret) {
-		debug("%s: uclass_get_device_by_ofnode failed\n", __func__);
-		goto err;
+	if (!desc->dev) {
+		ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
+						  &desc->dev);
+		if (ret) {
+			debug("%s: uclass_get_device_by_ofnode failed\n", __func__);
+			goto err;
+		}
 	}
 	ret = gpio_find_and_xlate(desc, args);
 	if (ret) {
@@ -663,8 +821,7 @@  static int gpio_request_tail(int ret, ofnode node,
 		goto err;
 	}
 	ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
-			       ofnode_get_name(node),
-			       list_name, index);
+			       nodename, list_name, index);
 	if (ret) {
 		debug("%s: dm_gpio_requestf failed\n", __func__);
 		goto err;
@@ -678,7 +835,7 @@  static int gpio_request_tail(int ret, ofnode node,
 	return 0;
 err:
 	debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n",
-	      __func__, ofnode_get_name(node), list_name, index, ret);
+	      __func__, nodename, list_name, index, ret);
 	return ret;
 }
 
@@ -692,8 +849,8 @@  static int _gpio_request_by_name_nodev(ofnode node, const char *list_name,
 	ret = ofnode_parse_phandle_with_args(node, list_name, "#gpio-cells", 0,
 					     index, &args);
 
-	return gpio_request_tail(ret, node, &args, list_name, index, desc,
-				 flags, add_index);
+	return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
+				 index, desc, flags, add_index, NULL);
 }
 
 int gpio_request_by_name_nodev(ofnode node, const char *list_name, int index,
@@ -707,13 +864,14 @@  int gpio_request_by_name(struct udevice *dev, const char *list_name, int index,
 			 struct gpio_desc *desc, int flags)
 {
 	struct ofnode_phandle_args args;
+	ofnode node;
 	int ret;
 
 	ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0,
 					 index, &args);
-
-	return gpio_request_tail(ret, dev_ofnode(dev), &args, list_name,
-				 index, desc, flags, index > 0);
+	node = dev_ofnode(dev);
+	return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
+				 index, desc, flags, index > 0, NULL);
 }
 
 int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
@@ -832,12 +990,17 @@  int gpio_get_number(const struct gpio_desc *desc)
 static int gpio_post_probe(struct udevice *dev)
 {
 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	int ret;
 
 	uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
 	if (!uc_priv->name)
 		return -ENOMEM;
 
-	return gpio_renumber(NULL);
+	ret = gpio_renumber(NULL);
+	if (ret)
+		return ret;
+
+	return gpio_hog(dev);
 }
 
 static int gpio_pre_remove(struct udevice *dev)
@@ -854,6 +1017,21 @@  static int gpio_pre_remove(struct udevice *dev)
 	return gpio_renumber(dev);
 }
 
+int gpio_dev_request_index(struct udevice *dev, const char *nodename,
+			   char *list_name, int index, int flags,
+			   int dtflags, struct gpio_desc *desc)
+{
+	struct ofnode_phandle_args args;
+
+	args.node =  ofnode_null();
+	args.args_count = 2;
+	args.args[0] = index;
+	args.args[1] = dtflags;
+
+	return gpio_request_tail(0, nodename, &args, list_name, index, desc,
+				 flags, 0, dev);
+}
+
 static int gpio_post_bind(struct udevice *dev)
 {
 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
@@ -885,6 +1063,7 @@  static int gpio_post_bind(struct udevice *dev)
 		reloc_done++;
 	}
 #endif
+
 	return 0;
 }
 
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index d03602696f..37f71e5708 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -348,6 +348,22 @@  const char *gpio_get_bank_info(struct udevice *dev, int *offset_count);
  */
 int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc);
 
+/**
+ * gpio_hog_lookup_name() - Look up a named GPIO and return the gpio descr.
+ *
+ * @name:	Name to look up
+ * @return:	Returns gpio_desc for gpio
+ */
+struct gpio_desc *gpio_hog_lookup_name(const char *name);
+
+/**
+ * gpio_hog_probe_all() - probe all gpio devices with
+ * gpio-hog subnodes.
+ *
+ * @return:	Returns return value from device_probe()
+ */
+int gpio_hog_probe_all(void);
+
 /**
  * gpio_lookup_name - Look up a GPIO name and return its details
  *
@@ -503,6 +519,22 @@  int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
 				    struct gpio_desc *desc_list, int max_count,
 				    int flags);
 
+/**
+ * gpio_dev_request_index() - request single GPIO from gpio device
+ *
+ * @dev:	GPIO device
+ * @nodename:	Name of node
+ * @list_name:	Name of GPIO list (e.g. "board-id-gpios")
+ * @index:	Index number of the GPIO in that list use request (0=first)
+ * @flags:	GPIOD_* flags
+ * @dtflags:	GPIO flags read from DT
+ * @desc:	GPIO descriotor filled from this function
+ * @return:	return value from gpio_request_tail()
+ */
+int gpio_dev_request_index(struct udevice *dev, const char *nodename,
+			   char *list_name, int index, int flags,
+			   int dtflags, struct gpio_desc *desc);
+
 /**
  * dm_gpio_free() - Free a single GPIO
  *