mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator
@ 2026-10-21 10:27 Paul Louvel
  2026-10-21 10:27 ` [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property Paul Louvel
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 10:27 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-kernel, devicetree, Thomas Petazzoni, Herve Codina,
	Miquel Raynal, Paul Louvel

Hi,

This series provides support for the LM51772, a buck-boost controller
from TI with I2C control, designed for USB Power Delivery applications.
The output voltage is software-configurable from 1 V to 48 V, and the
peak current limit threshold is programmed through software. The
LM51772 also provides an auxiliary fixed 5 V regulator output (VCC1).

Core changes
------------

Patch #3:

Some context for this patch: I am working on a board featuring the
PTN5110. This chip implements a Type-C Port Controller along with
other vendor-defined features. It can support up to two source power
rails for the source-only role in Power Delivery. One power rail is
used for vSafe5V, and the other is the so-called
"SourceVbusHighVoltage", providing voltages > 5 V. On my board, the
LM51772 provides this power path.

The PTN5110 driver is currently supported by the generic tcpci.c
implementation, but it had to be split out into a standalone module to
implement a custom set_vbus() callback. If a "HighVoltage" has been
negotiated between the source and the sink, this callback is
responsible for enabling the regulator supplying such voltages.

In short, the callback looks like this (my kernel changes related to
the functionality above are not upstream yet):

static int ptn5110_tcpc_set_vbus(struct tcpci *tcpci, struct tcpci_data *data, bool source,
				 bool sink)
{
	struct ptn5110_priv *priv = container_of(data, struct ptn5110_priv, data);
	int ret;

	ret = regulator_is_enabled(priv->vbus_high);
	if (ret < 0)
		return ret;

	if (ret && !source)
		return regulator_disable(priv->vbus_high);

	if (!ret && source)
		return regulator_enable(priv->vbus_high);

	return 0;
}

set_vbus() is called when the port is registered, at boot time, with
source=false and sink=false. Call trace:

    ptn5110_tcpc_set_vbus
    tcpci_set_vbus
    tcpm_reset_port
    tcpm_init
    tcpm_register_port
    tcpci_register_port
    ptn5110_tcpc_probe
    i2c_device_probe
    really_probe
    __driver_probe_device
    driver_probe_device
    __device_attach_driver
    bus_for_each_drv
    ...

The LM51772 output stage is enabled by default because the CONV_EN2
bit is set. Thus, regulator_is_enabled() returns true, and
regulator_disable() is then called, leading to an unbalanced disable.

My idea is to add a new device tree property,
"regulator-boot-force-off", to disable the regulator immediately when
it is registered with the regulator core, regardless of its previous
state.

Patch #4:

To avoid code duplication when setting and getting the voltage
selector, introduces a new vsel_reg_oversize member in
struct regulator_desc. The LM51772 stores the voltage selector across
two 8-bit registers: one for the least significant bits and one for
the most significant bits.

Thanks,

Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
Paul Louvel (5):
      regulator: dt-bindings: Add regulator-boot-force-off property
      regulator: dt-bindings: Add TI LM51772 support
      regulator: core: Add mechanism to force disable a regulator at boot
      regulator: Support regulators where the voltage selector spans two registers
      regulator: Add TI LM51772 support

 .../devicetree/bindings/regulator/regulator.yaml   |   8 +
 .../devicetree/bindings/regulator/ti,lm51772.yaml  | 239 ++++++
 MAINTAINERS                                        |   6 +
 drivers/regulator/Kconfig                          |  11 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/core.c                           | 154 ++--
 drivers/regulator/helpers.c                        |  71 +-
 drivers/regulator/lm51772-regulator.c              | 826 +++++++++++++++++++++
 drivers/regulator/of_regulator.c                   |   1 +
 include/linux/regulator/driver.h                   |   2 +
 include/linux/regulator/machine.h                  |   3 +-
 11 files changed, 1235 insertions(+), 87 deletions(-)
---
base-commit: 0785f7172040512cba759838b27286646773c735
change-id: 20261021-paul-lm51772-c96bbce762a8

Best regards,
--  
Paul Louvel, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


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

* [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property
  2026-10-21 10:27 [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator Paul Louvel
@ 2026-10-21 10:27 ` Paul Louvel
  2026-10-21 17:01   ` Conor Dooley
  2026-10-21 10:27 ` [PATCH 2/5] regulator: dt-bindings: Add TI LM51772 support Paul Louvel
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 10:27 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-kernel, devicetree, Thomas Petazzoni, Herve Codina,
	Miquel Raynal, Paul Louvel

Introduce a new property to immediately disable the regulator at boot
time.
This property is useful when a consumer device may query the state of
the regulator at boot time, and want to disable it right away, without a
prior regulator_enable().

Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
 Documentation/devicetree/bindings/regulator/regulator.yaml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/regulator/regulator.yaml b/Documentation/devicetree/bindings/regulator/regulator.yaml
index 019aeb664cae..2da8f70d7e7a 100644
--- a/Documentation/devicetree/bindings/regulator/regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/regulator.yaml
@@ -41,6 +41,14 @@ properties:
     description: boolean, regulator should never be disabled
     type: boolean
 
+  regulator-boot-force-off:
+    description: a regulator may be enabled by default when the system powers up.
+      A consumer may query the regulator's state during boot and disable it if
+      it is already enabled, potentially resulting in an unbalanced disable
+      operation. This property ensures that the regulator is disabled at boot,
+      regardless of its previous state.
+    type: boolean
+
   regulator-boot-on:
     description: bootloader/firmware enabled regulator.
       It's expected that this regulator was left on by the bootloader.

-- 
2.56.0


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

* [PATCH 2/5] regulator: dt-bindings: Add TI LM51772 support
  2026-10-21 10:27 [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator Paul Louvel
  2026-10-21 10:27 ` [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property Paul Louvel
@ 2026-10-21 10:27 ` Paul Louvel
  2026-10-21 10:27 ` [PATCH 3/5] regulator: core: Add mechanism to force disable a regulator at boot Paul Louvel
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 10:27 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-kernel, devicetree, Thomas Petazzoni, Herve Codina,
	Miquel Raynal, Paul Louvel

Add device tree binding for the TI LM51772 buck-boost voltage regulator,
which consist of a primary VOUT output, and a fixed 5V LDO.

Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
 .../devicetree/bindings/regulator/ti,lm51772.yaml  | 239 +++++++++++++++++++++
 MAINTAINERS                                        |   5 +
 2 files changed, 244 insertions(+)

diff --git a/Documentation/devicetree/bindings/regulator/ti,lm51772.yaml b/Documentation/devicetree/bindings/regulator/ti,lm51772.yaml
new file mode 100644
index 000000000000..1e72ac13ddfc
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/ti,lm51772.yaml
@@ -0,0 +1,239 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/ti,lm51772.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI LM51772 buck-boost voltage regulator
+
+maintainers:
+  - Paul Louvel <paul.louvel@bootlin.com>
+
+description: |
+  The LM51772 is a buck-boost controller with I2C control, designed for
+  USB Power Delivery applications. The output voltage is software
+  configurable between 1V to 48V, with:
+
+    – 1V up to 24V in 10mV monotonous steps.
+    – 3.3V up to 48V in 20mV monotonous steps.
+
+  The peak current limit threshold is programmed through software, between 0.5A
+  to 7A.
+  The LM51772 also provides an auxiliary fixed 5 V regulator output
+  (VCC1).
+
+properties:
+  compatible:
+    const: ti,lm51772
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+    description: Fault / warning irq.
+
+  vin-supply:
+    description: Regulator supply for the chip.
+
+  regulators:
+    type: object
+    description: List of regulators provided by this PMIC.
+
+    properties:
+      vout:
+        type: object
+        $ref: regulator.yaml#
+        description: Main output voltage.
+
+        properties:
+          ti,active-discharge-strength-milliamp:
+            description: Discharge current for VOUT.
+            $ref: /schemas/types.yaml#/definitions/uint32
+            multipleOf: 25
+            minimum: 25
+            maximum: 75
+            default: 25
+
+          ti,iset-pin:
+            description: |
+              Select the ISET pin as the source of the current limit, instead of
+              the ILIM DAC. When this property is present, the ISET pin is forced
+              in its active configuration and the ILIM DAC is disabled, so the
+              current limit can no longer be programmed through the
+              dedicated register.
+            type: boolean
+            default: false
+
+          ti,pcm-window-low-permille:
+            description: |
+              When VIN is inside a programmable window around VOUT, the PWM
+              logic stops switching entirely and the regulator connect VIN to VOUT.
+              This mode is called the Programmable Conduction Mode (PCM), and is an
+              efficiency features allowing the unit to avoid unnecessary switching
+              operation when VIN ≈ VOUT.
+
+              Lower threshold of the Programmable Conduction Mode (PCM)
+              window, expressed in permille (1/1000) below VOUT target.
+              If this property is zero, PCM is disabled.
+            $ref: /schemas/types.yaml#/definitions/uint32
+            multipleOf: 25
+            minimum: 0
+            maximum: 775
+            default: 300
+
+          ti,sense-res-milliohm:
+            description: |
+              Value in milliohms of the current sense resistor connected between the
+              ISNSP and ISNSN pins, used to translate the ILIM threshold voltage into
+              a current limit and vice versa.
+              TI recommands a value of 10 milliohms.
+            $ref: /schemas/types.yaml#/definitions/uint32
+            minimum: 1
+            default: 10
+
+          regulator-temp-warn-kelvin:
+            enum: [0, 1, 368, 383, 398, 413]
+
+          regulator-ramp-delay:
+            enum: [40000, 20000, 1000, 500]
+
+          regulator-min-microvolt:
+            minimum: 1000000
+            maximum: 48000000
+
+          regulator-max-microvolt:
+            minimum: 1000000
+            maximum: 48000000
+
+          regulator-min-microamp:
+            minimum: 500000
+            maximum: 70000000
+
+          regulator-max-microamp:
+            minimum: 500000
+            maximum: 70000000
+
+        unevaluatedProperties: false
+
+      vcc1:
+        type: object
+        $ref: regulator.yaml#
+        description: |
+          LDO regulator for auxiliary use.
+          It can used to power, for example, a PD controller that will drive this PMIC.
+
+        properties:
+          regulator-min-microvolt:
+            minimum: 5000000
+            maximum: 5000000
+
+          regulator-max-microvolt:
+            minimum: 5000000
+            maximum: 5000000
+
+        unevaluatedProperties: false
+
+  ti,sync-pin:
+    description: |
+      Selects the function of the SYNC pin, used when several converters
+      operate in parallel.
+    $ref: /schemas/types.yaml#/definitions/string
+    enum: [input-rising, input-falling, output-rising, output-falling]
+    default: input-rising
+
+  ti,ivp-thres-millivolt:
+    description: |
+      Input Overvoltage Protection and Regulation Threshold.
+      It shall avoid any damage to the device in case the current flows from the
+      output to the input and the input source cannot sink current.
+      This threshold is used for IVP (Input Voltage Protection) and IVR (Input
+      Voltage Regulation).
+    $ref: /schemas/types.yaml#/definitions/uint32
+    oneOf:
+      - multipleOf: 125
+        minimum: 4750
+        maximum: 23250
+      - multipleOf: 250
+        minimum: 23750
+        maximum: 50000
+    default: 50000
+
+  ti,ivp:
+    description: Enable the IVP feature of the regulator.
+    type: boolean
+    default: false
+
+  ti,ivr:
+    description: Enable the IVR feature of the regulator.
+    type: boolean
+    default: false
+
+  ti,nint:
+    description: |
+      Configures the nFLT/nINT pin to act as an interrupt pin.
+
+      nFLT mode: The pin remains low until the cause of the status change ceases.
+      In this mode use an level irq trigger.
+
+      nINT mode: The pin is asserted low to signal a status change.
+      In this mode use an edge irq trigger.
+    type: boolean
+    default: false
+
+  ti,cable-drop-compensation:
+    description: |
+      The cable drop compensation feature helps to keep the output voltage at the
+      nominal value over a wide range of load current without the need for
+      additional remote sensing. The cable drop compensation measures the
+      current and offsets the output voltage proportionally to the measured
+      current.
+    type: boolean
+    default: false
+
+dependencies:
+  ti,ivr: ["ti,ivp"]
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pmic@6a {
+            compatible = "ti,lm51772";
+            reg = <0x6a>;
+            interrupt-parent = <&irqc>;
+            interrupts = <1 IRQ_TYPE_EDGE_FALLING>;
+
+            ti,nint;
+
+            regulators {
+              vout {
+                  regulator-name = "vout";
+                  regulator-min-microvolt = <5000000>;
+                  regulator-max-microvolt = <20000000>;
+                  regulator-ov-protection-microvolt = <1000000>;
+                  regulator-min-microamp = <1000000>;
+                  regulator-max-microamp = <5000000>;
+                  regulator-temp-warn-kelvin = <368>;
+              };
+
+              vcc1 {
+                  regulator-name = "vcc1";
+                  regulator-min-microvolt = <5000000>;
+                  regulator-max-microvolt = <5000000>;
+              };
+            };
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 57285ff29a16..c958e116cd66 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27461,6 +27461,11 @@ S:	Maintained
 F:	sound/soc/codecs/isabelle*
 F:	sound/soc/codecs/lm49453*
 
+TI LM51772 BUCK-BOOST REGULATOR DRIVER
+M:	Paul Louvel <paul.louvel@bootlin.com>
+S:	Maintained
+F:	Documentation/devicetree/bindings/regulator/ti,lm51772.yaml
+
 TI LMP92064 ADC DRIVER
 M:	Leonard Göhrs <l.goehrs@pengutronix.de>
 R:	kernel@pengutronix.de

-- 
2.56.0


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

* [PATCH 3/5] regulator: core: Add mechanism to force disable a regulator at boot
  2026-10-21 10:27 [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator Paul Louvel
  2026-10-21 10:27 ` [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property Paul Louvel
  2026-10-21 10:27 ` [PATCH 2/5] regulator: dt-bindings: Add TI LM51772 support Paul Louvel
@ 2026-10-21 10:27 ` Paul Louvel
  2026-10-21 10:27 ` [PATCH 4/5] regulator: Support regulators where the voltage selector spans two registers Paul Louvel
  2026-10-21 10:27 ` [PATCH 5/5] regulator: Add TI LM51772 support Paul Louvel
  4 siblings, 0 replies; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 10:27 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-kernel, devicetree, Thomas Petazzoni, Herve Codina,
	Miquel Raynal, Paul Louvel

Regulation activity may be enabled by default when the device powers up.
A consumer may query the regulator's state during probe and may disable
it if it is already enabled, potentially resulting in an unbalanced
disable operation:

ret = regulator_is_enabled();
if (ret < 0)
  return ret;

if (ret)
  ret = regulator_disable();
        ^^^^^^^^^^^^^^^^^^^^

Add the 'regulator-boot-force-off' property, which disable the regulator
when setting machine constraints.

Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
 drivers/regulator/core.c          | 154 ++++++++++++++++++++------------------
 drivers/regulator/of_regulator.c  |   1 +
 include/linux/regulator/machine.h |   3 +-
 3 files changed, 84 insertions(+), 74 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 14422cfe80e2..b45048ec084a 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1450,6 +1450,81 @@ static int handle_notify_limits(struct regulator_dev *rdev,
 
 	return ret;
 }
+
+/**
+ * regulator_ena_gpio_ctrl - balance enable_count of each GPIO and actual GPIO pin control
+ * @rdev: regulator_dev structure
+ * @enable: enable GPIO at initial use?
+ *
+ * GPIO is enabled in case of initial use. (enable_count is 0)
+ * GPIO is disabled when it is not shared any more. (enable_count <= 1)
+ *
+ * Return: 0 on success or a negative error number on failure.
+ */
+static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
+{
+	struct regulator_enable_gpio *pin = rdev->ena_pin;
+	int ret;
+
+	if (!pin)
+		return -EINVAL;
+
+	if (enable) {
+		/* Enable GPIO at initial use */
+		if (pin->enable_count == 0) {
+			ret = gpiod_set_value_cansleep(pin->gpiod, 1);
+			if (ret)
+				return ret;
+		}
+
+		pin->enable_count++;
+	} else {
+		if (pin->enable_count > 1) {
+			pin->enable_count--;
+			return 0;
+		}
+
+		/* Disable GPIO if not used */
+		if (pin->enable_count <= 1) {
+			ret = gpiod_set_value_cansleep(pin->gpiod, 0);
+			if (ret)
+				return ret;
+
+			pin->enable_count = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int _regulator_do_disable(struct regulator_dev *rdev)
+{
+	int ret;
+
+	trace_regulator_disable(rdev_get_name(rdev));
+
+	if (rdev->ena_pin) {
+		if (rdev->ena_gpio_state) {
+			ret = regulator_ena_gpio_ctrl(rdev, false);
+			if (ret < 0)
+				return ret;
+			rdev->ena_gpio_state = 0;
+		}
+
+	} else if (rdev->desc->ops->disable) {
+		ret = rdev->desc->ops->disable(rdev);
+		if (ret != 0)
+			return ret;
+	}
+
+	if (rdev->desc->off_on_delay)
+		rdev->last_off = ktime_get_boottime();
+
+	trace_regulator_disable_complete(rdev_get_name(rdev));
+
+	return 0;
+}
+
 /**
  * set_machine_constraints - sets regulator constraints
  * @rdev: regulator source
@@ -1701,6 +1776,12 @@ static int set_machine_constraints(struct regulator_dev *rdev,
 		rdev->last_off = ktime_get();
 	}
 
+	if (rdev->constraints->boot_force_off) {
+		ret = _regulator_do_disable(rdev);
+		if (ret < 0)
+			return ret;
+	}
+
 	if (!rdev->constraints->pw_budget_mW)
 		rdev->constraints->pw_budget_mW = INT_MAX;
 
@@ -2910,51 +2991,6 @@ static void regulator_ena_gpio_free(struct regulator_dev *rdev)
 	rdev->ena_pin = NULL;
 }
 
-/**
- * regulator_ena_gpio_ctrl - balance enable_count of each GPIO and actual GPIO pin control
- * @rdev: regulator_dev structure
- * @enable: enable GPIO at initial use?
- *
- * GPIO is enabled in case of initial use. (enable_count is 0)
- * GPIO is disabled when it is not shared any more. (enable_count <= 1)
- *
- * Return: 0 on success or a negative error number on failure.
- */
-static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
-{
-	struct regulator_enable_gpio *pin = rdev->ena_pin;
-	int ret;
-
-	if (!pin)
-		return -EINVAL;
-
-	if (enable) {
-		/* Enable GPIO at initial use */
-		if (pin->enable_count == 0) {
-			ret = gpiod_set_value_cansleep(pin->gpiod, 1);
-			if (ret)
-				return ret;
-		}
-
-		pin->enable_count++;
-	} else {
-		if (pin->enable_count > 1) {
-			pin->enable_count--;
-			return 0;
-		}
-
-		/* Disable GPIO if not used */
-		if (pin->enable_count <= 1) {
-			ret = gpiod_set_value_cansleep(pin->gpiod, 0);
-			if (ret)
-				return ret;
-
-			pin->enable_count = 0;
-		}
-	}
-
-	return 0;
-}
 
 /**
  * _regulator_check_status_enabled - check if regulator status can be
@@ -3226,34 +3262,6 @@ int regulator_enable(struct regulator *regulator)
 }
 EXPORT_SYMBOL_GPL(regulator_enable);
 
-static int _regulator_do_disable(struct regulator_dev *rdev)
-{
-	int ret;
-
-	trace_regulator_disable(rdev_get_name(rdev));
-
-	if (rdev->ena_pin) {
-		if (rdev->ena_gpio_state) {
-			ret = regulator_ena_gpio_ctrl(rdev, false);
-			if (ret < 0)
-				return ret;
-			rdev->ena_gpio_state = 0;
-		}
-
-	} else if (rdev->desc->ops->disable) {
-		ret = rdev->desc->ops->disable(rdev);
-		if (ret != 0)
-			return ret;
-	}
-
-	if (rdev->desc->off_on_delay)
-		rdev->last_off = ktime_get_boottime();
-
-	trace_regulator_disable_complete(rdev_get_name(rdev));
-
-	return 0;
-}
-
 /* locks held by regulator_disable() */
 static int _regulator_disable(struct regulator *regulator)
 {
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 09e26febfc83..337629be58ca 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -132,6 +132,7 @@ static int of_get_regulation_constraints(struct device *dev,
 	constraints->always_on = of_property_read_bool(np, "regulator-always-on");
 	if (!constraints->always_on) /* status change should be possible. */
 		constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+	constraints->boot_force_off = of_property_read_bool(np, "regulator-boot-force-off");
 
 	constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
 	constraints->system_critical = of_property_read_bool(np,
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index 1fc440c5c4c7..35ab9de96e5c 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -227,7 +227,8 @@ struct regulation_constraints {
 	unsigned int active_discharge;
 
 	/* constraint flags */
-	unsigned always_on:1;	/* regulator never off when system is on */
+	unsigned always_on:1;		/* regulator never off when system is on */
+	unsigned boot_force_off:1;	/* force regulator off at boot */
 	unsigned boot_on:1;	/* bootloader/firmware enabled regulator */
 	unsigned apply_uV:1;	/* apply uV constraint if min == max */
 	unsigned ramp_disable:1; /* disable ramp delay */

-- 
2.56.0


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

* [PATCH 4/5] regulator: Support regulators where the voltage selector spans two registers
  2026-10-21 10:27 [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator Paul Louvel
                   ` (2 preceding siblings ...)
  2026-10-21 10:27 ` [PATCH 3/5] regulator: core: Add mechanism to force disable a regulator at boot Paul Louvel
@ 2026-10-21 10:27 ` Paul Louvel
  2026-10-21 10:27 ` [PATCH 5/5] regulator: Add TI LM51772 support Paul Louvel
  4 siblings, 0 replies; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 10:27 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-kernel, devicetree, Thomas Petazzoni, Herve Codina,
	Miquel Raynal, Paul Louvel

Some devices uses 8-bit registers to store the voltage selector across
two registers: one for the least significant bits and one for the most
significant bits.

Introduce vsel_reg_oversize to support voltage selectors that span two
registers. Write the selector in bulk across both registers using
little-endian encoding.

Assume that future hardware will require at most one additional register
to store the voltage selector, and that only a device with 8-bit
register will require such flag.

Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
 drivers/regulator/helpers.c      | 71 ++++++++++++++++++++++++++++++++--------
 include/linux/regulator/driver.h |  2 ++
 2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index 0def82eb8b46..1221128dde98 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -118,6 +118,37 @@ static int regulator_range_selector_to_index(struct regulator_dev *rdev,
 	return -EINVAL;
 }
 
+static int get_voltage_sel_reg(struct regulator_dev *rdev, unsigned int *sel)
+{
+	int ret, val_bytes;
+	unsigned int val;
+	__le16 buf;
+
+	if (rdev->desc->vsel_reg_oversize) {
+		val_bytes = regmap_get_val_bytes(rdev->regmap);
+
+		if (val_bytes != 1)
+			return -EINVAL;
+
+		ret = regmap_bulk_read(rdev->regmap, rdev->desc->vsel_reg, &buf, 2);
+		if (ret != 0)
+			return ret;
+
+		val = le16_to_cpu(buf);
+	} else {
+		ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
+		if (ret != 0)
+			return ret;
+	}
+
+	val &= rdev->desc->vsel_mask;
+	val >>= ffs(rdev->desc->vsel_mask) - 1;
+
+	*sel = val;
+
+	return 0;
+}
+
 /**
  * regulator_get_voltage_sel_pickable_regmap - pickable range get_voltage_sel
  *
@@ -140,7 +171,7 @@ int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
 	if (!r)
 		return -EINVAL;
 
-	ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
+	ret = get_voltage_sel_reg(rdev, &val);
 	if (ret != 0)
 		return ret;
 
@@ -148,9 +179,6 @@ int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
 	if (ret != 0)
 		return ret;
 
-	val &= rdev->desc->vsel_mask;
-	val >>= ffs(rdev->desc->vsel_mask) - 1;
-
 	range = regulator_range_selector_to_index(rdev, r_val);
 	if (range < 0)
 		return -EINVAL;
@@ -161,6 +189,19 @@ int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
 }
 EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_pickable_regmap);
 
+static int set_oversize_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
+{
+	int val_bytes = regmap_get_val_bytes(rdev->regmap);
+	__le16 val;
+
+	if (val_bytes != 1)
+		return -EINVAL;
+
+	val = cpu_to_le16(sel);
+
+	return regmap_bulk_write(rdev->regmap, rdev->desc->vsel_reg, &val, 2);
+}
+
 static int write_separate_vsel_and_range(struct regulator_dev *rdev,
 					 unsigned int sel, unsigned int range)
 {
@@ -173,15 +214,17 @@ static int write_separate_vsel_and_range(struct regulator_dev *rdev,
 	if (ret)
 		return ret;
 
+	if (rdev->desc->vsel_reg_oversize)
+		return set_oversize_voltage_sel(rdev, sel);
+
 	/*
 	 * Some PMICs treat the vsel_reg same as apply-bit. Force it to be
 	 * written if the range changed, even if the old selector was same as
 	 * the new one
 	 */
 	if (rdev->desc->range_applied_by_vsel && range_updated)
-		return regmap_write_bits(rdev->regmap,
-					rdev->desc->vsel_reg,
-					rdev->desc->vsel_mask, sel);
+		return regmap_write_bits(rdev->regmap, rdev->desc->vsel_reg,
+					 rdev->desc->vsel_mask, sel);
 
 	return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
 				  rdev->desc->vsel_mask, sel);
@@ -257,17 +300,15 @@ int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev)
 	unsigned int val;
 	int ret;
 
-	ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
+	ret = get_voltage_sel_reg(rdev, &val);
 	if (ret != 0)
 		return ret;
 
-	val &= rdev->desc->vsel_mask;
-	val >>= ffs(rdev->desc->vsel_mask) - 1;
-
 	return val;
 }
 EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap);
 
+
 /**
  * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users
  *
@@ -284,8 +325,12 @@ int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel)
 
 	sel <<= ffs(rdev->desc->vsel_mask) - 1;
 
-	ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
-				  rdev->desc->vsel_mask, sel);
+	if (rdev->desc->vsel_reg_oversize)
+		ret = set_oversize_voltage_sel(rdev, sel);
+	else
+		ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
+					 rdev->desc->vsel_mask, sel);
+
 	if (ret)
 		return ret;
 
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 55cc519103ed..8a0f3aebdade 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -312,6 +312,7 @@ enum regulator_type {
  * @range_applied_by_vsel: A flag to indicate that changes to vsel_range_reg
  *			   are only effective after vsel_reg is written
  * @vsel_reg: Register for selector when using ``regulator_map_*_voltage_*``
+ * @vsel_reg_oversize: Indicates that the vsel_reg spans on two registers
  * @vsel_mask: Mask for register bitfield used for selector
  * @vsel_step: Specify the resolution of selector stepping when setting
  *	       voltage. If 0, then no stepping is done (requested selector is
@@ -405,6 +406,7 @@ struct regulator_desc {
 	unsigned int vsel_range_mask;
 	bool range_applied_by_vsel;
 	unsigned int vsel_reg;
+	bool vsel_reg_oversize;
 	unsigned int vsel_mask;
 	unsigned int vsel_step;
 	unsigned int csel_reg;

-- 
2.56.0


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

* [PATCH 5/5] regulator: Add TI LM51772 support
  2026-10-21 10:27 [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator Paul Louvel
                   ` (3 preceding siblings ...)
  2026-10-21 10:27 ` [PATCH 4/5] regulator: Support regulators where the voltage selector spans two registers Paul Louvel
@ 2026-10-21 10:27 ` Paul Louvel
  2026-10-21 15:10   ` Uwe Kleine-König
  4 siblings, 1 reply; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 10:27 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-kernel, devicetree, Thomas Petazzoni, Herve Codina,
	Miquel Raynal, Paul Louvel

Add driver for the TI LM51772 buck-boost voltage regulator.

Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
---
 MAINTAINERS                           |   1 +
 drivers/regulator/Kconfig             |  11 +
 drivers/regulator/Makefile            |   1 +
 drivers/regulator/lm51772-regulator.c | 826 ++++++++++++++++++++++++++++++++++
 4 files changed, 839 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index c958e116cd66..a4c59be2551c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27465,6 +27465,7 @@ TI LM51772 BUCK-BOOST REGULATOR DRIVER
 M:	Paul Louvel <paul.louvel@bootlin.com>
 S:	Maintained
 F:	Documentation/devicetree/bindings/regulator/ti,lm51772.yaml
+F:	drivers/regulator/lm51772-regulator.c
 
 TI LMP92064 ADC DRIVER
 M:	Leonard Göhrs <l.goehrs@pengutronix.de>
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 7d151433747b..ce43a1dea33d 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -519,6 +519,17 @@ config REGULATOR_LM363X
 	  One boost output voltage is configurable and always on.
 	  Other LDOs are used for the display module.
 
+config REGULATOR_LM51772
+	tristate "TI LM51772 voltage regulator"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  This driver supports the TI LM51772 buck-boost controller, designed
+	  for USB Power Delivery applications. It provides a software
+	  configurable buck-boost output voltage and a fixed auxiliary 5 V LDO
+	  output, both controlled over I2C. The driver supports voltage
+	  setting, current limiting and fault/thermal reporting.
+
 config REGULATOR_LOCHNAGAR
 	tristate "Cirrus Logic Lochnagar regulator driver"
 	depends on MFD_LOCHNAGAR
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 6aabcc54382a..783eda233e25 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
 obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
 obj-$(CONFIG_REGULATOR_FP9931) += fp9931.o
 obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o
+obj-$(CONFIG_REGULATOR_LM51772) += lm51772-regulator.o
 obj-$(CONFIG_REGULATOR_LOCHNAGAR) += lochnagar-regulator.o
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
diff --git a/drivers/regulator/lm51772-regulator.c b/drivers/regulator/lm51772-regulator.c
new file mode 100644
index 000000000000..b5259f277243
--- /dev/null
+++ b/drivers/regulator/lm51772-regulator.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI LM51772 I2C regulator driver
+ *
+ * Datasheet: SNVSC22D (September 2025)
+ *
+ * Copyright (c) 2026 Bootlin
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/linear_range.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/coupler.h>
+#include <linux/regulator/driver.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#define LM51772_SENSE_RES_DEFAULT_MOHM		10
+
+#define LM51772_VCC1_LDO_UV			5000000
+#define LM51772_VCC1_LDO_NV			1
+
+#define LM51772_ILIM_THRESHOLD_MIN		0x0A
+#define LM51772_ILIM_THRESHOLD_MAX		0x8C
+
+/* Register map (datasheet table 8-1) */
+#define LM51772_REG_CLEAR_FAULTS		0x03
+#define LM51772_REG_ILIM_THRESHOLD		0x0A
+#define LM51772_REG_VOUT_TARGET1_LSB		0x0C
+#define LM51772_REG_VOUT_TARGET1_MSB		0x0D
+#define LM51772_REG_PD_STATUS_0			0x21
+#define LM51772_REG_STATUS_BYTE			0x78
+#define LM51772_REG_USB_PD_CONTROL_0		0x81
+#define LM51772_REG_MFR_SPECIFIC_D0		0xD0
+#define LM51772_REG_MFR_SPECIFIC_D1		0xD1
+#define LM51772_REG_MFR_SPECIFIC_D2		0xD2
+#define LM51772_REG_MFR_SPECIFIC_D3		0xD3
+#define LM51772_REG_MFR_SPECIFIC_D4		0xD4
+#define LM51772_REG_MFR_SPECIFIC_D5		0xD5
+#define LM51772_REG_MFR_SPECIFIC_D6		0xD6
+#define LM51772_REG_MFR_SPECIFIC_D7		0xD7
+#define LM51772_REG_MFR_SPECIFIC_D8		0xD8
+#define LM51772_REG_MFR_SPECIFIC_D9		0xD9
+#define LM51772_REG_IVP_VOLTAGE			0xDA
+
+#define LM51772_ILIM_THR_STEP_UV		500
+#define LM51772_ILIM_THR_MIN_UV			5000
+#define LM51772_ILIM_THR_MAX_UV			70000
+
+#define LM51772_VOUT_TARGET1_MASK		GENMASK(11, 0)
+
+/* USB_PD_CONTROL_0 bits */
+#define LM51772_FORCE_DISCH			BIT(1)
+#define LM51772_CONV_EN2			BIT(0)
+
+/* STATUS_BYTE bits */
+#define LM51772_STAT_OTHER			BIT(0)
+#define LM51772_STAT_CML			BIT(1)
+#define LM51772_STAT_TEMP			BIT(2)
+#define LM51772_STAT_INPUT			BIT(3)
+#define LM51772_STAT_IOUT			BIT(4)
+#define LM51772_STAT_VOUT			BIT(5)
+#define LM51772_STAT_OFF			BIT(6)
+
+/* MFR_SPECIFIC_D0 bits */
+#define LM51772_EN_VCC1				BIT(5)
+
+/* MFR_SPECIFIC_D1 bits */
+#define LM51772_EN_THER_WARN			BIT(7)
+#define LM51772_THW_THRESHOLD_MASK		GENMASK(6, 5)
+#define LM51772_EN_NINT				BIT(4)
+#define LM51772_CONV_EN				BIT(0)
+
+/* MFR_SPECIFIC_D2 bits */
+#define LM51772_DISCHARGE_STRENGTH_MASK		GENMASK(3, 2)
+#define LM51772_DVS_SLEW_RAMP_MASK		GENMASK(5, 4)
+#define LM51772_DVS_SLEW_RAMP_DEFAULT		0
+
+/* MFR_SPECIFIC_D3 bits */
+#define LM51772_EN_IVP				BIT(7)
+#define LM51772_SEL_IVR				BIT(6)
+#define LM51772_VDET_EN				BIT(5)
+
+/* MFR_SPECIFIC_D6 bits */
+#define LM51772_CONFIG_SYNC_PIN_MASK		GENMASK(7, 6)
+
+/* MFR_SPECIFIC_D8 bits */
+#define LM51772_SEL_FB_DIV20			BIT(7)
+#define LM51772_EN_CDC				BIT(6)
+
+/* MFR_SPECIFIC_D9 bits */
+#define LM51772_SEL_ISET_PIN			BIT(5)
+#define LM51772_PCM_WINDOW_LOW_MASK		GENMASK(4, 0)
+#define LM51772_PCM_WINDOW_LOW_MAX		0x1F
+#define LM51772_PCM_WINDOW_LOW_DFLT		0xC
+#define LM51772_PCM_WINDOW_LOW_STEP_PERMILLE	25
+
+#define LM51772_IVP_VOLTAGE_DFLT		0xFF
+
+#define LM51772_DIV10_MIN_UV			1000000
+#define LM51772_DIV10_STEP_UV			10000
+
+#define LM51772_DIV20_MIN_UV			3300000
+#define LM51772_DIV20_STEP_UV			20000
+
+#define LM51772_N_REGS				2
+
+struct lm51772_priv {
+	bool iset_pin;
+	bool nint;
+	u32 sense_res_milliohms;
+	u32 ovp_thres;
+};
+
+static int lm51772_regulator_set_thermal_protection(struct regulator_dev *rdev,
+						    int lim, int severity, bool enable)
+{
+	static const struct linear_range thw_range_celsius = LINEAR_RANGE(95, 0, 3, 15);
+	unsigned int regval = 0, mask = 0, selector = 0;
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	struct device *dev = rdev_get_dev(rdev);
+	long celsius = 0;
+	bool found;
+	int ret;
+
+	if (severity != REGULATOR_SEVERITY_WARN)
+		return -EOPNOTSUPP;
+
+	if (lim != 0) {
+		celsius = kelvin_to_celsius(lim);
+
+		ret = linear_range_get_selector_low_array(&thw_range_celsius, 1, celsius, &selector,
+							  &found);
+		if (ret || !found)
+			return -EINVAL;
+
+		selector = thw_range_celsius.max_sel - selector;
+
+		regval |= FIELD_PREP(LM51772_THW_THRESHOLD_MASK, selector);
+		mask |= LM51772_THW_THRESHOLD_MASK;
+	}
+
+	regval |= FIELD_PREP(LM51772_EN_THER_WARN, enable ? 1 : 0);
+	mask |= LM51772_EN_THER_WARN;
+
+	dev_dbg(dev, "thermal protection set to %ld deg C (selector 0x%02x, enable %d)\n", celsius,
+		selector, enable);
+
+	return regmap_update_bits(regmap, LM51772_REG_MFR_SPECIFIC_D1, mask, regval);
+}
+
+static int lm51772_set_conv_en(struct regmap *regmap, bool enable)
+{
+	int ret;
+
+	ret = regmap_update_bits(regmap, LM51772_REG_USB_PD_CONTROL_0,
+				 LM51772_CONV_EN2,
+				 FIELD_PREP(LM51772_CONV_EN2, enable));
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(regmap, LM51772_REG_MFR_SPECIFIC_D0,
+				  LM51772_CONV_EN,
+				  FIELD_PREP(LM51772_CONV_EN, enable));
+}
+
+static int lm51772_get_conv_en(struct regmap *regmap)
+{
+	unsigned int pd_ctrl, cfg0;
+	int ret;
+
+	ret = regmap_read(regmap, LM51772_REG_USB_PD_CONTROL_0, &pd_ctrl);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(regmap, LM51772_REG_MFR_SPECIFIC_D0, &cfg0);
+	if (ret)
+		return ret;
+
+	return FIELD_GET(LM51772_CONV_EN2, pd_ctrl) || FIELD_GET(LM51772_CONV_EN, cfg0);
+}
+
+static int lm51772_regulator_enable(struct regulator_dev *rdev)
+{
+	return lm51772_set_conv_en(rdev_get_regmap(rdev), true);
+}
+
+static int lm51772_regulator_disable(struct regulator_dev *rdev)
+{
+	return lm51772_set_conv_en(rdev_get_regmap(rdev), false);
+}
+
+static int lm51772_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	return lm51772_get_conv_en(rdev_get_regmap(rdev));
+}
+
+static int lm51772_set_ov(struct regulator_dev *rdev, int lim_uV)
+{
+	static const struct linear_range ovp_ranges[] = {
+		LINEAR_RANGE(4000000, 0, 0x17, 500000),
+		LINEAR_RANGE(16000000, 0x18, 0x3f, 1000000),
+	};
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	struct device *dev = rdev_get_dev(rdev);
+	unsigned int selector;
+	bool found;
+	int ret;
+
+	ret = linear_range_get_selector_low_array(ovp_ranges, ARRAY_SIZE(ovp_ranges), lim_uV,
+						  &selector, &found);
+	if (ret || !found)
+		return -EINVAL;
+
+	dev_dbg(dev, "OVP set to %d uV (selector 0x%02x)\n", lim_uV, selector);
+
+	return regmap_write(regmap, LM51772_REG_MFR_SPECIFIC_D5, selector);
+}
+
+static int lm51772_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
+{
+	const struct lm51772_priv *priv = rdev_get_drvdata(rdev);
+	int ret;
+
+	/*
+	 * The LM51772 uses hardcoded OVP thresholds. Update the OVP threshold
+	 * to match the new voltage.
+	 */
+	if (priv->ovp_thres) {
+		ret = regulator_list_voltage_pickable_linear_range(rdev, selector);
+		if (ret < 0)
+			return ret;
+
+		ret = lm51772_set_ov(rdev, ret + priv->ovp_thres);
+		if (ret)
+			return ret;
+	}
+
+	return regulator_set_voltage_sel_pickable_regmap(rdev, selector);
+}
+
+static int lm51772_regulator_set_over_voltage_protection(struct regulator_dev *rdev,
+							  int lim_uV, int severity, bool enable)
+{
+	struct lm51772_priv *priv = rdev_get_drvdata(rdev);
+	int ret;
+
+	/* OVP cannot be disabled. */
+	if (!enable)
+		return -EOPNOTSUPP;
+
+	/* There is no OVP warning. Error is not applicable. */
+	if (severity != REGULATOR_SEVERITY_PROT)
+		return -EOPNOTSUPP;
+
+	priv->ovp_thres = lim_uV;
+
+	ret = regulator_get_voltage_rdev(rdev);
+	if (ret < 0)
+		return ret;
+
+	return lm51772_set_ov(rdev, ret + priv->ovp_thres);
+}
+
+static int lm51772_regulator_get_current_limit(struct regulator_dev *rdev)
+{
+	struct lm51772_priv *priv = rdev_get_drvdata(rdev);
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	unsigned int ilim_reg;
+	int current_uA;
+	int ret;
+
+	if (priv->iset_pin)
+		return -EOPNOTSUPP;
+
+	ret = regmap_read(regmap, LM51772_REG_ILIM_THRESHOLD, &ilim_reg);
+	if (ret)
+		return ret;
+
+	ilim_reg = clamp_val(ilim_reg, LM51772_ILIM_THRESHOLD_MIN, LM51772_ILIM_THRESHOLD_MAX);
+
+	current_uA = (ilim_reg * 1000 * LM51772_ILIM_THR_STEP_UV) / priv->sense_res_milliohms;
+
+	dev_dbg(rdev_get_dev(rdev), "current limit: %d uA (ilim_reg 0x%02x, sense_res %d mOhm)\n",
+		current_uA, ilim_reg, priv->sense_res_milliohms);
+
+	return current_uA;
+}
+
+static int lm51772_regulator_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA)
+{
+	struct lm51772_priv *priv = rdev_get_drvdata(rdev);
+	u64 voltage_uV = ((u64)priv->sense_res_milliohms * max_uA) / 1000;
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	u8 ilim_reg;
+
+	if (priv->iset_pin)
+		return -EOPNOTSUPP;
+
+	if (voltage_uV < LM51772_ILIM_THR_MIN_UV || voltage_uV > LM51772_ILIM_THR_MAX_UV)
+		return -EINVAL;
+
+	ilim_reg = voltage_uV / LM51772_ILIM_THR_STEP_UV;
+
+	dev_dbg(rdev_get_dev(rdev),
+		"set current limit: %d uA (ilim_reg 0x%02x, sense_res %d mOhm)\n", max_uA, ilim_reg,
+		priv->sense_res_milliohms);
+
+	return regmap_write(regmap, LM51772_REG_ILIM_THRESHOLD, ilim_reg);
+}
+
+static unsigned int lm51772_status2err(unsigned int status_reg)
+{
+	unsigned int errors = 0;
+
+	/*
+	 * The BUSY bit in the status register is not applicable outside device startup.
+	 * Ignore it.
+	 */
+
+	if (status_reg & LM51772_STAT_TEMP)
+		errors |= (status_reg & LM51772_STAT_OFF) ? REGULATOR_ERROR_OVER_TEMP
+						    : REGULATOR_ERROR_OVER_TEMP_WARN;
+
+	if (status_reg & LM51772_STAT_IOUT)
+		errors |= REGULATOR_ERROR_OVER_CURRENT;
+
+	if (status_reg & LM51772_STAT_VOUT)
+		errors |= REGULATOR_ERROR_OVER_VOLTAGE_WARN;
+
+	if (status_reg & LM51772_STAT_CML || status_reg & LM51772_STAT_OTHER)
+		errors |= REGULATOR_ERROR_FAIL;
+
+	if (status_reg & LM51772_STAT_INPUT)
+		errors |= REGULATOR_ERROR_UNDER_VOLTAGE;
+
+	return errors;
+}
+
+static int lm51772_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+{
+	struct regmap *regmap = rdev_get_regmap(rdev);
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(regmap, LM51772_REG_STATUS_BYTE, &reg);
+	if (ret)
+		return ret;
+
+	*flags = lm51772_status2err(reg);
+
+	return 0;
+}
+
+static int lm51772_irq_map_event(int irq, struct regulator_irq_data *rid,
+			     unsigned long *dev_mask)
+{
+	struct regmap *regmap = rid->data;
+	unsigned int reg, errors;
+	int ret;
+
+	ret = regmap_read(regmap, LM51772_REG_STATUS_BYTE, &reg);
+	if (ret)
+		return REGULATOR_FAILED_RETRY;
+
+	errors = lm51772_status2err(reg);
+
+	for (size_t i = 0; i < rid->num_states; i++) {
+		rid->states[i].errors = errors & rid->states[i].possible_errs;
+		rid->states[i].notifs = regulator_err2notif(rid->states[i].errors);
+
+		if (reg & LM51772_STAT_OFF)
+			rid->states[i].notifs |= REGULATOR_EVENT_DISABLE;
+
+		if (rid->states[i].errors || rid->states[i].notifs & REGULATOR_EVENT_DISABLE)
+			*dev_mask |= BIT(i);
+	}
+
+	rid->opaque = reg;
+
+	return 0;
+}
+
+static int lm51772_irq_renable(struct regulator_irq_data *rid)
+{
+	struct regmap *regmap = rid->data;
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(regmap, LM51772_REG_STATUS_BYTE, &reg);
+	if (ret)
+		return REGULATOR_FAILED_RETRY;
+
+	if (rid->opaque && rid->opaque == reg) {
+		ret = regmap_read(regmap, LM51772_REG_CLEAR_FAULTS, &reg);
+		if (ret)
+			return REGULATOR_FAILED_RETRY;
+
+		return REGULATOR_ERROR_ON;
+	}
+
+	rid->opaque = 0;
+
+	return REGULATOR_ERROR_CLEARED;
+}
+
+static int lm51772_vout_of_parse(struct device_node *np,
+				 const struct regulator_desc *desc,
+				 struct regulator_config *cfg)
+{
+	u32 sense_res_milliohms, active_discharge_strength, pcm_window_low;
+	struct lm51772_priv *priv = cfg->driver_data;
+	int ret;
+
+	ret = of_property_read_u32(np, "ti,active-discharge-strength-milliamp",
+				   &active_discharge_strength);
+	if (ret && ret != -EINVAL)
+		return ret;
+
+	if (ret == -EINVAL) {
+		active_discharge_strength = 0;
+	} else {
+		if (active_discharge_strength < 25 || active_discharge_strength > 75 ||
+		    active_discharge_strength % 25)
+			return -EINVAL;
+
+		active_discharge_strength = active_discharge_strength / 25 - 1;
+	}
+
+	priv->iset_pin = of_property_read_bool(np, "ti,iset-pin");
+
+	ret = of_property_read_u32(np, "ti,pcm-window-low-permille", &pcm_window_low);
+	if (ret && ret != -EINVAL)
+		return ret;
+
+	if (ret == -EINVAL) {
+		pcm_window_low = LM51772_PCM_WINDOW_LOW_DFLT;
+	} else {
+		if (pcm_window_low % LM51772_PCM_WINDOW_LOW_STEP_PERMILLE)
+			return -EINVAL;
+
+		pcm_window_low /= LM51772_PCM_WINDOW_LOW_STEP_PERMILLE;
+		if (pcm_window_low > LM51772_PCM_WINDOW_LOW_MAX)
+			return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "ti,sense-res-milliohm", &sense_res_milliohms);
+	if (ret && ret != -EINVAL)
+		return ret;
+
+	if (ret == -EINVAL) {
+		priv->sense_res_milliohms = LM51772_SENSE_RES_DEFAULT_MOHM;
+	} else {
+		if (!sense_res_milliohms)
+			return -EINVAL;
+
+		priv->sense_res_milliohms = sense_res_milliohms;
+	}
+
+	ret = regmap_update_bits(cfg->regmap, LM51772_REG_MFR_SPECIFIC_D2,
+				 LM51772_DISCHARGE_STRENGTH_MASK,
+				 FIELD_PREP(LM51772_DISCHARGE_STRENGTH_MASK,
+					    active_discharge_strength));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(cfg->regmap, LM51772_REG_MFR_SPECIFIC_D9,
+				 LM51772_PCM_WINDOW_LOW_MASK | LM51772_SEL_ISET_PIN,
+				 FIELD_PREP(LM51772_PCM_WINDOW_LOW_MASK, pcm_window_low) |
+				 FIELD_PREP(LM51772_SEL_ISET_PIN, priv->iset_pin));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int lm51772_of_parse(struct lm51772_priv *priv, struct device *dev, struct regmap *regmap)
+{
+	static const char *const sync_pin_modes[] = {
+		"input-rising",
+		"input-falling",
+		"output-rising",
+		"output-falling",
+	};
+	static const struct linear_range ivp_ranges[] = {
+		LINEAR_RANGE(4750000, 0x00, 0x94, 125000),
+		LINEAR_RANGE(23500000, 0x95, 0xff, 250000)
+	};
+	bool ivp, ivr, cable_drop_compensation, found;
+	unsigned int sync_pin, selector, uv;
+	const char *mode;
+	u32 ivp_thres;
+	int ret;
+
+	ivp = device_property_read_bool(dev, "ti,ivp");
+	ivr = device_property_read_bool(dev, "ti,ivr");
+	priv->nint = device_property_read_bool(dev, "ti,nint");
+	cable_drop_compensation =
+		device_property_read_bool(dev, "ti,cable-drop-compensation");
+
+	if (ivr && !ivp) {
+		dev_err(dev, "ti,ivr requires ti,ivp\n");
+		return -EINVAL;
+	}
+
+	ret = device_property_read_string(dev, "ti,sync-pin", &mode);
+	if (ret && ret != -EINVAL)
+		return ret;
+
+	if (ret == -EINVAL) {
+		sync_pin = 0;
+	} else {
+		ret = match_string(sync_pin_modes, ARRAY_SIZE(sync_pin_modes), mode);
+		if (ret < 0)
+			return -EINVAL;
+
+		sync_pin = ret;
+	}
+
+	ret = device_property_read_u32(dev, "ti,ivp-thres-millivolt", &ivp_thres);
+	if (ret && ret != -EINVAL)
+		return ret;
+
+	if (ret == -EINVAL) {
+		ivp_thres = LM51772_IVP_VOLTAGE_DFLT;
+	} else {
+		ret = linear_range_get_selector_low_array(
+			ivp_ranges, ARRAY_SIZE(ivp_ranges), ivp_thres * 1000,
+			&selector, &found);
+		if (ret || !found)
+			return -EINVAL;
+
+		ret = linear_range_get_value_array(
+			ivp_ranges, ARRAY_SIZE(ivp_ranges), selector, &uv);
+		if (ret)
+			return -EINVAL;
+
+		if (uv != ivp_thres * 1000)
+			return -EINVAL;
+
+		ivp_thres = selector;
+	}
+
+	ret = regmap_update_bits(
+		regmap, LM51772_REG_MFR_SPECIFIC_D3, LM51772_EN_IVP | LM51772_SEL_IVR,
+		FIELD_PREP(LM51772_EN_IVP, ivp) | FIELD_PREP(LM51772_SEL_IVR, ivr));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(regmap, LM51772_REG_MFR_SPECIFIC_D6, LM51772_CONFIG_SYNC_PIN_MASK,
+				 FIELD_PREP(LM51772_CONFIG_SYNC_PIN_MASK, sync_pin));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(regmap, LM51772_REG_MFR_SPECIFIC_D8, LM51772_EN_CDC,
+				 FIELD_PREP(LM51772_EN_CDC, cable_drop_compensation));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(regmap, LM51772_REG_IVP_VOLTAGE, ivp_thres);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct regmap_range lm51772_readable_ranges[] = {
+	regmap_reg_range(LM51772_REG_CLEAR_FAULTS, LM51772_REG_CLEAR_FAULTS),
+	regmap_reg_range(LM51772_REG_ILIM_THRESHOLD, LM51772_REG_ILIM_THRESHOLD),
+	regmap_reg_range(LM51772_REG_VOUT_TARGET1_LSB, LM51772_REG_VOUT_TARGET1_MSB),
+	regmap_reg_range(LM51772_REG_PD_STATUS_0, LM51772_REG_PD_STATUS_0),
+	regmap_reg_range(LM51772_REG_STATUS_BYTE, LM51772_REG_STATUS_BYTE),
+	regmap_reg_range(LM51772_REG_USB_PD_CONTROL_0, LM51772_REG_USB_PD_CONTROL_0),
+	regmap_reg_range(LM51772_REG_MFR_SPECIFIC_D0, LM51772_REG_IVP_VOLTAGE)
+};
+
+static const struct regmap_access_table lm51772_readable_table = {
+	.yes_ranges = lm51772_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(lm51772_readable_ranges)
+};
+
+static const struct regmap_range lm51772_writeable_ranges[] = {
+	regmap_reg_range(LM51772_REG_CLEAR_FAULTS, LM51772_REG_CLEAR_FAULTS),
+	regmap_reg_range(LM51772_REG_STATUS_BYTE, LM51772_REG_STATUS_BYTE),
+	regmap_reg_range(LM51772_REG_ILIM_THRESHOLD, LM51772_REG_ILIM_THRESHOLD),
+	regmap_reg_range(LM51772_REG_VOUT_TARGET1_LSB, LM51772_REG_VOUT_TARGET1_MSB),
+	regmap_reg_range(LM51772_REG_USB_PD_CONTROL_0, LM51772_REG_USB_PD_CONTROL_0),
+	regmap_reg_range(LM51772_REG_MFR_SPECIFIC_D0, LM51772_REG_IVP_VOLTAGE)
+};
+
+static const struct regmap_access_table lm51772_writeable_table = {
+	.yes_ranges = lm51772_writeable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(lm51772_writeable_ranges)
+};
+
+static const struct regmap_range lm51772_volatile_ranges[] = {
+	regmap_reg_range(LM51772_REG_PD_STATUS_0, LM51772_REG_PD_STATUS_0),
+	regmap_reg_range(LM51772_REG_STATUS_BYTE, LM51772_REG_STATUS_BYTE)
+};
+
+static const struct regmap_access_table lm51772_volatile_table = {
+	.yes_ranges = lm51772_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(lm51772_volatile_ranges)
+};
+
+static const struct reg_default lm51772_reg_defaults[] = {
+	{ LM51772_REG_CLEAR_FAULTS, 0x00 },	{ LM51772_REG_ILIM_THRESHOLD, 0x64 },
+	{ LM51772_REG_VOUT_TARGET1_LSB, 0x58 }, { LM51772_REG_VOUT_TARGET1_MSB, 0x02 },
+	{ LM51772_REG_STATUS_BYTE, 0x00 },	{ LM51772_REG_USB_PD_CONTROL_0, 0x01 },
+	{ LM51772_REG_MFR_SPECIFIC_D0, 0x32 },	{ LM51772_REG_MFR_SPECIFIC_D1, 0x09 },
+	{ LM51772_REG_MFR_SPECIFIC_D2, 0x40 },	{ LM51772_REG_MFR_SPECIFIC_D3, 0x20 },
+	{ LM51772_REG_MFR_SPECIFIC_D4, 0x03 },	{ LM51772_REG_MFR_SPECIFIC_D5, 0x3F },
+	{ LM51772_REG_MFR_SPECIFIC_D6, 0x15 },	{ LM51772_REG_MFR_SPECIFIC_D7, 0x28 },
+	{ LM51772_REG_MFR_SPECIFIC_D8, 0x84 },	{ LM51772_REG_MFR_SPECIFIC_D9, 0x2C },
+	{ LM51772_REG_IVP_VOLTAGE, 0xFF }
+};
+
+static const struct regmap_config lm51772_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.rd_table = &lm51772_readable_table,
+	.wr_table = &lm51772_writeable_table,
+	.volatile_table = &lm51772_volatile_table,
+	.max_register = LM51772_REG_IVP_VOLTAGE,
+	.reg_defaults = lm51772_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(lm51772_reg_defaults),
+	.cache_type = REGCACHE_MAPLE
+};
+
+static const struct regulator_ops lm51772_reg_ops = {
+	.list_voltage = regulator_list_voltage_pickable_linear_range,
+	.map_voltage = regulator_map_voltage_pickable_linear_range,
+	.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
+	.set_voltage_sel = lm51772_regulator_set_voltage_sel,
+	.set_over_voltage_protection = lm51772_regulator_set_over_voltage_protection,
+	.set_thermal_protection = lm51772_regulator_set_thermal_protection,
+	.set_active_discharge = regulator_set_active_discharge_regmap,
+	.get_current_limit = lm51772_regulator_get_current_limit,
+	.set_current_limit = lm51772_regulator_set_current_limit,
+	.get_error_flags = lm51772_get_error_flags,
+	.set_ramp_delay = regulator_set_ramp_delay_regmap,
+	.is_enabled = lm51772_regulator_is_enabled,
+	.enable = lm51772_regulator_enable,
+	.disable = lm51772_regulator_disable
+};
+
+static const struct regulator_ops lm51772_fixed_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap
+};
+
+/*
+ * For < 24V, voltages are set in 10mV monotonous steps.
+ * For >= 24V, voltages are set in 20mV monotonous steps.
+ */
+static const struct linear_range lm51772_voltage_ranges[] = {
+	REGULATOR_LINEAR_RANGE(LM51772_DIV10_MIN_UV, 0, 99, 0),
+	REGULATOR_LINEAR_RANGE(LM51772_DIV10_MIN_UV, 100, 2399, LM51772_DIV10_STEP_UV),
+
+	REGULATOR_LINEAR_RANGE(LM51772_DIV20_MIN_UV, 0, 164, 0),
+	REGULATOR_LINEAR_RANGE(LM51772_DIV20_MIN_UV, 165, 2400, LM51772_DIV20_STEP_UV)
+};
+
+static const unsigned int lm51772_voltage_ranges_sel[] = { 0,  0, 1, 1 };
+
+static const unsigned int lm51772_ramp_delays[] = { 40000, 20000, 1000, 500 };
+
+static const struct regulator_desc lm51772_regulator_descs[] = {
+	{
+		.name = "vout",
+		.of_match = of_match_ptr("vout"),
+		.of_parse_cb = lm51772_vout_of_parse,
+		.regulators_node = of_match_ptr("regulators"),
+		.id = 0,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+		.ops = &lm51772_reg_ops,
+		.linear_ranges = lm51772_voltage_ranges,
+		.linear_range_selectors_bitfield = lm51772_voltage_ranges_sel,
+		.n_linear_ranges = ARRAY_SIZE(lm51772_voltage_ranges),
+		.n_voltages = 100 + 2300 + 165 + 2236,
+		.vsel_reg = LM51772_REG_VOUT_TARGET1_LSB,
+		.vsel_mask = LM51772_VOUT_TARGET1_MASK,
+		.vsel_reg_oversize = true,
+		.vsel_range_reg = LM51772_REG_MFR_SPECIFIC_D8,
+		.vsel_range_mask = LM51772_SEL_FB_DIV20,
+		.active_discharge_reg = LM51772_REG_USB_PD_CONTROL_0,
+		.active_discharge_mask = LM51772_FORCE_DISCH,
+		.active_discharge_on = FIELD_PREP_CONST(LM51772_FORCE_DISCH, 1),
+		.active_discharge_off = FIELD_PREP_CONST(LM51772_FORCE_DISCH, 0),
+		.ramp_reg = LM51772_REG_MFR_SPECIFIC_D2,
+		.ramp_mask = LM51772_DVS_SLEW_RAMP_MASK,
+		.ramp_delay_table = lm51772_ramp_delays,
+		.n_ramp_values = ARRAY_SIZE(lm51772_ramp_delays)
+	},
+	{
+		.name = "vcc1",
+		.of_match = of_match_ptr("vcc1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.id = 1,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+		.ops = &lm51772_fixed_ops,
+		.fixed_uV = LM51772_VCC1_LDO_UV,
+		.n_voltages = LM51772_VCC1_LDO_NV,
+		.enable_reg = LM51772_REG_MFR_SPECIFIC_D0,
+		.enable_mask = LM51772_EN_VCC1,
+	},
+};
+
+static int lm51772_setup_irq(struct device *dev, struct regmap *regmap, int irq,
+			     struct regulator_dev *rdev[LM51772_N_REGS], bool nint)
+{
+	int common_errs = REGULATOR_ERROR_OVER_TEMP | REGULATOR_ERROR_OVER_TEMP_WARN |
+			  REGULATOR_ERROR_FAIL | REGULATOR_ERROR_UNDER_VOLTAGE;
+	int per_rdev_errs[] = {
+		REGULATOR_ERROR_OVER_CURRENT | REGULATOR_ERROR_OVER_VOLTAGE_WARN,
+		0,
+	};
+	struct regulator_irq_desc irq_desc = {
+		.name = "lm51772-irq",
+		.data = regmap,
+		.map_event = lm51772_irq_map_event,
+		.renable = lm51772_irq_renable,
+		.irq_off_ms = 1000,
+		.reread_ms = 200,
+	};
+	void *retp;
+
+	retp = devm_regulator_irq_helper(dev, &irq_desc, irq, 0, common_errs, per_rdev_errs, rdev,
+					 LM51772_N_REGS);
+	if (IS_ERR(retp))
+		return PTR_ERR(retp);
+
+	return regmap_update_bits(regmap, LM51772_REG_MFR_SPECIFIC_D1, LM51772_EN_NINT,
+				  FIELD_PREP(LM51772_EN_NINT, nint));
+}
+
+static int lm51772_probe(struct i2c_client *client)
+{
+	struct regulator_dev *rdev[LM51772_N_REGS];
+	struct regulator_config config = {};
+	struct device *dev = &client->dev;
+	struct lm51772_priv *priv;
+	struct regmap *regmap;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return dev_err_probe(dev, -ENOMEM, "unable to allocate driver private data\n");
+
+	priv->sense_res_milliohms = LM51772_SENSE_RES_DEFAULT_MOHM;
+
+	ret = devm_regulator_get_enable_optional(dev, "vin");
+	if (ret < 0 && ret != -ENODEV)
+		return dev_err_probe(dev, ret, "unable to get vin regulator\n");
+
+	regmap = devm_regmap_init_i2c(client, &lm51772_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(dev, PTR_ERR(regmap), "unable to init regmap\n");
+
+	config.dev = dev;
+	config.driver_data = priv;
+	config.regmap = regmap;
+
+	ret = lm51772_of_parse(priv, dev, regmap);
+	if (ret)
+		return dev_err_probe(dev, ret, "unable to parse dt node\n");
+
+	for (size_t i = 0; i < LM51772_N_REGS; i++) {
+		rdev[i] = devm_regulator_register(dev, &lm51772_regulator_descs[i], &config);
+		if (IS_ERR(rdev[i]))
+			return dev_err_probe(dev, PTR_ERR(rdev[i]),
+					     "unable to register regulator %s\n",
+					     lm51772_regulator_descs[i].name);
+	}
+
+	ret = lm51772_setup_irq(dev, regmap, client->irq, rdev, priv->nint);
+	if (ret)
+		return dev_err_probe(dev, ret, "unable to request irq\n");
+
+	return 0;
+}
+
+static const struct of_device_id lm51772_of_match[] = {
+	{ .compatible = "ti,lm51772" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lm51772_of_match);
+
+static const struct i2c_device_id lm51772_id[] = {
+	{ "lm51772" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm51772_id);
+
+static struct i2c_driver lm51772_driver = {
+	.driver = {
+		.name = "lm51772",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+		.of_match_table = lm51772_of_match,
+	},
+	.probe = lm51772_probe,
+	.id_table = lm51772_id
+};
+
+module_i2c_driver(lm51772_driver);
+
+MODULE_AUTHOR("Paul Louvel <paul.louvel@bootlin.com>");
+MODULE_DESCRIPTION("TI LM51772 I2C regulator driver");
+MODULE_LICENSE("GPL");

-- 
2.56.0


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

* Re: [PATCH 5/5] regulator: Add TI LM51772 support
  2026-10-21 10:27 ` [PATCH 5/5] regulator: Add TI LM51772 support Paul Louvel
@ 2026-10-21 15:10   ` Uwe Kleine-König
  0 siblings, 0 replies; 9+ messages in thread
From: Uwe Kleine-König @ 2026-10-21 15:10 UTC (permalink / raw)
  To: Paul Louvel
  Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-kernel, devicetree, Thomas Petazzoni,
	Herve Codina, Miquel Raynal

[-- Attachment #1: Type: text/plain, Size: 809 bytes --]

On Wed, Oct 21, 2026 at 12:27:46PM +0200, Paul Louvel wrote:
> +#include <linux/i2c.h>
> [...]
> +#include <linux/mod_devicetable.h>

<linux/i2c.h> already provides both of_device_id and i2c_device_id.
<linux/mod_devicetable.h> provides a plethora of more device_id structs
and thus implies a lot more build dependencies. So please drop the
latter.

> +static const struct i2c_device_id lm51772_id[] = {
> +	{ "lm51772" },

Please use a named initializer.

> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(i2c, lm51772_id);
> +
> +static struct i2c_driver lm51772_driver = {
> +	.driver = {
> +		.name = "lm51772",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +		.of_match_table = lm51772_of_match,
> +	},
> +	.probe = lm51772_probe,
> +	.id_table = lm51772_id

missing trailing comma

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property
  2026-10-21 10:27 ` [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property Paul Louvel
@ 2026-10-21 17:01   ` Conor Dooley
  2026-10-21 17:11     ` Paul Louvel
  0 siblings, 1 reply; 9+ messages in thread
From: Conor Dooley @ 2026-10-21 17:01 UTC (permalink / raw)
  To: Paul Louvel
  Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-kernel, devicetree, Thomas Petazzoni,
	Herve Codina, Miquel Raynal

[-- Attachment #1: Type: text/plain, Size: 1941 bytes --]

On Wed, Oct 21, 2026 at 12:27:42PM +0200, Paul Louvel wrote:
> Introduce a new property to immediately disable the regulator at boot
> time.
> This property is useful when a consumer device may query the state of
> the regulator at boot time, and want to disable it right away, without a
> prior regulator_enable().
> 
> Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
> ---
>  Documentation/devicetree/bindings/regulator/regulator.yaml | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/regulator/regulator.yaml b/Documentation/devicetree/bindings/regulator/regulator.yaml
> index 019aeb664cae..2da8f70d7e7a 100644
> --- a/Documentation/devicetree/bindings/regulator/regulator.yaml
> +++ b/Documentation/devicetree/bindings/regulator/regulator.yaml
> @@ -41,6 +41,14 @@ properties:
>      description: boolean, regulator should never be disabled
>      type: boolean
>  
> +  regulator-boot-force-off:
> +    description: a regulator may be enabled by default when the system powers up.
> +      A consumer may query the regulator's state during boot and disable it if
> +      it is already enabled, potentially resulting in an unbalanced disable
> +      operation. This property ensures that the regulator is disabled at boot,
> +      regardless of its previous state.

To be honest, I'm not sure how the commentary about querying the state
or being unbalanced is relevant to the description - that sounds like
something specific to the linux regulator framework?

In fact, I'm not sure why this is needed at all? Can't you just change
the software so that it is okay to disable a regulator that has an
explicit enable count of zero?

> +    type: boolean
> +
>    regulator-boot-on:
>      description: bootloader/firmware enabled regulator.
>        It's expected that this regulator was left on by the bootloader.
> 
> -- 
> 2.56.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property
  2026-10-21 17:01   ` Conor Dooley
@ 2026-10-21 17:11     ` Paul Louvel
  0 siblings, 0 replies; 9+ messages in thread
From: Paul Louvel @ 2026-10-21 17:11 UTC (permalink / raw)
  To: Conor Dooley, Paul Louvel
  Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-kernel, devicetree, Thomas Petazzoni,
	Herve Codina, Miquel Raynal

On Wed Oct 21, 2026 at 7:01 PM CEST, Conor Dooley wrote:
> On Wed, Oct 21, 2026 at 12:27:42PM +0200, Paul Louvel wrote:
>> Introduce a new property to immediately disable the regulator at boot
>> time.
>> This property is useful when a consumer device may query the state of
>> the regulator at boot time, and want to disable it right away, without a
>> prior regulator_enable().
>> 
>> Signed-off-by: Paul Louvel <paul.louvel@bootlin.com>
>> ---
>>  Documentation/devicetree/bindings/regulator/regulator.yaml | 8 ++++++++
>>  1 file changed, 8 insertions(+)
>> 
>> diff --git a/Documentation/devicetree/bindings/regulator/regulator.yaml b/Documentation/devicetree/bindings/regulator/regulator.yaml
>> index 019aeb664cae..2da8f70d7e7a 100644
>> --- a/Documentation/devicetree/bindings/regulator/regulator.yaml
>> +++ b/Documentation/devicetree/bindings/regulator/regulator.yaml
>> @@ -41,6 +41,14 @@ properties:
>>      description: boolean, regulator should never be disabled
>>      type: boolean
>>  
>> +  regulator-boot-force-off:
>> +    description: a regulator may be enabled by default when the system powers up.
>> +      A consumer may query the regulator's state during boot and disable it if
>> +      it is already enabled, potentially resulting in an unbalanced disable
>> +      operation. This property ensures that the regulator is disabled at boot,
>> +      regardless of its previous state.
>
> To be honest, I'm not sure how the commentary about querying the state
> or being unbalanced is relevant to the description - that sounds like
> something specific to the linux regulator framework?

Yes it is. Maybe I miss something, does this binding is used outside linux ?

>
> In fact, I'm not sure why this is needed at all? Can't you just change
> the software so that it is okay to disable a regulator that has an
> explicit enable count of zero?
>

Do you mean by calling regulator_force_disable() instead ? I thinked about that
but I don't think this is ideal.
Or just patch the core to allow unbalanced disable downstream ?
My intent was to provide an upstream solution to the problem I described in the
cover letter.

>> +    type: boolean
>> +
>>    regulator-boot-on:
>>      description: bootloader/firmware enabled regulator.
>>        It's expected that this regulator was left on by the bootloader.
>> 
>> -- 
>> 2.56.0
>> 





-- 
Paul Louvel, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


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

end of thread, other threads:[~2026-10-21 17:11 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-21 10:27 [PATCH 0/5] Add TI LM51772 buck-boost voltage regulator Paul Louvel
2026-10-21 10:27 ` [PATCH 1/5] regulator: dt-bindings: Add regulator-boot-force-off property Paul Louvel
2026-10-21 17:01   ` Conor Dooley
2026-10-21 17:11     ` Paul Louvel
2026-10-21 10:27 ` [PATCH 2/5] regulator: dt-bindings: Add TI LM51772 support Paul Louvel
2026-10-21 10:27 ` [PATCH 3/5] regulator: core: Add mechanism to force disable a regulator at boot Paul Louvel
2026-10-21 10:27 ` [PATCH 4/5] regulator: Support regulators where the voltage selector spans two registers Paul Louvel
2026-10-21 10:27 ` [PATCH 5/5] regulator: Add TI LM51772 support Paul Louvel
2026-10-21 15:10   ` Uwe Kleine-König

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®