* [PATCH v2 1/2] hwmon: (pmbus) Add support for MPS multi-phase mp2869a/mp29612a controllers
2025-06-30 11:20 [PATCH v2 0/2] hwmon: (pmbus) Add support for MPS mp2869a/mp29612a controllers tzuhao.wtmh
@ 2025-06-30 11:20 ` tzuhao.wtmh
2025-07-16 20:36 ` Guenter Roeck
2025-07-16 23:52 ` Guenter Roeck
2025-06-30 11:20 ` [PATCH v2 2/2] dt-bindings: trivial-devices: Add mp2869a/mp29612a device entry tzuhao.wtmh
2025-06-30 11:29 ` [PATCH v2 0/2] hwmon: (pmbus) Add support for MPS mp2869a/mp29612a controllers Krzysztof Kozlowski
2 siblings, 2 replies; 9+ messages in thread
From: tzuhao.wtmh @ 2025-06-30 11:20 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jean Delvare,
Guenter Roeck, Jonathan Corbet, Jonathan Cameron, Naresh Solanki,
Rodrigo Gobbi, Fabio Estevam, Michal Simek, Henry Wu,
Grant Peltier, Laurent Pinchart, Cedric Encarnacion, Nuno Sa,
Cherrence Sarip, Jerome Brunet, Leo Yang,
John Erasmus Mari Geronimo, Kim Seer Paller, Alex Vdovydchenko,
Ninad Palsule, Mariel Tinaco, devicetree, linux-kernel,
linux-hwmon, linux-doc
Cc: peteryin.openbmc
From: Henry Wu <Henry_Wu@quantatw.com>
Add support for the mp2869a and mp29612a controllers from Monolithic Power
Systems, Inc. (MPS). These are dual-loop, digital, multi-phase modulation
controllers.
Signed-off-by: Henry Wu <Henry_Wu@quantatw.com>
---
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/mp2869a.rst | 86 +++++++++
drivers/hwmon/pmbus/Kconfig | 10 ++
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/mp2869a.c | 299 ++++++++++++++++++++++++++++++++
5 files changed, 397 insertions(+)
create mode 100644 Documentation/hwmon/mp2869a.rst
create mode 100644 drivers/hwmon/pmbus/mp2869a.c
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index b45bfb4ebf30..10bf4bd77f7b 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -172,6 +172,7 @@ Hardware Monitoring Kernel Drivers
menf21bmc
mlxreg-fan
mp2856
+ mp2869a
mp2888
mp2891
mp2975
diff --git a/Documentation/hwmon/mp2869a.rst b/Documentation/hwmon/mp2869a.rst
new file mode 100644
index 000000000000..a98ccb3d630d
--- /dev/null
+++ b/Documentation/hwmon/mp2869a.rst
@@ -0,0 +1,86 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver mp2896a
+=====================
+
+Supported chips:
+
+ * MPS MP2896A
+
+ Prefix: 'mp2896a'
+
+ * MPS MP29612A
+
+ Prefix: 'mp29612a'
+
+Author:
+
+ Henry Wu <Henry_WU@quantatw.com>
+
+Description
+-----------
+
+This driver implements support for Monolithic Power Systems, Inc. (MPS)
+MP2896A, a digital, multi-phase voltage regulator controller with PMBus interface.
+
+This device:
+
+- Supports up to two power rails.
+- Supports multiple PMBus pages for telemetry and configuration.
+- Supports VOUT readout in **VID format only** (no support for direct format).
+- Supports AMD SVI3 VID protocol with 5-mV/LSB resolution (if applicable).
+- Uses vendor-specific registers for VOUT scaling and phase configuration.
+
+Device supports:
+
+- SVID interface.
+- AVSBus interface.
+
+Device compliant with:
+
+- PMBus rev 1.3 interface.
+
+Sysfs Interface
+---------------
+
+The driver provides the following sysfs attributes:
+
+**Current measurements:**
+
+- Index 1: "iin"
+- Indexes 2, 3: "iout"
+
+**curr[1-3]_alarm**
+**curr[1-3]_input**
+**curr[1-3]_label**
+
+**Voltage measurements:**
+
+- Index 1: "vin"
+- Indexes 2, 3: "vout"
+
+**in[1-3]_crit**
+**in[1-3]_crit_alarm**
+**in[1-3]_input**
+**in[1-3]_label**
+**in[1-3]_lcrit**
+**in[1-3]_lcrit_alarm**
+
+**Power measurements:**
+
+- Index 1: "pin"
+- Indexes 2, 3: "pout"
+
+**power[1-3]_alarm**
+**power[1-3]_input**
+**power[1-3]_label**
+
+**Temperature measurements:**
+
+**temp[1-2]_crit**
+**temp[1-2]_crit_alarm**
+**temp[1-2]_input**
+**temp[1-2]_max**
+**temp[1-2]_max_alarm**
+
+
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 441f984a859d..93b558761cc6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -364,6 +364,16 @@ config SENSORS_MP2856
This driver can also be built as a module. If so, the module will
be called mp2856.
+config SENSORS_MP2869A
+ tristate "MP2869A PMBus sensor"
+ depends on I2C && PMBUS
+ help
+ If you say yes here you get support for the MPS MP2869A MP29612A
+ voltage regulator via the PMBus interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called mp2869a.
+
config SENSORS_MP2888
tristate "MPS MP2888"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 29cd8a3317d2..42087d0dedbc 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_MP2856) += mp2856.o
+obj-$(CONFIG_SENSORS_MP2869A) += mp2869a.o
obj-$(CONFIG_SENSORS_MP2888) += mp2888.o
obj-$(CONFIG_SENSORS_MP2891) += mp2891.o
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
diff --git a/drivers/hwmon/pmbus/mp2869a.c b/drivers/hwmon/pmbus/mp2869a.c
new file mode 100644
index 000000000000..e61f1380dbc1
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp2869a.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MP2856A/MP29612A
+ * Monolithic Power Systems VR Controller
+ *
+ * Copyright (C) 2023 Quanta Computer lnc.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+/* Vendor specific registers. */
+#define MP2869A_VOUT_MODE 0x20
+#define MP2869A_VOUT_MODE_MASK GENMASK(7, 5)
+#define MP2869A_VOUT_MODE_VID (0 << 5)
+
+#define MP2869A_READ_VOUT 0x8b
+
+#define MP2869A_MFR_VOUT_SCALE_LOOP 0x29
+#define MP2869A_VID_RES_MASK GENMASK(12, 10)
+#define MP2869A_VOUT_SCALE_MASK GENMASK(7, 0)
+
+#define MP2869A_MAX_PHASE_RAIL1 16
+#define MP2869A_MAX_PHASE_RAIL2 8
+#define MP29612A_MAX_PHASE_RAIL1 12
+#define MP29612A_MAX_PHASE_RAIL2 6
+
+#define MP2869A_PAGE_NUM 2
+#define MP29612A_PAGE_NUM 2
+
+#define MP2869A_GAIN_BASE 32000
+
+enum chips { mp2869a = 1, mp29612a };
+
+static const int mp2869a_max_phases[][MP2869A_PAGE_NUM] = {
+ [mp2869a] = { MP2869A_MAX_PHASE_RAIL1, MP2869A_MAX_PHASE_RAIL2 },
+ [mp29612a] = { MP29612A_MAX_PHASE_RAIL1, MP29612A_MAX_PHASE_RAIL2},
+};
+
+static const struct i2c_device_id mp2869a_id[] = {
+ { "mp2869a", mp2869a },
+ { "mp29612a", mp29612a },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mp2869a_id);
+
+struct mp2869a_data {
+ struct pmbus_driver_info info;
+ int vout_format[MP2869A_PAGE_NUM];
+ int curr_sense_gain[MP2869A_PAGE_NUM];
+ const int *max_phases;
+ enum chips chip_id;
+};
+
+#define to_mp2869a_data(x) container_of(x, struct mp2869a_data, info)
+
+static int
+mp2869a_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg,
+ u16 mask)
+{
+ int ret = pmbus_read_word_data(client, page, phase, reg);
+
+ return (ret > 0) ? ret & mask : ret;
+}
+
+struct mp2869a_vout_info {
+ int vid_step_uv;
+ int vdiff_gain_ratio;
+};
+
+static int
+mp2869a_read_vid_and_scale(struct i2c_client *client, struct mp2869a_data *data,
+ int page, int phase,
+ struct mp2869a_vout_info *out)
+{
+ int ret;
+ u16 reg_val;
+ u8 vid_sel, vscale;
+
+ ret = pmbus_read_word_data(client, page, phase,
+ MP2869A_MFR_VOUT_SCALE_LOOP);
+ if (ret < 0)
+ return ret;
+
+ reg_val = (u16)ret;
+
+ /* Analyze VID Step Resolution(bits 12:10) */
+ vid_sel = (reg_val >> 10) & 0x7;
+ switch (vid_sel) {
+ case 0b000:
+ out->vid_step_uv = 6250;
+ break;
+ case 0b001:
+ out->vid_step_uv = 5000;
+ break;
+ case 0b010:
+ out->vid_step_uv = 2500;
+ break;
+ case 0b011:
+ out->vid_step_uv = 2000;
+ break;
+ case 0b100:
+ out->vid_step_uv = 1000;
+ break;
+ case 0b101:
+ /* 1000000 / 256 */
+ out->vid_step_uv = 3906;
+ break;
+ case 0b110:
+ /* 1000000 / 512 */
+ out->vid_step_uv = 1953;
+ break;
+ case 0b111:
+ /* 1000000 / 1024 */
+ out->vid_step_uv = 977;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Analyze VOUT_SCALE_LOOP(bits 7:0) */
+ vscale = reg_val & 0xff;
+ if (vscale == 0)
+ return -EINVAL;
+ /* Store as "magnification * 1000" to avoid floating point */
+ out->vdiff_gain_ratio = MP2869A_GAIN_BASE / vscale;
+
+ return 0;
+}
+
+static int
+mp2869a_read_vout(struct i2c_client *client, struct mp2869a_data *data,
+ int page, int phase, u8 reg)
+{
+ int ret;
+ int raw;
+ struct mp2869a_vout_info vout_info;
+
+ raw = mp2869a_read_word_helper(client, page, phase, reg, GENMASK(11, 0));
+ if (raw < 0)
+ return raw;
+
+ ret = mp2869a_read_vid_and_scale(client, data, page, phase, &vout_info);
+ if (ret < 0)
+ return ret;
+
+ int m = 1;
+ int R = 0;
+ int step = vout_info.vid_step_uv;
+
+ /* Let step = 10^R / m */
+ while (step % 10 == 0 && R > -6) {
+ step /= 10;
+ R--;
+ }
+ /* approximate if step ≠ 1mV, 5mV, etc */
+ m = 1000000 / vout_info.vid_step_uv;
+
+ data->info.m[PSC_VOLTAGE_OUT] = m;
+ data->info.R[PSC_VOLTAGE_OUT] = R+3;
+ data->info.b[PSC_VOLTAGE_OUT] = 0;
+
+ return raw;
+}
+
+static int
+mp2869a_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp2869a_data *data = to_mp2869a_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_READ_VOUT:
+ ret = mp2869a_read_vout(client, data, page, phase, reg);
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return ret;
+}
+
+static int
+mp2869a_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /* Enforce VOUT direct format. */
+ return PB_VOUT_MODE_DIRECT;
+ default:
+ return -ENODATA;
+ }
+}
+
+static int
+mp2869a_identify_vout_format(struct i2c_client *client,
+ struct mp2869a_data *data)
+{
+ int i, ret;
+
+ for (i = 0; i < data->info.pages; i++) {
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MP2869A_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ switch (ret & MP2869A_VOUT_MODE_MASK) {
+ case MP2869A_VOUT_MODE_VID:
+ data->vout_format[i] = vid;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static const struct pmbus_driver_info mp2869a_info = {
+ .pages = MP2869A_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = -3,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP,
+ .read_byte_data = mp2869a_read_byte_data,
+ .read_word_data = mp2869a_read_word_data,
+};
+
+static int mp2869a_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ struct mp2869a_data *data;
+ int ret;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct mp2869a_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->chip_id = (enum chips)(kernel_ulong_t)i2c_get_match_data(client);
+ data->max_phases = mp2869a_max_phases[data->chip_id];
+ info = &data->info;
+ *info = mp2869a_info;
+
+ /* Identify vout format. */
+ ret = mp2869a_identify_vout_format(client, data);
+ if (ret)
+ return ret;
+
+ /* set the device to page 0 */
+ i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct of_device_id __maybe_unused mp2869a_of_match[] = {
+ { .compatible = "mps,mp2869a", .data = (void *)mp2869a },
+ { .compatible = "mps,mp29612a", .data = (void *)mp29612a},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, mp2869a_of_match);
+
+static struct i2c_driver mp2869a_driver = {
+ .driver = {
+ .name = "mp2869a",
+ .of_match_table = mp2869a_of_match,
+ },
+ .probe = mp2869a_probe,
+ .id_table = mp2869a_id,
+};
+
+module_i2c_driver(mp2869a_driver);
+
+MODULE_AUTHOR("Henry Wu <Henry_WU@quantatw.com>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP2869A/MP29612A device");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v2 2/2] dt-bindings: trivial-devices: Add mp2869a/mp29612a device entry
2025-06-30 11:20 [PATCH v2 0/2] hwmon: (pmbus) Add support for MPS mp2869a/mp29612a controllers tzuhao.wtmh
2025-06-30 11:20 ` [PATCH v2 1/2] hwmon: (pmbus) Add support for MPS multi-phase " tzuhao.wtmh
@ 2025-06-30 11:20 ` tzuhao.wtmh
2025-06-30 11:28 ` Krzysztof Kozlowski
2025-06-30 11:29 ` [PATCH v2 0/2] hwmon: (pmbus) Add support for MPS mp2869a/mp29612a controllers Krzysztof Kozlowski
2 siblings, 1 reply; 9+ messages in thread
From: tzuhao.wtmh @ 2025-06-30 11:20 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jean Delvare,
Guenter Roeck, Jonathan Corbet, Jonathan Cameron, Rodrigo Gobbi,
Michal Simek, Fabio Estevam, Naresh Solanki, Henry Wu,
Grant Peltier, Laurent Pinchart, Cedric Encarnacion,
Mariel Tinaco, Alex Vdovydchenko, John Erasmus Mari Geronimo,
Leo Yang, Ninad Palsule, Jerome Brunet, Kim Seer Paller, Nuno Sa,
devicetree, linux-kernel, linux-hwmon, linux-doc
Cc: peteryin.openbmc
From: Henry Wu <Henry_Wu@quantatw.com>
Add trivial-devices binding for mp2869a/mp29612a to enable automatic matching
in device tree.
Signed-off-by: Henry Wu <Henry_Wu@quantatw.com>
---
Documentation/devicetree/bindings/trivial-devices.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 27930708ccd5..5657fdcabb45 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -285,10 +285,14 @@ properties:
- mps,mp2856
# Monolithic Power Systems Inc. multi-phase controller mp2857
- mps,mp2857
+ # Monolithic Power Systems Inc. multi-phase controller mp2869a
+ - mps,mp2869a
# Monolithic Power Systems Inc. multi-phase controller mp2888
- mps,mp2888
# Monolithic Power Systems Inc. multi-phase controller mp2891
- mps,mp2891
+ # Monolithic Power Systems Inc. multi-phase controller mp29612a
+ - mps,mp29612a
# Monolithic Power Systems Inc. multi-phase controller mp2993
- mps,mp2993
# Monolithic Power Systems Inc. multi-phase hot-swap controller mp5920
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v2 0/2] hwmon: (pmbus) Add support for MPS mp2869a/mp29612a controllers
2025-06-30 11:20 [PATCH v2 0/2] hwmon: (pmbus) Add support for MPS mp2869a/mp29612a controllers tzuhao.wtmh
2025-06-30 11:20 ` [PATCH v2 1/2] hwmon: (pmbus) Add support for MPS multi-phase " tzuhao.wtmh
2025-06-30 11:20 ` [PATCH v2 2/2] dt-bindings: trivial-devices: Add mp2869a/mp29612a device entry tzuhao.wtmh
@ 2025-06-30 11:29 ` Krzysztof Kozlowski
2025-07-01 8:04 ` Henry Wu
2 siblings, 1 reply; 9+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-30 11:29 UTC (permalink / raw)
To: tzuhao.wtmh, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jean Delvare, Guenter Roeck, Jonathan Corbet, Jonathan Cameron,
Fabio Estevam, Naresh Solanki, Rodrigo Gobbi, Michal Simek,
Henry Wu, Grant Peltier, Laurent Pinchart, Cedric Encarnacion,
John Erasmus Mari Geronimo, Jerome Brunet, Ninad Palsule,
Leo Yang, Kim Seer Paller, Mariel Tinaco, Nuno Sa,
Thomas Weißschuh, Alex Vdovydchenko, devicetree,
linux-kernel, linux-hwmon, linux-doc
Cc: peteryin.openbmc
On 30/06/2025 13:20, tzuhao.wtmh@gmail.com wrote:
> From: Henry Wu <Henry_Wu@quantatw.com>
>
> Add support for the mp2869a and mp29612a controllers from Monolithic Power
> Systems, Inc. (MPS). These are dual-loop, digital, multi-phase modulation
> controllers.
>
> ---
>
> Changes in v2:
> - Fix coding style issues.
Which ones? How?
> - Using the pointer to replace the memcpy.
> - Fix wrong device tree entries.
What exactly? How did you fix it? I see zero changes. Your changelog
should be specific, not vague.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 9+ messages in thread