mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] regulator: core: flush deferred disables before suspend
@ 2026-10-27  6:20 Jiale Yao
  0 siblings, 0 replies; 2+ messages in thread
From: Jiale Yao @ 2026-10-27  6:20 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, linux-kernel; +Cc: stable

regulator_disable_deferred() can leave hardware access pending while the
system enters suspend. Allowing that work to run during device suspend can
access a regulator after its backing bus has been suspended.

Moving deferred disables to a freezable workqueue is not sufficient.
DAPM uses deferred disables while suspending audio devices, and work queued
after the workqueue is frozen would leave the supply enabled until resume.

Mark the regulator core as suspending in the PM prepare phase. Flush each
regulator's pending disable work before device suspend starts. Process new
deferred-disable requests synchronously until PM completes. Synchronize
the transition with the regulator lock so a request racing with prepare
is either flushed or handled synchronously.

Fixes: da07ecd93b19 ("regulator: Implement deferred disable support")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
 drivers/regulator/core.c | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 14422cfe80e2..160c588fbdd3 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -44,6 +44,7 @@ static LIST_HEAD(regulator_ena_gpio_list);
 static LIST_HEAD(regulator_supply_alias_list);
 static LIST_HEAD(regulator_coupler_list);
 static bool has_full_constraints;
+static bool regulator_suspending;
 
 static const struct bus_type regulator_bus;
 
@@ -3463,10 +3464,15 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
 {
 	struct regulator_dev *rdev = regulator->rdev;
 
-	if (!ms)
+	if (!ms || READ_ONCE(regulator_suspending))
 		return regulator_disable(regulator);
 
 	regulator_lock(rdev);
+	if (unlikely(READ_ONCE(regulator_suspending))) {
+		regulator_unlock(rdev);
+		return regulator_disable(regulator);
+	}
+
 	regulator->deferred_disables++;
 	mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
 			 msecs_to_jiffies(ms));
@@ -6327,6 +6333,24 @@ void regulator_unregister(struct regulator_dev *rdev)
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
 #ifdef CONFIG_SUSPEND
+static int regulator_prepare(struct device *dev)
+{
+	struct regulator_dev *rdev = dev_to_rdev(dev);
+
+	regulator_lock(rdev);
+	WRITE_ONCE(regulator_suspending, true);
+	regulator_unlock(rdev);
+
+	flush_delayed_work(&rdev->disable_work);
+
+	return 0;
+}
+
+static void regulator_complete(struct device *dev)
+{
+	WRITE_ONCE(regulator_suspending, false);
+}
+
 /**
  * regulator_suspend - prepare regulators for system wide suspend
  * @dev: ``&struct device`` pointer that is passed to _regulator_suspend()
@@ -6380,6 +6404,8 @@ static int regulator_resume(struct device *dev)
 }
 #else /* !CONFIG_SUSPEND */
 
+#define regulator_prepare	NULL
+#define regulator_complete	NULL
 #define regulator_suspend	NULL
 #define regulator_resume	NULL
 
@@ -6387,6 +6413,8 @@ static int regulator_resume(struct device *dev)
 
 #ifdef CONFIG_PM
 static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
+	.prepare	= regulator_prepare,
+	.complete	= regulator_complete,
 	.suspend	= regulator_suspend,
 	.resume		= regulator_resume,
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 2+ messages in thread
* [PATCH v2] regulator: core: flush deferred disables before suspend
@ 2026-10-27  6:26 Jiale Yao
  0 siblings, 0 replies; 2+ messages in thread
From: Jiale Yao @ 2026-10-27  6:26 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, linux-kernel; +Cc: stable

regulator_disable_deferred() can leave hardware access pending while the
system enters suspend. Allowing that work to run during device suspend can
access a regulator after its backing bus has been suspended.

Moving deferred disables to a freezable workqueue is not sufficient.
DAPM uses deferred disables while suspending audio devices, and work queued
after the workqueue is frozen would leave the supply enabled until resume.

Mark the regulator core as suspending in the PM prepare phase. Flush each
regulator's pending disable work before device suspend starts. Process new
deferred-disable requests synchronously until PM completes. Synchronize
the transition with the regulator lock so a request racing with prepare
is either flushed or handled synchronously.

Fixes: da07ecd93b19 ("regulator: Implement deferred disable support")
Cc: stable@vger.kernel.org
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
Changes in v2:
- Flush pending disables during PM prepare instead of freezing the work.
- Process new deferred disables synchronously until PM completes.
- Synchronize requests racing with PM prepare using the regulator lock.

Link to v1: https://lore.kernel.org/all/20261016100522.2582557-2-yaojiale02@163.com/
---
 drivers/regulator/core.c | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 14422cfe80e2..160c588fbdd3 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -44,6 +44,7 @@ static LIST_HEAD(regulator_ena_gpio_list);
 static LIST_HEAD(regulator_supply_alias_list);
 static LIST_HEAD(regulator_coupler_list);
 static bool has_full_constraints;
+static bool regulator_suspending;
 
 static const struct bus_type regulator_bus;
 
@@ -3463,10 +3464,15 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
 {
 	struct regulator_dev *rdev = regulator->rdev;
 
-	if (!ms)
+	if (!ms || READ_ONCE(regulator_suspending))
 		return regulator_disable(regulator);
 
 	regulator_lock(rdev);
+	if (unlikely(READ_ONCE(regulator_suspending))) {
+		regulator_unlock(rdev);
+		return regulator_disable(regulator);
+	}
+
 	regulator->deferred_disables++;
 	mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
 			 msecs_to_jiffies(ms));
@@ -6327,6 +6333,24 @@ void regulator_unregister(struct regulator_dev *rdev)
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
 #ifdef CONFIG_SUSPEND
+static int regulator_prepare(struct device *dev)
+{
+	struct regulator_dev *rdev = dev_to_rdev(dev);
+
+	regulator_lock(rdev);
+	WRITE_ONCE(regulator_suspending, true);
+	regulator_unlock(rdev);
+
+	flush_delayed_work(&rdev->disable_work);
+
+	return 0;
+}
+
+static void regulator_complete(struct device *dev)
+{
+	WRITE_ONCE(regulator_suspending, false);
+}
+
 /**
  * regulator_suspend - prepare regulators for system wide suspend
  * @dev: ``&struct device`` pointer that is passed to _regulator_suspend()
@@ -6380,6 +6404,8 @@ static int regulator_resume(struct device *dev)
 }
 #else /* !CONFIG_SUSPEND */
 
+#define regulator_prepare	NULL
+#define regulator_complete	NULL
 #define regulator_suspend	NULL
 #define regulator_resume	NULL
 
@@ -6387,6 +6413,8 @@ static int regulator_resume(struct device *dev)
 
 #ifdef CONFIG_PM
 static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
+	.prepare	= regulator_prepare,
+	.complete	= regulator_complete,
 	.suspend	= regulator_suspend,
 	.resume		= regulator_resume,
 };
-- 
2.34.1


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

end of thread, other threads:[~2026-10-27  6:26 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27  6:20 [PATCH v2] regulator: core: flush deferred disables before suspend Jiale Yao
2026-10-27  6:26 Jiale Yao

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

all inboxes | Powered by JetHome®