[PATCH v1 4/7] board: nxp: vid: introduce PMBus framework support

Vincent Jardin vjardin at free.fr
Thu Jul 2 19:15:48 CEST 2026


Wire board/nxp/common/vid.c into the <pmbus.h> framework so the legacy
NXP AVS path stops carrying its own duplicate copy of the PMBus
protocol and instead consumes the shared constants, decoders, and
transport helpers.

vid.c is the legacy NXP AVS/pre-kernel voltage-trim path for lx2160 CPUs.
It does PMBus to whichever core-rail voltage monitor the board carries:
LTC3882 or ISL68233 are selected with
CONFIG_VOL_MONITOR_LTC3882_*/CONFIG_VOL_MONITOR_ISL68233_*.

Before this change the file kept its own local PMBUS_CMD_* command-code
defines, its own inline LINEAR16 mantissa/exponent math, and called
into I2C through the I2C_READ/I2C_WRITE NXP wrappers in
board/nxp/common/i2c_common.{c,h} which is a parallel implementation of
exactly what <pmbus.h> + lib/pmbus.c provides.

The intent is to make vid.c an consumer of the new PMBus.

Compatibility with the former support:

  - CLI unchanged: vdd_override and vdd_read keep their existing
    semantics, return codes, and diagnostic output.

  - I2C transport unchanged on the wire: the framework's pmbus_*
    helpers call dm_i2c_read/dm_i2c_write: the same DM I2C
    backing that vid.c's former I2C_READ/I2C_WRITE macros already
    routed through on DM_I2C.

  - Numeric decode is bit-equivalent: pmbus_reg2data_linear16() and
    pmbus_data2reg_linear16() implement the PMBus 1.3 Part II
    mantissa/exponent.

Signed-off-by: Vincent Jardin <vjardin at free.fr>
---

 MAINTAINERS                                   |   2 +
 arch/Kconfig.nxp                              |   4 +
 board/nxp/common/vid.c                        | 104 +++++++++--------
 board/nxp/common/vid.h                        |  15 ++-
 .../regulator/lltc,ltc3882.yaml               |  91 +++++++++++++++
 .../regulator/renesas,isl68233.yaml           | 105 ++++++++++++++++++
 6 files changed, 266 insertions(+), 55 deletions(-)
 create mode 100644 doc/device-tree-bindings/regulator/lltc,ltc3882.yaml
 create mode 100644 doc/device-tree-bindings/regulator/renesas,isl68233.yaml

diff --git a/MAINTAINERS b/MAINTAINERS
index e22f1ff6641..94c342036d9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1585,8 +1585,10 @@ M:	Vincent Jardin <vjardin at free.fr>
 S:	Maintained
 F:	cmd/pmbus.c
 F:	doc/develop/pmbus.rst
+F:	doc/device-tree-bindings/regulator/lltc,ltc3882.yaml
 F:	doc/device-tree-bindings/regulator/mps,mpq8785.yaml
 F:	doc/device-tree-bindings/regulator/pmbus.yaml
+F:	doc/device-tree-bindings/regulator/renesas,isl68233.yaml
 F:	drivers/power/regulator/mpq8785.c
 F:	drivers/power/regulator/pmbus_generic.c
 F:	drivers/power/regulator/pmbus_helper.c
diff --git a/arch/Kconfig.nxp b/arch/Kconfig.nxp
index ef9087d0197..152692e3230 100644
--- a/arch/Kconfig.nxp
+++ b/arch/Kconfig.nxp
@@ -242,24 +242,28 @@ config VOL_MONITOR_IR36021_SET
 
 config VOL_MONITOR_LTC3882_READ
 	bool "Enable the LTC3882 voltage monitor read"
+	select PMBUS
 	help
 	 This option enables LTC3882 voltage monitor read
 	 functionality. It is used by the common VID driver.
 
 config VOL_MONITOR_LTC3882_SET
 	bool "Enable the LTC3882 voltage monitor set"
+	select PMBUS
 	help
 	 This option enables LTC3882 voltage monitor set
 	 functionality. It is used by the common VID driver.
 
 config VOL_MONITOR_ISL68233_READ
 	bool "Enable the ISL68233 voltage monitor read"
+	select PMBUS
 	help
 	 This option enables ISL68233 voltage monitor read
 	 functionality. It is used by the common VID driver.
 
 config VOL_MONITOR_ISL68233_SET
 	bool "Enable the ISL68233 voltage monitor set"
+	select PMBUS
 	help
 	 This option enables ISL68233 voltage monitor set
 	 functionality. It is used by the common VID driver.
diff --git a/board/nxp/common/vid.c b/board/nxp/common/vid.c
index 84cb43fad56..a91d6c9381a 100644
--- a/board/nxp/common/vid.c
+++ b/board/nxp/common/vid.c
@@ -11,6 +11,7 @@
 #include <i2c.h>
 #include <irq_func.h>
 #include <log.h>
+#include <pmbus.h>
 #include <vsprintf.h>
 #include <asm/io.h>
 #ifdef CONFIG_FSL_LSCH2
@@ -260,45 +261,38 @@ static int read_voltage_from_IR(int i2caddress)
  */
 #define VOUT_WARNING "VID: VOUT_MODE exponent has resolution worse than 1 V!\n"
 
-/* Checks the PMBus voltage monitor for the format used for voltage values */
-static int get_pmbus_multiplier(DEVICE_HANDLE_T dev)
+/*
+ * Read VOUT_MODE for downstream LINEAR16 decode/encode through the
+ * tree level <pmbus.h> helpers (pmbus_reg2data_linear16,
+ * pmbus_data2reg_linear16). Returns the raw VOUT_MODE byte on
+ * success, or 0 on bus error. Emits VOUT_WARNING on Linear mode
+ * chips with a non negative exponent (resolution >= 1 V is unusable
+ * for sub volt SoC rails) and an informational note on the
+ * unsupported VID format.
+ */
+static u8 vid_read_vout_mode(DEVICE_HANDLE_T dev)
 {
-	u8 mode;
-	int exponent, multiplier, ret;
+	u8 mode = 0;
+	int ret;
 
-	ret = I2C_READ(dev, PMBUS_CMD_VOUT_MODE, &mode, sizeof(mode));
+	ret = pmbus_read_byte(dev, PMBUS_VOUT_MODE, &mode);
 	if (ret) {
 		printf("VID: unable to determine voltage multiplier\n");
-		return 1;
+		return 0;
 	}
 
-	/* Upper 3 bits is mode, lower 5 bits is exponent */
-	exponent = (int)mode & 0x1F;
-	mode >>= 5;
-	switch (mode) {
-	case 0:
-		/* Linear, 5 bit twos component exponent */
-		if (exponent & 0x10) {
-			multiplier = 1 << (16 - (exponent & 0xF));
-		} else {
-			/* If exponent is >= 0, then resolution is 1 V! */
+	switch (mode & PB_VOUT_MODE_MODE_MASK) {
+	case PB_VOUT_MODE_LINEAR:
+		if (!(mode & 0x10))
 			printf(VOUT_WARNING);
-			multiplier = 1;
-		}
 		break;
-	case 1:
-		/* VID code identifier */
+	case PB_VOUT_MODE_VID:
 		printf("VID: custom VID codes are not supported\n");
-		multiplier = MV_PER_V;
 		break;
 	default:
-		/* Direct, in mV */
-		multiplier = MV_PER_V;
 		break;
 	}
-
-	debug("VID: calculated multiplier is %d\n", multiplier);
-	return multiplier;
+	return mode;
 }
 #endif
 
@@ -306,8 +300,8 @@ static int get_pmbus_multiplier(DEVICE_HANDLE_T dev)
 	defined(CONFIG_VOL_MONITOR_LTC3882_READ)
 static int read_voltage_from_pmbus(int i2caddress)
 {
-	int ret, multiplier, vout;
-	u8 channel = PWM_CHANNEL0;
+	int ret, vout;
+	u8 channel = PWM_CHANNEL0, vout_mode;
 	u16 vcode;
 	DEVICE_HANDLE_T dev;
 
@@ -317,25 +311,31 @@ static int read_voltage_from_pmbus(int i2caddress)
 		return ret;
 
 	/* Select the right page */
-	ret = I2C_WRITE(dev, PMBUS_CMD_PAGE, &channel, sizeof(channel));
+	ret = pmbus_write_byte(dev, PMBUS_PAGE, channel);
 	if (ret) {
 		printf("VID: failed to select VDD page %d\n", channel);
 		return ret;
 	}
 
-	/* VOUT is little endian */
-	ret = I2C_READ(dev, PMBUS_CMD_READ_VOUT, (void *)&vcode, sizeof(vcode));
+	ret = pmbus_read_word(dev, PMBUS_READ_VOUT, &vcode);
 	if (ret) {
 		printf("VID: failed to read core voltage\n");
 		return ret;
 	}
 
-	/* Scale down to the real mV */
-	multiplier = get_pmbus_multiplier(dev);
-	vout = (int)vcode;
-	/* Multiplier 1000 (direct mode) requires no change to convert */
-	if (multiplier != MV_PER_V)
-		vout = DIV_ROUND_UP(vout * MV_PER_V, multiplier);
+	/*
+	 * Decode LINEAR16 via the tree level helper from <pmbus.h>. For
+	 * non Linear VOUT_MODE settings the helper returns 0; fall back
+	 * to the historic mV pass through so existing LSCH boards keep.
+	 */
+	vout_mode = vid_read_vout_mode(dev);
+	if ((vout_mode & PB_VOUT_MODE_MODE_MASK) == PB_VOUT_MODE_LINEAR) {
+		s64 uv = pmbus_reg2data_linear16(vcode, vout_mode);
+
+		vout = (int)((uv + 500) / 1000);	/* round to mV */
+	} else {
+		vout = (int)vcode;
+	}
 	return vout - board_vdd_drop_compensation();
 }
 #endif
@@ -463,11 +463,13 @@ static int set_voltage_to_IR(int i2caddress, int vdd)
 static int set_voltage_to_pmbus(int i2caddress, int vdd)
 {
 	int ret, vdd_last, vdd_target = vdd;
-	int count = MAX_LOOP_WAIT_NEW_VOL, temp = 0, multiplier;
+	int count = MAX_LOOP_WAIT_NEW_VOL, temp = 0;
+	u8 vout_mode;
+	u16 raw;
 	unsigned char value;
 
 	/* The data to be sent with the PMBus command PAGE_PLUS_WRITE */
-	u8 buffer[5] = { 0x04, PWM_CHANNEL0, PMBUS_CMD_VOUT_COMMAND, 0, 0 };
+	u8 buffer[5] = { 0x04, PWM_CHANNEL0, PMBUS_VOUT_COMMAND, 0, 0 };
 	DEVICE_HANDLE_T dev;
 
 	/* Open device handle */
@@ -475,24 +477,28 @@ static int set_voltage_to_pmbus(int i2caddress, int vdd)
 	if (ret)
 		return ret;
 
-	/* Scale up to the proper value for the VOUT command, little endian */
-	multiplier = get_pmbus_multiplier(dev);
+	/*
+	 * Encode target mV as LINEAR16 raw via the tree level helper
+	 * from <pmbus.h>. For non Linear VOUT_MODE settings the helper
+	 * returns 0; fall back to the historic mV pass through.
+	 */
 	vdd += board_vdd_drop_compensation();
-	if (multiplier != MV_PER_V)
-		vdd = DIV_ROUND_UP(vdd * multiplier, MV_PER_V);
-	buffer[3] = vdd & 0xFF;
-	buffer[4] = (vdd & 0xFF00) >> 8;
+	vout_mode = vid_read_vout_mode(dev);
+	if ((vout_mode & PB_VOUT_MODE_MODE_MASK) == PB_VOUT_MODE_LINEAR)
+		raw = pmbus_data2reg_linear16((s64)vdd * 1000LL, vout_mode);
+	else
+		raw = (u16)vdd;
+	buffer[3] = raw & 0xFF;
+	buffer[4] = (raw & 0xFF00) >> 8;
 
 	/* Check write protect state */
-	ret = I2C_READ(dev, PMBUS_CMD_WRITE_PROTECT, (void *)&value,
-		       sizeof(value));
+	ret = pmbus_read_byte(dev, PMBUS_WRITE_PROTECT, &value);
 	if (ret)
 		goto exit;
 
 	if (value != EN_WRITE_ALL_CMD) {
 		value = EN_WRITE_ALL_CMD;
-		ret = I2C_WRITE(dev, PMBUS_CMD_WRITE_PROTECT,
-				(void *)&value, sizeof(value));
+		ret = pmbus_write_byte(dev, PMBUS_WRITE_PROTECT, value);
 		if (ret)
 			goto exit;
 	}
diff --git a/board/nxp/common/vid.h b/board/nxp/common/vid.h
index b34c080b4ba..72c1c089aa1 100644
--- a/board/nxp/common/vid.h
+++ b/board/nxp/common/vid.h
@@ -22,8 +22,9 @@
 #define IR_VDD_STEP_UP			5
 
 /* LTC3882 */
-#define PMBUS_CMD_WRITE_PROTECT         0x10
 /*
+ * PMBUS_WRITE_PROTECT (10h) provided by <pmbus.h>
+ *
  * WRITE_PROTECT command supported values
  * 0x80: Disable all writes except WRITE_PROTECT, PAGE,
  *       STORE_USER_ALL and MFR_EE_UNLOCK commands.
@@ -51,12 +52,14 @@
 #define VDD_MV_MAX			925
 #endif
 
-/* PM Bus commands code for LTC3882*/
+/*
+ * PM Bus commands code for LTC3882. PMBUS_PAGE / PMBUS_READ_VOUT /
+ * PMBUS_VOUT_MODE / PMBUS_VOUT_COMMAND are provided by <pmbus.h>.
+ * PMBUS_CMD_PAGE_PLUS_WRITE (05h) is the LTC3882 SMBus block write
+ * transaction not in <pmbus.h>'s standard subset, so keep its
+ * definition here.
+ */
 #define PWM_CHANNEL0                    0x0
-#define PMBUS_CMD_PAGE                  0x0
-#define PMBUS_CMD_READ_VOUT             0x8B
-#define PMBUS_CMD_VOUT_MODE			0x20
-#define PMBUS_CMD_VOUT_COMMAND          0x21
 #define PMBUS_CMD_PAGE_PLUS_WRITE       0x05
 
 #if defined(CONFIG_TARGET_LX2160AQDS) || defined(CONFIG_TARGET_LX2162AQDS) || \
diff --git a/doc/device-tree-bindings/regulator/lltc,ltc3882.yaml b/doc/device-tree-bindings/regulator/lltc,ltc3882.yaml
new file mode 100644
index 00000000000..7ed49d04a87
--- /dev/null
+++ b/doc/device-tree-bindings/regulator/lltc,ltc3882.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/lltc,ltc3882.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology LTC3882 dual output PMBus DC/DC controller (U-Boot subset)
+
+maintainers:
+  - Vincent Jardin <vjardin at free.fr>
+
+description: |
+  Dual output PolyPhase synchronous step down DC/DC controller with
+  digital power system management over PMBus.
+
+  This U-Boot subset mirrors the upstream Linux kernel binding at
+  linux/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml
+  (which covers the whole Linear / ADI pmbus family including the
+  LTC3882). Compatible strings, property names, and semantics are
+  identical so the same .dts file works under both U-Boot (BL33) and
+  Linux post handoff. Properties that only drive the kernel hwmon
+  publication are accepted and ignored by the U-Boot driver.
+
+  VOUT is reported in PMBus LINEAR16 format with the exponent
+  supplied by VOUT_MODE bits[4:0]. VIN, IIN, IOUT, and TEMPERATURE
+  use LINEAR11 (signed 5 bit exponent, signed 11 bit mantissa).
+
+  On NXP Layerscape boards (LX2160A class), the legacy AVS code in
+  board/nxp/common/vid.c speaks PMBus to the LTC3882 to read VOUT
+  and to write VOUT_COMMAND for pre kernel core voltage trim. That
+  path uses the legacy non DM I2C transport (fsl_i2c) and so does
+  not bind through this DT node today; the binding is published for
+  schema validation, for future U-Boot driver model migration, and
+  for matching the upstream Linux .dts.
+
+properties:
+  compatible:
+    enum:
+      - lltc,ltc3882
+
+  reg:
+    maxItems: 1
+
+  regulators:
+    type: object
+    description: |
+      Container for the two output rails the LTC3882 exposes
+      (vout0 and vout1). Each child node is a regulator described
+      by the standard regulator binding; child names indicate which
+      PMBus PAGE the rail maps to (vout0 = page 0, vout1 = page 1).
+
+    patternProperties:
+      "^vout[01]$":
+        $ref: /schemas/regulator/regulator.yaml#
+        type: object
+        unevaluatedProperties: false
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        regulator at 5e {
+            compatible = "lltc,ltc3882";
+            reg = <0x5e>;
+
+            regulators {
+                vout0 {
+                    regulator-name = "VDD_CORE";
+                    regulator-min-microvolt = < 775000>;
+                    regulator-max-microvolt = < 925000>;
+                    regulator-boot-on;
+                };
+                vout1 {
+                    regulator-name = "VDD_PLAT";
+                    regulator-min-microvolt = < 850000>;
+                    regulator-max-microvolt = <1000000>;
+                };
+            };
+        };
+    };
+...
diff --git a/doc/device-tree-bindings/regulator/renesas,isl68233.yaml b/doc/device-tree-bindings/regulator/renesas,isl68233.yaml
new file mode 100644
index 00000000000..41f18381942
--- /dev/null
+++ b/doc/device-tree-bindings/regulator/renesas,isl68233.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/renesas,isl68233.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas ISL68233 digital multiphase voltage regulator with PMBus (U-Boot subset)
+
+maintainers:
+  - Vincent Jardin <vjardin at free.fr>
+
+description: |
+  Renesas (formerly Intersil) digital multiphase voltage regulator
+  with PMBus.
+
+  This U-Boot subset mirrors the upstream Linux kernel binding at
+  linux/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml,
+  which covers the whole Intersil / Renesas ISL family including the
+  ISL68233. Compatible strings, property names, and semantics are
+  identical so the same .dts file works under both U-Boot (BL33) and
+  Linux post handoff. Properties that only drive the kernel hwmon
+  publication are accepted and ignored by the U-Boot driver.
+
+  VOUT is reported in PMBus LINEAR16 format with the exponent
+  supplied by VOUT_MODE bits[4:0]. VIN, IIN, IOUT, and TEMPERATURE
+  use LINEAR11 (signed 5 bit exponent, signed 11 bit mantissa). The
+  ISL68233 exposes multiple output rails through PMBus PAGE
+  switching; per channel sub nodes follow the upstream pattern.
+
+  On NXP Layerscape boards, the legacy AVS code in
+  board/nxp/common/vid.c speaks PMBus to the ISL68233 to read VOUT
+  and to write VOUT_COMMAND for pre kernel core voltage trim. That
+  path uses the legacy non DM I2C transport (fsl_i2c) and so does
+  not bind through this DT node today; the binding is published for
+  schema validation, for future U-Boot driver model migration, and
+  for matching the upstream Linux .dts.
+
+properties:
+  compatible:
+    enum:
+      - renesas,isl68233
+
+  reg:
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+patternProperties:
+  "^channel@([0-3])$":
+    type: object
+    description:
+      Container for properties specific to a particular channel (rail).
+
+    properties:
+      reg:
+        description: The channel (rail) index.
+        items:
+          minimum: 0
+          maximum: 3
+
+      vout-voltage-divider:
+        description: |
+          Resistances of a voltage divider placed between Vout and
+          the voltage sense (Vsense) pin for the given channel. Two
+          numbers <Rout Rtotal> which yields an adjusted Vout as
+          Vout_adj = Vout * Rtotal / Rout, given the original Vout
+          as reported by the Vsense pin.
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 2
+        maxItems: 2
+
+    required:
+      - reg
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        regulator at 60 {
+            compatible = "renesas,isl68233";
+            reg = <0x60>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            channel at 0 {
+                reg = <0>;
+                vout-voltage-divider = <1000 2000>;
+            };
+        };
+    };
+...
-- 
2.43.0



More information about the U-Boot mailing list