Add a stub driver for the Linear Technology LTM8054 Buck-Boost voltage
regulator. This version only supports enabling/disabling the regulator via
a GPIO, and reporting the output voltage level from the resistor divider
values given in the device tree.
Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
---
MAINTAINERS | 1 +
drivers/regulator/Kconfig | 8 +++
drivers/regulator/Makefile | 1 +
drivers/regulator/ltm8054-regulator.c | 120 ++++++++++++++++++++++++++++++++++
4 files changed, 130 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 7160179e6bf9d45a241582c1b6df8c0ebf6c3641..4bc1a0e4c087060295a927da02f56c332269035f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14521,6 +14521,7 @@ LTM8054 REGULATOR DRIVER
M: Romain Gantois <romain.gantois@bootlin.com>
S: Maintained
F: Documentation/devicetree/bindings/regulator/lltc,ltm8054.yaml
+F: drivers/regulator/ltm8054-regulator.c
LTP (Linux Test Project)
M: Andrea Cervesato <andrea.cervesato@suse.com>
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index eaa6df1c9f806652a21942bcb48084ba63f942d9..15fb71193b67d0b2daa631b69778dde9323aedd2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -577,6 +577,14 @@ config REGULATOR_LTC3676
This enables support for the LTC3676
8-output regulators controlled via I2C.
+config REGULATOR_LTM8054
+ tristate "LTM8054 Buck-Boost voltage regulator"
+ help
+ This driver provides support for the Linear Technology LTM8054
+ Buck-Boost micromodule regulator. The LTM8054 has an adjustable
+ output current limitation and a feedback pin for setting the
+ output voltage level.
+
config REGULATOR_MAX14577
tristate "Maxim 14577/77836 regulator"
depends on MFD_MAX14577
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index be98b29d6675d8be1ca984c2d137bdfc4ba2de87..0e61ef826c08f64ea638d19bf10e69abf1526aa7 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
+obj-$(CONFIG_REGULATOR_LTM8054) += ltm8054-regulator.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_MAX5970) += max5970-regulator.o
diff --git a/drivers/regulator/ltm8054-regulator.c b/drivers/regulator/ltm8054-regulator.c
new file mode 100644
index 0000000000000000000000000000000000000000..e41bd95da55fb87912e2cdf70bae231133c25745
--- /dev/null
+++ b/drivers/regulator/ltm8054-regulator.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Linear Technology LTM8054 Buck-Boost regulator driver
+ *
+ * Copyright (C) 2025 Bootlin
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+
+/* The LTM8054 regulates its FB pin to 1.2V */
+#define LTM8054_FB_V 1200000
+
+struct ltm8054_priv {
+ struct regulator_desc rdesc;
+};
+
+static int ltm8054_scale(unsigned int uV, u32 r1, u32 r2)
+{
+ u64 tmp;
+
+ tmp = (u64)uV * r1;
+ do_div(tmp, r2);
+
+ return uV + (unsigned int)tmp;
+}
+
+static const struct regulator_ops ltm8054_regulator_ops = {
+};
+
+static int ltm8054_of_parse(struct device *dev, struct ltm8054_priv *priv,
+ struct regulator_config *config)
+{
+ struct device_node *np = dev->of_node;
+ u32 r[2];
+ int ret;
+
+ config->of_node = np;
+
+ ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider", r, 2);
+ if (ret) {
+ dev_err(dev, "Failed to parse voltage divider\n");
+ return ret;
+ }
+
+ priv->rdesc.fixed_uV = ltm8054_scale(LTM8054_FB_V, r[0], r[1]);
+ priv->rdesc.min_uV = priv->rdesc.fixed_uV;
+ priv->rdesc.n_voltages = 1;
+
+ config->init_data = of_get_regulator_init_data(dev,
+ np,
+ &priv->rdesc);
+ if (!config->init_data) {
+ dev_err(dev, "failed to parse init data\n");
+ return -EINVAL;
+ }
+
+ config->ena_gpiod = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(config->ena_gpiod)) {
+ dev_err(dev, "unable to acquire enable gpio\n");
+ return PTR_ERR(config->ena_gpiod);
+ }
+
+ return 0;
+}
+
+static int ltm8054_probe(struct platform_device *pdev)
+{
+ struct regulator_config config = { 0 };
+ struct regulator_dev *rdev;
+ struct ltm8054_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->rdesc.name = "ltm8054-regulator",
+ priv->rdesc.ops = <m8054_regulator_ops,
+ priv->rdesc.type = REGULATOR_VOLTAGE,
+ priv->rdesc.owner = THIS_MODULE,
+
+ config.dev = &pdev->dev;
+ config.driver_data = priv;
+
+ ret = ltm8054_of_parse(&pdev->dev, priv, &config);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to parse device tree\n");
+
+ rdev = devm_regulator_register(&pdev->dev, &priv->rdesc, &config);
+ if (IS_ERR(rdev))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rdev), "failed to register regulator\n");
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused ltm8054_of_match[] = {
+ { .compatible = "lltc,ltm8054", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltm8054_of_match);
+
+static struct platform_driver ltm8054_driver = {
+ .probe = ltm8054_probe,
+ .driver = {
+ .name = "ltm8054",
+ .of_match_table = of_match_ptr(ltm8054_of_match),
+ },
+};
+
+module_platform_driver(ltm8054_driver);
+
+MODULE_DESCRIPTION("LTM8054 regulator driver");
+MODULE_AUTHOR("Romain Gantois <romain.gantois@bootlin.com>");
+MODULE_LICENSE("GPL");
--
2.51.0
On Tue, Sep 16, 2025 at 12:24:08PM +0200, Romain Gantois wrote: > Add a stub driver for the Linear Technology LTM8054 Buck-Boost voltage > regulator. This version only supports enabling/disabling the regulator via > a GPIO, and reporting the output voltage level from the resistor divider > values given in the device tree. ... > +#include <linux/module.h> > +#include <linux/of.h> I think we have already something agnostic in regulator API to get a regulator from a firmware node (rather than from specific OF/etc one). > +#include <linux/mod_devicetable.h> > +#include <linux/regulator/driver.h> > +#include <linux/regulator/of_regulator.h> > +#include <linux/platform_device.h> > +#include <linux/gpio/consumer.h> Can you keep it ordered? This way it's easy to maintain and avoid potential duplication (note, there are also many headers are missing here, but Mark usually not insisting in following IWYU principle [1]) ... > +/* The LTM8054 regulates its FB pin to 1.2V */ > +#define LTM8054_FB_V 1200000 It's actually _mV #define LTM8054_FB_mV 1200000 ... > +static int ltm8054_scale(unsigned int uV, u32 r1, u32 r2) > +{ > + u64 tmp; > + > + tmp = (u64)uV * r1; > + do_div(tmp, r2); > + > + return uV + (unsigned int)tmp; Why one needs a casting here? > +} ... > +static const struct regulator_ops ltm8054_regulator_ops = { > +}; Why it can be simply as static const struct regulator_ops ltm8054_regulator_ops; ... > +static int ltm8054_of_parse(struct device *dev, struct ltm8054_priv *priv, > + struct regulator_config *config) > +{ > + struct device_node *np = dev->of_node; > + u32 r[2]; > + int ret; > + > + config->of_node = np; > + > + ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider", r, 2); device_property_read_u32_array() ? ARRAY_SIZE() instead of 2 > + if (ret) { > + dev_err(dev, "Failed to parse voltage divider\n"); > + return ret; > + } > + > + priv->rdesc.fixed_uV = ltm8054_scale(LTM8054_FB_V, r[0], r[1]); > + priv->rdesc.min_uV = priv->rdesc.fixed_uV; > + priv->rdesc.n_voltages = 1; > + > + config->init_data = of_get_regulator_init_data(dev, > + np, > + &priv->rdesc); > + if (!config->init_data) { > + dev_err(dev, "failed to parse init data\n"); > + return -EINVAL; > + } > + > + config->ena_gpiod = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); > + if (IS_ERR(config->ena_gpiod)) { > + dev_err(dev, "unable to acquire enable gpio\n"); > + return PTR_ERR(config->ena_gpiod); All messages in cases of EPROBE_DEFER are problematic (for sure with GPIO), as it may well flood the logs. Solution: Use return dev_err_probe(...); pattern instead, > + } > + > + return 0; > +} ... > +static int ltm8054_probe(struct platform_device *pdev) > +{ > + struct regulator_config config = { 0 }; '0' is not required. The { } will have the same effect. > + struct regulator_dev *rdev; > + struct ltm8054_priv *priv; > + int ret; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->rdesc.name = "ltm8054-regulator", > + priv->rdesc.ops = <m8054_regulator_ops, > + priv->rdesc.type = REGULATOR_VOLTAGE, > + priv->rdesc.owner = THIS_MODULE, > + > + config.dev = &pdev->dev; > + config.driver_data = priv; > + > + ret = ltm8054_of_parse(&pdev->dev, priv, &config); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, "failed to parse device tree\n"); > + > + rdev = devm_regulator_register(&pdev->dev, &priv->rdesc, &config); > + if (IS_ERR(rdev)) > + return dev_err_probe(&pdev->dev, PTR_ERR(rdev), "failed to register regulator\n"); Using struct device *dev = &pdev->dev; at the top will allow to make a few lines shorter. > + return 0; > +} > + > +static const struct of_device_id __maybe_unused ltm8054_of_match[] = { > + { .compatible = "lltc,ltm8054", }, Inner comma is not required. > + {}, Drop the trailing comma here as it's a terminator entry. The absence of it will give a hint to the compiler as well. > +}; ... > +static struct platform_driver ltm8054_driver = { > + .probe = ltm8054_probe, > + .driver = { > + .name = "ltm8054", > + .of_match_table = of_match_ptr(ltm8054_of_match), Please, do not use of_match_ptr() and/or ACPI_PTR() in a new code. > + }, > +}; > + Unneeded blank line. > +module_platform_driver(ltm8054_driver); -- With Best Regards, Andy Shevchenko
Hello Andy, On Tuesday, 16 September 2025 15:12:37 CEST Andy Shevchenko wrote: > On Tue, Sep 16, 2025 at 12:24:08PM +0200, Romain Gantois wrote: > > Add a stub driver for the Linear Technology LTM8054 Buck-Boost voltage > > regulator. This version only supports enabling/disabling the regulator via > > a GPIO, and reporting the output voltage level from the resistor divider > > values given in the device tree. > > ... > > > +#include <linux/module.h> > > +#include <linux/of.h> > > I think we have already something agnostic in regulator API to get a > regulator from a firmware node (rather than from specific OF/etc one). > IIRC the "of_match" regulator descriptor property can be used for this, I'll have a second look and see if I can use that instead. > > +#include <linux/mod_devicetable.h> > > +#include <linux/regulator/driver.h> > > +#include <linux/regulator/of_regulator.h> > > +#include <linux/platform_device.h> > > +#include <linux/gpio/consumer.h> > > Can you keep it ordered? This way it's easy to maintain and avoid potential > duplication (note, there are also many headers are missing here, but Mark > usually not insisting in following IWYU principle [1]) > > ... I usually also try to follow IWYU for header inclusions so I'll look for those that I missed. > > > +/* The LTM8054 regulates its FB pin to 1.2V */ > > +#define LTM8054_FB_V 1200000 > > It's actually _mV > > #define LTM8054_FB_mV 1200000 > > ... > > > +static int ltm8054_scale(unsigned int uV, u32 r1, u32 r2) > > +{ > > + u64 tmp; > > + > > + tmp = (u64)uV * r1; > > + do_div(tmp, r2); > > + > > + return uV + (unsigned int)tmp; > > Why one needs a casting here? > Both of those are unsigned so the cast here is indeed unnecessary. > > +} > > ... > > > +static const struct regulator_ops ltm8054_regulator_ops = { > > +}; > > Why it can be simply as > > static const struct regulator_ops ltm8054_regulator_ops; > Yeah, this was mostly to have a clean diff on patch 4/4, I'll see if I can drop this struct and introduce it in patch 4/4. I wouldn't want to use it uninitialized though. > ... > > > +static int ltm8054_of_parse(struct device *dev, struct ltm8054_priv > > *priv, > > + struct regulator_config *config) > > +{ > > + struct device_node *np = dev->of_node; > > + u32 r[2]; > > + int ret; > > + > > + config->of_node = np; > > + > > + ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider", r, 2); > > device_property_read_u32_array() ? > > ARRAY_SIZE() instead of 2 > Yes, indeed. > > + if (ret) { > > + dev_err(dev, "Failed to parse voltage divider\n"); > > + return ret; > > + } > > + > > + priv->rdesc.fixed_uV = ltm8054_scale(LTM8054_FB_V, r[0], r[1]); > > + priv->rdesc.min_uV = priv->rdesc.fixed_uV; > > + priv->rdesc.n_voltages = 1; > > + > > + config->init_data = of_get_regulator_init_data(dev, > > + np, > > + &priv->rdesc); > > + if (!config->init_data) { > > + dev_err(dev, "failed to parse init data\n"); > > + return -EINVAL; > > + } > > + > > + config->ena_gpiod = devm_gpiod_get_optional(dev, "enable", > > GPIOD_OUT_LOW); + if (IS_ERR(config->ena_gpiod)) { > > + dev_err(dev, "unable to acquire enable gpio\n"); > > + return PTR_ERR(config->ena_gpiod); > > All messages in cases of EPROBE_DEFER are problematic (for sure with GPIO), > as it may well flood the logs. > Solution: Use > > return dev_err_probe(...); > > pattern instead, > Yes, I used that in the caller function but it doesn't solve the flooding issue, so I'll move dev_err_probe() to this function instead. > > + } > > + > > + return 0; > > +} > > ... > > > +static int ltm8054_probe(struct platform_device *pdev) > > +{ > > + struct regulator_config config = { 0 }; > > '0' is not required. The { } will have the same effect. > > > + struct regulator_dev *rdev; > > + struct ltm8054_priv *priv; > > + int ret; > > + > > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > > + if (!priv) > > + return -ENOMEM; > > + > > + priv->rdesc.name = "ltm8054-regulator", > > + priv->rdesc.ops = <m8054_regulator_ops, > > + priv->rdesc.type = REGULATOR_VOLTAGE, > > + priv->rdesc.owner = THIS_MODULE, > > + > > + config.dev = &pdev->dev; > > + config.driver_data = priv; > > + > > + ret = ltm8054_of_parse(&pdev->dev, priv, &config); > > + if (ret) > > + return dev_err_probe(&pdev->dev, ret, "failed to parse device tree\n"); > > + > > + rdev = devm_regulator_register(&pdev->dev, &priv->rdesc, &config); > > + if (IS_ERR(rdev)) > > + return dev_err_probe(&pdev->dev, PTR_ERR(rdev), "failed to register > > regulator\n"); > Using > > struct device *dev = &pdev->dev; > > at the top will allow to make a few lines shorter. > > > + return 0; > > +} > > + > > +static const struct of_device_id __maybe_unused ltm8054_of_match[] = { > > + { .compatible = "lltc,ltm8054", }, > > Inner comma is not required. > > > + {}, > > Drop the trailing comma here as it's a terminator entry. The absence of it > will give a hint to the compiler as well. > > > +}; > > ... > > > +static struct platform_driver ltm8054_driver = { > > + .probe = ltm8054_probe, > > + .driver = { > > + .name = "ltm8054", > > + .of_match_table = of_match_ptr(ltm8054_of_match), > > Please, do not use of_match_ptr() and/or ACPI_PTR() in a new code. > > > + }, > > +}; > > > > + > > Unneeded blank line. > > > +module_platform_driver(ltm8054_driver); Thanks for the review, -- Romain Gantois, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Tuesday, 16 September 2025 16:17:56 CEST Romain Gantois wrote: > Hello Andy, > > On Tuesday, 16 September 2025 15:12:37 CEST Andy Shevchenko wrote: > > On Tue, Sep 16, 2025 at 12:24:08PM +0200, Romain Gantois wrote: > > > Add a stub driver for the Linear Technology LTM8054 Buck-Boost voltage > > > regulator. This version only supports enabling/disabling the regulator > > > via > > > a GPIO, and reporting the output voltage level from the resistor divider > > > values given in the device tree. > > > > ... > > > > > +#include <linux/module.h> > > > +#include <linux/of.h> > > > > I think we have already something agnostic in regulator API to get a > > regulator from a firmware node (rather than from specific OF/etc one). > > IIRC the "of_match" regulator descriptor property can be used for this, I'll > have a second look and see if I can use that instead. > Looks like I misread your comment sorry, the "of_match" property is pretty much irrelevant to using fwnode_* wrappers, and I didn't find any of those in the regulator subsystem. I'm missing a Kconfig dependency on "OF" though, I'll have to add that. Thanks, -- Romain Gantois, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Thu, Sep 18, 2025 at 11:31:50AM +0200, Romain Gantois wrote: > On Tuesday, 16 September 2025 16:17:56 CEST Romain Gantois wrote: > > On Tuesday, 16 September 2025 15:12:37 CEST Andy Shevchenko wrote: > > > On Tue, Sep 16, 2025 at 12:24:08PM +0200, Romain Gantois wrote: ... > > > > +#include <linux/of.h> > > > > > > I think we have already something agnostic in regulator API to get a > > > regulator from a firmware node (rather than from specific OF/etc one). > > > > IIRC the "of_match" regulator descriptor property can be used for this, I'll > > have a second look and see if I can use that instead. > > Looks like I misread your comment sorry, the "of_match" property is pretty > much irrelevant to using fwnode_* wrappers, and I didn't find any of those in > the regulator subsystem. I'm missing a Kconfig dependency on "OF" though, I'll > have to add that. Why do we need to add that dependency? Yes, probably it won't function, but then it will decrease test coverage at compile time. -- With Best Regards, Andy Shevchenko
On Thursday, 18 September 2025 21:17:59 CEST Andy Shevchenko wrote: > On Thu, Sep 18, 2025 at 11:31:50AM +0200, Romain Gantois wrote: > > On Tuesday, 16 September 2025 16:17:56 CEST Romain Gantois wrote: > > > On Tuesday, 16 September 2025 15:12:37 CEST Andy Shevchenko wrote: > > > > On Tue, Sep 16, 2025 at 12:24:08PM +0200, Romain Gantois wrote: ... > > > > I think we have already something agnostic in regulator API to get a > > > > regulator from a firmware node (rather than from specific OF/etc one). > > > > > > IIRC the "of_match" regulator descriptor property can be used for this, > > > I'll have a second look and see if I can use that instead. > > > > Looks like I misread your comment sorry, the "of_match" property is pretty > > much irrelevant to using fwnode_* wrappers, and I didn't find any of those > > in the regulator subsystem. I'm missing a Kconfig dependency on "OF" > > though, I'll have to add that. > > Why do we need to add that dependency? Yes, probably it won't function, > but then it will decrease test coverage at compile time. Oh I didn't see it that way, in that case I'll just go the LTC3676 way and leave the OF dependency out. Thanks, -- Romain Gantois, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Tue, Sep 16, 2025 at 04:12:37PM +0300, Andy Shevchenko wrote: > On Tue, Sep 16, 2025 at 12:24:08PM +0200, Romain Gantois wrote: ... > there are also many headers are missing here, but Mark > usually not insisting in following IWYU principle [1]) Forgot to put a link: https://include-what-you-use.org/ -- With Best Regards, Andy Shevchenko
© 2016 - 2025 Red Hat, Inc.