Add U-Boot's PMBus 1.x layer: the decoder/transport library, the
pmbus CLI command and a generic DT binding.
The subsequent commits provide the UCLASS_REGULATOR adapter and per-chip
drivers.
U-Boot's PMBus support is not a hwmon clone of Linux's
drivers/hwmon/pmbus/. Linux owns the runtime side (polling, sysfs,
alert IRQs, fan loops). U-Boot owns the boot-time side in order to,
- identify the PMBus regulators a board carries: MFR_ID/
MFR_MODEL/MFR_REVISION + sanity checks.
- print telemetry (VIN/VOUT/IIN/IOUT/POUT/TEMP) so an
operator can confirm rail voltages and faults before the kernel
- decode any chip alerts (STATUS_VOUT/STATUS_IOUT/STATUS_INPUT/
STATUS_TEMPERATURE/STATUS_CML) so a boot log shows why the
previous boot failed or the board had been power cycled because
of an outage (typically over temperature or under current).
Out of scope by design: no periodic polling, no sysfs, no fan-speed
control loop, no PMBUS_VIRT_* sensor virtualisation, no caching.
If a use case needs any of those, the answer should be "wait until
Linux comes up". It shall remain a thin layer.
The constants and structural shape (command codes, status bit names,
sensor-class enum, format enum, struct pmbus_driver_info) are
mirrored from Linux drivers/hwmon/pmbus/pmbus.h verbatim. The
decoders/encoders are reimplemented from the PMBus 1.3
specification because the surrounding hwmon context (struct
pmbus_data, sysfs caching, hwmon publication) does not apply.
The main benefits:
- One framework + CLI for any board carrying PMBus regulators:
no per-board PMBus implementation required anymore.
- Boards call pmbus_print_telemetry() / pmbus_print_status_word()
directly from boot init for a snapshot, sharing all decode +
format-dispatch with the CLI.
- Linux-compatible constants and DT binding so porting an existing
drivers/hwmon/pmbus/ chip is mechanical.
- Boot-time AVS/VID rail trim reuses the same decoders and
encoders as the CLI and the regulator path: no duplicate math.
Signed-off-by: Vincent Jardin <vjardin@free.fr>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
638 lines
25 KiB
ReStructuredText
638 lines
25 KiB
ReStructuredText
.. 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", ®))
|
|
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", ®))
|
|
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/``.
|