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--2009 Jean Delvare <khali@linux-fr.org>
Chip details at <http://www.national.com/ds/LM/LM85.pdf>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102);
+enum chips {
+ any_chip, 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
{
int i;
- if (range >= lm85_range_map[15])
- return 15;
-
/* Find the closest match */
- for (i = 14; i >= 0; --i) {
- if (range >= lm85_range_map[i]) {
- if ((lm85_range_map[i + 1] - range) <
- (range - lm85_range_map[i]))
- return i + 1;
- return i;
- }
+ for (i = 0; i < 15; ++i) {
+ if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2)
+ break;
}
- return 0;
+ return i;
}
#define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
/* These are the PWM frequency encodings */
static const int lm85_freq_map[8] = { /* 1 Hz */
- 10, 15, 23, 30, 38, 47, 62, 94
+ 10, 15, 23, 30, 38, 47, 61, 94
+};
+static const int adm1027_freq_map[8] = { /* 1 Hz */
+ 11, 15, 22, 29, 35, 44, 59, 88
};
-static int FREQ_TO_REG(int freq)
+static int FREQ_TO_REG(const int *map, int freq)
{
int i;
/* Find the closest match */
for (i = 0; i < 7; ++i)
- if (freq <= (lm85_freq_map[i] + lm85_freq_map[i + 1]) / 2)
+ if (freq <= (map[i] + map[i + 1]) / 2)
break;
return i;
}
-#define FREQ_FROM_REG(val) lm85_freq_map[(val) & 0x07]
+
+static int FREQ_FROM_REG(const int *map, u8 reg)
+{
+ return map[reg & 0x07];
+}
/* Since we can't use strings, I'm abusing these numbers
* to stand in for the following meanings:
/* For each registered chip, we need to keep some data in memory.
The structure is dynamically allocated. */
struct lm85_data {
- struct i2c_client client;
struct device *hwmon_dev;
+ const int *freq_map;
enum chips type;
struct mutex update_lock;
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 int lm85_attach_adapter(struct i2c_adapter *adapter);
-static int lm85_detect(struct i2c_adapter *adapter, int address,
- int kind);
-static int lm85_detach_client(struct i2c_client *client);
+static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info);
+static int lm85_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int lm85_remove(struct i2c_client *client);
static int lm85_read_value(struct i2c_client *client, u8 reg);
static void lm85_write_value(struct i2c_client *client, u8 reg, int value);
static struct lm85_data *lm85_update_device(struct device *dev);
+static const struct i2c_device_id lm85_id[] = {
+ { "adm1027", adm1027 },
+ { "adt7463", adt7463 },
+ { "adt7468", adt7468 },
+ { "lm85", any_chip },
+ { "lm85b", lm85b },
+ { "lm85c", lm85c },
+ { "emc6d100", emc6d100 },
+ { "emc6d101", emc6d100 },
+ { "emc6d102", emc6d102 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm85_id);
+
static struct i2c_driver lm85_driver = {
+ .class = I2C_CLASS_HWMON,
.driver = {
.name = "lm85",
},
- .attach_adapter = lm85_attach_adapter,
- .detach_client = lm85_detach_client,
+ .probe = lm85_probe,
+ .remove = lm85_remove,
+ .id_table = lm85_id,
+ .detect = lm85_detect,
+ .address_list = normal_i2c,
};
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 {
{
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->pwm_freq[nr]));
+ return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map,
+ data->pwm_freq[nr]));
}
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(val);
+ 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]);
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]);
temp_auto(2);
temp_auto(3);
-static int lm85_attach_adapter(struct i2c_adapter *adapter)
-{
- if (!(adapter->class & I2C_CLASS_HWMON))
- return 0;
- return i2c_probe(adapter, &addr_data, lm85_detect);
-}
-
static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
dev_warn(&client->dev, "Device is not ready\n");
}
-static int lm85_detect(struct i2c_adapter *adapter, int address,
- int kind)
+static int lm85_is_fake(struct i2c_client *client)
{
- struct i2c_client *client;
- struct lm85_data *data;
- int err = 0;
+ /*
+ * 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, 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 */
- goto ERROR0;
+ return -ENODEV;
}
- if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) {
- err = -ENOMEM;
- goto ERROR0;
+ /* 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);
+
+ /* 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";
- client = &data->client;
- i2c_set_clientdata(client, data);
- client->addr = address;
- client->adapter = adapter;
- client->driver = &lm85_driver;
-
- /* 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);
-
- 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");
- goto ERROR1;
- }
- 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;
+ /* 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");
- goto ERROR1;
+ 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;
}
+ strlcpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int lm85_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lm85_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->type = id->driver_data;
+ mutex_init(&data->update_lock);
+
/* Fill in the chip specific driver values */
- switch (kind) {
- case lm85b:
- type_name = "lm85b";
- break;
- case lm85c:
- type_name = "lm85c";
- break;
+ switch (data->type) {
case adm1027:
- type_name = "adm1027";
- break;
case adt7463:
- type_name = "adt7463";
- break;
case emc6d100:
- type_name = "emc6d100";
- break;
case emc6d102:
- type_name = "emc6d102";
+ data->freq_map = adm1027_freq_map;
break;
default:
- type_name = "lm85";
+ data->freq_map = lm85_freq_map;
}
- strlcpy(client->name, type_name, I2C_NAME_SIZE);
-
- /* Fill in the remaining client fields */
- data->type = kind;
- mutex_init(&data->update_lock);
-
- /* Tell the I2C layer a new client has arrived */
- err = i2c_attach_client(client);
- if (err)
- goto ERROR1;
/* Set the VRM version */
data->vrm = vid_which_vrm();
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &lm85_group);
if (err)
- goto ERROR2;
+ 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 (!(kind == 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 (kind == emc6d100)
+ 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 (kind == emc6d100)
+ if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
- ERROR2:
- i2c_detach_client(client);
- ERROR1:
+ err_kfree:
kfree(data);
- ERROR0:
return err;
}
-static int lm85_detach_client(struct i2c_client *client)
+static int lm85_remove(struct i2c_client *client)
{
struct lm85_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
- i2c_detach_client(client);
kfree(data);
return 0;
}
* 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);