1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28d28cd1bSGuenter Roeck /*
38d28cd1bSGuenter Roeck * Hardware monitoring driver for LTC3815
48d28cd1bSGuenter Roeck *
58d28cd1bSGuenter Roeck * Copyright (c) 2015 Linear Technology
68d28cd1bSGuenter Roeck * Copyright (c) 2015 Guenter Roeck
78d28cd1bSGuenter Roeck */
88d28cd1bSGuenter Roeck
98d28cd1bSGuenter Roeck #include <linux/err.h>
108d28cd1bSGuenter Roeck #include <linux/i2c.h>
118d28cd1bSGuenter Roeck #include <linux/init.h>
128d28cd1bSGuenter Roeck #include <linux/jiffies.h>
138d28cd1bSGuenter Roeck #include <linux/kernel.h>
148d28cd1bSGuenter Roeck #include <linux/module.h>
158d28cd1bSGuenter Roeck #include "pmbus.h"
168d28cd1bSGuenter Roeck
178d28cd1bSGuenter Roeck #define LTC3815_MFR_IOUT_PEAK 0xd7
188d28cd1bSGuenter Roeck #define LTC3815_MFR_VOUT_PEAK 0xdd
198d28cd1bSGuenter Roeck #define LTC3815_MFR_VIN_PEAK 0xde
208d28cd1bSGuenter Roeck #define LTC3815_MFR_TEMP_PEAK 0xdf
218d28cd1bSGuenter Roeck #define LTC3815_MFR_IIN_PEAK 0xe1
228d28cd1bSGuenter Roeck #define LTC3815_MFR_SPECIAL_ID 0xe7
238d28cd1bSGuenter Roeck
248d28cd1bSGuenter Roeck #define LTC3815_ID 0x8000
258d28cd1bSGuenter Roeck #define LTC3815_ID_MASK 0xff00
268d28cd1bSGuenter Roeck
ltc3815_read_byte_data(struct i2c_client * client,int page,int reg)278d28cd1bSGuenter Roeck static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg)
288d28cd1bSGuenter Roeck {
298d28cd1bSGuenter Roeck int ret;
308d28cd1bSGuenter Roeck
318d28cd1bSGuenter Roeck switch (reg) {
328d28cd1bSGuenter Roeck case PMBUS_VOUT_MODE:
338d28cd1bSGuenter Roeck /*
348d28cd1bSGuenter Roeck * The chip returns 0x3e, suggesting VID mode with manufacturer
358d28cd1bSGuenter Roeck * specific VID codes. Since the output voltage is reported
368d28cd1bSGuenter Roeck * with a LSB of 0.5mV, override and report direct mode with
378d28cd1bSGuenter Roeck * appropriate coefficients.
388d28cd1bSGuenter Roeck */
398d28cd1bSGuenter Roeck ret = 0x40;
408d28cd1bSGuenter Roeck break;
418d28cd1bSGuenter Roeck default:
428d28cd1bSGuenter Roeck ret = -ENODATA;
438d28cd1bSGuenter Roeck break;
448d28cd1bSGuenter Roeck }
458d28cd1bSGuenter Roeck return ret;
468d28cd1bSGuenter Roeck }
478d28cd1bSGuenter Roeck
ltc3815_write_byte(struct i2c_client * client,int page,u8 reg)488d28cd1bSGuenter Roeck static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg)
498d28cd1bSGuenter Roeck {
508d28cd1bSGuenter Roeck int ret;
518d28cd1bSGuenter Roeck
528d28cd1bSGuenter Roeck switch (reg) {
538d28cd1bSGuenter Roeck case PMBUS_CLEAR_FAULTS:
548d28cd1bSGuenter Roeck /*
558d28cd1bSGuenter Roeck * LTC3815 does not support the CLEAR_FAULTS command.
568d28cd1bSGuenter Roeck * Emulate it by clearing the status register.
578d28cd1bSGuenter Roeck */
5843f33b6eSGuenter Roeck ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_WORD);
598d28cd1bSGuenter Roeck if (ret > 0) {
608d28cd1bSGuenter Roeck pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD,
618d28cd1bSGuenter Roeck ret);
628d28cd1bSGuenter Roeck ret = 0;
638d28cd1bSGuenter Roeck }
648d28cd1bSGuenter Roeck break;
658d28cd1bSGuenter Roeck default:
668d28cd1bSGuenter Roeck ret = -ENODATA;
678d28cd1bSGuenter Roeck break;
688d28cd1bSGuenter Roeck }
698d28cd1bSGuenter Roeck return ret;
708d28cd1bSGuenter Roeck }
718d28cd1bSGuenter Roeck
ltc3815_read_word_data(struct i2c_client * client,int page,int phase,int reg)7243f33b6eSGuenter Roeck static int ltc3815_read_word_data(struct i2c_client *client, int page,
7343f33b6eSGuenter Roeck int phase, int reg)
748d28cd1bSGuenter Roeck {
758d28cd1bSGuenter Roeck int ret;
768d28cd1bSGuenter Roeck
778d28cd1bSGuenter Roeck switch (reg) {
788d28cd1bSGuenter Roeck case PMBUS_VIRT_READ_VIN_MAX:
7943f33b6eSGuenter Roeck ret = pmbus_read_word_data(client, page, phase,
8043f33b6eSGuenter Roeck LTC3815_MFR_VIN_PEAK);
818d28cd1bSGuenter Roeck break;
828d28cd1bSGuenter Roeck case PMBUS_VIRT_READ_VOUT_MAX:
8343f33b6eSGuenter Roeck ret = pmbus_read_word_data(client, page, phase,
8443f33b6eSGuenter Roeck LTC3815_MFR_VOUT_PEAK);
858d28cd1bSGuenter Roeck break;
868d28cd1bSGuenter Roeck case PMBUS_VIRT_READ_TEMP_MAX:
8743f33b6eSGuenter Roeck ret = pmbus_read_word_data(client, page, phase,
8843f33b6eSGuenter Roeck LTC3815_MFR_TEMP_PEAK);
898d28cd1bSGuenter Roeck break;
908d28cd1bSGuenter Roeck case PMBUS_VIRT_READ_IOUT_MAX:
9143f33b6eSGuenter Roeck ret = pmbus_read_word_data(client, page, phase,
9243f33b6eSGuenter Roeck LTC3815_MFR_IOUT_PEAK);
938d28cd1bSGuenter Roeck break;
948d28cd1bSGuenter Roeck case PMBUS_VIRT_READ_IIN_MAX:
9543f33b6eSGuenter Roeck ret = pmbus_read_word_data(client, page, phase,
9643f33b6eSGuenter Roeck LTC3815_MFR_IIN_PEAK);
978d28cd1bSGuenter Roeck break;
988d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_VOUT_HISTORY:
998d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_VIN_HISTORY:
1008d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_TEMP_HISTORY:
1018d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_IOUT_HISTORY:
1028d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_IIN_HISTORY:
1038d28cd1bSGuenter Roeck ret = 0;
1048d28cd1bSGuenter Roeck break;
1058d28cd1bSGuenter Roeck default:
1068d28cd1bSGuenter Roeck ret = -ENODATA;
1078d28cd1bSGuenter Roeck break;
1088d28cd1bSGuenter Roeck }
1098d28cd1bSGuenter Roeck return ret;
1108d28cd1bSGuenter Roeck }
1118d28cd1bSGuenter Roeck
ltc3815_write_word_data(struct i2c_client * client,int page,int reg,u16 word)1128d28cd1bSGuenter Roeck static int ltc3815_write_word_data(struct i2c_client *client, int page,
1138d28cd1bSGuenter Roeck int reg, u16 word)
1148d28cd1bSGuenter Roeck {
1158d28cd1bSGuenter Roeck int ret;
1168d28cd1bSGuenter Roeck
1178d28cd1bSGuenter Roeck switch (reg) {
1188d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_IIN_HISTORY:
1198d28cd1bSGuenter Roeck ret = pmbus_write_word_data(client, page,
1208d28cd1bSGuenter Roeck LTC3815_MFR_IIN_PEAK, 0);
1218d28cd1bSGuenter Roeck break;
1228d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_IOUT_HISTORY:
1238d28cd1bSGuenter Roeck ret = pmbus_write_word_data(client, page,
1248d28cd1bSGuenter Roeck LTC3815_MFR_IOUT_PEAK, 0);
1258d28cd1bSGuenter Roeck break;
1268d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_VOUT_HISTORY:
1278d28cd1bSGuenter Roeck ret = pmbus_write_word_data(client, page,
1288d28cd1bSGuenter Roeck LTC3815_MFR_VOUT_PEAK, 0);
1298d28cd1bSGuenter Roeck break;
1308d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_VIN_HISTORY:
1318d28cd1bSGuenter Roeck ret = pmbus_write_word_data(client, page,
1328d28cd1bSGuenter Roeck LTC3815_MFR_VIN_PEAK, 0);
1338d28cd1bSGuenter Roeck break;
1348d28cd1bSGuenter Roeck case PMBUS_VIRT_RESET_TEMP_HISTORY:
1358d28cd1bSGuenter Roeck ret = pmbus_write_word_data(client, page,
1368d28cd1bSGuenter Roeck LTC3815_MFR_TEMP_PEAK, 0);
1378d28cd1bSGuenter Roeck break;
1388d28cd1bSGuenter Roeck default:
1398d28cd1bSGuenter Roeck ret = -ENODATA;
1408d28cd1bSGuenter Roeck break;
1418d28cd1bSGuenter Roeck }
1428d28cd1bSGuenter Roeck return ret;
1438d28cd1bSGuenter Roeck }
1448d28cd1bSGuenter Roeck
1458d28cd1bSGuenter Roeck static const struct i2c_device_id ltc3815_id[] = {
1468d28cd1bSGuenter Roeck {"ltc3815", 0},
1478d28cd1bSGuenter Roeck { }
1488d28cd1bSGuenter Roeck };
1498d28cd1bSGuenter Roeck MODULE_DEVICE_TABLE(i2c, ltc3815_id);
1508d28cd1bSGuenter Roeck
1518d28cd1bSGuenter Roeck static struct pmbus_driver_info ltc3815_info = {
1528d28cd1bSGuenter Roeck .pages = 1,
1538d28cd1bSGuenter Roeck .format[PSC_VOLTAGE_IN] = direct,
1548d28cd1bSGuenter Roeck .format[PSC_VOLTAGE_OUT] = direct,
1558d28cd1bSGuenter Roeck .format[PSC_CURRENT_IN] = direct,
1568d28cd1bSGuenter Roeck .format[PSC_CURRENT_OUT] = direct,
1578d28cd1bSGuenter Roeck .format[PSC_TEMPERATURE] = direct,
1588d28cd1bSGuenter Roeck .m[PSC_VOLTAGE_IN] = 250,
1598d28cd1bSGuenter Roeck .b[PSC_VOLTAGE_IN] = 0,
1608d28cd1bSGuenter Roeck .R[PSC_VOLTAGE_IN] = 0,
1618d28cd1bSGuenter Roeck .m[PSC_VOLTAGE_OUT] = 2,
1628d28cd1bSGuenter Roeck .b[PSC_VOLTAGE_OUT] = 0,
1638d28cd1bSGuenter Roeck .R[PSC_VOLTAGE_OUT] = 3,
1648d28cd1bSGuenter Roeck .m[PSC_CURRENT_IN] = 1,
1658d28cd1bSGuenter Roeck .b[PSC_CURRENT_IN] = 0,
1668d28cd1bSGuenter Roeck .R[PSC_CURRENT_IN] = 2,
1678d28cd1bSGuenter Roeck .m[PSC_CURRENT_OUT] = 1,
1688d28cd1bSGuenter Roeck .b[PSC_CURRENT_OUT] = 0,
1698d28cd1bSGuenter Roeck .R[PSC_CURRENT_OUT] = 2,
1708d28cd1bSGuenter Roeck .m[PSC_TEMPERATURE] = 1,
1718d28cd1bSGuenter Roeck .b[PSC_TEMPERATURE] = 0,
1728d28cd1bSGuenter Roeck .R[PSC_TEMPERATURE] = 0,
1738d28cd1bSGuenter Roeck .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT |
1748d28cd1bSGuenter Roeck PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
1758d28cd1bSGuenter Roeck .read_byte_data = ltc3815_read_byte_data,
1768d28cd1bSGuenter Roeck .read_word_data = ltc3815_read_word_data,
1778d28cd1bSGuenter Roeck .write_byte = ltc3815_write_byte,
1788d28cd1bSGuenter Roeck .write_word_data = ltc3815_write_word_data,
1798d28cd1bSGuenter Roeck };
1808d28cd1bSGuenter Roeck
ltc3815_probe(struct i2c_client * client)181dd431939SStephen Kitt static int ltc3815_probe(struct i2c_client *client)
1828d28cd1bSGuenter Roeck {
1838d28cd1bSGuenter Roeck int chip_id;
1848d28cd1bSGuenter Roeck
1858d28cd1bSGuenter Roeck if (!i2c_check_functionality(client->adapter,
1868d28cd1bSGuenter Roeck I2C_FUNC_SMBUS_READ_WORD_DATA))
1878d28cd1bSGuenter Roeck return -ENODEV;
1888d28cd1bSGuenter Roeck
1898d28cd1bSGuenter Roeck chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
1908d28cd1bSGuenter Roeck if (chip_id < 0)
1918d28cd1bSGuenter Roeck return chip_id;
1928d28cd1bSGuenter Roeck if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
1938d28cd1bSGuenter Roeck return -ENODEV;
1948d28cd1bSGuenter Roeck
195dd431939SStephen Kitt return pmbus_do_probe(client, <c3815_info);
1968d28cd1bSGuenter Roeck }
1978d28cd1bSGuenter Roeck
1988d28cd1bSGuenter Roeck static struct i2c_driver ltc3815_driver = {
1998d28cd1bSGuenter Roeck .driver = {
2008d28cd1bSGuenter Roeck .name = "ltc3815",
2018d28cd1bSGuenter Roeck },
202*1975d167SUwe Kleine-König .probe = ltc3815_probe,
2038d28cd1bSGuenter Roeck .id_table = ltc3815_id,
2048d28cd1bSGuenter Roeck };
2058d28cd1bSGuenter Roeck
2068d28cd1bSGuenter Roeck module_i2c_driver(ltc3815_driver);
2078d28cd1bSGuenter Roeck
2088d28cd1bSGuenter Roeck MODULE_AUTHOR("Guenter Roeck");
2098d28cd1bSGuenter Roeck MODULE_DESCRIPTION("PMBus driver for LTC3815");
2108d28cd1bSGuenter Roeck MODULE_LICENSE("GPL");
211b94ca77eSGuenter Roeck MODULE_IMPORT_NS(PMBUS);
212