[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251227-regulators-defer-v1-7-3104b22d84cb@linaro.org>
Date: Sat, 27 Dec 2025 12:17:51 +0000
From: André Draszik <andre.draszik@...aro.org>
To: Liam Girdwood <lgirdwood@...il.com>, Mark Brown <broonie@...nel.org>,
Javier Martinez Canillas <javier@....samsung.com>,
Jon Hunter <jonathanh@...dia.com>, Dmitry Baryshkov <lumag@...nel.org>,
Oleksij Rempel <o.rempel@...gutronix.de>
Cc: Peter Griffin <peter.griffin@...aro.org>,
Tudor Ambarus <tudor.ambarus@...aro.org>,
Will McVicker <willmcvicker@...gle.com>, Juan Yescas <jyescas@...gle.com>,
kernel-team@...roid.com, linux-kernel@...r.kernel.org,
André Draszik <andre.draszik@...aro.org>
Subject: [PATCH 7/8] regulator: core: reresolve unresolved supplies when
available
When a regulator A and its supply B are provided by different devices,
the driver implementing B might be last to probe (with A still
pending resolution of its supply B). While we try to resolve all
pending supplies for all regulators (including A) during
regulator_register() of B via regulator_register_resolve_supply(),
Supply resolution will still not work for A as the driver for B hasn't
finished binding to the PMIC device corresponding to B at that stage
yet. The regulator core explicitly only allows supplies from other
devices to be used once the relevant driver has fully bound, mainly to
avoid having to deal with cases where B itself might -EPROBE_DEFER.
In this case, A's supply will only be resolved as part of the core's
regulator_init_complete_work_function(), which currently is scheduled
to run after 30s. This was added as a work-around in
commit 3827b64dba27 ("regulator: core: Resolve supplies before
disabling unused regulators") to cover this situation.
There are two problems with that approach:
* it potentially runs long after all our consumers have probed
* an upcoming change will allow regulator_register() to complete
successfully even when required supplies (e.g. due to always-on or
boot-on) are missing at register time, deferring full configuration
of the regulator (and usability by consumers, i.e. usually consumer
probe) until the supply becomes available.
Resolving supplies in the late work func can therefore make it
impossible for consumers to probe at all, as the driver core will not
know to reprobe consumers when supplies have resolved.
We could schedule an earlier work to try to resolve supplies sooner,
but that'd be racy as consumers of A might try to probe before A's
supply gets fully resolved via this extra work.
Instead, add a very simple regulator bus and add a dummy device with a
corresponding driver to it for each regulator that is missing its
supply during regulator_register(). This way, the driver core will call
our bus' probe whenever a new (regulator) device was successfully
bound, allowing us to retry resolving the supply during (our bus) probe
and to bind this dummy device if successful. In turn this means the
driver core will see a newly bound device and retry probing of all
pending consumers, if any.
With that in place, we can avoid walking the full list of all known
regulators to try resolve missing supplies during regulator_register(),
as the driver core will invoke the bus probe for regulators that are
still pending their supplies. We can also drop the code trying to
resolve supplies one last time before unused regulators get disabled,
as all supplies should have resolved at that point in time, and if they
haven't then there's no point in trying again, as the outcome won't
change.
Note: We can not reuse the existing struct device created for each
rail, as a device can not be part of a class and a bus simultaneously.
Signed-off-by: André Draszik <andre.draszik@...aro.org>
---
drivers/regulator/core.c | 121 +++++++++++++++++++++++++++++++--------
include/linux/regulator/driver.h | 1 +
2 files changed, 98 insertions(+), 24 deletions(-)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 86dbee3ffda0b950619db8b52d6c6eab8be31a53..08e92b1ba2dc2ff9efdabaa16187a4a38cf66fb2 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -44,6 +44,8 @@ static LIST_HEAD(regulator_supply_alias_list);
static LIST_HEAD(regulator_coupler_list);
static bool has_full_constraints;
+static const struct bus_type regulator_bus;
+
static struct dentry *debugfs_root;
/*
@@ -5694,17 +5696,6 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
&rdev->bypass_count);
}
-static int regulator_register_resolve_supply(struct device *dev, void *data)
-{
- struct regulator_dev *rdev = dev_to_rdev(dev);
-
- if (regulator_resolve_supply(rdev))
- rdev_dbg(rdev, "unable to resolve supply '%s'\n",
- rdev->supply_name);
-
- return 0;
-}
-
int regulator_coupler_register(struct regulator_coupler *coupler)
{
mutex_lock(®ulator_list_mutex);
@@ -5923,6 +5914,7 @@ regulator_register(struct device *dev,
struct regulator_config *config = NULL;
static atomic_t regulator_no = ATOMIC_INIT(-1);
struct regulator_dev *rdev;
+ bool tried_supply_resolve = false;
bool dangling_cfg_gpiod = false;
bool dangling_of_gpiod = false;
int ret, i;
@@ -6093,6 +6085,7 @@ regulator_register(struct device *dev,
else
rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
ERR_PTR(ret));
+ tried_supply_resolve = true;
}
if (ret < 0)
goto wash;
@@ -6124,6 +6117,37 @@ regulator_register(struct device *dev,
if (ret != 0)
goto unset_supplies;
+ if (!tried_supply_resolve) {
+ /*
+ * As an optimisation, try to resolve our supply (if any) now to
+ * avoid adding the bus device. Errors are not fatal at this
+ * stage, we'll simply try again later.
+ */
+ ret = regulator_resolve_supply(rdev);
+ if (ret)
+ rdev_dbg(rdev,
+ "unable to resolve supply (ignoring): %pe\n",
+ ERR_PTR(ret));
+ }
+
+ /*
+ * If we have a supply but couldn't resolve it yet, register a device
+ * with our bus, so that the bus probe gets called whenever any new
+ * driver binds, allowing us to retry matching supplies and which then
+ * triggers (re)probe of consumers if successful.
+ */
+ if (rdev->supply_name && !rdev->supply) {
+ device_initialize(&rdev->bdev);
+ rdev->bdev.bus = ®ulator_bus;
+ rdev->bdev.parent = &rdev->dev;
+ device_set_pm_not_required(&rdev->dev);
+ dev_set_name(&rdev->bdev, "%s.bdev", dev_name(&rdev->dev));
+
+ ret = device_add(&rdev->bdev);
+ if (ret)
+ goto del_cdev_and_bdev;
+ }
+
rdev_init_debugfs(rdev);
/* try to resolve regulators coupling since a new one was registered */
@@ -6131,12 +6155,13 @@ regulator_register(struct device *dev,
regulator_resolve_coupling(rdev);
mutex_unlock(®ulator_list_mutex);
- /* try to resolve regulators supply since a new one was registered */
- class_for_each_device(®ulator_class, NULL, NULL,
- regulator_register_resolve_supply);
kfree(config);
return rdev;
+del_cdev_and_bdev:
+ if (rdev->bdev.bus == ®ulator_bus)
+ put_device(&rdev->bdev);
+ device_del(&rdev->dev);
unset_supplies:
mutex_lock(®ulator_list_mutex);
unset_regulator_supplies(rdev);
@@ -6189,6 +6214,9 @@ void regulator_unregister(struct regulator_dev *rdev)
unset_regulator_supplies(rdev);
list_del(&rdev->list);
regulator_ena_gpio_free(rdev);
+ if (rdev->bdev.bus == ®ulator_bus)
+ /* only if the device was added in the first place */
+ device_unregister(&rdev->bdev);
device_unregister(&rdev->dev);
mutex_unlock(®ulator_list_mutex);
@@ -6269,6 +6297,45 @@ const struct class regulator_class = {
.pm = ®ulator_pm_ops,
#endif
};
+
+#define bdev_to_rdev(__bdev) container_of_const(__bdev, struct regulator_dev, bdev)
+
+static int regulator_bus_match(struct device *bdev,
+ const struct device_driver *drv)
+{
+ /* Match always succeeds, we only have one driver */
+ return 1;
+}
+
+static int regulator_bus_probe(struct device *bdev)
+{
+ struct regulator_dev *rdev = bdev_to_rdev(bdev);
+ int ret;
+
+ ret = regulator_resolve_supply(rdev);
+ if (ret)
+ rdev_dbg(rdev,
+ "unable to resolve supply or constraints '%s': %pe\n",
+ rdev->supply_name, ERR_PTR(ret));
+ else
+ rdev_dbg(rdev, "resolved supply '%s'\n", rdev->supply_name);
+
+ return ret;
+}
+
+static const struct bus_type regulator_bus = {
+ .name = "regulator",
+ .match = regulator_bus_match,
+ .probe = regulator_bus_probe,
+};
+
+static struct device_driver regulator_bus_driver = {
+ .name = "regulator-bus-drv",
+ .bus = ®ulator_bus,
+ .suppress_bind_attrs = true,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+};
+
/**
* regulator_has_full_constraints - the system has fully specified constraints
*
@@ -6602,7 +6669,17 @@ static int __init regulator_init(void)
{
int ret;
+ ret = bus_register(®ulator_bus);
+ if (ret)
+ return ret;
+
ret = class_register(®ulator_class);
+ if (ret)
+ goto err_class;
+
+ ret = driver_register(®ulator_bus_driver);
+ if (ret)
+ goto err_driver;
debugfs_root = debugfs_create_dir("regulator", NULL);
if (IS_ERR(debugfs_root))
@@ -6619,6 +6696,12 @@ static int __init regulator_init(void)
regulator_coupler_register(&generic_regulator_coupler);
+ return 0;
+
+err_driver:
+ class_unregister(®ulator_class);
+err_class:
+ bus_unregister(®ulator_bus);
return ret;
}
@@ -6679,16 +6762,6 @@ __setup("regulator_ignore_unused", regulator_ignore_unused_setup);
static void regulator_init_complete_work_function(struct work_struct *work)
{
- /*
- * Regulators may had failed to resolve their input supplies
- * when were registered, either because the input supply was
- * not registered yet or because its parent device was not
- * bound yet. So attempt to resolve the input supplies for
- * pending regulators before trying to disable unused ones.
- */
- class_for_each_device(®ulator_class, NULL, NULL,
- regulator_register_resolve_supply);
-
/*
* For debugging purposes, it may be useful to prevent unused
* regulators from being disabled.
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 978cf593b6624228fe1fd9c2a3e186b53ef172f8..d38353f2b56f8bbab865d903ad0ec97ca0b5c834 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -635,6 +635,7 @@ struct regulator_dev {
int ref_cnt;
struct module *owner;
struct device dev;
+ struct device bdev;
struct regulation_constraints *constraints;
struct regulator *supply; /* for tree */
const char *supply_name;
--
2.52.0.351.gbe84eed79e-goog
Powered by blists - more mailing lists