320 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP9941)4 */5 6#include <linux/bitfield.h>7#include <linux/bits.h>8#include <linux/i2c.h>9#include <linux/module.h>10#include <linux/of_device.h>11#include "pmbus.h"12 13/*14 * Vender specific registers. The MFR_ICC_MAX(0x02) is used to15 * config the iin scale. The MFR_RESO_SET(0xC7) is used to16 * config the vout format. The MFR_VR_MULTI_CONFIG_R1(0x0D) is17 * used to identify the vout vid step.18 */19#define MFR_ICC_MAX 0x0220#define MFR_RESO_SET 0xC721#define MFR_VR_MULTI_CONFIG_R1 0x0D22 23#define MP9941_VIN_LIMIT_UINT 124#define MP9941_VIN_LIMIT_DIV 825#define MP9941_READ_VIN_UINT 126#define MP9941_READ_VIN_DIV 3227 28#define MP9941_PAGE_NUM 129 30#define MP9941_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \31 PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \32 PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \33 PMBUS_HAVE_IIN | \34 PMBUS_HAVE_STATUS_VOUT | \35 PMBUS_HAVE_STATUS_IOUT | \36 PMBUS_HAVE_STATUS_TEMP | \37 PMBUS_HAVE_STATUS_INPUT)38 39struct mp9941_data {40 struct pmbus_driver_info info;41 int vid_resolution;42};43 44#define to_mp9941_data(x) container_of(x, struct mp9941_data, info)45 46static int mp9941_set_vout_format(struct i2c_client *client)47{48 int ret;49 50 ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);51 if (ret < 0)52 return ret;53 54 ret = i2c_smbus_read_word_data(client, MFR_RESO_SET);55 if (ret < 0)56 return ret;57 58 /*59 * page = 0, MFR_RESO_SET[7:6] defines the vout format60 * 2'b11 set the vout format as direct61 */62 ret = (ret & ~GENMASK(7, 6)) | FIELD_PREP(GENMASK(7, 6), 3);63 64 return i2c_smbus_write_word_data(client, MFR_RESO_SET, ret);65}66 67static int68mp9941_identify_vid_resolution(struct i2c_client *client, struct pmbus_driver_info *info)69{70 struct mp9941_data *data = to_mp9941_data(info);71 int ret;72 73 /*74 * page = 2, MFR_VR_MULTI_CONFIG_R1[4:4] defines rail1 vid step value75 * 1'b0 represents the vid step value is 10mV76 * 1'b1 represents the vid step value is 5mV77 */78 ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);79 if (ret < 0)80 return ret;81 82 ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1);83 if (ret < 0)84 return ret;85 86 if (FIELD_GET(GENMASK(4, 4), ret))87 data->vid_resolution = 5;88 else89 data->vid_resolution = 10;90 91 return 0;92}93 94static int mp9941_identify_iin_scale(struct i2c_client *client)95{96 int ret;97 98 ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);99 if (ret < 0)100 return ret;101 102 ret = i2c_smbus_read_word_data(client, MFR_RESO_SET);103 if (ret < 0)104 return ret;105 106 ret = (ret & ~GENMASK(3, 2)) | FIELD_PREP(GENMASK(3, 2), 0);107 108 ret = i2c_smbus_write_word_data(client, MFR_RESO_SET, ret);109 if (ret < 0)110 return ret;111 112 /*113 * page = 2, MFR_ICC_MAX[15:13] defines the iin scale114 * 3'b000 set the iout scale as 0.5A/Lsb115 */116 ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);117 if (ret < 0)118 return ret;119 120 ret = i2c_smbus_read_word_data(client, MFR_ICC_MAX);121 if (ret < 0)122 return ret;123 124 ret = (ret & ~GENMASK(15, 13)) | FIELD_PREP(GENMASK(15, 13), 0);125 126 return i2c_smbus_write_word_data(client, MFR_ICC_MAX, ret);127}128 129static int mp9941_identify(struct i2c_client *client, struct pmbus_driver_info *info)130{131 int ret;132 133 ret = mp9941_identify_iin_scale(client);134 if (ret < 0)135 return ret;136 137 ret = mp9941_identify_vid_resolution(client, info);138 if (ret < 0)139 return ret;140 141 return mp9941_set_vout_format(client);142}143 144static int mp9941_read_word_data(struct i2c_client *client, int page, int phase,145 int reg)146{147 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);148 struct mp9941_data *data = to_mp9941_data(info);149 int ret;150 151 switch (reg) {152 case PMBUS_READ_VIN:153 /* The MP9941 vin scale is (1/32V)/Lsb */154 ret = pmbus_read_word_data(client, page, phase, reg);155 if (ret < 0)156 return ret;157 158 ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP9941_READ_VIN_UINT,159 MP9941_READ_VIN_DIV);160 break;161 case PMBUS_READ_IIN:162 ret = pmbus_read_word_data(client, page, phase, reg);163 if (ret < 0)164 return ret;165 166 ret = ret & GENMASK(10, 0);167 break;168 case PMBUS_VIN_OV_FAULT_LIMIT:169 /* The MP9941 vin ov limit scale is (1/8V)/Lsb */170 ret = pmbus_read_word_data(client, page, phase, reg);171 if (ret < 0)172 return ret;173 174 ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP9941_VIN_LIMIT_UINT,175 MP9941_VIN_LIMIT_DIV);176 break;177 case PMBUS_IIN_OC_WARN_LIMIT:178 ret = pmbus_read_word_data(client, page, phase, reg);179 if (ret < 0)180 return ret;181 182 ret = ret & GENMASK(7, 0);183 break;184 case PMBUS_VOUT_UV_FAULT_LIMIT:185 case PMBUS_MFR_VOUT_MIN:186 case PMBUS_MFR_VOUT_MAX:187 /*188 * The vout scale is set to 1mV/Lsb(using r/m/b scale).189 * But the vout uv limit and vout max/min scale is 1VID/Lsb,190 * so the vout uv limit and vout max/min value should be191 * multiplied by vid resolution.192 */193 ret = pmbus_read_word_data(client, page, phase, reg);194 if (ret < 0)195 return ret;196 197 ret = ret * data->vid_resolution;198 break;199 case PMBUS_READ_IOUT:200 case PMBUS_READ_POUT:201 case PMBUS_READ_TEMPERATURE_1:202 case PMBUS_READ_VOUT:203 case PMBUS_READ_PIN:204 case PMBUS_OT_FAULT_LIMIT:205 case PMBUS_OT_WARN_LIMIT:206 ret = -ENODATA;207 break;208 default:209 ret = -EINVAL;210 break;211 }212 213 return ret;214}215 216static int mp9941_write_word_data(struct i2c_client *client, int page, int reg,217 u16 word)218{219 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);220 struct mp9941_data *data = to_mp9941_data(info);221 int ret;222 223 switch (reg) {224 case PMBUS_VIN_OV_FAULT_LIMIT:225 /* The MP9941 vin ov limit scale is (1/8V)/Lsb */226 ret = pmbus_write_word_data(client, page, reg,227 DIV_ROUND_CLOSEST(word * MP9941_VIN_LIMIT_DIV,228 MP9941_VIN_LIMIT_UINT));229 break;230 case PMBUS_VOUT_UV_FAULT_LIMIT:231 case PMBUS_MFR_VOUT_MIN:232 case PMBUS_MFR_VOUT_MAX:233 ret = pmbus_write_word_data(client, page, reg,234 DIV_ROUND_CLOSEST(word, data->vid_resolution));235 break;236 case PMBUS_IIN_OC_WARN_LIMIT:237 case PMBUS_OT_FAULT_LIMIT:238 case PMBUS_OT_WARN_LIMIT:239 ret = -ENODATA;240 break;241 default:242 ret = -EINVAL;243 break;244 }245 246 return ret;247}248 249static const struct pmbus_driver_info mp9941_info = {250 .pages = MP9941_PAGE_NUM,251 .format[PSC_VOLTAGE_IN] = direct,252 .format[PSC_CURRENT_IN] = direct,253 .format[PSC_CURRENT_OUT] = linear,254 .format[PSC_POWER] = linear,255 .format[PSC_TEMPERATURE] = direct,256 .format[PSC_VOLTAGE_OUT] = direct,257 258 .m[PSC_TEMPERATURE] = 1,259 .R[PSC_TEMPERATURE] = 0,260 .b[PSC_TEMPERATURE] = 0,261 262 .m[PSC_VOLTAGE_IN] = 1,263 .R[PSC_VOLTAGE_IN] = 0,264 .b[PSC_VOLTAGE_IN] = 0,265 266 .m[PSC_CURRENT_IN] = 2,267 .R[PSC_CURRENT_IN] = 0,268 .b[PSC_CURRENT_IN] = 0,269 270 .m[PSC_VOLTAGE_OUT] = 1,271 .R[PSC_VOLTAGE_OUT] = 3,272 .b[PSC_VOLTAGE_OUT] = 0,273 274 .func[0] = MP9941_RAIL1_FUNC,275 .read_word_data = mp9941_read_word_data,276 .write_word_data = mp9941_write_word_data,277 .identify = mp9941_identify,278};279 280static int mp9941_probe(struct i2c_client *client)281{282 struct mp9941_data *data;283 284 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);285 if (!data)286 return -ENOMEM;287 288 memcpy(&data->info, &mp9941_info, sizeof(mp9941_info));289 290 return pmbus_do_probe(client, &data->info);291}292 293static const struct i2c_device_id mp9941_id[] = {294 {"mp9941", 0},295 {}296};297MODULE_DEVICE_TABLE(i2c, mp9941_id);298 299static const struct of_device_id __maybe_unused mp9941_of_match[] = {300 {.compatible = "mps,mp9941"},301 {}302};303MODULE_DEVICE_TABLE(of, mp9941_of_match);304 305static struct i2c_driver mp9941_driver = {306 .driver = {307 .name = "mp9941",308 .of_match_table = mp9941_of_match,309 },310 .probe = mp9941_probe,311 .id_table = mp9941_id,312};313 314module_i2c_driver(mp9941_driver);315 316MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");317MODULE_DESCRIPTION("PMBus driver for MPS MP9941");318MODULE_LICENSE("GPL");319MODULE_IMPORT_NS(PMBUS);320