1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Driver for Infineon XDP710 Hot-Swap Controller 4 */ 5 6 #include <linux/bitops.h> 7 #include <linux/i2c.h> 8 #include <linux/module.h> 9 #include <linux/of_device.h> 10 #include "pmbus.h" 11 12 #define XDP710_REG_CFG 0xD3 13 #define XDP710_V_SNS_CFG 0xD4 14 #define XDP710_CS_RNG 0xD5 15 16 /* 17 * The table to map configuration register values 18 * to sense resistor values 19 */ 20 static const int micro_ohm_rsense[] = { 21 200, 250, 300, 330, 400, 470, 500, 600, 22 670, 700, 750, 800, 900, 1000, 1100, 1200, 23 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 24 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 25 2800, 3000, 3100, 3200, 3300, 3400, 3500, 3600, 26 3700, 3800, 3900, 4000, 4100, 4200, 4300, 4400, 27 4500, 4600, 4700, 4800, 4900, 5000, 5500, 6000, 28 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000 29 }; 30 31 static struct pmbus_driver_info xdp710_info = { 32 .pages = 1, 33 .format[PSC_VOLTAGE_IN] = direct, 34 .format[PSC_VOLTAGE_OUT] = direct, 35 .format[PSC_CURRENT_OUT] = direct, 36 .format[PSC_POWER] = direct, 37 .format[PSC_TEMPERATURE] = direct, 38 .m[PSC_VOLTAGE_IN] = 4653, 39 .b[PSC_VOLTAGE_IN] = 0, 40 .R[PSC_VOLTAGE_IN] = -2, 41 .m[PSC_VOLTAGE_OUT] = 4653, 42 .b[PSC_VOLTAGE_OUT] = 0, 43 .R[PSC_VOLTAGE_OUT] = -2, 44 .m[PSC_CURRENT_OUT] = 23165, 45 .b[PSC_CURRENT_OUT] = 0, 46 .R[PSC_CURRENT_OUT] = -2, 47 .m[PSC_POWER] = 4211, 48 .b[PSC_POWER] = 0, 49 .R[PSC_POWER] = -2, 50 .m[PSC_TEMPERATURE] = 52, 51 .b[PSC_TEMPERATURE] = 14321, 52 .R[PSC_TEMPERATURE] = -1, 53 .func[0] = 54 PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN | 55 PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT | 56 PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, 57 }; 58 59 static int xdp710_probe(struct i2c_client *client) 60 { 61 struct pmbus_driver_info *info; 62 u8 cs_rng; 63 u8 vtlm_rng; 64 int rsense; 65 int ret; 66 int m = 0; 67 68 info = devm_kmemdup(&client->dev, &xdp710_info, sizeof(*info), 69 GFP_KERNEL); 70 if (!info) 71 return -ENOMEM; 72 73 ret = i2c_smbus_read_word_data(client, XDP710_CS_RNG); 74 if (ret < 0) { 75 dev_err(&client->dev, "Can't get CS_RNG"); 76 return ret; 77 } 78 cs_rng = (ret >> 6) & GENMASK(1, 0); 79 80 ret = i2c_smbus_read_word_data(client, XDP710_V_SNS_CFG); 81 if (ret < 0) { 82 dev_err(&client->dev, "Can't get V_SNS_CFG"); 83 return ret; 84 } 85 vtlm_rng = ret & GENMASK(1, 0); 86 87 ret = i2c_smbus_read_word_data(client, XDP710_REG_CFG); 88 if (ret < 0) { 89 dev_err(&client->dev, "Can't get REG_CFG"); 90 return ret; 91 } 92 ret &= GENMASK(5, 0); 93 rsense = micro_ohm_rsense[ret]; 94 95 info->m[PSC_VOLTAGE_IN] <<= vtlm_rng; 96 info->m[PSC_VOLTAGE_OUT] <<= vtlm_rng; 97 98 m = info->m[PSC_CURRENT_OUT]; 99 info->m[PSC_CURRENT_OUT] = DIV_ROUND_CLOSEST(m * rsense >> cs_rng, 1000); 100 101 m = info->m[PSC_POWER]; 102 info->m[PSC_POWER] = DIV_ROUND_CLOSEST(m * rsense >> cs_rng, 1000); 103 104 return pmbus_do_probe(client, info); 105 } 106 107 static const struct of_device_id xdp710_of_match[] = { 108 { .compatible = "infineon,xdp710" }, 109 {} 110 }; 111 112 static const struct i2c_device_id xdp710_id[] = { 113 {"xdp710"}, 114 { } 115 }; 116 MODULE_DEVICE_TABLE(i2c, xdp710_id); 117 118 static struct i2c_driver xdp710_driver = { 119 .driver = { 120 .name = "xdp710", 121 .of_match_table = xdp710_of_match, 122 }, 123 .probe = xdp710_probe, 124 .id_table = xdp710_id, 125 }; 126 module_i2c_driver(xdp710_driver); 127 128 MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); 129 MODULE_DESCRIPTION("PMBus driver for XDP710 HSC"); 130 MODULE_LICENSE("GPL"); 131 MODULE_IMPORT_NS(PMBUS); 132