1 // SPDX-License-Identifier: GPL-2.0-or-later 2 3 #include <linux/err.h> 4 #include <linux/i2c.h> 5 #include <linux/init.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/pmbus.h> 9 #include "pmbus.h" 10 11 /* LTC4286 register */ 12 #define LTC4286_MFR_CONFIG1 0xF2 13 14 /* LTC4286 configuration */ 15 #define VRANGE_SELECT_BIT BIT(1) 16 17 #define LTC4286_MFR_ID_SIZE 3 18 19 /* 20 * Initialize the MBR as default settings which is referred to LTC4286 datasheet 21 * (March 22, 2022 version) table 3 page 16 22 */ 23 static struct pmbus_driver_info ltc4286_info = { 24 .pages = 1, 25 .format[PSC_VOLTAGE_IN] = direct, 26 .format[PSC_VOLTAGE_OUT] = direct, 27 .format[PSC_CURRENT_OUT] = direct, 28 .format[PSC_POWER] = direct, 29 .format[PSC_TEMPERATURE] = direct, 30 .m[PSC_VOLTAGE_IN] = 32, 31 .b[PSC_VOLTAGE_IN] = 0, 32 .R[PSC_VOLTAGE_IN] = 1, 33 .m[PSC_VOLTAGE_OUT] = 32, 34 .b[PSC_VOLTAGE_OUT] = 0, 35 .R[PSC_VOLTAGE_OUT] = 1, 36 .m[PSC_CURRENT_OUT] = 1024, 37 .b[PSC_CURRENT_OUT] = 0, 38 /* 39 * The rsense value used in MBR formula in LTC4286 datasheet should be ohm unit. 40 * However, the rsense value that user input is micro ohm. 41 * Thus, the MBR setting which involves rsense should be shifted by 6 digits. 42 */ 43 .R[PSC_CURRENT_OUT] = 3 - 6, 44 .m[PSC_POWER] = 1, 45 .b[PSC_POWER] = 0, 46 /* 47 * The rsense value used in MBR formula in LTC4286 datasheet should be ohm unit. 48 * However, the rsense value that user input is micro ohm. 49 * Thus, the MBR setting which involves rsense should be shifted by 6 digits. 50 */ 51 .R[PSC_POWER] = 4 - 6, 52 .m[PSC_TEMPERATURE] = 1, 53 .b[PSC_TEMPERATURE] = 273, 54 .R[PSC_TEMPERATURE] = 0, 55 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | 56 PMBUS_HAVE_PIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT | 57 PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP, 58 }; 59 60 static const struct i2c_device_id ltc4286_id[] = { 61 { "ltc4286", }, 62 { "ltc4287", }, 63 {} 64 }; 65 MODULE_DEVICE_TABLE(i2c, ltc4286_id); 66 67 static int ltc4286_probe(struct i2c_client *client) 68 { 69 int ret; 70 const struct i2c_device_id *mid; 71 u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; 72 struct pmbus_driver_info *info; 73 u32 rsense; 74 int vrange_nval, vrange_oval; 75 76 ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer); 77 if (ret < 0) { 78 return dev_err_probe(&client->dev, ret, 79 "Failed to read manufacturer id\n"); 80 } 81 82 /* 83 * Refer to ltc4286 datasheet page 20 84 * the manufacturer id is LTC 85 */ 86 if (ret != LTC4286_MFR_ID_SIZE || 87 strncmp(block_buffer, "LTC", LTC4286_MFR_ID_SIZE)) { 88 return dev_err_probe(&client->dev, -ENODEV, 89 "Manufacturer id mismatch\n"); 90 } 91 92 ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); 93 if (ret < 0) { 94 return dev_err_probe(&client->dev, ret, 95 "Failed to read manufacturer model\n"); 96 } 97 98 for (mid = ltc4286_id; mid->name[0]; mid++) { 99 if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) 100 break; 101 } 102 if (!mid->name[0]) 103 return dev_err_probe(&client->dev, -ENODEV, 104 "Unsupported device\n"); 105 106 if (of_property_read_u32(client->dev.of_node, 107 "shunt-resistor-micro-ohms", &rsense)) 108 rsense = 300; /* 0.3 mOhm if not set via DT */ 109 110 if (rsense == 0) 111 return -EINVAL; 112 113 /* Check for the latter MBR value won't overflow */ 114 if (rsense > (INT_MAX / 1024)) 115 return -EINVAL; 116 117 info = devm_kmemdup(&client->dev, <c4286_info, sizeof(*info), 118 GFP_KERNEL); 119 if (!info) 120 return -ENOMEM; 121 122 /* Check MFR1 CONFIG register bit 1 VRANGE_SELECT before driver loading */ 123 vrange_oval = i2c_smbus_read_word_data(client, LTC4286_MFR_CONFIG1); 124 if (vrange_oval < 0) 125 return dev_err_probe(&client->dev, vrange_oval, 126 "Failed to read manufacturer configuration one\n"); 127 vrange_nval = vrange_oval; 128 129 if (device_property_read_bool(&client->dev, "adi,vrange-low-enable")) { 130 vrange_nval &= 131 ~VRANGE_SELECT_BIT; /* VRANGE_SELECT = 0, 25.6 volts */ 132 133 info->m[PSC_VOLTAGE_IN] = 128; 134 info->m[PSC_VOLTAGE_OUT] = 128; 135 info->m[PSC_POWER] = 4 * rsense; 136 } else { 137 vrange_nval |= 138 VRANGE_SELECT_BIT; /* VRANGE_SELECT = 1, 102.4 volts */ 139 140 info->m[PSC_POWER] = rsense; 141 } 142 if (vrange_nval != vrange_oval) { 143 /* Set MFR1 CONFIG register bit 1 VRANGE_SELECT */ 144 ret = i2c_smbus_write_word_data(client, LTC4286_MFR_CONFIG1, 145 vrange_nval); 146 if (ret < 0) 147 return dev_err_probe(&client->dev, ret, 148 "Failed to set vrange\n"); 149 } 150 151 info->m[PSC_CURRENT_OUT] = 1024 * rsense; 152 153 return pmbus_do_probe(client, info); 154 } 155 156 static const struct of_device_id ltc4286_of_match[] = { 157 { .compatible = "lltc,ltc4286" }, 158 { .compatible = "lltc,ltc4287" }, 159 {} 160 }; 161 162 static struct i2c_driver ltc4286_driver = { 163 .driver = { 164 .name = "ltc4286", 165 .of_match_table = ltc4286_of_match, 166 }, 167 .probe = ltc4286_probe, 168 .id_table = ltc4286_id, 169 }; 170 171 module_i2c_driver(ltc4286_driver); 172 173 MODULE_AUTHOR("Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>"); 174 MODULE_DESCRIPTION("PMBUS driver for LTC4286 and compatibles"); 175 MODULE_LICENSE("GPL"); 176