162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Nvidia Technologies Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include "pmbus.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Vendor specific registers. */ 1662306a36Sopenharmony_ci#define MP2888_MFR_SYS_CONFIG 0x44 1762306a36Sopenharmony_ci#define MP2888_MFR_READ_CS1_2 0x73 1862306a36Sopenharmony_ci#define MP2888_MFR_READ_CS3_4 0x74 1962306a36Sopenharmony_ci#define MP2888_MFR_READ_CS5_6 0x75 2062306a36Sopenharmony_ci#define MP2888_MFR_READ_CS7_8 0x76 2162306a36Sopenharmony_ci#define MP2888_MFR_READ_CS9_10 0x77 2262306a36Sopenharmony_ci#define MP2888_MFR_VR_CONFIG1 0xe1 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MP2888_TOTAL_CURRENT_RESOLUTION BIT(3) 2562306a36Sopenharmony_ci#define MP2888_PHASE_CURRENT_RESOLUTION BIT(4) 2662306a36Sopenharmony_ci#define MP2888_DRMOS_KCS GENMASK(2, 0) 2762306a36Sopenharmony_ci#define MP2888_TEMP_UNIT 10 2862306a36Sopenharmony_ci#define MP2888_MAX_PHASE 10 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct mp2888_data { 3162306a36Sopenharmony_ci struct pmbus_driver_info info; 3262306a36Sopenharmony_ci int total_curr_resolution; 3362306a36Sopenharmony_ci int phase_curr_resolution; 3462306a36Sopenharmony_ci int curr_sense_gain; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define to_mp2888_data(x) container_of(x, struct mp2888_data, info) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int mp2888_read_byte_data(struct i2c_client *client, int page, int reg) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci switch (reg) { 4262306a36Sopenharmony_ci case PMBUS_VOUT_MODE: 4362306a36Sopenharmony_ci /* Enforce VOUT direct format. */ 4462306a36Sopenharmony_ci return PB_VOUT_MODE_DIRECT; 4562306a36Sopenharmony_ci default: 4662306a36Sopenharmony_ci return -ENODATA; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int 5162306a36Sopenharmony_cimp2888_current_sense_gain_and_resolution_get(struct i2c_client *client, struct mp2888_data *data) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* 5662306a36Sopenharmony_ci * Obtain DrMOS current sense gain of power stage from the register 5762306a36Sopenharmony_ci * , bits 0-2. The value is selected as below: 5862306a36Sopenharmony_ci * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other 5962306a36Sopenharmony_ci * values are reserved. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2888_MFR_SYS_CONFIG); 6262306a36Sopenharmony_ci if (ret < 0) 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci switch (ret & MP2888_DRMOS_KCS) { 6662306a36Sopenharmony_ci case 0: 6762306a36Sopenharmony_ci data->curr_sense_gain = 85; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci case 1: 7062306a36Sopenharmony_ci data->curr_sense_gain = 97; 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case 2: 7362306a36Sopenharmony_ci data->curr_sense_gain = 100; 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci case 3: 7662306a36Sopenharmony_ci data->curr_sense_gain = 50; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci default: 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Obtain resolution selector for total and phase current report and protection. 8462306a36Sopenharmony_ci * 0: original resolution; 1: half resolution (in such case phase current value should 8562306a36Sopenharmony_ci * be doubled. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci data->total_curr_resolution = (ret & MP2888_TOTAL_CURRENT_RESOLUTION) >> 3; 8862306a36Sopenharmony_ci data->phase_curr_resolution = (ret & MP2888_PHASE_CURRENT_RESOLUTION) >> 4; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int 9462306a36Sopenharmony_cimp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page, int phase, u8 reg) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int ret; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 9962306a36Sopenharmony_ci if (ret < 0) 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!((phase + 1) % 2)) 10362306a36Sopenharmony_ci ret >>= 8; 10462306a36Sopenharmony_ci ret &= 0xff; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) 10862306a36Sopenharmony_ci * where: 10962306a36Sopenharmony_ci * - Kcs is the DrMOS current sense gain of power stage, which is obtained from the 11062306a36Sopenharmony_ci * register MP2888_MFR_VR_CONFIG1, bits 13-12 with the following selection of DrMOS 11162306a36Sopenharmony_ci * (data->curr_sense_gain): 11262306a36Sopenharmony_ci * 00b - 8.5µA/A, 01b - 9.7µA/A, 1b - 10µA/A, 11b - 5µA/A. 11362306a36Sopenharmony_ci * - Rcs is the internal phase current sense resistor. This parameter depends on hardware 11462306a36Sopenharmony_ci * assembly. By default it is set to 1kΩ. In case of different assembly, user should 11562306a36Sopenharmony_ci * scale this parameter by dividing it by Rcs. 11662306a36Sopenharmony_ci * If phase current resolution bit is set to 1, READ_CSx value should be doubled. 11762306a36Sopenharmony_ci * Note, that current phase sensing, providing by the device is not accurate. This is 11862306a36Sopenharmony_ci * because sampling of current occurrence of bit weight has a big deviation, especially for 11962306a36Sopenharmony_ci * light load. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret * 200 - 19600, data->curr_sense_gain); 12262306a36Sopenharmony_ci /* Scale according to total current resolution. */ 12362306a36Sopenharmony_ci ret = (data->total_curr_resolution) ? ret * 2 : ret; 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int 12862306a36Sopenharmony_cimp2888_read_phases(struct i2c_client *client, struct mp2888_data *data, int page, int phase) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci switch (phase) { 13362306a36Sopenharmony_ci case 0 ... 1: 13462306a36Sopenharmony_ci ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS1_2); 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case 2 ... 3: 13762306a36Sopenharmony_ci ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS3_4); 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case 4 ... 5: 14062306a36Sopenharmony_ci ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS5_6); 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci case 6 ... 7: 14362306a36Sopenharmony_ci ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS7_8); 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case 8 ... 9: 14662306a36Sopenharmony_ci ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS9_10); 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci default: 14962306a36Sopenharmony_ci return -ENODATA; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int mp2888_read_word_data(struct i2c_client *client, int page, int phase, int reg) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 15762306a36Sopenharmony_ci struct mp2888_data *data = to_mp2888_data(info); 15862306a36Sopenharmony_ci int ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (reg) { 16162306a36Sopenharmony_ci case PMBUS_READ_VIN: 16262306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 16362306a36Sopenharmony_ci if (ret <= 0) 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * READ_VIN requires fixup to scale it to linear11 format. Register data format 16862306a36Sopenharmony_ci * provides 10 bits for mantissa and 6 bits for exponent. Bits 15:10 are set with 16962306a36Sopenharmony_ci * the fixed value 111011b. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci ret = (ret & GENMASK(9, 0)) | ((ret & GENMASK(31, 10)) << 1); 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case PMBUS_OT_WARN_LIMIT: 17462306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 17562306a36Sopenharmony_ci if (ret < 0) 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Chip reports limits in degrees C, but the actual temperature in 10th of 17962306a36Sopenharmony_ci * degrees C - scaling is needed to match both. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci ret *= MP2888_TEMP_UNIT; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case PMBUS_READ_IOUT: 18462306a36Sopenharmony_ci if (phase != 0xff) 18562306a36Sopenharmony_ci return mp2888_read_phases(client, data, page, phase); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 18862306a36Sopenharmony_ci if (ret < 0) 18962306a36Sopenharmony_ci return ret; 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * READ_IOUT register has unused bits 15:12 with fixed value 1110b. Clear these 19262306a36Sopenharmony_ci * bits and scale with total current resolution. Data is provided in direct format. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci ret &= GENMASK(11, 0); 19562306a36Sopenharmony_ci ret = data->total_curr_resolution ? ret * 2 : ret; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci case PMBUS_IOUT_OC_WARN_LIMIT: 19862306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 19962306a36Sopenharmony_ci if (ret < 0) 20062306a36Sopenharmony_ci return ret; 20162306a36Sopenharmony_ci ret &= GENMASK(9, 0); 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * Chip reports limits with resolution 1A or 2A, if total current resolution bit is 20462306a36Sopenharmony_ci * set 1. Actual current is reported with 0.25A or respectively 0.5A resolution. 20562306a36Sopenharmony_ci * Scaling is needed to match both. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci ret = data->total_curr_resolution ? ret * 8 : ret * 4; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci case PMBUS_READ_POUT: 21062306a36Sopenharmony_ci case PMBUS_READ_PIN: 21162306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 21262306a36Sopenharmony_ci if (ret < 0) 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci ret = data->total_curr_resolution ? ret : DIV_ROUND_CLOSEST(ret, 2); 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci case PMBUS_POUT_OP_WARN_LIMIT: 21762306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 21862306a36Sopenharmony_ci if (ret < 0) 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Chip reports limits with resolution 1W or 2W, if total current resolution bit is 22262306a36Sopenharmony_ci * set 1. Actual power is reported with 0.5W or 1W respectively resolution. Scaling 22362306a36Sopenharmony_ci * is needed to match both. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci ret = data->total_curr_resolution ? ret * 2 : ret; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * The below registers are not implemented by device or implemented not according to the 22962306a36Sopenharmony_ci * spec. Skip all of them to avoid exposing non-relevant inputs to sysfs. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci case PMBUS_OT_FAULT_LIMIT: 23262306a36Sopenharmony_ci case PMBUS_UT_WARN_LIMIT: 23362306a36Sopenharmony_ci case PMBUS_UT_FAULT_LIMIT: 23462306a36Sopenharmony_ci case PMBUS_VIN_UV_FAULT_LIMIT: 23562306a36Sopenharmony_ci case PMBUS_VOUT_UV_WARN_LIMIT: 23662306a36Sopenharmony_ci case PMBUS_VOUT_OV_WARN_LIMIT: 23762306a36Sopenharmony_ci case PMBUS_VOUT_UV_FAULT_LIMIT: 23862306a36Sopenharmony_ci case PMBUS_VOUT_OV_FAULT_LIMIT: 23962306a36Sopenharmony_ci case PMBUS_VIN_OV_WARN_LIMIT: 24062306a36Sopenharmony_ci case PMBUS_IOUT_OC_LV_FAULT_LIMIT: 24162306a36Sopenharmony_ci case PMBUS_IOUT_OC_FAULT_LIMIT: 24262306a36Sopenharmony_ci case PMBUS_POUT_MAX: 24362306a36Sopenharmony_ci case PMBUS_IOUT_UC_FAULT_LIMIT: 24462306a36Sopenharmony_ci case PMBUS_POUT_OP_FAULT_LIMIT: 24562306a36Sopenharmony_ci case PMBUS_PIN_OP_WARN_LIMIT: 24662306a36Sopenharmony_ci case PMBUS_MFR_VIN_MIN: 24762306a36Sopenharmony_ci case PMBUS_MFR_VOUT_MIN: 24862306a36Sopenharmony_ci case PMBUS_MFR_VIN_MAX: 24962306a36Sopenharmony_ci case PMBUS_MFR_VOUT_MAX: 25062306a36Sopenharmony_ci case PMBUS_MFR_IIN_MAX: 25162306a36Sopenharmony_ci case PMBUS_MFR_IOUT_MAX: 25262306a36Sopenharmony_ci case PMBUS_MFR_PIN_MAX: 25362306a36Sopenharmony_ci case PMBUS_MFR_POUT_MAX: 25462306a36Sopenharmony_ci case PMBUS_MFR_MAX_TEMP_1: 25562306a36Sopenharmony_ci return -ENXIO; 25662306a36Sopenharmony_ci default: 25762306a36Sopenharmony_ci return -ENODATA; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int mp2888_write_word_data(struct i2c_client *client, int page, int reg, u16 word) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 26662306a36Sopenharmony_ci struct mp2888_data *data = to_mp2888_data(info); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci switch (reg) { 26962306a36Sopenharmony_ci case PMBUS_OT_WARN_LIMIT: 27062306a36Sopenharmony_ci word = DIV_ROUND_CLOSEST(word, MP2888_TEMP_UNIT); 27162306a36Sopenharmony_ci /* Drop unused bits 15:8. */ 27262306a36Sopenharmony_ci word = clamp_val(word, 0, GENMASK(7, 0)); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case PMBUS_IOUT_OC_WARN_LIMIT: 27562306a36Sopenharmony_ci /* Fix limit according to total curent resolution. */ 27662306a36Sopenharmony_ci word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 8) : 27762306a36Sopenharmony_ci DIV_ROUND_CLOSEST(word, 4); 27862306a36Sopenharmony_ci /* Drop unused bits 15:10. */ 27962306a36Sopenharmony_ci word = clamp_val(word, 0, GENMASK(9, 0)); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case PMBUS_POUT_OP_WARN_LIMIT: 28262306a36Sopenharmony_ci /* Fix limit according to total curent resolution. */ 28362306a36Sopenharmony_ci word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 4) : 28462306a36Sopenharmony_ci DIV_ROUND_CLOSEST(word, 2); 28562306a36Sopenharmony_ci /* Drop unused bits 15:10. */ 28662306a36Sopenharmony_ci word = clamp_val(word, 0, GENMASK(9, 0)); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci default: 28962306a36Sopenharmony_ci return -ENODATA; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci return pmbus_write_word_data(client, page, reg, word); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int 29562306a36Sopenharmony_cimp2888_identify_multiphase(struct i2c_client *client, struct mp2888_data *data, 29662306a36Sopenharmony_ci struct pmbus_driver_info *info) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int ret; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); 30162306a36Sopenharmony_ci if (ret < 0) 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Identify multiphase number - could be from 1 to 10. */ 30562306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2888_MFR_VR_CONFIG1); 30662306a36Sopenharmony_ci if (ret <= 0) 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci info->phases[0] = ret & GENMASK(3, 0); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * The device provides a total of 10 PWM pins, and can be configured to different phase 31362306a36Sopenharmony_ci * count applications for rail. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (info->phases[0] > MP2888_MAX_PHASE) 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct pmbus_driver_info mp2888_info = { 32262306a36Sopenharmony_ci .pages = 1, 32362306a36Sopenharmony_ci .format[PSC_VOLTAGE_IN] = linear, 32462306a36Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = direct, 32562306a36Sopenharmony_ci .format[PSC_TEMPERATURE] = direct, 32662306a36Sopenharmony_ci .format[PSC_CURRENT_IN] = linear, 32762306a36Sopenharmony_ci .format[PSC_CURRENT_OUT] = direct, 32862306a36Sopenharmony_ci .format[PSC_POWER] = direct, 32962306a36Sopenharmony_ci .m[PSC_TEMPERATURE] = 1, 33062306a36Sopenharmony_ci .R[PSC_TEMPERATURE] = 1, 33162306a36Sopenharmony_ci .m[PSC_VOLTAGE_OUT] = 1, 33262306a36Sopenharmony_ci .R[PSC_VOLTAGE_OUT] = 3, 33362306a36Sopenharmony_ci .m[PSC_CURRENT_OUT] = 4, 33462306a36Sopenharmony_ci .m[PSC_POWER] = 1, 33562306a36Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | 33662306a36Sopenharmony_ci PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 33762306a36Sopenharmony_ci PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | 33862306a36Sopenharmony_ci PMBUS_PHASE_VIRTUAL, 33962306a36Sopenharmony_ci .pfunc[0] = PMBUS_HAVE_IOUT, 34062306a36Sopenharmony_ci .pfunc[1] = PMBUS_HAVE_IOUT, 34162306a36Sopenharmony_ci .pfunc[2] = PMBUS_HAVE_IOUT, 34262306a36Sopenharmony_ci .pfunc[3] = PMBUS_HAVE_IOUT, 34362306a36Sopenharmony_ci .pfunc[4] = PMBUS_HAVE_IOUT, 34462306a36Sopenharmony_ci .pfunc[5] = PMBUS_HAVE_IOUT, 34562306a36Sopenharmony_ci .pfunc[6] = PMBUS_HAVE_IOUT, 34662306a36Sopenharmony_ci .pfunc[7] = PMBUS_HAVE_IOUT, 34762306a36Sopenharmony_ci .pfunc[8] = PMBUS_HAVE_IOUT, 34862306a36Sopenharmony_ci .pfunc[9] = PMBUS_HAVE_IOUT, 34962306a36Sopenharmony_ci .read_byte_data = mp2888_read_byte_data, 35062306a36Sopenharmony_ci .read_word_data = mp2888_read_word_data, 35162306a36Sopenharmony_ci .write_word_data = mp2888_write_word_data, 35262306a36Sopenharmony_ci}; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int mp2888_probe(struct i2c_client *client) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct pmbus_driver_info *info; 35762306a36Sopenharmony_ci struct mp2888_data *data; 35862306a36Sopenharmony_ci int ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(struct mp2888_data), GFP_KERNEL); 36162306a36Sopenharmony_ci if (!data) 36262306a36Sopenharmony_ci return -ENOMEM; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci memcpy(&data->info, &mp2888_info, sizeof(*info)); 36562306a36Sopenharmony_ci info = &data->info; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Identify multiphase configuration. */ 36862306a36Sopenharmony_ci ret = mp2888_identify_multiphase(client, data, info); 36962306a36Sopenharmony_ci if (ret) 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Obtain current sense gain of power stage and current resolution. */ 37362306a36Sopenharmony_ci ret = mp2888_current_sense_gain_and_resolution_get(client, data); 37462306a36Sopenharmony_ci if (ret) 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return pmbus_do_probe(client, info); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic const struct i2c_device_id mp2888_id[] = { 38162306a36Sopenharmony_ci {"mp2888", 0}, 38262306a36Sopenharmony_ci {} 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mp2888_id); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused mp2888_of_match[] = { 38862306a36Sopenharmony_ci {.compatible = "mps,mp2888"}, 38962306a36Sopenharmony_ci {} 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mp2888_of_match); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic struct i2c_driver mp2888_driver = { 39462306a36Sopenharmony_ci .driver = { 39562306a36Sopenharmony_ci .name = "mp2888", 39662306a36Sopenharmony_ci .of_match_table = of_match_ptr(mp2888_of_match), 39762306a36Sopenharmony_ci }, 39862306a36Sopenharmony_ci .probe = mp2888_probe, 39962306a36Sopenharmony_ci .id_table = mp2888_id, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cimodule_i2c_driver(mp2888_driver); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); 40562306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for MPS MP2888 device"); 40662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 40762306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS); 408