All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver
@ 2022-05-25 13:57 Guenter Roeck
  2022-05-25 13:57 ` [PATCH 01/40] hwmon: (lm90) Generate sysfs and udev events for all alarms Guenter Roeck
                   ` (39 more replies)
  0 siblings, 40 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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(-)

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 01/40] hwmon: (lm90) Generate sysfs and udev events for all alarms
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 02/40] hwmon: (lm90) Rework alarm/status handling Guenter Roeck
                   ` (38 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 02/40] hwmon: (lm90) Rework alarm/status handling
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
  2022-05-25 13:57 ` [PATCH 01/40] hwmon: (lm90) Generate sysfs and udev events for all alarms Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 03/40] hwmon: (lm90) Reorder include files in alphabetical order Guenter Roeck
                   ` (37 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 03/40] hwmon: (lm90) Reorder include files in alphabetical order
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
  2022-05-25 13:57 ` [PATCH 01/40] hwmon: (lm90) Generate sysfs and udev events for all alarms Guenter Roeck
  2022-05-25 13:57 ` [PATCH 02/40] hwmon: (lm90) Rework alarm/status handling Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 04/40] hwmon: (lm90) Reorder chip enumeration to be " Guenter Roeck
                   ` (36 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 04/40] hwmon: (lm90) Reorder chip enumeration to be in alphabetical order
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (2 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 03/40] hwmon: (lm90) Reorder include files in alphabetical order Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 05/40] hwmon: (lm90) Use BIT macro Guenter Roeck
                   ` (35 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 05/40] hwmon: (lm90) Use BIT macro
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (3 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 04/40] hwmon: (lm90) Reorder chip enumeration to be " Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 06/40] hwmon: (lm90) Move status register bit shifts to compile time Guenter Roeck
                   ` (34 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

Use BIT macro instead of shift operation to improve readability.
No functional change.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 06/40] hwmon: (lm90) Move status register bit shifts to compile time
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (4 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 05/40] hwmon: (lm90) Use BIT macro Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 07/40] hwmon: (lm90) Stop using R_/W_ register prefix Guenter Roeck
                   ` (33 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 07/40] hwmon: (lm90) Stop using R_/W_ register prefix
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (5 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 06/40] hwmon: (lm90) Move status register bit shifts to compile time Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 08/40] hwmon: (lm90) Improve PEC support Guenter Roeck
                   ` (32 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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 = &reg[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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 08/40] hwmon: (lm90) Improve PEC support
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (6 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 07/40] hwmon: (lm90) Stop using R_/W_ register prefix Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 09/40] hwmon: (lm90) Add partial PEC support for ADT7461 Guenter Roeck
                   ` (31 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 09/40] hwmon: (lm90) Add partial PEC support for ADT7461
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (7 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 08/40] hwmon: (lm90) Improve PEC support Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 10/40] hwmon: (lm90) Enable full PEC support for ADT7461A Guenter Roeck
                   ` (30 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 10/40] hwmon: (lm90) Enable full PEC support for ADT7461A
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (8 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 09/40] hwmon: (lm90) Add partial PEC support for ADT7461 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 11/40] hwmon: (lm90) Add support for unsigned and signed temperatures Guenter Roeck
                   ` (29 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 11/40] hwmon: (lm90) Add support for unsigned and signed temperatures
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (9 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 10/40] hwmon: (lm90) Enable full PEC support for ADT7461A Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 12/40] hwmon: (lm90) Only re-read registers if volatile Guenter Roeck
                   ` (28 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

ADT7461 and TMP451 temperature sensors support extended temperature ranges.
If standard temperature range is selected, the temperature range is
unsigned and limited to 0 .. 127 degrees C. For TMP461, the standard
temperature range is -128000 ... 127000 degrees C. Distinguish between
the two chips by introducing a feature flag indicating if the standard
temperature range is signed or unsigned. Use the same flag for MAX6646/
MAX6647 as well since those chips also support unsigned temperatures.

Note that while the datasheet for ADT7461 suggests that the default
temperature range is unsigned, tests with a real chip suggest that this
is not the case: If the temperature offset is set to a value << 0,
the temperature register does report negative values.

Tests with real chips show that MAX6680/MAX6681 and SA56004 report
temperatures of 128 degrees C and higher as negative temperatures.
Add respective comments to the code.

Also use clamp_val() and DIV_ROUND_CLOSEST where appropriate in
calculations.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/hwmon/lm90.c | 70 ++++++++++++++++++++++++++------------------
 1 file changed, 42 insertions(+), 28 deletions(-)

diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 42e72702b9a9..acb9ca3b99b0 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -170,6 +170,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99,
 #define LM90_FLAG_ADT7461_EXT	BIT(0)	/* ADT7461 extended mode	*/
 /* Device features */
 #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	*/
 #define LM90_HAVE_EMERGENCY	BIT(4)	/* 3rd upper (emergency) limit	*/
 #define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm		*/
@@ -354,6 +355,11 @@ static const struct lm90_params lm90_params[] = {
 		.max_convrate = 10,
 	},
 	[adt7461] = {
+		/*
+		 * Standard temperature range is supposed to be unsigned,
+		 * but that does not match reality. Negative temperatures
+		 * are always reported.
+		 */
 		.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
 		  | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP
 		  | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC,
@@ -392,7 +398,8 @@ static const struct lm90_params lm90_params[] = {
 		.max_convrate = 9,
 	},
 	[max6646] = {
-		.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT,
+		.flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT
+		  | LM90_HAVE_UNSIGNED_TEMP,
 		.alert_alarms = 0x7c,
 		.max_convrate = 6,
 		.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
@@ -416,6 +423,11 @@ static const struct lm90_params lm90_params[] = {
 		.reg_local_ext = MAX6657_REG_LOCAL_TEMPL,
 	},
 	[max6680] = {
+		/*
+		 * Apparent temperatures of 128 degrees C or higher are reported
+		 * and treated as negative temperatures (meaning min_alarm will
+		 * be set).
+		 */
 		.flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT
 		  | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT,
 		.alert_alarms = 0x7c,
@@ -434,6 +446,11 @@ static const struct lm90_params lm90_params[] = {
 		.max_convrate = 8,
 	},
 	[sa56004] = {
+		/*
+		 * Apparent temperatures of 128 degrees C or higher are reported
+		 * and treated as negative temperatures (meaning min_alarm will
+		 * be set).
+		 */
 		.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT,
 		.alert_alarms = 0x7b,
 		.max_convrate = 9,
@@ -441,7 +458,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_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT
+		  | LM90_HAVE_UNSIGNED_TEMP,
 		.alert_alarms = 0x7c,
 		.max_convrate = 9,
 		.reg_local_ext = TMP451_REG_LOCAL_TEMPL,
@@ -1118,33 +1136,27 @@ static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val)
 static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
 {
 	if (data->flags & LM90_FLAG_ADT7461_EXT) {
-		if (val <= -64000)
-			return 0;
-		if (val >= 191000)
-			return 0xFF;
-		return (val + 500 + 64000) / 1000;
+		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);
 	}
-	if (val <= 0)
-		return 0;
-	if (val >= 127000)
-		return 127;
-	return (val + 500) / 1000;
+	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) {
-		if (val <= -64000)
-			return 0;
-		if (val >= 191750)
-			return 0xFFC0;
-		return (val + 64000 + 125) / 250 * 64;
+		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);
 	}
-	if (val <= 0)
-		return 0;
-	if (val >= 127750)
-		return 0x7FC0;
-	return (val + 125) / 250 * 64;
+	return DIV_ROUND_CLOSEST(val, 1000) & 0xfff0;
 }
 
 /* pec used for devices with PEC support */
@@ -1190,7 +1202,7 @@ static int lm90_get_temp11(struct lm90_data *data, int index)
 
 	if (data->flags & LM90_HAVE_EXTENDED_TEMP)
 		temp = temp_from_u16_adt7461(data, temp11);
-	else if (data->kind == max6646)
+	else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
 		temp = temp_from_u16(temp11);
 	else
 		temp = temp_from_s16(temp11);
@@ -1227,7 +1239,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val)
 
 	if (data->flags & LM90_HAVE_EXTENDED_TEMP)
 		data->temp11[index] = temp_to_u16_adt7461(data, val);
-	else if (data->kind == max6646)
+	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);
@@ -1253,7 +1265,7 @@ static int lm90_get_temp8(struct lm90_data *data, int index)
 
 	if (data->flags & LM90_HAVE_EXTENDED_TEMP)
 		temp = temp_from_u8_adt7461(data, temp8);
-	else if (data->kind == max6646)
+	else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
 		temp = temp_from_u8(temp8);
 	else
 		temp = temp_from_s8(temp8);
@@ -1289,7 +1301,7 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val)
 
 	if (data->flags & LM90_HAVE_EXTENDED_TEMP)
 		data->temp8[index] = temp_to_u8_adt7461(data, val);
-	else if (data->kind == max6646)
+	else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
 		data->temp8[index] = temp_to_u8(val);
 	else
 		data->temp8[index] = temp_to_s8(val);
@@ -1307,7 +1319,7 @@ static int lm90_get_temphyst(struct lm90_data *data, int index)
 
 	if (data->flags & LM90_HAVE_EXTENDED_TEMP)
 		temp = temp_from_u8_adt7461(data, data->temp8[index]);
-	else if (data->kind == max6646)
+	else if (data->flags & LM90_HAVE_UNSIGNED_TEMP)
 		temp = temp_from_u8(data->temp8[index]);
 	else
 		temp = temp_from_s8(data->temp8[index]);
@@ -1326,7 +1338,7 @@ static int lm90_set_temphyst(struct lm90_data *data, long val)
 
 	if (data->flags & LM90_HAVE_EXTENDED_TEMP)
 		temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
-	else if (data->kind == max6646)
+	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]);
@@ -1901,6 +1913,8 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
 	 * Put MAX6680/MAX8881 into extended resolution (bit 0x10,
 	 * 0.125 degree resolution) and range (0x08, extend range
 	 * to -64 degree) mode for the remote temperature sensor.
+	 * Note that expeciments with an actual chip do not show a difference
+	 * if bit 3 is set or not.
 	 */
 	if (data->kind == max6680)
 		config |= 0x18;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 12/40] hwmon: (lm90) Only re-read registers if volatile
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (10 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 11/40] hwmon: (lm90) Add support for unsigned and signed temperatures Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions Guenter Roeck
                   ` (27 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (11 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 12/40] hwmon: (lm90) Only re-read registers if volatile Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-26  7:12   ` Slawomir Stepien
  2022-05-25 13:57 ` [PATCH 14/40] hwmon: (lm90) Use single flag to indicate extended temperature support Guenter Roeck
                   ` (26 subsequent siblings)
  39 siblings, 1 reply; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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 = &reg[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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 14/40] hwmon: (lm90) Use single flag to indicate extended temperature support
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (12 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 15/40] hwmon: (lm90) Rework detect function Guenter Roeck
                   ` (25 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 15/40] hwmon: (lm90) Rework detect function
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (13 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 14/40] hwmon: (lm90) Use single flag to indicate extended temperature support Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 16/40] hwmon: (lm90) Add support for additional chip revision of NCT1008 Guenter Roeck
                   ` (24 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 16/40] hwmon: (lm90) Add support for additional chip revision of NCT1008
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (14 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 15/40] hwmon: (lm90) Rework detect function Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 17/40] hwmon: (lm90) Fix/Add detection of G781-1 Guenter Roeck
                   ` (23 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 17/40] hwmon: (lm90) Fix/Add detection of G781-1
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (15 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 16/40] hwmon: (lm90) Add support for additional chip revision of NCT1008 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 18/40] hwmon: (lm90) Add flag to indicate 'alarms' attribute support Guenter Roeck
                   ` (22 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 18/40] hwmon: (lm90) Add flag to indicate 'alarms' attribute support
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (16 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 17/40] hwmon: (lm90) Fix/Add detection of G781-1 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 19/40] hwmon: (lm90) Add explicit support for MAX6648/MAX6692 Guenter Roeck
                   ` (21 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 19/40] hwmon: (lm90) Add explicit support for MAX6648/MAX6692
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (17 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 18/40] hwmon: (lm90) Add flag to indicate 'alarms' attribute support Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 20/40] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483 Guenter Roeck
                   ` (20 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 20/40] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (18 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 19/40] hwmon: (lm90) Add explicit support for MAX6648/MAX6692 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-27  5:08   ` Slawomir Stepien
  2022-05-25 13:57 ` [PATCH 21/40] hwmon: (lm90) Strengthen chip detection for ADM1032, ADT7461(A), and NCT1008 Guenter Roeck
                   ` (19 subsequent siblings)
  39 siblings, 1 reply; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 21/40] hwmon: (lm90) Strengthen chip detection for ADM1032, ADT7461(A), and NCT1008
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (19 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 20/40] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 22/40] hwmon: (lm90) Add support for MAX6690 Guenter Roeck
                   ` (18 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 22/40] hwmon: (lm90) Add support for MAX6690
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (20 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 21/40] hwmon: (lm90) Strengthen chip detection for ADM1032, ADT7461(A), and NCT1008 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 23/40] hwmon: (lm90) Add flag to indicate support for minimum temperature limits Guenter Roeck
                   ` (17 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 23/40] hwmon: (lm90) Add flag to indicate support for minimum temperature limits
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (21 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 22/40] hwmon: (lm90) Add support for MAX6690 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 24/40] hwmon: (lm90) Add flag to indicate conversion rate support Guenter Roeck
                   ` (16 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 24/40] hwmon: (lm90) Add flag to indicate conversion rate support
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (22 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 23/40] hwmon: (lm90) Add flag to indicate support for minimum temperature limits Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 25/40] hwmon: (lm90) Add support for MAX6642 Guenter Roeck
                   ` (15 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 25/40] hwmon: (lm90) Add support for MAX6642
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (23 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 24/40] hwmon: (lm90) Add flag to indicate conversion rate support Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 26/40] hwmon: (lm90) Let lm90_read16() handle 8-bit read operations Guenter Roeck
                   ` (14 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 26/40] hwmon: (lm90) Let lm90_read16() handle 8-bit read operations
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (24 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 25/40] hwmon: (lm90) Add support for MAX6642 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 27/40] hwmon: (lm90) Introduce 16-bit register write function Guenter Roeck
                   ` (13 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 27/40] hwmon: (lm90) Introduce 16-bit register write function
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (25 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 26/40] hwmon: (lm90) Let lm90_read16() handle 8-bit read operations Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 28/40] hwmon: (lm90) Support MAX1617 and LM84 Guenter Roeck
                   ` (12 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

Introduce 16-bit register write function to simplify the code in
some places.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 28/40] hwmon: (lm90) Support MAX1617 and LM84
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (26 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 27/40] hwmon: (lm90) Introduce 16-bit register write function Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 29/40] hwmon: (lm90) Add support for ADM1021, ADM1021A, and ADM1023 Guenter Roeck
                   ` (11 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 29/40] hwmon: (lm90) Add support for ADM1021, ADM1021A, and ADM1023
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (27 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 28/40] hwmon: (lm90) Support MAX1617 and LM84 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 30/40] hwmon: (lm90) Add remaining chips supported by adm1021 driver Guenter Roeck
                   ` (10 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 30/40] hwmon: (lm90) Add remaining chips supported by adm1021 driver
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (28 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 29/40] hwmon: (lm90) Add support for ADM1021, ADM1021A, and ADM1023 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 31/40] hwmon: (lm90) Combine lm86 and lm90 configuration Guenter Roeck
                   ` (9 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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 <jdelvare@suse.de>
 
 
@@ -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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 31/40] hwmon: (lm90) Combine lm86 and lm90 configuration
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (29 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 30/40] hwmon: (lm90) Add remaining chips supported by adm1021 driver Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 32/40] hwmon: (lm90) Add explicit support for NCT210 Guenter Roeck
                   ` (8 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 32/40] hwmon: (lm90) Add explicit support for NCT210
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (30 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 31/40] hwmon: (lm90) Combine lm86 and lm90 configuration Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 33/40] hwmon: (lm90) Add support for ON Semiconductor NCT214 and NCT72 Guenter Roeck
                   ` (7 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 33/40] hwmon: (lm90) Add support for ON Semiconductor NCT214 and NCT72
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (31 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 32/40] hwmon: (lm90) Add explicit support for NCT210 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 34/40] hwmon: (lm90) Add support for ON Semiconductor NCT218 Guenter Roeck
                   ` (6 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 34/40] hwmon: (lm90) Add support for ON Semiconductor NCT218
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (32 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 33/40] hwmon: (lm90) Add support for ON Semiconductor NCT214 and NCT72 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 35/40] hwmon: (lm90) Add support for ADT7421 Guenter Roeck
                   ` (5 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 35/40] hwmon: (lm90) Add support for ADT7421
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (33 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 34/40] hwmon: (lm90) Add support for ON Semiconductor NCT218 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 36/40] hwmon: (lm90) Only disable alerts if not already disabled Guenter Roeck
                   ` (4 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 36/40] hwmon: (lm90) Only disable alerts if not already disabled
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (34 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 35/40] hwmon: (lm90) Add support for ADT7421 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 37/40] hwmon: (lm90) Add explicit support for ADM1020 Guenter Roeck
                   ` (3 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

It was observed that the alert handler may be called from the i2c core
even after alerts have already been disabled. Only disable alerts if
they have not already been disabled.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 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 d1435f55e31d..6d1e7052e3ed 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -2658,8 +2658,10 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
 
 		if ((data->flags & LM90_HAVE_BROKEN_ALERT) &&
 		    (data->current_alarms & data->alert_alarms)) {
-			dev_dbg(&client->dev, "Disabling ALERT#\n");
-			lm90_update_confreg(data, data->config | 0x80);
+			if (!(data->config & 0x80)) {
+				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)));
 		}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 37/40] hwmon: (lm90) Add explicit support for ADM1020
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (35 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 36/40] hwmon: (lm90) Only disable alerts if not already disabled Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 38/40] hwmon: (lm90) Add support and detection of Philips/NXP NE1618 Guenter Roeck
                   ` (2 subsequent siblings)
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

ADM1020 is compatible with ADM1021 but has a separate chip revision and
a limited I2C address range.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 38/40] hwmon: (lm90) Add support and detection of Philips/NXP NE1618
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (36 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 37/40] hwmon: (lm90) Add explicit support for ADM1020 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 39/40] hwmon: (lm90) Add table with supported Analog/ONSEMI devices Guenter Roeck
  2022-05-25 13:57 ` [PATCH 40/40] hwmon: (lm90) Support temp_samples attribute Guenter Roeck
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 39/40] hwmon: (lm90) Add table with supported Analog/ONSEMI devices
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (37 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 38/40] hwmon: (lm90) Add support and detection of Philips/NXP NE1618 Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  2022-05-25 13:57 ` [PATCH 40/40] hwmon: (lm90) Support temp_samples attribute Guenter Roeck
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

Add table with device names and known register values for supported
devices from Analog / ON Semiconductor.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 40/40] hwmon: (lm90) Support temp_samples attribute
  2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
                   ` (38 preceding siblings ...)
  2022-05-25 13:57 ` [PATCH 39/40] hwmon: (lm90) Add table with supported Analog/ONSEMI devices Guenter Roeck
@ 2022-05-25 13:57 ` Guenter Roeck
  39 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-25 13:57 UTC (permalink / raw)
  To: linux-hwmon; +Cc: linux-kernel, Jean Delvare, Slawomir Stepien, Guenter Roeck

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 <linux@roeck-us.net>
---
 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


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions
  2022-05-25 13:57 ` [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions Guenter Roeck
@ 2022-05-26  7:12   ` Slawomir Stepien
  2022-05-26 14:32     ` Guenter Roeck
  0 siblings, 1 reply; 44+ messages in thread
From: Slawomir Stepien @ 2022-05-26  7:12 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Jean Delvare

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

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions
  2022-05-26  7:12   ` Slawomir Stepien
@ 2022-05-26 14:32     ` Guenter Roeck
  0 siblings, 0 replies; 44+ messages in thread
From: Guenter Roeck @ 2022-05-26 14:32 UTC (permalink / raw)
  To: Slawomir Stepien; +Cc: linux-hwmon, linux-kernel, Jean Delvare

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;
> 


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 20/40] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483
  2022-05-25 13:57 ` [PATCH 20/40] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483 Guenter Roeck
@ 2022-05-27  5:08   ` Slawomir Stepien
  0 siblings, 0 replies; 44+ messages in thread
From: Slawomir Stepien @ 2022-05-27  5:08 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Jean Delvare

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 <linux@roeck-us.net>
> ---
>  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 <sst@poczta.fm>

-- 
Slawomir Stepien

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2022-05-27  5:08 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-25 13:57 [PATCH 00/40] hwmon: (lm90) Various improvements to lm90 driver Guenter Roeck
2022-05-25 13:57 ` [PATCH 01/40] hwmon: (lm90) Generate sysfs and udev events for all alarms Guenter Roeck
2022-05-25 13:57 ` [PATCH 02/40] hwmon: (lm90) Rework alarm/status handling Guenter Roeck
2022-05-25 13:57 ` [PATCH 03/40] hwmon: (lm90) Reorder include files in alphabetical order Guenter Roeck
2022-05-25 13:57 ` [PATCH 04/40] hwmon: (lm90) Reorder chip enumeration to be " Guenter Roeck
2022-05-25 13:57 ` [PATCH 05/40] hwmon: (lm90) Use BIT macro Guenter Roeck
2022-05-25 13:57 ` [PATCH 06/40] hwmon: (lm90) Move status register bit shifts to compile time Guenter Roeck
2022-05-25 13:57 ` [PATCH 07/40] hwmon: (lm90) Stop using R_/W_ register prefix Guenter Roeck
2022-05-25 13:57 ` [PATCH 08/40] hwmon: (lm90) Improve PEC support Guenter Roeck
2022-05-25 13:57 ` [PATCH 09/40] hwmon: (lm90) Add partial PEC support for ADT7461 Guenter Roeck
2022-05-25 13:57 ` [PATCH 10/40] hwmon: (lm90) Enable full PEC support for ADT7461A Guenter Roeck
2022-05-25 13:57 ` [PATCH 11/40] hwmon: (lm90) Add support for unsigned and signed temperatures Guenter Roeck
2022-05-25 13:57 ` [PATCH 12/40] hwmon: (lm90) Only re-read registers if volatile Guenter Roeck
2022-05-25 13:57 ` [PATCH 13/40] hwmon: (lm90) Support multiple temperature resolutions Guenter Roeck
2022-05-26  7:12   ` Slawomir Stepien
2022-05-26 14:32     ` Guenter Roeck
2022-05-25 13:57 ` [PATCH 14/40] hwmon: (lm90) Use single flag to indicate extended temperature support Guenter Roeck
2022-05-25 13:57 ` [PATCH 15/40] hwmon: (lm90) Rework detect function Guenter Roeck
2022-05-25 13:57 ` [PATCH 16/40] hwmon: (lm90) Add support for additional chip revision of NCT1008 Guenter Roeck
2022-05-25 13:57 ` [PATCH 17/40] hwmon: (lm90) Fix/Add detection of G781-1 Guenter Roeck
2022-05-25 13:57 ` [PATCH 18/40] hwmon: (lm90) Add flag to indicate 'alarms' attribute support Guenter Roeck
2022-05-25 13:57 ` [PATCH 19/40] hwmon: (lm90) Add explicit support for MAX6648/MAX6692 Guenter Roeck
2022-05-25 13:57 ` [PATCH 20/40] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483 Guenter Roeck
2022-05-27  5:08   ` Slawomir Stepien
2022-05-25 13:57 ` [PATCH 21/40] hwmon: (lm90) Strengthen chip detection for ADM1032, ADT7461(A), and NCT1008 Guenter Roeck
2022-05-25 13:57 ` [PATCH 22/40] hwmon: (lm90) Add support for MAX6690 Guenter Roeck
2022-05-25 13:57 ` [PATCH 23/40] hwmon: (lm90) Add flag to indicate support for minimum temperature limits Guenter Roeck
2022-05-25 13:57 ` [PATCH 24/40] hwmon: (lm90) Add flag to indicate conversion rate support Guenter Roeck
2022-05-25 13:57 ` [PATCH 25/40] hwmon: (lm90) Add support for MAX6642 Guenter Roeck
2022-05-25 13:57 ` [PATCH 26/40] hwmon: (lm90) Let lm90_read16() handle 8-bit read operations Guenter Roeck
2022-05-25 13:57 ` [PATCH 27/40] hwmon: (lm90) Introduce 16-bit register write function Guenter Roeck
2022-05-25 13:57 ` [PATCH 28/40] hwmon: (lm90) Support MAX1617 and LM84 Guenter Roeck
2022-05-25 13:57 ` [PATCH 29/40] hwmon: (lm90) Add support for ADM1021, ADM1021A, and ADM1023 Guenter Roeck
2022-05-25 13:57 ` [PATCH 30/40] hwmon: (lm90) Add remaining chips supported by adm1021 driver Guenter Roeck
2022-05-25 13:57 ` [PATCH 31/40] hwmon: (lm90) Combine lm86 and lm90 configuration Guenter Roeck
2022-05-25 13:57 ` [PATCH 32/40] hwmon: (lm90) Add explicit support for NCT210 Guenter Roeck
2022-05-25 13:57 ` [PATCH 33/40] hwmon: (lm90) Add support for ON Semiconductor NCT214 and NCT72 Guenter Roeck
2022-05-25 13:57 ` [PATCH 34/40] hwmon: (lm90) Add support for ON Semiconductor NCT218 Guenter Roeck
2022-05-25 13:57 ` [PATCH 35/40] hwmon: (lm90) Add support for ADT7421 Guenter Roeck
2022-05-25 13:57 ` [PATCH 36/40] hwmon: (lm90) Only disable alerts if not already disabled Guenter Roeck
2022-05-25 13:57 ` [PATCH 37/40] hwmon: (lm90) Add explicit support for ADM1020 Guenter Roeck
2022-05-25 13:57 ` [PATCH 38/40] hwmon: (lm90) Add support and detection of Philips/NXP NE1618 Guenter Roeck
2022-05-25 13:57 ` [PATCH 39/40] hwmon: (lm90) Add table with supported Analog/ONSEMI devices Guenter Roeck
2022-05-25 13:57 ` [PATCH 40/40] hwmon: (lm90) Support temp_samples attribute Guenter Roeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.