hwmon: (lm90) Support ADT7461 in extended mode
authorNate Case <ncase@xes-inc.com>
Fri, 17 Oct 2008 15:51:10 +0000 (17:51 +0200)
committerJean Delvare <khali@mahadeva.delvare>
Fri, 17 Oct 2008 15:51:10 +0000 (17:51 +0200)
Support ADT7461 in extended temperature range mode, which will change
the range of readings from 0..127 to -64..191 degC.  Adjust the
register conversion functions accordingly.

Signed-off-by: Nate Case <ncase@xes-inc.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Tested-by: Martyn Welch <martyn.welch@gefanuc.com>
Documentation/hwmon/lm90
drivers/hwmon/Kconfig
drivers/hwmon/lm90.c

index 0c08c5c..53cd829 100644 (file)
@@ -32,7 +32,6 @@ Supported chips:
     Addresses scanned: I2C 0x4c and 0x4d
     Datasheet: Publicly available at the ON Semiconductor website
                http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461
-    Note: Only if in ADM1032 compatibility mode
   * Maxim MAX6657
     Prefix: 'max6657'
     Addresses scanned: I2C 0x4c
@@ -70,16 +69,13 @@ Description
 
 The LM90 is a digital temperature sensor. It senses its own temperature as
 well as the temperature of up to one external diode. It is compatible
-with many other devices such as the LM86, the LM89, the LM99, the ADM1032,
-the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are
-supported by this driver.
+with many other devices, many of which are supported by this driver.
 
 Note that there is no easy way to differentiate between the MAX6657,
 MAX6658 and MAX6659 variants. The extra address and features of the
 MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only
 differ in their pinout, therefore they obviously can't (and don't need to)
-be distinguished. Additionally, the ADT7461 is supported if found in
-ADM1032 compatibility mode.
+be distinguished.
 
 The specificity of this family of chipsets over the ADM1021/LM84
 family is that it features critical limits with hysteresis, and an
index ebacc0a..96701e0 100644 (file)
@@ -510,11 +510,8 @@ config SENSORS_LM90
        depends on I2C
        help
          If you say yes here you get support for National Semiconductor LM90,
-         LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657,
-         MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips.
-
-         The Analog Devices ADT7461 sensor chip is also supported, but only
-         if found in ADM1032 compatibility mode.
+         LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim
+         MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips.
 
          This driver can also be built as a module.  If so, the module
          will be called lm90.
index b2d9b3f..fe5d860 100644 (file)
  * chips. The MAX6680 and MAX6681 only differ in the pinout so they can
  * be treated identically.
  *
- * This driver also supports the ADT7461 chip from Analog Devices but
- * only in its "compatability mode". If an ADT7461 chip is found but
- * is configured in non-compatible mode (where its temperature
- * register values are decoded differently) it is ignored by this
- * driver.
+ * This driver also supports the ADT7461 chip from Analog Devices.
+ * It's supported in both compatibility and extended mode. It is mostly
+ * compatible with LM90 except for a data format difference for the
+ * temperature value registers.
  *
  * Since the LM90 was the first chipset supported by this driver, most
  * comments will refer to this chipset, but are actually general and
@@ -138,6 +137,11 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
 #define MAX6657_REG_R_LOCAL_TEMPL      0x11
 
 /*
+ * Device flags
+ */
+#define LM90_FLAG_ADT7461_EXT          0x01    /* ADT7461 extended mode */
+
+/*
  * Functions declaration
  */
 
@@ -191,6 +195,7 @@ struct lm90_data {
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
        int kind;
+       int flags;
 
        /* registers values */
        s8 temp8[4];    /* 0: local low limit
@@ -256,26 +261,61 @@ static u8 hyst_to_reg(long val)
 }
 
 /*
- * ADT7461 is almost identical to LM90 except that attempts to write
- * values that are outside the range 0 < temp < 127 are treated as
- * the boundary value.
+ * ADT7461 in compatibility mode is almost identical to LM90 except that
+ * attempts to write values that are outside the range 0 < temp < 127 are
+ * treated as the boundary value.
+ *
+ * ADT7461 in "extended mode" operation uses unsigned integers offset by
+ * 64 (e.g., 0 -> -64 degC).  The range is restricted to -64..191 degC.
  */
-static u8 temp1_to_reg_adt7461(long val)
+static inline int temp1_from_reg_adt7461(struct lm90_data *data, u8 val)
 {
-       if (val <= 0)
-               return 0;
-       if (val >= 127000)
-               return 127;
-       return (val + 500) / 1000;
+       if (data->flags & LM90_FLAG_ADT7461_EXT)
+               return (val - 64) * 1000;
+       else
+               return temp1_from_reg(val);
 }
 
-static u16 temp2_to_reg_adt7461(long val)
+static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val)
 {
-       if (val <= 0)
-               return 0;
-       if (val >= 127750)
-               return 0x7FC0;
-       return (val + 125) / 250 * 64;
+       if (data->flags & LM90_FLAG_ADT7461_EXT)
+               return (val - 0x4000) / 64 * 250;
+       else
+               return temp2_from_reg(val);
+}
+
+static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val)
+{
+       if (data->flags & LM90_FLAG_ADT7461_EXT) {
+               if (val <= -64000)
+                       return 0;
+               if (val >= 191000)
+                       return 0xFF;
+               return (val + 500 + 64000) / 1000;
+       } else {
+               if (val <= 0)
+                       return 0;
+               if (val >= 127000)
+                       return 127;
+               return (val + 500) / 1000;
+       }
+}
+
+static u16 temp2_to_reg_adt7461(struct lm90_data *data, long val)
+{
+       if (data->flags & LM90_FLAG_ADT7461_EXT) {
+               if (val <= -64000)
+                       return 0;
+               if (val >= 191750)
+                       return 0xFFC0;
+               return (val + 64000 + 125) / 250 * 64;
+       } else {
+               if (val <= 0)
+                       return 0;
+               if (val >= 127750)
+                       return 0x7FC0;
+               return (val + 125) / 250 * 64;
+       }
 }
 
 /*
@@ -287,7 +327,14 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
        struct lm90_data *data = lm90_update_device(dev);
-       return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]));
+       int temp;
+
+       if (data->kind == adt7461)
+               temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]);
+       else
+               temp = temp1_from_reg(data->temp8[attr->index]);
+
+       return sprintf(buf, "%d\n", temp);
 }
 
 static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
@@ -308,7 +355,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
 
        mutex_lock(&data->update_lock);
        if (data->kind == adt7461)
-               data->temp8[nr] = temp1_to_reg_adt7461(val);
+               data->temp8[nr] = temp1_to_reg_adt7461(data, val);
        else
                data->temp8[nr] = temp1_to_reg(val);
        i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]);
@@ -321,7 +368,14 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
        struct lm90_data *data = lm90_update_device(dev);
-       return sprintf(buf, "%d\n", temp2_from_reg(data->temp11[attr->index]));
+       int temp;
+
+       if (data->kind == adt7461)
+               temp = temp2_from_reg_adt7461(data, data->temp11[attr->index]);
+       else
+               temp = temp2_from_reg(data->temp11[attr->index]);
+
+       return sprintf(buf, "%d\n", temp);
 }
 
 static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
@@ -344,7 +398,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
 
        mutex_lock(&data->update_lock);
        if (data->kind == adt7461)
-               data->temp11[nr] = temp2_to_reg_adt7461(val);
+               data->temp11[nr] = temp2_to_reg_adt7461(data, val);
        else if (data->kind == max6657 || data->kind == max6680)
                data->temp11[nr] = temp1_to_reg(val) << 8;
        else
@@ -364,8 +418,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
        struct lm90_data *data = lm90_update_device(dev);
-       return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index])
-                      - temp1_from_reg(data->temp_hyst));
+       int temp;
+
+       if (data->kind == adt7461)
+               temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]);
+       else
+               temp = temp1_from_reg(data->temp8[attr->index]);
+
+       return sprintf(buf, "%d\n", temp - temp1_from_reg(data->temp_hyst));
 }
 
 static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
@@ -598,7 +658,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
                                kind = adm1032;
                        } else
                        if (chip_id == 0x51 /* ADT7461 */
-                        && (reg_config1 & 0x1F) == 0x00 /* check compat mode */
+                        && (reg_config1 & 0x1B) == 0x00
                         && reg_convrate <= 0x0A) {
                                kind = adt7461;
                        }
@@ -737,6 +797,12 @@ static void lm90_init_client(struct i2c_client *client)
        }
        config_orig = config;
 
+       /* Check Temperature Range Select */
+       if (data->kind == adt7461) {
+               if (config & 0x04)
+                       data->flags |= LM90_FLAG_ADT7461_EXT;
+       }
+
        /*
         * Put MAX6680/MAX8881 into extended resolution (bit 0x10,
         * 0.125 degree resolution) and range (0x08, extend range