// 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 #include #include #include #include #include #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 "); MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX16545/MAX16550 and Volterra VT7505"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("PMBUS");