diff mbox series

[PATCH/RFC,v4,1/2] reset: Add support for dedicated reset controls

Message ID 20180917163955.19023-2-geert+renesas@glider.be (mailing list archive)
State New, archived
Headers show
Series vfio: platform: Add generic reset controller support | expand

Commit Message

Geert Uytterhoeven Sept. 17, 2018, 4:39 p.m. UTC
In some SoCs multiple hardware blocks may share a reset control.
The existing reset control API for shared resets will only assert such a
reset when the drivers for all hardware blocks agree.
The existing exclusive reset control API still allows to assert such a
reset, but that impacts all other hardware blocks sharing the reset.

Sometimes a driver needs to reset a specific hardware block, and be 100%
sure it has no impact on other hardware blocks.  This is e.g. the case
for virtualization with device pass-through, where the host wants to
reset any exported device before and after exporting it for use by the
guest, for isolation.

Hence a new flag for dedicated resets is added to the internal methods,
with a new public reset_control_get_dedicated() method, to obtain an
exclusive handle to a reset that is dedicated to one specific hardware
block.

This supports both DT-based and lookup-based reset controls.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
  - New.

Notes:
  - Dedicated lookup-based reset controls were not tested,
  - Several internal functions now take 3 boolean flags, and should
    probably be converted to take a bitmask instead,
  - I think __device_reset() should call __reset_control_get() with
    dedicated=true.  However, that will impact existing users,
  - Should a different error than -EINVAL be returned on failure?
---
 drivers/reset/core.c  | 76 ++++++++++++++++++++++++++++++++++++++-----
 include/linux/reset.h | 60 ++++++++++++++++++++++------------
 2 files changed, 107 insertions(+), 29 deletions(-)

Comments

Geert Uytterhoeven Sept. 18, 2018, 6:42 a.m. UTC | #1
On Mon, Sep 17, 2018 at 6:40 PM Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> In some SoCs multiple hardware blocks may share a reset control.
> The existing reset control API for shared resets will only assert such a
> reset when the drivers for all hardware blocks agree.
> The existing exclusive reset control API still allows to assert such a
> reset, but that impacts all other hardware blocks sharing the reset.
>
> Sometimes a driver needs to reset a specific hardware block, and be 100%
> sure it has no impact on other hardware blocks.  This is e.g. the case
> for virtualization with device pass-through, where the host wants to
> reset any exported device before and after exporting it for use by the
> guest, for isolation.
>
> Hence a new flag for dedicated resets is added to the internal methods,
> with a new public reset_control_get_dedicated() method, to obtain an
> exclusive handle to a reset that is dedicated to one specific hardware
> block.
>
> This supports both DT-based and lookup-based reset controls.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
> v4:
>   - New.
>
> Notes:
>   - Dedicated lookup-based reset controls were not tested,

And untested code is buggy...

> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c

> @@ -541,9 +575,25 @@ __reset_controller_by_name(const char *name)
>         return NULL;
>  }
>
> +static bool __reset_is_dedicated(const struct reset_control_lookup *lookup)
> +{
> +       const struct reset_control_lookup *lookup2;
> +
> +       list_for_each_entry(lookup, &reset_lookup_list, list) {

... as the list_for_each() should use "lookup2" instead of "lookup" (warning
reported by kbuild robot).

> +               if (lookup2 == lookup)
> +                       continue;
> +
> +               if (lookup2->provider == lookup->provider &&
> +                   lookup2->index == lookup->index)
> +                       return false;
> +       }
> +
> +       return true;
> +}

Gr{oetje,eeting}s,

                        Geert
Eric Auger Sept. 19, 2018, 12:09 p.m. UTC | #2
Hi Geert,

On 9/17/18 6:39 PM, Geert Uytterhoeven wrote:
> In some SoCs multiple hardware blocks may share a reset control.
> The existing reset control API for shared resets will only assert such a
> reset when the drivers for all hardware blocks agree.
> The existing exclusive reset control API still allows to assert such a
> reset, but that impacts all other hardware blocks sharing the reset.
> 
> Sometimes a driver needs to reset a specific hardware block, and be 100%
> sure it has no impact on other hardware blocks.  This is e.g. the case
> for virtualization with device pass-through, where the host wants to
> reset any exported device before and after exporting it for use by the
> guest, for isolation.
> 
> Hence a new flag for dedicated resets is added to the internal methods,
> with a new public reset_control_get_dedicated() method, to obtain an
> exclusive handle to a reset that is dedicated to one specific hardware
> block.
> 
> This supports both DT-based and lookup-based reset controls.
> 
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
> v4:
>   - New.
> 
> Notes:
>   - Dedicated lookup-based reset controls were not tested,
>   - Several internal functions now take 3 boolean flags, and should
>     probably be converted to take a bitmask instead,
>   - I think __device_reset() should call __reset_control_get() with
>     dedicated=true.  However, that will impact existing users,
why should it?
>   - Should a different error than -EINVAL be returned on failure?
> ---
>  drivers/reset/core.c  | 76 ++++++++++++++++++++++++++++++++++++++-----
>  include/linux/reset.h | 60 ++++++++++++++++++++++------------
>  2 files changed, 107 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index 225e34c56b94a2e3..5bc4eeca70c0fcc2 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc)
>  	kref_put(&rstc->refcnt, __reset_control_release);
>  }
>  
> +static bool __of_reset_is_dedicated(const struct device_node *node,
> +				    const struct of_phandle_args args)
> +{
> +	struct of_phandle_args args2;
> +	struct device_node *node2;
> +	int index, ret;
> +
> +	for_each_node_with_property(node2, "resets") {
> +		if (node == node2)
> +			continue;
> +
> +		for (index = 0; ; index++) {
> +			ret = of_parse_phandle_with_args(node2, "resets",
> +							 "#reset-cells", index,
> +							 &args2);
> +			if (ret)
> +				break;
> +
> +			if (args2.np == args.np &&
> +			    args2.args_count == args.args_count &&
> +			    !memcmp(args2.args, args.args,
> +				    args.args_count * sizeof(args.args[0])))
> +				return false;
You need to call of_node_put(args2.np) (see of_parse_phandle_with_args
kernel doc)

Isn't it sufficient to check device_node handles are equal?

Thanks

Eric

> +		}
> +	}
> +
> +	return true;
> +}
> +
>  struct reset_control *__of_reset_control_get(struct device_node *node,
>  				     const char *id, int index, bool shared,
> -				     bool optional)
> +				     bool optional, bool dedicated)
>  {
>  	struct reset_control *rstc;
>  	struct reset_controller_dev *r, *rcdev;
> @@ -514,6 +543,11 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
>  		return ERR_PTR(rstc_id);
>  	}
>  
> +	if (dedicated && !__of_reset_is_dedicated(node, args)) {
> +		mutex_unlock(&reset_list_mutex);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
>  	/* reset_list_mutex also protects the rcdev's reset_control list */
>  	rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
>  
> @@ -541,9 +575,25 @@ __reset_controller_by_name(const char *name)
>  	return NULL;
>  }
>  
> +static bool __reset_is_dedicated(const struct reset_control_lookup *lookup)
> +{
> +	const struct reset_control_lookup *lookup2;
> +
> +	list_for_each_entry(lookup, &reset_lookup_list, list) {
> +		if (lookup2 == lookup)
> +			continue;
> +
> +		if (lookup2->provider == lookup->provider &&
> +		    lookup2->index == lookup->index)
> +			return false;
> +	}
> +
> +	return true;
> +}
> +
>  static struct reset_control *
>  __reset_control_get_from_lookup(struct device *dev, const char *con_id,
> -				bool shared, bool optional)
> +				bool shared, bool optional, bool dedicated)
>  {
>  	const struct reset_control_lookup *lookup;
>  	struct reset_controller_dev *rcdev;
> @@ -562,6 +612,11 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
>  		if ((!con_id && !lookup->con_id) ||
>  		    ((con_id && lookup->con_id) &&
>  		     !strcmp(con_id, lookup->con_id))) {
> +			if (dedicated && !__reset_is_dedicated(lookup)) {
> +				mutex_unlock(&reset_lookup_mutex);
> +				return ERR_PTR(-EINVAL);
> +			}
> +
>  			mutex_lock(&reset_list_mutex);
>  			rcdev = __reset_controller_by_name(lookup->provider);
>  			if (!rcdev) {
> @@ -588,13 +643,15 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
>  }
>  
>  struct reset_control *__reset_control_get(struct device *dev, const char *id,
> -					  int index, bool shared, bool optional)
> +					  int index, bool shared,
> +					  bool optional, bool dedicated)
>  {
>  	if (dev->of_node)
>  		return __of_reset_control_get(dev->of_node, id, index, shared,
> -					      optional);
> +					      optional, dedicated);
>  
> -	return __reset_control_get_from_lookup(dev, id, shared, optional);
> +	return __reset_control_get_from_lookup(dev, id, shared, optional,
> +					       dedicated);
>  }
>  EXPORT_SYMBOL_GPL(__reset_control_get);
>  
> @@ -635,7 +692,7 @@ static void devm_reset_control_release(struct device *dev, void *res)
>  
>  struct reset_control *__devm_reset_control_get(struct device *dev,
>  				     const char *id, int index, bool shared,
> -				     bool optional)
> +				     bool optional, bool dedicated)
>  {
>  	struct reset_control **ptr, *rstc;
>  
> @@ -644,7 +701,7 @@ struct reset_control *__devm_reset_control_get(struct device *dev,
>  	if (!ptr)
>  		return ERR_PTR(-ENOMEM);
>  
> -	rstc = __reset_control_get(dev, id, index, shared, optional);
> +	rstc = __reset_control_get(dev, id, index, shared, optional, dedicated);
>  	if (!IS_ERR(rstc)) {
>  		*ptr = rstc;
>  		devres_add(dev, ptr);
> @@ -671,7 +728,7 @@ int __device_reset(struct device *dev, bool optional)
>  	struct reset_control *rstc;
>  	int ret;
>  
> -	rstc = __reset_control_get(dev, NULL, 0, 0, optional);
> +	rstc = __reset_control_get(dev, NULL, 0, false, optional, false);
>  	if (IS_ERR(rstc))
>  		return PTR_ERR(rstc);
>  
> @@ -735,7 +792,8 @@ of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
>  		return ERR_PTR(-ENOMEM);
>  
>  	for (i = 0; i < num; i++) {
> -		rstc = __of_reset_control_get(np, NULL, i, shared, optional);
> +		rstc = __of_reset_control_get(np, NULL, i, shared, optional,
> +					      false);
>  		if (IS_ERR(rstc))
>  			goto err_rst;
>  		resets->rstc[i] = rstc;
> diff --git a/include/linux/reset.h b/include/linux/reset.h
> index 09732c36f3515a1e..6ca6e108b612f923 100644
> --- a/include/linux/reset.h
> +++ b/include/linux/reset.h
> @@ -17,15 +17,15 @@ int reset_control_status(struct reset_control *rstc);
>  
>  struct reset_control *__of_reset_control_get(struct device_node *node,
>  				     const char *id, int index, bool shared,
> -				     bool optional);
> +				     bool optional, bool dedicated);
>  struct reset_control *__reset_control_get(struct device *dev, const char *id,
>  					  int index, bool shared,
> -					  bool optional);
> +					  bool optional, bool dedicated);
>  void reset_control_put(struct reset_control *rstc);
>  int __device_reset(struct device *dev, bool optional);
>  struct reset_control *__devm_reset_control_get(struct device *dev,
>  				     const char *id, int index, bool shared,
> -				     bool optional);
> +				     bool optional, bool dedicated);
>  
>  struct reset_control *devm_reset_control_array_get(struct device *dev,
>  						   bool shared, bool optional);
> @@ -66,21 +66,23 @@ static inline int __device_reset(struct device *dev, bool optional)
>  static inline struct reset_control *__of_reset_control_get(
>  					struct device_node *node,
>  					const char *id, int index, bool shared,
> -					bool optional)
> +					bool optional, bool dedicated)
>  {
>  	return optional ? NULL : ERR_PTR(-ENOTSUPP);
>  }
>  
>  static inline struct reset_control *__reset_control_get(
>  					struct device *dev, const char *id,
> -					int index, bool shared, bool optional)
> +					int index, bool shared, bool optional,
> +					bool dedicated)
>  {
>  	return optional ? NULL : ERR_PTR(-ENOTSUPP);
>  }
>  
>  static inline struct reset_control *__devm_reset_control_get(
>  					struct device *dev, const char *id,
> -					int index, bool shared, bool optional)
> +					int index, bool shared, bool optional,
> +					bool dedicated)
>  {
>  	return optional ? NULL : ERR_PTR(-ENOTSUPP);
>  }
> @@ -127,7 +129,25 @@ static inline int device_reset_optional(struct device *dev)
>  static inline struct reset_control *
>  __must_check reset_control_get_exclusive(struct device *dev, const char *id)
>  {
> -	return __reset_control_get(dev, id, 0, false, false);
> +	return __reset_control_get(dev, id, 0, false, false, false);
> +}
> +
> +/**
> + * reset_control_get_dedicated - Lookup and obtain an exclusive reference
> + *                               to a dedicated reset controller.
> + * @dev: device to be reset by the controller
> + * @id: reset line name
> + *
> + * Returns a struct reset_control or IS_ERR() condition containing errno.
> + * If this function is called more than once for the same reset_control it will
> + * return -EBUSY.
> + *
> + * Use of id names is optional.
> + */
> +static inline struct reset_control *
> +__must_check reset_control_get_dedicated(struct device *dev, const char *id)
> +{
> +	return __reset_control_get(dev, id, 0, false, false, true);
>  }
>  
>  /**
> @@ -155,19 +175,19 @@ __must_check reset_control_get_exclusive(struct device *dev, const char *id)
>  static inline struct reset_control *reset_control_get_shared(
>  					struct device *dev, const char *id)
>  {
> -	return __reset_control_get(dev, id, 0, true, false);
> +	return __reset_control_get(dev, id, 0, true, false, false);
>  }
>  
>  static inline struct reset_control *reset_control_get_optional_exclusive(
>  					struct device *dev, const char *id)
>  {
> -	return __reset_control_get(dev, id, 0, false, true);
> +	return __reset_control_get(dev, id, 0, false, true, false);
>  }
>  
>  static inline struct reset_control *reset_control_get_optional_shared(
>  					struct device *dev, const char *id)
>  {
> -	return __reset_control_get(dev, id, 0, true, true);
> +	return __reset_control_get(dev, id, 0, true, true, false);
>  }
>  
>  /**
> @@ -183,7 +203,7 @@ static inline struct reset_control *reset_control_get_optional_shared(
>  static inline struct reset_control *of_reset_control_get_exclusive(
>  				struct device_node *node, const char *id)
>  {
> -	return __of_reset_control_get(node, id, 0, false, false);
> +	return __of_reset_control_get(node, id, 0, false, false, false);
>  }
>  
>  /**
> @@ -208,7 +228,7 @@ static inline struct reset_control *of_reset_control_get_exclusive(
>  static inline struct reset_control *of_reset_control_get_shared(
>  				struct device_node *node, const char *id)
>  {
> -	return __of_reset_control_get(node, id, 0, true, false);
> +	return __of_reset_control_get(node, id, 0, true, false, false);
>  }
>  
>  /**
> @@ -225,7 +245,7 @@ static inline struct reset_control *of_reset_control_get_shared(
>  static inline struct reset_control *of_reset_control_get_exclusive_by_index(
>  					struct device_node *node, int index)
>  {
> -	return __of_reset_control_get(node, NULL, index, false, false);
> +	return __of_reset_control_get(node, NULL, index, false, false, false);
>  }
>  
>  /**
> @@ -253,7 +273,7 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index(
>  static inline struct reset_control *of_reset_control_get_shared_by_index(
>  					struct device_node *node, int index)
>  {
> -	return __of_reset_control_get(node, NULL, index, true, false);
> +	return __of_reset_control_get(node, NULL, index, true, false, false);
>  }
>  
>  /**
> @@ -272,7 +292,7 @@ static inline struct reset_control *
>  __must_check devm_reset_control_get_exclusive(struct device *dev,
>  					      const char *id)
>  {
> -	return __devm_reset_control_get(dev, id, 0, false, false);
> +	return __devm_reset_control_get(dev, id, 0, false, false, false);
>  }
>  
>  /**
> @@ -287,19 +307,19 @@ __must_check devm_reset_control_get_exclusive(struct device *dev,
>  static inline struct reset_control *devm_reset_control_get_shared(
>  					struct device *dev, const char *id)
>  {
> -	return __devm_reset_control_get(dev, id, 0, true, false);
> +	return __devm_reset_control_get(dev, id, 0, true, false, false);
>  }
>  
>  static inline struct reset_control *devm_reset_control_get_optional_exclusive(
>  					struct device *dev, const char *id)
>  {
> -	return __devm_reset_control_get(dev, id, 0, false, true);
> +	return __devm_reset_control_get(dev, id, 0, false, true, false);
>  }
>  
>  static inline struct reset_control *devm_reset_control_get_optional_shared(
>  					struct device *dev, const char *id)
>  {
> -	return __devm_reset_control_get(dev, id, 0, true, true);
> +	return __devm_reset_control_get(dev, id, 0, true, true, false);
>  }
>  
>  /**
> @@ -317,7 +337,7 @@ static inline struct reset_control *devm_reset_control_get_optional_shared(
>  static inline struct reset_control *
>  devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
>  {
> -	return __devm_reset_control_get(dev, NULL, index, false, false);
> +	return __devm_reset_control_get(dev, NULL, index, false, false, false);
>  }
>  
>  /**
> @@ -333,7 +353,7 @@ devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
>  static inline struct reset_control *
>  devm_reset_control_get_shared_by_index(struct device *dev, int index)
>  {
> -	return __devm_reset_control_get(dev, NULL, index, true, false);
> +	return __devm_reset_control_get(dev, NULL, index, true, false, false);
>  }
>  
>  /*
>
Geert Uytterhoeven Sept. 19, 2018, 1:16 p.m. UTC | #3
Hi Eric,

On Wed, Sep 19, 2018 at 2:09 PM Auger Eric <eric.auger@redhat.com> wrote:
> On 9/17/18 6:39 PM, Geert Uytterhoeven wrote:
> > In some SoCs multiple hardware blocks may share a reset control.
> > The existing reset control API for shared resets will only assert such a
> > reset when the drivers for all hardware blocks agree.
> > The existing exclusive reset control API still allows to assert such a
> > reset, but that impacts all other hardware blocks sharing the reset.
> >
> > Sometimes a driver needs to reset a specific hardware block, and be 100%
> > sure it has no impact on other hardware blocks.  This is e.g. the case
> > for virtualization with device pass-through, where the host wants to
> > reset any exported device before and after exporting it for use by the
> > guest, for isolation.
> >
> > Hence a new flag for dedicated resets is added to the internal methods,
> > with a new public reset_control_get_dedicated() method, to obtain an
> > exclusive handle to a reset that is dedicated to one specific hardware
> > block.
> >
> > This supports both DT-based and lookup-based reset controls.
> >
> > Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > ---
> > v4:
> >   - New.
> >
> > Notes:
> >   - Dedicated lookup-based reset controls were not tested,
> >   - Several internal functions now take 3 boolean flags, and should
> >     probably be converted to take a bitmask instead,
> >   - I think __device_reset() should call __reset_control_get() with
> >     dedicated=true.  However, that will impact existing users,
>
> why should it?

device_reset{,_optional}() are supposed to reset the passed device.
If the reset is not dedicated, doing so will reset other devices, too.

> > --- a/drivers/reset/core.c
> > +++ b/drivers/reset/core.c
> > @@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc)
> >       kref_put(&rstc->refcnt, __reset_control_release);
> >  }
> >
> > +static bool __of_reset_is_dedicated(const struct device_node *node,
> > +                                 const struct of_phandle_args args)
> > +{
> > +     struct of_phandle_args args2;
> > +     struct device_node *node2;
> > +     int index, ret;
> > +
> > +     for_each_node_with_property(node2, "resets") {
> > +             if (node == node2)
> > +                     continue;
> > +
> > +             for (index = 0; ; index++) {
> > +                     ret = of_parse_phandle_with_args(node2, "resets",
> > +                                                      "#reset-cells", index,
> > +                                                      &args2);
> > +                     if (ret)
> > +                             break;
> > +
> > +                     if (args2.np == args.np &&
> > +                         args2.args_count == args.args_count &&
> > +                         !memcmp(args2.args, args.args,
> > +                                 args.args_count * sizeof(args.args[0])))
> > +                             return false;
> You need to call of_node_put(args2.np) (see of_parse_phandle_with_args
> kernel doc)

Thanks, nice catch!

> Isn't it sufficient to check device_node handles are equal?

That would make it work with #reset-cells == 0 only.
If #reset-cells > 0, the reset line specifier includes extra arguments.

On the Renesas SoCs I'm using, there's a single reset controller, so
args.np is always the same.  The actual reset line is specified by
args.args[0].  See the "resets" properties in e.g.
https://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git/tree/arch/arm64/boot/dts/renesas/r8a7795.dtsi

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Philipp Zabel Sept. 19, 2018, 2:58 p.m. UTC | #4
On Mon, 2018-09-17 at 18:39 +0200, Geert Uytterhoeven wrote:
> In some SoCs multiple hardware blocks may share a reset control.
> The existing reset control API for shared resets will only assert such a
> reset when the drivers for all hardware blocks agree.
> The existing exclusive reset control API still allows to assert such a
> reset, but that impacts all other hardware blocks sharing the reset.

I consider requesting exclusive access to a shared reset line a misuse
of the API. Are there such cases? Can they be fixed?

> Sometimes a driver needs to reset a specific hardware block, and be 100%
> sure it has no impact on other hardware blocks.  This is e.g. the case
> for virtualization with device pass-through, where the host wants to
> reset any exported device before and after exporting it for use by the
> guest, for isolation.
> 
> Hence a new flag for dedicated resets is added to the internal methods,
> with a new public reset_control_get_dedicated() method, to obtain an
> exclusive handle to a reset that is dedicated to one specific hardware
> block.

I'm not sure a new flag is necessary, this is what exclusive resets
should be.
Also I fear there will be confusion about the difference between
exclusive (refering to the reset control) and dedicated (refering to
the reset line) reset controls.

> This supports both DT-based and lookup-based reset controls.
> 
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
> v4:
>   - New.
> 
> Notes:
>   - Dedicated lookup-based reset controls were not tested,
>   - Several internal functions now take 3 boolean flags, and should
>     probably be converted to take a bitmask instead,

In case we have to add more flags, yes.

>   - I think __device_reset() should call __reset_control_get() with
>     dedicated=true.  However, that will impact existing users,

Which ones? And how?

>   - Should a different error than -EINVAL be returned on failure?
> ---
>  drivers/reset/core.c  | 76 ++++++++++++++++++++++++++++++++++++++-----
>  include/linux/reset.h | 60 ++++++++++++++++++++++------------
>  2 files changed, 107 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index 225e34c56b94a2e3..5bc4eeca70c0fcc2 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc)
>  	kref_put(&rstc->refcnt, __reset_control_release);
>  }
>  
> +static bool __of_reset_is_dedicated(const struct device_node *node,
> +				    const struct of_phandle_args args)
> +{
> +	struct of_phandle_args args2;
> +	struct device_node *node2;
> +	int index, ret;
> +
> +	for_each_node_with_property(node2, "resets") {
> +		if (node == node2)
> +			continue;
> +
> +		for (index = 0; ; index++) {
> +			ret = of_parse_phandle_with_args(node2, "resets",
> +							 "#reset-cells", index,
> +							 &args2);
> +			if (ret)
> +				break;
> +
> +			if (args2.np == args.np &&
> +			    args2.args_count == args.args_count &&
> +			    !memcmp(args2.args, args.args,
> +				    args.args_count * sizeof(args.args[0])))
> +				return false;
> +		}
> +	}
> +
> +	return true;
> +}

I want to hear the device tree maintainers' opinion about this.
I'd very much like to have such a check for exclusive resets, but my
understanding is that we are not allowed to make the assumption that
other nodes' "reset" properties follow the common reset signal device
tree bindings.

Maybe this is something that should be checked in a device tree
validation step?

regards
Philipp
Geert Uytterhoeven Sept. 19, 2018, 3:24 p.m. UTC | #5
Hi Philipp,

CC Mark & Rob (DT)

On Wed, Sep 19, 2018 at 4:58 PM Philipp Zabel <p.zabel@pengutronix.de> wrote:
> On Mon, 2018-09-17 at 18:39 +0200, Geert Uytterhoeven wrote:
> > In some SoCs multiple hardware blocks may share a reset control.
> > The existing reset control API for shared resets will only assert such a
> > reset when the drivers for all hardware blocks agree.
> > The existing exclusive reset control API still allows to assert such a
> > reset, but that impacts all other hardware blocks sharing the reset.
>
> I consider requesting exclusive access to a shared reset line a misuse
> of the API. Are there such cases? Can they be fixed?

I guess there are plenty.  Whether a line is shared or dedicated depends
on SoC integration.

The issue is that a driver requesting exclusive access has no way to know
if the reset line is dedicated to its device or not.  If no other
driver requested
the reset control (most drivers don't use reset controls), it will succeed.

> > Sometimes a driver needs to reset a specific hardware block, and be 100%
> > sure it has no impact on other hardware blocks.  This is e.g. the case
> > for virtualization with device pass-through, where the host wants to
> > reset any exported device before and after exporting it for use by the
> > guest, for isolation.
> >
> > Hence a new flag for dedicated resets is added to the internal methods,
> > with a new public reset_control_get_dedicated() method, to obtain an
> > exclusive handle to a reset that is dedicated to one specific hardware
> > block.
>
> I'm not sure a new flag is necessary, this is what exclusive resets
> should be.

So perhaps the check should be done for the existing exclusive resets
instead, without adding a new flag?

> Also I fear there will be confusion about the difference between
> exclusive (refering to the reset control) and dedicated (refering to
> the reset line) reset controls.

Indeed, exclusive has multiple meanings here:
  1. Exclusive vs. shared access to the reset control,
  2. Reset line is dedicated to a single device, or shared with multiple
     devices.

If we can simplify (exclusive == dedicated, 1.shared == 2.shared), life
can become simpler.

> >   - I think __device_reset() should call __reset_control_get() with
> >     dedicated=true.  However, that will impact existing users,
>
> Which ones? And how?

I didn't actually check which drivers.
If a reset is not dedicated, device_reset{,_optional}() will suddenly
start to fail if
a reset turns out to be not dedicated.
Well, currently the device will be reset multiple times, so people would
already have noticed...

> >   - Should a different error than -EINVAL be returned on failure?
> > ---
> >  drivers/reset/core.c  | 76 ++++++++++++++++++++++++++++++++++++++-----
> >  include/linux/reset.h | 60 ++++++++++++++++++++++------------
> >  2 files changed, 107 insertions(+), 29 deletions(-)
> >
> > diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> > index 225e34c56b94a2e3..5bc4eeca70c0fcc2 100644
> > --- a/drivers/reset/core.c
> > +++ b/drivers/reset/core.c
> > @@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc)
> >       kref_put(&rstc->refcnt, __reset_control_release);
> >  }
> >
> > +static bool __of_reset_is_dedicated(const struct device_node *node,
> > +                                 const struct of_phandle_args args)
> > +{
> > +     struct of_phandle_args args2;
> > +     struct device_node *node2;
> > +     int index, ret;
> > +
> > +     for_each_node_with_property(node2, "resets") {
> > +             if (node == node2)
> > +                     continue;
> > +
> > +             for (index = 0; ; index++) {
> > +                     ret = of_parse_phandle_with_args(node2, "resets",
> > +                                                      "#reset-cells", index,
> > +                                                      &args2);
> > +                     if (ret)
> > +                             break;
> > +
> > +                     if (args2.np == args.np &&
> > +                         args2.args_count == args.args_count &&
> > +                         !memcmp(args2.args, args.args,
> > +                                 args.args_count * sizeof(args.args[0])))
> > +                             return false;
> > +             }
> > +     }
> > +
> > +     return true;
> > +}
>
> I want to hear the device tree maintainers' opinion about this.
> I'd very much like to have such a check for exclusive resets, but my
> understanding is that we are not allowed to make the assumption that
> other nodes' "reset" properties follow the common reset signal device
> tree bindings.
>
> Maybe this is something that should be checked in a device tree
> validation step?

We already have SoCs where reset lines are shared among multiple on-chip
devices. So dtc validation won't work, and a runtime check is needed.

Gr{oetje,eeting}s,

                        Geert
Eric Auger Sept. 19, 2018, 3:28 p.m. UTC | #6
Hi Geert,

On 9/19/18 3:16 PM, Geert Uytterhoeven wrote:
> Hi Eric,
> 
> On Wed, Sep 19, 2018 at 2:09 PM Auger Eric <eric.auger@redhat.com> wrote:
>> On 9/17/18 6:39 PM, Geert Uytterhoeven wrote:
>>> In some SoCs multiple hardware blocks may share a reset control.
>>> The existing reset control API for shared resets will only assert such a
>>> reset when the drivers for all hardware blocks agree.
>>> The existing exclusive reset control API still allows to assert such a
>>> reset, but that impacts all other hardware blocks sharing the reset.
>>>
>>> Sometimes a driver needs to reset a specific hardware block, and be 100%
>>> sure it has no impact on other hardware blocks.  This is e.g. the case
>>> for virtualization with device pass-through, where the host wants to
>>> reset any exported device before and after exporting it for use by the
>>> guest, for isolation.
>>>
>>> Hence a new flag for dedicated resets is added to the internal methods,
>>> with a new public reset_control_get_dedicated() method, to obtain an
>>> exclusive handle to a reset that is dedicated to one specific hardware
>>> block.
>>>
>>> This supports both DT-based and lookup-based reset controls.
>>>
>>> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
>>> ---
>>> v4:
>>>   - New.
>>>
>>> Notes:
>>>   - Dedicated lookup-based reset controls were not tested,
>>>   - Several internal functions now take 3 boolean flags, and should
>>>     probably be converted to take a bitmask instead,
>>>   - I think __device_reset() should call __reset_control_get() with
>>>     dedicated=true.  However, that will impact existing users,
>>
>> why should it?
> 
> device_reset{,_optional}() are supposed to reset the passed device.
> If the reset is not dedicated, doing so will reset other devices, too.
ok, that's not obvious too me but I am not familiar enough with the API
and existing callers.
> 
>>> --- a/drivers/reset/core.c
>>> +++ b/drivers/reset/core.c
>>> @@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc)
>>>       kref_put(&rstc->refcnt, __reset_control_release);
>>>  }
>>>
>>> +static bool __of_reset_is_dedicated(const struct device_node *node,
>>> +                                 const struct of_phandle_args args)
>>> +{
>>> +     struct of_phandle_args args2;
>>> +     struct device_node *node2;
>>> +     int index, ret;
>>> +
>>> +     for_each_node_with_property(node2, "resets") {
>>> +             if (node == node2)
>>> +                     continue;
>>> +
>>> +             for (index = 0; ; index++) {
>>> +                     ret = of_parse_phandle_with_args(node2, "resets",
>>> +                                                      "#reset-cells", index,
>>> +                                                      &args2);
>>> +                     if (ret)
>>> +                             break;
>>> +
>>> +                     if (args2.np == args.np &&
>>> +                         args2.args_count == args.args_count &&
>>> +                         !memcmp(args2.args, args.args,
>>> +                                 args.args_count * sizeof(args.args[0])))
>>> +                             return false;
>> You need to call of_node_put(args2.np) (see of_parse_phandle_with_args
>> kernel doc)
> 
> Thanks, nice catch!
> 
>> Isn't it sufficient to check device_node handles are equal?
> 
> That would make it work with #reset-cells == 0 only.
> If #reset-cells > 0, the reset line specifier includes extra arguments.
> 
> On the Renesas SoCs I'm using, there's a single reset controller, so
> args.np is always the same.  The actual reset line is specified by
> args.args[0].  See the "resets" properties in e.g.
> https://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git/tree/arch/arm64/boot/dts/renesas/r8a7795.dtsi
OK get it now. Thank you for the explanations.

Best Regards

Eric
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds
>
Philipp Zabel Sept. 20, 2018, 9:27 a.m. UTC | #7
Hi Geert,

On Wed, 2018-09-19 at 17:24 +0200, Geert Uytterhoeven wrote:
> On Wed, Sep 19, 2018 at 4:58 PM Philipp Zabel <p.zabel@pengutronix.de> wrote:
[...]
> > I consider requesting exclusive access to a shared reset line a misuse
> > of the API. Are there such cases? Can they be fixed?
> 
> I guess there are plenty.

I did a cursory search for drivers that request exclusive reset controls
for resets that have multiple phandle references in the corresponding
DT. So far I have found none.

> Whether a line is shared or dedicated depends on SoC integration.
>
> The issue is that a driver requesting exclusive access has no way to know
> if the reset line is dedicated to its device or not. If no other
> driver requested the reset control (most drivers don't use reset
> controls), it will succeed.

True. It would be great to have a way to make sure an exclusive request
for a shared reset line never succeeds.

> > > Sometimes a driver needs to reset a specific hardware block, and be 100%
> > > sure it has no impact on other hardware blocks.  This is e.g. the case
> > > for virtualization with device pass-through, where the host wants to
> > > reset any exported device before and after exporting it for use by the
> > > guest, for isolation.
> > > 
> > > Hence a new flag for dedicated resets is added to the internal methods,
> > > with a new public reset_control_get_dedicated() method, to obtain an
> > > exclusive handle to a reset that is dedicated to one specific hardware
> > > block.
> > 
> > I'm not sure a new flag is necessary, this is what exclusive resets
> > should be.
> 
> So perhaps the check should be done for the existing exclusive resets
> instead, without adding a new flag?

That would be my preference, if possible.

> > Also I fear there will be confusion about the difference between
> > exclusive (refering to the reset control) and dedicated (refering to
> > the reset line) reset controls.
> 
> Indeed, exclusive has multiple meanings here:
>   1. Exclusive vs. shared access to the reset control,
>   2. Reset line is dedicated to a single device, or shared with multiple
>      devices.

2. is the more important factor, and that's what I have in mind when
talking about exclusive vs. shared resets. Admittedly, the kernel doc
comments only mention 1.

> If we can simplify (exclusive == dedicated, 1.shared == 2.shared), life
> can become simpler.

Well, it still has to be possible for drivers to request 1.shared
control over a dedicated reset line, just because the same driver may
work on multiple SoCs, only some of which have that reset line 2.shared.
But if we could make sure that exclusive requests are only possible for
dedicated reset lines, I'd be happier.

> > >   - I think __device_reset() should call __reset_control_get() with
> > >     dedicated=true.  However, that will impact existing users,
> > 
> > Which ones? And how?
> 
> I didn't actually check which drivers.
> If a reset is not dedicated, device_reset{,_optional}() will suddenly
> start to fail if
> a reset turns out to be not dedicated.
> Well, currently the device will be reset multiple times, so people would
> already have noticed...

Exactly. Naive exclusive control, as currently implemented, is bound to
fail if the reset line is shared. I am not aware of any cases where this
currently happens.
Of course there could always be those fragile cases where something just
works by accident and lucky timing.

[...]
> > I want to hear the device tree maintainers' opinion about this.
> > I'd very much like to have such a check for exclusive resets, but my
> > understanding is that we are not allowed to make the assumption that
> > other nodes' "reset" properties follow the common reset signal device
> > tree bindings.
> > 
> > Maybe this is something that should be checked in a device tree
> > validation step?
> 
> We already have SoCs where reset lines are shared among multiple on-chip
> devices. So dtc validation won't work, and a runtime check is needed.

Right, the dtb would have to be validated against driver code to get the
information whether a given phandle might be requested exclusively at
some point.

It would be better if this could be checked at runtime.

regards
Philipp
Geert Uytterhoeven Sept. 20, 2018, 9:37 a.m. UTC | #8
Hi Philip,

On Thu, Sep 20, 2018 at 11:27 AM Philipp Zabel <p.zabel@pengutronix.de> wrote:
> On Wed, 2018-09-19 at 17:24 +0200, Geert Uytterhoeven wrote:
> > On Wed, Sep 19, 2018 at 4:58 PM Philipp Zabel <p.zabel@pengutronix.de> wrote:
> [...]
> > > I consider requesting exclusive access to a shared reset line a misuse
> > > of the API. Are there such cases? Can they be fixed?
> >
> > I guess there are plenty.
>
> I did a cursory search for drivers that request exclusive reset controls
> for resets that have multiple phandle references in the corresponding
> DT. So far I have found none.

OK.

> > Whether a line is shared or dedicated depends on SoC integration.
> >
> > The issue is that a driver requesting exclusive access has no way to know
> > if the reset line is dedicated to its device or not. If no other
> > driver requested the reset control (most drivers don't use reset
> > controls), it will succeed.
>
> True. It would be great to have a way to make sure an exclusive request
> for a shared reset line never succeeds.
>
> > > > Sometimes a driver needs to reset a specific hardware block, and be 100%
> > > > sure it has no impact on other hardware blocks.  This is e.g. the case
> > > > for virtualization with device pass-through, where the host wants to
> > > > reset any exported device before and after exporting it for use by the
> > > > guest, for isolation.
> > > >
> > > > Hence a new flag for dedicated resets is added to the internal methods,
> > > > with a new public reset_control_get_dedicated() method, to obtain an
> > > > exclusive handle to a reset that is dedicated to one specific hardware
> > > > block.
> > >
> > > I'm not sure a new flag is necessary, this is what exclusive resets
> > > should be.
> >
> > So perhaps the check should be done for the existing exclusive resets
> > instead, without adding a new flag?
>
> That would be my preference, if possible.

OK, will make it so.

> > > Also I fear there will be confusion about the difference between
> > > exclusive (refering to the reset control) and dedicated (refering to
> > > the reset line) reset controls.
> >
> > Indeed, exclusive has multiple meanings here:
> >   1. Exclusive vs. shared access to the reset control,
> >   2. Reset line is dedicated to a single device, or shared with multiple
> >      devices.
>
> 2. is the more important factor, and that's what I have in mind when
> talking about exclusive vs. shared resets. Admittedly, the kernel doc
> comments only mention 1.

OK. That did contribute to my confustion...

Gr{oetje,eeting}s,

                        Geert
diff mbox series

Patch

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 225e34c56b94a2e3..5bc4eeca70c0fcc2 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -459,9 +459,38 @@  static void __reset_control_put_internal(struct reset_control *rstc)
 	kref_put(&rstc->refcnt, __reset_control_release);
 }
 
+static bool __of_reset_is_dedicated(const struct device_node *node,
+				    const struct of_phandle_args args)
+{
+	struct of_phandle_args args2;
+	struct device_node *node2;
+	int index, ret;
+
+	for_each_node_with_property(node2, "resets") {
+		if (node == node2)
+			continue;
+
+		for (index = 0; ; index++) {
+			ret = of_parse_phandle_with_args(node2, "resets",
+							 "#reset-cells", index,
+							 &args2);
+			if (ret)
+				break;
+
+			if (args2.np == args.np &&
+			    args2.args_count == args.args_count &&
+			    !memcmp(args2.args, args.args,
+				    args.args_count * sizeof(args.args[0])))
+				return false;
+		}
+	}
+
+	return true;
+}
+
 struct reset_control *__of_reset_control_get(struct device_node *node,
 				     const char *id, int index, bool shared,
-				     bool optional)
+				     bool optional, bool dedicated)
 {
 	struct reset_control *rstc;
 	struct reset_controller_dev *r, *rcdev;
@@ -514,6 +543,11 @@  struct reset_control *__of_reset_control_get(struct device_node *node,
 		return ERR_PTR(rstc_id);
 	}
 
+	if (dedicated && !__of_reset_is_dedicated(node, args)) {
+		mutex_unlock(&reset_list_mutex);
+		return ERR_PTR(-EINVAL);
+	}
+
 	/* reset_list_mutex also protects the rcdev's reset_control list */
 	rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
 
@@ -541,9 +575,25 @@  __reset_controller_by_name(const char *name)
 	return NULL;
 }
 
+static bool __reset_is_dedicated(const struct reset_control_lookup *lookup)
+{
+	const struct reset_control_lookup *lookup2;
+
+	list_for_each_entry(lookup, &reset_lookup_list, list) {
+		if (lookup2 == lookup)
+			continue;
+
+		if (lookup2->provider == lookup->provider &&
+		    lookup2->index == lookup->index)
+			return false;
+	}
+
+	return true;
+}
+
 static struct reset_control *
 __reset_control_get_from_lookup(struct device *dev, const char *con_id,
-				bool shared, bool optional)
+				bool shared, bool optional, bool dedicated)
 {
 	const struct reset_control_lookup *lookup;
 	struct reset_controller_dev *rcdev;
@@ -562,6 +612,11 @@  __reset_control_get_from_lookup(struct device *dev, const char *con_id,
 		if ((!con_id && !lookup->con_id) ||
 		    ((con_id && lookup->con_id) &&
 		     !strcmp(con_id, lookup->con_id))) {
+			if (dedicated && !__reset_is_dedicated(lookup)) {
+				mutex_unlock(&reset_lookup_mutex);
+				return ERR_PTR(-EINVAL);
+			}
+
 			mutex_lock(&reset_list_mutex);
 			rcdev = __reset_controller_by_name(lookup->provider);
 			if (!rcdev) {
@@ -588,13 +643,15 @@  __reset_control_get_from_lookup(struct device *dev, const char *con_id,
 }
 
 struct reset_control *__reset_control_get(struct device *dev, const char *id,
-					  int index, bool shared, bool optional)
+					  int index, bool shared,
+					  bool optional, bool dedicated)
 {
 	if (dev->of_node)
 		return __of_reset_control_get(dev->of_node, id, index, shared,
-					      optional);
+					      optional, dedicated);
 
-	return __reset_control_get_from_lookup(dev, id, shared, optional);
+	return __reset_control_get_from_lookup(dev, id, shared, optional,
+					       dedicated);
 }
 EXPORT_SYMBOL_GPL(__reset_control_get);
 
@@ -635,7 +692,7 @@  static void devm_reset_control_release(struct device *dev, void *res)
 
 struct reset_control *__devm_reset_control_get(struct device *dev,
 				     const char *id, int index, bool shared,
-				     bool optional)
+				     bool optional, bool dedicated)
 {
 	struct reset_control **ptr, *rstc;
 
@@ -644,7 +701,7 @@  struct reset_control *__devm_reset_control_get(struct device *dev,
 	if (!ptr)
 		return ERR_PTR(-ENOMEM);
 
-	rstc = __reset_control_get(dev, id, index, shared, optional);
+	rstc = __reset_control_get(dev, id, index, shared, optional, dedicated);
 	if (!IS_ERR(rstc)) {
 		*ptr = rstc;
 		devres_add(dev, ptr);
@@ -671,7 +728,7 @@  int __device_reset(struct device *dev, bool optional)
 	struct reset_control *rstc;
 	int ret;
 
-	rstc = __reset_control_get(dev, NULL, 0, 0, optional);
+	rstc = __reset_control_get(dev, NULL, 0, false, optional, false);
 	if (IS_ERR(rstc))
 		return PTR_ERR(rstc);
 
@@ -735,7 +792,8 @@  of_reset_control_array_get(struct device_node *np, bool shared, bool optional)
 		return ERR_PTR(-ENOMEM);
 
 	for (i = 0; i < num; i++) {
-		rstc = __of_reset_control_get(np, NULL, i, shared, optional);
+		rstc = __of_reset_control_get(np, NULL, i, shared, optional,
+					      false);
 		if (IS_ERR(rstc))
 			goto err_rst;
 		resets->rstc[i] = rstc;
diff --git a/include/linux/reset.h b/include/linux/reset.h
index 09732c36f3515a1e..6ca6e108b612f923 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -17,15 +17,15 @@  int reset_control_status(struct reset_control *rstc);
 
 struct reset_control *__of_reset_control_get(struct device_node *node,
 				     const char *id, int index, bool shared,
-				     bool optional);
+				     bool optional, bool dedicated);
 struct reset_control *__reset_control_get(struct device *dev, const char *id,
 					  int index, bool shared,
-					  bool optional);
+					  bool optional, bool dedicated);
 void reset_control_put(struct reset_control *rstc);
 int __device_reset(struct device *dev, bool optional);
 struct reset_control *__devm_reset_control_get(struct device *dev,
 				     const char *id, int index, bool shared,
-				     bool optional);
+				     bool optional, bool dedicated);
 
 struct reset_control *devm_reset_control_array_get(struct device *dev,
 						   bool shared, bool optional);
@@ -66,21 +66,23 @@  static inline int __device_reset(struct device *dev, bool optional)
 static inline struct reset_control *__of_reset_control_get(
 					struct device_node *node,
 					const char *id, int index, bool shared,
-					bool optional)
+					bool optional, bool dedicated)
 {
 	return optional ? NULL : ERR_PTR(-ENOTSUPP);
 }
 
 static inline struct reset_control *__reset_control_get(
 					struct device *dev, const char *id,
-					int index, bool shared, bool optional)
+					int index, bool shared, bool optional,
+					bool dedicated)
 {
 	return optional ? NULL : ERR_PTR(-ENOTSUPP);
 }
 
 static inline struct reset_control *__devm_reset_control_get(
 					struct device *dev, const char *id,
-					int index, bool shared, bool optional)
+					int index, bool shared, bool optional,
+					bool dedicated)
 {
 	return optional ? NULL : ERR_PTR(-ENOTSUPP);
 }
@@ -127,7 +129,25 @@  static inline int device_reset_optional(struct device *dev)
 static inline struct reset_control *
 __must_check reset_control_get_exclusive(struct device *dev, const char *id)
 {
-	return __reset_control_get(dev, id, 0, false, false);
+	return __reset_control_get(dev, id, 0, false, false, false);
+}
+
+/**
+ * reset_control_get_dedicated - Lookup and obtain an exclusive reference
+ *                               to a dedicated reset controller.
+ * @dev: device to be reset by the controller
+ * @id: reset line name
+ *
+ * Returns a struct reset_control or IS_ERR() condition containing errno.
+ * If this function is called more than once for the same reset_control it will
+ * return -EBUSY.
+ *
+ * Use of id names is optional.
+ */
+static inline struct reset_control *
+__must_check reset_control_get_dedicated(struct device *dev, const char *id)
+{
+	return __reset_control_get(dev, id, 0, false, false, true);
 }
 
 /**
@@ -155,19 +175,19 @@  __must_check reset_control_get_exclusive(struct device *dev, const char *id)
 static inline struct reset_control *reset_control_get_shared(
 					struct device *dev, const char *id)
 {
-	return __reset_control_get(dev, id, 0, true, false);
+	return __reset_control_get(dev, id, 0, true, false, false);
 }
 
 static inline struct reset_control *reset_control_get_optional_exclusive(
 					struct device *dev, const char *id)
 {
-	return __reset_control_get(dev, id, 0, false, true);
+	return __reset_control_get(dev, id, 0, false, true, false);
 }
 
 static inline struct reset_control *reset_control_get_optional_shared(
 					struct device *dev, const char *id)
 {
-	return __reset_control_get(dev, id, 0, true, true);
+	return __reset_control_get(dev, id, 0, true, true, false);
 }
 
 /**
@@ -183,7 +203,7 @@  static inline struct reset_control *reset_control_get_optional_shared(
 static inline struct reset_control *of_reset_control_get_exclusive(
 				struct device_node *node, const char *id)
 {
-	return __of_reset_control_get(node, id, 0, false, false);
+	return __of_reset_control_get(node, id, 0, false, false, false);
 }
 
 /**
@@ -208,7 +228,7 @@  static inline struct reset_control *of_reset_control_get_exclusive(
 static inline struct reset_control *of_reset_control_get_shared(
 				struct device_node *node, const char *id)
 {
-	return __of_reset_control_get(node, id, 0, true, false);
+	return __of_reset_control_get(node, id, 0, true, false, false);
 }
 
 /**
@@ -225,7 +245,7 @@  static inline struct reset_control *of_reset_control_get_shared(
 static inline struct reset_control *of_reset_control_get_exclusive_by_index(
 					struct device_node *node, int index)
 {
-	return __of_reset_control_get(node, NULL, index, false, false);
+	return __of_reset_control_get(node, NULL, index, false, false, false);
 }
 
 /**
@@ -253,7 +273,7 @@  static inline struct reset_control *of_reset_control_get_exclusive_by_index(
 static inline struct reset_control *of_reset_control_get_shared_by_index(
 					struct device_node *node, int index)
 {
-	return __of_reset_control_get(node, NULL, index, true, false);
+	return __of_reset_control_get(node, NULL, index, true, false, false);
 }
 
 /**
@@ -272,7 +292,7 @@  static inline struct reset_control *
 __must_check devm_reset_control_get_exclusive(struct device *dev,
 					      const char *id)
 {
-	return __devm_reset_control_get(dev, id, 0, false, false);
+	return __devm_reset_control_get(dev, id, 0, false, false, false);
 }
 
 /**
@@ -287,19 +307,19 @@  __must_check devm_reset_control_get_exclusive(struct device *dev,
 static inline struct reset_control *devm_reset_control_get_shared(
 					struct device *dev, const char *id)
 {
-	return __devm_reset_control_get(dev, id, 0, true, false);
+	return __devm_reset_control_get(dev, id, 0, true, false, false);
 }
 
 static inline struct reset_control *devm_reset_control_get_optional_exclusive(
 					struct device *dev, const char *id)
 {
-	return __devm_reset_control_get(dev, id, 0, false, true);
+	return __devm_reset_control_get(dev, id, 0, false, true, false);
 }
 
 static inline struct reset_control *devm_reset_control_get_optional_shared(
 					struct device *dev, const char *id)
 {
-	return __devm_reset_control_get(dev, id, 0, true, true);
+	return __devm_reset_control_get(dev, id, 0, true, true, false);
 }
 
 /**
@@ -317,7 +337,7 @@  static inline struct reset_control *devm_reset_control_get_optional_shared(
 static inline struct reset_control *
 devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
 {
-	return __devm_reset_control_get(dev, NULL, index, false, false);
+	return __devm_reset_control_get(dev, NULL, index, false, false, false);
 }
 
 /**
@@ -333,7 +353,7 @@  devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
 static inline struct reset_control *
 devm_reset_control_get_shared_by_index(struct device *dev, int index)
 {
-	return __devm_reset_control_get(dev, NULL, index, true, false);
+	return __devm_reset_control_get(dev, NULL, index, true, false, false);
 }
 
 /*