summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2021-11-06 19:59:26 +0300
committerGuenter Roeck <linux@roeck-us.net>2022-07-13 18:38:18 +0300
commita9f3d3a80a405249e18f9bf85271e4cee5c4160f (patch)
tree1ae9afe6b96a8fa5921dcbaacc3414e7c6de05e6
parent904a6fe689e28abff09c6b039a3dacec5a586c91 (diff)
downloadlinux-a9f3d3a80a405249e18f9bf85271e4cee5c4160f.tar.xz
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: Slawomir Stepien <sst@poczta.fm> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r--Documentation/hwmon/lm90.rst38
-rw-r--r--drivers/hwmon/Kconfig3
-rw-r--r--drivers/hwmon/lm90.c119
3 files changed, 129 insertions, 31 deletions
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index e947e609990b..53429f79b819 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -73,6 +73,36 @@ Supported chips:
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
Prefix: 'nct1008'
@@ -323,6 +353,14 @@ ADT7461, ADT7461A, NCT1008:
* Extended temperature range (breaks compatibility)
* Lower resolution for remote temperature
* 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:
* Better local resolution
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 590d3d550acb..df54628bd36b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1358,7 +1358,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, ADT7461, and ADT7461A,
+ LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A,
+ ADT7481, ADT7482, and ADT7483A,
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658,
MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index fded11b0003e..a915d0356d57 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -64,6 +64,10 @@
* and extended mode. They are mostly compatible with LM90 except for a data
* 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
* pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
*
@@ -114,7 +118,7 @@ static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
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,
sa56004, tmp451, tmp461, w83l771,
};
@@ -164,6 +168,13 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define TMP461_REG_CHEN 0x16
#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 */
#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */
#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */
@@ -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_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_R2OPEN BIT(2) /* remote2 is an open circuit */
#define MAX6696_STATUS2_R2LOW BIT(3) /* remote2 low temp limit tripped */
@@ -207,6 +219,9 @@ static const struct i2c_device_id lm90_id[] = {
{ "adm1032", adm1032 },
{ "adt7461", adt7461 },
{ "adt7461a", adt7461a },
+ { "adt7481", adt7481 },
+ { "adt7482", adt7481 },
+ { "adt7483a", adt7481 },
{ "g781", g781 },
{ "lm90", lm90 },
{ "lm86", lm86 },
@@ -344,6 +359,7 @@ struct lm90_params {
/* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate register value */
u8 resolution; /* 16-bit resolution (default 11 bit) */
+ u8 reg_status2; /* 2nd status register (optional) */
u8 reg_local_ext; /* Extended local temp register (optional) */
};
@@ -376,6 +392,16 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7c,
.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] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
@@ -453,6 +479,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_ALARMS,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
+ .reg_status2 = MAX6696_REG_STATUS2,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[w83l771] = {
@@ -549,6 +576,7 @@ struct lm90_data {
u16 alert_alarms; /* Which alarm bits trigger ALERT# */
/* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate */
+ u8 reg_status2; /* 2nd status register (optional) */
u8 reg_local_ext; /* local extension register offset */
/* registers values */
@@ -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
* 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) {
- u8 config = data->config & ~0x08;
+ if (second)
+ config |= 0x08;
- if (channel)
- config |= 0x08;
- err = lm90_update_confreg(data, config);
- }
- return err;
+ return lm90_update_confreg(data, config);
}
static int lm90_write_convrate(struct lm90_data *data, int val)
@@ -806,8 +830,8 @@ static int lm90_update_limits(struct device *dev)
data->temp[REMOTE_EMERG] = val << 8;
}
- if (data->kind == max6696) {
- val = lm90_select_remote_channel(data, 1);
+ if (data->flags & LM90_HAVE_TEMP3) {
+ val = lm90_select_remote_channel(data, true);
if (val < 0)
return val;
@@ -816,10 +840,12 @@ static int lm90_update_limits(struct device *dev)
return val;
data->temp[REMOTE2_CRIT] = val << 8;
- val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
- if (val < 0)
- return val;
- data->temp[REMOTE2_EMERG] = val << 8;
+ if (data->flags & LM90_HAVE_EMERGENCY) {
+ val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
+ if (val < 0)
+ return val;
+ data->temp[REMOTE2_EMERG] = val << 8;
+ }
val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
if (val < 0)
@@ -831,7 +857,7 @@ static int lm90_update_limits(struct device *dev)
return val;
data->temp[REMOTE2_HIGH] = val << 8;
- lm90_select_remote_channel(data, 0);
+ lm90_select_remote_channel(data, false);
}
return 0;
@@ -914,8 +940,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
return val;
alarms = val & ~LM90_STATUS_BUSY;
- if (data->kind == max6696) {
- val = lm90_read_reg(client, MAX6696_REG_STATUS2);
+ if (data->reg_status2) {
+ val = lm90_read_reg(client, data->reg_status2);
if (val < 0)
return val;
alarms |= val << 8;
@@ -1037,20 +1063,20 @@ static int lm90_update_device(struct device *dev)
return val;
data->temp[REMOTE_TEMP] = val;
- if (data->kind == max6696) {
- val = lm90_select_remote_channel(data, 1);
+ if (data->flags & LM90_HAVE_TEMP3) {
+ val = lm90_select_remote_channel(data, true);
if (val < 0)
return val;
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
LM90_REG_REMOTE_TEMPL, true);
if (val < 0) {
- lm90_select_remote_channel(data, 0);
+ lm90_select_remote_channel(data, false);
return 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);
@@ -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));
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);
if (err < 0)
@@ -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);
deselect:
if (channel > 1)
- lm90_select_remote_channel(data, 0);
+ lm90_select_remote_channel(data, false);
return err;
}
@@ -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,
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;
const char *name = NULL;
+ if (config2 < 0 || man_id2 < 0 || chip_id2 < 0)
+ return NULL;
+
switch (chip_id) {
case 0x40 ... 0x4f: /* ADM1032 */
if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
@@ -1592,6 +1624,28 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
convrate <= 0x0a)
name = "adt7461a";
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:
break;
}
@@ -1958,9 +2012,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
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;
/*
@@ -2119,13 +2173,18 @@ static int lm90_probe(struct i2c_client *client)
data->channel_config[2] = HWMON_T_INPUT |
HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_CRIT | HWMON_T_CRIT_HYST |
- HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
- HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM |
- HWMON_T_FAULT;
+ HWMON_T_CRIT_ALARM | 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_status2 = lm90_params[data->kind].reg_status2;
/* Set maximum conversion rate */
data->max_convrate = lm90_params[data->kind].max_convrate;