From ddfb41ca2a33c9f5053126324597510974724a1f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 11 Sep 2011 20:31:09 -0700 Subject: hwmon: (pmbus/ltc2978) Add support for LTC3880 to LTC2978 driver The LTC3880 PMBus command set is comparable to LTC2978. Add support for it to the LTC2978 driver. Signed-off-by: Guenter Roeck Reviewed-by: Robert Coulson --- drivers/hwmon/pmbus/ltc2978.c | 137 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 125 insertions(+), 12 deletions(-) (limited to 'drivers/hwmon/pmbus/ltc2978.c') diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 02b2e49adb31..820fff48910b 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -1,5 +1,5 @@ /* - * Hardware monitoring driver for LTC2978 + * Hardware monitoring driver for LTC2978 and LTC3880 * * Copyright (c) 2011 Ericsson AB. * @@ -26,19 +26,28 @@ #include #include "pmbus.h" -enum chips { ltc2978 }; +enum chips { ltc2978, ltc3880 }; +/* LTC2978 and LTC3880 */ #define LTC2978_MFR_VOUT_PEAK 0xdd #define LTC2978_MFR_VIN_PEAK 0xde #define LTC2978_MFR_TEMPERATURE_PEAK 0xdf #define LTC2978_MFR_SPECIAL_ID 0xe7 +/* LTC2978 only */ #define LTC2978_MFR_VOUT_MIN 0xfb #define LTC2978_MFR_VIN_MIN 0xfc #define LTC2978_MFR_TEMPERATURE_MIN 0xfd +/* LTC3880 only */ +#define LTC3880_MFR_IOUT_PEAK 0xd7 +#define LTC3880_MFR_CLEAR_PEAKS 0xe3 +#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 + #define LTC2978_ID_REV1 0x0121 #define LTC2978_ID_REV2 0x0122 +#define LTC3880_ID 0x4000 +#define LTC3880_ID_MASK 0xff00 /* * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which @@ -52,6 +61,8 @@ struct ltc2978_data { int vin_min, vin_max; int temp_min, temp_max; int vout_min[8], vout_max[8]; + int iout_max[2]; + int temp2_max[2]; struct pmbus_driver_info info; }; @@ -70,7 +81,8 @@ static inline int lin11_to_val(int data) return (e < 0 ? m >> -e : m << e); } -static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2978_read_word_data_common(struct i2c_client *client, int page, + int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -106,6 +118,25 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) ret = data->temp_max; } break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { case PMBUS_VIRT_READ_VIN_MIN: ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN); if (ret >= 0) { @@ -140,18 +171,74 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) ret = data->temp_min; } break; - case PMBUS_VIRT_RESET_VOUT_HISTORY: - case PMBUS_VIRT_RESET_VIN_HISTORY: - case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_READ_IOUT_MAX: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_READ_TEMP2_MAX: + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + ret = -ENXIO; + break; + default: + ret = ltc2978_read_word_data_common(client, page, reg); + break; + } + return ret; +} + +static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iout_max[page])) + data->iout_max[page] = ret; + ret = data->iout_max[page]; + } + break; + case PMBUS_VIRT_READ_TEMP2_MAX: + ret = pmbus_read_word_data(client, page, + LTC3880_MFR_TEMPERATURE2_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->temp2_max[page])) + data->temp2_max[page] = ret; + ret = data->temp2_max[page]; + } + break; + case PMBUS_VIRT_READ_VIN_MIN: + case PMBUS_VIRT_READ_VOUT_MIN: + case PMBUS_VIRT_READ_TEMP_MIN: + ret = -ENXIO; + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP2_HISTORY: ret = 0; break; default: - ret = -ENODATA; + ret = ltc2978_read_word_data_common(client, page, reg); break; } return ret; } +static int ltc2978_clear_peaks(struct i2c_client *client, int page, + enum chips id) +{ + int ret; + + if (id == ltc2978) + ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + else + ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); + + return ret; +} + static int ltc2978_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { @@ -160,20 +247,28 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, int ret; switch (reg) { + case PMBUS_VIRT_RESET_IOUT_HISTORY: + data->iout_max[page] = 0x7fff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + data->temp2_max[page] = 0x7fff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; case PMBUS_VIRT_RESET_VOUT_HISTORY: data->vout_min[page] = 0xffff; data->vout_max[page] = 0; - ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + ret = ltc2978_clear_peaks(client, page, data->id); break; case PMBUS_VIRT_RESET_VIN_HISTORY: data->vin_min = 0x7bff; data->vin_max = 0; - ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + ret = ltc2978_clear_peaks(client, page, data->id); break; case PMBUS_VIRT_RESET_TEMP_HISTORY: data->temp_min = 0x7bff; data->temp_max = 0x7fff; - ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + ret = ltc2978_clear_peaks(client, page, data->id); break; default: ret = -ENODATA; @@ -184,6 +279,7 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, static const struct i2c_device_id ltc2978_id[] = { {"ltc2978", ltc2978}, + {"ltc3880", ltc3880}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); @@ -211,6 +307,8 @@ static int ltc2978_probe(struct i2c_client *client, if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { data->id = ltc2978; + } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { + data->id = ltc3880; } else { dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); ret = -ENODEV; @@ -223,7 +321,6 @@ static int ltc2978_probe(struct i2c_client *client, ltc2978_id[data->id].name); info = &data->info; - info->read_word_data = ltc2978_read_word_data; info->write_word_data = ltc2978_write_word_data; data->vout_min[0] = 0xffff; @@ -233,6 +330,7 @@ static int ltc2978_probe(struct i2c_client *client, switch (id->driver_data) { case ltc2978: + info->read_word_data = ltc2978_read_word_data; info->pages = 8; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT @@ -243,6 +341,21 @@ static int ltc2978_probe(struct i2c_client *client, data->vout_min[i] = 0xffff; } break; + case ltc3880: + info->read_word_data = ltc3880_read_word_data; + info->pages = 2; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + data->vout_min[1] = 0xffff; + break; default: ret = -ENODEV; goto err_mem; @@ -289,7 +402,7 @@ static void __exit ltc2978_exit(void) } MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LTC2978"); +MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880"); MODULE_LICENSE("GPL"); module_init(ltc2978_init); module_exit(ltc2978_exit); -- cgit v1.2.3-59-g8ed1b