summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/develop/index.rst1
-rw-r--r--doc/develop/pmbus.rst637
-rw-r--r--doc/usage/cmd/dm.rst3
3 files changed, 640 insertions, 1 deletions
diff --git a/doc/develop/index.rst b/doc/develop/index.rst
index 51fd68fa04b..0b757e8601e 100644
--- a/doc/develop/index.rst
+++ b/doc/develop/index.rst
@@ -49,6 +49,7 @@ Implementation
logging
makefiles
menus
+ pmbus
printf
smbios
spl
diff --git a/doc/develop/pmbus.rst b/doc/develop/pmbus.rst
new file mode 100644
index 00000000000..f5550541e83
--- /dev/null
+++ b/doc/develop/pmbus.rst
@@ -0,0 +1,637 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+PMBus support in U-Boot
+=======================
+
+This document describes U-Boot's PMBus 1.x support: what it is for,
+how it is structured, and how to add support for a new PMBus chipn
+either from scratch from a chip datasheet or by porting an existing
+Linux ``drivers/hwmon/pmbus/`` driver.
+
+.. contents::
+ :local:
+ :depth: 2
+
+Intent and scope
+----------------
+
+U-Boot's PMBus layer is not a hardware monitoring (hwmon) clone of
+the Linux kernel's ``drivers/hwmon/pmbus/`` subsystem. Linux owns the
+runtime side: continuous polling, sysfs publication, alert IRQ
+handling, fan control loops. U-Boot owns the boot time side. Concretely
+the U-Boot PMBus support exists to:
+
+* Identify the PMBus regulator(s) a board carries at boot:
+ ``MFR_ID``, ``MFR_MODEL``, ``MFR_REVISION`` reads, plus a quick
+ ``STATUS_WORD`` sanity check.
+* Print telemetry so an operator can confirm rail voltages, input
+ current, and temperature before handing off to the kernel. One shot
+ reads, on demand, via the ``pmbus`` and ``regulator`` U-Boot
+ commands (``pmbus dev <name>; pmbus telemetry``,
+ ``regulator dev <name>; regulator value``).
+* Decode chip alerts when a rail trips an over/under voltage,
+ over current, or thermal threshold; so a boot log shows why the
+ previous boot failed, before the kernel even comes up.
+* Optionally trim a critical rail (typically the SoC core) before
+ the kernel takes over; "set the voltage prior to a kernel boot
+ to better protect the board". This is the existing
+ ``board/nxp/common/vid.c`` AVS path and any future per board
+ speed binning trim.
+
+Out of scope, by design:
+
+* No periodic polling. No worker thread. No background updates.
+* No sysfs / procfs / userspace surface. U-Boot has none.
+* No fan speed control loop. The kernel runs that.
+* No long tail of virtual sensor registers (``PMBUS_VIRT_*``).
+* No sensor caching / update timestamps.
+
+If you find yourself wanting any of those, the answer is "wait until
+Linux comes up". Keep U-Boot's PMBus surface minimal.
+
+Architecture overview
+---------------------
+
+The framework is split into four layers (layer 3 comes in two
+flavours, 3a and 3b),
+
+::
+
+ +----------------------------------------+
+ | Layer 1: include/pmbus.h |
+ | Standard PMBus 1.x command codes, |
+ | numeric format enum, sensor class |
+ | enum, struct pmbus_driver_info, |
+ | decoder + transport prototypes, |
+ | STATUS_WORD bit names. |
+ +----------------------------------------+
+ | Layer 2: lib/pmbus.c |
+ | Format decoders (LINEAR11/LINEAR16/|
+ | DIRECT) and encoder (LINEAR16), |
+ | two stage SMBus block read helper, |
+ | STATUS_*-bit print tables, generic |
+ | dispatcher pmbus_reg2data(). |
+ +----------------------------------------+
+ | Layer 3a: drivers/power/regulator/ |
+ | <chip>.c |
+ | UCLASS_REGULATOR per chip drivers |
+ | ; one struct pmbus_driver_info |
+ | plus regulator set_value/get_value |
+ | ops. Optional: per chip identify() |
+ | hook to refine format from the |
+ | chip's own VOUT_MODE. |
+ +----------------------------------------+
+ | Layer 3b: drivers/power/regulator/ |
+ | pmbus_generic.c |
+ | Catch all driver matching |
+ | compatible = "pmbus". |
+ | Auto detects format via VOUT_MODE |
+ | and PMBUS_QUERY where supported. |
+ | Use for compliant chips with no |
+ | per chip driver yet; ship |
+ | telemetry today, write a per chip |
+ | driver later only if quirks demand |
+ | it. |
+ +----------------------------------------+
+ | Layer 4: board/<vendor>/<board>/ |
+ | <chip>_diag.c |
+ | Diagnostic commands only: |
+ | <chip>_info / <chip>_raw |
+ | Reads via regulator_get_value() |
+ | and lib/pmbus.c helpers. LINEAR / |
+ | DIRECT math NOT here. |
+ +----------------------------------------+
+
+Generic vs. board specific separation rule. Layer 1, 2, and 3
+files are tree level and platform agnostic. Their comments may
+reference only:
+
+* the PMBus 1.x specification, and
+* chip manufacturer datasheets.
+
+Never a specific board, SoC, or product. Board-specific quirks
+(a particular bus number, a particular slave address, a particular
+PCB feedback divider, board local design notes) live exclusively in
+``board/<vendor>/<board>/`` files.
+
+CLI commands
+------------
+
+The framework publishes one top level command, ``pmbus``, plus a
+vendor namespace dispatcher so per chip code can register chip
+specific extensions without touching the framework.
+
+Active device model
+~~~~~~~~~~~~~~~~~~~
+
+``pmbus`` mirrors the ``regulator`` command: select an active device
+once, then operate on it across subcommands. The active device is
+selected by I2C bus (decimal sequence number) and 7 bit address (hex,
+``0x`` optional, à la ``i2c`` convention)::
+
+ => pmbus dev 0:10
+ pmbus: active i2c0:0x10 MFR_ID="MPS" MFR_MODEL="MPQ8785" vendor=mps
+
+The framework probes ``MFR_ID`` (in both natural and reverse byte
+orders) at selection time, looks the result up in the chip match
+registry populated by per chip code via ``pmbus_register_chip()``,
+and caches the matched ``pmbus_driver_info``. All subsequent
+subcommands consume that cached metadata.
+
+Standard subcommands
+~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ pmbus list list UCLASS_REGULATOR devices (DM bound)
+ pmbus dev [<bus>:<addr>] show / select active PMBus device
+ pmbus info identification banner + driver_info
+ pmbus telemetry decoded VIN, VOUT, IIN, IOUT, TEMP
+ pmbus status decode every STATUS_* register
+ pmbus dump hex dump of every standard register
+ pmbus read <reg> [b|w|s] raw read (b=byte, w=word, s=string)
+ pmbus write <reg> <val> [b|w] raw write
+ pmbus clear [faults] issue CLEAR_FAULTS (03h)
+ pmbus vout [<uV>] read or set VOUT_COMMAND (microvolts)
+ pmbus scan [<bus>] PMBus aware probe of one or all I2C buses
+
+The ``<reg>`` argument accepts either a hexadecimal address
+(``88``, ``0x`` optional) or a symbolic name (``READ_VIN``,
+``VOUT_MODE``, ``MFR_ID``); symbolic names win when both parsed.
+``<val>`` is hexadecimal too. Only ``pmbus vout``'s microvolt
+argument and bus numbers are decimal.
+Format selectors after the register select the SMBus transaction width:
+``b`` for byte, ``w`` for 16 bit little endian word,
+``s`` for the SMBus block read used by string registers.
+
+Decoded telemetry honours the active device's ``pmbus_driver_info``;
+when no chip match has been registered, VOUT falls back to LINEAR16
+driven by ``VOUT_MODE`` and the other sensors fall back to LINEAR11.
+
+Vendor namespace
+~~~~~~~~~~~~~~~~
+
+Per chip drivers and board files publish chip specific subcommands
+in the ``pmbus <vendor> ...`` namespace by calling
+``pmbus_register_vendor_handler()`` at init time. The framework
+dispatches ``pmbus mps last``, ``pmbus mps clear last``, and
+``pmbus mps clear force`` to the MPS handler when the active
+device matches the ``mps`` vendor. Additional vendor handlers for
+``lltc``, ``renesas``, etc. land alongside the per chip drivers
+that need them.
+
+Relationship to ``vdd_override`` / ``vdd_read``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The NXP Layerscape ``vdd_override <mV>`` and ``vdd_read`` commands
+remain available in their original form for compatibility with
+existing AVS production scripts. The new ``pmbus vout`` and
+``pmbus vout <uV>`` subcommands cover the read and single shot
+write paths against the same chips, but do not implement
+``vdd_override``'s full sequence (board drop compensation, fuse
+target derivation, multi step convergence loop, atomic
+``PAGE_PLUS_WRITE`` block transaction, ``WRITE_PROTECT`` dance).
+For interactive bring up ``pmbus vout`` is sufficient; for
+production AVS, ``vdd_override`` stays canonical.
+
+Lifecycle: from board boot to Linux handoff
+-------------------------------------------
+
+The PMBus framework spans the entire U-Boot lifecycle. This section
+walks the boot timeline phase by phase, showing when each piece
+comes online and how the regulator uclass and the ``pmbus`` CLI
+converge on the same chip.
+
+Timeline overview
+~~~~~~~~~~~~~~~~~
+
+::
+
+ Phase 0 chip power on chip ramps to NVM default VOUT
+ Phase 1 boot ROM / SPL / TF-A PMBus typically untouched
+ Phase 2 U-Boot relocation, DM init regulators bound, not probed
+ Phase 3 first regulator probe chip driver runs, framework lights up
+ Phase 4 board hooks / boot scripts snapshot, AVS trim, gating
+ Phase 5 Linux handoff DT passed, chip state preserved
+ Phase 6 Linux runtime kernel pmbus driver takes over
+
+Phase 0: chip power on
+~~~~~~~~~~~~~~~~~~~~~~
+
+When the regulator chip receives its input voltage, it ramps its
+output to the VOUT default programmed into its NVM at factory
+provisioning. PMBus is silent: no software runs anywhere on the
+SoC yet.
+
+Phase 1: pre U-Boot stages
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Boot ROMs, secondary boot loaders (SPL, ARM TF-A BL2 / BL31)
+typically do not touch PMBus. They focus on PLLs, DDR PHY init,
+and bringing up enough hardware to load the next stage. Some
+platforms have a pre U-Boot AVS path in board specific TF-A
+code that writes ``VOUT_COMMAND`` from a fuse derived target;
+that path is independent of the U-Boot framework described here.
+
+Phase 2: U-Boot relocation and DM init
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After relocation, U-Boot binds device tree nodes to drivers but
+does not probe them. UCLASS_REGULATOR devices for PMBus chips
+are bound (driver and DT match resolved) but the ``.probe``
+callback has not run yet.
+
+Framework state at this point:
+
+* chip match registry: empty
+* vendor handler registry: empty
+* active device: none
+* regulator uclass: devices bound, none probed
+
+Phase 3: lazy regulator probe
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first caller into the regulator uclass for a given chip
+triggers the chip driver's ``.probe``. Typical first callers:
+
+* a board ``EVENT_SPY`` at ``EVT_LAST_STAGE_INIT`` (boot snapshot)
+* a U-Boot script: ``regulator dev <name>; regulator value``
+* the ``pmbus dev <name>`` CLI command (resolves to the regulator)
+* another DT consumer with a ``regulator-supplies`` reference
+
+The probe chain looks like this::
+
+ <chip>_probe(dev)
+ pmbus_regulator_probe_common(dev, &<chip>_info, page)
+ dev_read_addr(dev) -> reg = <addr>
+ i2c_get_chip(dev->parent, addr) -> I2C chip handle
+ priv->i2c_dev = handle
+ priv->info = &<chip>_info
+ priv->page = page
+ (page > 0) write PMBUS_PAGE
+ <chip>_identify_vout(priv->i2c_dev) [optional]
+ read VOUT_MODE; refine info->format[PSC_VOLTAGE_OUT]
+ pmbus_regulator_apply_voltage_scale(dev, fb_div) [optional]
+ write PMBUS_VOUT_SCALE_LOOP if DT property set
+ pmbus_register_chip(&<chip>_match) [idempotent]
+ pmbus_register_vendor_handler(&<chip>_op) [idempotent]
+
+Once probed, three independent surfaces are functional against
+the same chip:
+
+* the regulator uclass API (``regulator_get_value``,
+ ``regulator_set_value``, ``regulator_get_enable``,
+ ``regulator_set_enable``)
+* the ``pmbus`` CLI (chip is reachable by name through
+ ``pmbus_resolve_by_name()``, by raw ``<bus>:<addr>`` through
+ ``pmbus_set_active()``)
+* the chip's vendor extension subcommands (``pmbus <vendor> ...``)
+
+Phase 4: board hooks and boot scripts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Boards hook the boot flow at well known points to drive board
+specific PMBus behaviour. The framework prescribes none of these;
+they are conventions:
+
+* boot time rail snapshot. An ``EVENT_SPY`` at
+ ``EVT_LAST_STAGE_INIT`` reads telemetry through the regulator
+ uclass and prints a one shot summary to the console. Useful
+ for operator visibility on serial during bring up.
+
+* pre kernel rail trim (AVS). A board hook in
+ ``board_late_init`` or a custom event spy reads a fuse derived
+ target voltage and calls ``regulator_set_value_force()`` to
+ trim the SoC core rail before kernel handoff.
+
+* Linux handoff gate. A bootcmd reads the rail voltage
+ through the regulator command and refuses to boot Linux if
+ the rail is outside the expected range.
+
+Phase 5: Linux handoff
+~~~~~~~~~~~~~~~~~~~~~~
+
+When U-Boot transfers control to Linux, it passes the device
+tree (potentially patched). The DT compatible strings for PMBus
+regulators must match those in the upstream kernel binding so
+the kernel's ``drivers/hwmon/pmbus/<chip>.c`` picks them up.
+Property names are shared with the kernel binding
+(``regulator-name``, ``regulator-min-microvolt``,
+``mps,vout-fb-divider-ratio-permille``, etc.); see "DT alignment
+with Linux" below.
+
+The chip itself is left in the state U-Boot wrote it to. If
+U-Boot trimmed VOUT, the chip stays at the trimmed voltage
+through handoff. ``CLEAR_FAULTS`` state is preserved unless an
+operator explicitly issued one.
+
+Phase 6: Linux runtime
+~~~~~~~~~~~~~~~~~~~~~~
+
+Linux's ``drivers/hwmon/pmbus/pmbus_core.c`` probes the chip,
+exposes telemetry under ``/sys/class/hwmon``, and takes over
+runtime voltage management through its regulator subsystem.
+The hwmon framework polls periodically; U-Boot does not.
+
+Operation paths through the regulator uclass
+--------------------------------------------
+
+After the first probe completes, calls into the regulator uclass
+for a PMBus chip flow through the shared helper.
+
+Read VOUT::
+
+ regulator_get_value(dev)
+ -> dm_regulator_ops->get_value
+ -> pmbus_regulator_get_value(dev)
+ pmbus_regulator_select_page(priv)
+ pmbus_read_byte(priv->i2c_dev, VOUT_MODE, &mode)
+ pmbus_read_word(priv->i2c_dev, READ_VOUT, &raw)
+ pmbus_reg2data(priv->info, PSC_VOLTAGE_OUT, raw, mode)
+ -> reg2data_linear16 (mode = 0)
+ -> reg2data_direct (chip configured for DIRECT)
+ return engineering value (microvolts)
+
+Write VOUT::
+
+ regulator_set_value(dev, uV)
+ -> dm_regulator_ops->set_value
+ -> pmbus_regulator_set_value(dev, uV)
+ pmbus_regulator_select_page(priv)
+ pmbus_read_byte(VOUT_MODE)
+ check (mode == LINEAR) [LINEAR16 only today]
+ raw = pmbus_data2reg_linear16(uV, mode)
+ dm_i2c_write(VOUT_COMMAND, raw)
+
+Read / write enable bit::
+
+ regulator_get_enable(dev)
+ -> pmbus_regulator_get_enable(dev)
+ pmbus_read_byte(OPERATION) & PB_OPERATION_ON
+
+ regulator_set_enable(dev, on)
+ -> pmbus_regulator_set_enable(dev, on)
+ read OPERATION, set or clear PB_OPERATION_ON, write back
+
+Bus traffic per call:
+
+* ``get_value`` : 1 byte read (VOUT_MODE) + 1 word read (READ_VOUT)
+ + 1 byte write (PAGE) when ``page > 0``
+* ``set_value`` : 1 byte read (VOUT_MODE) + 1 word write (VOUT_COMMAND)
+ + 1 byte write (PAGE) when ``page > 0``
+* ``get_enable`` : 1 byte read (OPERATION)
+* ``set_enable`` : 1 byte read (OPERATION) + 1 byte write (OPERATION)
+
+Common board hook patterns
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Boot time rail snapshot::
+
+ static int my_board_pmbus_snapshot(void)
+ {
+ struct udevice *reg;
+
+ if (regulator_get_by_platname("MY_RAIL", &reg))
+ return 0;
+ printf("MY_RAIL: VOUT = %d uV, enabled = %d\n",
+ regulator_get_value(reg),
+ regulator_get_enable(reg));
+ return 0;
+ }
+ EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, my_board_pmbus_snapshot);
+
+The first call to ``regulator_get_value()`` triggers the chip
+driver's ``.probe``, which seeds the chip match and vendor
+extension registries. Subsequent ``pmbus`` CLI commands work
+without further setup.
+
+Pre kernel rail trim (AVS)::
+
+ int board_late_init(void)
+ {
+ struct udevice *reg;
+ int target_uV = compute_avs_target();
+
+ if (regulator_get_by_platname("VDD_CORE", &reg))
+ return 0;
+ return regulator_set_value_force(reg, target_uV);
+ }
+
+Use ``regulator_set_value_force()`` when the target may sit
+outside the DT declared ``regulator-min-microvolt`` /
+``regulator-max-microvolt`` range; force bypasses the bounds
+check.
+
+Adding a new PMBus chip from scratch
+------------------------------------
+
+Use this path when the chip has no Linux driver yet, or when you want
+to validate the U-Boot port against the datasheet alone.
+
+1. Confirm PMBus 1.x compliance level. Locate in the chip
+ datasheet:
+
+ which PMBus standard command codes the chip implements
+ (``READ_VIN``, ``READ_VOUT``, ``STATUS_WORD``, ``MFR_ID`` ...),
+ which numeric format(s) it uses for VOUT (LINEAR16 with the
+ exponent in ``VOUT_MODE``, DIRECT with chip specific m/b/R, or
+ VID with one of the documented VRM tables),
+ which numeric format it uses for VIN, IIN, IOUT, TEMPERATURE
+ (most commonly LINEAR11; some MPS / MPS derivative chips use
+ DIRECT instead),
+ how many output rails it exposes (single page parts vs.
+ multi rail PMBus pages).
+
+2. Declare a ``struct pmbus_driver_info``. Wire each sensor
+ class to one ``enum pmbus_data_format``, plus the m/b/R triple if
+ the format is DIRECT::
+
+ static struct pmbus_driver_info chipname_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = pmbus_fmt_direct,
+ .format[PSC_VOLTAGE_OUT] = pmbus_fmt_linear,
+ .format[PSC_CURRENT_OUT] = pmbus_fmt_direct,
+ .format[PSC_TEMPERATURE] = pmbus_fmt_direct,
+ .m[PSC_VOLTAGE_IN] = 4, .R[PSC_VOLTAGE_IN] = 1,
+ .m[PSC_CURRENT_OUT] = 16, .R[PSC_CURRENT_OUT] = 0,
+ .m[PSC_TEMPERATURE] = 1, .R[PSC_TEMPERATURE] = 0,
+ };
+
+3. Bind to a DT compatible. Use the lowercase ``vendor,chip``
+ tuple Linux uses (see "DT alignment with Linux" below). Add the
+ driver under ``drivers/power/regulator/`` matching the existing
+ skeleton (``fan53555.c``, ``pca9450.c``).
+
+4. Rely on the DT binding from the Linux kernel which is imported into
+ U-Boot under ``dts/upstream/Bindings/`` (for PMBus chips,
+ ``dts/upstream/Bindings/hwmon/pmbus/``).
+
+5. Smoke test. With the chip wired up in DT::
+
+ => regulator dev <name>
+ => regulator value
+ => regulator info
+
+ Numbers should match the bench measurement to within the chip's
+ advertised LSB.
+
+Porting an existing Linux PMBus driver to U-Boot
+------------------------------------------------
+
+When the chip already has a ``linux/drivers/hwmon/pmbus/<chip>.c``,
+that driver is the authoritative reference for format, coefficients,
+and quirks. Take what carries; leave what does not.
+
+What carries verbatim
+~~~~~~~~~~~~~~~~~~~~~
+
+* Numeric formats (``format[PSC_*]``).
+* DIRECT coefficients (``m[]``, ``b[]``, ``R[]``).
+* Per page count and per page functionality bits (``pages``,
+ ``func[]``).
+* VOUT_MODE driven per chip identify hook (e.g. MPQ8785's
+ switch between LINEAR16 and VID coerced DIRECT m=64 R=1).
+* Vendor register addresses for chip specific quirks (fault
+ history, scale-loop, page mapping).
+
+What does not carry
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* ``hwmon_device_register()`` and the attribute groups it consumes.
+* ``struct pmbus_data`` / ``update_lock`` / ``last_updated``
+ U-Boot has no caching layer.
+* ALERT# IRQ wiring; U-Boot is single threaded boot code.
+* Fan control hooks (``read_fan_*``, ``set_pwm_*``).
+* Virtual register handling (``PMBUS_VIRT_READ_VIN_*`` etc.); those
+ are entirely a hwmon publication aid.
+* ``module_i2c_driver(...)`` and ``MODULE_*`` macros; U-Boot uses
+ ``U_BOOT_DRIVER(...)``.
+
+Worked example: porting MPQ8785
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Linux's ``drivers/hwmon/pmbus/mpq8785.c`` is 193 LOC; the U-Boot
+equivalent is ~150 LOC.
+
+The ``mpq8785_info`` struct transcribes verbatim::
+
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct, .m[PSC_VOLTAGE_IN] = 4, .R[PSC_VOLTAGE_IN] = 1,
+ .format[PSC_CURRENT_OUT] = direct, .m[PSC_CURRENT_OUT] = 16, .R[PSC_CURRENT_OUT] = 0,
+ .format[PSC_TEMPERATURE] = direct, .m[PSC_TEMPERATURE] = 1, .R[PSC_TEMPERATURE] = 0,
+
+The VOUT format is decided at probe time from VOUT_MODE bits[7:5] :
+mode 0 means LINEAR16, mode 1 or 2 means DIRECT m=64 R=1 (the chip's
+"VID" mode is coerced to DIRECT by the driver). Translate Linux's
+``mpq8785_identify()`` 1:1.
+
+The per chip quirks that carry over:
+
+* MPS NVM string byte order: chip stores ``S P M`` for the human
+ string ``MPS``. ``pmbus_read_string()`` accepts a ``reverse_bytes``
+ flag for this case.
+* ``mps,vout-fb-divider-ratio-permille`` DT property maps to
+ ``VOUT_SCALE_LOOP`` write at probe time.
+
+The quirks that do not carry over:
+
+* The ``PMBUS_VIRT_*`` virtual sensor wiring. Drop entirely.
+* The ``hwmon_chip_info`` attribute group registration.
+* The ``MODULE_AUTHOR`` / ``MODULE_LICENSE`` declarations.
+
+Using the generic ``compatible = "pmbus"`` driver
+-------------------------------------------------
+
+When a board carries a PMBus chip without a per chip U-Boot driver,
+the catch all ``drivers/power/regulator/pmbus_generic.c`` (Layer 3b)
+binds against ``compatible = "pmbus"``. It auto detects format via
+``VOUT_MODE`` and ``PMBUS_QUERY`` (where the chip supports it) and
+provides telemetry + voltage set/get against the standard PMBus 1.x
+subset.
+
+Decision tree:
+
+1. Try the generic driver first. Add the regulator node to the
+ board DT with ``compatible = "pmbus"`` plus the standard
+ regulator properties. Boot, run ``regulator value``, compare
+ against bench measurement.
+2. Switch to a per chip driver only when the generic one is
+ wrong: telemetry shows wrong values (chip uses DIRECT with
+ non default coefficients), an alert can't be decoded (chip has
+ vendor specific status bits), AVS is needed (the boot path has
+ to actively trim VOUT before kernel handoff), or the chip has an
+ ADDR-pin auto promotion / VID coercion / vendor register quirk.
+
+DT alignment with Linux
+-----------------------
+
+The same ``.dts`` file should work under both U-Boot (BL33) and Linux
+post handoff. To make that possible:
+
+* Reuse the upstream Linux compatible for every PMBus chip. Look
+ in ``linux/Documentation/devicetree/bindings/hwmon/pmbus/`` and
+ ``linux/Documentation/devicetree/bindings/regulator/``. The
+ ``<vendor>,<chip>`` tuple from the kernel binding goes into U-Boot's
+ ``of_match_table`` unchanged.
+* Reuse Linux property names verbatim: ``regulator-name``,
+ ``regulator-min-microvolt``, ``regulator-max-microvolt``,
+ ``regulator-boot-on``, ``regulator-always-on``,
+ ``mps,vout-fb-divider-ratio-permille``, etc.
+* The DT binding is the kernel's, imported under
+ ``dts/upstream/Bindings/`` (PMBus chips live in
+ ``dts/upstream/Bindings/hwmon/pmbus/``).
+
+Multi rail/multi page chips (e.g. ISL68137 with seven outputs)
+declare each rail as a child regulator node with ``reg = <page>``;
+each child binds as a UCLASS_REGULATOR with that PMBus PAGE setting
+applied at every read/write.
+
+Common pitfalls
+---------------
+
+These have all bitten contributors during nbxv3 bring up; record them
+here so the next port doesn't repeat them.
+
+* VOUT_MODE/DIRECT format confusion. Most generic PMBus call
+ sites assume LINEAR16. Several MPS chips report VOUT in DIRECT
+ format with chip specific m/b/R after a single VOUT_MODE read,
+ the same chip read at the same address produces different
+ numbers depending on the format the driver applies. Always read
+ ``VOUT_MODE`` at probe time and switch the decoder accordingly.
+ Linux's per chip ``identify()`` callbacks document the exact
+ rules; copy them rather than guessing.
+* SMBus block read protocol. Some I2C controllers strict check
+ block read transactions: the master must read the length byte
+ first, then reissue the read for the payload. Over reading a
+ fixed length and ignoring the length byte works on lenient
+ controllers but errors on strict ones. ``pmbus_read_string()``
+ does the two stage read; use it.
+* I2C bus number stability. ``uclass_get_device_by_seq()``
+ uses the DT alias index (``i2c0`` -> ``UCLASS_I2C`` seq 0) when
+ aliases are declared, otherwise falls back to probe order which
+ varies with which controllers are enabled in the defconfig.
+ Always declare DT aliases for I2C buses you reference by index.
+* ADDR-pin auto addressessing. Some chips (notably MPS parts) decode
+ their PMBus 7-bit address from an external resistor divider on
+ ADDR_VBOOT. The "default" address in the datasheet is the
+ factory fused slot; a board with a different divider or a die
+ with a different revision can land in another window. If the
+ driver hardcodes the default and the board side scan finds the
+ chip in another window, auto promote the working address rather
+ than failing the probe.
+* MFR string byte order. Most PMBus chips return ``MFR_ID``
+ characters in human order. Some MPS personalities reverse them.
+ Pass ``reverse_bytes=true`` to ``pmbus_read_string()`` for those;
+ spec compliant chips pass false.
+
+References
+----------
+
+* Linux PMBus core: ``linux/drivers/hwmon/pmbus/pmbus_core.c``,
+ decoder reference; ignore the hwmon publication and caching layers.
+* Linux PMBus header: ``linux/drivers/hwmon/pmbus/pmbus.h``; API
+ surface reference; many constants and the ``struct
+ pmbus_driver_info`` shape are mirrored verbatim into U-Boot's
+ ``include/pmbus.h``.
+* Linux DT bindings:
+ ``linux/Documentation/devicetree/bindings/hwmon/pmbus/``.
diff --git a/doc/usage/cmd/dm.rst b/doc/usage/cmd/dm.rst
index 196b22d1376..ac1c437bd9c 100644
--- a/doc/usage/cmd/dm.rst
+++ b/doc/usage/cmd/dm.rst
@@ -409,7 +409,8 @@ This example shows the abridged sandbox output::
regulator 1 [ ] sandbox_buck | | |-- buck2
regulator 2 [ ] sandbox_ldo | | |-- ldo1
regulator 3 [ ] sandbox_ldo | | |-- ldo2
- regulator 4 [ ] sandbox_buck | | `-- no_match_by_nodename
+ regulator 4 [ ] sandbox_buck | | |-- no_match_by_nodename
+ regulator 5 [ ] sandbox_ldo | | `-- ldo3
pmic 1 [ ] mc34708_pmic | `-- pmic@41
bootcount 0 [ + ] bootcount-rtc |-- bootcount@0
bootcount 1 [ ] bootcount-i2c-eeprom |-- bootcount