Merge branch 'for-2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[safe/jmp/linux-2.6] / drivers / hwmon / adt7470.c
index ab8d5eb..9e77571 100644 (file)
 #include <linux/mutex.h>
 #include <linux/delay.h>
 #include <linux/log2.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
 
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7470);
-
 /* ADT7470 registers */
 #define ADT7470_REG_BASE_ADDR                  0x20
 #define ADT7470_REG_TEMP_BASE_ADDR             0x20
@@ -132,6 +131,9 @@ I2C_CLIENT_INSMOD_1(adt7470);
 /* 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))
 #define FAN_RPM_TO_PERIOD      FAN_PERIOD_TO_RPM
@@ -148,6 +150,7 @@ struct adt7470_data {
        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];
@@ -164,16 +167,20 @@ 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,
                         const struct i2c_device_id *id);
-static int adt7470_detect(struct i2c_client *client, int kind,
+static int adt7470_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int adt7470_remove(struct i2c_client *client);
 
 static const struct i2c_device_id adt7470_id[] = {
-       { "adt7470", adt7470 },
+       { "adt7470", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adt7470_id);
@@ -187,7 +194,7 @@ static struct i2c_driver adt7470_driver = {
        .remove         = adt7470_remove,
        .id_table       = adt7470_id,
        .detect         = adt7470_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -221,19 +228,13 @@ 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, pwm[4], pwm_cfg[2];
+       unsigned long res;
        int i;
-
-       mutex_lock(&data->lock);
-       if (time_before(local_jiffies, data->sensors_last_updated +
-               SENSOR_REFRESH_INTERVAL)
-               && data->sensors_valid)
-               goto no_sensor_update;
+       u8 cfg, pwm[4], pwm_cfg[2];
 
        /* save pwm[1-4] config register */
        pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0));
@@ -259,9 +260,9 @@ static struct adt7470_data *adt7470_update_device(struct device *dev)
        i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
 
        /* Delay is 200ms * number of temp sensors. */
-       msleep((data->num_temp_sensors >= 0 ?
-               data->num_temp_sensors * 200 :
-               TEMP_COLLECTION_TIME));
+       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);
@@ -272,15 +273,81 @@ static struct adt7470_data *adt7470_update_device(struct device *dev)
        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]);
 
-       for (i = 0; i < ADT7470_TEMP_COUNT; i++)
+       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;
 
-       /* Figure out the number of temp sensors */
-       if (data->num_temp_sensors < 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++)
-                       if (data->temp[i])
-                               data->num_temp_sensors = i + 1;
+                       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,
@@ -329,9 +396,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++) {
@@ -365,6 +430,35 @@ 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)
@@ -389,6 +483,8 @@ static ssize_t set_num_temp_sensors(struct device *dev,
 
        mutex_lock(&data->lock);
        data->num_temp_sensors = temp;
+       if (temp < 0)
+               data->temperatures_probed = 0;
        mutex_unlock(&data->lock);
 
        return count;
@@ -862,6 +958,8 @@ 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);
@@ -1035,6 +1133,7 @@ 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,
@@ -1124,31 +1223,26 @@ static struct attribute *adt7470_attr[] =
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7470_detect(struct i2c_client *client, int kind,
+static int adt7470_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int vendor, device, revision;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind <= 0) {
-               int vendor, device, revision;
-
-               vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
-               if (vendor != ADT7470_VENDOR)
-                       return -ENODEV;
+       vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
+       if (vendor != ADT7470_VENDOR)
+               return -ENODEV;
 
-               device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
-               if (device != ADT7470_DEVICE)
-                       return -ENODEV;
+       device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
+       if (device != ADT7470_DEVICE)
+               return -ENODEV;
 
-               revision = i2c_smbus_read_byte_data(client,
-                                                   ADT7470_REG_REVISION);
-               if (revision != ADT7470_REVISION)
-                       return -ENODEV;
-       } else
-               dev_dbg(&adapter->dev, "detection forced\n");
+       revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION);
+       if (revision != ADT7470_REVISION)
+               return -ENODEV;
 
        strlcpy(info->type, "adt7470", I2C_NAME_SIZE);
 
@@ -1168,6 +1262,7 @@ static int adt7470_probe(struct i2c_client *client,
        }
 
        data->num_temp_sensors = -1;
+       data->auto_update_interval = AUTO_UPDATE_INTERVAL;
 
        i2c_set_clientdata(client, data);
        mutex_init(&data->lock);
@@ -1188,8 +1283,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:
@@ -1202,6 +1305,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);