This patch series implements various improvements to the lm90 driver.
- Generate sysfs and udev events for all alarms
- Add support for LM84, ADM1020, ADM1021, ADT7421, ADT7481, ADT7482,
ADT7483A, NCT210, NCT214, NCT218, NCT72, MAX1617, MAX1617A, MAX6642,
NE1617, NE1617A, NE1618, GL523SM, THMC10, and MC1066
- Explicit support for MAX6648
- Handle differences between ADT7461 and ADT7461A
- Individual drivers for ADM1021 and MAX6642 are now obsolete.
- Improved PEC support. Some chips support PEC only in read operations,
others support PEC for both read and write operations.
- Improved alarm handling
- Streamlined temperature conversion functions
- Reorganized and improved chip detection code
----------------------------------------------------------------
Guenter Roeck (40):
hwmon: (lm90) Generate sysfs and udev events for all alarms
hwmon: (lm90) Rework alarm/status handling
hwmon: (lm90) Reorder include files in alphabetical order
hwmon: (lm90) Reorder chip enumeration to be in alphabetical order
hwmon: (lm90) Use BIT macro
hwmon: (lm90) Move status register bit shifts to compile time
hwmon: (lm90) Stop using R_/W_ register prefix
hwmon: (lm90) Improve PEC support
hwmon: (lm90) Add partial PEC support for ADT7461
hwmon: (lm90) Enable full PEC support for ADT7461A
hwmon: (lm90) Add support for unsigned and signed temperatures
hwmon: (lm90) Only re-read registers if volatile
hwmon: (lm90) Support multiple temperature resolutions
hwmon: (lm90) Use single flag to indicate extended temperature support
hwmon: (lm90) Rework detect function
hwmon: (lm90) Add support for additional chip revision of NCT1008
hwmon: (lm90) Fix/Add detection of G781-1
hwmon: (lm90) Add flag to indicate 'alarms' attribute support
hwmon: (lm90) Add explicit support for MAX6648/MAX6692
hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483
hwmon: (lm90) Strengthen chip detection for ADM1032, ADT7461(A), and NCT1008
hwmon: (lm90) Add support for MAX6690
hwmon: (lm90) Add flag to indicate support for minimum temperature limits
hwmon: (lm90) Add flag to indicate conversion rate support
hwmon: (lm90) Add support for MAX6642
hwmon: (lm90) Let lm90_read16() handle 8-bit read operations
hwmon: (lm90) Introduce 16-bit register write function
hwmon: (lm90) Support MAX1617 and LM84
hwmon: (lm90) Add support for ADM1021, ADM1021A, and ADM1023
hwmon: (lm90) Add remaining chips supported by adm1021 driver
hwmon: (lm90) Combine lm86 and lm90 configuration
hwmon: (lm90) Add explicit support for NCT210
hwmon: (lm90) Add support for ON Semiconductor NCT214 and NCT72
hwmon: (lm90) Add support for ON Semiconductor NCT218
hwmon: (lm90) Add support for ADT7421
hwmon: (lm90) Only disable alerts if not already disabled
hwmon: (lm90) Add explicit support for ADM1020
hwmon: (lm90) Add support and detection of Philips/NXP NE1618
hwmon: (lm90) Add table with supported Analog/ONSEMI devices
hwmon: (lm90) Support temp_samples attribute
Documentation/hwmon/lm90.rst | 233 +++-
drivers/hwmon/Kconfig | 17 +-
drivers/hwmon/lm90.c | 2415 ++++++++++++++++++++++++++++--------------
3 files changed, 1832 insertions(+), 833 deletions(-)
NCT214 and NCT72 are compatible to ADT7461/ADT7461A but have full
PEC (packet error checking) support. PEC support is undocumented.
Both chips support the undocumented secondary chip and manufacturer
ID registers at 0x3e and 0x3f, and return 0x61 as chip ID. Use this
information to improve the accuracy of chip detection code.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 20 ++++++++++++++++++++
drivers/hwmon/Kconfig | 3 ++-
drivers/hwmon/lm90.c | 35 ++++++++++++++++++++++++++++++++++-
3 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index e9a8c11eba8b..d3836d1f1275 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -147,6 +147,26 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=NCT210
+ * ON Semiconductor NCT214
+
+ Prefix: 'nct214'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the ON Semiconductor website
+
+ https://www.onsemi.com/PowerSolutions/product.do?id=NCT214
+
+ * ON Semiconductor NCT72
+
+ Prefix: 'nct72'
+
+ Addresses scanned: I2C 0x4c - 0x4d
+
+ Datasheet: Publicly available at the ON Semiconductor website
+
+ https://www.onsemi.com/PowerSolutions/product.do?id=NCT72
+
* Maxim MAX1617
Prefix: 'max1617'
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d30ea2fea3e2..9353d207f254 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1365,7 +1365,8 @@ config SENSORS_LM90
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
- ON Semiconductor NCT1008, NCT210, Winbond/Nuvoton W83L771W/G/AWG/ASG,
+ ON Semiconductor NCT1008, NCT210, NCT72, NCT214,
+ Winbond/Nuvoton W83L771W/G/AWG/ASG,
Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 4ae8027722e0..2a1630b85967 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -69,6 +69,9 @@
* / ON Semiconductor. The chips are similar to ADT7461 but support two external
* temperature sensors.
*
+ * This driver also supports NCT72 and NCT214 from ON Semiconductor. The chips
+ * are similar to ADT7461/ADT7461A but have full PEC support (undocumented).
+ *
* This driver also supports the SA56004 from Philips. This device is
* pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
*
@@ -125,7 +128,7 @@ static const unsigned short normal_i2c[] = {
enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
g781, lm84, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
- nct210, sa56004, tmp451, tmp461, w83l771,
+ nct210, nct72, sa56004, tmp451, tmp461, w83l771,
};
/*
@@ -258,6 +261,8 @@ static const struct i2c_device_id lm90_id[] = {
{ "mc1066", max1617 },
{ "nct1008", adt7461a },
{ "nct210", nct210 },
+ { "nct214", nct72 },
+ { "nct72", nct72 },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
{ "thmc10", max1617 },
@@ -348,6 +353,14 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
.compatible = "onnn,nct1008",
.data = (void *)adt7461a
},
+ {
+ .compatible = "onnn,nct214",
+ .data = (void *)nct72
+ },
+ {
+ .compatible = "onnn,nct72",
+ .data = (void *)nct72
+ },
{
.compatible = "winbond,w83l771",
.data = (void *)w83l771
@@ -534,6 +547,15 @@ static const struct lm90_params lm90_params[] = {
.reg_status2 = MAX6696_REG_STATUS2,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
+ [nct72] = {
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
+ | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_UNSIGNED_TEMP
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ .alert_alarms = 0x7c,
+ .max_convrate = 10,
+ .resolution = 10,
+ },
[nct210] = {
.flags = LM90_HAVE_ALARMS | LM90_HAVE_BROKEN_ALERT
| LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
@@ -1816,12 +1838,23 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
convrate <= 0x0a)
name = "nct1008";
break;
+ case 0x55: /* NCT72 */
+ if (man_id2 == 0x41 && chip_id2 == 0x61 &&
+ (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ convrate <= 0x0a)
+ name = "nct72";
+ break;
case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */
if (man_id2 == 0x41 && chip_id2 == 0x61 &&
(address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
convrate <= 0x0a)
name = "adt7461a";
break;
+ case 0x5a: /* NCT214 */
+ if (man_id2 == 0x41 && chip_id2 == 0x61 &&
+ common_address && !(config1 & 0x1b) && convrate <= 0x0a)
+ name = "nct214";
+ break;
case 0x62: /* ADT7481, undocumented */
if (man_id2 == 0x41 && chip_id2 == 0x81 &&
(address == 0x4b || address == 0x4c) && !(config1 & 0x10) &&
--
2.35.1
Since temperature conversion functions are now unified, there is no need
to keep "the chip supports a configurable extended temperature range" and
"the chip has extended temperature range enabled" flags separate.
Use a single flag instead to reflect both.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 25 ++++++++++---------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 8e5f791aca07..a0a91aa66177 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -164,11 +164,8 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define TMP461_REG_CHEN 0x16
#define TMP461_REG_DFC 0x24
-/*
- * Device flags
- */
-#define LM90_FLAG_ADT7461_EXT BIT(0) /* ADT7461 extended mode */
/* Device features */
+#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */
#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */
#define LM90_HAVE_UNSIGNED_TEMP BIT(2) /* temperatures are unsigned */
#define LM90_HAVE_REM_LIMIT_EXT BIT(3) /* extended remote limit */
@@ -176,12 +173,11 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm */
#define LM90_HAVE_TEMP3 BIT(6) /* 3rd temperature sensor */
#define LM90_HAVE_BROKEN_ALERT BIT(7) /* Broken alert */
-#define LM90_HAVE_EXTENDED_TEMP BIT(8) /* extended temperature support */
-#define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */
-#define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */
-#define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */
-#define LM90_HAVE_PEC BIT(12) /* Chip supports PEC */
-#define LM90_HAVE_PARTIAL_PEC BIT(13) /* Partial PEC support (adm1032)*/
+#define LM90_PAUSE_FOR_CONFIG BIT(8) /* Pause conversion for config */
+#define LM90_HAVE_CRIT BIT(9) /* Chip supports CRIT/OVERT register */
+#define LM90_HAVE_CRIT_ALRM_SWP BIT(10) /* critical alarm bits swapped */
+#define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */
+#define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -1109,7 +1105,7 @@ static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution)
{
int val;
- if (flags & LM90_FLAG_ADT7461_EXT)
+ if (flags & LM90_HAVE_EXTENDED_TEMP)
val = regval - 0x4000;
else if (flags & LM90_HAVE_UNSIGNED_TEMP)
val = regval;
@@ -1136,7 +1132,7 @@ static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution)
int fraction = resolution > 8 ?
1000 - DIV_ROUND_CLOSEST(1000, BIT(resolution - 8)) : 0;
- if (flags & LM90_FLAG_ADT7461_EXT) {
+ if (flags & LM90_HAVE_EXTENDED_TEMP) {
val = clamp_val(val, -64000, 191000 + fraction);
val += 64000;
} else if (flags & LM90_HAVE_UNSIGNED_TEMP) {
@@ -1764,9 +1760,8 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
if (data->flags & LM90_HAVE_EXTENDED_TEMP) {
if (of_property_read_bool(np, "ti,extended-range-enable"))
config |= 0x04;
-
- if (config & 0x04)
- data->flags |= LM90_FLAG_ADT7461_EXT;
+ if (!(config & 0x04))
+ data->flags &= ~LM90_HAVE_EXTENDED_TEMP;
}
/*
--
2.35.1
The register write address either matches the read address, or it is the
read address plus 6. Simplify the code and resolve the write address
programmatically instead of having two defines for each register.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 234 +++++++++++++++++++++----------------------
1 file changed, 117 insertions(+), 117 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index cc26dd08fbff..995b27a248e6 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -123,58 +123,42 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99,
* The LM90 registers
*/
-#define LM90_REG_R_MAN_ID 0xFE
-#define LM90_REG_R_CHIP_ID 0xFF
-#define LM90_REG_R_CONFIG1 0x03
-#define LM90_REG_W_CONFIG1 0x09
-#define LM90_REG_R_CONFIG2 0xBF
-#define LM90_REG_W_CONFIG2 0xBF
-#define LM90_REG_R_CONVRATE 0x04
-#define LM90_REG_W_CONVRATE 0x0A
-#define LM90_REG_R_STATUS 0x02
-#define LM90_REG_R_LOCAL_TEMP 0x00
-#define LM90_REG_R_LOCAL_HIGH 0x05
-#define LM90_REG_W_LOCAL_HIGH 0x0B
-#define LM90_REG_R_LOCAL_LOW 0x06
-#define LM90_REG_W_LOCAL_LOW 0x0C
-#define LM90_REG_R_LOCAL_CRIT 0x20
-#define LM90_REG_W_LOCAL_CRIT 0x20
-#define LM90_REG_R_REMOTE_TEMPH 0x01
-#define LM90_REG_R_REMOTE_TEMPL 0x10
-#define LM90_REG_R_REMOTE_OFFSH 0x11
-#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_REMOTE_HIGHH 0x07
-#define LM90_REG_W_REMOTE_HIGHH 0x0D
-#define LM90_REG_R_REMOTE_HIGHL 0x13
-#define LM90_REG_W_REMOTE_HIGHL 0x13
-#define LM90_REG_R_REMOTE_LOWH 0x08
-#define LM90_REG_W_REMOTE_LOWH 0x0E
-#define LM90_REG_R_REMOTE_LOWL 0x14
-#define LM90_REG_W_REMOTE_LOWL 0x14
-#define LM90_REG_R_REMOTE_CRIT 0x19
-#define LM90_REG_W_REMOTE_CRIT 0x19
-#define LM90_REG_R_TCRIT_HYST 0x21
-#define LM90_REG_W_TCRIT_HYST 0x21
+#define LM90_REG_MAN_ID 0xFE
+#define LM90_REG_CHIP_ID 0xFF
+#define LM90_REG_CONFIG1 0x03
+#define LM90_REG_CONFIG2 0xBF
+#define LM90_REG_CONVRATE 0x04
+#define LM90_REG_STATUS 0x02
+#define LM90_REG_LOCAL_TEMP 0x00
+#define LM90_REG_LOCAL_HIGH 0x05
+#define LM90_REG_LOCAL_LOW 0x06
+#define LM90_REG_LOCAL_CRIT 0x20
+#define LM90_REG_REMOTE_TEMPH 0x01
+#define LM90_REG_REMOTE_TEMPL 0x10
+#define LM90_REG_REMOTE_OFFSH 0x11
+#define LM90_REG_REMOTE_OFFSL 0x12
+#define LM90_REG_REMOTE_HIGHH 0x07
+#define LM90_REG_REMOTE_HIGHL 0x13
+#define LM90_REG_REMOTE_LOWH 0x08
+#define LM90_REG_REMOTE_LOWL 0x14
+#define LM90_REG_REMOTE_CRIT 0x19
+#define LM90_REG_TCRIT_HYST 0x21
/* MAX6646/6647/6649/6654/6657/6658/6659/6695/6696 registers */
-#define MAX6657_REG_R_LOCAL_TEMPL 0x11
-#define MAX6696_REG_R_STATUS2 0x12
-#define MAX6659_REG_R_REMOTE_EMERG 0x16
-#define MAX6659_REG_W_REMOTE_EMERG 0x16
-#define MAX6659_REG_R_LOCAL_EMERG 0x17
-#define MAX6659_REG_W_LOCAL_EMERG 0x17
+#define MAX6657_REG_LOCAL_TEMPL 0x11
+#define MAX6696_REG_STATUS2 0x12
+#define MAX6659_REG_REMOTE_EMERG 0x16
+#define MAX6659_REG_LOCAL_EMERG 0x17
/* SA56004 registers */
-#define SA56004_REG_R_LOCAL_TEMPL 0x22
+#define SA56004_REG_LOCAL_TEMPL 0x22
#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */
/* TMP451/TMP461 registers */
-#define TMP451_REG_R_LOCAL_TEMPL 0x15
+#define TMP451_REG_LOCAL_TEMPL 0x15
#define TMP451_REG_CONALERT 0x22
#define TMP461_REG_CHEN 0x16
@@ -401,25 +385,25 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT,
.alert_alarms = 0x7c,
.max_convrate = 6,
- .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6654] = {
.flags = LM90_HAVE_BROKEN_ALERT,
.alert_alarms = 0x7c,
.max_convrate = 7,
- .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6657] = {
.flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT,
.alert_alarms = 0x7c,
.max_convrate = 8,
- .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6659] = {
.flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT,
.alert_alarms = 0x7c,
.max_convrate = 8,
- .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6680] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT
@@ -432,7 +416,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
- .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT,
@@ -443,21 +427,21 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT,
.alert_alarms = 0x7b,
.max_convrate = 9,
- .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = SA56004_REG_LOCAL_TEMPL,
},
[tmp451] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT,
.alert_alarms = 0x7c,
.max_convrate = 9,
- .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = TMP451_REG_LOCAL_TEMPL,
},
[tmp461] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT,
.alert_alarms = 0x7c,
.max_convrate = 9,
- .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL,
+ .reg_local_ext = TMP451_REG_LOCAL_TEMPL,
},
};
@@ -565,6 +549,29 @@ static int lm90_read_reg(struct i2c_client *client, u8 reg)
return err;
}
+/*
+ * Return register write address
+ *
+ * The write address for registers 0x03 .. 0x08 is the read address plus 6.
+ * For other registers the write address matches the read address.
+ */
+static u8 lm90_write_reg_addr(u8 reg)
+{
+ if (reg >= LM90_REG_CONFIG1 && reg <= LM90_REG_REMOTE_LOWH)
+ return reg + 6;
+ return reg;
+}
+
+/*
+ * Write into LM90 register.
+ * Convert register address to write address if needed, then execute the
+ * operation.
+ */
+static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val);
+}
+
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl)
{
int oldh, newh, l;
@@ -604,9 +611,7 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config)
if (data->config != config) {
int err;
- err = i2c_smbus_write_byte_data(data->client,
- LM90_REG_W_CONFIG1,
- config);
+ err = lm90_write_reg(data->client, LM90_REG_CONFIG1, config);
if (err)
return err;
data->config = config;
@@ -649,7 +654,7 @@ static int lm90_write_convrate(struct lm90_data *data, int val)
}
/* Set conv rate */
- err = i2c_smbus_write_byte_data(data->client, LM90_REG_W_CONVRATE, val);
+ err = lm90_write_reg(data->client, LM90_REG_CONVRATE, val);
/* Revert change to config */
lm90_update_confreg(data, config);
@@ -689,61 +694,61 @@ static int lm90_update_limits(struct device *dev)
int val;
if (data->flags & LM90_HAVE_CRIT) {
- val = lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT);
+ val = lm90_read_reg(client, LM90_REG_LOCAL_CRIT);
if (val < 0)
return val;
data->temp8[LOCAL_CRIT] = val;
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT);
if (val < 0)
return val;
data->temp8[REMOTE_CRIT] = val;
- val = lm90_read_reg(client, LM90_REG_R_TCRIT_HYST);
+ val = lm90_read_reg(client, LM90_REG_TCRIT_HYST);
if (val < 0)
return val;
data->temp_hyst = val;
}
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
if (val < 0)
return val;
data->temp11[REMOTE_LOW] = val << 8;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_LOWL);
if (val < 0)
return val;
data->temp11[REMOTE_LOW] |= val;
}
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH);
if (val < 0)
return val;
data->temp11[REMOTE_HIGH] = val << 8;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHL);
if (val < 0)
return val;
data->temp11[REMOTE_HIGH] |= val;
}
if (data->flags & LM90_HAVE_OFFSET) {
- val = lm90_read16(client, LM90_REG_R_REMOTE_OFFSH,
- LM90_REG_R_REMOTE_OFFSL);
+ val = lm90_read16(client, LM90_REG_REMOTE_OFFSH,
+ LM90_REG_REMOTE_OFFSL);
if (val < 0)
return val;
data->temp11[REMOTE_OFFSET] = val;
}
if (data->flags & LM90_HAVE_EMERGENCY) {
- val = lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG);
+ val = lm90_read_reg(client, MAX6659_REG_LOCAL_EMERG);
if (val < 0)
return val;
data->temp8[LOCAL_EMERG] = val;
- val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG);
+ val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
if (val < 0)
return val;
data->temp8[REMOTE_EMERG] = val;
@@ -754,22 +759,22 @@ static int lm90_update_limits(struct device *dev)
if (val < 0)
return val;
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT);
if (val < 0)
return val;
data->temp8[REMOTE2_CRIT] = val;
- val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG);
+ val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
if (val < 0)
return val;
data->temp8[REMOTE2_EMERG] = val;
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
if (val < 0)
return val;
data->temp11[REMOTE2_LOW] = val << 8;
- val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH);
+ val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH);
if (val < 0)
return val;
data->temp11[REMOTE2_HIGH] = val << 8;
@@ -852,13 +857,13 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
data->alarms_valid = false;
- val = lm90_read_reg(client, LM90_REG_R_STATUS);
+ val = lm90_read_reg(client, LM90_REG_STATUS);
if (val < 0)
return val;
alarms = val & ~LM90_STATUS_BUSY;
if (data->kind == max6696) {
- val = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
+ val = lm90_read_reg(client, MAX6696_REG_STATUS2);
if (val < 0)
return val;
alarms |= val << 8;
@@ -952,30 +957,30 @@ static int lm90_update_device(struct device *dev)
data->valid = false;
- val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW);
+ val = lm90_read_reg(client, LM90_REG_LOCAL_LOW);
if (val < 0)
return val;
data->temp8[LOCAL_LOW] = val;
- val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH);
+ val = lm90_read_reg(client, LM90_REG_LOCAL_HIGH);
if (val < 0)
return val;
data->temp8[LOCAL_HIGH] = val;
if (data->reg_local_ext) {
- val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
+ val = lm90_read16(client, LM90_REG_LOCAL_TEMP,
data->reg_local_ext);
if (val < 0)
return val;
data->temp11[LOCAL_TEMP] = val;
} else {
- val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP);
+ val = lm90_read_reg(client, LM90_REG_LOCAL_TEMP);
if (val < 0)
return val;
data->temp11[LOCAL_TEMP] = val << 8;
}
- val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
- LM90_REG_R_REMOTE_TEMPL);
+ val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
+ LM90_REG_REMOTE_TEMPL);
if (val < 0)
return val;
data->temp11[REMOTE_TEMP] = val;
@@ -985,8 +990,8 @@ static int lm90_update_device(struct device *dev)
if (val < 0)
return val;
- val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
- LM90_REG_R_REMOTE_TEMPL);
+ val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
+ LM90_REG_REMOTE_TEMPL);
if (val < 0) {
lm90_select_remote_channel(data, 0);
return val;
@@ -1191,11 +1196,11 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val)
u8 high;
u8 low;
} reg[] = {
- [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_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL },
- [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }
+ [REMOTE_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL },
+ [REMOTE_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL },
+ [REMOTE_OFFSET] = { LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL },
+ [REMOTE2_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL },
+ [REMOTE2_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL }
};
struct i2c_client *client = data->client;
struct reg *regp = ®[index];
@@ -1218,13 +1223,12 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val)
data->temp11[index] = temp_to_s8(val) << 8;
lm90_select_remote_channel(data, index >= 3);
- err = i2c_smbus_write_byte_data(client, regp->high,
- data->temp11[index] >> 8);
+ err = lm90_write_reg(client, regp->high, data->temp11[index] >> 8);
if (err < 0)
return err;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT)
- err = i2c_smbus_write_byte_data(client, regp->low,
- data->temp11[index] & 0xff);
+ err = lm90_write_reg(client, regp->low,
+ data->temp11[index] & 0xff);
lm90_select_remote_channel(data, 0);
return err;
@@ -1252,14 +1256,14 @@ static int lm90_get_temp8(struct lm90_data *data, int index)
static int lm90_set_temp8(struct lm90_data *data, int index, long val)
{
static const u8 reg[TEMP8_REG_NUM] = {
- LM90_REG_W_LOCAL_LOW,
- LM90_REG_W_LOCAL_HIGH,
- LM90_REG_W_LOCAL_CRIT,
- LM90_REG_W_REMOTE_CRIT,
- MAX6659_REG_W_LOCAL_EMERG,
- MAX6659_REG_W_REMOTE_EMERG,
- LM90_REG_W_REMOTE_CRIT,
- MAX6659_REG_W_REMOTE_EMERG,
+ LM90_REG_LOCAL_LOW,
+ LM90_REG_LOCAL_HIGH,
+ LM90_REG_LOCAL_CRIT,
+ LM90_REG_REMOTE_CRIT,
+ MAX6659_REG_LOCAL_EMERG,
+ MAX6659_REG_REMOTE_EMERG,
+ LM90_REG_REMOTE_CRIT,
+ MAX6659_REG_REMOTE_EMERG,
};
struct i2c_client *client = data->client;
int err;
@@ -1279,7 +1283,7 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val)
data->temp8[index] = temp_to_s8(val);
lm90_select_remote_channel(data, index >= 6);
- err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]);
+ err = lm90_write_reg(client, reg[index], data->temp8[index]);
lm90_select_remote_channel(data, 0);
return err;
@@ -1307,7 +1311,6 @@ static int lm90_set_temphyst(struct lm90_data *data, long val)
{
struct i2c_client *client = data->client;
int temp;
- int err;
if (data->flags & LM90_HAVE_EXTENDED_TEMP)
temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
@@ -1320,9 +1323,7 @@ static int lm90_set_temphyst(struct lm90_data *data, long val)
val = clamp_val(val, -128000l, 255000l);
data->temp_hyst = hyst_to_reg(temp - val);
- err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
- data->temp_hyst);
- return err;
+ return lm90_write_reg(client, LM90_REG_TCRIT_HYST, data->temp_hyst);
}
static const u8 lm90_temp_index[3] = {
@@ -1630,15 +1631,15 @@ static int lm90_detect(struct i2c_client *client,
return -ENODEV;
/* detection and identification */
- man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID);
- config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1);
- convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE);
+ man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID);
+ config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1);
+ convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE);
if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0)
return -ENODEV;
if (man_id == 0x01 || man_id == 0x5C || man_id == 0xA1) {
- config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2);
+ config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
if (config2 < 0)
return -ENODEV;
}
@@ -1697,19 +1698,19 @@ static int lm90_detect(struct i2c_client *client,
int emerg, emerg2, status2;
/*
- * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read
- * LM90_REG_R_MAN_ID in between. If MAX6659_REG_R_REMOTE_EMERG
+ * We read MAX6659_REG_REMOTE_EMERG twice, and re-read
+ * LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG
* exists, both readings will reflect the same value. Otherwise,
* the readings will be different.
*/
emerg = i2c_smbus_read_byte_data(client,
- MAX6659_REG_R_REMOTE_EMERG);
+ MAX6659_REG_REMOTE_EMERG);
man_id = i2c_smbus_read_byte_data(client,
- LM90_REG_R_MAN_ID);
+ LM90_REG_MAN_ID);
emerg2 = i2c_smbus_read_byte_data(client,
- MAX6659_REG_R_REMOTE_EMERG);
+ MAX6659_REG_REMOTE_EMERG);
status2 = i2c_smbus_read_byte_data(client,
- MAX6696_REG_R_STATUS2);
+ MAX6696_REG_STATUS2);
if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0)
return -ENODEV;
@@ -1820,7 +1821,7 @@ static int lm90_detect(struct i2c_client *client,
int local_ext, conalert, chen, dfc;
local_ext = i2c_smbus_read_byte_data(client,
- TMP451_REG_R_LOCAL_TEMPL);
+ TMP451_REG_LOCAL_TEMPL);
conalert = i2c_smbus_read_byte_data(client,
TMP451_REG_CONALERT);
chen = i2c_smbus_read_byte_data(client, TMP461_REG_CHEN);
@@ -1858,8 +1859,7 @@ static void lm90_restore_conf(void *_data)
/* Restore initial configuration */
lm90_write_convrate(data, data->convrate_orig);
- i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
- data->config_orig);
+ lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig);
}
static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
@@ -1867,7 +1867,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
struct device_node *np = client->dev.of_node;
int config, convrate;
- convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE);
+ convrate = lm90_read_reg(client, LM90_REG_CONVRATE);
if (convrate < 0)
return convrate;
data->convrate_orig = convrate;
@@ -1875,7 +1875,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
/*
* Start the conversions.
*/
- config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
+ config = lm90_read_reg(client, LM90_REG_CONFIG1);
if (config < 0)
return config;
data->config_orig = config;
--
2.35.1
NCT218 is compatible to NCT72 and NCT214. It also supports PEC (packet
error checking). Similar to NCT72 and NCT214, PEC support is undocumented.
Unlike NCT214 and NCT72, NCT218 does not support the undocumented secondary
chip and manufacturer ID registers at 0x3e and 0x3f and returns 0x00 when
reading those registers. The value for the chip revision register is not
documented but was observed to be 0xca. Use that information to improve
chip detection accuracy.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 10 ++++++++++
drivers/hwmon/Kconfig | 2 +-
drivers/hwmon/lm90.c | 31 +++++++++++++++++++++++++++++--
3 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index d3836d1f1275..c3ce54f61f44 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -157,6 +157,16 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=NCT214
+ * ON Semiconductor NCT218
+
+ Prefix: 'nct218'
+
+ Addresses scanned: I2C 0x4c - 0x4d
+
+ Datasheet: Publicly available at the ON Semiconductor website
+
+ https://www.onsemi.com/PowerSolutions/product.do?id=NCT218
+
* ON Semiconductor NCT72
Prefix: 'nct72'
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9353d207f254..32c605eaec7e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1365,7 +1365,7 @@ config SENSORS_LM90
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
- ON Semiconductor NCT1008, NCT210, NCT72, NCT214,
+ ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218,
Winbond/Nuvoton W83L771W/G/AWG/ASG,
Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 2a1630b85967..4194b8838f20 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -69,8 +69,9 @@
* / ON Semiconductor. The chips are similar to ADT7461 but support two external
* temperature sensors.
*
- * This driver also supports NCT72 and NCT214 from ON Semiconductor. The chips
- * are similar to ADT7461/ADT7461A but have full PEC support (undocumented).
+ * This driver also supports NCT72, NCT214, and NCT218 from ON Semiconductor.
+ * The chips are similar to ADT7461/ADT7461A but have full PEC support
+ * (undocumented).
*
* This driver also supports the SA56004 from Philips. This device is
* pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
@@ -262,6 +263,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "nct1008", adt7461a },
{ "nct210", nct210 },
{ "nct214", nct72 },
+ { "nct218", nct72 },
{ "nct72", nct72 },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
@@ -357,6 +359,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
.compatible = "onnn,nct214",
.data = (void *)nct72
},
+ {
+ .compatible = "onnn,nct218",
+ .data = (void *)nct72
+ },
{
.compatible = "onnn,nct72",
.data = (void *)nct72
@@ -1778,6 +1784,24 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id,
return name;
}
+static const char *lm90_detect_on(struct i2c_client *client, int chip_id, int config1,
+ int convrate)
+{
+ int address = client->addr;
+ const char *name = NULL;
+
+ switch (chip_id) {
+ case 0xca: /* NCT218 */
+ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ convrate <= 0x0a)
+ name = "nct218";
+ break;
+ default:
+ break;
+ }
+ return name;
+}
+
static const char *lm90_detect_analog(struct i2c_client *client, bool common_address,
int chip_id, int config1, int convrate)
{
@@ -2263,6 +2287,9 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
case 0x01: /* National Semiconductor */
name = lm90_detect_national(client, chip_id, config1, convrate);
break;
+ case 0x1a: /* ON */
+ name = lm90_detect_on(client, chip_id, config1, convrate);
+ break;
case 0x23: /* Genesys Logic */
if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "gl523sm";
--
2.35.1
Unlike MAX6646/MAX6647/MAX6649, MAX6648 and MAX6692 only support
a temperature range of 0..127 degrees C. Separate support for the
two sets of chips to be able to support maximum temperature ranges
correctly for all chips. Introduce new feature flag to indicate
temperature support up to 255 degrees C.
Since the chips are almost identical except for the supported temperature
range, automatic chip detection is limited. Effectively this means that
MAX6648 may be mis-detected as MAX6649 when auto-detected, but there is
nothing we can do about that.
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.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 12 +++++++++--
drivers/hwmon/lm90.c | 41 ++++++++++++++++++++++++++++++++----
2 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 9886a298797f..e947e609990b 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -105,7 +105,7 @@ Supported chips:
* Maxim MAX6648
- Prefix: 'max6646'
+ Prefix: 'max6648'
Addresses scanned: I2C 0x4c
@@ -191,7 +191,7 @@ Supported chips:
* Maxim MAX6692
- Prefix: 'max6646'
+ Prefix: 'max6648'
Addresses scanned: I2C 0x4c
@@ -324,6 +324,14 @@ ADT7461, ADT7461A, NCT1008:
* Lower resolution for remote temperature
* SMBus PEC support for Write Byte and Receive Byte transactions.
+MAX6646, MAX6647, MAX6649:
+ * Better local resolution
+ * Extended range unsigned external temperature
+
+MAX6648, MAX6692:
+ * Better local resolution
+ * Unsigned temperature
+
MAX6654:
* Better local resolution
* Selectable address
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index e23dcf299b03..df4b861024e3 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -115,7 +115,7 @@ static const unsigned short normal_i2c[] = {
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
- max6646, max6654, max6657, max6659, max6680, max6696,
+ max6646, max6648, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771,
};
@@ -179,6 +179,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */
#define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/
#define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */
+#define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -213,6 +214,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "lm99", lm99 },
{ "max6646", max6646 },
{ "max6647", max6646 },
+ { "max6648", max6648 },
{ "max6649", max6646 },
{ "max6654", max6654 },
{ "max6657", max6657 },
@@ -220,6 +222,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6659", max6659 },
{ "max6680", max6680 },
{ "max6681", max6680 },
+ { "max6692", max6648 },
{ "max6695", max6696 },
{ "max6696", max6696 },
{ "nct1008", adt7461a },
@@ -400,7 +403,14 @@ static const struct lm90_params lm90_params[] = {
},
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS,
+ | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS,
+ .alert_alarms = 0x7c,
+ .max_convrate = 6,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
+ },
+ [max6648] = {
+ .flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT
+ | LM90_HAVE_BROKEN_ALERT,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -1119,7 +1129,7 @@ static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution)
if (flags & LM90_HAVE_EXTENDED_TEMP)
val = regval - 0x4000;
- else if (flags & LM90_HAVE_UNSIGNED_TEMP)
+ else if (flags & (LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_EXT_UNSIGNED))
val = regval;
else
val = (s16)regval;
@@ -1147,6 +1157,8 @@ static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution)
if (flags & LM90_HAVE_EXTENDED_TEMP) {
val = clamp_val(val, -64000, 191000 + fraction);
val += 64000;
+ } else if (flags & LM90_HAVE_EXT_UNSIGNED) {
+ val = clamp_val(val, 0, 255000 + fraction);
} else if (flags & LM90_HAVE_UNSIGNED_TEMP) {
val = clamp_val(val, 0, 127000 + fraction);
} else {
@@ -1673,11 +1685,32 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
* The chip_id register of the MAX6646/6647/6649 holds the
* revision of the chip. The lowest 6 bits of the config1
* register are unused and should return zero when read.
+ * The I2C address of MAX6648/6692 is fixed at 0x4c.
+ * MAX6646 is at address 0x4d, MAX6647 is at address 0x4e,
+ * and MAX6649 is at address 0x4c. A slight difference between
+ * the two sets of chips is that the remote temperature register
+ * reports different values if the DXP pin is open or shorted.
+ * We can use that information to help distinguish between the
+ * chips. MAX6648 will be mis-detected as MAX6649 if the remote
+ * diode is connected, but there isn't really anything we can
+ * do about that.
*/
if (!(config1 & 0x3f) && convrate <= 0x07) {
+ int temp;
+
switch (address) {
case 0x4c:
- name = "max6649";
+ /*
+ * MAX6649 reports an external temperature
+ * value of 0xff if DXP is open or shorted.
+ * MAX6648 reports 0x80 in that case.
+ */
+ temp = i2c_smbus_read_byte_data(client,
+ LM90_REG_REMOTE_TEMPH);
+ if (temp == 0x80)
+ name = "max6648";
+ else
+ name = "max6649";
break;
case 0x4d:
name = "max6646";
--
2.35.1
Simplify the code a bit by handling single-register read operations
in lm90_read16(). All we need to do is to skip the low-byte read
operation if the register address is 0.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 47 ++++++++++++++++----------------------------
1 file changed, 17 insertions(+), 30 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 78eda648d960..20208f127508 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -673,6 +673,10 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl,
oldh = lm90_read_reg(client, regh);
if (oldh < 0)
return oldh;
+
+ if (!regl)
+ return oldh << 8;
+
l = lm90_read_reg(client, regl);
if (l < 0)
return l;
@@ -804,29 +808,19 @@ static int lm90_update_limits(struct device *dev)
data->temp_hyst = val;
}
- val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
+ val = lm90_read16(client, LM90_REG_REMOTE_LOWH,
+ (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_LOWL : 0,
+ false);
if (val < 0)
return val;
- data->temp[REMOTE_LOW] = val << 8;
+ data->temp[REMOTE_LOW] = val;
- if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
- val = lm90_read_reg(client, LM90_REG_REMOTE_LOWL);
- if (val < 0)
- return val;
- data->temp[REMOTE_LOW] |= val;
- }
-
- val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH);
+ val = lm90_read16(client, LM90_REG_REMOTE_HIGHH,
+ (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_HIGHL : 0,
+ false);
if (val < 0)
return val;
- data->temp[REMOTE_HIGH] = val << 8;
-
- if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
- val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHL);
- if (val < 0)
- return val;
- data->temp[REMOTE_HIGH] |= val;
- }
+ data->temp[REMOTE_HIGH] = val;
if (data->flags & LM90_HAVE_OFFSET) {
val = lm90_read16(client, LM90_REG_REMOTE_OFFSH,
@@ -1063,18 +1057,11 @@ static int lm90_update_device(struct device *dev)
return val;
data->temp[LOCAL_HIGH] = val << 8;
- if (data->reg_local_ext) {
- val = lm90_read16(client, LM90_REG_LOCAL_TEMP,
- data->reg_local_ext, true);
- if (val < 0)
- return val;
- data->temp[LOCAL_TEMP] = val;
- } else {
- val = lm90_read_reg(client, LM90_REG_LOCAL_TEMP);
- if (val < 0)
- return val;
- data->temp[LOCAL_TEMP] = val << 8;
- }
+ val = lm90_read16(client, LM90_REG_LOCAL_TEMP,
+ data->reg_local_ext, true);
+ if (val < 0)
+ return val;
+ data->temp[LOCAL_TEMP] = val;
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
LM90_REG_REMOTE_TEMPL, true);
if (val < 0)
--
2.35.1
All chips supported by the ADM1021 driver are also supported by the LM90
driver. Make that support official.
After this change, the adm1021 driver is only needed if the lm90 driver
is disabled. Also, the adm1021 driver misdetects a variety of chips as
MAX1617A, which is unwanted if any of those chips is in the system.
For this reason. make the adm1021 driver dependent on !SENSORS_LM90 to
show that it is not needed if the lm90 driver is enabled, and to avoid
misdetection if a chip supported by the lm90 driver is in the system.
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.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 34 +++++++++++++++++++++++++++++++++-
drivers/hwmon/Kconfig | 1 +
drivers/hwmon/lm90.c | 26 ++++++++++++++++++++++++++
3 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 699ac963722b..dfbdfe11606e 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -355,6 +355,38 @@ Supported chips:
https://www.ti.com/lit/gpn/tmp461
+ * Philips NE1617, NE1617A
+
+ Prefix: 'max1617' (probably detected as a max1617)
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheets: Publicly available at the Philips website
+
+ * Genesys Logic GL523SM
+
+ Prefix: 'gl523sm'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet:
+
+ * TI THMC10
+
+ Prefix: 'thmc10'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the TI website
+
+ * Onsemi MC1066
+
+ Prefix: 'mc1066'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the Onsemi website
+
Author: Jean Delvare <[email protected]>
@@ -385,7 +417,7 @@ features:
LM84:
* 8 bit sensor resolution
-ADM1021, MAX1617:
+ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
* 8 bit sensor resolution
* Low temperature limits
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 50fa255b1e3c..bb952287fcee 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -100,6 +100,7 @@ config SENSORS_AD7418
config SENSORS_ADM1021
tristate "Analog Devices ADM1021 and compatibles"
depends on I2C
+ depends on SENSORS_LM90=n
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 40c012bc6ca6..97453a7de1fa 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -233,6 +233,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "adt7482", adt7481 },
{ "adt7483a", adt7481 },
{ "g781", g781 },
+ { "gl523sm", max1617 },
{ "lm84", lm84 },
{ "lm86", lm86 },
{ "lm89", lm86 },
@@ -254,9 +255,11 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6692", max6648 },
{ "max6695", max6696 },
{ "max6696", max6696 },
+ { "mc1066", max1617 },
{ "nct1008", adt7461a },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
+ { "thmc10", max1617 },
{ "tmp451", tmp451 },
{ "tmp461", tmp461 },
{ }
@@ -2125,6 +2128,18 @@ static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id,
return NULL;
}
+static const char *lm90_detect_ti49(struct i2c_client *client, bool common_address,
+ int chip_id, int config1, int convrate)
+{
+ if (common_address && chip_id == 0x00 && !(config1 & 0x3f) && !(convrate & 0xf8)) {
+ /* THMC10: Unsupported registers return 0xff */
+ if (i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL) == 0xff &&
+ i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_CRIT) == 0xff)
+ return "thmc10";
+ }
+ return NULL;
+}
+
static const char *lm90_detect_ti(struct i2c_client *client, int chip_id,
int config1, int convrate)
{
@@ -2208,6 +2223,10 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
case 0x01: /* National Semiconductor */
name = lm90_detect_national(client, chip_id, config1, convrate);
break;
+ case 0x23: /* Genesys Logic */
+ if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8))
+ name = "gl523sm";
+ break;
case 0x41: /* Analog Devices */
name = lm90_detect_analog(client, common_address, chip_id, config1,
convrate);
@@ -2215,10 +2234,17 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
case 0x47: /* GMT */
name = lm90_detect_gmt(client, chip_id, config1, convrate);
break;
+ case 0x49: /* TI */
+ name = lm90_detect_ti49(client, common_address, chip_id, config1, convrate);
+ break;
case 0x4d: /* Maxim Integrated */
name = lm90_detect_maxim(client, common_address, chip_id,
config1, convrate);
break;
+ case 0x54: /* ON MC1066, Microchip TC1068, TCM1617 (originally TelCom) */
+ if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8))
+ name = "mc1066";
+ break;
case 0x55: /* TI */
name = lm90_detect_ti(client, chip_id, config1, convrate);
break;
--
2.35.1
Handling bit shifts necessary to extract status bits during compile time
reduces code and data size by almost 5% when building for x86_64.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 0f3fadc1631c..cc26dd08fbff 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1345,17 +1345,18 @@ static const u8 lm90_temp_emerg_index[3] = {
LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG
};
-static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 };
-static const u8 lm90_max_alarm_bits[3] = { 6, 4, 12 };
-static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 };
-static const u8 lm90_crit_alarm_bits_swapped[3] = { 1, 0, 9 };
-static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 };
-static const u8 lm90_fault_bits[3] = { 0, 2, 10 };
+static const u16 lm90_min_alarm_bits[3] = { BIT(5), BIT(3), BIT(11) };
+static const u16 lm90_max_alarm_bits[3] = { BIT(6), BIT(4), BIT(12) };
+static const u16 lm90_crit_alarm_bits[3] = { BIT(0), BIT(1), BIT(9) };
+static const u16 lm90_crit_alarm_bits_swapped[3] = { BIT(1), BIT(0), BIT(9) };
+static const u16 lm90_emergency_alarm_bits[3] = { BIT(15), BIT(13), BIT(14) };
+static const u16 lm90_fault_bits[3] = { BIT(0), BIT(2), BIT(10) };
static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
{
struct lm90_data *data = dev_get_drvdata(dev);
- int err, bit;
+ int err;
+ u16 bit;
mutex_lock(&data->update_lock);
err = lm90_update_device(dev);
@@ -1374,22 +1375,22 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
case hwmon_temp_fault:
switch (attr) {
case hwmon_temp_min_alarm:
- bit = BIT(lm90_min_alarm_bits[channel]);
+ bit = lm90_min_alarm_bits[channel];
break;
case hwmon_temp_max_alarm:
- bit = BIT(lm90_max_alarm_bits[channel]);
+ bit = lm90_max_alarm_bits[channel];
break;
case hwmon_temp_crit_alarm:
if (data->flags & LM90_HAVE_CRIT_ALRM_SWP)
- bit = BIT(lm90_crit_alarm_bits_swapped[channel]);
+ bit = lm90_crit_alarm_bits_swapped[channel];
else
- bit = BIT(lm90_crit_alarm_bits[channel]);
+ bit = lm90_crit_alarm_bits[channel];
break;
case hwmon_temp_emergency_alarm:
- bit = BIT(lm90_emergency_alarm_bits[channel]);
+ bit = lm90_emergency_alarm_bits[channel];
break;
case hwmon_temp_fault:
- bit = BIT(lm90_fault_bits[channel]);
+ bit = lm90_fault_bits[channel];
break;
}
*val = !!(data->alarms & bit);
--
2.35.1
Unlike ADM1023 and compatible chips, NCT210 does not support a temperature
offset register. A real chip was found to have a chip revision of 0x3f.
Use it to detect NCT210 explicitly.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 14 ++++++++++++++
drivers/hwmon/Kconfig | 2 +-
drivers/hwmon/lm90.c | 18 ++++++++++++++++--
3 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index dfbdfe11606e..e9a8c11eba8b 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -137,6 +137,16 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
+ * ON Semiconductor NCT210
+
+ Prefix: 'adm1021'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the ON Semiconductor website
+
+ https://www.onsemi.com/PowerSolutions/product.do?id=NCT210
+
* Maxim MAX1617
Prefix: 'max1617'
@@ -421,6 +431,10 @@ ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
* 8 bit sensor resolution
* Low temperature limits
+NCT210:
+ * 11 bit sensor resolution for remote temperature sensor
+ * Low temperature limits
+
ADM1021A, ADM1023:
* Temperature offset register for remote temperature sensor
* 11 bit resolution for remote temperature sensor
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bb952287fcee..d30ea2fea3e2 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1365,7 +1365,7 @@ config SENSORS_LM90
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
- ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
+ ON Semiconductor NCT1008, NCT210, Winbond/Nuvoton W83L771W/G/AWG/ASG,
Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 16481051a530..4ae8027722e0 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -125,7 +125,7 @@ static const unsigned short normal_i2c[] = {
enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
g781, lm84, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
- sa56004, tmp451, tmp461, w83l771,
+ nct210, sa56004, tmp451, tmp461, w83l771,
};
/*
@@ -257,6 +257,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6696", max6696 },
{ "mc1066", max1617 },
{ "nct1008", adt7461a },
+ { "nct210", nct210 },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
{ "thmc10", max1617 },
@@ -533,6 +534,14 @@ static const struct lm90_params lm90_params[] = {
.reg_status2 = MAX6696_REG_STATUS2,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
+ [nct210] = {
+ .flags = LM90_HAVE_ALARMS | LM90_HAVE_BROKEN_ALERT
+ | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
+ .alert_alarms = 0x7c,
+ .resolution = 11,
+ .max_convrate = 7,
+ },
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
@@ -1766,7 +1775,7 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
!(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "adm1021";
break;
- case 0x30 ... 0x3f: /* ADM1021A, ADM1023 */
+ case 0x30 ... 0x3e: /* ADM1021A, ADM1023 */
/*
* ADM1021A and compatible chips will be mis-detected as
* ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both
@@ -1784,6 +1793,11 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
!(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "adm1023";
break;
+ case 0x3f: /* NCT210 */
+ if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address &&
+ !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
+ name = "nct210";
+ break;
case 0x40 ... 0x4f: /* ADM1032 */
if (man_id2 == 0x00 && chip_id2 == 0x00 &&
(address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
--
2.35.1
Revision 0 of the ADT7461 datasheet suggests that the chip supports PEC
(packet error checking). This information is gone in later versions of the
datasheet. Experiments show that PEC support on ADT7461 is similar to PEC
support in ADM1032, ie it is only supported for read operations. Add
support for it to the driver.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 1 +
drivers/hwmon/lm90.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index f107d4a159fa..9886a298797f 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -322,6 +322,7 @@ ADM1032:
ADT7461, ADT7461A, NCT1008:
* Extended temperature range (breaks compatibility)
* Lower resolution for remote temperature
+ * SMBus PEC support for Write Byte and Receive Byte transactions.
MAX6654:
* Better local resolution
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 8ba95ea06f0c..6c79422da420 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -356,7 +356,7 @@ static const struct lm90_params lm90_params[] = {
[adt7461] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
- | LM90_HAVE_CRIT,
+ | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
--
2.35.1
When support for G781 was added, chips with ID 0x01 were found at I2C
addresses 0x4c and 0x4d. The G781 datasheet (version 1.3 from October 2003)
says that the device ID for G781-1 is 0x03, not 0x01. Also, the datasheet
states that the chip at I2C address is G781 and the chip at I2C address
0x4d is G781-1.
A G781-1 at I2C address 0x4d was now found to have a chip ID of 0x03
as suggested by the datasheet. Accept both 0x01 and 0x03 chip IDs at both
addresses to ensure that all variants of G781 are detected properly.
While at it, improve chip detection accuracy by reading two additional
registers and ensuring that only expected bits are set in those registers.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 39 ++++++++++++++++++++++++++++++++++-----
1 file changed, 34 insertions(+), 5 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 17312d173b8a..b39e31ded2cb 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1728,13 +1728,42 @@ static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id,
int config1, int convrate)
{
int address = client->addr;
- const char *name = NULL;
- if ((address == 0x4c || address == 0x4d) && chip_id == 0x01 &&
- !(config1 & 0x3f) && convrate <= 0x08)
- name = "g781";
+ /*
+ * According to the datasheet, G781 is supposed to be at I2C Address
+ * 0x4c and have a chip ID of 0x01. G781-1 is supposed to be at I2C
+ * address 0x4d and have a chip ID of 0x03. However, when support
+ * for G781 was added, chips at 0x4c and 0x4d were found to have a
+ * chip ID of 0x01. A G781-1 at I2C address 0x4d was now found with
+ * chip ID 0x03.
+ * To avoid detection failures, accept chip ID 0x01 and 0x03 at both
+ * addresses.
+ * G784 reports manufacturer ID 0x47 and chip ID 0x01. A public
+ * datasheet is not available. Extensive testing suggests that
+ * the chip appears to be fully compatible with G781.
+ * Available register dumps show that G751 also reports manufacturer
+ * ID 0x47 and chip ID 0x01 even though that chip does not officially
+ * support those registers. This makes chip detection somewhat
+ * vulnerable. To improve detection quality, read the offset low byte
+ * and alert fault queue registers and verify that only expected bits
+ * are set.
+ */
+ if ((chip_id == 0x01 || chip_id == 0x03) &&
+ (address == 0x4c || address == 0x4d) &&
+ !(config1 & 0x3f) && convrate <= 0x08) {
+ int reg;
- return name;
+ reg = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_OFFSL);
+ if (reg < 0 || reg & 0x1f)
+ return NULL;
+ reg = i2c_smbus_read_byte_data(client, TMP451_REG_CONALERT);
+ if (reg < 0 || reg & 0xf1)
+ return NULL;
+
+ return "g781";
+ }
+
+ return NULL;
}
static const char *lm90_detect_ti(struct i2c_client *client, int chip_id,
--
2.35.1
Reorder include files in alphabetical order to reduce the chance of
duplicates and to make it clear where new include files should be
added. Drop the unnecessary include of linux/sysfs.h. Include
linux/device.h instead because that is what is actually used.
No functional change.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 63ada2d0d839..b7f5b743c9f5 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -80,18 +80,18 @@
* concern all supported chipsets, unless mentioned otherwise.
*/
-#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
#include <linux/init.h>
-#include <linux/slab.h>
+#include <linux/interrupt.h>
#include <linux/jiffies.h>
-#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/err.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
-#include <linux/sysfs.h>
-#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <linux/workqueue.h>
/*
--
2.35.1
The detect function is getting larger and larger and difficult to
understand or review. Split it into per-manufacturer detect functions
to improve readability.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 379 +++++++++++++++++++++++++++----------------
1 file changed, 236 insertions(+), 143 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index a0a91aa66177..f676b809c470 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1498,78 +1498,88 @@ static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type,
}
}
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm90_detect(struct i2c_client *client,
- struct i2c_board_info *info)
+/*
+ * Per-manufacturer chip detect functions.
+ * Functions are expected to return a pointer to the chip name or NULL
+ * if detection was not successful.
+ */
+
+static const char *lm90_detect_national(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
{
- struct i2c_adapter *adapter = client->adapter;
+ int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
int address = client->addr;
const char *name = NULL;
- int man_id, chip_id, config1, config2, convrate;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
+ if (config2 < 0)
+ return NULL;
- /* detection and identification */
- man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID);
- config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1);
- convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE);
- if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0)
- return -ENODEV;
+ if ((config1 & 0x2a) || (config2 & 0xf8) || convrate > 0x09)
+ return NULL;
- if (man_id == 0x01 || man_id == 0x5C || man_id == 0xA1) {
- config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
- if (config2 < 0)
- return -ENODEV;
+ if (address != 0x4c && address != 0x4d)
+ return NULL;
+
+ switch (chip_id & 0xf0) {
+ case 0x10: /* LM86 */
+ if (address == 0x4c)
+ name = "lm86";
+ break;
+ case 0x20: /* LM90 */
+ if (address == 0x4c)
+ name = "lm90";
+ break;
+ case 0x30: /* LM89/LM99 */
+ name = "lm99"; /* detect LM89 as LM99 */
+ break;
+ default:
+ break;
}
- if ((address == 0x4C || address == 0x4D)
- && man_id == 0x01) { /* National Semiconductor */
- if ((config1 & 0x2A) == 0x00
- && (config2 & 0xF8) == 0x00
- && convrate <= 0x09) {
- if (address == 0x4C
- && (chip_id & 0xF0) == 0x20) { /* LM90 */
- name = "lm90";
- } else
- if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
- name = "lm99";
- dev_info(&adapter->dev,
- "Assuming LM99 chip at 0x%02x\n",
- address);
- dev_info(&adapter->dev,
- "If it is an LM89, instantiate it "
- "with the new_device sysfs "
- "interface\n");
- } else
- if (address == 0x4C
- && (chip_id & 0xF0) == 0x10) { /* LM86 */
- name = "lm86";
- }
- }
- } else
- if ((address == 0x4C || address == 0x4D)
- && man_id == 0x41) { /* Analog Devices */
- if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
- && (config1 & 0x3F) == 0x00
- && convrate <= 0x0A) {
+ return name;
+}
+
+static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int address = client->addr;
+ const char *name = NULL;
+
+ switch (chip_id) {
+ case 0x40 ... 0x4f: /* ADM1032 */
+ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
+ convrate <= 0x0a)
name = "adm1032";
- } else
- if (chip_id == 0x51 /* ADT7461 */
- && (config1 & 0x1B) == 0x00
- && convrate <= 0x0A) {
+ break;
+ case 0x51: /* ADT7461 */
+ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ convrate <= 0x0a)
name = "adt7461";
- } else
- if (chip_id == 0x57 /* ADT7461A, NCT1008 */
- && (config1 & 0x1B) == 0x00
- && convrate <= 0x0A) {
+ break;
+ case 0x57: /* ADT7461A, NCT1008 */
+ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ convrate <= 0x0a)
name = "adt7461a";
- }
- } else
- if (man_id == 0x4D) { /* Maxim */
- int emerg, emerg2, status2;
+ break;
+ default:
+ break;
+ }
+ return name;
+}
+
+static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int man_id, emerg, emerg2, status2;
+ int address = client->addr;
+ const char *name = NULL;
+
+ if ((address >= 0x48 && address <= 0x4b) || address == 0x4f)
+ return NULL;
+
+ switch (chip_id) {
+ case 0x01:
/*
* We read MAX6659_REG_REMOTE_EMERG twice, and re-read
* LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG
@@ -1585,30 +1595,8 @@ static int lm90_detect(struct i2c_client *client,
status2 = i2c_smbus_read_byte_data(client,
MAX6696_REG_STATUS2);
if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0)
- return -ENODEV;
+ return NULL;
- /*
- * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
- * register. Reading from that address will return the last
- * read value, which in our case is those of the man_id
- * register. Likewise, the config1 register seems to lack a
- * low nibble, so the value will be those of the previous
- * read, so in our case those of the man_id register.
- * MAX6659 has a third set of upper temperature limit registers.
- * Those registers also return values on MAX6657 and MAX6658,
- * thus the only way to detect MAX6659 is by its address.
- * For this reason it will be mis-detected as MAX6657 if its
- * address is 0x4C.
- */
- if (chip_id == man_id
- && (address == 0x4C || address == 0x4D || address == 0x4E)
- && (config1 & 0x1F) == (man_id & 0x0F)
- && convrate <= 0x09) {
- if (address == 0x4C)
- name = "max6657";
- else
- name = "max6659";
- } else
/*
* Even though MAX6695 and MAX6696 do not have a chip ID
* register, reading it returns 0x01. Bit 4 of the config1
@@ -1620,77 +1608,137 @@ static int lm90_detect(struct i2c_client *client,
* limit registers. We can detect those chips by checking if
* one of those registers exists.
*/
- if (chip_id == 0x01
- && (config1 & 0x10) == 0x00
- && (status2 & 0x01) == 0x00
- && emerg == emerg2
- && convrate <= 0x07) {
+ if (!(config1 & 0x10) && !(status2 & 0x01) && emerg == emerg2 &&
+ convrate <= 0x07)
name = "max6696";
- } else
/*
* The chip_id register of the MAX6680 and MAX6681 holds the
* revision of the chip. The lowest bit of the config1 register
* is unused and should return zero when read, so should the
* second to last bit of config1 (software reset).
*/
- if (chip_id == 0x01
- && (config1 & 0x03) == 0x00
- && convrate <= 0x07) {
+ else if (!(config1 & 0x03) && convrate <= 0x07)
name = "max6680";
- } else
- /*
- * The chip_id register of the MAX6646/6647/6649 holds the
- * revision of the chip. The lowest 6 bits of the config1
- * register are unused and should return zero when read.
- */
- if (chip_id == 0x59
- && (config1 & 0x3f) == 0x00
- && convrate <= 0x07) {
- name = "max6646";
- } else
+ break;
+ case 0x08:
/*
* The chip_id of the MAX6654 holds the revision of the chip.
* The lowest 3 bits of the config1 register are unused and
* should return zero when read.
*/
- if (chip_id == 0x08
- && (config1 & 0x07) == 0x00
- && convrate <= 0x07) {
+ if (!(config1 & 0x07) && convrate <= 0x07)
name = "max6654";
+ break;
+ case 0x4d:
+ /*
+ * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
+ * register. Reading from that address will return the last
+ * read value, which in our case is those of the man_id
+ * register, or 0x4d. Likewise, the config1 register seems to
+ * lack a low nibble, so the value will be those of the previous
+ * read, so in our case again those of the man_id register.
+ * MAX6659 has a third set of upper temperature limit registers.
+ * Those registers also return values on MAX6657 and MAX6658,
+ * thus the only way to detect MAX6659 is by its address.
+ * For this reason it will be mis-detected as MAX6657 if its
+ * address is 0x4c.
+ */
+ if ((address == 0x4c || address == 0x4d || address == 0x4e) &&
+ (config1 & 0x1f) == 0x0d && convrate <= 0x09) {
+ if (address == 0x4c)
+ name = "max6657";
+ else
+ name = "max6659";
}
- } else
- if (address == 0x4C
- && man_id == 0x5C) { /* Winbond/Nuvoton */
- if ((config1 & 0x2A) == 0x00
- && (config2 & 0xF8) == 0x00) {
- if (chip_id == 0x01 /* W83L771W/G */
- && convrate <= 0x09) {
- name = "w83l771";
- } else
- if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */
- && convrate <= 0x08) {
- name = "w83l771";
+ break;
+ case 0x59:
+ /*
+ * The chip_id register of the MAX6646/6647/6649 holds the
+ * revision of the chip. The lowest 6 bits of the config1
+ * register are unused and should return zero when read.
+ */
+ if (!(config1 & 0x3f) && convrate <= 0x07) {
+ switch (address) {
+ case 0x4c:
+ name = "max6649";
+ break;
+ case 0x4d:
+ name = "max6646";
+ break;
+ case 0x4e:
+ name = "max6647";
+ break;
+ default:
+ break;
}
}
- } else
- if (address >= 0x48 && address <= 0x4F
- && man_id == 0xA1) { /* NXP Semiconductor/Philips */
- if (chip_id == 0x00
- && (config1 & 0x2A) == 0x00
- && (config2 & 0xFE) == 0x00
- && convrate <= 0x09) {
- name = "sa56004";
+ break;
+ default:
+ break;
+ }
+
+ return name;
+}
+
+static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
+ int address = client->addr;
+ const char *name = NULL;
+
+ if (config2 < 0)
+ return ERR_PTR(-ENODEV);
+
+ if (address == 0x4c && !(config1 & 0x2a) && !(config2 & 0xf8)) {
+ if (chip_id == 0x01 && convrate <= 0x09) {
+ /* W83L771W/G */
+ name = "w83l771";
+ } else if ((chip_id & 0xfe) == 0x10 && convrate <= 0x08) {
+ /* W83L771AWG/ASG */
+ name = "w83l771";
}
- } else
- if ((address == 0x4C || address == 0x4D)
- && man_id == 0x47) { /* GMT */
- if (chip_id == 0x01 /* G781 */
- && (config1 & 0x3F) == 0x00
- && convrate <= 0x08)
- name = "g781";
- } else
- if (man_id == 0x55 && chip_id == 0x00 &&
- (config1 & 0x1B) == 0x00 && convrate <= 0x09) {
+ }
+ return name;
+}
+
+static const char *lm90_detect_nxp(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
+ int address = client->addr;
+ const char *name = NULL;
+
+ if (config2 < 0)
+ return NULL;
+
+ if (address >= 0x48 && address <= 0x4f && chip_id == 0x00 &&
+ !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09)
+ name = "sa56004";
+
+ return name;
+}
+
+static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int address = client->addr;
+ const char *name = NULL;
+
+ if ((address == 0x4c || address == 0x4d) && chip_id == 0x01 &&
+ !(config1 & 0x3f) && convrate <= 0x08)
+ name = "g781";
+
+ return name;
+}
+
+static const char *lm90_detect_ti(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int address = client->addr;
+ const char *name = NULL;
+
+ if (chip_id == 0x00 && !(config1 & 0x1b) && convrate <= 0x09) {
int local_ext, conalert, chen, dfc;
local_ext = i2c_smbus_read_byte_data(client,
@@ -1700,10 +1748,8 @@ static int lm90_detect(struct i2c_client *client,
chen = i2c_smbus_read_byte_data(client, TMP461_REG_CHEN);
dfc = i2c_smbus_read_byte_data(client, TMP461_REG_DFC);
- if ((local_ext & 0x0F) == 0x00 &&
- (conalert & 0xf1) == 0x01 &&
- (chen & 0xfc) == 0x00 &&
- (dfc & 0xfc) == 0x00) {
+ if (!(local_ext & 0x0f) && (conalert & 0xf1) == 0x01 &&
+ (chen & 0xfc) == 0x00 && (dfc & 0xfc) == 0x00) {
if (address == 0x4c && !(chen & 0x03))
name = "tmp451";
else if (address >= 0x48 && address <= 0x4f)
@@ -1711,10 +1757,57 @@ static int lm90_detect(struct i2c_client *client,
}
}
- if (!name) { /* identification failed */
+ return name;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int man_id, chip_id, config1, convrate;
+ const char *name = NULL;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* detection and identification */
+ man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID);
+ config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1);
+ convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE);
+ if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0)
+ return -ENODEV;
+
+ switch (man_id) {
+ case 0x01: /* National Semiconductor */
+ name = lm90_detect_national(client, chip_id, config1, convrate);
+ break;
+ case 0x41: /* Analog Devices */
+ name = lm90_detect_analog(client, chip_id, config1, convrate);
+ break;
+ case 0x47: /* GMT */
+ name = lm90_detect_gmt(client, chip_id, config1, convrate);
+ break;
+ case 0x4d: /* Maxim Integrated */
+ name = lm90_detect_maxim(client, chip_id, config1, convrate);
+ break;
+ case 0x55: /* TI */
+ name = lm90_detect_ti(client, chip_id, config1, convrate);
+ break;
+ case 0x5c: /* Winbond/Nuvoton */
+ name = lm90_detect_nuvoton(client, chip_id, config1, convrate);
+ break;
+ case 0xa1: /* NXP Semiconductor/Philips */
+ name = lm90_detect_nxp(client, chip_id, config1, convrate);
+ break;
+ default:
+ break;
+ }
+
+ if (!name) { /* identification failed */
dev_dbg(&adapter->dev,
- "Unsupported chip at 0x%02x (man_id=0x%02X, "
- "chip_id=0x%02X)\n", address, man_id, chip_id);
+ "Unsupported chip at 0x%02x (man_id=0x%02X, chip_id=0x%02X)\n",
+ client->addr, man_id, chip_id);
return -ENODEV;
}
--
2.35.1
PEC (packet error checking) support for ADM1032 is currently only enabled
if the chip was auto-detected, but not if a chip is instantiated
explicitly. Always enable PEC support by introducing a chip feature flag
indicating partial PEC support. Also, for consistency, disable PEC support
by default to match existing functionality if the chip was not auto-
detected.
At the same time, introduce generic support for PEC with a separate feature
flag. This will be used when support for chips with full PEC functionality
is added.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 6 ++--
drivers/hwmon/lm90.c | 56 +++++++++++++++++++-----------------
2 files changed, 32 insertions(+), 30 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 05391fb4042d..f107d4a159fa 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -423,6 +423,6 @@ two transactions will typically mean twice as much delay waiting for
transaction completion, effectively doubling the register cache refresh time.
I guess reliability comes at a price, but it's quite expensive this time.
-So, as not everyone might enjoy the slowdown, PEC can be disabled through
-sysfs. Just write 0 to the "pec" file and PEC will be disabled. Write 1
-to that file to enable PEC again.
+So, as not everyone might enjoy the slowdown, PEC is disabled by default and
+can be enabled through sysfs. Just write 1 to the "pec" file and PEC will be
+enabled. Write 0 to that file to disable PEC again.
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 995b27a248e6..8ba95ea06f0c 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -179,6 +179,8 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99,
#define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */
#define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */
#define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */
+#define LM90_HAVE_PEC BIT(12) /* Chip supports PEC */
+#define LM90_HAVE_PARTIAL_PEC BIT(13) /* Partial PEC support (adm1032)*/
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -346,7 +348,8 @@ struct lm90_params {
static const struct lm90_params lm90_params[] = {
[adm1032] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT,
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
+ | LM90_HAVE_PARTIAL_PEC,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -519,10 +522,10 @@ struct lm90_data {
*/
/*
- * The ADM1032 supports PEC but not on write byte transactions, so we need
+ * If the chip supports PEC but not on write byte transactions, we need
* to explicitly ask for a transaction without PEC.
*/
-static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value)
+static inline s32 lm90_write_no_pec(struct i2c_client *client, u8 value)
{
return i2c_smbus_xfer(client->adapter, client->addr,
client->flags & ~I2C_CLIENT_PEC,
@@ -531,22 +534,24 @@ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value)
/*
* It is assumed that client->update_lock is held (unless we are in
- * detection or initialization steps). This matters when PEC is enabled,
- * because we don't want the address pointer to change between the write
- * byte and the read byte transactions.
+ * detection or initialization steps). This matters when PEC is enabled
+ * for chips with partial PEC support, because we don't want the address
+ * pointer to change between the write byte and the read byte transactions.
*/
static int lm90_read_reg(struct i2c_client *client, u8 reg)
{
+ struct lm90_data *data = i2c_get_clientdata(client);
+ bool partial_pec = (client->flags & I2C_CLIENT_PEC) &&
+ (data->flags & LM90_HAVE_PARTIAL_PEC);
int err;
- if (client->flags & I2C_CLIENT_PEC) {
- err = adm1032_write_byte(client, reg);
- if (err >= 0)
- err = i2c_smbus_read_byte(client);
- } else
- err = i2c_smbus_read_byte_data(client, reg);
-
- return err;
+ if (partial_pec) {
+ err = lm90_write_no_pec(client, reg);
+ if (err)
+ return err;
+ return i2c_smbus_read_byte(client);
+ }
+ return i2c_smbus_read_byte_data(client, reg);
}
/*
@@ -1135,7 +1140,7 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
return (val + 125) / 250 * 64;
}
-/* pec used for ADM1032 only */
+/* pec used for devices with PEC support */
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
@@ -1675,13 +1680,6 @@ static int lm90_detect(struct i2c_client *client,
&& (config1 & 0x3F) == 0x00
&& convrate <= 0x0A) {
name = "adm1032";
- /*
- * The ADM1032 supports PEC, but only if combined
- * transactions are not used.
- */
- if (i2c_check_functionality(adapter,
- I2C_FUNC_SMBUS_BYTE))
- info->flags |= I2C_CLIENT_PEC;
} else
if (chip_id == 0x51 /* ADT7461 */
&& (config1 & 0x1B) == 0x00
@@ -2005,10 +2003,6 @@ static int lm90_probe(struct i2c_client *client)
data->kind = (enum chips)of_device_get_match_data(&client->dev);
else
data->kind = i2c_match_id(lm90_id, client)->driver_data;
- if (data->kind == adm1032) {
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- client->flags &= ~I2C_CLIENT_PEC;
- }
/*
* Different devices have different alarm bits triggering the
@@ -2019,6 +2013,14 @@ static int lm90_probe(struct i2c_client *client)
/* Set chip capabilities */
data->flags = lm90_params[data->kind].flags;
+ if ((data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) &&
+ !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_PEC))
+ data->flags &= ~(LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC);
+
+ if ((data->flags & LM90_HAVE_PARTIAL_PEC) &&
+ !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+ data->flags &= ~LM90_HAVE_PARTIAL_PEC;
+
data->chip.ops = &lm90_ops;
data->chip.info = data->info;
@@ -2081,7 +2083,7 @@ static int lm90_probe(struct i2c_client *client)
* The 'pec' attribute is attached to the i2c device and thus created
* separately.
*/
- if (client->flags & I2C_CLIENT_PEC) {
+ if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
return err;
--
2.35.1
Use BIT macro instead of shift operation to improve readability.
No functional change.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 55 ++++++++++++++++++++++----------------------
1 file changed, 28 insertions(+), 27 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 6728520a21ca..0f3fadc1631c 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -80,6 +80,7 @@
* concern all supported chipsets, unless mentioned otherwise.
*/
+#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
@@ -182,36 +183,36 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99,
/*
* Device flags
*/
-#define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */
+#define LM90_FLAG_ADT7461_EXT BIT(0) /* ADT7461 extended mode */
/* Device features */
-#define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */
-#define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */
-#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */
-#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */
-#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */
-#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */
-#define LM90_HAVE_EXTENDED_TEMP (1 << 8) /* extended temperature support*/
-#define LM90_PAUSE_FOR_CONFIG (1 << 9) /* Pause conversion for config */
-#define LM90_HAVE_CRIT (1 << 10)/* Chip supports CRIT/OVERT register */
-#define LM90_HAVE_CRIT_ALRM_SWP (1 << 11)/* critical alarm bits swapped */
+#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */
+#define LM90_HAVE_REM_LIMIT_EXT BIT(3) /* extended remote limit */
+#define LM90_HAVE_EMERGENCY BIT(4) /* 3rd upper (emergency) limit */
+#define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm */
+#define LM90_HAVE_TEMP3 BIT(6) /* 3rd temperature sensor */
+#define LM90_HAVE_BROKEN_ALERT BIT(7) /* Broken alert */
+#define LM90_HAVE_EXTENDED_TEMP BIT(8) /* extended temperature support */
+#define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */
+#define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */
+#define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */
/* LM90 status */
-#define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */
-#define LM90_STATUS_RTHRM (1 << 1) /* remote THERM limit tripped */
-#define LM90_STATUS_ROPEN (1 << 2) /* remote is an open circuit */
-#define LM90_STATUS_RLOW (1 << 3) /* remote low temp limit tripped */
-#define LM90_STATUS_RHIGH (1 << 4) /* remote high temp limit tripped */
-#define LM90_STATUS_LLOW (1 << 5) /* local low temp limit tripped */
-#define LM90_STATUS_LHIGH (1 << 6) /* local high temp limit tripped */
-#define LM90_STATUS_BUSY (1 << 7) /* conversion is ongoing */
-
-#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 */
-#define MAX6696_STATUS2_R2HIGH (1 << 4) /* remote2 high temp limit tripped */
-#define MAX6696_STATUS2_ROT2 (1 << 5) /* remote emergency limit tripped */
-#define MAX6696_STATUS2_R2OT2 (1 << 6) /* remote2 emergency limit tripped */
-#define MAX6696_STATUS2_LOT2 (1 << 7) /* local emergency limit tripped */
+#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
+#define LM90_STATUS_RTHRM BIT(1) /* remote THERM limit tripped */
+#define LM90_STATUS_ROPEN BIT(2) /* remote is an open circuit */
+#define LM90_STATUS_RLOW BIT(3) /* remote low temp limit tripped */
+#define LM90_STATUS_RHIGH BIT(4) /* remote high temp limit tripped */
+#define LM90_STATUS_LLOW BIT(5) /* local low temp limit tripped */
+#define LM90_STATUS_LHIGH BIT(6) /* local high temp limit tripped */
+#define LM90_STATUS_BUSY BIT(7) /* conversion is ongoing */
+
+#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 */
+#define MAX6696_STATUS2_R2HIGH BIT(4) /* remote2 high temp limit tripped */
+#define MAX6696_STATUS2_ROT2 BIT(5) /* remote emergency limit tripped */
+#define MAX6696_STATUS2_R2OT2 BIT(6) /* remote2 emergency limit tripped */
+#define MAX6696_STATUS2_LOT2 BIT(7) /* local emergency limit tripped */
/*
* Driver data (common to all clients)
--
2.35.1
Experiments show that ADT7461A and NCT1008 support PEC, even though it is
not documented. Enable support for it in the driver. Since ADT7461 only
supports partial PEC, this means that the configuration for ADT7461A
needs to be separated from ADT7461.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 6c79422da420..42e72702b9a9 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -114,7 +114,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, g781, lm86, lm90, lm99,
+enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
max6646, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771,
};
@@ -207,7 +207,7 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99,
static const struct i2c_device_id lm90_id[] = {
{ "adm1032", adm1032 },
{ "adt7461", adt7461 },
- { "adt7461a", adt7461 },
+ { "adt7461a", adt7461a },
{ "g781", g781 },
{ "lm90", lm90 },
{ "lm86", lm86 },
@@ -224,7 +224,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6681", max6680 },
{ "max6695", max6696 },
{ "max6696", max6696 },
- { "nct1008", adt7461 },
+ { "nct1008", adt7461a },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
{ "tmp451", tmp451 },
@@ -244,7 +244,7 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
},
{
.compatible = "adi,adt7461a",
- .data = (void *)adt7461
+ .data = (void *)adt7461a
},
{
.compatible = "gmt,g781",
@@ -312,7 +312,7 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
},
{
.compatible = "onnn,nct1008",
- .data = (void *)adt7461
+ .data = (void *)adt7461a
},
{
.compatible = "winbond,w83l771",
@@ -360,6 +360,13 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7c,
.max_convrate = 10,
},
+ [adt7461a] = {
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
+ | LM90_HAVE_CRIT | LM90_HAVE_PEC,
+ .alert_alarms = 0x7c,
+ .max_convrate = 10,
+ },
[g781] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT,
--
2.35.1
Reorder chip enumeration in alphabetical order to make it easier to
see which chips are supported, and to clarify where to add support
new chip types. No functional change.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index b7f5b743c9f5..6728520a21ca 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -113,8 +113,10 @@ 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 { adm1032, adt7461, g781, lm86, lm90, lm99,
+ max6646, max6654, max6657, max6659, max6680, max6696,
+ sa56004, tmp451, tmp461, w83l771,
+};
/*
* The LM90 registers
--
2.35.1
When reading 16-bit volatile registers, the code uses a trick to
determine if a temperature is consistent: It reads the high part
of the register twice. If the values are the same, the code assumes
that the reading is consistent. If the value differs, the code
re-reads the second register as well and assumes that it now has
correct values.
This is only necessary for volatile registers. Add a parameter to
lm90_read16() to indicate if the register is volatile to avoid the
extra overhead for non-volatile registers.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 41 +++++++++++++++++++++++------------------
1 file changed, 23 insertions(+), 18 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index acb9ca3b99b0..b20be0cb28b5 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -602,29 +602,34 @@ static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val)
return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val);
}
-static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl)
+static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl,
+ bool is_volatile)
{
int oldh, newh, l;
- /*
- * There is a trick here. We have to read two registers to have the
- * sensor temperature, but we have to beware a conversion could occur
- * between the readings. The datasheet says we should either use
- * the one-shot conversion register, which we don't want to do
- * (disables hardware monitoring) or monitor the busy bit, which is
- * impossible (we can't read the values and monitor that bit at the
- * exact same time). So the solution used here is to read the high
- * byte once, then the low byte, then the high byte again. If the new
- * high byte matches the old one, then we have a valid reading. Else
- * we have to read the low byte again, and now we believe we have a
- * correct reading.
- */
oldh = lm90_read_reg(client, regh);
if (oldh < 0)
return oldh;
l = lm90_read_reg(client, regl);
if (l < 0)
return l;
+
+ if (!is_volatile)
+ return (oldh << 8) | l;
+
+ /*
+ * For volatile registers we have to use a trick.
+ * We have to read two registers to have the sensor temperature,
+ * but we have to beware a conversion could occur between the
+ * readings. The datasheet says we should either use
+ * the one-shot conversion register, which we don't want to do
+ * (disables hardware monitoring) or monitor the busy bit, which is
+ * impossible (we can't read the values and monitor that bit at the
+ * exact same time). So the solution used here is to read the high
+ * the high byte again. If the new high byte matches the old one,
+ * then we have a valid reading. Otherwise we have to read the low
+ * byte again, and now we believe we have a correct reading.
+ */
newh = lm90_read_reg(client, regh);
if (newh < 0)
return newh;
@@ -766,7 +771,7 @@ static int lm90_update_limits(struct device *dev)
if (data->flags & LM90_HAVE_OFFSET) {
val = lm90_read16(client, LM90_REG_REMOTE_OFFSH,
- LM90_REG_REMOTE_OFFSL);
+ LM90_REG_REMOTE_OFFSL, false);
if (val < 0)
return val;
data->temp11[REMOTE_OFFSET] = val;
@@ -999,7 +1004,7 @@ static int lm90_update_device(struct device *dev)
if (data->reg_local_ext) {
val = lm90_read16(client, LM90_REG_LOCAL_TEMP,
- data->reg_local_ext);
+ data->reg_local_ext, true);
if (val < 0)
return val;
data->temp11[LOCAL_TEMP] = val;
@@ -1010,7 +1015,7 @@ static int lm90_update_device(struct device *dev)
data->temp11[LOCAL_TEMP] = val << 8;
}
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
- LM90_REG_REMOTE_TEMPL);
+ LM90_REG_REMOTE_TEMPL, true);
if (val < 0)
return val;
data->temp11[REMOTE_TEMP] = val;
@@ -1021,7 +1026,7 @@ static int lm90_update_device(struct device *dev)
return val;
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
- LM90_REG_REMOTE_TEMPL);
+ LM90_REG_REMOTE_TEMPL, true);
if (val < 0) {
lm90_select_remote_channel(data, 0);
return val;
--
2.35.1
Several of the chips supported by this driver support configuring the
number of samples (or the fault queue depth) necessary before a fault
or alarm is reported. This is done either with a bit in the configuration
register or with a separate "consecutive alert" register. Support this
functionality with the temp_samples attribute.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 105 +++++++++++++++++++++++++++++++++++++------
1 file changed, 91 insertions(+), 14 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 7f8397c362e8..4c25c9ffdfe9 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -206,6 +206,7 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
#define LM90_HAVE_LOW BIT(15) /* low limits */
#define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */
#define LM90_HAVE_REMOTE_EXT BIT(17) /* extended remote temperature */
+#define LM90_HAVE_FAULTQUEUE BIT(18) /* configurable samples count */
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -404,6 +405,8 @@ struct lm90_params {
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 faultqueue_mask; /* fault queue bit mask */
+ u8 faultqueue_depth; /* fault queue depth if mask is used */
};
static const struct lm90_params lm90_params[] = {
@@ -419,7 +422,8 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
| LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS
- | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT
+ | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -433,7 +437,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
- | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 10,
.resolution = 10,
@@ -442,7 +446,8 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS
- | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT
+ | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -451,7 +456,8 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC
| LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT
+ | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x1c7c,
.max_convrate = 11,
.resolution = 10,
@@ -461,7 +467,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
- | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
@@ -472,16 +478,22 @@ static const struct lm90_params lm90_params[] = {
[lm90] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT
+ | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7b,
.max_convrate = 9,
+ .faultqueue_mask = BIT(0),
+ .faultqueue_depth = 3,
},
[lm99] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT
+ | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7b,
.max_convrate = 9,
+ .faultqueue_mask = BIT(0),
+ .faultqueue_depth = 3,
},
[max1617] = {
.flags = LM90_HAVE_CONVRATE | LM90_HAVE_BROKEN_ALERT |
@@ -492,10 +504,12 @@ static const struct lm90_params lm90_params[] = {
},
[max6642] = {
.flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED
- | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x50,
.resolution = 10,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
+ .faultqueue_mask = BIT(4),
+ .faultqueue_depth = 2,
},
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
@@ -553,17 +567,20 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_EMERGENCY
| LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
- | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
.reg_status2 = MAX6696_REG_STATUS2,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
+ .faultqueue_mask = BIT(5),
+ .faultqueue_depth = 4,
},
[nct72] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_UNSIGNED_TEMP
- | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT
+ | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 10,
.resolution = 10,
@@ -598,16 +615,18 @@ static const struct lm90_params lm90_params[] = {
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
- | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7b,
.max_convrate = 9,
.reg_local_ext = SA56004_REG_LOCAL_TEMPL,
+ .faultqueue_mask = BIT(0),
+ .faultqueue_depth = 3,
},
[tmp451] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
| LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -617,7 +636,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
- | LM90_HAVE_REMOTE_EXT,
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -684,10 +703,13 @@ struct lm90_data {
u8 reg_status2; /* 2nd status register (optional) */
u8 reg_local_ext; /* local extension register offset */
u8 reg_remote_ext; /* remote temperature low byte */
+ u8 faultqueue_mask; /* fault queue mask */
+ u8 faultqueue_depth; /* fault queue mask */
/* registers values */
u16 temp[TEMP_REG_NUM];
u8 temp_hyst;
+ u8 conalert;
u16 reported_alarms; /* alarms reported as sysfs/udev events */
u16 current_alarms; /* current alarms, reported by chip */
u16 alarms; /* alarms not yet reported to user */
@@ -888,6 +910,26 @@ static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
return err;
}
+static int lm90_set_faultqueue(struct i2c_client *client,
+ struct lm90_data *data, int val)
+{
+ int err;
+
+ if (data->faultqueue_mask) {
+ err = lm90_update_confreg(data, val <= data->faultqueue_depth / 2 ?
+ data->config & ~data->faultqueue_mask :
+ data->config | data->faultqueue_mask);
+ } else {
+ static const u8 values[4] = {0, 2, 6, 0x0e};
+
+ data->conalert = (data->conalert & 0xf1) | values[val - 1];
+ err = lm90_write_reg(data->client, TMP451_REG_CONALERT,
+ data->conalert);
+ }
+
+ return err;
+}
+
static int lm90_update_limits(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
@@ -910,6 +952,12 @@ static int lm90_update_limits(struct device *dev)
return val;
data->temp_hyst = val;
}
+ if ((data->flags & LM90_HAVE_FAULTQUEUE) && !data->faultqueue_mask) {
+ val = lm90_read_reg(client, TMP451_REG_CONALERT);
+ if (val < 0)
+ return val;
+ data->conalert = val;
+ }
val = lm90_read16(client, LM90_REG_REMOTE_LOWH,
(data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_LOWL : 0,
@@ -1564,6 +1612,28 @@ static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val)
case hwmon_chip_alarms:
*val = data->alarms;
break;
+ case hwmon_chip_temp_samples:
+ if (data->faultqueue_mask) {
+ *val = (data->config & data->faultqueue_mask) ?
+ data->faultqueue_depth : 1;
+ } else {
+ switch (data->conalert & 0x0e) {
+ case 0x0:
+ default:
+ *val = 1;
+ break;
+ case 0x2:
+ *val = 2;
+ break;
+ case 0x6:
+ *val = 3;
+ break;
+ case 0xe:
+ *val = 4;
+ break;
+ }
+ }
+ break;
default:
return -EOPNOTSUPP;
}
@@ -1588,6 +1658,9 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val)
err = lm90_set_convrate(client, data,
clamp_val(val, 0, 100000));
break;
+ case hwmon_chip_temp_samples:
+ err = lm90_set_faultqueue(client, data, clamp_val(val, 1, 4));
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -1602,6 +1675,7 @@ static umode_t lm90_chip_is_visible(const void *data, u32 attr, int channel)
{
switch (attr) {
case hwmon_chip_update_interval:
+ case hwmon_chip_temp_samples:
return 0644;
case hwmon_chip_alarms:
return 0444;
@@ -2605,7 +2679,8 @@ static int lm90_probe(struct i2c_client *client)
data->chip_config[0] |= HWMON_C_ALARMS;
if (data->flags & LM90_HAVE_CONVRATE)
data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL;
-
+ if (data->flags & LM90_HAVE_FAULTQUEUE)
+ data->chip_config[0] |= HWMON_C_TEMP_SAMPLES;
data->info[1] = &data->temp_info;
info = &data->temp_info;
@@ -2656,6 +2731,8 @@ static int lm90_probe(struct i2c_client *client)
data->channel_config[2] |= HWMON_T_EMERGENCY_ALARM;
}
+ data->faultqueue_mask = lm90_params[data->kind].faultqueue_mask;
+ data->faultqueue_depth = lm90_params[data->kind].faultqueue_depth;
data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
if (data->flags & LM90_HAVE_REMOTE_EXT)
data->reg_remote_ext = LM90_REG_REMOTE_TEMPL;
--
2.35.1
So far the driver only generated sysfs and udev events for minimum and
maximum alarms. Also generate events for critical and emergency alarms.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 3820f0e61510..83d027c134be 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1829,6 +1829,26 @@ static bool lm90_is_tripped(struct i2c_client *client, u16 *status)
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
hwmon_temp_max_alarm, 2);
+ if (st & LM90_STATUS_LTHRM)
+ hwmon_notify_event(hwmon_dev, hwmon_temp,
+ hwmon_temp_crit_alarm, 0);
+ if (st & LM90_STATUS_RTHRM)
+ hwmon_notify_event(hwmon_dev, hwmon_temp,
+ hwmon_temp_crit_alarm, 1);
+ if (st2 & MAX6696_STATUS2_R2THRM)
+ hwmon_notify_event(hwmon_dev, hwmon_temp,
+ hwmon_temp_crit_alarm, 2);
+
+ if (st2 & MAX6696_STATUS2_LOT2)
+ hwmon_notify_event(hwmon_dev, hwmon_temp,
+ hwmon_temp_emergency_alarm, 0);
+ if (st2 & MAX6696_STATUS2_ROT2)
+ hwmon_notify_event(hwmon_dev, hwmon_temp,
+ hwmon_temp_emergency_alarm, 1);
+ if (st2 & MAX6696_STATUS2_R2OT2)
+ hwmon_notify_event(hwmon_dev, hwmon_temp,
+ hwmon_temp_emergency_alarm, 2);
+
return true;
}
--
2.35.1
MAX1617 and LM84 are stripped-down versions of LM90, so they can easily
be supported by the LM90 driver. The most difficult part is chip detection,
since those old chips do not support manufacturer ID or chip ID registers.
The "alarms" attribute is enabled for both chips to match the functionality
of the adm1021 driver. Chip detection was improved and is less prone to
misdetection than the chip detection in the adm1021 driver.
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.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 45 +++++-
drivers/hwmon/Kconfig | 9 +-
drivers/hwmon/lm90.c | 264 ++++++++++++++++++++++++++++++-----
3 files changed, 275 insertions(+), 43 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 313b18f6531d..8fe9013b9c39 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -3,6 +3,14 @@ Kernel driver lm90
Supported chips:
+ * National Semiconductor LM84
+
+ Prefix: 'lm84'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the National Semiconductor website
+
* National Semiconductor LM90
Prefix: 'lm90'
@@ -113,6 +121,22 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
+ * Maxim MAX1617
+
+ Prefix: 'max1617'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the Maxim website
+
+ * Maxim MAX1617A
+
+ Prefix: 'max1617a'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the Maxim website
+
* Maxim MAX6642
Prefix: 'max6642'
@@ -325,6 +349,12 @@ 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, many of which are supported by this driver.
+The family of chips supported by this driver is derived from MAX1617.
+This chip as well as various compatible chips support a local and a remote
+temperature sensor with 8 bit accuracy. Later chips provide improved accuracy
+and other additional features such as hysteresis and temperature offset
+registers.
+
Note that there is no easy way to differentiate between the MAX6657,
MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only
supported by this driver if the chip is located at address 0x4d or 0x4e,
@@ -332,15 +362,22 @@ or if the chip type is explicitly selected as max6659.
The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously
can't (and don't need to) be distinguished.
-The specificity of this family of chipsets over the ADM1021/LM84
-family is that it features critical limits with hysteresis, and an
-increased resolution of the remote temperature measurement.
-
The different chipsets of the family are not strictly identical, although
very similar. For reference, here comes a non-exhaustive list of specific
features:
+LM84:
+ * 8 bit sensor resolution
+
+MAX1617:
+ * 8 bit sensor resolution
+ * Low temperature limits
+
LM90:
+ * 11 bit resolution for remote temperature sensor
+ * Temperature offset register for remote temperature sensor
+ * Low and critical temperature limits
+ - Configurable conversion rate
* Filter and alert configuration register at 0xBF.
* ALERT is triggered by temperatures over critical limits.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 39ce1b2ccbb3..1dd812cf15bb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1358,11 +1358,12 @@ 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, ADT7461A,
+ If you say yes here you get support for National Semiconductor LM84,
+ LM90, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A,
ADT7481, ADT7482, and ADT7483A,
- Maxim MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657,
- MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
+ Maxim MAX1617, MAX6642, 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.
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 46837f0bf62e..09c3b9eb2f8b 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -80,6 +80,9 @@
* They are mostly compatible with ADT7461 except for local temperature
* low byte register and max conversion rate.
*
+ * This driver also supports MAX1617 and various clones such as G767
+ * and NE1617. Such clones will be detected as MAX1617.
+ *
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
@@ -119,8 +122,8 @@ 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, adt7481, g781, lm86, lm90, lm99,
- max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
+enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm86, lm90, lm99,
+ max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771,
};
@@ -194,6 +197,7 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99,
#define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/
#define LM90_HAVE_LOW BIT(15) /* low limits */
#define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */
+#define LM90_HAVE_REMOTE_EXT BIT(17) /* extended remote temperature */
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -226,10 +230,12 @@ static const struct i2c_device_id lm90_id[] = {
{ "adt7482", adt7481 },
{ "adt7483a", adt7481 },
{ "g781", g781 },
- { "lm90", lm90 },
+ { "lm84", lm84 },
{ "lm86", lm86 },
{ "lm89", lm86 },
+ { "lm90", lm90 },
{ "lm99", lm99 },
+ { "max1617", max1617 },
{ "max6642", max6642 },
{ "max6646", max6646 },
{ "max6647", max6646 },
@@ -373,7 +379,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
| LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS
- | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -386,7 +392,8 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 10,
.resolution = 10,
@@ -395,7 +402,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS
- | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -404,7 +411,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC
| LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x1c7c,
.max_convrate = 11,
.resolution = 10,
@@ -413,38 +420,54 @@ static const struct lm90_params lm90_params[] = {
[g781] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
+ [lm84] = {
+ .flags = LM90_HAVE_ALARMS,
+ .resolution = 8,
+ },
[lm86] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm90] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm99] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
+ [max1617] = {
+ .flags = LM90_HAVE_CONVRATE | LM90_HAVE_BROKEN_ALERT |
+ LM90_HAVE_LOW | LM90_HAVE_ALARMS,
+ .alert_alarms = 0x78,
+ .resolution = 8,
+ .max_convrate = 7,
+ },
[max6642] = {
- .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED,
+ .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x50,
- .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
.resolution = 10,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
| LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -452,28 +475,30 @@ static const struct lm90_params lm90_params[] = {
[max6648] = {
.flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6654] = {
.flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 7,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6657] = {
.flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6659] = {
.flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -486,14 +511,16 @@ static const struct lm90_params lm90_params[] = {
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT
| LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[max6696] = {
.flags = LM90_HAVE_EMERGENCY
| LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
.reg_status2 = MAX6696_REG_STATUS2,
@@ -501,7 +528,8 @@ static const struct lm90_params lm90_params[] = {
},
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 8,
},
@@ -512,7 +540,8 @@ static const struct lm90_params lm90_params[] = {
* be set).
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7b,
.max_convrate = 9,
.reg_local_ext = SA56004_REG_LOCAL_TEMPL,
@@ -521,7 +550,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
| LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -530,7 +559,8 @@ static const struct lm90_params lm90_params[] = {
[tmp461] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -596,6 +626,7 @@ struct lm90_data {
u8 max_convrate; /* Maximum conversion rate */
u8 reg_status2; /* 2nd status register (optional) */
u8 reg_local_ext; /* local extension register offset */
+ u8 reg_remote_ext; /* remote temperature low byte */
/* registers values */
u16 temp[TEMP_REG_NUM];
@@ -1078,7 +1109,7 @@ static int lm90_update_device(struct device *dev)
return val;
data->temp[LOCAL_TEMP] = val;
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
- LM90_REG_REMOTE_TEMPL, true);
+ data->reg_remote_ext, true);
if (val < 0)
return val;
data->temp[REMOTE_TEMP] = val;
@@ -1089,7 +1120,7 @@ static int lm90_update_device(struct device *dev)
return val;
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
- LM90_REG_REMOTE_TEMPL, true);
+ data->reg_remote_ext, true);
if (val < 0) {
lm90_select_remote_channel(data, false);
return val;
@@ -1150,6 +1181,9 @@ static int lm90_temp_get_resolution(struct lm90_data *data, int index)
{
switch (index) {
case REMOTE_TEMP:
+ if (data->reg_remote_ext)
+ return data->resolution;
+ return 8;
case REMOTE_OFFSET:
case REMOTE2_TEMP:
return data->resolution;
@@ -1558,11 +1592,118 @@ static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type,
}
}
-/*
- * Per-manufacturer chip detect functions.
- * Functions are expected to return a pointer to the chip name or NULL
- * if detection was not successful.
- */
+static const char *lm90_detect_lm84(struct i2c_client *client)
+{
+ static const u8 regs[] = {
+ LM90_REG_STATUS, LM90_REG_LOCAL_TEMP, LM90_REG_LOCAL_HIGH,
+ LM90_REG_REMOTE_TEMPH, LM90_REG_REMOTE_HIGHH
+ };
+ int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS);
+ int reg1, reg2, reg3, reg4;
+ bool nonzero = false;
+ u8 ff = 0xff;
+ int i;
+
+ if (status < 0 || (status & 0xab))
+ return NULL;
+
+ /*
+ * For LM84, undefined registers return the most recent value.
+ * Repeat several times, each time checking against a different
+ * (presumably) existing register.
+ */
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ reg1 = i2c_smbus_read_byte_data(client, regs[i]);
+ reg2 = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL);
+ reg3 = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW);
+ reg4 = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH);
+
+ if (reg1 < 0)
+ return NULL;
+
+ /* If any register has a different value, this is not an LM84 */
+ if (reg2 != reg1 || reg3 != reg1 || reg4 != reg1)
+ return NULL;
+
+ nonzero |= reg1 || reg2 || reg3 || reg4;
+ ff &= reg1;
+ }
+ /*
+ * If all registers always returned 0 or 0xff, all bets are off,
+ * and we can not make any predictions about the chip type.
+ */
+ return nonzero && ff != 0xff ? "lm84" : NULL;
+}
+
+static const char *lm90_detect_max1617(struct i2c_client *client, int config1)
+{
+ int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS);
+ int llo, rlo, lhi, rhi;
+
+ if (status < 0 || (status & 0x03))
+ return NULL;
+
+ if (config1 & 0x3f)
+ return NULL;
+
+ /*
+ * Fail if unsupported registers return anything but 0xff.
+ * The calling code already checked man_id and chip_id.
+ * A byte read operation repeats the most recent read operation
+ * and should also return 0xff.
+ */
+ if (i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL) != 0xff ||
+ i2c_smbus_read_byte_data(client, MAX6657_REG_LOCAL_TEMPL) != 0xff ||
+ i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWL) != 0xff ||
+ i2c_smbus_read_byte(client) != 0xff)
+ return NULL;
+
+ llo = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW);
+ rlo = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH);
+
+ lhi = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_HIGH);
+ rhi = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_HIGHH);
+
+ if (llo < 0 || rlo < 0)
+ return NULL;
+
+ /*
+ * A byte read operation repeats the most recent read and should
+ * return the same value.
+ */
+ if (i2c_smbus_read_byte(client) != rhi)
+ return NULL;
+
+ /*
+ * The following two checks are marginal since the checked values
+ * are strictly speaking valid.
+ */
+
+ /* fail for negative high limits; this also catches read errors */
+ if ((s8)lhi < 0 || (s8)rhi < 0)
+ return NULL;
+
+ /* fail if low limits are larger than or equal to high limits */
+ if ((s8)llo >= lhi || (s8)rlo >= rhi)
+ return NULL;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ /*
+ * Word read operations return 0xff in second byte
+ */
+ if (i2c_smbus_read_word_data(client, LM90_REG_REMOTE_TEMPL) !=
+ 0xffff)
+ return NULL;
+ if (i2c_smbus_read_word_data(client, LM90_REG_CONFIG1) !=
+ (config1 | 0xff00))
+ return NULL;
+ if (i2c_smbus_read_word_data(client, LM90_REG_LOCAL_HIGH) !=
+ (lhi | 0xff00))
+ return NULL;
+ }
+
+ return "max1617";
+}
static const char *lm90_detect_national(struct i2c_client *client, int chip_id,
int config1, int convrate)
@@ -1712,10 +1853,29 @@ static const char *lm90_detect_maxim(struct i2c_client *client, bool common_addr
* The chip_id register of the MAX6680 and MAX6681 holds the
* revision of the chip. The lowest bit of the config1 register
* is unused and should return zero when read, so should the
- * second to last bit of config1 (software reset).
+ * second to last bit of config1 (software reset). Register
+ * address 0x12 (LM90_REG_REMOTE_OFFSL) exists for this chip and
+ * should differ from emerg2, and emerg2 should match man_id
+ * since it does not exist.
*/
- else if (!(config1 & 0x03) && convrate <= 0x07)
+ else if (!(config1 & 0x03) && convrate <= 0x07 &&
+ emerg2 == man_id && emerg2 != status2)
name = "max6680";
+ /*
+ * MAX1617A does not have any extended registers (register
+ * address 0x10 or higher) except for manufacturer and
+ * device ID registers. Unlike other chips of this series,
+ * unsupported registers were observed to return a fixed value
+ * of 0x01.
+ * Note: Multiple chips with different markings labeled as
+ * "MAX1617" (no "A") were observed to report manufacturer ID
+ * 0x4d and device ID 0x01. It is unknown if other variants of
+ * MAX1617/MAX617A with different behavior exist. The detection
+ * code below works for those chips.
+ */
+ else if (!(config1 & 0x03f) && convrate <= 0x07 &&
+ emerg == 0x01 && emerg2 == 0x01 && status2 == 0x01)
+ name = "max1617";
break;
case 0x08:
/*
@@ -1961,7 +2121,7 @@ static const char *lm90_detect_ti(struct i2c_client *client, int chip_id,
static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- int man_id, chip_id, config1, convrate;
+ int man_id, chip_id, config1, convrate, lhigh;
const char *name = NULL;
int address = client->addr;
bool common_address =
@@ -1972,15 +2132,43 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
+ /*
+ * Get well defined register value for chips with neither man_id nor
+ * chip_id registers.
+ */
+ lhigh = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_HIGH);
+
/* detection and identification */
man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID);
chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID);
config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1);
convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE);
- if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0)
+ if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0 || lhigh < 0)
return -ENODEV;
+ /* Bail out immediately if all register report the same value */
+ if (lhigh == man_id && lhigh == chip_id && lhigh == config1 && lhigh == convrate)
+ return -ENODEV;
+
+ /*
+ * If reading man_id and chip_id both return the same value as lhigh,
+ * the chip may not support those registers and return the most recent read
+ * value. Check again with a different register and handle accordingly.
+ */
+ if (man_id == lhigh && chip_id == lhigh) {
+ convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE);
+ man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID);
+ if (convrate < 0 || man_id < 0 || chip_id < 0)
+ return -ENODEV;
+ if (man_id == convrate && chip_id == convrate)
+ man_id = -1;
+ }
switch (man_id) {
+ case -1: /* Chip does not support man_id / chip_id */
+ if (common_address && !convrate && !(config1 & 0x7f))
+ name = lm90_detect_lm84(client);
+ break;
case 0x01: /* National Semiconductor */
name = lm90_detect_national(client, chip_id, config1, convrate);
break;
@@ -2003,6 +2191,10 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
case 0xa1: /* NXP Semiconductor/Philips */
name = lm90_detect_nxp(client, chip_id, config1, convrate);
break;
+ case 0xff: /* MAX1617, G767, NE1617 */
+ if (common_address && chip_id == 0xff && convrate < 8)
+ name = lm90_detect_max1617(client, config1);
+ break;
default:
break;
}
@@ -2263,6 +2455,8 @@ static int lm90_probe(struct i2c_client *client)
}
data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
+ if (data->flags & LM90_HAVE_REMOTE_EXT)
+ data->reg_remote_ext = LM90_REG_REMOTE_TEMPL;
data->reg_status2 = lm90_params[data->kind].reg_status2;
/* Set maximum conversion rate */
--
2.35.1
ADM1020 is compatible with ADM1021 but has a separate chip revision and
a limited I2C address range.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 10 +++++++++-
drivers/hwmon/Kconfig | 4 ++--
drivers/hwmon/lm90.c | 10 +++++++++-
3 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index c3ce54f61f44..b2ca10f37218 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -51,6 +51,14 @@ Supported chips:
http://www.national.com/mpf/LM/LM86.html
+ * Analog Devices ADM1020
+
+ Prefix: 'adm1020'
+
+ Addresses scanned: I2C 0x4c - 0x4e
+
+ Datasheet: Publicly available at the Analog Devices website
+
* Analog Devices ADM1021
Prefix: 'adm1021'
@@ -457,7 +465,7 @@ features:
LM84:
* 8 bit sensor resolution
-ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
+ADM1020, ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
* 8 bit sensor resolution
* Low temperature limits
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 32c605eaec7e..494539e4be3d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1360,8 +1360,8 @@ config SENSORS_LM90
depends on I2C
help
If you say yes here you get support for National Semiconductor LM84,
- LM90, LM86, LM89 and LM99, Analog Devices ADM2021, ADM1021A, ADM1023,
- ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A,
+ LM90, LM86, LM89 and LM99, Analog Devices ADM1020, ADM2021, ADM1021A,
+ ADM1023, ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A,
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 6d1e7052e3ed..de51d205b63f 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -228,6 +228,7 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
*/
static const struct i2c_device_id lm90_id[] = {
+ { "adm1020", max1617 },
{ "adm1021", max1617 },
{ "adm1023", adm1023 },
{ "adm1032", adm1032 },
@@ -1829,7 +1830,8 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
(config1 & 0x0b) == 0x08 && convrate <= 0x0a)
name = "adt7421";
break;
- case 0x30 ... 0x3e: /* ADM1021A, ADM1023 */
+ case 0x30 ... 0x38: /* ADM1021A, ADM1023 */
+ case 0x3a ... 0x3e:
/*
* ADM1021A and compatible chips will be mis-detected as
* ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both
@@ -1847,6 +1849,12 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
!(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "adm1023";
break;
+ case 0x39: /* ADM1020 (undocumented) */
+ if (man_id2 == 0x00 && chip_id2 == 0x00 &&
+ (address == 0x4c || address == 0x4d || address == 0x4e) &&
+ !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
+ name = "adm1020";
+ break;
case 0x3f: /* NCT210 */
if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address &&
!(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
--
2.35.1
ADT7421 is similar to ADT7461A but supports configurable Beta Compensation.
Packet Error Checking (PEC) is supported but undocumented.
A devicetree node is not added for the added chip since it is quite
unlikely that such an old chip will ever be used in a devicetree based
system. It can be added later if needed.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 4194b8838f20..d1435f55e31d 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -231,6 +231,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "adm1021", max1617 },
{ "adm1023", adm1023 },
{ "adm1032", adm1032 },
+ { "adt7421", adt7461a },
{ "adt7461", adt7461 },
{ "adt7461a", adt7461a },
{ "adt7481", adt7481 },
@@ -1816,11 +1817,18 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
return NULL;
switch (chip_id) {
- case 0x00 ... 0x0f: /* ADM1021, undocumented */
+ case 0x00 ... 0x03: /* ADM1021 */
+ case 0x05 ... 0x0f:
if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address &&
!(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "adm1021";
break;
+ case 0x04: /* ADT7421 (undocumented) */
+ if (man_id2 == 0x41 && chip_id2 == 0x21 &&
+ (address == 0x4c || address == 0x4d) &&
+ (config1 & 0x0b) == 0x08 && convrate <= 0x0a)
+ name = "adt7421";
+ break;
case 0x30 ... 0x3e: /* ADM1021A, ADM1023 */
/*
* ADM1021A and compatible chips will be mis-detected as
--
2.35.1
Introduce 16-bit register write function to simplify the code in
some places.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 20208f127508..46837f0bf62e 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -665,6 +665,21 @@ static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val)
return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val);
}
+/*
+ * Write into 16-bit LM90 register.
+ * Convert register addresses to write address if needed, then execute the
+ * operation.
+ */
+static int lm90_write16(struct i2c_client *client, u8 regh, u8 regl, u16 val)
+{
+ int ret;
+
+ ret = lm90_write_reg(client, regh, val >> 8);
+ if (ret < 0 || !regl)
+ return ret;
+ return lm90_write_reg(client, regl, val & 0xff);
+}
+
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl,
bool is_volatile)
{
@@ -1240,12 +1255,8 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va
if (channel > 1)
lm90_select_remote_channel(data, true);
- err = lm90_write_reg(client, regh, data->temp[index] >> 8);
- if (err < 0)
- goto deselect;
- if (regl)
- err = lm90_write_reg(client, regl, data->temp[index] & 0xff);
-deselect:
+ err = lm90_write16(client, regh, regl, data->temp[index]);
+
if (channel > 1)
lm90_select_remote_channel(data, false);
@@ -1405,14 +1416,8 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
val = lm90_temp_to_reg(0, val,
lm90_temp_get_resolution(data, REMOTE_OFFSET));
data->temp[REMOTE_OFFSET] = val;
- err = i2c_smbus_write_byte_data(data->client,
- LM90_REG_REMOTE_OFFSH,
- val >> 8);
- if (err)
- break;
- err = i2c_smbus_write_byte_data(data->client,
- LM90_REG_REMOTE_OFFSL,
- val & 0xff);
+ err = lm90_write16(data->client, LM90_REG_REMOTE_OFFSH,
+ LM90_REG_REMOTE_OFFSL, val);
break;
default:
err = -EOPNOTSUPP;
--
2.35.1
MAX6690 is all but identical to MAX6654. Revision 1 of its
datasheet lists the same chip ID as MAX6654, and a chip labeled
MAX6654 was found to have the chip ID listed as MAX6690 chip ID
in Revision 2 of its datasheet.
A devicetree node is not added for this chip since it is quite unlikely
that such an old chip will ever be used in a devicetree based system.
It can be added later if needed.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 2 +-
drivers/hwmon/lm90.c | 17 ++++++++++++++++-
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 53429f79b819..45bc333a1219 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -370,7 +370,7 @@ MAX6648, MAX6692:
* Better local resolution
* Unsigned temperature
-MAX6654:
+MAX6654, MAX6690:
* Better local resolution
* Selectable address
* Remote sensor type selection
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index abc64738e892..22ea75535ab4 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -42,7 +42,8 @@
* accordingly, and is done during initialization. Extended precision is only
* available at conversion rates of 1 Hz and slower. Note that extended
* precision is not enabled by default, as this driver initializes all chips
- * to 2 Hz by design.
+ * to 2 Hz by design. The driver also supports MAX6690, which is practically
+ * identical to MAX6654.
*
* This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and
* MAX6692 chips made by Maxim. These are again similar to the LM86,
@@ -237,6 +238,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6659", max6659 },
{ "max6680", max6680 },
{ "max6681", max6680 },
+ { "max6690", max6654 },
{ "max6692", max6648 },
{ "max6695", max6696 },
{ "max6696", max6696 },
@@ -1716,6 +1718,19 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
if (!(config1 & 0x07) && convrate <= 0x07)
name = "max6654";
break;
+ case 0x09:
+ /*
+ * The chip_id of the MAX6690 holds the revision of the chip.
+ * The lowest 3 bits of the config1 register are unused and
+ * should return zero when read.
+ * Note that MAX6654 and MAX6690 are practically the same chips.
+ * The only diference is the rated accuracy. Rev. 1 of the
+ * MAX6690 datasheet lists a chip ID of 0x08, and a chip labeled
+ * MAX6654 was observed to have a chip ID of 0x09.
+ */
+ if (!(config1 & 0x07) && convrate <= 0x07)
+ name = "max6690";
+ break;
case 0x4d:
/*
* The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
--
2.35.1
Many chips supported by this driver clear status registers after it
is read and update it in the next measurement cycle. Normally this falls
under the radar because all registers are only read once per measurement
cycle. However, there is an exception: Status registers are always read
during interrupt and laert handling. This can result in invalid status
reports if userspace reads an alarm attribute immediately afterwards.
Rework alarm/status handling by keeping a shadow register with 'current'
alarms, and by ensuring that the register is either only updated once per
measurement cycle or not cleared.
A second problem is related to alert handling: Alert handling is disabled
for chips with broken alert after an alert was reported, but only
re-enabled if attributes are read by the user. This means that alert
conditions may appear and disappear unnoticed. Remedy the situation
by introducing a worker to periodically read the status register(s) while
alert handling is disabled, and re-enable alerts after the alert condition
clears.
Yet another problem is that sysfs and udev events are currently only
reported to userspace if an alarm is raised, but not if an alarm condition
clears. Use the new worker to detect that situation and also generate
sysfs and udev events in that case.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 314 +++++++++++++++++++++++++++----------------
1 file changed, 199 insertions(+), 115 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 83d027c134be..63ada2d0d839 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -92,6 +92,7 @@
#include <linux/sysfs.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
/*
* Addresses to scan
@@ -499,8 +500,11 @@ struct lm90_data {
const struct hwmon_channel_info *info[3];
struct hwmon_chip_info chip;
struct mutex update_lock;
+ struct delayed_work alert_work;
bool valid; /* true if register values are valid */
+ bool alarms_valid; /* true if status register values are valid */
unsigned long last_updated; /* in jiffies */
+ unsigned long alarms_updated; /* in jiffies */
int kind;
u32 flags;
@@ -518,7 +522,9 @@ struct lm90_data {
s8 temp8[TEMP8_REG_NUM];
s16 temp11[TEMP11_REG_NUM];
u8 temp_hyst;
- u16 alarms; /* bitvector (upper 8 bits for max6695/96) */
+ u16 reported_alarms; /* alarms reported as sysfs/udev events */
+ u16 current_alarms; /* current alarms, reported by chip */
+ u16 alarms; /* alarms not yet reported to user */
};
/*
@@ -771,6 +777,158 @@ static int lm90_update_limits(struct device *dev)
return 0;
}
+static void lm90_report_alarms(struct device *dev, struct lm90_data *data)
+{
+ u16 cleared_alarms = data->reported_alarms & ~data->current_alarms;
+ u16 new_alarms = data->current_alarms & ~data->reported_alarms;
+ struct device *hwmon_dev = data->hwmon_dev;
+ int st, st2;
+
+ if (!cleared_alarms && !new_alarms)
+ return;
+
+ st = new_alarms & 0xff;
+ st2 = new_alarms >> 8;
+
+ if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) ||
+ (st2 & MAX6696_STATUS2_LOT2))
+ dev_dbg(dev, "temp%d out of range, please check!\n", 1);
+ if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) ||
+ (st2 & MAX6696_STATUS2_ROT2))
+ dev_dbg(dev, "temp%d out of range, please check!\n", 2);
+ if (st & LM90_STATUS_ROPEN)
+ dev_dbg(dev, "temp%d diode open, please check!\n", 2);
+ if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH |
+ MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2))
+ dev_dbg(dev, "temp%d out of range, please check!\n", 3);
+ if (st2 & MAX6696_STATUS2_R2OPEN)
+ dev_dbg(dev, "temp%d diode open, please check!\n", 3);
+
+ st |= cleared_alarms & 0xff;
+ st2 |= cleared_alarms >> 8;
+
+ if (st & LM90_STATUS_LLOW)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 0);
+ if (st & LM90_STATUS_RLOW)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 1);
+ if (st2 & MAX6696_STATUS2_R2LOW)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 2);
+
+ if (st & LM90_STATUS_LHIGH)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 0);
+ if (st & LM90_STATUS_RHIGH)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 1);
+ if (st2 & MAX6696_STATUS2_R2HIGH)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 2);
+
+ if (st & LM90_STATUS_LTHRM)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 0);
+ if (st & LM90_STATUS_RTHRM)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 1);
+ if (st2 & MAX6696_STATUS2_R2THRM)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 2);
+
+ if (st2 & MAX6696_STATUS2_LOT2)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 0);
+ if (st2 & MAX6696_STATUS2_ROT2)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 1);
+ if (st2 & MAX6696_STATUS2_R2OT2)
+ hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 2);
+
+ data->reported_alarms = data->current_alarms;
+}
+
+static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
+{
+ if (force || !data->alarms_valid ||
+ time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) {
+ struct i2c_client *client = data->client;
+ bool check_enable;
+ u16 alarms;
+ int val;
+
+ data->alarms_valid = false;
+
+ val = lm90_read_reg(client, LM90_REG_R_STATUS);
+ if (val < 0)
+ return val;
+ alarms = val & ~LM90_STATUS_BUSY;
+
+ if (data->kind == max6696) {
+ val = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
+ if (val < 0)
+ return val;
+ alarms |= val << 8;
+ }
+ /*
+ * If the update is forced (called from interrupt or alert
+ * handler) and alarm data is valid, the alarms may have been
+ * updated after the last update interval, and the status
+ * register may still be cleared. Only add additional alarms
+ * in this case. Alarms will be cleared later if appropriate.
+ */
+ if (force && data->alarms_valid)
+ data->current_alarms |= alarms;
+ else
+ data->current_alarms = alarms;
+ data->alarms |= alarms;
+
+ check_enable = (client->irq || !(data->config_orig & 0x80)) &&
+ (data->config & 0x80);
+
+ if (force || check_enable)
+ lm90_report_alarms(&client->dev, data);
+
+ /*
+ * Re-enable ALERT# output if it was originally enabled, relevant
+ * alarms are all clear, and alerts are currently disabled.
+ * Otherwise (re)schedule worker if needed.
+ */
+ if (check_enable) {
+ if (!(data->current_alarms & data->alert_alarms)) {
+ dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+ lm90_update_confreg(data, data->config & ~0x80);
+ /*
+ * We may have been called from the update handler.
+ * If so, the worker, if scheduled, is no longer
+ * needed. Cancel it. Don't synchronize because
+ * it may already be running.
+ */
+ cancel_delayed_work(&data->alert_work);
+ } else {
+ schedule_delayed_work(&data->alert_work,
+ max_t(int, HZ, msecs_to_jiffies(data->update_interval)));
+ }
+ }
+ data->alarms_updated = jiffies;
+ data->alarms_valid = true;
+ }
+ return 0;
+}
+
+static int lm90_update_alarms(struct lm90_data *data, bool force)
+{
+ int err;
+
+ mutex_lock(&data->update_lock);
+ err = lm90_update_alarms_locked(data, force);
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static void lm90_alert_work(struct work_struct *__work)
+{
+ struct delayed_work *delayed_work = container_of(__work, struct delayed_work, work);
+ struct lm90_data *data = container_of(delayed_work, struct lm90_data, alert_work);
+
+ /* Nothing to do if alerts are enabled */
+ if (!(data->config & 0x80))
+ return;
+
+ lm90_update_alarms(data, true);
+}
+
static int lm90_update_device(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
@@ -819,11 +977,6 @@ static int lm90_update_device(struct device *dev)
return val;
data->temp11[REMOTE_TEMP] = val;
- val = lm90_read_reg(client, LM90_REG_R_STATUS);
- if (val < 0)
- return val;
- data->alarms = val & ~LM90_STATUS_BUSY;
-
if (data->kind == max6696) {
val = lm90_select_remote_channel(data, 1);
if (val < 0)
@@ -838,24 +991,11 @@ 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 (val < 0)
- return val;
- data->alarms |= val << 8;
}
- /*
- * Re-enable ALERT# output if it was originally enabled and
- * relevant alarms are all clear
- */
- if ((client->irq || !(data->config_orig & 0x80)) &&
- !(data->alarms & data->alert_alarms)) {
- if (data->config & 0x80) {
- dev_dbg(&client->dev, "Re-enabling ALERT#\n");
- lm90_update_confreg(data, data->config & ~0x80);
- }
- }
+ val = lm90_update_alarms_locked(data, false);
+ if (val < 0)
+ return val;
data->last_updated = jiffies;
data->valid = true;
@@ -1212,7 +1352,7 @@ static const u8 lm90_fault_bits[3] = { 0, 2, 10 };
static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
{
struct lm90_data *data = dev_get_drvdata(dev);
- int err;
+ int err, bit;
mutex_lock(&data->update_lock);
err = lm90_update_device(dev);
@@ -1225,22 +1365,33 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
*val = lm90_get_temp11(data, lm90_temp_index[channel]);
break;
case hwmon_temp_min_alarm:
- *val = (data->alarms >> lm90_min_alarm_bits[channel]) & 1;
- break;
case hwmon_temp_max_alarm:
- *val = (data->alarms >> lm90_max_alarm_bits[channel]) & 1;
- break;
case hwmon_temp_crit_alarm:
- if (data->flags & LM90_HAVE_CRIT_ALRM_SWP)
- *val = (data->alarms >> lm90_crit_alarm_bits_swapped[channel]) & 1;
- else
- *val = (data->alarms >> lm90_crit_alarm_bits[channel]) & 1;
- break;
case hwmon_temp_emergency_alarm:
- *val = (data->alarms >> lm90_emergency_alarm_bits[channel]) & 1;
- break;
case hwmon_temp_fault:
- *val = (data->alarms >> lm90_fault_bits[channel]) & 1;
+ switch (attr) {
+ case hwmon_temp_min_alarm:
+ bit = BIT(lm90_min_alarm_bits[channel]);
+ break;
+ case hwmon_temp_max_alarm:
+ bit = BIT(lm90_max_alarm_bits[channel]);
+ break;
+ case hwmon_temp_crit_alarm:
+ if (data->flags & LM90_HAVE_CRIT_ALRM_SWP)
+ bit = BIT(lm90_crit_alarm_bits_swapped[channel]);
+ else
+ bit = BIT(lm90_crit_alarm_bits[channel]);
+ break;
+ case hwmon_temp_emergency_alarm:
+ bit = BIT(lm90_emergency_alarm_bits[channel]);
+ break;
+ case hwmon_temp_fault:
+ bit = BIT(lm90_fault_bits[channel]);
+ break;
+ }
+ *val = !!(data->alarms & bit);
+ data->alarms &= ~bit;
+ data->alarms |= data->current_alarms;
break;
case hwmon_temp_min:
if (channel == 0)
@@ -1699,6 +1850,8 @@ static void lm90_restore_conf(void *_data)
struct lm90_data *data = _data;
struct i2c_client *client = data->client;
+ cancel_delayed_work_sync(&data->alert_work);
+
/* Restore initial configuration */
lm90_write_convrate(data, data->convrate_orig);
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
@@ -1771,93 +1924,23 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
return devm_add_action_or_reset(&client->dev, lm90_restore_conf, data);
}
-static bool lm90_is_tripped(struct i2c_client *client, u16 *status)
+static bool lm90_is_tripped(struct i2c_client *client)
{
struct lm90_data *data = i2c_get_clientdata(client);
- int st, st2 = 0;
-
- st = lm90_read_reg(client, LM90_REG_R_STATUS);
- if (st < 0)
- return false;
-
- if (data->kind == max6696) {
- st2 = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
- if (st2 < 0)
- return false;
- }
-
- *status = st | (st2 << 8);
+ int ret;
- if ((st & 0x7f) == 0 && (st2 & 0xfe) == 0)
+ ret = lm90_update_alarms(data, true);
+ if (ret < 0)
return false;
- if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) ||
- (st2 & MAX6696_STATUS2_LOT2))
- dev_dbg(&client->dev,
- "temp%d out of range, please check!\n", 1);
- if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) ||
- (st2 & MAX6696_STATUS2_ROT2))
- dev_dbg(&client->dev,
- "temp%d out of range, please check!\n", 2);
- if (st & LM90_STATUS_ROPEN)
- dev_dbg(&client->dev,
- "temp%d diode open, please check!\n", 2);
- if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH |
- MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2))
- dev_dbg(&client->dev,
- "temp%d out of range, please check!\n", 3);
- if (st2 & MAX6696_STATUS2_R2OPEN)
- dev_dbg(&client->dev,
- "temp%d diode open, please check!\n", 3);
-
- if (st & LM90_STATUS_LLOW)
- hwmon_notify_event(data->hwmon_dev, hwmon_temp,
- hwmon_temp_min_alarm, 0);
- if (st & LM90_STATUS_RLOW)
- hwmon_notify_event(data->hwmon_dev, hwmon_temp,
- hwmon_temp_min_alarm, 1);
- if (st2 & MAX6696_STATUS2_R2LOW)
- hwmon_notify_event(data->hwmon_dev, hwmon_temp,
- hwmon_temp_min_alarm, 2);
- if (st & LM90_STATUS_LHIGH)
- hwmon_notify_event(data->hwmon_dev, hwmon_temp,
- hwmon_temp_max_alarm, 0);
- if (st & LM90_STATUS_RHIGH)
- hwmon_notify_event(data->hwmon_dev, hwmon_temp,
- hwmon_temp_max_alarm, 1);
- if (st2 & MAX6696_STATUS2_R2HIGH)
- hwmon_notify_event(data->hwmon_dev, hwmon_temp,
- hwmon_temp_max_alarm, 2);
-
- if (st & LM90_STATUS_LTHRM)
- hwmon_notify_event(hwmon_dev, hwmon_temp,
- hwmon_temp_crit_alarm, 0);
- if (st & LM90_STATUS_RTHRM)
- hwmon_notify_event(hwmon_dev, hwmon_temp,
- hwmon_temp_crit_alarm, 1);
- if (st2 & MAX6696_STATUS2_R2THRM)
- hwmon_notify_event(hwmon_dev, hwmon_temp,
- hwmon_temp_crit_alarm, 2);
-
- if (st2 & MAX6696_STATUS2_LOT2)
- hwmon_notify_event(hwmon_dev, hwmon_temp,
- hwmon_temp_emergency_alarm, 0);
- if (st2 & MAX6696_STATUS2_ROT2)
- hwmon_notify_event(hwmon_dev, hwmon_temp,
- hwmon_temp_emergency_alarm, 1);
- if (st2 & MAX6696_STATUS2_R2OT2)
- hwmon_notify_event(hwmon_dev, hwmon_temp,
- hwmon_temp_emergency_alarm, 2);
-
- return true;
+ return !!data->current_alarms;
}
static irqreturn_t lm90_irq_thread(int irq, void *dev_id)
{
struct i2c_client *client = dev_id;
- u16 status;
- if (lm90_is_tripped(client, &status))
+ if (lm90_is_tripped(client))
return IRQ_HANDLED;
else
return IRQ_NONE;
@@ -1911,6 +1994,7 @@ static int lm90_probe(struct i2c_client *client)
data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
+ INIT_DELAYED_WORK(&data->alert_work, lm90_alert_work);
/* Set the device type */
if (client->dev.of_node)
@@ -2027,12 +2111,10 @@ static int lm90_probe(struct i2c_client *client)
static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
unsigned int flag)
{
- u16 alarms;
-
if (type != I2C_PROTOCOL_SMBUS_ALERT)
return;
- if (lm90_is_tripped(client, &alarms)) {
+ if (lm90_is_tripped(client)) {
/*
* Disable ALERT# output, because these chips don't implement
* SMBus alert correctly; they should only hold the alert line
@@ -2041,9 +2123,11 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
struct lm90_data *data = i2c_get_clientdata(client);
if ((data->flags & LM90_HAVE_BROKEN_ALERT) &&
- (alarms & data->alert_alarms)) {
+ (data->current_alarms & data->alert_alarms)) {
dev_dbg(&client->dev, "Disabling ALERT#\n");
lm90_update_confreg(data, data->config | 0x80);
+ schedule_delayed_work(&data->alert_work,
+ max_t(int, HZ, msecs_to_jiffies(data->update_interval)));
}
} else {
dev_dbg(&client->dev, "Everything OK\n");
--
2.35.1
A flag indicating support for setting the conversion rate doesn't cost
much and will enable us to add support for MAX6642 to the lm90 driver.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 66 ++++++++++++++++++++++++++------------------
1 file changed, 39 insertions(+), 27 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index abf1451c5652..90c3a496bb6c 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -193,6 +193,7 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99,
#define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */
#define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/
#define LM90_HAVE_LOW BIT(15) /* low limits */
+#define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -371,7 +372,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
| LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS
- | LM90_HAVE_LOW,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -384,7 +385,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 10,
.resolution = 10,
@@ -393,7 +394,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS
- | LM90_HAVE_LOW,
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -401,7 +402,8 @@ static const struct lm90_params lm90_params[] = {
.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 | LM90_HAVE_LOW,
+ | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE,
.alert_alarms = 0x1c7c,
.max_convrate = 11,
.resolution = 10,
@@ -410,58 +412,61 @@ static const struct lm90_params lm90_params[] = {
[g781] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[lm86] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm90] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm99] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6648] = {
.flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW,
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6654] = {
- .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 7,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6657] = {
.flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6659] = {
.flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -474,14 +479,14 @@ static const struct lm90_params lm90_params[] = {
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT
| LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[max6696] = {
.flags = LM90_HAVE_EMERGENCY
| LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
.reg_status2 = MAX6696_REG_STATUS2,
@@ -489,7 +494,7 @@ static const struct lm90_params lm90_params[] = {
},
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 8,
},
@@ -500,7 +505,7 @@ static const struct lm90_params lm90_params[] = {
* be set).
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7b,
.max_convrate = 9,
.reg_local_ext = SA56004_REG_LOCAL_TEMPL,
@@ -508,7 +513,8 @@ static const struct lm90_params lm90_params[] = {
[tmp451] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW
+ | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -517,7 +523,7 @@ static const struct lm90_params lm90_params[] = {
[tmp461] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -1979,7 +1985,8 @@ static void lm90_restore_conf(void *_data)
cancel_delayed_work_sync(&data->alert_work);
/* Restore initial configuration */
- lm90_write_convrate(data, data->convrate_orig);
+ if (data->flags & LM90_HAVE_CONVRATE)
+ lm90_write_convrate(data, data->convrate_orig);
lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig);
}
@@ -1988,10 +1995,15 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
struct device_node *np = client->dev.of_node;
int config, convrate;
- convrate = lm90_read_reg(client, LM90_REG_CONVRATE);
- if (convrate < 0)
- return convrate;
- data->convrate_orig = convrate;
+ if (data->flags & LM90_HAVE_CONVRATE) {
+ convrate = lm90_read_reg(client, LM90_REG_CONVRATE);
+ if (convrate < 0)
+ return convrate;
+ data->convrate_orig = convrate;
+ lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */
+ } else {
+ data->update_interval = 500;
+ }
/*
* Start the conversions.
@@ -2002,8 +2014,6 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
data->config_orig = config;
data->config = config;
- lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */
-
/* Check Temperature Range Select */
if (data->flags & LM90_HAVE_EXTENDED_TEMP) {
if (of_property_read_bool(np, "ti,extended-range-enable"))
@@ -2154,9 +2164,11 @@ static int lm90_probe(struct i2c_client *client)
info->type = hwmon_chip;
info->config = data->chip_config;
- data->chip_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL;
+ data->chip_config[0] = HWMON_C_REGISTER_TZ;
if (data->flags & LM90_HAVE_ALARMS)
data->chip_config[0] |= HWMON_C_ALARMS;
+ if (data->flags & LM90_HAVE_CONVRATE)
+ data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL;
data->info[1] = &data->temp_info;
--
2.35.1
The NCT1008 datasheet, Revision 3, states that its chip revision is
0x57. This matches the ADT7461A chip revision, and NCT1008 is therefore
detected as ADT7461A. In revision 6 of the datasheet, the chip revision
register is no longer documented. Multiple samples of NCT1008 were found
to report a chip revision of 0x54. As it turns out, one of the patches
submitted to add NCT1008 support to the lm90 driver already included a
check for chip revision 0x54. Unfortunately, that patch never made it into
the kernel. Remedy the situation and explicitly detect chips with revision
0x54 as NCT1008.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index f676b809c470..17312d173b8a 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1556,7 +1556,12 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
convrate <= 0x0a)
name = "adt7461";
break;
- case 0x57: /* ADT7461A, NCT1008 */
+ case 0x54: /* NCT1008 */
+ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ convrate <= 0x0a)
+ name = "nct1008";
+ break;
+ case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */
if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
convrate <= 0x0a)
name = "adt7461a";
--
2.35.1
A flag indicating support for minimum temperature limits doesn't cost much
and will enable us to add support for MAX6642 to the lm90 driver.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 54 +++++++++++++++++++++++++-------------------
1 file changed, 31 insertions(+), 23 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 22ea75535ab4..abf1451c5652 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -192,6 +192,7 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99,
#define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/
#define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */
#define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/
+#define LM90_HAVE_LOW BIT(15) /* low limits */
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -369,7 +370,8 @@ static const struct lm90_params lm90_params[] = {
[adm1032] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
- | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS,
+ | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS
+ | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -382,7 +384,7 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
| LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 10,
.resolution = 10,
@@ -390,7 +392,8 @@ static const struct lm90_params lm90_params[] = {
[adt7461a] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
- | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS,
+ | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS
+ | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -398,7 +401,7 @@ static const struct lm90_params lm90_params[] = {
.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,
+ | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW,
.alert_alarms = 0x1c7c,
.max_convrate = 11,
.resolution = 10,
@@ -407,58 +410,58 @@ static const struct lm90_params lm90_params[] = {
[g781] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[lm86] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm90] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm99] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS,
+ | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6648] = {
.flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_BROKEN_ALERT,
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6654] = {
- .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS,
+ .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 7,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6657] = {
.flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6659] = {
.flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -471,14 +474,14 @@ static const struct lm90_params lm90_params[] = {
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT
| LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[max6696] = {
.flags = LM90_HAVE_EMERGENCY
| LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
.reg_status2 = MAX6696_REG_STATUS2,
@@ -486,7 +489,7 @@ static const struct lm90_params lm90_params[] = {
},
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 8,
},
@@ -497,7 +500,7 @@ static const struct lm90_params lm90_params[] = {
* be set).
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7b,
.max_convrate = 9,
.reg_local_ext = SA56004_REG_LOCAL_TEMPL,
@@ -505,7 +508,7 @@ static const struct lm90_params lm90_params[] = {
[tmp451] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS,
+ | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -514,7 +517,7 @@ static const struct lm90_params lm90_params[] = {
[tmp461] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_ALARMS,
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -2161,10 +2164,15 @@ static int lm90_probe(struct i2c_client *client)
info->type = hwmon_temp;
info->config = data->channel_config;
- data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
- HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM;
- data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
- HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_FAULT;
+ data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_MAX_ALARM;
+ data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_MAX_ALARM | HWMON_T_FAULT;
+
+ if (data->flags & LM90_HAVE_LOW) {
+ data->channel_config[0] |= HWMON_T_MIN | HWMON_T_MIN_ALARM;
+ data->channel_config[1] |= HWMON_T_MIN | HWMON_T_MIN_ALARM;
+ }
if (data->flags & LM90_HAVE_CRIT) {
data->channel_config[0] |= HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_CRIT_HYST;
--
2.35.1
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.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 38 +++++++++++
drivers/hwmon/Kconfig | 3 +-
drivers/hwmon/lm90.c | 119 ++++++++++++++++++++++++++---------
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 df4b861024e3..d2477e0c1e1e 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;
}
@@ -1566,9 +1592,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) &&
@@ -1590,6 +1622,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;
}
@@ -1956,9 +2010,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;
/*
@@ -2117,13 +2171,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;
--
2.35.1
LM86 and LM90 support exactly the same features, so there is no need
to keep their configuration options separate. Combine to reduce data
size.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 97453a7de1fa..16481051a530 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -123,7 +123,7 @@ static const unsigned short normal_i2c[] = {
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
- g781, lm84, lm86, lm90, lm99,
+ g781, lm84, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771,
};
@@ -235,8 +235,8 @@ static const struct i2c_device_id lm90_id[] = {
{ "g781", g781 },
{ "gl523sm", max1617 },
{ "lm84", lm84 },
- { "lm86", lm86 },
- { "lm89", lm86 },
+ { "lm86", lm90 },
+ { "lm89", lm90 },
{ "lm90", lm90 },
{ "lm99", lm99 },
{ "max1617", max1617 },
@@ -289,11 +289,11 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
},
{
.compatible = "national,lm86",
- .data = (void *)lm86
+ .data = (void *)lm90
},
{
.compatible = "national,lm89",
- .data = (void *)lm86
+ .data = (void *)lm90
},
{
.compatible = "national,lm99",
@@ -443,13 +443,6 @@ static const struct lm90_params lm90_params[] = {
.flags = LM90_HAVE_ALARMS,
.resolution = 8,
},
- [lm86] = {
- .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
- | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
- .alert_alarms = 0x7b,
- .max_convrate = 9,
- },
[lm90] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW
--
2.35.1
We don't want to support the obsolete 'alarms' attribute for new
chips supported by this driver. Add flag to indicate 'alarms' attribute
support and use it for existing chips. This flag will not be set for
additional chips supported by this driver in the future.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 57 +++++++++++++++++++++++++++++---------------
1 file changed, 38 insertions(+), 19 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index b39e31ded2cb..e23dcf299b03 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -178,6 +178,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
#define LM90_HAVE_CRIT_ALRM_SWP BIT(10) /* critical alarm bits swapped */
#define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */
#define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/
+#define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */
/* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@@ -347,7 +348,7 @@ static const struct lm90_params lm90_params[] = {
[adm1032] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
- | LM90_HAVE_PARTIAL_PEC,
+ | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
@@ -359,7 +360,8 @@ static const struct lm90_params lm90_params[] = {
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
- | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC,
+ | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 10,
.resolution = 10,
@@ -367,55 +369,58 @@ static const struct lm90_params lm90_params[] = {
[adt7461a] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
- | LM90_HAVE_CRIT | LM90_HAVE_PEC,
+ | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 10,
},
[g781] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT,
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[lm86] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm90] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[lm99] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_CRIT,
+ | LM90_HAVE_CRIT | LM90_HAVE_ALARMS,
.alert_alarms = 0x7b,
.max_convrate = 9,
},
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
- | LM90_HAVE_UNSIGNED_TEMP,
+ | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6654] = {
- .flags = LM90_HAVE_BROKEN_ALERT,
+ .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 7,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6657] = {
- .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT,
+ .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[max6659] = {
- .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT,
+ .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -427,19 +432,22 @@ static const struct lm90_params lm90_params[] = {
* be set).
*/
.flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT
- | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT,
+ | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 7,
},
[max6696] = {
.flags = LM90_HAVE_EMERGENCY
- | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT,
+ | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x1c7c,
.max_convrate = 6,
.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
},
[w83l771] = {
- .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT,
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 8,
},
@@ -449,7 +457,8 @@ static const struct lm90_params lm90_params[] = {
* and treated as negative temperatures (meaning min_alarm will
* be set).
*/
- .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT,
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7b,
.max_convrate = 9,
.reg_local_ext = SA56004_REG_LOCAL_TEMPL,
@@ -457,7 +466,7 @@ static const struct lm90_params lm90_params[] = {
[tmp451] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
- | LM90_HAVE_UNSIGNED_TEMP,
+ | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -465,7 +474,8 @@ static const struct lm90_params lm90_params[] = {
},
[tmp461] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
- | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT,
+ | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS,
.alert_alarms = 0x7c,
.max_convrate = 9,
.resolution = 12,
@@ -505,7 +515,9 @@ enum lm90_temp_reg_index {
struct lm90_data {
struct i2c_client *client;
struct device *hwmon_dev;
+ u32 chip_config[2];
u32 channel_config[4];
+ struct hwmon_channel_info chip_info;
struct hwmon_channel_info temp_info;
const struct hwmon_channel_info *info[3];
struct hwmon_chip_info chip;
@@ -2028,8 +2040,15 @@ static int lm90_probe(struct i2c_client *client)
data->chip.ops = &lm90_ops;
data->chip.info = data->info;
- data->info[0] = HWMON_CHANNEL_INFO(chip,
- HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL | HWMON_C_ALARMS);
+ data->info[0] = &data->chip_info;
+ info = &data->chip_info;
+ info->type = hwmon_chip;
+ info->config = data->chip_config;
+
+ data->chip_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL;
+ if (data->flags & LM90_HAVE_ALARMS)
+ data->chip_config[0] |= HWMON_C_ALARMS;
+
data->info[1] = &data->temp_info;
info = &data->temp_info;
--
2.35.1
Both chips are quite similar to other chips of this series, so add
support for them to the lm90 driver. Also mention ON Semiconductor NCT210,
which is pin and register compatible to ADM1021A.
None of the chips support the secondary manufacturer and chip ID registers
at 0x3e and 0x3f, but return 0 when reading from those registers.
Use that information to improve the accuracy of chip detection code.
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.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 23 +++++++++++++++++-
drivers/hwmon/Kconfig | 4 +--
drivers/hwmon/lm90.c | 47 ++++++++++++++++++++++++++++++++----
3 files changed, 66 insertions(+), 8 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 8fe9013b9c39..699ac963722b 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -51,6 +51,22 @@ Supported chips:
http://www.national.com/mpf/LM/LM86.html
+ * Analog Devices ADM1021
+
+ Prefix: 'adm1021'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the Analog Devices website
+
+ * Analog Devices ADM1021A/ADM1023
+
+ Prefix: 'adm1023'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheet: Publicly available at the Analog Devices website
+
* Analog Devices ADM1032
Prefix: 'adm1032'
@@ -369,10 +385,15 @@ features:
LM84:
* 8 bit sensor resolution
-MAX1617:
+ADM1021, MAX1617:
* 8 bit sensor resolution
* Low temperature limits
+ADM1021A, ADM1023:
+ * Temperature offset register for remote temperature sensor
+ * 11 bit resolution for remote temperature sensor
+ * Low temperature limits
+
LM90:
* 11 bit resolution for remote temperature sensor
* Temperature offset register for remote temperature sensor
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 1dd812cf15bb..50fa255b1e3c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1359,8 +1359,8 @@ config SENSORS_LM90
depends on I2C
help
If you say yes here you get support for National Semiconductor LM84,
- LM90, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A,
- ADT7481, ADT7482, and ADT7483A,
+ LM90, LM86, LM89 and LM99, Analog Devices ADM2021, ADM1021A, ADM1023,
+ ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A,
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 09c3b9eb2f8b..40c012bc6ca6 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -122,7 +122,8 @@ 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, adt7481, g781, lm84, lm86, lm90, lm99,
+enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
+ g781, lm84, lm86, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771,
};
@@ -223,6 +224,8 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm86, lm90, lm99,
*/
static const struct i2c_device_id lm90_id[] = {
+ { "adm1021", max1617 },
+ { "adm1023", adm1023 },
{ "adm1032", adm1032 },
{ "adt7461", adt7461 },
{ "adt7461a", adt7461a },
@@ -375,6 +378,14 @@ struct lm90_params {
};
static const struct lm90_params lm90_params[] = {
+ [adm1023] = {
+ .flags = LM90_HAVE_ALARMS | LM90_HAVE_OFFSET | LM90_HAVE_BROKEN_ALERT
+ | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
+ .alert_alarms = 0x7c,
+ .resolution = 8,
+ .max_convrate = 7,
+ },
[adm1032] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
@@ -1740,19 +1751,43 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id,
return name;
}
-static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
- int config1, int convrate)
+static const char *lm90_detect_analog(struct i2c_client *client, bool common_address,
+ int chip_id, int config1, int convrate)
{
+ int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS);
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)
+ if (status < 0 || config2 < 0 || man_id2 < 0 || chip_id2 < 0)
return NULL;
switch (chip_id) {
+ case 0x00 ... 0x0f: /* ADM1021, undocumented */
+ if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address &&
+ !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
+ name = "adm1021";
+ break;
+ case 0x30 ... 0x3f: /* ADM1021A, ADM1023 */
+ /*
+ * ADM1021A and compatible chips will be mis-detected as
+ * ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both
+ * found to have a Chip ID of 0x3c.
+ * ADM1021A does not officially support low byte registers
+ * (0x12 .. 0x14), but a chip labeled ADM1021A does support it.
+ * Official support for the temperature offset high byte
+ * register (0x11) was added to revision F of the ADM1021A
+ * datasheet.
+ * It is currently unknown if there is a means to distinguish
+ * ADM1021A from ADM1023, and/or if revisions of ADM1021A exist
+ * which differ in functionality from ADM1023.
+ */
+ if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address &&
+ !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8))
+ name = "adm1023";
+ break;
case 0x40 ... 0x4f: /* ADM1032 */
if (man_id2 == 0x00 && chip_id2 == 0x00 &&
(address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
@@ -1793,6 +1828,7 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
break;
case 0x94: /* ADT7483 */
if (man_id2 == 0x41 && chip_id2 == 0x83 &&
+ common_address &&
((address >= 0x18 && address <= 0x1a) ||
(address >= 0x29 && address <= 0x2b) ||
(address >= 0x4c && address <= 0x4e)) &&
@@ -2173,7 +2209,8 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
name = lm90_detect_national(client, chip_id, config1, convrate);
break;
case 0x41: /* Analog Devices */
- name = lm90_detect_analog(client, chip_id, config1, convrate);
+ name = lm90_detect_analog(client, common_address, chip_id, config1,
+ convrate);
break;
case 0x47: /* GMT */
name = lm90_detect_gmt(client, chip_id, config1, convrate);
--
2.35.1
Add table with device names and known register values for supported
devices from Analog / ON Semiconductor.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 77102e0a4440..7f8397c362e8 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1828,6 +1828,42 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add
if (status < 0 || config2 < 0 || man_id2 < 0 || chip_id2 < 0)
return NULL;
+ /*
+ * The following chips should be detected by this function. Known
+ * register values are listed. Registers 0x3d .. 0x3e are undocumented
+ * for most of the chips, yet appear to return a well defined value.
+ * Register 0xff is undocumented for some of the chips. Register 0x3f
+ * is undocumented for all chips, but also returns a well defined value.
+ * Values are as reported from real chips unless mentioned otherwise.
+ * The code below checks values for registers 0x3d, 0x3e, and 0xff,
+ * but not for register 0x3f.
+ *
+ * Chip Register
+ * 3d 3e 3f fe ff Notes
+ * ----------------------------------------------------------
+ * adm1020 00 00 00 41 39
+ * adm1021 00 00 00 41 03
+ * adm1021a 00 00 00 41 3c
+ * adm1023 00 00 00 41 3c same as adm1021a
+ * adm1032 00 00 00 41 42
+ *
+ * adt7421 21 41 04 41 04
+ * adt7461 00 00 00 41 51
+ * adt7461a 61 41 05 41 57
+ * adt7481 81 41 02 41 62
+ * adt7482 - - - 41 65 datasheet
+ * 82 41 05 41 75 real chip
+ * adt7483 83 41 04 41 94
+ *
+ * nct72 61 41 07 41 55
+ * nct210 00 00 00 41 3f
+ * nct214 61 41 08 41 5a
+ * nct1008 - - - 41 57 datasheet rev. 3
+ * 61 41 06 41 54 real chip
+ *
+ * nvt210 - - - 41 - datasheet
+ * nvt211 - - - 41 - datasheet
+ */
switch (chip_id) {
case 0x00 ... 0x03: /* ADM1021 */
case 0x05 ... 0x0f:
--
2.35.1
While most LM90 compatible chips support a temperature sensor resolution
of 11 bit, this is not the case for all chips. ADT7461 only supports a
resolution of 10 bit, and TMP451/TMP461 support a resolution of 12 bit.
Add support for various temperature sensor resolutions. To do this,
model all temperature sensors as 16 bit sensors, and use unified
temperature conversion functions which take the sensor resolution
as parameter.
While enhancing functionality, this has the positive side effect of
reducing code size by about 5%.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 450 +++++++++++++++----------------------------
1 file changed, 153 insertions(+), 297 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index b20be0cb28b5..8e5f791aca07 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -343,6 +343,7 @@ struct lm90_params {
u16 alert_alarms; /* Which alarm bits trigger ALERT# */
/* Upper 8 bits for max6695/96 */
u8 max_convrate; /* Maximum conversion rate register value */
+ u8 resolution; /* 16-bit resolution (default 11 bit) */
u8 reg_local_ext; /* Extended local temp register (optional) */
};
@@ -365,6 +366,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC,
.alert_alarms = 0x7c,
.max_convrate = 10,
+ .resolution = 10,
},
[adt7461a] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
@@ -462,6 +464,7 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_UNSIGNED_TEMP,
.alert_alarms = 0x7c,
.max_convrate = 9,
+ .resolution = 12,
.reg_local_ext = TMP451_REG_LOCAL_TEMPL,
},
[tmp461] = {
@@ -469,14 +472,15 @@ static const struct lm90_params lm90_params[] = {
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT,
.alert_alarms = 0x7c,
.max_convrate = 9,
+ .resolution = 12,
.reg_local_ext = TMP451_REG_LOCAL_TEMPL,
},
};
/*
- * TEMP8 register index
+ * temperature register index
*/
-enum lm90_temp8_reg_index {
+enum lm90_temp_reg_index {
LOCAL_LOW = 0,
LOCAL_HIGH,
LOCAL_CRIT,
@@ -485,14 +489,8 @@ enum lm90_temp8_reg_index {
REMOTE_EMERG, /* max6659 and max6695/96 */
REMOTE2_CRIT, /* max6695/96 only */
REMOTE2_EMERG, /* max6695/96 only */
- TEMP8_REG_NUM
-};
-/*
- * TEMP11 register index
- */
-enum lm90_temp11_reg_index {
- REMOTE_TEMP = 0,
+ REMOTE_TEMP,
REMOTE_LOW,
REMOTE_HIGH,
REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */
@@ -500,7 +498,8 @@ enum lm90_temp11_reg_index {
REMOTE2_TEMP, /* max6695/96 only */
REMOTE2_LOW, /* max6695/96 only */
REMOTE2_HIGH, /* max6695/96 only */
- TEMP11_REG_NUM
+
+ TEMP_REG_NUM
};
/*
@@ -528,14 +527,14 @@ struct lm90_data {
u8 config; /* Current configuration register value */
u8 config_orig; /* Original configuration register value */
u8 convrate_orig; /* Original conversion rate register value */
+ u8 resolution; /* temperature resolution in bit */
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 */
/* registers values */
- s8 temp8[TEMP8_REG_NUM];
- s16 temp11[TEMP11_REG_NUM];
+ u16 temp[TEMP_REG_NUM];
u8 temp_hyst;
u16 reported_alarms; /* alarms reported as sysfs/udev events */
u16 current_alarms; /* current alarms, reported by chip */
@@ -732,12 +731,12 @@ static int lm90_update_limits(struct device *dev)
val = lm90_read_reg(client, LM90_REG_LOCAL_CRIT);
if (val < 0)
return val;
- data->temp8[LOCAL_CRIT] = val;
+ data->temp[LOCAL_CRIT] = val << 8;
val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT);
if (val < 0)
return val;
- data->temp8[REMOTE_CRIT] = val;
+ data->temp[REMOTE_CRIT] = val << 8;
val = lm90_read_reg(client, LM90_REG_TCRIT_HYST);
if (val < 0)
@@ -748,25 +747,25 @@ static int lm90_update_limits(struct device *dev)
val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
if (val < 0)
return val;
- data->temp11[REMOTE_LOW] = val << 8;
+ data->temp[REMOTE_LOW] = val << 8;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
val = lm90_read_reg(client, LM90_REG_REMOTE_LOWL);
if (val < 0)
return val;
- data->temp11[REMOTE_LOW] |= val;
+ data->temp[REMOTE_LOW] |= val;
}
val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH);
if (val < 0)
return val;
- data->temp11[REMOTE_HIGH] = val << 8;
+ data->temp[REMOTE_HIGH] = val << 8;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHL);
if (val < 0)
return val;
- data->temp11[REMOTE_HIGH] |= val;
+ data->temp[REMOTE_HIGH] |= val;
}
if (data->flags & LM90_HAVE_OFFSET) {
@@ -774,19 +773,19 @@ static int lm90_update_limits(struct device *dev)
LM90_REG_REMOTE_OFFSL, false);
if (val < 0)
return val;
- data->temp11[REMOTE_OFFSET] = val;
+ data->temp[REMOTE_OFFSET] = val;
}
if (data->flags & LM90_HAVE_EMERGENCY) {
val = lm90_read_reg(client, MAX6659_REG_LOCAL_EMERG);
if (val < 0)
return val;
- data->temp8[LOCAL_EMERG] = val;
+ data->temp[LOCAL_EMERG] = val << 8;
val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
if (val < 0)
return val;
- data->temp8[REMOTE_EMERG] = val;
+ data->temp[REMOTE_EMERG] = val << 8;
}
if (data->kind == max6696) {
@@ -797,22 +796,22 @@ static int lm90_update_limits(struct device *dev)
val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT);
if (val < 0)
return val;
- data->temp8[REMOTE2_CRIT] = val;
+ data->temp[REMOTE2_CRIT] = val << 8;
val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
if (val < 0)
return val;
- data->temp8[REMOTE2_EMERG] = val;
+ data->temp[REMOTE2_EMERG] = val << 8;
val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH);
if (val < 0)
return val;
- data->temp11[REMOTE2_LOW] = val << 8;
+ data->temp[REMOTE2_LOW] = val << 8;
val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH);
if (val < 0)
return val;
- data->temp11[REMOTE2_HIGH] = val << 8;
+ data->temp[REMOTE2_HIGH] = val << 8;
lm90_select_remote_channel(data, 0);
}
@@ -995,30 +994,30 @@ static int lm90_update_device(struct device *dev)
val = lm90_read_reg(client, LM90_REG_LOCAL_LOW);
if (val < 0)
return val;
- data->temp8[LOCAL_LOW] = val;
+ data->temp[LOCAL_LOW] = val << 8;
val = lm90_read_reg(client, LM90_REG_LOCAL_HIGH);
if (val < 0)
return val;
- data->temp8[LOCAL_HIGH] = val;
+ data->temp[LOCAL_HIGH] = val << 8;
if (data->reg_local_ext) {
val = lm90_read16(client, LM90_REG_LOCAL_TEMP,
data->reg_local_ext, true);
if (val < 0)
return val;
- data->temp11[LOCAL_TEMP] = val;
+ data->temp[LOCAL_TEMP] = val;
} else {
val = lm90_read_reg(client, LM90_REG_LOCAL_TEMP);
if (val < 0)
return val;
- data->temp11[LOCAL_TEMP] = val << 8;
+ data->temp[LOCAL_TEMP] = val << 8;
}
val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
LM90_REG_REMOTE_TEMPL, true);
if (val < 0)
return val;
- data->temp11[REMOTE_TEMP] = val;
+ data->temp[REMOTE_TEMP] = val;
if (data->kind == max6696) {
val = lm90_select_remote_channel(data, 1);
@@ -1031,7 +1030,7 @@ static int lm90_update_device(struct device *dev)
lm90_select_remote_channel(data, 0);
return val;
}
- data->temp11[REMOTE2_TEMP] = val;
+ data->temp[REMOTE2_TEMP] = val;
lm90_select_remote_channel(data, 0);
}
@@ -1047,123 +1046,6 @@ static int lm90_update_device(struct device *dev)
return 0;
}
-/*
- * Conversions
- * For local temperatures and limits, critical limits and the hysteresis
- * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
- * For remote temperatures and limits, it uses signed 11-bit values with
- * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some
- * Maxim chips use unsigned values.
- */
-
-static inline int temp_from_s8(s8 val)
-{
- return val * 1000;
-}
-
-static inline int temp_from_u8(u8 val)
-{
- return val * 1000;
-}
-
-static inline int temp_from_s16(s16 val)
-{
- return val / 32 * 125;
-}
-
-static inline int temp_from_u16(u16 val)
-{
- return val / 32 * 125;
-}
-
-static s8 temp_to_s8(long val)
-{
- if (val <= -128000)
- return -128;
- if (val >= 127000)
- return 127;
- if (val < 0)
- return (val - 500) / 1000;
- return (val + 500) / 1000;
-}
-
-static u8 temp_to_u8(long val)
-{
- if (val <= 0)
- return 0;
- if (val >= 255000)
- return 255;
- return (val + 500) / 1000;
-}
-
-static s16 temp_to_s16(long val)
-{
- if (val <= -128000)
- return 0x8000;
- if (val >= 127875)
- return 0x7FE0;
- if (val < 0)
- return (val - 62) / 125 * 32;
- return (val + 62) / 125 * 32;
-}
-
-static u8 hyst_to_reg(long val)
-{
- if (val <= 0)
- return 0;
- if (val >= 30500)
- return 31;
- return (val + 500) / 1000;
-}
-
-/*
- * 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 inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val)
-{
- if (data->flags & LM90_FLAG_ADT7461_EXT)
- return (val - 64) * 1000;
- return temp_from_s8(val);
-}
-
-static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val)
-{
- if (data->flags & LM90_FLAG_ADT7461_EXT)
- return (val - 0x4000) / 64 * 250;
- return temp_from_s16(val);
-}
-
-static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
-{
- if (data->flags & LM90_FLAG_ADT7461_EXT) {
- val = clamp_val(val, -64000, 191000);
- val += 64000;
- } else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) {
- val = clamp_val(val, 0, 127000);
- } else {
- val = clamp_val(val, -128000, 127000);
- }
- return DIV_ROUND_CLOSEST(val, 1000);
-}
-
-static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
-{
- if (data->flags & LM90_FLAG_ADT7461_EXT) {
- val = clamp_val(val, -64000, 191000);
- val += 64000;
- } else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) {
- val = clamp_val(val, 0, 127000);
- } else {
- val = clamp_val(val, -128000, 127000);
- }
- return DIV_ROUND_CLOSEST(val, 1000) & 0xfff0;
-}
-
/* pec used for devices with PEC support */
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
@@ -1200,159 +1082,141 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *dummy,
static DEVICE_ATTR_RW(pec);
-static int lm90_get_temp11(struct lm90_data *data, int index)
+static int lm90_temp_get_resolution(struct lm90_data *data, int index)
{
- s16 temp11 = data->temp11[index];
- int temp;
-
- if (data->flags & LM90_HAVE_EXTENDED_TEMP)
- temp = temp_from_u16_adt7461(data, temp11);
- else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
- temp = temp_from_u16(temp11);
- else
- temp = temp_from_s16(temp11);
-
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && index <= 2)
- temp += 16000;
-
- return temp;
+ switch (index) {
+ case REMOTE_TEMP:
+ case REMOTE_OFFSET:
+ case REMOTE2_TEMP:
+ return data->resolution;
+ case LOCAL_TEMP:
+ if (data->reg_local_ext)
+ return data->resolution;
+ return 8;
+ case REMOTE_LOW:
+ case REMOTE_HIGH:
+ case REMOTE2_LOW:
+ case REMOTE2_HIGH:
+ if (data->flags & LM90_HAVE_REM_LIMIT_EXT)
+ return data->resolution;
+ return 8;
+ default:
+ return 8;
+ }
}
-static int lm90_set_temp11(struct lm90_data *data, int index, long val)
+static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution)
{
- static struct reg {
- u8 high;
- u8 low;
- } reg[] = {
- [REMOTE_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL },
- [REMOTE_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL },
- [REMOTE_OFFSET] = { LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL },
- [REMOTE2_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL },
- [REMOTE2_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL }
- };
- struct i2c_client *client = data->client;
- struct reg *regp = ®[index];
- int err;
-
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && index <= 2) {
- /* prevent integer underflow */
- val = max(val, -128000l);
- val -= 16000;
- }
+ int val;
- if (data->flags & LM90_HAVE_EXTENDED_TEMP)
- data->temp11[index] = temp_to_u16_adt7461(data, val);
- else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
- data->temp11[index] = temp_to_u8(val) << 8;
- else if (data->flags & LM90_HAVE_REM_LIMIT_EXT)
- data->temp11[index] = temp_to_s16(val);
+ if (flags & LM90_FLAG_ADT7461_EXT)
+ val = regval - 0x4000;
+ else if (flags & LM90_HAVE_UNSIGNED_TEMP)
+ val = regval;
else
- data->temp11[index] = temp_to_s8(val) << 8;
-
- lm90_select_remote_channel(data, index >= 3);
- err = lm90_write_reg(client, regp->high, data->temp11[index] >> 8);
- if (err < 0)
- return err;
- if (data->flags & LM90_HAVE_REM_LIMIT_EXT)
- err = lm90_write_reg(client, regp->low,
- data->temp11[index] & 0xff);
+ val = (s16)regval;
- lm90_select_remote_channel(data, 0);
- return err;
+ return ((val >> (16 - resolution)) * 1000) >> (resolution - 8);
}
-static int lm90_get_temp8(struct lm90_data *data, int index)
+static int lm90_get_temp(struct lm90_data *data, int index, int channel)
{
- s8 temp8 = data->temp8[index];
- int temp;
-
- if (data->flags & LM90_HAVE_EXTENDED_TEMP)
- temp = temp_from_u8_adt7461(data, temp8);
- else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
- temp = temp_from_u8(temp8);
- else
- temp = temp_from_s8(temp8);
+ int temp = lm90_temp_from_reg(data->flags, data->temp[index],
+ lm90_temp_get_resolution(data, index));
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && index == 3)
+ /* +16 degrees offset for remote temperature on LM99 */
+ if (data->kind == lm99 && channel)
temp += 16000;
return temp;
}
-static int lm90_set_temp8(struct lm90_data *data, int index, long val)
+static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution)
{
- static const u8 reg[TEMP8_REG_NUM] = {
- LM90_REG_LOCAL_LOW,
- LM90_REG_LOCAL_HIGH,
- LM90_REG_LOCAL_CRIT,
- LM90_REG_REMOTE_CRIT,
- MAX6659_REG_LOCAL_EMERG,
- MAX6659_REG_REMOTE_EMERG,
- LM90_REG_REMOTE_CRIT,
- MAX6659_REG_REMOTE_EMERG,
+ int fraction = resolution > 8 ?
+ 1000 - DIV_ROUND_CLOSEST(1000, BIT(resolution - 8)) : 0;
+
+ if (flags & LM90_FLAG_ADT7461_EXT) {
+ val = clamp_val(val, -64000, 191000 + fraction);
+ val += 64000;
+ } else if (flags & LM90_HAVE_UNSIGNED_TEMP) {
+ val = clamp_val(val, 0, 127000 + fraction);
+ } else {
+ val = clamp_val(val, -128000, 127000 + fraction);
+ }
+
+ return DIV_ROUND_CLOSEST(val << (resolution - 8), 1000) << (16 - resolution);
+}
+
+static int lm90_set_temp(struct lm90_data *data, int index, int channel, long val)
+{
+ static const u8 regs[] = {
+ [LOCAL_LOW] = LM90_REG_LOCAL_LOW,
+ [LOCAL_HIGH] = LM90_REG_LOCAL_HIGH,
+ [LOCAL_CRIT] = LM90_REG_LOCAL_CRIT,
+ [REMOTE_CRIT] = LM90_REG_REMOTE_CRIT,
+ [LOCAL_EMERG] = MAX6659_REG_LOCAL_EMERG,
+ [REMOTE_EMERG] = MAX6659_REG_REMOTE_EMERG,
+ [REMOTE2_CRIT] = LM90_REG_REMOTE_CRIT,
+ [REMOTE2_EMERG] = MAX6659_REG_REMOTE_EMERG,
+ [REMOTE_LOW] = LM90_REG_REMOTE_LOWH,
+ [REMOTE_HIGH] = LM90_REG_REMOTE_HIGHH,
+ [REMOTE2_LOW] = LM90_REG_REMOTE_LOWH,
+ [REMOTE2_HIGH] = LM90_REG_REMOTE_HIGHH,
};
struct i2c_client *client = data->client;
+ u8 regh = regs[index];
+ u8 regl = 0;
int err;
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && index == 3) {
+ if (channel && (data->flags & LM90_HAVE_REM_LIMIT_EXT)) {
+ if (index == REMOTE_LOW || index == REMOTE2_LOW)
+ regl = LM90_REG_REMOTE_LOWL;
+ else if (index == REMOTE_HIGH || index == REMOTE2_HIGH)
+ regl = LM90_REG_REMOTE_HIGHL;
+ }
+
+ /* +16 degrees offset for remote temperature on LM99 */
+ if (data->kind == lm99 && channel) {
/* prevent integer underflow */
val = max(val, -128000l);
val -= 16000;
}
- if (data->flags & LM90_HAVE_EXTENDED_TEMP)
- data->temp8[index] = temp_to_u8_adt7461(data, val);
- else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
- data->temp8[index] = temp_to_u8(val);
- else
- data->temp8[index] = temp_to_s8(val);
+ data->temp[index] = lm90_temp_to_reg(data->flags, val,
+ lm90_temp_get_resolution(data, index));
- lm90_select_remote_channel(data, index >= 6);
- err = lm90_write_reg(client, reg[index], data->temp8[index]);
- lm90_select_remote_channel(data, 0);
+ if (channel > 1)
+ lm90_select_remote_channel(data, 1);
+
+ err = lm90_write_reg(client, regh, data->temp[index] >> 8);
+ if (err < 0)
+ goto deselect;
+ if (regl)
+ err = lm90_write_reg(client, regl, data->temp[index] & 0xff);
+deselect:
+ if (channel > 1)
+ lm90_select_remote_channel(data, 0);
return err;
}
-static int lm90_get_temphyst(struct lm90_data *data, int index)
+static int lm90_get_temphyst(struct lm90_data *data, int index, int channel)
{
- int temp;
-
- if (data->flags & LM90_HAVE_EXTENDED_TEMP)
- temp = temp_from_u8_adt7461(data, data->temp8[index]);
- else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
- temp = temp_from_u8(data->temp8[index]);
- else
- temp = temp_from_s8(data->temp8[index]);
-
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && index == 3)
- temp += 16000;
+ int temp = lm90_get_temp(data, index, channel);
- return temp - temp_from_s8(data->temp_hyst);
+ return temp - data->temp_hyst * 1000;
}
static int lm90_set_temphyst(struct lm90_data *data, long val)
{
- struct i2c_client *client = data->client;
- int temp;
-
- if (data->flags & LM90_HAVE_EXTENDED_TEMP)
- temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
- else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
- temp = temp_from_u8(data->temp8[LOCAL_CRIT]);
- else
- temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
+ int temp = lm90_get_temp(data, LOCAL_CRIT, 0);
/* prevent integer overflow/underflow */
val = clamp_val(val, -128000l, 255000l);
+ data->temp_hyst = clamp_val(DIV_ROUND_CLOSEST(temp - val, 1000), 0, 31);
- data->temp_hyst = hyst_to_reg(temp - val);
- return lm90_write_reg(client, LM90_REG_TCRIT_HYST, data->temp_hyst);
+ return lm90_write_reg(data->client, LM90_REG_TCRIT_HYST, data->temp_hyst);
}
static const u8 lm90_temp_index[3] = {
@@ -1396,7 +1260,7 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
switch (attr) {
case hwmon_temp_input:
- *val = lm90_get_temp11(data, lm90_temp_index[channel]);
+ *val = lm90_get_temp(data, lm90_temp_index[channel], channel);
break;
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
@@ -1428,35 +1292,26 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
data->alarms |= data->current_alarms;
break;
case hwmon_temp_min:
- if (channel == 0)
- *val = lm90_get_temp8(data,
- lm90_temp_min_index[channel]);
- else
- *val = lm90_get_temp11(data,
- lm90_temp_min_index[channel]);
+ *val = lm90_get_temp(data, lm90_temp_min_index[channel], channel);
break;
case hwmon_temp_max:
- if (channel == 0)
- *val = lm90_get_temp8(data,
- lm90_temp_max_index[channel]);
- else
- *val = lm90_get_temp11(data,
- lm90_temp_max_index[channel]);
+ *val = lm90_get_temp(data, lm90_temp_max_index[channel], channel);
break;
case hwmon_temp_crit:
- *val = lm90_get_temp8(data, lm90_temp_crit_index[channel]);
+ *val = lm90_get_temp(data, lm90_temp_crit_index[channel], channel);
break;
case hwmon_temp_crit_hyst:
- *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel]);
+ *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel], channel);
break;
case hwmon_temp_emergency:
- *val = lm90_get_temp8(data, lm90_temp_emerg_index[channel]);
+ *val = lm90_get_temp(data, lm90_temp_emerg_index[channel], channel);
break;
case hwmon_temp_emergency_hyst:
- *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]);
+ *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel], channel);
break;
case hwmon_temp_offset:
- *val = lm90_get_temp11(data, REMOTE_OFFSET);
+ *val = lm90_temp_from_reg(0, data->temp[REMOTE_OFFSET],
+ lm90_temp_get_resolution(data, REMOTE_OFFSET));
break;
default:
return -EOPNOTSUPP;
@@ -1477,36 +1332,36 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
switch (attr) {
case hwmon_temp_min:
- if (channel == 0)
- err = lm90_set_temp8(data,
- lm90_temp_min_index[channel],
- val);
- else
- err = lm90_set_temp11(data,
- lm90_temp_min_index[channel],
- val);
+ err = lm90_set_temp(data, lm90_temp_min_index[channel],
+ channel, val);
break;
case hwmon_temp_max:
- if (channel == 0)
- err = lm90_set_temp8(data,
- lm90_temp_max_index[channel],
- val);
- else
- err = lm90_set_temp11(data,
- lm90_temp_max_index[channel],
- val);
+ err = lm90_set_temp(data, lm90_temp_max_index[channel],
+ channel, val);
break;
case hwmon_temp_crit:
- err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val);
+ err = lm90_set_temp(data, lm90_temp_crit_index[channel],
+ channel, val);
break;
case hwmon_temp_crit_hyst:
err = lm90_set_temphyst(data, val);
break;
case hwmon_temp_emergency:
- err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val);
+ err = lm90_set_temp(data, lm90_temp_emerg_index[channel],
+ channel, val);
break;
case hwmon_temp_offset:
- err = lm90_set_temp11(data, REMOTE_OFFSET, val);
+ val = lm90_temp_to_reg(0, val,
+ lm90_temp_get_resolution(data, REMOTE_OFFSET));
+ data->temp[REMOTE_OFFSET] = val;
+ err = i2c_smbus_write_byte_data(data->client,
+ LM90_REG_REMOTE_OFFSH,
+ val >> 8);
+ if (err)
+ break;
+ err = i2c_smbus_write_byte_data(data->client,
+ LM90_REG_REMOTE_OFFSL,
+ val & 0xff);
break;
default:
err = -EOPNOTSUPP;
@@ -2035,6 +1890,7 @@ static int lm90_probe(struct i2c_client *client)
* ALERT# output
*/
data->alert_alarms = lm90_params[data->kind].alert_alarms;
+ data->resolution = lm90_params[data->kind].resolution ? : 11;
/* Set chip capabilities */
data->flags = lm90_params[data->kind].flags;
--
2.35.1
ADT7461A and NCT1008 support the undocumented manufacturer and chip ID
registers at 0x3e and 0x3f, and return 0x61 as chip ID. ADM1032 and
ADT7461 do not support those registers but return 0 when reading them.
Use this information to improve the accuracy of the chip detection code.
Signed-off-by: Guenter Roeck <[email protected]>
---
drivers/hwmon/lm90.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index d2477e0c1e1e..abc64738e892 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1603,22 +1603,26 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
switch (chip_id) {
case 0x40 ... 0x4f: /* ADM1032 */
- if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
+ if (man_id2 == 0x00 && chip_id2 == 0x00 &&
+ (address == 0x4c || address == 0x4d) && !(config1 & 0x3f) &&
convrate <= 0x0a)
name = "adm1032";
break;
case 0x51: /* ADT7461 */
- if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ if (man_id2 == 0x00 && chip_id2 == 0x00 &&
+ (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
convrate <= 0x0a)
name = "adt7461";
break;
case 0x54: /* NCT1008 */
- if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ if (man_id2 == 0x41 && chip_id2 == 0x61 &&
+ (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
convrate <= 0x0a)
name = "nct1008";
break;
case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */
- if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ if (man_id2 == 0x41 && chip_id2 == 0x61 &&
+ (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
convrate <= 0x0a)
name = "adt7461a";
break;
--
2.35.1
MAX6642 is a reduced version of LM90 with no low limits and no conversion
rate register. Its alert functionality is broken, similar to many other
chips supported by the lm90 driver.
After this change, the stand-alone max6642 driver is only needed if the
lm90 driver is disabled. Make it dependent on SENSORS_LM90=n to show that
it is not needed if the lm90 driver is enabled.
A devicetree node is not added for this chip since it is quite unlikely
that such an old chip will ever be used in a devicetree based system.
It can be added later if needed.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 16 ++++++++
drivers/hwmon/Kconfig | 5 ++-
drivers/hwmon/lm90.c | 78 +++++++++++++++++++++++++++++-------
3 files changed, 83 insertions(+), 16 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 45bc333a1219..313b18f6531d 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -113,6 +113,16 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
+ * Maxim MAX6642
+
+ Prefix: 'max6642'
+
+ Addresses scanned: I2C 0x48-0x4f
+
+ Datasheet: Publicly available at the Maxim website
+
+ http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
+
* Maxim MAX6646
Prefix: 'max6646'
@@ -362,6 +372,12 @@ ADT7481, ADT7482, ADT7483:
* Two remote sensors
* Selectable address (ADT7483)
+MAX6642:
+ * No critical limit register
+ * Conversion rate not configurable
+ * Better local resolution (10 bit)
+ * 10 bit external sensor resolution
+
MAX6646, MAX6647, MAX6649:
* Better local resolution
* Extended range unsigned external temperature
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index df54628bd36b..39ce1b2ccbb3 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1102,6 +1102,7 @@ config SENSORS_MAX6639
config SENSORS_MAX6642
tristate "Maxim MAX6642 sensor chip"
depends on I2C
+ depends on SENSORS_LM90=n
help
If you say yes here you get support for MAX6642 sensor chip.
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
@@ -1360,8 +1361,8 @@ config SENSORS_LM90
If you say yes here you get support for National Semiconductor LM90,
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,
+ Maxim MAX6642, 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.
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 90c3a496bb6c..78eda648d960 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -120,7 +120,7 @@ static const unsigned short normal_i2c[] = {
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99,
- max6646, max6648, max6654, max6657, max6659, max6680, max6696,
+ max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
sa56004, tmp451, tmp461, w83l771,
};
@@ -230,6 +230,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "lm86", lm86 },
{ "lm89", lm86 },
{ "lm99", lm99 },
+ { "max6642", max6642 },
{ "max6646", max6646 },
{ "max6647", max6646 },
{ "max6648", max6648 },
@@ -434,6 +435,12 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7b,
.max_convrate = 9,
},
+ [max6642] = {
+ .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED,
+ .alert_alarms = 0x50,
+ .reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
+ .resolution = 10,
+ },
[max6646] = {
.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
| LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW
@@ -1666,18 +1673,18 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id,
return name;
}
-static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
- int config1, int convrate)
+static const char *lm90_detect_maxim(struct i2c_client *client, bool common_address,
+ int chip_id, int config1, int convrate)
{
int man_id, emerg, emerg2, status2;
int address = client->addr;
const char *name = NULL;
- if ((address >= 0x48 && address <= 0x4b) || address == 0x4f)
- return NULL;
-
switch (chip_id) {
case 0x01:
+ if (!common_address)
+ break;
+
/*
* We read MAX6659_REG_REMOTE_EMERG twice, and re-read
* LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG
@@ -1724,7 +1731,7 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
* The lowest 3 bits of the config1 register are unused and
* should return zero when read.
*/
- if (!(config1 & 0x07) && convrate <= 0x07)
+ if (common_address && !(config1 & 0x07) && convrate <= 0x07)
name = "max6654";
break;
case 0x09:
@@ -1737,16 +1744,21 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
* MAX6690 datasheet lists a chip ID of 0x08, and a chip labeled
* MAX6654 was observed to have a chip ID of 0x09.
*/
- if (!(config1 & 0x07) && convrate <= 0x07)
+ if (common_address && !(config1 & 0x07) && convrate <= 0x07)
name = "max6690";
break;
case 0x4d:
/*
- * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
+ * MAX6642, MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
* register. Reading from that address will return the last
* read value, which in our case is those of the man_id
- * register, or 0x4d. Likewise, the config1 register seems to
- * lack a low nibble, so the value will be those of the previous
+ * register, or 0x4d.
+ * MAX6642 does not have a conversion rate register, nor low
+ * limit registers. Reading from those registers returns the
+ * last read value.
+ *
+ * For MAX6657, MAX6658 and MAX6659, the config1 register lacks
+ * a low nibble, so the value will be those of the previous
* read, so in our case again those of the man_id register.
* MAX6659 has a third set of upper temperature limit registers.
* Those registers also return values on MAX6657 and MAX6658,
@@ -1754,8 +1766,40 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id,
* For this reason it will be mis-detected as MAX6657 if its
* address is 0x4c.
*/
- if ((address == 0x4c || address == 0x4d || address == 0x4e) &&
- (config1 & 0x1f) == 0x0d && convrate <= 0x09) {
+ if (address >= 0x48 && address <= 0x4f && config1 == convrate &&
+ !(config1 & 0x0f)) {
+ int regval;
+
+ /*
+ * We know that this is not a MAX6657/58/59 because its
+ * configuration register has the wrong value and it does
+ * not appear to have a conversion rate register.
+ */
+
+ /* re-read manufacturer ID to have a good baseline */
+ if (i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID) != 0x4d)
+ break;
+
+ /* check various non-existing registers */
+ if (i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE) != 0x4d ||
+ i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW) != 0x4d ||
+ i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH) != 0x4d)
+ break;
+
+ /* check for unused status register bits */
+ regval = i2c_smbus_read_byte_data(client, LM90_REG_STATUS);
+ if (regval < 0 || (regval & 0x2b))
+ break;
+
+ /* re-check unsupported registers */
+ if (i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE) != regval ||
+ i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW) != regval ||
+ i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH) != regval)
+ break;
+
+ name = "max6642";
+ } else if ((address == 0x4c || address == 0x4d || address == 0x4e) &&
+ (config1 & 0x1f) == 0x0d && convrate <= 0x09) {
if (address == 0x4c)
name = "max6657";
else
@@ -1927,6 +1971,11 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
struct i2c_adapter *adapter = client->adapter;
int man_id, chip_id, config1, convrate;
const char *name = NULL;
+ int address = client->addr;
+ bool common_address =
+ (address >= 0x18 && address <= 0x1a) ||
+ (address >= 0x29 && address <= 0x2b) ||
+ (address >= 0x4c && address <= 0x4e);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -1950,7 +1999,8 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
name = lm90_detect_gmt(client, chip_id, config1, convrate);
break;
case 0x4d: /* Maxim Integrated */
- name = lm90_detect_maxim(client, chip_id, config1, convrate);
+ name = lm90_detect_maxim(client, common_address, chip_id,
+ config1, convrate);
break;
case 0x55: /* TI */
name = lm90_detect_ti(client, chip_id, config1, convrate);
--
2.35.1
NE1618 is similar to NE1617 but supports manufacturer and chip ID
registers as well as 11 bit external temperature resolution.
Signed-off-by: Guenter Roeck <[email protected]>
---
Documentation/hwmon/lm90.rst | 10 +++++++-
drivers/hwmon/Kconfig | 2 +-
drivers/hwmon/lm90.c | 44 ++++++++++++++++++++++++++----------
3 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index b2ca10f37218..ea1556be645c 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -411,6 +411,14 @@ Supported chips:
Datasheets: Publicly available at the Philips website
+ * Philips NE1618
+
+ Prefix: 'ne1618'
+
+ Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
+
+ Datasheets: Publicly available at the Philips website
+
* Genesys Logic GL523SM
Prefix: 'gl523sm'
@@ -469,7 +477,7 @@ ADM1020, ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
* 8 bit sensor resolution
* Low temperature limits
-NCT210:
+NCT210, NE1618:
* 11 bit sensor resolution for remote temperature sensor
* Low temperature limits
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 494539e4be3d..981df62bd6ab 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1367,7 +1367,7 @@ config SENSORS_LM90
MAX6696,
ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218,
Winbond/Nuvoton W83L771W/G/AWG/ASG,
- Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
+ Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
This driver can also be built as a module. If so, the module
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index de51d205b63f..77102e0a4440 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -87,6 +87,9 @@
* This driver also supports MAX1617 and various clones such as G767
* and NE1617. Such clones will be detected as MAX1617.
*
+ * This driver also supports NE1618 from Philips. It is similar to NE1617
+ * but supports 11 bit external temperature values.
+ *
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
@@ -129,7 +132,7 @@ static const unsigned short normal_i2c[] = {
enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
g781, lm84, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
- nct210, nct72, sa56004, tmp451, tmp461, w83l771,
+ nct210, nct72, ne1618, sa56004, tmp451, tmp461, w83l771,
};
/*
@@ -267,6 +270,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "nct214", nct72 },
{ "nct218", nct72 },
{ "nct72", nct72 },
+ { "ne1618", ne1618 },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
{ "thmc10", max1617 },
@@ -572,6 +576,13 @@ static const struct lm90_params lm90_params[] = {
.resolution = 11,
.max_convrate = 7,
},
+ [ne1618] = {
+ .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_BROKEN_ALERT
+ | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
+ .alert_alarms = 0x7c,
+ .resolution = 11,
+ .max_convrate = 7,
+ },
[w83l771] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
@@ -2149,20 +2160,29 @@ static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id,
return name;
}
-static const char *lm90_detect_nxp(struct i2c_client *client, int chip_id,
- int config1, int convrate)
+static const char *lm90_detect_nxp(struct i2c_client *client, bool common_address,
+ int chip_id, int config1, int convrate)
{
- int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
int address = client->addr;
const char *name = NULL;
+ int config2;
- if (config2 < 0)
- return NULL;
-
- if (address >= 0x48 && address <= 0x4f && chip_id == 0x00 &&
- !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09)
- name = "sa56004";
-
+ switch (chip_id) {
+ case 0x00:
+ config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
+ if (config2 < 0)
+ return NULL;
+ if (address >= 0x48 && address <= 0x4f &&
+ !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09)
+ name = "sa56004";
+ break;
+ case 0x80:
+ if (common_address && !(config1 & 0x3f) && convrate <= 0x07)
+ name = "ne1618";
+ break;
+ default:
+ break;
+ }
return name;
}
@@ -2335,7 +2355,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
name = lm90_detect_nuvoton(client, chip_id, config1, convrate);
break;
case 0xa1: /* NXP Semiconductor/Philips */
- name = lm90_detect_nxp(client, chip_id, config1, convrate);
+ name = lm90_detect_nxp(client, common_address, chip_id, config1, convrate);
break;
case 0xff: /* MAX1617, G767, NE1617 */
if (common_address && chip_id == 0xff && convrate < 8)
--
2.35.1
On maj 25, 2022 06:57, Guenter Roeck wrote:
> (...)
> @@ -1477,36 +1332,36 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
>
> switch (attr) {
> case hwmon_temp_min:
> - if (channel == 0)
> - err = lm90_set_temp8(data,
> - lm90_temp_min_index[channel],
> - val);
> - else
> - err = lm90_set_temp11(data,
> - lm90_temp_min_index[channel],
> - val);
> + err = lm90_set_temp(data, lm90_temp_min_index[channel],
> + channel, val);
> break;
> case hwmon_temp_max:
> - if (channel == 0)
> - err = lm90_set_temp8(data,
> - lm90_temp_max_index[channel],
> - val);
> - else
> - err = lm90_set_temp11(data,
> - lm90_temp_max_index[channel],
> - val);
> + err = lm90_set_temp(data, lm90_temp_max_index[channel],
> + channel, val);
> break;
> case hwmon_temp_crit:
> - err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val);
> + err = lm90_set_temp(data, lm90_temp_crit_index[channel],
> + channel, val);
> break;
> case hwmon_temp_crit_hyst:
> err = lm90_set_temphyst(data, val);
> break;
> case hwmon_temp_emergency:
> - err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val);
> + err = lm90_set_temp(data, lm90_temp_emerg_index[channel],
> + channel, val);
> break;
> case hwmon_temp_offset:
> - err = lm90_set_temp11(data, REMOTE_OFFSET, val);
> + val = lm90_temp_to_reg(0, val,
> + lm90_temp_get_resolution(data, REMOTE_OFFSET));
> + data->temp[REMOTE_OFFSET] = val;
I do not understand why you do this val assignment here, before doing real i2c write. That write
might fail and then we have "incorrect" value in data->temp.
> + err = i2c_smbus_write_byte_data(data->client,
> + LM90_REG_REMOTE_OFFSH,
> + val >> 8);
> + if (err)
> + break;
> + err = i2c_smbus_write_byte_data(data->client,
> + LM90_REG_REMOTE_OFFSL,
> + val & 0xff);
> break;
> default:
> err = -EOPNOTSUPP;
> @@ -2035,6 +1890,7 @@ static int lm90_probe(struct i2c_client *client)
> * ALERT# output
> */
> data->alert_alarms = lm90_params[data->kind].alert_alarms;
> + data->resolution = lm90_params[data->kind].resolution ? : 11;
>
> /* Set chip capabilities */
> data->flags = lm90_params[data->kind].flags;
--
Slawomir Stepien
On maj 25, 2022 06:57, Guenter Roeck wrote:
> 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.
>
> Signed-off-by: Guenter Roeck <[email protected]>
> ---
> Documentation/hwmon/lm90.rst | 38 +++++++++++
> drivers/hwmon/Kconfig | 3 +-
> drivers/hwmon/lm90.c | 119 ++++++++++++++++++++++++++---------
> 3 files changed, 129 insertions(+), 31 deletions(-)
Reviewed-by: Slawomir Stepien <[email protected]>
--
Slawomir Stepien
On 5/26/22 00:12, Slawomir Stepien wrote:
> On maj 25, 2022 06:57, Guenter Roeck wrote:
>> (...)
>> @@ -1477,36 +1332,36 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
>>
>> switch (attr) {
>> case hwmon_temp_min:
>> - if (channel == 0)
>> - err = lm90_set_temp8(data,
>> - lm90_temp_min_index[channel],
>> - val);
>> - else
>> - err = lm90_set_temp11(data,
>> - lm90_temp_min_index[channel],
>> - val);
>> + err = lm90_set_temp(data, lm90_temp_min_index[channel],
>> + channel, val);
>> break;
>> case hwmon_temp_max:
>> - if (channel == 0)
>> - err = lm90_set_temp8(data,
>> - lm90_temp_max_index[channel],
>> - val);
>> - else
>> - err = lm90_set_temp11(data,
>> - lm90_temp_max_index[channel],
>> - val);
>> + err = lm90_set_temp(data, lm90_temp_max_index[channel],
>> + channel, val);
>> break;
>> case hwmon_temp_crit:
>> - err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val);
>> + err = lm90_set_temp(data, lm90_temp_crit_index[channel],
>> + channel, val);
>> break;
>> case hwmon_temp_crit_hyst:
>> err = lm90_set_temphyst(data, val);
>> break;
>> case hwmon_temp_emergency:
>> - err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val);
>> + err = lm90_set_temp(data, lm90_temp_emerg_index[channel],
>> + channel, val);
>> break;
>> case hwmon_temp_offset:
>> - err = lm90_set_temp11(data, REMOTE_OFFSET, val);
>> + val = lm90_temp_to_reg(0, val,
>> + lm90_temp_get_resolution(data, REMOTE_OFFSET));
>> + data->temp[REMOTE_OFFSET] = val;
>
> I do not understand why you do this val assignment here, before doing real i2c write. That write
> might fail and then we have "incorrect" value in data->temp.
>
No special reason. I'll move the assignment to the end of the case statement.
Guenter
>> + err = i2c_smbus_write_byte_data(data->client,
>> + LM90_REG_REMOTE_OFFSH,
>> + val >> 8);
>> + if (err)
>> + break;
>> + err = i2c_smbus_write_byte_data(data->client,
>> + LM90_REG_REMOTE_OFFSL,
>> + val & 0xff);
>> break;
>> default:
>> err = -EOPNOTSUPP;
>> @@ -2035,6 +1890,7 @@ static int lm90_probe(struct i2c_client *client)
>> * ALERT# output
>> */
>> data->alert_alarms = lm90_params[data->kind].alert_alarms;
>> + data->resolution = lm90_params[data->kind].resolution ? : 11;
>>
>> /* Set chip capabilities */
>> data->flags = lm90_params[data->kind].flags;
>