From 628b4504cf69fbe262b41c16a4a3fd61b091d61c Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Thu, 28 Oct 2010 20:31:42 +0200 Subject: [PATCH 01/68] hwmon: (k8temp) Remove superfluous CPU family check The family check in k8temp is not required because the driver is already bound to a northbridge device only used with K8 CPUs. Signed-off-by: Andreas Herrmann Signed-off-by: Jean Delvare --- drivers/hwmon/k8temp.c | 53 ++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 39ead2a4d3c5..418496f13020 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -191,38 +191,31 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, model = boot_cpu_data.x86_model; stepping = boot_cpu_data.x86_mask; - switch (boot_cpu_data.x86) { - case 0xf: - /* feature available since SH-C0, exclude older revisions */ - if (((model == 4) && (stepping == 0)) || - ((model == 5) && (stepping <= 1))) { - err = -ENODEV; - goto exit_free; - } - - /* - * AMD NPT family 0fh, i.e. RevF and RevG: - * meaning of SEL_CORE bit is inverted - */ - if (model >= 0x40) { - data->swap_core_select = 1; - dev_warn(&pdev->dev, "Temperature readouts might be " - "wrong - check erratum #141\n"); - } - - if (is_rev_g_desktop(model)) { - /* - * RevG desktop CPUs (i.e. no socket S1G1 or - * ASB1 parts) need additional offset, - * otherwise reported temperature is below - * ambient temperature - */ - data->temp_offset = 21000; - } - - break; + /* feature available since SH-C0, exclude older revisions */ + if (((model == 4) && (stepping == 0)) || + ((model == 5) && (stepping <= 1))) { + err = -ENODEV; + goto exit_free; } + /* + * AMD NPT family 0fh, i.e. RevF and RevG: + * meaning of SEL_CORE bit is inverted + */ + if (model >= 0x40) { + data->swap_core_select = 1; + dev_warn(&pdev->dev, "Temperature readouts might be wrong - " + "check erratum #141\n"); + } + + /* + * RevG desktop CPUs (i.e. no socket S1G1 or ASB1 parts) need + * additional offset, otherwise reported temperature is below + * ambient temperature + */ + if (is_rev_g_desktop(model)) + data->temp_offset = 21000; + pci_read_config_byte(pdev, REG_TEMP, &scfg); scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ pci_write_config_byte(pdev, REG_TEMP, scfg); From 28e7438faacbd47aaf3e3b43c3074c64be98f5be Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:42 +0200 Subject: [PATCH 02/68] hwmon: Add tempX_emergency attribute to sysfs ABI Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 48ceabedf55d..645699010551 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -309,6 +309,20 @@ temp[1-*]_crit_hyst from the critical value. RW +temp[1-*]_emergency + Temperature emergency max value, for chips supporting more than + two upper temperature limits. Must be equal or greater than + corresponding temp_crit values. + Unit: millidegree Celsius + RW + +temp[1-*]_emergency_hyst + Temperature hysteresis value for emergency limit. + Unit: millidegree Celsius + Must be reported as an absolute temperature, NOT a delta + from the emergency value. + RW + temp[1-*]_lcrit Temperature critical min value, typically lower than corresponding temp_min values. Unit: millidegree Celsius @@ -505,6 +519,7 @@ fan[1-*]_max_alarm temp[1-*]_min_alarm temp[1-*]_max_alarm temp[1-*]_crit_alarm +temp[1-*]_emergency_alarm Limit alarm 0: no alarm 1: alarm From 11e578129af74c4866cf559e62e981c6415fffd9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:42 +0200 Subject: [PATCH 03/68] hwmon: (lm90) Fix checkpatch errors Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 76 +++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 760ef72eea56..aafed2898d95 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -387,8 +387,13 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); int nr = attr->index; + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; /* +16 degrees offset for temp2 for the LM99 */ if (data->kind == lm99 && attr->index == 3) @@ -442,8 +447,13 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); int nr = attr->index; + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; /* +16 degrees offset for temp2 for the LM99 */ if (data->kind == lm99 && attr->index <= 2) @@ -469,7 +479,8 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, return count; } -static ssize_t show_temphyst(struct device *dev, struct device_attribute *devattr, +static ssize_t show_temphyst(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -495,9 +506,14 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, { struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; int temp; + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; + mutex_lock(&data->update_lock); if (data->kind == adt7461) temp = temp_from_u8_adt7461(data, data->temp8[2]); @@ -600,7 +616,12 @@ static ssize_t set_pec(struct device *dev, struct device_attribute *dummy, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; switch (val) { case 0: @@ -622,8 +643,10 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); * Real code */ -/* The ADM1032 supports PEC but not on write byte transactions, so we need - to explicitly ask for a transaction without PEC. */ +/* + * The ADM1032 supports PEC but not on write byte transactions, so we need + * to explicitly ask for a transaction without PEC. + */ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) { return i2c_smbus_xfer(client->adapter, client->addr, @@ -631,20 +654,22 @@ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); } -/* It is assumed that client->update_lock is held (unless we are in - detection or initialization steps). This matters when PEC is enabled, - because we don't want the address pointer to change between the write - byte and the read byte transactions. */ -static int lm90_read_reg(struct i2c_client* client, u8 reg, u8 *value) +/* + * It is assumed that client->update_lock is held (unless we are in + * detection or initialization steps). This matters when PEC is enabled, + * because we don't want the address pointer to change between the write + * byte and the read byte transactions. + */ +static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value) { int err; - if (client->flags & I2C_CLIENT_PEC) { - err = adm1032_write_byte(client, reg); - if (err >= 0) - err = i2c_smbus_read_byte(client); - } else - err = i2c_smbus_read_byte_data(client, reg); + if (client->flags & I2C_CLIENT_PEC) { + err = adm1032_write_byte(client, reg); + if (err >= 0) + err = i2c_smbus_read_byte(client); + } else + err = i2c_smbus_read_byte_data(client, reg); if (err < 0) { dev_warn(&client->dev, "Register %#02x read failed (%d)\n", @@ -826,16 +851,18 @@ static int lm90_probe(struct i2c_client *new_client, lm90_init_client(new_client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group))) + err = sysfs_create_group(&new_client->dev.kobj, &lm90_group); + if (err) goto exit_free; if (new_client->flags & I2C_CLIENT_PEC) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_pec))) + err = device_create_file(&new_client->dev, &dev_attr_pec); + if (err) goto exit_remove_files; } if (data->kind != max6657 && data->kind != max6646) { - if ((err = device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_offset.dev_attr))) + err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_offset.dev_attr); + if (err) goto exit_remove_files; } @@ -883,9 +910,8 @@ static void lm90_init_client(struct i2c_client *client) * 0.125 degree resolution) and range (0x08, extend range * to -64 degree) mode for the remote temperature sensor. */ - if (data->kind == max6680) { + if (data->kind == max6680) config |= 0x18; - } config &= 0xBF; /* run */ if (config != data->config_orig) /* Only write if changed */ From 88073bb1ba969d4e3c41dc5f35c902c6b7dab0a7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 04/68] hwmon: (lm90) Introduce device feature bits Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index aafed2898d95..f628f2ae7e73 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -142,7 +142,11 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646, /* * Device flags */ -#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ +#define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */ +/* Device features */ +#define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ +#define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ +#define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ /* * Functions declaration @@ -462,17 +466,16 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) data->temp11[nr] = temp_to_u16_adt7461(data, val); - else if (data->kind == max6657 || data->kind == max6680) - data->temp11[nr] = temp_to_s8(val) << 8; else if (data->kind == max6646) data->temp11[nr] = temp_to_u8(val) << 8; - else + else if (data->flags & LM90_HAVE_REM_LIMIT_EXT) data->temp11[nr] = temp_to_s16(val); + else + data->temp11[nr] = temp_to_s8(val) << 8; i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - if (data->kind != max6657 && data->kind != max6680 - && data->kind != max6646) + if (data->flags & LM90_HAVE_REM_LIMIT_EXT) i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); @@ -847,6 +850,17 @@ static int lm90_probe(struct i2c_client *new_client, break; } + /* Set chip capabilities */ + if (data->kind != max6657 && data->kind != max6646) + data->flags |= LM90_HAVE_OFFSET; + + if (data->kind == max6657 || data->kind == max6646) + data->flags |= LM90_HAVE_LOCAL_EXT; + + if (data->kind != max6657 && data->kind != max6646 + && data->kind != max6680) + data->flags |= LM90_HAVE_REM_LIMIT_EXT; + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -859,7 +873,7 @@ static int lm90_probe(struct i2c_client *new_client, if (err) goto exit_remove_files; } - if (data->kind != max6657 && data->kind != max6646) { + if (data->flags & LM90_HAVE_OFFSET) { err = device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr); if (err) @@ -925,7 +939,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm90_group); device_remove_file(&client->dev, &dev_attr_pec); - if (data->kind != max6657 && data->kind != max6646) + if (data->flags & LM90_HAVE_OFFSET) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -1019,7 +1033,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - if (data->kind == max6657 || data->kind == max6646) { + if (data->flags & LM90_HAVE_LOCAL_EXT) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, MAX6657_REG_R_LOCAL_TEMPL, &data->temp11[4]); @@ -1033,22 +1047,20 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { data->temp11[1] = h << 8; - if (data->kind != max6657 && data->kind != max6680 - && data->kind != max6646 + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) data->temp11[1] |= l; } if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { data->temp11[2] = h << 8; - if (data->kind != max6657 && data->kind != max6680 - && data->kind != max6646 + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) data->temp11[2] |= l; } - if (data->kind != max6657 && data->kind != max6646) { + if (data->flags & LM90_HAVE_OFFSET) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, From b6fc1bacc7eae99d276d096fe0c702b1e13e4499 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 05/68] hwmon: (lm90) Introduce function to delete sysfs files Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index f628f2ae7e73..9b1d20515eba 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -815,6 +815,15 @@ static int lm90_detect(struct i2c_client *new_client, return 0; } +static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) +{ + if (data->flags & LM90_HAVE_OFFSET) + device_remove_file(&client->dev, + &sensor_dev_attr_temp2_offset.dev_attr); + device_remove_file(&client->dev, &dev_attr_pec); + sysfs_remove_group(&client->dev.kobj, &lm90_group); +} + static int lm90_probe(struct i2c_client *new_client, const struct i2c_device_id *id) { @@ -889,8 +898,7 @@ static int lm90_probe(struct i2c_client *new_client, return 0; exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm90_group); - device_remove_file(&new_client->dev, &dev_attr_pec); + lm90_remove_files(new_client, data); exit_free: kfree(data); exit: @@ -937,11 +945,7 @@ static int lm90_remove(struct i2c_client *client) struct lm90_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm90_group); - device_remove_file(&client->dev, &dev_attr_pec); - if (data->flags & LM90_HAVE_OFFSET) - device_remove_file(&client->dev, - &sensor_dev_attr_temp2_offset.dev_attr); + lm90_remove_files(client, data); /* Restore initial configuration */ i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, From 96512861c3733609ac3d558602574674fa95ebf4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 06/68] hwmon: (lm90) Simplify set_temp11 register calculations Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 55 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 9b1d20515eba..e7ef9657ae17 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -418,7 +418,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct lm90_data *data = lm90_update_device(dev); int temp; @@ -439,19 +439,20 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[6] = { - LM90_REG_W_REMOTE_LOWH, - LM90_REG_W_REMOTE_LOWL, - LM90_REG_W_REMOTE_HIGHH, - LM90_REG_W_REMOTE_HIGHL, - LM90_REG_W_REMOTE_OFFSH, - LM90_REG_W_REMOTE_OFFSL, + struct { + u8 high; + u8 low; + } reg[3] = { + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, + { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL } }; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - int nr = attr->index; + int nr = attr->nr; + int index = attr->index; long val; int err; @@ -460,24 +461,24 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, return err; /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && attr->index <= 2) + if (data->kind == lm99 && index <= 2) val -= 16000; mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp_to_u16_adt7461(data, val); + data->temp11[index] = temp_to_u16_adt7461(data, val); else if (data->kind == max6646) - data->temp11[nr] = temp_to_u8(val) << 8; + data->temp11[index] = temp_to_u8(val) << 8; else if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - data->temp11[nr] = temp_to_s16(val); + data->temp11[index] = temp_to_s16(val); else - data->temp11[nr] = temp_to_s8(val) << 8; + data->temp11[index] = temp_to_s8(val) << 8; - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], - data->temp11[nr] >> 8); + i2c_smbus_write_byte_data(client, reg[nr].high, + data->temp11[index] >> 8); if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); + i2c_smbus_write_byte_data(client, reg[nr].low, + data->temp11[index] & 0xff); mutex_unlock(&data->update_lock); return count; } @@ -549,16 +550,16 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, 0, 4); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, 0, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, set_temp8, 0); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 1); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 0, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, set_temp8, 1); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 2); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 1, 2); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, set_temp8, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, @@ -566,8 +567,8 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, set_temphyst, 2); static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); -static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 3); +static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 2, 3); /* Individual alarm files */ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); From 13c84951a3d75ba820adf47eb2a3b1c5ab1fa635 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 07/68] hwmon: (lm90) Add explicit support for max6659 Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 4 ++-- drivers/hwmon/lm90.c | 32 +++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 6a03dd4bcc94..d5ce9f4ddd66 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -63,8 +63,8 @@ Supported chips: Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 * Maxim MAX6659 - Prefix: 'max6657' - Addresses scanned: I2C 0x4c, 0x4d (unsupported 0x4e) + Prefix: 'max6659' + Addresses scanned: I2C 0x4c, 0x4d, 0x4e Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 * Maxim MAX6680 diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e7ef9657ae17..68ee8f78843e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -85,7 +85,7 @@ * and MAX6658 have address 0x4c. * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. * MAX6647 has address 0x4e. - * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). + * MAX6659 can have address 0x4c, 0x4d or 0x4e. * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. */ @@ -93,8 +93,8 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646, - w83l771 }; +enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, + max6646, w83l771 }; /* * The LM90 registers @@ -176,7 +176,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, - { "max6659", max6657 }, + { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, { "w83l771", w83l771 }, @@ -220,7 +220,7 @@ struct lm90_data { s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6646 and max6657) + 3: remote offset (except max6646 and max6657/58/59) 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ @@ -766,12 +766,20 @@ static int lm90_detect(struct i2c_client *new_client, * register. Likewise, the config1 register seems to lack a * low nibble, so the value will be those of the previous * read, so in our case those of the man_id register. + * MAX6659 has a third set of upper temperature limit registers. + * Those registers also return values on MAX6657 and MAX6658, + * thus the only way to detect MAX6659 is by its address. + * For this reason it will be mis-detected as MAX6657 if its + * address is 0x4C. */ if (chip_id == man_id - && (address == 0x4C || address == 0x4D) + && (address == 0x4C || address == 0x4D || address == 0x4E) && (reg_config1 & 0x1F) == (man_id & 0x0F) && reg_convrate <= 0x09) { - name = "max6657"; + if (address == 0x4C) + name = "max6657"; + else + name = "max6659"; } else /* * The chip_id register of the MAX6680 and MAX6681 holds the @@ -861,14 +869,16 @@ static int lm90_probe(struct i2c_client *new_client, } /* Set chip capabilities */ - if (data->kind != max6657 && data->kind != max6646) + if (data->kind != max6657 && data->kind != max6659 + && data->kind != max6646) data->flags |= LM90_HAVE_OFFSET; - if (data->kind == max6657 || data->kind == max6646) + if (data->kind == max6657 || data->kind == max6659 + || data->kind == max6646) data->flags |= LM90_HAVE_LOCAL_EXT; - if (data->kind != max6657 && data->kind != max6646 - && data->kind != max6680) + if (data->kind != max6657 && data->kind != max6659 + && data->kind != max6646 && data->kind != max6680) data->flags |= LM90_HAVE_REM_LIMIT_EXT; /* Initialize the LM90 chip */ From 6948708dd07573c578aa99f80915cd1867334abe Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 08/68] hwmon: (lm90) Add support for extra features of max6659 Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 9 +++--- drivers/hwmon/lm90.c | 65 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index d5ce9f4ddd66..bc2c2b4e0d53 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -101,10 +101,11 @@ well as the temperature of up to one external diode. It is compatible with many other devices, many of which are supported by this driver. Note that there is no easy way to differentiate between the MAX6657, -MAX6658 and MAX6659 variants. The extra address and features of the -MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only -differ in their pinout, therefore they obviously can't (and don't need to) -be distinguished. +MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only +supported by this driver if the chip is located at address 0x4d or 0x4e, +or if the chip type is explicitly selected as max6659. +The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously +can't (and don't need to) be distinguished. The specificity of this family of chipsets over the ADM1021/LM84 family is that it features critical limits with hysteresis, and an diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 68ee8f78843e..de544817d673 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -28,9 +28,11 @@ * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three - * variants. The extra address and features of the MAX6659 are not - * supported by this driver. These chips lack the remote temperature - * offset feature. + * variants. We use the device address to detect MAX6659, which will result + * in a detection as max6657 if it is on address 0x4c. The extra address + * and features of the MAX6659 are only supported if the chip is configured + * explicitly as max6659, or if its address is not 0x4c. + * These chips lack the remote temperature offset feature. * * This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and * MAX6692 chips made by Maxim. These are again similar to the LM86, @@ -138,6 +140,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, /* MAX6646/6647/6649/6657/6658/6659 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +#define MAX6659_REG_R_REMOTE_EMERG 0x16 +#define MAX6659_REG_W_REMOTE_EMERG 0x16 +#define MAX6659_REG_R_LOCAL_EMERG 0x17 +#define MAX6659_REG_W_LOCAL_EMERG 0x17 /* * Device flags @@ -147,6 +153,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ #define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ #define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ +#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ /* * Functions declaration @@ -213,10 +220,12 @@ struct lm90_data { u8 alert_alarms; /* Which alarm bits trigger ALERT# */ /* registers values */ - s8 temp8[4]; /* 0: local low limit + s8 temp8[6]; /* 0: local low limit 1: local high limit 2: local critical limit - 3: remote critical limit */ + 3: remote critical limit + 4: local emergency limit (max6659 only) + 5: remote emergency limit (max6659 only) */ s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit @@ -381,11 +390,13 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[4] = { + static const u8 reg[6] = { LM90_REG_W_LOCAL_LOW, LM90_REG_W_LOCAL_HIGH, LM90_REG_W_LOCAL_CRIT, LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_LOCAL_EMERG, + MAX6659_REG_W_REMOTE_EMERG, }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -608,6 +619,30 @@ static const struct attribute_group lm90_group = { .attrs = lm90_attributes, }; +/* + * Additional attributes for devices with emergency sensors + */ +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 4); +static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 5); +static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 4); +static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 5); + +static struct attribute *lm90_emergency_attributes[] = { + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + &sensor_dev_attr_temp2_emergency.dev_attr.attr, + &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_emergency_group = { + .attrs = lm90_emergency_attributes, +}; + /* pec used for ADM1032 only */ static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, char *buf) @@ -826,6 +861,9 @@ static int lm90_detect(struct i2c_client *new_client, static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) { + if (data->flags & LM90_HAVE_EMERGENCY) + sysfs_remove_group(&client->dev.kobj, + &lm90_emergency_group); if (data->flags & LM90_HAVE_OFFSET) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -881,6 +919,9 @@ static int lm90_probe(struct i2c_client *new_client, && data->kind != max6646 && data->kind != max6680) data->flags |= LM90_HAVE_REM_LIMIT_EXT; + if (data->kind == max6659) + data->flags |= LM90_HAVE_EMERGENCY; + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -899,6 +940,12 @@ static int lm90_probe(struct i2c_client *new_client, if (err) goto exit_remove_files; } + if (data->flags & LM90_HAVE_EMERGENCY) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_emergency_group); + if (err) + goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1082,6 +1129,12 @@ static struct lm90_data *lm90_update_device(struct device *dev) &l) == 0) data->temp11[3] = (h << 8) | l; } + if (data->flags & LM90_HAVE_EMERGENCY) { + lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, + &data->temp8[4]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[5]); + } lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); /* Re-enable ALERT# output if it was originally enabled and From 06e1c0a2167d48442d0bd06373390886670aa6e5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 09/68] hwmon: (lm90) Add support for max6695 and max6696 Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 17 +++ drivers/hwmon/Kconfig | 4 +- drivers/hwmon/lm90.c | 250 +++++++++++++++++++++++++++++++++++---- 3 files changed, 247 insertions(+), 24 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index bc2c2b4e0d53..6e963b696d85 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -84,6 +84,17 @@ Supported chips: Addresses scanned: I2C 0x4c Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500 + * Maxim MAX6695 + Prefix: 'max6695' + Addresses scanned: I2C 0x18 + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/datasheet/index.mvp/id/4199 + * Maxim MAX6696 + Prefix: 'max6695' + Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, + 0x4c, 0x4d and 0x4e + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/datasheet/index.mvp/id/4199 * Winbond/Nuvoton W83L771AWG/ASG Prefix: 'w83l771' Addresses scanned: I2C 0x4c @@ -152,6 +163,12 @@ MAX6680 and MAX6681: * Selectable address * Remote sensor type selection +MAX6695 and MAX6696: + * Better local resolution + * Selectable address (max6696) + * Second critical temperature limit + * Two remote sensors + W83L771AWG/ASG * The AWG and ASG variants only differ in package format. * Filter and alert configuration register at 0xBF diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c357c835eb1e..9a3742b67c8c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -614,8 +614,8 @@ config SENSORS_LM90 If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, - MAX6680, MAX6681 and MAX6692, and Winbond/Nuvoton W83L771AWG/ASG - sensor chips. + MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton + W83L771AWG/ASG sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index de544817d673..366bb624e655 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -44,6 +44,11 @@ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can * be treated identically. * + * This driver also supports the MAX6695 and MAX6696, two other sensor + * chips made by Maxim. These are also quite similar to other Maxim + * chips, but support three temperature sensors instead of two. MAX6695 + * and MAX6696 only differ in the pinout so they can be treated identically. + * * This driver also supports the ADT7461 chip from Analog Devices. * It's supported in both compatibility and extended mode. It is mostly * compatible with LM90 except for a data format difference for the @@ -96,7 +101,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771 }; + max6646, w83l771, max6696 }; /* * The LM90 registers @@ -137,9 +142,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* MAX6646/6647/6649/6657/6658/6659 registers */ +/* MAX6646/6647/6649/6657/6658/6659/6695/6696 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +#define MAX6696_REG_R_STATUS2 0x12 #define MAX6659_REG_R_REMOTE_EMERG 0x16 #define MAX6659_REG_W_REMOTE_EMERG 0x16 #define MAX6659_REG_R_LOCAL_EMERG 0x17 @@ -154,6 +160,8 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ #define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ #define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ +#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ +#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ /* * Functions declaration @@ -166,6 +174,9 @@ static void lm90_init_client(struct i2c_client *client); static void lm90_alert(struct i2c_client *client, unsigned int flag); static int lm90_remove(struct i2c_client *client); static struct lm90_data *lm90_update_device(struct device *dev); +static inline void lm90_select_remote_channel(struct i2c_client *client, + struct lm90_data *data, + int channel); /* * Driver data (common to all clients) @@ -186,6 +197,8 @@ static const struct i2c_device_id lm90_id[] = { { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "max6695", max6696 }, + { "max6696", max6696 }, { "w83l771", w83l771 }, { } }; @@ -217,22 +230,29 @@ struct lm90_data { int flags; u8 config_orig; /* Original configuration register value */ - u8 alert_alarms; /* Which alarm bits trigger ALERT# */ + u16 alert_alarms; /* Which alarm bits trigger ALERT# */ + /* Upper 8 bits for max6695/96 */ /* registers values */ - s8 temp8[6]; /* 0: local low limit + s8 temp8[8]; /* 0: local low limit 1: local high limit 2: local critical limit 3: remote critical limit - 4: local emergency limit (max6659 only) - 5: remote emergency limit (max6659 only) */ - s16 temp11[5]; /* 0: remote input + 4: local emergency limit (max6659 and max6695/96) + 5: remote emergency limit (max6659 and max6695/96) + 6: remote 2 critical limit (max6695/96 only) + 7: remote 2 emergency limit (max6695/96 only) */ + s16 temp11[8]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6646 and max6657/58/59) - 4: local input */ + 3: remote offset (except max6646, max6657/58/59, + and max6695/96) + 4: local input + 5: remote 2 input (max6695/96 only) + 6: remote 2 low limit (max6695/96 only) + 7: remote 2 high limit (ma6695/96 only) */ u8 temp_hyst; - u8 alarms; /* bitvector */ + u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; /* @@ -390,13 +410,15 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[6] = { + static const u8 reg[8] = { LM90_REG_W_LOCAL_LOW, LM90_REG_W_LOCAL_HIGH, LM90_REG_W_LOCAL_CRIT, LM90_REG_W_REMOTE_CRIT, MAX6659_REG_W_LOCAL_EMERG, MAX6659_REG_W_REMOTE_EMERG, + LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_REMOTE_EMERG, }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -421,7 +443,11 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, data->temp8[nr] = temp_to_u8(val); else data->temp8[nr] = temp_to_s8(val); + + lm90_select_remote_channel(client, data, nr >= 6); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); + lm90_select_remote_channel(client, data, 0); + mutex_unlock(&data->update_lock); return count; } @@ -453,10 +479,13 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, struct { u8 high; u8 low; - } reg[3] = { - { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, - { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, - { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL } + int channel; + } reg[5] = { + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 0 }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 0 }, + { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL, 0 }, + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 1 }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 } }; struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); @@ -485,11 +514,14 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, else data->temp11[index] = temp_to_s8(val) << 8; + lm90_select_remote_channel(client, data, reg[nr].channel); i2c_smbus_write_byte_data(client, reg[nr].high, data->temp11[index] >> 8); if (data->flags & LM90_HAVE_REM_LIMIT_EXT) i2c_smbus_write_byte_data(client, reg[nr].low, data->temp11[index] & 0xff); + lm90_select_remote_channel(client, data, 0); + mutex_unlock(&data->update_lock); return count; } @@ -643,6 +675,62 @@ static const struct attribute_group lm90_emergency_group = { .attrs = lm90_emergency_attributes, }; +static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 15); +static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 13); + +static struct attribute *lm90_emergency_alarm_attributes[] = { + &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_emergency_alarm_group = { + .attrs = lm90_emergency_alarm_attributes, +}; + +/* + * Additional attributes for devices with 3 temperature sensors + */ +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, 0, 5); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 3, 6); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 4, 7); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 6); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 7); +static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 7); + +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp3_emergency_alarm, S_IRUGO, show_alarm, NULL, 14); + +static struct attribute *lm90_temp3_attributes[] = { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_emergency.dev_attr.attr, + &sensor_dev_attr_temp3_emergency_hyst.dev_attr.attr, + + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_emergency_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_temp3_group = { + .attrs = lm90_temp3_attributes, +}; + /* pec used for ADM1032 only */ static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, char *buf) @@ -720,6 +808,30 @@ static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value) return 0; } +/* + * client->update_lock must be held when calling this function (unless we are + * in detection or initialization steps), and while a remote channel other + * than channel 0 is selected. Also, calling code must make sure to re-select + * external channel 0 before releasing the lock. This is necessary because + * various registers have different meanings as a result of selecting a + * non-default remote channel. + */ +static inline void lm90_select_remote_channel(struct i2c_client *client, + struct lm90_data *data, + int channel) +{ + u8 config; + + if (data->kind == max6696) { + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + config &= ~0x08; + if (channel) + config |= 0x08; + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config); + } +} + /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm90_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -794,6 +906,23 @@ static int lm90_detect(struct i2c_client *new_client, } } else if (man_id == 0x4D) { /* Maxim */ + int reg_emerg, reg_emerg2, reg_status2; + + /* + * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read + * LM90_REG_R_MAN_ID in between. If MAX6659_REG_R_REMOTE_EMERG + * exists, both readings will reflect the same value. Otherwise, + * the readings will be different. + */ + if ((reg_emerg = i2c_smbus_read_byte_data(new_client, + MAX6659_REG_R_REMOTE_EMERG)) < 0 + || i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID) < 0 + || (reg_emerg2 = i2c_smbus_read_byte_data(new_client, + MAX6659_REG_R_REMOTE_EMERG)) < 0 + || (reg_status2 = i2c_smbus_read_byte_data(new_client, + MAX6696_REG_R_STATUS2)) < 0) + return -ENODEV; + /* * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id * register. Reading from that address will return the last @@ -816,6 +945,24 @@ static int lm90_detect(struct i2c_client *new_client, else name = "max6659"; } else + /* + * Even though MAX6695 and MAX6696 do not have a chip ID + * register, reading it returns 0x01. Bit 4 of the config1 + * register is unused and should return zero when read. Bit 0 of + * the status2 register is unused and should return zero when + * read. + * + * MAX6695 and MAX6696 have an additional set of temperature + * limit registers. We can detect those chips by checking if + * one of those registers exists. + */ + if (chip_id == 0x01 + && (reg_config1 & 0x10) == 0x00 + && (reg_status2 & 0x01) == 0x00 + && reg_emerg == reg_emerg2 + && reg_convrate <= 0x07) { + name = "max6696"; + } else /* * The chip_id register of the MAX6680 and MAX6681 holds the * revision of the chip. The lowest bit of the config1 register @@ -861,6 +1008,11 @@ static int lm90_detect(struct i2c_client *new_client, static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) { + if (data->flags & LM90_HAVE_TEMP3) + sysfs_remove_group(&client->dev.kobj, &lm90_temp3_group); + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) + sysfs_remove_group(&client->dev.kobj, + &lm90_emergency_alarm_group); if (data->flags & LM90_HAVE_EMERGENCY) sysfs_remove_group(&client->dev.kobj, &lm90_emergency_group); @@ -901,6 +1053,9 @@ static int lm90_probe(struct i2c_client *new_client, case lm86: data->alert_alarms = 0x7b; break; + case max6696: + data->alert_alarms = 0x187c; + break; default: data->alert_alarms = 0x7c; break; @@ -908,20 +1063,24 @@ static int lm90_probe(struct i2c_client *new_client, /* Set chip capabilities */ if (data->kind != max6657 && data->kind != max6659 - && data->kind != max6646) + && data->kind != max6646 && data->kind != max6696) data->flags |= LM90_HAVE_OFFSET; if (data->kind == max6657 || data->kind == max6659 - || data->kind == max6646) + || data->kind == max6646 || data->kind == max6696) data->flags |= LM90_HAVE_LOCAL_EXT; if (data->kind != max6657 && data->kind != max6659 - && data->kind != max6646 && data->kind != max6680) + && data->kind != max6646 && data->kind != max6680 + && data->kind != max6696) data->flags |= LM90_HAVE_REM_LIMIT_EXT; - if (data->kind == max6659) + if (data->kind == max6659 || data->kind == max6696) data->flags |= LM90_HAVE_EMERGENCY; + if (data->kind == max6696) + data->flags |= LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3; + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -946,6 +1105,18 @@ static int lm90_probe(struct i2c_client *new_client, if (err) goto exit_remove_files; } + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_emergency_alarm_group); + if (err) + goto exit_remove_files; + } + if (data->flags & LM90_HAVE_TEMP3) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_temp3_group); + if (err) + goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -993,6 +1164,12 @@ static void lm90_init_client(struct i2c_client *client) if (data->kind == max6680) config |= 0x18; + /* + * Select external channel 0 for max6695/96 + */ + if (data->kind == max6696) + config &= ~0x08; + config &= 0xBF; /* run */ if (config != data->config_orig) /* Only write if changed */ i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); @@ -1016,10 +1193,14 @@ static int lm90_remove(struct i2c_client *client) static void lm90_alert(struct i2c_client *client, unsigned int flag) { struct lm90_data *data = i2c_get_clientdata(client); - u8 config, alarms; + u8 config, alarms, alarms2 = 0; lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); - if ((alarms & 0x7f) == 0) { + + if (data->kind == max6696) + lm90_read_reg(client, MAX6696_REG_R_STATUS2, &alarms2); + + if ((alarms & 0x7f) == 0 && (alarms2 & 0xfe) == 0) { dev_info(&client->dev, "Everything OK\n"); } else { if (alarms & 0x61) @@ -1032,6 +1213,10 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) dev_warn(&client->dev, "temp%d diode open, please check!\n", 2); + if (alarms2 & 0x18) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 3); + /* Disable ALERT# output, because these chips don't implement SMBus alert correctly; they should only hold the alert line low briefly. */ @@ -1087,6 +1272,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10) || !data->valid) { u8 h, l; + u8 alarms; dev_dbg(&client->dev, "Updating lm90 data.\n"); lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); @@ -1135,7 +1321,27 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, &data->temp8[5]); } - lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); + lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); + data->alarms = alarms; /* save as 16 bit value */ + + if (data->kind == max6696) { + lm90_select_remote_channel(client, data, 1); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, + &data->temp8[6]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[7]); + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]); + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) + data->temp11[6] = h << 8; + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) + data->temp11[7] = h << 8; + lm90_select_remote_channel(client, data, 0); + + if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2, + &alarms)) + data->alarms |= alarms << 8; + } /* Re-enable ALERT# output if it was originally enabled and * relevant alarms are all clear */ From 15b66ab69051c014d0ba9f46f7081a8a7e6ad1c3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 10/68] hwmon: (lm90) Rearrange code to no longer require forward declarations Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 523 +++++++++++++++++++++---------------------- 1 file changed, 256 insertions(+), 267 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 366bb624e655..302d9eb9f275 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -163,21 +163,6 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ #define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ -/* - * Functions declaration - */ - -static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info); -static int lm90_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static void lm90_init_client(struct i2c_client *client); -static void lm90_alert(struct i2c_client *client, unsigned int flag); -static int lm90_remove(struct i2c_client *client); -static struct lm90_data *lm90_update_device(struct device *dev); -static inline void lm90_select_remote_channel(struct i2c_client *client, - struct lm90_data *data, - int channel); - /* * Driver data (common to all clients) */ @@ -204,19 +189,6 @@ static const struct i2c_device_id lm90_id[] = { }; MODULE_DEVICE_TABLE(i2c, lm90_id); -static struct i2c_driver lm90_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm90", - }, - .probe = lm90_probe, - .remove = lm90_remove, - .alert = lm90_alert, - .id_table = lm90_id, - .detect = lm90_detect, - .address_list = normal_i2c, -}; - /* * Client data (each client gets its own) */ @@ -255,6 +227,209 @@ struct lm90_data { u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; +/* + * Support functions + */ + +/* + * The ADM1032 supports PEC but not on write byte transactions, so we need + * to explicitly ask for a transaction without PEC. + */ +static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags & ~I2C_CLIENT_PEC, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} + +/* + * It is assumed that client->update_lock is held (unless we are in + * detection or initialization steps). This matters when PEC is enabled, + * because we don't want the address pointer to change between the write + * byte and the read byte transactions. + */ +static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value) +{ + int err; + + if (client->flags & I2C_CLIENT_PEC) { + err = adm1032_write_byte(client, reg); + if (err >= 0) + err = i2c_smbus_read_byte(client); + } else + err = i2c_smbus_read_byte_data(client, reg); + + if (err < 0) { + dev_warn(&client->dev, "Register %#02x read failed (%d)\n", + reg, err); + return err; + } + *value = err; + + return 0; +} + +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +{ + int err; + u8 oldh, newh, l; + + /* + * There is a trick here. We have to read two registers to have the + * sensor temperature, but we have to beware a conversion could occur + * inbetween the readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * byte once, then the low byte, then the high byte again. If the new + * high byte matches the old one, then we have a valid reading. Else + * we have to read the low byte again, and now we believe we have a + * correct reading. + */ + if ((err = lm90_read_reg(client, regh, &oldh)) + || (err = lm90_read_reg(client, regl, &l)) + || (err = lm90_read_reg(client, regh, &newh))) + return err; + if (oldh != newh) { + err = lm90_read_reg(client, regl, &l); + if (err) + return err; + } + *value = (newh << 8) | l; + + return 0; +} + +/* + * client->update_lock must be held when calling this function (unless we are + * in detection or initialization steps), and while a remote channel other + * than channel 0 is selected. Also, calling code must make sure to re-select + * external channel 0 before releasing the lock. This is necessary because + * various registers have different meanings as a result of selecting a + * non-default remote channel. + */ +static inline void lm90_select_remote_channel(struct i2c_client *client, + struct lm90_data *data, + int channel) +{ + u8 config; + + if (data->kind == max6696) { + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + config &= ~0x08; + if (channel) + config |= 0x08; + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config); + } +} + +static struct lm90_data *lm90_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10) + || !data->valid) { + u8 h, l; + u8 alarms; + + dev_dbg(&client->dev, "Updating lm90 data.\n"); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); + lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); + + if (data->flags & LM90_HAVE_LOCAL_EXT) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[1] = h << 8; + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[1] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[2] = h << 8; + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[2] |= l; + } + + if (data->flags & LM90_HAVE_OFFSET) { + if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, + &h) == 0 + && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, + &l) == 0) + data->temp11[3] = (h << 8) | l; + } + if (data->flags & LM90_HAVE_EMERGENCY) { + lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, + &data->temp8[4]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[5]); + } + lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); + data->alarms = alarms; /* save as 16 bit value */ + + if (data->kind == max6696) { + lm90_select_remote_channel(client, data, 1); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, + &data->temp8[6]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[7]); + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]); + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) + data->temp11[6] = h << 8; + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) + data->temp11[7] = h << 8; + lm90_select_remote_channel(client, data, 0); + + if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2, + &alarms)) + data->alarms |= alarms << 8; + } + + /* Re-enable ALERT# output if it was originally enabled and + * relevant alarms are all clear */ + if ((data->config_orig & 0x80) == 0 + && (data->alarms & data->alert_alarms) == 0) { + u8 config; + + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + if (config & 0x80) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + i2c_smbus_write_byte_data(client, + LM90_REG_W_CONFIG1, + config & ~0x80); + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + /* * Conversions * For local temperatures and limits, critical limits and the hysteresis @@ -770,68 +945,6 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); * Real code */ -/* - * The ADM1032 supports PEC but not on write byte transactions, so we need - * to explicitly ask for a transaction without PEC. - */ -static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) -{ - return i2c_smbus_xfer(client->adapter, client->addr, - client->flags & ~I2C_CLIENT_PEC, - I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); -} - -/* - * It is assumed that client->update_lock is held (unless we are in - * detection or initialization steps). This matters when PEC is enabled, - * because we don't want the address pointer to change between the write - * byte and the read byte transactions. - */ -static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value) -{ - int err; - - if (client->flags & I2C_CLIENT_PEC) { - err = adm1032_write_byte(client, reg); - if (err >= 0) - err = i2c_smbus_read_byte(client); - } else - err = i2c_smbus_read_byte_data(client, reg); - - if (err < 0) { - dev_warn(&client->dev, "Register %#02x read failed (%d)\n", - reg, err); - return err; - } - *value = err; - - return 0; -} - -/* - * client->update_lock must be held when calling this function (unless we are - * in detection or initialization steps), and while a remote channel other - * than channel 0 is selected. Also, calling code must make sure to re-select - * external channel 0 before releasing the lock. This is necessary because - * various registers have different meanings as a result of selecting a - * non-default remote channel. - */ -static inline void lm90_select_remote_channel(struct i2c_client *client, - struct lm90_data *data, - int channel) -{ - u8 config; - - if (data->kind == max6696) { - lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); - config &= ~0x08; - if (channel) - config |= 0x08; - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - config); - } -} - /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm90_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -1023,6 +1136,47 @@ static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) sysfs_remove_group(&client->dev.kobj, &lm90_group); } +static void lm90_init_client(struct i2c_client *client) +{ + u8 config; + struct lm90_data *data = i2c_get_clientdata(client); + + /* + * Start the conversions. + */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + 5); /* 2 Hz */ + if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) { + dev_warn(&client->dev, "Initialization failed!\n"); + return; + } + data->config_orig = config; + + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + + /* + * Put MAX6680/MAX8881 into extended resolution (bit 0x10, + * 0.125 degree resolution) and range (0x08, extend range + * to -64 degree) mode for the remote temperature sensor. + */ + if (data->kind == max6680) + config |= 0x18; + + /* + * Select external channel 0 for max6695/96 + */ + if (data->kind == max6696) + config &= ~0x08; + + config &= 0xBF; /* run */ + if (config != data->config_orig) /* Only write if changed */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); +} + static int lm90_probe(struct i2c_client *new_client, const struct i2c_device_id *id) { @@ -1134,47 +1288,6 @@ static int lm90_probe(struct i2c_client *new_client, return err; } -static void lm90_init_client(struct i2c_client *client) -{ - u8 config; - struct lm90_data *data = i2c_get_clientdata(client); - - /* - * Start the conversions. - */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - 5); /* 2 Hz */ - if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) { - dev_warn(&client->dev, "Initialization failed!\n"); - return; - } - data->config_orig = config; - - /* Check Temperature Range Select */ - if (data->kind == adt7461) { - if (config & 0x04) - data->flags |= LM90_FLAG_ADT7461_EXT; - } - - /* - * Put MAX6680/MAX8881 into extended resolution (bit 0x10, - * 0.125 degree resolution) and range (0x08, extend range - * to -64 degree) mode for the remote temperature sensor. - */ - if (data->kind == max6680) - config |= 0x18; - - /* - * Select external channel 0 for max6695/96 - */ - if (data->kind == max6696) - config &= ~0x08; - - config &= 0xBF; /* run */ - if (config != data->config_orig) /* Only write if changed */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); -} - static int lm90_remove(struct i2c_client *client) { struct lm90_data *data = i2c_get_clientdata(client); @@ -1230,142 +1343,18 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) } } -static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) -{ - int err; - u8 oldh, newh, l; - - /* - * There is a trick here. We have to read two registers to have the - * sensor temperature, but we have to beware a conversion could occur - * inbetween the readings. The datasheet says we should either use - * the one-shot conversion register, which we don't want to do - * (disables hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit at the - * exact same time). So the solution used here is to read the high - * byte once, then the low byte, then the high byte again. If the new - * high byte matches the old one, then we have a valid reading. Else - * we have to read the low byte again, and now we believe we have a - * correct reading. - */ - if ((err = lm90_read_reg(client, regh, &oldh)) - || (err = lm90_read_reg(client, regl, &l)) - || (err = lm90_read_reg(client, regh, &newh))) - return err; - if (oldh != newh) { - err = lm90_read_reg(client, regl, &l); - if (err) - return err; - } - *value = (newh << 8) | l; - - return 0; -} - -static struct lm90_data *lm90_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10) - || !data->valid) { - u8 h, l; - u8 alarms; - - dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); - lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - - if (data->flags & LM90_HAVE_LOCAL_EXT) { - lm90_read16(client, LM90_REG_R_LOCAL_TEMP, - MAX6657_REG_R_LOCAL_TEMPL, - &data->temp11[4]); - } else { - if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, - &h) == 0) - data->temp11[4] = h << 8; - } - lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); - - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { - data->temp11[1] = h << 8; - if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) - && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, - &l) == 0) - data->temp11[1] |= l; - } - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { - data->temp11[2] = h << 8; - if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) - && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, - &l) == 0) - data->temp11[2] |= l; - } - - if (data->flags & LM90_HAVE_OFFSET) { - if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, - &h) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, - &l) == 0) - data->temp11[3] = (h << 8) | l; - } - if (data->flags & LM90_HAVE_EMERGENCY) { - lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, - &data->temp8[4]); - lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, - &data->temp8[5]); - } - lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); - data->alarms = alarms; /* save as 16 bit value */ - - if (data->kind == max6696) { - lm90_select_remote_channel(client, data, 1); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, - &data->temp8[6]); - lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, - &data->temp8[7]); - lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]); - if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) - data->temp11[6] = h << 8; - if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) - data->temp11[7] = h << 8; - lm90_select_remote_channel(client, data, 0); - - if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2, - &alarms)) - data->alarms |= alarms << 8; - } - - /* Re-enable ALERT# output if it was originally enabled and - * relevant alarms are all clear */ - if ((data->config_orig & 0x80) == 0 - && (data->alarms & data->alert_alarms) == 0) { - u8 config; - - lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); - if (config & 0x80) { - dev_dbg(&client->dev, "Re-enabling ALERT#\n"); - i2c_smbus_write_byte_data(client, - LM90_REG_W_CONFIG1, - config & ~0x80); - } - } - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} +static struct i2c_driver lm90_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm90", + }, + .probe = lm90_probe, + .remove = lm90_remove, + .alert = lm90_alert, + .id_table = lm90_id, + .detect = lm90_detect, + .address_list = normal_i2c, +}; static int __init sensors_lm90_init(void) { From 4667bcb8d8fc081a804a798df70dc91241946e0a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: [PATCH 11/68] hwmon: (lm90) Introduce chip parameter structure Instead of using switch/case and if statements in probe, define chip specific functionality in a parameter structure array. Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 92 +++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 302d9eb9f275..9df08e1cc518 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -189,6 +189,63 @@ static const struct i2c_device_id lm90_id[] = { }; MODULE_DEVICE_TABLE(i2c, lm90_id); +/* + * chip type specific parameters + */ +struct lm90_params { + u32 flags; /* Capabilities */ + u16 alert_alarms; /* Which alarm bits trigger ALERT# */ + /* Upper 8 bits for max6695/96 */ +}; + +static const struct lm90_params lm90_params[] = { + [adm1032] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7c, + }, + [adt7461] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7c, + }, + [lm86] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + }, + [lm90] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + }, + [lm99] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + }, + [max6646] = { + .flags = LM90_HAVE_LOCAL_EXT, + .alert_alarms = 0x7c, + }, + [max6657] = { + .flags = LM90_HAVE_LOCAL_EXT, + .alert_alarms = 0x7c, + }, + [max6659] = { + .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY, + .alert_alarms = 0x7c, + }, + [max6680] = { + .flags = LM90_HAVE_OFFSET, + .alert_alarms = 0x7c, + }, + [max6696] = { + .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY + | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, + .alert_alarms = 0x187c, + }, + [w83l771] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7c, + }, +}; + /* * Client data (each client gets its own) */ @@ -199,7 +256,7 @@ struct lm90_data { char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; - int flags; + u32 flags; u8 config_orig; /* Original configuration register value */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */ @@ -1201,39 +1258,10 @@ static int lm90_probe(struct i2c_client *new_client, /* Different devices have different alarm bits triggering the * ALERT# output */ - switch (data->kind) { - case lm90: - case lm99: - case lm86: - data->alert_alarms = 0x7b; - break; - case max6696: - data->alert_alarms = 0x187c; - break; - default: - data->alert_alarms = 0x7c; - break; - } + data->alert_alarms = lm90_params[data->kind].alert_alarms; /* Set chip capabilities */ - if (data->kind != max6657 && data->kind != max6659 - && data->kind != max6646 && data->kind != max6696) - data->flags |= LM90_HAVE_OFFSET; - - if (data->kind == max6657 || data->kind == max6659 - || data->kind == max6646 || data->kind == max6696) - data->flags |= LM90_HAVE_LOCAL_EXT; - - if (data->kind != max6657 && data->kind != max6659 - && data->kind != max6646 && data->kind != max6680 - && data->kind != max6696) - data->flags |= LM90_HAVE_REM_LIMIT_EXT; - - if (data->kind == max6659 || data->kind == max6696) - data->flags |= LM90_HAVE_EMERGENCY; - - if (data->kind == max6696) - data->flags |= LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3; + data->flags = lm90_params[data->kind].flags; /* Initialize the LM90 chip */ lm90_init_client(new_client); From 1179324c411edcefb28a5293f8cc6a5bd9567448 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 12/68] hwmon: (lm90) Introduce capability flag to indicate broken ALERT functionality Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 9df08e1cc518..1913f8a5d733 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -162,6 +162,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ #define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ +#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */ /* * Driver data (common to all clients) @@ -200,11 +201,13 @@ struct lm90_params { static const struct lm90_params lm90_params[] = { [adm1032] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, }, [adt7461] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, }, [lm86] = { @@ -1361,7 +1364,7 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) /* Disable ALERT# output, because these chips don't implement SMBus alert correctly; they should only hold the alert line low briefly. */ - if ((data->kind == adm1032 || data->kind == adt7461) + if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); From 0c01b644f77a3df892a48a59901997469aeab0a7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 13/68] hwmon: (lm90) Add support for update_interval sysfs attribute Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/lm90.c | 97 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1913f8a5d733..f9e76c757de8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -151,6 +151,9 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define MAX6659_REG_R_LOCAL_EMERG 0x17 #define MAX6659_REG_W_LOCAL_EMERG 0x17 +#define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */ +#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ + /* * Device flags */ @@ -197,6 +200,7 @@ struct lm90_params { u32 flags; /* Capabilities */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ + u8 max_convrate; /* Maximum conversion rate register value */ }; static const struct lm90_params lm90_params[] = { @@ -204,48 +208,59 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, + .max_convrate = 10, }, [adt7461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, + .max_convrate = 10, }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7b, + .max_convrate = 9, }, [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7b, + .max_convrate = 9, }, [lm99] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7b, + .max_convrate = 9, }, [max6646] = { .flags = LM90_HAVE_LOCAL_EXT, .alert_alarms = 0x7c, + .max_convrate = 6, }, [max6657] = { .flags = LM90_HAVE_LOCAL_EXT, .alert_alarms = 0x7c, + .max_convrate = 8, }, [max6659] = { .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY, .alert_alarms = 0x7c, + .max_convrate = 8, }, [max6680] = { .flags = LM90_HAVE_OFFSET, .alert_alarms = 0x7c, + .max_convrate = 7, }, [max6696] = { .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, .alert_alarms = 0x187c, + .max_convrate = 6, }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7c, + .max_convrate = 8, }, }; @@ -261,9 +276,13 @@ struct lm90_data { int kind; u32 flags; + int update_interval; /* in milliseconds */ + u8 config_orig; /* Original configuration register value */ + u8 convrate_orig; /* Original conversion rate register value */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ + u8 max_convrate; /* Maximum conversion rate */ /* registers values */ s8 temp8[8]; /* 0: local low limit @@ -385,15 +404,41 @@ static inline void lm90_select_remote_channel(struct i2c_client *client, } } +/* + * Set conversion rate. + * client->update_lock must be held when calling this function (unless we are + * in detection or initialization steps). + */ +static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, + unsigned int interval) +{ + int i; + unsigned int update_interval; + + /* Shift calculations to avoid rounding errors */ + interval <<= 6; + + /* find the nearest update rate */ + for (i = 0, update_interval = LM90_MAX_CONVRATE_MS << 6; + i < data->max_convrate; i++, update_interval >>= 1) + if (interval >= update_interval * 3 / 4) + break; + + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i); + data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64); +} + static struct lm90_data *lm90_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); + unsigned long next_update; mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10) - || !data->valid) { + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + if (time_after(jiffies, next_update) || !data->valid) { u8 h, l; u8 alarms; @@ -828,6 +873,34 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm90_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", data->update_interval); +} + +static ssize_t set_update_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = strict_strtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + lm90_set_convrate(client, data, val); + mutex_unlock(&data->update_lock); + + return count; +} + static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, 0, 4); static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, 0, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, @@ -859,6 +932,9 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); /* Raw alarm file for compatibility */ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, + set_update_interval); + static struct attribute *lm90_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, @@ -879,6 +955,7 @@ static struct attribute *lm90_attributes[] = { &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &dev_attr_alarms.attr, + &dev_attr_update_interval.attr, NULL }; @@ -1198,14 +1275,19 @@ static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) static void lm90_init_client(struct i2c_client *client) { - u8 config; + u8 config, convrate; struct lm90_data *data = i2c_get_clientdata(client); + if (lm90_read_reg(client, LM90_REG_R_CONVRATE, &convrate) < 0) { + dev_warn(&client->dev, "Failed to read convrate register!\n"); + convrate = LM90_DEF_CONVRATE_RVAL; + } + data->convrate_orig = convrate; + /* * Start the conversions. */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - 5); /* 2 Hz */ + lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) { dev_warn(&client->dev, "Initialization failed!\n"); return; @@ -1266,6 +1348,9 @@ static int lm90_probe(struct i2c_client *new_client, /* Set chip capabilities */ data->flags = lm90_params[data->kind].flags; + /* Set maximum conversion rate */ + data->max_convrate = lm90_params[data->kind].max_convrate; + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -1327,6 +1412,8 @@ static int lm90_remove(struct i2c_client *client) lm90_remove_files(client, data); /* Restore initial configuration */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + data->convrate_orig); i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, data->config_orig); From c4f99a2b8fb4c564865f0037a2b7be690d4409f3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 14/68] hwmon: (lm90) Add support for the W83L771W/G I was wondering if that chip ever existed publicly... Apparently yes, so add support for it. Signed-off-by: Jean Delvare Tested-by: Alexander Stein Acked-by: Guenter Roeck --- Documentation/hwmon/lm90 | 14 +++++++++++--- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 25 +++++++++++++++++++------ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 6e963b696d85..fa475c0a48a3 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -95,6 +95,10 @@ Supported chips: 0x4c, 0x4d and 0x4e Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/datasheet/index.mvp/id/4199 + * Winbond/Nuvoton W83L771W/G + Prefix: 'w83l771' + Addresses scanned: I2C 0x4c + Datasheet: No longer available * Winbond/Nuvoton W83L771AWG/ASG Prefix: 'w83l771' Addresses scanned: I2C 0x4c @@ -169,12 +173,16 @@ MAX6695 and MAX6696: * Second critical temperature limit * Two remote sensors -W83L771AWG/ASG - * The AWG and ASG variants only differ in package format. +W83L771W/G + * The G variant is lead-free, otherwise similar to the W. * Filter and alert configuration register at 0xBF - * Diode ideality factor configuration (remote sensor) at 0xE3 * Moving average (depending on conversion rate) +W83L771AWG/ASG + * Successor of the W83L771W/G, same features. + * The AWG and ASG variants only differ in package format. + * Diode ideality factor configuration (remote sensor) at 0xE3 + All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature, 0.125 degree for the remote temperature, except for the MAX6657, MAX6658 and MAX6659 which have a diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 9a3742b67c8c..3e88140b9d77 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -615,7 +615,7 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton - W83L771AWG/ASG sensor chips. + W83L771W/G/AWG/ASG sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index f9e76c757de8..812781c655a7 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -88,8 +88,8 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 - * and MAX6658 have address 0x4c. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657, + * MAX6658 and W83L771 have address 0x4c. * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e. @@ -1237,10 +1237,23 @@ static int lm90_detect(struct i2c_client *new_client, } else if (address == 0x4C && man_id == 0x5C) { /* Winbond/Nuvoton */ - if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ - && (reg_config1 & 0x2A) == 0x00 - && reg_convrate <= 0x08) { - name = "w83l771"; + int reg_config2; + + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG2); + if (reg_config2 < 0) + return -ENODEV; + + if ((reg_config1 & 0x2A) == 0x00 + && (reg_config2 & 0xF8) == 0x00) { + if (chip_id == 0x01 /* W83L771W/G */ + && reg_convrate <= 0x09) { + name = "w83l771"; + } else + if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ + && reg_convrate <= 0x08) { + name = "w83l771"; + } } } From e3cd9528af4d1fd404eefe16e52ae421f99a7817 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 15/68] hwmon: (lm75) Make the writing to sysfs more robust Currently we get the checkpatch warning consider using strict_strtol in preference to simple_strtol. Also we should not allow any partially numeric values. Signed-off-by: Shubhrajyoti D Signed-off-by: Jean Delvare --- drivers/hwmon/lm75.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index ab5b87a81677..5cb79d94a990 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -103,7 +103,12 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, struct i2c_client *client = to_i2c_client(dev); struct lm75_data *data = i2c_get_clientdata(client); int nr = attr->index; - long temp = simple_strtol(buf, NULL, 10); + long temp; + int error; + + error = strict_strtol(buf, 10, &temp); + if (error) + return error; mutex_lock(&data->update_lock); data->temp[nr] = LM75_TEMP_TO_REG(temp); From caaa0f36c378c6fec6fd5260a63566e002ac4a84 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 16/68] hwmon: (lm75) Trivial changes to pacify the checkpatch Some trivial changes to pacify the checkpatch. Signed-off-by: Shubhrajyoti D Signed-off-by: Jean Delvare --- drivers/hwmon/lm75.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 5cb79d94a990..f36eb80d227f 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -1,22 +1,22 @@ /* - lm75.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard - - 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. -*/ + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard + * + * 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 #include @@ -340,9 +340,11 @@ static struct i2c_driver lm75_driver = { /* register access */ -/* All registers are word-sized, except for the configuration register. - LM75 uses a high-byte first convention, which is exactly opposite to - the SMBus standard. */ +/* + * All registers are word-sized, except for the configuration register. + * LM75 uses a high-byte first convention, which is exactly opposite to + * the SMBus standard. + */ static int lm75_read_value(struct i2c_client *client, u8 reg) { int value; From 9c627e3be3e8f36eda73ed9e8aa4763582841c1f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 17/68] hwmon: (s3c-hwmon) Use a real mutex The semaphore which protects the ADC is semantically a mutex. Use a real mutex. Signed-off-by: Thomas Gleixner Cc: Ben Dooks Signed-off-by: Jean Delvare --- drivers/hwmon/s3c-hwmon.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 3f3f9a47acfd..05248f2d7581 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -51,7 +51,7 @@ struct s3c_hwmon_attr { * @attr: The holders for the channel attributes. */ struct s3c_hwmon { - struct semaphore lock; + struct mutex lock; struct s3c_adc_client *client; struct device *hwmon_dev; @@ -73,14 +73,14 @@ static int s3c_hwmon_read_ch(struct device *dev, { int ret; - ret = down_interruptible(&hwmon->lock); + ret = mutex_lock_interruptible(&hwmon->lock); if (ret < 0) return ret; dev_dbg(dev, "reading channel %d\n", channel); ret = s3c_adc_read(hwmon->client, channel); - up(&hwmon->lock); + mutex_unlock(&hwmon->lock); return ret; } @@ -296,7 +296,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) platform_set_drvdata(dev, hwmon); - init_MUTEX(&hwmon->lock); + mutex_init(&hwmon->lock); /* Register with the core ADC driver. */ From 62991f7ab395b57b86512d47a6017266e2e615f2 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 18/68] hwmon: (s3c-hwmon) Depend on S3C_ADC This way we don't need to modify Kconfig every time a new SoC comes along to make this driver support it. Also fix some typos while I'm at it. Signed-off-by: Maurus Cuelenaere Reviewed-by: Guenter Roeck Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3e88140b9d77..d875d102ee3f 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -763,14 +763,14 @@ config SENSORS_SHT15 will be called sht15. config SENSORS_S3C - tristate "S3C24XX/S3C64XX Inbuilt ADC" - depends on ARCH_S3C2410 + tristate "Samsung built-in ADC" + depends on S3C_ADC help If you say yes here you get support for the on-board ADCs of - the Samsung S3C24XX or S3C64XX series of SoC + the Samsung S3C24XX, S3C64XX and other series of SoC This driver can also be built as a module. If so, the module - will be called s3c-hwmo. + will be called s3c-hwmon. config SENSORS_S3C_RAW bool "Include raw channel attributes in sysfs" From 792d376b4f3f6af302d40373fa864f8b8e691c53 Mon Sep 17 00:00:00 2001 From: Wei Song Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: [PATCH 19/68] hwmon: New driver for the W83795G/ADG monitoring chips There is still much work needed, but I wanted to give Wei the credit he deserves. I've merged some of my own fixes already, to make gcc and checkpatch happy. Individual fixes and improvements from me will follow. [JD: Fix build errors] [JD: Coding style cleanups] [JD: Get rid of forward declarations] [JD: Drop VID support] [JD: Drop fault output control feature] [JD: Use lowercase for inline function names] [JD: Use strict variants of the strtol/ul functions] [JD: Shorten the read and write function names] Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/w83795.c | 2138 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2149 insertions(+) create mode 100644 drivers/hwmon/w83795.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d875d102ee3f..d229f4592845 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1031,6 +1031,16 @@ config SENSORS_W83793 This driver can also be built as a module. If so, the module will be called w83793. +config SENSORS_W83795 + tristate "Winbond/Nuvoton W83795G/ADG" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Winbond W83795G and + W83795ADG hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called w83795. + config SENSORS_W83L785TS tristate "Winbond W83L785TS-S" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d30f0f6870e0..641b8b349a3d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83792D) += w83792d.o obj-$(CONFIG_SENSORS_W83793) += w83793.o +obj-$(CONFIG_SENSORS_W83795) += w83795.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c new file mode 100644 index 000000000000..c6984c817067 --- /dev/null +++ b/drivers/hwmon/w83795.c @@ -0,0 +1,2138 @@ +/* + * w83795.c - Linux kernel driver for hardware monitoring + * Copyright (C) 2008 Nuvoton Technology Corp. + * Wei Song + * + * 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 - version 2. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp #dts wchipid vendid i2c ISA + * w83795g 21 14 8 6 8 0x79 0x5ca3 yes no + * w83795adg 18 14 2 6 8 0x79 0x5ca3 yes no + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +enum chips { w83795 }; + + +static int reset; +module_param(reset, bool, 0); +MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); + + +#define W83795_REG_BANKSEL 0x00 +#define W83795_REG_VENDORID 0xfd +#define W83795_REG_CHIPID 0xfe +#define W83795_REG_DEVICEID 0xfb + +#define W83795_REG_I2C_ADDR 0xfc +#define W83795_REG_CONFIG 0x01 +#define W83795_REG_CONFIG_CONFIG48 0x04 + +/* Multi-Function Pin Ctrl Registers */ +#define W83795_REG_VOLT_CTRL1 0x02 +#define W83795_REG_VOLT_CTRL2 0x03 +#define W83795_REG_TEMP_CTRL1 0x04 +#define W83795_REG_TEMP_CTRL2 0x05 +#define W83795_REG_FANIN_CTRL1 0x06 +#define W83795_REG_FANIN_CTRL2 0x07 +#define W83795_REG_VMIGB_CTRL 0x08 + +#define TEMP_CTRL_DISABLE 0 +#define TEMP_CTRL_TD 1 +#define TEMP_CTRL_VSEN 2 +#define TEMP_CTRL_TR 3 +#define TEMP_CTRL_SHIFT 4 +#define TEMP_CTRL_HASIN_SHIFT 5 +/* temp mode may effect VSEN17-12 (in20-15) */ +static u16 W83795_REG_TEMP_CTRL[][6] = { + /* Disable, TD, VSEN, TR, register shift value, has_in shift num */ + {0x00, 0x01, 0x02, 0x03, 0, 17}, /* TR1 */ + {0x00, 0x04, 0x08, 0x0C, 2, 18}, /* TR2 */ + {0x00, 0x10, 0x20, 0x30, 4, 19}, /* TR3 */ + {0x00, 0x40, 0x80, 0xC0, 6, 20}, /* TR4 */ + {0x00, 0x00, 0x02, 0x03, 0, 15}, /* TR5 */ + {0x00, 0x00, 0x08, 0x0C, 2, 16}, /* TR6 */ +}; + +#define TEMP_READ 0 +#define TEMP_CRIT 1 +#define TEMP_CRIT_HYST 2 +#define TEMP_WARN 3 +#define TEMP_WARN_HYST 4 +/* only crit and crit_hyst affect real-time alarm status + * current crit crit_hyst warn warn_hyst */ +static u16 W83795_REG_TEMP[][5] = { + {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */ + {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */ + {0x23, 0x9e, 0x9f, 0xa0, 0xa1}, /* TD3/TR3 */ + {0x24, 0xa2, 0xa3, 0xa4, 0xa5}, /* TD4/TR4 */ + {0x1f, 0xa6, 0xa7, 0xa8, 0xa9}, /* TR5 */ + {0x20, 0xaa, 0xab, 0xac, 0xad}, /* TR6 */ +}; + +#define IN_READ 0 +#define IN_MAX 1 +#define IN_LOW 2 +static const u16 W83795_REG_IN[][3] = { + /* Current, HL, LL */ + {0x10, 0x70, 0x71}, /* VSEN1 */ + {0x11, 0x72, 0x73}, /* VSEN2 */ + {0x12, 0x74, 0x75}, /* VSEN3 */ + {0x13, 0x76, 0x77}, /* VSEN4 */ + {0x14, 0x78, 0x79}, /* VSEN5 */ + {0x15, 0x7a, 0x7b}, /* VSEN6 */ + {0x16, 0x7c, 0x7d}, /* VSEN7 */ + {0x17, 0x7e, 0x7f}, /* VSEN8 */ + {0x18, 0x80, 0x81}, /* VSEN9 */ + {0x19, 0x82, 0x83}, /* VSEN10 */ + {0x1A, 0x84, 0x85}, /* VSEN11 */ + {0x1B, 0x86, 0x87}, /* VTT */ + {0x1C, 0x88, 0x89}, /* 3VDD */ + {0x1D, 0x8a, 0x8b}, /* 3VSB */ + {0x1E, 0x8c, 0x8d}, /* VBAT */ + {0x1F, 0xa6, 0xa7}, /* VSEN12 */ + {0x20, 0xaa, 0xab}, /* VSEN13 */ + {0x21, 0x96, 0x97}, /* VSEN14 */ + {0x22, 0x9a, 0x9b}, /* VSEN15 */ + {0x23, 0x9e, 0x9f}, /* VSEN16 */ + {0x24, 0xa2, 0xa3}, /* VSEN17 */ +}; +#define W83795_REG_VRLSB 0x3C +#define VRLSB_SHIFT 6 + +static const u8 W83795_REG_IN_HL_LSB[] = { + 0x8e, /* VSEN1-4 */ + 0x90, /* VSEN5-8 */ + 0x92, /* VSEN9-11 */ + 0x94, /* VTT, 3VDD, 3VSB, 3VBAT */ + 0xa8, /* VSEN12 */ + 0xac, /* VSEN13 */ + 0x98, /* VSEN14 */ + 0x9c, /* VSEN15 */ + 0xa0, /* VSEN16 */ + 0xa4, /* VSEN17 */ +}; + +#define IN_LSB_REG(index, type) \ + (((type) == 1) ? W83795_REG_IN_HL_LSB[(index)] \ + : (W83795_REG_IN_HL_LSB[(index)] + 1)) + +#define IN_LSB_REG_NUM 10 + +#define IN_LSB_SHIFT 0 +#define IN_LSB_IDX 1 +static const u8 IN_LSB_SHIFT_IDX[][2] = { + /* High/Low LSB shift, LSB No. */ + {0x00, 0x00}, /* VSEN1 */ + {0x02, 0x00}, /* VSEN2 */ + {0x04, 0x00}, /* VSEN3 */ + {0x06, 0x00}, /* VSEN4 */ + {0x00, 0x01}, /* VSEN5 */ + {0x02, 0x01}, /* VSEN6 */ + {0x04, 0x01}, /* VSEN7 */ + {0x06, 0x01}, /* VSEN8 */ + {0x00, 0x02}, /* VSEN9 */ + {0x02, 0x02}, /* VSEN10 */ + {0x04, 0x02}, /* VSEN11 */ + {0x00, 0x03}, /* VTT */ + {0x02, 0x03}, /* 3VDD */ + {0x04, 0x03}, /* 3VSB */ + {0x06, 0x03}, /* VBAT */ + {0x06, 0x04}, /* VSEN12 */ + {0x06, 0x05}, /* VSEN13 */ + {0x06, 0x06}, /* VSEN14 */ + {0x06, 0x07}, /* VSEN15 */ + {0x06, 0x08}, /* VSEN16 */ + {0x06, 0x09}, /* VSEN17 */ +}; + + +/* 3VDD, 3VSB, VBAT * 0.006 */ +#define REST_VLT_BEGIN 12 /* the 13th volt to 15th */ +#define REST_VLT_END 14 /* the 13th volt to 15th */ + +#define W83795_REG_FAN(index) (0x2E + (index)) +#define W83795_REG_FAN_MIN_HL(index) (0xB6 + (index)) +#define W83795_REG_FAN_MIN_LSB(index) (0xC4 + (index) / 2) +#define W83795_REG_FAN_MIN_LSB_SHIFT(index) \ + (((index) % 1) ? 4 : 0) +#define W83795_REG_FAN_CTRL_SHIFT(index) \ + (((index) > 7) ? ((index) - 8) : (index)) + +#define W83795_REG_VID_CTRL 0x6A + +#define ALARM_BEEP_REG_NUM 6 +#define W83795_REG_ALARM(index) (0x41 + (index)) +#define W83795_REG_BEEP(index) (0x50 + (index)) + +#define W83795_REG_CLR_CHASSIS 0x4D + + +#define W83795_REG_TEMP_NUM 6 +#define W83795_REG_FCMS1 0x201 +#define W83795_REG_FCMS2 0x208 +#define W83795_REG_TFMR(index) (0x202 + (index)) +#define W83795_REG_FOMC 0x20F +#define W83795_REG_FOPFP(index) (0x218 + (index)) + +#define W83795_REG_TSS(index) (0x209 + (index)) + +#define PWM_OUTPUT 0 +#define PWM_START 1 +#define PWM_NONSTOP 2 +#define PWM_STOP_TIME 3 +#define PWM_DIV 4 +#define W83795_REG_PWM(index, nr) \ + (((nr) == 0 ? 0x210 : \ + (nr) == 1 ? 0x220 : \ + (nr) == 2 ? 0x228 : \ + (nr) == 3 ? 0x230 : 0x218) + (index)) + +#define W83795_REG_FOPFP_DIV(index) \ + (((index) < 8) ? ((index) + 1) : \ + ((index) == 8) ? 12 : \ + (16 << ((index) - 9))) + +#define W83795_REG_FTSH(index) (0x240 + (index) * 2) +#define W83795_REG_FTSL(index) (0x241 + (index) * 2) +#define W83795_REG_TFTS 0x250 + +#define TEMP_PWM_TTTI 0 +#define TEMP_PWM_CTFS 1 +#define TEMP_PWM_HCT 2 +#define TEMP_PWM_HOT 3 +#define W83795_REG_TTTI(index) (0x260 + (index)) +#define W83795_REG_CTFS(index) (0x268 + (index)) +#define W83795_REG_HT(index) (0x270 + (index)) + +#define SF4_TEMP 0 +#define SF4_PWM 1 +#define W83795_REG_SF4_TEMP(temp_num, index) \ + (0x280 + 0x10 * (temp_num) + (index)) +#define W83795_REG_SF4_PWM(temp_num, index) \ + (0x288 + 0x10 * (temp_num) + (index)) + +#define W83795_REG_DTSC 0x301 +#define W83795_REG_DTSE 0x302 +#define W83795_REG_DTS(index) (0x26 + (index)) + +#define DTS_CRIT 0 +#define DTS_CRIT_HYST 1 +#define DTS_WARN 2 +#define DTS_WARN_HYST 3 +#define W83795_REG_DTS_EXT(index) (0xB2 + (index)) + +#define SETUP_PWM_DEFAULT 0 +#define SETUP_PWM_UPTIME 1 +#define SETUP_PWM_DOWNTIME 2 +#define W83795_REG_SETUP_PWM(index) (0x20C + (index)) + +static inline u16 in_from_reg(u8 index, u16 val) +{ + if ((index >= REST_VLT_BEGIN) && (index <= REST_VLT_END)) + return val * 6; + else + return val * 2; +} + +static inline u16 in_to_reg(u8 index, u16 val) +{ + if ((index >= REST_VLT_BEGIN) && (index <= REST_VLT_END)) + return val / 6; + else + return val / 2; +} + +static inline unsigned long fan_from_reg(u16 val) +{ + if ((val >= 0xff0) || (val == 0)) + return 0; + return 1350000UL / val; +} + +static inline u16 fan_to_reg(long rpm) +{ + if (rpm <= 0) + return 0x0fff; + return SENSORS_LIMIT((1350000 + (rpm >> 1)) / rpm, 1, 0xffe); +} + +static inline unsigned long time_from_reg(u8 reg) +{ + return reg * 100; +} + +static inline u8 time_to_reg(unsigned long val) +{ + return SENSORS_LIMIT((val + 50) / 100, 0, 0xff); +} + +static inline long temp_from_reg(s8 reg) +{ + return reg * 1000; +} + +static inline s8 temp_to_reg(long val, s8 min, s8 max) +{ + return SENSORS_LIMIT((val < 0 ? -val : val) / 1000, min, max); +} + + +enum chip_types {w83795g, w83795adg}; + +struct w83795_data { + struct device *hwmon_dev; + struct mutex update_lock; + unsigned long last_updated; /* In jiffies */ + enum chip_types chip_type; + + u8 bank; + + u32 has_in; /* Enable monitor VIN or not */ + u16 in[21][3]; /* Register value, read/high/low */ + u8 in_lsb[10][3]; /* LSB Register value, high/low */ + u8 has_gain; /* has gain: in17-20 * 8 */ + + u16 has_fan; /* Enable fan14-1 or not */ + u16 fan[14]; /* Register value combine */ + u16 fan_min[14]; /* Register value combine */ + + u8 has_temp; /* Enable monitor temp6-1 or not */ + u8 temp[6][5]; /* current, crit, crit_hyst, warn, warn_hyst */ + u8 temp_read_vrlsb[6]; + u8 temp_mode; /* bit 0: TR mode, bit 1: TD mode */ + u8 temp_src[3]; /* Register value */ + + u8 enable_dts; /* Enable PECI and SB-TSI, + * bit 0: =1 enable, =0 disable, + * bit 1: =1 AMD SB-TSI, =0 Intel PECI */ + u8 has_dts; /* Enable monitor DTS temp */ + u8 dts[8]; /* Register value */ + u8 dts_read_vrlsb[8]; /* Register value */ + u8 dts_ext[4]; /* Register value */ + + u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2, + * no config register, only affected by chip + * type */ + u8 pwm[8][5]; /* Register value, output, start, non stop, stop + * time, div */ + u8 pwm_fcms[2]; /* Register value */ + u8 pwm_tfmr[6]; /* Register value */ + u8 pwm_fomc; /* Register value */ + + u16 target_speed[8]; /* Register value, target speed for speed + * cruise */ + u8 tol_speed; /* tolerance of target speed */ + u8 pwm_temp[6][4]; /* TTTI, CTFS, HCT, HOT */ + u8 sf4_reg[6][2][7]; /* 6 temp, temp/dcpwm, 7 registers */ + + u8 setup_pwm[3]; /* Register value */ + + u8 alarms[6]; /* Register value */ + u8 beeps[6]; /* Register value */ + u8 beep_enable; + + char valid; +}; + +/* + * Hardware access + */ + +/* Ignore the possibility that somebody change bank outside the driver + * Must be called with data->update_lock held, except during initialization */ +static u8 w83795_read(struct i2c_client *client, u16 reg) +{ + struct w83795_data *data = i2c_get_clientdata(client); + u8 res = 0xff; + u8 new_bank = reg >> 8; + + new_bank |= data->bank & 0xfc; + if (data->bank != new_bank) { + if (i2c_smbus_write_byte_data + (client, W83795_REG_BANKSEL, new_bank) >= 0) + data->bank = new_bank; + else { + dev_err(&client->dev, + "set bank to %d failed, fall back " + "to bank %d, read reg 0x%x error\n", + new_bank, data->bank, reg); + res = 0x0; /* read 0x0 from the chip */ + goto END; + } + } + res = i2c_smbus_read_byte_data(client, reg & 0xff); +END: + return res; +} + +/* Must be called with data->update_lock held, except during initialization */ +static int w83795_write(struct i2c_client *client, u16 reg, u8 value) +{ + struct w83795_data *data = i2c_get_clientdata(client); + int res; + u8 new_bank = reg >> 8; + + new_bank |= data->bank & 0xfc; + if (data->bank != new_bank) { + res = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, + new_bank); + if (res >= 0) + data->bank = new_bank; + else { + dev_err(&client->dev, + "set bank to %d failed, fall back " + "to bank %d, write reg 0x%x error\n", + new_bank, data->bank, reg); + goto END; + } + } + + res = i2c_smbus_write_byte_data(client, reg & 0xff, value); +END: + return res; +} + +static struct w83795_data *w83795_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + u16 tmp; + int i; + + mutex_lock(&data->update_lock); + + if (!(time_after(jiffies, data->last_updated + HZ * 2) + || !data->valid)) + goto END; + + /* Update the voltages value */ + for (i = 0; i < ARRAY_SIZE(data->in); i++) { + if (!(data->has_in & (1 << i))) + continue; + tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2; + tmp |= (w83795_read(client, W83795_REG_VRLSB) + >> VRLSB_SHIFT) & 0x03; + data->in[i][IN_READ] = tmp; + } + + /* Update fan */ + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + if (!(data->has_fan & (1 << i))) + continue; + data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; + data->fan[i] |= + (w83795_read(client, W83795_REG_VRLSB >> 4)) & 0x0F; + } + + /* Update temperature */ + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + /* even stop monitor, register still keep value, just read out + * it */ + if (!(data->has_temp & (1 << i))) { + data->temp[i][TEMP_READ] = 0; + data->temp_read_vrlsb[i] = 0; + continue; + } + data->temp[i][TEMP_READ] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_READ]); + data->temp_read_vrlsb[i] = + w83795_read(client, W83795_REG_VRLSB); + } + + /* Update dts temperature */ + if (data->enable_dts != 0) { + for (i = 0; i < ARRAY_SIZE(data->dts); i++) { + if (!(data->has_dts & (1 << i))) + continue; + data->dts[i] = + w83795_read(client, W83795_REG_DTS(i)); + data->dts_read_vrlsb[i] = + w83795_read(client, W83795_REG_VRLSB); + } + } + + /* Update pwm output */ + for (i = 0; i < data->has_pwm; i++) { + data->pwm[i][PWM_OUTPUT] = + w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT)); + } + + /* update alarm */ + for (i = 0; i < ALARM_BEEP_REG_NUM; i++) + data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); + + data->last_updated = jiffies; + data->valid = 1; + +END: + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Sysfs attributes + */ + +#define ALARM_STATUS 0 +#define BEEP_ENABLE 1 +static ssize_t +show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index >> 3; + int bit = sensor_attr->index & 0x07; + u8 val; + + if (ALARM_STATUS == nr) { + val = (data->alarms[index] >> (bit)) & 1; + } else { /* BEEP_ENABLE */ + val = (data->beeps[index] >> (bit)) & 1; + } + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index >> 3; + int shift = sensor_attr->index & 0x07; + u8 beep_bit = 1 << shift; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beeps[index] = w83795_read(client, W83795_REG_BEEP(index)); + data->beeps[index] &= ~beep_bit; + data->beeps[index] |= val << shift; + w83795_write(client, W83795_REG_BEEP(index), data->beeps[index]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_beep_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + return sprintf(buf, "%u\n", data->beep_enable); +} + +static ssize_t +store_beep_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + u8 tmp; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beep_enable = val; + tmp = w83795_read(client, W83795_REG_BEEP(5)); + tmp &= 0x7f; + tmp |= val << 7; + w83795_write(client, W83795_REG_BEEP(5), tmp); + mutex_unlock(&data->update_lock); + + return count; +} + +/* Write any value to clear chassis alarm */ +static ssize_t +store_chassis_clear(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + u8 val; + + mutex_lock(&data->update_lock); + val = w83795_read(client, W83795_REG_CLR_CHASSIS); + val |= 0x80; + w83795_write(client, W83795_REG_CLR_CHASSIS, val); + mutex_unlock(&data->update_lock); + return count; +} + +#define FAN_INPUT 0 +#define FAN_MIN 1 +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + u16 val; + + if (FAN_INPUT == nr) + val = data->fan[index] & 0x0fff; + else + val = data->fan_min[index] & 0x0fff; + + return sprintf(buf, "%lu\n", fan_from_reg(val)); +} + +static ssize_t +store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + val = fan_to_reg(val); + + mutex_lock(&data->update_lock); + data->fan_min[index] = val; + w83795_write(client, W83795_REG_FAN_MIN_HL(index), (val >> 4) & 0xff); + val &= 0x0f; + if (index % 1) { + val <<= 4; + val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index)) + & 0x0f; + } else { + val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index)) + & 0xf0; + } + w83795_write(client, W83795_REG_FAN_MIN_LSB(index), val & 0xff); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u16 val; + + switch (nr) { + case PWM_STOP_TIME: + val = time_from_reg(data->pwm[index][nr]); + break; + case PWM_DIV: + val = W83795_REG_FOPFP_DIV(data->pwm[index][nr] & 0x0f); + break; + default: + val = data->pwm[index][nr]; + break; + } + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + int i; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (nr) { + case PWM_STOP_TIME: + val = time_to_reg(val); + break; + case PWM_DIV: + for (i = 0; i < 16; i++) { + if (W83795_REG_FOPFP_DIV(i) == val) { + val = i; + break; + } + } + if (i >= 16) + goto err_end; + val |= w83795_read(client, W83795_REG_PWM(index, nr)) & 0x80; + break; + default: + val = SENSORS_LIMIT(val, 0, 0xff); + break; + } + w83795_write(client, W83795_REG_PWM(index, nr), val); + data->pwm[index][nr] = val & 0xff; + mutex_unlock(&data->update_lock); + return count; +err_end: + mutex_unlock(&data->update_lock); + return -EINVAL; +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + int index = sensor_attr->index; + u8 tmp; + + if (1 == (data->pwm_fcms[0] & (1 << index))) { + tmp = 2; + goto out; + } + for (tmp = 0; tmp < 6; tmp++) { + if (data->pwm_tfmr[tmp] & (1 << index)) { + tmp = 3; + goto out; + } + } + if (data->pwm_fomc & (1 << index)) + tmp = 0; + else + tmp = 1; + +out: + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + unsigned long val; + int i; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (val) { + case 0: + case 1: + data->pwm_fcms[0] &= ~(1 << index); + w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]); + for (i = 0; i < 6; i++) { + data->pwm_tfmr[i] &= ~(1 << index); + w83795_write(client, W83795_REG_TFMR(i), + data->pwm_tfmr[i]); + } + data->pwm_fomc |= 1 << index; + data->pwm_fomc ^= val << index; + w83795_write(client, W83795_REG_FOMC, data->pwm_fomc); + break; + case 2: + data->pwm_fcms[0] |= (1 << index); + w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]); + break; + } + mutex_unlock(&data->update_lock); + return count; + + mutex_unlock(&data->update_lock); + return -EINVAL; +} + +static ssize_t +show_temp_src(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + int index = sensor_attr->index; + u8 val = index / 2; + u8 tmp = data->temp_src[val]; + + if (index % 1) + val = 4; + else + val = 0; + tmp >>= val; + tmp &= 0x0f; + + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_temp_src(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + unsigned long tmp; + u8 val = index / 2; + + if (strict_strtoul(buf, 10, &tmp) < 0) + return -EINVAL; + tmp = SENSORS_LIMIT(tmp, 0, 15); + + mutex_lock(&data->update_lock); + if (index % 1) { + tmp <<= 4; + data->temp_src[val] &= 0x0f; + } else { + data->temp_src[val] &= 0xf0; + } + data->temp_src[val] |= tmp; + w83795_write(client, W83795_REG_TSS(val), data->temp_src[val]); + mutex_unlock(&data->update_lock); + + return count; +} + +#define TEMP_PWM_ENABLE 0 +#define TEMP_PWM_FAN_MAP 1 +static ssize_t +show_temp_pwm_enable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u8 tmp = 0xff; + + switch (nr) { + case TEMP_PWM_ENABLE: + tmp = (data->pwm_fcms[1] >> index) & 1; + if (tmp) + tmp = 4; + else + tmp = 3; + break; + case TEMP_PWM_FAN_MAP: + tmp = data->pwm_tfmr[index]; + break; + } + + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_temp_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long tmp; + + if (strict_strtoul(buf, 10, &tmp) < 0) + return -EINVAL; + + switch (nr) { + case TEMP_PWM_ENABLE: + if ((tmp != 3) && (tmp != 4)) + return -EINVAL; + tmp -= 3; + mutex_lock(&data->update_lock); + data->pwm_fcms[1] &= ~(1 << index); + data->pwm_fcms[1] |= tmp << index; + w83795_write(client, W83795_REG_FCMS2, data->pwm_fcms[1]); + mutex_unlock(&data->update_lock); + break; + case TEMP_PWM_FAN_MAP: + mutex_lock(&data->update_lock); + tmp = SENSORS_LIMIT(tmp, 0, 0xff); + w83795_write(client, W83795_REG_TFMR(index), tmp); + data->pwm_tfmr[index] = tmp; + mutex_unlock(&data->update_lock); + break; + } + return count; +} + +#define FANIN_TARGET 0 +#define FANIN_TOL 1 +static ssize_t +show_fanin(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u16 tmp = 0; + + switch (nr) { + case FANIN_TARGET: + tmp = fan_from_reg(data->target_speed[index]); + break; + case FANIN_TOL: + tmp = data->tol_speed; + break; + } + + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_fanin(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (nr) { + case FANIN_TARGET: + val = fan_to_reg(SENSORS_LIMIT(val, 0, 0xfff)); + w83795_write(client, W83795_REG_FTSH(index), (val >> 4) & 0xff); + w83795_write(client, W83795_REG_FTSL(index), (val << 4) & 0xf0); + data->target_speed[index] = val; + break; + case FANIN_TOL: + val = SENSORS_LIMIT(val, 0, 0x3f); + w83795_write(client, W83795_REG_TFTS, val); + data->tol_speed = val; + break; + } + mutex_unlock(&data->update_lock); + + return count; +} + + +static ssize_t +show_temp_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + long tmp = temp_from_reg(data->pwm_temp[index][nr]); + + return sprintf(buf, "%ld\n", tmp); +} + +static ssize_t +store_temp_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + u8 tmp; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + val /= 1000; + + mutex_lock(&data->update_lock); + switch (nr) { + case TEMP_PWM_TTTI: + val = SENSORS_LIMIT(val, 0, 0x7f); + w83795_write(client, W83795_REG_TTTI(index), val); + break; + case TEMP_PWM_CTFS: + val = SENSORS_LIMIT(val, 0, 0x7f); + w83795_write(client, W83795_REG_CTFS(index), val); + break; + case TEMP_PWM_HCT: + val = SENSORS_LIMIT(val, 0, 0x0f); + tmp = w83795_read(client, W83795_REG_HT(index)); + tmp &= 0x0f; + tmp |= (val << 4) & 0xf0; + w83795_write(client, W83795_REG_HT(index), tmp); + break; + case TEMP_PWM_HOT: + val = SENSORS_LIMIT(val, 0, 0x0f); + tmp = w83795_read(client, W83795_REG_HT(index)); + tmp &= 0xf0; + tmp |= val & 0x0f; + w83795_write(client, W83795_REG_HT(index), tmp); + break; + } + data->pwm_temp[index][nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_sf4_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + + return sprintf(buf, "%u\n", data->sf4_reg[index][SF4_PWM][nr]); +} + +static ssize_t +store_sf4_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + w83795_write(client, W83795_REG_SF4_PWM(index, nr), val); + data->sf4_reg[index][SF4_PWM][nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_sf4_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + + return sprintf(buf, "%u\n", + (data->sf4_reg[index][SF4_TEMP][nr]) * 1000); +} + +static ssize_t +store_sf4_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + val /= 1000; + + mutex_lock(&data->update_lock); + w83795_write(client, W83795_REG_SF4_TEMP(index, nr), val); + data->sf4_reg[index][SF4_TEMP][nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + + +static ssize_t +show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + long temp = temp_from_reg(data->temp[index][nr] & 0x7f); + + if (TEMP_READ == nr) + temp += ((data->temp_read_vrlsb[index] >> VRLSB_SHIFT) & 0x03) + * 250; + if (data->temp[index][nr] & 0x80) + temp = -temp; + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +store_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + long tmp; + + if (strict_strtol(buf, 10, &tmp) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->temp[index][nr] = temp_to_reg(tmp, -128, 127); + w83795_write(client, W83795_REG_TEMP[index][nr], data->temp[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + + +static ssize_t +show_dts_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + u8 tmp; + + if (data->enable_dts == 0) + return sprintf(buf, "%d\n", 0); + + if ((data->has_dts >> index) & 0x01) { + if (data->enable_dts & 2) + tmp = 5; + else + tmp = 6; + } else { + tmp = 0; + } + + return sprintf(buf, "%d\n", tmp); +} + +static ssize_t +show_dts(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + long temp = temp_from_reg(data->dts[index] & 0x7f); + + temp += ((data->dts_read_vrlsb[index] >> VRLSB_SHIFT) & 0x03) * 250; + if (data->dts[index] & 0x80) + temp = -temp; + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +show_dts_ext(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + long temp = temp_from_reg(data->dts_ext[nr] & 0x7f); + + if (data->dts_ext[nr] & 0x80) + temp = -temp; + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +store_dts_ext(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + long tmp; + + if (strict_strtol(buf, 10, &tmp) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->dts_ext[nr] = temp_to_reg(tmp, -128, 127); + w83795_write(client, W83795_REG_DTS_EXT(nr), data->dts_ext[nr]); + mutex_unlock(&data->update_lock); + return count; +} + + +/* + Type 3: Thermal diode + Type 4: Thermistor + + Temp5-6, default TR + Temp1-4, default TD +*/ + +static ssize_t +show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + u8 tmp; + + if (data->has_temp >> index & 0x01) { + if (data->temp_mode >> index & 0x01) + tmp = 3; + else + tmp = 4; + } else { + tmp = 0; + } + + return sprintf(buf, "%d\n", tmp); +} + +static ssize_t +store_temp_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + unsigned long val; + u8 tmp; + u32 mask; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if ((val != 4) && (val != 3)) + return -EINVAL; + if ((index > 3) && (val == 3)) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val == 3) { + val = TEMP_CTRL_TD; + data->has_temp |= 1 << index; + data->temp_mode |= 1 << index; + } else if (val == 4) { + val = TEMP_CTRL_TR; + data->has_temp |= 1 << index; + tmp = 1 << index; + data->temp_mode &= ~tmp; + } + + if (index > 3) + tmp = w83795_read(client, W83795_REG_TEMP_CTRL1); + else + tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); + + mask = 0x03 << W83795_REG_TEMP_CTRL[index][TEMP_CTRL_SHIFT]; + tmp &= ~mask; + tmp |= W83795_REG_TEMP_CTRL[index][val]; + + mask = 1 << W83795_REG_TEMP_CTRL[index][TEMP_CTRL_HASIN_SHIFT]; + data->has_in &= ~mask; + + if (index > 3) + w83795_write(client, W83795_REG_TEMP_CTRL1, tmp); + else + w83795_write(client, W83795_REG_TEMP_CTRL2, tmp); + + mutex_unlock(&data->update_lock); + return count; +} + + +/* show/store VIN */ +static ssize_t +show_in(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + u16 val = data->in[index][nr]; + u8 lsb_idx; + + switch (nr) { + case IN_READ: + /* calculate this value again by sensors as sensors3.conf */ + if ((index >= 17) && + ((data->has_gain >> (index - 17)) & 1)) + val *= 8; + break; + case IN_MAX: + case IN_LOW: + lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX]; + val <<= 2; + val |= (data->in_lsb[lsb_idx][nr] >> + IN_LSB_SHIFT_IDX[lsb_idx][IN_LSB_SHIFT]) & 0x03; + if ((index >= 17) && + ((data->has_gain >> (index - 17)) & 1)) + val *= 8; + break; + } + val = in_from_reg(index, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + u8 tmp; + u8 lsb_idx; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + val = in_to_reg(index, val); + + if ((index >= 17) && + ((data->has_gain >> (index - 17)) & 1)) + val /= 8; + val = SENSORS_LIMIT(val, 0, 0x3FF); + mutex_lock(&data->update_lock); + + lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX]; + tmp = w83795_read(client, IN_LSB_REG(lsb_idx, nr)); + tmp &= ~(0x03 << IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]); + tmp |= (val & 0x03) << IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]; + w83795_write(client, IN_LSB_REG(lsb_idx, nr), tmp); + data->in_lsb[lsb_idx][nr] = tmp; + + tmp = (val >> 2) & 0xff; + w83795_write(client, W83795_REG_IN[index][nr], tmp); + data->in[index][nr] = tmp; + + mutex_unlock(&data->update_lock); + return count; +} + + +static ssize_t +show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + u16 val = data->setup_pwm[nr]; + + switch (nr) { + case SETUP_PWM_UPTIME: + case SETUP_PWM_DOWNTIME: + val = time_from_reg(val); + break; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_sf_setup(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + switch (nr) { + case SETUP_PWM_DEFAULT: + val = SENSORS_LIMIT(val, 0, 0xff); + break; + case SETUP_PWM_UPTIME: + case SETUP_PWM_DOWNTIME: + val = time_to_reg(val); + if (val == 0) + return -EINVAL; + break; + } + + mutex_lock(&data->update_lock); + data->setup_pwm[nr] = val; + w83795_write(client, W83795_REG_SETUP_PWM(nr), val); + mutex_unlock(&data->update_lock); + return count; +} + + +#define NOT_USED -1 + +#define SENSOR_ATTR_IN(index) \ + SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ + IN_READ, index), \ + SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \ + store_in, IN_MAX, index), \ + SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \ + store_in, IN_LOW, index), \ + SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \ + NULL, ALARM_STATUS, index + ((index > 14) ? 1 : 0)), \ + SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, \ + index + ((index > 14) ? 1 : 0)) + +#define SENSOR_ATTR_FAN(index) \ + SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ + NULL, FAN_INPUT, index - 1), \ + SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \ + show_fan, store_fan_min, FAN_MIN, index - 1), \ + SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \ + NULL, ALARM_STATUS, index + 31), \ + SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) + +#define SENSOR_ATTR_PWM(index) \ + SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ + store_pwm, PWM_OUTPUT, index - 1), \ + SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ + SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_START, index - 1), \ + SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \ + SENSOR_ATTR_2(fan##index##_div, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_DIV, index - 1), \ + SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1) + +#define SENSOR_ATTR_FANIN_TARGET(index) \ + SENSOR_ATTR_2(speed_cruise##index##_target, S_IWUSR | S_IRUGO, \ + show_fanin, store_fanin, FANIN_TARGET, index - 1) + +#define SENSOR_ATTR_DTS(index) \ + SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \ + show_dts_mode, NULL, NOT_USED, index - 7), \ + SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_dts, \ + NULL, NOT_USED, index - 7), \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_dts_ext, \ + store_dts_ext, DTS_CRIT, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + show_dts_ext, store_dts_ext, DTS_CRIT_HYST, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_dts_ext, \ + store_dts_ext, DTS_WARN, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ + show_dts_ext, store_dts_ext, DTS_WARN_HYST, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ + show_alarm_beep, NULL, ALARM_STATUS, index + 17), \ + SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) + +#define SENSOR_ATTR_TEMP(index) \ + SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \ + show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ + SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ + NULL, TEMP_READ, index - 1), \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ + store_temp, TEMP_CRIT, index - 1), \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \ + SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_temp, \ + store_temp, TEMP_WARN, index - 1), \ + SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ + show_temp, store_temp, TEMP_WARN_HYST, index - 1), \ + SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ + show_alarm_beep, NULL, ALARM_STATUS, \ + index + (index > 4 ? 11 : 17)), \ + SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, \ + index + (index > 4 ? 11 : 17)), \ + SENSOR_ATTR_2(temp##index##_source_sel, S_IWUSR | S_IRUGO, \ + show_temp_src, store_temp_src, NOT_USED, index - 1), \ + SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \ + show_temp_pwm_enable, store_temp_pwm_enable, \ + TEMP_PWM_ENABLE, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_channels_pwm, S_IWUSR | S_IRUGO, \ + show_temp_pwm_enable, store_temp_pwm_enable, \ + TEMP_PWM_FAN_MAP, index - 1), \ + SENSOR_ATTR_2(thermal_cruise##index, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_TTTI, index - 1), \ + SENSOR_ATTR_2(temp##index##_crit, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_CTFS, index - 1), \ + SENSOR_ATTR_2(temp##index##_crit_hyst, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_HCT, index - 1), \ + SENSOR_ATTR_2(temp##index##_operation_hyst, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_HOT, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 0, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 1, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 2, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 3, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 4, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 5, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 6, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 0, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 1, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 2, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 3, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 4, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 5, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 6, index - 1) + + +static struct sensor_device_attribute_2 w83795_in[] = { + SENSOR_ATTR_IN(0), + SENSOR_ATTR_IN(1), + SENSOR_ATTR_IN(2), + SENSOR_ATTR_IN(3), + SENSOR_ATTR_IN(4), + SENSOR_ATTR_IN(5), + SENSOR_ATTR_IN(6), + SENSOR_ATTR_IN(7), + SENSOR_ATTR_IN(8), + SENSOR_ATTR_IN(9), + SENSOR_ATTR_IN(10), + SENSOR_ATTR_IN(11), + SENSOR_ATTR_IN(12), + SENSOR_ATTR_IN(13), + SENSOR_ATTR_IN(14), + SENSOR_ATTR_IN(15), + SENSOR_ATTR_IN(16), + SENSOR_ATTR_IN(17), + SENSOR_ATTR_IN(18), + SENSOR_ATTR_IN(19), + SENSOR_ATTR_IN(20), +}; + +static struct sensor_device_attribute_2 w83795_fan[] = { + SENSOR_ATTR_FAN(1), + SENSOR_ATTR_FAN(2), + SENSOR_ATTR_FAN(3), + SENSOR_ATTR_FAN(4), + SENSOR_ATTR_FAN(5), + SENSOR_ATTR_FAN(6), + SENSOR_ATTR_FAN(7), + SENSOR_ATTR_FAN(8), + SENSOR_ATTR_FAN(9), + SENSOR_ATTR_FAN(10), + SENSOR_ATTR_FAN(11), + SENSOR_ATTR_FAN(12), + SENSOR_ATTR_FAN(13), + SENSOR_ATTR_FAN(14), +}; + +static struct sensor_device_attribute_2 w83795_temp[] = { + SENSOR_ATTR_TEMP(1), + SENSOR_ATTR_TEMP(2), + SENSOR_ATTR_TEMP(3), + SENSOR_ATTR_TEMP(4), + SENSOR_ATTR_TEMP(5), + SENSOR_ATTR_TEMP(6), +}; + +static struct sensor_device_attribute_2 w83795_dts[] = { + SENSOR_ATTR_DTS(7), + SENSOR_ATTR_DTS(8), + SENSOR_ATTR_DTS(9), + SENSOR_ATTR_DTS(10), + SENSOR_ATTR_DTS(11), + SENSOR_ATTR_DTS(12), + SENSOR_ATTR_DTS(13), + SENSOR_ATTR_DTS(14), +}; + +static struct sensor_device_attribute_2 w83795_static[] = { + SENSOR_ATTR_FANIN_TARGET(1), + SENSOR_ATTR_FANIN_TARGET(2), + SENSOR_ATTR_FANIN_TARGET(3), + SENSOR_ATTR_FANIN_TARGET(4), + SENSOR_ATTR_FANIN_TARGET(5), + SENSOR_ATTR_FANIN_TARGET(6), + SENSOR_ATTR_FANIN_TARGET(7), + SENSOR_ATTR_FANIN_TARGET(8), + SENSOR_ATTR_PWM(1), + SENSOR_ATTR_PWM(2), +}; + +/* all registers existed in 795g than 795adg, + * like PWM3 - PWM8 */ +static struct sensor_device_attribute_2 w83795_left_reg[] = { + SENSOR_ATTR_PWM(3), + SENSOR_ATTR_PWM(4), + SENSOR_ATTR_PWM(5), + SENSOR_ATTR_PWM(6), + SENSOR_ATTR_PWM(7), + SENSOR_ATTR_PWM(8), +}; + +static struct sensor_device_attribute_2 sda_single_files[] = { + SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep, + store_chassis_clear, ALARM_STATUS, 46), + SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable, + store_beep_enable, NOT_USED, NOT_USED), + SENSOR_ATTR_2(speed_cruise_tolerance, S_IWUSR | S_IRUGO, show_fanin, + store_fanin, FANIN_TOL, NOT_USED), + SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup, + store_sf_setup, SETUP_PWM_DEFAULT, NOT_USED), + SENSOR_ATTR_2(pwm_uptime, S_IWUSR | S_IRUGO, show_sf_setup, + store_sf_setup, SETUP_PWM_UPTIME, NOT_USED), + SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup, + store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED), +}; + +/* + * Driver interface + */ + +static void w83795_init_client(struct i2c_client *client) +{ + if (reset) + w83795_write(client, W83795_REG_CONFIG, 0x80); + + /* Start monitoring */ + w83795_write(client, W83795_REG_CONFIG, + w83795_read(client, W83795_REG_CONFIG) | 0x01); +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int w83795_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + u8 tmp, bank; + struct i2c_adapter *adapter = client->adapter; + unsigned short address = client->addr; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); + + tmp = bank & 0x80 ? 0x5c : 0xa3; + /* Check Nuvoton vendor ID */ + if (tmp != i2c_smbus_read_byte_data(client, + W83795_REG_VENDORID)) { + pr_debug("w83795: Detection failed at check " + "vendor id\n"); + return -ENODEV; + } + + /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR + should match */ + if ((bank & 0x07) == 0 + && (i2c_smbus_read_byte_data(client, W83795_REG_I2C_ADDR) & 0x7f) != + address) { + pr_debug("w83795: Detection failed at check " + "i2c addr\n"); + return -ENODEV; + } + + /* Determine the chip type now */ + if (0x79 != i2c_smbus_read_byte_data(client, + W83795_REG_CHIPID)) { + pr_debug("w83795: Detection failed at check " + "chip id\n"); + return -ENODEV; + } + +#if 0 + /* Check 795 chip type: 795G or 795ADG */ + if (W83795_REG_CONFIG_CONFIG48 & + w83795_read(client, W83795_REG_CONFIG)) { + data->chip_type = w83795adg; + } else { + data->chip_type = w83795g; + } +#endif + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(info->type, "w83795", I2C_NAME_SIZE); + + return 0; +} + +static int w83795_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + u8 tmp; + struct device *dev = &client->dev; + struct w83795_data *data; + int err = 0; + + data = kzalloc(sizeof(struct w83795_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); + mutex_init(&data->update_lock); + + /* Initialize the chip */ + w83795_init_client(client); + + /* Check 795 chip type: 795G or 795ADG */ + if (W83795_REG_CONFIG_CONFIG48 & + w83795_read(client, W83795_REG_CONFIG)) { + data->chip_type = w83795adg; + } else { + data->chip_type = w83795g; + } + + data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1); + data->has_in |= w83795_read(client, W83795_REG_VOLT_CTRL2) << 8; + /* VSEN11-9 not for 795adg */ + if (data->chip_type == w83795adg) + data->has_in &= 0xf8ff; + data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1); + data->has_fan |= w83795_read(client, W83795_REG_FANIN_CTRL2) << 8; + + /* VDSEN12-17 and TR1-6, TD1-4 use same register */ + tmp = w83795_read(client, W83795_REG_TEMP_CTRL1); + if (tmp & 0x20) + data->enable_dts = 1; + else + data->enable_dts = 0; + data->has_temp = 0; + data->temp_mode = 0; + if (tmp & 0x08) { + if (tmp & 0x04) + data->has_temp |= 0x20; + else + data->has_in |= 0x10000; + } + if (tmp & 0x02) { + if (tmp & 0x01) + data->has_temp |= 0x10; + else + data->has_in |= 0x8000; + } + tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); + if (tmp & 0x40) { + data->has_temp |= 0x08; + if (!(tmp & 0x80)) + data->temp_mode |= 0x08; + } else if (tmp & 0x80) { + data->has_in |= 0x100000; + } + if (tmp & 0x10) { + data->has_temp |= 0x04; + if (!(tmp & 0x20)) + data->temp_mode |= 0x04; + } else if (tmp & 0x20) { + data->has_in |= 0x80000; + } + if (tmp & 0x04) { + data->has_temp |= 0x02; + if (!(tmp & 0x08)) + data->temp_mode |= 0x02; + } else if (tmp & 0x08) { + data->has_in |= 0x40000; + } + if (tmp & 0x01) { + data->has_temp |= 0x01; + if (!(tmp & 0x02)) + data->temp_mode |= 0x01; + } else if (tmp & 0x02) { + data->has_in |= 0x20000; + } + + /* Check DTS enable status */ + if (data->enable_dts == 0) { + data->has_dts = 0; + } else { + if (1 & w83795_read(client, W83795_REG_DTSC)) + data->enable_dts |= 2; + data->has_dts = w83795_read(client, W83795_REG_DTSE); + } + + /* First update the voltages measured value and limits */ + for (i = 0; i < ARRAY_SIZE(data->in); i++) { + if (!(data->has_in & (1 << i))) + continue; + data->in[i][IN_MAX] = + w83795_read(client, W83795_REG_IN[i][IN_MAX]); + data->in[i][IN_LOW] = + w83795_read(client, W83795_REG_IN[i][IN_LOW]); + tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2; + tmp |= (w83795_read(client, W83795_REG_VRLSB) + >> VRLSB_SHIFT) & 0x03; + data->in[i][IN_READ] = tmp; + } + for (i = 0; i < IN_LSB_REG_NUM; i++) { + data->in_lsb[i][IN_MAX] = + w83795_read(client, IN_LSB_REG(i, IN_MAX)); + data->in_lsb[i][IN_LOW] = + w83795_read(client, IN_LSB_REG(i, IN_LOW)); + } + data->has_gain = w83795_read(client, W83795_REG_VMIGB_CTRL) & 0x0f; + + /* First update fan and limits */ + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + if (!(data->has_fan & (1 << i))) + continue; + data->fan_min[i] = + w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; + data->fan_min[i] |= + (w83795_read(client, W83795_REG_FAN_MIN_LSB(i) >> + W83795_REG_FAN_MIN_LSB_SHIFT(i))) & 0x0F; + data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; + data->fan[i] |= + (w83795_read(client, W83795_REG_VRLSB >> 4)) & 0x0F; + } + + /* temperature and limits */ + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + if (!(data->has_temp & (1 << i))) + continue; + data->temp[i][TEMP_CRIT] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_CRIT]); + data->temp[i][TEMP_CRIT_HYST] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_CRIT_HYST]); + data->temp[i][TEMP_WARN] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_WARN]); + data->temp[i][TEMP_WARN_HYST] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_WARN_HYST]); + data->temp[i][TEMP_READ] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_READ]); + data->temp_read_vrlsb[i] = + w83795_read(client, W83795_REG_VRLSB); + } + + /* dts temperature and limits */ + if (data->enable_dts != 0) { + data->dts_ext[DTS_CRIT] = + w83795_read(client, W83795_REG_DTS_EXT(DTS_CRIT)); + data->dts_ext[DTS_CRIT_HYST] = + w83795_read(client, W83795_REG_DTS_EXT(DTS_CRIT_HYST)); + data->dts_ext[DTS_WARN] = + w83795_read(client, W83795_REG_DTS_EXT(DTS_WARN)); + data->dts_ext[DTS_WARN_HYST] = + w83795_read(client, W83795_REG_DTS_EXT(DTS_WARN_HYST)); + for (i = 0; i < ARRAY_SIZE(data->dts); i++) { + if (!(data->has_dts & (1 << i))) + continue; + data->dts[i] = w83795_read(client, W83795_REG_DTS(i)); + data->dts_read_vrlsb[i] = + w83795_read(client, W83795_REG_VRLSB); + } + } + + /* First update temp source selction */ + for (i = 0; i < 3; i++) + data->temp_src[i] = w83795_read(client, W83795_REG_TSS(i)); + + /* pwm and smart fan */ + if (data->chip_type == w83795g) + data->has_pwm = 8; + else + data->has_pwm = 2; + data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1); + data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2); + /* w83795adg only support pwm2-0 */ + for (i = 0; i < W83795_REG_TEMP_NUM; i++) + data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); + data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); + for (i = 0; i < data->has_pwm; i++) { + for (tmp = 0; tmp < 5; tmp++) { + data->pwm[i][tmp] = + w83795_read(client, W83795_REG_PWM(i, tmp)); + } + } + for (i = 0; i < 8; i++) { + data->target_speed[i] = + w83795_read(client, W83795_REG_FTSH(i)) << 4; + data->target_speed[i] |= + w83795_read(client, W83795_REG_FTSL(i)) >> 4; + } + data->tol_speed = w83795_read(client, W83795_REG_TFTS) & 0x3f; + + for (i = 0; i < W83795_REG_TEMP_NUM; i++) { + data->pwm_temp[i][TEMP_PWM_TTTI] = + w83795_read(client, W83795_REG_TTTI(i)) & 0x7f; + data->pwm_temp[i][TEMP_PWM_CTFS] = + w83795_read(client, W83795_REG_CTFS(i)); + tmp = w83795_read(client, W83795_REG_HT(i)); + data->pwm_temp[i][TEMP_PWM_HCT] = (tmp >> 4) & 0x0f; + data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f; + } + for (i = 0; i < W83795_REG_TEMP_NUM; i++) { + for (tmp = 0; tmp < 7; tmp++) { + data->sf4_reg[i][SF4_TEMP][tmp] = + w83795_read(client, + W83795_REG_SF4_TEMP(i, tmp)); + data->sf4_reg[i][SF4_PWM][tmp] = + w83795_read(client, W83795_REG_SF4_PWM(i, tmp)); + } + } + + /* Setup PWM Register */ + for (i = 0; i < 3; i++) { + data->setup_pwm[i] = + w83795_read(client, W83795_REG_SETUP_PWM(i)); + } + + /* alarm and beep */ + for (i = 0; i < ALARM_BEEP_REG_NUM; i++) { + data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); + data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); + } + data->beep_enable = + (w83795_read(client, W83795_REG_BEEP(5)) >> 7) & 0x01; + + /* Register sysfs hooks */ + for (i = 0; i < ARRAY_SIZE(w83795_in); i++) { + if (!(data->has_in & (1 << (i / 6)))) + continue; + err = device_create_file(dev, &w83795_in[i].dev_attr); + if (err) + goto exit_remove; + } + + for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) { + if (!(data->has_fan & (1 << (i / 5)))) + continue; + err = device_create_file(dev, &w83795_fan[i].dev_attr); + if (err) + goto exit_remove; + } + + for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { + err = device_create_file(dev, &sda_single_files[i].dev_attr); + if (err) + goto exit_remove; + } + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { + if (!(data->has_temp & (1 << (i / 29)))) + continue; + err = device_create_file(dev, &w83795_temp[i].dev_attr); + if (err) + goto exit_remove; + } + + if (data->enable_dts != 0) { + for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { + if (!(data->has_dts & (1 << (i / 8)))) + continue; + err = device_create_file(dev, &w83795_dts[i].dev_attr); + if (err) + goto exit_remove; + } + } + + if (data->chip_type == w83795g) { + for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) { + err = device_create_file(dev, + &w83795_left_reg[i].dev_attr); + if (err) + goto exit_remove; + } + } + + for (i = 0; i < ARRAY_SIZE(w83795_static); i++) { + err = device_create_file(dev, &w83795_static[i].dev_attr); + if (err) + goto exit_remove; + } + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + + /* Unregister sysfs hooks */ +exit_remove: + for (i = 0; i < ARRAY_SIZE(w83795_in); i++) + device_remove_file(dev, &w83795_in[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) + device_remove_file(dev, &w83795_fan[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) + device_remove_file(dev, &sda_single_files[i].dev_attr); + + if (data->chip_type == w83795g) { + for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) + device_remove_file(dev, &w83795_left_reg[i].dev_attr); + } + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) + device_remove_file(dev, &w83795_temp[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) + device_remove_file(dev, &w83795_dts[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_static); i++) + device_remove_file(dev, &w83795_static[i].dev_attr); + + kfree(data); +exit: + return err; +} + +static int w83795_remove(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int i; + + hwmon_device_unregister(data->hwmon_dev); + + for (i = 0; i < ARRAY_SIZE(w83795_in); i++) + device_remove_file(dev, &w83795_in[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) + device_remove_file(dev, &w83795_fan[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) + device_remove_file(dev, &sda_single_files[i].dev_attr); + + if (data->chip_type == w83795g) { + for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) + device_remove_file(dev, &w83795_left_reg[i].dev_attr); + } + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) + device_remove_file(dev, &w83795_temp[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) + device_remove_file(dev, &w83795_dts[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_static); i++) + device_remove_file(dev, &w83795_static[i].dev_attr); + + kfree(data); + + return 0; +} + + +static const struct i2c_device_id w83795_id[] = { + { "w83795", w83795 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, w83795_id); + +static struct i2c_driver w83795_driver = { + .driver = { + .name = "w83795", + }, + .probe = w83795_probe, + .remove = w83795_remove, + .id_table = w83795_id, + + .class = I2C_CLASS_HWMON, + .detect = w83795_detect, + .address_list = normal_i2c, +}; + +static int __init sensors_w83795_init(void) +{ + return i2c_add_driver(&w83795_driver); +} + +static void __exit sensors_w83795_exit(void) +{ + i2c_del_driver(&w83795_driver); +} + +MODULE_AUTHOR("Wei Song"); +MODULE_DESCRIPTION("w83795 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83795_init); +module_exit(sensors_w83795_exit); From 315bacfdbe5a77309c256b296f30bee13d59462b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 20/68] hwmon: (w83795) Misc cleanups * Improve driver description. * Drop unused macro. * Drop unreachable code. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index c6984c817067..7b93ac296d03 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -183,8 +183,6 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define W83795_REG_FAN_MIN_LSB(index) (0xC4 + (index) / 2) #define W83795_REG_FAN_MIN_LSB_SHIFT(index) \ (((index) % 1) ? 4 : 0) -#define W83795_REG_FAN_CTRL_SHIFT(index) \ - (((index) > 7) ? ((index) - 8) : (index)) #define W83795_REG_VID_CTRL 0x6A @@ -793,9 +791,6 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, } mutex_unlock(&data->update_lock); return count; - - mutex_unlock(&data->update_lock); - return -EINVAL; } static ssize_t @@ -2131,7 +2126,7 @@ static void __exit sensors_w83795_exit(void) } MODULE_AUTHOR("Wei Song"); -MODULE_DESCRIPTION("w83795 driver"); +MODULE_DESCRIPTION("W83795G/ADG hardware monitoring driver"); MODULE_LICENSE("GPL"); module_init(sensors_w83795_init); From 093d1a4794cc23dd221019eb1cdf42b16b48abcc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 21/68] hwmon: (w83795) Drop duplicate enum Enum chips and chip_types are redundant, get rid of the former. Fix the detection code to properly identify the chip variant and name the client accordingly. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 7b93ac296d03..d7e1d3693a20 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -38,8 +38,6 @@ /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -enum chips { w83795 }; - static int reset; module_param(reset, bool, 0); @@ -1686,6 +1684,7 @@ static int w83795_detect(struct i2c_client *client, u8 tmp, bank; struct i2c_adapter *adapter = client->adapter; unsigned short address = client->addr; + const char *chip_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -1718,18 +1717,21 @@ static int w83795_detect(struct i2c_client *client, return -ENODEV; } -#if 0 - /* Check 795 chip type: 795G or 795ADG */ + /* Check 795 chip type: 795G or 795ADG + Usually we don't write to chips during detection, but here we don't + quite have the choice; hopefully it's OK, we are about to return + success anyway */ + if ((bank & 0x07) != 0) + i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, + bank & ~0x07); if (W83795_REG_CONFIG_CONFIG48 & - w83795_read(client, W83795_REG_CONFIG)) { - data->chip_type = w83795adg; + i2c_smbus_read_byte_data(client, W83795_REG_CONFIG)) { + chip_name = "w83795adg"; } else { - data->chip_type = w83795g; + chip_name = "w83795g"; } -#endif - /* Fill in the remaining client fields and put into the global list */ - strlcpy(info->type, "w83795", I2C_NAME_SIZE); + strlcpy(info->type, chip_name, I2C_NAME_SIZE); return 0; } @@ -1750,20 +1752,13 @@ static int w83795_probe(struct i2c_client *client, } i2c_set_clientdata(client, data); + data->chip_type = id->driver_data; data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); mutex_init(&data->update_lock); /* Initialize the chip */ w83795_init_client(client); - /* Check 795 chip type: 795G or 795ADG */ - if (W83795_REG_CONFIG_CONFIG48 & - w83795_read(client, W83795_REG_CONFIG)) { - data->chip_type = w83795adg; - } else { - data->chip_type = w83795g; - } - data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1); data->has_in |= w83795_read(client, W83795_REG_VOLT_CTRL2) << 8; /* VSEN11-9 not for 795adg */ @@ -2097,7 +2092,8 @@ static int w83795_remove(struct i2c_client *client) static const struct i2c_device_id w83795_id[] = { - { "w83795", w83795 }, + { "w83795g", w83795g }, + { "w83795adg", w83795adg }, { } }; MODULE_DEVICE_TABLE(i2c, w83795_id); From b2469f422f9ee2054359c4ec609c3bdb1f2d52f5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 22/68] hwmon: (w83795) Refactor bank selection Move the bank selection code to a separate function, to avoid duplicating it in read and write functions. Improve error reporting on register access error. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 89 +++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index d7e1d3693a20..c7f6b1fd0899 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -360,60 +360,67 @@ struct w83795_data { /* * Hardware access + * We assume that nobdody can change the bank outside the driver. */ -/* Ignore the possibility that somebody change bank outside the driver - * Must be called with data->update_lock held, except during initialization */ -static u8 w83795_read(struct i2c_client *client, u16 reg) +/* Must be called with data->update_lock held, except during initialization */ +static int w83795_set_bank(struct i2c_client *client, u8 bank) { struct w83795_data *data = i2c_get_clientdata(client); - u8 res = 0xff; - u8 new_bank = reg >> 8; + int err; - new_bank |= data->bank & 0xfc; - if (data->bank != new_bank) { - if (i2c_smbus_write_byte_data - (client, W83795_REG_BANKSEL, new_bank) >= 0) - data->bank = new_bank; - else { - dev_err(&client->dev, - "set bank to %d failed, fall back " - "to bank %d, read reg 0x%x error\n", - new_bank, data->bank, reg); - res = 0x0; /* read 0x0 from the chip */ - goto END; - } + /* If the same bank is already set, nothing to do */ + if ((data->bank & 0x07) == bank) + return 0; + + /* Change to new bank, preserve all other bits */ + bank |= data->bank & ~0x07; + err = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank); + if (err < 0) { + dev_err(&client->dev, + "Failed to set bank to %d, err %d\n", + (int)bank, err); + return err; } - res = i2c_smbus_read_byte_data(client, reg & 0xff); -END: - return res; + data->bank = bank; + + return 0; +} + +/* Must be called with data->update_lock held, except during initialization */ +static u8 w83795_read(struct i2c_client *client, u16 reg) +{ + int err; + + err = w83795_set_bank(client, reg >> 8); + if (err < 0) + return 0x00; /* Arbitrary */ + + err = i2c_smbus_read_byte_data(client, reg & 0xff); + if (err < 0) { + dev_err(&client->dev, + "Failed to read from register 0x%03x, err %d\n", + (int)reg, err); + return 0x00; /* Arbitrary */ + } + return err; } /* Must be called with data->update_lock held, except during initialization */ static int w83795_write(struct i2c_client *client, u16 reg, u8 value) { - struct w83795_data *data = i2c_get_clientdata(client); - int res; - u8 new_bank = reg >> 8; + int err; - new_bank |= data->bank & 0xfc; - if (data->bank != new_bank) { - res = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, - new_bank); - if (res >= 0) - data->bank = new_bank; - else { - dev_err(&client->dev, - "set bank to %d failed, fall back " - "to bank %d, write reg 0x%x error\n", - new_bank, data->bank, reg); - goto END; - } - } + err = w83795_set_bank(client, reg >> 8); + if (err < 0) + return err; - res = i2c_smbus_write_byte_data(client, reg & 0xff, value); -END: - return res; + err = i2c_smbus_write_byte_data(client, reg & 0xff, value); + if (err < 0) + dev_err(&client->dev, + "Failed to write to register 0x%03x, err %d\n", + (int)reg, err); + return err; } static struct w83795_data *w83795_update_device(struct device *dev) From 2be381de0f1e5e0324e8b373e7a84fc9d25d05d3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 23/68] hwmon: (w83795) Improve detection routine Check for additional identification registers. Improve debugging messages on failed detection. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 82 +++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index c7f6b1fd0899..6596303566e1 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -48,6 +48,7 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define W83795_REG_VENDORID 0xfd #define W83795_REG_CHIPID 0xfe #define W83795_REG_DEVICEID 0xfb +#define W83795_REG_DEVICEID_A 0xff #define W83795_REG_I2C_ADDR 0xfc #define W83795_REG_CONFIG 0x01 @@ -1684,11 +1685,31 @@ static void w83795_init_client(struct i2c_client *client) w83795_read(client, W83795_REG_CONFIG) | 0x01); } +static int w83795_get_device_id(struct i2c_client *client) +{ + int device_id; + + device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID); + + /* Special case for rev. A chips; can't be checked first because later + revisions emulate this for compatibility */ + if (device_id < 0 || (device_id & 0xf0) != 0x50) { + int alt_id; + + alt_id = i2c_smbus_read_byte_data(client, + W83795_REG_DEVICEID_A); + if (alt_id == 0x50) + device_id = alt_id; + } + + return device_id; +} + /* Return 0 if detection is successful, -ENODEV otherwise */ static int w83795_detect(struct i2c_client *client, struct i2c_board_info *info) { - u8 tmp, bank; + int bank, vendor_id, device_id, expected, i2c_addr, config; struct i2c_adapter *adapter = client->adapter; unsigned short address = client->addr; const char *chip_name; @@ -1696,32 +1717,44 @@ static int w83795_detect(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); + if (bank < 0 || (bank & 0x7c)) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, check %s\n", + address, "bank"); + return -ENODEV; + } - tmp = bank & 0x80 ? 0x5c : 0xa3; /* Check Nuvoton vendor ID */ - if (tmp != i2c_smbus_read_byte_data(client, - W83795_REG_VENDORID)) { - pr_debug("w83795: Detection failed at check " - "vendor id\n"); + vendor_id = i2c_smbus_read_byte_data(client, W83795_REG_VENDORID); + expected = bank & 0x80 ? 0x5c : 0xa3; + if (vendor_id != expected) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, check %s\n", + address, "vendor id"); + return -ENODEV; + } + + /* Check device ID */ + device_id = w83795_get_device_id(client) | + (i2c_smbus_read_byte_data(client, W83795_REG_CHIPID) << 8); + if ((device_id >> 4) != 0x795) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, check %s\n", + address, "device id\n"); return -ENODEV; } /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR should match */ - if ((bank & 0x07) == 0 - && (i2c_smbus_read_byte_data(client, W83795_REG_I2C_ADDR) & 0x7f) != - address) { - pr_debug("w83795: Detection failed at check " - "i2c addr\n"); - return -ENODEV; - } - - /* Determine the chip type now */ - if (0x79 != i2c_smbus_read_byte_data(client, - W83795_REG_CHIPID)) { - pr_debug("w83795: Detection failed at check " - "chip id\n"); - return -ENODEV; + if ((bank & 0x07) == 0) { + i2c_addr = i2c_smbus_read_byte_data(client, + W83795_REG_I2C_ADDR); + if ((i2c_addr & 0x7f) != address) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, " + "check %s\n", address, "i2c addr"); + return -ENODEV; + } } /* Check 795 chip type: 795G or 795ADG @@ -1731,14 +1764,15 @@ static int w83795_detect(struct i2c_client *client, if ((bank & 0x07) != 0) i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank & ~0x07); - if (W83795_REG_CONFIG_CONFIG48 & - i2c_smbus_read_byte_data(client, W83795_REG_CONFIG)) { + config = i2c_smbus_read_byte_data(client, W83795_REG_CONFIG); + if (config & W83795_REG_CONFIG_CONFIG48) chip_name = "w83795adg"; - } else { + else chip_name = "w83795g"; - } strlcpy(info->type, chip_name, I2C_NAME_SIZE); + dev_info(&adapter->dev, "Found %s rev. %c at 0x%02hx\n", chip_name, + 'A' + (device_id & 0xf), address); return 0; } From 2fa09878ba1dc458b8cb6e9c8fbc8f2e5401d2b2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 24/68] hwmon: (w83795) Move files removal to a separate function Sysfs files must be removed on device removal but also when device registration fails. Move the code to a separate function to avoid code redundancy. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 81 ++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 6596303566e1..00d539a74d0c 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1777,6 +1777,35 @@ static int w83795_detect(struct i2c_client *client, return 0; } +static void w83795_remove_files(struct device *dev) +{ + struct w83795_data *data = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(w83795_in); i++) + device_remove_file(dev, &w83795_in[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) + device_remove_file(dev, &w83795_fan[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) + device_remove_file(dev, &sda_single_files[i].dev_attr); + + if (data->chip_type == w83795g) { + for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) + device_remove_file(dev, &w83795_left_reg[i].dev_attr); + } + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) + device_remove_file(dev, &w83795_temp[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) + device_remove_file(dev, &w83795_dts[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(w83795_static); i++) + device_remove_file(dev, &w83795_static[i].dev_attr); +} + static int w83795_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2065,31 +2094,8 @@ static int w83795_probe(struct i2c_client *client, return 0; - /* Unregister sysfs hooks */ exit_remove: - for (i = 0; i < ARRAY_SIZE(w83795_in); i++) - device_remove_file(dev, &w83795_in[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) - device_remove_file(dev, &w83795_fan[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) - device_remove_file(dev, &sda_single_files[i].dev_attr); - - if (data->chip_type == w83795g) { - for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) - device_remove_file(dev, &w83795_left_reg[i].dev_attr); - } - - for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) - device_remove_file(dev, &w83795_temp[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) - device_remove_file(dev, &w83795_dts[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_static); i++) - device_remove_file(dev, &w83795_static[i].dev_attr); - + w83795_remove_files(dev); kfree(data); exit: return err; @@ -2098,34 +2104,9 @@ static int w83795_probe(struct i2c_client *client, static int w83795_remove(struct i2c_client *client) { struct w83795_data *data = i2c_get_clientdata(client); - struct device *dev = &client->dev; - int i; hwmon_device_unregister(data->hwmon_dev); - - for (i = 0; i < ARRAY_SIZE(w83795_in); i++) - device_remove_file(dev, &w83795_in[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) - device_remove_file(dev, &w83795_fan[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) - device_remove_file(dev, &sda_single_files[i].dev_attr); - - if (data->chip_type == w83795g) { - for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) - device_remove_file(dev, &w83795_left_reg[i].dev_attr); - } - - for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) - device_remove_file(dev, &w83795_temp[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) - device_remove_file(dev, &w83795_dts[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_static); i++) - device_remove_file(dev, &w83795_static[i].dev_attr); - + w83795_remove_files(&client->dev); kfree(data); return 0; From 892514a6372d49ffe9749fb86cc587b4dce90733 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 25/68] hwmon: (w83795) Move file creation to a separate function too Function w83795_probe() is way too big, move file creation to a separate function to make it more readable. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 121 ++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 55 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 00d539a74d0c..309e2e5a187e 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1777,6 +1777,69 @@ static int w83795_detect(struct i2c_client *client, return 0; } +static int w83795_create_files(struct device *dev) +{ + struct w83795_data *data = dev_get_drvdata(dev); + int err, i; + + for (i = 0; i < ARRAY_SIZE(w83795_in); i++) { + if (!(data->has_in & (1 << (i / 6)))) + continue; + err = device_create_file(dev, &w83795_in[i].dev_attr); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) { + if (!(data->has_fan & (1 << (i / 5)))) + continue; + err = device_create_file(dev, &w83795_fan[i].dev_attr); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { + err = device_create_file(dev, &sda_single_files[i].dev_attr); + if (err) + return err; + } + + if (data->chip_type == w83795g) { + for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) { + err = device_create_file(dev, + &w83795_left_reg[i].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { + if (!(data->has_temp & (1 << (i / 29)))) + continue; + err = device_create_file(dev, &w83795_temp[i].dev_attr); + if (err) + return err; + } + + if (data->enable_dts != 0) { + for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { + if (!(data->has_dts & (1 << (i / 8)))) + continue; + err = device_create_file(dev, &w83795_dts[i].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(w83795_static); i++) { + err = device_create_file(dev, &w83795_static[i].dev_attr); + if (err) + return err; + } + + return 0; +} + static void w83795_remove_files(struct device *dev) { struct w83795_data *data = dev_get_drvdata(dev); @@ -2030,61 +2093,9 @@ static int w83795_probe(struct i2c_client *client, data->beep_enable = (w83795_read(client, W83795_REG_BEEP(5)) >> 7) & 0x01; - /* Register sysfs hooks */ - for (i = 0; i < ARRAY_SIZE(w83795_in); i++) { - if (!(data->has_in & (1 << (i / 6)))) - continue; - err = device_create_file(dev, &w83795_in[i].dev_attr); - if (err) - goto exit_remove; - } - - for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) { - if (!(data->has_fan & (1 << (i / 5)))) - continue; - err = device_create_file(dev, &w83795_fan[i].dev_attr); - if (err) - goto exit_remove; - } - - for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { - err = device_create_file(dev, &sda_single_files[i].dev_attr); - if (err) - goto exit_remove; - } - - for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { - if (!(data->has_temp & (1 << (i / 29)))) - continue; - err = device_create_file(dev, &w83795_temp[i].dev_attr); - if (err) - goto exit_remove; - } - - if (data->enable_dts != 0) { - for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { - if (!(data->has_dts & (1 << (i / 8)))) - continue; - err = device_create_file(dev, &w83795_dts[i].dev_attr); - if (err) - goto exit_remove; - } - } - - if (data->chip_type == w83795g) { - for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) { - err = device_create_file(dev, - &w83795_left_reg[i].dev_attr); - if (err) - goto exit_remove; - } - } - - for (i = 0; i < ARRAY_SIZE(w83795_static); i++) { - err = device_create_file(dev, &w83795_static[i].dev_attr); - if (err) - goto exit_remove; - } + err = w83795_create_files(dev); + if (err) + goto exit_remove; data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { From 6f3dcde9c6dcbbe781a2a98900552bcb989733e3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 26/68] hwmon: (w83795) Merge w83795_create_files and w83795_remove_files Functions w83795_create_files and w83795_remove_files iterate over the same set of files, just calling a different function. Merge them into a single function which takes the action as a parameter. This saves code, and also ensure that file creation and deletion are in sync. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 55 +++++++++++++----------------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 309e2e5a187e..ca8b0aec53e6 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1777,7 +1777,8 @@ static int w83795_detect(struct i2c_client *client, return 0; } -static int w83795_create_files(struct device *dev) +static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, + const struct device_attribute *)) { struct w83795_data *data = dev_get_drvdata(dev); int err, i; @@ -1785,7 +1786,7 @@ static int w83795_create_files(struct device *dev) for (i = 0; i < ARRAY_SIZE(w83795_in); i++) { if (!(data->has_in & (1 << (i / 6)))) continue; - err = device_create_file(dev, &w83795_in[i].dev_attr); + err = fn(dev, &w83795_in[i].dev_attr); if (err) return err; } @@ -1793,21 +1794,20 @@ static int w83795_create_files(struct device *dev) for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) { if (!(data->has_fan & (1 << (i / 5)))) continue; - err = device_create_file(dev, &w83795_fan[i].dev_attr); + err = fn(dev, &w83795_fan[i].dev_attr); if (err) return err; } for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { - err = device_create_file(dev, &sda_single_files[i].dev_attr); + err = fn(dev, &sda_single_files[i].dev_attr); if (err) return err; } if (data->chip_type == w83795g) { for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) { - err = device_create_file(dev, - &w83795_left_reg[i].dev_attr); + err = fn(dev, &w83795_left_reg[i].dev_attr); if (err) return err; } @@ -1816,7 +1816,7 @@ static int w83795_create_files(struct device *dev) for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { if (!(data->has_temp & (1 << (i / 29)))) continue; - err = device_create_file(dev, &w83795_temp[i].dev_attr); + err = fn(dev, &w83795_temp[i].dev_attr); if (err) return err; } @@ -1825,14 +1825,14 @@ static int w83795_create_files(struct device *dev) for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { if (!(data->has_dts & (1 << (i / 8)))) continue; - err = device_create_file(dev, &w83795_dts[i].dev_attr); + err = fn(dev, &w83795_dts[i].dev_attr); if (err) return err; } } for (i = 0; i < ARRAY_SIZE(w83795_static); i++) { - err = device_create_file(dev, &w83795_static[i].dev_attr); + err = fn(dev, &w83795_static[i].dev_attr); if (err) return err; } @@ -1840,33 +1840,12 @@ static int w83795_create_files(struct device *dev) return 0; } -static void w83795_remove_files(struct device *dev) +/* We need a wrapper that fits in w83795_handle_files */ +static int device_remove_file_wrapper(struct device *dev, + const struct device_attribute *attr) { - struct w83795_data *data = dev_get_drvdata(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(w83795_in); i++) - device_remove_file(dev, &w83795_in[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) - device_remove_file(dev, &w83795_fan[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) - device_remove_file(dev, &sda_single_files[i].dev_attr); - - if (data->chip_type == w83795g) { - for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) - device_remove_file(dev, &w83795_left_reg[i].dev_attr); - } - - for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) - device_remove_file(dev, &w83795_temp[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) - device_remove_file(dev, &w83795_dts[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(w83795_static); i++) - device_remove_file(dev, &w83795_static[i].dev_attr); + device_remove_file(dev, attr); + return 0; } static int w83795_probe(struct i2c_client *client, @@ -2093,7 +2072,7 @@ static int w83795_probe(struct i2c_client *client, data->beep_enable = (w83795_read(client, W83795_REG_BEEP(5)) >> 7) & 0x01; - err = w83795_create_files(dev); + err = w83795_handle_files(dev, device_create_file); if (err) goto exit_remove; @@ -2106,7 +2085,7 @@ static int w83795_probe(struct i2c_client *client, return 0; exit_remove: - w83795_remove_files(dev); + w83795_handle_files(dev, device_remove_file_wrapper); kfree(data); exit: return err; @@ -2117,7 +2096,7 @@ static int w83795_remove(struct i2c_client *client) struct w83795_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - w83795_remove_files(&client->dev); + w83795_handle_files(&client->dev, device_remove_file_wrapper); kfree(data); return 0; From 87df0dad3e024538d2d0d2ce786230e639c2ea8b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:45 +0200 Subject: [PATCH 27/68] hwmon: (w83795) Use 2D arrays for many device attributes Use 2D arrays for in, fan, temp and dts device attributes. Using linear arrays is too risky as we have to skip some groups depending on the device model and configuration. Adding or removing an attribute would let the driver build silently but then it would crash at runtime. With 2D arrays, the consistency checking happens at build time, which is much safer. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 66 +++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index ca8b0aec53e6..393f1025f6c8 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1447,7 +1447,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, #define NOT_USED -1 -#define SENSOR_ATTR_IN(index) \ +#define SENSOR_ATTR_IN(index) { \ SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ IN_READ, index), \ SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \ @@ -1458,9 +1458,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, NULL, ALARM_STATUS, index + ((index > 14) ? 1 : 0)), \ SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \ show_alarm_beep, store_beep, BEEP_ENABLE, \ - index + ((index > 14) ? 1 : 0)) + index + ((index > 14) ? 1 : 0)) } -#define SENSOR_ATTR_FAN(index) \ +#define SENSOR_ATTR_FAN(index) { \ SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ NULL, FAN_INPUT, index - 1), \ SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \ @@ -1468,7 +1468,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \ NULL, ALARM_STATUS, index + 31), \ SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ - show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) + show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) } #define SENSOR_ATTR_PWM(index) \ SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ @@ -1488,7 +1488,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(speed_cruise##index##_target, S_IWUSR | S_IRUGO, \ show_fanin, store_fanin, FANIN_TARGET, index - 1) -#define SENSOR_ATTR_DTS(index) \ +#define SENSOR_ATTR_DTS(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \ show_dts_mode, NULL, NOT_USED, index - 7), \ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_dts, \ @@ -1504,9 +1504,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ show_alarm_beep, NULL, ALARM_STATUS, index + 17), \ SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ - show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) + show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) } -#define SENSOR_ATTR_TEMP(index) \ +#define SENSOR_ATTR_TEMP(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \ show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ @@ -1568,10 +1568,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\ show_sf4_temp, store_sf4_temp, 5, index - 1), \ SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\ - show_sf4_temp, store_sf4_temp, 6, index - 1) + show_sf4_temp, store_sf4_temp, 6, index - 1) } -static struct sensor_device_attribute_2 w83795_in[] = { +static struct sensor_device_attribute_2 w83795_in[][5] = { SENSOR_ATTR_IN(0), SENSOR_ATTR_IN(1), SENSOR_ATTR_IN(2), @@ -1595,7 +1595,7 @@ static struct sensor_device_attribute_2 w83795_in[] = { SENSOR_ATTR_IN(20), }; -static struct sensor_device_attribute_2 w83795_fan[] = { +static struct sensor_device_attribute_2 w83795_fan[][4] = { SENSOR_ATTR_FAN(1), SENSOR_ATTR_FAN(2), SENSOR_ATTR_FAN(3), @@ -1612,7 +1612,7 @@ static struct sensor_device_attribute_2 w83795_fan[] = { SENSOR_ATTR_FAN(14), }; -static struct sensor_device_attribute_2 w83795_temp[] = { +static struct sensor_device_attribute_2 w83795_temp[][29] = { SENSOR_ATTR_TEMP(1), SENSOR_ATTR_TEMP(2), SENSOR_ATTR_TEMP(3), @@ -1621,7 +1621,7 @@ static struct sensor_device_attribute_2 w83795_temp[] = { SENSOR_ATTR_TEMP(6), }; -static struct sensor_device_attribute_2 w83795_dts[] = { +static struct sensor_device_attribute_2 w83795_dts[][8] = { SENSOR_ATTR_DTS(7), SENSOR_ATTR_DTS(8), SENSOR_ATTR_DTS(9), @@ -1781,22 +1781,26 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, const struct device_attribute *)) { struct w83795_data *data = dev_get_drvdata(dev); - int err, i; + int err, i, j; for (i = 0; i < ARRAY_SIZE(w83795_in); i++) { - if (!(data->has_in & (1 << (i / 6)))) + if (!(data->has_in & (1 << i))) continue; - err = fn(dev, &w83795_in[i].dev_attr); - if (err) - return err; + for (j = 0; j < ARRAY_SIZE(w83795_in[0]); j++) { + err = fn(dev, &w83795_in[i][j].dev_attr); + if (err) + return err; + } } for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) { - if (!(data->has_fan & (1 << (i / 5)))) + if (!(data->has_fan & (1 << i))) continue; - err = fn(dev, &w83795_fan[i].dev_attr); - if (err) - return err; + for (j = 0; j < ARRAY_SIZE(w83795_fan[0]); j++) { + err = fn(dev, &w83795_fan[i][j].dev_attr); + if (err) + return err; + } } for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { @@ -1814,20 +1818,24 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, } for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { - if (!(data->has_temp & (1 << (i / 29)))) + if (!(data->has_temp & (1 << i))) continue; - err = fn(dev, &w83795_temp[i].dev_attr); - if (err) - return err; + for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) { + err = fn(dev, &w83795_temp[i][j].dev_attr); + if (err) + return err; + } } if (data->enable_dts != 0) { for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { - if (!(data->has_dts & (1 << (i / 8)))) + if (!(data->has_dts & (1 << i))) continue; - err = fn(dev, &w83795_dts[i].dev_attr); - if (err) - return err; + for (j = 0; j < ARRAY_SIZE(w83795_dts[0]); j++) { + err = fn(dev, &w83795_dts[i][j].dev_attr); + if (err) + return err; + } } } From b5f6a90a0bbedc07a0b0b82b83aa716c7203fcd2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 28/68] hwmon: (w83795) Move PWM attributes to a dedidated array Use a dedicated 2D array for PWM attributes. This way, PWM attributes are handled the same way as other attributes, this is more consistent. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 393f1025f6c8..76e89c7bc465 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1470,7 +1470,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) } -#define SENSOR_ATTR_PWM(index) \ +#define SENSOR_ATTR_PWM(index) { \ SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ store_pwm, PWM_OUTPUT, index - 1), \ SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ @@ -1482,7 +1482,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(fan##index##_div, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_DIV, index - 1), \ SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ - show_pwm_enable, store_pwm_enable, NOT_USED, index - 1) + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1) } #define SENSOR_ATTR_FANIN_TARGET(index) \ SENSOR_ATTR_2(speed_cruise##index##_target, S_IWUSR | S_IRUGO, \ @@ -1641,13 +1641,11 @@ static struct sensor_device_attribute_2 w83795_static[] = { SENSOR_ATTR_FANIN_TARGET(6), SENSOR_ATTR_FANIN_TARGET(7), SENSOR_ATTR_FANIN_TARGET(8), - SENSOR_ATTR_PWM(1), - SENSOR_ATTR_PWM(2), }; -/* all registers existed in 795g than 795adg, - * like PWM3 - PWM8 */ -static struct sensor_device_attribute_2 w83795_left_reg[] = { +static struct sensor_device_attribute_2 w83795_pwm[][6] = { + SENSOR_ATTR_PWM(1), + SENSOR_ATTR_PWM(2), SENSOR_ATTR_PWM(3), SENSOR_ATTR_PWM(4), SENSOR_ATTR_PWM(5), @@ -1809,9 +1807,9 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, return err; } - if (data->chip_type == w83795g) { - for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) { - err = fn(dev, &w83795_left_reg[i].dev_attr); + for (i = 0; i < data->has_pwm; i++) { + for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) { + err = fn(dev, &w83795_pwm[i][j].dev_attr); if (err) return err; } From b2cc528e5838d744892b30c5104bd872a439088f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 29/68] hwmon: (w83795) Only create fan[1-8]_target files when needed Only create fan[1-8]_target files when the fan in question can be controlled (PWM output is present.) Also name these files according to the standard. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 76e89c7bc465..00d64b0c200f 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1482,11 +1482,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(fan##index##_div, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_DIV, index - 1), \ SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ - show_pwm_enable, store_pwm_enable, NOT_USED, index - 1) } - -#define SENSOR_ATTR_FANIN_TARGET(index) \ - SENSOR_ATTR_2(speed_cruise##index##_target, S_IWUSR | S_IRUGO, \ - show_fanin, store_fanin, FANIN_TARGET, index - 1) + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ + SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \ + show_fanin, store_fanin, FANIN_TARGET, index - 1) } #define SENSOR_ATTR_DTS(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \ @@ -1632,18 +1630,7 @@ static struct sensor_device_attribute_2 w83795_dts[][8] = { SENSOR_ATTR_DTS(14), }; -static struct sensor_device_attribute_2 w83795_static[] = { - SENSOR_ATTR_FANIN_TARGET(1), - SENSOR_ATTR_FANIN_TARGET(2), - SENSOR_ATTR_FANIN_TARGET(3), - SENSOR_ATTR_FANIN_TARGET(4), - SENSOR_ATTR_FANIN_TARGET(5), - SENSOR_ATTR_FANIN_TARGET(6), - SENSOR_ATTR_FANIN_TARGET(7), - SENSOR_ATTR_FANIN_TARGET(8), -}; - -static struct sensor_device_attribute_2 w83795_pwm[][6] = { +static struct sensor_device_attribute_2 w83795_pwm[][7] = { SENSOR_ATTR_PWM(1), SENSOR_ATTR_PWM(2), SENSOR_ATTR_PWM(3), @@ -1837,12 +1824,6 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, } } - for (i = 0; i < ARRAY_SIZE(w83795_static); i++) { - err = fn(dev, &w83795_static[i].dev_attr); - if (err) - return err; - } - return 0; } From 86ef4d2fd5921ff0bcfd1c0d88403a08862087bc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 30/68] hwmon: (w83795) Add const markers Attribute structures can be made const. Same for the I2C address list. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 00d64b0c200f..b45416011c2a 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -36,7 +36,9 @@ #include /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { + 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; static int reset; @@ -70,7 +72,7 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define TEMP_CTRL_SHIFT 4 #define TEMP_CTRL_HASIN_SHIFT 5 /* temp mode may effect VSEN17-12 (in20-15) */ -static u16 W83795_REG_TEMP_CTRL[][6] = { +static const u16 W83795_REG_TEMP_CTRL[][6] = { /* Disable, TD, VSEN, TR, register shift value, has_in shift num */ {0x00, 0x01, 0x02, 0x03, 0, 17}, /* TR1 */ {0x00, 0x04, 0x08, 0x0C, 2, 18}, /* TR2 */ @@ -87,7 +89,7 @@ static u16 W83795_REG_TEMP_CTRL[][6] = { #define TEMP_WARN_HYST 4 /* only crit and crit_hyst affect real-time alarm status * current crit crit_hyst warn warn_hyst */ -static u16 W83795_REG_TEMP[][5] = { +static const u16 W83795_REG_TEMP[][5] = { {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */ {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */ {0x23, 0x9e, 0x9f, 0xa0, 0xa1}, /* TD3/TR3 */ @@ -1593,7 +1595,7 @@ static struct sensor_device_attribute_2 w83795_in[][5] = { SENSOR_ATTR_IN(20), }; -static struct sensor_device_attribute_2 w83795_fan[][4] = { +static const struct sensor_device_attribute_2 w83795_fan[][4] = { SENSOR_ATTR_FAN(1), SENSOR_ATTR_FAN(2), SENSOR_ATTR_FAN(3), @@ -1610,7 +1612,7 @@ static struct sensor_device_attribute_2 w83795_fan[][4] = { SENSOR_ATTR_FAN(14), }; -static struct sensor_device_attribute_2 w83795_temp[][29] = { +static const struct sensor_device_attribute_2 w83795_temp[][29] = { SENSOR_ATTR_TEMP(1), SENSOR_ATTR_TEMP(2), SENSOR_ATTR_TEMP(3), @@ -1619,7 +1621,7 @@ static struct sensor_device_attribute_2 w83795_temp[][29] = { SENSOR_ATTR_TEMP(6), }; -static struct sensor_device_attribute_2 w83795_dts[][8] = { +static const struct sensor_device_attribute_2 w83795_dts[][8] = { SENSOR_ATTR_DTS(7), SENSOR_ATTR_DTS(8), SENSOR_ATTR_DTS(9), @@ -1630,7 +1632,7 @@ static struct sensor_device_attribute_2 w83795_dts[][8] = { SENSOR_ATTR_DTS(14), }; -static struct sensor_device_attribute_2 w83795_pwm[][7] = { +static const struct sensor_device_attribute_2 w83795_pwm[][7] = { SENSOR_ATTR_PWM(1), SENSOR_ATTR_PWM(2), SENSOR_ATTR_PWM(3), @@ -1641,7 +1643,7 @@ static struct sensor_device_attribute_2 w83795_pwm[][7] = { SENSOR_ATTR_PWM(8), }; -static struct sensor_device_attribute_2 sda_single_files[] = { +static const struct sensor_device_attribute_2 sda_single_files[] = { SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep, store_chassis_clear, ALARM_STATUS, 46), SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable, From 80646b95863ba282330d07290c79254c2f002139 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 31/68] hwmon: (w83795) Only start monitoring if needed This saves an SMBus write if monitoring was already enabled. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index b45416011c2a..62deffcc64a3 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -55,6 +55,7 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define W83795_REG_I2C_ADDR 0xfc #define W83795_REG_CONFIG 0x01 #define W83795_REG_CONFIG_CONFIG48 0x04 +#define W83795_REG_CONFIG_START 0x01 /* Multi-Function Pin Ctrl Registers */ #define W83795_REG_VOLT_CTRL1 0x02 @@ -1664,12 +1665,18 @@ static const struct sensor_device_attribute_2 sda_single_files[] = { static void w83795_init_client(struct i2c_client *client) { + u8 config; + if (reset) w83795_write(client, W83795_REG_CONFIG, 0x80); - /* Start monitoring */ - w83795_write(client, W83795_REG_CONFIG, - w83795_read(client, W83795_REG_CONFIG) | 0x01); + /* Start monitoring if needed */ + config = w83795_read(client, W83795_REG_CONFIG); + if (!(config & W83795_REG_CONFIG_START)) { + dev_info(&client->dev, "Enabling monitoring operations\n"); + w83795_write(client, W83795_REG_CONFIG, + config | W83795_REG_CONFIG_START); + } } static int w83795_get_device_id(struct i2c_client *client) From 6f9dfd85f7b14dd3ea87106909dc54def08947b1 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 32/68] hwmon: (w83795) Fix in17-in20 gain factor Gain bit set means 1x gain and cleared means 8x gain, not the other way around. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 62deffcc64a3..77b4f7859df9 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1335,7 +1335,7 @@ show_in(struct device *dev, struct device_attribute *attr, char *buf) case IN_READ: /* calculate this value again by sensors as sensors3.conf */ if ((index >= 17) && - ((data->has_gain >> (index - 17)) & 1)) + !((data->has_gain >> (index - 17)) & 1)) val *= 8; break; case IN_MAX: @@ -1345,7 +1345,7 @@ show_in(struct device *dev, struct device_attribute *attr, char *buf) val |= (data->in_lsb[lsb_idx][nr] >> IN_LSB_SHIFT_IDX[lsb_idx][IN_LSB_SHIFT]) & 0x03; if ((index >= 17) && - ((data->has_gain >> (index - 17)) & 1)) + !((data->has_gain >> (index - 17)) & 1)) val *= 8; break; } @@ -1373,7 +1373,7 @@ store_in(struct device *dev, struct device_attribute *attr, val = in_to_reg(index, val); if ((index >= 17) && - ((data->has_gain >> (index - 17)) & 1)) + !((data->has_gain >> (index - 17)) & 1)) val /= 8; val = SENSORS_LIMIT(val, 0, 0x3FF); mutex_lock(&data->update_lock); From 71caf46fbf39207a324a16ddb6d8ef37c05777b9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 33/68] hwmon: (w83795) Clean up probe function * The data structure is zalloc'd, so no need to set individual fields to 0 explicitly. * Refactor the handling of pins that can be used for either temperature or voltage monitoring. * Misc other clean-ups. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 91 ++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 57 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 77b4f7859df9..b797b9ce2907 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1844,6 +1844,26 @@ static int device_remove_file_wrapper(struct device *dev, return 0; } +/* Check pins that can be used for either temperature or voltage monitoring */ +static void w83795_apply_temp_config(struct w83795_data *data, u8 config, + int temp_chan, int in_chan) +{ + /* config is a 2-bit value */ + switch (config) { + case 0x2: /* Voltage monitoring */ + data->has_in |= 1 << in_chan; + break; + case 0x1: /* Thermal diode */ + if (temp_chan >= 4) + break; + data->temp_mode |= 1 << temp_chan; + /* fall through */ + case 0x3: /* Thermistor */ + data->has_temp |= 1 << temp_chan; + break; + } +} + static int w83795_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1851,7 +1871,7 @@ static int w83795_probe(struct i2c_client *client, u8 tmp; struct device *dev = &client->dev; struct w83795_data *data; - int err = 0; + int err; data = kzalloc(sizeof(struct w83795_data), GFP_KERNEL); if (!data) { @@ -1867,68 +1887,26 @@ static int w83795_probe(struct i2c_client *client, /* Initialize the chip */ w83795_init_client(client); - data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1); - data->has_in |= w83795_read(client, W83795_REG_VOLT_CTRL2) << 8; - /* VSEN11-9 not for 795adg */ - if (data->chip_type == w83795adg) - data->has_in &= 0xf8ff; - data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1); - data->has_fan |= w83795_read(client, W83795_REG_FANIN_CTRL2) << 8; + /* Check which voltages and fans are present */ + data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1) + | (w83795_read(client, W83795_REG_VOLT_CTRL2) << 8); + data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1) + | (w83795_read(client, W83795_REG_FANIN_CTRL2) << 8); - /* VDSEN12-17 and TR1-6, TD1-4 use same register */ + /* Check which analog temperatures and extra voltages are present */ tmp = w83795_read(client, W83795_REG_TEMP_CTRL1); if (tmp & 0x20) data->enable_dts = 1; - else - data->enable_dts = 0; - data->has_temp = 0; - data->temp_mode = 0; - if (tmp & 0x08) { - if (tmp & 0x04) - data->has_temp |= 0x20; - else - data->has_in |= 0x10000; - } - if (tmp & 0x02) { - if (tmp & 0x01) - data->has_temp |= 0x10; - else - data->has_in |= 0x8000; - } + w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 5, 16); + w83795_apply_temp_config(data, tmp & 0x3, 4, 15); tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); - if (tmp & 0x40) { - data->has_temp |= 0x08; - if (!(tmp & 0x80)) - data->temp_mode |= 0x08; - } else if (tmp & 0x80) { - data->has_in |= 0x100000; - } - if (tmp & 0x10) { - data->has_temp |= 0x04; - if (!(tmp & 0x20)) - data->temp_mode |= 0x04; - } else if (tmp & 0x20) { - data->has_in |= 0x80000; - } - if (tmp & 0x04) { - data->has_temp |= 0x02; - if (!(tmp & 0x08)) - data->temp_mode |= 0x02; - } else if (tmp & 0x08) { - data->has_in |= 0x40000; - } - if (tmp & 0x01) { - data->has_temp |= 0x01; - if (!(tmp & 0x02)) - data->temp_mode |= 0x01; - } else if (tmp & 0x02) { - data->has_in |= 0x20000; - } + w83795_apply_temp_config(data, tmp >> 6, 3, 20); + w83795_apply_temp_config(data, (tmp >> 4) & 0x3, 2, 19); + w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 1, 18); + w83795_apply_temp_config(data, tmp & 0x3, 0, 17); /* Check DTS enable status */ - if (data->enable_dts == 0) { - data->has_dts = 0; - } else { + if (data->enable_dts) { if (1 & w83795_read(client, W83795_REG_DTSC)) data->enable_dts |= 2; data->has_dts = w83795_read(client, W83795_REG_DTSE); @@ -2017,7 +1995,6 @@ static int w83795_probe(struct i2c_client *client, data->has_pwm = 2; data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1); data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2); - /* w83795adg only support pwm2-0 */ for (i = 0; i < W83795_REG_TEMP_NUM; i++) data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); From 6c82b2f3fb31e43a9b898769afd2151ca64986a4 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 34/68] hwmon: (w83795) Fix LSB reading of fan speeds Misplaced parentheses caused the wrong register value to be read, resulting in random LSB for fan speed values and limits. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index b797b9ce2907..24e44245e548 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -272,7 +272,7 @@ static inline u16 in_to_reg(u8 index, u16 val) static inline unsigned long fan_from_reg(u16 val) { - if ((val >= 0xff0) || (val == 0)) + if ((val == 0xfff) || (val == 0)) return 0; return 1350000UL / val; } @@ -456,7 +456,7 @@ static struct w83795_data *w83795_update_device(struct device *dev) continue; data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; data->fan[i] |= - (w83795_read(client, W83795_REG_VRLSB >> 4)) & 0x0F; + (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F; } /* Update temperature */ @@ -1940,11 +1940,11 @@ static int w83795_probe(struct i2c_client *client, data->fan_min[i] = w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; data->fan_min[i] |= - (w83795_read(client, W83795_REG_FAN_MIN_LSB(i) >> - W83795_REG_FAN_MIN_LSB_SHIFT(i))) & 0x0F; + (w83795_read(client, W83795_REG_FAN_MIN_LSB(i)) >> + W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; data->fan[i] |= - (w83795_read(client, W83795_REG_VRLSB >> 4)) & 0x0F; + (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F; } /* temperature and limits */ From 0e256018b0f35d1b22ca37e1d0e207f7ba3d0076 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:46 +0200 Subject: [PATCH 35/68] hwmon: (w83795) Add support for dynamic in0-2 limits The W83795G can be configured to set the in0, in1 and/or in2 voltage limits dynamically based on VID input pins. Switch the respective sysfs attributes to read-only. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 24e44245e548..458fb297f696 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -316,6 +316,7 @@ struct w83795_data { u8 bank; u32 has_in; /* Enable monitor VIN or not */ + u8 has_dyn_in; /* Only in2-0 can have this */ u16 in[21][3]; /* Register value, read/high/low */ u8 in_lsb[10][3]; /* LSB Register value, high/low */ u8 has_gain; /* has gain: in17-20 * 8 */ @@ -450,6 +451,23 @@ static struct w83795_data *w83795_update_device(struct device *dev) data->in[i][IN_READ] = tmp; } + /* in0-2 can have dynamic limits (W83795G only) */ + if (data->has_dyn_in) { + u8 lsb_max = w83795_read(client, IN_LSB_REG(0, IN_MAX)); + u8 lsb_low = w83795_read(client, IN_LSB_REG(0, IN_LOW)); + + for (i = 0; i < 3; i++) { + if (!(data->has_dyn_in & (1 << i))) + continue; + data->in[i][IN_MAX] = + w83795_read(client, W83795_REG_IN[i][IN_MAX]); + data->in[i][IN_LOW] = + w83795_read(client, W83795_REG_IN[i][IN_LOW]); + data->in_lsb[i][IN_MAX] = (lsb_max >> (2 * i)) & 0x03; + data->in_lsb[i][IN_LOW] = (lsb_low >> (2 * i)) & 0x03; + } + } + /* Update fan */ for (i = 0; i < ARRAY_SIZE(data->fan); i++) { if (!(data->has_fan & (1 << i))) @@ -1450,6 +1468,8 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, #define NOT_USED -1 +/* Don't change the attribute order, _max and _min are accessed by index + * somewhere else in the code */ #define SENSOR_ATTR_IN(index) { \ SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ IN_READ, index), \ @@ -1844,6 +1864,39 @@ static int device_remove_file_wrapper(struct device *dev, return 0; } +static void w83795_check_dynamic_in_limits(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + u8 vid_ctl; + int i, err_max, err_min; + + vid_ctl = w83795_read(client, W83795_REG_VID_CTRL); + + /* Return immediately if VRM isn't configured */ + if ((vid_ctl & 0x07) == 0x00 || (vid_ctl & 0x07) == 0x07) + return; + + data->has_dyn_in = (vid_ctl >> 3) & 0x07; + for (i = 0; i < 2; i++) { + if (!(data->has_dyn_in & (1 << i))) + continue; + + /* Voltage limits in dynamic mode, switch to read-only */ + err_max = sysfs_chmod_file(&client->dev.kobj, + &w83795_in[i][2].dev_attr.attr, + S_IRUGO); + err_min = sysfs_chmod_file(&client->dev.kobj, + &w83795_in[i][3].dev_attr.attr, + S_IRUGO); + if (err_max || err_min) + dev_warn(&client->dev, "Failed to set in%d limits " + "read-only (%d, %d)\n", i, err_max, err_min); + else + dev_info(&client->dev, "in%d limits set dynamically " + "from VID\n", i); + } +} + /* Check pins that can be used for either temperature or voltage monitoring */ static void w83795_apply_temp_config(struct w83795_data *data, u8 config, int temp_chan, int in_chan) @@ -2049,6 +2102,9 @@ static int w83795_probe(struct i2c_client *client, if (err) goto exit_remove; + if (data->chip_type == w83795g) + w83795_check_dynamic_in_limits(client); + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); From 01879a855fb6bdb3fb820344a7a145de8a5cdbda Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 36/68] hwmon: (w83795) Fix PWM duty cycle frequency attributes The PWM duty cycle frequenty attributes are improperly named (fanN_div instead of pwmN_div) and contain raw values instead of actual frequencies. Rename them and fix their contents. Also improve the logic when the user asks for a new frequency, to always pick the closest supported frequency. The algorithm could certainly be optimized, but the operation is infrequent enough that I don't think it's worth the effort. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 90 +++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 458fb297f696..58c61f11ed66 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -200,7 +200,6 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define W83795_REG_FCMS2 0x208 #define W83795_REG_TFMR(index) (0x202 + (index)) #define W83795_REG_FOMC 0x20F -#define W83795_REG_FOPFP(index) (0x218 + (index)) #define W83795_REG_TSS(index) (0x209 + (index)) @@ -208,18 +207,13 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define PWM_START 1 #define PWM_NONSTOP 2 #define PWM_STOP_TIME 3 -#define PWM_DIV 4 +#define PWM_FREQ 4 #define W83795_REG_PWM(index, nr) \ (((nr) == 0 ? 0x210 : \ (nr) == 1 ? 0x220 : \ (nr) == 2 ? 0x228 : \ (nr) == 3 ? 0x230 : 0x218) + (index)) -#define W83795_REG_FOPFP_DIV(index) \ - (((index) < 8) ? ((index) + 1) : \ - ((index) == 8) ? 12 : \ - (16 << ((index) - 9))) - #define W83795_REG_FTSH(index) (0x240 + (index) * 2) #define W83795_REG_FTSL(index) (0x241 + (index) * 2) #define W83795_REG_TFTS 0x250 @@ -304,6 +298,50 @@ static inline s8 temp_to_reg(long val, s8 min, s8 max) return SENSORS_LIMIT((val < 0 ? -val : val) / 1000, min, max); } +static const u16 pwm_freq_cksel0[16] = { + 1024, 512, 341, 256, 205, 171, 146, 128, + 85, 64, 32, 16, 8, 4, 2, 1 +}; + +static unsigned int pwm_freq_from_reg(u8 reg, u16 clkin) +{ + unsigned long base_clock; + + if (reg & 0x80) { + base_clock = clkin * 1000 / ((clkin == 48000) ? 384 : 256); + return base_clock / ((reg & 0x7f) + 1); + } else + return pwm_freq_cksel0[reg & 0x0f]; +} + +static u8 pwm_freq_to_reg(unsigned long val, u16 clkin) +{ + unsigned long base_clock; + u8 reg0, reg1; + unsigned long best0, best1; + + /* Best fit for cksel = 0 */ + for (reg0 = 0; reg0 < ARRAY_SIZE(pwm_freq_cksel0) - 1; reg0++) { + if (val > (pwm_freq_cksel0[reg0] + + pwm_freq_cksel0[reg0 + 1]) / 2) + break; + } + if (val < 375) /* cksel = 1 can't beat this */ + return reg0; + best0 = pwm_freq_cksel0[reg0]; + + /* Best fit for cksel = 1 */ + base_clock = clkin * 1000 / ((clkin == 48000) ? 384 : 256); + reg1 = SENSORS_LIMIT(DIV_ROUND_CLOSEST(base_clock, val), 1, 128); + best1 = base_clock / reg1; + reg1 = 0x80 | (reg1 - 1); + + /* Choose the closest one */ + if (abs(val - best0) > abs(val - best1)) + return reg1; + else + return reg0; +} enum chip_types {w83795g, w83795adg}; @@ -343,7 +381,8 @@ struct w83795_data { * no config register, only affected by chip * type */ u8 pwm[8][5]; /* Register value, output, start, non stop, stop - * time, div */ + * time, freq */ + u16 clkin; /* CLKIN frequency in kHz */ u8 pwm_fcms[2]; /* Register value */ u8 pwm_tfmr[6]; /* Register value */ u8 pwm_fomc; /* Register value */ @@ -688,14 +727,14 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf) to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; - u16 val; + unsigned int val; switch (nr) { case PWM_STOP_TIME: val = time_from_reg(data->pwm[index][nr]); break; - case PWM_DIV: - val = W83795_REG_FOPFP_DIV(data->pwm[index][nr] & 0x0f); + case PWM_FREQ: + val = pwm_freq_from_reg(data->pwm[index][nr], data->clkin); break; default: val = data->pwm[index][nr]; @@ -716,7 +755,6 @@ store_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->nr; int index = sensor_attr->index; unsigned long val; - int i; if (strict_strtoul(buf, 10, &val) < 0) return -EINVAL; @@ -726,28 +764,17 @@ store_pwm(struct device *dev, struct device_attribute *attr, case PWM_STOP_TIME: val = time_to_reg(val); break; - case PWM_DIV: - for (i = 0; i < 16; i++) { - if (W83795_REG_FOPFP_DIV(i) == val) { - val = i; - break; - } - } - if (i >= 16) - goto err_end; - val |= w83795_read(client, W83795_REG_PWM(index, nr)) & 0x80; + case PWM_FREQ: + val = pwm_freq_to_reg(val, data->clkin); break; default: val = SENSORS_LIMIT(val, 0, 0xff); break; } w83795_write(client, W83795_REG_PWM(index, nr), val); - data->pwm[index][nr] = val & 0xff; + data->pwm[index][nr] = val; mutex_unlock(&data->update_lock); return count; -err_end: - mutex_unlock(&data->update_lock); - return -EINVAL; } static ssize_t @@ -1502,8 +1529,8 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, show_pwm, store_pwm, PWM_START, index - 1), \ SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \ - SENSOR_ATTR_2(fan##index##_div, S_IWUSR | S_IRUGO, \ - show_pwm, store_pwm, PWM_DIV, index - 1), \ + SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_FREQ, index - 1), \ SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \ @@ -1685,6 +1712,10 @@ static const struct sensor_device_attribute_2 sda_single_files[] = { static void w83795_init_client(struct i2c_client *client) { + struct w83795_data *data = i2c_get_clientdata(client); + static const u16 clkin[4] = { /* in kHz */ + 14318, 24000, 33333, 48000 + }; u8 config; if (reset) @@ -1697,6 +1728,9 @@ static void w83795_init_client(struct i2c_client *client) w83795_write(client, W83795_REG_CONFIG, config | W83795_REG_CONFIG_START); } + + data->clkin = clkin[(config >> 3) & 0x3]; + dev_dbg(&client->dev, "clkin = %u kHz\n", data->clkin); } static int w83795_get_device_id(struct i2c_client *client) From a0ce402fb006bd694436be8c8522fbe5e6823ac1 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 37/68] hwmon: (w83795) Rename temperature limit attributes Follow the standard for temperature limit attribute naming, so that libsensors will pick the values. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 58c61f11ed66..5ce276b67528 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1541,13 +1541,13 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, show_dts_mode, NULL, NOT_USED, index - 7), \ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_dts, \ NULL, NOT_USED, index - 7), \ - SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_dts_ext, \ + SENSOR_ATTR_2(temp##index##_crit, S_IRUGO | S_IWUSR, show_dts_ext, \ store_dts_ext, DTS_CRIT, NOT_USED), \ - SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + SENSOR_ATTR_2(temp##index##_crit_hyst, S_IRUGO | S_IWUSR, \ show_dts_ext, store_dts_ext, DTS_CRIT_HYST, NOT_USED), \ - SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_dts_ext, \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_dts_ext, \ store_dts_ext, DTS_WARN, NOT_USED), \ - SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ show_dts_ext, store_dts_ext, DTS_WARN_HYST, NOT_USED), \ SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ show_alarm_beep, NULL, ALARM_STATUS, index + 17), \ @@ -1559,13 +1559,13 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ NULL, TEMP_READ, index - 1), \ - SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ + SENSOR_ATTR_2(temp##index##_crit, S_IRUGO | S_IWUSR, show_temp, \ store_temp, TEMP_CRIT, index - 1), \ - SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + SENSOR_ATTR_2(temp##index##_crit_hyst, S_IRUGO | S_IWUSR, \ show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \ - SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_temp, \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ store_temp, TEMP_WARN, index - 1), \ - SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ show_temp, store_temp, TEMP_WARN_HYST, index - 1), \ SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ show_alarm_beep, NULL, ALARM_STATUS, \ @@ -1583,9 +1583,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, TEMP_PWM_FAN_MAP, index - 1), \ SENSOR_ATTR_2(thermal_cruise##index, S_IWUSR | S_IRUGO, \ show_temp_pwm, store_temp_pwm, TEMP_PWM_TTTI, index - 1), \ - SENSOR_ATTR_2(temp##index##_crit, S_IWUSR | S_IRUGO, \ + SENSOR_ATTR_2(temp##index##_warn, S_IWUSR | S_IRUGO, \ show_temp_pwm, store_temp_pwm, TEMP_PWM_CTFS, index - 1), \ - SENSOR_ATTR_2(temp##index##_crit_hyst, S_IWUSR | S_IRUGO, \ + SENSOR_ATTR_2(temp##index##_warn_hyst, S_IWUSR | S_IRUGO, \ show_temp_pwm, store_temp_pwm, TEMP_PWM_HCT, index - 1), \ SENSOR_ATTR_2(temp##index##_operation_hyst, S_IWUSR | S_IRUGO, \ show_temp_pwm, store_temp_pwm, TEMP_PWM_HOT, index - 1), \ From dd127f5ccd7c61eb7ee215120a7809eb67c1ed7f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 38/68] hwmon: (w83795) Properly handle negative temperatures The temperature registers hold regular 2's complement values, no need to add any arithmetics. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 5ce276b67528..f4b7cb45ebcb 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -295,7 +295,7 @@ static inline long temp_from_reg(s8 reg) static inline s8 temp_to_reg(long val, s8 min, s8 max) { - return SENSORS_LIMIT((val < 0 ? -val : val) / 1000, min, max); + return SENSORS_LIMIT(val / 1000, min, max); } static const u16 pwm_freq_cksel0[16] = { @@ -364,7 +364,7 @@ struct w83795_data { u16 fan_min[14]; /* Register value combine */ u8 has_temp; /* Enable monitor temp6-1 or not */ - u8 temp[6][5]; /* current, crit, crit_hyst, warn, warn_hyst */ + s8 temp[6][5]; /* current, crit, crit_hyst, warn, warn_hyst */ u8 temp_read_vrlsb[6]; u8 temp_mode; /* bit 0: TR mode, bit 1: TD mode */ u8 temp_src[3]; /* Register value */ @@ -373,9 +373,9 @@ struct w83795_data { * bit 0: =1 enable, =0 disable, * bit 1: =1 AMD SB-TSI, =0 Intel PECI */ u8 has_dts; /* Enable monitor DTS temp */ - u8 dts[8]; /* Register value */ + s8 dts[8]; /* Register value */ u8 dts_read_vrlsb[8]; /* Register value */ - u8 dts_ext[4]; /* Register value */ + s8 dts_ext[4]; /* Register value */ u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2, * no config register, only affected by chip @@ -1170,13 +1170,11 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf) int nr = sensor_attr->nr; int index = sensor_attr->index; struct w83795_data *data = w83795_update_device(dev); - long temp = temp_from_reg(data->temp[index][nr] & 0x7f); + long temp = temp_from_reg(data->temp[index][nr]); if (TEMP_READ == nr) temp += ((data->temp_read_vrlsb[index] >> VRLSB_SHIFT) & 0x03) * 250; - if (data->temp[index][nr] & 0x80) - temp = -temp; return sprintf(buf, "%ld\n", temp); } @@ -1235,11 +1233,9 @@ show_dts(struct device *dev, struct device_attribute *attr, char *buf) to_sensor_dev_attr_2(attr); int index = sensor_attr->index; struct w83795_data *data = w83795_update_device(dev); - long temp = temp_from_reg(data->dts[index] & 0x7f); + long temp = temp_from_reg(data->dts[index]); temp += ((data->dts_read_vrlsb[index] >> VRLSB_SHIFT) & 0x03) * 250; - if (data->dts[index] & 0x80) - temp = -temp; return sprintf(buf, "%ld\n", temp); } @@ -1251,10 +1247,8 @@ show_dts_ext(struct device *dev, struct device_attribute *attr, char *buf) int nr = sensor_attr->nr; struct i2c_client *client = to_i2c_client(dev); struct w83795_data *data = i2c_get_clientdata(client); - long temp = temp_from_reg(data->dts_ext[nr] & 0x7f); + long temp = temp_from_reg(data->dts_ext[nr]); - if (data->dts_ext[nr] & 0x80) - temp = -temp; return sprintf(buf, "%ld\n", temp); } From 54891a3c135b30ca0718dbf81f78260795a5b2fe Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 39/68] hwmon: (w83795) Report PECI agent Tbase values On systems with PECI, report PECI agent Tbase temperature values. This is informative only. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index f4b7cb45ebcb..b360696f241d 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -236,6 +236,7 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define W83795_REG_DTSC 0x301 #define W83795_REG_DTSE 0x302 #define W83795_REG_DTS(index) (0x26 + (index)) +#define W83795_REG_PECI_TBASE(index) (0x320 + (index)) #define DTS_CRIT 0 #define DTS_CRIT_HYST 1 @@ -1993,6 +1994,18 @@ static int w83795_probe(struct i2c_client *client, data->has_dts = w83795_read(client, W83795_REG_DTSE); } + /* Report PECI Tbase values */ + if (data->enable_dts == 1) { + for (i = 0; i < 8; i++) { + if (!(data->has_dts & (1 << i))) + continue; + tmp = w83795_read(client, W83795_REG_PECI_TBASE(i)); + dev_info(&client->dev, + "PECI agent %d Tbase temperature: %u\n", + i + 1, (unsigned int)tmp & 0x7f); + } + } + /* First update the voltages measured value and limits */ for (i = 0; i < ARRAY_SIZE(data->in); i++) { if (!(data->has_in & (1 << i))) From 02728ffef9c2cf067e654e832b8c282a8a4d0b16 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 40/68] hwmon: (w83795) Rework beep_enable implementation Handle beep_enable just like all other beep bits. It doesn't need anything special, so let's avoid redundant code. This also saves a duplicate register read at initialization time. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 40 ++-------------------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index b360696f241d..fd70a7b46295 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -398,7 +398,6 @@ struct w83795_data { u8 alarms[6]; /* Register value */ u8 beeps[6]; /* Register value */ - u8 beep_enable; char valid; }; @@ -616,39 +615,6 @@ store_beep(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t -show_beep_enable(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); - return sprintf(buf, "%u\n", data->beep_enable); -} - -static ssize_t -store_beep_enable(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); - unsigned long val; - u8 tmp; - - if (strict_strtoul(buf, 10, &val) < 0) - return -EINVAL; - if (val != 0 && val != 1) - return -EINVAL; - - mutex_lock(&data->update_lock); - data->beep_enable = val; - tmp = w83795_read(client, W83795_REG_BEEP(5)); - tmp &= 0x7f; - tmp |= val << 7; - w83795_write(client, W83795_REG_BEEP(5), tmp); - mutex_unlock(&data->update_lock); - - return count; -} - /* Write any value to clear chassis alarm */ static ssize_t store_chassis_clear(struct device *dev, @@ -1689,8 +1655,8 @@ static const struct sensor_device_attribute_2 w83795_pwm[][7] = { static const struct sensor_device_attribute_2 sda_single_files[] = { SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep, store_chassis_clear, ALARM_STATUS, 46), - SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable, - store_beep_enable, NOT_USED, NOT_USED), + SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep, + store_beep, BEEP_ENABLE, 47), SENSOR_ATTR_2(speed_cruise_tolerance, S_IWUSR | S_IRUGO, show_fanin, store_fanin, FANIN_TOL, NOT_USED), SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup, @@ -2136,8 +2102,6 @@ static int w83795_probe(struct i2c_client *client, data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); } - data->beep_enable = - (w83795_read(client, W83795_REG_BEEP(5)) >> 7) & 0x01; err = w83795_handle_files(dev, device_create_file); if (err) From c1a792a6eea42d88a99e6ace215b22fbdb76c7b6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 41/68] hwmon: (w83795) Avoid reading the same register twice Shorten driver load time by avoiding duplicate register access during initialization. This saves 112 ms on modprobe on my test system. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index fd70a7b46295..aea3275f5dae 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1986,6 +1986,9 @@ static int w83795_probe(struct i2c_client *client, data->in[i][IN_READ] = tmp; } for (i = 0; i < IN_LSB_REG_NUM; i++) { + if ((i == 2 && data->chip_type == w83795adg) || + (i >= 4 && !(data->has_in & (1 << (i + 11))))) + continue; data->in_lsb[i][IN_MAX] = w83795_read(client, IN_LSB_REG(i, IN_MAX)); data->in_lsb[i][IN_LOW] = @@ -1995,13 +1998,17 @@ static int w83795_probe(struct i2c_client *client, /* First update fan and limits */ for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + /* Each register contains LSB for 2 fans, but we want to + * read it only once to save time */ + if ((i & 1) == 0 && (data->has_fan & (3 << i))) + tmp = w83795_read(client, W83795_REG_FAN_MIN_LSB(i)); + if (!(data->has_fan & (1 << i))) continue; data->fan_min[i] = w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; data->fan_min[i] |= - (w83795_read(client, W83795_REG_FAN_MIN_LSB(i)) >> - W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; + (tmp >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; data->fan[i] |= (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F; From a654b9d4e7152b06557e2f22bcf0a245bc88bc98 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 42/68] hwmon: (w83795) Get rid of VRLSB_SHIFT VRLSB_SHIFT is a non-sense, the actual shift depends on the sensor type (fans need 4, other sensors need 6). Get rid of it to prevent any confusion. Also get rid of the useless masking, the meaningful bits are always the MSb so there's nothing to mask out after shifting. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index aea3275f5dae..71b7a254beeb 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -127,7 +127,6 @@ static const u16 W83795_REG_IN[][3] = { {0x24, 0xa2, 0xa3}, /* VSEN17 */ }; #define W83795_REG_VRLSB 0x3C -#define VRLSB_SHIFT 6 static const u8 W83795_REG_IN_HL_LSB[] = { 0x8e, /* VSEN1-4 */ @@ -485,8 +484,7 @@ static struct w83795_data *w83795_update_device(struct device *dev) if (!(data->has_in & (1 << i))) continue; tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2; - tmp |= (w83795_read(client, W83795_REG_VRLSB) - >> VRLSB_SHIFT) & 0x03; + tmp |= w83795_read(client, W83795_REG_VRLSB) >> 6; data->in[i][IN_READ] = tmp; } @@ -1140,8 +1138,7 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf) long temp = temp_from_reg(data->temp[index][nr]); if (TEMP_READ == nr) - temp += ((data->temp_read_vrlsb[index] >> VRLSB_SHIFT) & 0x03) - * 250; + temp += (data->temp_read_vrlsb[index] >> 6) * 250; return sprintf(buf, "%ld\n", temp); } @@ -1202,7 +1199,7 @@ show_dts(struct device *dev, struct device_attribute *attr, char *buf) struct w83795_data *data = w83795_update_device(dev); long temp = temp_from_reg(data->dts[index]); - temp += ((data->dts_read_vrlsb[index] >> VRLSB_SHIFT) & 0x03) * 250; + temp += (data->dts_read_vrlsb[index] >> 6) * 250; return sprintf(buf, "%ld\n", temp); } @@ -1981,8 +1978,7 @@ static int w83795_probe(struct i2c_client *client, data->in[i][IN_LOW] = w83795_read(client, W83795_REG_IN[i][IN_LOW]); tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2; - tmp |= (w83795_read(client, W83795_REG_VRLSB) - >> VRLSB_SHIFT) & 0x03; + tmp |= w83795_read(client, W83795_REG_VRLSB) >> 6; data->in[i][IN_READ] = tmp; } for (i = 0; i < IN_LSB_REG_NUM; i++) { @@ -2010,8 +2006,7 @@ static int w83795_probe(struct i2c_client *client, data->fan_min[i] |= (tmp >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; - data->fan[i] |= - (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F; + data->fan[i] |= w83795_read(client, W83795_REG_VRLSB) >> 4; } /* temperature and limits */ From 7eb8d508b5cfabbb147670ee063438469d94c465 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:47 +0200 Subject: [PATCH 43/68] hwmon: (w83795) Fix parity checks x % 1 is obviously wrong, as it always evaluates to 0. You want x % 2, or x & 1, for parity checking. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 71b7a254beeb..ec8a1d1f7d79 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -183,7 +183,7 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define W83795_REG_FAN_MIN_HL(index) (0xB6 + (index)) #define W83795_REG_FAN_MIN_LSB(index) (0xC4 + (index) / 2) #define W83795_REG_FAN_MIN_LSB_SHIFT(index) \ - (((index) % 1) ? 4 : 0) + (((index) & 1) ? 4 : 0) #define W83795_REG_VID_CTRL 0x6A @@ -670,7 +670,7 @@ store_fan_min(struct device *dev, struct device_attribute *attr, data->fan_min[index] = val; w83795_write(client, W83795_REG_FAN_MIN_HL(index), (val >> 4) & 0xff); val &= 0x0f; - if (index % 1) { + if (index & 1) { val <<= 4; val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index)) & 0x0f; @@ -823,7 +823,7 @@ show_temp_src(struct device *dev, struct device_attribute *attr, char *buf) u8 val = index / 2; u8 tmp = data->temp_src[val]; - if (index % 1) + if (index & 1) val = 4; else val = 0; @@ -850,7 +850,7 @@ store_temp_src(struct device *dev, struct device_attribute *attr, tmp = SENSORS_LIMIT(tmp, 0, 15); mutex_lock(&data->update_lock); - if (index % 1) { + if (index & 1) { tmp <<= 4; data->temp_src[val] &= 0x0f; } else { From 49c7347a44224b5b87c7e6a3c6c92619d77c06d5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 44/68] hwmon: (w83795) Drop REST_VLT_BEGIN/END Get rid of REST_VLT_BEGIN and REST_VLT_END, they don't make the code more readable. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index ec8a1d1f7d79..4e54edc415d7 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -175,10 +175,6 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { }; -/* 3VDD, 3VSB, VBAT * 0.006 */ -#define REST_VLT_BEGIN 12 /* the 13th volt to 15th */ -#define REST_VLT_END 14 /* the 13th volt to 15th */ - #define W83795_REG_FAN(index) (0x2E + (index)) #define W83795_REG_FAN_MIN_HL(index) (0xB6 + (index)) #define W83795_REG_FAN_MIN_LSB(index) (0xC4 + (index) / 2) @@ -250,7 +246,8 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { static inline u16 in_from_reg(u8 index, u16 val) { - if ((index >= REST_VLT_BEGIN) && (index <= REST_VLT_END)) + /* 3VDD, 3VSB and VBAT: 6 mV/bit; other inputs: 2 mV/bit */ + if (index >= 12 && index <= 14) return val * 6; else return val * 2; @@ -258,7 +255,7 @@ static inline u16 in_from_reg(u8 index, u16 val) static inline u16 in_to_reg(u8 index, u16 val) { - if ((index >= REST_VLT_BEGIN) && (index <= REST_VLT_END)) + if (index >= 12 && index <= 14) return val / 6; else return val / 2; From cd316df582925f3dab1ce5863651b3e260687035 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 45/68] hwmon: (w83795) Drop _NUM constants Consistently use ARRAY_SIZE() to control for loops. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 4e54edc415d7..c8f62044ac19 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -145,8 +145,6 @@ static const u8 W83795_REG_IN_HL_LSB[] = { (((type) == 1) ? W83795_REG_IN_HL_LSB[(index)] \ : (W83795_REG_IN_HL_LSB[(index)] + 1)) -#define IN_LSB_REG_NUM 10 - #define IN_LSB_SHIFT 0 #define IN_LSB_IDX 1 static const u8 IN_LSB_SHIFT_IDX[][2] = { @@ -183,14 +181,12 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define W83795_REG_VID_CTRL 0x6A -#define ALARM_BEEP_REG_NUM 6 #define W83795_REG_ALARM(index) (0x41 + (index)) #define W83795_REG_BEEP(index) (0x50 + (index)) #define W83795_REG_CLR_CHASSIS 0x4D -#define W83795_REG_TEMP_NUM 6 #define W83795_REG_FCMS1 0x201 #define W83795_REG_FCMS2 0x208 #define W83795_REG_TFMR(index) (0x202 + (index)) @@ -545,7 +541,7 @@ static struct w83795_data *w83795_update_device(struct device *dev) } /* update alarm */ - for (i = 0; i < ALARM_BEEP_REG_NUM; i++) + for (i = 0; i < ARRAY_SIZE(data->alarms); i++) data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); data->last_updated = jiffies; @@ -1978,7 +1974,7 @@ static int w83795_probe(struct i2c_client *client, tmp |= w83795_read(client, W83795_REG_VRLSB) >> 6; data->in[i][IN_READ] = tmp; } - for (i = 0; i < IN_LSB_REG_NUM; i++) { + for (i = 0; i < ARRAY_SIZE(data->in_lsb); i++) { if ((i == 2 && data->chip_type == w83795adg) || (i >= 4 && !(data->has_in & (1 << (i + 11))))) continue; @@ -2054,7 +2050,7 @@ static int w83795_probe(struct i2c_client *client, data->has_pwm = 2; data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1); data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2); - for (i = 0; i < W83795_REG_TEMP_NUM; i++) + for (i = 0; i < ARRAY_SIZE(data->pwm_tfmr); i++) data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); for (i = 0; i < data->has_pwm; i++) { @@ -2071,7 +2067,7 @@ static int w83795_probe(struct i2c_client *client, } data->tol_speed = w83795_read(client, W83795_REG_TFTS) & 0x3f; - for (i = 0; i < W83795_REG_TEMP_NUM; i++) { + for (i = 0; i < ARRAY_SIZE(data->pwm_temp); i++) { data->pwm_temp[i][TEMP_PWM_TTTI] = w83795_read(client, W83795_REG_TTTI(i)) & 0x7f; data->pwm_temp[i][TEMP_PWM_CTFS] = @@ -2080,7 +2076,7 @@ static int w83795_probe(struct i2c_client *client, data->pwm_temp[i][TEMP_PWM_HCT] = (tmp >> 4) & 0x0f; data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f; } - for (i = 0; i < W83795_REG_TEMP_NUM; i++) { + for (i = 0; i < ARRAY_SIZE(data->sf4_reg); i++) { for (tmp = 0; tmp < 7; tmp++) { data->sf4_reg[i][SF4_TEMP][tmp] = w83795_read(client, @@ -2097,7 +2093,7 @@ static int w83795_probe(struct i2c_client *client, } /* alarm and beep */ - for (i = 0; i < ALARM_BEEP_REG_NUM; i++) { + for (i = 0; i < ARRAY_SIZE(data->alarms); i++) { data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); } From 39deb6993e7c22274c272c95013eef886f7004e8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 46/68] hwmon: (w83795) Simplify temperature sensor type handling All 3 temperature sensor type sysfs functions (show_temp_mode, store_temp_mode and show_dts_mode) can be simplified. We don't create these files when the correponding input isn't in temperature monitoring mode, so there is no point in handling that case. Likewise, we don't allow changing inputs from temperature to voltage, so the code handling this case is dead and can be removed. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 107 +++++++++-------------------------------- 1 file changed, 24 insertions(+), 83 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index c8f62044ac19..07e8de58dcd4 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -66,23 +66,6 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define W83795_REG_FANIN_CTRL2 0x07 #define W83795_REG_VMIGB_CTRL 0x08 -#define TEMP_CTRL_DISABLE 0 -#define TEMP_CTRL_TD 1 -#define TEMP_CTRL_VSEN 2 -#define TEMP_CTRL_TR 3 -#define TEMP_CTRL_SHIFT 4 -#define TEMP_CTRL_HASIN_SHIFT 5 -/* temp mode may effect VSEN17-12 (in20-15) */ -static const u16 W83795_REG_TEMP_CTRL[][6] = { - /* Disable, TD, VSEN, TR, register shift value, has_in shift num */ - {0x00, 0x01, 0x02, 0x03, 0, 17}, /* TR1 */ - {0x00, 0x04, 0x08, 0x0C, 2, 18}, /* TR2 */ - {0x00, 0x10, 0x20, 0x30, 4, 19}, /* TR3 */ - {0x00, 0x40, 0x80, 0xC0, 6, 20}, /* TR4 */ - {0x00, 0x00, 0x02, 0x03, 0, 15}, /* TR5 */ - {0x00, 0x00, 0x08, 0x0C, 2, 16}, /* TR6 */ -}; - #define TEMP_READ 0 #define TEMP_CRIT 1 #define TEMP_CRIT_HYST 2 @@ -359,7 +342,7 @@ struct w83795_data { u8 has_temp; /* Enable monitor temp6-1 or not */ s8 temp[6][5]; /* current, crit, crit_hyst, warn, warn_hyst */ u8 temp_read_vrlsb[6]; - u8 temp_mode; /* bit 0: TR mode, bit 1: TD mode */ + u8 temp_mode; /* Bit vector, 0 = TR, 1 = TD */ u8 temp_src[3]; /* Register value */ u8 enable_dts; /* Enable PECI and SB-TSI, @@ -509,13 +492,6 @@ static struct w83795_data *w83795_update_device(struct device *dev) /* Update temperature */ for (i = 0; i < ARRAY_SIZE(data->temp); i++) { - /* even stop monitor, register still keep value, just read out - * it */ - if (!(data->has_temp & (1 << i))) { - data->temp[i][TEMP_READ] = 0; - data->temp_read_vrlsb[i] = 0; - continue; - } data->temp[i][TEMP_READ] = w83795_read(client, W83795_REG_TEMP[i][TEMP_READ]); data->temp_read_vrlsb[i] = @@ -1163,22 +1139,12 @@ show_dts_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct w83795_data *data = i2c_get_clientdata(client); - struct sensor_device_attribute_2 *sensor_attr = - to_sensor_dev_attr_2(attr); - int index = sensor_attr->index; - u8 tmp; + int tmp; - if (data->enable_dts == 0) - return sprintf(buf, "%d\n", 0); - - if ((data->has_dts >> index) & 0x01) { - if (data->enable_dts & 2) - tmp = 5; - else - tmp = 6; - } else { - tmp = 0; - } + if (data->enable_dts & 2) + tmp = 5; + else + tmp = 6; return sprintf(buf, "%d\n", tmp); } @@ -1231,14 +1197,6 @@ store_dts_ext(struct device *dev, struct device_attribute *attr, } -/* - Type 3: Thermal diode - Type 4: Thermistor - - Temp5-6, default TR - Temp1-4, default TD -*/ - static ssize_t show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1247,20 +1205,17 @@ show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int index = sensor_attr->index; - u8 tmp; + int tmp; - if (data->has_temp >> index & 0x01) { - if (data->temp_mode >> index & 0x01) - tmp = 3; - else - tmp = 4; - } else { - tmp = 0; - } + if (data->temp_mode & (1 << index)) + tmp = 3; /* Thermal diode */ + else + tmp = 4; /* Thermistor */ return sprintf(buf, "%d\n", tmp); } +/* Only for temp1-4 (temp5-6 can only be thermistor) */ static ssize_t store_temp_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1270,45 +1225,31 @@ store_temp_mode(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int index = sensor_attr->index; + int reg_shift; unsigned long val; u8 tmp; - u32 mask; if (strict_strtoul(buf, 10, &val) < 0) return -EINVAL; if ((val != 4) && (val != 3)) return -EINVAL; - if ((index > 3) && (val == 3)) - return -EINVAL; mutex_lock(&data->update_lock); if (val == 3) { - val = TEMP_CTRL_TD; - data->has_temp |= 1 << index; + /* Thermal diode */ + val = 0x01; data->temp_mode |= 1 << index; } else if (val == 4) { - val = TEMP_CTRL_TR; - data->has_temp |= 1 << index; - tmp = 1 << index; - data->temp_mode &= ~tmp; + /* Thermistor */ + val = 0x03; + data->temp_mode &= ~(1 << index); } - if (index > 3) - tmp = w83795_read(client, W83795_REG_TEMP_CTRL1); - else - tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); - - mask = 0x03 << W83795_REG_TEMP_CTRL[index][TEMP_CTRL_SHIFT]; - tmp &= ~mask; - tmp |= W83795_REG_TEMP_CTRL[index][val]; - - mask = 1 << W83795_REG_TEMP_CTRL[index][TEMP_CTRL_HASIN_SHIFT]; - data->has_in &= ~mask; - - if (index > 3) - w83795_write(client, W83795_REG_TEMP_CTRL1, tmp); - else - w83795_write(client, W83795_REG_TEMP_CTRL2, tmp); + reg_shift = 2 * index; + tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); + tmp &= ~(0x03 << reg_shift); + tmp |= val << reg_shift; + w83795_write(client, W83795_REG_TEMP_CTRL2, tmp); mutex_unlock(&data->update_lock); return count; @@ -1506,7 +1447,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) } #define SENSOR_ATTR_TEMP(index) { \ - SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \ + SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \ show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ NULL, TEMP_READ, index - 1), \ From 5f7b77cbd4599b6a90d18981d9883a3e20e689fb Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 47/68] hwmon: (w83795) Don't pre-read values we'll update later There is no point in reading registers during initialization if we will refresh the values in the update function later. This is only slowing down the driver loading with no benefit, stop doing it. This change saves 480 ms on driver load on my test system. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 07e8de58dcd4..a1d884bb58ad 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1903,7 +1903,7 @@ static int w83795_probe(struct i2c_client *client, } } - /* First update the voltages measured value and limits */ + /* Read the voltage limits */ for (i = 0; i < ARRAY_SIZE(data->in); i++) { if (!(data->has_in & (1 << i))) continue; @@ -1911,9 +1911,6 @@ static int w83795_probe(struct i2c_client *client, w83795_read(client, W83795_REG_IN[i][IN_MAX]); data->in[i][IN_LOW] = w83795_read(client, W83795_REG_IN[i][IN_LOW]); - tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2; - tmp |= w83795_read(client, W83795_REG_VRLSB) >> 6; - data->in[i][IN_READ] = tmp; } for (i = 0; i < ARRAY_SIZE(data->in_lsb); i++) { if ((i == 2 && data->chip_type == w83795adg) || @@ -1926,7 +1923,7 @@ static int w83795_probe(struct i2c_client *client, } data->has_gain = w83795_read(client, W83795_REG_VMIGB_CTRL) & 0x0f; - /* First update fan and limits */ + /* Read the fan limits */ for (i = 0; i < ARRAY_SIZE(data->fan); i++) { /* Each register contains LSB for 2 fans, but we want to * read it only once to save time */ @@ -1939,11 +1936,9 @@ static int w83795_probe(struct i2c_client *client, w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; data->fan_min[i] |= (tmp >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; - data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; - data->fan[i] |= w83795_read(client, W83795_REG_VRLSB) >> 4; } - /* temperature and limits */ + /* Read the temperature limits */ for (i = 0; i < ARRAY_SIZE(data->temp); i++) { if (!(data->has_temp & (1 << i))) continue; @@ -1955,13 +1950,9 @@ static int w83795_probe(struct i2c_client *client, w83795_read(client, W83795_REG_TEMP[i][TEMP_WARN]); data->temp[i][TEMP_WARN_HYST] = w83795_read(client, W83795_REG_TEMP[i][TEMP_WARN_HYST]); - data->temp[i][TEMP_READ] = - w83795_read(client, W83795_REG_TEMP[i][TEMP_READ]); - data->temp_read_vrlsb[i] = - w83795_read(client, W83795_REG_VRLSB); } - /* dts temperature and limits */ + /* Read the DTS limits */ if (data->enable_dts != 0) { data->dts_ext[DTS_CRIT] = w83795_read(client, W83795_REG_DTS_EXT(DTS_CRIT)); @@ -1971,13 +1962,6 @@ static int w83795_probe(struct i2c_client *client, w83795_read(client, W83795_REG_DTS_EXT(DTS_WARN)); data->dts_ext[DTS_WARN_HYST] = w83795_read(client, W83795_REG_DTS_EXT(DTS_WARN_HYST)); - for (i = 0; i < ARRAY_SIZE(data->dts); i++) { - if (!(data->has_dts & (1 << i))) - continue; - data->dts[i] = w83795_read(client, W83795_REG_DTS(i)); - data->dts_read_vrlsb[i] = - w83795_read(client, W83795_REG_VRLSB); - } } /* First update temp source selction */ @@ -1995,10 +1979,9 @@ static int w83795_probe(struct i2c_client *client, data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); for (i = 0; i < data->has_pwm; i++) { - for (tmp = 0; tmp < 5; tmp++) { + for (tmp = PWM_START; tmp <= PWM_FREQ; tmp++) data->pwm[i][tmp] = w83795_read(client, W83795_REG_PWM(i, tmp)); - } } for (i = 0; i < 8; i++) { data->target_speed[i] = @@ -2033,11 +2016,9 @@ static int w83795_probe(struct i2c_client *client, w83795_read(client, W83795_REG_SETUP_PWM(i)); } - /* alarm and beep */ - for (i = 0; i < ARRAY_SIZE(data->alarms); i++) { - data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); + /* Read beep settings */ + for (i = 0; i < ARRAY_SIZE(data->beeps); i++) data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); - } err = w83795_handle_files(dev, device_create_file); if (err) From fd7f82b8a37ff4d400c2db9c9fa8f9467dc54a08 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 48/68] hwmon: (w83795) Make W83795_REG_PWM more efficient Cascaded conditionals are inefficient. Reorder the fields so that PWM register addresses can be computed more efficiently. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- drivers/hwmon/w83795.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index a1d884bb58ad..35f87ca88cf3 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -178,15 +178,11 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = { #define W83795_REG_TSS(index) (0x209 + (index)) #define PWM_OUTPUT 0 -#define PWM_START 1 -#define PWM_NONSTOP 2 -#define PWM_STOP_TIME 3 -#define PWM_FREQ 4 -#define W83795_REG_PWM(index, nr) \ - (((nr) == 0 ? 0x210 : \ - (nr) == 1 ? 0x220 : \ - (nr) == 2 ? 0x228 : \ - (nr) == 3 ? 0x230 : 0x218) + (index)) +#define PWM_FREQ 1 +#define PWM_START 2 +#define PWM_NONSTOP 3 +#define PWM_STOP_TIME 4 +#define W83795_REG_PWM(index, nr) (0x210 + (nr) * 8 + (index)) #define W83795_REG_FTSH(index) (0x240 + (index) * 2) #define W83795_REG_FTSL(index) (0x241 + (index) * 2) @@ -356,8 +352,8 @@ struct w83795_data { u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2, * no config register, only affected by chip * type */ - u8 pwm[8][5]; /* Register value, output, start, non stop, stop - * time, freq */ + u8 pwm[8][5]; /* Register value, output, freq, start, + * non stop, stop time */ u16 clkin; /* CLKIN frequency in kHz */ u8 pwm_fcms[2]; /* Register value */ u8 pwm_tfmr[6]; /* Register value */ @@ -1979,7 +1975,7 @@ static int w83795_probe(struct i2c_client *client, data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); for (i = 0; i < data->has_pwm; i++) { - for (tmp = PWM_START; tmp <= PWM_FREQ; tmp++) + for (tmp = PWM_FREQ; tmp <= PWM_STOP_TIME; tmp++) data->pwm[i][tmp] = w83795_read(client, W83795_REG_PWM(i, tmp)); } From 476a4e5d3768ac72bb342166db294c0a9d2c010f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 49/68] hwmon: (w83795) Pack similar register reads Pack similar register reads using for loops. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 35f87ca88cf3..e25a2e31df3f 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1938,26 +1938,16 @@ static int w83795_probe(struct i2c_client *client, for (i = 0; i < ARRAY_SIZE(data->temp); i++) { if (!(data->has_temp & (1 << i))) continue; - data->temp[i][TEMP_CRIT] = - w83795_read(client, W83795_REG_TEMP[i][TEMP_CRIT]); - data->temp[i][TEMP_CRIT_HYST] = - w83795_read(client, W83795_REG_TEMP[i][TEMP_CRIT_HYST]); - data->temp[i][TEMP_WARN] = - w83795_read(client, W83795_REG_TEMP[i][TEMP_WARN]); - data->temp[i][TEMP_WARN_HYST] = - w83795_read(client, W83795_REG_TEMP[i][TEMP_WARN_HYST]); + for (tmp = TEMP_CRIT; tmp <= TEMP_WARN_HYST; tmp++) + data->temp[i][tmp] = + w83795_read(client, W83795_REG_TEMP[i][tmp]); } /* Read the DTS limits */ if (data->enable_dts != 0) { - data->dts_ext[DTS_CRIT] = - w83795_read(client, W83795_REG_DTS_EXT(DTS_CRIT)); - data->dts_ext[DTS_CRIT_HYST] = - w83795_read(client, W83795_REG_DTS_EXT(DTS_CRIT_HYST)); - data->dts_ext[DTS_WARN] = - w83795_read(client, W83795_REG_DTS_EXT(DTS_WARN)); - data->dts_ext[DTS_WARN_HYST] = - w83795_read(client, W83795_REG_DTS_EXT(DTS_WARN_HYST)); + for (i = DTS_CRIT; i <= DTS_WARN_HYST; i++) + data->dts_ext[i] = + w83795_read(client, W83795_REG_DTS_EXT(i)); } /* First update temp source selction */ From 0d7237bfd13a0a8df270654223c15a16b368a3bd Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 50/68] hwmon: (w83795) Move register reads to dedicated functions Move initial register reads out of probe, to dedicated functions. This makes the code clearer, and will be needed if we want to delay calling these functions until they are needed, or want to call them periodically. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- drivers/hwmon/w83795.c | 218 ++++++++++++++++++++++------------------- 1 file changed, 119 insertions(+), 99 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index e25a2e31df3f..511bced1ee97 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -438,6 +438,123 @@ static int w83795_write(struct i2c_client *client, u16 reg, u8 value) return err; } +static void w83795_update_limits(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + int i, limit; + + /* Read the voltage limits */ + for (i = 0; i < ARRAY_SIZE(data->in); i++) { + if (!(data->has_in & (1 << i))) + continue; + data->in[i][IN_MAX] = + w83795_read(client, W83795_REG_IN[i][IN_MAX]); + data->in[i][IN_LOW] = + w83795_read(client, W83795_REG_IN[i][IN_LOW]); + } + for (i = 0; i < ARRAY_SIZE(data->in_lsb); i++) { + if ((i == 2 && data->chip_type == w83795adg) || + (i >= 4 && !(data->has_in & (1 << (i + 11))))) + continue; + data->in_lsb[i][IN_MAX] = + w83795_read(client, IN_LSB_REG(i, IN_MAX)); + data->in_lsb[i][IN_LOW] = + w83795_read(client, IN_LSB_REG(i, IN_LOW)); + } + + /* Read the fan limits */ + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + u8 lsb; + + /* Each register contains LSB for 2 fans, but we want to + * read it only once to save time */ + if ((i & 1) == 0 && (data->has_fan & (3 << i))) + lsb = w83795_read(client, W83795_REG_FAN_MIN_LSB(i)); + + if (!(data->has_fan & (1 << i))) + continue; + data->fan_min[i] = + w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; + data->fan_min[i] |= + (lsb >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; + } + + /* Read the temperature limits */ + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + if (!(data->has_temp & (1 << i))) + continue; + for (limit = TEMP_CRIT; limit <= TEMP_WARN_HYST; limit++) + data->temp[i][limit] = + w83795_read(client, W83795_REG_TEMP[i][limit]); + } + + /* Read the DTS limits */ + if (data->enable_dts != 0) { + for (limit = DTS_CRIT; limit <= DTS_WARN_HYST; limit++) + data->dts_ext[limit] = + w83795_read(client, W83795_REG_DTS_EXT(limit)); + } + + /* Read beep settings */ + for (i = 0; i < ARRAY_SIZE(data->beeps); i++) + data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); +} + +static void w83795_update_pwm_config(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + int i, tmp; + + /* Read temperature source selection */ + for (i = 0; i < ARRAY_SIZE(data->temp_src); i++) + data->temp_src[i] = w83795_read(client, W83795_REG_TSS(i)); + + /* Read automatic fan speed control settings */ + data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1); + data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2); + for (i = 0; i < ARRAY_SIZE(data->pwm_tfmr); i++) + data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); + data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); + for (i = 0; i < data->has_pwm; i++) { + for (tmp = PWM_FREQ; tmp <= PWM_STOP_TIME; tmp++) + data->pwm[i][tmp] = + w83795_read(client, W83795_REG_PWM(i, tmp)); + } + for (i = 0; i < ARRAY_SIZE(data->target_speed); i++) { + data->target_speed[i] = + w83795_read(client, W83795_REG_FTSH(i)) << 4; + data->target_speed[i] |= + w83795_read(client, W83795_REG_FTSL(i)) >> 4; + } + data->tol_speed = w83795_read(client, W83795_REG_TFTS) & 0x3f; + + for (i = 0; i < ARRAY_SIZE(data->pwm_temp); i++) { + data->pwm_temp[i][TEMP_PWM_TTTI] = + w83795_read(client, W83795_REG_TTTI(i)) & 0x7f; + data->pwm_temp[i][TEMP_PWM_CTFS] = + w83795_read(client, W83795_REG_CTFS(i)); + tmp = w83795_read(client, W83795_REG_HT(i)); + data->pwm_temp[i][TEMP_PWM_HCT] = (tmp >> 4) & 0x0f; + data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f; + } + + /* Read SmartFanIV trip points */ + for (i = 0; i < ARRAY_SIZE(data->sf4_reg); i++) { + for (tmp = 0; tmp < 7; tmp++) { + data->sf4_reg[i][SF4_TEMP][tmp] = + w83795_read(client, + W83795_REG_SF4_TEMP(i, tmp)); + data->sf4_reg[i][SF4_PWM][tmp] = + w83795_read(client, W83795_REG_SF4_PWM(i, tmp)); + } + } + + /* Read setup PWM */ + for (i = 0; i < ARRAY_SIZE(data->setup_pwm); i++) + data->setup_pwm[i] = + w83795_read(client, W83795_REG_SETUP_PWM(i)); +} + static struct w83795_data *w83795_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1899,112 +2016,15 @@ static int w83795_probe(struct i2c_client *client, } } - /* Read the voltage limits */ - for (i = 0; i < ARRAY_SIZE(data->in); i++) { - if (!(data->has_in & (1 << i))) - continue; - data->in[i][IN_MAX] = - w83795_read(client, W83795_REG_IN[i][IN_MAX]); - data->in[i][IN_LOW] = - w83795_read(client, W83795_REG_IN[i][IN_LOW]); - } - for (i = 0; i < ARRAY_SIZE(data->in_lsb); i++) { - if ((i == 2 && data->chip_type == w83795adg) || - (i >= 4 && !(data->has_in & (1 << (i + 11))))) - continue; - data->in_lsb[i][IN_MAX] = - w83795_read(client, IN_LSB_REG(i, IN_MAX)); - data->in_lsb[i][IN_LOW] = - w83795_read(client, IN_LSB_REG(i, IN_LOW)); - } data->has_gain = w83795_read(client, W83795_REG_VMIGB_CTRL) & 0x0f; - - /* Read the fan limits */ - for (i = 0; i < ARRAY_SIZE(data->fan); i++) { - /* Each register contains LSB for 2 fans, but we want to - * read it only once to save time */ - if ((i & 1) == 0 && (data->has_fan & (3 << i))) - tmp = w83795_read(client, W83795_REG_FAN_MIN_LSB(i)); - - if (!(data->has_fan & (1 << i))) - continue; - data->fan_min[i] = - w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; - data->fan_min[i] |= - (tmp >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; - } - - /* Read the temperature limits */ - for (i = 0; i < ARRAY_SIZE(data->temp); i++) { - if (!(data->has_temp & (1 << i))) - continue; - for (tmp = TEMP_CRIT; tmp <= TEMP_WARN_HYST; tmp++) - data->temp[i][tmp] = - w83795_read(client, W83795_REG_TEMP[i][tmp]); - } - - /* Read the DTS limits */ - if (data->enable_dts != 0) { - for (i = DTS_CRIT; i <= DTS_WARN_HYST; i++) - data->dts_ext[i] = - w83795_read(client, W83795_REG_DTS_EXT(i)); - } - - /* First update temp source selction */ - for (i = 0; i < 3; i++) - data->temp_src[i] = w83795_read(client, W83795_REG_TSS(i)); + w83795_update_limits(client); /* pwm and smart fan */ if (data->chip_type == w83795g) data->has_pwm = 8; else data->has_pwm = 2; - data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1); - data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2); - for (i = 0; i < ARRAY_SIZE(data->pwm_tfmr); i++) - data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); - data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); - for (i = 0; i < data->has_pwm; i++) { - for (tmp = PWM_FREQ; tmp <= PWM_STOP_TIME; tmp++) - data->pwm[i][tmp] = - w83795_read(client, W83795_REG_PWM(i, tmp)); - } - for (i = 0; i < 8; i++) { - data->target_speed[i] = - w83795_read(client, W83795_REG_FTSH(i)) << 4; - data->target_speed[i] |= - w83795_read(client, W83795_REG_FTSL(i)) >> 4; - } - data->tol_speed = w83795_read(client, W83795_REG_TFTS) & 0x3f; - - for (i = 0; i < ARRAY_SIZE(data->pwm_temp); i++) { - data->pwm_temp[i][TEMP_PWM_TTTI] = - w83795_read(client, W83795_REG_TTTI(i)) & 0x7f; - data->pwm_temp[i][TEMP_PWM_CTFS] = - w83795_read(client, W83795_REG_CTFS(i)); - tmp = w83795_read(client, W83795_REG_HT(i)); - data->pwm_temp[i][TEMP_PWM_HCT] = (tmp >> 4) & 0x0f; - data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f; - } - for (i = 0; i < ARRAY_SIZE(data->sf4_reg); i++) { - for (tmp = 0; tmp < 7; tmp++) { - data->sf4_reg[i][SF4_TEMP][tmp] = - w83795_read(client, - W83795_REG_SF4_TEMP(i, tmp)); - data->sf4_reg[i][SF4_PWM][tmp] = - w83795_read(client, W83795_REG_SF4_PWM(i, tmp)); - } - } - - /* Setup PWM Register */ - for (i = 0; i < 3; i++) { - data->setup_pwm[i] = - w83795_read(client, W83795_REG_SETUP_PWM(i)); - } - - /* Read beep settings */ - for (i = 0; i < ARRAY_SIZE(data->beeps); i++) - data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); + w83795_update_pwm_config(client); err = w83795_handle_files(dev, device_create_file); if (err) From 2ae61de90545fe3dadae8119181c430d89a8627b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 51/68] hwmon: (w83795) Delay reading limit registers Wait until we need the limit register values, instead of pre-reading them. This saves 544 ms on modprobe on my test system. Obviously this time is added when first running "sensors" or any other monitoring application, but I think it is better than slowing down the boot. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 511bced1ee97..6464a52b134b 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -371,6 +371,7 @@ struct w83795_data { u8 beeps[6]; /* Register value */ char valid; + char valid_limits; }; /* @@ -498,6 +499,8 @@ static void w83795_update_limits(struct i2c_client *client) /* Read beep settings */ for (i = 0; i < ARRAY_SIZE(data->beeps); i++) data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); + + data->valid_limits = 1; } static void w83795_update_pwm_config(struct i2c_client *client) @@ -564,6 +567,9 @@ static struct w83795_data *w83795_update_device(struct device *dev) mutex_lock(&data->update_lock); + if (!data->valid_limits) + w83795_update_limits(client); + if (!(time_after(jiffies, data->last_updated + HZ * 2) || !data->valid)) goto END; @@ -2017,7 +2023,6 @@ static int w83795_probe(struct i2c_client *client, } data->has_gain = w83795_read(client, W83795_REG_VMIGB_CTRL) & 0x0f; - w83795_update_limits(client); /* pwm and smart fan */ if (data->chip_type == w83795g) From 1bb3450ce5050bc677c1995a60a0ddfe9e33e7b6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:48 +0200 Subject: [PATCH 52/68] hwmon: (w83795) Delay reading pwm config registers Wait until we need the pwm config register values, instead of pre-reading them. This saves over 1 second on modprobe on my test system. Obviously this time is added when first accessing pwm config attributes, however not everybody will use them, so it seems unfair to slow down driver loading (and thus boot) for an optional feature. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 51 ++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 6464a52b134b..78a876f35fa3 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -372,6 +372,7 @@ struct w83795_data { char valid; char valid_limits; + char valid_pwm_config; }; /* @@ -503,11 +504,17 @@ static void w83795_update_limits(struct i2c_client *client) data->valid_limits = 1; } -static void w83795_update_pwm_config(struct i2c_client *client) +static struct w83795_data *w83795_update_pwm_config(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct w83795_data *data = i2c_get_clientdata(client); int i, tmp; + mutex_lock(&data->update_lock); + + if (data->valid_pwm_config) + goto END; + /* Read temperature source selection */ for (i = 0; i < ARRAY_SIZE(data->temp_src); i++) data->temp_src[i] = w83795_read(client, W83795_REG_TSS(i)); @@ -556,6 +563,12 @@ static void w83795_update_pwm_config(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(data->setup_pwm); i++) data->setup_pwm[i] = w83795_read(client, W83795_REG_SETUP_PWM(i)); + + data->valid_pwm_config = 1; + +END: + mutex_unlock(&data->update_lock); + return data; } static struct w83795_data *w83795_update_device(struct device *dev) @@ -775,13 +788,16 @@ store_fan_min(struct device *dev, struct device_attribute *attr, static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) { - struct w83795_data *data = w83795_update_device(dev); + struct w83795_data *data; struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; unsigned int val; + data = nr == PWM_OUTPUT ? w83795_update_device(dev) + : w83795_update_pwm_config(dev); + switch (nr) { case PWM_STOP_TIME: val = time_from_reg(data->pwm[index][nr]); @@ -835,8 +851,7 @@ show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); int index = sensor_attr->index; u8 tmp; @@ -864,7 +879,7 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int index = sensor_attr->index; @@ -905,8 +920,7 @@ show_temp_src(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); int index = sensor_attr->index; u8 val = index / 2; u8 tmp = data->temp_src[val]; @@ -926,7 +940,7 @@ store_temp_src(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int index = sensor_attr->index; @@ -957,8 +971,7 @@ static ssize_t show_temp_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; @@ -986,7 +999,7 @@ store_temp_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; @@ -1023,8 +1036,7 @@ store_temp_pwm_enable(struct device *dev, struct device_attribute *attr, static ssize_t show_fanin(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; @@ -1081,8 +1093,7 @@ store_fanin(struct device *dev, struct device_attribute *attr, static ssize_t show_temp_pwm(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; @@ -1143,8 +1154,7 @@ store_temp_pwm(struct device *dev, struct device_attribute *attr, static ssize_t show_sf4_pwm(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; @@ -1179,8 +1189,7 @@ store_sf4_pwm(struct device *dev, struct device_attribute *attr, static ssize_t show_sf4_temp(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; @@ -1456,8 +1465,7 @@ show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = w83795_update_pwm_config(dev); u16 val = data->setup_pwm[nr]; switch (nr) { @@ -2029,7 +2037,6 @@ static int w83795_probe(struct i2c_client *client, data->has_pwm = 8; else data->has_pwm = 2; - w83795_update_pwm_config(client); err = w83795_handle_files(dev, device_create_file); if (err) From 21fc977551da43ec21ae0ebb859411afd4243d2b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 53/68] hwmon: (w83795) Use dev_get_drvdata() where possible When we don't need the client pointer, calling dev_get_drvdata() is more efficient that calling to_i2c_client() and then i2c_get_clientdata(). Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 78a876f35fa3..e916c04e8681 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1265,8 +1265,7 @@ store_temp(struct device *dev, struct device_attribute *attr, static ssize_t show_dts_mode(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = dev_get_drvdata(dev); int tmp; if (data->enable_dts & 2) @@ -1296,8 +1295,7 @@ show_dts_ext(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = dev_get_drvdata(dev); long temp = temp_from_reg(data->dts_ext[nr]); return sprintf(buf, "%ld\n", temp); @@ -1328,8 +1326,7 @@ store_dts_ext(struct device *dev, struct device_attribute *attr, static ssize_t show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct w83795_data *data = i2c_get_clientdata(client); + struct w83795_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); int index = sensor_attr->index; From 5d2cd958f777e533fa2809527836996f59b2d823 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 54/68] hwmon: (w83795) Fix LSB reading of voltage limits Wrong index caused the wrong register value to be read, resulting in random LSB for voltage limits. Signed-off-by: Jean Delvare --- drivers/hwmon/w83795.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index e916c04e8681..9d73768060d4 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1405,7 +1405,7 @@ show_in(struct device *dev, struct device_attribute *attr, char *buf) lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX]; val <<= 2; val |= (data->in_lsb[lsb_idx][nr] >> - IN_LSB_SHIFT_IDX[lsb_idx][IN_LSB_SHIFT]) & 0x03; + IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]) & 0x03; if ((index >= 17) && !((data->has_gain >> (index - 17)) & 1)) val *= 8; From eb02755a50f8f3508030b311e3788426fbdc23b7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 55/68] hwmon: (w83795) More style cleanups Cleanups suggested by Guenter Roeck, falling into 4 categories: * Swapping test orders, because if (var == CONSTANT) is much easier to read than if (CONSTANT == var). * Simplifying comparisons with 0. * Dropping unneeded masks. * Dropping unneeded parentheses and curly braces. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- drivers/hwmon/w83795.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 9d73768060d4..ce1e71ba6c73 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -491,7 +491,7 @@ static void w83795_update_limits(struct i2c_client *client) } /* Read the DTS limits */ - if (data->enable_dts != 0) { + if (data->enable_dts) { for (limit = DTS_CRIT; limit <= DTS_WARN_HYST; limit++) data->dts_ext[limit] = w83795_read(client, W83795_REG_DTS_EXT(limit)); @@ -544,7 +544,7 @@ static struct w83795_data *w83795_update_pwm_config(struct device *dev) data->pwm_temp[i][TEMP_PWM_CTFS] = w83795_read(client, W83795_REG_CTFS(i)); tmp = w83795_read(client, W83795_REG_HT(i)); - data->pwm_temp[i][TEMP_PWM_HCT] = (tmp >> 4) & 0x0f; + data->pwm_temp[i][TEMP_PWM_HCT] = tmp >> 4; data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f; } @@ -618,8 +618,7 @@ static struct w83795_data *w83795_update_device(struct device *dev) if (!(data->has_fan & (1 << i))) continue; data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; - data->fan[i] |= - (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F; + data->fan[i] |= w83795_read(client, W83795_REG_VRLSB) >> 4; } /* Update temperature */ @@ -631,7 +630,7 @@ static struct w83795_data *w83795_update_device(struct device *dev) } /* Update dts temperature */ - if (data->enable_dts != 0) { + if (data->enable_dts) { for (i = 0; i < ARRAY_SIZE(data->dts); i++) { if (!(data->has_dts & (1 << i))) continue; @@ -677,11 +676,10 @@ show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) int bit = sensor_attr->index & 0x07; u8 val; - if (ALARM_STATUS == nr) { - val = (data->alarms[index] >> (bit)) & 1; - } else { /* BEEP_ENABLE */ - val = (data->beeps[index] >> (bit)) & 1; - } + if (nr == ALARM_STATUS) + val = (data->alarms[index] >> bit) & 1; + else /* BEEP_ENABLE */ + val = (data->beeps[index] >> bit) & 1; return sprintf(buf, "%u\n", val); } @@ -744,7 +742,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct w83795_data *data = w83795_update_device(dev); u16 val; - if (FAN_INPUT == nr) + if (nr == FAN_INPUT) val = data->fan[index] & 0x0fff; else val = data->fan_min[index] & 0x0fff; @@ -1011,7 +1009,7 @@ store_temp_pwm_enable(struct device *dev, struct device_attribute *attr, switch (nr) { case TEMP_PWM_ENABLE: - if ((tmp != 3) && (tmp != 4)) + if (tmp != 3 && tmp != 4) return -EINVAL; tmp -= 3; mutex_lock(&data->update_lock); @@ -1074,7 +1072,7 @@ store_fanin(struct device *dev, struct device_attribute *attr, switch (nr) { case FANIN_TARGET: val = fan_to_reg(SENSORS_LIMIT(val, 0, 0xfff)); - w83795_write(client, W83795_REG_FTSH(index), (val >> 4) & 0xff); + w83795_write(client, W83795_REG_FTSH(index), val >> 4); w83795_write(client, W83795_REG_FTSL(index), (val << 4) & 0xf0); data->target_speed[index] = val; break; @@ -1234,7 +1232,7 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf) struct w83795_data *data = w83795_update_device(dev); long temp = temp_from_reg(data->temp[index][nr]); - if (TEMP_READ == nr) + if (nr == TEMP_READ) temp += (data->temp_read_vrlsb[index] >> 6) * 250; return sprintf(buf, "%ld\n", temp); } @@ -1891,7 +1889,7 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, } } - if (data->enable_dts != 0) { + if (data->enable_dts) { for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { if (!(data->has_dts & (1 << i))) continue; From e3760b432d589ddd9bdb8c337539b815125bc7aa Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 56/68] hwmon: (w83795) Add myself as co-author and maintainer I've made so many changes to the w83795 driver that it's only fair to list myself as a co-author. I'll also maintain the driver for some time. There's more work needed on the driver for sure. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- MAINTAINERS | 6 ++++++ drivers/hwmon/w83795.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 1e6b6bdf6340..19f262c27323 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6457,6 +6457,12 @@ S: Maintained F: Documentation/hwmon/w83793 F: drivers/hwmon/w83793.c +W83795 HARDWARE MONITORING DRIVER +M: Jean Delvare +L: lm-sensors@lm-sensors.org +S: Maintained +F: drivers/hwmon/w83795.c + W83L51xD SD/MMC CARD INTERFACE DRIVER M: Pierre Ossman S: Maintained diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index ce1e71ba6c73..a9116ad747f2 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -2,6 +2,7 @@ * w83795.c - Linux kernel driver for hardware monitoring * Copyright (C) 2008 Nuvoton Technology Corp. * Wei Song + * Copyright (C) 2010 Jean Delvare * * 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 @@ -2097,7 +2098,7 @@ static void __exit sensors_w83795_exit(void) i2c_del_driver(&w83795_driver); } -MODULE_AUTHOR("Wei Song"); +MODULE_AUTHOR("Wei Song, Jean Delvare "); MODULE_DESCRIPTION("W83795G/ADG hardware monitoring driver"); MODULE_LICENSE("GPL"); From 00030af23153f1958f015df8b9fec8c29fcca8b8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 57/68] hwmon: (w83795) Exclude fan control feature by default The fan control feature of the w83795 driver is insufficiently reviewed and tested for public consumption at this time, so make it optional and disabled by default. We will change the default when review and testing is deemed sufficient. Ultimately the option will go away. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- drivers/hwmon/Kconfig | 17 +++++++++++++++++ drivers/hwmon/w83795.c | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d229f4592845..a06ac97229d9 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1041,6 +1041,23 @@ config SENSORS_W83795 This driver can also be built as a module. If so, the module will be called w83795. +config SENSORS_W83795_FANCTRL + boolean "Include fan control support (DANGEROUS)" + depends on SENSORS_W83795 && EXPERIMENTAL + default n + help + If you say yes here, support for the both manual and automatic + fan control features will be included in the driver. + + This part of the code wasn't carefully reviewed and tested yet, + so enabling this option is strongly discouraged on production + servers. Only developers and testers should enable it for the + time being. + + Please also note that this option will create sysfs attribute + files which may change in the future, so you shouldn't rely + on them being stable. + config SENSORS_W83L785TS tristate "Winbond W83L785TS-S" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index a9116ad747f2..c461e753f9bf 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1455,6 +1455,7 @@ store_in(struct device *dev, struct device_attribute *attr, } +#ifdef CONFIG_SENSORS_W83795_FANCTRL static ssize_t show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1506,6 +1507,7 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, mutex_unlock(&data->update_lock); return count; } +#endif #define NOT_USED -1 @@ -1711,6 +1713,7 @@ static const struct sensor_device_attribute_2 sda_single_files[] = { store_chassis_clear, ALARM_STATUS, 46), SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep, store_beep, BEEP_ENABLE, 47), +#ifdef CONFIG_SENSORS_W83795_FANCTRL SENSOR_ATTR_2(speed_cruise_tolerance, S_IWUSR | S_IRUGO, show_fanin, store_fanin, FANIN_TOL, NOT_USED), SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup, @@ -1719,6 +1722,7 @@ static const struct sensor_device_attribute_2 sda_single_files[] = { store_sf_setup, SETUP_PWM_UPTIME, NOT_USED), SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup, store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED), +#endif }; /* @@ -1872,6 +1876,7 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, return err; } +#ifdef CONFIG_SENSORS_W83795_FANCTRL for (i = 0; i < data->has_pwm; i++) { for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) { err = fn(dev, &w83795_pwm[i][j].dev_attr); @@ -1879,11 +1884,16 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, return err; } } +#endif for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { if (!(data->has_temp & (1 << i))) continue; +#ifdef CONFIG_SENSORS_W83795_FANCTRL for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) { +#else + for (j = 0; j < 8; j++) { +#endif err = fn(dev, &w83795_temp[i][j].dev_attr); if (err) return err; From 24377101023d3c66136123bd8b20441814da6a90 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 58/68] hwmon: (w83795) Use standard attributes for chassis intrusion Follow the standard attribute naming for the chassis intrusion feature. I couldn't test the beeping (my board apparently doesn't do that) but the alarm works fine. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- drivers/hwmon/w83795.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index c461e753f9bf..1d840aa83782 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -713,7 +713,7 @@ store_beep(struct device *dev, struct device_attribute *attr, return count; } -/* Write any value to clear chassis alarm */ +/* Write 0 to clear chassis alarm */ static ssize_t store_chassis_clear(struct device *dev, struct device_attribute *attr, const char *buf, @@ -721,7 +721,10 @@ store_chassis_clear(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct w83795_data *data = i2c_get_clientdata(client); - u8 val; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0 || val != 0) + return -EINVAL; mutex_lock(&data->update_lock); val = w83795_read(client, W83795_REG_CLR_CHASSIS); @@ -1709,8 +1712,10 @@ static const struct sensor_device_attribute_2 w83795_pwm[][7] = { }; static const struct sensor_device_attribute_2 sda_single_files[] = { - SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep, + SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep, store_chassis_clear, ALARM_STATUS, 46), + SENSOR_ATTR_2(intrusion0_beep, S_IWUSR | S_IRUGO, show_alarm_beep, + store_beep, BEEP_ENABLE, 46), SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep, store_beep, BEEP_ENABLE, 47), #ifdef CONFIG_SENSORS_W83795_FANCTRL From 4275fcd65d3b4a504b7779e532c81a927afd28c0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:49 +0200 Subject: [PATCH 59/68] hwmon: (pcf8591) Register as a hwmon device Register PCF8591 devices as hwmon devices. There's little point in implementing the standard sysfs interface if we don't register it in a way libsensors will pick it. Signed-off-by: Jean Delvare Reviewed-by: Guenter Roeck Cc: Aurelien Jarno --- drivers/hwmon/pcf8591.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index d44787949851..d7df0e6c1ef4 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, @@ -71,6 +73,7 @@ MODULE_PARM_DESC(input_mode, #define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) struct pcf8591_data { + struct device *hwmon_dev; struct mutex update_lock; u8 control; @@ -221,6 +224,12 @@ static int pcf8591_probe(struct i2c_client *client, goto exit_sysfs_remove; } + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_sysfs_remove; + } + return 0; exit_sysfs_remove: @@ -234,6 +243,9 @@ static int pcf8591_probe(struct i2c_client *client, static int pcf8591_remove(struct i2c_client *client) { + struct pcf8591_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); kfree(i2c_get_clientdata(client)); From 6dfee85397a47063291fe199eaf950bee7944454 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 60/68] hwmon: (pcf8591) Don't attempt to detect devices The PCF8591 can't be detected, don't even try. There are plenty of other means to instantiate i2c devices these days. Signed-off-by: Jean Delvare Reviewed-by: Guenter Roeck Cc: Aurelien Jarno --- Documentation/hwmon/pcf8591 | 18 ++++++++---------- drivers/hwmon/pcf8591.c | 26 -------------------------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/Documentation/hwmon/pcf8591 b/Documentation/hwmon/pcf8591 index e76a7892f68e..ac020b3bb7b3 100644 --- a/Documentation/hwmon/pcf8591 +++ b/Documentation/hwmon/pcf8591 @@ -4,7 +4,7 @@ Kernel driver pcf8591 Supported chips: * Philips/NXP PCF8591 Prefix: 'pcf8591' - Addresses scanned: I2C 0x48 - 0x4f + Addresses scanned: none Datasheet: Publicly available at the NXP website http://www.nxp.com/pip/PCF8591_6.html @@ -58,18 +58,16 @@ Module parameters Accessing PCF8591 via /sys interface ------------------------------------- -! Be careful ! -The PCF8591 is plainly impossible to detect! Stupid chip. -So every chip with address in the interval [0x48..0x4f] is -detected as PCF8591. If you have other chips in this address -range, the workaround is to load this module after the one -for your others chips. +The PCF8591 is plainly impossible to detect! Thus the driver won't even +try. You have to explicitly instantiate the device at the relevant +address (in the interval [0x48..0x4f]) either through platform data, or +using the sysfs interface. See Documentation/i2c/instantiating-devices +for details. -On detection (i.e. insmod, modprobe et al.), directories are being -created for each detected PCF8591: +Directories are being created for each instantiated PCF8591: /sys/bus/i2c/devices/<0>-<1>/ -where <0> is the bus the chip was detected on (e. g. i2c-0) +where <0> is the bus the chip is connected to (e. g. i2c-0) and <1> the chip address ([48..4f]) Inside these directories, there are such files: diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index d7df0e6c1ef4..dc7259d69812 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -26,10 +26,6 @@ #include #include -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; - /* Insmod parameters */ static int input_mode; @@ -170,24 +166,6 @@ static const struct attribute_group pcf8591_attr_group_opt = { * Real code */ -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8591_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE - | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - /* Now, we would do the remaining detection. But the PCF8591 is plainly - impossible to detect! Stupid chip. */ - - strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); - - return 0; -} - static int pcf8591_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -307,10 +285,6 @@ static struct i2c_driver pcf8591_driver = { .probe = pcf8591_probe, .remove = pcf8591_remove, .id_table = pcf8591_id, - - .class = I2C_CLASS_HWMON, /* Nearest choice */ - .detect = pcf8591_detect, - .address_list = normal_i2c, }; static int __init pcf8591_init(void) From bd5f47ec961594b1091839333600008f8166fd00 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 61/68] Move ams driver to macintosh The ams driver isn't a hardware monitoring driver, so it shouldn't live under driver/hwmon. drivers/macintosh seems much more appropriate, as the driver is only useful on PowerBooks and iBooks. Signed-off-by: Jean Delvare Cc: Guenter Roeck Cc: Stelian Pop Cc: Michael Hanselmann Cc: Benjamin Herrenschmidt Cc: Grant Likely --- MAINTAINERS | 2 +- drivers/hwmon/Kconfig | 26 -------------------- drivers/hwmon/Makefile | 1 - drivers/macintosh/Kconfig | 26 ++++++++++++++++++++ drivers/macintosh/Makefile | 2 ++ drivers/{hwmon => macintosh}/ams/Makefile | 0 drivers/{hwmon => macintosh}/ams/ams-core.c | 0 drivers/{hwmon => macintosh}/ams/ams-i2c.c | 0 drivers/{hwmon => macintosh}/ams/ams-input.c | 0 drivers/{hwmon => macintosh}/ams/ams-pmu.c | 0 drivers/{hwmon => macintosh}/ams/ams.h | 0 11 files changed, 29 insertions(+), 28 deletions(-) rename drivers/{hwmon => macintosh}/ams/Makefile (100%) rename drivers/{hwmon => macintosh}/ams/ams-core.c (100%) rename drivers/{hwmon => macintosh}/ams/ams-i2c.c (100%) rename drivers/{hwmon => macintosh}/ams/ams-input.c (100%) rename drivers/{hwmon => macintosh}/ams/ams-pmu.c (100%) rename drivers/{hwmon => macintosh}/ams/ams.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 19f262c27323..49e88f3872c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -432,7 +432,7 @@ AMS (Apple Motion Sensor) DRIVER M: Stelian Pop M: Michael Hanselmann S: Supported -F: drivers/hwmon/ams/ +F: drivers/macintosh/ams/ AMSO1100 RNIC DRIVER M: Tom Tucker diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index a06ac97229d9..ad54f6432e2d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -249,32 +249,6 @@ config SENSORS_K10TEMP This driver can also be built as a module. If so, the module will be called k10temp. -config SENSORS_AMS - tristate "Apple Motion Sensor driver" - depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL - select INPUT_POLLDEV - help - Support for the motion sensor included in PowerBooks. Includes - implementations for PMU and I2C. - - This driver can also be built as a module. If so, the module - will be called ams. - -config SENSORS_AMS_PMU - bool "PMU variant" - depends on SENSORS_AMS && ADB_PMU - default y - help - PMU variant of motion sensor, found in late 2005 PowerBooks. - -config SENSORS_AMS_I2C - bool "I2C variant" - depends on SENSORS_AMS && I2C - default y - help - I2C variant of motion sensor, found in early 2005 PowerBooks and - iBooks. - config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on X86 && I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 641b8b349a3d..2479b3da272c 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -36,7 +36,6 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o -obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index fd85bde283a0..3d7355ff7308 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -256,4 +256,30 @@ config PMAC_RACKMETER This driver provides some support to control the front panel blue LEDs "vu-meter" of the XServer macs. +config SENSORS_AMS + tristate "Apple Motion Sensor driver" + depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL + select INPUT_POLLDEV + help + Support for the motion sensor included in PowerBooks. Includes + implementations for PMU and I2C. + + This driver can also be built as a module. If so, the module + will be called ams. + +config SENSORS_AMS_PMU + bool "PMU variant" + depends on SENSORS_AMS && ADB_PMU + default y + help + PMU variant of motion sensor, found in late 2005 PowerBooks. + +config SENSORS_AMS_I2C + bool "I2C variant" + depends on SENSORS_AMS && I2C + default y + help + I2C variant of motion sensor, found in early 2005 PowerBooks and + iBooks. + endif # MACINTOSH_DRIVERS diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index e3132efa17c0..6652a6ebb6fa 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -48,3 +48,5 @@ obj-$(CONFIG_WINDFARM_PM121) += windfarm_pm121.o windfarm_smu_sat.o \ windfarm_max6690_sensor.o \ windfarm_lm75_sensor.o windfarm_pid.o obj-$(CONFIG_PMAC_RACKMETER) += rack-meter.o + +obj-$(CONFIG_SENSORS_AMS) += ams/ diff --git a/drivers/hwmon/ams/Makefile b/drivers/macintosh/ams/Makefile similarity index 100% rename from drivers/hwmon/ams/Makefile rename to drivers/macintosh/ams/Makefile diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/macintosh/ams/ams-core.c similarity index 100% rename from drivers/hwmon/ams/ams-core.c rename to drivers/macintosh/ams/ams-core.c diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c similarity index 100% rename from drivers/hwmon/ams/ams-i2c.c rename to drivers/macintosh/ams/ams-i2c.c diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/macintosh/ams/ams-input.c similarity index 100% rename from drivers/hwmon/ams/ams-input.c rename to drivers/macintosh/ams/ams-input.c diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/macintosh/ams/ams-pmu.c similarity index 100% rename from drivers/hwmon/ams/ams-pmu.c rename to drivers/macintosh/ams/ams-pmu.c diff --git a/drivers/hwmon/ams/ams.h b/drivers/macintosh/ams/ams.h similarity index 100% rename from drivers/hwmon/ams/ams.h rename to drivers/macintosh/ams/ams.h From 918ee91c071d6248c48bc6457a6dea286146e3ad Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 62/68] hwmon: I2C addresses are constant We can mark normal_i2c const. Almost all drivers do that already, so fix the 3 remaining ones before they are used as (bad) examples for new drivers. Signed-off-by: Jean Delvare Cc: George Joseph Reviewed-by: Guenter Roeck --- drivers/hwmon/adt7475.c | 2 +- drivers/hwmon/asc7621.c | 4 ++-- drivers/hwmon/tmp421.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index a0c385145686..b5fcd87931cb 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -146,7 +146,7 @@ #define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx)) #define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx)) -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { adt7473, adt7475, adt7476, adt7490 }; diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 89b4f3babe87..d2596cec18b5 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -28,7 +28,7 @@ #include /* Addresses to scan */ -static unsigned short normal_i2c[] = { +static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; @@ -52,7 +52,7 @@ struct asc7621_chip { u8 company_id; u8 verstep_reg; u8 verstep_id; - unsigned short *addresses; + const unsigned short *addresses; }; static struct asc7621_chip asc7621_chips[] = { diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 6b4165c12092..0517a8f09d35 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -36,8 +36,8 @@ #include /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f, - I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f, + I2C_CLIENT_END }; enum chips { tmp421, tmp422, tmp423 }; From fa7a5797e57d2ed71f9a6fb44f0ae42c2d7b74b7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 63/68] hwmon: (lm85) Fix ADT7468 frequency table The ADT7468 uses the same frequency table as the ADT7463. Signed-off-by: Jean Delvare Cc: stable@kernel.org Cc: Darrick J. Wong Acked-by: Guenter Roeck --- drivers/hwmon/lm85.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index b3841a615595..2e8f0c9458d4 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1259,6 +1259,7 @@ static int lm85_probe(struct i2c_client *client, switch (data->type) { case adm1027: case adt7463: + case adt7468: case emc6d100: case emc6d102: data->freq_map = adm1027_freq_map; From c36364dbf38382ef6be2fb99a3ce361a679c0ecb Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 64/68] hwmon: (lm85) Document the ADT7468 as supported Signed-off-by: Jean Delvare Cc: Darrick J. Wong Acked-by: Guenter Roeck --- Documentation/hwmon/lm85 | 53 ++++++++++++++++++++++------------------ drivers/hwmon/Kconfig | 3 ++- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index b98e0e0d1910..83cd5bb1ac79 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -14,6 +14,10 @@ Supported chips: Prefix: 'adt7463' Addresses scanned: I2C 0x2c, 0x2d, 0x2e Datasheet: http://www.onsemi.com/PowerSolutions/product.do?id=ADT7463 + * Analog Devices ADT7468 + Prefix: 'adt7468' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.onsemi.com/PowerSolutions/product.do?id=ADT7468 * SMSC EMC6D100, SMSC EMC6D101 Prefix: 'emc6d100' Addresses scanned: I2C 0x2c, 0x2d, 0x2e @@ -34,7 +38,7 @@ Description ----------- This driver implements support for the National Semiconductor LM85 and -compatible chips including the Analog Devices ADM1027, ADT7463 and +compatible chips including the Analog Devices ADM1027, ADT7463, ADT7468 and SMSC EMC6D10x chips family. The LM85 uses the 2-wire interface compatible with the SMBUS 2.0 @@ -87,14 +91,15 @@ To smooth the response of fans to changes in temperature, the LM85 has an optional filter for smoothing temperatures. The ADM1027 has the same config option but uses it to rate limit the changes to fan speed instead. -The ADM1027 and ADT7463 have a 10-bit ADC and can therefore measure -temperatures with 0.25 degC resolution. They also provide an offset to the -temperature readings that is automatically applied during measurement. -This offset can be used to zero out any errors due to traces and placement. -The documentation says that the offset is in 0.25 degC steps, but in -initial testing of the ADM1027 it was 1.00 degC steps. Analog Devices has -confirmed this "bug". The ADT7463 is reported to work as described in the -documentation. The current lm85 driver does not show the offset register. +The ADM1027, ADT7463 and ADT7468 have a 10-bit ADC and can therefore +measure temperatures with 0.25 degC resolution. They also provide an offset +to the temperature readings that is automatically applied during +measurement. This offset can be used to zero out any errors due to traces +and placement. The documentation says that the offset is in 0.25 degC +steps, but in initial testing of the ADM1027 it was 1.00 degC steps. Analog +Devices has confirmed this "bug". The ADT7463 is reported to work as +described in the documentation. The current lm85 driver does not show the +offset register. See the vendor datasheets for more information. There is application note from National (AN-1260) with some additional information about the LM85. @@ -125,17 +130,17 @@ datasheet for a complete description of the differences. Other than identifying the chip, the driver behaves no differently with regard to these two chips. The LM85B is recommended for new designs. -The ADM1027 and ADT7463 chips have an optional SMBALERT output that can be -used to signal the chipset in case a limit is exceeded or the temperature -sensors fail. Individual sensor interrupts can be masked so they won't -trigger SMBALERT. The SMBALERT output if configured replaces one of the other -functions (PWM2 or IN0). This functionality is not implemented in current -driver. +The ADM1027, ADT7463 and ADT7468 chips have an optional SMBALERT output +that can be used to signal the chipset in case a limit is exceeded or the +temperature sensors fail. Individual sensor interrupts can be masked so +they won't trigger SMBALERT. The SMBALERT output if configured replaces one +of the other functions (PWM2 or IN0). This functionality is not implemented +in current driver. -The ADT7463 also has an optional THERM output/input which can be connected -to the processor PROC_HOT output. If available, the autofan control -dynamic Tmin feature can be enabled to keep the system temperature within -spec (just?!) with the least possible fan noise. +The ADT7463 and ADT7468 also have an optional THERM output/input which can +be connected to the processor PROC_HOT output. If available, the autofan +control dynamic Tmin feature can be enabled to keep the system temperature +within spec (just?!) with the least possible fan noise. Configuration Notes ------------------- @@ -201,8 +206,8 @@ the temperatures to compensate for systemic errors in the measurements. These features are not currently supported by the lm85 driver. -In addition to the ADM1027 features, the ADT7463 also has Tmin control -and THERM asserted counts. Automatic Tmin control acts to adjust the -Tmin value to maintain the measured temperature sensor at a specified -temperature. There isn't much documentation on this feature in the -ADT7463 data sheet. This is not supported by current driver. +In addition to the ADM1027 features, the ADT7463 and ADT7468 also have +Tmin control and THERM asserted counts. Automatic Tmin control acts to +adjust the Tmin value to maintain the measured temperature sensor at a +specified temperature. There isn't much documentation on this feature in +the ADT7463 data sheet. This is not supported by current driver. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ad54f6432e2d..ba6905a212bc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -565,7 +565,8 @@ config SENSORS_LM85 select HWMON_VID help If you say yes here you get support for National Semiconductor LM85 - sensor chips and clones: ADT7463, EMC6D100, EMC6D102 and ADM1027. + sensor chips and clones: ADM1027, ADT7463, ADT7468, EMC6D100, + EMC6D101 and EMC6D102. This driver can also be built as a module. If so, the module will be called lm85. From f6c61cff8bcb58b8dfb645d4243a049908c02024 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 65/68] hwmon: (lm85) Add support for ADT7468 high-frequency PWM mode The ADT7468 supports a high-frequency PWM output mode where all PWM outputs are driven by a 22.5 kHz clock. Add support for this mode, and document it, as it may surprise the user that setting one PWM output frequency also affects the other PWM outputs. Signed-off-by: Jean Delvare Cc: Darrick J. Wong Acked-by: Guenter Roeck --- Documentation/hwmon/lm85 | 7 +++++++ drivers/hwmon/lm85.c | 35 ++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index 83cd5bb1ac79..239258a63c81 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -101,6 +101,13 @@ Devices has confirmed this "bug". The ADT7463 is reported to work as described in the documentation. The current lm85 driver does not show the offset register. +The ADT7468 has a high-frequency PWM mode, where all PWM outputs are +driven by a 22.5 kHz clock. This is a global mode, not per-PWM output, +which means that setting any PWM frequency above 11.3 kHz will switch +all 3 PWM outputs to a 22.5 kHz frequency. Conversely, setting any PWM +frequency below 11.3 kHz will switch all 3 PWM outputs to a frequency +between 10 and 100 Hz, which can then be tuned separately. + See the vendor datasheets for more information. There is application note from National (AN-1260) with some additional information about the LM85. The Analog Devices datasheet is very detailed and describes a procedure for diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 2e8f0c9458d4..1e229847f37a 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -64,9 +64,12 @@ enum chips { #define LM85_REG_VERSTEP 0x3f #define ADT7468_REG_CFG5 0x7c -#define ADT7468_OFF64 0x01 +#define ADT7468_OFF64 (1 << 0) +#define ADT7468_HFPWM (1 << 1) #define IS_ADT7468_OFF64(data) \ ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64)) +#define IS_ADT7468_HFPWM(data) \ + ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_HFPWM)) /* These are the recognized values for the above regs */ #define LM85_COMPANY_NATIONAL 0x01 @@ -567,8 +570,14 @@ static ssize_t show_pwm_freq(struct device *dev, { int nr = to_sensor_dev_attr(attr)->index; struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map, - data->pwm_freq[nr])); + int freq; + + if (IS_ADT7468_HFPWM(data)) + freq = 22500; + else + freq = FREQ_FROM_REG(data->freq_map, data->pwm_freq[nr]); + + return sprintf(buf, "%d\n", freq); } static ssize_t set_pwm_freq(struct device *dev, @@ -580,10 +589,22 @@ static ssize_t set_pwm_freq(struct device *dev, long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->pwm_freq[nr]); + /* The ADT7468 has a special high-frequency PWM output mode, + * where all PWM outputs are driven by a 22.5 kHz clock. + * This might confuse the user, but there's not much we can do. */ + if (data->type == adt7468 && val >= 11300) { /* High freq. mode */ + data->cfg5 &= ~ADT7468_HFPWM; + lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5); + } else { /* Low freq. mode */ + data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + if (data->type == adt7468) { + data->cfg5 |= ADT7468_HFPWM; + lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5); + } + } mutex_unlock(&data->update_lock); return count; } From 0b22ce3b7de718882e2c2ca979490a50d798a838 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: [PATCH 66/68] hwmon: Remove many EXPERIMENTAL flags Remove the dependency on EXPERIMENTAL for all drivers which are in the kernel tree for a long time, are known to work properly and for which we have documentation. Signed-off-by: Jean Delvare Cc: Corentin Labbe Cc: Riku Voipio Cc: Juerg Haefliger Acked-by: Guenter Roeck --- drivers/hwmon/Kconfig | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ba6905a212bc..aefb9e6db237 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -129,7 +129,7 @@ config SENSORS_ADM1025 config SENSORS_ADM1026 tristate "Analog Devices ADM1026 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for Analog Devices ADM1026 @@ -140,7 +140,7 @@ config SENSORS_ADM1026 config SENSORS_ADM1029 tristate "Analog Devices ADM1029" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Analog Devices ADM1029 sensor chip. @@ -151,7 +151,7 @@ config SENSORS_ADM1029 config SENSORS_ADM1031 tristate "Analog Devices ADM1031 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Analog Devices ADM1031 and ADM1030 sensor chips. @@ -202,7 +202,7 @@ config SENSORS_ADT7470 config SENSORS_ADT7475 tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the Analog Devices @@ -296,7 +296,6 @@ config SENSORS_I5K_AMB config SENSORS_F71805F tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" - depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG @@ -307,7 +306,6 @@ config SENSORS_F71805F config SENSORS_F71882FG tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000" - depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG, @@ -317,8 +315,8 @@ config SENSORS_F71882FG will be called f71882fg. config SENSORS_F75375S - tristate "Fintek F75375S/SP and F75373"; - depends on I2C && EXPERIMENTAL + tristate "Fintek F75375S/SP and F75373" + depends on I2C help If you say yes here you get support for hardware monitoring features of the Fintek F75375S/SP and F75373 @@ -473,7 +471,7 @@ config SENSORS_LM63 config SENSORS_LM70 tristate "National Semiconductor LM70 / Texas Instruments TMP121" - depends on SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER help If you say yes here you get support for the National Semiconductor LM70 and Texas Instruments TMP121/TMP123 digital temperature @@ -541,7 +539,7 @@ config SENSORS_LM78 config SENSORS_LM80 tristate "National Semiconductor LM80" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for National Semiconductor LM80 sensor chips. @@ -561,7 +559,7 @@ config SENSORS_LM83 config SENSORS_LM85 tristate "National Semiconductor LM85 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for National Semiconductor LM85 @@ -701,7 +699,6 @@ config SENSORS_PC87360 config SENSORS_PC87427 tristate "National Semiconductor PC87427" - depends on EXPERIMENTAL help If you say yes here you get access to the hardware monitoring functions of the National Semiconductor PC87427 Super-I/O chip. @@ -829,7 +826,7 @@ config SENSORS_SMSC47M1 config SENSORS_SMSC47M192 tristate "SMSC LPC47M192 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the temperature and @@ -885,7 +882,7 @@ config SENSORS_AMC6821 config SENSORS_THMC50 tristate "Texas Instruments THMC50 / Analog Devices ADM1022" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Texas Instruments THMC50 sensor chips and clones: the Analog Devices ADM1022. @@ -943,7 +940,6 @@ config SENSORS_VIA686A config SENSORS_VT1211 tristate "VIA VT1211" - depends on EXPERIMENTAL select HWMON_VID help If you say yes here then you get support for hardware monitoring @@ -987,7 +983,7 @@ config SENSORS_W83791D config SENSORS_W83792D tristate "Winbond W83792D" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Winbond W83792D chip. From 0df6454da937548594774788b009616ed27607ed Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:51 +0200 Subject: [PATCH 67/68] hwmon: (it87) Move conversion functions Move conversion functions until after structure defintions. This is needed for future changes which make use of the structures in the conversion funtcions. Signed-off-by: Jean Delvare --- drivers/hwmon/it87.c | 102 ++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index f7701295937d..7a3616ccbf05 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -202,56 +202,6 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) #define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, - 254); -} - -static inline u16 FAN16_TO_REG(long rpm) -{ - if (rpm == 0) - return 0xffff; - return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); -} - -#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) -/* The divider is fixed to 2 in 16-bit mode */ -#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2)) - -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ - ((val)+500)/1000),-128,127)) -#define TEMP_FROM_REG(val) ((val) * 1000) - -#define PWM_TO_REG(val) ((val) >> 1) -#define PWM_FROM_REG(val) (((val)&0x7f) << 1) - -static int DIV_TO_REG(int val) -{ - int answer = 0; - while (answer < 7 && (val >>= 1)) - answer++; - return answer; -} -#define DIV_FROM_REG(val) (1 << (val)) - -static const unsigned int pwm_freq[8] = { - 48000000 / 128, - 24000000 / 128, - 12000000 / 128, - 8000000 / 128, - 6000000 / 128, - 3000000 / 128, - 1500000 / 128, - 750000 / 128, -}; - struct it87_sio_data { enum chips type; @@ -310,6 +260,58 @@ struct it87_data { s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255)) +#define IN_FROM_REG(val) ((val) * 16) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +static inline u16 FAN16_TO_REG(long rpm) +{ + if (rpm == 0) + return 0xffff; + return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); +} + +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 255 ? 0 : \ + 1350000 / ((val) * (div))) +/* The divider is fixed to 2 in 16-bit mode */ +#define FAN16_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \ + 1350000 / ((val) * 2)) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (((val) - 500) / 1000) : \ + ((val) + 500) / 1000), -128, 127)) +#define TEMP_FROM_REG(val) ((val) * 1000) + +#define PWM_TO_REG(val) ((val) >> 1) +#define PWM_FROM_REG(val) (((val) & 0x7f) << 1) + +static int DIV_TO_REG(int val) +{ + int answer = 0; + while (answer < 7 && (val >>= 1)) + answer++; + return answer; +} +#define DIV_FROM_REG(val) (1 << (val)) + +static const unsigned int pwm_freq[8] = { + 48000000 / 128, + 24000000 / 128, + 12000000 / 128, + 8000000 / 128, + 6000000 / 128, + 3000000 / 128, + 1500000 / 128, + 750000 / 128, +}; + static inline int has_16bit_fans(const struct it87_data *data) { /* IT8705F Datasheet 0.4.1, 3h == Version G. From 44c1bcd4bcde32b2a31a6775a277706ab489c0dc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:51 +0200 Subject: [PATCH 68/68] hwmon: (it87) Add support for the IT8721F/IT8758E Add support for the IT8721F/IT8758E. These new chips differ from the older IT87xxF chips in the following ways: * ADC LSB is 12 mV instead of 16 mV. * PWM values are 8-bit instead of 7-bit. There are other minor changes we don't have to care about in the driver. Another change is that we will handle internal voltage scaling in the driver instead of delegating the work to user-space. Signed-off-by: Jean Delvare --- Documentation/hwmon/it87 | 28 +++++++--- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/it87.c | 116 +++++++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 33 deletions(-) diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 8d08bf0d38ed..38425f0f2645 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -22,6 +22,10 @@ Supported chips: Prefix: 'it8720' Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Not publicly available + * IT8721F/IT8758E + Prefix: 'it8721' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available * SiS950 [clone of IT8705F] Prefix: 'it87' Addresses scanned: from Super I/O config space (8 I/O ports) @@ -67,7 +71,7 @@ Description ----------- This driver implements support for the IT8705F, IT8712F, IT8716F, -IT8718F, IT8720F, IT8726F and SiS950 chips. +IT8718F, IT8720F, IT8721F, IT8726F, IT8758E and SiS950 chips. These chips are 'Super I/O chips', supporting floppy disks, infrared ports, joysticks and other miscellaneous stuff. For hardware monitoring, they @@ -86,14 +90,15 @@ the driver won't notice and report changes in the VID value. The two upper VID bits share their pins with voltage inputs (in5 and in6) so you can't have both on a given board. -The IT8716F, IT8718F, IT8720F and later IT8712F revisions have support for -2 additional fans. The additional fans are supported by the driver. +The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E and later IT8712F revisions +have support for 2 additional fans. The additional fans are supported by the +driver. -The IT8716F, IT8718F and IT8720F, and late IT8712F and IT8705F also have -optional 16-bit tachometer counters for fans 1 to 3. This is better (no more -fan clock divider mess) but not compatible with the older chips and -revisions. The 16-bit tachometer mode is enabled by the driver when one -of the above chips is detected. +The IT8716F, IT8718F, IT8720F and IT8721F/IT8758E, and late IT8712F and +IT8705F also have optional 16-bit tachometer counters for fans 1 to 3. This +is better (no more fan clock divider mess) but not compatible with the older +chips and revisions. The 16-bit tachometer mode is enabled by the driver when +one of the above chips is detected. The IT8726F is just bit enhanced IT8716F with additional hardware for AMD power sequencing. Therefore the chip will appear as IT8716F @@ -115,7 +120,12 @@ alarm is triggered if the voltage has crossed a programmable minimum or maximum limit. Note that minimum in this case always means 'closest to zero'; this is important for negative voltage measurements. All voltage inputs can measure voltages between 0 and 4.08 volts, with a resolution of -0.016 volt. The battery voltage in8 does not have limit registers. +0.016 volt (except IT8721F/IT8758E: 0.012 volt.) The battery voltage in8 does +not have limit registers. + +On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside +the chip (in7, in8 and optionally in3). The driver handles this transparently +so user-space doesn't have to care. The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value: the voltage level your processor should work with. This is hardcoded by diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index aefb9e6db237..a56f6adf3b76 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -428,8 +428,8 @@ config SENSORS_IT87 select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F, IT8720F and IT8726F sensor chips, and the - SiS960 clone. + IT8716F, IT8718F, IT8720F, IT8721F, IT8726F and IT8758E sensor + chips, and the SiS960 clone. This driver can also be built as a module. If so, the module will be called it87. diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 7a3616ccbf05..14a5d981be7d 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -15,7 +15,9 @@ * IT8716F Super I/O chip w/LPC interface * IT8718F Super I/O chip w/LPC interface * IT8720F Super I/O chip w/LPC interface + * IT8721F Super I/O chip w/LPC interface * IT8726F Super I/O chip w/LPC interface + * IT8758E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -54,7 +56,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -126,6 +128,7 @@ superio_exit(void) #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 #define IT8720F_DEVID 0x8720 +#define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -229,6 +232,7 @@ struct it87_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ + u16 in_scaled; /* Internal voltage sensors are scaled */ u8 in[9]; /* Register value */ u8 in_max[8]; /* Register value */ u8 in_min[8]; /* Register value */ @@ -260,8 +264,32 @@ struct it87_data { s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255)) -#define IN_FROM_REG(val) ((val) * 16) +static u8 in_to_reg(const struct it87_data *data, int nr, long val) +{ + long lsb; + + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + lsb = 24; + else + lsb = 12; + } else + lsb = 16; + + val = DIV_ROUND_CLOSEST(val, lsb); + return SENSORS_LIMIT(val, 0, 255); +} + +static int in_from_reg(const struct it87_data *data, int nr, int val) +{ + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + return val * 24; + else + return val * 12; + } else + return val * 16; +} static inline u8 FAN_TO_REG(long rpm, int div) { @@ -289,8 +317,22 @@ static inline u16 FAN16_TO_REG(long rpm) ((val) + 500) / 1000), -128, 127)) #define TEMP_FROM_REG(val) ((val) * 1000) -#define PWM_TO_REG(val) ((val) >> 1) -#define PWM_FROM_REG(val) (((val) & 0x7f) << 1) +static u8 pwm_to_reg(const struct it87_data *data, long val) +{ + if (data->type == it8721) + return val; + else + return val >> 1; +} + +static int pwm_from_reg(const struct it87_data *data, u8 reg) +{ + if (data->type == it8721) + return reg; + else + return (reg & 0x7f) << 1; +} + static int DIV_TO_REG(int val) { @@ -321,7 +363,8 @@ static inline int has_16bit_fans(const struct it87_data *data) || (data->type == it8712 && data->revision >= 0x08) || data->type == it8716 || data->type == it8718 - || data->type == it8720; + || data->type == it8720 + || data->type == it8721; } static inline int has_old_autopwm(const struct it87_data *data) @@ -359,7 +402,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr])); } static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, @@ -369,7 +412,7 @@ static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_min[nr])); } static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, @@ -379,7 +422,7 @@ static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_max[nr])); } static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, @@ -395,7 +438,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); + data->in_min[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MIN(nr), data->in_min[nr]); mutex_unlock(&data->update_lock); @@ -414,7 +457,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); + data->in_max[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MAX(nr), data->in_max[nr]); mutex_unlock(&data->update_lock); @@ -644,7 +687,8 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->pwm_duty[nr])); } static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -814,7 +858,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->pwm_duty[nr] = PWM_TO_REG(val); + data->pwm_duty[nr] = pwm_to_reg(data, val); /* If we are in manual mode, write the duty cycle immediately; * otherwise, just store it for later use. */ if (!(data->pwm_ctrl[nr] & 0x80)) { @@ -918,7 +962,8 @@ static ssize_t show_auto_pwm(struct device *dev, int nr = sensor_attr->nr; int point = sensor_attr->index; - return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->auto_pwm[nr][point])); } static ssize_t set_auto_pwm(struct device *dev, @@ -935,7 +980,7 @@ static ssize_t set_auto_pwm(struct device *dev, return -EINVAL; mutex_lock(&data->update_lock); - data->auto_pwm[nr][point] = PWM_TO_REG(val); + data->auto_pwm[nr][point] = pwm_to_reg(data, val); it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), data->auto_pwm[nr][point]); mutex_unlock(&data->update_lock); @@ -1205,9 +1250,16 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr, "5VSB", "Vbat", }; + static const char *labels_it8721[] = { + "+3.3V", + "3VSB", + "Vbat", + }; + struct it87_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%s\n", labels[nr]); + return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr] + : labels[nr]); } static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); @@ -1492,6 +1544,9 @@ static int __init it87_find(unsigned short *address, case IT8720F_DEVID: sio_data->type = it8720; break; + case IT8721F_DEVID: + sio_data->type = it8721; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1532,11 +1587,17 @@ static int __init it87_find(unsigned short *address, int reg; superio_select(GPIO); - /* We need at least 4 VID pins */ + reg = superio_inb(IT87_SIO_GPIO3_REG); - if (reg & 0x0f) { - pr_info("it87: VID is disabled (pins used for GPIO)\n"); + if (sio_data->type == it8721) { + /* The IT8721F/IT8758E doesn't have VID pins at all */ sio_data->skip_vid = 1; + } else { + /* We need at least 4 VID pins */ + if (reg & 0x0f) { + pr_info("it87: VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } } /* Check if fan3 is there or not */ @@ -1574,7 +1635,7 @@ static int __init it87_find(unsigned short *address, } if (reg & (1 << 0)) sio_data->internal |= (1 << 0); - if (reg & (1 << 1)) + if ((reg & (1 << 1)) || sio_data->type == it8721) sio_data->internal |= (1 << 1); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; @@ -1652,6 +1713,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8716", "it8718", "it8720", + "it8721", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1688,6 +1750,16 @@ static int __devinit it87_probe(struct platform_device *pdev) /* Check PWM configuration */ enable_pwm_interface = it87_check_pwm(dev); + /* Starting with IT8721F, we handle scaling of internal voltages */ + if (data->type == it8721) { + if (sio_data->internal & (1 << 0)) + data->in_scaled |= (1 << 3); /* in3 is AVCC */ + if (sio_data->internal & (1 << 1)) + data->in_scaled |= (1 << 7); /* in7 is VSB */ + if (sio_data->internal & (1 << 2)) + data->in_scaled |= (1 << 8); /* in8 is Vbat */ + } + /* Initialize the IT87 chip */ it87_init_device(pdev); @@ -2053,7 +2125,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. - The 8718 and the 8720 don't use IT87_REG_VID for the + The 8718 and later don't use IT87_REG_VID for the same purpose. */ if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(data, IT87_REG_VID); @@ -2153,7 +2225,7 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, " "Jean Delvare "); -MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); +MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0);