The LTM8054 supports setting a fixed output current limit using a sense
resistor connected to a dedicated pin. This limit can then be lowered
dynamically by varying the voltage level of the CTL pin.
Support controlling the LTM8054's output current limit.
Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
---
drivers/regulator/Kconfig | 1 +
drivers/regulator/ltm8054-regulator.c | 113 +++++++++++++++++++++++++++++++++-
2 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c48b2af350974b3715a1ecf05dec656a92268294..e9ee6ed9fe3587c542223a6d6be78412e96797ee 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -579,6 +579,7 @@ config REGULATOR_LTC3676
config REGULATOR_LTM8054
tristate "LTM8054 Buck-Boost voltage regulator"
+ depends on IIO
help
This driver provides support for the Analog Devices LTM8054
Buck-Boost micromodule regulator. The LTM8054 has an adjustable
diff --git a/drivers/regulator/ltm8054-regulator.c b/drivers/regulator/ltm8054-regulator.c
index bc8cf98b5a3b5663481d148330de70a8165e5981..172ec32c5a9517c6fb38ded8095ffc8e1acf55f0 100644
--- a/drivers/regulator/ltm8054-regulator.c
+++ b/drivers/regulator/ltm8054-regulator.c
@@ -17,6 +17,8 @@
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
+#include <linux/iio/consumer.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -26,10 +28,25 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
+#include <linux/units.h>
+
/* The LTM8054 regulates its FB pin to 1.2V */
#define LTM8054_FB_uV 1200000
+/* Threshold voltage between the Vout and Iout pins which triggers current
+ * limiting.
+ */
+#define LTM8054_VOUT_IOUT_MAX_uV 58000
+
+#define LTM8054_MAX_CTL_uV 1200000
+#define LTM8054_MIN_CTL_uV 50000
+
struct ltm8054_priv {
+ struct iio_channel *ctl_dac;
+
+ int min_uA;
+ int max_uA;
+
struct regulator_desc rdesc;
};
@@ -43,18 +60,103 @@ static int ltm8054_scale(unsigned int uV, u32 r1, u32 r2)
return uV + tmp;
}
-static const struct regulator_ops ltm8054_regulator_ops = { };
+static int ltm8054_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA)
+{
+ struct ltm8054_priv *priv = rdev_get_drvdata(rdev);
+ u64 vdac_uV;
+
+ min_uA = clamp_t(int, min_uA, priv->min_uA, priv->max_uA);
+
+ /* adjusted current limit = Rsense current limit * CTL pin voltage / max CTL pin voltage */
+ vdac_uV = (u64)min_uA * LTM8054_MAX_CTL_uV;
+ do_div(vdac_uV, priv->max_uA);
+
+ dev_dbg(&rdev->dev,
+ "Setting current limit to %duA, CTL pin to %duV\n", min_uA, (int)vdac_uV);
+
+ /* Standard IIO voltage unit is mV, scale accordingly. */
+ return iio_write_channel_processed_scale(priv->ctl_dac, vdac_uV, 1000);
+}
+
+static int ltm8054_get_current_limit(struct regulator_dev *rdev)
+{
+ struct ltm8054_priv *priv = rdev_get_drvdata(rdev);
+ int ret, vdac_uv;
+ u64 uA;
+
+ ret = iio_read_channel_processed_scale(priv->ctl_dac, &vdac_uv, 1000);
+ if (ret < 0) {
+ dev_err(&rdev->dev, "failed to read CTL DAC voltage, err %d\n", ret);
+ return ret;
+ }
+
+ uA = (u64)vdac_uv * priv->max_uA;
+ do_div(uA, LTM8054_MAX_CTL_uV);
+
+ return uA;
+}
+
+static const struct regulator_ops ltm8054_regulator_ops = {
+ .set_current_limit = ltm8054_set_current_limit,
+ .get_current_limit = ltm8054_get_current_limit,
+};
+
+static int ltm8054_init_ctl_dac(struct platform_device *pdev, struct ltm8054_priv *priv)
+{
+ struct iio_channel *ctl_dac;
+ enum iio_chan_type type;
+ int ret;
+
+ ctl_dac = devm_iio_channel_get(&pdev->dev, "ctl");
+ if (IS_ERR(ctl_dac))
+ return PTR_ERR(ctl_dac);
+
+ ret = iio_get_channel_type(ctl_dac, &type);
+ if (ret)
+ return ret;
+
+ if (type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ priv->ctl_dac = ctl_dac;
+
+ return 0;
+}
static int ltm8054_of_parse(struct device *dev, struct ltm8054_priv *priv,
struct regulator_config *config)
{
struct device_node *np = dev->of_node;
+ u32 rsense;
u32 r[2];
+ u64 tmp;
int ret;
config->of_node = np;
- ret = device_property_read_u32_array(dev, "lltc,fb-voltage-divider", r, ARRAY_SIZE(r));
+ ret = device_property_read_u32(dev, "adi,iout-rsense-micro-ohms", &rsense);
+ if (ret)
+ return ret;
+
+ if (rsense == 0)
+ return -EINVAL;
+
+ /* The maximum output current limit is the one set by the Rsense resistor */
+ tmp = (u64)LTM8054_VOUT_IOUT_MAX_uV * MICRO;
+ do_div(tmp, rsense);
+ priv->max_uA = tmp;
+
+ /*
+ * Applying a voltage below LTM8054_MAX_CTL_uV on the CTL pin reduces
+ * the output current limit. If this level drops below
+ * LTM8054_MIN_CTL_uV the regulator stops switching.
+ */
+
+ tmp = (u64)priv->max_uA * LTM8054_MIN_CTL_uV;
+ do_div(tmp, LTM8054_MAX_CTL_uV);
+ priv->min_uA = tmp;
+
+ ret = device_property_read_u32_array(dev, "lltc,fb-voltage-divider", r, 2);
if (ret)
return ret;
@@ -62,6 +164,9 @@ static int ltm8054_of_parse(struct device *dev, struct ltm8054_priv *priv,
priv->rdesc.min_uV = priv->rdesc.fixed_uV;
priv->rdesc.n_voltages = 1;
+ dev_dbg(dev, "max_uA: %d min_uA: %d fixed_uV: %d\n",
+ priv->max_uA, priv->min_uA, priv->rdesc.fixed_uV);
+
config->init_data = of_get_regulator_init_data(dev,
np,
&priv->rdesc);
@@ -99,6 +204,10 @@ static int ltm8054_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "failed to parse device tree\n");
+ ret = ltm8054_init_ctl_dac(pdev, priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize CTL DAC\n");
+
rdev = devm_regulator_register(dev, &priv->rdesc, &config);
if (IS_ERR(rdev))
return dev_err_probe(dev, PTR_ERR(rdev), "failed to register regulator\n");
--
2.51.0
Hello everyone,
I've encountered a rather troublesome issue with this particular patch,
which has delayed version 3 of this series. I'd like to describe it here,
so that you can tell me if you have any suggestions for an upstreamable
solution.
The problem concerns the set_current_limit() and get_current_limit()
callbacks:
On Thursday, 25 September 2025 14:37:37 CEST Romain Gantois wrote:
...
> -static const struct regulator_ops ltm8054_regulator_ops = { };
> +static int ltm8054_set_current_limit(struct regulator_dev *rdev, int
> min_uA, int max_uA) +{
> + struct ltm8054_priv *priv = rdev_get_drvdata(rdev);
> + u64 vdac_uV;
> +
> + min_uA = clamp_t(int, min_uA, priv->min_uA, priv->max_uA);
> +
> + /* adjusted current limit = Rsense current limit * CTL pin voltage /
max
> CTL pin voltage */ + vdac_uV = (u64)min_uA * LTM8054_MAX_CTL_uV;
> + do_div(vdac_uV, priv->max_uA);
> +
> + dev_dbg(&rdev->dev,
> + "Setting current limit to %duA, CTL pin to %duV\n", min_uA,
> (int)vdac_uV); +
> + /* Standard IIO voltage unit is mV, scale accordingly. */
> + return iio_write_channel_processed_scale(priv->ctl_dac, vdac_uV,
1000);
> +}
> +
> +static int ltm8054_get_current_limit(struct regulator_dev *rdev)
> +{
> + struct ltm8054_priv *priv = rdev_get_drvdata(rdev);
> + int ret, vdac_uv;
> + u64 uA;
> +
> + ret = iio_read_channel_processed_scale(priv->ctl_dac, &vdac_uv, 1000);
> + if (ret < 0) {
> + dev_err(&rdev->dev, "failed to read CTL DAC voltage, err %d\n",
ret);
> + return ret;
> + }
> +
> + uA = (u64)vdac_uv * priv->max_uA;
> + do_div(uA, LTM8054_MAX_CTL_uV);
> +
> + return uA;
> +}
> +
> +static const struct regulator_ops ltm8054_regulator_ops = {
> + .set_current_limit = ltm8054_set_current_limit,
> + .get_current_limit = ltm8054_get_current_limit,
> +};
> +
...
I've encountered a lockdep splat while testing these callbacks. I've
included a summary of the splat at the end of this email [1].
After investigating, it seems like the issue lies with IIO callbacks in the
ad5592r driver being called with the LTM8054 regulator device lock held.
The ad5592r callbacks themselves call into the regulator core to enable the
DAC's regulators, which might try the LTM8054 lock again in the same
thread, causing a deadlock. This would only happen if the LTM8054 was
supplying voltage to the ad5592r.
There are two parts to this issue:
1. Making sure that the CTL IIO channel used by an LTM8054 device isn't
supplied by the LTM8054 itself (or a consumer of the LTM8054). Solving this
removes the risk of an actual deadlock.
2. Silencing the lockdep splat. The splat seems to be triggered by the IIO
driver taking the general regulator ww_mutex context, which means it will
still occur even if we've made sure that the IIO channel isn't a consumer
of the LTM8054 regulator.
For part 1., a potential solution would be to create a device link with the
LTM8054 device as a consumer and the CTL IIO channel as a supplier. IIUC
device links do not tolerate cycles, so this should ensure that the IIO
channel isn't a direct or indirect consumer of the LTM8054.
However, the LTM8054 driver cannot access the IIO device struct to create the
link, so adding a new IIO consumer API function could be necessary.
For part 2., I'm having more trouble finding a proper solution. One
potential fix would be to put the IIO channel reads/writes in a LTM8054
driver work item and have them run without the regulator lock held. This
would incidentally also solve part 1., however it would make the current
limit operations asynchronous, and it seems like a lot of unnecessary
complexity.
Please tell me if you have any suggestions for solving this, I'll keep
searching on my side in the meantime.
Thanks,
--
Romain Gantois, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
[1] lockdep splat summary
```
WARNING: possible circular locking dependency detected
6.17.0-rc6+ #9 Not tainted
------------------------------------------------------
kworker/u17:0/34 is trying to acquire lock:
(&iio_dev_opaque->info_exist_lock){+.+.}-{4:4}, at:
iio_read_channel_processed_scale+0x40/0x120
but task is already holding lock:
(regulator_ww_class_mutex){+.+.}-{4:4}, at: ww_mutex_trylock+0x184/0x3a0
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #2 (regulator_ww_class_mutex){+.+.}-{4:4}:
lock_acquire+0xf0/0x2c0
regulator_lock_dependent+0x120/0x270
regulator_enable+0x38/0xd0
ad5592r_probe+0xcc/0x630
ad5593r_i2c_probe+0x58/0x80
...
ret_from_fork+0x10/0x20
-> #1 (regulator_ww_class_acquire){+.+.}-{0:0}:
reacquire_held_locks+0xd4/0x1c0
lock_release+0x148/0x2c0
__mutex_unlock_slowpath+0x3c/0x2f0
mutex_unlock+0x1c/0x30
regulator_lock_dependent+0x1d4/0x270
regulator_get_voltage+0x34/0xd0
ad5592r_read_raw+0x154/0x2f0
iio_channel_read.isra.0+0xac/0xd0
iio_write_channel_processed_scale+0x64/0x1e0
ltm8054_set_current_limit+0x70/0xd0
...
ret_from_fork+0x10/0x20
-> #0 (&iio_dev_opaque->info_exist_lock){+.+.}-{4:4}:
check_prev_add+0x104/0xc60
__lock_acquire+0x12a4/0x15c0
lock_acquire+0xf0/0x2c0
__mutex_lock+0x90/0xc80
mutex_lock_nested+0x28/0x40
iio_read_channel_processed_scale+0x40/0x120
ltm8054_get_current_limit+0x34/0xa0
kthread+0x11c/0x1f0
...
ret_from_fork+0x10/0x20
other info that might help us debug this:
Chain exists of:
&iio_dev_opaque->info_exist_lock --> regulator_ww_class_acquire -->
regulator_ww_class_mutex
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(regulator_ww_class_mutex);
lock(regulator_ww_class_acquire);
lock(regulator_ww_class_mutex);
lock(&iio_dev_opaque->info_exist_lock);
*** DEADLOCK ***
```
On Wed, Oct 22, 2025 at 11:06 AM Romain Gantois <romain.gantois@bootlin.com> wrote: ... > I've encountered a lockdep splat while testing these callbacks. I've > included a summary of the splat at the end of this email [1]. > > After investigating, it seems like the issue lies with IIO callbacks in the > ad5592r driver being called with the LTM8054 regulator device lock held. > > The ad5592r callbacks themselves call into the regulator core to enable the > DAC's regulators, which might try the LTM8054 lock again in the same > thread, causing a deadlock. This would only happen if the LTM8054 was > supplying voltage to the ad5592r. > > There are two parts to this issue: > > 1. Making sure that the CTL IIO channel used by an LTM8054 device isn't > supplied by the LTM8054 itself (or a consumer of the LTM8054). Solving this > removes the risk of an actual deadlock. > > 2. Silencing the lockdep splat. The splat seems to be triggered by the IIO > driver taking the general regulator ww_mutex context, which means it will > still occur even if we've made sure that the IIO channel isn't a consumer > of the LTM8054 regulator. > > For part 1., a potential solution would be to create a device link with the > LTM8054 device as a consumer and the CTL IIO channel as a supplier. IIUC > device links do not tolerate cycles, so this should ensure that the IIO > channel isn't a direct or indirect consumer of the LTM8054. > > However, the LTM8054 driver cannot access the IIO device struct to create the > link, so adding a new IIO consumer API function could be necessary. > > For part 2., I'm having more trouble finding a proper solution. One > potential fix would be to put the IIO channel reads/writes in a LTM8054 > driver work item and have them run without the regulator lock held. This > would incidentally also solve part 1., however it would make the current > limit operations asynchronous, and it seems like a lot of unnecessary > complexity. Interesting that locking a single regulator, there is no context and hence the lock class is global. Hence whoever calls a regulator will have the same lockdep splat, even when false positive. Basically the solution for those cases (and I don't know if yours / this one falls into the category) is to enable context for the single regulator locking and set up a lockdep class (so the regulator core should call lockdep_set_class() at mutex initialisation). > Please tell me if you have any suggestions for solving this, I'll keep > searching on my side in the meantime. -- With Best Regards, Andy Shevchenko
© 2016 - 2026 Red Hat, Inc.