From c8a04a4c79a9fb99215e93884ff76b89e9947275 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Thu, 13 Jul 2023 06:16:53 +0000 Subject: [PATCH backport 6.1.42 48/85] hwmon: (pmbus) Add support for MPS Multi-phase mp2891 controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce driver for dual-loop, digital, multi-phase controller MP2891 from Monolithic Power Systems, Inc. (MPS) vendor. The MP2891 can work with MPS’s Intelli-PhaseTM products to complete the multi-phase voltage regulator (VR) solution with minimal external components. This device supports: - Two power rails. - Programmable Multi-Phase up to 16 Phases on rail 1, and a maximum of 8 phases on rail 2. Signed-off-by: Vadim Pasternak --- Documentation/hwmon/mp2891.rst | 128 ++++++++++++ drivers/hwmon/pmbus/Kconfig | 9 + drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp2891.c | 357 +++++++++++++++++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 Documentation/hwmon/mp2891.rst create mode 100644 drivers/hwmon/pmbus/mp2891.c diff --git a/Documentation/hwmon/mp2891.rst b/Documentation/hwmon/mp2891.rst new file mode 100644 index 000000000000..c4bda3d7ee8a --- /dev/null +++ b/Documentation/hwmon/mp2891.rst @@ -0,0 +1,128 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mp2891 +==================== + +Supported chips: + + * MPS MP2891 + + Prefix: 'mp2891' + +Author: + + Vadim Pasternak + +Description +----------- + +This driver implements support for Monolithic Power Systems, Inc. (MPS) +vendor dual-loop, digital, multi-phase controller MP2891. +The MP2891 can work with MPS’s Intelli-PhaseTM products to complete the +multi-phase voltage regulator (VR) solution with minimal external components. + +MP2891 is available in a QFN-56 (7mmx7mm) package. + +This device supports: + +- Two power rails. +- Programmable Multi-Phase up to 16 Phases on rail 1, and a maximum of 8 phases + on rail 2. +- PWM-VID Interface. +- Two pages for telemetry. +- Programmable pins for PMBus Address. +- Ability to store and restore device configurations. +- 200kHz to 3MHz Switching Frequency. +- Automatic Loop Compensation. +- Couple Inductor Mode. +- Supports Multi-Configuration for 6 Different Applications. +- Flexible Pulse-Width Modulation (PWM) Assignment for 2 Rails. +- Automatic Phase-Shedding (APS) to Improve Overall Efficiency. +- Phase-to-Phase Active Current Balancing with Configurable Offsets for Thermal + Balance. +- Digital Load-Line Regulation. +- Overclocking Mode by Adding Offset to VOUT. + +Device complaint with: + +- PMBus rev 1.3 interface. + +Device supports direct format for reading output current, output voltage, +input and output power and temperature. +Device supports linear format for reading input voltage and input power. + +The driver provides the next attributes for the current: + +- for current out input and maximum alarm; +- for phase current: input and label. + +The driver exports the following attributes via the 'sysfs' files, where: + +- 'n' is number of configured phases (from 1 to 10); +- index 1 for "iout"; +- indexes 2 ... 1 + n for phases. + +**curr[1-{1+n}]_input** + +**curr[1-{1+n}]_label** + +**curr1_max** + +**curr1_max_alarm** + +The driver provides the next attributes for the voltage: + +- for voltage in: input, low and high critical thresholds, low and high + critical alarms; +- for voltage out: input and high alarm; + +The driver exports the following attributes via the 'sysfs' files, where + +**in1_crit** + +**in1_crit_alarm** + +**in1_input** + +**in1_label** + +**in1_min** + +**in1_min_alarm** + +**in2_alarm** + +**in2_input** + +**in2_label** + +The driver provides the next attributes for the power: + +- for power in alarm and input. +- for power out: cap, cap alarm an input. + +The driver exports the following attributes via the 'sysfs' files, where +- indexes 1 for "pin"; +- indexes 2 for "pout"; + +**power1_alarm** + +**power1_input** + +**power1_label** + +**power2_input** + +**power2_label** + +**power2_max** + +**power2_max_alarm** + +The driver provides the next attributes for the temperature: + +**temp1_input** + +**temp1_max** + +**temp1_max_alarm** diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 89668af67206..77d67344cee4 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -299,6 +299,15 @@ config SENSORS_MP2888 This driver can also be built as a module. If so, the module will be called mp2888. +config SENSORS_MP2891 + tristate "MPS MP2891" + help + If you say yes here you get hardware monitoring support for MPS + MP2891 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2891. + config SENSORS_MP2975 tristate "MPS MP2975" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 0002dbe22d52..8e767d7b8c5b 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_MP2888) += mp2888.o +obj-$(CONFIG_SENSORS_MP2891) += mp2891.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c new file mode 100644 index 000000000000..e9e82844ee2a --- /dev/null +++ b/drivers/hwmon/pmbus/mp2891.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2891) + * + * Copyright (C) 2023 Nvidia + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +/* Vendor specific registers. */ +/* + * MP2891_MFR_SVI3_IOUT_PRT: + * bits 15:5 - reserved zeros; + * bits 4:3 - set SVI3 Vout digital filter + * b00: 5kHz + * b01: 2kHz + * b10: 1kHz + * b11: no filter + * bits 2:0 - define output current scaling selection of rail1. + * b000: 1 A/LSB + * b001: (1/32) A/LSB + * b010: (1/16) A/LSB + * b011: (1/8) A/LSB + * b100: (1/4) A/LSB + * b101: (1/2) A/LSB + * b110: 1 A/LSB + * b111: 2 A/LSB + */ +#define MP2891_MFR_SVI3_IOUT_PRT 0x65 +/* + * MP2891_MFR_VOUT_LOOP_CTRL: + * bits 15:14 define the VID step. + * b00: 6.25mV + * b01: 5mV + * b10: 2mV + * b11: 1mV + * bit 13 enable bit of 2.5mV resolution. + * b0: disable + * b1: enable + * bits 12:11 reserved zeros + * bit 10 defines rail remote sense amplifier gain: + * b0: 1 + * b1: 0.5 + * bit 9 DC reference_select + * b0: Comp EA uses Vfb and Vref + * b1: Comp EA uses Vdiff and Vref + * bit 8 enables DC loop calibration at DCM. + * b0: disable + * b1: enable + * bit 7 enables DC loop calibration both at DCM and CCM operation. + * b0: disable + * b1: enable + * bit 6 - holds DC loop when the PWM time interval meets PWM switching period condition + * set with PMBus command MFR_VR_CONFIG1 (B7h), bit [3:2]. + * b0: disable hold DC loop when PWM switching period condition meets + * b1: hold DC loop when PWM switching period condition meets + * bit 5 hold DC loop when phase count is changed. + * b0: disable hold DC loop when phase number change + * b1: hold the DC loop when phase number change. + * bit 4 hold DC loop regulation when a load transient event is detected. + * b0: disable hold DC loop when meets VFB+/- window condition + * b1: hold DC loop when meets VFB+/- window condition + * bits 3:0 set the DC loop minimal holding time in direct format. + */ +#define MP2891_MFR_VOUT_LOOP_CTRL 0xbd + +#define MP2891_VID_STEP_POS 14 +#define MP2891_VID_STEP_MASK GENMASK(MP2891_VID_STEP_POS + 1, MP2891_VID_STEP_POS) +#define MP2891_DAC_2P5MV_MASK BIT(13) +#define MP2891_IOUT_SCALE_MASK GENMASK(2, 0) + +#define MP2891_PAGE_NUM 2 +#define MP2891_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \ + PMBUS_PHASE_VIRTUAL) + +#define MP2891_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_POUT | PMBUS_PHASE_VIRTUAL) + +struct mp2891_data { + struct pmbus_driver_info info; + int vid_step[MP2891_PAGE_NUM]; + int iout_scale[MP2891_PAGE_NUM]; +}; + +#define to_mp2891_data(x) container_of(x, struct mp2891_data, info) + +static int mp2891_read_vout(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2891_data *data = to_mp2891_data(info); + + ret = pmbus_read_word_data(client, page, phase, reg); + + return ret < 0 ? ret : ret * data->vid_step[page] / 100; +} + +static int mp2891_read_iout(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2891_data *data = to_mp2891_data(info); + + ret = pmbus_read_word_data(client, page, phase, reg); + + return ret < 0 ? ret : ret * data->iout_scale[page]; +} + +static int mp2891_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * Enforce VOUT direct format, since device allows to set the + * different formats for the different rails. Conversion from + * VID to direct provided by driver internally, in case it is + * necessary. + */ + ret = PB_VOUT_MODE_DIRECT; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2891_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + switch (reg) { + case PMBUS_READ_VOUT: + return mp2891_read_vout(client, page, phase, reg); + case PMBUS_READ_IOUT: + return mp2891_read_iout(client, page, phase, reg); + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_POUT_MAX: + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_PIN_MAX: + case PMBUS_MFR_POUT_MAX: + case PMBUS_MFR_MAX_TEMP_1: + return -ENXIO; + default: + return -EINVAL; + } +} + +static int mp2891_identify_vid(struct i2c_client *client, struct mp2891_data *data, u32 reg, + int page) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + /* + * Obtain vid_step from MP2891_MFR_VOUT_LOOP_CTRL register: + * bit 13 = 1, the vid_step is below 2.5mV/LSB; + * bit 13 = 0, the vid_step is defined by bits 15:14: + * 00b - 6.25mV/LSB, 01b - 5mV/LSB, 10b - 2mV/LSB, 11b - 1mV + */ + if ((ret & MP2891_DAC_2P5MV_MASK) >> MP2891_VID_STEP_POS) { + data->vid_step[page] = 250; + return 0; + } + + switch ((ret & MP2891_VID_STEP_MASK) >> MP2891_VID_STEP_POS) { + case 0: + data->vid_step[page] = 625; + break; + case 1: + data->vid_step[page] = 500; + break; + case 2: + data->vid_step[page] = 200; + break; + default: + data->vid_step[page] = 100; + break; + } + + return 0; +} + +static int mp2891_identify_rails_vid(struct i2c_client *client, struct mp2891_data *data) +{ + int ret; + + /* Identify vid_step for rail 1. */ + ret = mp2891_identify_vid(client, data, MP2891_MFR_VOUT_LOOP_CTRL, 0); + if (ret < 0) + return ret; + + /* Identify vid_step for rail 2. */ + return mp2891_identify_vid(client, data, MP2891_MFR_VOUT_LOOP_CTRL, 1); +} + +static int +mp2891_iout_scale_get(struct i2c_client *client, struct mp2891_data *data, u32 reg, int page) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + /* + * Obtain iout_scale from the register MP2891_MFR_SVI3_IOUT_PRT, bits 2-0. + * The value is selected as below: + * 000b - 1A/LSB, 001b - (1/32)A/LSB, 010b - (1/16)A/LSB, + * 011b - (1/8)A/LSB, 100b - (1/4)A/LSB, 101b - (1/2)A/LSB + * 110b - 1A/LSB, 111b - 2A/LSB + */ + switch (ret & MP2891_IOUT_SCALE_MASK) { + case 0: + case 6: + data->iout_scale[page] = 32; + return 0; + case 1: + data->iout_scale[page] = 1; + return 0; + case 2: + data->iout_scale[page] = 2; + return 0; + case 3: + data->iout_scale[page] = 4; + return 0; + case 4: + data->iout_scale[page] = 8; + return 0; + case 5: + data->iout_scale[page] = 16; + return 0; + default: + data->iout_scale[page] = 64; + return 0; + } +} + +static int mp2891_rails_iout_scale_get(struct i2c_client *client, struct mp2891_data *data) +{ + int ret; + + /* Get iout_scale for rail 1. */ + ret = mp2891_iout_scale_get(client, data, MP2891_MFR_SVI3_IOUT_PRT, 0); + /* Get iout_scale for rail 2. */ + return ret < 0 ? ret : mp2891_iout_scale_get(client, data, MP2891_MFR_SVI3_IOUT_PRT, 1); +} + +static struct pmbus_driver_info mp2891_info = { + .pages = MP2891_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_POWER] = linear, + .m[PSC_VOLTAGE_IN] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .m[PSC_CURRENT_OUT] = 32, + .m[PSC_TEMPERATURE] = 1, + .R[PSC_VOLTAGE_IN] = 3, + .R[PSC_VOLTAGE_OUT] = 3, + .R[PSC_CURRENT_OUT] = 0, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_OUT] = 0, + .b[PSC_CURRENT_OUT] = 0, + .b[PSC_TEMPERATURE] = 0, + .func[0] = MP2891_RAIL1_FUNC, + .func[1] = MP2891_RAIL2_FUNC, + .read_word_data = mp2891_read_word_data, + .read_byte_data = mp2891_read_byte_data, +}; + +static int mp2891_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + struct mp2891_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2891_data), GFP_KERNEL); + + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2891_info, sizeof(*info)); + info = &data->info; + + /* Identify VID setting per rail - obtain the vid_step of output voltage. */ + ret = mp2891_identify_rails_vid(client, data); + if (ret < 0) + return ret; + + /* Get iout scale per rail - obtain current scale. */ + ret = mp2891_rails_iout_scale_get(client, data); + if (ret < 0) + return ret; + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id mp2891_id[] = { + {"mp2891", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2891_id); + +static const struct of_device_id __maybe_unused mp2891_of_match[] = { + {.compatible = "mps,mp2891"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2891_of_match); + +static struct i2c_driver mp2891_driver = { + .driver = { + .name = "mp2891", + .of_match_table = mp2891_of_match, + }, + .probe_new = mp2891_probe, + .id_table = mp2891_id, +}; + +module_i2c_driver(mp2891_driver); + +MODULE_DESCRIPTION("PMBus driver for MPS MP2891 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- 2.20.1