From: Slawomir Stepien <slawomir.stepien@xxxxxxxxx> The ADT7481 is a 3-channel digital thermometer and under/over temperature alarm, intended for use in PCs and thermal management systems. >From driver point-of-view, it is quite similar to MAX6696 so a lot of code for MAX6696 is reused. Signed-off-by: Slawomir Stepien <slawomir.stepien@xxxxxxxxx> --- Documentation/hwmon/lm90.rst | 12 +++- drivers/hwmon/Kconfig | 15 +++-- drivers/hwmon/lm90.c | 114 ++++++++++++++++++++++++++--------- 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 05391fb4042d..2e67d602e275 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -73,6 +73,16 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A + * Analog Devices ADT7481 + + Prefix: 'adt7481' + + Addresses scanned: I2C 0x4c and 0x4b + + Datasheet: Publicly available at the ON Semiconductor website + + http://www.onsemi.com/PowerSolutions/product.do?id=ADT7481 + * ON Semiconductor NCT1008 Prefix: 'nct1008' @@ -319,7 +329,7 @@ ADM1032: * ALERT is triggered by open remote sensor. * SMBus PEC support for Write Byte and Receive Byte transactions. -ADT7461, ADT7461A, NCT1008: +ADT7461, ADT7461A, ADT7481, NCT1008: * Extended temperature range (breaks compatibility) * Lower resolution for remote temperature diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f2b038fa3b84..9586b610b5d8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1339,13 +1339,16 @@ config SENSORS_LM90 tristate "National Semiconductor LM90 and compatibles" 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, + If you say yes here you will get support for sensor chips: + National Semiconductor LM90, LM86, LM89 and LM99, + Analog Devices ADM1032, ADT7461, ADT7461A and ADT7481, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, - MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, - ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG, - Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 - sensor chips. + MAX6659, MAX6680, MAX6681, MAX6692, MAX6695 and MAX6696, + ON Semiconductor NCT1008, + Winbond/Nuvoton W83L771W/G/AWG/ASG, + Philips SA56004, + GMT G781, + Texas Instruments TMP451 and TMP461. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1c9493c70813..00fd5734f217 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -59,10 +59,10 @@ * chips, but support three temperature sensors instead of two. MAX6695 * and MAX6696 only differ in the pinout so they can be treated identically. * - * This driver also supports ADT7461 and ADT7461A from Analog Devices as well as - * NCT1008 from ON Semiconductor. The chips are supported in both compatibility - * and extended mode. They are mostly compatible with LM90 except for a data - * format difference for the temperature value registers. + * This driver also supports ADT7461, ADT7461A and ADT7481 from Analog Devices + * as well as NCT1008 from ON Semiconductor. The chips are supported in both + * compatibility and extended mode. They are mostly compatible with LM90 except + * for a data format difference for the temperature value registers. * * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. @@ -106,14 +106,16 @@ * MAX6654, MAX6680, and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, * 0x2a, 0x2b, 0x4c, 0x4d or 0x4e. * SA56004 can have address 0x48 through 0x4F. + * ADT7481 can have address 0x4c or 0x4b (ADT7481-1). */ 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 { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004, g781, tmp451, tmp461, max6654 }; +enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, adt7481, + max6680, max6646, w83l771, max6696, sa56004, g781, tmp451, tmp461, + max6654 }; /* * The LM90 registers @@ -141,6 +143,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_REG_W_REMOTE_OFFSH 0x11 #define LM90_REG_R_REMOTE_OFFSL 0x12 #define LM90_REG_W_REMOTE_OFFSL 0x12 +#define LM90_REG_R_REMOTE2_OFFSH 0x34 +#define LM90_REG_W_REMOTE2_OFFSH 0x34 +#define LM90_REG_R_REMOTE2_OFFSL 0x35 +#define LM90_REG_W_REMOTE2_OFFSL 0x35 #define LM90_REG_R_REMOTE_HIGHH 0x07 #define LM90_REG_W_REMOTE_HIGHH 0x0D #define LM90_REG_R_REMOTE_HIGHL 0x13 @@ -154,6 +160,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 +/* ADT7481 registers */ + +#define ADT7481_REG_R_STATUS2 0x23 + /* MAX6646/6647/6649/6654/6657/6658/6659/6695/6696 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 @@ -202,6 +212,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_STATUS_LHIGH (1 << 6) /* local high temp limit tripped */ #define LM90_STATUS_BUSY (1 << 7) /* conversion is ongoing */ +/* Note: bits 1, 2, 3 and 4 are used by ADT7481 too */ #define MAX6696_STATUS2_R2THRM (1 << 1) /* remote2 THERM limit tripped */ #define MAX6696_STATUS2_R2OPEN (1 << 2) /* remote2 is an open circuit */ #define MAX6696_STATUS2_R2LOW (1 << 3) /* remote2 low temp limit tripped */ @@ -218,6 +229,7 @@ static const struct i2c_device_id lm90_id[] = { { "adm1032", adm1032 }, { "adt7461", adt7461 }, { "adt7461a", adt7461 }, + { "adt7481", adt7481 }, { "g781", g781 }, { "lm90", lm90 }, { "lm86", lm86 }, @@ -256,6 +268,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .compatible = "adi,adt7461a", .data = (void *)adt7461 }, + { + .compatible = "adi,adt7481", + .data = (void *)adt7481 + }, { .compatible = "gmt,g781", .data = (void *)g781 @@ -353,6 +369,7 @@ struct lm90_params { /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate register value */ u8 reg_local_ext; /* Extended local temp register (optional) */ + u8 reg_status2; /* Second status register (optional) */ }; static const struct lm90_params lm90_params[] = { @@ -369,6 +386,14 @@ 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_CRIT | LM90_HAVE_TEMP3, + .alert_alarms = 0x1c7c, + .max_convrate = 11, + .reg_status2 = ADT7481_REG_R_STATUS2, + }, [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT, @@ -429,6 +454,7 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_status2 = MAX6696_REG_R_STATUS2, }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT, @@ -480,10 +506,11 @@ enum lm90_temp11_reg_index { REMOTE_LOW, REMOTE_HIGH, REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */ + REMOTE2_OFFSET, /* for dev that can set offset for the 2nd channel */ LOCAL_TEMP, - REMOTE2_TEMP, /* max6695/96 only */ - REMOTE2_LOW, /* max6695/96 only */ - REMOTE2_HIGH, /* max6695/96 only */ + REMOTE2_TEMP, /* max6695/96 and adt7481 only */ + REMOTE2_LOW, /* max6695/96 and adt7481 only */ + REMOTE2_HIGH, /* max6695/96 and adt7481 only */ TEMP11_REG_NUM }; @@ -512,7 +539,8 @@ 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_local_ext; /* local extension register offset */ + u8 reg_local_ext; /* Local extension register offset */ + u8 reg_status2; /* Second status register */ /* registers values */ s8 temp8[TEMP8_REG_NUM]; @@ -617,7 +645,7 @@ static int lm90_select_remote_channel(struct lm90_data *data, int channel) { int err = 0; - if (data->kind == max6696) { + if (data->kind == max6696 || data->kind == adt7481) { u8 config = data->config & ~0x08; if (channel) @@ -726,6 +754,14 @@ static int lm90_update_limits(struct device *dev) if (val < 0) return val; data->temp11[REMOTE_OFFSET] = val; + + if (data->flags & LM90_HAVE_TEMP3) { + val = lm90_read16(client, LM90_REG_R_REMOTE2_OFFSH, + LM90_REG_R_REMOTE2_OFFSL); + if (val < 0) + return val; + data->temp11[REMOTE2_OFFSET] = val; + } } if (data->flags & LM90_HAVE_EMERGENCY) { @@ -740,7 +776,7 @@ static int lm90_update_limits(struct device *dev) data->temp8[REMOTE_EMERG] = val; } - if (data->kind == max6696) { + if (data->kind == max6696 || data->kind == adt7481) { val = lm90_select_remote_channel(data, 1); if (val < 0) return val; @@ -750,10 +786,12 @@ static int lm90_update_limits(struct device *dev) return val; data->temp8[REMOTE2_CRIT] = val; - val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG); - if (val < 0) - return val; - data->temp8[REMOTE2_EMERG] = val; + if (data->kind == max6696) { + val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG); + if (val < 0) + return val; + data->temp8[REMOTE2_EMERG] = val; + } val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH); if (val < 0) @@ -824,7 +862,7 @@ static int lm90_update_device(struct device *dev) return val; data->alarms = val & ~LM90_STATUS_BUSY; - if (data->kind == max6696) { + if (data->kind == max6696 || data->kind == adt7481) { val = lm90_select_remote_channel(data, 1); if (val < 0) return val; @@ -838,8 +876,10 @@ static int lm90_update_device(struct device *dev) data->temp11[REMOTE2_TEMP] = val; lm90_select_remote_channel(data, 0); + } - val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); + if (data->reg_status2) { + val = lm90_read_reg(client, data->reg_status2); if (val < 0) return val; data->alarms |= val << 8; @@ -1051,6 +1091,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) [REMOTE_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, [REMOTE_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, [REMOTE_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL }, + [REMOTE2_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL }, [REMOTE2_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL } }; @@ -1074,7 +1115,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) else data->temp11[index] = temp_to_s8(val) << 8; - lm90_select_remote_channel(data, index >= 3); + lm90_select_remote_channel(data, index >= REMOTE2_OFFSET); err = i2c_smbus_write_byte_data(client, regp->high, data->temp11[index] >> 8); if (err < 0) @@ -1271,7 +1312,10 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]); break; case hwmon_temp_offset: - *val = lm90_get_temp11(data, REMOTE_OFFSET); + if (channel == 1) + *val = lm90_get_temp11(data, REMOTE_OFFSET); + else + *val = lm90_get_temp11(data, REMOTE2_OFFSET); break; default: return -EOPNOTSUPP; @@ -1321,7 +1365,10 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val); break; case hwmon_temp_offset: - err = lm90_set_temp11(data, REMOTE_OFFSET, val); + if (channel == 1) + err = lm90_set_temp11(data, REMOTE_OFFSET, val); + else + err = lm90_set_temp11(data, REMOTE2_OFFSET, val); break; default: err = -EOPNOTSUPP; @@ -1513,7 +1560,7 @@ static int lm90_detect(struct i2c_client *client, } } } else - if ((address == 0x4C || address == 0x4D) + if ((address == 0x4C || address == 0x4D || address == 0x4B) && man_id == 0x41) { /* Analog Devices */ if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ && (config1 & 0x3F) == 0x00 @@ -1536,6 +1583,11 @@ static int lm90_detect(struct i2c_client *client, && (config1 & 0x1B) == 0x00 && convrate <= 0x0A) { name = "adt7461a"; + } else + if (chip_id == 0x62 /* ADT7481 (undocumented in datasheet) */ + && (config1 & 0x1B) == 0x00 + && convrate <= 0x0B) { + name = "adt7481"; } } else if (man_id == 0x4D) { /* Maxim */ @@ -1749,9 +1801,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 max6695/96 or adt7481 */ - if (data->kind == max6696) + if (data->kind == max6696 || data->kind == adt7481) config &= ~0x08; /* @@ -1776,11 +1828,11 @@ static bool lm90_is_tripped(struct i2c_client *client, u16 *status) if (st < 0) return false; - if (data->kind == max6696) { - st2 = lm90_read_reg(client, MAX6696_REG_R_STATUS2); - if (st2 < 0) - return false; - } + if (data->reg_status2) + st2 = lm90_read_reg(client, data->reg_status2); + + if (st2 < 0) + return false; *status = st | (st2 << 8); @@ -1951,9 +2003,13 @@ static int lm90_probe(struct i2c_client *client) HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM | HWMON_T_FAULT; + + if (data->flags & LM90_HAVE_OFFSET) + data->channel_config[2] |= HWMON_T_OFFSET; } 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; -- 2.36.1