diff mbox series

[PATCH/RFC,2/2] gpio: of: Add DT overlay support for GPIO hogs

Message ID 20191230133852.5890-3-geert+renesas@glider.be
State New
Headers show
Series gpio: of: Add DT overlay support for GPIO hogs | expand

Commit Message

Geert Uytterhoeven Dec. 30, 2019, 1:38 p.m. UTC
As GPIO hogs are configured at GPIO controller initialization time,
adding/removing GPIO hogs in DT overlays does not work.

Add support for GPIO hogs described in DT overlays by registering an OF
reconfiguration notifier, to handle the addition and removal of GPIO hog
subnodes to/from a GPIO controller device node.

Note that when a GPIO hog device node is being removed, its "gpios"
properties is no longer available, so we have to keep track of which
node a hog belongs to, which is done by adding a pointer to the hog's
device node to struct gpio_desc.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 drivers/gpio/gpiolib-of.c | 84 +++++++++++++++++++++++++++++++++++++++
 drivers/gpio/gpiolib-of.h |  2 +
 drivers/gpio/gpiolib.c    | 14 +++++--
 drivers/gpio/gpiolib.h    |  3 ++
 4 files changed, 100 insertions(+), 3 deletions(-)

Comments

Frank Rowand Jan. 6, 2020, 11:34 p.m. UTC | #1
On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
> As GPIO hogs are configured at GPIO controller initialization time,
> adding/removing GPIO hogs in DT overlays does not work.
> 
> Add support for GPIO hogs described in DT overlays by registering an OF
> reconfiguration notifier, to handle the addition and removal of GPIO hog
> subnodes to/from a GPIO controller device node.
> 
> Note that when a GPIO hog device node is being removed, its "gpios"
> properties is no longer available, so we have to keep track of which
> node a hog belongs to, which is done by adding a pointer to the hog's
> device node to struct gpio_desc.

If I have read the patches and the existing overlay source correctly,
then some observations:

- A gpio hog node added in an overlay will be properly processed.

- A gpio hog node already existing in the live devicetree, but with a
  non-active status will be properly processed if the status of the
  gpio hog node is changed to "ok" in the overlay.

- If a gpio hog node already exists in the live devicetree with an
  active status, then any updated or added properties in that gpio
  hog node in the overlay will have no effect.

  There is a scenario where the updated property would have an effect:
  apply a second overlay that sets the status to inactive, then apply
  a third overlay that sets the status back to active.  This is a
  rather contrived example and I think it should be documented as
  not supported and the result undefined.

It would be good to document this explicitly.


> 
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
>  drivers/gpio/gpiolib-of.c | 84 +++++++++++++++++++++++++++++++++++++++
>  drivers/gpio/gpiolib-of.h |  2 +
>  drivers/gpio/gpiolib.c    | 14 +++++--
>  drivers/gpio/gpiolib.h    |  3 ++
>  4 files changed, 100 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
> index dfae797846bb746b..89a6138ac0a4b506 100644
> --- a/drivers/gpio/gpiolib-of.c
> +++ b/drivers/gpio/gpiolib-of.c
> @@ -649,6 +649,10 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
>  		ret = gpiod_hog(desc, name, lflags, dflags);
>  		if (ret < 0)
>  			return ret;
> +
> +#ifdef CONFIG_OF_DYNAMIC
> +		desc->hog = hog;
> +#endif
>  	}
>  
>  	return 0;
> @@ -676,11 +680,91 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
>  			of_node_put(np);
>  			return ret;
>  		}
> +
> +		of_node_set_flag(np, OF_POPULATED);
>  	}
>  
>  	return 0;
>  }
>  
> +#ifdef CONFIG_OF_DYNAMIC
> +/**
> + * of_gpiochip_remove_hog - Remove all hogs in a hog device node
> + * @chip:	gpio chip to act on
> + * @hog:	device node describing the hogs
> + */
> +static void of_gpiochip_remove_hog(struct gpio_chip *chip,
> +				   struct device_node *hog)
> +{
> +	struct gpio_desc *descs = chip->gpiodev->descs;
> +	unsigned int i;
> +
> +	for (i = 0; i < chip->ngpio; i++) {
> +		if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) &&
> +		    descs[i].hog == hog)
> +			gpiochip_free_own_desc(&descs[i]);
> +	}
> +}
> +
> +static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
> +{
> +	return chip->gpiodev->dev.of_node == data;
> +}
> +
> +static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
> +{
> +	return gpiochip_find(np, of_gpiochip_match_node);
> +}
> +
> +static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
> +			  void *arg)
> +{
> +	struct of_reconfig_data *rd = arg;
> +	struct gpio_chip *chip;
> +	int ret;
> +
> +	switch (of_reconfig_get_state_change(action, arg)) {
> +	case OF_RECONFIG_CHANGE_ADD:
> +		if (!of_property_read_bool(rd->dn, "gpio-hog"))
> +			return NOTIFY_OK;	/* not for us */
> +
> +		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
> +			return NOTIFY_OK;

I don't think OF_POPULATED could be already set.  It seems to be a
bug if it is.


> +
> +		chip = of_find_gpiochip_by_node(rd->dn->parent);
> +		if (chip == NULL)
> +			return NOTIFY_OK;	/* not for us */

If I understand correctly, "not for us" is a misleading comment.
The notification is for the node rd->dn->parent, but the device
does not exist, so we can't do the hogging at the moment.  (If the
device is created later, then the gpio hog child node will exist,
and the init will "do the right thing" with the hog node -- so
not a problem.)

 
> +
> +		ret = of_gpiochip_add_hog(chip, rd->dn);
> +		if (ret < 0) {
> +			pr_err("%s: failed to add hogs for %pOF\n", __func__,
> +			       rd->dn);
> +			of_node_clear_flag(rd->dn, OF_POPULATED);
> +			return notifier_from_errno(ret);
> +		}
> +		break;
> +
> +	case OF_RECONFIG_CHANGE_REMOVE:
> +		if (!of_node_check_flag(rd->dn, OF_POPULATED))
> +			return NOTIFY_OK;	/* already depopulated */

I don't think OF_POPULATED could be already cleared.  It seems to be a
bug if it is.

> +
> +		chip = of_find_gpiochip_by_node(rd->dn->parent);
> +		if (chip == NULL)
> +			return NOTIFY_OK;	/* not for us */

Again, a misleading comment.

> +
> +		of_gpiochip_remove_hog(chip, rd->dn);
> +		of_node_clear_flag(rd->dn, OF_POPULATED);
> +		break;
> +	}
> +
> +	return NOTIFY_OK;
> +}
> +
> +struct notifier_block gpio_of_notifier = {
> +	.notifier_call = of_gpio_notify,
> +};
> +#endif /* CONFIG_OF_DYNAMIC */
> +
>  /**
>   * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
>   * @gc:		pointer to the gpio_chip structure
> diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
> index 9768831b1fe2f25b..ed26664f153782fc 100644
> --- a/drivers/gpio/gpiolib-of.h
> +++ b/drivers/gpio/gpiolib-of.h
> @@ -35,4 +35,6 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
>  }
>  #endif /* CONFIG_OF_GPIO */
>  
> +extern struct notifier_block gpio_of_notifier;
> +
>  #endif /* GPIOLIB_OF_H */
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index bff5ac774d870b67..ef12cfcaf0962c1c 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -2952,6 +2952,9 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
>  		clear_bit(FLAG_PULL_DOWN, &desc->flags);
>  		clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
>  		clear_bit(FLAG_IS_HOGGED, &desc->flags);
> +#ifdef CONFIG_OF_DYNAMIC
> +		desc->hog = NULL;
> +#endif
>  		ret = true;
>  	}
>  
> @@ -5145,10 +5148,15 @@ static int __init gpiolib_dev_init(void)
>  	if (ret < 0) {
>  		pr_err("gpiolib: failed to allocate char dev region\n");
>  		bus_unregister(&gpio_bus_type);
> -	} else {
> -		gpiolib_initialized = true;
> -		gpiochip_setup_devs();
> +		return ret;
>  	}
> +
> +	gpiolib_initialized = true;
> +	gpiochip_setup_devs();
> +
> +	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
> +		WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
> +
>  	return ret;
>  }
>  core_initcall(gpiolib_dev_init);
> diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
> index a4a759920faa48ab..7af9931e8572304a 100644
> --- a/drivers/gpio/gpiolib.h
> +++ b/drivers/gpio/gpiolib.h
> @@ -118,6 +118,9 @@ struct gpio_desc {
>  	const char		*label;
>  	/* Name of the GPIO */
>  	const char		*name;
> +#ifdef CONFIG_OF_DYNAMIC
> +	struct device_node	*hog;
> +#endif
>  };
>  
>  int gpiod_request(struct gpio_desc *desc, const char *label);
>
Frank Rowand Jan. 7, 2020, 7:10 a.m. UTC | #2
On 1/6/20 5:34 PM, Frank Rowand wrote:
> On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
>> As GPIO hogs are configured at GPIO controller initialization time,
>> adding/removing GPIO hogs in DT overlays does not work.
>>
>> Add support for GPIO hogs described in DT overlays by registering an OF
>> reconfiguration notifier, to handle the addition and removal of GPIO hog
>> subnodes to/from a GPIO controller device node.
>>
>> Note that when a GPIO hog device node is being removed, its "gpios"
>> properties is no longer available, so we have to keep track of which
>> node a hog belongs to, which is done by adding a pointer to the hog's
>> device node to struct gpio_desc.
> 
> If I have read the patches and the existing overlay source correctly,
> then some observations:
> 
> - A gpio hog node added in an overlay will be properly processed.
> 
> - A gpio hog node already existing in the live devicetree, but with a
>   non-active status will be properly processed if the status of the
>   gpio hog node is changed to "ok" in the overlay.
> 
> - If a gpio hog node already exists in the live devicetree with an
>   active status, then any updated or added properties in that gpio
>   hog node in the overlay will have no effect.
> 
>   There is a scenario where the updated property would have an effect:
>   apply a second overlay that sets the status to inactive, then apply
>   a third overlay that sets the status back to active.  This is a
>   rather contrived example and I think it should be documented as
>   not supported and the result undefined.

I went back and double checked the related code.  For gpio hog nodes
that are in a non-overlay, the status property is checked because
of_gpiochip_scan_gpios() uses for_each_available_child_of_node()
to search for gpio hog nodes, and for_each_available_child_of_node()
checks the status property.  But in the case of a gpio hog node
added by an overlay, of_gpio_notify() does not check the status
property in the gpio hog node.  The check for the status property
should be added to of_gpio_notify().

-Frank

> 
> It would be good to document this explicitly.
> 
> 
>>
>> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
>> ---
>>  drivers/gpio/gpiolib-of.c | 84 +++++++++++++++++++++++++++++++++++++++
>>  drivers/gpio/gpiolib-of.h |  2 +
>>  drivers/gpio/gpiolib.c    | 14 +++++--
>>  drivers/gpio/gpiolib.h    |  3 ++
>>  4 files changed, 100 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
>> index dfae797846bb746b..89a6138ac0a4b506 100644
>> --- a/drivers/gpio/gpiolib-of.c
>> +++ b/drivers/gpio/gpiolib-of.c
>> @@ -649,6 +649,10 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
>>  		ret = gpiod_hog(desc, name, lflags, dflags);
>>  		if (ret < 0)
>>  			return ret;
>> +
>> +#ifdef CONFIG_OF_DYNAMIC
>> +		desc->hog = hog;
>> +#endif
>>  	}
>>  
>>  	return 0;
>> @@ -676,11 +680,91 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
>>  			of_node_put(np);
>>  			return ret;
>>  		}
>> +
>> +		of_node_set_flag(np, OF_POPULATED);
>>  	}
>>  
>>  	return 0;
>>  }
>>  
>> +#ifdef CONFIG_OF_DYNAMIC
>> +/**
>> + * of_gpiochip_remove_hog - Remove all hogs in a hog device node
>> + * @chip:	gpio chip to act on
>> + * @hog:	device node describing the hogs
>> + */
>> +static void of_gpiochip_remove_hog(struct gpio_chip *chip,
>> +				   struct device_node *hog)
>> +{
>> +	struct gpio_desc *descs = chip->gpiodev->descs;
>> +	unsigned int i;
>> +
>> +	for (i = 0; i < chip->ngpio; i++) {
>> +		if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) &&
>> +		    descs[i].hog == hog)
>> +			gpiochip_free_own_desc(&descs[i]);
>> +	}
>> +}
>> +
>> +static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
>> +{
>> +	return chip->gpiodev->dev.of_node == data;
>> +}
>> +
>> +static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
>> +{
>> +	return gpiochip_find(np, of_gpiochip_match_node);
>> +}
>> +
>> +static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
>> +			  void *arg)
>> +{
>> +	struct of_reconfig_data *rd = arg;
>> +	struct gpio_chip *chip;
>> +	int ret;
>> +
>> +	switch (of_reconfig_get_state_change(action, arg)) {
>> +	case OF_RECONFIG_CHANGE_ADD:
>> +		if (!of_property_read_bool(rd->dn, "gpio-hog"))
>> +			return NOTIFY_OK;	/* not for us */
>> +
>> +		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
>> +			return NOTIFY_OK;
> 
> I don't think OF_POPULATED could be already set.  It seems to be a
> bug if it is.
> 
> 
>> +
>> +		chip = of_find_gpiochip_by_node(rd->dn->parent);
>> +		if (chip == NULL)
>> +			return NOTIFY_OK;	/* not for us */
> 
> If I understand correctly, "not for us" is a misleading comment.
> The notification is for the node rd->dn->parent, but the device
> does not exist, so we can't do the hogging at the moment.  (If the
> device is created later, then the gpio hog child node will exist,
> and the init will "do the right thing" with the hog node -- so
> not a problem.)
> 
>  
>> +
>> +		ret = of_gpiochip_add_hog(chip, rd->dn);
>> +		if (ret < 0) {
>> +			pr_err("%s: failed to add hogs for %pOF\n", __func__,
>> +			       rd->dn);
>> +			of_node_clear_flag(rd->dn, OF_POPULATED);
>> +			return notifier_from_errno(ret);
>> +		}
>> +		break;
>> +
>> +	case OF_RECONFIG_CHANGE_REMOVE:
>> +		if (!of_node_check_flag(rd->dn, OF_POPULATED))
>> +			return NOTIFY_OK;	/* already depopulated */
> 
> I don't think OF_POPULATED could be already cleared.  It seems to be a
> bug if it is.
> 
>> +
>> +		chip = of_find_gpiochip_by_node(rd->dn->parent);
>> +		if (chip == NULL)
>> +			return NOTIFY_OK;	/* not for us */
> 
> Again, a misleading comment.
> 
>> +
>> +		of_gpiochip_remove_hog(chip, rd->dn);
>> +		of_node_clear_flag(rd->dn, OF_POPULATED);
>> +		break;
>> +	}
>> +
>> +	return NOTIFY_OK;
>> +}
>> +
>> +struct notifier_block gpio_of_notifier = {
>> +	.notifier_call = of_gpio_notify,
>> +};
>> +#endif /* CONFIG_OF_DYNAMIC */
>> +
>>  /**
>>   * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
>>   * @gc:		pointer to the gpio_chip structure
>> diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
>> index 9768831b1fe2f25b..ed26664f153782fc 100644
>> --- a/drivers/gpio/gpiolib-of.h
>> +++ b/drivers/gpio/gpiolib-of.h
>> @@ -35,4 +35,6 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
>>  }
>>  #endif /* CONFIG_OF_GPIO */
>>  
>> +extern struct notifier_block gpio_of_notifier;
>> +
>>  #endif /* GPIOLIB_OF_H */
>> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
>> index bff5ac774d870b67..ef12cfcaf0962c1c 100644
>> --- a/drivers/gpio/gpiolib.c
>> +++ b/drivers/gpio/gpiolib.c
>> @@ -2952,6 +2952,9 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
>>  		clear_bit(FLAG_PULL_DOWN, &desc->flags);
>>  		clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
>>  		clear_bit(FLAG_IS_HOGGED, &desc->flags);
>> +#ifdef CONFIG_OF_DYNAMIC
>> +		desc->hog = NULL;
>> +#endif
>>  		ret = true;
>>  	}
>>  
>> @@ -5145,10 +5148,15 @@ static int __init gpiolib_dev_init(void)
>>  	if (ret < 0) {
>>  		pr_err("gpiolib: failed to allocate char dev region\n");
>>  		bus_unregister(&gpio_bus_type);
>> -	} else {
>> -		gpiolib_initialized = true;
>> -		gpiochip_setup_devs();
>> +		return ret;
>>  	}
>> +
>> +	gpiolib_initialized = true;
>> +	gpiochip_setup_devs();
>> +
>> +	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
>> +		WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
>> +
>>  	return ret;
>>  }
>>  core_initcall(gpiolib_dev_init);
>> diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
>> index a4a759920faa48ab..7af9931e8572304a 100644
>> --- a/drivers/gpio/gpiolib.h
>> +++ b/drivers/gpio/gpiolib.h
>> @@ -118,6 +118,9 @@ struct gpio_desc {
>>  	const char		*label;
>>  	/* Name of the GPIO */
>>  	const char		*name;
>> +#ifdef CONFIG_OF_DYNAMIC
>> +	struct device_node	*hog;
>> +#endif
>>  };
>>  
>>  int gpiod_request(struct gpio_desc *desc, const char *label);
>>
> 
>
Frank Rowand Jan. 7, 2020, 7:25 a.m. UTC | #3
On 1/7/20 1:10 AM, Frank Rowand wrote:
> On 1/6/20 5:34 PM, Frank Rowand wrote:
>> On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
>>> As GPIO hogs are configured at GPIO controller initialization time,
>>> adding/removing GPIO hogs in DT overlays does not work.
>>>
>>> Add support for GPIO hogs described in DT overlays by registering an OF
>>> reconfiguration notifier, to handle the addition and removal of GPIO hog
>>> subnodes to/from a GPIO controller device node.
>>>
>>> Note that when a GPIO hog device node is being removed, its "gpios"
>>> properties is no longer available, so we have to keep track of which
>>> node a hog belongs to, which is done by adding a pointer to the hog's
>>> device node to struct gpio_desc.
>>
>> If I have read the patches and the existing overlay source correctly,
>> then some observations:
>>
>> - A gpio hog node added in an overlay will be properly processed.
>>
>> - A gpio hog node already existing in the live devicetree, but with a
>>   non-active status will be properly processed if the status of the
>>   gpio hog node is changed to "ok" in the overlay.
>>
>> - If a gpio hog node already exists in the live devicetree with an
>>   active status, then any updated or added properties in that gpio
>>   hog node in the overlay will have no effect.
>>
>>   There is a scenario where the updated property would have an effect:
>>   apply a second overlay that sets the status to inactive, then apply
>>   a third overlay that sets the status back to active.  This is a
>>   rather contrived example and I think it should be documented as
>>   not supported and the result undefined.
> 
> I went back and double checked the related code.  For gpio hog nodes
> that are in a non-overlay, the status property is checked because
> of_gpiochip_scan_gpios() uses for_each_available_child_of_node()
> to search for gpio hog nodes, and for_each_available_child_of_node()
> checks the status property.  But in the case of a gpio hog node
> added by an overlay, of_gpio_notify() does not check the status
> property in the gpio hog node.  The check for the status property
> should be added to of_gpio_notify().

One more thing I have not thought through is the case where the
overlay contains both the gpio node and the gpio hog node.  I'll
walk through that tomorrow.

-Frank

> 
> -Frank
> 
>>
>> It would be good to document this explicitly.
>>
>>
>>>
>>> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
>>> ---
>>>  drivers/gpio/gpiolib-of.c | 84 +++++++++++++++++++++++++++++++++++++++
>>>  drivers/gpio/gpiolib-of.h |  2 +
>>>  drivers/gpio/gpiolib.c    | 14 +++++--
>>>  drivers/gpio/gpiolib.h    |  3 ++
>>>  4 files changed, 100 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
>>> index dfae797846bb746b..89a6138ac0a4b506 100644
>>> --- a/drivers/gpio/gpiolib-of.c
>>> +++ b/drivers/gpio/gpiolib-of.c
>>> @@ -649,6 +649,10 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
>>>  		ret = gpiod_hog(desc, name, lflags, dflags);
>>>  		if (ret < 0)
>>>  			return ret;
>>> +
>>> +#ifdef CONFIG_OF_DYNAMIC
>>> +		desc->hog = hog;
>>> +#endif
>>>  	}
>>>  
>>>  	return 0;
>>> @@ -676,11 +680,91 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
>>>  			of_node_put(np);
>>>  			return ret;
>>>  		}
>>> +
>>> +		of_node_set_flag(np, OF_POPULATED);
>>>  	}
>>>  
>>>  	return 0;
>>>  }
>>>  
>>> +#ifdef CONFIG_OF_DYNAMIC
>>> +/**
>>> + * of_gpiochip_remove_hog - Remove all hogs in a hog device node
>>> + * @chip:	gpio chip to act on
>>> + * @hog:	device node describing the hogs
>>> + */
>>> +static void of_gpiochip_remove_hog(struct gpio_chip *chip,
>>> +				   struct device_node *hog)
>>> +{
>>> +	struct gpio_desc *descs = chip->gpiodev->descs;
>>> +	unsigned int i;
>>> +
>>> +	for (i = 0; i < chip->ngpio; i++) {
>>> +		if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) &&
>>> +		    descs[i].hog == hog)
>>> +			gpiochip_free_own_desc(&descs[i]);
>>> +	}
>>> +}
>>> +
>>> +static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
>>> +{
>>> +	return chip->gpiodev->dev.of_node == data;
>>> +}
>>> +
>>> +static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
>>> +{
>>> +	return gpiochip_find(np, of_gpiochip_match_node);
>>> +}
>>> +
>>> +static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
>>> +			  void *arg)
>>> +{
>>> +	struct of_reconfig_data *rd = arg;
>>> +	struct gpio_chip *chip;
>>> +	int ret;
>>> +
>>> +	switch (of_reconfig_get_state_change(action, arg)) {
>>> +	case OF_RECONFIG_CHANGE_ADD:
>>> +		if (!of_property_read_bool(rd->dn, "gpio-hog"))
>>> +			return NOTIFY_OK;	/* not for us */
>>> +
>>> +		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
>>> +			return NOTIFY_OK;
>>
>> I don't think OF_POPULATED could be already set.  It seems to be a
>> bug if it is.
>>
>>
>>> +
>>> +		chip = of_find_gpiochip_by_node(rd->dn->parent);
>>> +		if (chip == NULL)
>>> +			return NOTIFY_OK;	/* not for us */
>>
>> If I understand correctly, "not for us" is a misleading comment.
>> The notification is for the node rd->dn->parent, but the device
>> does not exist, so we can't do the hogging at the moment.  (If the
>> device is created later, then the gpio hog child node will exist,
>> and the init will "do the right thing" with the hog node -- so
>> not a problem.)
>>
>>  
>>> +
>>> +		ret = of_gpiochip_add_hog(chip, rd->dn);
>>> +		if (ret < 0) {
>>> +			pr_err("%s: failed to add hogs for %pOF\n", __func__,
>>> +			       rd->dn);
>>> +			of_node_clear_flag(rd->dn, OF_POPULATED);
>>> +			return notifier_from_errno(ret);
>>> +		}
>>> +		break;
>>> +
>>> +	case OF_RECONFIG_CHANGE_REMOVE:
>>> +		if (!of_node_check_flag(rd->dn, OF_POPULATED))
>>> +			return NOTIFY_OK;	/* already depopulated */
>>
>> I don't think OF_POPULATED could be already cleared.  It seems to be a
>> bug if it is.
>>
>>> +
>>> +		chip = of_find_gpiochip_by_node(rd->dn->parent);
>>> +		if (chip == NULL)
>>> +			return NOTIFY_OK;	/* not for us */
>>
>> Again, a misleading comment.
>>
>>> +
>>> +		of_gpiochip_remove_hog(chip, rd->dn);
>>> +		of_node_clear_flag(rd->dn, OF_POPULATED);
>>> +		break;
>>> +	}
>>> +
>>> +	return NOTIFY_OK;
>>> +}
>>> +
>>> +struct notifier_block gpio_of_notifier = {
>>> +	.notifier_call = of_gpio_notify,
>>> +};
>>> +#endif /* CONFIG_OF_DYNAMIC */
>>> +
>>>  /**
>>>   * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
>>>   * @gc:		pointer to the gpio_chip structure
>>> diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
>>> index 9768831b1fe2f25b..ed26664f153782fc 100644
>>> --- a/drivers/gpio/gpiolib-of.h
>>> +++ b/drivers/gpio/gpiolib-of.h
>>> @@ -35,4 +35,6 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
>>>  }
>>>  #endif /* CONFIG_OF_GPIO */
>>>  
>>> +extern struct notifier_block gpio_of_notifier;
>>> +
>>>  #endif /* GPIOLIB_OF_H */
>>> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
>>> index bff5ac774d870b67..ef12cfcaf0962c1c 100644
>>> --- a/drivers/gpio/gpiolib.c
>>> +++ b/drivers/gpio/gpiolib.c
>>> @@ -2952,6 +2952,9 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
>>>  		clear_bit(FLAG_PULL_DOWN, &desc->flags);
>>>  		clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
>>>  		clear_bit(FLAG_IS_HOGGED, &desc->flags);
>>> +#ifdef CONFIG_OF_DYNAMIC
>>> +		desc->hog = NULL;
>>> +#endif
>>>  		ret = true;
>>>  	}
>>>  
>>> @@ -5145,10 +5148,15 @@ static int __init gpiolib_dev_init(void)
>>>  	if (ret < 0) {
>>>  		pr_err("gpiolib: failed to allocate char dev region\n");
>>>  		bus_unregister(&gpio_bus_type);
>>> -	} else {
>>> -		gpiolib_initialized = true;
>>> -		gpiochip_setup_devs();
>>> +		return ret;
>>>  	}
>>> +
>>> +	gpiolib_initialized = true;
>>> +	gpiochip_setup_devs();
>>> +
>>> +	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
>>> +		WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
>>> +
>>>  	return ret;
>>>  }
>>>  core_initcall(gpiolib_dev_init);
>>> diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
>>> index a4a759920faa48ab..7af9931e8572304a 100644
>>> --- a/drivers/gpio/gpiolib.h
>>> +++ b/drivers/gpio/gpiolib.h
>>> @@ -118,6 +118,9 @@ struct gpio_desc {
>>>  	const char		*label;
>>>  	/* Name of the GPIO */
>>>  	const char		*name;
>>> +#ifdef CONFIG_OF_DYNAMIC
>>> +	struct device_node	*hog;
>>> +#endif
>>>  };
>>>  
>>>  int gpiod_request(struct gpio_desc *desc, const char *label);
>>>
>>
>>
> 
>
Geert Uytterhoeven Jan. 7, 2020, 7:59 a.m. UTC | #4
Hi Frank,

On Tue, Jan 7, 2020 at 12:34 AM Frank Rowand <frowand.list@gmail.com> wrote:
> On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
> > As GPIO hogs are configured at GPIO controller initialization time,
> > adding/removing GPIO hogs in DT overlays does not work.
> >
> > Add support for GPIO hogs described in DT overlays by registering an OF
> > reconfiguration notifier, to handle the addition and removal of GPIO hog
> > subnodes to/from a GPIO controller device node.
> >
> > Note that when a GPIO hog device node is being removed, its "gpios"
> > properties is no longer available, so we have to keep track of which
> > node a hog belongs to, which is done by adding a pointer to the hog's
> > device node to struct gpio_desc.
>
> If I have read the patches and the existing overlay source correctly,
> then some observations:
>
> - A gpio hog node added in an overlay will be properly processed.
>
> - A gpio hog node already existing in the live devicetree, but with a
>   non-active status will be properly processed if the status of the
>   gpio hog node is changed to "ok" in the overlay.
>
> - If a gpio hog node already exists in the live devicetree with an
>   active status, then any updated or added properties in that gpio
>   hog node in the overlay will have no effect.
>
>   There is a scenario where the updated property would have an effect:
>   apply a second overlay that sets the status to inactive, then apply
>   a third overlay that sets the status back to active.  This is a
>   rather contrived example and I think it should be documented as
>   not supported and the result undefined.
>
> It would be good to document this explicitly.

I didn't verify this in detail, but I believe the existing overlay
support for platform, i2c, and SPI devices behaves the same.

> > --- a/drivers/gpio/gpiolib-of.c
> > +++ b/drivers/gpio/gpiolib-of.c

> > +static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
> > +                       void *arg)
> > +{
> > +     struct of_reconfig_data *rd = arg;
> > +     struct gpio_chip *chip;
> > +     int ret;
> > +
> > +     switch (of_reconfig_get_state_change(action, arg)) {
> > +     case OF_RECONFIG_CHANGE_ADD:
> > +             if (!of_property_read_bool(rd->dn, "gpio-hog"))
> > +                     return NOTIFY_OK;       /* not for us */
> > +
> > +             if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
> > +                     return NOTIFY_OK;
>
> I don't think OF_POPULATED could be already set.  It seems to be a
> bug if it is.

For a real gpio-hog it indeed is not.  But this function is called for
every change made to the device tree (add a printk() and look at the
output during boot).  So this serves as a (cheap) line of defense.
The of_find_gpiochip_by_node() call below is more expensive to call.

> > +
> > +             chip = of_find_gpiochip_by_node(rd->dn->parent);
> > +             if (chip == NULL)
> > +                     return NOTIFY_OK;       /* not for us */
>
> If I understand correctly, "not for us" is a misleading comment.
> The notification is for the node rd->dn->parent, but the device
> does not exist, so we can't do the hogging at the moment.  (If the
> device is created later, then the gpio hog child node will exist,
> and the init will "do the right thing" with the hog node -- so
> not a problem.)

This function is called for all additions to the device tree.
So rd->dn->parent may not even be a gpio controller node.
Hence unless this is a gpio controller node for this hog, this
notification is "not for us".

> > +
> > +             ret = of_gpiochip_add_hog(chip, rd->dn);
> > +             if (ret < 0) {
> > +                     pr_err("%s: failed to add hogs for %pOF\n", __func__,
> > +                            rd->dn);
> > +                     of_node_clear_flag(rd->dn, OF_POPULATED);
> > +                     return notifier_from_errno(ret);
> > +             }
> > +             break;
> > +
> > +     case OF_RECONFIG_CHANGE_REMOVE:
> > +             if (!of_node_check_flag(rd->dn, OF_POPULATED))
> > +                     return NOTIFY_OK;       /* already depopulated */
>
> I don't think OF_POPULATED could be already cleared.  It seems to be a
> bug if it is.

Same here. First line of defense.

> > +
> > +             chip = of_find_gpiochip_by_node(rd->dn->parent);
> > +             if (chip == NULL)
> > +                     return NOTIFY_OK;       /* not for us */
>
> Again, a misleading comment.

Same here. rd->dn->parent may be something else.

> > +
> > +             of_gpiochip_remove_hog(chip, rd->dn);
> > +             of_node_clear_flag(rd->dn, OF_POPULATED);
> > +             break;
> > +     }
> > +
> > +     return NOTIFY_OK;
> > +}

Gr{oetje,eeting}s,

                        Geert
Geert Uytterhoeven Jan. 7, 2020, 8:02 a.m. UTC | #5
Hi Frank,

On Tue, Jan 7, 2020 at 8:26 AM Frank Rowand <frowand.list@gmail.com> wrote:
> On 1/7/20 1:10 AM, Frank Rowand wrote:
> > On 1/6/20 5:34 PM, Frank Rowand wrote:
> >> On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
> >>> As GPIO hogs are configured at GPIO controller initialization time,
> >>> adding/removing GPIO hogs in DT overlays does not work.
> >>>
> >>> Add support for GPIO hogs described in DT overlays by registering an OF
> >>> reconfiguration notifier, to handle the addition and removal of GPIO hog
> >>> subnodes to/from a GPIO controller device node.
> >>>
> >>> Note that when a GPIO hog device node is being removed, its "gpios"
> >>> properties is no longer available, so we have to keep track of which
> >>> node a hog belongs to, which is done by adding a pointer to the hog's
> >>> device node to struct gpio_desc.
> >>
> >> If I have read the patches and the existing overlay source correctly,
> >> then some observations:
> >>
> >> - A gpio hog node added in an overlay will be properly processed.
> >>
> >> - A gpio hog node already existing in the live devicetree, but with a
> >>   non-active status will be properly processed if the status of the
> >>   gpio hog node is changed to "ok" in the overlay.
> >>
> >> - If a gpio hog node already exists in the live devicetree with an
> >>   active status, then any updated or added properties in that gpio
> >>   hog node in the overlay will have no effect.
> >>
> >>   There is a scenario where the updated property would have an effect:
> >>   apply a second overlay that sets the status to inactive, then apply
> >>   a third overlay that sets the status back to active.  This is a
> >>   rather contrived example and I think it should be documented as
> >>   not supported and the result undefined.
> >
> > I went back and double checked the related code.  For gpio hog nodes
> > that are in a non-overlay, the status property is checked because
> > of_gpiochip_scan_gpios() uses for_each_available_child_of_node()
> > to search for gpio hog nodes, and for_each_available_child_of_node()
> > checks the status property.  But in the case of a gpio hog node
> > added by an overlay, of_gpio_notify() does not check the status
> > property in the gpio hog node.  The check for the status property
> > should be added to of_gpio_notify().
>
> One more thing I have not thought through is the case where the
> overlay contains both the gpio node and the gpio hog node.  I'll
> walk through that tomorrow.

I haven't veried that, but I assume that works already without my patch,
as GPIO hogs are configured at GPIO controller initialization time.

Gr{oetje,eeting}s,

                        Geert
Geert Uytterhoeven Jan. 7, 2020, 8:11 a.m. UTC | #6
Hi Frank,

On Tue, Jan 7, 2020 at 8:10 AM Frank Rowand <frowand.list@gmail.com> wrote:
> On 1/6/20 5:34 PM, Frank Rowand wrote:
> > On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
> >> As GPIO hogs are configured at GPIO controller initialization time,
> >> adding/removing GPIO hogs in DT overlays does not work.
> >>
> >> Add support for GPIO hogs described in DT overlays by registering an OF
> >> reconfiguration notifier, to handle the addition and removal of GPIO hog
> >> subnodes to/from a GPIO controller device node.
> >>
> >> Note that when a GPIO hog device node is being removed, its "gpios"
> >> properties is no longer available, so we have to keep track of which
> >> node a hog belongs to, which is done by adding a pointer to the hog's
> >> device node to struct gpio_desc.
> >
> > If I have read the patches and the existing overlay source correctly,
> > then some observations:
> >
> > - A gpio hog node added in an overlay will be properly processed.
> >
> > - A gpio hog node already existing in the live devicetree, but with a
> >   non-active status will be properly processed if the status of the
> >   gpio hog node is changed to "ok" in the overlay.
> >
> > - If a gpio hog node already exists in the live devicetree with an
> >   active status, then any updated or added properties in that gpio
> >   hog node in the overlay will have no effect.
> >
> >   There is a scenario where the updated property would have an effect:
> >   apply a second overlay that sets the status to inactive, then apply
> >   a third overlay that sets the status back to active.  This is a
> >   rather contrived example and I think it should be documented as
> >   not supported and the result undefined.
>
> I went back and double checked the related code.  For gpio hog nodes
> that are in a non-overlay, the status property is checked because
> of_gpiochip_scan_gpios() uses for_each_available_child_of_node()
> to search for gpio hog nodes, and for_each_available_child_of_node()
> checks the status property.  But in the case of a gpio hog node
> added by an overlay, of_gpio_notify() does not check the status
> property in the gpio hog node.  The check for the status property
> should be added to of_gpio_notify().

Right.  of_device_is_available() should be called to check this.
Note that of_i2c_notify() and of_spi_notify() also lack such a check.
of_platform_notify() calls of_platform_device_create_pdata(), which does
have the check.

Gr{oetje,eeting}s,

                        Geert
Frank Rowand Jan. 24, 2020, 9:57 p.m. UTC | #7
On 1/7/20 2:11 AM, Geert Uytterhoeven wrote:
> Hi Frank,
> 
> On Tue, Jan 7, 2020 at 8:10 AM Frank Rowand <frowand.list@gmail.com> wrote:
>> On 1/6/20 5:34 PM, Frank Rowand wrote:
>>> On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
>>>> As GPIO hogs are configured at GPIO controller initialization time,
>>>> adding/removing GPIO hogs in DT overlays does not work.
>>>>
>>>> Add support for GPIO hogs described in DT overlays by registering an OF
>>>> reconfiguration notifier, to handle the addition and removal of GPIO hog
>>>> subnodes to/from a GPIO controller device node.
>>>>
>>>> Note that when a GPIO hog device node is being removed, its "gpios"
>>>> properties is no longer available, so we have to keep track of which
>>>> node a hog belongs to, which is done by adding a pointer to the hog's
>>>> device node to struct gpio_desc.
>>>
>>> If I have read the patches and the existing overlay source correctly,
>>> then some observations:
>>>
>>> - A gpio hog node added in an overlay will be properly processed.
>>>
>>> - A gpio hog node already existing in the live devicetree, but with a
>>>   non-active status will be properly processed if the status of the
>>>   gpio hog node is changed to "ok" in the overlay.

Verified by testing.


>>> - If a gpio hog node already exists in the live devicetree with an
>>>   active status, then any updated or added properties in that gpio
>>>   hog node in the overlay will have no effect.

Should be documented.


>>>   There is a scenario where the updated property would have an effect:
>>>   apply a second overlay that sets the status to inactive, then apply
>>>   a third overlay that sets the status back to active.  This is a
>>>   rather contrived example and I think it should be documented as
>>>   not supported and the result undefined.

I was wrong in this case.

of_reconfig_get_state_change() does not simply report whether a node
is added or removed, which confused me because it returns
OF_RECONFIG_CHANGE_ADD and OF_RECONFIG_CHANGE_REMOVE (as well as
no change), which I was incorrectly translating to node added or
node removed.   OF_RECONFIG_CHANGE_ADD and OF_RECONFIG_CHANGE_REMOVE
properly report a node becoming available or available due to changes
in the "status" property, as well as accounting for a node being
added or removed.

So the case that I was worried about is handled correctly.


>> I went back and double checked the related code.  For gpio hog nodes
>> that are in a non-overlay, the status property is checked because
>> of_gpiochip_scan_gpios() uses for_each_available_child_of_node()
>> to search for gpio hog nodes, and for_each_available_child_of_node()
>> checks the status property.  But in the case of a gpio hog node
>> added by an overlay, of_gpio_notify() does not check the status
>> property in the gpio hog node.  The check for the status property
>> should be added to of_gpio_notify().
> 
> Right.  of_device_is_available() should be called to check this.
> Note that of_i2c_notify() and of_spi_notify() also lack such a check.
> of_platform_notify() calls of_platform_device_create_pdata(), which does
> have the check.

And thus I was wrong about this also, so of_gpio_notify() does not need to
check the status property, since of_reconfig_get_state_change() already
implicitly incorporates this check.

> 
> Gr{oetje,eeting}s,
> 
>                         Geert
>
Frank Rowand Jan. 24, 2020, 10:02 p.m. UTC | #8
On 1/24/20 3:57 PM, Frank Rowand wrote:
> On 1/7/20 2:11 AM, Geert Uytterhoeven wrote:
>> Hi Frank,
>>
>> On Tue, Jan 7, 2020 at 8:10 AM Frank Rowand <frowand.list@gmail.com> wrote:
>>> On 1/6/20 5:34 PM, Frank Rowand wrote:
>>>> On 12/30/19 7:38 AM, Geert Uytterhoeven wrote:
>>>>> As GPIO hogs are configured at GPIO controller initialization time,
>>>>> adding/removing GPIO hogs in DT overlays does not work.
>>>>>
>>>>> Add support for GPIO hogs described in DT overlays by registering an OF
>>>>> reconfiguration notifier, to handle the addition and removal of GPIO hog
>>>>> subnodes to/from a GPIO controller device node.
>>>>>
>>>>> Note that when a GPIO hog device node is being removed, its "gpios"
>>>>> properties is no longer available, so we have to keep track of which
>>>>> node a hog belongs to, which is done by adding a pointer to the hog's
>>>>> device node to struct gpio_desc.
>>>>
>>>> If I have read the patches and the existing overlay source correctly,
>>>> then some observations:
>>>>
>>>> - A gpio hog node added in an overlay will be properly processed.
>>>>
>>>> - A gpio hog node already existing in the live devicetree, but with a
>>>>   non-active status will be properly processed if the status of the
>>>>   gpio hog node is changed to "ok" in the overlay.
> 
> Verified by testing.
> 
> 
>>>> - If a gpio hog node already exists in the live devicetree with an
>>>>   active status, then any updated or added properties in that gpio
>>>>   hog node in the overlay will have no effect.
> 
> Should be documented.
> 
> 
>>>>   There is a scenario where the updated property would have an effect:
>>>>   apply a second overlay that sets the status to inactive, then apply
>>>>   a third overlay that sets the status back to active.  This is a
>>>>   rather contrived example and I think it should be documented as
>>>>   not supported and the result undefined.
> 
> I was wrong in this case.
> 
> of_reconfig_get_state_change() does not simply report whether a node
> is added or removed, which confused me because it returns
> OF_RECONFIG_CHANGE_ADD and OF_RECONFIG_CHANGE_REMOVE (as well as
> no change), which I was incorrectly translating to node added or
> node removed.   OF_RECONFIG_CHANGE_ADD and OF_RECONFIG_CHANGE_REMOVE
> properly report a node becoming available or available due to changes
                                            ^^^^^^^^^^^^
                                            or unavailable

> in the "status" property, as well as accounting for a node being
> added or removed.
> 
> So the case that I was worried about is handled correctly.
> 
> 
>>> I went back and double checked the related code.  For gpio hog nodes
>>> that are in a non-overlay, the status property is checked because
>>> of_gpiochip_scan_gpios() uses for_each_available_child_of_node()
>>> to search for gpio hog nodes, and for_each_available_child_of_node()
>>> checks the status property.  But in the case of a gpio hog node
>>> added by an overlay, of_gpio_notify() does not check the status
>>> property in the gpio hog node.  The check for the status property
>>> should be added to of_gpio_notify().
>>
>> Right.  of_device_is_available() should be called to check this.
>> Note that of_i2c_notify() and of_spi_notify() also lack such a check.
>> of_platform_notify() calls of_platform_device_create_pdata(), which does
>> have the check.
> 
> And thus I was wrong about this also, so of_gpio_notify() does not need to
> check the status property, since of_reconfig_get_state_change() already
> implicitly incorporates this check.
> 
>>
>> Gr{oetje,eeting}s,
>>
>>                         Geert
>>
> 
>
diff mbox series

Patch

diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index dfae797846bb746b..89a6138ac0a4b506 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -649,6 +649,10 @@  static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
 		ret = gpiod_hog(desc, name, lflags, dflags);
 		if (ret < 0)
 			return ret;
+
+#ifdef CONFIG_OF_DYNAMIC
+		desc->hog = hog;
+#endif
 	}
 
 	return 0;
@@ -676,11 +680,91 @@  static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
 			of_node_put(np);
 			return ret;
 		}
+
+		of_node_set_flag(np, OF_POPULATED);
 	}
 
 	return 0;
 }
 
+#ifdef CONFIG_OF_DYNAMIC
+/**
+ * of_gpiochip_remove_hog - Remove all hogs in a hog device node
+ * @chip:	gpio chip to act on
+ * @hog:	device node describing the hogs
+ */
+static void of_gpiochip_remove_hog(struct gpio_chip *chip,
+				   struct device_node *hog)
+{
+	struct gpio_desc *descs = chip->gpiodev->descs;
+	unsigned int i;
+
+	for (i = 0; i < chip->ngpio; i++) {
+		if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) &&
+		    descs[i].hog == hog)
+			gpiochip_free_own_desc(&descs[i]);
+	}
+}
+
+static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
+{
+	return chip->gpiodev->dev.of_node == data;
+}
+
+static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
+{
+	return gpiochip_find(np, of_gpiochip_match_node);
+}
+
+static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
+			  void *arg)
+{
+	struct of_reconfig_data *rd = arg;
+	struct gpio_chip *chip;
+	int ret;
+
+	switch (of_reconfig_get_state_change(action, arg)) {
+	case OF_RECONFIG_CHANGE_ADD:
+		if (!of_property_read_bool(rd->dn, "gpio-hog"))
+			return NOTIFY_OK;	/* not for us */
+
+		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
+			return NOTIFY_OK;
+
+		chip = of_find_gpiochip_by_node(rd->dn->parent);
+		if (chip == NULL)
+			return NOTIFY_OK;	/* not for us */
+
+		ret = of_gpiochip_add_hog(chip, rd->dn);
+		if (ret < 0) {
+			pr_err("%s: failed to add hogs for %pOF\n", __func__,
+			       rd->dn);
+			of_node_clear_flag(rd->dn, OF_POPULATED);
+			return notifier_from_errno(ret);
+		}
+		break;
+
+	case OF_RECONFIG_CHANGE_REMOVE:
+		if (!of_node_check_flag(rd->dn, OF_POPULATED))
+			return NOTIFY_OK;	/* already depopulated */
+
+		chip = of_find_gpiochip_by_node(rd->dn->parent);
+		if (chip == NULL)
+			return NOTIFY_OK;	/* not for us */
+
+		of_gpiochip_remove_hog(chip, rd->dn);
+		of_node_clear_flag(rd->dn, OF_POPULATED);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+struct notifier_block gpio_of_notifier = {
+	.notifier_call = of_gpio_notify,
+};
+#endif /* CONFIG_OF_DYNAMIC */
+
 /**
  * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
  * @gc:		pointer to the gpio_chip structure
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
index 9768831b1fe2f25b..ed26664f153782fc 100644
--- a/drivers/gpio/gpiolib-of.h
+++ b/drivers/gpio/gpiolib-of.h
@@ -35,4 +35,6 @@  static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
 }
 #endif /* CONFIG_OF_GPIO */
 
+extern struct notifier_block gpio_of_notifier;
+
 #endif /* GPIOLIB_OF_H */
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index bff5ac774d870b67..ef12cfcaf0962c1c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2952,6 +2952,9 @@  static bool gpiod_free_commit(struct gpio_desc *desc)
 		clear_bit(FLAG_PULL_DOWN, &desc->flags);
 		clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
 		clear_bit(FLAG_IS_HOGGED, &desc->flags);
+#ifdef CONFIG_OF_DYNAMIC
+		desc->hog = NULL;
+#endif
 		ret = true;
 	}
 
@@ -5145,10 +5148,15 @@  static int __init gpiolib_dev_init(void)
 	if (ret < 0) {
 		pr_err("gpiolib: failed to allocate char dev region\n");
 		bus_unregister(&gpio_bus_type);
-	} else {
-		gpiolib_initialized = true;
-		gpiochip_setup_devs();
+		return ret;
 	}
+
+	gpiolib_initialized = true;
+	gpiochip_setup_devs();
+
+	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+		WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
+
 	return ret;
 }
 core_initcall(gpiolib_dev_init);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a4a759920faa48ab..7af9931e8572304a 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -118,6 +118,9 @@  struct gpio_desc {
 	const char		*label;
 	/* Name of the GPIO */
 	const char		*name;
+#ifdef CONFIG_OF_DYNAMIC
+	struct device_node	*hog;
+#endif
 };
 
 int gpiod_request(struct gpio_desc *desc, const char *label);