Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
- Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2007--2009 Jean Delvare <khali@linux-fr.org>
Chip details at <http://www.national.com/ds/LM/LM85.pdf>
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102);
+I2C_CLIENT_INSMOD_7(lm85b, lm85c, adm1027, adt7463, adt7468, emc6d100,
+ emc6d102);
/* The LM85 registers */
#define LM85_REG_COMPANY 0x3e
#define LM85_REG_VERSTEP 0x3f
+
+#define ADT7468_REG_CFG5 0x7c
+#define ADT7468_OFF64 0x01
+#define IS_ADT7468_OFF64(data) \
+ ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64))
+
/* These are the recognized values for the above regs */
#define LM85_COMPANY_NATIONAL 0x01
#define LM85_COMPANY_ANALOG_DEV 0x41
#define LM85_COMPANY_SMSC 0x5c
#define LM85_VERSTEP_VMASK 0xf0
#define LM85_VERSTEP_GENERIC 0x60
+#define LM85_VERSTEP_GENERIC2 0x70
#define LM85_VERSTEP_LM85C 0x60
#define LM85_VERSTEP_LM85B 0x62
+#define LM85_VERSTEP_LM96000_1 0x68
+#define LM85_VERSTEP_LM96000_2 0x69
#define LM85_VERSTEP_ADM1027 0x60
#define LM85_VERSTEP_ADT7463 0x62
#define LM85_VERSTEP_ADT7463C 0x6A
+#define LM85_VERSTEP_ADT7468_1 0x71
+#define LM85_VERSTEP_ADT7468_2 0x72
#define LM85_VERSTEP_EMC6D100_A0 0x60
#define LM85_VERSTEP_EMC6D100_A1 0x61
#define LM85_VERSTEP_EMC6D102 0x65
u8 vid; /* Register value */
u8 vrm; /* VRM version */
u32 alarms; /* Register encoding, combined */
+ u8 cfg5; /* Config Register 5 on ADT7468 */
struct lm85_autofan autofan[3];
struct lm85_zone zone[3];
};
static const struct i2c_device_id lm85_id[] = {
{ "adm1027", adm1027 },
{ "adt7463", adt7463 },
+ { "adt7468", adt7468 },
{ "lm85", any_chip },
{ "lm85b", lm85b },
{ "lm85c", lm85c },
struct lm85_data *data = lm85_update_device(dev);
int vid;
- if (data->type == adt7463 && (data->vid & 0x80)) {
+ if ((data->type == adt7463 || data->type == adt7468) &&
+ (data->vid & 0x80)) {
/* 6-pin VID (VRM 10) */
vid = vid_from_reg(data->vid & 0x3f, data->vrm);
} else {
struct lm85_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
+ if (IS_ADT7468_OFF64(data))
+ val += 64;
+
mutex_lock(&data->update_lock);
data->temp_min[nr] = TEMP_TO_REG(val);
lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]);
struct lm85_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
+ if (IS_ADT7468_OFF64(data))
+ val += 64;
+
mutex_lock(&data->update_lock);
data->temp_max[nr] = TEMP_TO_REG(val);
lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]);
dev_warn(&client->dev, "Device is not ready\n");
}
+static int lm85_is_fake(struct i2c_client *client)
+{
+ /*
+ * Differenciate between real LM96000 and Winbond WPCD377I. The latter
+ * emulate the former except that it has no hardware monitoring function
+ * so the readings are always 0.
+ */
+ int i;
+ u8 in_temp, fan;
+
+ for (i = 0; i < 8; i++) {
+ in_temp = i2c_smbus_read_byte_data(client, 0x20 + i);
+ fan = i2c_smbus_read_byte_data(client, 0x28 + i);
+ if (in_temp != 0x00 || fan != 0xff)
+ return 0;
+ }
+
+ return 1;
+}
+
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm85_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
const char *type_name;
+ int company, verstep;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
/* We need to be able to do byte I/O */
return -ENODEV;
}
- /* If auto-detecting, determine the chip type */
- if (kind < 0) {
- int company = lm85_read_value(client, LM85_REG_COMPANY);
- int verstep = lm85_read_value(client, LM85_REG_VERSTEP);
+ /* Determine the chip type */
+ company = lm85_read_value(client, LM85_REG_COMPANY);
+ verstep = lm85_read_value(client, LM85_REG_VERSTEP);
- dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
- "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
- address, company, verstep);
+ dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
+ "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ address, company, verstep);
- /* All supported chips have the version in common */
- if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) {
- dev_dbg(&adapter->dev, "Autodetection failed: "
- "unsupported version\n");
- return -ENODEV;
- }
- kind = any_chip;
-
- /* Now, refine the detection */
- if (company == LM85_COMPANY_NATIONAL) {
- switch (verstep) {
- case LM85_VERSTEP_LM85C:
- kind = lm85c;
- break;
- case LM85_VERSTEP_LM85B:
- kind = lm85b;
- break;
- }
- } else if (company == LM85_COMPANY_ANALOG_DEV) {
- switch (verstep) {
- case LM85_VERSTEP_ADM1027:
- kind = adm1027;
- break;
- case LM85_VERSTEP_ADT7463:
- case LM85_VERSTEP_ADT7463C:
- kind = adt7463;
- break;
- }
- } else if (company == LM85_COMPANY_SMSC) {
- switch (verstep) {
- case LM85_VERSTEP_EMC6D100_A0:
- case LM85_VERSTEP_EMC6D100_A1:
- /* Note: we can't tell a '100 from a '101 */
- kind = emc6d100;
- break;
- case LM85_VERSTEP_EMC6D102:
- kind = emc6d102;
- break;
+ /* All supported chips have the version in common */
+ if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC &&
+ (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) {
+ dev_dbg(&adapter->dev,
+ "Autodetection failed: unsupported version\n");
+ return -ENODEV;
+ }
+ type_name = "lm85";
+
+ /* Now, refine the detection */
+ if (company == LM85_COMPANY_NATIONAL) {
+ switch (verstep) {
+ case LM85_VERSTEP_LM85C:
+ type_name = "lm85c";
+ break;
+ case LM85_VERSTEP_LM85B:
+ type_name = "lm85b";
+ break;
+ case LM85_VERSTEP_LM96000_1:
+ case LM85_VERSTEP_LM96000_2:
+ /* Check for Winbond WPCD377I */
+ if (lm85_is_fake(client)) {
+ dev_dbg(&adapter->dev,
+ "Found Winbond WPCD377I, ignoring\n");
+ return -ENODEV;
}
- } else {
- dev_dbg(&adapter->dev, "Autodetection failed: "
- "unknown vendor\n");
- return -ENODEV;
+ break;
}
+ } else if (company == LM85_COMPANY_ANALOG_DEV) {
+ switch (verstep) {
+ case LM85_VERSTEP_ADM1027:
+ type_name = "adm1027";
+ break;
+ case LM85_VERSTEP_ADT7463:
+ case LM85_VERSTEP_ADT7463C:
+ type_name = "adt7463";
+ break;
+ case LM85_VERSTEP_ADT7468_1:
+ case LM85_VERSTEP_ADT7468_2:
+ type_name = "adt7468";
+ break;
+ }
+ } else if (company == LM85_COMPANY_SMSC) {
+ switch (verstep) {
+ case LM85_VERSTEP_EMC6D100_A0:
+ case LM85_VERSTEP_EMC6D100_A1:
+ /* Note: we can't tell a '100 from a '101 */
+ type_name = "emc6d100";
+ break;
+ case LM85_VERSTEP_EMC6D102:
+ type_name = "emc6d102";
+ break;
+ }
+ } else {
+ dev_dbg(&adapter->dev,
+ "Autodetection failed: unknown vendor\n");
+ return -ENODEV;
}
- switch (kind) {
- case lm85b:
- type_name = "lm85b";
- break;
- case lm85c:
- type_name = "lm85c";
- break;
- case adm1027:
- type_name = "adm1027";
- break;
- case adt7463:
- type_name = "adt7463";
- break;
- case emc6d100:
- type_name = "emc6d100";
- break;
- case emc6d102:
- type_name = "emc6d102";
- break;
- default:
- type_name = "lm85";
- }
strlcpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &lm85_group);
if (err)
- goto ERROR1;
+ goto err_kfree;
- /* The ADT7463 has an optional VRM 10 mode where pin 21 is used
+ /* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used
as a sixth digital VID input rather than an analog input. */
data->vid = lm85_read_value(client, LM85_REG_VID);
- if (!(data->type == adt7463 && (data->vid & 0x80)))
+ if (!((data->type == adt7463 || data->type == adt7468) &&
+ (data->vid & 0x80)))
if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in4)))
- goto ERROR3;
+ goto err_remove_files;
/* The EMC6D100 has 3 additional voltage inputs */
if (data->type == emc6d100)
if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in567)))
- goto ERROR3;
+ goto err_remove_files;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
- goto ERROR3;
+ goto err_remove_files;
}
return 0;
/* Error out and cleanup code */
- ERROR3:
+ err_remove_files:
sysfs_remove_group(&client->dev.kobj, &lm85_group);
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
- ERROR1:
+ err_kfree:
kfree(data);
return err;
}
* There are 2 additional resolution bits per channel and we
* have room for 4, so we shift them to the left.
*/
- if (data->type == adm1027 || data->type == adt7463) {
+ if (data->type == adm1027 || data->type == adt7463 ||
+ data->type == adt7468) {
int ext1 = lm85_read_value(client,
ADM1027_REG_EXTEND_ADC1);
int ext2 = lm85_read_value(client,
lm85_read_value(client, LM85_REG_FAN(i));
}
- if (!(data->type == adt7463 && (data->vid & 0x80))) {
+ if (!((data->type == adt7463 || data->type == adt7468) &&
+ (data->vid & 0x80))) {
data->in[4] = lm85_read_value(client,
LM85_REG_IN(4));
}
+ if (data->type == adt7468)
+ data->cfg5 = lm85_read_value(client, ADT7468_REG_CFG5);
+
for (i = 0; i <= 2; ++i) {
data->temp[i] =
lm85_read_value(client, LM85_REG_TEMP(i));
data->pwm[i] =
lm85_read_value(client, LM85_REG_PWM(i));
+
+ if (IS_ADT7468_OFF64(data))
+ data->temp[i] -= 64;
}
data->alarms = lm85_read_value(client, LM85_REG_ALARM1);
lm85_read_value(client, LM85_REG_FAN_MIN(i));
}
- if (!(data->type == adt7463 && (data->vid & 0x80))) {
+ if (!((data->type == adt7463 || data->type == adt7468) &&
+ (data->vid & 0x80))) {
data->in_min[4] = lm85_read_value(client,
LM85_REG_IN_MIN(4));
data->in_max[4] = lm85_read_value(client,
lm85_read_value(client, LM85_REG_AFAN_LIMIT(i));
data->zone[i].critical =
lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i));
+
+ if (IS_ADT7468_OFF64(data)) {
+ data->temp_min[i] -= 64;
+ data->temp_max[i] -= 64;
+ data->zone[i].limit -= 64;
+ data->zone[i].critical -= 64;
+ }
}
i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);