[PATCH v2 05/13] power-domain: Add refcounting
Simon Glass
sjg at chromium.org
Thu Dec 5 18:56:44 CET 2024
Hi Miquel,
On Thu, 5 Dec 2024 at 06:54, Miquel Raynal <miquel.raynal at bootlin.com> wrote:
>
> It is very surprising that such an uclass, specifically designed to
> handle resources that may be shared by different devices, is not keeping
> the count of the number of times a power domain has been
> enabled/disabled to avoid shutting it down unexpectedly or disabling it
> several times.
>
> Doing this causes troubles on eg. i.MX8MP because disabling power
> domains can be done in a recursive loops were the same power domain
> disabled up to 4 times in a row. PGCs seem to have tight FSM internal
> timings to respect and it is easy to produce a race condition that puts
> the power domains in an unstable state, leading to ADB400 errors and
> later crashes in Linux.
>
> Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
> ---
> drivers/power/domain/power-domain-uclass.c | 37 ++++++++++++++++++++++++++++--
> include/power-domain.h | 4 ++--
> 2 files changed, 37 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
> index 938bd8cbc9ffd1ba2109d702f886b6a99288d063..55a3d95d83c5aaac31acbd877199c87f8f6fb880 100644
> --- a/drivers/power/domain/power-domain-uclass.c
> +++ b/drivers/power/domain/power-domain-uclass.c
> @@ -12,6 +12,10 @@
> #include <power-domain-uclass.h>
> #include <dm/device-internal.h>
>
> +struct power_domain_priv {
> + int on_count;
> +};
> +
> static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
> {
> return (struct power_domain_ops *)dev->driver->ops;
> @@ -110,19 +114,47 @@ int power_domain_free(struct power_domain *power_domain)
> int power_domain_on(struct power_domain *power_domain)
> {
> struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
> + struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
> + int ret;
>
> debug("%s(power_domain=%p)\n", __func__, power_domain);
>
> - return ops->on ? ops->on(power_domain) : 0;
> + if (priv->on_count++ > 0)
> + return 0;
-EALREADY - see drivers/power/regulator/regulator-uclass.c
> +
> + ret = ops->on ? ops->on(power_domain) : 0;
> + if (ret) {
> + priv->on_count--;
> + return ret;
> + }
> +
> + return 0;
> }
>
> int power_domain_off(struct power_domain *power_domain)
> {
> struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
> + struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
> + int ret;
>
> debug("%s(power_domain=%p)\n", __func__, power_domain);
>
> - return ops->off ? ops->off(power_domain) : 0;
> + if (priv->on_count <= 0) {
> + debug("Power domain %s already off.\n", power_domain->dev->name);
> + priv->on_count--;
> + return 0;
> + }
> +
> + if (priv->on_count-- > 1)
> + return 0;
-EBUSY otherwise no one knows whether it actually happened
You could add a helper to silently ignore -EBUSY ?
> +
> + ret = ops->off ? ops->off(power_domain) : 0;
> + if (ret) {
> + priv->on_count++;
> + return ret;
> + }
> +
> + return 0;
> }
>
> #if CONFIG_IS_ENABLED(OF_REAL)
> @@ -180,4 +212,5 @@ int dev_power_domain_off(struct udevice *dev)
> UCLASS_DRIVER(power_domain) = {
> .id = UCLASS_POWER_DOMAIN,
> .name = "power_domain",
> + .per_device_auto = sizeof(struct power_domain_priv),
> };
> diff --git a/include/power-domain.h b/include/power-domain.h
> index 18525073e5e3534fcbac6fae4e18462f29a4dc49..0266fc2f864ef3645d2a8b3c6fa66fdbd868799c 100644
> --- a/include/power-domain.h
> +++ b/include/power-domain.h
> @@ -147,7 +147,7 @@ static inline int power_domain_free(struct power_domain *power_domain)
> #endif
>
> /**
> - * power_domain_on - Enable power to a power domain.
> + * power_domain_on - Enable power to a power domain (with refcounting)
> *
> * @power_domain: A power domain struct that was previously successfully
> * requested by power_domain_get().
> @@ -163,7 +163,7 @@ static inline int power_domain_on(struct power_domain *power_domain)
> #endif
>
> /**
> - * power_domain_off - Disable power to a power domain.
> + * power_domain_off - Disable power to a power domain (with refcounting)
> *
> * @power_domain: A power domain struct that was previously successfully
> * requested by power_domain_get().
>
> --
> 2.47.0
>
Regards,
Simon
More information about the U-Boot
mailing list