Merge branch 'rc-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuil...
[pandora-kernel.git] / drivers / hwmon / lm90.c
index 7c9bdc1..760ef72 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
  *          monitoring
- * Copyright (C) 2003-2009  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2010  Jean Delvare <khali@linux-fr.org>
  *
  * Based on the lm83 driver. The LM90 is a sensor chip made by National
  * Semiconductor. It reports up to two temperatures (its own plus up to
@@ -93,7 +93,8 @@
 static const unsigned short normal_i2c[] = {
        0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 };
+enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646,
+       w83l771 };
 
 /*
  * The LM90 registers
@@ -151,6 +152,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info);
 static int lm90_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
 static void lm90_init_client(struct i2c_client *client);
+static void lm90_alert(struct i2c_client *client, unsigned int flag);
 static int lm90_remove(struct i2c_client *client);
 static struct lm90_data *lm90_update_device(struct device *dev);
 
@@ -173,6 +175,7 @@ static const struct i2c_device_id lm90_id[] = {
        { "max6659", max6657 },
        { "max6680", max6680 },
        { "max6681", max6680 },
+       { "w83l771", w83l771 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm90_id);
@@ -184,6 +187,7 @@ static struct i2c_driver lm90_driver = {
        },
        .probe          = lm90_probe,
        .remove         = lm90_remove,
+       .alert          = lm90_alert,
        .id_table       = lm90_id,
        .detect         = lm90_detect,
        .address_list   = normal_i2c,
@@ -201,6 +205,9 @@ struct lm90_data {
        int kind;
        int flags;
 
+       u8 config_orig;         /* Original configuration register value */
+       u8 alert_alarms;        /* Which alarm bits trigger ALERT# */
+
        /* registers values */
        s8 temp8[4];    /* 0: local low limit
                           1: local high limit
@@ -758,6 +765,14 @@ static int lm90_detect(struct i2c_client *new_client,
                 && reg_convrate <= 0x07) {
                        name = "max6646";
                }
+       } 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";
+               }
        }
 
        if (!name) { /* identification failed */
@@ -794,6 +809,19 @@ static int lm90_probe(struct i2c_client *new_client,
                        new_client->flags &= ~I2C_CLIENT_PEC;
        }
 
+       /* Different devices have different alarm bits triggering the
+        * ALERT# output */
+       switch (data->kind) {
+       case lm90:
+       case lm99:
+       case lm86:
+               data->alert_alarms = 0x7b;
+               break;
+       default:
+               data->alert_alarms = 0x7c;
+               break;
+       }
+
        /* Initialize the LM90 chip */
        lm90_init_client(new_client);
 
@@ -830,7 +858,7 @@ exit:
 
 static void lm90_init_client(struct i2c_client *client)
 {
-       u8 config, config_orig;
+       u8 config;
        struct lm90_data *data = i2c_get_clientdata(client);
 
        /*
@@ -842,7 +870,7 @@ static void lm90_init_client(struct i2c_client *client)
                dev_warn(&client->dev, "Initialization failed!\n");
                return;
        }
-       config_orig = config;
+       data->config_orig = config;
 
        /* Check Temperature Range Select */
        if (data->kind == adt7461) {
@@ -860,7 +888,7 @@ static void lm90_init_client(struct i2c_client *client)
        }
 
        config &= 0xBF; /* run */
-       if (config != config_orig) /* Only write if changed */
+       if (config != data->config_orig) /* Only write if changed */
                i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
 }
 
@@ -875,10 +903,46 @@ static int lm90_remove(struct i2c_client *client)
                device_remove_file(&client->dev,
                                   &sensor_dev_attr_temp2_offset.dev_attr);
 
+       /* Restore initial configuration */
+       i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
+                                 data->config_orig);
+
        kfree(data);
        return 0;
 }
 
+static void lm90_alert(struct i2c_client *client, unsigned int flag)
+{
+       struct lm90_data *data = i2c_get_clientdata(client);
+       u8 config, alarms;
+
+       lm90_read_reg(client, LM90_REG_R_STATUS, &alarms);
+       if ((alarms & 0x7f) == 0) {
+               dev_info(&client->dev, "Everything OK\n");
+       } else {
+               if (alarms & 0x61)
+                       dev_warn(&client->dev,
+                                "temp%d out of range, please check!\n", 1);
+               if (alarms & 0x1a)
+                       dev_warn(&client->dev,
+                                "temp%d out of range, please check!\n", 2);
+               if (alarms & 0x04)
+                       dev_warn(&client->dev,
+                                "temp%d diode open, please check!\n", 2);
+
+               /* 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)
+                && (alarms & data->alert_alarms)) {
+                       dev_dbg(&client->dev, "Disabling ALERT#\n");
+                       lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
+                       i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
+                                                 config | 0x80);
+               }
+       }
+}
+
 static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
 {
        int err;
@@ -918,7 +982,8 @@ static struct lm90_data *lm90_update_device(struct device *dev)
 
        mutex_lock(&data->update_lock);
 
-       if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
+       if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10)
+        || !data->valid) {
                u8 h, l;
 
                dev_dbg(&client->dev, "Updating lm90 data.\n");
@@ -966,6 +1031,21 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                }
                lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms);
 
+               /* Re-enable ALERT# output if it was originally enabled and
+                * relevant alarms are all clear */
+               if ((data->config_orig & 0x80) == 0
+                && (data->alarms & data->alert_alarms) == 0) {
+                       u8 config;
+
+                       lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
+                       if (config & 0x80) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               i2c_smbus_write_byte_data(client,
+                                                         LM90_REG_W_CONFIG1,
+                                                         config & ~0x80);
+                       }
+               }
+
                data->last_updated = jiffies;
                data->valid = 1;
        }