diff options
Diffstat (limited to 'drivers/hwmon/lm80.c')
| -rw-r--r-- | drivers/hwmon/lm80.c | 756 |
1 files changed, 408 insertions, 348 deletions
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 18a0e6c5fe8..4bcd9b88294 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -1,8 +1,8 @@ /* * lm80.c - From lm_sensors, Linux kernel modules for hardware - * monitoring + * monitoring * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - * and Philip Edelbrock <phil@netroedge.com> + * and Philip Edelbrock <phil@netroedge.com> * * Ported to Linux 2.6 by Tiago Sousa <mirage@kaotik.org> * @@ -60,254 +60,384 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, #define LM80_REG_FANDIV 0x05 #define LM80_REG_RES 0x06 +#define LM96080_REG_CONV_RATE 0x07 +#define LM96080_REG_MAN_ID 0x3e +#define LM96080_REG_DEV_ID 0x3f -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define IN_TO_REG(val) (SENSORS_LIMIT(((val)+5)/10,0,255)) -#define IN_FROM_REG(val) ((val)*10) +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + * Fixing this is just not worth it. + */ + +#define IN_TO_REG(val) (clamp_val(((val) + 5) / 10, 0, 255)) +#define IN_FROM_REG(val) ((val) * 10) static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) { if (rpm == 0) return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm*div / 2) / (rpm*div), 1, 254); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } -#define FAN_FROM_REG(val,div) ((val)==0?-1:\ - (val)==255?0:1350000/((div)*(val))) +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ + (val) == 255 ? 0 : 1350000/((div) * (val))) -static inline long TEMP_FROM_REG(u16 temp) -{ - long res; +#define TEMP_FROM_REG(reg) ((reg) * 125 / 32) +#define TEMP_TO_REG(temp) (DIV_ROUND_CLOSEST(clamp_val((temp), \ + -128000, 127000), 1000) << 8) - temp >>= 4; - if (temp < 0x0800) - res = 625 * (long) temp; - else - res = ((long) temp - 0x01000) * 625; +#define DIV_FROM_REG(val) (1 << (val)) - return res / 10; -} +enum temp_index { + t_input = 0, + t_hot_max, + t_hot_hyst, + t_os_max, + t_os_hyst, + t_num_temp +}; -#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000) +static const u8 temp_regs[t_num_temp] = { + [t_input] = LM80_REG_TEMP, + [t_hot_max] = LM80_REG_TEMP_HOT_MAX, + [t_hot_hyst] = LM80_REG_TEMP_HOT_HYST, + [t_os_max] = LM80_REG_TEMP_OS_MAX, + [t_os_hyst] = LM80_REG_TEMP_OS_HYST, +}; -#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val)<0?\ - ((val)-500)/1000:((val)+500)/1000,0,255) +enum in_index { + i_input = 0, + i_max, + i_min, + i_num_in +}; -#define DIV_FROM_REG(val) (1 << (val)) +enum fan_index { + f_input, + f_min, + f_num_fan +}; /* * Client data (each client gets its own) */ struct lm80_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; + char error; /* !=0 if error occurred during last update */ char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u8 in[7]; /* Register value */ - u8 in_max[7]; /* Register value */ - u8 in_min[7]; /* Register value */ - u8 fan[2]; /* Register value */ - u8 fan_min[2]; /* Register value */ + u8 in[i_num_in][7]; /* Register value, 1st index is enum in_index */ + u8 fan[f_num_fan][2]; /* Register value, 1st index enum fan_index */ u8 fan_div[2]; /* Register encoding, shifted right */ - u16 temp; /* Register values, shifted right */ - u8 temp_hot_max; /* Register value */ - u8 temp_hot_hyst; /* Register value */ - u8 temp_os_max; /* Register value */ - u8 temp_os_hyst; /* Register value */ + s16 temp[t_num_temp]; /* Register values, normalized to 16 bit */ u16 alarms; /* Register encoding, combined */ }; -/* - * Functions declaration - */ +static int lm80_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} -static int lm80_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm80_init_client(struct i2c_client *client); -static int lm80_remove(struct i2c_client *client); -static struct lm80_data *lm80_update_device(struct device *dev); -static int lm80_read_value(struct i2c_client *client, u8 reg); -static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value); +static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} -/* - * Driver data (common to all clients) - */ +/* Called when we have found a new LM80 and after read errors */ +static void lm80_init_client(struct i2c_client *client) +{ + /* + * Reset all except Watchdog values and last conversion values + * This sets fan-divs to 2, among others. This makes most other + * initializations unnecessary + */ + lm80_write_value(client, LM80_REG_CONFIG, 0x80); + /* Set 11-bit temperature resolution */ + lm80_write_value(client, LM80_REG_RES, 0x08); -static const struct i2c_device_id lm80_id[] = { - { "lm80", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm80_id); + /* Start monitoring */ + lm80_write_value(client, LM80_REG_CONFIG, 0x01); +} -static struct i2c_driver lm80_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm80", - }, - .probe = lm80_probe, - .remove = lm80_remove, - .id_table = lm80_id, - .detect = lm80_detect, - .address_list = normal_i2c, -}; +static struct lm80_data *lm80_update_device(struct device *dev) +{ + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + int rv; + int prev_rv; + struct lm80_data *ret = data; + + mutex_lock(&data->update_lock); + + if (data->error) + lm80_init_client(client); + + if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { + dev_dbg(dev, "Starting lm80 update\n"); + for (i = 0; i <= 6; i++) { + rv = lm80_read_value(client, LM80_REG_IN(i)); + if (rv < 0) + goto abort; + data->in[i_input][i] = rv; + + rv = lm80_read_value(client, LM80_REG_IN_MIN(i)); + if (rv < 0) + goto abort; + data->in[i_min][i] = rv; + + rv = lm80_read_value(client, LM80_REG_IN_MAX(i)); + if (rv < 0) + goto abort; + data->in[i_max][i] = rv; + } + + rv = lm80_read_value(client, LM80_REG_FAN1); + if (rv < 0) + goto abort; + data->fan[f_input][0] = rv; + + rv = lm80_read_value(client, LM80_REG_FAN_MIN(1)); + if (rv < 0) + goto abort; + data->fan[f_min][0] = rv; + + rv = lm80_read_value(client, LM80_REG_FAN2); + if (rv < 0) + goto abort; + data->fan[f_input][1] = rv; + + rv = lm80_read_value(client, LM80_REG_FAN_MIN(2)); + if (rv < 0) + goto abort; + data->fan[f_min][1] = rv; + + prev_rv = rv = lm80_read_value(client, LM80_REG_TEMP); + if (rv < 0) + goto abort; + rv = lm80_read_value(client, LM80_REG_RES); + if (rv < 0) + goto abort; + data->temp[t_input] = (prev_rv << 8) | (rv & 0xf0); + + for (i = t_input + 1; i < t_num_temp; i++) { + rv = lm80_read_value(client, temp_regs[i]); + if (rv < 0) + goto abort; + data->temp[i] = rv << 8; + } + + rv = lm80_read_value(client, LM80_REG_FANDIV); + if (rv < 0) + goto abort; + data->fan_div[0] = (rv >> 2) & 0x03; + data->fan_div[1] = (rv >> 4) & 0x03; + + prev_rv = rv = lm80_read_value(client, LM80_REG_ALARM1); + if (rv < 0) + goto abort; + rv = lm80_read_value(client, LM80_REG_ALARM2); + if (rv < 0) + goto abort; + data->alarms = prev_rv + (rv << 8); + + data->last_updated = jiffies; + data->valid = 1; + data->error = 0; + } + goto done; + +abort: + ret = ERR_PTR(rv); + data->valid = 0; + data->error = 1; + +done: + mutex_unlock(&data->update_lock); + + return ret; +} /* * Sysfs stuff */ -#define show_in(suffix, value) \ -static ssize_t show_in_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - int nr = to_sensor_dev_attr(attr)->index; \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", IN_FROM_REG(data->value[nr])); \ +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm80_data *data = lm80_update_device(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr][index])); } -show_in(min, in_min) -show_in(max, in_max) -show_in(input, in) - -#define set_in(suffix, value, reg) \ -static ssize_t set_in_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - int nr = to_sensor_dev_attr(attr)->index; \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock);\ - data->value[nr] = IN_TO_REG(val); \ - lm80_write_value(client, reg(nr), data->value[nr]); \ - mutex_unlock(&data->update_lock);\ - return count; \ + +static ssize_t set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + long val; + u8 reg; + int err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + reg = nr == i_min ? LM80_REG_IN_MIN(index) : LM80_REG_IN_MAX(index); + + mutex_lock(&data->update_lock); + data->in[nr][index] = IN_TO_REG(val); + lm80_write_value(client, reg, data->in[nr][index]); + mutex_unlock(&data->update_lock); + return count; } -set_in(min, in_min, LM80_REG_IN_MIN) -set_in(max, in_max, LM80_REG_IN_MAX) - -#define show_fan(suffix, value) \ -static ssize_t show_fan_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - int nr = to_sensor_dev_attr(attr)->index; \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[nr], \ - DIV_FROM_REG(data->fan_div[nr]))); \ + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr][index], + DIV_FROM_REG(data->fan_div[index]))); } -show_fan(min, fan_min) -show_fan(input, fan) static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) { int nr = to_sensor_dev_attr(attr)->index; struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - long val = simple_strtoul(buf, NULL, 10); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; mutex_lock(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]); + data->fan[nr][index] = FAN_TO_REG(val, + DIV_FROM_REG(data->fan_div[index])); + lm80_write_value(client, LM80_REG_FAN_MIN(index + 1), + data->fan[nr][index]); mutex_unlock(&data->update_lock); return count; } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - unsigned long min, val = simple_strtoul(buf, NULL, 10); + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long min, val; u8 reg; + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; /* Save fan_min */ mutex_lock(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], + min = FAN_FROM_REG(data->fan[f_min][nr], DIV_FROM_REG(data->fan_div[nr])); switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } - reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1)))) - | (data->fan_div[nr] << (2 * (nr + 1))); + reg = (lm80_read_value(client, LM80_REG_FANDIV) & + ~(3 << (2 * (nr + 1)))) | (data->fan_div[nr] << (2 * (nr + 1))); lm80_write_value(client, LM80_REG_FANDIV, reg); /* Restore fan_min */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]); + data->fan[f_min][nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), + data->fan[f_min][nr]); mutex_unlock(&data->update_lock); return count; } -static ssize_t show_temp_input1(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm80_data *data = lm80_update_device(dev); - return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp)); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -#define show_temp(suffix, value) \ -static ssize_t show_temp_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \ -} -show_temp(hot_max, temp_hot_max); -show_temp(hot_hyst, temp_hot_hyst); -show_temp(os_max, temp_os_max); -show_temp(os_hyst, temp_os_hyst); - -#define set_temp(suffix, value, reg) \ -static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtoul(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->value = TEMP_LIMIT_TO_REG(val); \ - lm80_write_value(client, reg, data->value); \ - mutex_unlock(&data->update_lock); \ - return count; \ +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; + long val; + int err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + data->temp[nr] = TEMP_TO_REG(val); + lm80_write_value(client, temp_regs[nr], data->temp[nr] >> 8); + mutex_unlock(&data->update_lock); + return count; } -set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX); -set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST); -set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX); -set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST); static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%u\n", data->alarms); } @@ -316,63 +446,65 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, { int bitnr = to_sensor_dev_attr(attr)->index; struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 0); -static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 1); -static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 2); -static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 3); -static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 4); -static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 5); -static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 6); -static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 0); -static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 1); -static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 2); -static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 3); -static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 4); -static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 5); -static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 6); -static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL, 0); -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in_input, NULL, 1); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in_input, NULL, 2); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in_input, NULL, 3); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in_input, NULL, 4); -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in_input, NULL, 5); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in_input, NULL, 6); -static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, - show_fan_min, set_fan_min, 0); -static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, - show_fan_min, set_fan_min, 1); -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 1); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 2); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 3); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 4); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 5); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 6); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 0); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 2); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 3); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 4); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 5); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 6); +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, i_input, 0); +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, i_input, 1); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, i_input, 2); +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, i_input, 3); +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, i_input, 4); +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, i_input, 5); +static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in, NULL, i_input, 6); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IWUSR | S_IRUGO, + show_fan, set_fan_min, f_min, 0); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IWUSR | S_IRUGO, + show_fan, set_fan_min, f_min, 1); +static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, f_input, 0); +static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, f_input, 1); static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div, set_fan_div, 0); static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div, set_fan_div, 1); -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max, - set_temp_hot_max); -static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst, - set_temp_hot_hyst); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max, - set_temp_os_max); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst, - set_temp_os_hyst); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_hot_max); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_hot_hyst); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_os_max); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_os_hyst); static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); @@ -390,7 +522,7 @@ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 13); * Real code */ -static struct attribute *lm80_attributes[] = { +static struct attribute *lm80_attrs[] = { &sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr, &sensor_dev_attr_in2_min.dev_attr.attr, @@ -418,11 +550,11 @@ static struct attribute *lm80_attributes[] = { &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, - &dev_attr_temp1_input.attr, - &dev_attr_temp1_max.attr, - &dev_attr_temp1_max_hyst.attr, - &dev_attr_temp1_crit.attr, - &dev_attr_temp1_crit_hyst.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, &dev_attr_alarms.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, @@ -437,32 +569,50 @@ static struct attribute *lm80_attributes[] = { &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group lm80_group = { - .attrs = lm80_attributes, -}; +ATTRIBUTE_GROUPS(lm80); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int i, cur; + int i, cur, man_id, dev_id; + const char *name = NULL; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Now, we do the remaining detection. It is lousy. */ - if (lm80_read_value(client, LM80_REG_ALARM2) & 0xc0) + /* First check for unused bits, common to both chip types */ + if ((lm80_read_value(client, LM80_REG_ALARM2) & 0xc0) + || (lm80_read_value(client, LM80_REG_CONFIG) & 0x80)) return -ENODEV; - for (i = 0x2a; i <= 0x3d; i++) { - cur = i2c_smbus_read_byte_data(client, i); - if ((i2c_smbus_read_byte_data(client, i + 0x40) != cur) - || (i2c_smbus_read_byte_data(client, i + 0x80) != cur) - || (i2c_smbus_read_byte_data(client, i + 0xc0) != cur)) - return -ENODEV; + + /* + * The LM96080 has manufacturer and stepping/die rev registers so we + * can just check that. The LM80 does not have such registers so we + * have to use a more expensive trick. + */ + man_id = lm80_read_value(client, LM96080_REG_MAN_ID); + dev_id = lm80_read_value(client, LM96080_REG_DEV_ID); + if (man_id == 0x01 && dev_id == 0x08) { + /* Check more unused bits for confirmation */ + if (lm80_read_value(client, LM96080_REG_CONV_RATE) & 0xfe) + return -ENODEV; + + name = "lm96080"; + } else { + /* Check 6-bit addressing */ + for (i = 0x2a; i <= 0x3d; i++) { + cur = i2c_smbus_read_byte_data(client, i); + if ((i2c_smbus_read_byte_data(client, i + 0x40) != cur) + || (i2c_smbus_read_byte_data(client, i + 0x80) != cur) + || (i2c_smbus_read_byte_data(client, i + 0xc0) != cur)) + return -ENODEV; + } + + name = "lm80"; } - strlcpy(info->type, "lm80", I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -470,145 +620,55 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) static int lm80_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct device *hwmon_dev; struct lm80_data *data; - int err; - data = kzalloc(sizeof(struct lm80_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM80 chip */ lm80_init_client(client); /* A few vars need to be filled upon startup */ - data->fan_min[0] = lm80_read_value(client, LM80_REG_FAN_MIN(1)); - data->fan_min[1] = lm80_read_value(client, LM80_REG_FAN_MIN(2)); + data->fan[f_min][0] = lm80_read_value(client, LM80_REG_FAN_MIN(1)); + data->fan[f_min][1] = lm80_read_value(client, LM80_REG_FAN_MIN(2)); - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &lm80_group))) - goto error_free; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, lm80_groups); - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error_remove; - } - - return 0; - -error_remove: - sysfs_remove_group(&client->dev.kobj, &lm80_group); -error_free: - kfree(data); -exit: - return err; + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int lm80_remove(struct i2c_client *client) -{ - struct lm80_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm80_group); - - kfree(data); - return 0; -} - -static int lm80_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new LM80. */ -static void lm80_init_client(struct i2c_client *client) -{ - /* Reset all except Watchdog values and last conversion values - This sets fan-divs to 2, among others. This makes most other - initializations unnecessary */ - lm80_write_value(client, LM80_REG_CONFIG, 0x80); - /* Set 11-bit temperature resolution */ - lm80_write_value(client, LM80_REG_RES, 0x08); - - /* Start monitoring */ - lm80_write_value(client, LM80_REG_CONFIG, 0x01); -} - -static struct lm80_data *lm80_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - dev_dbg(&client->dev, "Starting lm80 update\n"); - for (i = 0; i <= 6; i++) { - data->in[i] = - lm80_read_value(client, LM80_REG_IN(i)); - data->in_min[i] = - lm80_read_value(client, LM80_REG_IN_MIN(i)); - data->in_max[i] = - lm80_read_value(client, LM80_REG_IN_MAX(i)); - } - data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); - data->fan_min[0] = - lm80_read_value(client, LM80_REG_FAN_MIN(1)); - data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); - data->fan_min[1] = - lm80_read_value(client, LM80_REG_FAN_MIN(2)); - - data->temp = - (lm80_read_value(client, LM80_REG_TEMP) << 8) | - (lm80_read_value(client, LM80_REG_RES) & 0xf0); - data->temp_os_max = - lm80_read_value(client, LM80_REG_TEMP_OS_MAX); - data->temp_os_hyst = - lm80_read_value(client, LM80_REG_TEMP_OS_HYST); - data->temp_hot_max = - lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); - data->temp_hot_hyst = - lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); - - i = lm80_read_value(client, LM80_REG_FANDIV); - data->fan_div[0] = (i >> 2) & 0x03; - data->fan_div[1] = (i >> 4) & 0x03; - data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + - (lm80_read_value(client, LM80_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); +/* + * Driver data (common to all clients) + */ - return data; -} +static const struct i2c_device_id lm80_id[] = { + { "lm80", 0 }, + { "lm96080", 1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm80_id); -static int __init sensors_lm80_init(void) -{ - return i2c_add_driver(&lm80_driver); -} +static struct i2c_driver lm80_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm80", + }, + .probe = lm80_probe, + .id_table = lm80_id, + .detect = lm80_detect, + .address_list = normal_i2c, +}; -static void __exit sensors_lm80_exit(void) -{ - i2c_del_driver(&lm80_driver); -} +module_i2c_driver(lm80_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " "Philip Edelbrock <phil@netroedge.com>"); MODULE_DESCRIPTION("LM80 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm80_init); -module_exit(sensors_lm80_exit); |
