Commit 23b2d477 authored by Nate Case's avatar Nate Case Committed by Jean Delvare

hwmon: (lm90) Support ADT7461 in extended mode

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: default avatarNate Case <ncase@xes-inc.com>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
Tested-by: default avatarMartyn Welch <martyn.welch@gefanuc.com>
parent cea50fe2
...@@ -32,7 +32,6 @@ Supported chips: ...@@ -32,7 +32,6 @@ Supported chips:
Addresses scanned: I2C 0x4c and 0x4d Addresses scanned: I2C 0x4c and 0x4d
Datasheet: Publicly available at the ON Semiconductor website Datasheet: Publicly available at the ON Semiconductor website
http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461
Note: Only if in ADM1032 compatibility mode
* Maxim MAX6657 * Maxim MAX6657
Prefix: 'max6657' Prefix: 'max6657'
Addresses scanned: I2C 0x4c Addresses scanned: I2C 0x4c
...@@ -70,16 +69,13 @@ Description ...@@ -70,16 +69,13 @@ Description
The LM90 is a digital temperature sensor. It senses its own temperature as 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 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, with many other devices, many of which are supported by this driver.
the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are
supported by this driver.
Note that there is no easy way to differentiate between the MAX6657, Note that there is no easy way to differentiate between the MAX6657,
MAX6658 and MAX6659 variants. The extra address and features of the MAX6658 and MAX6659 variants. The extra address and features of the
MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only 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) differ in their pinout, therefore they obviously can't (and don't need to)
be distinguished. Additionally, the ADT7461 is supported if found in be distinguished.
ADM1032 compatibility mode.
The specificity of this family of chipsets over the ADM1021/LM84 The specificity of this family of chipsets over the ADM1021/LM84
family is that it features critical limits with hysteresis, and an family is that it features critical limits with hysteresis, and an
......
...@@ -510,11 +510,8 @@ config SENSORS_LM90 ...@@ -510,11 +510,8 @@ config SENSORS_LM90
depends on I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor LM90, If you say yes here you get support for National Semiconductor LM90,
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim
MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. 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.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called lm90. will be called lm90.
......
...@@ -37,11 +37,10 @@ ...@@ -37,11 +37,10 @@
* chips. The MAX6680 and MAX6681 only differ in the pinout so they can * chips. The MAX6680 and MAX6681 only differ in the pinout so they can
* be treated identically. * be treated identically.
* *
* This driver also supports the ADT7461 chip from Analog Devices but * This driver also supports the ADT7461 chip from Analog Devices.
* only in its "compatability mode". If an ADT7461 chip is found but * It's supported in both compatibility and extended mode. It is mostly
* is configured in non-compatible mode (where its temperature * compatible with LM90 except for a data format difference for the
* register values are decoded differently) it is ignored by this * temperature value registers.
* driver.
* *
* Since the LM90 was the first chipset supported by this driver, most * Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and * comments will refer to this chipset, but are actually general and
...@@ -137,6 +136,11 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); ...@@ -137,6 +136,11 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680);
#define MAX6657_REG_R_LOCAL_TEMPL 0x11 #define MAX6657_REG_R_LOCAL_TEMPL 0x11
/*
* Device flags
*/
#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */
/* /*
* Functions declaration * Functions declaration
*/ */
...@@ -191,6 +195,7 @@ struct lm90_data { ...@@ -191,6 +195,7 @@ struct lm90_data {
char valid; /* zero until following fields are valid */ char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */ unsigned long last_updated; /* in jiffies */
int kind; int kind;
int flags;
/* registers values */ /* registers values */
s8 temp8[4]; /* 0: local low limit s8 temp8[4]; /* 0: local low limit
...@@ -256,26 +261,61 @@ static u8 hyst_to_reg(long val) ...@@ -256,26 +261,61 @@ static u8 hyst_to_reg(long val)
} }
/* /*
* ADT7461 is almost identical to LM90 except that attempts to write * ADT7461 in compatibility mode is almost identical to LM90 except that
* values that are outside the range 0 < temp < 127 are treated as * attempts to write values that are outside the range 0 < temp < 127 are
* the boundary value. * 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 (data->flags & LM90_FLAG_ADT7461_EXT)
return (val - 64) * 1000;
else
return temp1_from_reg(val);
}
static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val)
{ {
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) if (val <= 0)
return 0; return 0;
if (val >= 127000) if (val >= 127000)
return 127; return 127;
return (val + 500) / 1000; return (val + 500) / 1000;
}
} }
static u16 temp2_to_reg_adt7461(long val) 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) if (val <= 0)
return 0; return 0;
if (val >= 127750) if (val >= 127750)
return 0x7FC0; return 0x7FC0;
return (val + 125) / 250 * 64; return (val + 125) / 250 * 64;
}
} }
/* /*
...@@ -287,7 +327,14 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, ...@@ -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 sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev); 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, 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, ...@@ -308,7 +355,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (data->kind == adt7461) if (data->kind == adt7461)
data->temp8[nr] = temp1_to_reg_adt7461(val); data->temp8[nr] = temp1_to_reg_adt7461(data, val);
else else
data->temp8[nr] = temp1_to_reg(val); data->temp8[nr] = temp1_to_reg(val);
i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); 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, ...@@ -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 sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev); 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, 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, ...@@ -344,7 +398,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (data->kind == adt7461) 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) else if (data->kind == max6657 || data->kind == max6680)
data->temp11[nr] = temp1_to_reg(val) << 8; data->temp11[nr] = temp1_to_reg(val) << 8;
else else
...@@ -364,8 +418,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt ...@@ -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 sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev); struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]) int temp;
- temp1_from_reg(data->temp_hyst));
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, 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, ...@@ -598,7 +658,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
kind = adm1032; kind = adm1032;
} else } else
if (chip_id == 0x51 /* ADT7461 */ if (chip_id == 0x51 /* ADT7461 */
&& (reg_config1 & 0x1F) == 0x00 /* check compat mode */ && (reg_config1 & 0x1B) == 0x00
&& reg_convrate <= 0x0A) { && reg_convrate <= 0x0A) {
kind = adt7461; kind = adt7461;
} }
...@@ -737,6 +797,12 @@ static void lm90_init_client(struct i2c_client *client) ...@@ -737,6 +797,12 @@ static void lm90_init_client(struct i2c_client *client)
} }
config_orig = config; 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, * Put MAX6680/MAX8881 into extended resolution (bit 0x10,
* 0.125 degree resolution) and range (0x08, extend range * 0.125 degree resolution) and range (0x08, extend range
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment