1 /* 2 * Hardware monitoring driver for LTC3815 3 * 4 * Copyright (c) 2015 Linear Technology 5 * Copyright (c) 2015 Guenter Roeck 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include <linux/err.h> 19 #include <linux/i2c.h> 20 #include <linux/init.h> 21 #include <linux/jiffies.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include "pmbus.h" 25 26 #define LTC3815_MFR_IOUT_PEAK 0xd7 27 #define LTC3815_MFR_VOUT_PEAK 0xdd 28 #define LTC3815_MFR_VIN_PEAK 0xde 29 #define LTC3815_MFR_TEMP_PEAK 0xdf 30 #define LTC3815_MFR_IIN_PEAK 0xe1 31 #define LTC3815_MFR_SPECIAL_ID 0xe7 32 33 #define LTC3815_ID 0x8000 34 #define LTC3815_ID_MASK 0xff00 35 36 static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg) 37 { 38 int ret; 39 40 switch (reg) { 41 case PMBUS_VOUT_MODE: 42 /* 43 * The chip returns 0x3e, suggesting VID mode with manufacturer 44 * specific VID codes. Since the output voltage is reported 45 * with a LSB of 0.5mV, override and report direct mode with 46 * appropriate coefficients. 47 */ 48 ret = 0x40; 49 break; 50 default: 51 ret = -ENODATA; 52 break; 53 } 54 return ret; 55 } 56 57 static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg) 58 { 59 int ret; 60 61 switch (reg) { 62 case PMBUS_CLEAR_FAULTS: 63 /* 64 * LTC3815 does not support the CLEAR_FAULTS command. 65 * Emulate it by clearing the status register. 66 */ 67 ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD); 68 if (ret > 0) { 69 pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD, 70 ret); 71 ret = 0; 72 } 73 break; 74 default: 75 ret = -ENODATA; 76 break; 77 } 78 return ret; 79 } 80 81 static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg) 82 { 83 int ret; 84 85 switch (reg) { 86 case PMBUS_VIRT_READ_VIN_MAX: 87 ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK); 88 break; 89 case PMBUS_VIRT_READ_VOUT_MAX: 90 ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK); 91 break; 92 case PMBUS_VIRT_READ_TEMP_MAX: 93 ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK); 94 break; 95 case PMBUS_VIRT_READ_IOUT_MAX: 96 ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK); 97 break; 98 case PMBUS_VIRT_READ_IIN_MAX: 99 ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK); 100 break; 101 case PMBUS_VIRT_RESET_VOUT_HISTORY: 102 case PMBUS_VIRT_RESET_VIN_HISTORY: 103 case PMBUS_VIRT_RESET_TEMP_HISTORY: 104 case PMBUS_VIRT_RESET_IOUT_HISTORY: 105 case PMBUS_VIRT_RESET_IIN_HISTORY: 106 ret = 0; 107 break; 108 default: 109 ret = -ENODATA; 110 break; 111 } 112 return ret; 113 } 114 115 static int ltc3815_write_word_data(struct i2c_client *client, int page, 116 int reg, u16 word) 117 { 118 int ret; 119 120 switch (reg) { 121 case PMBUS_VIRT_RESET_IIN_HISTORY: 122 ret = pmbus_write_word_data(client, page, 123 LTC3815_MFR_IIN_PEAK, 0); 124 break; 125 case PMBUS_VIRT_RESET_IOUT_HISTORY: 126 ret = pmbus_write_word_data(client, page, 127 LTC3815_MFR_IOUT_PEAK, 0); 128 break; 129 case PMBUS_VIRT_RESET_VOUT_HISTORY: 130 ret = pmbus_write_word_data(client, page, 131 LTC3815_MFR_VOUT_PEAK, 0); 132 break; 133 case PMBUS_VIRT_RESET_VIN_HISTORY: 134 ret = pmbus_write_word_data(client, page, 135 LTC3815_MFR_VIN_PEAK, 0); 136 break; 137 case PMBUS_VIRT_RESET_TEMP_HISTORY: 138 ret = pmbus_write_word_data(client, page, 139 LTC3815_MFR_TEMP_PEAK, 0); 140 break; 141 default: 142 ret = -ENODATA; 143 break; 144 } 145 return ret; 146 } 147 148 static const struct i2c_device_id ltc3815_id[] = { 149 {"ltc3815", 0}, 150 { } 151 }; 152 MODULE_DEVICE_TABLE(i2c, ltc3815_id); 153 154 static struct pmbus_driver_info ltc3815_info = { 155 .pages = 1, 156 .format[PSC_VOLTAGE_IN] = direct, 157 .format[PSC_VOLTAGE_OUT] = direct, 158 .format[PSC_CURRENT_IN] = direct, 159 .format[PSC_CURRENT_OUT] = direct, 160 .format[PSC_TEMPERATURE] = direct, 161 .m[PSC_VOLTAGE_IN] = 250, 162 .b[PSC_VOLTAGE_IN] = 0, 163 .R[PSC_VOLTAGE_IN] = 0, 164 .m[PSC_VOLTAGE_OUT] = 2, 165 .b[PSC_VOLTAGE_OUT] = 0, 166 .R[PSC_VOLTAGE_OUT] = 3, 167 .m[PSC_CURRENT_IN] = 1, 168 .b[PSC_CURRENT_IN] = 0, 169 .R[PSC_CURRENT_IN] = 2, 170 .m[PSC_CURRENT_OUT] = 1, 171 .b[PSC_CURRENT_OUT] = 0, 172 .R[PSC_CURRENT_OUT] = 2, 173 .m[PSC_TEMPERATURE] = 1, 174 .b[PSC_TEMPERATURE] = 0, 175 .R[PSC_TEMPERATURE] = 0, 176 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT | 177 PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, 178 .read_byte_data = ltc3815_read_byte_data, 179 .read_word_data = ltc3815_read_word_data, 180 .write_byte = ltc3815_write_byte, 181 .write_word_data = ltc3815_write_word_data, 182 }; 183 184 static int ltc3815_probe(struct i2c_client *client, 185 const struct i2c_device_id *id) 186 { 187 int chip_id; 188 189 if (!i2c_check_functionality(client->adapter, 190 I2C_FUNC_SMBUS_READ_WORD_DATA)) 191 return -ENODEV; 192 193 chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID); 194 if (chip_id < 0) 195 return chip_id; 196 if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID) 197 return -ENODEV; 198 199 return pmbus_do_probe(client, id, <c3815_info); 200 } 201 202 static struct i2c_driver ltc3815_driver = { 203 .driver = { 204 .name = "ltc3815", 205 }, 206 .probe = ltc3815_probe, 207 .remove = pmbus_do_remove, 208 .id_table = ltc3815_id, 209 }; 210 211 module_i2c_driver(ltc3815_driver); 212 213 MODULE_AUTHOR("Guenter Roeck"); 214 MODULE_DESCRIPTION("PMBus driver for LTC3815"); 215 MODULE_LICENSE("GPL"); 216