Commit a9f3d3a8 authored by Guenter Roeck's avatar Guenter Roeck

hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483

ADT7481, ADT7482, and ADT7483 are similar to ADT7461, but support two
external temperature sensors, similar to MAX6695/6696. They support an
extended temperature range similar to ADT7461. Registers for the second
external channel can be accessed directly or by using the same method as
used by MAX6695/6696. For simplicity, the access method implemented for
MAX6695/6696 is used.

The chips support PEC (packet error checking). Set the PEC feature flag
and let the user decide if it should be enabled or not (it is by default
disabled).

Even though it is only documented for ADT7483, all three chips support a
secondary manufacturer ID register at 0x3e and a chip ID register at 0x3f.
Use the contents of those registers register for improved chip detection
accuracy. Add the same check to the ADT7461A detection code since this chip
also supports the same (undocumented) registers.

Devicetree nodes are not added for the added chips since it is quite
unlikely that such old chips will ever be used in a devicetree based
system. They can be added later if needed.
Reviewed-by: default avatarSlawomir Stepien <sst@poczta.fm>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 904a6fe6
...@@ -73,6 +73,36 @@ Supported chips: ...@@ -73,6 +73,36 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A
* Analog Devices ADT7481
Prefix: 'adt7481'
Addresses scanned: I2C 0x4b and 0x4c
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7481
* Analog Devices ADT7482
Prefix: 'adt7482'
Addresses scanned: I2C 0x4c
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7482
* Analog Devices ADT7483A
Prefix: 'adt7483a'
Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7483A
* ON Semiconductor NCT1008 * ON Semiconductor NCT1008
Prefix: 'nct1008' Prefix: 'nct1008'
...@@ -323,6 +353,14 @@ ADT7461, ADT7461A, NCT1008: ...@@ -323,6 +353,14 @@ ADT7461, ADT7461A, NCT1008:
* Extended temperature range (breaks compatibility) * Extended temperature range (breaks compatibility)
* Lower resolution for remote temperature * Lower resolution for remote temperature
* SMBus PEC support for Write Byte and Receive Byte transactions. * SMBus PEC support for Write Byte and Receive Byte transactions.
* 10 bit temperature resolution
ADT7481, ADT7482, ADT7483:
* Temperature offset register
* SMBus PEC support
* 10 bit temperature resolution for external sensors
* Two remote sensors
* Selectable address (ADT7483)
MAX6646, MAX6647, MAX6649: MAX6646, MAX6647, MAX6649:
* Better local resolution * Better local resolution
......
...@@ -1358,7 +1358,8 @@ config SENSORS_LM90 ...@@ -1358,7 +1358,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, ADT7461, and ADT7461A, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A,
ADT7481, ADT7482, and ADT7483A,
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658,
MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG, ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
......
...@@ -64,6 +64,10 @@ ...@@ -64,6 +64,10 @@
* and extended mode. They are mostly compatible with LM90 except for a data * and extended mode. They are mostly compatible with LM90 except for a data
* format difference for the temperature value registers. * format difference for the temperature value registers.
* *
* This driver also supports ADT7481, ADT7482, and ADT7483 from Analog Devices
* / ON Semiconductor. The chips are similar to ADT7461 but support two external
* temperature sensors.
*
* This driver also supports the SA56004 from Philips. This device is * This driver also supports the SA56004 from Philips. This device is
* pin-compatible with the LM86, the ED/EDP parts are also address-compatible. * pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
* *
...@@ -114,7 +118,7 @@ static const unsigned short normal_i2c[] = { ...@@ -114,7 +118,7 @@ static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99,
max6646, max6648, max6654, max6657, max6659, max6680, max6696, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771, sa56004, tmp451, tmp461, w83l771,
}; };
...@@ -164,6 +168,13 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, ...@@ -164,6 +168,13 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define TMP461_REG_CHEN 0x16 #define TMP461_REG_CHEN 0x16
#define TMP461_REG_DFC 0x24 #define TMP461_REG_DFC 0x24
/* ADT7481 registers */
#define ADT7481_REG_STATUS2 0x23
#define ADT7481_REG_CONFIG2 0x24
#define ADT7481_REG_MAN_ID 0x3e
#define ADT7481_REG_CHIP_ID 0x3d
/* Device features */ /* Device features */
#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */ #define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */
#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ #define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */
...@@ -191,6 +202,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, ...@@ -191,6 +202,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define LM90_STATUS_LHIGH BIT(6) /* local high temp limit tripped */ #define LM90_STATUS_LHIGH BIT(6) /* local high temp limit tripped */
#define LM90_STATUS_BUSY BIT(7) /* conversion is ongoing */ #define LM90_STATUS_BUSY BIT(7) /* conversion is ongoing */
/* MAX6695/6696 and ADT7481 2nd status register */
#define MAX6696_STATUS2_R2THRM BIT(1) /* remote2 THERM limit tripped */ #define MAX6696_STATUS2_R2THRM BIT(1) /* remote2 THERM limit tripped */
#define MAX6696_STATUS2_R2OPEN BIT(2) /* remote2 is an open circuit */ #define MAX6696_STATUS2_R2OPEN BIT(2) /* remote2 is an open circuit */
#define MAX6696_STATUS2_R2LOW BIT(3) /* remote2 low temp limit tripped */ #define MAX6696_STATUS2_R2LOW BIT(3) /* remote2 low temp limit tripped */
...@@ -207,6 +219,9 @@ static const struct i2c_device_id lm90_id[] = { ...@@ -207,6 +219,9 @@ static const struct i2c_device_id lm90_id[] = {
{ "adm1032", adm1032 }, { "adm1032", adm1032 },
{ "adt7461", adt7461 }, { "adt7461", adt7461 },
{ "adt7461a", adt7461a }, { "adt7461a", adt7461a },
{ "adt7481", adt7481 },
{ "adt7482", adt7481 },
{ "adt7483a", adt7481 },
{ "g781", g781 }, { "g781", g781 },
{ "lm90", lm90 }, { "lm90", lm90 },
{ "lm86", lm86 }, { "lm86", lm86 },
...@@ -344,6 +359,7 @@ struct lm90_params { ...@@ -344,6 +359,7 @@ struct lm90_params {
/* Upper 8 bits for max6695/96 */ /* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate register value */ u8 max_convrate; /* Maximum conversion rate register value */
u8 resolution; /* 16-bit resolution (default 11 bit) */ u8 resolution; /* 16-bit resolution (default 11 bit) */
u8 reg_status2; /* 2nd status register (optional) */
u8 reg_local_ext; /* Extended local temp register (optional) */ u8 reg_local_ext; /* Extended local temp register (optional) */
}; };
...@@ -376,6 +392,16 @@ static const struct lm90_params lm90_params[] = { ...@@ -376,6 +392,16 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7c, .alert_alarms = 0x7c,
.max_convrate = 10, .max_convrate = 10,
}, },
[adt7481] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC
| LM90_HAVE_TEMP3 | LM90_HAVE_CRIT,
.alert_alarms = 0x1c7c,
.max_convrate = 11,
.resolution = 10,
.reg_status2 = ADT7481_REG_STATUS2,
},
[g781] = { [g781] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
...@@ -453,6 +479,7 @@ static const struct lm90_params lm90_params[] = { ...@@ -453,6 +479,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_ALARMS, | LM90_HAVE_ALARMS,
.alert_alarms = 0x1c7c, .alert_alarms = 0x1c7c,
.max_convrate = 6, .max_convrate = 6,
.reg_status2 = MAX6696_REG_STATUS2,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
}, },
[w83l771] = { [w83l771] = {
...@@ -549,6 +576,7 @@ struct lm90_data { ...@@ -549,6 +576,7 @@ struct lm90_data {
u16 alert_alarms; /* Which alarm bits trigger ALERT# */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */
/* Upper 8 bits for max6695/96 */ /* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate */ u8 max_convrate; /* Maximum conversion rate */
u8 reg_status2; /* 2nd status register (optional) */
u8 reg_local_ext; /* local extension register offset */ u8 reg_local_ext; /* local extension register offset */
/* registers values */ /* registers values */
...@@ -679,18 +707,14 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config) ...@@ -679,18 +707,14 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config)
* various registers have different meanings as a result of selecting a * various registers have different meanings as a result of selecting a
* non-default remote channel. * non-default remote channel.
*/ */
static int lm90_select_remote_channel(struct lm90_data *data, int channel) static int lm90_select_remote_channel(struct lm90_data *data, bool second)
{ {
int err = 0; u8 config = data->config & ~0x08;
if (data->kind == max6696) { if (second)
u8 config = data->config & ~0x08; config |= 0x08;
if (channel) return lm90_update_confreg(data, config);
config |= 0x08;
err = lm90_update_confreg(data, config);
}
return err;
} }
static int lm90_write_convrate(struct lm90_data *data, int val) static int lm90_write_convrate(struct lm90_data *data, int val)
...@@ -806,8 +830,8 @@ static int lm90_update_limits(struct device *dev) ...@@ -806,8 +830,8 @@ static int lm90_update_limits(struct device *dev)
data->temp[REMOTE_EMERG] = val << 8; data->temp[REMOTE_EMERG] = val << 8;
} }
if (data->kind == max6696) { if (data->flags & LM90_HAVE_TEMP3) {
val = lm90_select_remote_channel(data, 1); val = lm90_select_remote_channel(data, true);
if (val < 0) if (val < 0)
return val; return val;
...@@ -816,10 +840,12 @@ static int lm90_update_limits(struct device *dev) ...@@ -816,10 +840,12 @@ static int lm90_update_limits(struct device *dev)
return val; return val;
data->temp[REMOTE2_CRIT] = val << 8; data->temp[REMOTE2_CRIT] = val << 8;
val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); if (data->flags & LM90_HAVE_EMERGENCY) {
if (val < 0) val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
return val; if (val < 0)
data->temp[REMOTE2_EMERG] = val << 8; return val;
data->temp[REMOTE2_EMERG] = val << 8;
}
val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
if (val < 0) if (val < 0)
...@@ -831,7 +857,7 @@ static int lm90_update_limits(struct device *dev) ...@@ -831,7 +857,7 @@ static int lm90_update_limits(struct device *dev)
return val; return val;
data->temp[REMOTE2_HIGH] = val << 8; data->temp[REMOTE2_HIGH] = val << 8;
lm90_select_remote_channel(data, 0); lm90_select_remote_channel(data, false);
} }
return 0; return 0;
...@@ -914,8 +940,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) ...@@ -914,8 +940,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
return val; return val;
alarms = val & ~LM90_STATUS_BUSY; alarms = val & ~LM90_STATUS_BUSY;
if (data->kind == max6696) { if (data->reg_status2) {
val = lm90_read_reg(client, MAX6696_REG_STATUS2); val = lm90_read_reg(client, data->reg_status2);
if (val < 0) if (val < 0)
return val; return val;
alarms |= val << 8; alarms |= val << 8;
...@@ -1037,20 +1063,20 @@ static int lm90_update_device(struct device *dev) ...@@ -1037,20 +1063,20 @@ static int lm90_update_device(struct device *dev)
return val; return val;
data->temp[REMOTE_TEMP] = val; data->temp[REMOTE_TEMP] = val;
if (data->kind == max6696) { if (data->flags & LM90_HAVE_TEMP3) {
val = lm90_select_remote_channel(data, 1); val = lm90_select_remote_channel(data, true);
if (val < 0) if (val < 0)
return val; return val;
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
LM90_REG_REMOTE_TEMPL, true); LM90_REG_REMOTE_TEMPL, true);
if (val < 0) { if (val < 0) {
lm90_select_remote_channel(data, 0); lm90_select_remote_channel(data, false);
return val; return val;
} }
data->temp[REMOTE2_TEMP] = val; data->temp[REMOTE2_TEMP] = val;
lm90_select_remote_channel(data, 0); lm90_select_remote_channel(data, false);
} }
val = lm90_update_alarms_locked(data, false); val = lm90_update_alarms_locked(data, false);
...@@ -1207,7 +1233,7 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va ...@@ -1207,7 +1233,7 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va
lm90_temp_get_resolution(data, index)); lm90_temp_get_resolution(data, index));
if (channel > 1) if (channel > 1)
lm90_select_remote_channel(data, 1); lm90_select_remote_channel(data, true);
err = lm90_write_reg(client, regh, data->temp[index] >> 8); err = lm90_write_reg(client, regh, data->temp[index] >> 8);
if (err < 0) if (err < 0)
...@@ -1216,7 +1242,7 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va ...@@ -1216,7 +1242,7 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va
err = lm90_write_reg(client, regl, data->temp[index] & 0xff); err = lm90_write_reg(client, regl, data->temp[index] & 0xff);
deselect: deselect:
if (channel > 1) if (channel > 1)
lm90_select_remote_channel(data, 0); lm90_select_remote_channel(data, false);
return err; return err;
} }
...@@ -1568,9 +1594,15 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id, ...@@ -1568,9 +1594,15 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id,
static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
int config1, int convrate) int config1, int convrate)
{ {
int config2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CONFIG2);
int man_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_MAN_ID);
int chip_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CHIP_ID);
int address = client->addr; int address = client->addr;
const char *name = NULL; const char *name = NULL;
if (config2 < 0 || man_id2 < 0 || chip_id2 < 0)
return NULL;
switch (chip_id) { switch (chip_id) {
case 0x40 ... 0x4f: /* ADM1032 */ case 0x40 ... 0x4f: /* ADM1032 */
if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
...@@ -1592,6 +1624,28 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, ...@@ -1592,6 +1624,28 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
convrate <= 0x0a) convrate <= 0x0a)
name = "adt7461a"; name = "adt7461a";
break; break;
case 0x62: /* ADT7481, undocumented */
if (man_id2 == 0x41 && chip_id2 == 0x81 &&
(address == 0x4b || address == 0x4c) && !(config1 & 0x10) &&
!(config2 & 0x7f) && (convrate & 0x0f) <= 0x0b) {
name = "adt7481";
}
break;
case 0x65: /* ADT7482, datasheet */
case 0x75: /* ADT7482, real chip */
if (man_id2 == 0x41 && chip_id2 == 0x82 &&
address == 0x4c && !(config1 & 0x10) && !(config2 & 0x7f) &&
convrate <= 0x0a)
name = "adt7482";
break;
case 0x94: /* ADT7483 */
if (man_id2 == 0x41 && chip_id2 == 0x83 &&
((address >= 0x18 && address <= 0x1a) ||
(address >= 0x29 && address <= 0x2b) ||
(address >= 0x4c && address <= 0x4e)) &&
!(config1 & 0x10) && !(config2 & 0x7f) && convrate <= 0x0a)
name = "adt7483a";
break;
default: default:
break; break;
} }
...@@ -1958,9 +2012,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) ...@@ -1958,9 +2012,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
config |= 0x20; config |= 0x20;
/* /*
* Select external channel 0 for max6695/96 * Select external channel 0 for devices with three sensors
*/ */
if (data->kind == max6696) if (data->flags & LM90_HAVE_TEMP3)
config &= ~0x08; config &= ~0x08;
/* /*
...@@ -2119,13 +2173,18 @@ static int lm90_probe(struct i2c_client *client) ...@@ -2119,13 +2173,18 @@ static int lm90_probe(struct i2c_client *client)
data->channel_config[2] = HWMON_T_INPUT | data->channel_config[2] = HWMON_T_INPUT |
HWMON_T_MIN | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_CRIT | HWMON_T_CRIT_HYST |
HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT;
HWMON_T_FAULT; if (data->flags & LM90_HAVE_EMERGENCY) {
data->channel_config[2] |= HWMON_T_EMERGENCY |
HWMON_T_EMERGENCY_HYST;
}
if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
data->channel_config[2] |= HWMON_T_EMERGENCY_ALARM;
} }
data->reg_local_ext = lm90_params[data->kind].reg_local_ext; data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
data->reg_status2 = lm90_params[data->kind].reg_status2;
/* Set maximum conversion rate */ /* Set maximum conversion rate */
data->max_convrate = lm90_params[data->kind].max_convrate; data->max_convrate = lm90_params[data->kind].max_convrate;
......
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