1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * mp5926.c - pmbus driver for mps mp5926 4 * 5 * Copyright 2025 Monolithic Power Systems, Inc 6 * 7 * Author: Yuxi Wang <Yuxi.Wang@monolithicpower.com> 8 */ 9 10 #include <linux/bitfield.h> 11 #include <linux/bits.h> 12 #include <linux/i2c.h> 13 #include <linux/module.h> 14 #include <linux/of_device.h> 15 #include <linux/pmbus.h> 16 #include "pmbus.h" 17 18 #define PAGE 0x01 19 #define EFUSE_CFG 0xCF 20 #define I_SCALE_SEL 0xC6 21 #define MP5926_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ 22 PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | \ 23 PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_INPUT | \ 24 PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_VOUT) 25 26 struct mp5926_data { 27 struct pmbus_driver_info info; 28 u8 vout_mode; 29 u8 vout_linear_exponent; 30 }; 31 32 #define to_mp5926_data(x) container_of(x, struct mp5926_data, info) 33 34 static int mp5926_read_byte_data(struct i2c_client *client, int page, 35 int reg) 36 { 37 const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 38 struct mp5926_data *data = to_mp5926_data(info); 39 int ret; 40 41 switch (reg) { 42 case PMBUS_VOUT_MODE: 43 if (data->vout_mode == linear) { 44 /* 45 * The VOUT format used by the chip is linear11, 46 * not linear16. Report that VOUT is in linear mode 47 * and return exponent value extracted while probing 48 * the chip. 49 */ 50 return data->vout_linear_exponent; 51 } 52 return PB_VOUT_MODE_DIRECT; 53 default: 54 ret = -ENODATA; 55 break; 56 } 57 return ret; 58 } 59 60 static int mp5926_read_word_data(struct i2c_client *client, int page, int phase, 61 int reg) 62 { 63 const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 64 struct mp5926_data *data = to_mp5926_data(info); 65 int ret; 66 67 switch (reg) { 68 case PMBUS_READ_VOUT: 69 ret = pmbus_read_word_data(client, page, phase, reg); 70 if (ret < 0) 71 return ret; 72 /* 73 * Because the VOUT format used by the chip is linear11 and not 74 * linear16, we disregard bits[15:11]. The exponent is reported 75 * as part of the VOUT_MODE command. 76 */ 77 if (data->vout_mode == linear) 78 ret = ((s16)((ret & 0x7ff) << 5)) >> 5; 79 break; 80 default: 81 ret = -ENODATA; 82 break; 83 } 84 return ret; 85 } 86 87 static struct pmbus_driver_info mp5926_info = { 88 .pages = PAGE, 89 .format[PSC_VOLTAGE_IN] = direct, 90 .format[PSC_CURRENT_IN] = direct, 91 .format[PSC_VOLTAGE_OUT] = direct, 92 .format[PSC_TEMPERATURE] = direct, 93 .format[PSC_POWER] = direct, 94 95 .m[PSC_VOLTAGE_IN] = 16, 96 .b[PSC_VOLTAGE_IN] = 0, 97 .R[PSC_VOLTAGE_IN] = 0, 98 99 .m[PSC_CURRENT_IN] = 16, 100 .b[PSC_CURRENT_IN] = 0, 101 .R[PSC_CURRENT_IN] = 0, 102 103 .m[PSC_VOLTAGE_OUT] = 16, 104 .b[PSC_VOLTAGE_OUT] = 0, 105 .R[PSC_VOLTAGE_OUT] = 0, 106 107 .m[PSC_TEMPERATURE] = 4, 108 .b[PSC_TEMPERATURE] = 0, 109 .R[PSC_TEMPERATURE] = 0, 110 111 .m[PSC_POWER] = 25, 112 .b[PSC_POWER] = 0, 113 .R[PSC_POWER] = -2, 114 115 .read_word_data = mp5926_read_word_data, 116 .read_byte_data = mp5926_read_byte_data, 117 .func[0] = MP5926_FUNC, 118 }; 119 120 static int mp5926_probe(struct i2c_client *client) 121 { 122 struct mp5926_data *data; 123 struct pmbus_driver_info *info; 124 int ret; 125 126 data = devm_kzalloc(&client->dev, sizeof(struct mp5926_data), 127 GFP_KERNEL); 128 if (!data) 129 return -ENOMEM; 130 131 memcpy(&data->info, &mp5926_info, sizeof(*info)); 132 info = &data->info; 133 ret = i2c_smbus_read_word_data(client, EFUSE_CFG); 134 if (ret < 0) 135 return ret; 136 if (ret & BIT(12)) { 137 data->vout_mode = linear; 138 data->info.format[PSC_VOLTAGE_IN] = linear; 139 data->info.format[PSC_CURRENT_IN] = linear; 140 data->info.format[PSC_VOLTAGE_OUT] = linear; 141 data->info.format[PSC_TEMPERATURE] = linear; 142 data->info.format[PSC_POWER] = linear; 143 ret = i2c_smbus_read_word_data(client, PMBUS_READ_VOUT); 144 if (ret < 0) 145 return dev_err_probe(&client->dev, ret, "Can't get vout exponent."); 146 data->vout_linear_exponent = (u8)((ret >> 11) & 0x1f); 147 } else { 148 data->vout_mode = direct; 149 ret = i2c_smbus_read_word_data(client, I_SCALE_SEL); 150 if (ret < 0) 151 return ret; 152 if (ret & BIT(6)) 153 data->info.m[PSC_CURRENT_IN] = 4; 154 } 155 156 return pmbus_do_probe(client, info); 157 } 158 159 static const struct i2c_device_id mp5926_id[] = { 160 { "mp5926", 0 }, 161 {} 162 }; 163 MODULE_DEVICE_TABLE(i2c, mp5926_id); 164 165 static const struct of_device_id mp5926_of_match[] = { 166 { .compatible = "mps,mp5926" }, 167 {} 168 }; 169 MODULE_DEVICE_TABLE(of, mp5926_of_match); 170 171 static struct i2c_driver mp5926_driver = { 172 .probe = mp5926_probe, 173 .driver = { 174 .name = "mp5926", 175 .of_match_table = mp5926_of_match, 176 }, 177 .id_table = mp5926_id, 178 }; 179 180 module_i2c_driver(mp5926_driver); 181 MODULE_AUTHOR("Yuxi Wang <Yuxi.Wang@monolithicpower.com>"); 182 MODULE_DESCRIPTION("MPS MP5926 pmbus driver"); 183 MODULE_LICENSE("GPL"); 184 MODULE_IMPORT_NS("PMBUS"); 185