Devicetree
 help / color / mirror / Atom feed
* [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes
@ 2026-08-25  4:16 Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 1/7] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

Hi everyone,

This is v5 of my Chromebook trackpad supply fixes and optimization
series.

Changes since v4:
- Made regulator core try to set last_on timestamp for regulators that
  were left on by reset default or firmware, but not enabled in kernel
- Dropped two patches that were merged
- Link to v4:
  https://lore.kernel.org/all/20260811122011.3539250-1-wenst@chromium.org/

Changes since v3:
- Added __private modified to regulator_bulk_data.wait_us field and
  switched to ACCESS_PRIVATE accessor for the field (Andy)
- Moved wait outside regulator lock scope (Sashiko)
- Moved "remaining" value assignment closer to conditional (Andy)
- Fixed variable unit name in regulator_enable_and_wait() prototype
  (Andy, Sashiko)
- Added commit message section showing average delay needed for I2C
  prober (patch 6) (Andy)
- Link to v3:
  https://lore.kernel.org/all/20260721075226.2347933-1-wenst@chromium.org/

Changes since v2:
- Added new regulator "enable and wait" functions that wait until at
  least the given amount of time has passed since the regulator was
  last enabled.
- Converted patches to use the new functions
- New header inclusion cleanup patch for elan_i2c driver

This series fixes the trackpad descriptions on some MediaTek-based
Chromebooks: either the trackpad's supply was set as always-on to
workaround missing delays in the driver, or the delay and supply
are missing from the trackpad's device node.

v1 was just the first patch [1]. It has since grown to cover multiple
drivers and devices.


Patch 1 adds new "enable and wait" functions for single and bulk
regulator enable.

Patch 2 adds the correct enable delay after enabling the supply regulator
for the Elan trackpad to initialize. This uses the new "enable and wait"
regulator enable function.

Patch 3 applies the same logic of skipping the power on delay to the
i2c-hid-of driver.

Patch 4 applies the same logic of skipping the power on delay to the
i2c OF component prober library.

Patch 5 adds a delay between when the device node found is enabled and
when regulator_disable() is called. This gives an asynchronously probing
driver some time to increment the enable count of their regulator
reference, thus keeping the device operational and allowing the driver
to skip the initialization delay.

Patch 6 removes the "always-on" setting from the trackpad supply for
Elm / Hana and adds the correct delay to the second source trackpad.
This corrects the hardware description.

Patch 7 adds the supply and power on delay properties to the Synaptics
trackpad on the Spherion device. Combined with previous driver changes
this should cause no actual functional changes or delays.


Please take a look. Patches 2, 3, and 4 have build time dependencies
on patch 1. I suggest placing patch 1 on a separate immutable branch
or tag for the other maintainers to pull in. The three patches all have
different maintainers. Or if it's not too late, Mark could take patch
1 for v7.3-rc1 and the rest can go in later.

The DT changes must go in after all the driver changes land, especially
patch 2 that adds delays to the Elan trackpad driver. Otherwise the user
could potentially end up with a non-functional trackpad on the device.


Thanks
ChenYu

[1] https://lore.kernel.org/all/20241001093815.2481899-1-wenst@chromium.org/

Chen-Yu Tsai (7):
  regulator: core: Add "enable and wait" functions
  Input: elan_i2c - Wait for initialization after enabling regulator
    supply
  HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently
    long
  i2c: of-prober: skip post-power-on delay if powered on sufficiently
    long
  i2c: of-prober: Defer regulator_disable() on successful probe in
    simple helper
  arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as
    always-on
  arm64: dts: mediatek: mt8192-asurada-spherion: Add Synaptics
    trackpad's supply

 .../boot/dts/mediatek/mt8173-elm-hana.dtsi    |   8 +-
 arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi  |   1 -
 .../mediatek/mt8192-asurada-spherion-r0.dts   |   2 +
 drivers/hid/i2c-hid/i2c-hid-of.c              |   9 +-
 drivers/i2c/i2c-core-of-prober.c              |  25 ++--
 drivers/input/mouse/elan_i2c_core.c           |   7 +-
 drivers/regulator/core.c                      | 117 +++++++++++++++---
 include/linux/regulator/consumer.h            |  21 +++-
 include/linux/regulator/driver.h              |   2 +
 9 files changed, 149 insertions(+), 43 deletions(-)

-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v5 1/7] regulator: core: Add "enable and wait" functions
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  2026-08-25  4:47   ` sashiko-bot
  2026-08-25  7:44   ` Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 2/7] Input: elan_i2c - Wait for initialization after enabling regulator supply Chen-Yu Tsai
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

In device power sequencing and initialization use cases, it is common
for the driver to enable the regulator and then wait for a certain
period of time to pass before continuing.

In cases where the regulator supply is always on, or has been turned on
or left on by another consumer, the driver could shorten the delay or
skip it altogether, provided that enough time has already passed since
the regulator was _actually_ turned on.

Tracking this requires support from the regulator core. Introduce a
"last turned on" timestamp field to the regulator device, and "enable
and wait" functions to the single and bulk regulator consumer APIs.
The existing "enable without wait" functions are then converted to
macros that expand to the new functions.

The timestamp is updated each time the regulator is actually turned on.
For regulators left on by hardware default or by firmware, the core will
set the timestamp if it detects it was left on and its supply (and their
supply, and so on) is on as well. This is unfortunately best effort
only. The core can only assume a dangling regulator (one without a
supply) has power. This also applies to the dummy regulator.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v4:
- Try to update last_on timestamp for regulators that were left on

Changes since v3:
- Added __private modified to regulator_bulk_data.wait_us field and
  switched to ACCESS_PRIVATE accessor for the field
- Moved wait outside regulator lock scope
- Moved "remaining" value assignment closer to conditional
- Fixed variable unit name in regulator_enable_and_wait() prototype

Changes since v2:
- New patch
---
 drivers/regulator/core.c           | 117 +++++++++++++++++++++++++----
 include/linux/regulator/consumer.h |  21 ++++--
 include/linux/regulator/driver.h   |   2 +
 3 files changed, 119 insertions(+), 21 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 6a4008f387b5..3cc01b2f4c9f 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -99,7 +99,7 @@ struct regulator_event_work {
 	unsigned long event;
 };
 
-static int _regulator_enable(struct regulator *regulator);
+static int _regulator_enable(struct regulator *regulator, ktime_t *last_on);
 static int _regulator_is_enabled(struct regulator_dev *rdev);
 static int _regulator_disable(struct regulator *regulator);
 static int _regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags);
@@ -1675,7 +1675,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
 		    (rdev->constraints->always_on ||
 		     !regulator_is_enabled(rdev->supply))) {
 			ret = (is_locked
-			       ? _regulator_enable(rdev->supply)
+			       ? _regulator_enable(rdev->supply, NULL)
 			       : regulator_enable(rdev->supply));
 			if (ret < 0) {
 				_regulator_put(rdev->supply);
@@ -2186,6 +2186,44 @@ 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.
+ *
+ * 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;
@@ -2370,6 +2408,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))
+		rdev->last_on = ktime_get_boottime();
+
 	regulator_unlock_dependent(rdev, &ww_ctx);
 
 	if (!do_final_setup)
@@ -3061,6 +3107,8 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
 		fsleep(delay);
 	}
 
+	rdev->last_on = ktime_get_boottime();
+
 	trace_regulator_enable_complete(rdev_get_name(rdev));
 
 	return 0;
@@ -3130,8 +3178,8 @@ static int _regulator_handle_consumer_disable(struct regulator *regulator)
 	return 0;
 }
 
-/* locks held by regulator_enable() */
-static int _regulator_enable(struct regulator *regulator)
+/* locks held by regulator_enable_and_wait() */
+static int _regulator_enable(struct regulator *regulator, ktime_t *last_on)
 {
 	struct regulator_dev *rdev = regulator->rdev;
 	int ret;
@@ -3139,7 +3187,7 @@ static int _regulator_enable(struct regulator *regulator)
 	lockdep_assert_held_once(&rdev->mutex.base);
 
 	if (rdev->use_count == 0 && rdev->supply) {
-		ret = _regulator_enable(rdev->supply);
+		ret = _regulator_enable(rdev->supply, NULL);
 		if (ret < 0)
 			return ret;
 	}
@@ -3177,13 +3225,19 @@ 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 {
+			/* regulator already enabled somehow, but timestamp might be invalid */
+			if (!rdev->last_on)
+				rdev->last_on = ktime_get_boottime();
 		}
-		/* Fallthrough on positive return values - already enabled */
 	}
 
 	if (regulator->enable_count == 1)
 		rdev->use_count++;
 
+	if (last_on)
+		*last_on = rdev->last_on;
+
 	return 0;
 
 err_consumer_disable:
@@ -3197,31 +3251,49 @@ static int _regulator_enable(struct regulator *regulator)
 }
 
 /**
- * regulator_enable - enable regulator output
+ * regulator_enable_and_wait - enable regulator output and wait for time
+ *			       passed after regulator actually enabled
  * @regulator: regulator source
+ * @wait_us: time to wait after regulator actually turned on; 0 to not wait
  *
  * Request that the regulator be enabled with the regulator output at
  * the predefined voltage or current value.  Calls to regulator_enable()
  * must be balanced with calls to regulator_disable().
  *
+ * If wait_us is greater than zero, then check that wait_us has passed since
+ * the regulator is _actually_ enabled before returning.
+ *
  * NOTE: the output value can be set by other drivers, boot loader or may be
  * hardwired in the regulator.
  *
  * Return: 0 on success or a negative error number on failure.
  */
-int regulator_enable(struct regulator *regulator)
+int regulator_enable_and_wait(struct regulator *regulator, unsigned int wait_us)
 {
 	struct regulator_dev *rdev = regulator->rdev;
 	struct ww_acquire_ctx ww_ctx;
+	ktime_t last_on = 0;
 	int ret;
 
 	regulator_lock_dependent(rdev, &ww_ctx);
-	ret = _regulator_enable(regulator);
+	ret = _regulator_enable(regulator, &last_on);
 	regulator_unlock_dependent(rdev, &ww_ctx);
 
+	if (ret)
+		return ret;
+
+	if (wait_us) {
+		ktime_t end = ktime_add_us(last_on, wait_us);
+		s64 remaining;
+
+		remaining = ktime_us_delta(end, ktime_get_boottime());
+		if (remaining > 0)
+			fsleep(remaining);
+	}
+
 	return ret;
 }
-EXPORT_SYMBOL_GPL(regulator_enable);
+EXPORT_SYMBOL_GPL(regulator_enable_and_wait);
 
 static int _regulator_do_disable(struct regulator_dev *rdev)
 {
@@ -5390,30 +5462,38 @@ static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
 {
 	struct regulator_bulk_data *bulk = data;
 
-	bulk->ret = regulator_enable(bulk->consumer);
+	bulk->ret = regulator_enable_and_wait(bulk->consumer, ACCESS_PRIVATE(bulk, wait_us));
 }
 
 /**
- * regulator_bulk_enable - enable multiple regulator consumers
+ * regulator_bulk_enable_and_wait - enable multiple regulator consumers and
+ *				    wait for time passed after regulators are
+ *				    actually enabled
  *
  * @num_consumers: Number of consumers
  * @consumers:     Consumer data; clients are stored here.
+ * @wait_us: time to wait after regulators actually turned on; 0 to not wait
  *
  * This convenience API allows consumers to enable multiple regulator
  * clients in a single API call.  If any consumers cannot be enabled
  * then any others that were enabled will be disabled again prior to
  * return.
  *
+ * If wait_us is greater than zero, then check that wait_us has passed since
+ * the regulators are _actually_ enabled before returning.
+ *
  * Return: 0 on success or a negative error number on failure.
  */
-int regulator_bulk_enable(int num_consumers,
-			  struct regulator_bulk_data *consumers)
+int regulator_bulk_enable_and_wait(int num_consumers,
+				   struct regulator_bulk_data *consumers,
+				   unsigned int wait_us)
 {
 	ASYNC_DOMAIN_EXCLUSIVE(async_domain);
 	int i;
 	int ret = 0;
 
 	for (i = 0; i < num_consumers; i++) {
+		ACCESS_PRIVATE(consumers, wait_us) = wait_us;
 		async_schedule_domain(regulator_bulk_enable_async,
 				      &consumers[i], &async_domain);
 	}
@@ -5441,7 +5521,7 @@ int regulator_bulk_enable(int num_consumers,
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(regulator_bulk_enable);
+EXPORT_SYMBOL_GPL(regulator_bulk_enable_and_wait);
 
 /**
  * regulator_bulk_disable - disable multiple regulator consumers
@@ -6243,6 +6323,13 @@ 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.
+	 */
+	if (!rdev->supply_name && !rdev->last_on && _regulator_is_enabled(rdev))
+		rdev->last_on = ktime_get_boottime();
+
 	rdev_init_debugfs(rdev);
 
 	/* try to resolve regulators coupling since a new one was registered */
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index 56fe2693d9b2..0b83acc015d4 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -145,6 +145,7 @@ struct regulator_bulk_data {
 
 	/* private: Internal use */
 	int ret;
+	unsigned int __private wait_us;
 };
 
 #if defined(CONFIG_REGULATOR)
@@ -192,7 +193,7 @@ int devm_regulator_bulk_register_supply_alias(struct device *dev,
 					      int num_id);
 
 /* regulator output control and status */
-int __must_check regulator_enable(struct regulator *regulator);
+int __must_check regulator_enable_and_wait(struct regulator *regulator, unsigned int wait_us);
 int regulator_disable(struct regulator *regulator);
 int regulator_force_disable(struct regulator *regulator);
 int regulator_is_enabled(struct regulator *regulator);
@@ -209,8 +210,9 @@ int __must_check devm_regulator_bulk_get_const(
 	struct device *dev, int num_consumers,
 	const struct regulator_bulk_data *in_consumers,
 	struct regulator_bulk_data **out_consumers);
-int __must_check regulator_bulk_enable(int num_consumers,
-				       struct regulator_bulk_data *consumers);
+int __must_check regulator_bulk_enable_and_wait(int num_consumers,
+						struct regulator_bulk_data *consumers,
+						unsigned int wait_us);
 int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers,
 				   const char * const *id);
 int regulator_bulk_disable(int num_consumers,
@@ -410,7 +412,8 @@ static inline int devm_regulator_bulk_register_supply_alias(struct device *dev,
 	return 0;
 }
 
-static inline int regulator_enable(struct regulator *regulator)
+static inline int regulator_enable_and_wait(struct regulator *regulator,
+					    unsigned int wait_us)
 {
 	return 0;
 }
@@ -457,8 +460,9 @@ static inline int devm_regulator_bulk_get_const(
 	return 0;
 }
 
-static inline int regulator_bulk_enable(int num_consumers,
-					struct regulator_bulk_data *consumers)
+static inline int regulator_bulk_enable_and_wait(int num_consumers,
+						 struct regulator_bulk_data *consumers,
+						 unsigned int wait_us)
 {
 	return 0;
 }
@@ -676,6 +680,11 @@ regulator_is_equal(struct regulator *reg1, struct regulator *reg2)
 }
 #endif
 
+#define regulator_enable(regulator)	regulator_enable_and_wait(regulator, 0)
+
+#define regulator_bulk_enable(num_consumers, consumers)	\
+		regulator_bulk_enable_and_wait(num_consumers, consumers, 0)
+
 #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_REGULATOR)
 struct regulator *__must_check of_regulator_get(struct device *dev,
 						struct device_node *node,
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index cc6ce709ec86..8a74aa681df3 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -658,6 +658,8 @@ struct regulator_dev {
 	unsigned int constraints_pending:1;
 	unsigned int is_switch:1;
 
+	/* time when this regulator was enabled last time */
+	ktime_t last_on;
 	/* time when this regulator was disabled last time */
 	ktime_t last_off;
 	int cached_err;
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 2/7] Input: elan_i2c - Wait for initialization after enabling regulator supply
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 1/7] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long Chen-Yu Tsai
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

Elan trackpad controllers require some delay after enabling power to
the controller for the hardware and firmware to initialize:

  - 2ms for hardware initialization
  - 100ms for firmware initialization

Until then, the hardware will not respond to I2C transfers. This was
observed on the MT8173 Chromebooks after the regulator supply for the
trackpad was changed to "not always on".

Switch to the new regulator_enable_and_wait(). This makes sure that
enough time has passed since the regulator was first enabled, satisfying
the power sequencing delay requirement. This allows the delay to be
skipped if the regulator supply was already enabled by some other part
of the kernel, such as the I2C OF component prober.

Fixes: 6696777c6506 ("Input: add driver for Elan I2C/SMbus touchpad")
Link: https://lore.kernel.org/all/20241001093815.2481899-1-wenst@chromium.org/
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- Switched to new regulator_enable_and_wait() API

Changes since v1:
- Delay only if the regulator was previously disabled / turned off
- Link to v1
  https://lore.kernel.org/all/20241001093815.2481899-1-wenst@chromium.org/
---
 drivers/input/mouse/elan_i2c_core.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index f5e505edbc33..4bbc6cef8be6 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -36,6 +36,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/string_choices.h>
+#include <linux/time.h>
 #include <linux/uaccess.h>
 #include <linux/unaligned.h>
 
@@ -47,6 +48,8 @@
 #define ETP_FWIDTH_REDUCE	90
 #define ETP_FINGER_WIDTH	15
 #define ETP_RETRY_COUNT		3
+/* H/W init 2 ms + F/W init 100 ms w/ round up */
+#define ETP_POWER_ON_DELAY_US	(110 * USEC_PER_MSEC)
 
 /* quirks to control the device */
 #define ETP_QUIRK_QUICK_WAKEUP	BIT(0)
@@ -1260,7 +1263,7 @@ static int elan_probe(struct i2c_client *client)
 	if (IS_ERR(data->vcc))
 		return dev_err_probe(dev, PTR_ERR(data->vcc), "Failed to get 'vcc' regulator\n");
 
-	error = regulator_enable(data->vcc);
+	error = regulator_enable_and_wait(data->vcc, ETP_POWER_ON_DELAY_US);
 	if (error) {
 		dev_err(dev, "Failed to enable regulator: %d\n", error);
 		return error;
@@ -1416,7 +1419,7 @@ static int elan_resume(struct device *dev)
 	int error;
 
 	if (!device_may_wakeup(dev)) {
-		error = regulator_enable(data->vcc);
+		error = regulator_enable_and_wait(data->vcc, ETP_POWER_ON_DELAY_US);
 		if (error) {
 			dev_err(dev, "error %d enabling regulator\n", error);
 			goto err;
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 1/7] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 2/7] Input: elan_i2c - Wait for initialization after enabling regulator supply Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  2026-08-25  4:48   ` sashiko-bot
  2026-08-25  4:16 ` [PATCH v5 4/7] i2c: of-prober: " Chen-Yu Tsai
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

On some devices the HID device is powered from an always-on power rail,
or the power rail has been left on by either POR defaults or the
bootloader. By the time the driver probes, the device most certainly
has finished initializing. There is no need for the delay.

In such designs, the system integrators tend to work around the delay
to avoid the boot time penalty by simply omitting it from the device
tree. This is undesired, as the device tree is not fully describing
the hardware.

Switch to the new regulator_bulk_enable_and_wait() function that makes
sure a certain amount of time has passed since the regulator supplies
were actually enabled.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- Switched to new regulator_bulk_enable_and_wait() API
---
 drivers/hid/i2c-hid/i2c-hid-of.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/i2c-hid/i2c-hid-of.c b/drivers/hid/i2c-hid/i2c-hid-of.c
index 59393d71ddb9..fdaad451e710 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of.c
@@ -29,6 +29,7 @@
 #include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/regulator/consumer.h>
+#include <linux/time.h>
 
 #include "i2c-hid.h"
 
@@ -48,16 +49,14 @@ static int i2c_hid_of_power_up(struct i2chid_ops *ops)
 	struct device *dev = &ihid_of->client->dev;
 	int ret;
 
-	ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies),
-				    ihid_of->supplies);
+	ret = regulator_bulk_enable_and_wait(ARRAY_SIZE(ihid_of->supplies),
+					     ihid_of->supplies,
+					     ihid_of->post_power_delay_ms * USEC_PER_MSEC);
 	if (ret) {
 		dev_warn(dev, "Failed to enable supplies: %d\n", ret);
 		return ret;
 	}
 
-	if (ihid_of->post_power_delay_ms)
-		msleep(ihid_of->post_power_delay_ms);
-
 	gpiod_set_value_cansleep(ihid_of->reset_gpio, 0);
 	if (ihid_of->post_reset_delay_ms)
 		msleep(ihid_of->post_reset_delay_ms);
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 4/7] i2c: of-prober: skip post-power-on delay if powered on sufficiently long
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
                   ` (2 preceding siblings ...)
  2026-08-25  4:16 ` [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper Chen-Yu Tsai
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

On some devices the I2C component is powered from an always-on power
rail, or the power rail has been left on by either POR defaults or
the bootloader. By the time the prober probes the device, the device
most certainly has finished initializing and can respond. There is no
need for the delay.

In such designs, the system integrators tend to work around the delay
to avoid the boot time penalty by simply omitting it from the device
tree and the component prober. This is undesired, as the device tree
is not fully describing the hardware.

Switch to the new regulator_enable_and_wait() function that makes sure
a certain amount of time has passed since the regulator supply was
actually enabled.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v2:
- Switched to new regulator_enable_and_wait() API
---
 drivers/i2c/i2c-core-of-prober.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
index 6a82b03809d4..f9f3c0ef93ff 100644
--- a/drivers/i2c/i2c-core-of-prober.c
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -18,6 +18,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/stddef.h>
+#include <linux/time.h>
 
 /*
  * Some devices, such as Google Hana Chromebooks, are produced by multiple
@@ -226,13 +227,11 @@ static int i2c_of_probe_simple_enable_regulator(struct device *dev, struct i2c_o
 
 	dev_dbg(dev, "Enabling regulator supply \"%s\"\n", ctx->opts->supply_name);
 
-	ret = regulator_enable(ctx->supply);
+	ret = regulator_enable_and_wait(ctx->supply,
+					ctx->opts->post_power_on_delay_ms * USEC_PER_MSEC);
 	if (ret)
 		return ret;
 
-	if (ctx->opts->post_power_on_delay_ms)
-		msleep(ctx->opts->post_power_on_delay_ms);
-
 	return 0;
 }
 
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
                   ` (3 preceding siblings ...)
  2026-08-25  4:16 ` [PATCH v5 4/7] i2c: of-prober: " Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  2026-08-25  4:48   ` sashiko-bot
  2026-08-25  4:16 ` [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on Chen-Yu Tsai
  2026-08-25  4:16 ` [PATCH v5 7/7] arm64: dts: mediatek: mt8192-asurada-spherion: Add Synaptics trackpad's supply Chen-Yu Tsai
  6 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

When a I2C component is found, it's device node is immediately enabled.
This triggers device creation and driver binding. The prober will hold
the regulator enable reference across this part. If the driver probes
synchronously, then it happens within this window. On the other hand,
if the driver probes asynchronously, there is high chance that it
happens after the prober's cleanup function was called, in which case
the regulator would have been disabled when the driver's probe function
is called. This would then require the driver to wait 100 ms for the
hardware to reinitialize, even if the probe function was just a split
second late and the regulator was disabled a few milliseconds ago.

Recently, some of the drivers for the component that are targeted by the
I2C OF component prober gained the ability to skip waiting for hardware
initialization if the regulator was left enabled. This happens when the
PMIC has them on by default, or if the component prober left them on
after probing the component.

Tests on the Hana Chromebook showed that if the prober and trackpad
drivers are both builtin, then the time between the prober enabling the
device node and the trackpad driver asynchronously probing is between
5 ms and 30 ms, though sometimes there are outliers exceeding 100 ms.

Wait 100 ms before dropping the enable refcount on our end so that the
actual driver has the opportunity to catch and increase the refcount on
their end.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v3:
- Added commit message section showing average delay needed
---
 drivers/i2c/i2c-core-of-prober.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
index f9f3c0ef93ff..68c929b16b06 100644
--- a/drivers/i2c/i2c-core-of-prober.c
+++ b/drivers/i2c/i2c-core-of-prober.c
@@ -235,11 +235,23 @@ static int i2c_of_probe_simple_enable_regulator(struct device *dev, struct i2c_o
 	return 0;
 }
 
-static void i2c_of_probe_simple_disable_regulator(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
+static void i2c_of_probe_simple_disable_regulator(struct device *dev,
+						  struct i2c_of_probe_simple_ctx *ctx,
+						  bool defer_disable)
 {
 	if (!ctx->supply)
 		return;
 
+	/*
+	 * Wait a bit of time for async drivers to probe and increase the
+	 * regulator enable count. This allows the drivers to check and
+	 * skip waiting for re-initialization.
+	 */
+	if (defer_disable) {
+		dev_dbg(dev, "Deferring regulator disable\n");
+		msleep(100);
+	}
+
 	dev_dbg(dev, "Disabling regulator supply \"%s\"\n", ctx->opts->supply_name);
 
 	regulator_disable(ctx->supply);
@@ -356,7 +368,7 @@ int i2c_of_probe_simple_enable(struct device *dev, struct device_node *bus_node,
 	return 0;
 
 out_disable_regulator:
-	i2c_of_probe_simple_disable_regulator(dev, ctx);
+	i2c_of_probe_simple_disable_regulator(dev, ctx, false);
 out_put_gpiod:
 	i2c_of_probe_simple_put_gpiod(ctx);
 out_put_supply:
@@ -401,7 +413,7 @@ void i2c_of_probe_simple_cleanup(struct device *dev, void *data)
 	i2c_of_probe_simple_disable_gpio(dev, ctx);
 	i2c_of_probe_simple_put_gpiod(ctx);
 
-	i2c_of_probe_simple_disable_regulator(dev, ctx);
+	i2c_of_probe_simple_disable_regulator(dev, ctx, true);
 	i2c_of_probe_simple_put_supply(ctx);
 }
 EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup, "I2C_OF_PROBER");
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
                   ` (4 preceding siblings ...)
  2026-08-25  4:16 ` [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  2026-08-25  4:50   ` sashiko-bot
  2026-08-25  4:16 ` [PATCH v5 7/7] arm64: dts: mediatek: mt8192-asurada-spherion: Add Synaptics trackpad's supply Chen-Yu Tsai
  6 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel

Up until now, the MT8173 elm/hana device tree has set the dedicated
regulator supplying the trackpad as always-on, simply because the Elan
driver was missing proper delays. As a result the delay for the
Synaptics trackpad was also omitted, as it was not strictly required
under such a model and delayed the availability of the trackpad to the
user.

The Elan driver recently gained proper delays after power up, with
opportunistic skipping of the delay when the regulator was originally
on. The I2C HID driver gained similar opportunistic delay skipping.
So has the I2C OF component prober library.

Now fix the device tree to have the regulator not be always on, and
let the I2C HID device have the correct post-power-on delay time.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
 arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi | 8 +-------
 arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi      | 1 -
 2 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi
index 1004eb8ea52c..b9e311fcd9a0 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi
@@ -62,13 +62,7 @@ trackpad2: trackpad@2c {
 		pinctrl-0 = <&trackpad_irq>;
 		reg = <0x2c>;
 		hid-descr-addr = <0x0020>;
-		/*
-		 * The trackpad needs a post-power-on delay of 100ms,
-		 * but at time of writing, the power supply for it on
-		 * this board is always on. The delay is therefore not
-		 * added to avoid impacting the readiness of the
-		 * trackpad.
-		 */
+		post-power-on-delay-ms = <100>;
 		vdd-supply = <&mt6397_vgp6_reg>;
 		wakeup-source;
 		status = "fail-needs-probe";
diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
index a0573bc359fb..6b9f47f515c7 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
@@ -1093,7 +1093,6 @@ mt6397_vgp6_reg: ldo_vgp6 {
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
 				regulator-enable-ramp-delay = <218>;
-				regulator-always-on;
 			};
 
 			mt6397_vibr_reg: ldo_vibr {
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 7/7] arm64: dts: mediatek: mt8192-asurada-spherion: Add Synaptics trackpad's supply
  2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
                   ` (5 preceding siblings ...)
  2026-08-25  4:16 ` [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on Chen-Yu Tsai
@ 2026-08-25  4:16 ` Chen-Yu Tsai
  6 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  4:16 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Chen-Yu Tsai, Benson Leung, Tzung-Bi Shih, linux-mediatek,
	devicetree, linux-arm-kernel, chrome-platform, linux-input,
	linux-i2c, linux-kernel, stable+noautosel

The Synaptics trackpad, like the Elan trackpad option, is fed from the
system 3.3V power rail. Add it to the trackpad device node.

Also add the correct post-power-on delay, even though in practice it is
not required. The Synaptics trackpad requires 100ms after power-on (or
deasserting the reset, whichever comes later) to fully initialize. The
power is always on and the reset pin is not routed out, so the
implementation could try skipping the delay.

Cc: <stable+noautosel@kernel.org> # Without driver changes only lengthens probe time
Fixes: 925ebc0cd55c ("arm64: dts: mt8192-asurada-spherion: Add Synaptics trackpad support")
Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
I think this shouldn't be backported, as backporting it without the
driver enhancements just delays the trackpad probing with no real
gains.
---
 arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts b/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts
index 68caf4c58cfe..8adbfc307fca 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dts
@@ -94,6 +94,8 @@ trackpad@2c {
 		hid-descr-addr = <0x20>;
 		interrupts-extended = <&pio 15 IRQ_TYPE_LEVEL_LOW>;
 		wakeup-source;
+		vdd-supply = <&pp3300_u>;
+		post-power-on-delay-ms = <100>;
 		status = "fail-needs-probe";
 	};
 };
-- 
2.55.0.860.g4b6b3295ed-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 1/7] regulator: core: Add "enable and wait" functions
  2026-08-25  4:16 ` [PATCH v5 1/7] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
@ 2026-08-25  4:47   ` sashiko-bot
  2026-08-25  7:44   ` Chen-Yu Tsai
  1 sibling, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-25  4:47 UTC (permalink / raw)
  To: Chen-Yu Tsai; +Cc: linux-input, dmitry.torokhov, conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] regulator_bulk_enable_and_wait incorrectly assigns wait_us only to the first consumer, leaving the rest uninitialized.
- [High] regulator_register accesses hardware state via _regulator_is_enabled and updates rdev->last_on without holding rdev->mutex after the device is exposed.
- [Medium] _regulator_is_enabled() and _regulator_is_enabled_recursive() can return negative error codes, but they are used in boolean contexts where negative values evaluate to true.
--

--- Patch [1]: [PATCH v5 1/7] regulator: core: Add "enable and wait" functions ---
commit 5fd97a59a5c2607c473a3bc31a58415b701f2335
Author: Chen-Yu Tsai <wenst@chromium.org>

regulator: core: Add "enable and wait" functions

This patch introduces "enable and wait" functions to the single and bulk
regulator consumer APIs. It tracks a "last turned on" timestamp to shorten
or skip delays if the regulator is already on. The timestamp is also best
effort populated for regulators left on by hardware or firmware.

> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
> index 1797929dfe566..f70be7e38e599 100644
> --- a/drivers/regulator/core.c
> +++ b/drivers/regulator/core.c
> @@ -2352,6 +2390,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))
> +		rdev->last_on = ktime_get_boottime();
> +

[Severity: Medium]
_regulator_is_enabled_recursive() can return negative error codes. Since it
is evaluated in a boolean context here, won't negative values evaluate to
true, causing last_on to be incorrectly updated if there is a hardware read
failure?

>  	regulator_unlock_dependent(rdev, &ww_ctx);
>  
>  	if (!do_final_setup)

[ ... ]

> @@ -5372,30 +5444,38 @@ static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
>  int regulator_bulk_enable_and_wait(int num_consumers,
>  				   struct regulator_bulk_data *consumers,
>  				   unsigned int wait_us)
>  {
>  	ASYNC_DOMAIN_EXCLUSIVE(async_domain);
>  	int i;
>  	int ret = 0;
>  
>  	for (i = 0; i < num_consumers; i++) {
> +		ACCESS_PRIVATE(consumers, wait_us) = wait_us;
>  		async_schedule_domain(regulator_bulk_enable_async,
>  				      &consumers[i], &async_domain);
>  	}

[Severity: High]
Does this loop assign wait_us only to the first element of the array instead
of using the loop counter i? For subsequent consumers where i > 0, the
wait_us field remains uninitialized.

When regulator_bulk_enable_async() executes for these subsequent consumers,
could it read uninitialized memory and pass it to fsleep(), potentially
hanging the kernel thread indefinitely or skipping required delays? Should
this be ACCESS_PRIVATE(&consumers[i], wait_us)?

>  	async_synchronize_full_domain(&async_domain);

[ ... ]

> @@ -6225,6 +6305,13 @@ 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.
> +	 */
> +	if (!rdev->supply_name && !rdev->last_on && _regulator_is_enabled(rdev))
> +		rdev->last_on = ktime_get_boottime();
> +

[Severity: Medium]
Similar to the case in regulator_resolve_supply(), _regulator_is_enabled()
can return negative error codes. Could this cause last_on to be updated
incorrectly if the hardware read fails during probe?

[Severity: High]
Since device_add(&rdev->dev) is called earlier in regulator_register(),
exposing the device globally and allowing concurrent regulator_enable()
calls, is it safe to call _regulator_is_enabled(rdev) and update
rdev->last_on here without holding rdev->mutex? This could lead to a data
race on last_on and unsynchronized hardware callbacks.

>  	rdev_init_debugfs(rdev);
>  
>  	/* try to resolve regulators coupling since a new one was registered */

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long
  2026-08-25  4:16 ` [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long Chen-Yu Tsai
@ 2026-08-25  4:48   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-25  4:48 UTC (permalink / raw)
  To: Chen-Yu Tsai; +Cc: devicetree, dmitry.torokhov, robh, linux-input, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The patch relies on a buggy core regulator API, resulting in incorrect concurrent wait times for regulator sequencing that truncates the required hardware initialization delay.
- [High] The reset assertion pulse during probe may be too short if regulators are already enabled, potentially violating hardware reset timing requirements.
--

--- Patch [3]: [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long ---
commit 3ba58a3bbf6e0eecad8d9882947c8fc478c04ddc
Author: Chen-Yu Tsai <wenst@chromium.org>
Subject: HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long

This patch switches the driver to use regulator_bulk_enable_and_wait() to
avoid unnecessary boot delays if the device has been powered on long enough.
It omits the unconditional post power delay in favor of measuring the time
since the regulators were actually enabled.

> diff --git a/drivers/hid/i2c-hid/i2c-hid-of.c b/drivers/hid/i2c-hid/i2c-hid-of.c
> index 59393d71ddb9d..fdaad451e7102 100644
> --- a/drivers/hid/i2c-hid/i2c-hid-of.c
> +++ b/drivers/hid/i2c-hid/i2c-hid-of.c

[ ... ]

> @@ -48,16 +49,14 @@ static int i2c_hid_of_power_up(struct i2chid_ops *ops)
>  	struct device *dev = &ihid_of->client->dev;
>  	int ret;
>  
> -	ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies),
> -				    ihid_of->supplies);
> +	ret = regulator_bulk_enable_and_wait(ARRAY_SIZE(ihid_of->supplies),
> +					     ihid_of->supplies,
> +					     ihid_of->post_power_delay_ms * USEC_PER_MSEC);

[Severity: High]
Will this correctly wait for all regulator supplies to stabilize?

When looking at the implementation of regulator_bulk_enable_and_wait()
in drivers/regulator/core.c, the setup loop contains:

for (i = 0; i < num_consumers; i++) {
        ACCESS_PRIVATE(consumers, wait_us) = wait_us;
        async_schedule_domain(regulator_bulk_enable_async,
                              &consumers[i], &async_domain);
}

Since ACCESS_PRIVATE(consumers, wait_us) expands to consumers[0].wait_us
without indexing by i, only the first regulator receives the configured wait
time.

If the first regulator was already enabled by the bootloader (meaning its wait
time evaluates to 0) and the second regulator is newly enabled, won't the
function return instantly and skip the required hardware stabilization delay
for the second supply?

>  	if (ret) {
>  		dev_warn(dev, "Failed to enable supplies: %d\n", ret);
>  		return ret;
>  	}
>  
> -	if (ihid_of->post_power_delay_ms)
> -		msleep(ihid_of->post_power_delay_ms);
> -
>  	gpiod_set_value_cansleep(ihid_of->reset_gpio, 0);

[Severity: High]
Could skipping this delay cause the reset assertion pulse to be too short?

In i2c_hid_of_probe(), the reset GPIO is asserted just before calling
i2c_hid_of_power_up(). If the regulators are already enabled,
regulator_bulk_enable_and_wait() will return almost instantly.

Since the unconditional delay is now omitted, the code reaches
gpiod_set_value_cansleep() immediately. Does this reduce the reset assertion
pulse width from the previously guaranteed post_power_delay_ms down to just
a few microseconds?

Could this fail to properly reset I2C HID devices that require a longer
minimum reset pulse?

>  	if (ihid_of->post_reset_delay_ms)
>  		msleep(ihid_of->post_reset_delay_ms);

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper
  2026-08-25  4:16 ` [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper Chen-Yu Tsai
@ 2026-08-25  4:48   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-25  4:48 UTC (permalink / raw)
  To: Chen-Yu Tsai; +Cc: dmitry.torokhov, robh, linux-input, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] regulator_bulk_enable_and_wait() fails to set wait_us for all consumers except the first one, leading to uninitialized memory reads
- [High] _regulator_is_enabled(rdev) is called locklessly in regulator_register() after the device is exposed, causing data races
- [Medium] Unconditional 100ms boot penalty in i2c_of_probe_simple_cleanup when no I2C component is found
--

--- Patch [5]: [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper ---
commit 227ad2dc27d8706adbb141c1fb7f00da090435f9
Author: Chen-Yu Tsai <wenst@chromium.org>
Subject: i2c: of-prober: Defer regulator_disable() on successful probe in simple helper

This commit delays dropping the regulator enable refcount for 100 ms when the
cleanup function is called. This gives asynchronous drivers time to probe
and increase the refcount themselves, avoiding the need to wait for hardware
re-initialization if the regulator was disabled just prior to probing.

> diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c
> index f9f3c0ef93ff5..68c929b16b066 100644
> --- a/drivers/i2c/i2c-core-of-prober.c
> +++ b/drivers/i2c/i2c-core-of-prober.c
[ ... ]
> @@ -235,11 +235,23 @@ static int i2c_of_probe_simple_enable_regulator(struct device *dev, struct i2c_o
>  	return 0;
>  }
>  
> -static void i2c_of_probe_simple_disable_regulator(struct device *dev, struct i2c_of_probe_simple_ctx *ctx)
> +static void i2c_of_probe_simple_disable_regulator(struct device *dev,
> +						  struct i2c_of_probe_simple_ctx *ctx,
> +						  bool defer_disable)
>  {
>  	if (!ctx->supply)
>  		return;
>  
> +	/*
> +	 * Wait a bit of time for async drivers to probe and increase the
> +	 * regulator enable count. This allows the drivers to check and
> +	 * skip waiting for re-initialization.
> +	 */
> +	if (defer_disable) {
> +		dev_dbg(dev, "Deferring regulator disable\n");
> +		msleep(100);
> +	}
> +

[Severity: Medium]
Will this unconditionally penalize boot time by 100ms when no I2C component is
found?

If the prober finishes without finding any responding devices, it falls back
to calling the cleanup function. Since i2c_of_probe_simple_cleanup() hardcodes
defer_disable to true, this sleep is triggered even when there is no device
present to probe asynchronously.

[ ... ]
> @@ -401,7 +413,7 @@ void i2c_of_probe_simple_cleanup(struct device *dev, void *data)
>  	i2c_of_probe_simple_disable_gpio(dev, ctx);
>  	i2c_of_probe_simple_put_gpiod(ctx);
>  
> -	i2c_of_probe_simple_disable_regulator(dev, ctx);
> +	i2c_of_probe_simple_disable_regulator(dev, ctx, true);
>  	i2c_of_probe_simple_put_supply(ctx);
>  }

While reviewing this series, I also noticed regressions introduced in
patch 1 of this series.

commit 5fd97a59a5c2607c473a3bc31a58415b701f2335
Author: Chen-Yu Tsai <wenst@chromium.org>
Subject: regulator: core: Add "enable and wait" functions

This patch introduces a "last turned on" timestamp field to the regulator
device and adds "enable and wait" functions to the single and bulk regulator
consumer APIs. The timestamp is updated each time the regulator is actually
turned on, allowing drivers to shorten or skip post-power-on delays.

> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
> index 1797929dfe566..f70be7e38e599 100644
> --- a/drivers/regulator/core.c
> +++ b/drivers/regulator/core.c
[ ... ]
> @@ -5372,30 +5444,38 @@ static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
>  {
>  	struct regulator_bulk_data *bulk = data;
>  
> -	bulk->ret = regulator_enable(bulk->consumer);
> +	bulk->ret = regulator_enable_and_wait(bulk->consumer, ACCESS_PRIVATE(bulk, wait_us));
>  }
>  
>  /**
> - * regulator_bulk_enable - enable multiple regulator consumers
> + * regulator_bulk_enable_and_wait - enable multiple regulator consumers and
> + *				    wait for time passed after regulators are
> + *				    actually enabled
[ ... ]
> -int regulator_bulk_enable(int num_consumers,
> -			  struct regulator_bulk_data *consumers)
> +int regulator_bulk_enable_and_wait(int num_consumers,
> +				   struct regulator_bulk_data *consumers,
> +				   unsigned int wait_us)
>  {
>  	ASYNC_DOMAIN_EXCLUSIVE(async_domain);
>  	int i;
>  	int ret = 0;
>  
>  	for (i = 0; i < num_consumers; i++) {
> +		ACCESS_PRIVATE(consumers, wait_us) = wait_us;
>  		async_schedule_domain(regulator_bulk_enable_async,
>  				      &consumers[i], &async_domain);
>  	}

[Severity: High]
Does this code fail to set wait_us for all consumers except the first one?

Because consumers is an array pointer, ACCESS_PRIVATE(consumers, wait_us)
expands to consumers[0].wait_us, completely ignoring the loop index i.

This leaves consumers[1..N].wait_us uninitialized. When the async worker later
calls regulator_enable_and_wait(), it will read uninitialized memory. This can
cause long boot delays if the garbage value is large, or hardware
synchronization failures if it happens to be zero.

[ ... ]
> @@ -6225,6 +6305,13 @@ 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.
> +	 */
> +	if (!rdev->supply_name && !rdev->last_on && _regulator_is_enabled(rdev))
> +		rdev->last_on = ktime_get_boottime();
> +
>  	rdev_init_debugfs(rdev);

[Severity: High]
Can this cause data races or hardware state corruption?

This calls _regulator_is_enabled(rdev) locklessly, and potentially writes to
rdev->last_on, after device_add() has already exposed the device to consumers
and userspace sysfs.

If userspace reads sysfs state attributes concurrently, or a driver calls
regulator_enable(), it could invoke the driver's is_enabled hardware callback
without synchronization.

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on
  2026-08-25  4:16 ` [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on Chen-Yu Tsai
@ 2026-08-25  4:50   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-25  4:50 UTC (permalink / raw)
  To: Chen-Yu Tsai; +Cc: dmitry.torokhov, devicetree, robh, conor+dt, linux-input

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Removing `regulator-always-on` breaks trackpad detection on Hana boards because the I2C component prober configuration hardcodes a 0ms post-power-on delay.
--

--- Patch [6]: [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on ---
commit 6318d2b884a1dcd37dded2350e385637ee0e9028
Author: Chen-Yu Tsai <wenst@chromium.org>

arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on

Up until now, the MT8173 elm/hana device tree has set the dedicated
regulator supplying the trackpad as always-on. This commit removes the
regulator-always-on property and adds the correct post-power-on-delay-ms.

> diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
> index a0573bc359fb6..6b9f47f515c72 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi
> @@ -1093,7 +1093,6 @@ mt6397_vgp6_reg: ldo_vgp6 {
>  				regulator-min-microvolt = <3300000>;
>  				regulator-max-microvolt = <3300000>;
>  				regulator-enable-ramp-delay = <218>;
> -				regulator-always-on;

[Severity: High]
Does removing regulator-always-on here break trackpad detection on Hana boards?

When the I2C component prober runs at boot on Hana boards to determine which
trackpad is present, it uses the delay configured in its own array:

drivers/platform/chrome/chromeos_of_hw_prober.c:chromeos_i2c_probe_hana_trackpad
	/*
	 * ELAN trackpad needs 2 ms for H/W init and 100 ms for F/W init.
	 * Synaptics trackpad needs 100 ms.
	 * However, the regulator is set to "always-on", presumably to
	 * avoid this delay. The ELAN driver is also missing delays.
	 */
	.post_power_on_delay_ms = 0,

The prober passes this 0ms delay to regulator_enable_and_wait():

drivers/i2c/i2c-core-of-prober.c:i2c_of_probe_simple_enable_regulator() {
	...
	ret = regulator_enable_and_wait(ctx->supply,
					ctx->opts->post_power_on_delay_ms * USEC_PER_MSEC);
	...
}

Since 0ms is requested, it won't wait before performing the I2C read. Will this
cause the trackpad to fail probing since the regulator is no longer always-on
and the trackpad needs 100ms to boot up and respond? 

Does the prober's post_power_on_delay_ms need to be updated to 100 to match
this change?

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 1/7] regulator: core: Add "enable and wait" functions
  2026-08-25  4:16 ` [PATCH v5 1/7] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
  2026-08-25  4:47   ` sashiko-bot
@ 2026-08-25  7:44   ` Chen-Yu Tsai
  1 sibling, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2026-08-25  7:44 UTC (permalink / raw)
  To: Mark Brown, Matthias Brugger, AngeloGioacchino Del Regno,
	Dmitry Torokhov, Jiri Kosina, Andi Shyti
  Cc: Benson Leung, Tzung-Bi Shih, linux-mediatek, devicetree,
	linux-arm-kernel, chrome-platform, linux-input, linux-i2c,
	linux-kernel

On Tue, Aug 25, 2026 at 12:33 PM Chen-Yu Tsai <wenst@chromium.org> wrote:
>
> In device power sequencing and initialization use cases, it is common
> for the driver to enable the regulator and then wait for a certain
> period of time to pass before continuing.
>
> In cases where the regulator supply is always on, or has been turned on
> or left on by another consumer, the driver could shorten the delay or
> skip it altogether, provided that enough time has already passed since
> the regulator was _actually_ turned on.
>
> Tracking this requires support from the regulator core. Introduce a
> "last turned on" timestamp field to the regulator device, and "enable
> and wait" functions to the single and bulk regulator consumer APIs.
> The existing "enable without wait" functions are then converted to
> macros that expand to the new functions.
>
> The timestamp is updated each time the regulator is actually turned on.
> For regulators left on by hardware default or by firmware, the core will
> set the timestamp if it detects it was left on and its supply (and their
> supply, and so on) is on as well. This is unfortunately best effort
> only. The core can only assume a dangling regulator (one without a
> supply) has power. This also applies to the dummy regulator.
>
> Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
> ---
> Changes since v4:
> - Try to update last_on timestamp for regulators that were left on

Sashiko flaged a few issues with the changes:

- _regulator_is_enabled() called and subsequent timestamp update
  without lock
- bulk consumer wait_us incorrectly assigned
- _regulator_is_enabled() could return an error

Will fix and send a new version. I probably should get a local AI review
working first.


ChenYu

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-08-25  7:44 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25  4:16 [PATCH v5 0/7] arm64: mediatek: Chromebook trackpad supply fixes Chen-Yu Tsai
2026-08-25  4:16 ` [PATCH v5 1/7] regulator: core: Add "enable and wait" functions Chen-Yu Tsai
2026-08-25  4:47   ` sashiko-bot
2026-08-25  7:44   ` Chen-Yu Tsai
2026-08-25  4:16 ` [PATCH v5 2/7] Input: elan_i2c - Wait for initialization after enabling regulator supply Chen-Yu Tsai
2026-08-25  4:16 ` [PATCH v5 3/7] HID: i2c-hid-of: skip post-power-on delay if powered on sufficiently long Chen-Yu Tsai
2026-08-25  4:48   ` sashiko-bot
2026-08-25  4:16 ` [PATCH v5 4/7] i2c: of-prober: " Chen-Yu Tsai
2026-08-25  4:16 ` [PATCH v5 5/7] i2c: of-prober: Defer regulator_disable() on successful probe in simple helper Chen-Yu Tsai
2026-08-25  4:48   ` sashiko-bot
2026-08-25  4:16 ` [PATCH v5 6/7] arm64: dts: mediatek: mt8173-elm-hana: Unmark trackpad supply as always-on Chen-Yu Tsai
2026-08-25  4:50   ` sashiko-bot
2026-08-25  4:16 ` [PATCH v5 7/7] arm64: dts: mediatek: mt8192-asurada-spherion: Add Synaptics trackpad's supply Chen-Yu Tsai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox