1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers 4 * 5 * Copyright (c) 2020 Mellanox Technologies. All rights reserved. 6 */ 7 8 #include <linux/err.h> 9 #include <linux/i2c.h> 10 #include <linux/init.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/regulator/driver.h> 14 15 #include "pmbus.h" 16 17 #define XDPE122_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ 18 #define XDPE122_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ 19 #define XDPE122_PROT_IMVP9_10MV 0x03 /* IMVP9 mode, 10-mV DAC */ 20 #define XDPE122_AMD_625MV 0x10 /* AMD mode 6.25mV */ 21 #define XDPE122_PAGE_NUM 2 22 23 static int xdpe122_read_word_data(struct i2c_client *client, int page, 24 int phase, int reg) 25 { 26 const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 27 long val; 28 s16 exponent; 29 s32 mantissa; 30 int ret; 31 32 switch (reg) { 33 case PMBUS_VOUT_OV_FAULT_LIMIT: 34 case PMBUS_VOUT_UV_FAULT_LIMIT: 35 ret = pmbus_read_word_data(client, page, phase, reg); 36 if (ret < 0) 37 return ret; 38 39 /* Convert register value to LINEAR11 data. */ 40 exponent = ((s16)ret) >> 11; 41 mantissa = ((s16)((ret & GENMASK(10, 0)) << 5)) >> 5; 42 val = mantissa * 1000L; 43 if (exponent >= 0) 44 val <<= exponent; 45 else 46 val >>= -exponent; 47 48 /* Convert data to VID register. */ 49 switch (info->vrm_version[page]) { 50 case vr13: 51 if (val >= 500) 52 return 1 + DIV_ROUND_CLOSEST(val - 500, 10); 53 return 0; 54 case vr12: 55 if (val >= 250) 56 return 1 + DIV_ROUND_CLOSEST(val - 250, 5); 57 return 0; 58 case imvp9: 59 if (val >= 200) 60 return 1 + DIV_ROUND_CLOSEST(val - 200, 10); 61 return 0; 62 case amd625mv: 63 if (val >= 200 && val <= 1550) 64 return DIV_ROUND_CLOSEST((1550 - val) * 100, 65 625); 66 return 0; 67 default: 68 return -EINVAL; 69 } 70 default: 71 return -ENODATA; 72 } 73 74 return 0; 75 } 76 77 static int xdpe122_identify(struct i2c_client *client, 78 struct pmbus_driver_info *info) 79 { 80 u8 vout_params; 81 int i, ret, vout_mode; 82 83 vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); 84 if (vout_mode >= 0 && vout_mode != 0xff) { 85 switch (vout_mode >> 5) { 86 case 0: 87 info->format[PSC_VOLTAGE_OUT] = linear; 88 return 0; 89 case 1: 90 info->format[PSC_VOLTAGE_OUT] = vid; 91 info->read_word_data = xdpe122_read_word_data; 92 break; 93 default: 94 return -ENODEV; 95 } 96 } 97 98 for (i = 0; i < XDPE122_PAGE_NUM; i++) { 99 /* Read the register with VOUT scaling value.*/ 100 ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); 101 if (ret < 0) 102 return ret; 103 104 vout_params = ret & GENMASK(4, 0); 105 106 switch (vout_params) { 107 case XDPE122_PROT_VR12_5_10MV: 108 info->vrm_version[i] = vr13; 109 break; 110 case XDPE122_PROT_VR12_5MV: 111 info->vrm_version[i] = vr12; 112 break; 113 case XDPE122_PROT_IMVP9_10MV: 114 info->vrm_version[i] = imvp9; 115 break; 116 case XDPE122_AMD_625MV: 117 info->vrm_version[i] = amd625mv; 118 break; 119 default: 120 return -EINVAL; 121 } 122 } 123 124 return 0; 125 } 126 127 static const struct regulator_desc __maybe_unused xdpe122_reg_desc[] = { 128 PMBUS_REGULATOR("vout", 0), 129 PMBUS_REGULATOR("vout", 1), 130 }; 131 132 static struct pmbus_driver_info xdpe122_info = { 133 .pages = XDPE122_PAGE_NUM, 134 .format[PSC_VOLTAGE_IN] = linear, 135 .format[PSC_TEMPERATURE] = linear, 136 .format[PSC_CURRENT_IN] = linear, 137 .format[PSC_CURRENT_OUT] = linear, 138 .format[PSC_POWER] = linear, 139 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 140 PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 141 PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 142 PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, 143 .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 144 PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 145 PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 146 PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, 147 .identify = xdpe122_identify, 148 #if IS_ENABLED(CONFIG_SENSORS_XDPE122_REGULATOR) 149 .num_regulators = 2, 150 .reg_desc = xdpe122_reg_desc, 151 #endif 152 }; 153 154 static int xdpe122_probe(struct i2c_client *client) 155 { 156 struct pmbus_driver_info *info; 157 158 info = devm_kmemdup(&client->dev, &xdpe122_info, sizeof(*info), 159 GFP_KERNEL); 160 if (!info) 161 return -ENOMEM; 162 163 return pmbus_do_probe(client, info); 164 } 165 166 static const struct i2c_device_id xdpe122_id[] = { 167 {"xdpe11280"}, 168 {"xdpe12254"}, 169 {"xdpe12284"}, 170 {} 171 }; 172 173 MODULE_DEVICE_TABLE(i2c, xdpe122_id); 174 175 static const struct of_device_id __maybe_unused xdpe122_of_match[] = { 176 {.compatible = "infineon,xdpe11280"}, 177 {.compatible = "infineon,xdpe12254"}, 178 {.compatible = "infineon,xdpe12284"}, 179 {} 180 }; 181 MODULE_DEVICE_TABLE(of, xdpe122_of_match); 182 183 static struct i2c_driver xdpe122_driver = { 184 .driver = { 185 .name = "xdpe12284", 186 .of_match_table = of_match_ptr(xdpe122_of_match), 187 }, 188 .probe = xdpe122_probe, 189 .id_table = xdpe122_id, 190 }; 191 192 module_i2c_driver(xdpe122_driver); 193 194 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 195 MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); 196 MODULE_LICENSE("GPL"); 197 MODULE_IMPORT_NS(PMBUS); 198