Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hwmon: add MP2869,MP29608,MP29612 and MP29816 series driver

Add support for MPS VR mp2869/mp2869a,mp29608/mp29608a,mp29612/mp29612a
and mp29816/mp29816a/mp29816b/mp29816c controller. This driver exposes
telemetry and limit value readings and writtings.

Signed-off-by: Wensheng Wang <wenswang@yeah.net>
Link: https://lore.kernel.org/r/20250805102020.749850-2-wenswang@yeah.net
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Wensheng Wang and committed by
Guenter Roeck
a3a2923a c97c66e0

+852
+1
Documentation/hwmon/index.rst
··· 173 173 menf21bmc 174 174 mlxreg-fan 175 175 mp2856 176 + mp2869 176 177 mp2888 177 178 mp2891 178 179 mp2975
+175
Documentation/hwmon/mp2869.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + Kernel driver mp2869 4 + ==================== 5 + 6 + Supported chips: 7 + 8 + * MPS mp2869 9 + 10 + Prefix: 'mp2869' 11 + 12 + * MPS mp29608 13 + 14 + Prefix: 'mp29608' 15 + 16 + * MPS mp29612 17 + 18 + Prefix: 'mp29612' 19 + 20 + * MPS mp29816 21 + 22 + Prefix: 'mp29816' 23 + 24 + Author: 25 + 26 + Wensheng Wang <wenswang@yeah.net> 27 + 28 + Description 29 + ----------- 30 + 31 + This driver implements support for Monolithic Power Systems, Inc. (MPS) 32 + MP2869 Dual Loop Digital Multi-phase Controller. 33 + 34 + Device compliant with: 35 + 36 + - PMBus rev 1.3 interface. 37 + 38 + The driver exports the following attributes via the 'sysfs' files 39 + for input voltage: 40 + 41 + **in1_input** 42 + 43 + **in1_label** 44 + 45 + **in1_crit** 46 + 47 + **in1_crit_alarm** 48 + 49 + **in1_lcrit** 50 + 51 + **in1_lcrit_alarm** 52 + 53 + **in1_min** 54 + 55 + **in1_min_alarm** 56 + 57 + The driver provides the following attributes for output voltage: 58 + 59 + **in2_input** 60 + 61 + **in2_label** 62 + 63 + **in2_crit** 64 + 65 + **in2_crit_alarm** 66 + 67 + **in2_lcrit** 68 + 69 + **in2_lcrit_alarm** 70 + 71 + **in3_input** 72 + 73 + **in3_label** 74 + 75 + **in3_crit** 76 + 77 + **in3_crit_alarm** 78 + 79 + **in3_lcrit** 80 + 81 + **in3_lcrit_alarm** 82 + 83 + The driver provides the following attributes for input current: 84 + 85 + **curr1_input** 86 + 87 + **curr1_label** 88 + 89 + **curr2_input** 90 + 91 + **curr2_label** 92 + 93 + The driver provides the following attributes for output current: 94 + 95 + **curr3_input** 96 + 97 + **curr3_label** 98 + 99 + **curr3_crit** 100 + 101 + **curr3_crit_alarm** 102 + 103 + **curr3_max** 104 + 105 + **curr3_max_alarm** 106 + 107 + **curr4_input** 108 + 109 + **curr4_label** 110 + 111 + **curr4_crit** 112 + 113 + **curr4_crit_alarm** 114 + 115 + **curr4_max** 116 + 117 + **curr4_max_alarm** 118 + 119 + The driver provides the following attributes for input power: 120 + 121 + **power1_input** 122 + 123 + **power1_label** 124 + 125 + **power2_input** 126 + 127 + **power2_label** 128 + 129 + The driver provides the following attributes for output power: 130 + 131 + **power3_input** 132 + 133 + **power3_label** 134 + 135 + **power3_input** 136 + 137 + **power3_label** 138 + 139 + **power3_max** 140 + 141 + **power3_max_alarm** 142 + 143 + **power4_input** 144 + 145 + **power4_label** 146 + 147 + **power4_input** 148 + 149 + **power4_label** 150 + 151 + **power4_max** 152 + 153 + **power4_max_alarm** 154 + 155 + The driver provides the following attributes for temperature: 156 + 157 + **temp1_input** 158 + 159 + **temp1_crit** 160 + 161 + **temp1_crit_alarm** 162 + 163 + **temp1_max** 164 + 165 + **temp1_max_alarm** 166 + 167 + **temp2_input** 168 + 169 + **temp2_crit** 170 + 171 + **temp2_crit_alarm** 172 + 173 + **temp2_max** 174 + 175 + **temp2_max_alarm**
+7
MAINTAINERS
··· 17175 17175 F: Documentation/devicetree/bindings/leds/backlight/mps,mp3309c.yaml 17176 17176 F: drivers/video/backlight/mp3309c.c 17177 17177 17178 + MPS MP2869 DRIVER 17179 + M: Wensheng Wang <wenswang@yeah.net> 17180 + L: linux-hwmon@vger.kernel.org 17181 + S: Maintained 17182 + F: Documentation/hwmon/mp2869.rst 17183 + F: drivers/hwmon/pmbus/mp2869.c 17184 + 17178 17185 MPS MP2891 DRIVER 17179 17186 M: Noah Wang <noahwang.wang@outlook.com> 17180 17187 L: linux-hwmon@vger.kernel.org
+9
drivers/hwmon/pmbus/Kconfig
··· 374 374 This driver can also be built as a module. If so, the module will 375 375 be called mp2856. 376 376 377 + config SENSORS_MP2869 378 + tristate "MPS MP2869" 379 + help 380 + If you say yes here you get hardware monitoring support for MPS 381 + MP2869 Dual Loop Digital Multi-Phase Controller. 382 + 383 + This driver can also be built as a module. If so, the module will 384 + be called mp2869. 385 + 377 386 config SENSORS_MP2888 378 387 tristate "MPS MP2888" 379 388 help
+1
drivers/hwmon/pmbus/Makefile
··· 37 37 obj-$(CONFIG_SENSORS_MAX34440) += max34440.o 38 38 obj-$(CONFIG_SENSORS_MAX8688) += max8688.o 39 39 obj-$(CONFIG_SENSORS_MP2856) += mp2856.o 40 + obj-$(CONFIG_SENSORS_MP2869) += mp2869.o 40 41 obj-$(CONFIG_SENSORS_MP2888) += mp2888.o 41 42 obj-$(CONFIG_SENSORS_MP2891) += mp2891.o 42 43 obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
+659
drivers/hwmon/pmbus/mp2869.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2869) 4 + */ 5 + 6 + #include <linux/bitfield.h> 7 + #include <linux/i2c.h> 8 + #include <linux/module.h> 9 + #include <linux/of_device.h> 10 + #include "pmbus.h" 11 + 12 + /* 13 + * Vender specific registers, the register MFR_SVI3_IOUT_PRT(0x67), 14 + * READ_PIN_EST(0x94)and READ_IIN_EST(0x95) redefine the standard 15 + * PMBUS register. The MFR_VOUT_LOOP_CTRL(0x29) is used to identify 16 + * the vout scale and the MFR_SVI3_IOUT_PRT(0x67) is used to identify 17 + * the iout scale. The READ_PIN_EST(0x94) is used to read input power 18 + * per rail. The MP2891 does not have standard READ_IIN register(0x89), 19 + * the iin telemetry can be obtained through the vendor redefined 20 + * register READ_IIN_EST(0x95). 21 + */ 22 + #define MFR_SVI3_IOUT_PRT 0x67 23 + #define MFR_READ_PIN_EST 0x94 24 + #define MFR_READ_IIN_EST 0x95 25 + #define MFR_TSNS_FLT_SET 0xBB 26 + 27 + #define MP2869_VIN_OV_FAULT_GAIN 4 28 + #define MP2869_READ_VOUT_DIV 1024 29 + #define MP2869_READ_IOUT_DIV 32 30 + #define MP2869_OVUV_LIMIT_SCALE 10 31 + #define MP2869_OVUV_DELTA_SCALE 50 32 + #define MP2869_TEMP_LIMIT_OFFSET 40 33 + #define MP2869_IOUT_LIMIT_UINT 8 34 + #define MP2869_POUT_OP_GAIN 2 35 + 36 + #define MP2869_PAGE_NUM 2 37 + 38 + #define MP2869_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ 39 + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ 40 + PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \ 41 + PMBUS_HAVE_IIN | \ 42 + PMBUS_HAVE_STATUS_VOUT | \ 43 + PMBUS_HAVE_STATUS_IOUT | \ 44 + PMBUS_HAVE_STATUS_TEMP | \ 45 + PMBUS_HAVE_STATUS_INPUT) 46 + 47 + #define MP2869_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ 48 + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \ 49 + PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | \ 50 + PMBUS_HAVE_STATUS_VOUT | \ 51 + PMBUS_HAVE_STATUS_IOUT | \ 52 + PMBUS_HAVE_STATUS_TEMP | \ 53 + PMBUS_HAVE_STATUS_INPUT) 54 + 55 + struct mp2869_data { 56 + struct pmbus_driver_info info; 57 + bool mfr_thwn_flt_en; 58 + int vout_scale[MP2869_PAGE_NUM]; 59 + int iout_scale[MP2869_PAGE_NUM]; 60 + }; 61 + 62 + static const int mp2869_vout_sacle[8] = {6400, 5120, 2560, 2048, 1024, 63 + 4, 2, 1}; 64 + static const int mp2869_iout_sacle[8] = {32, 1, 2, 4, 8, 16, 32, 64}; 65 + 66 + #define to_mp2869_data(x) container_of(x, struct mp2869_data, info) 67 + 68 + static u16 mp2869_reg2data_linear11(u16 word) 69 + { 70 + s16 exponent; 71 + s32 mantissa; 72 + s64 val; 73 + 74 + exponent = ((s16)word) >> 11; 75 + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; 76 + val = mantissa; 77 + 78 + if (exponent >= 0) 79 + val <<= exponent; 80 + else 81 + val >>= -exponent; 82 + 83 + return val; 84 + } 85 + 86 + static int 87 + mp2869_identify_thwn_flt(struct i2c_client *client, struct pmbus_driver_info *info, 88 + int page) 89 + { 90 + struct mp2869_data *data = to_mp2869_data(info); 91 + int ret; 92 + 93 + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); 94 + if (ret < 0) 95 + return ret; 96 + 97 + ret = i2c_smbus_read_word_data(client, MFR_TSNS_FLT_SET); 98 + if (ret < 0) 99 + return ret; 100 + 101 + data->mfr_thwn_flt_en = FIELD_GET(GENMASK(13, 13), ret); 102 + 103 + return 0; 104 + } 105 + 106 + static int 107 + mp2869_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, 108 + int page) 109 + { 110 + struct mp2869_data *data = to_mp2869_data(info); 111 + int ret; 112 + 113 + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); 114 + if (ret < 0) 115 + return ret; 116 + 117 + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_SCALE_LOOP); 118 + if (ret < 0) 119 + return ret; 120 + 121 + /* 122 + * The output voltage is equal to the READ_VOUT(0x8B) register value multiply 123 + * by vout_scale. 124 + * Obtain vout scale from the register PMBUS_VOUT_SCALE_LOOP, bits 12-10 125 + * PMBUS_VOUT_SCALE_LOOP[12:10]: 126 + * 000b - 6.25mV/LSB, 001b - 5mV/LSB, 010b - 2.5mV/LSB, 011b - 2mV/LSB 127 + * 100b - 1mV/Lsb, 101b - (1/256)mV/LSB, 110b - (1/512)mV/LSB, 128 + * 111b - (1/1024)mV/LSB 129 + */ 130 + data->vout_scale[page] = mp2869_vout_sacle[FIELD_GET(GENMASK(12, 10), ret)]; 131 + 132 + return 0; 133 + } 134 + 135 + static int 136 + mp2869_identify_iout_scale(struct i2c_client *client, struct pmbus_driver_info *info, 137 + int page) 138 + { 139 + struct mp2869_data *data = to_mp2869_data(info); 140 + int ret; 141 + 142 + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); 143 + if (ret < 0) 144 + return ret; 145 + 146 + ret = i2c_smbus_read_word_data(client, MFR_SVI3_IOUT_PRT); 147 + if (ret < 0) 148 + return ret; 149 + 150 + /* 151 + * The output current is equal to the READ_IOUT(0x8C) register value 152 + * multiply by iout_scale. 153 + * Obtain iout_scale from the register MFR_SVI3_IOUT_PRT[2:0]. 154 + * The value is selected as below: 155 + * 000b - 1A/LSB, 001b - (1/32)A/LSB, 010b - (1/16)A/LSB, 156 + * 011b - (1/8)A/LSB, 100b - (1/4)A/LSB, 101b - (1/2)A/LSB 157 + * 110b - 1A/LSB, 111b - 2A/LSB 158 + */ 159 + data->iout_scale[page] = mp2869_iout_sacle[FIELD_GET(GENMASK(2, 0), ret)]; 160 + 161 + return 0; 162 + } 163 + 164 + static int mp2869_read_byte_data(struct i2c_client *client, int page, int reg) 165 + { 166 + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 167 + struct mp2869_data *data = to_mp2869_data(info); 168 + int ret; 169 + 170 + switch (reg) { 171 + case PMBUS_VOUT_MODE: 172 + /* 173 + * The calculation of vout in this driver is based on direct format. 174 + * As a result, the format of vout is enforced to direct. 175 + */ 176 + ret = PB_VOUT_MODE_DIRECT; 177 + break; 178 + case PMBUS_STATUS_BYTE: 179 + /* 180 + * If the tsns digital fault is enabled, the TEMPERATURE flag 181 + * of PMBUS_STATUS_BYTE should come from STATUS_MFR_SPECIFIC 182 + * register bit1. 183 + */ 184 + if (!data->mfr_thwn_flt_en) 185 + return -ENODATA; 186 + 187 + ret = pmbus_read_byte_data(client, page, reg); 188 + if (ret < 0) 189 + return ret; 190 + 191 + ret = (ret & ~GENMASK(2, 2)) | 192 + FIELD_PREP(GENMASK(2, 2), 193 + FIELD_GET(GENMASK(1, 1), 194 + pmbus_read_byte_data(client, page, 195 + PMBUS_STATUS_MFR_SPECIFIC))); 196 + break; 197 + case PMBUS_STATUS_TEMPERATURE: 198 + /* 199 + * If the tsns digital fault is enabled, the OT Fault and OT Warning 200 + * flag of PMBUS_STATUS_TEMPERATURE should come from STATUS_MFR_SPECIFIC 201 + * register bit1. 202 + */ 203 + if (!data->mfr_thwn_flt_en) 204 + return -ENODATA; 205 + 206 + ret = pmbus_read_byte_data(client, page, reg); 207 + if (ret < 0) 208 + return ret; 209 + 210 + ret = (ret & ~GENMASK(7, 6)) | 211 + FIELD_PREP(GENMASK(6, 6), 212 + FIELD_GET(GENMASK(1, 1), 213 + pmbus_read_byte_data(client, page, 214 + PMBUS_STATUS_MFR_SPECIFIC))) | 215 + FIELD_PREP(GENMASK(7, 7), 216 + FIELD_GET(GENMASK(1, 1), 217 + pmbus_read_byte_data(client, page, 218 + PMBUS_STATUS_MFR_SPECIFIC))); 219 + break; 220 + default: 221 + ret = -ENODATA; 222 + break; 223 + } 224 + 225 + return ret; 226 + } 227 + 228 + static int mp2869_read_word_data(struct i2c_client *client, int page, int phase, 229 + int reg) 230 + { 231 + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 232 + struct mp2869_data *data = to_mp2869_data(info); 233 + int ret; 234 + 235 + switch (reg) { 236 + case PMBUS_STATUS_WORD: 237 + /* 238 + * If the tsns digital fault is enabled, the OT Fault flag 239 + * of PMBUS_STATUS_WORD should come from STATUS_MFR_SPECIFIC 240 + * register bit1. 241 + */ 242 + if (!data->mfr_thwn_flt_en) 243 + return -ENODATA; 244 + 245 + ret = pmbus_read_word_data(client, page, phase, reg); 246 + if (ret < 0) 247 + return ret; 248 + 249 + ret = (ret & ~GENMASK(2, 2)) | 250 + FIELD_PREP(GENMASK(2, 2), 251 + FIELD_GET(GENMASK(1, 1), 252 + pmbus_read_byte_data(client, page, 253 + PMBUS_STATUS_MFR_SPECIFIC))); 254 + break; 255 + case PMBUS_READ_VIN: 256 + /* 257 + * The MP2869 PMBUS_READ_VIN[10:0] is the vin value, the vin scale is 258 + * 31.25mV/LSB. And the vin scale is set to 31.25mV/Lsb(using r/m/b scale) 259 + * in MP2869 pmbus_driver_info struct, so the word data bit0-bit10 can be 260 + * returned to pmbus core directly. 261 + */ 262 + ret = pmbus_read_word_data(client, page, phase, reg); 263 + if (ret < 0) 264 + return ret; 265 + 266 + ret = FIELD_GET(GENMASK(10, 0), ret); 267 + break; 268 + case PMBUS_READ_IIN: 269 + /* 270 + * The MP2869 redefine the standard 0x95 register as iin telemetry 271 + * per rail. 272 + */ 273 + ret = pmbus_read_word_data(client, page, phase, MFR_READ_IIN_EST); 274 + if (ret < 0) 275 + return ret; 276 + 277 + break; 278 + case PMBUS_READ_PIN: 279 + /* 280 + * The MP2869 redefine the standard 0x94 register as pin telemetry 281 + * per rail. The MP2869 MFR_READ_PIN_EST register is linear11 format, 282 + * but the pin scale is set to 1W/Lsb(using r/m/b scale). As a result, 283 + * the pin read from MP2869 should be converted to W, then return 284 + * the result to pmbus core. 285 + */ 286 + ret = pmbus_read_word_data(client, page, phase, MFR_READ_PIN_EST); 287 + if (ret < 0) 288 + return ret; 289 + 290 + ret = mp2869_reg2data_linear11(ret); 291 + break; 292 + case PMBUS_READ_VOUT: 293 + ret = pmbus_read_word_data(client, page, phase, reg); 294 + if (ret < 0) 295 + return ret; 296 + 297 + ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * data->vout_scale[page], 298 + MP2869_READ_VOUT_DIV); 299 + break; 300 + case PMBUS_READ_IOUT: 301 + ret = pmbus_read_word_data(client, page, phase, reg); 302 + if (ret < 0) 303 + return ret; 304 + 305 + ret = DIV_ROUND_CLOSEST((ret & GENMASK(10, 0)) * data->iout_scale[page], 306 + MP2869_READ_IOUT_DIV); 307 + break; 308 + case PMBUS_READ_POUT: 309 + /* 310 + * The MP2869 PMBUS_READ_POUT register is linear11 format, but the pout 311 + * scale is set to 1W/Lsb(using r/m/b scale). As a result, the pout read 312 + * from MP2869 should be converted to W, then return the result to pmbus 313 + * core. 314 + */ 315 + ret = pmbus_read_word_data(client, page, phase, reg); 316 + if (ret < 0) 317 + return ret; 318 + 319 + ret = mp2869_reg2data_linear11(ret); 320 + break; 321 + case PMBUS_READ_TEMPERATURE_1: 322 + ret = pmbus_read_word_data(client, page, phase, reg); 323 + if (ret < 0) 324 + return ret; 325 + 326 + ret = FIELD_GET(GENMASK(10, 0), ret); 327 + break; 328 + case PMBUS_VOUT_OV_FAULT_LIMIT: 329 + ret = pmbus_read_word_data(client, page, phase, reg); 330 + if (ret < 0) 331 + return ret; 332 + 333 + if (FIELD_GET(GENMASK(12, 9), ret)) 334 + ret = FIELD_GET(GENMASK(8, 0), ret) * MP2869_OVUV_LIMIT_SCALE + 335 + (FIELD_GET(GENMASK(12, 9), ret) + 1) * MP2869_OVUV_DELTA_SCALE; 336 + else 337 + ret = FIELD_GET(GENMASK(8, 0), ret) * MP2869_OVUV_LIMIT_SCALE; 338 + break; 339 + case PMBUS_VOUT_UV_FAULT_LIMIT: 340 + ret = pmbus_read_word_data(client, page, phase, reg); 341 + if (ret < 0) 342 + return ret; 343 + 344 + if (FIELD_GET(GENMASK(12, 9), ret)) 345 + ret = FIELD_GET(GENMASK(8, 0), ret) * MP2869_OVUV_LIMIT_SCALE - 346 + (FIELD_GET(GENMASK(12, 9), ret) + 1) * MP2869_OVUV_DELTA_SCALE; 347 + else 348 + ret = FIELD_GET(GENMASK(8, 0), ret) * MP2869_OVUV_LIMIT_SCALE; 349 + break; 350 + case PMBUS_OT_FAULT_LIMIT: 351 + case PMBUS_OT_WARN_LIMIT: 352 + /* 353 + * The scale of MP2869 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT 354 + * is 1°C/LSB and they have 40°C offset. 355 + */ 356 + ret = pmbus_read_word_data(client, page, phase, reg); 357 + if (ret < 0) 358 + return ret; 359 + 360 + ret = (ret & GENMASK(7, 0)) - MP2869_TEMP_LIMIT_OFFSET; 361 + break; 362 + case PMBUS_VIN_OV_FAULT_LIMIT: 363 + ret = pmbus_read_word_data(client, page, phase, reg); 364 + if (ret < 0) 365 + return ret; 366 + 367 + ret = (ret & GENMASK(7, 0)) * MP2869_VIN_OV_FAULT_GAIN; 368 + break; 369 + case PMBUS_VIN_UV_WARN_LIMIT: 370 + case PMBUS_VIN_UV_FAULT_LIMIT: 371 + ret = pmbus_read_word_data(client, page, phase, reg); 372 + if (ret < 0) 373 + return ret; 374 + 375 + ret = FIELD_GET(GENMASK(9, 0), ret); 376 + break; 377 + case PMBUS_IOUT_OC_FAULT_LIMIT: 378 + case PMBUS_IOUT_OC_WARN_LIMIT: 379 + ret = pmbus_read_word_data(client, page, phase, reg); 380 + if (ret < 0) 381 + return ret; 382 + 383 + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * data->iout_scale[page] * 384 + MP2869_IOUT_LIMIT_UINT, MP2869_READ_IOUT_DIV); 385 + break; 386 + case PMBUS_POUT_OP_WARN_LIMIT: 387 + ret = pmbus_read_word_data(client, page, phase, reg); 388 + if (ret < 0) 389 + return ret; 390 + 391 + ret = (ret & GENMASK(7, 0)) * MP2869_POUT_OP_GAIN; 392 + break; 393 + default: 394 + ret = -EINVAL; 395 + break; 396 + } 397 + 398 + return ret; 399 + } 400 + 401 + static int mp2869_write_word_data(struct i2c_client *client, int page, int reg, 402 + u16 word) 403 + { 404 + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 405 + struct mp2869_data *data = to_mp2869_data(info); 406 + int ret; 407 + 408 + switch (reg) { 409 + case PMBUS_VOUT_UV_FAULT_LIMIT: 410 + /* 411 + * The MP2869 PMBUS_VOUT_UV_FAULT_LIMIT[8:0] is the limit value, 412 + * and bit9-bit15 should not be changed. 413 + */ 414 + ret = pmbus_read_word_data(client, page, 0xff, reg); 415 + if (ret < 0) 416 + return ret; 417 + 418 + if (FIELD_GET(GENMASK(12, 9), ret)) 419 + ret = pmbus_write_word_data(client, page, reg, 420 + (ret & ~GENMASK(8, 0)) | 421 + FIELD_PREP(GENMASK(8, 0), 422 + DIV_ROUND_CLOSEST(word + 423 + (FIELD_GET(GENMASK(12, 9), 424 + ret) + 1) * 425 + MP2869_OVUV_DELTA_SCALE, 426 + MP2869_OVUV_LIMIT_SCALE))); 427 + else 428 + ret = pmbus_write_word_data(client, page, reg, 429 + (ret & ~GENMASK(8, 0)) | 430 + FIELD_PREP(GENMASK(8, 0), 431 + DIV_ROUND_CLOSEST(word, 432 + MP2869_OVUV_LIMIT_SCALE))); 433 + break; 434 + case PMBUS_VOUT_OV_FAULT_LIMIT: 435 + /* 436 + * The MP2869 PMBUS_VOUT_OV_FAULT_LIMIT[8:0] is the limit value, 437 + * and bit9-bit15 should not be changed. 438 + */ 439 + ret = pmbus_read_word_data(client, page, 0xff, reg); 440 + if (ret < 0) 441 + return ret; 442 + 443 + if (FIELD_GET(GENMASK(12, 9), ret)) 444 + ret = pmbus_write_word_data(client, page, reg, 445 + (ret & ~GENMASK(8, 0)) | 446 + FIELD_PREP(GENMASK(8, 0), 447 + DIV_ROUND_CLOSEST(word - 448 + (FIELD_GET(GENMASK(12, 9), 449 + ret) + 1) * 450 + MP2869_OVUV_DELTA_SCALE, 451 + MP2869_OVUV_LIMIT_SCALE))); 452 + else 453 + ret = pmbus_write_word_data(client, page, reg, 454 + (ret & ~GENMASK(8, 0)) | 455 + FIELD_PREP(GENMASK(8, 0), 456 + DIV_ROUND_CLOSEST(word, 457 + MP2869_OVUV_LIMIT_SCALE))); 458 + break; 459 + case PMBUS_OT_FAULT_LIMIT: 460 + case PMBUS_OT_WARN_LIMIT: 461 + /* 462 + * If the tsns digital fault is enabled, the PMBUS_OT_FAULT_LIMIT and 463 + * PMBUS_OT_WARN_LIMIT can not be written. 464 + */ 465 + if (data->mfr_thwn_flt_en) 466 + return -EINVAL; 467 + 468 + /* 469 + * The MP2869 scale of MP2869 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT 470 + * have 40°C offset. The bit0-bit7 is the limit value, and bit8-bit15 471 + * should not be changed. 472 + */ 473 + ret = pmbus_read_word_data(client, page, 0xff, reg); 474 + if (ret < 0) 475 + return ret; 476 + 477 + ret = pmbus_write_word_data(client, page, reg, 478 + (ret & ~GENMASK(7, 0)) | 479 + FIELD_PREP(GENMASK(7, 0), 480 + word + MP2869_TEMP_LIMIT_OFFSET)); 481 + break; 482 + case PMBUS_VIN_OV_FAULT_LIMIT: 483 + /* 484 + * The MP2869 PMBUS_VIN_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 485 + * should not be changed. The scale of PMBUS_VIN_OV_FAULT_LIMIT is 125mV/Lsb, 486 + * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale), so the word data 487 + * should divide by MP2869_VIN_OV_FAULT_GAIN(4) 488 + */ 489 + ret = pmbus_read_word_data(client, page, 0xff, reg); 490 + if (ret < 0) 491 + return ret; 492 + 493 + ret = pmbus_write_word_data(client, page, reg, 494 + (ret & ~GENMASK(7, 0)) | 495 + FIELD_PREP(GENMASK(7, 0), 496 + DIV_ROUND_CLOSEST(word, 497 + MP2869_VIN_OV_FAULT_GAIN))); 498 + break; 499 + case PMBUS_VIN_UV_WARN_LIMIT: 500 + case PMBUS_VIN_UV_FAULT_LIMIT: 501 + /* 502 + * The PMBUS_VIN_UV_LIMIT[9:0] is the limit value, and bit10-bit15 should 503 + * not be changed. The scale of PMBUS_VIN_UV_LIMIT is 31.25mV/Lsb, and the 504 + * vin scale is set to 31.25mV/Lsb(using r/m/b scale), so the word data can 505 + * be written directly. 506 + */ 507 + ret = pmbus_read_word_data(client, page, 0xff, reg); 508 + if (ret < 0) 509 + return ret; 510 + 511 + ret = pmbus_write_word_data(client, page, reg, 512 + (ret & ~GENMASK(9, 0)) | 513 + FIELD_PREP(GENMASK(9, 0), 514 + word)); 515 + break; 516 + case PMBUS_IOUT_OC_FAULT_LIMIT: 517 + case PMBUS_IOUT_OC_WARN_LIMIT: 518 + ret = pmbus_write_word_data(client, page, reg, 519 + DIV_ROUND_CLOSEST(word * MP2869_READ_IOUT_DIV, 520 + MP2869_IOUT_LIMIT_UINT * 521 + data->iout_scale[page])); 522 + break; 523 + case PMBUS_POUT_OP_WARN_LIMIT: 524 + /* 525 + * The POUT_OP_WARN_LIMIT[11:0] is the limit value, and bit12-bit15 should 526 + * not be changed. The scale of POUT_OP_WARN_LIMIT is 2W/Lsb. 527 + */ 528 + ret = pmbus_read_word_data(client, page, 0xff, reg); 529 + if (ret < 0) 530 + return ret; 531 + 532 + ret = pmbus_write_word_data(client, page, reg, 533 + (ret & ~GENMASK(11, 0)) | 534 + FIELD_PREP(GENMASK(11, 0), 535 + DIV_ROUND_CLOSEST(word, 536 + MP2869_POUT_OP_GAIN))); 537 + break; 538 + default: 539 + ret = -EINVAL; 540 + break; 541 + } 542 + 543 + return ret; 544 + } 545 + 546 + static int mp2869_identify(struct i2c_client *client, struct pmbus_driver_info *info) 547 + { 548 + int ret; 549 + 550 + /* Identify whether tsns digital fault is enable */ 551 + ret = mp2869_identify_thwn_flt(client, info, 1); 552 + if (ret < 0) 553 + return 0; 554 + 555 + /* Identify vout scale for rail1. */ 556 + ret = mp2869_identify_vout_scale(client, info, 0); 557 + if (ret < 0) 558 + return ret; 559 + 560 + /* Identify vout scale for rail2. */ 561 + ret = mp2869_identify_vout_scale(client, info, 1); 562 + if (ret < 0) 563 + return ret; 564 + 565 + /* Identify iout scale for rail 1. */ 566 + ret = mp2869_identify_iout_scale(client, info, 0); 567 + if (ret < 0) 568 + return ret; 569 + 570 + /* Identify iout scale for rail 2. */ 571 + return mp2869_identify_iout_scale(client, info, 1); 572 + } 573 + 574 + static const struct pmbus_driver_info mp2869_info = { 575 + .pages = MP2869_PAGE_NUM, 576 + .format[PSC_VOLTAGE_IN] = direct, 577 + .format[PSC_CURRENT_IN] = linear, 578 + .format[PSC_CURRENT_OUT] = direct, 579 + .format[PSC_TEMPERATURE] = direct, 580 + .format[PSC_POWER] = direct, 581 + .format[PSC_VOLTAGE_OUT] = direct, 582 + 583 + .m[PSC_VOLTAGE_IN] = 32, 584 + .R[PSC_VOLTAGE_IN] = 0, 585 + .b[PSC_VOLTAGE_IN] = 0, 586 + 587 + .m[PSC_VOLTAGE_OUT] = 1, 588 + .R[PSC_VOLTAGE_OUT] = 3, 589 + .b[PSC_VOLTAGE_OUT] = 0, 590 + 591 + .m[PSC_CURRENT_OUT] = 1, 592 + .R[PSC_CURRENT_OUT] = 0, 593 + .b[PSC_CURRENT_OUT] = 0, 594 + 595 + .m[PSC_TEMPERATURE] = 1, 596 + .R[PSC_TEMPERATURE] = 0, 597 + .b[PSC_TEMPERATURE] = 0, 598 + 599 + .m[PSC_POWER] = 1, 600 + .R[PSC_POWER] = 0, 601 + .b[PSC_POWER] = 0, 602 + 603 + .func[0] = MP2869_RAIL1_FUNC, 604 + .func[1] = MP2869_RAIL2_FUNC, 605 + .read_word_data = mp2869_read_word_data, 606 + .write_word_data = mp2869_write_word_data, 607 + .read_byte_data = mp2869_read_byte_data, 608 + .identify = mp2869_identify, 609 + }; 610 + 611 + static int mp2869_probe(struct i2c_client *client) 612 + { 613 + struct pmbus_driver_info *info; 614 + struct mp2869_data *data; 615 + 616 + data = devm_kzalloc(&client->dev, sizeof(struct mp2869_data), 617 + GFP_KERNEL); 618 + if (!data) 619 + return -ENOMEM; 620 + 621 + memcpy(&data->info, &mp2869_info, sizeof(*info)); 622 + info = &data->info; 623 + 624 + return pmbus_do_probe(client, info); 625 + } 626 + 627 + static const struct i2c_device_id mp2869_id[] = { 628 + {"mp2869", 0}, 629 + {"mp29608", 1}, 630 + {"mp29612", 2}, 631 + {"mp29816", 3}, 632 + {} 633 + }; 634 + MODULE_DEVICE_TABLE(i2c, mp2869_id); 635 + 636 + static const struct of_device_id __maybe_unused mp2869_of_match[] = { 637 + {.compatible = "mps,mp2869", .data = (void *)0}, 638 + {.compatible = "mps,mp29608", .data = (void *)1}, 639 + {.compatible = "mps,mp29612", .data = (void *)2}, 640 + {.compatible = "mps,mp29816", .data = (void *)3}, 641 + {} 642 + }; 643 + MODULE_DEVICE_TABLE(of, mp2869_of_match); 644 + 645 + static struct i2c_driver mp2869_driver = { 646 + .driver = { 647 + .name = "mp2869", 648 + .of_match_table = mp2869_of_match, 649 + }, 650 + .probe = mp2869_probe, 651 + .id_table = mp2869_id, 652 + }; 653 + 654 + module_i2c_driver(mp2869_driver); 655 + 656 + MODULE_AUTHOR("Wensheng Wang <wenswang@yeah.net>"); 657 + MODULE_DESCRIPTION("PMBus driver for MPS MP2869"); 658 + MODULE_LICENSE("GPL"); 659 + MODULE_IMPORT_NS("PMBUS");