All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Chen-Yu Tsai" <wenst@chromium.org>
Cc: devicetree@vger.kernel.org, linux-input@vger.kernel.org,
	robh@kernel.org, conor+dt@kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v7 1/8] regulator: core: Add "enable and wait" functions
Date: Tue, 03 Nov 2026 05:11:32 +0000	[thread overview]
Message-ID: <sashiko-outbox-194592@kernel.org> (raw)
In-Reply-To: <20261103043734.2179858-2-wenst@chromium.org>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] regulator: core: unlocked _regulator_is_enabled_recursive() in early resolve
- [Medium] regulator: core: stale last_on timestamp after disable skips enable wait
- [Medium] regulator: core: uninitialized last_on in set_machine_constraints()
--

commit 10272782c962e94533f9c00c5f660ea31ea2ca13
Author: Chen-Yu Tsai <wenst@chromium.org>

regulator: core: Add "enable and wait" functions

This commit adds a last_on timestamp field to struct regulator_dev and
introduces regulator_enable_and_wait() and regulator_bulk_enable_and_wait()
to wait for the remaining turn-on delay after a regulator is actually
enabled.

> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
> index 14422cfe80e2d..071f9f8185f63 100644
> --- a/drivers/regulator/core.c
> +++ b/drivers/regulator/core.c

[ ... ]

> @@ -2188,6 +2188,45 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev,
>  	return ERR_PTR(-ENODEV);
>  }
>  
> +/**
> + * _regulator_is_enabled_recursive - is the regulator output enabled all
> + *				     the way to the root supply
> + * @rdev: regulator device
> + *
> + * Only intended for checking regulators that have been left on by
> + * hardware default or firmware.
> + *
> + * Assumes supplies have been resolved. Must be called with dependent
> + * locks held.
> + *
> + * Return: Positive if the regulator output backing the source/client,
> + *	   and all of its upstream supplies, have requested that their
> + *	   respective device be enabled, zero if any of them hasn't,
> + *	   else a negative error number.
> + */
> +static int _regulator_is_enabled_recursive(struct regulator_dev *rdev)
> +{
> +	int ret;
> +
> +	ret = _regulator_is_enabled(rdev);
> +	if (ret <= 0)
> +		return ret;
> +
> +	/*
> +	 * If .last_on was set, then this rdev was either enabled through
> +	 * _regulator_do_enable(), or had been checked before as the target
> +	 * of regulator_resolve_supply().
> +	 */
> +	if (rdev->last_on)
> +		return ret;
> +
> +	/* This is the root supply; we can only assume it actually has power */
> +	if (!rdev->supply)
> +		return ret;
> +
> +	return _regulator_is_enabled_recursive(rdev->supply->rdev);
> +}
> +
>  static int regulator_resolve_supply(struct regulator_dev *rdev)
>  {
>  	struct regulator_dev *r;
> @@ -2373,6 +2412,14 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
>  		}
>  		rdev->constraints_pending = false;
>  	}
> +
> +	/*
> +	 * regulator was left on but not enabled with .always_on or .boot_on
> +	 * constraints, and thus .last_on timestamp is still invalid.
> +	 */
> +	if (!rdev->last_on && _regulator_is_enabled_recursive(rdev) > 0)
> +		rdev->last_on = ktime_get_boottime();
> +
>  	regulator_unlock_dependent(rdev, &ww_ctx);

[Severity: Medium]
Does this call _regulator_is_enabled_recursive(rdev) without holding any
regulator locks during the early supply resolution path in
regulator_register()?

In drivers/regulator/core.c:regulator_register(), when the initial call to
set_machine_constraints(rdev, false) returns -EPROBE_DEFER,
regulator_resolve_supply(rdev) is called before regulator_init_coupling(rdev)
has initialized rdev->coupling_desc.n_coupled:

drivers/regulator/core.c:regulator_register() {
    ...
	ret = set_machine_constraints(rdev, false);
	if (ret == -EPROBE_DEFER) {
		rdev_dbg(rdev, "will resolve supply early: %s\n",
			 rdev->supply_name);
		ret = regulator_resolve_supply(rdev);
		if (!ret)
			ret = set_machine_constraints(rdev, false);
		else
			rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
				 ERR_PTR(ret));
		tried_supply_resolve = true;
	}
    ...
	ret = regulator_init_coupling(rdev);
    ...
}

At that point, rdev->coupling_desc.n_coupled is still 0, so
regulator_lock_dependent() -> regulator_lock_recursive() performs zero loop
iterations:

drivers/regulator/core.c:regulator_lock_recursive() {
    ...
	for (i = 0; i < rdev->coupling_desc.n_coupled; i++) {
		c_rdev = rdev->coupling_desc.coupled_rdevs[i];
        ...
	}
    ...
}

Previously, rdev->constraints_pending was false during that early call, so
nothing ran inside the regulator_lock_dependent() section. With this change,
_regulator_is_enabled_recursive(rdev) runs outside the
if (rdev->constraints_pending) block and walks rdev and its live upstream
supply chain without holding rdev->mutex or any supply mutexes.

[ ... ]

> @@ -3180,13 +3231,26 @@ static int _regulator_enable(struct regulator *regulator)
>  		} else if (ret < 0) {
>  			rdev_err(rdev, "is_enabled() failed: %pe\n", ERR_PTR(ret));
>  			goto err_consumer_disable;
> +		} else {
> +			/*
> +			 * Either the regulator was already enabled somehow, or this regulator
> +			 * itself has no on/off control. Either way our timestamp cannot be
> +			 * earlier than its supply's timestamp. supply_last_on == 0 if there
> +			 * is no supply.
> +			 */
> +			if (rdev->last_on < supply_last_on)
> +				rdev->last_on = supply_last_on;
> +			if (!rdev->last_on)
> +				rdev->last_on = ktime_get_boottime();
>  		}
> -		/* Fallthrough on positive return values - already enabled */
>  	}

[Severity: Medium]
Can rdev->last_on hold a stale timestamp from before a previous disable,
causing regulator_enable_and_wait() to skip the wait_us delay?

When a regulator is disabled in drivers/regulator/core.c:_regulator_do_disable(),
rdev->last_off is updated if off_on_delay is set, but rdev->last_on is never
reset to 0:

drivers/regulator/core.c:_regulator_do_disable() {
    ...
	if (rdev->desc->off_on_delay)
		rdev->last_off = ktime_get_boottime();

	trace_regulator_disable_complete(rdev_get_name(rdev));

	return 0;
}

This can leave a stale rdev->last_on in two ways:

1. If a regulator is enabled at T1 (setting rdev->last_on = T1), disabled via
_regulator_do_disable() at T2, and later re-enabled in hardware while
rdev->use_count == 0 (for example by firmware across suspend/resume or via
regulator_hardware_enable()), a subsequent regulator_enable_and_wait() call
sees rdev->use_count == 0 and _regulator_is_enabled(rdev) > 0. In
_regulator_enable(), if rdev has no supply or its supply remained on since
T1 (so supply_last_on <= T1), the check if (!rdev->last_on) is false because
rdev->last_on is still T1. As a result, rdev->last_on is not updated to
ktime_get_boottime() and regulator_enable_and_wait() skips the required wait.

2. In _regulator_is_enabled_recursive(), if (rdev->last_on) short-circuits
the walk up the supply chain. In a supply chain root -> mid -> leaf where
root and mid are initially on (setting root->last_on and mid->last_on) and
root is later disabled while mid->use_count == 0 and
_regulator_is_enabled(mid) > 0 (such as a passive/fixed regulator without an
is_enabled() op), mid->last_on remains non-zero. When leaf resolves its
supply in regulator_resolve_supply(), _regulator_is_enabled_recursive(mid)
returns positive without checking root, setting leaf->last_on =
ktime_get_boottime() while root is actually off.

Should rdev->last_on be cleared when a regulator or its upstream supply is
disabled?

[ ... ]

> @@ -6251,6 +6341,15 @@ regulator_register(struct device *dev,
>  			goto del_cdev_and_bdev;
>  	}
>  
> +	/*
> +	 * If no supply was given, then the last_on timestamp could not have
> +	 * been updated in regulator_resolve_supply(). Check it here.
> +	 */
> +	regulator_lock(rdev);
> +	if (!rdev->supply_name && !rdev->last_on && _regulator_is_enabled(rdev) > 0)
> +		rdev->last_on = ktime_get_boottime();
> +	regulator_unlock(rdev);

[Severity: Medium]
What happens to rdev->last_on when set_machine_constraints() enables a
regulator that has no direct enable operation (!rdev->ena_pin &&
!rdev->desc->ops->enable)?

In drivers/regulator/core.c:set_machine_constraints(), when always_on or
boot_on is set on a regulator without enable ops, _regulator_do_enable(rdev)
returns -EINVAL before reaching rdev->last_on = ktime_get_boottime(), and
set_machine_constraints() ignores -EINVAL and increments rdev->use_count for
always_on:

drivers/regulator/core.c:set_machine_constraints() {
    ...
		ret = _regulator_do_enable(rdev);
		if (ret < 0 && ret != -EINVAL) {
			rdev_err(rdev, "failed to enable: %pe\n", ERR_PTR(ret));
			if (supply_enabled)
				regulator_disable(rdev->supply);
			return ret;
		}

		if (rdev->constraints->always_on)
			rdev->use_count++;
    ...
}

This appears to leave rdev->last_on as 0 in two cases:

1. For a regulator with a supply (rdev->supply_name != NULL), no enable ops,
and boot_on set whose supply is initially off, the first
set_machine_constraints(rdev, false) call in regulator_register() returns
-EPROBE_DEFER while rdev->constraints_pending is still false. Early supply
resolution in regulator_resolve_supply(rdev) then runs
_regulator_is_enabled_recursive(rdev) while rdev->supply is still off,
leaving rdev->last_on as 0. Next, regulator_register() calls
set_machine_constraints(rdev, false) a second time, which enables
rdev->supply, gets -EINVAL from _regulator_do_enable(rdev), and leaves
rdev->last_on as 0. Because tried_supply_resolve is true and
rdev->supply_name is non-NULL, neither the second regulator_resolve_supply()
call nor the !rdev->supply_name check above runs, leaving rdev->last_on as 0
after registration completes.

2. For a supply-less always_on regulator without enable ops,
set_machine_constraints() sets rdev->use_count = 1 with rdev->last_on = 0,
and regulator_register() publishes the regulator via
set_consumer_device_supply() and device_add(&rdev->dev) before reaching the
!rdev->supply_name check above. If a consumer probes concurrently on another
thread and calls regulator_enable_and_wait() in that window,
_regulator_enable() skips the rdev->use_count == 0 branch, reads
last_on == 0, and returns without waiting for wait_us.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103043734.2179858-1-wenst@chromium.org?part=1

  reply	other threads:[~2026-11-03  5:11 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03  4:37 [PATCH v7 0/8] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
2026-11-03  4:37 ` [PATCH v7 1/8] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
2026-11-03  5:11   ` sashiko-bot [this message]
2026-11-03  4:37 ` [PATCH v7 2/8] Input: elan_i2c - Wait for initialization after enabling regulator supply Chen-Yu Tsai
2026-11-03  4:37 ` [PATCH v7 3/8] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long Chen-Yu Tsai
2026-11-03  4:52   ` sashiko-bot
2026-11-03  4:37 ` [PATCH v7 4/8] i2c: of-prober: " Chen-Yu Tsai
2026-11-03  4:37 ` [PATCH v7 5/8] i2c: of-prober: Let cleanup op know if a device was found Chen-Yu Tsai
2026-11-03  4:37 ` [PATCH v7 6/8] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper Chen-Yu Tsai
2026-11-03  4:37 ` [PATCH v7 7/8] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on Chen-Yu Tsai
2026-11-03  4:37 ` [PATCH v7 8/8] arm64: dts: mediatek: mt8192-asurada-spherion: Add Synaptics trackpad's supply Chen-Yu Tsai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-194592@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=wenst@chromium.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.