Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee13...
[pandora-kernel.git] / drivers / hwmon / lm90.c
index 5acc12a..6882ce7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
  *          monitoring
- * Copyright (C) 2003-2005  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2006  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
@@ -31,7 +31,7 @@
  * Devices. That chip is similar to the LM90, with a few differences
  * that are not handled by this driver. Complete datasheet can be
  * obtained from Analog's website at:
- *   http://products.analog.com/products/info.asp?product=ADM1032
+ *   http://www.analog.com/en/prod/0,2877,ADM1032,00.html
  * Among others, it has a higher accuracy than the LM90, much like the
  * LM86 does.
  *
@@ -49,7 +49,7 @@
  * register values are decoded differently) it is ignored by this
  * driver. Complete datasheet can be obtained from Analog's website
  * at:
- *   http://products.analog.com/products/info.asp?product=ADT7461
+ *   http://www.analog.com/en/prod/0,2877,ADT7461,00.html
  *
  * Since the LM90 was the first chipset supported by this driver, most
  * comments will refer to this chipset, but are actually general and
 #include <linux/hwmon-sysfs.h>
 #include <linux/hwmon.h>
 #include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
 
 /*
  * Addresses to scan
  * Address is fully defined internally and cannot be changed except for
  * MAX6659.
- * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c.
- * LM89-1, and LM99-1 have address 0x4d.
+ * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658
+ * have address 0x4c.
+ * ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d.
  * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
- * ADT7461 always has address 0x4c.
  */
 
 static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END };
@@ -186,10 +188,10 @@ static struct lm90_data *lm90_update_device(struct device *dev);
  */
 
 static struct i2c_driver lm90_driver = {
-       .owner          = THIS_MODULE,
-       .name           = "lm90",
+       .driver = {
+               .name   = "lm90",
+       },
        .id             = I2C_DRIVERID_LM90,
-       .flags          = I2C_DF_NOTIFY,
        .attach_adapter = lm90_attach_adapter,
        .detach_client  = lm90_detach_client,
 };
@@ -201,7 +203,7 @@ static struct i2c_driver lm90_driver = {
 struct lm90_data {
        struct i2c_client client;
        struct class_device *class_dev;
-       struct semaphore update_lock;
+       struct mutex update_lock;
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
        int kind;
@@ -247,13 +249,13 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
        long val = simple_strtol(buf, NULL, 10);
        int nr = attr->index;
 
-       down(&data->update_lock);
+       mutex_lock(&data->update_lock);
        if (data->kind == adt7461)
                data->temp8[nr] = TEMP1_TO_REG_ADT7461(val);
        else
                data->temp8[nr] = TEMP1_TO_REG(val);
        i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]);
-       up(&data->update_lock);
+       mutex_unlock(&data->update_lock);
        return count;
 }
 
@@ -281,7 +283,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
        long val = simple_strtol(buf, NULL, 10);
        int nr = attr->index;
 
-       down(&data->update_lock);
+       mutex_lock(&data->update_lock);
        if (data->kind == adt7461)
                data->temp11[nr] = TEMP2_TO_REG_ADT7461(val);
        else
@@ -290,7 +292,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
                                  data->temp11[nr] >> 8);
        i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
                                  data->temp11[nr] & 0xff);
-       up(&data->update_lock);
+       mutex_unlock(&data->update_lock);
        return count;
 }
 
@@ -311,11 +313,11 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
        long val = simple_strtol(buf, NULL, 10);
        long hyst;
 
-       down(&data->update_lock);
+       mutex_lock(&data->update_lock);
        hyst = TEMP1_FROM_REG(data->temp8[3]) - val;
        i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
                                  HYST_TO_REG(hyst));
-       up(&data->update_lock);
+       mutex_unlock(&data->update_lock);
        return count;
 }
 
@@ -326,6 +328,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
        return sprintf(buf, "%d\n", data->alarms);
 }
 
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+                         *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct lm90_data *data = lm90_update_device(dev);
+       int bitnr = attr->index;
+
+       return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
@@ -343,17 +355,102 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
 static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
        set_temphyst, 3);
 static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
+
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+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 struct attribute *lm90_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+
+       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &dev_attr_alarms.attr,
+       NULL
+};
+
+static const struct attribute_group lm90_group = {
+       .attrs = lm90_attributes,
+};
+
+/* pec used for ADM1032 only */
+static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
+                       char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
+}
+
+static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
+                      const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       long val = simple_strtol(buf, NULL, 10);
+
+       switch (val) {
+       case 0:
+               client->flags &= ~I2C_CLIENT_PEC;
+               break;
+       case 1:
+               client->flags |= I2C_CLIENT_PEC;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
+
 /*
  * Real code
  */
 
+/* The ADM1032 supports PEC but not on write byte transactions, so we need
+   to explicitely ask for a transaction without PEC. */
+static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value)
+{
+       return i2c_smbus_xfer(client->adapter, client->addr,
+                             client->flags & ~I2C_CLIENT_PEC,
+                             I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
+}
+
+/* It is assumed that client->update_lock is held (unless we are in
+   detection or initialization steps). This matters when PEC is enabled,
+   because we don't want the address pointer to change between the write
+   byte and the read byte transactions. */
 static int lm90_read_reg(struct i2c_client* client, u8 reg, u8 *value)
 {
        int err;
 
-       err = i2c_smbus_read_byte_data(client, reg);
+       if (client->flags & I2C_CLIENT_PEC) {
+               err = adm1032_write_byte(client, reg);
+               if (err >= 0)
+                       err = i2c_smbus_read_byte(client);
+       } else
+               err = i2c_smbus_read_byte_data(client, reg);
 
        if (err < 0) {
                dev_warn(&client->dev, "Register %#02x read failed (%d)\n",
@@ -452,14 +549,12 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
                        }
                } else
                if (man_id == 0x41) { /* Analog Devices */
-                       if (address == 0x4C
-                        && (chip_id & 0xF0) == 0x40 /* ADM1032 */
+                       if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
                         && (reg_config1 & 0x3F) == 0x00
                         && reg_convrate <= 0x0A) {
                                kind = adm1032;
                        } else
-                       if (address == 0x4c
-                        && chip_id == 0x51 /* ADT7461 */
+                       if (chip_id == 0x51 /* ADT7461 */
                         && (reg_config1 & 0x1F) == 0x00 /* check compat mode */
                         && reg_convrate <= 0x0A) {
                                kind = adt7461;
@@ -494,6 +589,10 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
                name = "lm90";
        } else if (kind == adm1032) {
                name = "adm1032";
+               /* The ADM1032 supports PEC, but only if combined
+                  transactions are not used. */
+               if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+                       new_client->flags |= I2C_CLIENT_PEC;
        } else if (kind == lm99) {
                name = "lm99";
        } else if (kind == lm86) {
@@ -508,7 +607,7 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
        strlcpy(new_client->name, name, I2C_NAME_SIZE);
        data->valid = 0;
        data->kind = kind;
-       init_MUTEX(&data->update_lock);
+       mutex_init(&data->update_lock);
 
        /* Tell the I2C layer a new client has arrived */
        if ((err = i2c_attach_client(new_client)))
@@ -518,36 +617,25 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
        lm90_init_client(new_client);
 
        /* Register sysfs hooks */
+       if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group)))
+               goto exit_detach;
+       if (new_client->flags & I2C_CLIENT_PEC) {
+               if ((err = device_create_file(&new_client->dev,
+                                             &dev_attr_pec)))
+                       goto exit_remove_files;
+       }
+
        data->class_dev = hwmon_device_register(&new_client->dev);
        if (IS_ERR(data->class_dev)) {
                err = PTR_ERR(data->class_dev);
-               goto exit_detach;
+               goto exit_remove_files;
        }
 
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp1_input.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp2_input.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp1_min.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp2_min.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp1_max.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp2_max.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp1_crit.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp2_crit.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp1_crit_hyst.dev_attr);
-       device_create_file(&new_client->dev,
-                          &sensor_dev_attr_temp2_crit_hyst.dev_attr);
-       device_create_file(&new_client->dev, &dev_attr_alarms);
-
        return 0;
 
+exit_remove_files:
+       sysfs_remove_group(&new_client->dev.kobj, &lm90_group);
+       device_remove_file(&new_client->dev, &dev_attr_pec);
 exit_detach:
        i2c_detach_client(new_client);
 exit_free:
@@ -580,6 +668,8 @@ static int lm90_detach_client(struct i2c_client *client)
        int err;
 
        hwmon_device_unregister(data->class_dev);
+       sysfs_remove_group(&client->dev.kobj, &lm90_group);
+       device_remove_file(&client->dev, &dev_attr_pec);
 
        if ((err = i2c_detach_client(client)))
                return err;
@@ -593,7 +683,7 @@ 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);
 
-       down(&data->update_lock);
+       mutex_lock(&data->update_lock);
 
        if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
                u8 oldh, newh, l;
@@ -639,7 +729,7 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                data->valid = 1;
        }
 
-       up(&data->update_lock);
+       mutex_unlock(&data->update_lock);
 
        return data;
 }