summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/hwmon/index.rst1
-rw-r--r--Documentation/hwmon/vt7505.rst89
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/hwmon/pmbus/Kconfig9
-rw-r--r--drivers/hwmon/pmbus/Makefile1
-rw-r--r--drivers/hwmon/pmbus/vt7505.c287
6 files changed, 395 insertions, 0 deletions
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index a8b3e870d13e..e924e511bc60 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -282,6 +282,7 @@ Hardware Monitoring Kernel Drivers
vexpress
via686a
vt1211
+ vt7505
w83627ehf
w83627hf
w83773g
diff --git a/Documentation/hwmon/vt7505.rst b/Documentation/hwmon/vt7505.rst
new file mode 100644
index 000000000000..e56dc0055e89
--- /dev/null
+++ b/Documentation/hwmon/vt7505.rst
@@ -0,0 +1,89 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver vt7505
+====================
+
+Supported chips:
+
+ * Analog Devices MAX16545
+
+ Prefix: 'max16545'
+
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max16545b-max16545c.pdf
+
+ * Analog Devices MAX16550
+
+ Prefix: 'max16550'
+
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max16550.pdf
+
+ * Volterra VT7505
+
+ Prefix: 'vt7505'
+
+ Datasheet: Not publicly available; register-compatible with the MAX16545.
+
+Author: Georgi Vlaev <gvlaev@juniper.net>
+
+Description
+-----------
+
+This driver supports hardware monitoring for the Analog Devices MAX16545 and
+MAX16550, and the Volterra VT7505 PMBus controllers.
+
+The devices report input and output voltage, input and output current,
+input power, and temperature. All values use the PMBus direct data format.
+Peak (maximum) values for input voltage, output voltage, output current,
+input power and temperature are exposed and can be reset. Writing to any
+``*_reset_history`` attribute clears all of the device's peak values.
+
+The reported current and power values depend on the resistance of the
+external current-report resistor connected between the ILOAD pin and
+ground. This resistance can be provided via the ``adi,rload-ohms``
+device tree property; a default of 4750 ohms is used when it is absent.
+
+Sysfs entries
+-------------
+
+The limit and alarm attributes are created by the PMBus core for the limit
+registers that the device implements. See
+Documentation/hwmon/sysfs-interface.rst for the meaning of each attribute.
+
+======================= ========================================================
+in1_label "vin"
+in1_input Measured input voltage.
+in1_min Input undervoltage warning limit.
+in1_min_alarm Input undervoltage warning alarm.
+in1_lcrit Input undervoltage fault limit.
+in1_lcrit_alarm Input undervoltage fault alarm.
+in1_highest Historical maximum input voltage.
+in1_reset_history Write to reset all peak values.
+in2_label "vout1"
+in2_input Measured output voltage.
+in2_min Output undervoltage warning limit.
+in2_min_alarm Output undervoltage warning alarm.
+in2_highest Historical maximum output voltage.
+in2_reset_history Write to reset all peak values.
+curr1_label "iin"
+curr1_input Measured input current.
+curr1_alarm Input current alarm.
+curr2_label "iout1"
+curr2_input Measured output current.
+curr2_max Output overcurrent warning limit.
+curr2_max_alarm Output overcurrent warning alarm.
+curr2_highest Historical maximum output current.
+curr2_reset_history Write to reset all peak values.
+power1_label "pin"
+power1_input Measured input power.
+power1_max Input overpower warning limit.
+power1_alarm Input overpower warning alarm.
+power1_input_highest Historical maximum input power.
+power1_reset_history Write to reset all peak values.
+temp1_input Measured temperature.
+temp1_max Overtemperature warning limit.
+temp1_max_alarm Overtemperature warning alarm.
+temp1_crit Overtemperature fault limit.
+temp1_crit_alarm Overtemperature fault alarm.
+temp1_highest Historical maximum temperature.
+temp1_reset_history Write to reset all peak values.
+======================= ========================================================
diff --git a/MAINTAINERS b/MAINTAINERS
index 119bc58dbc43..2142c15b9946 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28972,6 +28972,14 @@ S: Maintained
F: Documentation/hwmon/vt1211.rst
F: drivers/hwmon/vt1211.c
+VT7505 HARDWARE MONITOR DRIVER
+M: Sanman Pradhan <psanman@juniper.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/hwmon/pmbus/adi,vt7505.yaml
+F: Documentation/hwmon/vt7505.rst
+F: drivers/hwmon/pmbus/vt7505.c
+
VT8231 HARDWARE MONITOR DRIVER
M: Roger Lucas <vt8231@hiddenengine.co.uk>
L: linux-hwmon@vger.kernel.org
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 9b4bfdd00310..becc311dacc6 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -796,6 +796,15 @@ config SENSORS_UCD9200
This driver can also be built as a module. If so, the module will
be called ucd9200.
+config SENSORS_VT7505
+ tristate "Analog Devices MAX16545, MAX16550 and Volterra VT7505"
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices MAX16545, MAX16550 and Volterra VT7505 PMBus controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called vt7505.
+
config SENSORS_XDP710
tristate "Infineon XDP710 family"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 080d366809f3..daa4b49bc90f 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
+obj-$(CONFIG_SENSORS_VT7505) += vt7505.o
obj-$(CONFIG_SENSORS_XDP710) += xdp710.o
obj-$(CONFIG_SENSORS_XDP720) += xdp720.o
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
diff --git a/drivers/hwmon/pmbus/vt7505.c b/drivers/hwmon/pmbus/vt7505.c
new file mode 100644
index 000000000000..6a66f06eddb8
--- /dev/null
+++ b/drivers/hwmon/pmbus/vt7505.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Analog Devices MAX16545/MAX16550 and
+ * Volterra VT7505 PMBus controllers.
+ *
+ * Copyright 2026 Hewlett Packard Enterprise Development LP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pmbus.h>
+
+#include "pmbus.h"
+
+#define VT7505_MFR_CONFIG 0xd0
+#define VT7505_CFG_OCP_S_FILT_MASK GENMASK(15, 14)
+
+#define VT7505_MFR_PEAK_VIN 0xd1
+#define VT7505_MFR_PEAK_IOUT 0xd2
+#define VT7505_MFR_PEAK_PIN 0xd3
+#define VT7505_MFR_PEAK_TEMP 0xd4
+#define VT7505_MFR_CLEAR_PEAKS 0xd5
+#define VT7505_MFR_PEAK_VOUT 0xfd
+
+#define VT7505_RLOAD_DEFAULT 4750
+/* Largest RLOAD for which coeff * rload / 1000 still fits in s32. */
+#define VT7505_RLOAD_MAX 283383959U
+
+struct vt7505_chip_data {
+ int temp_m;
+ int temp_b;
+ bool has_ocp_filter;
+};
+
+static const struct vt7505_chip_data max16545_data = {
+ .temp_m = 205,
+ .temp_b = 6545,
+};
+
+static const struct vt7505_chip_data max16550_data = {
+ .temp_m = 199,
+ .temp_b = 7046,
+ .has_ocp_filter = true,
+};
+
+static const struct vt7505_chip_data vt7505_data = {
+ .temp_m = 205,
+ .temp_b = 6545,
+ .has_ocp_filter = true,
+};
+
+static int vt7505_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MAX:
+ return pmbus_read_word_data(client, page, phase,
+ VT7505_MFR_PEAK_VIN);
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ return pmbus_read_word_data(client, page, phase,
+ VT7505_MFR_PEAK_IOUT);
+ case PMBUS_VIRT_READ_PIN_MAX:
+ return pmbus_read_word_data(client, page, phase,
+ VT7505_MFR_PEAK_PIN);
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ return pmbus_read_word_data(client, page, phase,
+ VT7505_MFR_PEAK_TEMP);
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ return pmbus_read_word_data(client, page, phase,
+ VT7505_MFR_PEAK_VOUT);
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ return 0;
+ default:
+ return -ENODATA;
+ }
+}
+
+static int vt7505_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ switch (reg) {
+ /*
+ * A single reset command clears all peak values. CLEAR_PEAKS is a
+ * send-byte command; the device NAKs a word or byte-data write to it.
+ */
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ return pmbus_write_byte(client, page,
+ VT7505_MFR_CLEAR_PEAKS);
+ default:
+ return -ENODATA;
+ }
+}
+
+/*
+ * None of these controllers implement the standard PMBus WRITE_PROTECT
+ * (0x10) register, so tell the core not to access it.
+ */
+static struct pmbus_platform_data vt7505_pdata = {
+ .flags = PMBUS_NO_WRITE_PROTECT,
+};
+
+static int vt7505_set_ocp_filter(struct i2c_client *client)
+{
+ u32 ocp_us;
+ u8 field;
+ int ret;
+ u16 word;
+
+ if (of_property_read_u32(client->dev.of_node, "adi,ocp-severe-filter-us",
+ &ocp_us))
+ return 0;
+
+ switch (ocp_us) {
+ case 0:
+ field = 0;
+ break;
+ case 1:
+ field = 1;
+ break;
+ case 2:
+ field = 2;
+ break;
+ case 10:
+ field = 3;
+ break;
+ default:
+ return dev_err_probe(&client->dev, -EINVAL,
+ "invalid adi,ocp-severe-filter-us value %u\n",
+ ocp_us);
+ }
+
+ ret = i2c_smbus_read_word_data(client, VT7505_MFR_CONFIG);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "failed to read MFR_CONFIG\n");
+
+ word = ret & ~VT7505_CFG_OCP_S_FILT_MASK;
+ word |= FIELD_PREP(VT7505_CFG_OCP_S_FILT_MASK, field);
+
+ ret = i2c_smbus_write_word_data(client, VT7505_MFR_CONFIG, word);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "failed to write MFR_CONFIG\n");
+
+ return 0;
+}
+
+static void vt7505_set_m(int *m, u32 rload)
+{
+ u64 val = (u64)*m * rload;
+
+ /* rload is range-checked in probe, so the result fits in int. */
+ *m = DIV_ROUND_CLOSEST_ULL(val, 1000);
+}
+
+static int vt7505_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const struct vt7505_chip_data *chip;
+ struct pmbus_driver_info *info;
+ u32 rload;
+ int ret;
+
+ chip = i2c_get_match_data(client);
+ if (!chip)
+ return -ENODEV;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ dev->platform_data = &vt7505_pdata;
+
+ /*
+ * The m coefficient used in the direct-format current and power
+ * calculations depends on RLOAD, the external current-report resistor
+ * connected between the ILOAD pin and ground. Use the default value if
+ * none is specified.
+ */
+ if (of_property_read_u32(dev->of_node, "adi,rload-ohms", &rload))
+ rload = VT7505_RLOAD_DEFAULT;
+
+ if (!rload || rload > VT7505_RLOAD_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "adi,rload-ohms must be 1-%u\n",
+ VT7505_RLOAD_MAX);
+
+ info->pages = 1;
+ info->read_word_data = vt7505_read_word_data;
+ info->write_word_data = vt7505_write_word_data;
+
+ info->format[PSC_VOLTAGE_IN] = direct;
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->format[PSC_CURRENT_IN] = direct;
+ info->format[PSC_CURRENT_OUT] = direct;
+ info->format[PSC_POWER] = direct;
+ info->format[PSC_TEMPERATURE] = direct;
+
+ /*
+ * Direct data format coefficients from the device datasheet ("PMBus
+ * Equation Parameters"). The current and power m coefficients scale
+ * with RLOAD; vt7505_set_m() applies the 1/1000 factor below, giving
+ * m = 3.824 * RLOAD for current and 0.895 * RLOAD for power. The
+ * temperature coefficients are chip specific (see the chip data).
+ */
+ info->m[PSC_VOLTAGE_IN] = 7578;
+ info->R[PSC_VOLTAGE_IN] = -2;
+ info->m[PSC_VOLTAGE_OUT] = 7578;
+ info->R[PSC_VOLTAGE_OUT] = -2;
+ info->m[PSC_CURRENT_IN] = 3824;
+ info->b[PSC_CURRENT_IN] = -4300;
+ info->R[PSC_CURRENT_IN] = -3;
+ info->m[PSC_CURRENT_OUT] = 3824;
+ info->b[PSC_CURRENT_OUT] = -4300;
+ info->R[PSC_CURRENT_OUT] = -3;
+ info->m[PSC_POWER] = 895;
+ info->b[PSC_POWER] = -9100;
+ info->R[PSC_POWER] = -2;
+
+ vt7505_set_m(&info->m[PSC_CURRENT_IN], rload);
+ vt7505_set_m(&info->m[PSC_CURRENT_OUT], rload);
+ vt7505_set_m(&info->m[PSC_POWER], rload);
+
+ info->m[PSC_TEMPERATURE] = chip->temp_m;
+ info->b[PSC_TEMPERATURE] = chip->temp_b;
+ info->R[PSC_TEMPERATURE] = -2;
+
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_PIN;
+
+ /*
+ * The severe OCP deglitch filter is programmable on the MAX16550 and
+ * the VT7505, but fixed on the MAX16545.
+ */
+ if (chip->has_ocp_filter) {
+ ret = vt7505_set_ocp_filter(client);
+ if (ret)
+ return ret;
+ }
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id vt7505_id[] = {
+ { .name = "max16545", .driver_data = (kernel_ulong_t)&max16545_data },
+ { .name = "max16550", .driver_data = (kernel_ulong_t)&max16550_data },
+ { .name = "vt7505", .driver_data = (kernel_ulong_t)&vt7505_data },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vt7505_id);
+
+static const struct of_device_id vt7505_of_match[] = {
+ { .compatible = "adi,max16545", .data = &max16545_data },
+ { .compatible = "adi,max16550", .data = &max16550_data },
+ { .compatible = "adi,vt7505", .data = &vt7505_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vt7505_of_match);
+
+static struct i2c_driver vt7505_driver = {
+ .driver = {
+ .name = "vt7505",
+ .of_match_table = vt7505_of_match,
+ },
+ .probe = vt7505_probe,
+ .id_table = vt7505_id,
+};
+module_i2c_driver(vt7505_driver);
+
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX16545/MAX16550 and Volterra VT7505");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");