diff options
Diffstat (limited to 'drivers/hwmon/pmbus')
| -rw-r--r-- | drivers/hwmon/pmbus/Kconfig | 40 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/Makefile | 2 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/adm1275.c | 314 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/lm25066.c | 518 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/ltc2978.c | 507 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/max16064.c | 19 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/max34440.c | 221 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/max8688.c | 25 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/pmbus.c | 48 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/pmbus.h | 82 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/pmbus_core.c | 1067 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/ucd9000.c | 62 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/ucd9200.c | 50 | ||||
| -rw-r--r-- | drivers/hwmon/pmbus/zl6100.c | 419 |
14 files changed, 2429 insertions, 945 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index c9237b9dcff..39cc63edfbb 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -4,7 +4,7 @@ menuconfig PMBUS tristate "PMBus support" - depends on I2C && EXPERIMENTAL + depends on I2C default n help Say yes here if you want to enable PMBus support. @@ -19,18 +19,20 @@ config SENSORS_PMBUS default y help If you say yes here you get hardware monitoring support for generic - PMBus devices, including but not limited to ADP4000, BMR450, BMR451, - BMR453, BMR454, LTC2978, NCP4200, and NCP4208. + PMBus devices, including but not limited to ADP4000, BMR453, BMR454, + MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, UDT020, TPS40400, + and TPS40422. This driver can also be built as a module. If so, the module will be called pmbus. config SENSORS_ADM1275 - tristate "Analog Devices ADM1275" + tristate "Analog Devices ADM1275 and compatibles" default n help If you say yes here you get hardware monitoring support for Analog - Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. + Devices ADM1075, ADM1275, and ADM1276 Hot-Swap Controller and Digital + Power Monitors. This driver can also be built as a module. If so, the module will be called adm1275. @@ -40,11 +42,21 @@ config SENSORS_LM25066 default n help If you say yes here you get hardware monitoring support for National - Semiconductor LM25066, LM5064, and LM5066. + Semiconductor LM25056, LM25066, LM5064, and LM5066. This driver can also be built as a module. If so, the module will be called lm25066. +config SENSORS_LTC2978 + tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883" + default n + help + If you say yes here you get hardware monitoring support for Linear + Technology LTC2974, LTC2978, LTC3880, and LTC3883. + + This driver can also be built as a module. If so, the module will + be called ltc2978. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n @@ -56,11 +68,11 @@ config SENSORS_MAX16064 be called max16064. config SENSORS_MAX34440 - tristate "Maxim MAX34440/MAX34441" + tristate "Maxim MAX34440 and compatibles" default n help If you say yes here you get hardware monitoring support for Maxim - MAX34440 and MAX34441. + MAX34440, MAX34441, MAX34446, MAX34460, and MAX34461. This driver can also be built as a module. If so, the module will be called max34440. @@ -97,4 +109,16 @@ config SENSORS_UCD9200 This driver can also be built as a module. If so, the module will be called ucd9200. +config SENSORS_ZL6100 + tristate "Intersil ZL6100 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for Intersil + ZL2004, ZL2005, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, ZL6105, + ZL9101M, and ZL9117M Digital DC/DC Controllers, as well as for + Ericsson BMR450, BMR451, BMR462, BMR463, and BMR464. + + This driver can also be built as a module. If so, the module will + be called zl6100. + endif # PMBUS diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 623eedb1ed9..789376c85db 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -6,8 +6,10 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o +obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index c936e278230..60aad9570f0 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -23,6 +23,8 @@ #include <linux/i2c.h> #include "pmbus.h" +enum chips { adm1075, adm1275, adm1276 }; + #define ADM1275_PEAK_IOUT 0xd0 #define ADM1275_PEAK_VIN 0xd1 #define ADM1275_PEAK_VOUT 0xd2 @@ -30,15 +32,82 @@ #define ADM1275_VIN_VOUT_SELECT (1 << 6) #define ADM1275_VRANGE (1 << 5) +#define ADM1075_IRANGE_50 (1 << 4) +#define ADM1075_IRANGE_25 (1 << 3) +#define ADM1075_IRANGE_MASK ((1 << 3) | (1 << 4)) + +#define ADM1275_IOUT_WARN2_LIMIT 0xd7 +#define ADM1275_DEVICE_CONFIG 0xd8 + +#define ADM1275_IOUT_WARN2_SELECT (1 << 4) + +#define ADM1276_PEAK_PIN 0xda + +#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0) + +#define ADM1075_READ_VAUX 0xdd +#define ADM1075_VAUX_OV_WARN_LIMIT 0xde +#define ADM1075_VAUX_UV_WARN_LIMIT 0xdf +#define ADM1075_VAUX_STATUS 0xf6 + +#define ADM1075_VAUX_OV_WARN (1<<7) +#define ADM1075_VAUX_UV_WARN (1<<6) + +struct adm1275_data { + int id; + bool have_oc_fault; + struct pmbus_driver_info info; +}; + +#define to_adm1275_data(x) container_of(x, struct adm1275_data, info) static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) { - int ret; + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); + int ret = 0; if (page) - return -EINVAL; + return -ENXIO; switch (reg) { + case PMBUS_IOUT_UC_FAULT_LIMIT: + if (data->have_oc_fault) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + if (!data->have_oc_fault) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + break; + case PMBUS_VOUT_OV_WARN_LIMIT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = pmbus_read_word_data(client, 0, + ADM1075_VAUX_OV_WARN_LIMIT); + break; + case PMBUS_VOUT_UV_WARN_LIMIT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = pmbus_read_word_data(client, 0, + ADM1075_VAUX_UV_WARN_LIMIT); + break; + case PMBUS_READ_VOUT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1075_READ_VAUX); + break; case PMBUS_VIRT_READ_IOUT_MAX: ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); break; @@ -48,10 +117,20 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) case PMBUS_VIRT_READ_VIN_MAX: ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); break; + case PMBUS_VIRT_READ_PIN_MAX: + if (data->id == adm1275) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); + break; case PMBUS_VIRT_RESET_IOUT_HISTORY: case PMBUS_VIRT_RESET_VOUT_HISTORY: case PMBUS_VIRT_RESET_VIN_HISTORY: - ret = 0; + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + if (data->id == adm1275) + ret = -ENXIO; break; default: ret = -ENODATA; @@ -66,9 +145,14 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, int ret; if (page) - return -EINVAL; + return -ENXIO; switch (reg) { + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT, + word); + break; case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); break; @@ -78,6 +162,9 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIRT_RESET_VIN_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0); + break; default: ret = -ENODATA; break; @@ -85,26 +172,119 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, return ret; } +static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); + int mfr_status, ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_STATUS_IOUT: + ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT); + if (ret < 0) + break; + mfr_status = pmbus_read_byte_data(client, page, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfr_status < 0) { + ret = mfr_status; + break; + } + if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) { + ret |= data->have_oc_fault ? + PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT; + } + break; + case PMBUS_STATUS_VOUT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = 0; + mfr_status = pmbus_read_byte_data(client, 0, + ADM1075_VAUX_STATUS); + if (mfr_status & ADM1075_VAUX_OV_WARN) + ret |= PB_VOLTAGE_OV_WARNING; + if (mfr_status & ADM1075_VAUX_UV_WARN) + ret |= PB_VOLTAGE_UV_WARNING; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id adm1275_id[] = { + { "adm1075", adm1075 }, + { "adm1275", adm1275 }, + { "adm1276", adm1276 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adm1275_id); + static int adm1275_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int config; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + int config, device_config; int ret; struct pmbus_driver_info *info; + struct adm1275_data *data; + const struct i2c_device_id *mid; if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE_DATA)) + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) return -ENODEV; - info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL); - if (!info) - return -ENOMEM; + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + if (ret != 3 || strncmp(block_buffer, "ADI", 3)) { + dev_err(&client->dev, "Unsupported Manufacturer ID\n"); + return -ENODEV; + } - config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); - if (config < 0) { - ret = config; - goto err_mem; + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; + } + for (mid = adm1275_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) + break; } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + if (id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); + if (config < 0) + return config; + + device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG); + if (device_config < 0) + return device_config; + + data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = mid->driver_data; + + info = &data->info; info->pages = 1; info->format[PSC_VOLTAGE_IN] = direct; @@ -116,9 +296,17 @@ static int adm1275_probe(struct i2c_client *client, info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; info->read_word_data = adm1275_read_word_data; + info->read_byte_data = adm1275_read_byte_data; info->write_word_data = adm1275_write_word_data; - if (config & ADM1275_VRANGE) { + if (data->id == adm1075) { + info->m[PSC_VOLTAGE_IN] = 27169; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -1; + info->m[PSC_VOLTAGE_OUT] = 27169; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -1; + } else if (config & ADM1275_VRANGE) { info->m[PSC_VOLTAGE_IN] = 19199; info->b[PSC_VOLTAGE_IN] = 0; info->R[PSC_VOLTAGE_IN] = -2; @@ -134,58 +322,76 @@ static int adm1275_probe(struct i2c_client *client, info->R[PSC_VOLTAGE_OUT] = -1; } - if (config & ADM1275_VIN_VOUT_SELECT) - info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; - else - info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; - - ret = pmbus_do_probe(client, id, info); - if (ret) - goto err_mem; - return 0; + if (device_config & ADM1275_IOUT_WARN2_SELECT) + data->have_oc_fault = true; -err_mem: - kfree(info); - return ret; -} - -static int adm1275_remove(struct i2c_client *client) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - int ret; + switch (data->id) { + case adm1075: + info->format[PSC_POWER] = direct; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -1; + switch (config & ADM1075_IRANGE_MASK) { + case ADM1075_IRANGE_25: + info->m[PSC_POWER] = 8549; + info->m[PSC_CURRENT_OUT] = 806; + break; + case ADM1075_IRANGE_50: + info->m[PSC_POWER] = 4279; + info->m[PSC_CURRENT_OUT] = 404; + break; + default: + dev_err(&client->dev, "Invalid input current range"); + info->m[PSC_POWER] = 0; + info->m[PSC_CURRENT_OUT] = 0; + break; + } + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_STATUS_INPUT; + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + break; + case adm1275: + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + else + info->func[0] |= + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; + break; + case adm1276: + info->format[PSC_POWER] = direct; + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_STATUS_INPUT; + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + if (config & ADM1275_VRANGE) { + info->m[PSC_POWER] = 6043; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } else { + info->m[PSC_POWER] = 2115; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -1; + } + break; + } - ret = pmbus_do_remove(client); - kfree(info); - return ret; + return pmbus_do_probe(client, id, info); } -static const struct i2c_device_id adm1275_id[] = { - {"adm1275", 0}, - { } -}; -MODULE_DEVICE_TABLE(i2c, adm1275_id); - static struct i2c_driver adm1275_driver = { .driver = { .name = "adm1275", }, .probe = adm1275_probe, - .remove = adm1275_remove, + .remove = pmbus_do_remove, .id_table = adm1275_id, }; -static int __init adm1275_init(void) -{ - return i2c_add_driver(&adm1275_driver); -} - -static void __exit adm1275_exit(void) -{ - i2c_del_driver(&adm1275_driver); -} +module_i2c_driver(adm1275_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); MODULE_LICENSE("GPL"); -module_init(adm1275_init); -module_exit(adm1275_exit); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index d4bc114572d..a26b1d1d951 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -1,7 +1,8 @@ /* - * Hardware monitoring driver for LM25066 / LM5064 / LM5066 + * Hardware monitoring driver for LM25056 / LM25063 / LM25066 / LM5064 / LM5066 * * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2013 Guenter Roeck * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +27,7 @@ #include <linux/i2c.h> #include "pmbus.h" -enum chips { lm25066, lm5064, lm5066 }; +enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 }; #define LM25066_READ_VAUX 0xd0 #define LM25066_MFR_READ_IIN 0xd1 @@ -43,8 +44,176 @@ enum chips { lm25066, lm5064, lm5066 }; #define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ +/* LM25056 only */ + +#define LM25056_VAUX_OV_WARN_LIMIT 0xe3 +#define LM25056_VAUX_UV_WARN_LIMIT 0xe4 + +#define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1) +#define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0) + +/* LM25063 only */ + +#define LM25063_READ_VOUT_MAX 0xe5 +#define LM25063_READ_VOUT_MIN 0xe6 + +struct __coeff { + short m, b, R; +}; + +#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES) +#define PSC_POWER_L (PSC_NUM_CLASSES + 1) + +static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = { + [lm25056] = { + [PSC_VOLTAGE_IN] = { + .m = 16296, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 13797, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 6726, + .R = -2, + }, + [PSC_POWER] = { + .m = 5501, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 26882, + .R = -4, + }, + [PSC_TEMPERATURE] = { + .m = 1580, + .b = -14500, + .R = -2, + }, + }, + [lm25066] = { + [PSC_VOLTAGE_IN] = { + .m = 22070, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 22070, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 13661, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 6852, + .R = -2, + }, + [PSC_POWER] = { + .m = 736, + .R = -2, + }, + [PSC_POWER_L] = { + .m = 369, + .R = -2, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, + [lm25063] = { + [PSC_VOLTAGE_IN] = { + .m = 16000, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 16000, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10000, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 10000, + .R = -2, + }, + [PSC_POWER] = { + .m = 5000, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 5000, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 15596, + .R = -3, + }, + }, + [lm5064] = { + [PSC_VOLTAGE_IN] = { + .m = 4611, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4621, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10742, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 5456, + .R = -2, + }, + [PSC_POWER] = { + .m = 1204, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 612, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, + [lm5066] = { + [PSC_VOLTAGE_IN] = { + .m = 4587, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4587, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10753, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 5405, + .R = -2, + }, + [PSC_POWER] = { + .m = 1204, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 605, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, +}; + struct lm25066_data { int id; + u16 rlimit; /* Maximum register value */ struct pmbus_driver_info info; }; @@ -56,42 +225,35 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) const struct lm25066_data *data = to_lm25066_data(info); int ret; - if (page > 1) - return -EINVAL; - - /* Map READ_VAUX into READ_VOUT register on page 1 */ - if (page == 1) { - switch (reg) { - case PMBUS_READ_VOUT: - ret = pmbus_read_word_data(client, 0, - LM25066_READ_VAUX); - if (ret < 0) - break; - /* Adjust returned value to match VOUT coefficients */ - switch (data->id) { - case lm25066: - /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */ - ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); - break; - case lm5064: - /* VOUT: 4.53 mV VAUX: 700 uV LSB */ - ret = DIV_ROUND_CLOSEST(ret * 70, 453); - break; - case lm5066: - /* VOUT: 2.18 mV VAUX: 725 uV LSB */ - ret = DIV_ROUND_CLOSEST(ret * 725, 2180); - break; - } + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + switch (data->id) { + case lm25056: + /* VIN: 6.14 mV VAUX: 293 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + case lm25063: + /* VIN: 6.25 mV VAUX: 200.0 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 20, 625); break; - default: - /* No other valid registers on page 1 */ - ret = -EINVAL; + case lm25066: + /* VIN: 4.54 mV VAUX: 283.2 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); + break; + case lm5064: + /* VIN: 4.53 mV VAUX: 700 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 70, 453); + break; + case lm5066: + /* VIN: 2.18 mV VAUX: 725 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 725, 2180); break; } - goto done; - } - - switch (reg) { + break; case PMBUS_READ_IIN: ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); break; @@ -128,28 +290,130 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) ret = -ENODATA; break; } -done: + return ret; +} + +static int lm25063_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MAX); + break; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MIN); + break; + default: + ret = lm25066_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25056_VAUX_UV_WARN_LIMIT); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25056_VAUX_OV_WARN_LIMIT); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + default: + ret = lm25066_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret, s; + + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) + break; + s = 0; + if (ret & LM25056_MFR_STS_VAUX_UV_WARN) + s |= PB_VOLTAGE_UV_WARNING; + if (ret & LM25056_MFR_STS_VAUX_OV_WARN) + s |= PB_VOLTAGE_OV_WARNING; + ret = s; + break; + default: + ret = -ENODATA; + break; + } return ret; } static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); int ret; - if (page > 1) - return -EINVAL; - switch (reg) { + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, reg, word); + pmbus_clear_cache(client); + break; case PMBUS_IIN_OC_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_IIN_OC_WARN_LIMIT, word); + pmbus_clear_cache(client); break; case PMBUS_PIN_OP_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_PIN_OP_WARN_LIMIT, word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + /* Adjust from VIN coefficients (for LM25056) */ + word = DIV_ROUND_CLOSEST((int)word * 6140, 293); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, + LM25056_VAUX_UV_WARN_LIMIT, word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + /* Adjust from VIN coefficients (for LM25056) */ + word = DIV_ROUND_CLOSEST((int)word * 6140, 293); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, + LM25056_VAUX_OV_WARN_LIMIT, word); + pmbus_clear_cache(client); break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); @@ -165,146 +429,82 @@ static int lm25066_probe(struct i2c_client *client, const struct i2c_device_id *id) { int config; - int ret; struct lm25066_data *data; struct pmbus_driver_info *info; + struct __coeff *coeff; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) return -ENODEV; - data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(struct lm25066_data), + GFP_KERNEL); if (!data) return -ENOMEM; config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); - if (config < 0) { - ret = config; - goto err_mem; - } + if (config < 0) + return config; data->id = id->driver_data; info = &data->info; - info->pages = 2; + info->pages = 1; info->format[PSC_VOLTAGE_IN] = direct; info->format[PSC_VOLTAGE_OUT] = direct; info->format[PSC_CURRENT_IN] = direct; info->format[PSC_TEMPERATURE] = direct; info->format[PSC_POWER] = direct; - info->m[PSC_TEMPERATURE] = 16; - info->b[PSC_TEMPERATURE] = 0; - info->R[PSC_TEMPERATURE] = 0; - - info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT - | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN - | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - info->func[1] = PMBUS_HAVE_VOUT; - - info->read_word_data = lm25066_read_word_data; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON + | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + + if (data->id == lm25056) { + info->func[0] |= PMBUS_HAVE_STATUS_VMON; + info->read_word_data = lm25056_read_word_data; + info->read_byte_data = lm25056_read_byte_data; + data->rlimit = 0x0fff; + } else if (data->id == lm25063) { + info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_POUT; + info->read_word_data = lm25063_read_word_data; + data->rlimit = 0xffff; + } else { + info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + info->read_word_data = lm25066_read_word_data; + data->rlimit = 0x0fff; + } info->write_word_data = lm25066_write_word_data; - switch (id->driver_data) { - case lm25066: - info->m[PSC_VOLTAGE_IN] = 22070; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 22070; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - - if (config & LM25066_DEV_SETUP_CL) { - info->m[PSC_CURRENT_IN] = 6852; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 369; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -2; - } else { - info->m[PSC_CURRENT_IN] = 13661; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 736; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -2; - } - break; - case lm5064: - info->m[PSC_VOLTAGE_IN] = 22075; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 22075; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - - if (config & LM25066_DEV_SETUP_CL) { - info->m[PSC_CURRENT_IN] = 6713; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 3619; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } else { - info->m[PSC_CURRENT_IN] = 13426; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 7238; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } - break; - case lm5066: - info->m[PSC_VOLTAGE_IN] = 4587; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 4587; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - - if (config & LM25066_DEV_SETUP_CL) { - info->m[PSC_CURRENT_IN] = 10753; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 1204; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } else { - info->m[PSC_CURRENT_IN] = 5405; - info->b[PSC_CURRENT_IN] = 0; - info->R[PSC_CURRENT_IN] = -2; - info->m[PSC_POWER] = 605; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -3; - } - break; - default: - ret = -ENODEV; - goto err_mem; + coeff = &lm25066_coeff[data->id][0]; + info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m; + info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b; + info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R; + info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m; + info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b; + info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R; + info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; + info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; + info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; + info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; + info->b[PSC_POWER] = coeff[PSC_POWER].b; + info->R[PSC_POWER] = coeff[PSC_POWER].R; + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; + info->m[PSC_POWER] = coeff[PSC_POWER_L].m; + } else { + info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; + info->m[PSC_POWER] = coeff[PSC_POWER].m; } - ret = pmbus_do_probe(client, id, info); - if (ret) - goto err_mem; - return 0; - -err_mem: - kfree(data); - return ret; -} - -static int lm25066_remove(struct i2c_client *client) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - const struct lm25066_data *data = to_lm25066_data(info); - int ret; - - ret = pmbus_do_remove(client); - kfree(data); - return ret; + return pmbus_do_probe(client, id, info); } static const struct i2c_device_id lm25066_id[] = { + {"lm25056", lm25056}, + {"lm25063", lm25063}, {"lm25066", lm25066}, {"lm5064", lm5064}, {"lm5066", lm5066}, @@ -319,22 +519,12 @@ static struct i2c_driver lm25066_driver = { .name = "lm25066", }, .probe = lm25066_probe, - .remove = lm25066_remove, + .remove = pmbus_do_remove, .id_table = lm25066_id, }; -static int __init lm25066_init(void) -{ - return i2c_add_driver(&lm25066_driver); -} - -static void __exit lm25066_exit(void) -{ - i2c_del_driver(&lm25066_driver); -} +module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); +MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); MODULE_LICENSE("GPL"); -module_init(lm25066_init); -module_exit(lm25066_exit); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c new file mode 100644 index 00000000000..e24ed521051 --- /dev/null +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -0,0 +1,507 @@ +/* + * Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880, + * LTC3883, and LTM4676 + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2013, 2014 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "pmbus.h" + +enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; + +/* Common for all chips */ +#define LTC2978_MFR_VOUT_PEAK 0xdd +#define LTC2978_MFR_VIN_PEAK 0xde +#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf +#define LTC2978_MFR_SPECIAL_ID 0xe7 + +/* LTC2974, LCT2977, and LTC2978 */ +#define LTC2978_MFR_VOUT_MIN 0xfb +#define LTC2978_MFR_VIN_MIN 0xfc +#define LTC2978_MFR_TEMPERATURE_MIN 0xfd + +/* LTC2974 only */ +#define LTC2974_MFR_IOUT_PEAK 0xd7 +#define LTC2974_MFR_IOUT_MIN 0xd8 + +/* LTC3880, LTC3883, and LTM4676 */ +#define LTC3880_MFR_IOUT_PEAK 0xd7 +#define LTC3880_MFR_CLEAR_PEAKS 0xe3 +#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 + +/* LTC3883 only */ +#define LTC3883_MFR_IIN_PEAK 0xe1 + +#define LTC2974_ID_REV1 0x0212 +#define LTC2974_ID_REV2 0x0213 +#define LTC2977_ID 0x0130 +#define LTC2978_ID_REV1 0x0121 +#define LTC2978_ID_REV2 0x0122 +#define LTC2978A_ID 0x0124 +#define LTC3880_ID 0x4000 +#define LTC3880_ID_MASK 0xff00 +#define LTC3883_ID 0x4300 +#define LTC3883_ID_MASK 0xff00 +#define LTM4676_ID 0x4480 /* datasheet claims 0x440X */ +#define LTM4676_ID_MASK 0xfff0 + +#define LTC2974_NUM_PAGES 4 +#define LTC2978_NUM_PAGES 8 +#define LTC3880_NUM_PAGES 2 +#define LTC3883_NUM_PAGES 1 + +/* + * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which + * happens pretty much each time chip data is updated. Raw peak data therefore + * does not provide much value. To be able to provide useful peak data, keep an + * internal cache of measured peak data, which is only cleared if an explicit + * "clear peak" command is executed for the sensor in question. + */ + +struct ltc2978_data { + enum chips id; + u16 vin_min, vin_max; + u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES]; + u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES]; + u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES]; + u16 iin_max; + u16 temp2_max; + struct pmbus_driver_info info; +}; + +#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info) + +static inline int lin11_to_val(int data) +{ + s16 e = ((s16)data) >> 11; + s32 m = (((s16)(data << 5)) >> 5); + + /* + * mantissa is 10 bit + sign, exponent adds up to 15 bit. + * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31). + */ + e += 6; + return (e < 0 ? m >> -e : m << e); +} + +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); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) > lin11_to_val(data->vin_max)) + data->vin_max = ret; + ret = data->vin_max; + } + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); + if (ret >= 0) { + /* + * VOUT is 16 bit unsigned with fixed exponent, + * so we can compare it directly + */ + if (ret > data->vout_max[page]) + data->vout_max[page] = ret; + ret = data->vout_max[page]; + } + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + LTC2978_MFR_TEMPERATURE_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->temp_max[page])) + data->temp_max[page] = ret; + ret = data->temp_max[page]; + } + 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) { + if (lin11_to_val(ret) < lin11_to_val(data->vin_min)) + data->vin_min = ret; + ret = data->vin_min; + } + break; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); + if (ret >= 0) { + /* + * VOUT_MIN is known to not be supported on some lots + * of LTC2978 revision 1, and will return the maximum + * possible voltage if read. If VOUT_MAX is valid and + * lower than the reading of VOUT_MIN, use it instead. + */ + if (data->vout_max[page] && ret > data->vout_max[page]) + ret = data->vout_max[page]; + if (ret < data->vout_min[page]) + data->vout_min[page] = ret; + ret = data->vout_min[page]; + } + break; + case PMBUS_VIRT_READ_TEMP_MIN: + ret = pmbus_read_word_data(client, page, + LTC2978_MFR_TEMPERATURE_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) + < lin11_to_val(data->temp_min[page])) + data->temp_min[page] = ret; + ret = data->temp_min[page]; + } + break; + 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 ltc2974_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, LTC2974_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_IOUT_MIN: + ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) + < lin11_to_val(data->iout_min[page])) + data->iout_min[page] = ret; + ret = data->iout_min[page]; + } + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = 0; + break; + default: + ret = ltc2978_read_word_data(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)) + data->temp2_max = ret; + ret = data->temp2_max; + } + 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 = ltc2978_read_word_data_common(client, page, reg); + break; + } + return ret; +} + +static int ltc3883_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_IIN_MAX: + ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iin_max)) + data->iin_max = ret; + ret = data->iin_max; + } + break; + case PMBUS_VIRT_RESET_IIN_HISTORY: + ret = 0; + break; + default: + ret = ltc3880_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int ltc2978_clear_peaks(struct i2c_client *client, int page, + enum chips id) +{ + int ret; + + if (id == ltc3880 || id == ltc3883) + ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); + else + ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + + return ret; +} + +static int ltc2978_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + 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_RESET_IIN_HISTORY: + data->iin_max = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + data->iout_max[page] = 0x7c00; + data->iout_min[page] = 0xfbff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + data->temp2_max = 0x7c00; + 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 = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + data->vin_min = 0x7bff; + data->vin_max = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + data->temp_min[page] = 0x7bff; + data->temp_max[page] = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id ltc2978_id[] = { + {"ltc2974", ltc2974}, + {"ltc2977", ltc2977}, + {"ltc2978", ltc2978}, + {"ltc3880", ltc3880}, + {"ltc3883", ltc3883}, + {"ltm4676", ltm4676}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2978_id); + +static int ltc2978_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int chip_id, i; + struct ltc2978_data *data; + struct pmbus_driver_info *info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct ltc2978_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); + if (chip_id < 0) + return chip_id; + + if (chip_id == LTC2974_ID_REV1 || chip_id == LTC2974_ID_REV2) { + data->id = ltc2974; + } else if (chip_id == LTC2977_ID) { + data->id = ltc2977; + } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2 || + chip_id == LTC2978A_ID) { + data->id = ltc2978; + } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { + data->id = ltc3880; + } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) { + data->id = ltc3883; + } else if ((chip_id & LTM4676_ID_MASK) == LTM4676_ID) { + data->id = ltm4676; + } else { + dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); + return -ENODEV; + } + if (data->id != id->driver_data) + dev_warn(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, + ltc2978_id[data->id].name); + + info = &data->info; + info->write_word_data = ltc2978_write_word_data; + + data->vin_min = 0x7bff; + data->vin_max = 0x7c00; + for (i = 0; i < ARRAY_SIZE(data->vout_min); i++) + data->vout_min[i] = 0xffff; + for (i = 0; i < ARRAY_SIZE(data->iout_min); i++) + data->iout_min[i] = 0xfbff; + for (i = 0; i < ARRAY_SIZE(data->iout_max); i++) + data->iout_max[i] = 0x7c00; + for (i = 0; i < ARRAY_SIZE(data->temp_min); i++) + data->temp_min[i] = 0x7bff; + for (i = 0; i < ARRAY_SIZE(data->temp_max); i++) + data->temp_max[i] = 0x7c00; + data->temp2_max = 0x7c00; + + switch (data->id) { + case ltc2974: + info->read_word_data = ltc2974_read_word_data; + info->pages = LTC2974_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + } + break; + case ltc2977: + case ltc2978: + info->read_word_data = ltc2978_read_word_data; + info->pages = LTC2978_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + for (i = 1; i < LTC2978_NUM_PAGES; i++) { + info->func[i] = PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT; + } + break; + case ltc3880: + case ltm4676: + info->read_word_data = ltc3880_read_word_data; + info->pages = LTC3880_NUM_PAGES; + 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; + break; + case ltc3883: + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + 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_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + break; + default: + return -ENODEV; + } + return pmbus_do_probe(client, id, info); +} + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc2978_driver = { + .driver = { + .name = "ltc2978", + }, + .probe = ltc2978_probe, + .remove = pmbus_do_remove, + .id_table = ltc2978_id, +}; + +module_i2c_driver(ltc2978_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, LTC3883, and LTM4676"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index e50b296e8db..fa237a3c329 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -103,11 +103,6 @@ static int max16064_probe(struct i2c_client *client, return pmbus_do_probe(client, id, &max16064_info); } -static int max16064_remove(struct i2c_client *client) -{ - return pmbus_do_remove(client); -} - static const struct i2c_device_id max16064_id[] = { {"max16064", 0}, {} @@ -121,22 +116,12 @@ static struct i2c_driver max16064_driver = { .name = "max16064", }, .probe = max16064_probe, - .remove = max16064_remove, + .remove = pmbus_do_remove, .id_table = max16064_id, }; -static int __init max16064_init(void) -{ - return i2c_add_driver(&max16064_driver); -} - -static void __exit max16064_exit(void) -{ - i2c_del_driver(&max16064_driver); -} +module_i2c_driver(max16064_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064"); MODULE_LICENSE("GPL"); -module_init(max16064_init); -module_exit(max16064_exit); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index fda621d2e45..7e930c3ce1a 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -2,6 +2,7 @@ * Hardware monitoring driver for Maxim MAX34440/MAX34441 * * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,34 +26,83 @@ #include <linux/i2c.h> #include "pmbus.h" -enum chips { max34440, max34441 }; +enum chips { max34440, max34441, max34446, max34460, max34461 }; #define MAX34440_MFR_VOUT_PEAK 0xd4 #define MAX34440_MFR_IOUT_PEAK 0xd5 #define MAX34440_MFR_TEMPERATURE_PEAK 0xd6 +#define MAX34440_MFR_VOUT_MIN 0xd7 + +#define MAX34446_MFR_POUT_PEAK 0xe0 +#define MAX34446_MFR_POUT_AVG 0xe1 +#define MAX34446_MFR_IOUT_AVG 0xe2 +#define MAX34446_MFR_TEMPERATURE_AVG 0xe3 #define MAX34440_STATUS_OC_WARN (1 << 0) #define MAX34440_STATUS_OC_FAULT (1 << 1) #define MAX34440_STATUS_OT_FAULT (1 << 5) #define MAX34440_STATUS_OT_WARN (1 << 6) +struct max34440_data { + int id; + struct pmbus_driver_info info; +}; + +#define to_max34440_data(x) container_of(x, struct max34440_data, info) + static int max34440_read_word_data(struct i2c_client *client, int page, int reg) { int ret; + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); switch (reg) { + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_VOUT_MIN); + break; case PMBUS_VIRT_READ_VOUT_MAX: ret = pmbus_read_word_data(client, page, MAX34440_MFR_VOUT_PEAK); break; + case PMBUS_VIRT_READ_IOUT_AVG: + if (data->id != max34446) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_IOUT_AVG); + break; case PMBUS_VIRT_READ_IOUT_MAX: ret = pmbus_read_word_data(client, page, MAX34440_MFR_IOUT_PEAK); break; + case PMBUS_VIRT_READ_POUT_AVG: + if (data->id != max34446) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_POUT_AVG); + break; + case PMBUS_VIRT_READ_POUT_MAX: + if (data->id != max34446) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_POUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_AVG: + if (data->id != max34446 && data->id != max34460 && + data->id != max34461) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_TEMPERATURE_AVG); + break; case PMBUS_VIRT_READ_TEMP_MAX: ret = pmbus_read_word_data(client, page, MAX34440_MFR_TEMPERATURE_PEAK); break; + case PMBUS_VIRT_RESET_POUT_HISTORY: + if (data->id != max34446) + return -ENXIO; + ret = 0; + break; case PMBUS_VIRT_RESET_VOUT_HISTORY: case PMBUS_VIRT_RESET_IOUT_HISTORY: case PMBUS_VIRT_RESET_TEMP_HISTORY: @@ -68,21 +118,42 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg) static int max34440_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); int ret; switch (reg) { + case PMBUS_VIRT_RESET_POUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_POUT_PEAK, 0); + if (ret) + break; + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_POUT_AVG, 0); + break; case PMBUS_VIRT_RESET_VOUT_HISTORY: ret = pmbus_write_word_data(client, page, + MAX34440_MFR_VOUT_MIN, 0x7fff); + if (ret) + break; + ret = pmbus_write_word_data(client, page, MAX34440_MFR_VOUT_PEAK, 0); break; case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = pmbus_write_word_data(client, page, MAX34440_MFR_IOUT_PEAK, 0); + if (!ret && data->id == max34446) + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_IOUT_AVG, 0); + break; case PMBUS_VIRT_RESET_TEMP_HISTORY: ret = pmbus_write_word_data(client, page, MAX34440_MFR_TEMPERATURE_PEAK, - 0xffff); + 0x8000); + if (!ret && data->id == max34446) + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_TEMPERATURE_AVG, 0); break; default: ret = -ENODATA; @@ -93,12 +164,14 @@ static int max34440_write_word_data(struct i2c_client *client, int page, static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) { - int ret; + int ret = 0; int mfg_status; - ret = pmbus_set_page(client, page); - if (ret < 0) - return ret; + if (page >= 0) { + ret = pmbus_set_page(client, page); + if (ret < 0) + return ret; + } switch (reg) { case PMBUS_STATUS_IOUT: @@ -214,25 +287,135 @@ static struct pmbus_driver_info max34440_info[] = { .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, + [max34446] = { + .pages = 7, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 3, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 3, + .m[PSC_POWER] = 1, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[4] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[5] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34460] = { + .pages = 18, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[14] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[15] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34461] = { + .pages = 23, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[12] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[13] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[14] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[15] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + /* page 16 is reserved */ + .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, }; static int max34440_probe(struct i2c_client *client, const struct i2c_device_id *id) { - return pmbus_do_probe(client, id, &max34440_info[id->driver_data]); -} + struct max34440_data *data; -static int max34440_remove(struct i2c_client *client) -{ - return pmbus_do_remove(client); + data = devm_kzalloc(&client->dev, sizeof(struct max34440_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + data->id = id->driver_data; + data->info = max34440_info[id->driver_data]; + + return pmbus_do_probe(client, id, &data->info); } static const struct i2c_device_id max34440_id[] = { {"max34440", max34440}, {"max34441", max34441}, + {"max34446", max34446}, + {"max34460", max34460}, + {"max34461", max34461}, {} }; - MODULE_DEVICE_TABLE(i2c, max34440_id); /* This is the driver that will be inserted */ @@ -241,22 +424,12 @@ static struct i2c_driver max34440_driver = { .name = "max34440", }, .probe = max34440_probe, - .remove = max34440_remove, + .remove = pmbus_do_remove, .id_table = max34440_id, }; -static int __init max34440_init(void) -{ - return i2c_add_driver(&max34440_driver); -} - -static void __exit max34440_exit(void) -{ - i2c_del_driver(&max34440_driver); -} +module_i2c_driver(max34440_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); MODULE_LICENSE("GPL"); -module_init(max34440_init); -module_exit(max34440_exit); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index c3e72f1a3cf..f04454a42fd 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -45,7 +45,7 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg) int ret; if (page) - return -EINVAL; + return -ENXIO; switch (reg) { case PMBUS_VIRT_READ_VOUT_MAX: @@ -101,8 +101,8 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) int ret = 0; int mfg_status; - if (page) - return -EINVAL; + if (page > 0) + return -ENXIO; switch (reg) { case PMBUS_STATUS_VOUT: @@ -180,11 +180,6 @@ static int max8688_probe(struct i2c_client *client, return pmbus_do_probe(client, id, &max8688_info); } -static int max8688_remove(struct i2c_client *client) -{ - return pmbus_do_remove(client); -} - static const struct i2c_device_id max8688_id[] = { {"max8688", 0}, { } @@ -198,22 +193,12 @@ static struct i2c_driver max8688_driver = { .name = "max8688", }, .probe = max8688_probe, - .remove = max8688_remove, + .remove = pmbus_do_remove, .id_table = max8688_id, }; -static int __init max8688_init(void) -{ - return i2c_add_driver(&max8688_driver); -} - -static void __exit max8688_exit(void) -{ - i2c_del_driver(&max8688_driver); -} +module_i2c_driver(max8688_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); MODULE_LICENSE("GPL"); -module_init(max8688_init); -module_exit(max8688_exit); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 73de9f1f319..7e91700131a 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -166,34 +166,16 @@ static int pmbus_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pmbus_driver_info *info; - int ret; - info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL); + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); if (!info) return -ENOMEM; info->pages = id->driver_data; info->identify = pmbus_identify; - ret = pmbus_do_probe(client, id, info); - if (ret < 0) - goto out; - return 0; - -out: - kfree(info); - return ret; -} - -static int pmbus_remove(struct i2c_client *client) -{ - int ret; - const struct pmbus_driver_info *info; - - info = pmbus_get_driver_info(client); - ret = pmbus_do_remove(client); - kfree(info); - return ret; + return pmbus_do_probe(client, id, info); } /* @@ -201,14 +183,18 @@ static int pmbus_remove(struct i2c_client *client) */ static const struct i2c_device_id pmbus_id[] = { {"adp4000", 1}, - {"bmr450", 1}, - {"bmr451", 1}, {"bmr453", 1}, {"bmr454", 1}, - {"ltc2978", 8}, + {"mdt040", 1}, {"ncp4200", 1}, {"ncp4208", 1}, + {"pdt003", 1}, + {"pdt006", 1}, + {"pdt012", 1}, {"pmbus", 0}, + {"tps40400", 1}, + {"tps40422", 2}, + {"udt020", 1}, {} }; @@ -220,22 +206,12 @@ static struct i2c_driver pmbus_driver = { .name = "pmbus", }, .probe = pmbus_probe, - .remove = pmbus_remove, + .remove = pmbus_do_remove, .id_table = pmbus_id, }; -static int __init pmbus_init(void) -{ - return i2c_add_driver(&pmbus_driver); -} - -static void __exit pmbus_exit(void) -{ - i2c_del_driver(&pmbus_driver); -} +module_i2c_driver(pmbus_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("Generic PMBus driver"); MODULE_LICENSE("GPL"); -module_init(pmbus_init); -module_exit(pmbus_exit); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 0808d986d75..fa9beb3eb60 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -2,6 +2,7 @@ * pmbus.h - Common defines and structures for PMBus devices * * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -134,32 +135,55 @@ * Semantics: * Virtual registers are all word size. * READ registers are read-only; writes are either ignored or return an error. - * RESET registers are read/write. Reading returns zero (used for detection), - * writing any value causes the associated history to be reset. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. */ #define PMBUS_VIRT_BASE 0x100 -#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 0) -#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 1) -#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 2) -#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 3) -#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 4) -#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 5) -#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 6) -#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 7) -#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 8) -#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 9) -#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 10) -#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 11) -#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 12) -#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 13) -#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 14) -#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 15) -#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 16) -#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 17) -#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 18) -#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 19) -#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 20) -#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22) +#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23) +#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24) +#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25) +#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26) +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27) +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28) +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29) + +#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30) +#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31) +#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32) +#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33) +#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) +#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) /* * CAPABILITY @@ -301,6 +325,8 @@ enum pmbus_sensor_classes { #define PMBUS_HAVE_STATUS_TEMP (1 << 15) #define PMBUS_HAVE_STATUS_FAN12 (1 << 16) #define PMBUS_HAVE_STATUS_FAN34 (1 << 17) +#define PMBUS_HAVE_VMON (1 << 18) +#define PMBUS_HAVE_STATUS_VMON (1 << 19) enum pmbus_data_format { linear = 0, direct, vid }; @@ -320,11 +346,18 @@ struct pmbus_driver_info { * The following functions map manufacturing specific register values * to PMBus standard register values. Specify only if mapping is * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. */ int (*read_byte_data)(struct i2c_client *client, int page, int reg); int (*read_word_data)(struct i2c_client *client, int page, int reg); int (*write_word_data)(struct i2c_client *client, int page, int reg, u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); /* * The identify function determines supported PMBus functionality. * This function is only necessary if a chip driver supports multiple @@ -336,6 +369,7 @@ struct pmbus_driver_info { /* Function declarations */ +void pmbus_clear_cache(struct i2c_client *client); int pmbus_set_page(struct i2c_client *client, u8 page); int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 5c1b6cf3170..291d11fe93e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2,6 +2,7 @@ * Hardware monitoring driver for PMBus devices * * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,46 +27,15 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#include <linux/delay.h> +#include <linux/jiffies.h> #include <linux/i2c/pmbus.h> #include "pmbus.h" /* - * Constants needed to determine number of sensors, booleans, and labels. + * Number of additional attribute pointers to allocate + * with each call to krealloc */ -#define PMBUS_MAX_INPUT_SENSORS 22 /* 10*volt, 7*curr, 5*power */ -#define PMBUS_VOUT_SENSORS_PER_PAGE 9 /* input, min, max, lcrit, - crit, lowest, highest, avg, - reset */ -#define PMBUS_IOUT_SENSORS_PER_PAGE 8 /* input, min, max, crit, - lowest, highest, avg, - reset */ -#define PMBUS_POUT_SENSORS_PER_PAGE 4 /* input, cap, max, crit */ -#define PMBUS_MAX_SENSORS_PER_FAN 1 /* input */ -#define PMBUS_MAX_SENSORS_PER_TEMP 8 /* input, min, max, lcrit, - crit, lowest, highest, - reset */ - -#define PMBUS_MAX_INPUT_BOOLEANS 7 /* v: min_alarm, max_alarm, - lcrit_alarm, crit_alarm; - c: alarm, crit_alarm; - p: crit_alarm */ -#define PMBUS_VOUT_BOOLEANS_PER_PAGE 4 /* min_alarm, max_alarm, - lcrit_alarm, crit_alarm */ -#define PMBUS_IOUT_BOOLEANS_PER_PAGE 3 /* alarm, lcrit_alarm, - crit_alarm */ -#define PMBUS_POUT_BOOLEANS_PER_PAGE 2 /* alarm, crit_alarm */ -#define PMBUS_MAX_BOOLEANS_PER_FAN 2 /* alarm, fault */ -#define PMBUS_MAX_BOOLEANS_PER_TEMP 4 /* min_alarm, max_alarm, - lcrit_alarm, crit_alarm */ - -#define PMBUS_MAX_INPUT_LABELS 4 /* vin, vcap, iin, pin */ - -/* - * status, status_vout, status_iout, status_fans, status_fan34, and status_temp - * are paged. status_input is unpaged. - */ -#define PB_NUM_STATUS_REG (PMBUS_PAGES * 6 + 1) +#define PMBUS_ATTR_ALLOC_SIZE 32 /* * Index into status register array, per status register group @@ -75,14 +45,18 @@ #define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) #define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) #define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) -#define PB_STATUS_INPUT_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) -#define PB_STATUS_TEMP_BASE (PB_STATUS_INPUT_BASE + 1) +#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) +#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) +#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) + +#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) #define PMBUS_NAME_SIZE 24 struct pmbus_sensor { + struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ - struct sensor_device_attribute attribute; + struct device_attribute attribute; u8 page; /* page number */ u16 reg; /* register */ enum pmbus_sensor_classes class; /* sensor class */ @@ -90,52 +64,43 @@ struct pmbus_sensor { int data; /* Sensor data. Negative if there was a read error */ }; +#define to_pmbus_sensor(_attr) \ + container_of(_attr, struct pmbus_sensor, attribute) struct pmbus_boolean { char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ struct sensor_device_attribute attribute; + struct pmbus_sensor *s1; + struct pmbus_sensor *s2; }; +#define to_pmbus_boolean(_attr) \ + container_of(_attr, struct pmbus_boolean, attribute) struct pmbus_label { char name[PMBUS_NAME_SIZE]; /* sysfs label name */ - struct sensor_device_attribute attribute; + struct device_attribute attribute; char label[PMBUS_NAME_SIZE]; /* label */ }; +#define to_pmbus_label(_attr) \ + container_of(_attr, struct pmbus_label, attribute) struct pmbus_data { + struct device *dev; struct device *hwmon_dev; u32 flags; /* from platform data */ - int exponent; /* linear mode: exponent for output voltages */ + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ const struct pmbus_driver_info *info; int max_attributes; int num_attributes; - struct attribute **attributes; struct attribute_group group; + const struct attribute_group *groups[2]; - /* - * Sensors cover both sensor and limit registers. - */ - int max_sensors; - int num_sensors; struct pmbus_sensor *sensors; - /* - * Booleans are used for alarms. - * Values are determined from status registers. - */ - int max_booleans; - int num_booleans; - struct pmbus_boolean *booleans; - /* - * Labels are used to map generic names (e.g., "in1") - * to PMBus specific names (e.g., "vin" or "vout1"). - */ - int max_labels; - int num_labels; - struct pmbus_label *labels; struct mutex update_lock; bool valid; @@ -146,10 +111,19 @@ struct pmbus_data { * so we keep them all together. */ u8 status[PB_NUM_STATUS_REG]; + u8 status_register; u8 currpage; }; +void pmbus_clear_cache(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + data->valid = false; +} +EXPORT_SYMBOL_GPL(pmbus_clear_cache); + int pmbus_set_page(struct i2c_client *client, u8 page) { struct pmbus_data *data = i2c_get_clientdata(client); @@ -160,7 +134,7 @@ int pmbus_set_page(struct i2c_client *client, u8 page) rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); if (newpage != page) - rv = -EINVAL; + rv = -EIO; else data->currpage = page; } @@ -182,6 +156,24 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) } EXPORT_SYMBOL_GPL(pmbus_write_byte); +/* + * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte) { + status = info->write_byte(client, page, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte(client, page, value); +} + int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word) { int rv; @@ -211,7 +203,7 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, return status; } if (reg >= PMBUS_VIRT_BASE) - return -EINVAL; + return -ENXIO; return pmbus_write_word_data(client, page, reg, word); } @@ -243,7 +235,7 @@ static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) return status; } if (reg >= PMBUS_VIRT_BASE) - return -EINVAL; + return -ENXIO; return pmbus_read_word_data(client, page, reg); } @@ -281,7 +273,7 @@ static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) static void pmbus_clear_fault_page(struct i2c_client *client, int page) { - pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); } void pmbus_clear_faults(struct i2c_client *client) @@ -296,40 +288,42 @@ EXPORT_SYMBOL_GPL(pmbus_clear_faults); static int pmbus_check_status_cml(struct i2c_client *client) { + struct pmbus_data *data = i2c_get_clientdata(client); int status, status2; - status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE); + status = _pmbus_read_byte_data(client, -1, data->status_register); if (status < 0 || (status & PB_STATUS_CML)) { - status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) - return -EINVAL; + return -EIO; } return 0; } -bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) +static bool pmbus_check_register(struct i2c_client *client, + int (*func)(struct i2c_client *client, + int page, int reg), + int page, int reg) { int rv; struct pmbus_data *data = i2c_get_clientdata(client); - rv = _pmbus_read_byte_data(client, page, reg); + rv = func(client, page, reg); if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) rv = pmbus_check_status_cml(client); pmbus_clear_fault_page(client, -1); return rv >= 0; } + +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); +} EXPORT_SYMBOL_GPL(pmbus_check_byte_register); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) { - int rv; - struct pmbus_data *data = i2c_get_clientdata(client); - - rv = _pmbus_read_word_data(client, page, reg); - if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) - rv = pmbus_check_status_cml(client); - pmbus_clear_fault_page(client, -1); - return rv >= 0; + return pmbus_check_register(client, _pmbus_read_word_data, page, reg); } EXPORT_SYMBOL_GPL(pmbus_check_word_register); @@ -341,53 +335,43 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) } EXPORT_SYMBOL_GPL(pmbus_get_driver_info); +static struct _pmbus_status { + u32 func; + u16 base; + u16 reg; +} pmbus_status[] = { + { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, + { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, + { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, + PMBUS_STATUS_TEMPERATURE }, + { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, + { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, +}; + static struct pmbus_data *pmbus_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; + struct pmbus_sensor *sensor; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - int i; - - for (i = 0; i < info->pages; i++) - data->status[PB_STATUS_BASE + i] - = pmbus_read_byte_data(client, i, - PMBUS_STATUS_BYTE); - for (i = 0; i < info->pages; i++) { - if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT)) - continue; - data->status[PB_STATUS_VOUT_BASE + i] - = _pmbus_read_byte_data(client, i, PMBUS_STATUS_VOUT); - } - for (i = 0; i < info->pages; i++) { - if (!(info->func[i] & PMBUS_HAVE_STATUS_IOUT)) - continue; - data->status[PB_STATUS_IOUT_BASE + i] - = _pmbus_read_byte_data(client, i, PMBUS_STATUS_IOUT); - } - for (i = 0; i < info->pages; i++) { - if (!(info->func[i] & PMBUS_HAVE_STATUS_TEMP)) - continue; - data->status[PB_STATUS_TEMP_BASE + i] - = _pmbus_read_byte_data(client, i, - PMBUS_STATUS_TEMPERATURE); - } - for (i = 0; i < info->pages; i++) { - if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN12)) - continue; - data->status[PB_STATUS_FAN_BASE + i] - = _pmbus_read_byte_data(client, i, - PMBUS_STATUS_FAN_12); - } + int i, j; for (i = 0; i < info->pages; i++) { - if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN34)) - continue; - data->status[PB_STATUS_FAN34_BASE + i] - = _pmbus_read_byte_data(client, i, - PMBUS_STATUS_FAN_34); + data->status[PB_STATUS_BASE + i] + = _pmbus_read_byte_data(client, i, + data->status_register); + for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { + struct _pmbus_status *s = &pmbus_status[j]; + + if (!(info->func[i] & s->func)) + continue; + data->status[s->base + i] + = _pmbus_read_byte_data(client, i, + s->reg); + } } if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) @@ -395,9 +379,12 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) = _pmbus_read_byte_data(client, 0, PMBUS_STATUS_INPUT); - for (i = 0; i < data->num_sensors; i++) { - struct pmbus_sensor *sensor = &data->sensors[i]; + if (info->func[0] & PMBUS_HAVE_STATUS_VMON) + data->status[PB_STATUS_VMON_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_VIRT_STATUS_VMON); + for (sensor = data->sensors; sensor; sensor = sensor->next) { if (!data->valid || sensor->update) sensor->data = _pmbus_read_word_data(client, @@ -424,16 +411,11 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, long val; if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ - exponent = data->exponent; + exponent = data->exponent[sensor->page]; mantissa = (u16) sensor->data; } else { /* LINEAR11 */ - exponent = (sensor->data >> 11) & 0x001f; - mantissa = sensor->data & 0x07ff; - - if (exponent > 0x0f) - exponent |= 0xffe0; /* sign extend exponent */ - if (mantissa > 0x03ff) - mantissa |= 0xfffff800; /* sign extend mantissa */ + exponent = ((s16)sensor->data) >> 11; + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; } val = mantissa; @@ -535,7 +517,7 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) #define MIN_MANTISSA (511 * 1000) static u16 pmbus_data2reg_linear(struct pmbus_data *data, - enum pmbus_sensor_classes class, long val) + struct pmbus_sensor *sensor, long val) { s16 exponent = 0, mantissa; bool negative = false; @@ -544,7 +526,7 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, if (val == 0) return 0; - if (class == PSC_VOLTAGE_OUT) { + if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 does not support negative voltages */ if (val < 0) return 0; @@ -553,10 +535,10 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, * For a static exponents, we don't have a choice * but to adjust the value to it. */ - if (data->exponent < 0) - val <<= -data->exponent; + if (data->exponent[sensor->page] < 0) + val <<= -data->exponent[sensor->page]; else - val >>= data->exponent; + val >>= data->exponent[sensor->page]; val = DIV_ROUND_CLOSEST(val, 1000); return val & 0xffff; } @@ -567,14 +549,14 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, } /* Power is in uW. Convert to mW before converting. */ - if (class == PSC_POWER) + if (sensor->class == PSC_POWER) val = DIV_ROUND_CLOSEST(val, 1000L); /* * For simplicity, convert fan data to milli-units * before calculating the exponent. */ - if (class == PSC_FAN) + if (sensor->class == PSC_FAN) val = val * 1000; /* Reduce large mantissa until it fits into 10 bit */ @@ -604,22 +586,22 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, } static u16 pmbus_data2reg_direct(struct pmbus_data *data, - enum pmbus_sensor_classes class, long val) + struct pmbus_sensor *sensor, long val) { long m, b, R; - m = data->info->m[class]; - b = data->info->b[class]; - R = data->info->R[class]; + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; /* Power is in uW. Adjust R and b. */ - if (class == PSC_POWER) { + if (sensor->class == PSC_POWER) { R -= 3; b *= 1000; } /* Calculate Y = (m * X + b) * 10^R */ - if (class != PSC_FAN) { + if (sensor->class != PSC_FAN) { R -= 3; /* Adjust R and b for data in milli-units */ b *= 1000; } @@ -638,28 +620,28 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, } static u16 pmbus_data2reg_vid(struct pmbus_data *data, - enum pmbus_sensor_classes class, long val) + struct pmbus_sensor *sensor, long val) { - val = SENSORS_LIMIT(val, 500, 1600); + val = clamp_val(val, 500, 1600); return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); } static u16 pmbus_data2reg(struct pmbus_data *data, - enum pmbus_sensor_classes class, long val) + struct pmbus_sensor *sensor, long val) { u16 regval; - switch (data->info->format[class]) { + switch (data->info->format[sensor->class]) { case direct: - regval = pmbus_data2reg_direct(data, class, val); + regval = pmbus_data2reg_direct(data, sensor, val); break; case vid: - regval = pmbus_data2reg_vid(data, class, val); + regval = pmbus_data2reg_vid(data, sensor, val); break; case linear: default: - regval = pmbus_data2reg_linear(data, class, val); + regval = pmbus_data2reg_linear(data, sensor, val); break; } return regval; @@ -667,25 +649,20 @@ static u16 pmbus_data2reg(struct pmbus_data *data, /* * Return boolean calculated from converted data. - * <index> defines a status register index and mask, and optionally - * two sensor indexes. - * The upper half-word references the two sensors, - * two sensor indices. - * The upper half-word references the two optional sensors, - * the lower half word references status register and mask. - * The function returns true if (status[reg] & mask) is true and, - * if specified, if v1 >= v2. - * To determine if an object exceeds upper limits, specify <v, limit>. - * To determine if an object exceeds lower limits, specify <limit, v>. + * <index> defines a status register index and mask. + * The mask is in the lower 8 bits, the register index is in bits 8..23. * - * For booleans created with pmbus_add_boolean_reg(), only the lower 16 bits of - * index are set. s1 and s2 (the sensor index values) are zero in this case. - * The function returns true if (status[reg] & mask) is true. + * The associated pmbus_boolean structure contains optional pointers to two + * sensor attributes. If specified, those attributes are compared against each + * other to determine if a limit has been exceeded. * - * If the boolean was created with pmbus_add_boolean_cmp(), a comparison against - * a specified limit has to be performed to determine the boolean result. + * If the sensor attribute pointers are NULL, the function returns true if + * (status[reg] & mask) is true. + * + * If sensor attribute pointers are provided, a comparison against a specified + * limit has to be performed to determine the boolean result. * In this case, the function returns true if v1 >= v2 (where v1 and v2 are - * sensor values referenced by sensor indices s1 and s2). + * sensor values referenced by sensor attribute pointers s1 and s2). * * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>. * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>. @@ -693,13 +670,14 @@ static u16 pmbus_data2reg(struct pmbus_data *data, * If a negative value is stored in any of the referenced registers, this value * reflects an error code which will be returned. */ -static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) +static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, + int index) { - u8 s1 = (index >> 24) & 0xff; - u8 s2 = (index >> 16) & 0xff; - u8 reg = (index >> 8) & 0xff; + struct pmbus_sensor *s1 = b->s1; + struct pmbus_sensor *s2 = b->s2; + u16 reg = (index >> 8) & 0xffff; u8 mask = index & 0xff; - int status; + int ret, status; u8 regval; status = data->status[reg]; @@ -707,48 +685,46 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) return status; regval = status & mask; - if (!s1 && !s2) - *val = !!regval; - else { + if (!s1 && !s2) { + ret = !!regval; + } else if (!s1 || !s2) { + WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); + return 0; + } else { long v1, v2; - struct pmbus_sensor *sensor1, *sensor2; - - sensor1 = &data->sensors[s1]; - if (sensor1->data < 0) - return sensor1->data; - sensor2 = &data->sensors[s2]; - if (sensor2->data < 0) - return sensor2->data; - - v1 = pmbus_reg2data(data, sensor1); - v2 = pmbus_reg2data(data, sensor2); - *val = !!(regval && v1 >= v2); + + if (s1->data < 0) + return s1->data; + if (s2->data < 0) + return s2->data; + + v1 = pmbus_reg2data(data, s1); + v2 = pmbus_reg2data(data, s2); + ret = !!(regval && v1 >= v2); } - return 0; + return ret; } static ssize_t pmbus_show_boolean(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_boolean *boolean = to_pmbus_boolean(attr); struct pmbus_data *data = pmbus_update_device(dev); int val; - int err; - err = pmbus_get_boolean(data, attr->index, &val); - if (err) - return err; + val = pmbus_get_boolean(data, boolean, attr->index); + if (val < 0) + return val; return snprintf(buf, PAGE_SIZE, "%d\n", val); } static ssize_t pmbus_show_sensor(struct device *dev, - struct device_attribute *da, char *buf) + struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct pmbus_data *data = pmbus_update_device(dev); - struct pmbus_sensor *sensor; + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); - sensor = &data->sensors[attr->index]; if (sensor->data < 0) return sensor->data; @@ -759,25 +735,24 @@ static ssize_t pmbus_set_sensor(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); - struct pmbus_sensor *sensor = &data->sensors[attr->index]; + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); ssize_t rv = count; long val = 0; int ret; u16 regval; - if (strict_strtol(buf, 10, &val) < 0) + if (kstrtol(buf, 10, &val) < 0) return -EINVAL; mutex_lock(&data->update_lock); - regval = pmbus_data2reg(data, sensor->class, val); + regval = pmbus_data2reg(data, sensor, val); ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); if (ret < 0) rv = ret; else - data->sensors[attr->index].data = regval; + sensor->data = regval; mutex_unlock(&data->update_lock); return rv; } @@ -785,102 +760,132 @@ static ssize_t pmbus_set_sensor(struct device *dev, static ssize_t pmbus_show_label(struct device *dev, struct device_attribute *da, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct pmbus_data *data = i2c_get_clientdata(client); - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_label *label = to_pmbus_label(da); - return snprintf(buf, PAGE_SIZE, "%s\n", - data->labels[attr->index].label); + return snprintf(buf, PAGE_SIZE, "%s\n", label->label); } -#define PMBUS_ADD_ATTR(data, _name, _idx, _mode, _type, _show, _set) \ -do { \ - struct sensor_device_attribute *a \ - = &data->_type##s[data->num_##_type##s].attribute; \ - BUG_ON(data->num_attributes >= data->max_attributes); \ - sysfs_attr_init(&a->dev_attr.attr); \ - a->dev_attr.attr.name = _name; \ - a->dev_attr.attr.mode = _mode; \ - a->dev_attr.show = _show; \ - a->dev_attr.store = _set; \ - a->index = _idx; \ - data->attributes[data->num_attributes] = &a->dev_attr.attr; \ - data->num_attributes++; \ -} while (0) - -#define PMBUS_ADD_GET_ATTR(data, _name, _type, _idx) \ - PMBUS_ADD_ATTR(data, _name, _idx, S_IRUGO, _type, \ - pmbus_show_##_type, NULL) - -#define PMBUS_ADD_SET_ATTR(data, _name, _type, _idx) \ - PMBUS_ADD_ATTR(data, _name, _idx, S_IWUSR | S_IRUGO, _type, \ - pmbus_show_##_type, pmbus_set_##_type) - -static void pmbus_add_boolean(struct pmbus_data *data, - const char *name, const char *type, int seq, - int idx) +static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) { - struct pmbus_boolean *boolean; - - BUG_ON(data->num_booleans >= data->max_booleans); - - boolean = &data->booleans[data->num_booleans]; + if (data->num_attributes >= data->max_attributes - 1) { + int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; + void *new_attrs = krealloc(data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); + if (!new_attrs) + return -ENOMEM; + data->group.attrs = new_attrs; + data->max_attributes = new_max_attrs; + } - snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", - name, seq, type); - PMBUS_ADD_GET_ATTR(data, boolean->name, boolean, idx); - data->num_booleans++; + data->group.attrs[data->num_attributes++] = attr; + data->group.attrs[data->num_attributes] = NULL; + return 0; } -static void pmbus_add_boolean_reg(struct pmbus_data *data, - const char *name, const char *type, - int seq, int reg, int bit) +static void pmbus_dev_attr_init(struct device_attribute *dev_attr, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) { - pmbus_add_boolean(data, name, type, seq, (reg << 8) | bit); + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = mode; + dev_attr->show = show; + dev_attr->store = store; } -static void pmbus_add_boolean_cmp(struct pmbus_data *data, - const char *name, const char *type, - int seq, int i1, int i2, int reg, int mask) +static void pmbus_attr_init(struct sensor_device_attribute *a, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count), + int idx) { - pmbus_add_boolean(data, name, type, seq, - (i1 << 24) | (i2 << 16) | (reg << 8) | mask); + pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store); + a->index = idx; } -static void pmbus_add_sensor(struct pmbus_data *data, +static int pmbus_add_boolean(struct pmbus_data *data, const char *name, const char *type, int seq, - int page, int reg, enum pmbus_sensor_classes class, - bool update, bool readonly) + struct pmbus_sensor *s1, + struct pmbus_sensor *s2, + u16 reg, u8 mask) +{ + struct pmbus_boolean *boolean; + struct sensor_device_attribute *a; + + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); + if (!boolean) + return -ENOMEM; + + a = &boolean->attribute; + + snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", + name, seq, type); + boolean->s1 = s1; + boolean->s2 = s2; + pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL, + (reg << 8) | mask); + + return pmbus_add_attribute(data, &a->dev_attr.attr); +} + +static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, + const char *name, const char *type, + int seq, int page, int reg, + enum pmbus_sensor_classes class, + bool update, bool readonly) { struct pmbus_sensor *sensor; + struct device_attribute *a; - BUG_ON(data->num_sensors >= data->max_sensors); + sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + a = &sensor->attribute; - sensor = &data->sensors[data->num_sensors]; snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", name, seq, type); sensor->page = page; sensor->reg = reg; sensor->class = class; sensor->update = update; - if (readonly) - PMBUS_ADD_GET_ATTR(data, sensor->name, sensor, - data->num_sensors); - else - PMBUS_ADD_SET_ATTR(data, sensor->name, sensor, - data->num_sensors); - data->num_sensors++; + pmbus_dev_attr_init(a, sensor->name, + readonly ? S_IRUGO : S_IRUGO | S_IWUSR, + pmbus_show_sensor, pmbus_set_sensor); + + if (pmbus_add_attribute(data, &a->attr)) + return NULL; + + sensor->next = data->sensors; + data->sensors = sensor; + + return sensor; } -static void pmbus_add_label(struct pmbus_data *data, - const char *name, int seq, - const char *lstring, int index) +static int pmbus_add_label(struct pmbus_data *data, + const char *name, int seq, + const char *lstring, int index) { struct pmbus_label *label; + struct device_attribute *a; + + label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); + if (!label) + return -ENOMEM; - BUG_ON(data->num_labels >= data->max_labels); + a = &label->attribute; - label = &data->labels[data->num_labels]; snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); if (!index) strncpy(label->label, lstring, sizeof(label->label) - 1); @@ -888,65 +893,8 @@ static void pmbus_add_label(struct pmbus_data *data, snprintf(label->label, sizeof(label->label), "%s%d", lstring, index); - PMBUS_ADD_GET_ATTR(data, label->name, label, data->num_labels); - data->num_labels++; -} - -/* - * Determine maximum number of sensors, booleans, and labels. - * To keep things simple, only make a rough high estimate. - */ -static void pmbus_find_max_attr(struct i2c_client *client, - struct pmbus_data *data) -{ - const struct pmbus_driver_info *info = data->info; - int page, max_sensors, max_booleans, max_labels; - - max_sensors = PMBUS_MAX_INPUT_SENSORS; - max_booleans = PMBUS_MAX_INPUT_BOOLEANS; - max_labels = PMBUS_MAX_INPUT_LABELS; - - for (page = 0; page < info->pages; page++) { - if (info->func[page] & PMBUS_HAVE_VOUT) { - max_sensors += PMBUS_VOUT_SENSORS_PER_PAGE; - max_booleans += PMBUS_VOUT_BOOLEANS_PER_PAGE; - max_labels++; - } - if (info->func[page] & PMBUS_HAVE_IOUT) { - max_sensors += PMBUS_IOUT_SENSORS_PER_PAGE; - max_booleans += PMBUS_IOUT_BOOLEANS_PER_PAGE; - max_labels++; - } - if (info->func[page] & PMBUS_HAVE_POUT) { - max_sensors += PMBUS_POUT_SENSORS_PER_PAGE; - max_booleans += PMBUS_POUT_BOOLEANS_PER_PAGE; - max_labels++; - } - if (info->func[page] & PMBUS_HAVE_FAN12) { - max_sensors += 2 * PMBUS_MAX_SENSORS_PER_FAN; - max_booleans += 2 * PMBUS_MAX_BOOLEANS_PER_FAN; - } - if (info->func[page] & PMBUS_HAVE_FAN34) { - max_sensors += 2 * PMBUS_MAX_SENSORS_PER_FAN; - max_booleans += 2 * PMBUS_MAX_BOOLEANS_PER_FAN; - } - if (info->func[page] & PMBUS_HAVE_TEMP) { - max_sensors += PMBUS_MAX_SENSORS_PER_TEMP; - max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP; - } - if (info->func[page] & PMBUS_HAVE_TEMP2) { - max_sensors += PMBUS_MAX_SENSORS_PER_TEMP; - max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP; - } - if (info->func[page] & PMBUS_HAVE_TEMP3) { - max_sensors += PMBUS_MAX_SENSORS_PER_TEMP; - max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP; - } - } - data->max_sensors = max_sensors; - data->max_booleans = max_booleans; - data->max_labels = max_labels; - data->max_attributes = max_sensors + max_booleans + max_labels; + pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL); + return pmbus_add_attribute(data, &a->attr); } /* @@ -959,10 +907,12 @@ static void pmbus_find_max_attr(struct i2c_client *client, */ struct pmbus_limit_attr { u16 reg; /* Limit register */ + u16 sbit; /* Alarm attribute status bit */ bool update; /* True if register needs updates */ + bool low; /* True if low limit; for limits with compare + functions only */ const char *attr; /* Attribute name */ const char *alarm; /* Alarm attribute name */ - u32 sbit; /* Alarm attribute status bit */ }; /* @@ -970,7 +920,9 @@ struct pmbus_limit_attr { * description includes a reference to the associated limit attributes. */ struct pmbus_sensor_attr { - u8 reg; /* sensor register */ + u16 reg; /* sensor register */ + u8 gbit; /* generic status bit */ + u8 nlimit; /* # of limit registers */ enum pmbus_sensor_classes class;/* sensor class */ const char *label; /* sensor label */ bool paged; /* true if paged sensor */ @@ -979,46 +931,47 @@ struct pmbus_sensor_attr { u32 func; /* sensor mask */ u32 sfunc; /* sensor status mask */ int sbase; /* status base register */ - u32 gbit; /* generic status bit */ const struct pmbus_limit_attr *limit;/* limit registers */ - int nlimit; /* # of limit registers */ }; /* * Add a set of limit attributes and, if supported, the associated * alarm attributes. + * returns 0 if no alarm register found, 1 if an alarm register was found, + * < 0 on errors. */ -static bool pmbus_add_limit_attrs(struct i2c_client *client, - struct pmbus_data *data, - const struct pmbus_driver_info *info, - const char *name, int index, int page, - int cbase, - const struct pmbus_sensor_attr *attr) +static int pmbus_add_limit_attrs(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, int index, int page, + struct pmbus_sensor *base, + const struct pmbus_sensor_attr *attr) { const struct pmbus_limit_attr *l = attr->limit; int nlimit = attr->nlimit; - bool have_alarm = false; - int i, cindex; + int have_alarm = 0; + int i, ret; + struct pmbus_sensor *curr; for (i = 0; i < nlimit; i++) { if (pmbus_check_word_register(client, page, l->reg)) { - cindex = data->num_sensors; - pmbus_add_sensor(data, name, l->attr, index, page, - l->reg, attr->class, - attr->update || l->update, - false); + curr = pmbus_add_sensor(data, name, l->attr, index, + page, l->reg, attr->class, + attr->update || l->update, + false); + if (!curr) + return -ENOMEM; if (l->sbit && (info->func[page] & attr->sfunc)) { - if (attr->compare) { - pmbus_add_boolean_cmp(data, name, - l->alarm, index, - cbase, cindex, - attr->sbase + page, l->sbit); - } else { - pmbus_add_boolean_reg(data, name, - l->alarm, index, - attr->sbase + page, l->sbit); - } - have_alarm = true; + ret = pmbus_add_boolean(data, name, + l->alarm, index, + attr->compare ? l->low ? curr : base + : NULL, + attr->compare ? l->low ? base : curr + : NULL, + attr->sbase + page, l->sbit); + if (ret) + return ret; + have_alarm = 1; } } l++; @@ -1026,45 +979,59 @@ static bool pmbus_add_limit_attrs(struct i2c_client *client, return have_alarm; } -static void pmbus_add_sensor_attrs_one(struct i2c_client *client, - struct pmbus_data *data, - const struct pmbus_driver_info *info, - const char *name, - int index, int page, - const struct pmbus_sensor_attr *attr) +static int pmbus_add_sensor_attrs_one(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, + int index, int page, + const struct pmbus_sensor_attr *attr) { - bool have_alarm; - int cbase = data->num_sensors; - - if (attr->label) - pmbus_add_label(data, name, index, attr->label, - attr->paged ? page + 1 : 0); - pmbus_add_sensor(data, name, "input", index, page, attr->reg, - attr->class, true, true); + struct pmbus_sensor *base; + int ret; + + if (attr->label) { + ret = pmbus_add_label(data, name, index, attr->label, + attr->paged ? page + 1 : 0); + if (ret) + return ret; + } + base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, + attr->class, true, true); + if (!base) + return -ENOMEM; if (attr->sfunc) { - have_alarm = pmbus_add_limit_attrs(client, data, info, name, - index, page, cbase, attr); + ret = pmbus_add_limit_attrs(client, data, info, name, + index, page, base, attr); + if (ret < 0) + return ret; /* * Add generic alarm attribute only if there are no individual * alarm attributes, if there is a global alarm bit, and if * the generic status register for this page is accessible. */ - if (!have_alarm && attr->gbit && - pmbus_check_byte_register(client, page, PMBUS_STATUS_BYTE)) - pmbus_add_boolean_reg(data, name, "alarm", index, - PB_STATUS_BASE + page, - attr->gbit); + if (!ret && attr->gbit && + pmbus_check_byte_register(client, page, + data->status_register)) { + ret = pmbus_add_boolean(data, name, "alarm", index, + NULL, NULL, + PB_STATUS_BASE + page, + attr->gbit); + if (ret) + return ret; + } } + return 0; } -static void pmbus_add_sensor_attrs(struct i2c_client *client, - struct pmbus_data *data, - const char *name, - const struct pmbus_sensor_attr *attrs, - int nattrs) +static int pmbus_add_sensor_attrs(struct i2c_client *client, + struct pmbus_data *data, + const char *name, + const struct pmbus_sensor_attr *attrs, + int nattrs) { const struct pmbus_driver_info *info = data->info; int index, i; + int ret; index = 1; for (i = 0; i < nattrs; i++) { @@ -1074,12 +1041,16 @@ static void pmbus_add_sensor_attrs(struct i2c_client *client, for (page = 0; page < pages; page++) { if (!(info->func[page] & attrs->func)) continue; - pmbus_add_sensor_attrs_one(client, data, info, name, - index, page, attrs); + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + attrs); + if (ret) + return ret; index++; } attrs++; } + return 0; } static const struct pmbus_limit_attr vin_limit_attrs[] = { @@ -1121,6 +1092,30 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = { }, }; +static const struct pmbus_limit_attr vmon_limit_attrs[] = { + { + .reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + } +}; + static const struct pmbus_limit_attr vout_limit_attrs[] = { { .reg = PMBUS_VOUT_UV_WARN_LIMIT, @@ -1172,6 +1167,15 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .limit = vin_limit_attrs, .nlimit = ARRAY_SIZE(vin_limit_attrs), }, { + .reg = PMBUS_VIRT_READ_VMON, + .class = PSC_VOLTAGE_IN, + .label = "vmon", + .func = PMBUS_HAVE_VMON, + .sfunc = PMBUS_HAVE_STATUS_VMON, + .sbase = PB_STATUS_VMON_BASE, + .limit = vmon_limit_attrs, + .nlimit = ARRAY_SIZE(vmon_limit_attrs), + }, { .reg = PMBUS_READ_VCAP, .class = PSC_VOLTAGE_IN, .label = "vcap", @@ -1317,6 +1321,17 @@ static const struct pmbus_limit_attr pout_limit_attrs[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_POUT_OP_FAULT, + }, { + .reg = PMBUS_VIRT_READ_POUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_POUT_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_POUT_HISTORY, + .attr = "reset_history", } }; @@ -1348,11 +1363,13 @@ static const struct pmbus_sensor_attr power_attributes[] = { static const struct pmbus_limit_attr temp_limit_attrs[] = { { .reg = PMBUS_UT_WARN_LIMIT, + .low = true, .attr = "min", .alarm = "min_alarm", .sbit = PB_TEMP_UT_WARNING, }, { .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, .attr = "lcrit", .alarm = "lcrit_alarm", .sbit = PB_TEMP_UT_FAULT, @@ -1370,6 +1387,9 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = { .reg = PMBUS_VIRT_READ_TEMP_MIN, .attr = "lowest", }, { + .reg = PMBUS_VIRT_READ_TEMP_AVG, + .attr = "average", + }, { .reg = PMBUS_VIRT_READ_TEMP_MAX, .attr = "highest", }, { @@ -1378,14 +1398,54 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = { } }; -static const struct pmbus_limit_attr temp_limit_attrs23[] = { +static const struct pmbus_limit_attr temp_limit_attrs2[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs3[] = { { .reg = PMBUS_UT_WARN_LIMIT, + .low = true, .attr = "min", .alarm = "min_alarm", .sbit = PB_TEMP_UT_WARNING, }, { .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, .attr = "lcrit", .alarm = "lcrit_alarm", .sbit = PB_TEMP_UT_FAULT, @@ -1425,8 +1485,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .sfunc = PMBUS_HAVE_STATUS_TEMP, .sbase = PB_STATUS_TEMP_BASE, .gbit = PB_STATUS_TEMPERATURE, - .limit = temp_limit_attrs23, - .nlimit = ARRAY_SIZE(temp_limit_attrs23), + .limit = temp_limit_attrs2, + .nlimit = ARRAY_SIZE(temp_limit_attrs2), }, { .reg = PMBUS_READ_TEMPERATURE_3, .class = PSC_TEMPERATURE, @@ -1437,8 +1497,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .sfunc = PMBUS_HAVE_STATUS_TEMP, .sbase = PB_STATUS_TEMP_BASE, .gbit = PB_STATUS_TEMPERATURE, - .limit = temp_limit_attrs23, - .nlimit = ARRAY_SIZE(temp_limit_attrs23), + .limit = temp_limit_attrs3, + .nlimit = ARRAY_SIZE(temp_limit_attrs3), } }; @@ -1478,12 +1538,13 @@ static const u32 pmbus_fan_status_flags[] = { }; /* Fans */ -static void pmbus_add_fan_attributes(struct i2c_client *client, - struct pmbus_data *data) +static int pmbus_add_fan_attributes(struct i2c_client *client, + struct pmbus_data *data) { const struct pmbus_driver_info *info = data->info; int index = 1; int page; + int ret; for (page = 0; page < info->pages; page++) { int f; @@ -1509,9 +1570,10 @@ static void pmbus_add_fan_attributes(struct i2c_client *client, (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) continue; - pmbus_add_sensor(data, "fan", "input", index, page, - pmbus_fan_registers[f], PSC_FAN, true, - true); + if (pmbus_add_sensor(data, "fan", "input", index, + page, pmbus_fan_registers[f], + PSC_FAN, true, true) == NULL) + return -ENOMEM; /* * Each fan status register covers multiple fans, @@ -1526,39 +1588,55 @@ static void pmbus_add_fan_attributes(struct i2c_client *client, base = PB_STATUS_FAN34_BASE + page; else base = PB_STATUS_FAN_BASE + page; - pmbus_add_boolean_reg(data, "fan", "alarm", - index, base, + ret = pmbus_add_boolean(data, "fan", + "alarm", index, NULL, NULL, base, PB_FAN_FAN1_WARNING >> (f & 1)); - pmbus_add_boolean_reg(data, "fan", "fault", - index, base, + if (ret) + return ret; + ret = pmbus_add_boolean(data, "fan", + "fault", index, NULL, NULL, base, PB_FAN_FAN1_FAULT >> (f & 1)); + if (ret) + return ret; } index++; } } + return 0; } -static void pmbus_find_attributes(struct i2c_client *client, - struct pmbus_data *data) +static int pmbus_find_attributes(struct i2c_client *client, + struct pmbus_data *data) { + int ret; + /* Voltage sensors */ - pmbus_add_sensor_attrs(client, data, "in", voltage_attributes, - ARRAY_SIZE(voltage_attributes)); + ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes, + ARRAY_SIZE(voltage_attributes)); + if (ret) + return ret; /* Current sensors */ - pmbus_add_sensor_attrs(client, data, "curr", current_attributes, - ARRAY_SIZE(current_attributes)); + ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes, + ARRAY_SIZE(current_attributes)); + if (ret) + return ret; /* Power sensors */ - pmbus_add_sensor_attrs(client, data, "power", power_attributes, - ARRAY_SIZE(power_attributes)); + ret = pmbus_add_sensor_attrs(client, data, "power", power_attributes, + ARRAY_SIZE(power_attributes)); + if (ret) + return ret; /* Temperature sensors */ - pmbus_add_sensor_attrs(client, data, "temp", temp_attributes, - ARRAY_SIZE(temp_attributes)); + ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes, + ARRAY_SIZE(temp_attributes)); + if (ret) + return ret; /* Fans */ - pmbus_add_fan_attributes(client, data); + ret = pmbus_add_fan_attributes(client, data); + return ret; } /* @@ -1566,12 +1644,13 @@ static void pmbus_find_attributes(struct i2c_client *client, * This function is called for all chips. */ static int pmbus_identify_common(struct i2c_client *client, - struct pmbus_data *data) + struct pmbus_data *data, int page) { - int vout_mode = -1, exponent; + int vout_mode = -1; - if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) - vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) + vout_mode = _pmbus_read_byte_data(client, page, + PMBUS_VOUT_MODE); if (vout_mode >= 0 && vout_mode != 0xff) { /* * Not all chips support the VOUT_MODE command, @@ -1582,11 +1661,7 @@ static int pmbus_identify_common(struct i2c_client *client, if (data->info->format[PSC_VOLTAGE_OUT] != linear) return -ENODEV; - exponent = vout_mode & 0x1f; - /* and sign-extend it */ - if (exponent & 0x10) - exponent |= ~0x1f; - data->exponent = exponent; + data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; break; case 1: /* VID mode */ if (data->info->format[PSC_VOLTAGE_OUT] != vid) @@ -1601,140 +1676,115 @@ static int pmbus_identify_common(struct i2c_client *client, } } - /* Determine maximum number of sensors, booleans, and labels */ - pmbus_find_max_attr(client, data); - pmbus_clear_fault_page(client, 0); + pmbus_clear_fault_page(client, page); + return 0; +} + +static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + int page, ret; + + /* + * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try + * to use PMBUS_STATUS_WORD instead if that is the case. + * Bail out if both registers are not supported. + */ + data->status_register = PMBUS_STATUS_BYTE; + ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + if (ret < 0 || ret == 0xff) { + data->status_register = PMBUS_STATUS_WORD; + ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + if (ret < 0 || ret == 0xffff) { + dev_err(dev, "PMBus status register not found\n"); + return -ENODEV; + } + } + + pmbus_clear_faults(client); + + if (info->identify) { + ret = (*info->identify)(client, info); + if (ret < 0) { + dev_err(dev, "Chip identification failed\n"); + return ret; + } + } + + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { + dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages); + return -ENODEV; + } + + for (page = 0; page < info->pages; page++) { + ret = pmbus_identify_common(client, data, page); + if (ret < 0) { + dev_err(dev, "Failed to identify chip capabilities\n"); + return ret; + } + } return 0; } int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info) { - const struct pmbus_platform_data *pdata = client->dev.platform_data; + struct device *dev = &client->dev; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); struct pmbus_data *data; int ret; - if (!info) { - dev_err(&client->dev, "Missing chip information"); + if (!info) return -ENODEV; - } if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "No memory to allocate driver data\n"); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; - } i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - - /* Bail out if PMBus status register does not exist. */ - if (i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE) < 0) { - dev_err(&client->dev, "PMBus status register not found\n"); - ret = -ENODEV; - goto out_data; - } + data->dev = dev; if (pdata) data->flags = pdata->flags; data->info = info; - pmbus_clear_faults(client); - - if (info->identify) { - ret = (*info->identify)(client, info); - if (ret < 0) { - dev_err(&client->dev, "Chip identification failed\n"); - goto out_data; - } - } - - if (info->pages <= 0 || info->pages > PMBUS_PAGES) { - dev_err(&client->dev, "Bad number of PMBus pages: %d\n", - info->pages); - ret = -EINVAL; - goto out_data; - } - - ret = pmbus_identify_common(client, data); - if (ret < 0) { - dev_err(&client->dev, "Failed to identify chip capabilities\n"); - goto out_data; - } - - ret = -ENOMEM; - data->sensors = kzalloc(sizeof(struct pmbus_sensor) * data->max_sensors, - GFP_KERNEL); - if (!data->sensors) { - dev_err(&client->dev, "No memory to allocate sensor data\n"); - goto out_data; - } - - data->booleans = kzalloc(sizeof(struct pmbus_boolean) - * data->max_booleans, GFP_KERNEL); - if (!data->booleans) { - dev_err(&client->dev, "No memory to allocate boolean data\n"); - goto out_sensors; - } - - data->labels = kzalloc(sizeof(struct pmbus_label) * data->max_labels, - GFP_KERNEL); - if (!data->labels) { - dev_err(&client->dev, "No memory to allocate label data\n"); - goto out_booleans; - } - - data->attributes = kzalloc(sizeof(struct attribute *) - * data->max_attributes, GFP_KERNEL); - if (!data->attributes) { - dev_err(&client->dev, "No memory to allocate attribute data\n"); - goto out_labels; - } + ret = pmbus_init_common(client, data, info); + if (ret < 0) + return ret; - pmbus_find_attributes(client, data); + ret = pmbus_find_attributes(client, data); + if (ret) + goto out_kfree; /* * If there are no attributes, something is wrong. * Bail out instead of trying to register nothing. */ if (!data->num_attributes) { - dev_err(&client->dev, "No attributes found\n"); + dev_err(dev, "No attributes found\n"); ret = -ENODEV; - goto out_attributes; + goto out_kfree; } - /* Register sysfs hooks */ - data->group.attrs = data->attributes; - ret = sysfs_create_group(&client->dev.kobj, &data->group); - if (ret) { - dev_err(&client->dev, "Failed to create sysfs entries\n"); - goto out_attributes; - } - data->hwmon_dev = hwmon_device_register(&client->dev); + data->groups[0] = &data->group; + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, data->groups); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); - dev_err(&client->dev, "Failed to register hwmon device\n"); - goto out_hwmon_device_register; + dev_err(dev, "Failed to register hwmon device\n"); + goto out_kfree; } return 0; -out_hwmon_device_register: - sysfs_remove_group(&client->dev.kobj, &data->group); -out_attributes: - kfree(data->attributes); -out_labels: - kfree(data->labels); -out_booleans: - kfree(data->booleans); -out_sensors: - kfree(data->sensors); -out_data: - kfree(data); +out_kfree: + kfree(data->group.attrs); return ret; } EXPORT_SYMBOL_GPL(pmbus_do_probe); @@ -1743,12 +1793,7 @@ int pmbus_do_remove(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &data->group); - kfree(data->attributes); - kfree(data->labels); - kfree(data->booleans); - kfree(data->sensors); - kfree(data); + kfree(data->group.attrs); return 0; } EXPORT_SYMBOL_GPL(pmbus_do_remove); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index ace1c731973..fbb1479d3ad 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -74,8 +74,8 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_FAN_CONFIG_12: - if (page) - return -EINVAL; + if (page > 0) + return -ENXIO; ret = ucd9000_get_fan_config(client, 0); if (ret < 0) @@ -88,8 +88,8 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) ret = fan_config; break; case PMBUS_FAN_CONFIG_34: - if (page) - return -EINVAL; + if (page > 0) + return -ENXIO; ret = ucd9000_get_fan_config(client, 2); if (ret < 0) @@ -141,13 +141,11 @@ static int ucd9000_probe(struct i2c_client *client, block_buffer[ret] = '\0'; dev_info(&client->dev, "Device ID %s\n", block_buffer); - mid = NULL; - for (i = 0; i < ARRAY_SIZE(ucd9000_id); i++) { - mid = &ucd9000_id[i]; + for (mid = ucd9000_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) break; } - if (!mid || !strlen(mid->name)) { + if (!mid->name[0]) { dev_err(&client->dev, "Unsupported device\n"); return -ENODEV; } @@ -157,7 +155,8 @@ static int ucd9000_probe(struct i2c_client *client, "Device mismatch: Configured %s, detected %s\n", id->name, mid->name); - data = kzalloc(sizeof(struct ucd9000_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data), + GFP_KERNEL); if (!data) return -ENOMEM; info = &data->info; @@ -166,13 +165,12 @@ static int ucd9000_probe(struct i2c_client *client, if (ret < 0) { dev_err(&client->dev, "Failed to read number of active pages\n"); - goto out; + return ret; } info->pages = ret; if (!info->pages) { dev_err(&client->dev, "No pages configured\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } /* The internal temperature sensor is always active */ @@ -183,8 +181,7 @@ static int ucd9000_probe(struct i2c_client *client, block_buffer); if (ret <= 0) { dev_err(&client->dev, "Failed to read configuration data\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } for (i = 0; i < ret; i++) { int page = UCD9000_MON_PAGE(block_buffer[i]); @@ -220,7 +217,7 @@ static int ucd9000_probe(struct i2c_client *client, UCD9000_FAN_CONFIG, data->fan_data[i]); if (ret < 0) - goto out; + return ret; } i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0); @@ -229,50 +226,21 @@ static int ucd9000_probe(struct i2c_client *client, | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; } - ret = pmbus_do_probe(client, mid, info); - if (ret < 0) - goto out; - return 0; - -out: - kfree(data); - return ret; + return pmbus_do_probe(client, mid, info); } -static int ucd9000_remove(struct i2c_client *client) -{ - int ret; - struct ucd9000_data *data; - - data = to_ucd9000_data(pmbus_get_driver_info(client)); - ret = pmbus_do_remove(client); - kfree(data); - return ret; -} - - /* This is the driver that will be inserted */ static struct i2c_driver ucd9000_driver = { .driver = { .name = "ucd9000", }, .probe = ucd9000_probe, - .remove = ucd9000_remove, + .remove = pmbus_do_remove, .id_table = ucd9000_id, }; -static int __init ucd9000_init(void) -{ - return i2c_add_driver(&ucd9000_driver); -} - -static void __exit ucd9000_exit(void) -{ - i2c_del_driver(&ucd9000_driver); -} +module_i2c_driver(ucd9000_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); MODULE_LICENSE("GPL"); -module_init(ucd9000_init); -module_exit(ucd9000_exit); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index ffcc1cf3609..033d6aca47d 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -68,13 +68,11 @@ static int ucd9200_probe(struct i2c_client *client, block_buffer[ret] = '\0'; dev_info(&client->dev, "Device ID %s\n", block_buffer); - mid = NULL; - for (i = 0; i < ARRAY_SIZE(ucd9200_id); i++) { - mid = &ucd9200_id[i]; + for (mid = ucd9200_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) break; } - if (!mid || !strlen(mid->name)) { + if (!mid->name[0]) { dev_err(&client->dev, "Unsupported device\n"); return -ENODEV; } @@ -83,7 +81,8 @@ static int ucd9200_probe(struct i2c_client *client, "Device mismatch: Configured %s, detected %s\n", id->name, mid->name); - info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL); + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); if (!info) return -ENOMEM; @@ -91,7 +90,7 @@ static int ucd9200_probe(struct i2c_client *client, block_buffer); if (ret < 0) { dev_err(&client->dev, "Failed to read phase information\n"); - goto out; + return ret; } /* @@ -108,8 +107,7 @@ static int ucd9200_probe(struct i2c_client *client, } if (!info->pages) { dev_err(&client->dev, "No rails configured\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } dev_info(&client->dev, "%d rails configured\n", info->pages); @@ -139,7 +137,7 @@ static int ucd9200_probe(struct i2c_client *client, if (ret < 0) { dev_err(&client->dev, "Failed to initialize PHASE registers\n"); - goto out; + return ret; } } if (info->pages > 1) @@ -162,49 +160,21 @@ static int ucd9200_probe(struct i2c_client *client, if (mid->driver_data == ucd9240) info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12; - ret = pmbus_do_probe(client, mid, info); - if (ret < 0) - goto out; - return 0; -out: - kfree(info); - return ret; -} - -static int ucd9200_remove(struct i2c_client *client) -{ - int ret; - const struct pmbus_driver_info *info; - - info = pmbus_get_driver_info(client); - ret = pmbus_do_remove(client); - kfree(info); - return ret; + return pmbus_do_probe(client, mid, info); } - /* This is the driver that will be inserted */ static struct i2c_driver ucd9200_driver = { .driver = { .name = "ucd9200", }, .probe = ucd9200_probe, - .remove = ucd9200_remove, + .remove = pmbus_do_remove, .id_table = ucd9200_id, }; -static int __init ucd9200_init(void) -{ - return i2c_add_driver(&ucd9200_driver); -} - -static void __exit ucd9200_exit(void) -{ - i2c_del_driver(&ucd9200_driver); -} +module_i2c_driver(ucd9200_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); MODULE_LICENSE("GPL"); -module_init(ucd9200_init); -module_exit(ucd9200_exit); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c new file mode 100644 index 00000000000..81964412125 --- /dev/null +++ b/drivers/hwmon/pmbus/zl6100.c @@ -0,0 +1,419 @@ +/* + * Hardware monitoring driver for ZL6100 and compatibles + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/ktime.h> +#include <linux/delay.h> +#include "pmbus.h" + +enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, + zl9101, zl9117 }; + +struct zl6100_data { + int id; + ktime_t access; /* chip access time */ + int delay; /* Delay between chip accesses in uS */ + struct pmbus_driver_info info; +}; + +#define to_zl6100_data(x) container_of(x, struct zl6100_data, info) + +#define ZL6100_MFR_CONFIG 0xd0 +#define ZL6100_DEVICE_ID 0xe4 + +#define ZL6100_MFR_XTEMP_ENABLE (1 << 7) + +#define MFR_VMON_OV_FAULT_LIMIT 0xf5 +#define MFR_VMON_UV_FAULT_LIMIT 0xf6 +#define MFR_READ_VMON 0xf7 + +#define VMON_UV_WARNING (1 << 5) +#define VMON_OV_WARNING (1 << 4) +#define VMON_UV_FAULT (1 << 1) +#define VMON_OV_FAULT (1 << 0) + +#define ZL6100_WAIT_TIME 1000 /* uS */ + +static ushort delay = ZL6100_WAIT_TIME; +module_param(delay, ushort, 0644); +MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); + +/* Convert linear sensor value to milli-units */ +static long zl6100_l2d(s16 l) +{ + s16 exponent; + s32 mantissa; + long val; + + exponent = l >> 11; + mantissa = ((s16)((l & 0x7ff) << 5)) >> 5; + + val = mantissa; + + /* scale result to milli-units */ + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 zl6100_d2l(long val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (val < 0) { + negative = true; + val = -val; + } + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* Ensure that resulting number is within range */ + if (mantissa > 0x3ff) + mantissa = 0x3ff; + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + +/* Some chips need a delay between accesses */ +static inline void zl6100_wait(const struct zl6100_data *data) +{ + if (data->delay) { + s64 delta = ktime_us_delta(ktime_get(), data->access); + if (delta < data->delay) + udelay(data->delay - delta); + } +} + +static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret, vreg; + + if (page > 0) + return -ENXIO; + + if (data->id == zl2005) { + /* + * Limit register detection is not reliable on ZL2005. + * Make sure registers are not erroneously detected. + */ + switch (reg) { + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + return -ENXIO; + } + } + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + vreg = MFR_READ_VMON; + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + vreg = MFR_VMON_OV_FAULT_LIMIT; + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + vreg = MFR_VMON_UV_FAULT_LIMIT; + break; + default: + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + vreg = reg; + break; + } + + zl6100_wait(data); + ret = pmbus_read_word_data(client, page, vreg); + data->access = ktime_get(); + if (ret < 0) + return ret; + + switch (reg) { + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10)); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10)); + break; + } + + return ret; +} + +static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret, status; + + if (page > 0) + return -ENXIO; + + zl6100_wait(data); + + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) + break; + + status = 0; + if (ret & VMON_UV_WARNING) + status |= PB_VOLTAGE_UV_WARNING; + if (ret & VMON_OV_WARNING) + status |= PB_VOLTAGE_OV_WARNING; + if (ret & VMON_UV_FAULT) + status |= PB_VOLTAGE_UV_FAULT; + if (ret & VMON_OV_FAULT) + status |= PB_VOLTAGE_OV_FAULT; + ret = status; + break; + default: + ret = pmbus_read_byte_data(client, page, reg); + break; + } + data->access = ktime_get(); + + return ret; +} + +static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret, vreg; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9)); + vreg = MFR_VMON_OV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + vreg = MFR_VMON_OV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11)); + vreg = MFR_VMON_UV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + vreg = MFR_VMON_UV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + default: + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + vreg = reg; + } + + zl6100_wait(data); + ret = pmbus_write_word_data(client, page, vreg, word); + data->access = ktime_get(); + + return ret; +} + +static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + zl6100_wait(data); + ret = pmbus_write_byte(client, page, value); + data->access = ktime_get(); + + return ret; +} + +static const struct i2c_device_id zl6100_id[] = { + {"bmr450", zl2005}, + {"bmr451", zl2005}, + {"bmr462", zl2008}, + {"bmr463", zl2008}, + {"bmr464", zl2008}, + {"zl2004", zl2004}, + {"zl2005", zl2005}, + {"zl2006", zl2006}, + {"zl2008", zl2008}, + {"zl2105", zl2105}, + {"zl2106", zl2106}, + {"zl6100", zl6100}, + {"zl6105", zl6105}, + {"zl9101", zl9101}, + {"zl9117", zl9117}, + { } +}; +MODULE_DEVICE_TABLE(i2c, zl6100_id); + +static int zl6100_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct zl6100_data *data; + struct pmbus_driver_info *info; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID, + device_id); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device ID\n"); + return ret; + } + device_id[ret] = '\0'; + dev_info(&client->dev, "Device ID %s\n", device_id); + + mid = NULL; + for (mid = zl6100_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + if (id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = mid->driver_data; + + /* + * According to information from the chip vendor, all currently + * supported chips are known to require a wait time between I2C + * accesses. + */ + data->delay = delay; + + /* + * Since there was a direct I2C device access above, wait before + * accessing the chip again. + */ + data->access = ktime_get(); + zl6100_wait(data); + + info = &data->info; + + info->pages = 1; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + + /* + * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage + * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. + */ + if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) + info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; + + ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL6100_MFR_XTEMP_ENABLE) + info->func[0] |= PMBUS_HAVE_TEMP2; + + data->access = ktime_get(); + zl6100_wait(data); + + info->read_word_data = zl6100_read_word_data; + info->read_byte_data = zl6100_read_byte_data; + info->write_word_data = zl6100_write_word_data; + info->write_byte = zl6100_write_byte; + + return pmbus_do_probe(client, mid, info); +} + +static struct i2c_driver zl6100_driver = { + .driver = { + .name = "zl6100", + }, + .probe = zl6100_probe, + .remove = pmbus_do_remove, + .id_table = zl6100_id, +}; + +module_i2c_driver(zl6100_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); +MODULE_LICENSE("GPL"); |
