.. SPDX-License-Identifier: GPL-2.0-only
.. Copyright (c) 2026 Free Mobile - Vincent Jardin <vjardin@free.fr>

Kernel driver mpq8646
=====================

Supported chips:

  * MPS MPQ8646

    Prefix: 'mpq8646'

Author:
  - Vincent Jardin <vjardin@free.fr>

Chip-identity
-------------

Support the boards that are designed with the MPS MPQ8646 probed thanks
to``MFR_MODEL`` register.

This driver targets the MPQ8646 silicon specifically.

Description
-----------

The MPQ8646 is a fully integrated, PMBus-compatible, high-frequency,
synchronous buck converter. It offers a compact solution that
achieves up high Amps output current per phase, with excellent load
and line regulation over a wide input supply range. The chip
operates at high efficiency over a wide output current load range.

The PMBus interface provides converter configurations and key
parameters monitoring.

The device adopts MPS's proprietary multi-phase digital
constant-on-time (MCOT) control, which provides fast transient
response and eases loop stabilization. The MCOT scheme also allows
multiple devices or channels to be connected in parallel with
excellent current sharing and phase interleaving for high-current
applications.

Fully integrated protection features include over-current
protection (OCP), over-voltage protection (OVP), under-voltage
protection (UVP), and over-temperature protection (OTP).

This device is compliant with:

- PMBus rev 1.3 interface.

The driver exports the following attributes via the 'sysfs' files
for input voltage:

- in1_input
- in1_label
- in1_max
- in1_max_alarm
- in1_min
- in1_min_alarm
- in1_crit
- in1_crit_alarm

The driver provides the following attributes for output voltage:

- in2_input
- in2_label
- in2_alarm
- in2_max
- in2_max_alarm
- in2_min
- in2_min_alarm
- in2_crit
- in2_crit_alarm
- in2_lcrit
- in2_lcrit_alarm

The driver provides the following attributes for output current:

- curr1_input
- curr1_label
- curr1_max
- curr1_max_alarm
- curr1_crit
- curr1_crit_alarm

The driver provides the following attributes for temperature:

- temp1_input
- temp1_max
- temp1_max_alarm
- temp1_crit
- temp1_crit_alarm

Alarm acknowledgment
---------------------

The hwmon-class ``inX_alarm``/``currX_alarm``/``tempX_alarm`` files are
read-only in pmbus_core. The driver exposes the standard
``PMBUS_VIRT_RESET_*_HISTORY`` virtual-register channel for fault
acknowledgment: writing ``1`` to ``inX_reset_history`` /
``currX_reset_history`` / ``tempX_reset_history`` sends the chip a
``CLEAR_FAULTS`` (0x03) Send-Byte, which clears the latched
``STATUS_WORD`` / ``STATUS_VOUT`` / ``STATUS_IOUT`` /
``STATUS_INPUT`` / ``STATUS_TEMPERATURE`` bits the chip is currently
exposing.

The MPS-specific NVM post-mortem register
``PROTECTION_LAST`` (0xFB) is not cleared by this path; it can be read
(decoded) via the ``protection_last`` debugfs entry described below.

Regulator framework integration
-------------------------------

When ``CONFIG_REGULATOR=y`` is set, the chip is
exposed under ``/sys/class/regulator/`` and accepts the standard
regulator framework operations:

- ``regulator_enable()`` / ``regulator_disable()`` -> ``OPERATION`` (0x01)
- ``regulator_set_voltage()`` -> ``VOUT_COMMAND`` (0x21)
- ``regulator_get_voltage()`` -> ``READ_VOUT`` (0x8B)
- ``regulator_is_enabled()`` -> ``OPERATION`` bit-decode

The single regulator descriptor is named ``"vout"`` (page 0). For a
multi-page configuration the descriptor table can be extended in
the driver.

In-driver alarm-poll fallback
-----------------------------

On boards where the chip's ``SMBALERT#`` pin is unavailable to the
SoC, ``pmbus_core::pmbus_irq_setup`` cannot deliver SMBALERT-driven
``poll(POLLPRI)`` wakes or ``udev change@...`` events on the
``inX_alarm`` files. The driver provides a ``delayed_work``-based
polling fallback that
periodically invokes the pmbus core fault check,
``pmbus_check_and_notify_faults()``, which sends the notifications and
then clears the latched faults, exactly like the ``SMBALERT#``
interrupt path. The frequency of polling is tunable:

::

  /sys/kernel/debug/i2c/i2c-<bus>/<bus>-<addr>/alarm_poll_interval_ms

The default is 1000 ms.

Set it to 0 to disable. The worker self-suppresses
when ``client->irq != 0`` (i.e. when DT supplies an
``interrupts = <...>``  property on the regulator node), so adding
``SMBALERT#`` wiring is a zero-driver-change uplift on a future
board rev.

MPS post-mortem (PROTECTION_LAST)
---------------------------------

The MPQ8646 silicon keeps a single 16-bit NVM-backed record of the
last protection event in ``PROTECTION_LAST`` (0xFB). It survives
chip power/reset. The following debugfs entries expose it:

::

  /sys/kernel/debug/i2c/i2c-<bus>/<bus>-<addr>/protection_last     (RO)
  /sys/kernel/debug/i2c/i2c-<bus>/<bus>-<addr>/status_decoded      (RO)

``protection_last`` decodes the 16-bit value based on the datasheet
fault names (``INIT_FAULT``, ``NVM_CRC_ERROR``, ``NVM_FAULT``,
``OC_PHASE_FAULT``, ``OTP_SELF_FAULT``, ``SWITCH_PRD_FAULT``,
``VIN_OV_FAULT``, ``VOUT_OV_FAULT``, ``VOUT_UV_FAULT``,
``OC_TOT_FAULT``, ``VIN_UVLO_FAULT``, ``DRMOS_OTP``).

``status_decoded`` reads ``STATUS_WORD`` (0x79) and renders the
16 bits with MPS-extension labels (bit12 = ``NVM_SUMMARY``,
bit8 = ``WATCH_DOG``, bit0 = ``DRMOS_FAULT``) instead of the
PMBus 1.3 spec generic names.

NVMEM snapshot
--------------

When ``CONFIG_NVMEM=y`` is set, the chip's NVM-backed
observability registers are exposed as a single 16-byte read-only
``nvmem_device`` at ``/sys/bus/nvmem/devices/<i2c-name>/nvmem``.
Layout (little-endian for 16-bit fields, zero-fill on per-entry read
failure and for the reserved tail):

::

  offset 0..1   PROTECTION_LAST  (0xFB)  word
  offset 2..3   MFR_RETRY_TIMES  (0xF4)  word
  offset 4..5   MFR_CONFIG_ID    (0xC0)  word
  offset 6..7   MFR_VBOOT_CFG    (0xFC)  word
  offset 8      MFR_SILICON_REV  (0xC3)  byte
  offset 9..15  reserved (zero)

Suitable for single-``cat`` post-mortem capture by a fleet daemon.

Diagnostics and introspection
-----------------------------

When ``CONFIG_DEBUG_FS=y`` is set, the driver adds a read-only
decode surface to the client's pmbus debugfs directory,
``/sys/kernel/debug/i2c/i2c-<bus>/<bus>-<addr>/``.
The driver intentionally exposes no raw register poke/peek debugfs;
use i2c-dev (``i2cget``/``i2cset``/``i2ctransfer``) for that.

Identity / observability (read-only):

==========================  ==================  =================================
File                        PMBus / MFR cmd     Description
==========================  ==================  =================================
``mfr_config_id``           0xC0 (word)         board / SKU NVM identifier
``mfr_config_code_rev``     0xC1 (word)         NVM image revision (PART_RECOG +
                                                config code rev fields)
``mfr_silicon_rev``         0xC3 (byte)         die revision
``mfr_retry_times``         0xF4 (word)         per-fault-class recovery-mode
                                                configuration (NOT a retry
                                                count). Four 4-bit fields
                                                [15:12]=OTP, [11:8]=VOUT_OV,
                                                [7:4]=VOUT_UV, [3:0]=OCP.
                                                Each field: 0x0=latch-off,
                                                0x1..0xE=retry N times then
                                                latch off, 0xF=hiccup
                                                (retry indefinitely). The
                                                chip exposes no in-NVM
                                                retry-event counter; track
                                                transitions externally if
                                                needed (poll
                                                ``protection_last`` or watch
                                                ``inX_alarm`` / ``temp1_alarm``
                                                ``poll(POLLPRI)`` wakes).
``mfr_vboot_cfg``           0xFC (word)         ADDR/VBOOT latched at POR
==========================  ==================  =================================

Timing / UVLO knobs (read-only):

==========================  ==================  =================================
File                        PMBus cmd           Description
==========================  ==================  =================================
``vin_on``                  0x35 (word)         UVLO turn-on threshold
``vin_off``                 0x36 (word)         UVLO turn-off threshold
``ton_delay``               0x60 (word)         soft-start delay
``ton_rise``                0x61 (word)         soft-start ramp time
``toff_delay``              0x64 (word)         soft-stop delay
``toff_fall``               0x65 (word)         soft-stop ramp time
==========================  ==================  =================================

The only writable entry is ``alarm_poll_interval_ms`` (the alarm-poll
worker cadence, see above), it is driver-local and never touches the
chip.

Unsafe provisioning
-------------------

Available only when ``CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE=y``
(``default n``, it is for boards bring up only). They WRITE to the regulator.
On many designs the MPQ8646 powers the main CPU core rail, so a wrong write can
brown out the board or persist a bad setpoint into finite-cycle NVM.
WARNING: Some wrong register writes can and likely will physically damage or
destroy the chip and/or the board. An explicit warning banner is printed at probe
time when this option is enabled.

===============================  ===============  =========================================================
File                             PMBus / MFR cmd  Description
===============================  ===============  =========================================================
``store_all``                    0x15             STORE_USER_ALL Send-Byte (commit RAM config to NVM)
``restore_all``                  0x16             RESTORE_USER_ALL Send-Byte (revert RAM to last-NVM image)
``clear_protection_last``        0x08             CLEAR_LAST_FAULT Send-Byte
``clear_protection_last_force``  --               gated CLEAR_LAST_FAULT, see below
``on_off_config``                0x02 (byte)      PMBus vs CTRL-pin on/off source + active polarity
``vout_margin_high``             0x25 (word)      margin-high VOUT setpoint
``vout_margin_low``              0x26 (word)      margin-low VOUT setpoint
``mfr_pmbus_lock``               0xEE (word)      programmable PMBus write-lock
``mfr_product_rev_user``         0xC2 (word)      user-programmable product revision
===============================  ===============  =========================================================

``clear_protection_last`` writes ``CLEAR_LAST_FAULT`` (0x08) Send-Byte.
The chip silently no-ops unless ``MFR_CFG_EXT`` (0xF5) bit[6] is set.

``clear_protection_last_force`` performs the unlock with the following
six-step dancing:

1. read ``WRITE_PROTECT`` (0x10) and ``MFR_CFG_EXT`` (0xF5) for restore
2. clear ``WRITE_PROTECT`` if set
3. set ``MFR_CFG_EXT`` bit[6] = 1, preserving other bits
4. send ``CLEAR_LAST_FAULT`` (0x08)
5. restore ``MFR_CFG_EXT`` (with retry to handle the chip's
   undocumented post-NVM-write busy window)
6. restore ``WRITE_PROTECT``

Devicetree
----------

The driver uses ``compatible = "mps,mpq8646"``. There are some few optional
properties:

- ``mps,vout-fb-divider-ratio-permille`` : it writes ``VOUT_SCALE_LOOP``
  (0x29) at probe to compensate for an external resistor divider in
  the VOUT feedback path. The valid range is 11-bit.
- ``interrupts = <...>`` : if the board routes the chip's
  ``SMBALERT#`` pin to a SoC GPIO, declaring it here lights up
  ``pmbus_core::pmbus_irq_setup`` and disables the in-driver
  alarm-poll fallback
