From 37324ac91b34af33d6fb9b2977c24ced9ae99392 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 16 Jan 2013 11:36:44 -0800 Subject: [PATCH] Align with upstream driver No alert support in this branch. Signed-off-by: Guenter Roeck --- adm1275.c | 119 ++-------------------- lm25066.c | 12 +-- ltc2978.c | 15 +-- max16064.c | 12 +-- max34440.c | 55 +++++++--- max8688.c | 12 +-- pmbus.c | 12 +-- pmbus.h | 13 ++- pmbus_core.c | 278 +++++++++++++++++++-------------------------------- ucd9000.c | 12 +-- ucd9200.c | 12 +-- zl6100.c | 182 ++++++++++++--------------------- 12 files changed, 233 insertions(+), 501 deletions(-) diff --git a/adm1275.c b/adm1275.c index fbca63a..60aad95 100644 --- a/adm1275.c +++ b/adm1275.c @@ -36,17 +36,6 @@ enum chips { adm1075, adm1275, adm1276 }; #define ADM1075_IRANGE_25 (1 << 3) #define ADM1075_IRANGE_MASK ((1 << 3) | (1 << 4)) -#define ADM1275_ALERT1_CONFIG 0xd5 -#define ADM1275_ALERT2_CONFIG 0xd6 - -#define ADM1275_CONVERT_EN (1 << 2) -#define ADM1275_GPO_EN (1 << 1) -#define ADM1275_GPO_DATA (1 << 0) -#define ADM1275_SMBALERT_MASK (ADM1275_CONVERT_EN | ADM1275_GPO_EN \ - | ADM1275_GPO_DATA) -#define ADM1275_ALERT_MASK 0xf81f /* hw-generated alarms and - configuration bits */ - #define ADM1275_IOUT_WARN2_LIMIT 0xd7 #define ADM1275_DEVICE_CONFIG 0xd8 @@ -67,8 +56,6 @@ enum chips { adm1075, adm1275, adm1276 }; struct adm1275_data { int id; bool have_oc_fault; - u16 alert1, alert2; - u16 alert1_curr, alert2_curr; struct pmbus_driver_info info; }; @@ -230,65 +217,6 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) return ret; } -/* - * ADM1275 and ADM1276 keep generating SMBUS alerts for software-implemented - * alarms, even if there is no change in alarm status. This causes a lot of I2C - * bus traffic as well as "Duplicate alert" kernel log warnings. - * Since this is undesirable, disable generation of SMBus alerts for the - * affected alarm bits while any of the alarms is active. Re-enable SMBus alarm - * notifications if no further alarms are pending. - * This means that after the first warning alarm bit is set, subsequent alarm - * bits are only detected by the PMBus core polling function, which may result - * in slight reporting delays (up to 1 second) if another alarm is activated. - * This may not be optimal, but is still better than the overhead of having to - * handle continuous SMBus interrupts. - */ -static void adm1275_alert_handler(struct i2c_client *client, bool alarm) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct adm1275_data *data = to_adm1275_data(info); - int reg; - - if (data->id == adm1275 && (data->alert1 & ADM1275_SMBALERT_MASK) == 0) { - reg = data->alert1_curr; - if (alarm) { - reg &= ADM1275_ALERT_MASK; - if (reg != data->alert1_curr) { - i2c_smbus_write_word_data(client, - ADM1275_ALERT1_CONFIG, - reg); - data->alert1_curr = reg; - } - } else { - if (reg != data->alert1) { - i2c_smbus_write_word_data(client, - ADM1275_ALERT1_CONFIG, - data->alert1); - data->alert1_curr = data->alert1; - } - } - } - if ((data->alert2 & ADM1275_SMBALERT_MASK) == 0) { - reg = data->alert2_curr; - if (alarm) { - reg &= ADM1275_ALERT_MASK; - if (reg != data->alert2_curr) { - i2c_smbus_write_word_data(client, - ADM1275_ALERT2_CONFIG, - reg); - data->alert2_curr = reg; - } - } else { - if (reg != data->alert2) { - i2c_smbus_write_word_data(client, - ADM1275_ALERT2_CONFIG, - data->alert2); - data->alert2_curr = data->alert2; - } - } - } -} - static const struct i2c_device_id adm1275_id[] = { { "adm1075", adm1075 }, { "adm1275", adm1275 }, @@ -309,15 +237,9 @@ static int adm1275_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA - | I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data), - GFP_KERNEL); - if (!data) - 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"); @@ -355,19 +277,12 @@ static int adm1275_probe(struct i2c_client *client, if (device_config < 0) return device_config; - data->id = mid->driver_data; - if (data->id == adm1275) { - data->alert1 = i2c_smbus_read_word_data(client, - ADM1275_ALERT1_CONFIG); - if (data->alert1 < 0) - return data->alert1; - data->alert1_curr = data->alert1; - } + data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - data->alert2 = i2c_smbus_read_word_data(client, ADM1275_ALERT2_CONFIG); - if (data->alert2 < 0) - return data->alert2; - data->alert2_curr = data->alert2; + data->id = mid->driver_data; info = &data->info; @@ -383,7 +298,6 @@ static int adm1275_probe(struct i2c_client *client, info->read_word_data = adm1275_read_word_data; info->read_byte_data = adm1275_read_byte_data; info->write_word_data = adm1275_write_word_data; - info->alert_handler = adm1275_alert_handler; if (data->id == adm1075) { info->m[PSC_VOLTAGE_IN] = 27169; @@ -467,36 +381,17 @@ static int adm1275_probe(struct i2c_client *client, return pmbus_do_probe(client, id, info); } -static int adm1275_remove(struct i2c_client *client) -{ - pmbus_do_remove(client); - return 0; -} - static struct i2c_driver adm1275_driver = { .driver = { .name = "adm1275", }, .probe = adm1275_probe, - .remove = adm1275_remove, -#if defined(CONFIG_I2C_SMBUS) || defined(CONFIG_I2C_PMBUS_MODULE) - .alert = pmbus_do_alert, -#endif + .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 and compatibles"); MODULE_LICENSE("GPL"); -module_init(adm1275_init); -module_exit(adm1275_exit); diff --git a/lm25066.c b/lm25066.c index 5c0e124..c299392 100644 --- a/lm25066.c +++ b/lm25066.c @@ -314,18 +314,8 @@ static struct i2c_driver lm25066_driver = { .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_LICENSE("GPL"); -module_init(lm25066_init); -module_exit(lm25066_exit); diff --git a/ltc2978.c b/ltc2978.c index 1be8185..9652a2c 100644 --- a/ltc2978.c +++ b/ltc2978.c @@ -368,24 +368,11 @@ static struct i2c_driver ltc2978_driver = { }, .probe = ltc2978_probe, .remove = pmbus_do_remove, -#if defined(CONFIG_I2C_SMBUS) || defined(CONFIG_I2C_PMBUS_MODULE) - .alert = pmbus_do_alert, -#endif .id_table = ltc2978_id, }; -static int __init ltc2978_init(void) -{ - return i2c_add_driver(<c2978_driver); -} - -static void __exit ltc2978_exit(void) -{ - i2c_del_driver(<c2978_driver); -} +module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880"); MODULE_LICENSE("GPL"); -module_init(ltc2978_init); -module_exit(ltc2978_exit); diff --git a/max16064.c b/max16064.c index 90bca64..fa237a3 100644 --- a/max16064.c +++ b/max16064.c @@ -120,18 +120,8 @@ static struct i2c_driver max16064_driver = { .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/max34440.c b/max34440.c index 8d0448c..7e930c3 100644 --- a/max34440.c +++ b/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,7 +26,7 @@ #include #include "pmbus.h" -enum chips { max34440, max34441, max34446, max34460 }; +enum chips { max34440, max34441, max34446, max34460, max34461 }; #define MAX34440_MFR_VOUT_PEAK 0xd4 #define MAX34440_MFR_IOUT_PEAK 0xd5 @@ -87,7 +88,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg) MAX34446_MFR_POUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_AVG: - if (data->id != max34446 && data->id != max34460) + if (data->id != max34446 && data->id != max34460 && + data->id != max34461) return -ENXIO; ret = pmbus_read_word_data(client, page, MAX34446_MFR_TEMPERATURE_AVG); @@ -353,6 +355,42 @@ static struct pmbus_driver_info max34440_info[] = { .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, @@ -375,6 +413,7 @@ static const struct i2c_device_id max34440_id[] = { {"max34441", max34441}, {"max34446", max34446}, {"max34460", max34460}, + {"max34461", max34461}, {} }; MODULE_DEVICE_TABLE(i2c, max34440_id); @@ -389,18 +428,8 @@ static struct i2c_driver max34440_driver = { .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/max8688.c b/max8688.c index 72f0dfa..f04454a 100644 --- a/max8688.c +++ b/max8688.c @@ -197,18 +197,8 @@ static struct i2c_driver max8688_driver = { .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/pmbus.c b/pmbus.c index e358afd..7e91700 100644 --- a/pmbus.c +++ b/pmbus.c @@ -210,18 +210,8 @@ static struct i2c_driver pmbus_driver = { .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/pmbus.h b/pmbus.h index e189ac9..164d177 100644 --- a/pmbus.h +++ b/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 @@ -177,6 +178,13 @@ #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 */ @@ -317,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 }; @@ -332,7 +342,6 @@ struct pmbus_driver_info { int R[PSC_NUM_CLASSES]; /* exponent */ u32 func[PMBUS_PAGES]; /* Functionality, per page */ - u8 status_register; /* Set if not PMBUS_STATUS_BYTE */ /* * The following functions map manufacturing specific register values * to PMBus standard register values. Specify only if mapping is @@ -349,7 +358,6 @@ struct pmbus_driver_info { 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); - void (*alert_handler)(struct i2c_client *client, bool alarm); /* * The identify function determines supported PMBus functionality. * This function is only necessary if a chip driver supports multiple @@ -369,7 +377,6 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value); void pmbus_clear_faults(struct i2c_client *client); bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); -void pmbus_do_alert(struct i2c_client *client, unsigned int flag); int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info); int pmbus_do_remove(struct i2c_client *client); diff --git a/pmbus_core.c b/pmbus_core.c index a3433f3..c547f40 100644 --- a/pmbus_core.c +++ b/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,16 +27,14 @@ #include #include #include -#include -#include -#include +#include #include #include "pmbus.h" /* * Constants needed to determine number of sensors, booleans, and labels. */ -#define PMBUS_MAX_INPUT_SENSORS 22 /* 10*volt, 7*curr, 5*power */ +#define PMBUS_MAX_INPUT_SENSORS 27 /* 15*volt, 7*curr, 5*power */ #define PMBUS_VOUT_SENSORS_PER_PAGE 9 /* input, min, max, lcrit, crit, lowest, highest, avg, reset */ @@ -51,20 +50,24 @@ * 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_MAX_INPUT_BOOLEANS 11 /* v: min_alarm, max_alarm, + * lcrit_alarm, crit_alarm; + * vmon: 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_POUT_BOOLEANS_PER_PAGE 3 /* cap_alarm, 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 */ +#define PMBUS_MAX_INPUT_LABELS 5 /* vin, vcap, vmon, iin, pin */ /* * status, status_vout, status_iout, status_fans, status_fan34, and status_temp @@ -81,7 +84,8 @@ #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_VMON_BASE (PB_STATUS_INPUT_BASE + 1) +#define PB_STATUS_TEMP_BASE (PB_STATUS_VMON_BASE + 1) #define PMBUS_NAME_SIZE 24 @@ -98,12 +102,9 @@ struct pmbus_sensor { struct pmbus_boolean { char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ - int previous; /* previously reported value */ struct sensor_device_attribute attribute; }; -#define to_pmbus_boolean(a) container_of(a, struct pmbus_boolean, attribute) - struct pmbus_label { char name[PMBUS_NAME_SIZE]; /* sysfs label name */ struct sensor_device_attribute attribute; @@ -154,14 +155,9 @@ struct pmbus_data { * so we keep them all together. */ u8 status[PB_NUM_STATUS_REG]; + u8 status_register; u8 currpage; - - u8 status_register; - bool alarm; /* true if there are active alarms */ - struct task_struct *alert_thread; - struct completion alert_thread_stop; - struct mutex alert_lock; }; int pmbus_set_page(struct i2c_client *client, u8 page) @@ -428,6 +424,11 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) = _pmbus_read_byte_data(client, 0, PMBUS_STATUS_INPUT); + 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 (i = 0; i < data->num_sensors; i++) { struct pmbus_sensor *sensor = &data->sensors[i]; @@ -668,7 +669,7 @@ 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) { - val = SENSORS_LIMIT(val, 500, 1600); + val = clamp_val(val, 500, 1600); return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); } @@ -721,13 +722,13 @@ 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, int index) { u8 s1 = (index >> 24) & 0xff; u8 s2 = (index >> 16) & 0xff; u8 reg = (index >> 8) & 0xff; u8 mask = index & 0xff; - int status; + int ret, status; u8 regval; status = data->status[reg]; @@ -736,7 +737,7 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) regval = status & mask; if (!s1 && !s2) - *val = !!regval; + ret = !!regval; else { long v1, v2; struct pmbus_sensor *sensor1, *sensor2; @@ -750,9 +751,9 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) v1 = pmbus_reg2data(data, sensor1); v2 = pmbus_reg2data(data, sensor2); - *val = !!(regval && v1 >= v2); + ret = !!(regval && v1 >= v2); } - return 0; + return ret; } static ssize_t pmbus_show_boolean(struct device *dev, @@ -760,15 +761,11 @@ static ssize_t pmbus_show_boolean(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct pmbus_data *data = pmbus_update_device(dev); - struct pmbus_boolean *bool; int val; - int err; - err = pmbus_get_boolean(data, attr->index, &val); - if (err) - return err; - bool = to_pmbus_boolean(attr); - bool->previous = val; + val = pmbus_get_boolean(data, attr->index); + if (val < 0) + return val; return snprintf(buf, PAGE_SIZE, "%d\n", val); } @@ -799,7 +796,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, int ret; u16 regval; - if (strict_strtol(buf, 10, &val) < 0) + if (kstrtol(buf, 10, &val) < 0) return -EINVAL; mutex_lock(&data->update_lock); @@ -824,10 +821,6 @@ static ssize_t pmbus_show_label(struct device *dev, data->labels[attr->index].label); } -#ifndef sysfs_attr_init -#define sysfs_attr_init(attr) do {} while(0) -#endif - #define PMBUS_ADD_ATTR(data, _name, _idx, _mode, _type, _show, _set) \ do { \ struct sensor_device_attribute *a \ @@ -1007,7 +1000,7 @@ 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 */ enum pmbus_sensor_classes class;/* sensor class */ const char *label; /* sensor label */ bool paged; /* true if paged sensor */ @@ -1160,6 +1153,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, @@ -1210,6 +1227,15 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .gbit = PB_STATUS_VIN_UV, .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, @@ -1698,93 +1724,49 @@ static int pmbus_identify_common(struct i2c_client *client, return 0; } -static int pmbus_alert_thread(void *p); - -static void _pmbus_do_alert(struct i2c_client *client, bool clear) +static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + struct pmbus_driver_info *info) { - struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - int i; - bool update = false; - bool alarm = false; + int ret; /* - * Update sensor data only if this is an alert callback, or if the data - * is older than 200 ms. Otherwise we rather skip the update, since - * clearing faults (which is done as part of the update and is needed - * for handling alerts) may temporarily clear alarm status bits even if - * an alarm is still active. - * Selecting 200 ms is a more or less arbitrary value. The value needs - * to be be longer than the slowest imaginable status update cycle on - * any PMBus device, yet small enough to capture state changes - * reasonably fast. + * 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. */ - if (clear || !data->valid - || time_after(jiffies, data->last_updated + HZ/5)) { - data->valid = 0; - pmbus_update_device(&client->dev); - } - - for (i = 0; inum_booleans; i++) { - struct pmbus_boolean *bool = &data->booleans[i]; - struct sensor_device_attribute *attr = &bool->attribute; - int err, val; - - err = pmbus_get_boolean(data, attr->index, &val); - if (!err) { - if (val != bool->previous) { - // FIXME - remove for submission - dev_info(&client->dev, "%s: %d->%d\n", - bool->name, bool->previous, val); - bool->previous = val; - sysfs_notify(&client->dev.kobj, NULL, - bool->name); - update = true; - } - alarm = alarm || val; + 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(&client->dev, + "PMBus status register not found\n"); + return -ENODEV; } } - if (update) - kobject_uevent(&client->dev.kobj, KOBJ_CHANGE); - if (data->alarm != alarm && info->alert_handler) - info->alert_handler(client, alarm); - data->alarm = alarm; - if (alarm && data->alert_thread == NULL) { - init_completion(&data->alert_thread_stop); - data->alert_thread = kthread_run(pmbus_alert_thread, client, - dev_name(data->hwmon_dev)); - if (IS_ERR(data->alert_thread)) - data->alert_thread = NULL; - } -} -void pmbus_do_alert(struct i2c_client *client, unsigned int flag) -{ - struct pmbus_data *data = i2c_get_clientdata(client); + pmbus_clear_faults(client); - mutex_lock(&data->alert_lock); - _pmbus_do_alert(client, true); - mutex_unlock(&data->alert_lock); -} -EXPORT_SYMBOL(pmbus_do_alert); + if (info->identify) { + ret = (*info->identify)(client, info); + if (ret < 0) { + dev_err(&client->dev, "Chip identification failed\n"); + return ret; + } + } -static int pmbus_alert_thread(void *p) -{ - struct i2c_client *client = p; - struct pmbus_data *data = i2c_get_clientdata(client); + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { + dev_err(&client->dev, "Bad number of PMBus pages: %d\n", + info->pages); + return -ENODEV; + } - mutex_lock(&data->alert_lock); - while (!kthread_should_stop() && data->alarm) { - _pmbus_do_alert(client, false); - if (!data->alarm || kthread_should_stop()) - break; - mutex_unlock(&data->alert_lock); - msleep_interruptible(MSEC_PER_SEC); - mutex_lock(&data->alert_lock); + ret = pmbus_identify_common(client, data); + if (ret < 0) { + dev_err(&client->dev, "Failed to identify chip capabilities\n"); + return ret; } - data->alert_thread = NULL; - complete_all(&data->alert_thread_stop); - mutex_unlock(&data->alert_lock); return 0; } @@ -1795,10 +1777,8 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, 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 @@ -1806,77 +1786,39 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, return -ENODEV; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "No memory to allocate driver data\n"); + if (!data) return -ENOMEM; - } i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - mutex_init(&data->alert_lock); - - data->status_register = info->status_register ? : PMBUS_STATUS_BYTE; - - /* Bail out if PMBus status register does not exist. */ - if (i2c_smbus_read_byte_data(client, data->status_register) < 0) { - dev_err(&client->dev, "PMBus status register not found\n"); - return -ENODEV; - } 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"); - return ret; - } - } - - if (info->pages <= 0 || info->pages > PMBUS_PAGES) { - dev_err(&client->dev, "Bad number of PMBus pages: %d\n", - info->pages); - return -ENODEV; - } - - ret = pmbus_identify_common(client, data); - if (ret < 0) { - dev_err(&client->dev, "Failed to identify chip capabilities\n"); + ret = pmbus_init_common(client, data, info); + if (ret < 0) return ret; - } - ret = -ENOMEM; data->sensors = devm_kzalloc(&client->dev, sizeof(struct pmbus_sensor) * data->max_sensors, GFP_KERNEL); - if (!data->sensors) { - dev_err(&client->dev, "No memory to allocate sensor data\n"); + if (!data->sensors) return -ENOMEM; - } data->booleans = devm_kzalloc(&client->dev, sizeof(struct pmbus_boolean) * data->max_booleans, GFP_KERNEL); - if (!data->booleans) { - dev_err(&client->dev, "No memory to allocate boolean data\n"); + if (!data->booleans) return -ENOMEM; - } data->labels = devm_kzalloc(&client->dev, sizeof(struct pmbus_label) * data->max_labels, GFP_KERNEL); - if (!data->labels) { - dev_err(&client->dev, "No memory to allocate label data\n"); + if (!data->labels) return -ENOMEM; - } data->attributes = devm_kzalloc(&client->dev, sizeof(struct attribute *) * data->max_attributes, GFP_KERNEL); - if (!data->attributes) { - dev_err(&client->dev, "No memory to allocate attribute data\n"); + if (!data->attributes) return -ENOMEM; - } pmbus_find_attributes(client, data); @@ -1913,16 +1855,6 @@ EXPORT_SYMBOL_GPL(pmbus_do_probe); int pmbus_do_remove(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->alert_lock); - if (data->alert_thread) { - kthread_stop(data->alert_thread); - mutex_unlock(&data->alert_lock); - wait_for_completion(&data->alert_thread_stop); - } else { - mutex_unlock(&data->alert_lock); - } - hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &data->group); return 0; diff --git a/ucd9000.c b/ucd9000.c index 77344e9..fbb1479 100644 --- a/ucd9000.c +++ b/ucd9000.c @@ -239,18 +239,8 @@ static struct i2c_driver ucd9000_driver = { .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/ucd9200.c b/ucd9200.c index 7dda5cc..033d6ac 100644 --- a/ucd9200.c +++ b/ucd9200.c @@ -173,18 +173,8 @@ static struct i2c_driver ucd9200_driver = { .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/zl6100.c b/zl6100.c index d150597..bf2ce29 100644 --- a/zl6100.c +++ b/zl6100.c @@ -2,6 +2,7 @@ * 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 @@ -56,8 +57,6 @@ struct zl6100_data { #define ZL6100_WAIT_TIME 1000 /* uS */ -#define ZL6100_VOUT_EXPONENT -13 - static ushort delay = ZL6100_WAIT_TIME; module_param(delay, ushort, 0644); MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); @@ -72,37 +71,13 @@ static inline void zl6100_wait(const struct zl6100_data *data) } } -static int zl6100_lin11_to_lin16(int reg) -{ - int mantissa = ((s16)((reg & 0x7ff) << 5)) >> 5; - int shift = ZL6100_VOUT_EXPONENT - (((s16)reg) >> 11); - - if (shift < 0) - mantissa <<= -shift; - else if (shift) - mantissa >>= shift; - - return mantissa & 0xffff; -} - -static u16 zl6100_lin16_to_lin11(u16 reg) -{ - int exp = ZL6100_VOUT_EXPONENT; - - while (reg > 0x3ff) { - exp++; - reg >>= 1; - } - return reg | (exp << 11); -} - 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; - if (page > info->pages || reg >= PMBUS_VIRT_BASE) + if (page > 0) return -ENXIO; if (data->id == zl2005) { @@ -118,31 +93,26 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) } } - zl6100_wait(data); - - if (page == 1) { - page = 0; - switch (reg) { - case PMBUS_READ_VOUT: - reg = MFR_READ_VMON; - break; - case PMBUS_VOUT_OV_FAULT_LIMIT: - reg = MFR_VMON_OV_FAULT_LIMIT; - break; - case PMBUS_VOUT_UV_FAULT_LIMIT: - reg = MFR_VMON_UV_FAULT_LIMIT; - break; - default: - /* No other valid registers on page 1 */ + switch (reg) { + case PMBUS_VIRT_READ_VMON: + reg = MFR_READ_VMON; + break; + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + reg = MFR_VMON_OV_FAULT_LIMIT; + break; + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + reg = MFR_VMON_UV_FAULT_LIMIT; + break; + default: + if (reg >= PMBUS_VIRT_BASE) return -ENXIO; - } - ret = pmbus_read_word_data(client, page, reg); - if (ret > 0) - ret = zl6100_lin11_to_lin16(ret); - } else { - ret = pmbus_read_word_data(client, page, reg); + break; } + + zl6100_wait(data); + ret = pmbus_read_word_data(client, page, reg); data->access = ktime_get(); + return ret; } @@ -152,38 +122,35 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) struct zl6100_data *data = to_zl6100_data(info); int ret, status; - if (page > info->pages) + if (page > 0) return -ENXIO; zl6100_wait(data); - if (page == 1) { - switch (reg) { - case PMBUS_STATUS_VOUT: - 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; + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) break; - default: - /* No other valid registers on page 1 */ - return -ENXIO; - } - } else { + + 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; } @@ -194,23 +161,19 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, struct zl6100_data *data = to_zl6100_data(info); int ret; - if (page > info->pages || reg >= PMBUS_VIRT_BASE) + if (page > 0) return -ENXIO; - if (page == 1) { - page = 0; - switch (reg) { - case PMBUS_VOUT_OV_FAULT_LIMIT: - reg = MFR_VMON_OV_FAULT_LIMIT; - break; - case PMBUS_VOUT_UV_FAULT_LIMIT: - reg = MFR_VMON_UV_FAULT_LIMIT; - break; - default: - /* No other valid registers on page 1 */ + switch (reg) { + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + reg = MFR_VMON_OV_FAULT_LIMIT; + break; + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + reg = MFR_VMON_UV_FAULT_LIMIT; + break; + default: + if (reg >= PMBUS_VIRT_BASE) return -ENXIO; - } - word = zl6100_lin16_to_lin11(word); } zl6100_wait(data); @@ -224,16 +187,14 @@ 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 = 0; + int ret; - if (page > info->pages) + if (page > 0) return -ENXIO; - if (page <= 0) { - zl6100_wait(data); - ret = pmbus_write_byte(client, page, value); - data->access = ktime_get(); - } + zl6100_wait(data); + ret = pmbus_write_byte(client, page, value); + data->access = ktime_get(); return ret; } @@ -303,17 +264,11 @@ static int zl6100_probe(struct i2c_client *client, data->id = mid->driver_data; /* - * ZL2005, ZL2008, ZL2105, ZL6100, ZL9101M, and ZL9117M are known to - * require a wait time between I2C accesses. ZL2004 and ZL6105 are known - * to be safe. Other chips have not yet been tested. - * - * Only clear the wait time for chips known to be safe. The wait time - * can be cleared later for additional chips if tests show that it - * is not needed (in other words, better be safe than sorry). + * According to information from the chip vendor, all currently + * supported chips are known to require a wait time between I2C + * accesses. */ data->delay = delay; - if (data->id == zl2004 || data->id == zl6105) - data->delay = 0; /* * Since there was a direct I2C device access above, wait before @@ -332,13 +287,10 @@ static int zl6100_probe(struct i2c_client *client, /* * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage - * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as output - * voltage on the second page (vout2). + * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. */ - if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) { - info->pages = 2; - info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; - } + 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) @@ -367,18 +319,8 @@ static struct i2c_driver zl6100_driver = { .id_table = zl6100_id, }; -static int __init zl6100_init(void) -{ - return i2c_add_driver(&zl6100_driver); -} - -static void __exit zl6100_exit(void) -{ - i2c_del_driver(&zl6100_driver); -} +module_i2c_driver(zl6100_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); MODULE_LICENSE("GPL"); -module_init(zl6100_init); -module_exit(zl6100_exit);