Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jlbec...
[pandora-kernel.git] / drivers / hwmon / lm90.c
index 9df08e1..812781c 100644 (file)
@@ -88,8 +88,8 @@
  * Addresses to scan
  * Address is fully defined internally and cannot be changed except for
  * MAX6659, MAX6680 and MAX6681.
- * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657
- * and MAX6658 have address 0x4c.
+ * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657,
+ * MAX6658 and W83L771 have address 0x4c.
  * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d.
  * MAX6647 has address 0x4e.
  * MAX6659 can have address 0x4c, 0x4d or 0x4e.
@@ -151,6 +151,9 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define MAX6659_REG_R_LOCAL_EMERG      0x17
 #define MAX6659_REG_W_LOCAL_EMERG      0x17
 
+#define LM90_DEF_CONVRATE_RVAL 6       /* Def conversion rate register value */
+#define LM90_MAX_CONVRATE_MS   16000   /* Maximum conversion rate in ms */
+
 /*
  * Device flags
  */
@@ -162,6 +165,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define LM90_HAVE_EMERGENCY    (1 << 4) /* 3rd upper (emergency) limit */
 #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm           */
 #define LM90_HAVE_TEMP3                (1 << 6) /* 3rd temperature sensor      */
+#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert                */
 
 /*
  * Driver data (common to all clients)
@@ -196,53 +200,67 @@ struct lm90_params {
        u32 flags;              /* Capabilities */
        u16 alert_alarms;       /* Which alarm bits trigger ALERT# */
                                /* Upper 8 bits for max6695/96 */
+       u8 max_convrate;        /* Maximum conversion rate register value */
 };
 
 static const struct lm90_params lm90_params[] = {
        [adm1032] = {
-               .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
+               .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
+                 | LM90_HAVE_BROKEN_ALERT,
                .alert_alarms = 0x7c,
+               .max_convrate = 10,
        },
        [adt7461] = {
-               .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
+               .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
+                 | LM90_HAVE_BROKEN_ALERT,
                .alert_alarms = 0x7c,
+               .max_convrate = 10,
        },
        [lm86] = {
                .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
                .alert_alarms = 0x7b,
+               .max_convrate = 9,
        },
        [lm90] = {
                .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
                .alert_alarms = 0x7b,
+               .max_convrate = 9,
        },
        [lm99] = {
                .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
                .alert_alarms = 0x7b,
+               .max_convrate = 9,
        },
        [max6646] = {
                .flags = LM90_HAVE_LOCAL_EXT,
                .alert_alarms = 0x7c,
+               .max_convrate = 6,
        },
        [max6657] = {
                .flags = LM90_HAVE_LOCAL_EXT,
                .alert_alarms = 0x7c,
+               .max_convrate = 8,
        },
        [max6659] = {
                .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY,
                .alert_alarms = 0x7c,
+               .max_convrate = 8,
        },
        [max6680] = {
                .flags = LM90_HAVE_OFFSET,
                .alert_alarms = 0x7c,
+               .max_convrate = 7,
        },
        [max6696] = {
                .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY
                  | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3,
                .alert_alarms = 0x187c,
+               .max_convrate = 6,
        },
        [w83l771] = {
                .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
                .alert_alarms = 0x7c,
+               .max_convrate = 8,
        },
 };
 
@@ -258,9 +276,13 @@ struct lm90_data {
        int kind;
        u32 flags;
 
+       int update_interval;    /* in milliseconds */
+
        u8 config_orig;         /* Original configuration register value */
+       u8 convrate_orig;       /* Original conversion rate register value */
        u16 alert_alarms;       /* Which alarm bits trigger ALERT# */
                                /* Upper 8 bits for max6695/96 */
+       u8 max_convrate;        /* Maximum conversion rate */
 
        /* registers values */
        s8 temp8[8];    /* 0: local low limit
@@ -382,15 +404,41 @@ static inline void lm90_select_remote_channel(struct i2c_client *client,
        }
 }
 
+/*
+ * Set conversion rate.
+ * client->update_lock must be held when calling this function (unless we are
+ * in detection or initialization steps).
+ */
+static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
+                             unsigned int interval)
+{
+       int i;
+       unsigned int update_interval;
+
+       /* Shift calculations to avoid rounding errors */
+       interval <<= 6;
+
+       /* find the nearest update rate */
+       for (i = 0, update_interval = LM90_MAX_CONVRATE_MS << 6;
+            i < data->max_convrate; i++, update_interval >>= 1)
+               if (interval >= update_interval * 3 / 4)
+                       break;
+
+       i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i);
+       data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64);
+}
+
 static struct lm90_data *lm90_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct lm90_data *data = i2c_get_clientdata(client);
+       unsigned long next_update;
 
        mutex_lock(&data->update_lock);
 
-       if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10)
-        || !data->valid) {
+       next_update = data->last_updated
+         + msecs_to_jiffies(data->update_interval) + 1;
+       if (time_after(jiffies, next_update) || !data->valid) {
                u8 h, l;
                u8 alarms;
 
@@ -825,6 +873,34 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute
        return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
 }
 
+static ssize_t show_update_interval(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct lm90_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", data->update_interval);
+}
+
+static ssize_t set_update_interval(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm90_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+       int err;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err)
+               return err;
+
+       mutex_lock(&data->update_lock);
+       lm90_set_convrate(client, data, val);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
 static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, 0, 4);
 static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, 0, 0);
 static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
@@ -856,6 +932,9 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
 /* Raw alarm file for compatibility */
 static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
 
+static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
+                  set_update_interval);
+
 static struct attribute *lm90_attributes[] = {
        &sensor_dev_attr_temp1_input.dev_attr.attr,
        &sensor_dev_attr_temp2_input.dev_attr.attr,
@@ -876,6 +955,7 @@ static struct attribute *lm90_attributes[] = {
        &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
        &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
        &dev_attr_alarms.attr,
+       &dev_attr_update_interval.attr,
        NULL
 };
 
@@ -1157,10 +1237,23 @@ static int lm90_detect(struct i2c_client *new_client,
        } else
        if (address == 0x4C
         && man_id == 0x5C) { /* Winbond/Nuvoton */
-               if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */
-                && (reg_config1 & 0x2A) == 0x00
-                && reg_convrate <= 0x08) {
-                       name = "w83l771";
+               int reg_config2;
+
+               reg_config2 = i2c_smbus_read_byte_data(new_client,
+                                               LM90_REG_R_CONFIG2);
+               if (reg_config2 < 0)
+                       return -ENODEV;
+
+               if ((reg_config1 & 0x2A) == 0x00
+                && (reg_config2 & 0xF8) == 0x00) {
+                       if (chip_id == 0x01 /* W83L771W/G */
+                        && reg_convrate <= 0x09) {
+                               name = "w83l771";
+                       } else
+                       if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */
+                        && reg_convrate <= 0x08) {
+                               name = "w83l771";
+                       }
                }
        }
 
@@ -1195,14 +1288,19 @@ static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data)
 
 static void lm90_init_client(struct i2c_client *client)
 {
-       u8 config;
+       u8 config, convrate;
        struct lm90_data *data = i2c_get_clientdata(client);
 
+       if (lm90_read_reg(client, LM90_REG_R_CONVRATE, &convrate) < 0) {
+               dev_warn(&client->dev, "Failed to read convrate register!\n");
+               convrate = LM90_DEF_CONVRATE_RVAL;
+       }
+       data->convrate_orig = convrate;
+
        /*
         * Start the conversions.
         */
-       i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
-                                 5); /* 2 Hz */
+       lm90_set_convrate(client, data, 500);   /* 500ms; 2Hz conversion rate */
        if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) {
                dev_warn(&client->dev, "Initialization failed!\n");
                return;
@@ -1263,6 +1361,9 @@ static int lm90_probe(struct i2c_client *new_client,
        /* Set chip capabilities */
        data->flags = lm90_params[data->kind].flags;
 
+       /* Set maximum conversion rate */
+       data->max_convrate = lm90_params[data->kind].max_convrate;
+
        /* Initialize the LM90 chip */
        lm90_init_client(new_client);
 
@@ -1324,6 +1425,8 @@ static int lm90_remove(struct i2c_client *client)
        lm90_remove_files(client, data);
 
        /* Restore initial configuration */
+       i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
+                                 data->convrate_orig);
        i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
                                  data->config_orig);
 
@@ -1361,7 +1464,7 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag)
                /* Disable ALERT# output, because these chips don't implement
                  SMBus alert correctly; they should only hold the alert line
                  low briefly. */
-               if ((data->kind == adm1032 || data->kind == adt7461)
+               if ((data->flags & LM90_HAVE_BROKEN_ALERT)
                 && (alarms & data->alert_alarms)) {
                        dev_dbg(&client->dev, "Disabling ALERT#\n");
                        lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);