summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/regulator/mps,mpq4210.yaml69
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/regulator/Kconfig11
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/mpq4210.c243
5 files changed, 330 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/regulator/mps,mpq4210.yaml b/Documentation/devicetree/bindings/regulator/mps,mpq4210.yaml
new file mode 100644
index 000000000000..e9e7a87e7533
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mps,mpq4210.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/mps,mpq4210.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Monolithic Power Systems MPQ4210 buck-boost regulator
+
+maintainers:
+ - Tapio Reijonen <tapio.reijonen@vaisala.com>
+
+description:
+ The MPQ4210 is a synchronous four-switch buck-boost controller with an I2C
+ interface. The output voltage is programmed through an 11-bit feedback
+ reference DAC and scaled by an external feedback resistor divider.
+
+ The controller ramps the feedback reference at 38, 50, 75 or 150 mV/ms.
+ The divider scales the ramp rate along with the voltage, so the output
+ ramp rates that regulator-ramp-delay can select are those values
+ multiplied by (R1 + R2) / R2.
+
+allOf:
+ - $ref: regulator.yaml#
+
+properties:
+ compatible:
+ const: mps,mpq4210
+
+ reg:
+ maxItems: 1
+
+ enable-gpios:
+ description: GPIO connected to the EN pin. If absent, EN is assumed to
+ be permanently asserted.
+ maxItems: 1
+
+ mps,fb-voltage-divider:
+ description: An array of two integers containing the resistor values R1
+ and R2 of the output feedback voltage divider in ohms.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ maxItems: 2
+
+required:
+ - compatible
+ - reg
+ - mps,fb-voltage-divider
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ regulator@64 {
+ compatible = "mps,mpq4210";
+ reg = <0x64>;
+ regulator-name = "vout";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <20000000>;
+ enable-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
+ /* R1 = 82 kOhm, R2 = 9.1 kOhm */
+ mps,fb-voltage-divider = <82000 9100>;
+ /* 50 mV/ms reference ramp x (82000 + 9100) / 9100 */
+ regulator-ramp-delay = <500>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..78d28c103eb1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18372,6 +18372,12 @@ F: scripts/module*
F: tools/testing/selftests/kmod/
F: tools/testing/selftests/module/
+MONOLITHIC POWER SYSTEM MPQ4210 REGULATOR DRIVER
+M: Tapio Reijonen <tapio.reijonen@vaisala.com>
+S: Maintained
+F: Documentation/devicetree/bindings/regulator/mps,mpq4210.yaml
+F: drivers/regulator/mpq4210.c
+
MONOLITHIC POWER SYSTEM PMIC DRIVER
M: Saravanan Sekar <sravanhome@gmail.com>
S: Maintained
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c8940e2c7e04..4431c48dbfc2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -870,6 +870,17 @@ config REGULATOR_MP886X
help
This driver supports the MP8869 voltage regulator.
+config REGULATOR_MPQ4210
+ tristate "MPS MPQ4210 regulator driver"
+ depends on I2C && OF
+ select REGMAP_I2C
+ help
+ This driver supports the MPQ4210 synchronous buck-boost controller.
+ It supports get/set voltage, enable/disable and ramp rate selection
+ through the regulator interface.
+ Say M here if you want to include support for the regulator as a
+ module. The module will be named "mpq4210".
+
config REGULATOR_MPQ7920
tristate "Monolithic MPQ7920 PMIC"
depends on I2C && OF
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 5a764cec8df8..af69a0ee877d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o
obj-$(CONFIG_REGULATOR_MP5416) += mp5416.o
obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o
obj-$(CONFIG_REGULATOR_MP886X) += mp886x.o
+obj-$(CONFIG_REGULATOR_MPQ4210) += mpq4210.o
obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
obj-$(CONFIG_REGULATOR_MT6315) += mt6315-regulator.o
diff --git a/drivers/regulator/mpq4210.c b/drivers/regulator/mpq4210.c
new file mode 100644
index 000000000000..227136d82258
--- /dev/null
+++ b/drivers/regulator/mpq4210.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Monolithic Power Systems MPQ4210 buck-boost regulator
+ *
+ * Copyright (c) 2026 Vaisala Oyj
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/math.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define MPQ4210_REF_LSB 0x00
+#define MPQ4210_REF_LSB_MASK GENMASK(2, 0)
+#define MPQ4210_REF_LSB_BITS 3
+#define MPQ4210_REF_MSB 0x01
+#define MPQ4210_CONTROL1 0x02
+#define MPQ4210_CONTROL1_SR GENMASK(7, 6)
+#define MPQ4210_CONTROL1_RESERVED BIT(2)
+#define MPQ4210_CONTROL1_GO BIT(1)
+#define MPQ4210_CONTROL1_ENPWR BIT(0)
+#define MPQ4210_INT_MASK 0x06
+
+/*
+ * The feedback reference is an 11 bit value with a 1mV step. The datasheet
+ * specifies no reference below 0.3V, so those selectors are not offered.
+ */
+#define MPQ4210_REF_MIN 0x12c
+#define MPQ4210_REF_MAX 0x7ff
+#define MPQ4210_REF_STEP_UV 1000
+
+#define MPQ4210_ENPWR_DELAY_MS 200
+
+/* Feedback reference slew rate per MPQ4210_CONTROL1_SR value, in uV/us. */
+static const unsigned int mpq4210_ref_slew_rate[] = { 38, 50, 75, 150 };
+
+static const struct regmap_config mpq4210_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MPQ4210_INT_MASK,
+};
+
+/* Scale a feedback reference value by the output voltage divider ratio. */
+static unsigned int mpq4210_scale(unsigned int val, u32 r1, u32 r2)
+{
+ u64 tmp = (u64)val * (r1 + r2);
+
+ do_div(tmp, r2);
+
+ return tmp;
+}
+
+static int mpq4210_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
+{
+ int ret;
+
+ ret = regmap_write(rdev->regmap, MPQ4210_REF_LSB,
+ sel & MPQ4210_REF_LSB_MASK);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(rdev->regmap, MPQ4210_REF_MSB,
+ sel >> MPQ4210_REF_LSB_BITS);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(rdev->regmap, MPQ4210_CONTROL1,
+ MPQ4210_CONTROL1_GO);
+}
+
+static int mpq4210_get_voltage_sel(struct regulator_dev *rdev)
+{
+ unsigned int lsb, msb;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, MPQ4210_REF_MSB, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(rdev->regmap, MPQ4210_REF_LSB, &lsb);
+ if (ret)
+ return ret;
+
+ return (msb << MPQ4210_REF_LSB_BITS) | (lsb & MPQ4210_REF_LSB_MASK);
+}
+
+static int mpq4210_enable(struct regulator_dev *rdev)
+{
+ int ret;
+
+ ret = regmap_set_bits(rdev->regmap, MPQ4210_CONTROL1,
+ MPQ4210_CONTROL1_GO);
+ if (ret)
+ return ret;
+
+ /* The reference has to settle before power switching may start. */
+ msleep(MPQ4210_ENPWR_DELAY_MS);
+
+ return regmap_set_bits(rdev->regmap, MPQ4210_CONTROL1,
+ MPQ4210_CONTROL1_ENPWR);
+}
+
+static const struct regulator_ops mpq4210_regulator_ops = {
+ .set_voltage_sel = mpq4210_set_voltage_sel,
+ .get_voltage_sel = mpq4210_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ .enable = mpq4210_enable,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_desc mpq4210_regulator = {
+ .name = "mpq4210",
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &mpq4210_regulator_ops,
+ .n_voltages = MPQ4210_REF_MAX + 1,
+ .linear_min_sel = MPQ4210_REF_MIN,
+ .enable_reg = MPQ4210_CONTROL1,
+ .enable_mask = MPQ4210_CONTROL1_ENPWR,
+ .ramp_reg = MPQ4210_CONTROL1,
+ .ramp_mask = MPQ4210_CONTROL1_SR,
+ .n_ramp_values = ARRAY_SIZE(mpq4210_ref_slew_rate),
+};
+
+static int mpq4210_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regulator_config config = { };
+ struct regulator_desc *desc;
+ struct regulator_dev *rdev;
+ struct gpio_desc *enable;
+ struct regmap *regmap;
+ unsigned int *slew;
+ unsigned int i, val;
+ u32 r[2];
+ int ret;
+
+ ret = of_property_read_u32_array(dev->of_node, "mps,fb-voltage-divider",
+ r, ARRAY_SIZE(r));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read mps,fb-voltage-divider\n");
+
+ if (!r[1])
+ return dev_err_probe(dev, -EINVAL,
+ "feedback divider R2 must not be zero\n");
+
+ /* The controller does not answer on the bus while EN is deasserted. */
+ enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(enable))
+ return dev_err_probe(dev, PTR_ERR(enable),
+ "failed to get enable GPIO\n");
+
+ regmap = devm_regmap_init_i2c(client, &mpq4210_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to init regmap\n");
+
+ desc = devm_kmemdup(dev, &mpq4210_regulator, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->min_uV = mpq4210_scale(MPQ4210_REF_MIN * MPQ4210_REF_STEP_UV,
+ r[0], r[1]);
+ desc->uV_step = mpq4210_scale(MPQ4210_REF_STEP_UV, r[0], r[1]);
+
+ slew = devm_kcalloc(dev, ARRAY_SIZE(mpq4210_ref_slew_rate),
+ sizeof(*slew), GFP_KERNEL);
+ if (!slew)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(mpq4210_ref_slew_rate); i++)
+ slew[i] = mpq4210_scale(mpq4210_ref_slew_rate[i], r[0], r[1]);
+
+ desc->ramp_delay_table = slew;
+
+ ret = regmap_read(regmap, MPQ4210_CONTROL1, &val);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to read control 1\n");
+
+ /* The core overrides this if the board sets regulator-ramp-delay. */
+ desc->ramp_delay = slew[FIELD_GET(MPQ4210_CONTROL1_SR, val)];
+
+ /*
+ * Documented as reserved, but the datasheet requires it to be set
+ * before the controller starts up.
+ */
+ ret = regmap_set_bits(regmap, MPQ4210_CONTROL1,
+ MPQ4210_CONTROL1_RESERVED);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to write control 1\n");
+
+ config.dev = dev;
+ config.regmap = regmap;
+ config.of_node = dev->of_node;
+ config.init_data = of_get_regulator_init_data(dev, dev->of_node, desc);
+ if (!config.init_data)
+ return -ENOMEM;
+
+ rdev = devm_regulator_register(dev, desc, &config);
+ if (IS_ERR(rdev))
+ return dev_err_probe(dev, PTR_ERR(rdev),
+ "failed to register regulator\n");
+
+ return 0;
+}
+
+static const struct of_device_id mpq4210_of_match[] = {
+ { .compatible = "mps,mpq4210" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mpq4210_of_match);
+
+static const struct i2c_device_id mpq4210_i2c_id[] = {
+ { .name = "mpq4210" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpq4210_i2c_id);
+
+static struct i2c_driver mpq4210_regulator_driver = {
+ .driver = {
+ .name = "mpq4210",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = mpq4210_of_match,
+ },
+ .probe = mpq4210_i2c_probe,
+ .id_table = mpq4210_i2c_id,
+};
+
+module_i2c_driver(mpq4210_regulator_driver);
+
+MODULE_DESCRIPTION("Monolithic Power Systems MPQ4210 voltage regulator driver");
+MODULE_AUTHOR("Tapio Reijonen <tapio.reijonen@vaisala.com>");
+MODULE_LICENSE("GPL");