diff options
Diffstat (limited to 'drivers/hwmon/adt7470.c')
-rw-r--r-- | drivers/hwmon/adt7470.c | 227 |
1 files changed, 200 insertions, 27 deletions
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 1311a595147..633e1a1e9d7 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -28,6 +28,7 @@ #include <linux/mutex.h> #include <linux/delay.h> #include <linux/log2.h> +#include <linux/kthread.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; @@ -74,6 +75,7 @@ I2C_CLIENT_INSMOD_1(adt7470); #define ADT7470_REG_PWM12_CFG 0x68 #define ADT7470_PWM2_AUTO_MASK 0x40 #define ADT7470_PWM1_AUTO_MASK 0x80 +#define ADT7470_PWM_AUTO_MASK 0xC0 #define ADT7470_REG_PWM34_CFG 0x69 #define ADT7470_PWM3_AUTO_MASK 0x40 #define ADT7470_PWM4_AUTO_MASK 0x80 @@ -128,8 +130,11 @@ I2C_CLIENT_INSMOD_1(adt7470); /* How often do we reread sensor limit values? (In jiffies) */ #define LIMIT_REFRESH_INTERVAL (60 * HZ) -/* sleep 1s while gathering temperature data */ -#define TEMP_COLLECTION_TIME 1000 +/* Wait at least 200ms per sensor for 10 sensors */ +#define TEMP_COLLECTION_TIME 2000 + +/* auto update thing won't fire more than every 2s */ +#define AUTO_UPDATE_INTERVAL 2000 /* datasheet says to divide this number by the fan reading to get fan rpm */ #define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) @@ -137,8 +142,6 @@ I2C_CLIENT_INSMOD_1(adt7470); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) -#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) - struct adt7470_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -148,6 +151,9 @@ struct adt7470_data { unsigned long sensors_last_updated; /* In jiffies */ unsigned long limits_last_updated; /* In jiffies */ + int num_temp_sensors; /* -1 = probe */ + int temperatures_probed; + s8 temp[ADT7470_TEMP_COUNT]; s8 temp_min[ADT7470_TEMP_COUNT]; s8 temp_max[ADT7470_TEMP_COUNT]; @@ -163,6 +169,10 @@ struct adt7470_data { u8 pwm_min[ADT7470_PWM_COUNT]; s8 pwm_tmin[ADT7470_PWM_COUNT]; u8 pwm_auto_temp[ADT7470_PWM_COUNT]; + + struct task_struct *auto_update; + struct completion auto_update_stop; + unsigned int auto_update_interval; }; static int adt7470_probe(struct i2c_client *client, @@ -220,40 +230,126 @@ static void adt7470_init_client(struct i2c_client *client) } } -static struct adt7470_data *adt7470_update_device(struct device *dev) +/* Probe for temperature sensors. Assumes lock is held */ +static int adt7470_read_temperatures(struct i2c_client *client, + struct adt7470_data *data) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7470_data *data = i2c_get_clientdata(client); - unsigned long local_jiffies = jiffies; - u8 cfg; + unsigned long res; int i; + u8 cfg, pwm[4], pwm_cfg[2]; - mutex_lock(&data->lock); - if (time_before(local_jiffies, data->sensors_last_updated + - SENSOR_REFRESH_INTERVAL) - && data->sensors_valid) - goto no_sensor_update; + /* save pwm[1-4] config register */ + pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0)); + pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2)); + + /* set manual pwm to whatever it is set to now */ + for (i = 0; i < ADT7470_FAN_COUNT; i++) + pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i)); + + /* put pwm in manual mode */ + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), + pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK)); + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), + pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK)); + + /* write pwm control to whatever it was */ + for (i = 0; i < ADT7470_FAN_COUNT; i++) + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]); /* start reading temperature sensors */ cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); cfg |= 0x80; i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); - /* - * Delay is 200ms * number of tmp05 sensors. Too bad - * there's no way to figure out how many are connected. - * For now, assume 1s will work. - */ - msleep(TEMP_COLLECTION_TIME); + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? + data->num_temp_sensors * 200 : + TEMP_COLLECTION_TIME)); /* done reading temperature sensors */ cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); cfg &= ~0x80; i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); - for (i = 0; i < ADT7470_TEMP_COUNT; i++) + /* restore pwm[1-4] config registers */ + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + + if (res) { + printk(KERN_ERR "ha ha, interrupted"); + return -EAGAIN; + } + + /* Only count fans if we have to */ + if (data->num_temp_sensors >= 0) + return 0; + + for (i = 0; i < ADT7470_TEMP_COUNT; i++) { data->temp[i] = i2c_smbus_read_byte_data(client, ADT7470_TEMP_REG(i)); + if (data->temp[i]) + data->num_temp_sensors = i + 1; + } + data->temperatures_probed = 1; + return 0; +} + +static int adt7470_update_thread(void *p) +{ + struct i2c_client *client = p; + struct adt7470_data *data = i2c_get_clientdata(client); + + while (!kthread_should_stop()) { + mutex_lock(&data->lock); + adt7470_read_temperatures(client, data); + mutex_unlock(&data->lock); + if (kthread_should_stop()) + break; + msleep_interruptible(data->auto_update_interval); + } + + complete_all(&data->auto_update_stop); + return 0; +} + +static struct adt7470_data *adt7470_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + u8 cfg; + int i; + int need_sensors = 1; + int need_limits = 1; + + /* + * Figure out if we need to update the shadow registers. + * Lockless means that we may occasionally report out of + * date data. + */ + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) && + data->sensors_valid) + need_sensors = 0; + + if (time_before(local_jiffies, data->limits_last_updated + + LIMIT_REFRESH_INTERVAL) && + data->limits_valid) + need_limits = 0; + + if (!need_sensors && !need_limits) + return data; + + mutex_lock(&data->lock); + if (!need_sensors) + goto no_sensor_update; + + if (!data->temperatures_probed) + adt7470_read_temperatures(client, data); + else + for (i = 0; i < ADT7470_TEMP_COUNT; i++) + data->temp[i] = i2c_smbus_read_byte_data(client, + ADT7470_TEMP_REG(i)); for (i = 0; i < ADT7470_FAN_COUNT; i++) data->fan[i] = adt7470_read_word_data(client, @@ -302,9 +398,7 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) data->sensors_valid = 1; no_sensor_update: - if (time_before(local_jiffies, data->limits_last_updated + - LIMIT_REFRESH_INTERVAL) - && data->limits_valid) + if (!need_limits) goto out; for (i = 0; i < ADT7470_TEMP_COUNT; i++) { @@ -338,6 +432,66 @@ out: return data; } +static ssize_t show_auto_update_interval(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->auto_update_interval); +} + +static ssize_t set_auto_update_interval(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_num_temp_sensors(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->num_temp_sensors); +} + +static ssize_t set_num_temp_sensors(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, -1, 10); + + mutex_lock(&data->lock); + data->num_temp_sensors = temp; + if (temp < 0) + data->temperatures_probed = 0; + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_temp_min(struct device *dev, struct device_attribute *devattr, char *buf) @@ -360,7 +514,7 @@ static ssize_t set_temp_min(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -394,7 +548,7 @@ static ssize_t set_temp_max(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -671,7 +825,7 @@ static ssize_t set_pwm_tmin(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -804,6 +958,10 @@ static ssize_t show_alarm(struct device *dev, } static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL); +static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors, + set_num_temp_sensors); +static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO, + show_auto_update_interval, set_auto_update_interval); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max, 0); @@ -976,6 +1134,8 @@ static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, static struct attribute *adt7470_attr[] = { &dev_attr_alarm_mask.attr, + &dev_attr_num_temp_sensors.attr, + &dev_attr_auto_update_interval.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -1108,6 +1268,9 @@ static int adt7470_probe(struct i2c_client *client, goto exit; } + data->num_temp_sensors = -1; + data->auto_update_interval = AUTO_UPDATE_INTERVAL; + i2c_set_clientdata(client, data); mutex_init(&data->lock); @@ -1127,8 +1290,16 @@ static int adt7470_probe(struct i2c_client *client, goto exit_remove; } + init_completion(&data->auto_update_stop); + data->auto_update = kthread_run(adt7470_update_thread, client, + dev_name(data->hwmon_dev)); + if (IS_ERR(data->auto_update)) + goto exit_unregister; + return 0; +exit_unregister: + hwmon_device_unregister(data->hwmon_dev); exit_remove: sysfs_remove_group(&client->dev.kobj, &data->attrs); exit_free: @@ -1141,6 +1312,8 @@ static int adt7470_remove(struct i2c_client *client) { struct adt7470_data *data = i2c_get_clientdata(client); + kthread_stop(data->auto_update); + wait_for_completion(&data->auto_update_stop); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &data->attrs); kfree(data); |