All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-18 13:38 ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

Currently each time the temperature register is read the driver also reads the
threshold and hysteresis registers. This increases the amount of I2C traffic and
time needed to read the temperature by a factor of ~5. Neither the threshold nor
the hysteresis change on their own, so once we've read them, we should be able
to just use the cached value of the registers. This patch modifies the code
accordingly and only reads the threshold and hysteresis registers once during
probe.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

---
Changes since v1:
	* Fix error checking for i2c reads
---
 drivers/hwmon/adt7410.c | 91 +++++++++++++++++++++++++++++++------------------
 1 file changed, 58 insertions(+), 33 deletions(-)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 99a7290..b6acfa4 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -119,45 +119,33 @@ static int adt7410_temp_ready(struct i2c_client *client)
 	return -ETIMEDOUT;
 }
 
-static struct adt7410_data *adt7410_update_device(struct device *dev)
+static int adt7410_update_temp(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct adt7410_data *data = i2c_get_clientdata(client);
-	struct adt7410_data *ret = data;
+	int ret = 0;
+
 	mutex_lock(&data->update_lock);
 
 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
 	    || !data->valid) {
-		int i, status;
+		int temp;
 
 		dev_dbg(&client->dev, "Starting update\n");
 
-		status = adt7410_temp_ready(client); /* check for new value */
-		if (unlikely(status)) {
-			ret = ERR_PTR(status);
+		ret = adt7410_temp_ready(client); /* check for new value */
+		if (ret)
 			goto abort;
-		}
-		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
-			status = i2c_smbus_read_word_swapped(client,
-							ADT7410_REG_TEMP[i]);
-			if (unlikely(status < 0)) {
-				dev_dbg(dev,
-					"Failed to read value: reg %d, error %d\n",
-					ADT7410_REG_TEMP[i], status);
-				ret = ERR_PTR(status);
-				goto abort;
-			}
-			data->temp[i] = status;
-		}
-		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
-		if (unlikely(status < 0)) {
-			dev_dbg(dev,
-				"Failed to read value: reg %d, error %d\n",
-				ADT7410_T_HYST, status);
-			ret = ERR_PTR(status);
+
+		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
+		if (temp < 0) {
+			ret = temp;
+			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
 			goto abort;
 		}
-		data->hyst = status;
+		data->temp[0] = temp;
+
 		data->last_updated = jiffies;
 		data->valid = true;
 	}
@@ -167,6 +155,35 @@ abort:
 	return ret;
 }
 
+static int adt7410_fill_cache(struct i2c_client *client)
+{
+	struct adt7410_data *data = i2c_get_clientdata(client);
+	int ret;
+	int i;
+
+	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
+		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
+		if (ret < 0) {
+			dev_dbg(&client->dev,
+				"Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
+			return ret;
+		}
+		data->temp[i] = ret;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"Failed to read value: hyst reg, error %d\n",
+			ret);
+		return ret;
+	}
+	data->hyst = ret;
+
+	return 0;
+}
+
 static s16 ADT7410_TEMP_TO_REG(long temp)
 {
 	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
@@ -193,10 +210,16 @@ static ssize_t adt7410_show_temp(struct device *dev,
 				 struct device_attribute *da, char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct adt7410_data *data = adt7410_update_device(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adt7410_data *data = i2c_get_clientdata(client);
 
-	if (IS_ERR(data))
-		return PTR_ERR(data);
+	if (attr->index == 0) {
+		int ret;
+
+		ret = adt7410_update_temp(dev);
+		if (ret)
+			return ret;
+	}
 
 	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
 		       data->temp[attr->index]));
@@ -232,13 +255,11 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
 				   char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct adt7410_data *data;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adt7410_data *data = i2c_get_clientdata(client);
 	int nr = attr->index;
 	int hyst;
 
-	data = adt7410_update_device(dev);
-	if (IS_ERR(data))
-		return PTR_ERR(data);
 	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
 
 	/*
@@ -371,6 +392,10 @@ static int adt7410_probe(struct i2c_client *client,
 	}
 	dev_dbg(&client->dev, "Config %02x\n", data->config);
 
+	ret = adt7410_fill_cache(client);
+	if (ret)
+		goto exit_restore;
+
 	/* Register sysfs hooks */
 	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
 	if (ret)
-- 
1.8.0


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

* [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-18 13:38 ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

Currently each time the temperature register is read the driver also reads the
threshold and hysteresis registers. This increases the amount of I2C traffic and
time needed to read the temperature by a factor of ~5. Neither the threshold nor
the hysteresis change on their own, so once we've read them, we should be able
to just use the cached value of the registers. This patch modifies the code
accordingly and only reads the threshold and hysteresis registers once during
probe.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

---
Changes since v1:
	* Fix error checking for i2c reads
---
 drivers/hwmon/adt7410.c | 91 +++++++++++++++++++++++++++++++------------------
 1 file changed, 58 insertions(+), 33 deletions(-)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 99a7290..b6acfa4 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -119,45 +119,33 @@ static int adt7410_temp_ready(struct i2c_client *client)
 	return -ETIMEDOUT;
 }
 
-static struct adt7410_data *adt7410_update_device(struct device *dev)
+static int adt7410_update_temp(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct adt7410_data *data = i2c_get_clientdata(client);
-	struct adt7410_data *ret = data;
+	int ret = 0;
+
 	mutex_lock(&data->update_lock);
 
 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
 	    || !data->valid) {
-		int i, status;
+		int temp;
 
 		dev_dbg(&client->dev, "Starting update\n");
 
-		status = adt7410_temp_ready(client); /* check for new value */
-		if (unlikely(status)) {
-			ret = ERR_PTR(status);
+		ret = adt7410_temp_ready(client); /* check for new value */
+		if (ret)
 			goto abort;
-		}
-		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
-			status = i2c_smbus_read_word_swapped(client,
-							ADT7410_REG_TEMP[i]);
-			if (unlikely(status < 0)) {
-				dev_dbg(dev,
-					"Failed to read value: reg %d, error %d\n",
-					ADT7410_REG_TEMP[i], status);
-				ret = ERR_PTR(status);
-				goto abort;
-			}
-			data->temp[i] = status;
-		}
-		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
-		if (unlikely(status < 0)) {
-			dev_dbg(dev,
-				"Failed to read value: reg %d, error %d\n",
-				ADT7410_T_HYST, status);
-			ret = ERR_PTR(status);
+
+		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
+		if (temp < 0) {
+			ret = temp;
+			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
 			goto abort;
 		}
-		data->hyst = status;
+		data->temp[0] = temp;
+
 		data->last_updated = jiffies;
 		data->valid = true;
 	}
@@ -167,6 +155,35 @@ abort:
 	return ret;
 }
 
+static int adt7410_fill_cache(struct i2c_client *client)
+{
+	struct adt7410_data *data = i2c_get_clientdata(client);
+	int ret;
+	int i;
+
+	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
+		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
+		if (ret < 0) {
+			dev_dbg(&client->dev,
+				"Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
+			return ret;
+		}
+		data->temp[i] = ret;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"Failed to read value: hyst reg, error %d\n",
+			ret);
+		return ret;
+	}
+	data->hyst = ret;
+
+	return 0;
+}
+
 static s16 ADT7410_TEMP_TO_REG(long temp)
 {
 	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
@@ -193,10 +210,16 @@ static ssize_t adt7410_show_temp(struct device *dev,
 				 struct device_attribute *da, char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct adt7410_data *data = adt7410_update_device(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adt7410_data *data = i2c_get_clientdata(client);
 
-	if (IS_ERR(data))
-		return PTR_ERR(data);
+	if (attr->index = 0) {
+		int ret;
+
+		ret = adt7410_update_temp(dev);
+		if (ret)
+			return ret;
+	}
 
 	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
 		       data->temp[attr->index]));
@@ -232,13 +255,11 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
 				   char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct adt7410_data *data;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adt7410_data *data = i2c_get_clientdata(client);
 	int nr = attr->index;
 	int hyst;
 
-	data = adt7410_update_device(dev);
-	if (IS_ERR(data))
-		return PTR_ERR(data);
 	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
 
 	/*
@@ -371,6 +392,10 @@ static int adt7410_probe(struct i2c_client *client,
 	}
 	dev_dbg(&client->dev, "Config %02x\n", data->config);
 
+	ret = adt7410_fill_cache(client);
+	if (ret)
+		goto exit_restore;
+
 	/* Register sysfs hooks */
 	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
 	if (ret)
-- 
1.8.0


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-18 13:38   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

The adt7310/adt7320 is the SPI version of the adt7410/adt7420. The register map
layout is a bit different, i.e. the register addresses differ between the two
variants, but the bit layouts of the individual registers are identical. So both
chip variants can easily be supported by the same driver. The issue of non
matching register address layouts is solved by a simple look-up table which
translates the I2C addresses to the SPI addresses.

The patch moves the bulk of the adt7410 driver to a common module that will be
shared by the adt7410 and adt7310 drivers. This common module implements the
driver logic and uses a set of virtual functions to perform IO access. The
adt7410 and adt7310 driver modules provide proper implementations of these IO
accessor functions for I2C respective SPI.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

---
Changes since v1:
	* Update the driver documentation to include ADT7310/ADT7320/ADT7420
	* Pass the result of the read methods via the return value instead of a
	  pointer argument
	* Simplify spi read methods by using spi_w8r8 and spi_w8r16
	* Update module description of the shared module
	* Fix some typos
---
 Documentation/hwmon/adt7410 |  42 ++--
 drivers/hwmon/Kconfig       |  20 ++
 drivers/hwmon/Makefile      |   2 +
 drivers/hwmon/adt7310.c     | 115 +++++++++++
 drivers/hwmon/adt7410.c     | 464 +++---------------------------------------
 drivers/hwmon/adt7x10.c     | 476 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/adt7x10.h     |  48 +++++
 7 files changed, 720 insertions(+), 447 deletions(-)
 create mode 100644 drivers/hwmon/adt7310.c
 create mode 100644 drivers/hwmon/adt7x10.c
 create mode 100644 drivers/hwmon/adt7x10.h

diff --git a/Documentation/hwmon/adt7410 b/Documentation/hwmon/adt7410
index 9600400..e452ae0 100644
--- a/Documentation/hwmon/adt7410
+++ b/Documentation/hwmon/adt7410
@@ -7,25 +7,41 @@ Supported chips:
     Addresses scanned: I2C 0x48 - 0x4B
     Datasheet: Publicly available at the Analog Devices website
                http://www.analog.com/static/imported-files/data_sheets/ADT7410.pdf
+  * Analog Devices ADT7420
+    Prefix: 'adt7420'
+    Addresses scanned: I2C 0x48 - 0x4B
+    Datasheet: Publicly available at the Analog Devices website
+               http://www.analog.com/static/imported-files/data_sheets/ADT7420.pdf
+  * Analog Devices ADT7310
+    Prefix: 'adt7310'
+    Addresses scanned: I2C 0x48 - 0x4B
+    Datasheet: Publicly available at the Analog Devices website
+               http://www.analog.com/static/imported-files/data_sheets/ADT7310.pdf
+  * Analog Devices ADT7320
+    Prefix: 'adt7320'
+    Addresses scanned: I2C 0x48 - 0x4B
+    Datasheet: Publicly available at the Analog Devices website
+               http://www.analog.com/static/imported-files/data_sheets/ADT7320.pdf
 
 Author: Hartmut Knaack <knaack.h@gmx.de>
 
 Description
 -----------
 
-The ADT7410 is a temperature sensor with rated temperature range of -55°C to
-+150°C. It has a high accuracy of +/-0.5°C and can be operated at a resolution
-of 13 bits (0.0625°C) or 16 bits (0.0078°C). The sensor provides an INT pin to
-indicate that a minimum or maximum temperature set point has been exceeded, as
-well as a critical temperature (CT) pin to indicate that the critical
-temperature set point has been exceeded. Both pins can be set up with a common
-hysteresis of 0°C - 15°C and a fault queue, ranging from 1 to 4 events. Both
-pins can individually set to be active-low or active-high, while the whole
-device can either run in comparator mode or interrupt mode. The ADT7410
-supports continous temperature sampling, as well as sampling one temperature
-value per second or even justget one sample on demand for power saving.
-Besides, it can completely power down its ADC, if power management is
-required.
+The ADT7410 and similar are a temperature sensors with rated temperature range
+of -55°C to +150°C (ADT7310/ADT7410) or -40°C to +150°C (ADT7320/ADT7420). They
+have a high accuracy of +/-0.5°C (ADT7310/ADT7410) or +/-0.2C (ADT7430/ADT7420)
+and can be operated at a resolution of 13 bits (0.0625°C) or 16 bits (0.0078°C).
+The sensor provides an INT pin to indicate that a minimum or maximum temperature
+set point has been exceeded, as well as a critical temperature (CT) pin to
+indicate that the critical temperature set point has been exceeded. Both pins
+can be set up with a common hysteresis of 0°C - 15°C and a fault queue, ranging
+from 1 to 4 events. Both pins can individually set to be active-low or
+active-high, while the whole device can either run in comparator mode or
+interrupt mode. The ADT7410 supports continous temperature sampling, as well as
+sampling one temperature value per second or even justget one sample on demand
+for power saving.  Besides, it can completely power down its ADC, if power
+management is required.
 
 Configuration Notes
 -------------------
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 89ac1cb..aaa14f4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -179,9 +179,29 @@ config SENSORS_ADM9240
 	  This driver can also be built as a module.  If so, the module
 	  will be called adm9240.
 
+config SENSORS_ADT7X10
+	tristate
+	help
+	  This module contains common code shared by the ADT7310/ADT7320 and
+	  ADT7410/ADT7420 temperature monitoring chip drivers.
+
+	  If build as a module, the module will be called adt7x10.
+
+config SENSORS_ADT7310
+	tristate "Analog Devices ADT7310/ADT7320"
+	depends on SPI_MASTER
+	select SENSORS_ADT7X10
+	help
+	  If you say yes here you get support for the Analog Devices
+	  ADT7310 and ADT7320 temperature monitoring chips.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called adt7310.
+
 config SENSORS_ADT7410
 	tristate "Analog Devices ADT7410/ADT7420"
 	depends on I2C
+	select SENSORS_ADT7X10
 	help
 	  If you say yes here you get support for the Analog Devices
 	  ADT7410 and ADT7420 temperature monitoring chips.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8d6d97e..5d36a57 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
 obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
 obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
 obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
+obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
+obj-$(CONFIG_SENSORS_ADT7310)	+= adt7310.o
 obj-$(CONFIG_SENSORS_ADT7410)	+= adt7410.o
 obj-$(CONFIG_SENSORS_ADT7411)	+= adt7411.o
 obj-$(CONFIG_SENSORS_ADT7462)	+= adt7462.o
diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
new file mode 100644
index 0000000..2196ac3
--- /dev/null
+++ b/drivers/hwmon/adt7310.c
@@ -0,0 +1,115 @@
+/*
+ * ADT7310/ADT7310 digital temperature sensor driver
+ *
+ * Copyright 2010-2013 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+#include "adt7x10.h"
+
+static const u8 adt7310_reg_table[] = {
+	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
+	[ADT7410_STATUS]	= ADT7310_STATUS,
+	[ADT7410_CONFIG]	= ADT7310_CONFIG,
+	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
+	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
+	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
+	[ADT7410_T_HYST]	= ADT7310_T_HYST,
+	[ADT7410_ID]		= ADT7310_ID,
+};
+
+#define ADT7310_CMD_REG_OFFSET	3
+#define ADT7310_CMD_READ	0x40
+
+#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
+
+static int adt7310_spi_read_word(struct device *dev, u8 reg)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+
+	ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+	if (ret < 0)
+		return ret;
+
+	return be16_to_cpu(ret);
+}
+
+static int adt7310_spi_write_word(struct device *dev, u8 reg,
+	u16 data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u8 buf[3];
+
+	buf[0] = AD7310_COMMAND(reg);
+	put_unaligned_be16(data, &buf[1]);
+
+	return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adt7310_spi_read_byte(struct device *dev, u8 reg)
+{
+	struct spi_device *spi = to_spi_device(dev);
+
+	return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+}
+
+static int adt7310_spi_write_byte(struct device *dev, u8 reg,
+	u8 data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u8 buf[2];
+
+	buf[0] = AD7310_COMMAND(reg);
+	buf[1] = data;
+
+	return spi_write(spi, buf, sizeof(buf));
+}
+
+static const struct adt7410_ops adt7310_spi_ops = {
+	.read_word = adt7310_spi_read_word,
+	.write_word = adt7310_spi_write_word,
+	.read_byte = adt7310_spi_read_byte,
+	.write_byte = adt7310_spi_write_byte,
+};
+
+static int adt7310_spi_probe(struct spi_device *spi)
+{
+	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
+			&adt7310_spi_ops);
+}
+
+static int adt7310_spi_remove(struct spi_device *spi)
+{
+	return adt7410_remove(&spi->dev);
+}
+
+static const struct spi_device_id adt7310_id[] = {
+	{ "adt7310", 0 },
+	{ "adt7320", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(spi, adt7310_id);
+
+static struct spi_driver adt7310_driver = {
+	.driver = {
+		.name = "adt7310",
+		.owner = THIS_MODULE,
+		.pm	= ADT7410_DEV_PM_OPS,
+	},
+	.probe = adt7310_spi_probe,
+	.remove = adt7310_spi_remove,
+	.id_table = adt7310_id,
+};
+module_spi_driver(adt7310_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index b6acfa4..b500ab3 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -1,485 +1,81 @@
 /*
- * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
- *	 monitoring
- * This driver handles the ADT7410 and compatible digital temperature sensors.
- * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
- * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
- * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
+ * ADT7410/ADT7420 digital temperature sensor driver
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Copyright 2010-2013 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under the GPL-2 or later.
  */
 
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
 #include <linux/i2c.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-
-/*
- * ADT7410 registers definition
- */
-
-#define ADT7410_TEMPERATURE		0
-#define ADT7410_STATUS			2
-#define ADT7410_CONFIG			3
-#define ADT7410_T_ALARM_HIGH		4
-#define ADT7410_T_ALARM_LOW		6
-#define ADT7410_T_CRIT			8
-#define ADT7410_T_HYST			0xA
-
-/*
- * ADT7410 status
- */
-#define ADT7410_STAT_T_LOW		(1 << 4)
-#define ADT7410_STAT_T_HIGH		(1 << 5)
-#define ADT7410_STAT_T_CRIT		(1 << 6)
-#define ADT7410_STAT_NOT_RDY		(1 << 7)
 
-/*
- * ADT7410 config
- */
-#define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
-#define ADT7410_CT_POLARITY		(1 << 2)
-#define ADT7410_INT_POLARITY		(1 << 3)
-#define ADT7410_EVENT_MODE		(1 << 4)
-#define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
-#define ADT7410_FULL			(0 << 5 | 0 << 6)
-#define ADT7410_PD			(1 << 5 | 1 << 6)
-#define ADT7410_RESOLUTION		(1 << 7)
+#include "adt7x10.h"
 
-/*
- * ADT7410 masks
- */
-#define ADT7410_T13_VALUE_MASK			0xFFF8
-#define ADT7410_T_HYST_MASK			0xF
-
-/* straight from the datasheet */
-#define ADT7410_TEMP_MIN (-55000)
-#define ADT7410_TEMP_MAX 150000
-
-enum adt7410_type {		/* keep sorted in alphabetical order */
-	adt7410,
-};
-
-static const u8 ADT7410_REG_TEMP[4] = {
-	ADT7410_TEMPERATURE,		/* input */
-	ADT7410_T_ALARM_HIGH,		/* high */
-	ADT7410_T_ALARM_LOW,		/* low */
-	ADT7410_T_CRIT,			/* critical */
-};
-
-/* Each client has this additional data */
-struct adt7410_data {
-	struct device		*hwmon_dev;
-	struct mutex		update_lock;
-	u8			config;
-	u8			oldconfig;
-	bool			valid;		/* true if registers valid */
-	unsigned long		last_updated;	/* In jiffies */
-	s16			temp[4];	/* Register values,
-						   0 = input
-						   1 = high
-						   2 = low
-						   3 = critical */
-	u8			hyst;		/* hysteresis offset */
-};
-
-/*
- * adt7410 register access by I2C
- */
-static int adt7410_temp_ready(struct i2c_client *client)
+static int adt7410_i2c_read_word(struct device *dev, u8 reg)
 {
-	int i, status;
-
-	for (i = 0; i < 6; i++) {
-		status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
-		if (status < 0)
-			return status;
-		if (!(status & ADT7410_STAT_NOT_RDY))
-			return 0;
-		msleep(60);
-	}
-	return -ETIMEDOUT;
+	return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg);
 }
 
-static int adt7410_update_temp(struct device *dev)
+static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
 {
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-	int ret = 0;
-
-	mutex_lock(&data->update_lock);
-
-	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
-	    || !data->valid) {
-		int temp;
-
-		dev_dbg(&client->dev, "Starting update\n");
-
-		ret = adt7410_temp_ready(client); /* check for new value */
-		if (ret)
-			goto abort;
-
-		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
-		if (temp < 0) {
-			ret = temp;
-			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
-				ADT7410_REG_TEMP[0], ret);
-			goto abort;
-		}
-		data->temp[0] = temp;
-
-		data->last_updated = jiffies;
-		data->valid = true;
-	}
-
-abort:
-	mutex_unlock(&data->update_lock);
-	return ret;
-}
-
-static int adt7410_fill_cache(struct i2c_client *client)
-{
-	struct adt7410_data *data = i2c_get_clientdata(client);
-	int ret;
-	int i;
-
-	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
-		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
-		if (ret < 0) {
-			dev_dbg(&client->dev,
-				"Failed to read value: reg %d, error %d\n",
-				ADT7410_REG_TEMP[0], ret);
-			return ret;
-		}
-		data->temp[i] = ret;
-	}
-
-	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
-	if (ret < 0) {
-		dev_dbg(&client->dev,
-			"Failed to read value: hyst reg, error %d\n",
-			ret);
-		return ret;
-	}
-	data->hyst = ret;
-
-	return 0;
+	return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data);
 }
 
-static s16 ADT7410_TEMP_TO_REG(long temp)
+static int adt7410_i2c_read_byte(struct device *dev, u8 reg)
 {
-	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
-					   ADT7410_TEMP_MAX) * 128, 1000);
+	return i2c_smbus_read_byte_data(to_i2c_client(dev), reg);
 }
 
-static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
+static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
 {
-	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
-	if (!(data->config & ADT7410_RESOLUTION))
-		reg &= ADT7410_T13_VALUE_MASK;
-	/*
-	 * temperature is stored in twos complement format, in steps of
-	 * 1/128°C
-	 */
-	return DIV_ROUND_CLOSEST(reg * 1000, 128);
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);
 }
 
-/*-----------------------------------------------------------------------*/
-
-/* sysfs attributes for hwmon */
-
-static ssize_t adt7410_show_temp(struct device *dev,
-				 struct device_attribute *da, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-
-	if (attr->index == 0) {
-		int ret;
-
-		ret = adt7410_update_temp(dev);
-		if (ret)
-			return ret;
-	}
-
-	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
-		       data->temp[attr->index]));
-}
-
-static ssize_t adt7410_set_temp(struct device *dev,
-				struct device_attribute *da,
-				const char *buf, size_t count)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-	int nr = attr->index;
-	long temp;
-	int ret;
-
-	ret = kstrtol(buf, 10, &temp);
-	if (ret)
-		return ret;
-
-	mutex_lock(&data->update_lock);
-	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
-	ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
-					   data->temp[nr]);
-	if (ret)
-		count = ret;
-	mutex_unlock(&data->update_lock);
-	return count;
-}
-
-static ssize_t adt7410_show_t_hyst(struct device *dev,
-				   struct device_attribute *da,
-				   char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-	int nr = attr->index;
-	int hyst;
-
-	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
-
-	/*
-	 * hysteresis is stored as a 4 bit offset in the device, convert it
-	 * to an absolute value
-	 */
-	if (nr == 2)	/* min has positive offset, others have negative */
-		hyst = -hyst;
-	return sprintf(buf, "%d\n",
-		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
-}
-
-static ssize_t adt7410_set_t_hyst(struct device *dev,
-				  struct device_attribute *da,
-				  const char *buf, size_t count)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-	int limit, ret;
-	long hyst;
-
-	ret = kstrtol(buf, 10, &hyst);
-	if (ret)
-		return ret;
-	/* convert absolute hysteresis value to a 4 bit delta value */
-	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
-	hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
-	data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
-			       ADT7410_T_HYST_MASK);
-	ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
-	if (ret)
-		return ret;
-
-	return count;
-}
-
-static ssize_t adt7410_show_alarm(struct device *dev,
-				  struct device_attribute *da,
-				  char *buf)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	int ret;
-
-	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
-	if (ret < 0)
-		return ret;
-
-	return sprintf(buf, "%d\n", !!(ret & attr->index));
-}
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
-			  adt7410_show_temp, adt7410_set_temp, 1);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
-			  adt7410_show_temp, adt7410_set_temp, 2);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
-			  adt7410_show_temp, adt7410_set_temp, 3);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
-			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
-static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
-			  adt7410_show_t_hyst, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
-			  adt7410_show_t_hyst, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
-			  NULL, ADT7410_STAT_T_LOW);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
-			  NULL, ADT7410_STAT_T_HIGH);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
-			  NULL, ADT7410_STAT_T_CRIT);
-
-static struct attribute *adt7410_attributes[] = {
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_min.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
-	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
-	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	NULL
-};
-
-static const struct attribute_group adt7410_group = {
-	.attrs = adt7410_attributes,
+static const struct adt7410_ops adt7410_i2c_ops = {
+	.read_word = adt7410_i2c_read_word,
+	.write_word = adt7410_i2c_write_word,
+	.read_byte = adt7410_i2c_read_byte,
+	.write_byte = adt7410_i2c_write_byte,
 };
 
-/*-----------------------------------------------------------------------*/
-
-/* device probe and removal */
-
-static int adt7410_probe(struct i2c_client *client,
-			 const struct i2c_device_id *id)
+static int adt7410_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
 {
-	struct adt7410_data *data;
-	int ret;
 
 	if (!i2c_check_functionality(client->adapter,
 			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
 		return -ENODEV;
 
-	data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
-			    GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	i2c_set_clientdata(client, data);
-	mutex_init(&data->update_lock);
-
-	/* configure as specified */
-	ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
-	if (ret < 0) {
-		dev_dbg(&client->dev, "Can't read config? %d\n", ret);
-		return ret;
-	}
-	data->oldconfig = ret;
-	/*
-	 * Set to 16 bit resolution, continous conversion and comparator mode.
-	 */
-	ret &= ~ADT7410_MODE_MASK;
-	data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
-			ADT7410_EVENT_MODE;
-	if (data->config != data->oldconfig) {
-		ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
-						data->config);
-		if (ret)
-			return ret;
-	}
-	dev_dbg(&client->dev, "Config %02x\n", data->config);
-
-	ret = adt7410_fill_cache(client);
-	if (ret)
-		goto exit_restore;
-
-	/* Register sysfs hooks */
-	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
-	if (ret)
-		goto exit_restore;
-
-	data->hwmon_dev = hwmon_device_register(&client->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		goto exit_remove;
-	}
-
-	dev_info(&client->dev, "sensor '%s'\n", client->name);
-
-	return 0;
-
-exit_remove:
-	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
-exit_restore:
-	i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
-	return ret;
+	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
 }
 
-static int adt7410_remove(struct i2c_client *client)
+static int adt7410_i2c_remove(struct i2c_client *client)
 {
-	struct adt7410_data *data = i2c_get_clientdata(client);
-
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
-	if (data->oldconfig != data->config)
-		i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
-					  data->oldconfig);
-	return 0;
+	return adt7410_remove(&client->dev);
 }
 
 static const struct i2c_device_id adt7410_ids[] = {
-	{ "adt7410", adt7410, },
-	{ "adt7420", adt7410, },
-	{ /* LIST END */ }
+	{ "adt7410", 0 },
+	{ "adt7420", 0 },
+	{}
 };
 MODULE_DEVICE_TABLE(i2c, adt7410_ids);
 
-#ifdef CONFIG_PM_SLEEP
-static int adt7410_suspend(struct device *dev)
-{
-	int ret;
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-
-	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
-					data->config | ADT7410_PD);
-	return ret;
-}
-
-static int adt7410_resume(struct device *dev)
-{
-	int ret;
-	struct i2c_client *client = to_i2c_client(dev);
-	struct adt7410_data *data = i2c_get_clientdata(client);
-
-	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
-	return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
-
-#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
-#else
-#define ADT7410_DEV_PM_OPS NULL
-#endif /* CONFIG_PM */
-
 static struct i2c_driver adt7410_driver = {
 	.class		= I2C_CLASS_HWMON,
 	.driver = {
 		.name	= "adt7410",
 		.pm	= ADT7410_DEV_PM_OPS,
 	},
-	.probe		= adt7410_probe,
-	.remove		= adt7410_remove,
+	.probe		= adt7410_i2c_probe,
+	.remove		= adt7410_i2c_remove,
 	.id_table	= adt7410_ids,
 	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
 };
-
 module_i2c_driver(adt7410_driver);
 
-MODULE_AUTHOR("Hartmut Knaack");
-MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7410/AD7420 driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
new file mode 100644
index 0000000..eeff198c
--- /dev/null
+++ b/drivers/hwmon/adt7x10.c
@@ -0,0 +1,476 @@
+/*
+ * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
+ *	 monitoring
+ * This driver handles the ADT7410 and compatible digital temperature sensors.
+ * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
+ * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
+ * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include "adt7x10.h"
+
+/*
+ * ADT7410 status
+ */
+#define ADT7410_STAT_T_LOW		(1 << 4)
+#define ADT7410_STAT_T_HIGH		(1 << 5)
+#define ADT7410_STAT_T_CRIT		(1 << 6)
+#define ADT7410_STAT_NOT_RDY		(1 << 7)
+
+/*
+ * ADT7410 config
+ */
+#define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
+#define ADT7410_CT_POLARITY		(1 << 2)
+#define ADT7410_INT_POLARITY		(1 << 3)
+#define ADT7410_EVENT_MODE		(1 << 4)
+#define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
+#define ADT7410_FULL			(0 << 5 | 0 << 6)
+#define ADT7410_PD			(1 << 5 | 1 << 6)
+#define ADT7410_RESOLUTION		(1 << 7)
+
+/*
+ * ADT7410 masks
+ */
+#define ADT7410_T13_VALUE_MASK			0xFFF8
+#define ADT7410_T_HYST_MASK			0xF
+
+/* straight from the datasheet */
+#define ADT7410_TEMP_MIN (-55000)
+#define ADT7410_TEMP_MAX 150000
+
+/* Each client has this additional data */
+struct adt7410_data {
+	const struct adt7410_ops *ops;
+	const char		*name;
+	struct device		*hwmon_dev;
+	struct mutex		update_lock;
+	u8			config;
+	u8			oldconfig;
+	bool			valid;		/* true if registers valid */
+	unsigned long		last_updated;	/* In jiffies */
+	s16			temp[4];	/* Register values,
+						   0 = input
+						   1 = high
+						   2 = low
+						   3 = critical */
+	u8			hyst;		/* hysteresis offset */
+};
+
+static int adt7410_read_byte(struct device *dev, u8 reg)
+{
+	struct adt7410_data *d = dev_get_drvdata(dev);
+	return d->ops->read_byte(dev, reg);
+}
+
+static int adt7410_write_byte(struct device *dev, u8 reg, u8 data)
+{
+	struct adt7410_data *d = dev_get_drvdata(dev);
+	return d->ops->write_byte(dev, reg, data);
+}
+
+static int adt7410_read_word(struct device *dev, u8 reg)
+{
+	struct adt7410_data *d = dev_get_drvdata(dev);
+	return d->ops->read_word(dev, reg);
+}
+
+static int adt7410_write_word(struct device *dev, u8 reg, u16 data)
+{
+	struct adt7410_data *d = dev_get_drvdata(dev);
+	return d->ops->write_word(dev, reg, data);
+}
+
+static const u8 ADT7410_REG_TEMP[4] = {
+	ADT7410_TEMPERATURE,		/* input */
+	ADT7410_T_ALARM_HIGH,		/* high */
+	ADT7410_T_ALARM_LOW,		/* low */
+	ADT7410_T_CRIT,			/* critical */
+};
+
+static int adt7410_temp_ready(struct device *dev)
+{
+	int i, status;
+
+	for (i = 0; i < 6; i++) {
+		status = adt7410_read_byte(dev, ADT7410_STATUS);
+		if (status < 0)
+			return status;
+		if (!(status & ADT7410_STAT_NOT_RDY))
+			return 0;
+		msleep(60);
+	}
+	return -ETIMEDOUT;
+}
+
+static int adt7410_update_temp(struct device *dev)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&data->update_lock);
+
+	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+	    || !data->valid) {
+		int temp;
+
+		dev_dbg(dev, "Starting update\n");
+
+		ret = adt7410_temp_ready(dev); /* check for new value */
+		if (ret)
+			goto abort;
+
+		temp = adt7410_read_word(dev, ADT7410_REG_TEMP[0]);
+		if (temp < 0) {
+			ret = temp;
+			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
+			goto abort;
+		}
+		data->temp[0] = temp;
+		data->last_updated = jiffies;
+		data->valid = true;
+	}
+
+abort:
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static int adt7410_fill_cache(struct device *dev)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+	int ret;
+	int i;
+
+	for (i = 1; i < ARRAY_SIZE(data->temp); i++) {
+		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[i]);
+		if (ret < 0) {
+			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
+			return ret;
+		}
+		data->temp[i] = ret;
+	}
+
+	ret = adt7410_read_byte(dev, ADT7410_T_HYST);
+	if (ret < 0) {
+		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_T_HYST, ret);
+		return ret;
+	}
+	data->hyst = ret;
+
+	return 0;
+}
+
+static s16 ADT7410_TEMP_TO_REG(long temp)
+{
+	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
+					       ADT7410_TEMP_MAX) * 128, 1000);
+}
+
+static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
+{
+	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
+	if (!(data->config & ADT7410_RESOLUTION))
+		reg &= ADT7410_T13_VALUE_MASK;
+	/*
+	 * temperature is stored in twos complement format, in steps of
+	 * 1/128°C
+	 */
+	return DIV_ROUND_CLOSEST(reg * 1000, 128);
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
+
+static ssize_t adt7410_show_temp(struct device *dev,
+				 struct device_attribute *da, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct adt7410_data *data = dev_get_drvdata(dev);
+
+
+	if (attr->index == 0) {
+		int ret;
+
+		ret = adt7410_update_temp(dev);
+		if (ret)
+			return ret;
+	}
+
+	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
+		       data->temp[attr->index]));
+}
+
+static ssize_t adt7410_set_temp(struct device *dev,
+				struct device_attribute *da,
+				const char *buf, size_t count)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct adt7410_data *data = dev_get_drvdata(dev);
+	int nr = attr->index;
+	long temp;
+	int ret;
+
+	ret = kstrtol(buf, 10, &temp);
+	if (ret)
+		return ret;
+
+	mutex_lock(&data->update_lock);
+	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
+	ret = adt7410_write_word(dev, ADT7410_REG_TEMP[nr], data->temp[nr]);
+	if (ret)
+		count = ret;
+	mutex_unlock(&data->update_lock);
+	return count;
+}
+
+static ssize_t adt7410_show_t_hyst(struct device *dev,
+				   struct device_attribute *da,
+				   char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct adt7410_data *data = dev_get_drvdata(dev);
+	int nr = attr->index;
+	int hyst;
+
+	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
+
+	/*
+	 * hysteresis is stored as a 4 bit offset in the device, convert it
+	 * to an absolute value
+	 */
+	if (nr == 2)	/* min has positive offset, others have negative */
+		hyst = -hyst;
+	return sprintf(buf, "%d\n",
+		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
+}
+
+static ssize_t adt7410_set_t_hyst(struct device *dev,
+				  struct device_attribute *da,
+				  const char *buf, size_t count)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+	int limit, ret;
+	long hyst;
+
+	ret = kstrtol(buf, 10, &hyst);
+	if (ret)
+		return ret;
+	/* convert absolute hysteresis value to a 4 bit delta value */
+	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
+	hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
+	data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000),
+				   0, ADT7410_T_HYST_MASK);
+	ret = adt7410_write_byte(dev, ADT7410_T_HYST, data->hyst);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t adt7410_show_alarm(struct device *dev,
+				  struct device_attribute *da,
+				  char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	int ret;
+
+	ret = adt7410_read_byte(dev, ADT7410_STATUS);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", !!(ret & attr->index));
+}
+
+static ssize_t adt7410_show_name(struct device *dev,
+				  struct device_attribute *da, char *buf)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", data->name);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+			  adt7410_show_temp, adt7410_set_temp, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+			  adt7410_show_temp, adt7410_set_temp, 2);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+			  adt7410_show_temp, adt7410_set_temp, 3);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
+			  adt7410_show_t_hyst, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
+			  adt7410_show_t_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
+			  NULL, ADT7410_STAT_T_LOW);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
+			  NULL, ADT7410_STAT_T_HIGH);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
+			  NULL, ADT7410_STAT_T_CRIT);
+static DEVICE_ATTR(name, S_IRUGO, adt7410_show_name, NULL);
+
+static struct attribute *adt7410_attributes[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_max.dev_attr.attr,
+	&sensor_dev_attr_temp1_min.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit.dev_attr.attr,
+	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adt7410_group = {
+	.attrs = adt7410_attributes,
+};
+
+int adt7410_probe(struct device *dev, const char *name,
+	const struct adt7410_ops *ops)
+{
+	struct adt7410_data *data;
+	int ret;
+
+	data = devm_kzalloc(dev, sizeof(struct adt7410_data),
+			    GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->ops = ops;
+	data->name = name;
+
+	dev_set_drvdata(dev, data);
+	mutex_init(&data->update_lock);
+
+	/* configure as specified */
+	ret = adt7410_read_byte(dev, ADT7410_CONFIG);
+	if (ret < 0) {
+		dev_dbg(dev, "Can't read config? %d\n", ret);
+		return ret;
+	}
+	data->oldconfig = ret;
+
+	/*
+	 * Set to 16 bit resolution, continous conversion and comparator mode.
+	 */
+	data->config = data->oldconfig;
+	data->config &= ~ADT7410_MODE_MASK;
+	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
+	if (data->config != data->oldconfig) {
+		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
+		if (ret)
+			return ret;
+	}
+	dev_dbg(dev, "Config %02x\n", data->config);
+
+	ret = adt7410_fill_cache(dev);
+	if (ret)
+		goto exit_restore;
+
+	/* Register sysfs hooks */
+	ret = sysfs_create_group(&dev->kobj, &adt7410_group);
+	if (ret)
+		goto exit_restore;
+
+	/*
+	 * The I2C device will already have it's own 'name' attribute, but for
+	 * the SPI device we need to register it. name will only be non NULL if
+	 * the device doesn't register the 'name' attribute on its own.
+	 */
+	if (name) {
+		ret = device_create_file(dev, &dev_attr_name);
+		if (ret)
+			goto exit_remove;
+	}
+
+	data->hwmon_dev = hwmon_device_register(dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		ret = PTR_ERR(data->hwmon_dev);
+		goto exit_remove_name;
+	}
+
+	return 0;
+
+exit_remove_name:
+	if (name)
+		device_remove_file(dev, &dev_attr_name);
+exit_remove:
+	sysfs_remove_group(&dev->kobj, &adt7410_group);
+exit_restore:
+	adt7410_write_byte(dev, ADT7410_CONFIG, data->oldconfig);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adt7410_probe);
+
+int adt7410_remove(struct device *dev)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	if (data->name)
+		device_remove_file(dev, &dev_attr_name);
+	sysfs_remove_group(&dev->kobj, &adt7410_group);
+	if (data->oldconfig != data->config)
+		adt7410_write_byte(dev, ADT7410_CONFIG,
+					  data->oldconfig);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adt7410_remove);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int adt7410_suspend(struct device *dev)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+
+	return adt7410_write_byte(dev, ADT7410_CONFIG,
+		data->config | ADT7410_PD);
+}
+
+static int adt7410_resume(struct device *dev)
+{
+	struct adt7410_data *data = dev_get_drvdata(dev);
+
+	return adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
+}
+
+SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
+EXPORT_SYMBOL_GPL(adt7410_dev_pm_ops);
+
+#endif /* CONFIG_PM_SLEEP */
+
+MODULE_AUTHOR("Hartmut Knaack");
+MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
new file mode 100644
index 0000000..a7165e6
--- /dev/null
+++ b/drivers/hwmon/adt7x10.h
@@ -0,0 +1,48 @@
+#ifndef __HWMON_ADT7X10_H__
+#define __HWMON_ADT7X10_H__
+
+#include <linux/types.h>
+#include <linux/pm.h>
+
+/* ADT7410 registers definition */
+#define ADT7410_TEMPERATURE		0
+#define ADT7410_STATUS			2
+#define ADT7410_CONFIG			3
+#define ADT7410_T_ALARM_HIGH		4
+#define ADT7410_T_ALARM_LOW		6
+#define ADT7410_T_CRIT			8
+#define ADT7410_T_HYST			0xA
+#define ADT7410_ID			0xB
+
+/* ADT7310 registers definition */
+#define ADT7310_STATUS			0
+#define ADT7310_CONFIG			1
+#define ADT7310_TEMPERATURE		2
+#define ADT7310_ID			3
+#define ADT7310_T_CRIT			4
+#define ADT7310_T_HYST			5
+#define ADT7310_T_ALARM_HIGH		6
+#define ADT7310_T_ALARM_LOW		7
+
+struct device;
+
+struct adt7410_ops {
+	int (*read_byte)(struct device *, u8 reg);
+	int (*write_byte)(struct device *, u8 reg, u8 data);
+	int (*read_word)(struct device *, u8 reg);
+	int (*write_word)(struct device *, u8 reg, u16 data);
+};
+
+int adt7410_probe(struct device *dev, const char *name,
+	const struct adt7410_ops *ops);
+int adt7410_remove(struct device *dev);
+
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops adt7410_dev_pm_ops;
+#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
+#else
+#define ADT7410_DEV_PM_OPS NULL
+#endif
+
+#endif
-- 
1.8.0


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

* [lm-sensors] [PATCH v2 2/4] hwmon: (adt7410)  =?utf-8?q?Add_support_for_the_ad
@ 2013-02-18 13:38   ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

VGhlIGFkdDczMTAvYWR0NzMyMCBpcyB0aGUgU1BJIHZlcnNpb24gb2YgdGhlIGFkdDc0MTAvYWR0
NzQyMC4gVGhlIHJlZ2lzdGVyIG1hcApsYXlvdXQgaXMgYSBiaXQgZGlmZmVyZW50LCBpLmUuIHRo
ZSByZWdpc3RlciBhZGRyZXNzZXMgZGlmZmVyIGJldHdlZW4gdGhlIHR3bwp2YXJpYW50cywgYnV0
IHRoZSBiaXQgbGF5b3V0cyBvZiB0aGUgaW5kaXZpZHVhbCByZWdpc3RlcnMgYXJlIGlkZW50aWNh
bC4gU28gYm90aApjaGlwIHZhcmlhbnRzIGNhbiBlYXNpbHkgYmUgc3VwcG9ydGVkIGJ5IHRoZSBz
YW1lIGRyaXZlci4gVGhlIGlzc3VlIG9mIG5vbgptYXRjaGluZyByZWdpc3RlciBhZGRyZXNzIGxh
eW91dHMgaXMgc29sdmVkIGJ5IGEgc2ltcGxlIGxvb2stdXAgdGFibGUgd2hpY2gKdHJhbnNsYXRl
cyB0aGUgSTJDIGFkZHJlc3NlcyB0byB0aGUgU1BJIGFkZHJlc3Nlcy4KClRoZSBwYXRjaCBtb3Zl
cyB0aGUgYnVsayBvZiB0aGUgYWR0NzQxMCBkcml2ZXIgdG8gYSBjb21tb24gbW9kdWxlIHRoYXQg
d2lsbCBiZQpzaGFyZWQgYnkgdGhlIGFkdDc0MTAgYW5kIGFkdDczMTAgZHJpdmVycy4gVGhpcyBj
b21tb24gbW9kdWxlIGltcGxlbWVudHMgdGhlCmRyaXZlciBsb2dpYyBhbmQgdXNlcyBhIHNldCBv
ZiB2aXJ0dWFsIGZ1bmN0aW9ucyB0byBwZXJmb3JtIElPIGFjY2Vzcy4gVGhlCmFkdDc0MTAgYW5k
IGFkdDczMTAgZHJpdmVyIG1vZHVsZXMgcHJvdmlkZSBwcm9wZXIgaW1wbGVtZW50YXRpb25zIG9m
IHRoZXNlIElPCmFjY2Vzc29yIGZ1bmN0aW9ucyBmb3IgSTJDIHJlc3BlY3RpdmUgU1BJLgoKU2ln
bmVkLW9mZi1ieTogTGFycy1QZXRlciBDbGF1c2VuIDxsYXJzQG1ldGFmb28uZGU+CgotLS0KQ2hh
bmdlcyBzaW5jZSB2MToKCSogVXBkYXRlIHRoZSBkcml2ZXIgZG9jdW1lbnRhdGlvbiB0byBpbmNs
dWRlIEFEVDczMTAvQURUNzMyMC9BRFQ3NDIwCgkqIFBhc3MgdGhlIHJlc3VsdCBvZiB0aGUgcmVh
ZCBtZXRob2RzIHZpYSB0aGUgcmV0dXJuIHZhbHVlIGluc3RlYWQgb2YgYQoJICBwb2ludGVyIGFy
Z3VtZW50CgkqIFNpbXBsaWZ5IHNwaSByZWFkIG1ldGhvZHMgYnkgdXNpbmcgc3BpX3c4cjggYW5k
IHNwaV93OHIxNgoJKiBVcGRhdGUgbW9kdWxlIGRlc2NyaXB0aW9uIG9mIHRoZSBzaGFyZWQgbW9k
dWxlCgkqIEZpeCBzb21lIHR5cG9zCi0tLQogRG9jdW1lbnRhdGlvbi9od21vbi9hZHQ3NDEwIHwg
IDQyICsrLS0KIGRyaXZlcnMvaHdtb24vS2NvbmZpZyAgICAgICB8ICAyMCArKwogZHJpdmVycy9o
d21vbi9NYWtlZmlsZSAgICAgIHwgICAyICsKIGRyaXZlcnMvaHdtb24vYWR0NzMxMC5jICAgICB8
IDExNSArKysrKysrKysrKwogZHJpdmVycy9od21vbi9hZHQ3NDEwLmMgICAgIHwgNDY0ICsrKy0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogZHJpdmVycy9od21vbi9hZHQ3
eDEwLmMgICAgIHwgNDc2ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr
KysrCiBkcml2ZXJzL2h3bW9uL2FkdDd4MTAuaCAgICAgfCAgNDggKysrKysKIDcgZmlsZXMgY2hh
bmdlZCwgNzIwIGluc2VydGlvbnMoKyksIDQ0NyBkZWxldGlvbnMoLSkKIGNyZWF0ZSBtb2RlIDEw
MDY0NCBkcml2ZXJzL2h3bW9uL2FkdDczMTAuYwogY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMv
aHdtb24vYWR0N3gxMC5jCiBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9od21vbi9hZHQ3eDEw
LmgKCmRpZmYgLS1naXQgYS9Eb2N1bWVudGF0aW9uL2h3bW9uL2FkdDc0MTAgYi9Eb2N1bWVudGF0
aW9uL2h3bW9uL2FkdDc0MTAKaW5kZXggOTYwMDQwMC4uZTQ1MmFlMCAxMDA2NDQKLS0tIGEvRG9j
dW1lbnRhdGlvbi9od21vbi9hZHQ3NDEwCisrKyBiL0RvY3VtZW50YXRpb24vaHdtb24vYWR0NzQx
MApAQCAtNywyNSArNyw0MSBAQCBTdXBwb3J0ZWQgY2hpcHM6CiAgICAgQWRkcmVzc2VzIHNjYW5u
ZWQ6IEkyQyAweDQ4IC0gMHg0QgogICAgIERhdGFzaGVldDogUHVibGljbHkgYXZhaWxhYmxlIGF0
IHRoZSBBbmFsb2cgRGV2aWNlcyB3ZWJzaXRlCiAgICAgICAgICAgICAgICBodHRwOi8vd3d3LmFu
YWxvZy5jb20vc3RhdGljL2ltcG9ydGVkLWZpbGVzL2RhdGFfc2hlZXRzL0FEVDc0MTAucGRmCisg
ICogQW5hbG9nIERldmljZXMgQURUNzQyMAorICAgIFByZWZpeDogJ2FkdDc0MjAnCisgICAgQWRk
cmVzc2VzIHNjYW5uZWQ6IEkyQyAweDQ4IC0gMHg0QgorICAgIERhdGFzaGVldDogUHVibGljbHkg
YXZhaWxhYmxlIGF0IHRoZSBBbmFsb2cgRGV2aWNlcyB3ZWJzaXRlCisgICAgICAgICAgICAgICBo
dHRwOi8vd3d3LmFuYWxvZy5jb20vc3RhdGljL2ltcG9ydGVkLWZpbGVzL2RhdGFfc2hlZXRzL0FE
VDc0MjAucGRmCisgICogQW5hbG9nIERldmljZXMgQURUNzMxMAorICAgIFByZWZpeDogJ2FkdDcz
MTAnCisgICAgQWRkcmVzc2VzIHNjYW5uZWQ6IEkyQyAweDQ4IC0gMHg0QgorICAgIERhdGFzaGVl
dDogUHVibGljbHkgYXZhaWxhYmxlIGF0IHRoZSBBbmFsb2cgRGV2aWNlcyB3ZWJzaXRlCisgICAg
ICAgICAgICAgICBodHRwOi8vd3d3LmFuYWxvZy5jb20vc3RhdGljL2ltcG9ydGVkLWZpbGVzL2Rh
dGFfc2hlZXRzL0FEVDczMTAucGRmCisgICogQW5hbG9nIERldmljZXMgQURUNzMyMAorICAgIFBy
ZWZpeDogJ2FkdDczMjAnCisgICAgQWRkcmVzc2VzIHNjYW5uZWQ6IEkyQyAweDQ4IC0gMHg0Qgor
ICAgIERhdGFzaGVldDogUHVibGljbHkgYXZhaWxhYmxlIGF0IHRoZSBBbmFsb2cgRGV2aWNlcyB3
ZWJzaXRlCisgICAgICAgICAgICAgICBodHRwOi8vd3d3LmFuYWxvZy5jb20vc3RhdGljL2ltcG9y
dGVkLWZpbGVzL2RhdGFfc2hlZXRzL0FEVDczMjAucGRmCiAKIEF1dGhvcjogSGFydG11dCBLbmFh
Y2sgPGtuYWFjay5oQGdteC5kZT4KIAogRGVzY3JpcHRpb24KIC0tLS0tLS0tLS0tCiAKLVRoZSBB
RFQ3NDEwIGlzIGEgdGVtcGVyYXR1cmUgc2Vuc29yIHdpdGggcmF0ZWQgdGVtcGVyYXR1cmUgcmFu
Z2Ugb2YgLTU1wrBDIHRvCi0rMTUwwrBDLiBJdCBoYXMgYSBoaWdoIGFjY3VyYWN5IG9mICsvLTAu
NcKwQyBhbmQgY2FuIGJlIG9wZXJhdGVkIGF0IGEgcmVzb2x1dGlvbgotb2YgMTMgYml0cyAoMC4w
NjI1wrBDKSBvciAxNiBiaXRzICgwLjAwNzjCsEMpLiBUaGUgc2Vuc29yIHByb3ZpZGVzIGFuIElO
VCBwaW4gdG8KLWluZGljYXRlIHRoYXQgYSBtaW5pbXVtIG9yIG1heGltdW0gdGVtcGVyYXR1cmUg
c2V0IHBvaW50IGhhcyBiZWVuIGV4Y2VlZGVkLCBhcwotd2VsbCBhcyBhIGNyaXRpY2FsIHRlbXBl
cmF0dXJlIChDVCkgcGluIHRvIGluZGljYXRlIHRoYXQgdGhlIGNyaXRpY2FsCi10ZW1wZXJhdHVy
ZSBzZXQgcG9pbnQgaGFzIGJlZW4gZXhjZWVkZWQuIEJvdGggcGlucyBjYW4gYmUgc2V0IHVwIHdp
dGggYSBjb21tb24KLWh5c3RlcmVzaXMgb2YgMMKwQyAtIDE1wrBDIGFuZCBhIGZhdWx0IHF1ZXVl
LCByYW5naW5nIGZyb20gMSB0byA0IGV2ZW50cy4gQm90aAotcGlucyBjYW4gaW5kaXZpZHVhbGx5
IHNldCB0byBiZSBhY3RpdmUtbG93IG9yIGFjdGl2ZS1oaWdoLCB3aGlsZSB0aGUgd2hvbGUKLWRl
dmljZSBjYW4gZWl0aGVyIHJ1biBpbiBjb21wYXJhdG9yIG1vZGUgb3IgaW50ZXJydXB0IG1vZGUu
IFRoZSBBRFQ3NDEwCi1zdXBwb3J0cyBjb250aW5vdXMgdGVtcGVyYXR1cmUgc2FtcGxpbmcsIGFz
IHdlbGwgYXMgc2FtcGxpbmcgb25lIHRlbXBlcmF0dXJlCi12YWx1ZSBwZXIgc2Vjb25kIG9yIGV2
ZW4ganVzdGdldCBvbmUgc2FtcGxlIG9uIGRlbWFuZCBmb3IgcG93ZXIgc2F2aW5nLgotQmVzaWRl
cywgaXQgY2FuIGNvbXBsZXRlbHkgcG93ZXIgZG93biBpdHMgQURDLCBpZiBwb3dlciBtYW5hZ2Vt
ZW50IGlzCi1yZXF1aXJlZC4KK1RoZSBBRFQ3NDEwIGFuZCBzaW1pbGFyIGFyZSBhIHRlbXBlcmF0
dXJlIHNlbnNvcnMgd2l0aCByYXRlZCB0ZW1wZXJhdHVyZSByYW5nZQorb2YgLTU1wrBDIHRvICsx
NTDCsEMgKEFEVDczMTAvQURUNzQxMCkgb3IgLTQwwrBDIHRvICsxNTDCsEMgKEFEVDczMjAvQURU
NzQyMCkuIFRoZXkKK2hhdmUgYSBoaWdoIGFjY3VyYWN5IG9mICsvLTAuNcKwQyAoQURUNzMxMC9B
RFQ3NDEwKSBvciArLy0wLjJDIChBRFQ3NDMwL0FEVDc0MjApCithbmQgY2FuIGJlIG9wZXJhdGVk
IGF0IGEgcmVzb2x1dGlvbiBvZiAxMyBiaXRzICgwLjA2MjXCsEMpIG9yIDE2IGJpdHMgKDAuMDA3
OMKwQykuCitUaGUgc2Vuc29yIHByb3ZpZGVzIGFuIElOVCBwaW4gdG8gaW5kaWNhdGUgdGhhdCBh
IG1pbmltdW0gb3IgbWF4aW11bSB0ZW1wZXJhdHVyZQorc2V0IHBvaW50IGhhcyBiZWVuIGV4Y2Vl
ZGVkLCBhcyB3ZWxsIGFzIGEgY3JpdGljYWwgdGVtcGVyYXR1cmUgKENUKSBwaW4gdG8KK2luZGlj
YXRlIHRoYXQgdGhlIGNyaXRpY2FsIHRlbXBlcmF0dXJlIHNldCBwb2ludCBoYXMgYmVlbiBleGNl
ZWRlZC4gQm90aCBwaW5zCitjYW4gYmUgc2V0IHVwIHdpdGggYSBjb21tb24gaHlzdGVyZXNpcyBv
ZiAwwrBDIC0gMTXCsEMgYW5kIGEgZmF1bHQgcXVldWUsIHJhbmdpbmcKK2Zyb20gMSB0byA0IGV2
ZW50cy4gQm90aCBwaW5zIGNhbiBpbmRpdmlkdWFsbHkgc2V0IHRvIGJlIGFjdGl2ZS1sb3cgb3IK
K2FjdGl2ZS1oaWdoLCB3aGlsZSB0aGUgd2hvbGUgZGV2aWNlIGNhbiBlaXRoZXIgcnVuIGluIGNv
bXBhcmF0b3IgbW9kZSBvcgoraW50ZXJydXB0IG1vZGUuIFRoZSBBRFQ3NDEwIHN1cHBvcnRzIGNv
bnRpbm91cyB0ZW1wZXJhdHVyZSBzYW1wbGluZywgYXMgd2VsbCBhcworc2FtcGxpbmcgb25lIHRl
bXBlcmF0dXJlIHZhbHVlIHBlciBzZWNvbmQgb3IgZXZlbiBqdXN0Z2V0IG9uZSBzYW1wbGUgb24g
ZGVtYW5kCitmb3IgcG93ZXIgc2F2aW5nLiAgQmVzaWRlcywgaXQgY2FuIGNvbXBsZXRlbHkgcG93
ZXIgZG93biBpdHMgQURDLCBpZiBwb3dlcgorbWFuYWdlbWVudCBpcyByZXF1aXJlZC4KIAogQ29u
ZmlndXJhdGlvbiBOb3RlcwogLS0tLS0tLS0tLS0tLS0tLS0tLQpkaWZmIC0tZ2l0IGEvZHJpdmVy
cy9od21vbi9LY29uZmlnIGIvZHJpdmVycy9od21vbi9LY29uZmlnCmluZGV4IDg5YWMxY2IuLmFh
YTE0ZjQgMTAwNjQ0Ci0tLSBhL2RyaXZlcnMvaHdtb24vS2NvbmZpZworKysgYi9kcml2ZXJzL2h3
bW9uL0tjb25maWcKQEAgLTE3OSw5ICsxNzksMjkgQEAgY29uZmlnIFNFTlNPUlNfQURNOTI0MAog
CSAgVGhpcyBkcml2ZXIgY2FuIGFsc28gYmUgYnVpbHQgYXMgYSBtb2R1bGUuICBJZiBzbywgdGhl
IG1vZHVsZQogCSAgd2lsbCBiZSBjYWxsZWQgYWRtOTI0MC4KIAorY29uZmlnIFNFTlNPUlNfQURU
N1gxMAorCXRyaXN0YXRlCisJaGVscAorCSAgVGhpcyBtb2R1bGUgY29udGFpbnMgY29tbW9uIGNv
ZGUgc2hhcmVkIGJ5IHRoZSBBRFQ3MzEwL0FEVDczMjAgYW5kCisJICBBRFQ3NDEwL0FEVDc0MjAg
dGVtcGVyYXR1cmUgbW9uaXRvcmluZyBjaGlwIGRyaXZlcnMuCisKKwkgIElmIGJ1aWxkIGFzIGEg
bW9kdWxlLCB0aGUgbW9kdWxlIHdpbGwgYmUgY2FsbGVkIGFkdDd4MTAuCisKK2NvbmZpZyBTRU5T
T1JTX0FEVDczMTAKKwl0cmlzdGF0ZSAiQW5hbG9nIERldmljZXMgQURUNzMxMC9BRFQ3MzIwIgor
CWRlcGVuZHMgb24gU1BJX01BU1RFUgorCXNlbGVjdCBTRU5TT1JTX0FEVDdYMTAKKwloZWxwCisJ
ICBJZiB5b3Ugc2F5IHllcyBoZXJlIHlvdSBnZXQgc3VwcG9ydCBmb3IgdGhlIEFuYWxvZyBEZXZp
Y2VzCisJICBBRFQ3MzEwIGFuZCBBRFQ3MzIwIHRlbXBlcmF0dXJlIG1vbml0b3JpbmcgY2hpcHMu
CisKKwkgIFRoaXMgZHJpdmVyIGNhbiBhbHNvIGJlIGJ1aWx0IGFzIGEgbW9kdWxlLiBJZiBzbywg
dGhlIG1vZHVsZQorCSAgd2lsbCBiZSBjYWxsZWQgYWR0NzMxMC4KKwogY29uZmlnIFNFTlNPUlNf
QURUNzQxMAogCXRyaXN0YXRlICJBbmFsb2cgRGV2aWNlcyBBRFQ3NDEwL0FEVDc0MjAiCiAJZGVw
ZW5kcyBvbiBJMkMKKwlzZWxlY3QgU0VOU09SU19BRFQ3WDEwCiAJaGVscAogCSAgSWYgeW91IHNh
eSB5ZXMgaGVyZSB5b3UgZ2V0IHN1cHBvcnQgZm9yIHRoZSBBbmFsb2cgRGV2aWNlcwogCSAgQURU
NzQxMCBhbmQgQURUNzQyMCB0ZW1wZXJhdHVyZSBtb25pdG9yaW5nIGNoaXBzLgpkaWZmIC0tZ2l0
IGEvZHJpdmVycy9od21vbi9NYWtlZmlsZSBiL2RyaXZlcnMvaHdtb24vTWFrZWZpbGUKaW5kZXgg
OGQ2ZDk3ZS4uNWQzNmE1NyAxMDA2NDQKLS0tIGEvZHJpdmVycy9od21vbi9NYWtlZmlsZQorKysg
Yi9kcml2ZXJzL2h3bW9uL01ha2VmaWxlCkBAIC0zNCw2ICszNCw4IEBAIG9iai0kKENPTkZJR19T
RU5TT1JTX0FETTkyNDApCSs9IGFkbTkyNDAubwogb2JqLSQoQ09ORklHX1NFTlNPUlNfQURTMTAx
NSkJKz0gYWRzMTAxNS5vCiBvYmotJChDT05GSUdfU0VOU09SU19BRFM3ODI4KQkrPSBhZHM3ODI4
Lm8KIG9iai0kKENPTkZJR19TRU5TT1JTX0FEUzc4NzEpCSs9IGFkczc4NzEubworb2JqLSQoQ09O
RklHX1NFTlNPUlNfQURUN1gxMCkJKz0gYWR0N3gxMC5vCitvYmotJChDT05GSUdfU0VOU09SU19B
RFQ3MzEwKQkrPSBhZHQ3MzEwLm8KIG9iai0kKENPTkZJR19TRU5TT1JTX0FEVDc0MTApCSs9IGFk
dDc0MTAubwogb2JqLSQoQ09ORklHX1NFTlNPUlNfQURUNzQxMSkJKz0gYWR0NzQxMS5vCiBvYmot
JChDT05GSUdfU0VOU09SU19BRFQ3NDYyKQkrPSBhZHQ3NDYyLm8KZGlmZiAtLWdpdCBhL2RyaXZl
cnMvaHdtb24vYWR0NzMxMC5jIGIvZHJpdmVycy9od21vbi9hZHQ3MzEwLmMKbmV3IGZpbGUgbW9k
ZSAxMDA2NDQKaW5kZXggMDAwMDAwMC4uMjE5NmFjMwotLS0gL2Rldi9udWxsCisrKyBiL2RyaXZl
cnMvaHdtb24vYWR0NzMxMC5jCkBAIC0wLDAgKzEsMTE1IEBACisvKgorICogQURUNzMxMC9BRFQ3
MzEwIGRpZ2l0YWwgdGVtcGVyYXR1cmUgc2Vuc29yIGRyaXZlcgorICoKKyAqIENvcHlyaWdodCAy
MDEwLTIwMTMgQW5hbG9nIERldmljZXMgSW5jLgorICogICBBdXRob3I6IExhcnMtUGV0ZXIgQ2xh
dXNlbiA8bGFyc0BtZXRhZm9vLmRlPgorICoKKyAqIExpY2Vuc2VkIHVuZGVyIHRoZSBHUEwtMiBv
ciBsYXRlci4KKyAqLworCisjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+CisjaW5jbHVkZSA8bGlu
dXgvaW5pdC5oPgorI2luY2x1ZGUgPGxpbnV4L3NwaS9zcGkuaD4KKyNpbmNsdWRlIDxhc20vdW5h
bGlnbmVkLmg+CisKKyNpbmNsdWRlICJhZHQ3eDEwLmgiCisKK3N0YXRpYyBjb25zdCB1OCBhZHQ3
MzEwX3JlZ190YWJsZVtdID0geworCVtBRFQ3NDEwX1RFTVBFUkFUVVJFXSAgID0gQURUNzMxMF9U
RU1QRVJBVFVSRSwKKwlbQURUNzQxMF9TVEFUVVNdCT0gQURUNzMxMF9TVEFUVVMsCisJW0FEVDc0
MTBfQ09ORklHXQk9IEFEVDczMTBfQ09ORklHLAorCVtBRFQ3NDEwX1RfQUxBUk1fSElHSF0JPSBB
RFQ3MzEwX1RfQUxBUk1fSElHSCwKKwlbQURUNzQxMF9UX0FMQVJNX0xPV10JPSBBRFQ3MzEwX1Rf
QUxBUk1fTE9XLAorCVtBRFQ3NDEwX1RfQ1JJVF0JPSBBRFQ3MzEwX1RfQ1JJVCwKKwlbQURUNzQx
MF9UX0hZU1RdCT0gQURUNzMxMF9UX0hZU1QsCisJW0FEVDc0MTBfSURdCQk9IEFEVDczMTBfSUQs
Cit9OworCisjZGVmaW5lIEFEVDczMTBfQ01EX1JFR19PRkZTRVQJMworI2RlZmluZSBBRFQ3MzEw
X0NNRF9SRUFECTB4NDAKKworI2RlZmluZSBBRDczMTBfQ09NTUFORChyZWcpIChhZHQ3MzEwX3Jl
Z190YWJsZVsocmVnKV0gPDwgQURUNzMxMF9DTURfUkVHX09GRlNFVCkKKworc3RhdGljIGludCBh
ZHQ3MzEwX3NwaV9yZWFkX3dvcmQoc3RydWN0IGRldmljZSAqZGV2LCB1OCByZWcpCit7CisJc3Ry
dWN0IHNwaV9kZXZpY2UgKnNwaSA9IHRvX3NwaV9kZXZpY2UoZGV2KTsKKwlpbnQgcmV0OworCisJ
cmV0ID0gc3BpX3c4cjE2KHNwaSwgQUQ3MzEwX0NPTU1BTkQocmVnKSB8IEFEVDczMTBfQ01EX1JF
QUQpOworCWlmIChyZXQgPCAwKQorCQlyZXR1cm4gcmV0OworCisJcmV0dXJuIGJlMTZfdG9fY3B1
KHJldCk7Cit9CisKK3N0YXRpYyBpbnQgYWR0NzMxMF9zcGlfd3JpdGVfd29yZChzdHJ1Y3QgZGV2
aWNlICpkZXYsIHU4IHJlZywKKwl1MTYgZGF0YSkKK3sKKwlzdHJ1Y3Qgc3BpX2RldmljZSAqc3Bp
ID0gdG9fc3BpX2RldmljZShkZXYpOworCXU4IGJ1ZlszXTsKKworCWJ1ZlswXSA9IEFENzMxMF9D
T01NQU5EKHJlZyk7CisJcHV0X3VuYWxpZ25lZF9iZTE2KGRhdGEsICZidWZbMV0pOworCisJcmV0
dXJuIHNwaV93cml0ZShzcGksIGJ1Ziwgc2l6ZW9mKGJ1ZikpOworfQorCitzdGF0aWMgaW50IGFk
dDczMTBfc3BpX3JlYWRfYnl0ZShzdHJ1Y3QgZGV2aWNlICpkZXYsIHU4IHJlZykKK3sKKwlzdHJ1
Y3Qgc3BpX2RldmljZSAqc3BpID0gdG9fc3BpX2RldmljZShkZXYpOworCisJcmV0dXJuIHNwaV93
OHI4KHNwaSwgQUQ3MzEwX0NPTU1BTkQocmVnKSB8IEFEVDczMTBfQ01EX1JFQUQpOworfQorCitz
dGF0aWMgaW50IGFkdDczMTBfc3BpX3dyaXRlX2J5dGUoc3RydWN0IGRldmljZSAqZGV2LCB1OCBy
ZWcsCisJdTggZGF0YSkKK3sKKwlzdHJ1Y3Qgc3BpX2RldmljZSAqc3BpID0gdG9fc3BpX2Rldmlj
ZShkZXYpOworCXU4IGJ1ZlsyXTsKKworCWJ1ZlswXSA9IEFENzMxMF9DT01NQU5EKHJlZyk7CisJ
YnVmWzFdID0gZGF0YTsKKworCXJldHVybiBzcGlfd3JpdGUoc3BpLCBidWYsIHNpemVvZihidWYp
KTsKK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBhZHQ3NDEwX29wcyBhZHQ3MzEwX3NwaV9vcHMg
PSB7CisJLnJlYWRfd29yZCA9IGFkdDczMTBfc3BpX3JlYWRfd29yZCwKKwkud3JpdGVfd29yZCA9
IGFkdDczMTBfc3BpX3dyaXRlX3dvcmQsCisJLnJlYWRfYnl0ZSA9IGFkdDczMTBfc3BpX3JlYWRf
Ynl0ZSwKKwkud3JpdGVfYnl0ZSA9IGFkdDczMTBfc3BpX3dyaXRlX2J5dGUsCit9OworCitzdGF0
aWMgaW50IGFkdDczMTBfc3BpX3Byb2JlKHN0cnVjdCBzcGlfZGV2aWNlICpzcGkpCit7CisJcmV0
dXJuIGFkdDc0MTBfcHJvYmUoJnNwaS0+ZGV2LCBzcGlfZ2V0X2RldmljZV9pZChzcGkpLT5uYW1l
LAorCQkJJmFkdDczMTBfc3BpX29wcyk7Cit9CisKK3N0YXRpYyBpbnQgYWR0NzMxMF9zcGlfcmVt
b3ZlKHN0cnVjdCBzcGlfZGV2aWNlICpzcGkpCit7CisJcmV0dXJuIGFkdDc0MTBfcmVtb3ZlKCZz
cGktPmRldik7Cit9CisKK3N0YXRpYyBjb25zdCBzdHJ1Y3Qgc3BpX2RldmljZV9pZCBhZHQ3MzEw
X2lkW10gPSB7CisJeyAiYWR0NzMxMCIsIDAgfSwKKwl7ICJhZHQ3MzIwIiwgMCB9LAorCXt9Cit9
OworTU9EVUxFX0RFVklDRV9UQUJMRShzcGksIGFkdDczMTBfaWQpOworCitzdGF0aWMgc3RydWN0
IHNwaV9kcml2ZXIgYWR0NzMxMF9kcml2ZXIgPSB7CisJLmRyaXZlciA9IHsKKwkJLm5hbWUgPSAi
YWR0NzMxMCIsCisJCS5vd25lciA9IFRISVNfTU9EVUxFLAorCQkucG0JPSBBRFQ3NDEwX0RFVl9Q
TV9PUFMsCisJfSwKKwkucHJvYmUgPSBhZHQ3MzEwX3NwaV9wcm9iZSwKKwkucmVtb3ZlID0gYWR0
NzMxMF9zcGlfcmVtb3ZlLAorCS5pZF90YWJsZSA9IGFkdDczMTBfaWQsCit9OworbW9kdWxlX3Nw
aV9kcml2ZXIoYWR0NzMxMF9kcml2ZXIpOworCitNT0RVTEVfQVVUSE9SKCJMYXJzLVBldGVyIENs
YXVzZW4gPGxhcnNAbWV0YWZvby5kZT4iKTsKK01PRFVMRV9ERVNDUklQVElPTigiQURUNzMxMC9B
RFQ3MzIwIGRyaXZlciIpOworTU9EVUxFX0xJQ0VOU0UoIkdQTCIpOwpkaWZmIC0tZ2l0IGEvZHJp
dmVycy9od21vbi9hZHQ3NDEwLmMgYi9kcml2ZXJzL2h3bW9uL2FkdDc0MTAuYwppbmRleCBiNmFj
ZmE0Li5iNTAwYWIzIDEwMDY0NAotLS0gYS9kcml2ZXJzL2h3bW9uL2FkdDc0MTAuYworKysgYi9k
cml2ZXJzL2h3bW9uL2FkdDc0MTAuYwpAQCAtMSw0ODUgKzEsODEgQEAKIC8qCi0gKiBhZHQ3NDEw
LmMgLSBQYXJ0IG9mIGxtX3NlbnNvcnMsIExpbnV4IGtlcm5lbCBtb2R1bGVzIGZvciBoYXJkd2Fy
ZQotICoJIG1vbml0b3JpbmcKLSAqIFRoaXMgZHJpdmVyIGhhbmRsZXMgdGhlIEFEVDc0MTAgYW5k
IGNvbXBhdGlibGUgZGlnaXRhbCB0ZW1wZXJhdHVyZSBzZW5zb3JzLgotICogSGFydG11dCBLbmFh
Y2sgPGtuYWFjay5oQGdteC5kZT4gMjAxMi0wNy0yMgotICogYmFzZWQgb24gbG03NS5jIGJ5IEZy
b2RvIExvb2lqYWFyZCA8ZnJvZG9sQGRkcy5ubD4KLSAqIGFuZCBhZHQ3NDEwLmMgZnJvbSBpaW8t
c3RhZ2luZyBieSBTb25pYyBaaGFuZyA8c29uaWMuemhhbmdAYW5hbG9nLmNvbT4KKyAqIEFEVDc0
MTAvQURUNzQyMCBkaWdpdGFsIHRlbXBlcmF0dXJlIHNlbnNvciBkcml2ZXIKICAqCi0gKiBUaGlz
IHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29y
IG1vZGlmeQotICogaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMg
TGljZW5zZSBhcyBwdWJsaXNoZWQgYnkKLSAqIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247
IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yCi0gKiAoYXQgeW91ciBvcHRpb24p
IGFueSBsYXRlciB2ZXJzaW9uLgorICogQ29weXJpZ2h0IDIwMTAtMjAxMyBBbmFsb2cgRGV2aWNl
cyBJbmMuCisgKiAgIEF1dGhvcjogTGFycy1QZXRlciBDbGF1c2VuIDxsYXJzQG1ldGFmb28uZGU+
CiAgKgotICogVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQg
d2lsbCBiZSB1c2VmdWwsCi0gKiBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZl
biB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgotICogTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1Mg
Rk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQotICogR05VIEdlbmVyYWwgUHVibGlj
IExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KLSAqCi0gKiBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2
ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQotICogYWxvbmcgd2l0
aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKLSAqIEZv
dW5kYXRpb24sIEluYy4sIDY3NSBNYXNzIEF2ZSwgQ2FtYnJpZGdlLCBNQSAwMjEzOSwgVVNBLgor
ICogTGljZW5zZWQgdW5kZXIgdGhlIEdQTC0yIG9yIGxhdGVyLgogICovCiAKICNpbmNsdWRlIDxs
aW51eC9tb2R1bGUuaD4KICNpbmNsdWRlIDxsaW51eC9pbml0Lmg+Ci0jaW5jbHVkZSA8bGludXgv
c2xhYi5oPgotI2luY2x1ZGUgPGxpbnV4L2ppZmZpZXMuaD4KICNpbmNsdWRlIDxsaW51eC9pMmMu
aD4KLSNpbmNsdWRlIDxsaW51eC9od21vbi5oPgotI2luY2x1ZGUgPGxpbnV4L2h3bW9uLXN5c2Zz
Lmg+Ci0jaW5jbHVkZSA8bGludXgvZXJyLmg+Ci0jaW5jbHVkZSA8bGludXgvbXV0ZXguaD4KLSNp
bmNsdWRlIDxsaW51eC9kZWxheS5oPgotCi0vKgotICogQURUNzQxMCByZWdpc3RlcnMgZGVmaW5p
dGlvbgotICovCi0KLSNkZWZpbmUgQURUNzQxMF9URU1QRVJBVFVSRQkJMAotI2RlZmluZSBBRFQ3
NDEwX1NUQVRVUwkJCTIKLSNkZWZpbmUgQURUNzQxMF9DT05GSUcJCQkzCi0jZGVmaW5lIEFEVDc0
MTBfVF9BTEFSTV9ISUdICQk0Ci0jZGVmaW5lIEFEVDc0MTBfVF9BTEFSTV9MT1cJCTYKLSNkZWZp
bmUgQURUNzQxMF9UX0NSSVQJCQk4Ci0jZGVmaW5lIEFEVDc0MTBfVF9IWVNUCQkJMHhBCi0KLS8q
Ci0gKiBBRFQ3NDEwIHN0YXR1cwotICovCi0jZGVmaW5lIEFEVDc0MTBfU1RBVF9UX0xPVwkJKDEg
PDwgNCkKLSNkZWZpbmUgQURUNzQxMF9TVEFUX1RfSElHSAkJKDEgPDwgNSkKLSNkZWZpbmUgQURU
NzQxMF9TVEFUX1RfQ1JJVAkJKDEgPDwgNikKLSNkZWZpbmUgQURUNzQxMF9TVEFUX05PVF9SRFkJ
CSgxIDw8IDcpCiAKLS8qCi0gKiBBRFQ3NDEwIGNvbmZpZwotICovCi0jZGVmaW5lIEFEVDc0MTBf
RkFVTFRfUVVFVUVfTUFTSwkoMSA8PCAwIHwgMSA8PCAxKQotI2RlZmluZSBBRFQ3NDEwX0NUX1BP
TEFSSVRZCQkoMSA8PCAyKQotI2RlZmluZSBBRFQ3NDEwX0lOVF9QT0xBUklUWQkJKDEgPDwgMykK
LSNkZWZpbmUgQURUNzQxMF9FVkVOVF9NT0RFCQkoMSA8PCA0KQotI2RlZmluZSBBRFQ3NDEwX01P
REVfTUFTSwkJKDEgPDwgNSB8IDEgPDwgNikKLSNkZWZpbmUgQURUNzQxMF9GVUxMCQkJKDAgPDwg
NSB8IDAgPDwgNikKLSNkZWZpbmUgQURUNzQxMF9QRAkJCSgxIDw8IDUgfCAxIDw8IDYpCi0jZGVm
aW5lIEFEVDc0MTBfUkVTT0xVVElPTgkJKDEgPDwgNykKKyNpbmNsdWRlICJhZHQ3eDEwLmgiCiAK
LS8qCi0gKiBBRFQ3NDEwIG1hc2tzCi0gKi8KLSNkZWZpbmUgQURUNzQxMF9UMTNfVkFMVUVfTUFT
SwkJCTB4RkZGOAotI2RlZmluZSBBRFQ3NDEwX1RfSFlTVF9NQVNLCQkJMHhGCi0KLS8qIHN0cmFp
Z2h0IGZyb20gdGhlIGRhdGFzaGVldCAqLwotI2RlZmluZSBBRFQ3NDEwX1RFTVBfTUlOICgtNTUw
MDApCi0jZGVmaW5lIEFEVDc0MTBfVEVNUF9NQVggMTUwMDAwCi0KLWVudW0gYWR0NzQxMF90eXBl
IHsJCS8qIGtlZXAgc29ydGVkIGluIGFscGhhYmV0aWNhbCBvcmRlciAqLwotCWFkdDc0MTAsCi19
OwotCi1zdGF0aWMgY29uc3QgdTggQURUNzQxMF9SRUdfVEVNUFs0XSA9IHsKLQlBRFQ3NDEwX1RF
TVBFUkFUVVJFLAkJLyogaW5wdXQgKi8KLQlBRFQ3NDEwX1RfQUxBUk1fSElHSCwJCS8qIGhpZ2gg
Ki8KLQlBRFQ3NDEwX1RfQUxBUk1fTE9XLAkJLyogbG93ICovCi0JQURUNzQxMF9UX0NSSVQsCQkJ
LyogY3JpdGljYWwgKi8KLX07Ci0KLS8qIEVhY2ggY2xpZW50IGhhcyB0aGlzIGFkZGl0aW9uYWwg
ZGF0YSAqLwotc3RydWN0IGFkdDc0MTBfZGF0YSB7Ci0Jc3RydWN0IGRldmljZQkJKmh3bW9uX2Rl
djsKLQlzdHJ1Y3QgbXV0ZXgJCXVwZGF0ZV9sb2NrOwotCXU4CQkJY29uZmlnOwotCXU4CQkJb2xk
Y29uZmlnOwotCWJvb2wJCQl2YWxpZDsJCS8qIHRydWUgaWYgcmVnaXN0ZXJzIHZhbGlkICovCi0J
dW5zaWduZWQgbG9uZwkJbGFzdF91cGRhdGVkOwkvKiBJbiBqaWZmaWVzICovCi0JczE2CQkJdGVt
cFs0XTsJLyogUmVnaXN0ZXIgdmFsdWVzLAotCQkJCQkJICAgMCA9IGlucHV0Ci0JCQkJCQkgICAx
ID0gaGlnaAotCQkJCQkJICAgMiA9IGxvdwotCQkJCQkJICAgMyA9IGNyaXRpY2FsICovCi0JdTgJ
CQloeXN0OwkJLyogaHlzdGVyZXNpcyBvZmZzZXQgKi8KLX07Ci0KLS8qCi0gKiBhZHQ3NDEwIHJl
Z2lzdGVyIGFjY2VzcyBieSBJMkMKLSAqLwotc3RhdGljIGludCBhZHQ3NDEwX3RlbXBfcmVhZHko
c3RydWN0IGkyY19jbGllbnQgKmNsaWVudCkKK3N0YXRpYyBpbnQgYWR0NzQxMF9pMmNfcmVhZF93
b3JkKHN0cnVjdCBkZXZpY2UgKmRldiwgdTggcmVnKQogewotCWludCBpLCBzdGF0dXM7Ci0KLQlm
b3IgKGkgPSAwOyBpIDwgNjsgaSsrKSB7Ci0JCXN0YXR1cyA9IGkyY19zbWJ1c19yZWFkX2J5dGVf
ZGF0YShjbGllbnQsIEFEVDc0MTBfU1RBVFVTKTsKLQkJaWYgKHN0YXR1cyA8IDApCi0JCQlyZXR1
cm4gc3RhdHVzOwotCQlpZiAoIShzdGF0dXMgJiBBRFQ3NDEwX1NUQVRfTk9UX1JEWSkpCi0JCQly
ZXR1cm4gMDsKLQkJbXNsZWVwKDYwKTsKLQl9Ci0JcmV0dXJuIC1FVElNRURPVVQ7CisJcmV0dXJu
IGkyY19zbWJ1c19yZWFkX3dvcmRfc3dhcHBlZCh0b19pMmNfY2xpZW50KGRldiksIHJlZyk7CiB9
CiAKLXN0YXRpYyBpbnQgYWR0NzQxMF91cGRhdGVfdGVtcChzdHJ1Y3QgZGV2aWNlICpkZXYpCitz
dGF0aWMgaW50IGFkdDc0MTBfaTJjX3dyaXRlX3dvcmQoc3RydWN0IGRldmljZSAqZGV2LCB1OCBy
ZWcsIHUxNiBkYXRhKQogewotCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQgPSB0b19pMmNfY2xp
ZW50KGRldik7Ci0Jc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50ZGF0
YShjbGllbnQpOwotCWludCByZXQgPSAwOwotCi0JbXV0ZXhfbG9jaygmZGF0YS0+dXBkYXRlX2xv
Y2spOwotCi0JaWYgKHRpbWVfYWZ0ZXIoamlmZmllcywgZGF0YS0+bGFzdF91cGRhdGVkICsgSFog
KyBIWiAvIDIpCi0JICAgIHx8ICFkYXRhLT52YWxpZCkgewotCQlpbnQgdGVtcDsKLQotCQlkZXZf
ZGJnKCZjbGllbnQtPmRldiwgIlN0YXJ0aW5nIHVwZGF0ZVxuIik7Ci0KLQkJcmV0ID0gYWR0NzQx
MF90ZW1wX3JlYWR5KGNsaWVudCk7IC8qIGNoZWNrIGZvciBuZXcgdmFsdWUgKi8KLQkJaWYgKHJl
dCkKLQkJCWdvdG8gYWJvcnQ7Ci0KLQkJdGVtcCA9IGkyY19zbWJ1c19yZWFkX3dvcmRfc3dhcHBl
ZChjbGllbnQsIEFEVDc0MTBfUkVHX1RFTVBbMF0pOwotCQlpZiAodGVtcCA8IDApIHsKLQkJCXJl
dCA9IHRlbXA7Ci0JCQlkZXZfZGJnKGRldiwgIkZhaWxlZCB0byByZWFkIHZhbHVlOiByZWcgJWQs
IGVycm9yICVkXG4iLAotCQkJCUFEVDc0MTBfUkVHX1RFTVBbMF0sIHJldCk7Ci0JCQlnb3RvIGFi
b3J0OwotCQl9Ci0JCWRhdGEtPnRlbXBbMF0gPSB0ZW1wOwotCi0JCWRhdGEtPmxhc3RfdXBkYXRl
ZCA9IGppZmZpZXM7Ci0JCWRhdGEtPnZhbGlkID0gdHJ1ZTsKLQl9Ci0KLWFib3J0OgotCW11dGV4
X3VubG9jaygmZGF0YS0+dXBkYXRlX2xvY2spOwotCXJldHVybiByZXQ7Ci19Ci0KLXN0YXRpYyBp
bnQgYWR0NzQxMF9maWxsX2NhY2hlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQpCi17Ci0Jc3Ry
dWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50ZGF0YShjbGllbnQpOwotCWlu
dCByZXQ7Ci0JaW50IGk7Ci0KLQlmb3IgKGkgPSAxOyBpIDwgQVJSQVlfU0laRShBRFQ3NDEwX1JF
R19URU1QKTsgaSsrKSB7Ci0JCXJldCA9IGkyY19zbWJ1c19yZWFkX3dvcmRfc3dhcHBlZChjbGll
bnQsIEFEVDc0MTBfUkVHX1RFTVBbaV0pOwotCQlpZiAocmV0IDwgMCkgewotCQkJZGV2X2RiZygm
Y2xpZW50LT5kZXYsCi0JCQkJIkZhaWxlZCB0byByZWFkIHZhbHVlOiByZWcgJWQsIGVycm9yICVk
XG4iLAotCQkJCUFEVDc0MTBfUkVHX1RFTVBbMF0sIHJldCk7Ci0JCQlyZXR1cm4gcmV0OwotCQl9
Ci0JCWRhdGEtPnRlbXBbaV0gPSByZXQ7Ci0JfQotCi0JcmV0ID0gaTJjX3NtYnVzX3JlYWRfYnl0
ZV9kYXRhKGNsaWVudCwgQURUNzQxMF9UX0hZU1QpOwotCWlmIChyZXQgPCAwKSB7Ci0JCWRldl9k
YmcoJmNsaWVudC0+ZGV2LAotCQkJIkZhaWxlZCB0byByZWFkIHZhbHVlOiBoeXN0IHJlZywgZXJy
b3IgJWRcbiIsCi0JCQlyZXQpOwotCQlyZXR1cm4gcmV0OwotCX0KLQlkYXRhLT5oeXN0ID0gcmV0
OwotCi0JcmV0dXJuIDA7CisJcmV0dXJuIGkyY19zbWJ1c193cml0ZV93b3JkX3N3YXBwZWQodG9f
aTJjX2NsaWVudChkZXYpLCByZWcsIGRhdGEpOwogfQogCi1zdGF0aWMgczE2IEFEVDc0MTBfVEVN
UF9UT19SRUcobG9uZyB0ZW1wKQorc3RhdGljIGludCBhZHQ3NDEwX2kyY19yZWFkX2J5dGUoc3Ry
dWN0IGRldmljZSAqZGV2LCB1OCByZWcpCiB7Ci0JcmV0dXJuIERJVl9ST1VORF9DTE9TRVNUKGNs
YW1wX3ZhbCh0ZW1wLCBBRFQ3NDEwX1RFTVBfTUlOLAotCQkJCQkgICBBRFQ3NDEwX1RFTVBfTUFY
KSAqIDEyOCwgMTAwMCk7CisJcmV0dXJuIGkyY19zbWJ1c19yZWFkX2J5dGVfZGF0YSh0b19pMmNf
Y2xpZW50KGRldiksIHJlZyk7CiB9CiAKLXN0YXRpYyBpbnQgQURUNzQxMF9SRUdfVE9fVEVNUChz
dHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhLCBzMTYgcmVnKQorc3RhdGljIGludCBhZHQ3NDEwX2ky
Y193cml0ZV9ieXRlKHN0cnVjdCBkZXZpY2UgKmRldiwgdTggcmVnLCB1OCBkYXRhKQogewotCS8q
IGluIDEzIGJpdCBtb2RlLCBiaXRzIDAtMiBhcmUgc3RhdHVzIGZsYWdzIC0gbWFzayB0aGVtIG91
dCAqLwotCWlmICghKGRhdGEtPmNvbmZpZyAmIEFEVDc0MTBfUkVTT0xVVElPTikpCi0JCXJlZyAm
PSBBRFQ3NDEwX1QxM19WQUxVRV9NQVNLOwotCS8qCi0JICogdGVtcGVyYXR1cmUgaXMgc3RvcmVk
IGluIHR3b3MgY29tcGxlbWVudCBmb3JtYXQsIGluIHN0ZXBzIG9mCi0JICogMS8xMjjCsEMKLQkg
Ki8KLQlyZXR1cm4gRElWX1JPVU5EX0NMT1NFU1QocmVnICogMTAwMCwgMTI4KTsKKwlyZXR1cm4g
aTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YSh0b19pMmNfY2xpZW50KGRldiksIHJlZywgZGF0YSk7
CiB9CiAKLS8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qLwotCi0vKiBzeXNmcyBhdHRyaWJ1dGVzIGZvciBod21v
biAqLwotCi1zdGF0aWMgc3NpemVfdCBhZHQ3NDEwX3Nob3dfdGVtcChzdHJ1Y3QgZGV2aWNlICpk
ZXYsCi0JCQkJIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICpkYSwgY2hhciAqYnVmKQotewotCXN0
cnVjdCBzZW5zb3JfZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciA9IHRvX3NlbnNvcl9kZXZfYXR0cihk
YSk7Ci0Jc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCA9IHRvX2kyY19jbGllbnQoZGV2KTsKLQlz
dHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhID0gaTJjX2dldF9jbGllbnRkYXRhKGNsaWVudCk7Ci0K
LQlpZiAoYXR0ci0+aW5kZXggPT0gMCkgewotCQlpbnQgcmV0OwotCi0JCXJldCA9IGFkdDc0MTBf
dXBkYXRlX3RlbXAoZGV2KTsKLQkJaWYgKHJldCkKLQkJCXJldHVybiByZXQ7Ci0JfQotCi0JcmV0
dXJuIHNwcmludGYoYnVmLCAiJWRcbiIsIEFEVDc0MTBfUkVHX1RPX1RFTVAoZGF0YSwKLQkJICAg
ICAgIGRhdGEtPnRlbXBbYXR0ci0+aW5kZXhdKSk7Ci19Ci0KLXN0YXRpYyBzc2l6ZV90IGFkdDc0
MTBfc2V0X3RlbXAoc3RydWN0IGRldmljZSAqZGV2LAotCQkJCXN0cnVjdCBkZXZpY2VfYXR0cmli
dXRlICpkYSwKLQkJCQljb25zdCBjaGFyICpidWYsIHNpemVfdCBjb3VudCkKLXsKLQlzdHJ1Y3Qg
c2Vuc29yX2RldmljZV9hdHRyaWJ1dGUgKmF0dHIgPSB0b19zZW5zb3JfZGV2X2F0dHIoZGEpOwot
CXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQgPSB0b19pMmNfY2xpZW50KGRldik7Ci0Jc3RydWN0
IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50ZGF0YShjbGllbnQpOwotCWludCBu
ciA9IGF0dHItPmluZGV4OwotCWxvbmcgdGVtcDsKLQlpbnQgcmV0OwotCi0JcmV0ID0ga3N0cnRv
bChidWYsIDEwLCAmdGVtcCk7Ci0JaWYgKHJldCkKLQkJcmV0dXJuIHJldDsKLQotCW11dGV4X2xv
Y2soJmRhdGEtPnVwZGF0ZV9sb2NrKTsKLQlkYXRhLT50ZW1wW25yXSA9IEFEVDc0MTBfVEVNUF9U
T19SRUcodGVtcCk7Ci0JcmV0ID0gaTJjX3NtYnVzX3dyaXRlX3dvcmRfc3dhcHBlZChjbGllbnQs
IEFEVDc0MTBfUkVHX1RFTVBbbnJdLAotCQkJCQkgICBkYXRhLT50ZW1wW25yXSk7Ci0JaWYgKHJl
dCkKLQkJY291bnQgPSByZXQ7Ci0JbXV0ZXhfdW5sb2NrKCZkYXRhLT51cGRhdGVfbG9jayk7Ci0J
cmV0dXJuIGNvdW50OwotfQotCi1zdGF0aWMgc3NpemVfdCBhZHQ3NDEwX3Nob3dfdF9oeXN0KHN0
cnVjdCBkZXZpY2UgKmRldiwKLQkJCQkgICBzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqZGEsCi0J
CQkJICAgY2hhciAqYnVmKQotewotCXN0cnVjdCBzZW5zb3JfZGV2aWNlX2F0dHJpYnV0ZSAqYXR0
ciA9IHRvX3NlbnNvcl9kZXZfYXR0cihkYSk7Ci0Jc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCA9
IHRvX2kyY19jbGllbnQoZGV2KTsKLQlzdHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhID0gaTJjX2dl
dF9jbGllbnRkYXRhKGNsaWVudCk7Ci0JaW50IG5yID0gYXR0ci0+aW5kZXg7Ci0JaW50IGh5c3Q7
Ci0KLQloeXN0ID0gKGRhdGEtPmh5c3QgJiBBRFQ3NDEwX1RfSFlTVF9NQVNLKSAqIDEwMDA7Ci0K
LQkvKgotCSAqIGh5c3RlcmVzaXMgaXMgc3RvcmVkIGFzIGEgNCBiaXQgb2Zmc2V0IGluIHRoZSBk
ZXZpY2UsIGNvbnZlcnQgaXQKLQkgKiB0byBhbiBhYnNvbHV0ZSB2YWx1ZQotCSAqLwotCWlmIChu
ciA9PSAyKQkvKiBtaW4gaGFzIHBvc2l0aXZlIG9mZnNldCwgb3RoZXJzIGhhdmUgbmVnYXRpdmUg
Ki8KLQkJaHlzdCA9IC1oeXN0OwotCXJldHVybiBzcHJpbnRmKGJ1ZiwgIiVkXG4iLAotCQkgICAg
ICAgQURUNzQxMF9SRUdfVE9fVEVNUChkYXRhLCBkYXRhLT50ZW1wW25yXSkgLSBoeXN0KTsKLX0K
LQotc3RhdGljIHNzaXplX3QgYWR0NzQxMF9zZXRfdF9oeXN0KHN0cnVjdCBkZXZpY2UgKmRldiwK
LQkJCQkgIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICpkYSwKLQkJCQkgIGNvbnN0IGNoYXIgKmJ1
Ziwgc2l6ZV90IGNvdW50KQotewotCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQgPSB0b19pMmNf
Y2xpZW50KGRldik7Ci0Jc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50
ZGF0YShjbGllbnQpOwotCWludCBsaW1pdCwgcmV0OwotCWxvbmcgaHlzdDsKLQotCXJldCA9IGtz
dHJ0b2woYnVmLCAxMCwgJmh5c3QpOwotCWlmIChyZXQpCi0JCXJldHVybiByZXQ7Ci0JLyogY29u
dmVydCBhYnNvbHV0ZSBoeXN0ZXJlc2lzIHZhbHVlIHRvIGEgNCBiaXQgZGVsdGEgdmFsdWUgKi8K
LQlsaW1pdCA9IEFEVDc0MTBfUkVHX1RPX1RFTVAoZGF0YSwgZGF0YS0+dGVtcFsxXSk7Ci0JaHlz
dCA9IGNsYW1wX3ZhbChoeXN0LCBBRFQ3NDEwX1RFTVBfTUlOLCBBRFQ3NDEwX1RFTVBfTUFYKTsK
LQlkYXRhLT5oeXN0ID0gY2xhbXBfdmFsKERJVl9ST1VORF9DTE9TRVNUKGxpbWl0IC0gaHlzdCwg
MTAwMCksIDAsCi0JCQkgICAgICAgQURUNzQxMF9UX0hZU1RfTUFTSyk7Ci0JcmV0ID0gaTJjX3Nt
YnVzX3dyaXRlX2J5dGVfZGF0YShjbGllbnQsIEFEVDc0MTBfVF9IWVNULCBkYXRhLT5oeXN0KTsK
LQlpZiAocmV0KQotCQlyZXR1cm4gcmV0OwotCi0JcmV0dXJuIGNvdW50OwotfQotCi1zdGF0aWMg
c3NpemVfdCBhZHQ3NDEwX3Nob3dfYWxhcm0oc3RydWN0IGRldmljZSAqZGV2LAotCQkJCSAgc3Ry
dWN0IGRldmljZV9hdHRyaWJ1dGUgKmRhLAotCQkJCSAgY2hhciAqYnVmKQotewotCXN0cnVjdCBp
MmNfY2xpZW50ICpjbGllbnQgPSB0b19pMmNfY2xpZW50KGRldik7Ci0Jc3RydWN0IHNlbnNvcl9k
ZXZpY2VfYXR0cmlidXRlICphdHRyID0gdG9fc2Vuc29yX2Rldl9hdHRyKGRhKTsKLQlpbnQgcmV0
OwotCi0JcmV0ID0gaTJjX3NtYnVzX3JlYWRfYnl0ZV9kYXRhKGNsaWVudCwgQURUNzQxMF9TVEFU
VVMpOwotCWlmIChyZXQgPCAwKQotCQlyZXR1cm4gcmV0OwotCi0JcmV0dXJuIHNwcmludGYoYnVm
LCAiJWRcbiIsICEhKHJldCAmIGF0dHItPmluZGV4KSk7Ci19Ci0KLXN0YXRpYyBTRU5TT1JfREVW
SUNFX0FUVFIodGVtcDFfaW5wdXQsIFNfSVJVR08sIGFkdDc0MTBfc2hvd190ZW1wLCBOVUxMLCAw
KTsKLXN0YXRpYyBTRU5TT1JfREVWSUNFX0FUVFIodGVtcDFfbWF4LCBTX0lXVVNSIHwgU19JUlVH
TywKLQkJCSAgYWR0NzQxMF9zaG93X3RlbXAsIGFkdDc0MTBfc2V0X3RlbXAsIDEpOwotc3RhdGlj
IFNFTlNPUl9ERVZJQ0VfQVRUUih0ZW1wMV9taW4sIFNfSVdVU1IgfCBTX0lSVUdPLAotCQkJICBh
ZHQ3NDEwX3Nob3dfdGVtcCwgYWR0NzQxMF9zZXRfdGVtcCwgMik7Ci1zdGF0aWMgU0VOU09SX0RF
VklDRV9BVFRSKHRlbXAxX2NyaXQsIFNfSVdVU1IgfCBTX0lSVUdPLAotCQkJICBhZHQ3NDEwX3No
b3dfdGVtcCwgYWR0NzQxMF9zZXRfdGVtcCwgMyk7Ci1zdGF0aWMgU0VOU09SX0RFVklDRV9BVFRS
KHRlbXAxX21heF9oeXN0LCBTX0lXVVNSIHwgU19JUlVHTywKLQkJCSAgYWR0NzQxMF9zaG93X3Rf
aHlzdCwgYWR0NzQxMF9zZXRfdF9oeXN0LCAxKTsKLXN0YXRpYyBTRU5TT1JfREVWSUNFX0FUVFIo
dGVtcDFfbWluX2h5c3QsIFNfSVJVR08sCi0JCQkgIGFkdDc0MTBfc2hvd190X2h5c3QsIE5VTEws
IDIpOwotc3RhdGljIFNFTlNPUl9ERVZJQ0VfQVRUUih0ZW1wMV9jcml0X2h5c3QsIFNfSVJVR08s
Ci0JCQkgIGFkdDc0MTBfc2hvd190X2h5c3QsIE5VTEwsIDMpOwotc3RhdGljIFNFTlNPUl9ERVZJ
Q0VfQVRUUih0ZW1wMV9taW5fYWxhcm0sIFNfSVJVR08sIGFkdDc0MTBfc2hvd19hbGFybSwKLQkJ
CSAgTlVMTCwgQURUNzQxMF9TVEFUX1RfTE9XKTsKLXN0YXRpYyBTRU5TT1JfREVWSUNFX0FUVFIo
dGVtcDFfbWF4X2FsYXJtLCBTX0lSVUdPLCBhZHQ3NDEwX3Nob3dfYWxhcm0sCi0JCQkgIE5VTEws
IEFEVDc0MTBfU1RBVF9UX0hJR0gpOwotc3RhdGljIFNFTlNPUl9ERVZJQ0VfQVRUUih0ZW1wMV9j
cml0X2FsYXJtLCBTX0lSVUdPLCBhZHQ3NDEwX3Nob3dfYWxhcm0sCi0JCQkgIE5VTEwsIEFEVDc0
MTBfU1RBVF9UX0NSSVQpOwotCi1zdGF0aWMgc3RydWN0IGF0dHJpYnV0ZSAqYWR0NzQxMF9hdHRy
aWJ1dGVzW10gPSB7Ci0JJnNlbnNvcl9kZXZfYXR0cl90ZW1wMV9pbnB1dC5kZXZfYXR0ci5hdHRy
LAotCSZzZW5zb3JfZGV2X2F0dHJfdGVtcDFfbWF4LmRldl9hdHRyLmF0dHIsCi0JJnNlbnNvcl9k
ZXZfYXR0cl90ZW1wMV9taW4uZGV2X2F0dHIuYXR0ciwKLQkmc2Vuc29yX2Rldl9hdHRyX3RlbXAx
X2NyaXQuZGV2X2F0dHIuYXR0ciwKLQkmc2Vuc29yX2Rldl9hdHRyX3RlbXAxX21heF9oeXN0LmRl
dl9hdHRyLmF0dHIsCi0JJnNlbnNvcl9kZXZfYXR0cl90ZW1wMV9taW5faHlzdC5kZXZfYXR0ci5h
dHRyLAotCSZzZW5zb3JfZGV2X2F0dHJfdGVtcDFfY3JpdF9oeXN0LmRldl9hdHRyLmF0dHIsCi0J
JnNlbnNvcl9kZXZfYXR0cl90ZW1wMV9taW5fYWxhcm0uZGV2X2F0dHIuYXR0ciwKLQkmc2Vuc29y
X2Rldl9hdHRyX3RlbXAxX21heF9hbGFybS5kZXZfYXR0ci5hdHRyLAotCSZzZW5zb3JfZGV2X2F0
dHJfdGVtcDFfY3JpdF9hbGFybS5kZXZfYXR0ci5hdHRyLAotCU5VTEwKLX07Ci0KLXN0YXRpYyBj
b25zdCBzdHJ1Y3QgYXR0cmlidXRlX2dyb3VwIGFkdDc0MTBfZ3JvdXAgPSB7Ci0JLmF0dHJzID0g
YWR0NzQxMF9hdHRyaWJ1dGVzLAorc3RhdGljIGNvbnN0IHN0cnVjdCBhZHQ3NDEwX29wcyBhZHQ3
NDEwX2kyY19vcHMgPSB7CisJLnJlYWRfd29yZCA9IGFkdDc0MTBfaTJjX3JlYWRfd29yZCwKKwku
d3JpdGVfd29yZCA9IGFkdDc0MTBfaTJjX3dyaXRlX3dvcmQsCisJLnJlYWRfYnl0ZSA9IGFkdDc0
MTBfaTJjX3JlYWRfYnl0ZSwKKwkud3JpdGVfYnl0ZSA9IGFkdDc0MTBfaTJjX3dyaXRlX2J5dGUs
CiB9OwogCi0vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KLQotLyogZGV2aWNlIHByb2JlIGFuZCByZW1vdmFs
ICovCi0KLXN0YXRpYyBpbnQgYWR0NzQxMF9wcm9iZShzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50
LAotCQkJIGNvbnN0IHN0cnVjdCBpMmNfZGV2aWNlX2lkICppZCkKK3N0YXRpYyBpbnQgYWR0NzQx
MF9pMmNfcHJvYmUoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCwKKwljb25zdCBzdHJ1Y3QgaTJj
X2RldmljZV9pZCAqaWQpCiB7Ci0Jc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YTsKLQlpbnQgcmV0
OwogCiAJaWYgKCFpMmNfY2hlY2tfZnVuY3Rpb25hbGl0eShjbGllbnQtPmFkYXB0ZXIsCiAJCQlJ
MkNfRlVOQ19TTUJVU19CWVRFX0RBVEEgfCBJMkNfRlVOQ19TTUJVU19XT1JEX0RBVEEpKQogCQly
ZXR1cm4gLUVOT0RFVjsKIAotCWRhdGEgPSBkZXZtX2t6YWxsb2MoJmNsaWVudC0+ZGV2LCBzaXpl
b2Yoc3RydWN0IGFkdDc0MTBfZGF0YSksCi0JCQkgICAgR0ZQX0tFUk5FTCk7Ci0JaWYgKCFkYXRh
KQotCQlyZXR1cm4gLUVOT01FTTsKLQotCWkyY19zZXRfY2xpZW50ZGF0YShjbGllbnQsIGRhdGEp
OwotCW11dGV4X2luaXQoJmRhdGEtPnVwZGF0ZV9sb2NrKTsKLQotCS8qIGNvbmZpZ3VyZSBhcyBz
cGVjaWZpZWQgKi8KLQlyZXQgPSBpMmNfc21idXNfcmVhZF9ieXRlX2RhdGEoY2xpZW50LCBBRFQ3
NDEwX0NPTkZJRyk7Ci0JaWYgKHJldCA8IDApIHsKLQkJZGV2X2RiZygmY2xpZW50LT5kZXYsICJD
YW4ndCByZWFkIGNvbmZpZz8gJWRcbiIsIHJldCk7Ci0JCXJldHVybiByZXQ7Ci0JfQotCWRhdGEt
Pm9sZGNvbmZpZyA9IHJldDsKLQkvKgotCSAqIFNldCB0byAxNiBiaXQgcmVzb2x1dGlvbiwgY29u
dGlub3VzIGNvbnZlcnNpb24gYW5kIGNvbXBhcmF0b3IgbW9kZS4KLQkgKi8KLQlyZXQgJj0gfkFE
VDc0MTBfTU9ERV9NQVNLOwotCWRhdGEtPmNvbmZpZyA9IHJldCB8IEFEVDc0MTBfRlVMTCB8IEFE
VDc0MTBfUkVTT0xVVElPTiB8Ci0JCQlBRFQ3NDEwX0VWRU5UX01PREU7Ci0JaWYgKGRhdGEtPmNv
bmZpZyAhPSBkYXRhLT5vbGRjb25maWcpIHsKLQkJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVf
ZGF0YShjbGllbnQsIEFEVDc0MTBfQ09ORklHLAotCQkJCQkJZGF0YS0+Y29uZmlnKTsKLQkJaWYg
KHJldCkKLQkJCXJldHVybiByZXQ7Ci0JfQotCWRldl9kYmcoJmNsaWVudC0+ZGV2LCAiQ29uZmln
ICUwMnhcbiIsIGRhdGEtPmNvbmZpZyk7Ci0KLQlyZXQgPSBhZHQ3NDEwX2ZpbGxfY2FjaGUoY2xp
ZW50KTsKLQlpZiAocmV0KQotCQlnb3RvIGV4aXRfcmVzdG9yZTsKLQotCS8qIFJlZ2lzdGVyIHN5
c2ZzIGhvb2tzICovCi0JcmV0ID0gc3lzZnNfY3JlYXRlX2dyb3VwKCZjbGllbnQtPmRldi5rb2Jq
LCAmYWR0NzQxMF9ncm91cCk7Ci0JaWYgKHJldCkKLQkJZ290byBleGl0X3Jlc3RvcmU7Ci0KLQlk
YXRhLT5od21vbl9kZXYgPSBod21vbl9kZXZpY2VfcmVnaXN0ZXIoJmNsaWVudC0+ZGV2KTsKLQlp
ZiAoSVNfRVJSKGRhdGEtPmh3bW9uX2RldikpIHsKLQkJcmV0ID0gUFRSX0VSUihkYXRhLT5od21v
bl9kZXYpOwotCQlnb3RvIGV4aXRfcmVtb3ZlOwotCX0KLQotCWRldl9pbmZvKCZjbGllbnQtPmRl
diwgInNlbnNvciAnJXMnXG4iLCBjbGllbnQtPm5hbWUpOwotCi0JcmV0dXJuIDA7Ci0KLWV4aXRf
cmVtb3ZlOgotCXN5c2ZzX3JlbW92ZV9ncm91cCgmY2xpZW50LT5kZXYua29iaiwgJmFkdDc0MTBf
Z3JvdXApOwotZXhpdF9yZXN0b3JlOgotCWkyY19zbWJ1c193cml0ZV9ieXRlX2RhdGEoY2xpZW50
LCBBRFQ3NDEwX0NPTkZJRywgZGF0YS0+b2xkY29uZmlnKTsKLQlyZXR1cm4gcmV0OworCXJldHVy
biBhZHQ3NDEwX3Byb2JlKCZjbGllbnQtPmRldiwgTlVMTCwgJmFkdDc0MTBfaTJjX29wcyk7CiB9
CiAKLXN0YXRpYyBpbnQgYWR0NzQxMF9yZW1vdmUoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCkK
K3N0YXRpYyBpbnQgYWR0NzQxMF9pMmNfcmVtb3ZlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQp
CiB7Ci0Jc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50ZGF0YShjbGll
bnQpOwotCi0JaHdtb25fZGV2aWNlX3VucmVnaXN0ZXIoZGF0YS0+aHdtb25fZGV2KTsKLQlzeXNm
c19yZW1vdmVfZ3JvdXAoJmNsaWVudC0+ZGV2LmtvYmosICZhZHQ3NDEwX2dyb3VwKTsKLQlpZiAo
ZGF0YS0+b2xkY29uZmlnICE9IGRhdGEtPmNvbmZpZykKLQkJaTJjX3NtYnVzX3dyaXRlX2J5dGVf
ZGF0YShjbGllbnQsIEFEVDc0MTBfQ09ORklHLAotCQkJCQkgIGRhdGEtPm9sZGNvbmZpZyk7Ci0J
cmV0dXJuIDA7CisJcmV0dXJuIGFkdDc0MTBfcmVtb3ZlKCZjbGllbnQtPmRldik7CiB9CiAKIHN0
YXRpYyBjb25zdCBzdHJ1Y3QgaTJjX2RldmljZV9pZCBhZHQ3NDEwX2lkc1tdID0gewotCXsgImFk
dDc0MTAiLCBhZHQ3NDEwLCB9LAotCXsgImFkdDc0MjAiLCBhZHQ3NDEwLCB9LAotCXsgLyogTElT
VCBFTkQgKi8gfQorCXsgImFkdDc0MTAiLCAwIH0sCisJeyAiYWR0NzQyMCIsIDAgfSwKKwl7fQog
fTsKIE1PRFVMRV9ERVZJQ0VfVEFCTEUoaTJjLCBhZHQ3NDEwX2lkcyk7CiAKLSNpZmRlZiBDT05G
SUdfUE1fU0xFRVAKLXN0YXRpYyBpbnQgYWR0NzQxMF9zdXNwZW5kKHN0cnVjdCBkZXZpY2UgKmRl
dikKLXsKLQlpbnQgcmV0OwotCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQgPSB0b19pMmNfY2xp
ZW50KGRldik7Ci0Jc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50ZGF0
YShjbGllbnQpOwotCi0JcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjbGllbnQsIEFE
VDc0MTBfQ09ORklHLAotCQkJCQlkYXRhLT5jb25maWcgfCBBRFQ3NDEwX1BEKTsKLQlyZXR1cm4g
cmV0OwotfQotCi1zdGF0aWMgaW50IGFkdDc0MTBfcmVzdW1lKHN0cnVjdCBkZXZpY2UgKmRldikK
LXsKLQlpbnQgcmV0OwotCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQgPSB0b19pMmNfY2xpZW50
KGRldik7Ci0Jc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGkyY19nZXRfY2xpZW50ZGF0YShj
bGllbnQpOwotCi0JcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjbGllbnQsIEFEVDc0
MTBfQ09ORklHLCBkYXRhLT5jb25maWcpOwotCXJldHVybiByZXQ7Ci19Ci0KLXN0YXRpYyBTSU1Q
TEVfREVWX1BNX09QUyhhZHQ3NDEwX2Rldl9wbV9vcHMsIGFkdDc0MTBfc3VzcGVuZCwgYWR0NzQx
MF9yZXN1bWUpOwotCi0jZGVmaW5lIEFEVDc0MTBfREVWX1BNX09QUyAoJmFkdDc0MTBfZGV2X3Bt
X29wcykKLSNlbHNlCi0jZGVmaW5lIEFEVDc0MTBfREVWX1BNX09QUyBOVUxMCi0jZW5kaWYgLyog
Q09ORklHX1BNICovCi0KIHN0YXRpYyBzdHJ1Y3QgaTJjX2RyaXZlciBhZHQ3NDEwX2RyaXZlciA9
IHsKIAkuY2xhc3MJCT0gSTJDX0NMQVNTX0hXTU9OLAogCS5kcml2ZXIgPSB7CiAJCS5uYW1lCT0g
ImFkdDc0MTAiLAogCQkucG0JPSBBRFQ3NDEwX0RFVl9QTV9PUFMsCiAJfSwKLQkucHJvYmUJCT0g
YWR0NzQxMF9wcm9iZSwKLQkucmVtb3ZlCQk9IGFkdDc0MTBfcmVtb3ZlLAorCS5wcm9iZQkJPSBh
ZHQ3NDEwX2kyY19wcm9iZSwKKwkucmVtb3ZlCQk9IGFkdDc0MTBfaTJjX3JlbW92ZSwKIAkuaWRf
dGFibGUJPSBhZHQ3NDEwX2lkcywKIAkuYWRkcmVzc19saXN0CT0gSTJDX0FERFJTKDB4NDgsIDB4
NDksIDB4NGEsIDB4NGIpLAogfTsKLQogbW9kdWxlX2kyY19kcml2ZXIoYWR0NzQxMF9kcml2ZXIp
OwogCi1NT0RVTEVfQVVUSE9SKCJIYXJ0bXV0IEtuYWFjayIpOwotTU9EVUxFX0RFU0NSSVBUSU9O
KCJBRFQ3NDEwL0FEVDc0MjAgZHJpdmVyIik7CitNT0RVTEVfQVVUSE9SKCJMYXJzLVBldGVyIENs
YXVzZW4gPGxhcnNAbWV0YWZvby5kZT4iKTsKK01PRFVMRV9ERVNDUklQVElPTigiQURUNzQxMC9B
RDc0MjAgZHJpdmVyIik7CiBNT0RVTEVfTElDRU5TRSgiR1BMIik7CmRpZmYgLS1naXQgYS9kcml2
ZXJzL2h3bW9uL2FkdDd4MTAuYyBiL2RyaXZlcnMvaHdtb24vYWR0N3gxMC5jCm5ldyBmaWxlIG1v
ZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAuLmVlZmYxOThjCi0tLSAvZGV2L251bGwKKysrIGIvZHJp
dmVycy9od21vbi9hZHQ3eDEwLmMKQEAgLTAsMCArMSw0NzYgQEAKKy8qCisgKiBhZHQ3NDEwLmMg
LSBQYXJ0IG9mIGxtX3NlbnNvcnMsIExpbnV4IGtlcm5lbCBtb2R1bGVzIGZvciBoYXJkd2FyZQor
ICoJIG1vbml0b3JpbmcKKyAqIFRoaXMgZHJpdmVyIGhhbmRsZXMgdGhlIEFEVDc0MTAgYW5kIGNv
bXBhdGlibGUgZGlnaXRhbCB0ZW1wZXJhdHVyZSBzZW5zb3JzLgorICogSGFydG11dCBLbmFhY2sg
PGtuYWFjay5oQGdteC5kZT4gMjAxMi0wNy0yMgorICogYmFzZWQgb24gbG03NS5jIGJ5IEZyb2Rv
IExvb2lqYWFyZCA8ZnJvZG9sQGRkcy5ubD4KKyAqIGFuZCBhZHQ3NDEwLmMgZnJvbSBpaW8tc3Rh
Z2luZyBieSBTb25pYyBaaGFuZyA8c29uaWMuemhhbmdAYW5hbG9nLmNvbT4KKyAqCisgKiBUaGlz
IHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29y
IG1vZGlmeQorICogaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMg
TGljZW5zZSBhcyBwdWJsaXNoZWQgYnkKKyAqIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247
IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yCisgKiAoYXQgeW91ciBvcHRpb24p
IGFueSBsYXRlciB2ZXJzaW9uLgorICoKKyAqIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBp
biB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAorICogYnV0IFdJVEhPVVQgQU5ZIFdB
UlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKKyAqIE1FUkNIQU5U
QUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKKyAq
IEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCisgKgorICogWW91
IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExp
Y2Vuc2UKKyAqIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBG
cmVlIFNvZnR3YXJlCisgKiBGb3VuZGF0aW9uLCBJbmMuLCA2NzUgTWFzcyBBdmUsIENhbWJyaWRn
ZSwgTUEgMDIxMzksIFVTQS4KKyAqLworCisjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+CisjaW5j
bHVkZSA8bGludXgvaW5pdC5oPgorI2luY2x1ZGUgPGxpbnV4L3NsYWIuaD4KKyNpbmNsdWRlIDxs
aW51eC9qaWZmaWVzLmg+CisjaW5jbHVkZSA8bGludXgvaHdtb24uaD4KKyNpbmNsdWRlIDxsaW51
eC9od21vbi1zeXNmcy5oPgorI2luY2x1ZGUgPGxpbnV4L2Vyci5oPgorI2luY2x1ZGUgPGxpbnV4
L211dGV4Lmg+CisjaW5jbHVkZSA8bGludXgvZGVsYXkuaD4KKworI2luY2x1ZGUgImFkdDd4MTAu
aCIKKworLyoKKyAqIEFEVDc0MTAgc3RhdHVzCisgKi8KKyNkZWZpbmUgQURUNzQxMF9TVEFUX1Rf
TE9XCQkoMSA8PCA0KQorI2RlZmluZSBBRFQ3NDEwX1NUQVRfVF9ISUdICQkoMSA8PCA1KQorI2Rl
ZmluZSBBRFQ3NDEwX1NUQVRfVF9DUklUCQkoMSA8PCA2KQorI2RlZmluZSBBRFQ3NDEwX1NUQVRf
Tk9UX1JEWQkJKDEgPDwgNykKKworLyoKKyAqIEFEVDc0MTAgY29uZmlnCisgKi8KKyNkZWZpbmUg
QURUNzQxMF9GQVVMVF9RVUVVRV9NQVNLCSgxIDw8IDAgfCAxIDw8IDEpCisjZGVmaW5lIEFEVDc0
MTBfQ1RfUE9MQVJJVFkJCSgxIDw8IDIpCisjZGVmaW5lIEFEVDc0MTBfSU5UX1BPTEFSSVRZCQko
MSA8PCAzKQorI2RlZmluZSBBRFQ3NDEwX0VWRU5UX01PREUJCSgxIDw8IDQpCisjZGVmaW5lIEFE
VDc0MTBfTU9ERV9NQVNLCQkoMSA8PCA1IHwgMSA8PCA2KQorI2RlZmluZSBBRFQ3NDEwX0ZVTEwJ
CQkoMCA8PCA1IHwgMCA8PCA2KQorI2RlZmluZSBBRFQ3NDEwX1BECQkJKDEgPDwgNSB8IDEgPDwg
NikKKyNkZWZpbmUgQURUNzQxMF9SRVNPTFVUSU9OCQkoMSA8PCA3KQorCisvKgorICogQURUNzQx
MCBtYXNrcworICovCisjZGVmaW5lIEFEVDc0MTBfVDEzX1ZBTFVFX01BU0sJCQkweEZGRjgKKyNk
ZWZpbmUgQURUNzQxMF9UX0hZU1RfTUFTSwkJCTB4RgorCisvKiBzdHJhaWdodCBmcm9tIHRoZSBk
YXRhc2hlZXQgKi8KKyNkZWZpbmUgQURUNzQxMF9URU1QX01JTiAoLTU1MDAwKQorI2RlZmluZSBB
RFQ3NDEwX1RFTVBfTUFYIDE1MDAwMAorCisvKiBFYWNoIGNsaWVudCBoYXMgdGhpcyBhZGRpdGlv
bmFsIGRhdGEgKi8KK3N0cnVjdCBhZHQ3NDEwX2RhdGEgeworCWNvbnN0IHN0cnVjdCBhZHQ3NDEw
X29wcyAqb3BzOworCWNvbnN0IGNoYXIJCSpuYW1lOworCXN0cnVjdCBkZXZpY2UJCSpod21vbl9k
ZXY7CisJc3RydWN0IG11dGV4CQl1cGRhdGVfbG9jazsKKwl1OAkJCWNvbmZpZzsKKwl1OAkJCW9s
ZGNvbmZpZzsKKwlib29sCQkJdmFsaWQ7CQkvKiB0cnVlIGlmIHJlZ2lzdGVycyB2YWxpZCAqLwor
CXVuc2lnbmVkIGxvbmcJCWxhc3RfdXBkYXRlZDsJLyogSW4gamlmZmllcyAqLworCXMxNgkJCXRl
bXBbNF07CS8qIFJlZ2lzdGVyIHZhbHVlcywKKwkJCQkJCSAgIDAgPSBpbnB1dAorCQkJCQkJICAg
MSA9IGhpZ2gKKwkJCQkJCSAgIDIgPSBsb3cKKwkJCQkJCSAgIDMgPSBjcml0aWNhbCAqLworCXU4
CQkJaHlzdDsJCS8qIGh5c3RlcmVzaXMgb2Zmc2V0ICovCit9OworCitzdGF0aWMgaW50IGFkdDc0
MTBfcmVhZF9ieXRlKHN0cnVjdCBkZXZpY2UgKmRldiwgdTggcmVnKQoreworCXN0cnVjdCBhZHQ3
NDEwX2RhdGEgKmQgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlyZXR1cm4gZC0+b3BzLT5yZWFk
X2J5dGUoZGV2LCByZWcpOworfQorCitzdGF0aWMgaW50IGFkdDc0MTBfd3JpdGVfYnl0ZShzdHJ1
Y3QgZGV2aWNlICpkZXYsIHU4IHJlZywgdTggZGF0YSkKK3sKKwlzdHJ1Y3QgYWR0NzQxMF9kYXRh
ICpkID0gZGV2X2dldF9kcnZkYXRhKGRldik7CisJcmV0dXJuIGQtPm9wcy0+d3JpdGVfYnl0ZShk
ZXYsIHJlZywgZGF0YSk7Cit9CisKK3N0YXRpYyBpbnQgYWR0NzQxMF9yZWFkX3dvcmQoc3RydWN0
IGRldmljZSAqZGV2LCB1OCByZWcpCit7CisJc3RydWN0IGFkdDc0MTBfZGF0YSAqZCA9IGRldl9n
ZXRfZHJ2ZGF0YShkZXYpOworCXJldHVybiBkLT5vcHMtPnJlYWRfd29yZChkZXYsIHJlZyk7Cit9
CisKK3N0YXRpYyBpbnQgYWR0NzQxMF93cml0ZV93b3JkKHN0cnVjdCBkZXZpY2UgKmRldiwgdTgg
cmVnLCB1MTYgZGF0YSkKK3sKKwlzdHJ1Y3QgYWR0NzQxMF9kYXRhICpkID0gZGV2X2dldF9kcnZk
YXRhKGRldik7CisJcmV0dXJuIGQtPm9wcy0+d3JpdGVfd29yZChkZXYsIHJlZywgZGF0YSk7Cit9
CisKK3N0YXRpYyBjb25zdCB1OCBBRFQ3NDEwX1JFR19URU1QWzRdID0geworCUFEVDc0MTBfVEVN
UEVSQVRVUkUsCQkvKiBpbnB1dCAqLworCUFEVDc0MTBfVF9BTEFSTV9ISUdILAkJLyogaGlnaCAq
LworCUFEVDc0MTBfVF9BTEFSTV9MT1csCQkvKiBsb3cgKi8KKwlBRFQ3NDEwX1RfQ1JJVCwJCQkv
KiBjcml0aWNhbCAqLworfTsKKworc3RhdGljIGludCBhZHQ3NDEwX3RlbXBfcmVhZHkoc3RydWN0
IGRldmljZSAqZGV2KQoreworCWludCBpLCBzdGF0dXM7CisKKwlmb3IgKGkgPSAwOyBpIDwgNjsg
aSsrKSB7CisJCXN0YXR1cyA9IGFkdDc0MTBfcmVhZF9ieXRlKGRldiwgQURUNzQxMF9TVEFUVVMp
OworCQlpZiAoc3RhdHVzIDwgMCkKKwkJCXJldHVybiBzdGF0dXM7CisJCWlmICghKHN0YXR1cyAm
IEFEVDc0MTBfU1RBVF9OT1RfUkRZKSkKKwkJCXJldHVybiAwOworCQltc2xlZXAoNjApOworCX0K
KwlyZXR1cm4gLUVUSU1FRE9VVDsKK30KKworc3RhdGljIGludCBhZHQ3NDEwX3VwZGF0ZV90ZW1w
KHN0cnVjdCBkZXZpY2UgKmRldikKK3sKKwlzdHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhID0gZGV2
X2dldF9kcnZkYXRhKGRldik7CisJaW50IHJldCA9IDA7CisKKwltdXRleF9sb2NrKCZkYXRhLT51
cGRhdGVfbG9jayk7CisKKwlpZiAodGltZV9hZnRlcihqaWZmaWVzLCBkYXRhLT5sYXN0X3VwZGF0
ZWQgKyBIWiArIEhaIC8gMikKKwkgICAgfHwgIWRhdGEtPnZhbGlkKSB7CisJCWludCB0ZW1wOwor
CisJCWRldl9kYmcoZGV2LCAiU3RhcnRpbmcgdXBkYXRlXG4iKTsKKworCQlyZXQgPSBhZHQ3NDEw
X3RlbXBfcmVhZHkoZGV2KTsgLyogY2hlY2sgZm9yIG5ldyB2YWx1ZSAqLworCQlpZiAocmV0KQor
CQkJZ290byBhYm9ydDsKKworCQl0ZW1wID0gYWR0NzQxMF9yZWFkX3dvcmQoZGV2LCBBRFQ3NDEw
X1JFR19URU1QWzBdKTsKKwkJaWYgKHRlbXAgPCAwKSB7CisJCQlyZXQgPSB0ZW1wOworCQkJZGV2
X2RiZyhkZXYsICJGYWlsZWQgdG8gcmVhZCB2YWx1ZTogcmVnICVkLCBlcnJvciAlZFxuIiwKKwkJ
CQlBRFQ3NDEwX1JFR19URU1QWzBdLCByZXQpOworCQkJZ290byBhYm9ydDsKKwkJfQorCQlkYXRh
LT50ZW1wWzBdID0gdGVtcDsKKwkJZGF0YS0+bGFzdF91cGRhdGVkID0gamlmZmllczsKKwkJZGF0
YS0+dmFsaWQgPSB0cnVlOworCX0KKworYWJvcnQ6CisJbXV0ZXhfdW5sb2NrKCZkYXRhLT51cGRh
dGVfbG9jayk7CisJcmV0dXJuIHJldDsKK30KKworc3RhdGljIGludCBhZHQ3NDEwX2ZpbGxfY2Fj
aGUoc3RydWN0IGRldmljZSAqZGV2KQoreworCXN0cnVjdCBhZHQ3NDEwX2RhdGEgKmRhdGEgPSBk
ZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlpbnQgcmV0OworCWludCBpOworCisJZm9yIChpID0gMTsg
aSA8IEFSUkFZX1NJWkUoZGF0YS0+dGVtcCk7IGkrKykgeworCQlyZXQgPSBhZHQ3NDEwX3JlYWRf
d29yZChkZXYsIEFEVDc0MTBfUkVHX1RFTVBbaV0pOworCQlpZiAocmV0IDwgMCkgeworCQkJZGV2
X2RiZyhkZXYsICJGYWlsZWQgdG8gcmVhZCB2YWx1ZTogcmVnICVkLCBlcnJvciAlZFxuIiwKKwkJ
CQlBRFQ3NDEwX1JFR19URU1QWzBdLCByZXQpOworCQkJcmV0dXJuIHJldDsKKwkJfQorCQlkYXRh
LT50ZW1wW2ldID0gcmV0OworCX0KKworCXJldCA9IGFkdDc0MTBfcmVhZF9ieXRlKGRldiwgQURU
NzQxMF9UX0hZU1QpOworCWlmIChyZXQgPCAwKSB7CisJCWRldl9kYmcoZGV2LCAiRmFpbGVkIHRv
IHJlYWQgdmFsdWU6IHJlZyAlZCwgZXJyb3IgJWRcbiIsCisJCQkJQURUNzQxMF9UX0hZU1QsIHJl
dCk7CisJCXJldHVybiByZXQ7CisJfQorCWRhdGEtPmh5c3QgPSByZXQ7CisKKwlyZXR1cm4gMDsK
K30KKworc3RhdGljIHMxNiBBRFQ3NDEwX1RFTVBfVE9fUkVHKGxvbmcgdGVtcCkKK3sKKwlyZXR1
cm4gRElWX1JPVU5EX0NMT1NFU1QoY2xhbXBfdmFsKHRlbXAsIEFEVDc0MTBfVEVNUF9NSU4sCisJ
CQkJCSAgICAgICBBRFQ3NDEwX1RFTVBfTUFYKSAqIDEyOCwgMTAwMCk7Cit9CisKK3N0YXRpYyBp
bnQgQURUNzQxMF9SRUdfVE9fVEVNUChzdHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhLCBzMTYgcmVn
KQoreworCS8qIGluIDEzIGJpdCBtb2RlLCBiaXRzIDAtMiBhcmUgc3RhdHVzIGZsYWdzIC0gbWFz
ayB0aGVtIG91dCAqLworCWlmICghKGRhdGEtPmNvbmZpZyAmIEFEVDc0MTBfUkVTT0xVVElPTikp
CisJCXJlZyAmPSBBRFQ3NDEwX1QxM19WQUxVRV9NQVNLOworCS8qCisJICogdGVtcGVyYXR1cmUg
aXMgc3RvcmVkIGluIHR3b3MgY29tcGxlbWVudCBmb3JtYXQsIGluIHN0ZXBzIG9mCisJICogMS8x
MjjCsEMKKwkgKi8KKwlyZXR1cm4gRElWX1JPVU5EX0NMT1NFU1QocmVnICogMTAwMCwgMTI4KTsK
K30KKworLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovCisKKy8qIHN5c2ZzIGF0dHJpYnV0ZXMgZm9yIGh3bW9u
ICovCisKK3N0YXRpYyBzc2l6ZV90IGFkdDc0MTBfc2hvd190ZW1wKHN0cnVjdCBkZXZpY2UgKmRl
diwKKwkJCQkgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmRhLCBjaGFyICpidWYpCit7CisJc3Ry
dWN0IHNlbnNvcl9kZXZpY2VfYXR0cmlidXRlICphdHRyID0gdG9fc2Vuc29yX2Rldl9hdHRyKGRh
KTsKKwlzdHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKGRldik7CisK
KworCWlmIChhdHRyLT5pbmRleCA9PSAwKSB7CisJCWludCByZXQ7CisKKwkJcmV0ID0gYWR0NzQx
MF91cGRhdGVfdGVtcChkZXYpOworCQlpZiAocmV0KQorCQkJcmV0dXJuIHJldDsKKwl9CisKKwly
ZXR1cm4gc3ByaW50ZihidWYsICIlZFxuIiwgQURUNzQxMF9SRUdfVE9fVEVNUChkYXRhLAorCQkg
ICAgICAgZGF0YS0+dGVtcFthdHRyLT5pbmRleF0pKTsKK30KKworc3RhdGljIHNzaXplX3QgYWR0
NzQxMF9zZXRfdGVtcChzdHJ1Y3QgZGV2aWNlICpkZXYsCisJCQkJc3RydWN0IGRldmljZV9hdHRy
aWJ1dGUgKmRhLAorCQkJCWNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNvdW50KQoreworCXN0cnVj
dCBzZW5zb3JfZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciA9IHRvX3NlbnNvcl9kZXZfYXR0cihkYSk7
CisJc3RydWN0IGFkdDc0MTBfZGF0YSAqZGF0YSA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCWlu
dCBuciA9IGF0dHItPmluZGV4OworCWxvbmcgdGVtcDsKKwlpbnQgcmV0OworCisJcmV0ID0ga3N0
cnRvbChidWYsIDEwLCAmdGVtcCk7CisJaWYgKHJldCkKKwkJcmV0dXJuIHJldDsKKworCW11dGV4
X2xvY2soJmRhdGEtPnVwZGF0ZV9sb2NrKTsKKwlkYXRhLT50ZW1wW25yXSA9IEFEVDc0MTBfVEVN
UF9UT19SRUcodGVtcCk7CisJcmV0ID0gYWR0NzQxMF93cml0ZV93b3JkKGRldiwgQURUNzQxMF9S
RUdfVEVNUFtucl0sIGRhdGEtPnRlbXBbbnJdKTsKKwlpZiAocmV0KQorCQljb3VudCA9IHJldDsK
KwltdXRleF91bmxvY2soJmRhdGEtPnVwZGF0ZV9sb2NrKTsKKwlyZXR1cm4gY291bnQ7Cit9CisK
K3N0YXRpYyBzc2l6ZV90IGFkdDc0MTBfc2hvd190X2h5c3Qoc3RydWN0IGRldmljZSAqZGV2LAor
CQkJCSAgIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICpkYSwKKwkJCQkgICBjaGFyICpidWYpCit7
CisJc3RydWN0IHNlbnNvcl9kZXZpY2VfYXR0cmlidXRlICphdHRyID0gdG9fc2Vuc29yX2Rldl9h
dHRyKGRhKTsKKwlzdHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKGRl
dik7CisJaW50IG5yID0gYXR0ci0+aW5kZXg7CisJaW50IGh5c3Q7CisKKwloeXN0ID0gKGRhdGEt
Pmh5c3QgJiBBRFQ3NDEwX1RfSFlTVF9NQVNLKSAqIDEwMDA7CisKKwkvKgorCSAqIGh5c3RlcmVz
aXMgaXMgc3RvcmVkIGFzIGEgNCBiaXQgb2Zmc2V0IGluIHRoZSBkZXZpY2UsIGNvbnZlcnQgaXQK
KwkgKiB0byBhbiBhYnNvbHV0ZSB2YWx1ZQorCSAqLworCWlmIChuciA9PSAyKQkvKiBtaW4gaGFz
IHBvc2l0aXZlIG9mZnNldCwgb3RoZXJzIGhhdmUgbmVnYXRpdmUgKi8KKwkJaHlzdCA9IC1oeXN0
OworCXJldHVybiBzcHJpbnRmKGJ1ZiwgIiVkXG4iLAorCQkgICAgICAgQURUNzQxMF9SRUdfVE9f
VEVNUChkYXRhLCBkYXRhLT50ZW1wW25yXSkgLSBoeXN0KTsKK30KKworc3RhdGljIHNzaXplX3Qg
YWR0NzQxMF9zZXRfdF9oeXN0KHN0cnVjdCBkZXZpY2UgKmRldiwKKwkJCQkgIHN0cnVjdCBkZXZp
Y2VfYXR0cmlidXRlICpkYSwKKwkJCQkgIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNvdW50KQor
eworCXN0cnVjdCBhZHQ3NDEwX2RhdGEgKmRhdGEgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlp
bnQgbGltaXQsIHJldDsKKwlsb25nIGh5c3Q7CisKKwlyZXQgPSBrc3RydG9sKGJ1ZiwgMTAsICZo
eXN0KTsKKwlpZiAocmV0KQorCQlyZXR1cm4gcmV0OworCS8qIGNvbnZlcnQgYWJzb2x1dGUgaHlz
dGVyZXNpcyB2YWx1ZSB0byBhIDQgYml0IGRlbHRhIHZhbHVlICovCisJbGltaXQgPSBBRFQ3NDEw
X1JFR19UT19URU1QKGRhdGEsIGRhdGEtPnRlbXBbMV0pOworCWh5c3QgPSBjbGFtcF92YWwoaHlz
dCwgQURUNzQxMF9URU1QX01JTiwgQURUNzQxMF9URU1QX01BWCk7CisJZGF0YS0+aHlzdCA9IGNs
YW1wX3ZhbChESVZfUk9VTkRfQ0xPU0VTVChsaW1pdCAtIGh5c3QsIDEwMDApLAorCQkJCSAgIDAs
IEFEVDc0MTBfVF9IWVNUX01BU0spOworCXJldCA9IGFkdDc0MTBfd3JpdGVfYnl0ZShkZXYsIEFE
VDc0MTBfVF9IWVNULCBkYXRhLT5oeXN0KTsKKwlpZiAocmV0KQorCQlyZXR1cm4gcmV0OworCisJ
cmV0dXJuIGNvdW50OworfQorCitzdGF0aWMgc3NpemVfdCBhZHQ3NDEwX3Nob3dfYWxhcm0oc3Ry
dWN0IGRldmljZSAqZGV2LAorCQkJCSAgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmRhLAorCQkJ
CSAgY2hhciAqYnVmKQoreworCXN0cnVjdCBzZW5zb3JfZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciA9
IHRvX3NlbnNvcl9kZXZfYXR0cihkYSk7CisJaW50IHJldDsKKworCXJldCA9IGFkdDc0MTBfcmVh
ZF9ieXRlKGRldiwgQURUNzQxMF9TVEFUVVMpOworCWlmIChyZXQgPCAwKQorCQlyZXR1cm4gcmV0
OworCisJcmV0dXJuIHNwcmludGYoYnVmLCAiJWRcbiIsICEhKHJldCAmIGF0dHItPmluZGV4KSk7
Cit9CisKK3N0YXRpYyBzc2l6ZV90IGFkdDc0MTBfc2hvd19uYW1lKHN0cnVjdCBkZXZpY2UgKmRl
diwKKwkJCQkgIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICpkYSwgY2hhciAqYnVmKQoreworCXN0
cnVjdCBhZHQ3NDEwX2RhdGEgKmRhdGEgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKworCXJldHVy
biBzcHJpbnRmKGJ1ZiwgIiVzXG4iLCBkYXRhLT5uYW1lKTsKK30KKworc3RhdGljIFNFTlNPUl9E
RVZJQ0VfQVRUUih0ZW1wMV9pbnB1dCwgU19JUlVHTywgYWR0NzQxMF9zaG93X3RlbXAsIE5VTEws
IDApOworc3RhdGljIFNFTlNPUl9ERVZJQ0VfQVRUUih0ZW1wMV9tYXgsIFNfSVdVU1IgfCBTX0lS
VUdPLAorCQkJICBhZHQ3NDEwX3Nob3dfdGVtcCwgYWR0NzQxMF9zZXRfdGVtcCwgMSk7CitzdGF0
aWMgU0VOU09SX0RFVklDRV9BVFRSKHRlbXAxX21pbiwgU19JV1VTUiB8IFNfSVJVR08sCisJCQkg
IGFkdDc0MTBfc2hvd190ZW1wLCBhZHQ3NDEwX3NldF90ZW1wLCAyKTsKK3N0YXRpYyBTRU5TT1Jf
REVWSUNFX0FUVFIodGVtcDFfY3JpdCwgU19JV1VTUiB8IFNfSVJVR08sCisJCQkgIGFkdDc0MTBf
c2hvd190ZW1wLCBhZHQ3NDEwX3NldF90ZW1wLCAzKTsKK3N0YXRpYyBTRU5TT1JfREVWSUNFX0FU
VFIodGVtcDFfbWF4X2h5c3QsIFNfSVdVU1IgfCBTX0lSVUdPLAorCQkJICBhZHQ3NDEwX3Nob3df
dF9oeXN0LCBhZHQ3NDEwX3NldF90X2h5c3QsIDEpOworc3RhdGljIFNFTlNPUl9ERVZJQ0VfQVRU
Uih0ZW1wMV9taW5faHlzdCwgU19JUlVHTywKKwkJCSAgYWR0NzQxMF9zaG93X3RfaHlzdCwgTlVM
TCwgMik7CitzdGF0aWMgU0VOU09SX0RFVklDRV9BVFRSKHRlbXAxX2NyaXRfaHlzdCwgU19JUlVH
TywKKwkJCSAgYWR0NzQxMF9zaG93X3RfaHlzdCwgTlVMTCwgMyk7CitzdGF0aWMgU0VOU09SX0RF
VklDRV9BVFRSKHRlbXAxX21pbl9hbGFybSwgU19JUlVHTywgYWR0NzQxMF9zaG93X2FsYXJtLAor
CQkJICBOVUxMLCBBRFQ3NDEwX1NUQVRfVF9MT1cpOworc3RhdGljIFNFTlNPUl9ERVZJQ0VfQVRU
Uih0ZW1wMV9tYXhfYWxhcm0sIFNfSVJVR08sIGFkdDc0MTBfc2hvd19hbGFybSwKKwkJCSAgTlVM
TCwgQURUNzQxMF9TVEFUX1RfSElHSCk7CitzdGF0aWMgU0VOU09SX0RFVklDRV9BVFRSKHRlbXAx
X2NyaXRfYWxhcm0sIFNfSVJVR08sIGFkdDc0MTBfc2hvd19hbGFybSwKKwkJCSAgTlVMTCwgQURU
NzQxMF9TVEFUX1RfQ1JJVCk7CitzdGF0aWMgREVWSUNFX0FUVFIobmFtZSwgU19JUlVHTywgYWR0
NzQxMF9zaG93X25hbWUsIE5VTEwpOworCitzdGF0aWMgc3RydWN0IGF0dHJpYnV0ZSAqYWR0NzQx
MF9hdHRyaWJ1dGVzW10gPSB7CisJJnNlbnNvcl9kZXZfYXR0cl90ZW1wMV9pbnB1dC5kZXZfYXR0
ci5hdHRyLAorCSZzZW5zb3JfZGV2X2F0dHJfdGVtcDFfbWF4LmRldl9hdHRyLmF0dHIsCisJJnNl
bnNvcl9kZXZfYXR0cl90ZW1wMV9taW4uZGV2X2F0dHIuYXR0ciwKKwkmc2Vuc29yX2Rldl9hdHRy
X3RlbXAxX2NyaXQuZGV2X2F0dHIuYXR0ciwKKwkmc2Vuc29yX2Rldl9hdHRyX3RlbXAxX21heF9o
eXN0LmRldl9hdHRyLmF0dHIsCisJJnNlbnNvcl9kZXZfYXR0cl90ZW1wMV9taW5faHlzdC5kZXZf
YXR0ci5hdHRyLAorCSZzZW5zb3JfZGV2X2F0dHJfdGVtcDFfY3JpdF9oeXN0LmRldl9hdHRyLmF0
dHIsCisJJnNlbnNvcl9kZXZfYXR0cl90ZW1wMV9taW5fYWxhcm0uZGV2X2F0dHIuYXR0ciwKKwkm
c2Vuc29yX2Rldl9hdHRyX3RlbXAxX21heF9hbGFybS5kZXZfYXR0ci5hdHRyLAorCSZzZW5zb3Jf
ZGV2X2F0dHJfdGVtcDFfY3JpdF9hbGFybS5kZXZfYXR0ci5hdHRyLAorCU5VTEwKK307CisKK3N0
YXRpYyBjb25zdCBzdHJ1Y3QgYXR0cmlidXRlX2dyb3VwIGFkdDc0MTBfZ3JvdXAgPSB7CisJLmF0
dHJzID0gYWR0NzQxMF9hdHRyaWJ1dGVzLAorfTsKKworaW50IGFkdDc0MTBfcHJvYmUoc3RydWN0
IGRldmljZSAqZGV2LCBjb25zdCBjaGFyICpuYW1lLAorCWNvbnN0IHN0cnVjdCBhZHQ3NDEwX29w
cyAqb3BzKQoreworCXN0cnVjdCBhZHQ3NDEwX2RhdGEgKmRhdGE7CisJaW50IHJldDsKKworCWRh
dGEgPSBkZXZtX2t6YWxsb2MoZGV2LCBzaXplb2Yoc3RydWN0IGFkdDc0MTBfZGF0YSksCisJCQkg
ICAgR0ZQX0tFUk5FTCk7CisJaWYgKCFkYXRhKQorCQlyZXR1cm4gLUVOT01FTTsKKworCWRhdGEt
Pm9wcyA9IG9wczsKKwlkYXRhLT5uYW1lID0gbmFtZTsKKworCWRldl9zZXRfZHJ2ZGF0YShkZXYs
IGRhdGEpOworCW11dGV4X2luaXQoJmRhdGEtPnVwZGF0ZV9sb2NrKTsKKworCS8qIGNvbmZpZ3Vy
ZSBhcyBzcGVjaWZpZWQgKi8KKwlyZXQgPSBhZHQ3NDEwX3JlYWRfYnl0ZShkZXYsIEFEVDc0MTBf
Q09ORklHKTsKKwlpZiAocmV0IDwgMCkgeworCQlkZXZfZGJnKGRldiwgIkNhbid0IHJlYWQgY29u
ZmlnPyAlZFxuIiwgcmV0KTsKKwkJcmV0dXJuIHJldDsKKwl9CisJZGF0YS0+b2xkY29uZmlnID0g
cmV0OworCisJLyoKKwkgKiBTZXQgdG8gMTYgYml0IHJlc29sdXRpb24sIGNvbnRpbm91cyBjb252
ZXJzaW9uIGFuZCBjb21wYXJhdG9yIG1vZGUuCisJICovCisJZGF0YS0+Y29uZmlnID0gZGF0YS0+
b2xkY29uZmlnOworCWRhdGEtPmNvbmZpZyAmPSB+QURUNzQxMF9NT0RFX01BU0s7CisJZGF0YS0+
Y29uZmlnIHw9IEFEVDc0MTBfRlVMTCB8IEFEVDc0MTBfUkVTT0xVVElPTiB8IEFEVDc0MTBfRVZF
TlRfTU9ERTsKKwlpZiAoZGF0YS0+Y29uZmlnICE9IGRhdGEtPm9sZGNvbmZpZykgeworCQlyZXQg
PSBhZHQ3NDEwX3dyaXRlX2J5dGUoZGV2LCBBRFQ3NDEwX0NPTkZJRywgZGF0YS0+Y29uZmlnKTsK
KwkJaWYgKHJldCkKKwkJCXJldHVybiByZXQ7CisJfQorCWRldl9kYmcoZGV2LCAiQ29uZmlnICUw
MnhcbiIsIGRhdGEtPmNvbmZpZyk7CisKKwlyZXQgPSBhZHQ3NDEwX2ZpbGxfY2FjaGUoZGV2KTsK
KwlpZiAocmV0KQorCQlnb3RvIGV4aXRfcmVzdG9yZTsKKworCS8qIFJlZ2lzdGVyIHN5c2ZzIGhv
b2tzICovCisJcmV0ID0gc3lzZnNfY3JlYXRlX2dyb3VwKCZkZXYtPmtvYmosICZhZHQ3NDEwX2dy
b3VwKTsKKwlpZiAocmV0KQorCQlnb3RvIGV4aXRfcmVzdG9yZTsKKworCS8qCisJICogVGhlIEky
QyBkZXZpY2Ugd2lsbCBhbHJlYWR5IGhhdmUgaXQncyBvd24gJ25hbWUnIGF0dHJpYnV0ZSwgYnV0
IGZvcgorCSAqIHRoZSBTUEkgZGV2aWNlIHdlIG5lZWQgdG8gcmVnaXN0ZXIgaXQuIG5hbWUgd2ls
bCBvbmx5IGJlIG5vbiBOVUxMIGlmCisJICogdGhlIGRldmljZSBkb2Vzbid0IHJlZ2lzdGVyIHRo
ZSAnbmFtZScgYXR0cmlidXRlIG9uIGl0cyBvd24uCisJICovCisJaWYgKG5hbWUpIHsKKwkJcmV0
ID0gZGV2aWNlX2NyZWF0ZV9maWxlKGRldiwgJmRldl9hdHRyX25hbWUpOworCQlpZiAocmV0KQor
CQkJZ290byBleGl0X3JlbW92ZTsKKwl9CisKKwlkYXRhLT5od21vbl9kZXYgPSBod21vbl9kZXZp
Y2VfcmVnaXN0ZXIoZGV2KTsKKwlpZiAoSVNfRVJSKGRhdGEtPmh3bW9uX2RldikpIHsKKwkJcmV0
ID0gUFRSX0VSUihkYXRhLT5od21vbl9kZXYpOworCQlnb3RvIGV4aXRfcmVtb3ZlX25hbWU7CisJ
fQorCisJcmV0dXJuIDA7CisKK2V4aXRfcmVtb3ZlX25hbWU6CisJaWYgKG5hbWUpCisJCWRldmlj
ZV9yZW1vdmVfZmlsZShkZXYsICZkZXZfYXR0cl9uYW1lKTsKK2V4aXRfcmVtb3ZlOgorCXN5c2Zz
X3JlbW92ZV9ncm91cCgmZGV2LT5rb2JqLCAmYWR0NzQxMF9ncm91cCk7CitleGl0X3Jlc3RvcmU6
CisJYWR0NzQxMF93cml0ZV9ieXRlKGRldiwgQURUNzQxMF9DT05GSUcsIGRhdGEtPm9sZGNvbmZp
Zyk7CisJcmV0dXJuIHJldDsKK30KK0VYUE9SVF9TWU1CT0xfR1BMKGFkdDc0MTBfcHJvYmUpOwor
CitpbnQgYWR0NzQxMF9yZW1vdmUoc3RydWN0IGRldmljZSAqZGV2KQoreworCXN0cnVjdCBhZHQ3
NDEwX2RhdGEgKmRhdGEgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKworCWh3bW9uX2RldmljZV91
bnJlZ2lzdGVyKGRhdGEtPmh3bW9uX2Rldik7CisJaWYgKGRhdGEtPm5hbWUpCisJCWRldmljZV9y
ZW1vdmVfZmlsZShkZXYsICZkZXZfYXR0cl9uYW1lKTsKKwlzeXNmc19yZW1vdmVfZ3JvdXAoJmRl
di0+a29iaiwgJmFkdDc0MTBfZ3JvdXApOworCWlmIChkYXRhLT5vbGRjb25maWcgIT0gZGF0YS0+
Y29uZmlnKQorCQlhZHQ3NDEwX3dyaXRlX2J5dGUoZGV2LCBBRFQ3NDEwX0NPTkZJRywKKwkJCQkJ
ICBkYXRhLT5vbGRjb25maWcpOworCXJldHVybiAwOworfQorRVhQT1JUX1NZTUJPTF9HUEwoYWR0
NzQxMF9yZW1vdmUpOworCisjaWZkZWYgQ09ORklHX1BNX1NMRUVQCisKK3N0YXRpYyBpbnQgYWR0
NzQxMF9zdXNwZW5kKHN0cnVjdCBkZXZpY2UgKmRldikKK3sKKwlzdHJ1Y3QgYWR0NzQxMF9kYXRh
ICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKGRldik7CisKKwlyZXR1cm4gYWR0NzQxMF93cml0ZV9i
eXRlKGRldiwgQURUNzQxMF9DT05GSUcsCisJCWRhdGEtPmNvbmZpZyB8IEFEVDc0MTBfUEQpOwor
fQorCitzdGF0aWMgaW50IGFkdDc0MTBfcmVzdW1lKHN0cnVjdCBkZXZpY2UgKmRldikKK3sKKwlz
dHJ1Y3QgYWR0NzQxMF9kYXRhICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKGRldik7CisKKwlyZXR1
cm4gYWR0NzQxMF93cml0ZV9ieXRlKGRldiwgQURUNzQxMF9DT05GSUcsIGRhdGEtPmNvbmZpZyk7
Cit9CisKK1NJTVBMRV9ERVZfUE1fT1BTKGFkdDc0MTBfZGV2X3BtX29wcywgYWR0NzQxMF9zdXNw
ZW5kLCBhZHQ3NDEwX3Jlc3VtZSk7CitFWFBPUlRfU1lNQk9MX0dQTChhZHQ3NDEwX2Rldl9wbV9v
cHMpOworCisjZW5kaWYgLyogQ09ORklHX1BNX1NMRUVQICovCisKK01PRFVMRV9BVVRIT1IoIkhh
cnRtdXQgS25hYWNrIik7CitNT0RVTEVfREVTQ1JJUFRJT04oIkFEVDc0MTAvQURUNzQyMCwgQURU
NzMxMC9BRFQ3MzIwIGNvbW1vbiBjb2RlIik7CitNT0RVTEVfTElDRU5TRSgiR1BMIik7CmRpZmYg
LS1naXQgYS9kcml2ZXJzL2h3bW9uL2FkdDd4MTAuaCBiL2RyaXZlcnMvaHdtb24vYWR0N3gxMC5o
Cm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAuLmE3MTY1ZTYKLS0tIC9kZXYvbnVs
bAorKysgYi9kcml2ZXJzL2h3bW9uL2FkdDd4MTAuaApAQCAtMCwwICsxLDQ4IEBACisjaWZuZGVm
IF9fSFdNT05fQURUN1gxMF9IX18KKyNkZWZpbmUgX19IV01PTl9BRFQ3WDEwX0hfXworCisjaW5j
bHVkZSA8bGludXgvdHlwZXMuaD4KKyNpbmNsdWRlIDxsaW51eC9wbS5oPgorCisvKiBBRFQ3NDEw
IHJlZ2lzdGVycyBkZWZpbml0aW9uICovCisjZGVmaW5lIEFEVDc0MTBfVEVNUEVSQVRVUkUJCTAK
KyNkZWZpbmUgQURUNzQxMF9TVEFUVVMJCQkyCisjZGVmaW5lIEFEVDc0MTBfQ09ORklHCQkJMwor
I2RlZmluZSBBRFQ3NDEwX1RfQUxBUk1fSElHSAkJNAorI2RlZmluZSBBRFQ3NDEwX1RfQUxBUk1f
TE9XCQk2CisjZGVmaW5lIEFEVDc0MTBfVF9DUklUCQkJOAorI2RlZmluZSBBRFQ3NDEwX1RfSFlT
VAkJCTB4QQorI2RlZmluZSBBRFQ3NDEwX0lECQkJMHhCCisKKy8qIEFEVDczMTAgcmVnaXN0ZXJz
IGRlZmluaXRpb24gKi8KKyNkZWZpbmUgQURUNzMxMF9TVEFUVVMJCQkwCisjZGVmaW5lIEFEVDcz
MTBfQ09ORklHCQkJMQorI2RlZmluZSBBRFQ3MzEwX1RFTVBFUkFUVVJFCQkyCisjZGVmaW5lIEFE
VDczMTBfSUQJCQkzCisjZGVmaW5lIEFEVDczMTBfVF9DUklUCQkJNAorI2RlZmluZSBBRFQ3MzEw
X1RfSFlTVAkJCTUKKyNkZWZpbmUgQURUNzMxMF9UX0FMQVJNX0hJR0gJCTYKKyNkZWZpbmUgQURU
NzMxMF9UX0FMQVJNX0xPVwkJNworCitzdHJ1Y3QgZGV2aWNlOworCitzdHJ1Y3QgYWR0NzQxMF9v
cHMgeworCWludCAoKnJlYWRfYnl0ZSkoc3RydWN0IGRldmljZSAqLCB1OCByZWcpOworCWludCAo
KndyaXRlX2J5dGUpKHN0cnVjdCBkZXZpY2UgKiwgdTggcmVnLCB1OCBkYXRhKTsKKwlpbnQgKCpy
ZWFkX3dvcmQpKHN0cnVjdCBkZXZpY2UgKiwgdTggcmVnKTsKKwlpbnQgKCp3cml0ZV93b3JkKShz
dHJ1Y3QgZGV2aWNlICosIHU4IHJlZywgdTE2IGRhdGEpOworfTsKKworaW50IGFkdDc0MTBfcHJv
YmUoc3RydWN0IGRldmljZSAqZGV2LCBjb25zdCBjaGFyICpuYW1lLAorCWNvbnN0IHN0cnVjdCBh
ZHQ3NDEwX29wcyAqb3BzKTsKK2ludCBhZHQ3NDEwX3JlbW92ZShzdHJ1Y3QgZGV2aWNlICpkZXYp
OworCisKKyNpZmRlZiBDT05GSUdfUE1fU0xFRVAKK2V4dGVybiBjb25zdCBzdHJ1Y3QgZGV2X3Bt
X29wcyBhZHQ3NDEwX2Rldl9wbV9vcHM7CisjZGVmaW5lIEFEVDc0MTBfREVWX1BNX09QUyAoJmFk
dDc0MTBfZGV2X3BtX29wcykKKyNlbHNlCisjZGVmaW5lIEFEVDc0MTBfREVWX1BNX09QUyBOVUxM
CisjZW5kaWYKKworI2VuZGlmCi0tIAoxLjguMAoKCl9fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fCmxtLXNlbnNvcnMgbWFpbGluZyBsaXN0CmxtLXNlbnNvcnNA
bG0tc2Vuc29ycy5vcmcKaHR0cDovL2xpc3RzLmxtLXNlbnNvcnMub3JnL21haWxtYW4vbGlzdGlu
Zm8vbG0tc2Vuc29ycw=

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

* [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
  2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-18 13:38   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

This allows a userspace application to poll() on the alarm files to get notified
in case of an temperature threshold event.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

---
Changes since v1:
	* Switch from level triggered and interrupt mode to edge triggered and
	  comparator mode. In interrupt mode the status register get's cleared after
	  the first read and so the _alarm files will always read 0, which is not
	  really what we want.
	* Use dev_name(dev) for the interrupt name, instead of 'name' which will be
	  NULL for I2C devices
---
 drivers/hwmon/adt7310.c |  4 ++--
 drivers/hwmon/adt7410.c |  4 ++--
 drivers/hwmon/adt7x10.c | 43 +++++++++++++++++++++++++++++++++++++++----
 drivers/hwmon/adt7x10.h |  4 ++--
 4 files changed, 45 insertions(+), 10 deletions(-)

diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
index 2196ac3..e58bb68 100644
--- a/drivers/hwmon/adt7310.c
+++ b/drivers/hwmon/adt7310.c
@@ -82,13 +82,13 @@ static const struct adt7410_ops adt7310_spi_ops = {
 
 static int adt7310_spi_probe(struct spi_device *spi)
 {
-	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
+	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq,
 			&adt7310_spi_ops);
 }
 
 static int adt7310_spi_remove(struct spi_device *spi)
 {
-	return adt7410_remove(&spi->dev);
+	return adt7410_remove(&spi->dev, spi->irq);
 }
 
 static const struct spi_device_id adt7310_id[] = {
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index b500ab3..3a7d905 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -48,12 +48,12 @@ static int adt7410_i2c_probe(struct i2c_client *client,
 			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
 		return -ENODEV;
 
-	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
+	return adt7410_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops);
 }
 
 static int adt7410_i2c_remove(struct i2c_client *client)
 {
-	return adt7410_remove(&client->dev);
+	return adt7410_remove(&client->dev, client->irq);
 }
 
 static const struct i2c_device_id adt7410_ids[] = {
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
index eeff198c..2340a70 100644
--- a/drivers/hwmon/adt7x10.c
+++ b/drivers/hwmon/adt7x10.c
@@ -30,6 +30,7 @@
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 
 #include "adt7x10.h"
 
@@ -112,6 +113,25 @@ static const u8 ADT7410_REG_TEMP[4] = {
 	ADT7410_T_CRIT,			/* critical */
 };
 
+static irqreturn_t adt7410_irq_handler(int irq, void *private)
+{
+	struct device *dev = private;
+	int status;
+
+	status = adt7410_read_byte(dev, ADT7410_STATUS);
+	if (status < 0)
+		return IRQ_HANDLED;
+
+	if (status & ADT7410_STAT_T_HIGH)
+		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
+	if (status & ADT7410_STAT_T_LOW)
+		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
+	if (status & ADT7410_STAT_T_CRIT)
+		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
+
+	return IRQ_HANDLED;
+}
+
 static int adt7410_temp_ready(struct device *dev)
 {
 	int i, status;
@@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
 	.attrs = adt7410_attributes,
 };
 
-int adt7410_probe(struct device *dev, const char *name,
+int adt7410_probe(struct device *dev, const char *name, int irq,
 	const struct adt7410_ops *ops)
 {
 	struct adt7410_data *data;
@@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
 	data->oldconfig = ret;
 
 	/*
-	 * Set to 16 bit resolution, continous conversion and comparator mode.
+	 * Set to 16 bit resolution and continous conversion
 	 */
 	data->config = data->oldconfig;
-	data->config &= ~ADT7410_MODE_MASK;
+	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
+			ADT7410_INT_POLARITY);
 	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
+
 	if (data->config != data->oldconfig) {
 		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
 		if (ret)
@@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
 		goto exit_remove_name;
 	}
 
+	if (irq > 0) {
+		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
+				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				dev_name(dev), dev);
+		if (ret)
+			goto exit_hwmon_device_unregister;
+	}
+
 	return 0;
 
+exit_hwmon_device_unregister:
+	hwmon_device_unregister(data->hwmon_dev);
 exit_remove_name:
 	if (name)
 		device_remove_file(dev, &dev_attr_name);
@@ -434,10 +466,13 @@ exit_restore:
 }
 EXPORT_SYMBOL_GPL(adt7410_probe);
 
-int adt7410_remove(struct device *dev)
+int adt7410_remove(struct device *dev, int irq)
 {
 	struct adt7410_data *data = dev_get_drvdata(dev);
 
+	if (irq > 0)
+		free_irq(irq, dev);
+
 	hwmon_device_unregister(data->hwmon_dev);
 	if (data->name)
 		device_remove_file(dev, &dev_attr_name);
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
index a7165e6..d67badf 100644
--- a/drivers/hwmon/adt7x10.h
+++ b/drivers/hwmon/adt7x10.h
@@ -33,9 +33,9 @@ struct adt7410_ops {
 	int (*write_word)(struct device *, u8 reg, u16 data);
 };
 
-int adt7410_probe(struct device *dev, const char *name,
+int adt7410_probe(struct device *dev, const char *name, int irq,
 	const struct adt7410_ops *ops);
-int adt7410_remove(struct device *dev);
+int adt7410_remove(struct device *dev, int irq);
 
 
 #ifdef CONFIG_PM_SLEEP
-- 
1.8.0


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

* [lm-sensors] [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
@ 2013-02-18 13:38   ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

This allows a userspace application to poll() on the alarm files to get notified
in case of an temperature threshold event.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

---
Changes since v1:
	* Switch from level triggered and interrupt mode to edge triggered and
	  comparator mode. In interrupt mode the status register get's cleared after
	  the first read and so the _alarm files will always read 0, which is not
	  really what we want.
	* Use dev_name(dev) for the interrupt name, instead of 'name' which will be
	  NULL for I2C devices
---
 drivers/hwmon/adt7310.c |  4 ++--
 drivers/hwmon/adt7410.c |  4 ++--
 drivers/hwmon/adt7x10.c | 43 +++++++++++++++++++++++++++++++++++++++----
 drivers/hwmon/adt7x10.h |  4 ++--
 4 files changed, 45 insertions(+), 10 deletions(-)

diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
index 2196ac3..e58bb68 100644
--- a/drivers/hwmon/adt7310.c
+++ b/drivers/hwmon/adt7310.c
@@ -82,13 +82,13 @@ static const struct adt7410_ops adt7310_spi_ops = {
 
 static int adt7310_spi_probe(struct spi_device *spi)
 {
-	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
+	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq,
 			&adt7310_spi_ops);
 }
 
 static int adt7310_spi_remove(struct spi_device *spi)
 {
-	return adt7410_remove(&spi->dev);
+	return adt7410_remove(&spi->dev, spi->irq);
 }
 
 static const struct spi_device_id adt7310_id[] = {
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index b500ab3..3a7d905 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -48,12 +48,12 @@ static int adt7410_i2c_probe(struct i2c_client *client,
 			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
 		return -ENODEV;
 
-	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
+	return adt7410_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops);
 }
 
 static int adt7410_i2c_remove(struct i2c_client *client)
 {
-	return adt7410_remove(&client->dev);
+	return adt7410_remove(&client->dev, client->irq);
 }
 
 static const struct i2c_device_id adt7410_ids[] = {
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
index eeff198c..2340a70 100644
--- a/drivers/hwmon/adt7x10.c
+++ b/drivers/hwmon/adt7x10.c
@@ -30,6 +30,7 @@
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 
 #include "adt7x10.h"
 
@@ -112,6 +113,25 @@ static const u8 ADT7410_REG_TEMP[4] = {
 	ADT7410_T_CRIT,			/* critical */
 };
 
+static irqreturn_t adt7410_irq_handler(int irq, void *private)
+{
+	struct device *dev = private;
+	int status;
+
+	status = adt7410_read_byte(dev, ADT7410_STATUS);
+	if (status < 0)
+		return IRQ_HANDLED;
+
+	if (status & ADT7410_STAT_T_HIGH)
+		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
+	if (status & ADT7410_STAT_T_LOW)
+		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
+	if (status & ADT7410_STAT_T_CRIT)
+		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
+
+	return IRQ_HANDLED;
+}
+
 static int adt7410_temp_ready(struct device *dev)
 {
 	int i, status;
@@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
 	.attrs = adt7410_attributes,
 };
 
-int adt7410_probe(struct device *dev, const char *name,
+int adt7410_probe(struct device *dev, const char *name, int irq,
 	const struct adt7410_ops *ops)
 {
 	struct adt7410_data *data;
@@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
 	data->oldconfig = ret;
 
 	/*
-	 * Set to 16 bit resolution, continous conversion and comparator mode.
+	 * Set to 16 bit resolution and continous conversion
 	 */
 	data->config = data->oldconfig;
-	data->config &= ~ADT7410_MODE_MASK;
+	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
+			ADT7410_INT_POLARITY);
 	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
+
 	if (data->config != data->oldconfig) {
 		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
 		if (ret)
@@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
 		goto exit_remove_name;
 	}
 
+	if (irq > 0) {
+		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
+				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				dev_name(dev), dev);
+		if (ret)
+			goto exit_hwmon_device_unregister;
+	}
+
 	return 0;
 
+exit_hwmon_device_unregister:
+	hwmon_device_unregister(data->hwmon_dev);
 exit_remove_name:
 	if (name)
 		device_remove_file(dev, &dev_attr_name);
@@ -434,10 +466,13 @@ exit_restore:
 }
 EXPORT_SYMBOL_GPL(adt7410_probe);
 
-int adt7410_remove(struct device *dev)
+int adt7410_remove(struct device *dev, int irq)
 {
 	struct adt7410_data *data = dev_get_drvdata(dev);
 
+	if (irq > 0)
+		free_irq(irq, dev);
+
 	hwmon_device_unregister(data->hwmon_dev);
 	if (data->name)
 		device_remove_file(dev, &dev_attr_name);
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
index a7165e6..d67badf 100644
--- a/drivers/hwmon/adt7x10.h
+++ b/drivers/hwmon/adt7x10.h
@@ -33,9 +33,9 @@ struct adt7410_ops {
 	int (*write_word)(struct device *, u8 reg, u16 data);
 };
 
-int adt7410_probe(struct device *dev, const char *name,
+int adt7410_probe(struct device *dev, const char *name, int irq,
 	const struct adt7410_ops *ops);
-int adt7410_remove(struct device *dev);
+int adt7410_remove(struct device *dev, int irq);
 
 
 #ifdef CONFIG_PM_SLEEP
-- 
1.8.0


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* [PATCH v2 4/4] staging:iio: Remove adt7410 driver
  2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-18 13:38   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

The adt7410 hwmon driver is feature wise more or less on par with the IIO
driver. So we can finally remove the IIO driver.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/staging/iio/adc/Kconfig   |    7 -
 drivers/staging/iio/adc/Makefile  |    1 -
 drivers/staging/iio/adc/adt7410.c | 1102 -------------------------------------
 3 files changed, 1110 deletions(-)
 delete mode 100644 drivers/staging/iio/adc/adt7410.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index fb8c239..029ae54 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -90,13 +90,6 @@ config AD7192
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7192.
 
-config ADT7410
-	tristate "Analog Devices ADT7310/ADT7410 temperature sensor driver"
-	depends on I2C || SPI_MASTER
-	help
-	  Say yes here to build support for Analog Devices ADT7310/ADT7410
-	  temperature sensors.
-
 config AD7280
 	tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
 	depends on SPI
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index d285596..3e9fb14 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7780) += ad7780.o
 obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_AD7192) += ad7192.o
-obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
 obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c
deleted file mode 100644
index 35455e1..0000000
--- a/drivers/staging/iio/adc/adt7410.c
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * ADT7410 digital temperature sensor driver supporting ADT7310/ADT7410
- *
- * Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/sysfs.h>
-#include <linux/list.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
-#include <linux/module.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
-#include <linux/iio/events.h>
-
-/*
- * ADT7410 registers definition
- */
-
-#define ADT7410_TEMPERATURE		0
-#define ADT7410_STATUS			2
-#define ADT7410_CONFIG			3
-#define ADT7410_T_ALARM_HIGH		4
-#define ADT7410_T_ALARM_LOW		6
-#define ADT7410_T_CRIT			8
-#define ADT7410_T_HYST			0xA
-#define ADT7410_ID			0xB
-#define ADT7410_RESET			0x2F
-
-/*
- * ADT7310 registers definition
- */
-
-#define ADT7310_STATUS			0
-#define ADT7310_CONFIG			1
-#define ADT7310_TEMPERATURE		2
-#define ADT7310_ID			3
-#define ADT7310_T_CRIT			4
-#define ADT7310_T_HYST			5
-#define ADT7310_T_ALARM_HIGH		6
-#define ADT7310_T_ALARM_LOW		7
-
-/*
- * ADT7410 status
- */
-#define ADT7410_STAT_T_LOW		0x10
-#define ADT7410_STAT_T_HIGH		0x20
-#define ADT7410_STAT_T_CRIT		0x40
-#define ADT7410_STAT_NOT_RDY		0x80
-
-/*
- * ADT7410 config
- */
-#define ADT7410_FAULT_QUEUE_MASK	0x3
-#define ADT7410_CT_POLARITY		0x4
-#define ADT7410_INT_POLARITY		0x8
-#define ADT7410_EVENT_MODE		0x10
-#define ADT7410_MODE_MASK		0x60
-#define ADT7410_ONESHOT			0x20
-#define ADT7410_SPS			0x40
-#define ADT7410_PD			0x60
-#define ADT7410_RESOLUTION		0x80
-
-/*
- * ADT7410 masks
- */
-#define ADT7410_T16_VALUE_SIGN			0x8000
-#define ADT7410_T16_VALUE_FLOAT_OFFSET		7
-#define ADT7410_T16_VALUE_FLOAT_MASK		0x7F
-#define ADT7410_T13_VALUE_SIGN			0x1000
-#define ADT7410_T13_VALUE_OFFSET		3
-#define ADT7410_T13_VALUE_FLOAT_OFFSET		4
-#define ADT7410_T13_VALUE_FLOAT_MASK		0xF
-#define ADT7410_T_HYST_MASK			0xF
-#define ADT7410_DEVICE_ID_MASK			0xF
-#define ADT7410_MANUFACTORY_ID_MASK		0xF0
-#define ADT7410_MANUFACTORY_ID_OFFSET		4
-
-
-#define ADT7310_CMD_REG_MASK			0x28
-#define ADT7310_CMD_REG_OFFSET			3
-#define ADT7310_CMD_READ			0x40
-#define ADT7310_CMD_CON_READ			0x4
-
-#define ADT7410_IRQS				2
-
-/*
- * struct adt7410_chip_info - chip specifc information
- */
-
-struct adt7410_chip_info;
-
-struct adt7410_ops {
-	int (*read_word)(struct adt7410_chip_info *, u8 reg, u16 *data);
-	int (*write_word)(struct adt7410_chip_info *, u8 reg, u16 data);
-	int (*read_byte)(struct adt7410_chip_info *, u8 reg, u8 *data);
-	int (*write_byte)(struct adt7410_chip_info *, u8 reg, u8 data);
-};
-
-struct adt7410_chip_info {
-	struct device *dev;
-	u8  config;
-
-	const struct adt7410_ops *ops;
-};
-
-static int adt7410_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
-{
-	return chip->ops->read_word(chip, reg, data);
-}
-
-static int adt7410_write_word(struct adt7410_chip_info *chip, u8 reg, u16 data)
-{
-	return chip->ops->write_word(chip, reg, data);
-}
-
-static int adt7410_read_byte(struct adt7410_chip_info *chip, u8 reg, u8 *data)
-{
-	return chip->ops->read_byte(chip, reg, data);
-}
-
-static int adt7410_write_byte(struct adt7410_chip_info *chip, u8 reg, u8 data)
-{
-	return chip->ops->write_byte(chip, reg, data);
-}
-
-static ssize_t adt7410_show_mode(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u8 config;
-
-	config = chip->config & ADT7410_MODE_MASK;
-
-	switch (config) {
-	case ADT7410_PD:
-		return sprintf(buf, "power-down\n");
-	case ADT7410_ONESHOT:
-		return sprintf(buf, "one-shot\n");
-	case ADT7410_SPS:
-		return sprintf(buf, "sps\n");
-	default:
-		return sprintf(buf, "full\n");
-	}
-}
-
-static ssize_t adt7410_store_mode(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u16 config;
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config & (~ADT7410_MODE_MASK);
-	if (strcmp(buf, "power-down"))
-		config |= ADT7410_PD;
-	else if (strcmp(buf, "one-shot"))
-		config |= ADT7410_ONESHOT;
-	else if (strcmp(buf, "sps"))
-		config |= ADT7410_SPS;
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return len;
-}
-
-static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
-		adt7410_show_mode,
-		adt7410_store_mode,
-		0);
-
-static ssize_t adt7410_show_available_modes(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
-}
-
-static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7410_show_available_modes, NULL, 0);
-
-static ssize_t adt7410_show_resolution(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-	int bits;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	if (chip->config & ADT7410_RESOLUTION)
-		bits = 16;
-	else
-		bits = 13;
-
-	return sprintf(buf, "%d bits\n", bits);
-}
-
-static ssize_t adt7410_store_resolution(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	unsigned long data;
-	u16 config;
-	int ret;
-
-	ret = strict_strtoul(buf, 10, &data);
-	if (ret)
-		return -EINVAL;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config & (~ADT7410_RESOLUTION);
-	if (data)
-		config |= ADT7410_RESOLUTION;
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return len;
-}
-
-static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
-		adt7410_show_resolution,
-		adt7410_store_resolution,
-		0);
-
-static ssize_t adt7410_show_id(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u8 id;
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_ID, &id);
-	if (ret)
-		return -EIO;
-
-	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
-			id & ADT7410_DEVICE_ID_MASK,
-			(id & ADT7410_MANUFACTORY_ID_MASK) >> ADT7410_MANUFACTORY_ID_OFFSET);
-}
-
-static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
-		adt7410_show_id,
-		NULL,
-		0);
-
-static ssize_t adt7410_convert_temperature(struct adt7410_chip_info *chip,
-		u16 data, char *buf)
-{
-	char sign = ' ';
-
-	if (!(chip->config & ADT7410_RESOLUTION))
-		data &= ~0x7;
-
-	if (data & ADT7410_T16_VALUE_SIGN) {
-		/* convert supplement to positive value */
-		data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
-		sign = '-';
-	}
-	return sprintf(buf, "%c%d.%.7d\n", sign,
-			(data >> ADT7410_T16_VALUE_FLOAT_OFFSET),
-			(data & ADT7410_T16_VALUE_FLOAT_MASK) * 78125);
-}
-
-static ssize_t adt7410_show_value(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u8 status;
-	u16 data;
-	int ret, i = 0;
-
-	do {
-		ret = adt7410_read_byte(chip, ADT7410_STATUS, &status);
-		if (ret)
-			return -EIO;
-		i++;
-		if (i == 10000)
-			return -EIO;
-	} while (status & ADT7410_STAT_NOT_RDY);
-
-	ret = adt7410_read_word(chip, ADT7410_TEMPERATURE, &data);
-	if (ret)
-		return -EIO;
-
-	return adt7410_convert_temperature(chip, data, buf);
-}
-
-static IIO_DEVICE_ATTR(value, S_IRUGO, adt7410_show_value, NULL, 0);
-
-static struct attribute *adt7410_attributes[] = {
-	&iio_dev_attr_available_modes.dev_attr.attr,
-	&iio_dev_attr_mode.dev_attr.attr,
-	&iio_dev_attr_resolution.dev_attr.attr,
-	&iio_dev_attr_id.dev_attr.attr,
-	&iio_dev_attr_value.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group adt7410_attribute_group = {
-	.attrs = adt7410_attributes,
-};
-
-static irqreturn_t adt7410_event_handler(int irq, void *private)
-{
-	struct iio_dev *indio_dev = private;
-	struct adt7410_chip_info *chip = iio_priv(indio_dev);
-	s64 timestamp = iio_get_time_ns();
-	u8 status;
-
-	if (adt7410_read_byte(chip, ADT7410_STATUS, &status))
-		return IRQ_HANDLED;
-
-	if (status & ADT7410_STAT_T_HIGH)
-		iio_push_event(indio_dev,
-			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
-						    IIO_EV_TYPE_THRESH,
-						    IIO_EV_DIR_RISING),
-			       timestamp);
-	if (status & ADT7410_STAT_T_LOW)
-		iio_push_event(indio_dev,
-			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
-						    IIO_EV_TYPE_THRESH,
-						    IIO_EV_DIR_FALLING),
-			       timestamp);
-	if (status & ADT7410_STAT_T_CRIT)
-		iio_push_event(indio_dev,
-			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
-						    IIO_EV_TYPE_THRESH,
-						    IIO_EV_DIR_RISING),
-			       timestamp);
-
-	return IRQ_HANDLED;
-}
-
-static ssize_t adt7410_show_event_mode(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	if (chip->config & ADT7410_EVENT_MODE)
-		return sprintf(buf, "interrupt\n");
-	else
-		return sprintf(buf, "comparator\n");
-}
-
-static ssize_t adt7410_set_event_mode(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u16 config;
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config &= ~ADT7410_EVENT_MODE;
-	if (strcmp(buf, "comparator") != 0)
-		config |= ADT7410_EVENT_MODE;
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return ret;
-}
-
-static ssize_t adt7410_show_available_event_modes(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return sprintf(buf, "comparator\ninterrupt\n");
-}
-
-static ssize_t adt7410_show_fault_queue(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	return sprintf(buf, "%d\n", chip->config & ADT7410_FAULT_QUEUE_MASK);
-}
-
-static ssize_t adt7410_set_fault_queue(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	unsigned long data;
-	int ret;
-	u8 config;
-
-	ret = strict_strtoul(buf, 10, &data);
-	if (ret || data > 3)
-		return -EINVAL;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
-	config |= data;
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return ret;
-}
-
-static inline ssize_t adt7410_show_t_bound(struct device *dev,
-		struct device_attribute *attr,
-		u8 bound_reg,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u16 data;
-	int ret;
-
-	ret = adt7410_read_word(chip, bound_reg, &data);
-	if (ret)
-		return -EIO;
-
-	return adt7410_convert_temperature(chip, data, buf);
-}
-
-static inline ssize_t adt7410_set_t_bound(struct device *dev,
-		struct device_attribute *attr,
-		u8 bound_reg,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	long tmp1, tmp2;
-	u16 data;
-	char *pos;
-	int ret;
-
-	pos = strchr(buf, '.');
-
-	ret = strict_strtol(buf, 10, &tmp1);
-
-	if (ret || tmp1 > 127 || tmp1 < -128)
-		return -EINVAL;
-
-	if (pos) {
-		len = strlen(pos);
-
-		if (chip->config & ADT7410_RESOLUTION) {
-			if (len > ADT7410_T16_VALUE_FLOAT_OFFSET)
-				len = ADT7410_T16_VALUE_FLOAT_OFFSET;
-			pos[len] = 0;
-			ret = strict_strtol(pos, 10, &tmp2);
-
-			if (!ret)
-				tmp2 = (tmp2 / 78125) * 78125;
-		} else {
-			if (len > ADT7410_T13_VALUE_FLOAT_OFFSET)
-				len = ADT7410_T13_VALUE_FLOAT_OFFSET;
-			pos[len] = 0;
-			ret = strict_strtol(pos, 10, &tmp2);
-
-			if (!ret)
-				tmp2 = (tmp2 / 625) * 625;
-		}
-	}
-
-	if (tmp1 < 0)
-		data = (u16)(-tmp1);
-	else
-		data = (u16)tmp1;
-
-	if (chip->config & ADT7410_RESOLUTION) {
-		data = (data << ADT7410_T16_VALUE_FLOAT_OFFSET) |
-			(tmp2 & ADT7410_T16_VALUE_FLOAT_MASK);
-
-		if (tmp1 < 0)
-			/* convert positive value to supplyment */
-			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
-	} else {
-		data = (data << ADT7410_T13_VALUE_FLOAT_OFFSET) |
-			(tmp2 & ADT7410_T13_VALUE_FLOAT_MASK);
-
-		if (tmp1 < 0)
-			/* convert positive value to supplyment */
-			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
-		data <<= ADT7410_T13_VALUE_OFFSET;
-	}
-
-	ret = adt7410_write_word(chip, bound_reg, data);
-	if (ret)
-		return -EIO;
-
-	return ret;
-}
-
-static ssize_t adt7410_show_t_alarm_high(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return adt7410_show_t_bound(dev, attr,
-			ADT7410_T_ALARM_HIGH, buf);
-}
-
-static inline ssize_t adt7410_set_t_alarm_high(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	return adt7410_set_t_bound(dev, attr,
-			ADT7410_T_ALARM_HIGH, buf, len);
-}
-
-static ssize_t adt7410_show_t_alarm_low(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return adt7410_show_t_bound(dev, attr,
-			ADT7410_T_ALARM_LOW, buf);
-}
-
-static inline ssize_t adt7410_set_t_alarm_low(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	return adt7410_set_t_bound(dev, attr,
-			ADT7410_T_ALARM_LOW, buf, len);
-}
-
-static ssize_t adt7410_show_t_crit(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return adt7410_show_t_bound(dev, attr,
-			ADT7410_T_CRIT, buf);
-}
-
-static inline ssize_t adt7410_set_t_crit(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	return adt7410_set_t_bound(dev, attr,
-			ADT7410_T_CRIT, buf, len);
-}
-
-static ssize_t adt7410_show_t_hyst(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-	u8 t_hyst;
-
-	ret = adt7410_read_byte(chip, ADT7410_T_HYST, &t_hyst);
-	if (ret)
-		return -EIO;
-
-	return sprintf(buf, "%d\n", t_hyst & ADT7410_T_HYST_MASK);
-}
-
-static inline ssize_t adt7410_set_t_hyst(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-	unsigned long data;
-	u8 t_hyst;
-
-	ret = strict_strtol(buf, 10, &data);
-
-	if (ret || data > ADT7410_T_HYST_MASK)
-		return -EINVAL;
-
-	t_hyst = (u8)data;
-
-	ret = adt7410_write_byte(chip, ADT7410_T_HYST, t_hyst);
-	if (ret)
-		return -EIO;
-
-	return ret;
-}
-
-static IIO_DEVICE_ATTR(event_mode,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_event_mode, adt7410_set_event_mode, 0);
-static IIO_DEVICE_ATTR(available_event_modes,
-		       S_IRUGO,
-		       adt7410_show_available_event_modes, NULL, 0);
-static IIO_DEVICE_ATTR(fault_queue,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
-static IIO_DEVICE_ATTR(t_alarm_high,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
-static IIO_DEVICE_ATTR(t_alarm_low,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
-static IIO_DEVICE_ATTR(t_crit,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_crit, adt7410_set_t_crit, 0);
-static IIO_DEVICE_ATTR(t_hyst,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
-
-static struct attribute *adt7410_event_int_attributes[] = {
-	&iio_dev_attr_event_mode.dev_attr.attr,
-	&iio_dev_attr_available_event_modes.dev_attr.attr,
-	&iio_dev_attr_fault_queue.dev_attr.attr,
-	&iio_dev_attr_t_alarm_high.dev_attr.attr,
-	&iio_dev_attr_t_alarm_low.dev_attr.attr,
-	&iio_dev_attr_t_crit.dev_attr.attr,
-	&iio_dev_attr_t_hyst.dev_attr.attr,
-	NULL,
-};
-
-static struct attribute_group adt7410_event_attribute_group = {
-	.attrs = adt7410_event_int_attributes,
-	.name = "events",
-};
-
-static const struct iio_info adt7410_info = {
-	.attrs = &adt7410_attribute_group,
-	.event_attrs = &adt7410_event_attribute_group,
-	.driver_module = THIS_MODULE,
-};
-
-/*
- * device probe and remove
- */
-
-static int adt7410_probe(struct device *dev, int irq,
-	const char *name, const struct adt7410_ops *ops)
-{
-	unsigned long *adt7410_platform_data = dev->platform_data;
-	unsigned long local_pdata[] = {0, 0};
-	struct adt7410_chip_info *chip;
-	struct iio_dev *indio_dev;
-	int ret = 0;
-
-	indio_dev = iio_device_alloc(sizeof(*chip));
-	if (indio_dev == NULL) {
-		ret = -ENOMEM;
-		goto error_ret;
-	}
-	chip = iio_priv(indio_dev);
-	/* this is only used for device removal purposes */
-	dev_set_drvdata(dev, indio_dev);
-
-	chip->dev = dev;
-	chip->ops = ops;
-
-	indio_dev->name = name;
-	indio_dev->dev.parent = dev;
-	indio_dev->info = &adt7410_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
-
-	if (!adt7410_platform_data)
-		adt7410_platform_data = local_pdata;
-
-	/* CT critcal temperature event. line 0 */
-	if (irq) {
-		ret = request_threaded_irq(irq,
-					   NULL,
-					   &adt7410_event_handler,
-					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-					   name,
-					   indio_dev);
-		if (ret)
-			goto error_free_dev;
-	}
-
-	/* INT bound temperature alarm event. line 1 */
-	if (adt7410_platform_data[0]) {
-		ret = request_threaded_irq(adt7410_platform_data[0],
-					   NULL,
-					   &adt7410_event_handler,
-					   adt7410_platform_data[1] |
-					   IRQF_ONESHOT,
-					   name,
-					   indio_dev);
-		if (ret)
-			goto error_unreg_ct_irq;
-	}
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret) {
-		ret = -EIO;
-		goto error_unreg_int_irq;
-	}
-
-	chip->config |= ADT7410_RESOLUTION;
-
-	if (irq && adt7410_platform_data[0]) {
-
-		/* set irq polarity low level */
-		chip->config &= ~ADT7410_CT_POLARITY;
-
-		if (adt7410_platform_data[1] & IRQF_TRIGGER_HIGH)
-			chip->config |= ADT7410_INT_POLARITY;
-		else
-			chip->config &= ~ADT7410_INT_POLARITY;
-	}
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, chip->config);
-	if (ret) {
-		ret = -EIO;
-		goto error_unreg_int_irq;
-	}
-	ret = iio_device_register(indio_dev);
-	if (ret)
-		goto error_unreg_int_irq;
-
-	dev_info(dev, "%s temperature sensor registered.\n",
-			 name);
-
-	return 0;
-
-error_unreg_int_irq:
-	free_irq(adt7410_platform_data[0], indio_dev);
-error_unreg_ct_irq:
-	free_irq(irq, indio_dev);
-error_free_dev:
-	iio_device_free(indio_dev);
-error_ret:
-	return ret;
-}
-
-static int adt7410_remove(struct device *dev, int irq)
-{
-	struct iio_dev *indio_dev = dev_get_drvdata(dev);
-	unsigned long *adt7410_platform_data = dev->platform_data;
-
-	iio_device_unregister(indio_dev);
-	if (adt7410_platform_data[0])
-		free_irq(adt7410_platform_data[0], indio_dev);
-	if (irq)
-		free_irq(irq, indio_dev);
-	iio_device_free(indio_dev);
-
-	return 0;
-}
-
-#if IS_ENABLED(CONFIG_I2C)
-
-static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg,
-	u16 *data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_read_word_data(client, reg);
-	if (ret < 0) {
-		dev_err(&client->dev, "I2C read error\n");
-		return ret;
-	}
-
-	*data = swab16((u16)ret);
-
-	return 0;
-}
-
-static int adt7410_i2c_write_word(struct adt7410_chip_info *chip, u8 reg,
-	u16 data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
-	if (ret < 0)
-		dev_err(&client->dev, "I2C write error\n");
-
-	return ret;
-}
-
-static int adt7410_i2c_read_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 *data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_read_byte_data(client, reg);
-	if (ret < 0) {
-		dev_err(&client->dev, "I2C read error\n");
-		return ret;
-	}
-
-	*data = (u8)ret;
-
-	return 0;
-}
-
-static int adt7410_i2c_write_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_write_byte_data(client, reg, data);
-	if (ret < 0)
-		dev_err(&client->dev, "I2C write error\n");
-
-	return ret;
-}
-
-static const struct adt7410_ops adt7410_i2c_ops = {
-	.read_word = adt7410_i2c_read_word,
-	.write_word = adt7410_i2c_write_word,
-	.read_byte = adt7410_i2c_read_byte,
-	.write_byte = adt7410_i2c_write_byte,
-};
-
-static int adt7410_i2c_probe(struct i2c_client *client,
-	const struct i2c_device_id *id)
-{
-	return adt7410_probe(&client->dev, client->irq, id->name,
-		&adt7410_i2c_ops);
-}
-
-static int adt7410_i2c_remove(struct i2c_client *client)
-{
-	return adt7410_remove(&client->dev, client->irq);
-}
-
-static const struct i2c_device_id adt7410_id[] = {
-	{ "adt7410", 0 },
-	{}
-};
-
-MODULE_DEVICE_TABLE(i2c, adt7410_id);
-
-static struct i2c_driver adt7410_driver = {
-	.driver = {
-		.name = "adt7410",
-	},
-	.probe = adt7410_i2c_probe,
-	.remove = adt7410_i2c_remove,
-	.id_table = adt7410_id,
-};
-
-static int __init adt7410_i2c_init(void)
-{
-	return i2c_add_driver(&adt7410_driver);
-}
-
-static void __exit adt7410_i2c_exit(void)
-{
-	i2c_del_driver(&adt7410_driver);
-}
-
-#else
-
-static int  __init adt7410_i2c_init(void) { return 0; };
-static void __exit adt7410_i2c_exit(void) {};
-
-#endif
-
-#if IS_ENABLED(CONFIG_SPI_MASTER)
-
-static const u8 adt7371_reg_table[] = {
-	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
-	[ADT7410_STATUS]	= ADT7310_STATUS,
-	[ADT7410_CONFIG]	= ADT7310_CONFIG,
-	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
-	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
-	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
-	[ADT7410_T_HYST]	= ADT7310_T_HYST,
-	[ADT7410_ID]		= ADT7310_ID,
-};
-
-#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
-
-static int adt7310_spi_read_word(struct adt7410_chip_info *chip,
-	u8 reg, u16 *data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 command = AD7310_COMMAND(reg);
-	int ret = 0;
-
-	command |= ADT7310_CMD_READ;
-	ret = spi_write(spi, &command, sizeof(command));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write command error\n");
-		return ret;
-	}
-
-	ret = spi_read(spi, (u8 *)data, sizeof(*data));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI read word error\n");
-		return ret;
-	}
-
-	*data = be16_to_cpu(*data);
-
-	return 0;
-}
-
-static int adt7310_spi_write_word(struct adt7410_chip_info *chip, u8 reg,
-	u16 data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 buf[3];
-	int ret = 0;
-
-	buf[0] = AD7310_COMMAND(reg);
-	buf[1] = (u8)(data >> 8);
-	buf[2] = (u8)(data & 0xFF);
-
-	ret = spi_write(spi, buf, 3);
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write word error\n");
-		return ret;
-	}
-
-	return ret;
-}
-
-static int adt7310_spi_read_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 *data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 command = AD7310_COMMAND(reg);
-	int ret = 0;
-
-	command |= ADT7310_CMD_READ;
-	ret = spi_write(spi, &command, sizeof(command));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write command error\n");
-		return ret;
-	}
-
-	ret = spi_read(spi, data, sizeof(*data));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI read byte error\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-static int adt7310_spi_write_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 buf[2];
-	int ret = 0;
-
-	buf[0] = AD7310_COMMAND(reg);
-	buf[1] = data;
-
-	ret = spi_write(spi, buf, 2);
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write byte error\n");
-		return ret;
-	}
-
-	return ret;
-}
-
-static const struct adt7410_ops adt7310_spi_ops = {
-	.read_word = adt7310_spi_read_word,
-	.write_word = adt7310_spi_write_word,
-	.read_byte = adt7310_spi_read_byte,
-	.write_byte = adt7310_spi_write_byte,
-};
-
-static int adt7310_spi_probe(struct spi_device *spi)
-{
-	return adt7410_probe(&spi->dev, spi->irq,
-		spi_get_device_id(spi)->name, &adt7310_spi_ops);
-}
-
-static int adt7310_spi_remove(struct spi_device *spi)
-{
-	return adt7410_remove(&spi->dev, spi->irq);
-}
-
-static const struct spi_device_id adt7310_id[] = {
-	{ "adt7310", 0 },
-	{}
-};
-MODULE_DEVICE_TABLE(spi, adt7310_id);
-
-static struct spi_driver adt7310_driver = {
-	.driver = {
-		.name = "adt7310",
-		.owner = THIS_MODULE,
-	},
-	.probe = adt7310_spi_probe,
-	.remove = adt7310_spi_remove,
-	.id_table = adt7310_id,
-};
-
-static int __init adt7310_spi_init(void)
-{
-	return spi_register_driver(&adt7310_driver);
-}
-
-static void adt7310_spi_exit(void)
-{
-	spi_unregister_driver(&adt7310_driver);
-}
-
-#else
-
-static int __init adt7310_spi_init(void) { return 0; };
-static void adt7310_spi_exit(void) {};
-
-#endif
-
-static int __init adt7410_init(void)
-{
-	int ret;
-
-	ret = adt7310_spi_init();
-	if (ret)
-		return ret;
-
-	ret = adt7410_i2c_init();
-	if (ret)
-		adt7310_spi_exit();
-
-	return ret;
-}
-module_init(adt7410_init);
-
-static void __exit adt7410_exit(void)
-{
-	adt7410_i2c_exit();
-	adt7310_spi_exit();
-}
-module_exit(adt7410_exit);
-
-MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("Analog Devices ADT7310/ADT7410 digital temperature sensor driver");
-MODULE_LICENSE("GPL v2");
-- 
1.8.0


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

* [lm-sensors] [PATCH v2 4/4] staging:iio: Remove adt7410 driver
@ 2013-02-18 13:38   ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-18 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

The adt7410 hwmon driver is feature wise more or less on par with the IIO
driver. So we can finally remove the IIO driver.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/staging/iio/adc/Kconfig   |    7 -
 drivers/staging/iio/adc/Makefile  |    1 -
 drivers/staging/iio/adc/adt7410.c | 1102 -------------------------------------
 3 files changed, 1110 deletions(-)
 delete mode 100644 drivers/staging/iio/adc/adt7410.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index fb8c239..029ae54 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -90,13 +90,6 @@ config AD7192
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7192.
 
-config ADT7410
-	tristate "Analog Devices ADT7310/ADT7410 temperature sensor driver"
-	depends on I2C || SPI_MASTER
-	help
-	  Say yes here to build support for Analog Devices ADT7310/ADT7410
-	  temperature sensors.
-
 config AD7280
 	tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
 	depends on SPI
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index d285596..3e9fb14 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7780) += ad7780.o
 obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_AD7192) += ad7192.o
-obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
 obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c
deleted file mode 100644
index 35455e1..0000000
--- a/drivers/staging/iio/adc/adt7410.c
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * ADT7410 digital temperature sensor driver supporting ADT7310/ADT7410
- *
- * Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/sysfs.h>
-#include <linux/list.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
-#include <linux/module.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
-#include <linux/iio/events.h>
-
-/*
- * ADT7410 registers definition
- */
-
-#define ADT7410_TEMPERATURE		0
-#define ADT7410_STATUS			2
-#define ADT7410_CONFIG			3
-#define ADT7410_T_ALARM_HIGH		4
-#define ADT7410_T_ALARM_LOW		6
-#define ADT7410_T_CRIT			8
-#define ADT7410_T_HYST			0xA
-#define ADT7410_ID			0xB
-#define ADT7410_RESET			0x2F
-
-/*
- * ADT7310 registers definition
- */
-
-#define ADT7310_STATUS			0
-#define ADT7310_CONFIG			1
-#define ADT7310_TEMPERATURE		2
-#define ADT7310_ID			3
-#define ADT7310_T_CRIT			4
-#define ADT7310_T_HYST			5
-#define ADT7310_T_ALARM_HIGH		6
-#define ADT7310_T_ALARM_LOW		7
-
-/*
- * ADT7410 status
- */
-#define ADT7410_STAT_T_LOW		0x10
-#define ADT7410_STAT_T_HIGH		0x20
-#define ADT7410_STAT_T_CRIT		0x40
-#define ADT7410_STAT_NOT_RDY		0x80
-
-/*
- * ADT7410 config
- */
-#define ADT7410_FAULT_QUEUE_MASK	0x3
-#define ADT7410_CT_POLARITY		0x4
-#define ADT7410_INT_POLARITY		0x8
-#define ADT7410_EVENT_MODE		0x10
-#define ADT7410_MODE_MASK		0x60
-#define ADT7410_ONESHOT			0x20
-#define ADT7410_SPS			0x40
-#define ADT7410_PD			0x60
-#define ADT7410_RESOLUTION		0x80
-
-/*
- * ADT7410 masks
- */
-#define ADT7410_T16_VALUE_SIGN			0x8000
-#define ADT7410_T16_VALUE_FLOAT_OFFSET		7
-#define ADT7410_T16_VALUE_FLOAT_MASK		0x7F
-#define ADT7410_T13_VALUE_SIGN			0x1000
-#define ADT7410_T13_VALUE_OFFSET		3
-#define ADT7410_T13_VALUE_FLOAT_OFFSET		4
-#define ADT7410_T13_VALUE_FLOAT_MASK		0xF
-#define ADT7410_T_HYST_MASK			0xF
-#define ADT7410_DEVICE_ID_MASK			0xF
-#define ADT7410_MANUFACTORY_ID_MASK		0xF0
-#define ADT7410_MANUFACTORY_ID_OFFSET		4
-
-
-#define ADT7310_CMD_REG_MASK			0x28
-#define ADT7310_CMD_REG_OFFSET			3
-#define ADT7310_CMD_READ			0x40
-#define ADT7310_CMD_CON_READ			0x4
-
-#define ADT7410_IRQS				2
-
-/*
- * struct adt7410_chip_info - chip specifc information
- */
-
-struct adt7410_chip_info;
-
-struct adt7410_ops {
-	int (*read_word)(struct adt7410_chip_info *, u8 reg, u16 *data);
-	int (*write_word)(struct adt7410_chip_info *, u8 reg, u16 data);
-	int (*read_byte)(struct adt7410_chip_info *, u8 reg, u8 *data);
-	int (*write_byte)(struct adt7410_chip_info *, u8 reg, u8 data);
-};
-
-struct adt7410_chip_info {
-	struct device *dev;
-	u8  config;
-
-	const struct adt7410_ops *ops;
-};
-
-static int adt7410_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
-{
-	return chip->ops->read_word(chip, reg, data);
-}
-
-static int adt7410_write_word(struct adt7410_chip_info *chip, u8 reg, u16 data)
-{
-	return chip->ops->write_word(chip, reg, data);
-}
-
-static int adt7410_read_byte(struct adt7410_chip_info *chip, u8 reg, u8 *data)
-{
-	return chip->ops->read_byte(chip, reg, data);
-}
-
-static int adt7410_write_byte(struct adt7410_chip_info *chip, u8 reg, u8 data)
-{
-	return chip->ops->write_byte(chip, reg, data);
-}
-
-static ssize_t adt7410_show_mode(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u8 config;
-
-	config = chip->config & ADT7410_MODE_MASK;
-
-	switch (config) {
-	case ADT7410_PD:
-		return sprintf(buf, "power-down\n");
-	case ADT7410_ONESHOT:
-		return sprintf(buf, "one-shot\n");
-	case ADT7410_SPS:
-		return sprintf(buf, "sps\n");
-	default:
-		return sprintf(buf, "full\n");
-	}
-}
-
-static ssize_t adt7410_store_mode(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u16 config;
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config & (~ADT7410_MODE_MASK);
-	if (strcmp(buf, "power-down"))
-		config |= ADT7410_PD;
-	else if (strcmp(buf, "one-shot"))
-		config |= ADT7410_ONESHOT;
-	else if (strcmp(buf, "sps"))
-		config |= ADT7410_SPS;
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return len;
-}
-
-static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
-		adt7410_show_mode,
-		adt7410_store_mode,
-		0);
-
-static ssize_t adt7410_show_available_modes(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
-}
-
-static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7410_show_available_modes, NULL, 0);
-
-static ssize_t adt7410_show_resolution(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-	int bits;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	if (chip->config & ADT7410_RESOLUTION)
-		bits = 16;
-	else
-		bits = 13;
-
-	return sprintf(buf, "%d bits\n", bits);
-}
-
-static ssize_t adt7410_store_resolution(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	unsigned long data;
-	u16 config;
-	int ret;
-
-	ret = strict_strtoul(buf, 10, &data);
-	if (ret)
-		return -EINVAL;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config & (~ADT7410_RESOLUTION);
-	if (data)
-		config |= ADT7410_RESOLUTION;
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return len;
-}
-
-static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
-		adt7410_show_resolution,
-		adt7410_store_resolution,
-		0);
-
-static ssize_t adt7410_show_id(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u8 id;
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_ID, &id);
-	if (ret)
-		return -EIO;
-
-	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
-			id & ADT7410_DEVICE_ID_MASK,
-			(id & ADT7410_MANUFACTORY_ID_MASK) >> ADT7410_MANUFACTORY_ID_OFFSET);
-}
-
-static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
-		adt7410_show_id,
-		NULL,
-		0);
-
-static ssize_t adt7410_convert_temperature(struct adt7410_chip_info *chip,
-		u16 data, char *buf)
-{
-	char sign = ' ';
-
-	if (!(chip->config & ADT7410_RESOLUTION))
-		data &= ~0x7;
-
-	if (data & ADT7410_T16_VALUE_SIGN) {
-		/* convert supplement to positive value */
-		data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
-		sign = '-';
-	}
-	return sprintf(buf, "%c%d.%.7d\n", sign,
-			(data >> ADT7410_T16_VALUE_FLOAT_OFFSET),
-			(data & ADT7410_T16_VALUE_FLOAT_MASK) * 78125);
-}
-
-static ssize_t adt7410_show_value(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u8 status;
-	u16 data;
-	int ret, i = 0;
-
-	do {
-		ret = adt7410_read_byte(chip, ADT7410_STATUS, &status);
-		if (ret)
-			return -EIO;
-		i++;
-		if (i = 10000)
-			return -EIO;
-	} while (status & ADT7410_STAT_NOT_RDY);
-
-	ret = adt7410_read_word(chip, ADT7410_TEMPERATURE, &data);
-	if (ret)
-		return -EIO;
-
-	return adt7410_convert_temperature(chip, data, buf);
-}
-
-static IIO_DEVICE_ATTR(value, S_IRUGO, adt7410_show_value, NULL, 0);
-
-static struct attribute *adt7410_attributes[] = {
-	&iio_dev_attr_available_modes.dev_attr.attr,
-	&iio_dev_attr_mode.dev_attr.attr,
-	&iio_dev_attr_resolution.dev_attr.attr,
-	&iio_dev_attr_id.dev_attr.attr,
-	&iio_dev_attr_value.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group adt7410_attribute_group = {
-	.attrs = adt7410_attributes,
-};
-
-static irqreturn_t adt7410_event_handler(int irq, void *private)
-{
-	struct iio_dev *indio_dev = private;
-	struct adt7410_chip_info *chip = iio_priv(indio_dev);
-	s64 timestamp = iio_get_time_ns();
-	u8 status;
-
-	if (adt7410_read_byte(chip, ADT7410_STATUS, &status))
-		return IRQ_HANDLED;
-
-	if (status & ADT7410_STAT_T_HIGH)
-		iio_push_event(indio_dev,
-			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
-						    IIO_EV_TYPE_THRESH,
-						    IIO_EV_DIR_RISING),
-			       timestamp);
-	if (status & ADT7410_STAT_T_LOW)
-		iio_push_event(indio_dev,
-			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
-						    IIO_EV_TYPE_THRESH,
-						    IIO_EV_DIR_FALLING),
-			       timestamp);
-	if (status & ADT7410_STAT_T_CRIT)
-		iio_push_event(indio_dev,
-			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
-						    IIO_EV_TYPE_THRESH,
-						    IIO_EV_DIR_RISING),
-			       timestamp);
-
-	return IRQ_HANDLED;
-}
-
-static ssize_t adt7410_show_event_mode(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	if (chip->config & ADT7410_EVENT_MODE)
-		return sprintf(buf, "interrupt\n");
-	else
-		return sprintf(buf, "comparator\n");
-}
-
-static ssize_t adt7410_set_event_mode(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u16 config;
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config &= ~ADT7410_EVENT_MODE;
-	if (strcmp(buf, "comparator") != 0)
-		config |= ADT7410_EVENT_MODE;
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return ret;
-}
-
-static ssize_t adt7410_show_available_event_modes(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return sprintf(buf, "comparator\ninterrupt\n");
-}
-
-static ssize_t adt7410_show_fault_queue(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	return sprintf(buf, "%d\n", chip->config & ADT7410_FAULT_QUEUE_MASK);
-}
-
-static ssize_t adt7410_set_fault_queue(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	unsigned long data;
-	int ret;
-	u8 config;
-
-	ret = strict_strtoul(buf, 10, &data);
-	if (ret || data > 3)
-		return -EINVAL;
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret)
-		return -EIO;
-
-	config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
-	config |= data;
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
-	if (ret)
-		return -EIO;
-
-	chip->config = config;
-
-	return ret;
-}
-
-static inline ssize_t adt7410_show_t_bound(struct device *dev,
-		struct device_attribute *attr,
-		u8 bound_reg,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	u16 data;
-	int ret;
-
-	ret = adt7410_read_word(chip, bound_reg, &data);
-	if (ret)
-		return -EIO;
-
-	return adt7410_convert_temperature(chip, data, buf);
-}
-
-static inline ssize_t adt7410_set_t_bound(struct device *dev,
-		struct device_attribute *attr,
-		u8 bound_reg,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	long tmp1, tmp2;
-	u16 data;
-	char *pos;
-	int ret;
-
-	pos = strchr(buf, '.');
-
-	ret = strict_strtol(buf, 10, &tmp1);
-
-	if (ret || tmp1 > 127 || tmp1 < -128)
-		return -EINVAL;
-
-	if (pos) {
-		len = strlen(pos);
-
-		if (chip->config & ADT7410_RESOLUTION) {
-			if (len > ADT7410_T16_VALUE_FLOAT_OFFSET)
-				len = ADT7410_T16_VALUE_FLOAT_OFFSET;
-			pos[len] = 0;
-			ret = strict_strtol(pos, 10, &tmp2);
-
-			if (!ret)
-				tmp2 = (tmp2 / 78125) * 78125;
-		} else {
-			if (len > ADT7410_T13_VALUE_FLOAT_OFFSET)
-				len = ADT7410_T13_VALUE_FLOAT_OFFSET;
-			pos[len] = 0;
-			ret = strict_strtol(pos, 10, &tmp2);
-
-			if (!ret)
-				tmp2 = (tmp2 / 625) * 625;
-		}
-	}
-
-	if (tmp1 < 0)
-		data = (u16)(-tmp1);
-	else
-		data = (u16)tmp1;
-
-	if (chip->config & ADT7410_RESOLUTION) {
-		data = (data << ADT7410_T16_VALUE_FLOAT_OFFSET) |
-			(tmp2 & ADT7410_T16_VALUE_FLOAT_MASK);
-
-		if (tmp1 < 0)
-			/* convert positive value to supplyment */
-			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
-	} else {
-		data = (data << ADT7410_T13_VALUE_FLOAT_OFFSET) |
-			(tmp2 & ADT7410_T13_VALUE_FLOAT_MASK);
-
-		if (tmp1 < 0)
-			/* convert positive value to supplyment */
-			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
-		data <<= ADT7410_T13_VALUE_OFFSET;
-	}
-
-	ret = adt7410_write_word(chip, bound_reg, data);
-	if (ret)
-		return -EIO;
-
-	return ret;
-}
-
-static ssize_t adt7410_show_t_alarm_high(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return adt7410_show_t_bound(dev, attr,
-			ADT7410_T_ALARM_HIGH, buf);
-}
-
-static inline ssize_t adt7410_set_t_alarm_high(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	return adt7410_set_t_bound(dev, attr,
-			ADT7410_T_ALARM_HIGH, buf, len);
-}
-
-static ssize_t adt7410_show_t_alarm_low(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return adt7410_show_t_bound(dev, attr,
-			ADT7410_T_ALARM_LOW, buf);
-}
-
-static inline ssize_t adt7410_set_t_alarm_low(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	return adt7410_set_t_bound(dev, attr,
-			ADT7410_T_ALARM_LOW, buf, len);
-}
-
-static ssize_t adt7410_show_t_crit(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	return adt7410_show_t_bound(dev, attr,
-			ADT7410_T_CRIT, buf);
-}
-
-static inline ssize_t adt7410_set_t_crit(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	return adt7410_set_t_bound(dev, attr,
-			ADT7410_T_CRIT, buf, len);
-}
-
-static ssize_t adt7410_show_t_hyst(struct device *dev,
-		struct device_attribute *attr,
-		char *buf)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-	u8 t_hyst;
-
-	ret = adt7410_read_byte(chip, ADT7410_T_HYST, &t_hyst);
-	if (ret)
-		return -EIO;
-
-	return sprintf(buf, "%d\n", t_hyst & ADT7410_T_HYST_MASK);
-}
-
-static inline ssize_t adt7410_set_t_hyst(struct device *dev,
-		struct device_attribute *attr,
-		const char *buf,
-		size_t len)
-{
-	struct iio_dev *dev_info = dev_to_iio_dev(dev);
-	struct adt7410_chip_info *chip = iio_priv(dev_info);
-	int ret;
-	unsigned long data;
-	u8 t_hyst;
-
-	ret = strict_strtol(buf, 10, &data);
-
-	if (ret || data > ADT7410_T_HYST_MASK)
-		return -EINVAL;
-
-	t_hyst = (u8)data;
-
-	ret = adt7410_write_byte(chip, ADT7410_T_HYST, t_hyst);
-	if (ret)
-		return -EIO;
-
-	return ret;
-}
-
-static IIO_DEVICE_ATTR(event_mode,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_event_mode, adt7410_set_event_mode, 0);
-static IIO_DEVICE_ATTR(available_event_modes,
-		       S_IRUGO,
-		       adt7410_show_available_event_modes, NULL, 0);
-static IIO_DEVICE_ATTR(fault_queue,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
-static IIO_DEVICE_ATTR(t_alarm_high,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
-static IIO_DEVICE_ATTR(t_alarm_low,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
-static IIO_DEVICE_ATTR(t_crit,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_crit, adt7410_set_t_crit, 0);
-static IIO_DEVICE_ATTR(t_hyst,
-		       S_IRUGO | S_IWUSR,
-		       adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
-
-static struct attribute *adt7410_event_int_attributes[] = {
-	&iio_dev_attr_event_mode.dev_attr.attr,
-	&iio_dev_attr_available_event_modes.dev_attr.attr,
-	&iio_dev_attr_fault_queue.dev_attr.attr,
-	&iio_dev_attr_t_alarm_high.dev_attr.attr,
-	&iio_dev_attr_t_alarm_low.dev_attr.attr,
-	&iio_dev_attr_t_crit.dev_attr.attr,
-	&iio_dev_attr_t_hyst.dev_attr.attr,
-	NULL,
-};
-
-static struct attribute_group adt7410_event_attribute_group = {
-	.attrs = adt7410_event_int_attributes,
-	.name = "events",
-};
-
-static const struct iio_info adt7410_info = {
-	.attrs = &adt7410_attribute_group,
-	.event_attrs = &adt7410_event_attribute_group,
-	.driver_module = THIS_MODULE,
-};
-
-/*
- * device probe and remove
- */
-
-static int adt7410_probe(struct device *dev, int irq,
-	const char *name, const struct adt7410_ops *ops)
-{
-	unsigned long *adt7410_platform_data = dev->platform_data;
-	unsigned long local_pdata[] = {0, 0};
-	struct adt7410_chip_info *chip;
-	struct iio_dev *indio_dev;
-	int ret = 0;
-
-	indio_dev = iio_device_alloc(sizeof(*chip));
-	if (indio_dev = NULL) {
-		ret = -ENOMEM;
-		goto error_ret;
-	}
-	chip = iio_priv(indio_dev);
-	/* this is only used for device removal purposes */
-	dev_set_drvdata(dev, indio_dev);
-
-	chip->dev = dev;
-	chip->ops = ops;
-
-	indio_dev->name = name;
-	indio_dev->dev.parent = dev;
-	indio_dev->info = &adt7410_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
-
-	if (!adt7410_platform_data)
-		adt7410_platform_data = local_pdata;
-
-	/* CT critcal temperature event. line 0 */
-	if (irq) {
-		ret = request_threaded_irq(irq,
-					   NULL,
-					   &adt7410_event_handler,
-					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-					   name,
-					   indio_dev);
-		if (ret)
-			goto error_free_dev;
-	}
-
-	/* INT bound temperature alarm event. line 1 */
-	if (adt7410_platform_data[0]) {
-		ret = request_threaded_irq(adt7410_platform_data[0],
-					   NULL,
-					   &adt7410_event_handler,
-					   adt7410_platform_data[1] |
-					   IRQF_ONESHOT,
-					   name,
-					   indio_dev);
-		if (ret)
-			goto error_unreg_ct_irq;
-	}
-
-	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
-	if (ret) {
-		ret = -EIO;
-		goto error_unreg_int_irq;
-	}
-
-	chip->config |= ADT7410_RESOLUTION;
-
-	if (irq && adt7410_platform_data[0]) {
-
-		/* set irq polarity low level */
-		chip->config &= ~ADT7410_CT_POLARITY;
-
-		if (adt7410_platform_data[1] & IRQF_TRIGGER_HIGH)
-			chip->config |= ADT7410_INT_POLARITY;
-		else
-			chip->config &= ~ADT7410_INT_POLARITY;
-	}
-
-	ret = adt7410_write_byte(chip, ADT7410_CONFIG, chip->config);
-	if (ret) {
-		ret = -EIO;
-		goto error_unreg_int_irq;
-	}
-	ret = iio_device_register(indio_dev);
-	if (ret)
-		goto error_unreg_int_irq;
-
-	dev_info(dev, "%s temperature sensor registered.\n",
-			 name);
-
-	return 0;
-
-error_unreg_int_irq:
-	free_irq(adt7410_platform_data[0], indio_dev);
-error_unreg_ct_irq:
-	free_irq(irq, indio_dev);
-error_free_dev:
-	iio_device_free(indio_dev);
-error_ret:
-	return ret;
-}
-
-static int adt7410_remove(struct device *dev, int irq)
-{
-	struct iio_dev *indio_dev = dev_get_drvdata(dev);
-	unsigned long *adt7410_platform_data = dev->platform_data;
-
-	iio_device_unregister(indio_dev);
-	if (adt7410_platform_data[0])
-		free_irq(adt7410_platform_data[0], indio_dev);
-	if (irq)
-		free_irq(irq, indio_dev);
-	iio_device_free(indio_dev);
-
-	return 0;
-}
-
-#if IS_ENABLED(CONFIG_I2C)
-
-static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg,
-	u16 *data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_read_word_data(client, reg);
-	if (ret < 0) {
-		dev_err(&client->dev, "I2C read error\n");
-		return ret;
-	}
-
-	*data = swab16((u16)ret);
-
-	return 0;
-}
-
-static int adt7410_i2c_write_word(struct adt7410_chip_info *chip, u8 reg,
-	u16 data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
-	if (ret < 0)
-		dev_err(&client->dev, "I2C write error\n");
-
-	return ret;
-}
-
-static int adt7410_i2c_read_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 *data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_read_byte_data(client, reg);
-	if (ret < 0) {
-		dev_err(&client->dev, "I2C read error\n");
-		return ret;
-	}
-
-	*data = (u8)ret;
-
-	return 0;
-}
-
-static int adt7410_i2c_write_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 data)
-{
-	struct i2c_client *client = to_i2c_client(chip->dev);
-	int ret = 0;
-
-	ret = i2c_smbus_write_byte_data(client, reg, data);
-	if (ret < 0)
-		dev_err(&client->dev, "I2C write error\n");
-
-	return ret;
-}
-
-static const struct adt7410_ops adt7410_i2c_ops = {
-	.read_word = adt7410_i2c_read_word,
-	.write_word = adt7410_i2c_write_word,
-	.read_byte = adt7410_i2c_read_byte,
-	.write_byte = adt7410_i2c_write_byte,
-};
-
-static int adt7410_i2c_probe(struct i2c_client *client,
-	const struct i2c_device_id *id)
-{
-	return adt7410_probe(&client->dev, client->irq, id->name,
-		&adt7410_i2c_ops);
-}
-
-static int adt7410_i2c_remove(struct i2c_client *client)
-{
-	return adt7410_remove(&client->dev, client->irq);
-}
-
-static const struct i2c_device_id adt7410_id[] = {
-	{ "adt7410", 0 },
-	{}
-};
-
-MODULE_DEVICE_TABLE(i2c, adt7410_id);
-
-static struct i2c_driver adt7410_driver = {
-	.driver = {
-		.name = "adt7410",
-	},
-	.probe = adt7410_i2c_probe,
-	.remove = adt7410_i2c_remove,
-	.id_table = adt7410_id,
-};
-
-static int __init adt7410_i2c_init(void)
-{
-	return i2c_add_driver(&adt7410_driver);
-}
-
-static void __exit adt7410_i2c_exit(void)
-{
-	i2c_del_driver(&adt7410_driver);
-}
-
-#else
-
-static int  __init adt7410_i2c_init(void) { return 0; };
-static void __exit adt7410_i2c_exit(void) {};
-
-#endif
-
-#if IS_ENABLED(CONFIG_SPI_MASTER)
-
-static const u8 adt7371_reg_table[] = {
-	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
-	[ADT7410_STATUS]	= ADT7310_STATUS,
-	[ADT7410_CONFIG]	= ADT7310_CONFIG,
-	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
-	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
-	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
-	[ADT7410_T_HYST]	= ADT7310_T_HYST,
-	[ADT7410_ID]		= ADT7310_ID,
-};
-
-#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
-
-static int adt7310_spi_read_word(struct adt7410_chip_info *chip,
-	u8 reg, u16 *data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 command = AD7310_COMMAND(reg);
-	int ret = 0;
-
-	command |= ADT7310_CMD_READ;
-	ret = spi_write(spi, &command, sizeof(command));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write command error\n");
-		return ret;
-	}
-
-	ret = spi_read(spi, (u8 *)data, sizeof(*data));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI read word error\n");
-		return ret;
-	}
-
-	*data = be16_to_cpu(*data);
-
-	return 0;
-}
-
-static int adt7310_spi_write_word(struct adt7410_chip_info *chip, u8 reg,
-	u16 data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 buf[3];
-	int ret = 0;
-
-	buf[0] = AD7310_COMMAND(reg);
-	buf[1] = (u8)(data >> 8);
-	buf[2] = (u8)(data & 0xFF);
-
-	ret = spi_write(spi, buf, 3);
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write word error\n");
-		return ret;
-	}
-
-	return ret;
-}
-
-static int adt7310_spi_read_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 *data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 command = AD7310_COMMAND(reg);
-	int ret = 0;
-
-	command |= ADT7310_CMD_READ;
-	ret = spi_write(spi, &command, sizeof(command));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write command error\n");
-		return ret;
-	}
-
-	ret = spi_read(spi, data, sizeof(*data));
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI read byte error\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-static int adt7310_spi_write_byte(struct adt7410_chip_info *chip, u8 reg,
-	u8 data)
-{
-	struct spi_device *spi = to_spi_device(chip->dev);
-	u8 buf[2];
-	int ret = 0;
-
-	buf[0] = AD7310_COMMAND(reg);
-	buf[1] = data;
-
-	ret = spi_write(spi, buf, 2);
-	if (ret < 0) {
-		dev_err(&spi->dev, "SPI write byte error\n");
-		return ret;
-	}
-
-	return ret;
-}
-
-static const struct adt7410_ops adt7310_spi_ops = {
-	.read_word = adt7310_spi_read_word,
-	.write_word = adt7310_spi_write_word,
-	.read_byte = adt7310_spi_read_byte,
-	.write_byte = adt7310_spi_write_byte,
-};
-
-static int adt7310_spi_probe(struct spi_device *spi)
-{
-	return adt7410_probe(&spi->dev, spi->irq,
-		spi_get_device_id(spi)->name, &adt7310_spi_ops);
-}
-
-static int adt7310_spi_remove(struct spi_device *spi)
-{
-	return adt7410_remove(&spi->dev, spi->irq);
-}
-
-static const struct spi_device_id adt7310_id[] = {
-	{ "adt7310", 0 },
-	{}
-};
-MODULE_DEVICE_TABLE(spi, adt7310_id);
-
-static struct spi_driver adt7310_driver = {
-	.driver = {
-		.name = "adt7310",
-		.owner = THIS_MODULE,
-	},
-	.probe = adt7310_spi_probe,
-	.remove = adt7310_spi_remove,
-	.id_table = adt7310_id,
-};
-
-static int __init adt7310_spi_init(void)
-{
-	return spi_register_driver(&adt7310_driver);
-}
-
-static void adt7310_spi_exit(void)
-{
-	spi_unregister_driver(&adt7310_driver);
-}
-
-#else
-
-static int __init adt7310_spi_init(void) { return 0; };
-static void adt7310_spi_exit(void) {};
-
-#endif
-
-static int __init adt7410_init(void)
-{
-	int ret;
-
-	ret = adt7310_spi_init();
-	if (ret)
-		return ret;
-
-	ret = adt7410_i2c_init();
-	if (ret)
-		adt7310_spi_exit();
-
-	return ret;
-}
-module_init(adt7410_init);
-
-static void __exit adt7410_exit(void)
-{
-	adt7410_i2c_exit();
-	adt7310_spi_exit();
-}
-module_exit(adt7410_exit);
-
-MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("Analog Devices ADT7310/ADT7410 digital temperature sensor driver");
-MODULE_LICENSE("GPL v2");
-- 
1.8.0


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-18 20:22   ` Hartmut Knaack
  -1 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-18 20:22 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors, linux-iio

Lars-Peter Clausen schrieb:
> Currently each time the temperature register is read the driver also reads the
> threshold and hysteresis registers. This increases the amount of I2C traffic and
> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> the hysteresis change on their own, so once we've read them, we should be able
> to just use the cached value of the registers. This patch modifies the code
> accordingly and only reads the threshold and hysteresis registers once during
> probe.
I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>
> ---
> Changes since v1:
> 	* Fix error checking for i2c reads
> ---
>  drivers/hwmon/adt7410.c | 91 +++++++++++++++++++++++++++++++------------------
>  1 file changed, 58 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 99a7290..b6acfa4 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -119,45 +119,33 @@ static int adt7410_temp_ready(struct i2c_client *client)
>  	return -ETIMEDOUT;
>  }
>  
> -static struct adt7410_data *adt7410_update_device(struct device *dev)
> +static int adt7410_update_temp(struct device *dev)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
>  	struct adt7410_data *data = i2c_get_clientdata(client);
> -	struct adt7410_data *ret = data;
> +	int ret = 0;
> +
>  	mutex_lock(&data->update_lock);
>  
>  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
>  	    || !data->valid) {
> -		int i, status;
> +		int temp;
>  
>  		dev_dbg(&client->dev, "Starting update\n");
>  
> -		status = adt7410_temp_ready(client); /* check for new value */
> -		if (unlikely(status)) {
> -			ret = ERR_PTR(status);
> +		ret = adt7410_temp_ready(client); /* check for new value */
> +		if (ret)
>  			goto abort;
> -		}
> -		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
> -			status = i2c_smbus_read_word_swapped(client,
> -							ADT7410_REG_TEMP[i]);
> -			if (unlikely(status < 0)) {
> -				dev_dbg(dev,
> -					"Failed to read value: reg %d, error %d\n",
> -					ADT7410_REG_TEMP[i], status);
> -				ret = ERR_PTR(status);
> -				goto abort;
> -			}
> -			data->temp[i] = status;
> -		}
> -		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> -		if (unlikely(status < 0)) {
> -			dev_dbg(dev,
> -				"Failed to read value: reg %d, error %d\n",
> -				ADT7410_T_HYST, status);
> -			ret = ERR_PTR(status);
> +
> +		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
> +		if (temp < 0) {
> +			ret = temp;
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
>  			goto abort;
>  		}
> -		data->hyst = status;
> +		data->temp[0] = temp;
> +
>  		data->last_updated = jiffies;
>  		data->valid = true;
>  	}
> @@ -167,6 +155,35 @@ abort:
>  	return ret;
>  }
>  
> +static int adt7410_fill_cache(struct i2c_client *client)
> +{
> +	struct adt7410_data *data = i2c_get_clientdata(client);
> +	int ret;
> +	int i;
> +
> +	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> +		if (ret < 0) {
> +			dev_dbg(&client->dev,
> +				"Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			return ret;
> +		}
> +		data->temp[i] = ret;
> +	}
> +
> +	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> +	if (ret < 0) {
> +		dev_dbg(&client->dev,
> +			"Failed to read value: hyst reg, error %d\n",
> +			ret);
> +		return ret;
> +	}
> +	data->hyst = ret;
> +
> +	return 0;
> +}
> +
>  static s16 ADT7410_TEMP_TO_REG(long temp)
>  {
>  	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
> @@ -193,10 +210,16 @@ static ssize_t adt7410_show_temp(struct device *dev,
>  				 struct device_attribute *da, char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct adt7410_data *data = adt7410_update_device(dev);
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct adt7410_data *data = i2c_get_clientdata(client);
>  
> -	if (IS_ERR(data))
> -		return PTR_ERR(data);
> +	if (attr->index == 0) {
> +		int ret;
> +
> +		ret = adt7410_update_temp(dev);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
>  		       data->temp[attr->index]));
> @@ -232,13 +255,11 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
>  				   char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct adt7410_data *data;
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct adt7410_data *data = i2c_get_clientdata(client);
>  	int nr = attr->index;
>  	int hyst;
>  
> -	data = adt7410_update_device(dev);
> -	if (IS_ERR(data))
> -		return PTR_ERR(data);
>  	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
>  
>  	/*
> @@ -371,6 +392,10 @@ static int adt7410_probe(struct i2c_client *client,
>  	}
>  	dev_dbg(&client->dev, "Config %02x\n", data->config);
>  
> +	ret = adt7410_fill_cache(client);
> +	if (ret)
> +		goto exit_restore;
> +
>  	/* Register sysfs hooks */
>  	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
>  	if (ret)


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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-18 20:22   ` Hartmut Knaack
  0 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-18 20:22 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors, linux-iio

Lars-Peter Clausen schrieb:
> Currently each time the temperature register is read the driver also reads the
> threshold and hysteresis registers. This increases the amount of I2C traffic and
> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> the hysteresis change on their own, so once we've read them, we should be able
> to just use the cached value of the registers. This patch modifies the code
> accordingly and only reads the threshold and hysteresis registers once during
> probe.
I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>
> ---
> Changes since v1:
> 	* Fix error checking for i2c reads
> ---
>  drivers/hwmon/adt7410.c | 91 +++++++++++++++++++++++++++++++------------------
>  1 file changed, 58 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 99a7290..b6acfa4 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -119,45 +119,33 @@ static int adt7410_temp_ready(struct i2c_client *client)
>  	return -ETIMEDOUT;
>  }
>  
> -static struct adt7410_data *adt7410_update_device(struct device *dev)
> +static int adt7410_update_temp(struct device *dev)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
>  	struct adt7410_data *data = i2c_get_clientdata(client);
> -	struct adt7410_data *ret = data;
> +	int ret = 0;
> +
>  	mutex_lock(&data->update_lock);
>  
>  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
>  	    || !data->valid) {
> -		int i, status;
> +		int temp;
>  
>  		dev_dbg(&client->dev, "Starting update\n");
>  
> -		status = adt7410_temp_ready(client); /* check for new value */
> -		if (unlikely(status)) {
> -			ret = ERR_PTR(status);
> +		ret = adt7410_temp_ready(client); /* check for new value */
> +		if (ret)
>  			goto abort;
> -		}
> -		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
> -			status = i2c_smbus_read_word_swapped(client,
> -							ADT7410_REG_TEMP[i]);
> -			if (unlikely(status < 0)) {
> -				dev_dbg(dev,
> -					"Failed to read value: reg %d, error %d\n",
> -					ADT7410_REG_TEMP[i], status);
> -				ret = ERR_PTR(status);
> -				goto abort;
> -			}
> -			data->temp[i] = status;
> -		}
> -		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> -		if (unlikely(status < 0)) {
> -			dev_dbg(dev,
> -				"Failed to read value: reg %d, error %d\n",
> -				ADT7410_T_HYST, status);
> -			ret = ERR_PTR(status);
> +
> +		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
> +		if (temp < 0) {
> +			ret = temp;
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
>  			goto abort;
>  		}
> -		data->hyst = status;
> +		data->temp[0] = temp;
> +
>  		data->last_updated = jiffies;
>  		data->valid = true;
>  	}
> @@ -167,6 +155,35 @@ abort:
>  	return ret;
>  }
>  
> +static int adt7410_fill_cache(struct i2c_client *client)
> +{
> +	struct adt7410_data *data = i2c_get_clientdata(client);
> +	int ret;
> +	int i;
> +
> +	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> +		if (ret < 0) {
> +			dev_dbg(&client->dev,
> +				"Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			return ret;
> +		}
> +		data->temp[i] = ret;
> +	}
> +
> +	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> +	if (ret < 0) {
> +		dev_dbg(&client->dev,
> +			"Failed to read value: hyst reg, error %d\n",
> +			ret);
> +		return ret;
> +	}
> +	data->hyst = ret;
> +
> +	return 0;
> +}
> +
>  static s16 ADT7410_TEMP_TO_REG(long temp)
>  {
>  	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
> @@ -193,10 +210,16 @@ static ssize_t adt7410_show_temp(struct device *dev,
>  				 struct device_attribute *da, char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct adt7410_data *data = adt7410_update_device(dev);
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct adt7410_data *data = i2c_get_clientdata(client);
>  
> -	if (IS_ERR(data))
> -		return PTR_ERR(data);
> +	if (attr->index = 0) {
> +		int ret;
> +
> +		ret = adt7410_update_temp(dev);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
>  		       data->temp[attr->index]));
> @@ -232,13 +255,11 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
>  				   char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct adt7410_data *data;
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct adt7410_data *data = i2c_get_clientdata(client);
>  	int nr = attr->index;
>  	int hyst;
>  
> -	data = adt7410_update_device(dev);
> -	if (IS_ERR(data))
> -		return PTR_ERR(data);
>  	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
>  
>  	/*
> @@ -371,6 +392,10 @@ static int adt7410_probe(struct i2c_client *client,
>  	}
>  	dev_dbg(&client->dev, "Config %02x\n", data->config);
>  
> +	ret = adt7410_fill_cache(client);
> +	if (ret)
> +		goto exit_restore;
> +
>  	/* Register sysfs hooks */
>  	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
>  	if (ret)


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-18 20:22   ` [lm-sensors] " Hartmut Knaack
@ 2013-02-19  1:02     ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:02 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> Lars-Peter Clausen schrieb:
> > Currently each time the temperature register is read the driver also reads the
> > threshold and hysteresis registers. This increases the amount of I2C traffic and
> > time needed to read the temperature by a factor of ~5. Neither the threshold nor
> > the hysteresis change on their own, so once we've read them, we should be able
> > to just use the cached value of the registers. This patch modifies the code
> > accordingly and only reads the threshold and hysteresis registers once during
> > probe.
> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.

I am not concerned about that, unless there is a known issue with the chip
and it is known to return bad data under some circumstances. I am doing the
same in the PMBus drivers, since there are simply too many limit registers
to read on some of the chips (there may literally be more than a hundred).
That works fine most of the time; if it does not work, it is a chip problem,
an i2c bus master problem, a hardware signal problem, or a combination of all.
I actually think it is better if the problem is exposed by cached bad readings.
Then there is a chance to do something about it. This would be much better
than just re-reading the value next time and ignoring the problem.

Something else - the commit window just opened (a week earlier than I guessed),
so this part of the series is going to miss 3.9. Hartmut, if you plan to provide
an Acked-by or Reviewed-by or Tested-by for the first part of the series, you'll
have to do it soon, as I plan to send my push request to Linus around Thursday.

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-19  1:02     ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:02 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> Lars-Peter Clausen schrieb:
> > Currently each time the temperature register is read the driver also reads the
> > threshold and hysteresis registers. This increases the amount of I2C traffic and
> > time needed to read the temperature by a factor of ~5. Neither the threshold nor
> > the hysteresis change on their own, so once we've read them, we should be able
> > to just use the cached value of the registers. This patch modifies the code
> > accordingly and only reads the threshold and hysteresis registers once during
> > probe.
> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.

I am not concerned about that, unless there is a known issue with the chip
and it is known to return bad data under some circumstances. I am doing the
same in the PMBus drivers, since there are simply too many limit registers
to read on some of the chips (there may literally be more than a hundred).
That works fine most of the time; if it does not work, it is a chip problem,
an i2c bus master problem, a hardware signal problem, or a combination of all.
I actually think it is better if the problem is exposed by cached bad readings.
Then there is a chance to do something about it. This would be much better
than just re-reading the value next time and ignoring the problem.

Something else - the commit window just opened (a week earlier than I guessed),
so this part of the series is going to miss 3.9. Hartmut, if you plan to provide
an Acked-by or Reviewed-by or Tested-by for the first part of the series, you'll
have to do it soon, as I plan to send my push request to Linus around Thursday.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-18 13:38   ` [lm-sensors] [PATCH v2 2/4] hwmon: (adt7410) =?utf-8?q?Add_support_for_the_ad Lars-Peter Clausen
@ 2013-02-19  1:30     ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:30 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Mon, Feb 18, 2013 at 02:38:57PM +0100, Lars-Peter Clausen wrote:
> The adt7310/adt7320 is the SPI version of the adt7410/adt7420. The register map
> layout is a bit different, i.e. the register addresses differ between the two
> variants, but the bit layouts of the individual registers are identical. So both
> chip variants can easily be supported by the same driver. The issue of non
> matching register address layouts is solved by a simple look-up table which
> translates the I2C addresses to the SPI addresses.
> 
> The patch moves the bulk of the adt7410 driver to a common module that will be
> shared by the adt7410 and adt7310 drivers. This common module implements the
> driver logic and uses a set of virtual functions to perform IO access. The
> adt7410 and adt7310 driver modules provide proper implementations of these IO
> accessor functions for I2C respective SPI.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> 
> ---
> Changes since v1:
> 	* Update the driver documentation to include ADT7310/ADT7320/ADT7420
> 	* Pass the result of the read methods via the return value instead of a
> 	  pointer argument
> 	* Simplify spi read methods by using spi_w8r8 and spi_w8r16
> 	* Update module description of the shared module
> 	* Fix some typos
> ---
>  Documentation/hwmon/adt7410 |  42 ++--
>  drivers/hwmon/Kconfig       |  20 ++
>  drivers/hwmon/Makefile      |   2 +
>  drivers/hwmon/adt7310.c     | 115 +++++++++++
>  drivers/hwmon/adt7410.c     | 464 +++---------------------------------------
>  drivers/hwmon/adt7x10.c     | 476 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/adt7x10.h     |  48 +++++
>  7 files changed, 720 insertions(+), 447 deletions(-)
>  create mode 100644 drivers/hwmon/adt7310.c
>  create mode 100644 drivers/hwmon/adt7x10.c
>  create mode 100644 drivers/hwmon/adt7x10.h
> 
> diff --git a/Documentation/hwmon/adt7410 b/Documentation/hwmon/adt7410
> index 9600400..e452ae0 100644
> --- a/Documentation/hwmon/adt7410
> +++ b/Documentation/hwmon/adt7410
> @@ -7,25 +7,41 @@ Supported chips:
>      Addresses scanned: I2C 0x48 - 0x4B
>      Datasheet: Publicly available at the Analog Devices website
>                 http://www.analog.com/static/imported-files/data_sheets/ADT7410.pdf
> +  * Analog Devices ADT7420
> +    Prefix: 'adt7420'
> +    Addresses scanned: I2C 0x48 - 0x4B
> +    Datasheet: Publicly available at the Analog Devices website
> +               http://www.analog.com/static/imported-files/data_sheets/ADT7420.pdf
> +  * Analog Devices ADT7310
> +    Prefix: 'adt7310'
> +    Addresses scanned: I2C 0x48 - 0x4B
> +    Datasheet: Publicly available at the Analog Devices website
> +               http://www.analog.com/static/imported-files/data_sheets/ADT7310.pdf
> +  * Analog Devices ADT7320
> +    Prefix: 'adt7320'
> +    Addresses scanned: I2C 0x48 - 0x4B
> +    Datasheet: Publicly available at the Analog Devices website
> +               http://www.analog.com/static/imported-files/data_sheets/ADT7320.pdf
>  
>  Author: Hartmut Knaack <knaack.h@gmx.de>
>  
>  Description
>  -----------
>  
> -The ADT7410 is a temperature sensor with rated temperature range of -55°C to
> -+150°C. It has a high accuracy of +/-0.5°C and can be operated at a resolution
> -of 13 bits (0.0625°C) or 16 bits (0.0078°C). The sensor provides an INT pin to
> -indicate that a minimum or maximum temperature set point has been exceeded, as
> -well as a critical temperature (CT) pin to indicate that the critical
> -temperature set point has been exceeded. Both pins can be set up with a common
> -hysteresis of 0°C - 15°C and a fault queue, ranging from 1 to 4 events. Both
> -pins can individually set to be active-low or active-high, while the whole
> -device can either run in comparator mode or interrupt mode. The ADT7410
> -supports continous temperature sampling, as well as sampling one temperature
> -value per second or even justget one sample on demand for power saving.
> -Besides, it can completely power down its ADC, if power management is
> -required.
> +The ADT7410 and similar are a temperature sensors with rated temperature range
> +of -55°C to +150°C (ADT7310/ADT7410) or -40°C to +150°C (ADT7320/ADT7420). They
> +have a high accuracy of +/-0.5°C (ADT7310/ADT7410) or +/-0.2C (ADT7430/ADT7420)
> +and can be operated at a resolution of 13 bits (0.0625°C) or 16 bits (0.0078°C).
> +The sensor provides an INT pin to indicate that a minimum or maximum temperature
> +set point has been exceeded, as well as a critical temperature (CT) pin to
> +indicate that the critical temperature set point has been exceeded. Both pins
> +can be set up with a common hysteresis of 0°C - 15°C and a fault queue, ranging
> +from 1 to 4 events. Both pins can individually set to be active-low or
> +active-high, while the whole device can either run in comparator mode or
> +interrupt mode. The ADT7410 supports continous temperature sampling, as well as
> +sampling one temperature value per second or even justget one sample on demand
> +for power saving.  Besides, it can completely power down its ADC, if power
> +management is required.
>  
>  Configuration Notes
>  -------------------
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 89ac1cb..aaa14f4 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -179,9 +179,29 @@ config SENSORS_ADM9240
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called adm9240.
>  
> +config SENSORS_ADT7X10
> +	tristate
> +	help
> +	  This module contains common code shared by the ADT7310/ADT7320 and
> +	  ADT7410/ADT7420 temperature monitoring chip drivers.
> +
> +	  If build as a module, the module will be called adt7x10.
> +
> +config SENSORS_ADT7310
> +	tristate "Analog Devices ADT7310/ADT7320"
> +	depends on SPI_MASTER
> +	select SENSORS_ADT7X10
> +	help
> +	  If you say yes here you get support for the Analog Devices
> +	  ADT7310 and ADT7320 temperature monitoring chips.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called adt7310.
> +
>  config SENSORS_ADT7410
>  	tristate "Analog Devices ADT7410/ADT7420"
>  	depends on I2C
> +	select SENSORS_ADT7X10
>  	help
>  	  If you say yes here you get support for the Analog Devices
>  	  ADT7410 and ADT7420 temperature monitoring chips.
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 8d6d97e..5d36a57 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
>  obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
>  obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
>  obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
> +obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
> +obj-$(CONFIG_SENSORS_ADT7310)	+= adt7310.o
>  obj-$(CONFIG_SENSORS_ADT7410)	+= adt7410.o
>  obj-$(CONFIG_SENSORS_ADT7411)	+= adt7411.o
>  obj-$(CONFIG_SENSORS_ADT7462)	+= adt7462.o
> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> new file mode 100644
> index 0000000..2196ac3
> --- /dev/null
> +++ b/drivers/hwmon/adt7310.c
> @@ -0,0 +1,115 @@
> +/*
> + * ADT7310/ADT7310 digital temperature sensor driver
> + *
> + * Copyright 2010-2013 Analog Devices Inc.

Not really; copyright is yours, unless you are signing it off to analog or if
you are working for them and have permission (or the duty) to sign off the
copyright. Even then it should be 2013 only for this file.

> + *   Author: Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/spi/spi.h>
> +#include <asm/unaligned.h>
> +
> +#include "adt7x10.h"
> +
> +static const u8 adt7310_reg_table[] = {
> +	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
> +	[ADT7410_STATUS]	= ADT7310_STATUS,
> +	[ADT7410_CONFIG]	= ADT7310_CONFIG,
> +	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
> +	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
> +	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
> +	[ADT7410_T_HYST]	= ADT7310_T_HYST,
> +	[ADT7410_ID]		= ADT7310_ID,
> +};
> +
> +#define ADT7310_CMD_REG_OFFSET	3
> +#define ADT7310_CMD_READ	0x40
> +
> +#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> +
> +static int adt7310_spi_read_word(struct device *dev, u8 reg)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	int ret;
> +
> +	ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
> +	if (ret < 0)
> +		return ret;
> +
> +	return be16_to_cpu(ret);
> +}
> +
> +static int adt7310_spi_write_word(struct device *dev, u8 reg,
> +	u16 data)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	u8 buf[3];
> +
> +	buf[0] = AD7310_COMMAND(reg);
> +	put_unaligned_be16(data, &buf[1]);
> +
> +	return spi_write(spi, buf, sizeof(buf));
> +}
> +
> +static int adt7310_spi_read_byte(struct device *dev, u8 reg)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +
> +	return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
> +}
> +
> +static int adt7310_spi_write_byte(struct device *dev, u8 reg,
> +	u8 data)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	u8 buf[2];
> +
> +	buf[0] = AD7310_COMMAND(reg);
> +	buf[1] = data;
> +
> +	return spi_write(spi, buf, sizeof(buf));
> +}
> +
> +static const struct adt7410_ops adt7310_spi_ops = {
> +	.read_word = adt7310_spi_read_word,
> +	.write_word = adt7310_spi_write_word,
> +	.read_byte = adt7310_spi_read_byte,
> +	.write_byte = adt7310_spi_write_byte,
> +};
> +
> +static int adt7310_spi_probe(struct spi_device *spi)
> +{
> +	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
> +			&adt7310_spi_ops);
> +}
> +
> +static int adt7310_spi_remove(struct spi_device *spi)
> +{
> +	return adt7410_remove(&spi->dev);
> +}
> +
> +static const struct spi_device_id adt7310_id[] = {
> +	{ "adt7310", 0 },
> +	{ "adt7320", 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(spi, adt7310_id);
> +
> +static struct spi_driver adt7310_driver = {
> +	.driver = {
> +		.name = "adt7310",
> +		.owner = THIS_MODULE,
> +		.pm	= ADT7410_DEV_PM_OPS,

Should probably be named ADT7X10_DEV_PM_OPS to avoid confusion.

> +	},
> +	.probe = adt7310_spi_probe,
> +	.remove = adt7310_spi_remove,
> +	.id_table = adt7310_id,
> +};
> +module_spi_driver(adt7310_driver);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index b6acfa4..b500ab3 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -1,485 +1,81 @@
>  /*
> - * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
> - *	 monitoring
> - * This driver handles the ADT7410 and compatible digital temperature sensors.
> - * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
> - * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
> - * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
> + * ADT7410/ADT7420 digital temperature sensor driver
>   *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> + * Copyright 2010-2013 Analog Devices Inc.
> + *   Author: Lars-Peter Clausen <lars@metafoo.de>

Same as before.

>   *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + * Licensed under the GPL-2 or later.
>   */
>  
>  #include <linux/module.h>
>  #include <linux/init.h>
> -#include <linux/slab.h>
> -#include <linux/jiffies.h>
>  #include <linux/i2c.h>
> -#include <linux/hwmon.h>
> -#include <linux/hwmon-sysfs.h>
> -#include <linux/err.h>
> -#include <linux/mutex.h>
> -#include <linux/delay.h>
> -
> -/*
> - * ADT7410 registers definition
> - */
> -
> -#define ADT7410_TEMPERATURE		0
> -#define ADT7410_STATUS			2
> -#define ADT7410_CONFIG			3
> -#define ADT7410_T_ALARM_HIGH		4
> -#define ADT7410_T_ALARM_LOW		6
> -#define ADT7410_T_CRIT			8
> -#define ADT7410_T_HYST			0xA
> -
> -/*
> - * ADT7410 status
> - */
> -#define ADT7410_STAT_T_LOW		(1 << 4)
> -#define ADT7410_STAT_T_HIGH		(1 << 5)
> -#define ADT7410_STAT_T_CRIT		(1 << 6)
> -#define ADT7410_STAT_NOT_RDY		(1 << 7)
>  
> -/*
> - * ADT7410 config
> - */
> -#define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
> -#define ADT7410_CT_POLARITY		(1 << 2)
> -#define ADT7410_INT_POLARITY		(1 << 3)
> -#define ADT7410_EVENT_MODE		(1 << 4)
> -#define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
> -#define ADT7410_FULL			(0 << 5 | 0 << 6)
> -#define ADT7410_PD			(1 << 5 | 1 << 6)
> -#define ADT7410_RESOLUTION		(1 << 7)
> +#include "adt7x10.h"
>  
> -/*
> - * ADT7410 masks
> - */
> -#define ADT7410_T13_VALUE_MASK			0xFFF8
> -#define ADT7410_T_HYST_MASK			0xF
> -
> -/* straight from the datasheet */
> -#define ADT7410_TEMP_MIN (-55000)
> -#define ADT7410_TEMP_MAX 150000
> -
> -enum adt7410_type {		/* keep sorted in alphabetical order */
> -	adt7410,
> -};
> -
> -static const u8 ADT7410_REG_TEMP[4] = {
> -	ADT7410_TEMPERATURE,		/* input */
> -	ADT7410_T_ALARM_HIGH,		/* high */
> -	ADT7410_T_ALARM_LOW,		/* low */
> -	ADT7410_T_CRIT,			/* critical */
> -};
> -
> -/* Each client has this additional data */
> -struct adt7410_data {
> -	struct device		*hwmon_dev;
> -	struct mutex		update_lock;
> -	u8			config;
> -	u8			oldconfig;
> -	bool			valid;		/* true if registers valid */
> -	unsigned long		last_updated;	/* In jiffies */
> -	s16			temp[4];	/* Register values,
> -						   0 = input
> -						   1 = high
> -						   2 = low
> -						   3 = critical */
> -	u8			hyst;		/* hysteresis offset */
> -};
> -
> -/*
> - * adt7410 register access by I2C
> - */
> -static int adt7410_temp_ready(struct i2c_client *client)
> +static int adt7410_i2c_read_word(struct device *dev, u8 reg)
>  {
> -	int i, status;
> -
> -	for (i = 0; i < 6; i++) {
> -		status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
> -		if (status < 0)
> -			return status;
> -		if (!(status & ADT7410_STAT_NOT_RDY))
> -			return 0;
> -		msleep(60);
> -	}
> -	return -ETIMEDOUT;
> +	return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg);
>  }
>  
> -static int adt7410_update_temp(struct device *dev)
> +static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int ret = 0;
> -
> -	mutex_lock(&data->update_lock);
> -
> -	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
> -	    || !data->valid) {
> -		int temp;
> -
> -		dev_dbg(&client->dev, "Starting update\n");
> -
> -		ret = adt7410_temp_ready(client); /* check for new value */
> -		if (ret)
> -			goto abort;
> -
> -		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
> -		if (temp < 0) {
> -			ret = temp;
> -			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> -				ADT7410_REG_TEMP[0], ret);
> -			goto abort;
> -		}
> -		data->temp[0] = temp;
> -
> -		data->last_updated = jiffies;
> -		data->valid = true;
> -	}
> -
> -abort:
> -	mutex_unlock(&data->update_lock);
> -	return ret;
> -}
> -
> -static int adt7410_fill_cache(struct i2c_client *client)
> -{
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int ret;
> -	int i;
> -
> -	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
> -		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> -		if (ret < 0) {
> -			dev_dbg(&client->dev,
> -				"Failed to read value: reg %d, error %d\n",
> -				ADT7410_REG_TEMP[0], ret);
> -			return ret;
> -		}
> -		data->temp[i] = ret;
> -	}
> -
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> -	if (ret < 0) {
> -		dev_dbg(&client->dev,
> -			"Failed to read value: hyst reg, error %d\n",
> -			ret);
> -		return ret;
> -	}
> -	data->hyst = ret;
> -
> -	return 0;
> +	return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data);
>  }
>  
> -static s16 ADT7410_TEMP_TO_REG(long temp)
> +static int adt7410_i2c_read_byte(struct device *dev, u8 reg)
>  {
> -	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
> -					   ADT7410_TEMP_MAX) * 128, 1000);
> +	return i2c_smbus_read_byte_data(to_i2c_client(dev), reg);
>  }
>  
> -static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
> +static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
>  {
> -	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
> -	if (!(data->config & ADT7410_RESOLUTION))
> -		reg &= ADT7410_T13_VALUE_MASK;
> -	/*
> -	 * temperature is stored in twos complement format, in steps of
> -	 * 1/128°C
> -	 */
> -	return DIV_ROUND_CLOSEST(reg * 1000, 128);
> +	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);
>  }
>  
> -/*-----------------------------------------------------------------------*/
> -
> -/* sysfs attributes for hwmon */
> -
> -static ssize_t adt7410_show_temp(struct device *dev,
> -				 struct device_attribute *da, char *buf)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	if (attr->index == 0) {
> -		int ret;
> -
> -		ret = adt7410_update_temp(dev);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
> -		       data->temp[attr->index]));
> -}
> -
> -static ssize_t adt7410_set_temp(struct device *dev,
> -				struct device_attribute *da,
> -				const char *buf, size_t count)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int nr = attr->index;
> -	long temp;
> -	int ret;
> -
> -	ret = kstrtol(buf, 10, &temp);
> -	if (ret)
> -		return ret;
> -
> -	mutex_lock(&data->update_lock);
> -	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
> -	ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
> -					   data->temp[nr]);
> -	if (ret)
> -		count = ret;
> -	mutex_unlock(&data->update_lock);
> -	return count;
> -}
> -
> -static ssize_t adt7410_show_t_hyst(struct device *dev,
> -				   struct device_attribute *da,
> -				   char *buf)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int nr = attr->index;
> -	int hyst;
> -
> -	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
> -
> -	/*
> -	 * hysteresis is stored as a 4 bit offset in the device, convert it
> -	 * to an absolute value
> -	 */
> -	if (nr == 2)	/* min has positive offset, others have negative */
> -		hyst = -hyst;
> -	return sprintf(buf, "%d\n",
> -		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
> -}
> -
> -static ssize_t adt7410_set_t_hyst(struct device *dev,
> -				  struct device_attribute *da,
> -				  const char *buf, size_t count)
> -{
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int limit, ret;
> -	long hyst;
> -
> -	ret = kstrtol(buf, 10, &hyst);
> -	if (ret)
> -		return ret;
> -	/* convert absolute hysteresis value to a 4 bit delta value */
> -	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
> -	hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
> -	data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
> -			       ADT7410_T_HYST_MASK);
> -	ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
> -	if (ret)
> -		return ret;
> -
> -	return count;
> -}
> -
> -static ssize_t adt7410_show_alarm(struct device *dev,
> -				  struct device_attribute *da,
> -				  char *buf)
> -{
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	int ret;
> -
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
> -	if (ret < 0)
> -		return ret;
> -
> -	return sprintf(buf, "%d\n", !!(ret & attr->index));
> -}
> -
> -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
> -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
> -			  adt7410_show_temp, adt7410_set_temp, 1);
> -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
> -			  adt7410_show_temp, adt7410_set_temp, 2);
> -static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
> -			  adt7410_show_temp, adt7410_set_temp, 3);
> -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
> -			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
> -static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
> -			  adt7410_show_t_hyst, NULL, 2);
> -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
> -			  adt7410_show_t_hyst, NULL, 3);
> -static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
> -			  NULL, ADT7410_STAT_T_LOW);
> -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
> -			  NULL, ADT7410_STAT_T_HIGH);
> -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
> -			  NULL, ADT7410_STAT_T_CRIT);
> -
> -static struct attribute *adt7410_attributes[] = {
> -	&sensor_dev_attr_temp1_input.dev_attr.attr,
> -	&sensor_dev_attr_temp1_max.dev_attr.attr,
> -	&sensor_dev_attr_temp1_min.dev_attr.attr,
> -	&sensor_dev_attr_temp1_crit.dev_attr.attr,
> -	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> -	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
> -	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
> -	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
> -	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
> -	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
> -	NULL
> -};
> -
> -static const struct attribute_group adt7410_group = {
> -	.attrs = adt7410_attributes,
> +static const struct adt7410_ops adt7410_i2c_ops = {
> +	.read_word = adt7410_i2c_read_word,
> +	.write_word = adt7410_i2c_write_word,
> +	.read_byte = adt7410_i2c_read_byte,
> +	.write_byte = adt7410_i2c_write_byte,
>  };
>  
> -/*-----------------------------------------------------------------------*/
> -
> -/* device probe and removal */
> -
> -static int adt7410_probe(struct i2c_client *client,
> -			 const struct i2c_device_id *id)
> +static int adt7410_i2c_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
>  {
> -	struct adt7410_data *data;
> -	int ret;
>  
>  	if (!i2c_check_functionality(client->adapter,
>  			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
>  		return -ENODEV;
>  
> -	data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
> -			    GFP_KERNEL);
> -	if (!data)
> -		return -ENOMEM;
> -
> -	i2c_set_clientdata(client, data);
> -	mutex_init(&data->update_lock);
> -
> -	/* configure as specified */
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
> -	if (ret < 0) {
> -		dev_dbg(&client->dev, "Can't read config? %d\n", ret);
> -		return ret;
> -	}
> -	data->oldconfig = ret;
> -	/*
> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
> -	 */
> -	ret &= ~ADT7410_MODE_MASK;
> -	data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
> -			ADT7410_EVENT_MODE;
> -	if (data->config != data->oldconfig) {
> -		ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
> -						data->config);
> -		if (ret)
> -			return ret;
> -	}
> -	dev_dbg(&client->dev, "Config %02x\n", data->config);
> -
> -	ret = adt7410_fill_cache(client);
> -	if (ret)
> -		goto exit_restore;
> -
> -	/* Register sysfs hooks */
> -	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
> -	if (ret)
> -		goto exit_restore;
> -
> -	data->hwmon_dev = hwmon_device_register(&client->dev);
> -	if (IS_ERR(data->hwmon_dev)) {
> -		ret = PTR_ERR(data->hwmon_dev);
> -		goto exit_remove;
> -	}
> -
> -	dev_info(&client->dev, "sensor '%s'\n", client->name);
> -
> -	return 0;
> -
> -exit_remove:
> -	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
> -exit_restore:
> -	i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
> -	return ret;
> +	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
>  }
>  
> -static int adt7410_remove(struct i2c_client *client)
> +static int adt7410_i2c_remove(struct i2c_client *client)
>  {
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	hwmon_device_unregister(data->hwmon_dev);
> -	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
> -	if (data->oldconfig != data->config)
> -		i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
> -					  data->oldconfig);
> -	return 0;
> +	return adt7410_remove(&client->dev);
>  }
>  
>  static const struct i2c_device_id adt7410_ids[] = {
> -	{ "adt7410", adt7410, },
> -	{ "adt7420", adt7410, },
> -	{ /* LIST END */ }
> +	{ "adt7410", 0 },
> +	{ "adt7420", 0 },
> +	{}
>  };
>  MODULE_DEVICE_TABLE(i2c, adt7410_ids);
>  
> -#ifdef CONFIG_PM_SLEEP
> -static int adt7410_suspend(struct device *dev)
> -{
> -	int ret;
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
> -					data->config | ADT7410_PD);
> -	return ret;
> -}
> -
> -static int adt7410_resume(struct device *dev)
> -{
> -	int ret;
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
> -	return ret;
> -}
> -
> -static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
> -
> -#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
> -#else
> -#define ADT7410_DEV_PM_OPS NULL
> -#endif /* CONFIG_PM */
> -
>  static struct i2c_driver adt7410_driver = {
>  	.class		= I2C_CLASS_HWMON,
>  	.driver = {
>  		.name	= "adt7410",
>  		.pm	= ADT7410_DEV_PM_OPS,
>  	},
> -	.probe		= adt7410_probe,
> -	.remove		= adt7410_remove,
> +	.probe		= adt7410_i2c_probe,
> +	.remove		= adt7410_i2c_remove,
>  	.id_table	= adt7410_ids,
>  	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
>  };
> -
>  module_i2c_driver(adt7410_driver);
>  
> -MODULE_AUTHOR("Hartmut Knaack");
> -MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("ADT7410/AD7420 driver");
>  MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
> new file mode 100644
> index 0000000..eeff198c
> --- /dev/null
> +++ b/drivers/hwmon/adt7x10.c
> @@ -0,0 +1,476 @@
> +/*
> + * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware

Needs file name and purpose update

> + *	 monitoring
> + * This driver handles the ADT7410 and compatible digital temperature sensors.
> + * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
> + * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
> + * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +
> +#include "adt7x10.h"
> +
> +/*
> + * ADT7410 status
> + */
> +#define ADT7410_STAT_T_LOW		(1 << 4)
> +#define ADT7410_STAT_T_HIGH		(1 << 5)
> +#define ADT7410_STAT_T_CRIT		(1 << 6)
> +#define ADT7410_STAT_NOT_RDY		(1 << 7)
> +
> +/*
> + * ADT7410 config
> + */
> +#define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
> +#define ADT7410_CT_POLARITY		(1 << 2)
> +#define ADT7410_INT_POLARITY		(1 << 3)
> +#define ADT7410_EVENT_MODE		(1 << 4)
> +#define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
> +#define ADT7410_FULL			(0 << 5 | 0 << 6)
> +#define ADT7410_PD			(1 << 5 | 1 << 6)
> +#define ADT7410_RESOLUTION		(1 << 7)
> +
> +/*
> + * ADT7410 masks
> + */
> +#define ADT7410_T13_VALUE_MASK			0xFFF8
> +#define ADT7410_T_HYST_MASK			0xF
> +
> +/* straight from the datasheet */
> +#define ADT7410_TEMP_MIN (-55000)
> +#define ADT7410_TEMP_MAX 150000
> +
> +/* Each client has this additional data */
> +struct adt7410_data {
> +	const struct adt7410_ops *ops;
> +	const char		*name;
> +	struct device		*hwmon_dev;
> +	struct mutex		update_lock;
> +	u8			config;
> +	u8			oldconfig;
> +	bool			valid;		/* true if registers valid */
> +	unsigned long		last_updated;	/* In jiffies */
> +	s16			temp[4];	/* Register values,
> +						   0 = input
> +						   1 = high
> +						   2 = low
> +						   3 = critical */
> +	u8			hyst;		/* hysteresis offset */
> +};
> +
> +static int adt7410_read_byte(struct device *dev, u8 reg)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_byte(dev, reg);
> +}
> +
> +static int adt7410_write_byte(struct device *dev, u8 reg, u8 data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->write_byte(dev, reg, data);
> +}
> +
> +static int adt7410_read_word(struct device *dev, u8 reg)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_word(dev, reg);
> +}
> +
> +static int adt7410_write_word(struct device *dev, u8 reg, u16 data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->write_word(dev, reg, data);
> +}
> +
> +static const u8 ADT7410_REG_TEMP[4] = {
> +	ADT7410_TEMPERATURE,		/* input */
> +	ADT7410_T_ALARM_HIGH,		/* high */
> +	ADT7410_T_ALARM_LOW,		/* low */
> +	ADT7410_T_CRIT,			/* critical */
> +};
> +
> +static int adt7410_temp_ready(struct device *dev)
> +{
> +	int i, status;
> +
> +	for (i = 0; i < 6; i++) {
> +		status = adt7410_read_byte(dev, ADT7410_STATUS);
> +		if (status < 0)
> +			return status;
> +		if (!(status & ADT7410_STAT_NOT_RDY))
> +			return 0;
> +		msleep(60);
> +	}
> +	return -ETIMEDOUT;
> +}
> +
> +static int adt7410_update_temp(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	mutex_lock(&data->update_lock);
> +
> +	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
> +	    || !data->valid) {
> +		int temp;
> +
> +		dev_dbg(dev, "Starting update\n");
> +
> +		ret = adt7410_temp_ready(dev); /* check for new value */
> +		if (ret)
> +			goto abort;
> +
> +		temp = adt7410_read_word(dev, ADT7410_REG_TEMP[0]);
> +		if (temp < 0) {
> +			ret = temp;
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			goto abort;
> +		}
> +		data->temp[0] = temp;
> +		data->last_updated = jiffies;
> +		data->valid = true;
> +	}
> +
> +abort:
> +	mutex_unlock(&data->update_lock);
> +	return ret;
> +}
> +
> +static int adt7410_fill_cache(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int ret;
> +	int i;
> +
> +	for (i = 1; i < ARRAY_SIZE(data->temp); i++) {
> +		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[i]);
> +		if (ret < 0) {
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);

ADT7410_REG_TEMP[i] as Hartmut pointed out

> +			return ret;
> +		}
> +		data->temp[i] = ret;
> +	}
> +
> +	ret = adt7410_read_byte(dev, ADT7410_T_HYST);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_T_HYST, ret);
> +		return ret;
> +	}
> +	data->hyst = ret;
> +
> +	return 0;
> +}
> +
> +static s16 ADT7410_TEMP_TO_REG(long temp)
> +{
> +	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
> +					       ADT7410_TEMP_MAX) * 128, 1000);
> +}
> +
> +static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
> +{
> +	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
> +	if (!(data->config & ADT7410_RESOLUTION))
> +		reg &= ADT7410_T13_VALUE_MASK;
> +	/*
> +	 * temperature is stored in twos complement format, in steps of
> +	 * 1/128°C
> +	 */
> +	return DIV_ROUND_CLOSEST(reg * 1000, 128);
> +}
> +
> +/*-----------------------------------------------------------------------*/
> +
> +/* sysfs attributes for hwmon */
> +
> +static ssize_t adt7410_show_temp(struct device *dev,
> +				 struct device_attribute *da, char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +
> +	if (attr->index == 0) {
> +		int ret;
> +
> +		ret = adt7410_update_temp(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
> +		       data->temp[attr->index]));
> +}
> +
> +static ssize_t adt7410_set_temp(struct device *dev,
> +				struct device_attribute *da,
> +				const char *buf, size_t count)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int nr = attr->index;
> +	long temp;
> +	int ret;
> +
> +	ret = kstrtol(buf, 10, &temp);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&data->update_lock);
> +	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
> +	ret = adt7410_write_word(dev, ADT7410_REG_TEMP[nr], data->temp[nr]);
> +	if (ret)
> +		count = ret;
> +	mutex_unlock(&data->update_lock);
> +	return count;
> +}
> +
> +static ssize_t adt7410_show_t_hyst(struct device *dev,
> +				   struct device_attribute *da,
> +				   char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int nr = attr->index;
> +	int hyst;
> +
> +	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
> +
> +	/*
> +	 * hysteresis is stored as a 4 bit offset in the device, convert it
> +	 * to an absolute value
> +	 */
> +	if (nr == 2)	/* min has positive offset, others have negative */
> +		hyst = -hyst;
> +	return sprintf(buf, "%d\n",
> +		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
> +}
> +
> +static ssize_t adt7410_set_t_hyst(struct device *dev,
> +				  struct device_attribute *da,
> +				  const char *buf, size_t count)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int limit, ret;
> +	long hyst;
> +
> +	ret = kstrtol(buf, 10, &hyst);
> +	if (ret)
> +		return ret;
> +	/* convert absolute hysteresis value to a 4 bit delta value */
> +	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
> +	hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
> +	data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000),
> +				   0, ADT7410_T_HYST_MASK);
> +	ret = adt7410_write_byte(dev, ADT7410_T_HYST, data->hyst);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t adt7410_show_alarm(struct device *dev,
> +				  struct device_attribute *da,
> +				  char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	int ret;
> +
> +	ret = adt7410_read_byte(dev, ADT7410_STATUS);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", !!(ret & attr->index));
> +}
> +
> +static ssize_t adt7410_show_name(struct device *dev,
> +				  struct device_attribute *da, char *buf)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%s\n", data->name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
> +			  adt7410_show_temp, adt7410_set_temp, 1);
> +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
> +			  adt7410_show_temp, adt7410_set_temp, 2);
> +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
> +			  adt7410_show_temp, adt7410_set_temp, 3);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
> +			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
> +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
> +			  adt7410_show_t_hyst, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
> +			  adt7410_show_t_hyst, NULL, 3);
> +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
> +			  NULL, ADT7410_STAT_T_LOW);
> +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
> +			  NULL, ADT7410_STAT_T_HIGH);
> +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
> +			  NULL, ADT7410_STAT_T_CRIT);
> +static DEVICE_ATTR(name, S_IRUGO, adt7410_show_name, NULL);
> +
> +static struct attribute *adt7410_attributes[] = {
> +	&sensor_dev_attr_temp1_input.dev_attr.attr,
> +	&sensor_dev_attr_temp1_max.dev_attr.attr,
> +	&sensor_dev_attr_temp1_min.dev_attr.attr,
> +	&sensor_dev_attr_temp1_crit.dev_attr.attr,
> +	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> +	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
> +	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
> +	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
> +	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
> +	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adt7410_group = {
> +	.attrs = adt7410_attributes,
> +};
> +
> +int adt7410_probe(struct device *dev, const char *name,
> +	const struct adt7410_ops *ops)

Preferred alignment is to have the second line start at the (. Usually I don't
complain but since you'll have to do another rev anyway maybe you can fix this
whereever it occurs.

> +{
> +	struct adt7410_data *data;
> +	int ret;
> +
> +	data = devm_kzalloc(dev, sizeof(struct adt7410_data),
> +			    GFP_KERNEL);

No need to split the line above.

> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->ops = ops;
> +	data->name = name;
> +
> +	dev_set_drvdata(dev, data);
> +	mutex_init(&data->update_lock);
> +
> +	/* configure as specified */
> +	ret = adt7410_read_byte(dev, ADT7410_CONFIG);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Can't read config? %d\n", ret);
> +		return ret;
> +	}
> +	data->oldconfig = ret;
> +
> +	/*
> +	 * Set to 16 bit resolution, continous conversion and comparator mode.
> +	 */
> +	data->config = data->oldconfig;
> +	data->config &= ~ADT7410_MODE_MASK;
> +	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
> +	if (data->config != data->oldconfig) {
> +		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
> +		if (ret)
> +			return ret;
> +	}
> +	dev_dbg(dev, "Config %02x\n", data->config);
> +
> +	ret = adt7410_fill_cache(dev);
> +	if (ret)
> +		goto exit_restore;
> +
> +	/* Register sysfs hooks */
> +	ret = sysfs_create_group(&dev->kobj, &adt7410_group);
> +	if (ret)
> +		goto exit_restore;
> +
> +	/*
> +	 * The I2C device will already have it's own 'name' attribute, but for
> +	 * the SPI device we need to register it. name will only be non NULL if
> +	 * the device doesn't register the 'name' attribute on its own.
> +	 */
> +	if (name) {
> +		ret = device_create_file(dev, &dev_attr_name);
> +		if (ret)
> +			goto exit_remove;
> +	}
> +
> +	data->hwmon_dev = hwmon_device_register(dev);
> +	if (IS_ERR(data->hwmon_dev)) {
> +		ret = PTR_ERR(data->hwmon_dev);
> +		goto exit_remove_name;
> +	}
> +
> +	return 0;
> +
> +exit_remove_name:
> +	if (name)
> +		device_remove_file(dev, &dev_attr_name);
> +exit_remove:
> +	sysfs_remove_group(&dev->kobj, &adt7410_group);
> +exit_restore:
> +	adt7410_write_byte(dev, ADT7410_CONFIG, data->oldconfig);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(adt7410_probe);
> +
> +int adt7410_remove(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	hwmon_device_unregister(data->hwmon_dev);
> +	if (data->name)
> +		device_remove_file(dev, &dev_attr_name);
> +	sysfs_remove_group(&dev->kobj, &adt7410_group);
> +	if (data->oldconfig != data->config)
> +		adt7410_write_byte(dev, ADT7410_CONFIG,
> +					  data->oldconfig);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(adt7410_remove);
> +
> +#ifdef CONFIG_PM_SLEEP
> +
> +static int adt7410_suspend(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	return adt7410_write_byte(dev, ADT7410_CONFIG,
> +		data->config | ADT7410_PD);
> +}
> +
> +static int adt7410_resume(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	return adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
> +}
> +
> +SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
> +EXPORT_SYMBOL_GPL(adt7410_dev_pm_ops);
> +
> +#endif /* CONFIG_PM_SLEEP */
> +
> +MODULE_AUTHOR("Hartmut Knaack");
> +MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> new file mode 100644
> index 0000000..a7165e6
> --- /dev/null
> +++ b/drivers/hwmon/adt7x10.h
> @@ -0,0 +1,48 @@
> +#ifndef __HWMON_ADT7X10_H__
> +#define __HWMON_ADT7X10_H__
> +
> +#include <linux/types.h>
> +#include <linux/pm.h>
> +
> +/* ADT7410 registers definition */
> +#define ADT7410_TEMPERATURE		0
> +#define ADT7410_STATUS			2
> +#define ADT7410_CONFIG			3
> +#define ADT7410_T_ALARM_HIGH		4
> +#define ADT7410_T_ALARM_LOW		6
> +#define ADT7410_T_CRIT			8
> +#define ADT7410_T_HYST			0xA
> +#define ADT7410_ID			0xB
> +
> +/* ADT7310 registers definition */
> +#define ADT7310_STATUS			0
> +#define ADT7310_CONFIG			1
> +#define ADT7310_TEMPERATURE		2
> +#define ADT7310_ID			3
> +#define ADT7310_T_CRIT			4
> +#define ADT7310_T_HYST			5
> +#define ADT7310_T_ALARM_HIGH		6
> +#define ADT7310_T_ALARM_LOW		7
> +
> +struct device;
> +
> +struct adt7410_ops {
> +	int (*read_byte)(struct device *, u8 reg);
> +	int (*write_byte)(struct device *, u8 reg, u8 data);
> +	int (*read_word)(struct device *, u8 reg);
> +	int (*write_word)(struct device *, u8 reg, u16 data);
> +};
> +
> +int adt7410_probe(struct device *dev, const char *name,
> +	const struct adt7410_ops *ops);
> +int adt7410_remove(struct device *dev);
> +

I think the above should be adt7x10_ops, adt7x10_probe and adt7x10_remove, to
indicate that those are common functions.

I would actually suggest to rename all functions in adt7x10.c (not just the
exported ones).

> +
> +#ifdef CONFIG_PM_SLEEP
> +extern const struct dev_pm_ops adt7410_dev_pm_ops;
> +#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)

As mentioned above, I think ADT7X10_DEV_PM_OPS would be better here.

> +#else
> +#define ADT7410_DEV_PM_OPS NULL
> +#endif
> +
> +#endif
> -- 
> 1.8.0
> 
> 

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

* Re: [lm-sensors] [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
@ 2013-02-19  1:30     ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:30 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Mon, Feb 18, 2013 at 02:38:57PM +0100, Lars-Peter Clausen wrote:
> The adt7310/adt7320 is the SPI version of the adt7410/adt7420. The register map
> layout is a bit different, i.e. the register addresses differ between the two
> variants, but the bit layouts of the individual registers are identical. So both
> chip variants can easily be supported by the same driver. The issue of non
> matching register address layouts is solved by a simple look-up table which
> translates the I2C addresses to the SPI addresses.
> 
> The patch moves the bulk of the adt7410 driver to a common module that will be
> shared by the adt7410 and adt7310 drivers. This common module implements the
> driver logic and uses a set of virtual functions to perform IO access. The
> adt7410 and adt7310 driver modules provide proper implementations of these IO
> accessor functions for I2C respective SPI.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> 
> ---
> Changes since v1:
> 	* Update the driver documentation to include ADT7310/ADT7320/ADT7420
> 	* Pass the result of the read methods via the return value instead of a
> 	  pointer argument
> 	* Simplify spi read methods by using spi_w8r8 and spi_w8r16
> 	* Update module description of the shared module
> 	* Fix some typos
> ---
>  Documentation/hwmon/adt7410 |  42 ++--
>  drivers/hwmon/Kconfig       |  20 ++
>  drivers/hwmon/Makefile      |   2 +
>  drivers/hwmon/adt7310.c     | 115 +++++++++++
>  drivers/hwmon/adt7410.c     | 464 +++---------------------------------------
>  drivers/hwmon/adt7x10.c     | 476 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/adt7x10.h     |  48 +++++
>  7 files changed, 720 insertions(+), 447 deletions(-)
>  create mode 100644 drivers/hwmon/adt7310.c
>  create mode 100644 drivers/hwmon/adt7x10.c
>  create mode 100644 drivers/hwmon/adt7x10.h
> 
> diff --git a/Documentation/hwmon/adt7410 b/Documentation/hwmon/adt7410
> index 9600400..e452ae0 100644
> --- a/Documentation/hwmon/adt7410
> +++ b/Documentation/hwmon/adt7410
> @@ -7,25 +7,41 @@ Supported chips:
>      Addresses scanned: I2C 0x48 - 0x4B
>      Datasheet: Publicly available at the Analog Devices website
>                 http://www.analog.com/static/imported-files/data_sheets/ADT7410.pdf
> +  * Analog Devices ADT7420
> +    Prefix: 'adt7420'
> +    Addresses scanned: I2C 0x48 - 0x4B
> +    Datasheet: Publicly available at the Analog Devices website
> +               http://www.analog.com/static/imported-files/data_sheets/ADT7420.pdf
> +  * Analog Devices ADT7310
> +    Prefix: 'adt7310'
> +    Addresses scanned: I2C 0x48 - 0x4B
> +    Datasheet: Publicly available at the Analog Devices website
> +               http://www.analog.com/static/imported-files/data_sheets/ADT7310.pdf
> +  * Analog Devices ADT7320
> +    Prefix: 'adt7320'
> +    Addresses scanned: I2C 0x48 - 0x4B
> +    Datasheet: Publicly available at the Analog Devices website
> +               http://www.analog.com/static/imported-files/data_sheets/ADT7320.pdf
>  
>  Author: Hartmut Knaack <knaack.h@gmx.de>
>  
>  Description
>  -----------
>  
> -The ADT7410 is a temperature sensor with rated temperature range of -55°C to
> -+150°C. It has a high accuracy of +/-0.5°C and can be operated at a resolution
> -of 13 bits (0.0625°C) or 16 bits (0.0078°C). The sensor provides an INT pin to
> -indicate that a minimum or maximum temperature set point has been exceeded, as
> -well as a critical temperature (CT) pin to indicate that the critical
> -temperature set point has been exceeded. Both pins can be set up with a common
> -hysteresis of 0°C - 15°C and a fault queue, ranging from 1 to 4 events. Both
> -pins can individually set to be active-low or active-high, while the whole
> -device can either run in comparator mode or interrupt mode. The ADT7410
> -supports continous temperature sampling, as well as sampling one temperature
> -value per second or even justget one sample on demand for power saving.
> -Besides, it can completely power down its ADC, if power management is
> -required.
> +The ADT7410 and similar are a temperature sensors with rated temperature range
> +of -55°C to +150°C (ADT7310/ADT7410) or -40°C to +150°C (ADT7320/ADT7420). They
> +have a high accuracy of +/-0.5°C (ADT7310/ADT7410) or +/-0.2C (ADT7430/ADT7420)
> +and can be operated at a resolution of 13 bits (0.0625°C) or 16 bits (0.0078°C).
> +The sensor provides an INT pin to indicate that a minimum or maximum temperature
> +set point has been exceeded, as well as a critical temperature (CT) pin to
> +indicate that the critical temperature set point has been exceeded. Both pins
> +can be set up with a common hysteresis of 0°C - 15°C and a fault queue, ranging
> +from 1 to 4 events. Both pins can individually set to be active-low or
> +active-high, while the whole device can either run in comparator mode or
> +interrupt mode. The ADT7410 supports continous temperature sampling, as well as
> +sampling one temperature value per second or even justget one sample on demand
> +for power saving.  Besides, it can completely power down its ADC, if power
> +management is required.
>  
>  Configuration Notes
>  -------------------
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 89ac1cb..aaa14f4 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -179,9 +179,29 @@ config SENSORS_ADM9240
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called adm9240.
>  
> +config SENSORS_ADT7X10
> +	tristate
> +	help
> +	  This module contains common code shared by the ADT7310/ADT7320 and
> +	  ADT7410/ADT7420 temperature monitoring chip drivers.
> +
> +	  If build as a module, the module will be called adt7x10.
> +
> +config SENSORS_ADT7310
> +	tristate "Analog Devices ADT7310/ADT7320"
> +	depends on SPI_MASTER
> +	select SENSORS_ADT7X10
> +	help
> +	  If you say yes here you get support for the Analog Devices
> +	  ADT7310 and ADT7320 temperature monitoring chips.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called adt7310.
> +
>  config SENSORS_ADT7410
>  	tristate "Analog Devices ADT7410/ADT7420"
>  	depends on I2C
> +	select SENSORS_ADT7X10
>  	help
>  	  If you say yes here you get support for the Analog Devices
>  	  ADT7410 and ADT7420 temperature monitoring chips.
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 8d6d97e..5d36a57 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
>  obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
>  obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
>  obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
> +obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
> +obj-$(CONFIG_SENSORS_ADT7310)	+= adt7310.o
>  obj-$(CONFIG_SENSORS_ADT7410)	+= adt7410.o
>  obj-$(CONFIG_SENSORS_ADT7411)	+= adt7411.o
>  obj-$(CONFIG_SENSORS_ADT7462)	+= adt7462.o
> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> new file mode 100644
> index 0000000..2196ac3
> --- /dev/null
> +++ b/drivers/hwmon/adt7310.c
> @@ -0,0 +1,115 @@
> +/*
> + * ADT7310/ADT7310 digital temperature sensor driver
> + *
> + * Copyright 2010-2013 Analog Devices Inc.

Not really; copyright is yours, unless you are signing it off to analog or if
you are working for them and have permission (or the duty) to sign off the
copyright. Even then it should be 2013 only for this file.

> + *   Author: Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/spi/spi.h>
> +#include <asm/unaligned.h>
> +
> +#include "adt7x10.h"
> +
> +static const u8 adt7310_reg_table[] = {
> +	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
> +	[ADT7410_STATUS]	= ADT7310_STATUS,
> +	[ADT7410_CONFIG]	= ADT7310_CONFIG,
> +	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
> +	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
> +	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
> +	[ADT7410_T_HYST]	= ADT7310_T_HYST,
> +	[ADT7410_ID]		= ADT7310_ID,
> +};
> +
> +#define ADT7310_CMD_REG_OFFSET	3
> +#define ADT7310_CMD_READ	0x40
> +
> +#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> +
> +static int adt7310_spi_read_word(struct device *dev, u8 reg)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	int ret;
> +
> +	ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
> +	if (ret < 0)
> +		return ret;
> +
> +	return be16_to_cpu(ret);
> +}
> +
> +static int adt7310_spi_write_word(struct device *dev, u8 reg,
> +	u16 data)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	u8 buf[3];
> +
> +	buf[0] = AD7310_COMMAND(reg);
> +	put_unaligned_be16(data, &buf[1]);
> +
> +	return spi_write(spi, buf, sizeof(buf));
> +}
> +
> +static int adt7310_spi_read_byte(struct device *dev, u8 reg)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +
> +	return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
> +}
> +
> +static int adt7310_spi_write_byte(struct device *dev, u8 reg,
> +	u8 data)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	u8 buf[2];
> +
> +	buf[0] = AD7310_COMMAND(reg);
> +	buf[1] = data;
> +
> +	return spi_write(spi, buf, sizeof(buf));
> +}
> +
> +static const struct adt7410_ops adt7310_spi_ops = {
> +	.read_word = adt7310_spi_read_word,
> +	.write_word = adt7310_spi_write_word,
> +	.read_byte = adt7310_spi_read_byte,
> +	.write_byte = adt7310_spi_write_byte,
> +};
> +
> +static int adt7310_spi_probe(struct spi_device *spi)
> +{
> +	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
> +			&adt7310_spi_ops);
> +}
> +
> +static int adt7310_spi_remove(struct spi_device *spi)
> +{
> +	return adt7410_remove(&spi->dev);
> +}
> +
> +static const struct spi_device_id adt7310_id[] = {
> +	{ "adt7310", 0 },
> +	{ "adt7320", 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(spi, adt7310_id);
> +
> +static struct spi_driver adt7310_driver = {
> +	.driver = {
> +		.name = "adt7310",
> +		.owner = THIS_MODULE,
> +		.pm	= ADT7410_DEV_PM_OPS,

Should probably be named ADT7X10_DEV_PM_OPS to avoid confusion.

> +	},
> +	.probe = adt7310_spi_probe,
> +	.remove = adt7310_spi_remove,
> +	.id_table = adt7310_id,
> +};
> +module_spi_driver(adt7310_driver);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index b6acfa4..b500ab3 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -1,485 +1,81 @@
>  /*
> - * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
> - *	 monitoring
> - * This driver handles the ADT7410 and compatible digital temperature sensors.
> - * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
> - * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
> - * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
> + * ADT7410/ADT7420 digital temperature sensor driver
>   *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> + * Copyright 2010-2013 Analog Devices Inc.
> + *   Author: Lars-Peter Clausen <lars@metafoo.de>

Same as before.

>   *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + * Licensed under the GPL-2 or later.
>   */
>  
>  #include <linux/module.h>
>  #include <linux/init.h>
> -#include <linux/slab.h>
> -#include <linux/jiffies.h>
>  #include <linux/i2c.h>
> -#include <linux/hwmon.h>
> -#include <linux/hwmon-sysfs.h>
> -#include <linux/err.h>
> -#include <linux/mutex.h>
> -#include <linux/delay.h>
> -
> -/*
> - * ADT7410 registers definition
> - */
> -
> -#define ADT7410_TEMPERATURE		0
> -#define ADT7410_STATUS			2
> -#define ADT7410_CONFIG			3
> -#define ADT7410_T_ALARM_HIGH		4
> -#define ADT7410_T_ALARM_LOW		6
> -#define ADT7410_T_CRIT			8
> -#define ADT7410_T_HYST			0xA
> -
> -/*
> - * ADT7410 status
> - */
> -#define ADT7410_STAT_T_LOW		(1 << 4)
> -#define ADT7410_STAT_T_HIGH		(1 << 5)
> -#define ADT7410_STAT_T_CRIT		(1 << 6)
> -#define ADT7410_STAT_NOT_RDY		(1 << 7)
>  
> -/*
> - * ADT7410 config
> - */
> -#define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
> -#define ADT7410_CT_POLARITY		(1 << 2)
> -#define ADT7410_INT_POLARITY		(1 << 3)
> -#define ADT7410_EVENT_MODE		(1 << 4)
> -#define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
> -#define ADT7410_FULL			(0 << 5 | 0 << 6)
> -#define ADT7410_PD			(1 << 5 | 1 << 6)
> -#define ADT7410_RESOLUTION		(1 << 7)
> +#include "adt7x10.h"
>  
> -/*
> - * ADT7410 masks
> - */
> -#define ADT7410_T13_VALUE_MASK			0xFFF8
> -#define ADT7410_T_HYST_MASK			0xF
> -
> -/* straight from the datasheet */
> -#define ADT7410_TEMP_MIN (-55000)
> -#define ADT7410_TEMP_MAX 150000
> -
> -enum adt7410_type {		/* keep sorted in alphabetical order */
> -	adt7410,
> -};
> -
> -static const u8 ADT7410_REG_TEMP[4] = {
> -	ADT7410_TEMPERATURE,		/* input */
> -	ADT7410_T_ALARM_HIGH,		/* high */
> -	ADT7410_T_ALARM_LOW,		/* low */
> -	ADT7410_T_CRIT,			/* critical */
> -};
> -
> -/* Each client has this additional data */
> -struct adt7410_data {
> -	struct device		*hwmon_dev;
> -	struct mutex		update_lock;
> -	u8			config;
> -	u8			oldconfig;
> -	bool			valid;		/* true if registers valid */
> -	unsigned long		last_updated;	/* In jiffies */
> -	s16			temp[4];	/* Register values,
> -						   0 = input
> -						   1 = high
> -						   2 = low
> -						   3 = critical */
> -	u8			hyst;		/* hysteresis offset */
> -};
> -
> -/*
> - * adt7410 register access by I2C
> - */
> -static int adt7410_temp_ready(struct i2c_client *client)
> +static int adt7410_i2c_read_word(struct device *dev, u8 reg)
>  {
> -	int i, status;
> -
> -	for (i = 0; i < 6; i++) {
> -		status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
> -		if (status < 0)
> -			return status;
> -		if (!(status & ADT7410_STAT_NOT_RDY))
> -			return 0;
> -		msleep(60);
> -	}
> -	return -ETIMEDOUT;
> +	return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg);
>  }
>  
> -static int adt7410_update_temp(struct device *dev)
> +static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int ret = 0;
> -
> -	mutex_lock(&data->update_lock);
> -
> -	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
> -	    || !data->valid) {
> -		int temp;
> -
> -		dev_dbg(&client->dev, "Starting update\n");
> -
> -		ret = adt7410_temp_ready(client); /* check for new value */
> -		if (ret)
> -			goto abort;
> -
> -		temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
> -		if (temp < 0) {
> -			ret = temp;
> -			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> -				ADT7410_REG_TEMP[0], ret);
> -			goto abort;
> -		}
> -		data->temp[0] = temp;
> -
> -		data->last_updated = jiffies;
> -		data->valid = true;
> -	}
> -
> -abort:
> -	mutex_unlock(&data->update_lock);
> -	return ret;
> -}
> -
> -static int adt7410_fill_cache(struct i2c_client *client)
> -{
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int ret;
> -	int i;
> -
> -	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
> -		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> -		if (ret < 0) {
> -			dev_dbg(&client->dev,
> -				"Failed to read value: reg %d, error %d\n",
> -				ADT7410_REG_TEMP[0], ret);
> -			return ret;
> -		}
> -		data->temp[i] = ret;
> -	}
> -
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> -	if (ret < 0) {
> -		dev_dbg(&client->dev,
> -			"Failed to read value: hyst reg, error %d\n",
> -			ret);
> -		return ret;
> -	}
> -	data->hyst = ret;
> -
> -	return 0;
> +	return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data);
>  }
>  
> -static s16 ADT7410_TEMP_TO_REG(long temp)
> +static int adt7410_i2c_read_byte(struct device *dev, u8 reg)
>  {
> -	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
> -					   ADT7410_TEMP_MAX) * 128, 1000);
> +	return i2c_smbus_read_byte_data(to_i2c_client(dev), reg);
>  }
>  
> -static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
> +static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
>  {
> -	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
> -	if (!(data->config & ADT7410_RESOLUTION))
> -		reg &= ADT7410_T13_VALUE_MASK;
> -	/*
> -	 * temperature is stored in twos complement format, in steps of
> -	 * 1/128°C
> -	 */
> -	return DIV_ROUND_CLOSEST(reg * 1000, 128);
> +	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);
>  }
>  
> -/*-----------------------------------------------------------------------*/
> -
> -/* sysfs attributes for hwmon */
> -
> -static ssize_t adt7410_show_temp(struct device *dev,
> -				 struct device_attribute *da, char *buf)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	if (attr->index == 0) {
> -		int ret;
> -
> -		ret = adt7410_update_temp(dev);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
> -		       data->temp[attr->index]));
> -}
> -
> -static ssize_t adt7410_set_temp(struct device *dev,
> -				struct device_attribute *da,
> -				const char *buf, size_t count)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int nr = attr->index;
> -	long temp;
> -	int ret;
> -
> -	ret = kstrtol(buf, 10, &temp);
> -	if (ret)
> -		return ret;
> -
> -	mutex_lock(&data->update_lock);
> -	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
> -	ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
> -					   data->temp[nr]);
> -	if (ret)
> -		count = ret;
> -	mutex_unlock(&data->update_lock);
> -	return count;
> -}
> -
> -static ssize_t adt7410_show_t_hyst(struct device *dev,
> -				   struct device_attribute *da,
> -				   char *buf)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int nr = attr->index;
> -	int hyst;
> -
> -	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
> -
> -	/*
> -	 * hysteresis is stored as a 4 bit offset in the device, convert it
> -	 * to an absolute value
> -	 */
> -	if (nr == 2)	/* min has positive offset, others have negative */
> -		hyst = -hyst;
> -	return sprintf(buf, "%d\n",
> -		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
> -}
> -
> -static ssize_t adt7410_set_t_hyst(struct device *dev,
> -				  struct device_attribute *da,
> -				  const char *buf, size_t count)
> -{
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -	int limit, ret;
> -	long hyst;
> -
> -	ret = kstrtol(buf, 10, &hyst);
> -	if (ret)
> -		return ret;
> -	/* convert absolute hysteresis value to a 4 bit delta value */
> -	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
> -	hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
> -	data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
> -			       ADT7410_T_HYST_MASK);
> -	ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
> -	if (ret)
> -		return ret;
> -
> -	return count;
> -}
> -
> -static ssize_t adt7410_show_alarm(struct device *dev,
> -				  struct device_attribute *da,
> -				  char *buf)
> -{
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	int ret;
> -
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
> -	if (ret < 0)
> -		return ret;
> -
> -	return sprintf(buf, "%d\n", !!(ret & attr->index));
> -}
> -
> -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
> -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
> -			  adt7410_show_temp, adt7410_set_temp, 1);
> -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
> -			  adt7410_show_temp, adt7410_set_temp, 2);
> -static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
> -			  adt7410_show_temp, adt7410_set_temp, 3);
> -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
> -			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
> -static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
> -			  adt7410_show_t_hyst, NULL, 2);
> -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
> -			  adt7410_show_t_hyst, NULL, 3);
> -static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
> -			  NULL, ADT7410_STAT_T_LOW);
> -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
> -			  NULL, ADT7410_STAT_T_HIGH);
> -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
> -			  NULL, ADT7410_STAT_T_CRIT);
> -
> -static struct attribute *adt7410_attributes[] = {
> -	&sensor_dev_attr_temp1_input.dev_attr.attr,
> -	&sensor_dev_attr_temp1_max.dev_attr.attr,
> -	&sensor_dev_attr_temp1_min.dev_attr.attr,
> -	&sensor_dev_attr_temp1_crit.dev_attr.attr,
> -	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> -	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
> -	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
> -	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
> -	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
> -	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
> -	NULL
> -};
> -
> -static const struct attribute_group adt7410_group = {
> -	.attrs = adt7410_attributes,
> +static const struct adt7410_ops adt7410_i2c_ops = {
> +	.read_word = adt7410_i2c_read_word,
> +	.write_word = adt7410_i2c_write_word,
> +	.read_byte = adt7410_i2c_read_byte,
> +	.write_byte = adt7410_i2c_write_byte,
>  };
>  
> -/*-----------------------------------------------------------------------*/
> -
> -/* device probe and removal */
> -
> -static int adt7410_probe(struct i2c_client *client,
> -			 const struct i2c_device_id *id)
> +static int adt7410_i2c_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
>  {
> -	struct adt7410_data *data;
> -	int ret;
>  
>  	if (!i2c_check_functionality(client->adapter,
>  			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
>  		return -ENODEV;
>  
> -	data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
> -			    GFP_KERNEL);
> -	if (!data)
> -		return -ENOMEM;
> -
> -	i2c_set_clientdata(client, data);
> -	mutex_init(&data->update_lock);
> -
> -	/* configure as specified */
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
> -	if (ret < 0) {
> -		dev_dbg(&client->dev, "Can't read config? %d\n", ret);
> -		return ret;
> -	}
> -	data->oldconfig = ret;
> -	/*
> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
> -	 */
> -	ret &= ~ADT7410_MODE_MASK;
> -	data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
> -			ADT7410_EVENT_MODE;
> -	if (data->config != data->oldconfig) {
> -		ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
> -						data->config);
> -		if (ret)
> -			return ret;
> -	}
> -	dev_dbg(&client->dev, "Config %02x\n", data->config);
> -
> -	ret = adt7410_fill_cache(client);
> -	if (ret)
> -		goto exit_restore;
> -
> -	/* Register sysfs hooks */
> -	ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
> -	if (ret)
> -		goto exit_restore;
> -
> -	data->hwmon_dev = hwmon_device_register(&client->dev);
> -	if (IS_ERR(data->hwmon_dev)) {
> -		ret = PTR_ERR(data->hwmon_dev);
> -		goto exit_remove;
> -	}
> -
> -	dev_info(&client->dev, "sensor '%s'\n", client->name);
> -
> -	return 0;
> -
> -exit_remove:
> -	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
> -exit_restore:
> -	i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
> -	return ret;
> +	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
>  }
>  
> -static int adt7410_remove(struct i2c_client *client)
> +static int adt7410_i2c_remove(struct i2c_client *client)
>  {
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	hwmon_device_unregister(data->hwmon_dev);
> -	sysfs_remove_group(&client->dev.kobj, &adt7410_group);
> -	if (data->oldconfig != data->config)
> -		i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
> -					  data->oldconfig);
> -	return 0;
> +	return adt7410_remove(&client->dev);
>  }
>  
>  static const struct i2c_device_id adt7410_ids[] = {
> -	{ "adt7410", adt7410, },
> -	{ "adt7420", adt7410, },
> -	{ /* LIST END */ }
> +	{ "adt7410", 0 },
> +	{ "adt7420", 0 },
> +	{}
>  };
>  MODULE_DEVICE_TABLE(i2c, adt7410_ids);
>  
> -#ifdef CONFIG_PM_SLEEP
> -static int adt7410_suspend(struct device *dev)
> -{
> -	int ret;
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
> -					data->config | ADT7410_PD);
> -	return ret;
> -}
> -
> -static int adt7410_resume(struct device *dev)
> -{
> -	int ret;
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct adt7410_data *data = i2c_get_clientdata(client);
> -
> -	ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
> -	return ret;
> -}
> -
> -static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
> -
> -#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
> -#else
> -#define ADT7410_DEV_PM_OPS NULL
> -#endif /* CONFIG_PM */
> -
>  static struct i2c_driver adt7410_driver = {
>  	.class		= I2C_CLASS_HWMON,
>  	.driver = {
>  		.name	= "adt7410",
>  		.pm	= ADT7410_DEV_PM_OPS,
>  	},
> -	.probe		= adt7410_probe,
> -	.remove		= adt7410_remove,
> +	.probe		= adt7410_i2c_probe,
> +	.remove		= adt7410_i2c_remove,
>  	.id_table	= adt7410_ids,
>  	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
>  };
> -
>  module_i2c_driver(adt7410_driver);
>  
> -MODULE_AUTHOR("Hartmut Knaack");
> -MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("ADT7410/AD7420 driver");
>  MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
> new file mode 100644
> index 0000000..eeff198c
> --- /dev/null
> +++ b/drivers/hwmon/adt7x10.c
> @@ -0,0 +1,476 @@
> +/*
> + * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware

Needs file name and purpose update

> + *	 monitoring
> + * This driver handles the ADT7410 and compatible digital temperature sensors.
> + * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
> + * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
> + * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +
> +#include "adt7x10.h"
> +
> +/*
> + * ADT7410 status
> + */
> +#define ADT7410_STAT_T_LOW		(1 << 4)
> +#define ADT7410_STAT_T_HIGH		(1 << 5)
> +#define ADT7410_STAT_T_CRIT		(1 << 6)
> +#define ADT7410_STAT_NOT_RDY		(1 << 7)
> +
> +/*
> + * ADT7410 config
> + */
> +#define ADT7410_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
> +#define ADT7410_CT_POLARITY		(1 << 2)
> +#define ADT7410_INT_POLARITY		(1 << 3)
> +#define ADT7410_EVENT_MODE		(1 << 4)
> +#define ADT7410_MODE_MASK		(1 << 5 | 1 << 6)
> +#define ADT7410_FULL			(0 << 5 | 0 << 6)
> +#define ADT7410_PD			(1 << 5 | 1 << 6)
> +#define ADT7410_RESOLUTION		(1 << 7)
> +
> +/*
> + * ADT7410 masks
> + */
> +#define ADT7410_T13_VALUE_MASK			0xFFF8
> +#define ADT7410_T_HYST_MASK			0xF
> +
> +/* straight from the datasheet */
> +#define ADT7410_TEMP_MIN (-55000)
> +#define ADT7410_TEMP_MAX 150000
> +
> +/* Each client has this additional data */
> +struct adt7410_data {
> +	const struct adt7410_ops *ops;
> +	const char		*name;
> +	struct device		*hwmon_dev;
> +	struct mutex		update_lock;
> +	u8			config;
> +	u8			oldconfig;
> +	bool			valid;		/* true if registers valid */
> +	unsigned long		last_updated;	/* In jiffies */
> +	s16			temp[4];	/* Register values,
> +						   0 = input
> +						   1 = high
> +						   2 = low
> +						   3 = critical */
> +	u8			hyst;		/* hysteresis offset */
> +};
> +
> +static int adt7410_read_byte(struct device *dev, u8 reg)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_byte(dev, reg);
> +}
> +
> +static int adt7410_write_byte(struct device *dev, u8 reg, u8 data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->write_byte(dev, reg, data);
> +}
> +
> +static int adt7410_read_word(struct device *dev, u8 reg)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_word(dev, reg);
> +}
> +
> +static int adt7410_write_word(struct device *dev, u8 reg, u16 data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->write_word(dev, reg, data);
> +}
> +
> +static const u8 ADT7410_REG_TEMP[4] = {
> +	ADT7410_TEMPERATURE,		/* input */
> +	ADT7410_T_ALARM_HIGH,		/* high */
> +	ADT7410_T_ALARM_LOW,		/* low */
> +	ADT7410_T_CRIT,			/* critical */
> +};
> +
> +static int adt7410_temp_ready(struct device *dev)
> +{
> +	int i, status;
> +
> +	for (i = 0; i < 6; i++) {
> +		status = adt7410_read_byte(dev, ADT7410_STATUS);
> +		if (status < 0)
> +			return status;
> +		if (!(status & ADT7410_STAT_NOT_RDY))
> +			return 0;
> +		msleep(60);
> +	}
> +	return -ETIMEDOUT;
> +}
> +
> +static int adt7410_update_temp(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	mutex_lock(&data->update_lock);
> +
> +	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
> +	    || !data->valid) {
> +		int temp;
> +
> +		dev_dbg(dev, "Starting update\n");
> +
> +		ret = adt7410_temp_ready(dev); /* check for new value */
> +		if (ret)
> +			goto abort;
> +
> +		temp = adt7410_read_word(dev, ADT7410_REG_TEMP[0]);
> +		if (temp < 0) {
> +			ret = temp;
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			goto abort;
> +		}
> +		data->temp[0] = temp;
> +		data->last_updated = jiffies;
> +		data->valid = true;
> +	}
> +
> +abort:
> +	mutex_unlock(&data->update_lock);
> +	return ret;
> +}
> +
> +static int adt7410_fill_cache(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int ret;
> +	int i;
> +
> +	for (i = 1; i < ARRAY_SIZE(data->temp); i++) {
> +		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[i]);
> +		if (ret < 0) {
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);

ADT7410_REG_TEMP[i] as Hartmut pointed out

> +			return ret;
> +		}
> +		data->temp[i] = ret;
> +	}
> +
> +	ret = adt7410_read_byte(dev, ADT7410_T_HYST);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_T_HYST, ret);
> +		return ret;
> +	}
> +	data->hyst = ret;
> +
> +	return 0;
> +}
> +
> +static s16 ADT7410_TEMP_TO_REG(long temp)
> +{
> +	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
> +					       ADT7410_TEMP_MAX) * 128, 1000);
> +}
> +
> +static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
> +{
> +	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
> +	if (!(data->config & ADT7410_RESOLUTION))
> +		reg &= ADT7410_T13_VALUE_MASK;
> +	/*
> +	 * temperature is stored in twos complement format, in steps of
> +	 * 1/128°C
> +	 */
> +	return DIV_ROUND_CLOSEST(reg * 1000, 128);
> +}
> +
> +/*-----------------------------------------------------------------------*/
> +
> +/* sysfs attributes for hwmon */
> +
> +static ssize_t adt7410_show_temp(struct device *dev,
> +				 struct device_attribute *da, char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +
> +	if (attr->index == 0) {
> +		int ret;
> +
> +		ret = adt7410_update_temp(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
> +		       data->temp[attr->index]));
> +}
> +
> +static ssize_t adt7410_set_temp(struct device *dev,
> +				struct device_attribute *da,
> +				const char *buf, size_t count)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int nr = attr->index;
> +	long temp;
> +	int ret;
> +
> +	ret = kstrtol(buf, 10, &temp);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&data->update_lock);
> +	data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
> +	ret = adt7410_write_word(dev, ADT7410_REG_TEMP[nr], data->temp[nr]);
> +	if (ret)
> +		count = ret;
> +	mutex_unlock(&data->update_lock);
> +	return count;
> +}
> +
> +static ssize_t adt7410_show_t_hyst(struct device *dev,
> +				   struct device_attribute *da,
> +				   char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int nr = attr->index;
> +	int hyst;
> +
> +	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
> +
> +	/*
> +	 * hysteresis is stored as a 4 bit offset in the device, convert it
> +	 * to an absolute value
> +	 */
> +	if (nr == 2)	/* min has positive offset, others have negative */
> +		hyst = -hyst;
> +	return sprintf(buf, "%d\n",
> +		       ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
> +}
> +
> +static ssize_t adt7410_set_t_hyst(struct device *dev,
> +				  struct device_attribute *da,
> +				  const char *buf, size_t count)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +	int limit, ret;
> +	long hyst;
> +
> +	ret = kstrtol(buf, 10, &hyst);
> +	if (ret)
> +		return ret;
> +	/* convert absolute hysteresis value to a 4 bit delta value */
> +	limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
> +	hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
> +	data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000),
> +				   0, ADT7410_T_HYST_MASK);
> +	ret = adt7410_write_byte(dev, ADT7410_T_HYST, data->hyst);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t adt7410_show_alarm(struct device *dev,
> +				  struct device_attribute *da,
> +				  char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	int ret;
> +
> +	ret = adt7410_read_byte(dev, ADT7410_STATUS);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", !!(ret & attr->index));
> +}
> +
> +static ssize_t adt7410_show_name(struct device *dev,
> +				  struct device_attribute *da, char *buf)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%s\n", data->name);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
> +			  adt7410_show_temp, adt7410_set_temp, 1);
> +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
> +			  adt7410_show_temp, adt7410_set_temp, 2);
> +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
> +			  adt7410_show_temp, adt7410_set_temp, 3);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
> +			  adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
> +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
> +			  adt7410_show_t_hyst, NULL, 2);
> +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
> +			  adt7410_show_t_hyst, NULL, 3);
> +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
> +			  NULL, ADT7410_STAT_T_LOW);
> +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
> +			  NULL, ADT7410_STAT_T_HIGH);
> +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
> +			  NULL, ADT7410_STAT_T_CRIT);
> +static DEVICE_ATTR(name, S_IRUGO, adt7410_show_name, NULL);
> +
> +static struct attribute *adt7410_attributes[] = {
> +	&sensor_dev_attr_temp1_input.dev_attr.attr,
> +	&sensor_dev_attr_temp1_max.dev_attr.attr,
> +	&sensor_dev_attr_temp1_min.dev_attr.attr,
> +	&sensor_dev_attr_temp1_crit.dev_attr.attr,
> +	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> +	&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
> +	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
> +	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
> +	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
> +	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adt7410_group = {
> +	.attrs = adt7410_attributes,
> +};
> +
> +int adt7410_probe(struct device *dev, const char *name,
> +	const struct adt7410_ops *ops)

Preferred alignment is to have the second line start at the (. Usually I don't
complain but since you'll have to do another rev anyway maybe you can fix this
whereever it occurs.

> +{
> +	struct adt7410_data *data;
> +	int ret;
> +
> +	data = devm_kzalloc(dev, sizeof(struct adt7410_data),
> +			    GFP_KERNEL);

No need to split the line above.

> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->ops = ops;
> +	data->name = name;
> +
> +	dev_set_drvdata(dev, data);
> +	mutex_init(&data->update_lock);
> +
> +	/* configure as specified */
> +	ret = adt7410_read_byte(dev, ADT7410_CONFIG);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Can't read config? %d\n", ret);
> +		return ret;
> +	}
> +	data->oldconfig = ret;
> +
> +	/*
> +	 * Set to 16 bit resolution, continous conversion and comparator mode.
> +	 */
> +	data->config = data->oldconfig;
> +	data->config &= ~ADT7410_MODE_MASK;
> +	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
> +	if (data->config != data->oldconfig) {
> +		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
> +		if (ret)
> +			return ret;
> +	}
> +	dev_dbg(dev, "Config %02x\n", data->config);
> +
> +	ret = adt7410_fill_cache(dev);
> +	if (ret)
> +		goto exit_restore;
> +
> +	/* Register sysfs hooks */
> +	ret = sysfs_create_group(&dev->kobj, &adt7410_group);
> +	if (ret)
> +		goto exit_restore;
> +
> +	/*
> +	 * The I2C device will already have it's own 'name' attribute, but for
> +	 * the SPI device we need to register it. name will only be non NULL if
> +	 * the device doesn't register the 'name' attribute on its own.
> +	 */
> +	if (name) {
> +		ret = device_create_file(dev, &dev_attr_name);
> +		if (ret)
> +			goto exit_remove;
> +	}
> +
> +	data->hwmon_dev = hwmon_device_register(dev);
> +	if (IS_ERR(data->hwmon_dev)) {
> +		ret = PTR_ERR(data->hwmon_dev);
> +		goto exit_remove_name;
> +	}
> +
> +	return 0;
> +
> +exit_remove_name:
> +	if (name)
> +		device_remove_file(dev, &dev_attr_name);
> +exit_remove:
> +	sysfs_remove_group(&dev->kobj, &adt7410_group);
> +exit_restore:
> +	adt7410_write_byte(dev, ADT7410_CONFIG, data->oldconfig);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(adt7410_probe);
> +
> +int adt7410_remove(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	hwmon_device_unregister(data->hwmon_dev);
> +	if (data->name)
> +		device_remove_file(dev, &dev_attr_name);
> +	sysfs_remove_group(&dev->kobj, &adt7410_group);
> +	if (data->oldconfig != data->config)
> +		adt7410_write_byte(dev, ADT7410_CONFIG,
> +					  data->oldconfig);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(adt7410_remove);
> +
> +#ifdef CONFIG_PM_SLEEP
> +
> +static int adt7410_suspend(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	return adt7410_write_byte(dev, ADT7410_CONFIG,
> +		data->config | ADT7410_PD);
> +}
> +
> +static int adt7410_resume(struct device *dev)
> +{
> +	struct adt7410_data *data = dev_get_drvdata(dev);
> +
> +	return adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
> +}
> +
> +SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
> +EXPORT_SYMBOL_GPL(adt7410_dev_pm_ops);
> +
> +#endif /* CONFIG_PM_SLEEP */
> +
> +MODULE_AUTHOR("Hartmut Knaack");
> +MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> new file mode 100644
> index 0000000..a7165e6
> --- /dev/null
> +++ b/drivers/hwmon/adt7x10.h
> @@ -0,0 +1,48 @@
> +#ifndef __HWMON_ADT7X10_H__
> +#define __HWMON_ADT7X10_H__
> +
> +#include <linux/types.h>
> +#include <linux/pm.h>
> +
> +/* ADT7410 registers definition */
> +#define ADT7410_TEMPERATURE		0
> +#define ADT7410_STATUS			2
> +#define ADT7410_CONFIG			3
> +#define ADT7410_T_ALARM_HIGH		4
> +#define ADT7410_T_ALARM_LOW		6
> +#define ADT7410_T_CRIT			8
> +#define ADT7410_T_HYST			0xA
> +#define ADT7410_ID			0xB
> +
> +/* ADT7310 registers definition */
> +#define ADT7310_STATUS			0
> +#define ADT7310_CONFIG			1
> +#define ADT7310_TEMPERATURE		2
> +#define ADT7310_ID			3
> +#define ADT7310_T_CRIT			4
> +#define ADT7310_T_HYST			5
> +#define ADT7310_T_ALARM_HIGH		6
> +#define ADT7310_T_ALARM_LOW		7
> +
> +struct device;
> +
> +struct adt7410_ops {
> +	int (*read_byte)(struct device *, u8 reg);
> +	int (*write_byte)(struct device *, u8 reg, u8 data);
> +	int (*read_word)(struct device *, u8 reg);
> +	int (*write_word)(struct device *, u8 reg, u16 data);
> +};
> +
> +int adt7410_probe(struct device *dev, const char *name,
> +	const struct adt7410_ops *ops);
> +int adt7410_remove(struct device *dev);
> +

I think the above should be adt7x10_ops, adt7x10_probe and adt7x10_remove, to
indicate that those are common functions.

I would actually suggest to rename all functions in adt7x10.c (not just the
exported ones).

> +
> +#ifdef CONFIG_PM_SLEEP
> +extern const struct dev_pm_ops adt7410_dev_pm_ops;
> +#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)

As mentioned above, I think ADT7X10_DEV_PM_OPS would be better here.

> +#else
> +#define ADT7410_DEV_PM_OPS NULL
> +#endif
> +
> +#endif
> -- 
> 1.8.0
> 
> 

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-19  1:32   ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:32 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Mon, Feb 18, 2013 at 02:38:56PM +0100, Lars-Peter Clausen wrote:
> Currently each time the temperature register is read the driver also reads the
> threshold and hysteresis registers. This increases the amount of I2C traffic and
> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> the hysteresis change on their own, so once we've read them, we should be able
> to just use the cached value of the registers. This patch modifies the code
> accordingly and only reads the threshold and hysteresis registers once during
> probe.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> 
Just one comment (thanks to Hartmut), otherwise looks good.

> ---
> Changes since v1:
> 	* Fix error checking for i2c reads
> ---
[ ... ]

> +static int adt7410_fill_cache(struct i2c_client *client)
> +{
> +	struct adt7410_data *data = i2c_get_clientdata(client);
> +	int ret;
> +	int i;
> +
> +	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> +		if (ret < 0) {
> +			dev_dbg(&client->dev,
> +				"Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);

	ADT7410_REG_TEMP[i]

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-19  1:32   ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:32 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Mon, Feb 18, 2013 at 02:38:56PM +0100, Lars-Peter Clausen wrote:
> Currently each time the temperature register is read the driver also reads the
> threshold and hysteresis registers. This increases the amount of I2C traffic and
> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> the hysteresis change on their own, so once we've read them, we should be able
> to just use the cached value of the registers. This patch modifies the code
> accordingly and only reads the threshold and hysteresis registers once during
> probe.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> 
Just one comment (thanks to Hartmut), otherwise looks good.

> ---
> Changes since v1:
> 	* Fix error checking for i2c reads
> ---
[ ... ]

> +static int adt7410_fill_cache(struct i2c_client *client)
> +{
> +	struct adt7410_data *data = i2c_get_clientdata(client);
> +	int ret;
> +	int i;
> +
> +	for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> +		if (ret < 0) {
> +			dev_dbg(&client->dev,
> +				"Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);

	ADT7410_REG_TEMP[i]

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
  2013-02-18 13:38   ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-19  1:39     ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:39 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Mon, Feb 18, 2013 at 02:38:58PM +0100, Lars-Peter Clausen wrote:
> This allows a userspace application to poll() on the alarm files to get notified
> in case of an temperature threshold event.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> 
> ---
> Changes since v1:
> 	* Switch from level triggered and interrupt mode to edge triggered and
> 	  comparator mode. In interrupt mode the status register get's cleared after
> 	  the first read and so the _alarm files will always read 0, which is not
> 	  really what we want.
> 	* Use dev_name(dev) for the interrupt name, instead of 'name' which will be
> 	  NULL for I2C devices
> ---
>  drivers/hwmon/adt7310.c |  4 ++--
>  drivers/hwmon/adt7410.c |  4 ++--
>  drivers/hwmon/adt7x10.c | 43 +++++++++++++++++++++++++++++++++++++++----
>  drivers/hwmon/adt7x10.h |  4 ++--
>  4 files changed, 45 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> index 2196ac3..e58bb68 100644
> --- a/drivers/hwmon/adt7310.c
> +++ b/drivers/hwmon/adt7310.c
> @@ -82,13 +82,13 @@ static const struct adt7410_ops adt7310_spi_ops = {
>  
>  static int adt7310_spi_probe(struct spi_device *spi)
>  {
> -	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
> +	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq,
>  			&adt7310_spi_ops);
>  }
>  
>  static int adt7310_spi_remove(struct spi_device *spi)
>  {
> -	return adt7410_remove(&spi->dev);
> +	return adt7410_remove(&spi->dev, spi->irq);
>  }
>  
>  static const struct spi_device_id adt7310_id[] = {
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index b500ab3..3a7d905 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -48,12 +48,12 @@ static int adt7410_i2c_probe(struct i2c_client *client,
>  			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
>  		return -ENODEV;
>  
> -	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
> +	return adt7410_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops);
>  }
>  
>  static int adt7410_i2c_remove(struct i2c_client *client)
>  {
> -	return adt7410_remove(&client->dev);
> +	return adt7410_remove(&client->dev, client->irq);
>  }
>  
>  static const struct i2c_device_id adt7410_ids[] = {
> diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
> index eeff198c..2340a70 100644
> --- a/drivers/hwmon/adt7x10.c
> +++ b/drivers/hwmon/adt7x10.c
> @@ -30,6 +30,7 @@
>  #include <linux/err.h>
>  #include <linux/mutex.h>
>  #include <linux/delay.h>
> +#include <linux/interrupt.h>
>  
>  #include "adt7x10.h"
>  
> @@ -112,6 +113,25 @@ static const u8 ADT7410_REG_TEMP[4] = {
>  	ADT7410_T_CRIT,			/* critical */
>  };
>  
> +static irqreturn_t adt7410_irq_handler(int irq, void *private)
> +{
> +	struct device *dev = private;
> +	int status;
> +
> +	status = adt7410_read_byte(dev, ADT7410_STATUS);
> +	if (status < 0)
> +		return IRQ_HANDLED;
> +
> +	if (status & ADT7410_STAT_T_HIGH)
> +		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
> +	if (status & ADT7410_STAT_T_LOW)
> +		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
> +	if (status & ADT7410_STAT_T_CRIT)
> +		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
> +
Question about semantics: Do you want a notification on all attributes each time
one is set during an interrupt, or do you want a notification each time an
attribute changes state ? With the above code, the 1->0 transition does not
result in a notification. Is that on purpose ?

> +	return IRQ_HANDLED;
> +}
> +
>  static int adt7410_temp_ready(struct device *dev)
>  {
>  	int i, status;
> @@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
>  	.attrs = adt7410_attributes,
>  };
>  
> -int adt7410_probe(struct device *dev, const char *name,
> +int adt7410_probe(struct device *dev, const char *name, int irq,
>  	const struct adt7410_ops *ops)
>  {
>  	struct adt7410_data *data;
> @@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
>  	data->oldconfig = ret;
>  
>  	/*
> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
> +	 * Set to 16 bit resolution and continous conversion
>  	 */
>  	data->config = data->oldconfig;
> -	data->config &= ~ADT7410_MODE_MASK;
> +	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
> +			ADT7410_INT_POLARITY);
>  	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
> +
>  	if (data->config != data->oldconfig) {
>  		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
>  		if (ret)
> @@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
>  		goto exit_remove_name;
>  	}
>  
> +	if (irq > 0) {
> +		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
> +				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				dev_name(dev), dev);

If you use devm_request_threaded_irq, you don't have to release it on remove.

> +		if (ret)
> +			goto exit_hwmon_device_unregister;
> +	}
> +

This code should be before hwmon registration.

>  	return 0;
>  
> +exit_hwmon_device_unregister:
> +	hwmon_device_unregister(data->hwmon_dev);
>  exit_remove_name:
>  	if (name)
>  		device_remove_file(dev, &dev_attr_name);
> @@ -434,10 +466,13 @@ exit_restore:
>  }
>  EXPORT_SYMBOL_GPL(adt7410_probe);
>  
> -int adt7410_remove(struct device *dev)
> +int adt7410_remove(struct device *dev, int irq)
>  {
>  	struct adt7410_data *data = dev_get_drvdata(dev);
>  
> +	if (irq > 0)
> +		free_irq(irq, dev);
> +
>  	hwmon_device_unregister(data->hwmon_dev);
>  	if (data->name)
>  		device_remove_file(dev, &dev_attr_name);
> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> index a7165e6..d67badf 100644
> --- a/drivers/hwmon/adt7x10.h
> +++ b/drivers/hwmon/adt7x10.h
> @@ -33,9 +33,9 @@ struct adt7410_ops {
>  	int (*write_word)(struct device *, u8 reg, u16 data);
>  };
>  
> -int adt7410_probe(struct device *dev, const char *name,
> +int adt7410_probe(struct device *dev, const char *name, int irq,
>  	const struct adt7410_ops *ops);
> -int adt7410_remove(struct device *dev);
> +int adt7410_remove(struct device *dev, int irq);
>  
>  
>  #ifdef CONFIG_PM_SLEEP
> -- 
> 1.8.0
> 
> 

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

* Re: [lm-sensors] [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
@ 2013-02-19  1:39     ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19  1:39 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Mon, Feb 18, 2013 at 02:38:58PM +0100, Lars-Peter Clausen wrote:
> This allows a userspace application to poll() on the alarm files to get notified
> in case of an temperature threshold event.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> 
> ---
> Changes since v1:
> 	* Switch from level triggered and interrupt mode to edge triggered and
> 	  comparator mode. In interrupt mode the status register get's cleared after
> 	  the first read and so the _alarm files will always read 0, which is not
> 	  really what we want.
> 	* Use dev_name(dev) for the interrupt name, instead of 'name' which will be
> 	  NULL for I2C devices
> ---
>  drivers/hwmon/adt7310.c |  4 ++--
>  drivers/hwmon/adt7410.c |  4 ++--
>  drivers/hwmon/adt7x10.c | 43 +++++++++++++++++++++++++++++++++++++++----
>  drivers/hwmon/adt7x10.h |  4 ++--
>  4 files changed, 45 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> index 2196ac3..e58bb68 100644
> --- a/drivers/hwmon/adt7310.c
> +++ b/drivers/hwmon/adt7310.c
> @@ -82,13 +82,13 @@ static const struct adt7410_ops adt7310_spi_ops = {
>  
>  static int adt7310_spi_probe(struct spi_device *spi)
>  {
> -	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name,
> +	return adt7410_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq,
>  			&adt7310_spi_ops);
>  }
>  
>  static int adt7310_spi_remove(struct spi_device *spi)
>  {
> -	return adt7410_remove(&spi->dev);
> +	return adt7410_remove(&spi->dev, spi->irq);
>  }
>  
>  static const struct spi_device_id adt7310_id[] = {
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index b500ab3..3a7d905 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -48,12 +48,12 @@ static int adt7410_i2c_probe(struct i2c_client *client,
>  			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
>  		return -ENODEV;
>  
> -	return adt7410_probe(&client->dev, NULL, &adt7410_i2c_ops);
> +	return adt7410_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops);
>  }
>  
>  static int adt7410_i2c_remove(struct i2c_client *client)
>  {
> -	return adt7410_remove(&client->dev);
> +	return adt7410_remove(&client->dev, client->irq);
>  }
>  
>  static const struct i2c_device_id adt7410_ids[] = {
> diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
> index eeff198c..2340a70 100644
> --- a/drivers/hwmon/adt7x10.c
> +++ b/drivers/hwmon/adt7x10.c
> @@ -30,6 +30,7 @@
>  #include <linux/err.h>
>  #include <linux/mutex.h>
>  #include <linux/delay.h>
> +#include <linux/interrupt.h>
>  
>  #include "adt7x10.h"
>  
> @@ -112,6 +113,25 @@ static const u8 ADT7410_REG_TEMP[4] = {
>  	ADT7410_T_CRIT,			/* critical */
>  };
>  
> +static irqreturn_t adt7410_irq_handler(int irq, void *private)
> +{
> +	struct device *dev = private;
> +	int status;
> +
> +	status = adt7410_read_byte(dev, ADT7410_STATUS);
> +	if (status < 0)
> +		return IRQ_HANDLED;
> +
> +	if (status & ADT7410_STAT_T_HIGH)
> +		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
> +	if (status & ADT7410_STAT_T_LOW)
> +		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
> +	if (status & ADT7410_STAT_T_CRIT)
> +		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
> +
Question about semantics: Do you want a notification on all attributes each time
one is set during an interrupt, or do you want a notification each time an
attribute changes state ? With the above code, the 1->0 transition does not
result in a notification. Is that on purpose ?

> +	return IRQ_HANDLED;
> +}
> +
>  static int adt7410_temp_ready(struct device *dev)
>  {
>  	int i, status;
> @@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
>  	.attrs = adt7410_attributes,
>  };
>  
> -int adt7410_probe(struct device *dev, const char *name,
> +int adt7410_probe(struct device *dev, const char *name, int irq,
>  	const struct adt7410_ops *ops)
>  {
>  	struct adt7410_data *data;
> @@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
>  	data->oldconfig = ret;
>  
>  	/*
> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
> +	 * Set to 16 bit resolution and continous conversion
>  	 */
>  	data->config = data->oldconfig;
> -	data->config &= ~ADT7410_MODE_MASK;
> +	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
> +			ADT7410_INT_POLARITY);
>  	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
> +
>  	if (data->config != data->oldconfig) {
>  		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
>  		if (ret)
> @@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
>  		goto exit_remove_name;
>  	}
>  
> +	if (irq > 0) {
> +		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
> +				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				dev_name(dev), dev);

If you use devm_request_threaded_irq, you don't have to release it on remove.

> +		if (ret)
> +			goto exit_hwmon_device_unregister;
> +	}
> +

This code should be before hwmon registration.

>  	return 0;
>  
> +exit_hwmon_device_unregister:
> +	hwmon_device_unregister(data->hwmon_dev);
>  exit_remove_name:
>  	if (name)
>  		device_remove_file(dev, &dev_attr_name);
> @@ -434,10 +466,13 @@ exit_restore:
>  }
>  EXPORT_SYMBOL_GPL(adt7410_probe);
>  
> -int adt7410_remove(struct device *dev)
> +int adt7410_remove(struct device *dev, int irq)
>  {
>  	struct adt7410_data *data = dev_get_drvdata(dev);
>  
> +	if (irq > 0)
> +		free_irq(irq, dev);
> +
>  	hwmon_device_unregister(data->hwmon_dev);
>  	if (data->name)
>  		device_remove_file(dev, &dev_attr_name);
> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> index a7165e6..d67badf 100644
> --- a/drivers/hwmon/adt7x10.h
> +++ b/drivers/hwmon/adt7x10.h
> @@ -33,9 +33,9 @@ struct adt7410_ops {
>  	int (*write_word)(struct device *, u8 reg, u16 data);
>  };
>  
> -int adt7410_probe(struct device *dev, const char *name,
> +int adt7410_probe(struct device *dev, const char *name, int irq,
>  	const struct adt7410_ops *ops);
> -int adt7410_remove(struct device *dev);
> +int adt7410_remove(struct device *dev, int irq);
>  
>  
>  #ifdef CONFIG_PM_SLEEP
> -- 
> 1.8.0
> 
> 

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-19  1:30     ` [lm-sensors] " Guenter Roeck
@ 2013-02-19 11:57       ` Lars-Peter Clausen
  -1 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-19 11:57 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On 02/19/2013 02:30 AM, Guenter Roeck wrote:
> On Mon, Feb 18, 2013 at 02:38:57PM +0100, Lars-Peter Clausen wrote:
[...]
>> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
>> new file mode 100644
>> index 0000000..2196ac3
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7310.c
>> @@ -0,0 +1,115 @@
>> +/*
>> + * ADT7310/ADT7310 digital temperature sensor driver
>> + *
>> + * Copyright 2010-2013 Analog Devices Inc.
> 
> Not really; copyright is yours, unless you are signing it off to analog or if
> you are working for them and have permission (or the duty) to sign off the
> copyright. Even then it should be 2013 only for this file.

I work for them. The copyright notice comes from the IIO adt7410/adt7310
driver, where also this code comes from. Although this portion of the code
was written in 2012, so maybe change it to 2012-2013.

[...]
>> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
>> new file mode 100644
>> index 0000000..a7165e6
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7x10.h
>> @@ -0,0 +1,48 @@
>> +#ifndef __HWMON_ADT7X10_H__
>> +#define __HWMON_ADT7X10_H__
>> +
>> +#include <linux/types.h>
>> +#include <linux/pm.h>
>> +
>> +/* ADT7410 registers definition */
>> +#define ADT7410_TEMPERATURE		0
>> +#define ADT7410_STATUS			2
>> +#define ADT7410_CONFIG			3
>> +#define ADT7410_T_ALARM_HIGH		4
>> +#define ADT7410_T_ALARM_LOW		6
>> +#define ADT7410_T_CRIT			8
>> +#define ADT7410_T_HYST			0xA
>> +#define ADT7410_ID			0xB
>> +
>> +/* ADT7310 registers definition */
>> +#define ADT7310_STATUS			0
>> +#define ADT7310_CONFIG			1
>> +#define ADT7310_TEMPERATURE		2
>> +#define ADT7310_ID			3
>> +#define ADT7310_T_CRIT			4
>> +#define ADT7310_T_HYST			5
>> +#define ADT7310_T_ALARM_HIGH		6
>> +#define ADT7310_T_ALARM_LOW		7
>> +
>> +struct device;
>> +
>> +struct adt7410_ops {
>> +	int (*read_byte)(struct device *, u8 reg);
>> +	int (*write_byte)(struct device *, u8 reg, u8 data);
>> +	int (*read_word)(struct device *, u8 reg);
>> +	int (*write_word)(struct device *, u8 reg, u16 data);
>> +};
>> +
>> +int adt7410_probe(struct device *dev, const char *name,
>> +	const struct adt7410_ops *ops);
>> +int adt7410_remove(struct device *dev);
>> +
> 
> I think the above should be adt7x10_ops, adt7x10_probe and adt7x10_remove, to
> indicate that those are common functions.
> 
> I would actually suggest to rename all functions in adt7x10.c (not just the
> exported ones).

I normally don't like to rename all the symbols in one file just because
support for another part is added, since it just cause lots of noise in the
diff. My initial plan was to call the two new modules adt7410-i2c and
adt7410-spi, but then though it is probably going to be confusing to have
the driver for the adt7310 called adt7410-spi. In this case the renaming
probably doesn't hurt since we already have lots of diff noise anyway. It
probably would have made sense to make the review easier to split this up in
two patches, one which renames adt7410 to adt7x10 and a second one which
adds the new functionality.

Thanks,
- Lars

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

* Re: [lm-sensors] [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
@ 2013-02-19 11:57       ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-19 11:57 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On 02/19/2013 02:30 AM, Guenter Roeck wrote:
> On Mon, Feb 18, 2013 at 02:38:57PM +0100, Lars-Peter Clausen wrote:
[...]
>> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
>> new file mode 100644
>> index 0000000..2196ac3
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7310.c
>> @@ -0,0 +1,115 @@
>> +/*
>> + * ADT7310/ADT7310 digital temperature sensor driver
>> + *
>> + * Copyright 2010-2013 Analog Devices Inc.
> 
> Not really; copyright is yours, unless you are signing it off to analog or if
> you are working for them and have permission (or the duty) to sign off the
> copyright. Even then it should be 2013 only for this file.

I work for them. The copyright notice comes from the IIO adt7410/adt7310
driver, where also this code comes from. Although this portion of the code
was written in 2012, so maybe change it to 2012-2013.

[...]
>> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
>> new file mode 100644
>> index 0000000..a7165e6
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7x10.h
>> @@ -0,0 +1,48 @@
>> +#ifndef __HWMON_ADT7X10_H__
>> +#define __HWMON_ADT7X10_H__
>> +
>> +#include <linux/types.h>
>> +#include <linux/pm.h>
>> +
>> +/* ADT7410 registers definition */
>> +#define ADT7410_TEMPERATURE		0
>> +#define ADT7410_STATUS			2
>> +#define ADT7410_CONFIG			3
>> +#define ADT7410_T_ALARM_HIGH		4
>> +#define ADT7410_T_ALARM_LOW		6
>> +#define ADT7410_T_CRIT			8
>> +#define ADT7410_T_HYST			0xA
>> +#define ADT7410_ID			0xB
>> +
>> +/* ADT7310 registers definition */
>> +#define ADT7310_STATUS			0
>> +#define ADT7310_CONFIG			1
>> +#define ADT7310_TEMPERATURE		2
>> +#define ADT7310_ID			3
>> +#define ADT7310_T_CRIT			4
>> +#define ADT7310_T_HYST			5
>> +#define ADT7310_T_ALARM_HIGH		6
>> +#define ADT7310_T_ALARM_LOW		7
>> +
>> +struct device;
>> +
>> +struct adt7410_ops {
>> +	int (*read_byte)(struct device *, u8 reg);
>> +	int (*write_byte)(struct device *, u8 reg, u8 data);
>> +	int (*read_word)(struct device *, u8 reg);
>> +	int (*write_word)(struct device *, u8 reg, u16 data);
>> +};
>> +
>> +int adt7410_probe(struct device *dev, const char *name,
>> +	const struct adt7410_ops *ops);
>> +int adt7410_remove(struct device *dev);
>> +
> 
> I think the above should be adt7x10_ops, adt7x10_probe and adt7x10_remove, to
> indicate that those are common functions.
> 
> I would actually suggest to rename all functions in adt7x10.c (not just the
> exported ones).

I normally don't like to rename all the symbols in one file just because
support for another part is added, since it just cause lots of noise in the
diff. My initial plan was to call the two new modules adt7410-i2c and
adt7410-spi, but then though it is probably going to be confusing to have
the driver for the adt7310 called adt7410-spi. In this case the renaming
probably doesn't hurt since we already have lots of diff noise anyway. It
probably would have made sense to make the review easier to split this up in
two patches, one which renames adt7410 to adt7x10 and a second one which
adds the new functionality.

Thanks,
- Lars

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
  2013-02-19  1:39     ` [lm-sensors] " Guenter Roeck
@ 2013-02-19 12:05       ` Lars-Peter Clausen
  -1 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-19 12:05 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On 02/19/2013 02:39 AM, Guenter Roeck wrote:
> On Mon, Feb 18, 2013 at 02:38:58PM +0100, Lars-Peter Clausen wrote:
>> This allows a userspace application to poll() on the alarm files to get notified
>> in case of an temperature threshold event.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>>
[...]
>>  
>> +static irqreturn_t adt7410_irq_handler(int irq, void *private)
>> +{
>> +	struct device *dev = private;
>> +	int status;
>> +
>> +	status = adt7410_read_byte(dev, ADT7410_STATUS);
>> +	if (status < 0)
>> +		return IRQ_HANDLED;
>> +
>> +	if (status & ADT7410_STAT_T_HIGH)
>> +		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
>> +	if (status & ADT7410_STAT_T_LOW)
>> +		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
>> +	if (status & ADT7410_STAT_T_CRIT)
>> +		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
>> +
> Question about semantics: Do you want a notification on all attributes each time
> one is set during an interrupt, or do you want a notification each time an
> attribute changes state ? With the above code, the 1->0 transition does not
> result in a notification. Is that on purpose ?
> 

Now that the code uses an edge triggered IRQ and comparator mode it would
actually be possible to also generate an event for a 1->0 transition, but
I'm not sure how much sense that makes. Usually you'd only want to respond
quickly to the case where a threshold event happens.

>> +	return IRQ_HANDLED;
>> +}
>> +
>>  static int adt7410_temp_ready(struct device *dev)
>>  {
>>  	int i, status;
>> @@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
>>  	.attrs = adt7410_attributes,
>>  };
>>  
>> -int adt7410_probe(struct device *dev, const char *name,
>> +int adt7410_probe(struct device *dev, const char *name, int irq,
>>  	const struct adt7410_ops *ops)
>>  {
>>  	struct adt7410_data *data;
>> @@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
>>  	data->oldconfig = ret;
>>  
>>  	/*
>> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
>> +	 * Set to 16 bit resolution and continous conversion
>>  	 */
>>  	data->config = data->oldconfig;
>> -	data->config &= ~ADT7410_MODE_MASK;
>> +	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
>> +			ADT7410_INT_POLARITY);
>>  	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
>> +
>>  	if (data->config != data->oldconfig) {
>>  		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
>>  		if (ret)
>> @@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
>>  		goto exit_remove_name;
>>  	}
>>  
>> +	if (irq > 0) {
>> +		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
>> +				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +				dev_name(dev), dev);
> 
> If you use devm_request_threaded_irq, you don't have to release it on remove.

devm_request_threaded_irq is tricky, since the IRQ will only be freed after
the drivers remove callback has been run. This can cause race conditions,
since the IRQ handler often access resources already freed in the remove
callback. In this case it will work fine since sysfs_notify handles the case
where the files are already gone gracefully and just does nothing in that
case. I'd still prefer to keep the explicit free, so things are cleaned up
in the reverse order to the way they were set up.

> 
>> +		if (ret)
>> +			goto exit_hwmon_device_unregister;
>> +	}
>> +
> 
> This code should be before hwmon registration.

Why? It doesn't make sense to signal an event before userspace can actually
discover the device.

> 
>>  	return 0;
>>  
>> +exit_hwmon_device_unregister:
>> +	hwmon_device_unregister(data->hwmon_dev);
>>  exit_remove_name:
>>  	if (name)
>>  		device_remove_file(dev, &dev_attr_name);
>> @@ -434,10 +466,13 @@ exit_restore:
>>  }
>>  EXPORT_SYMBOL_GPL(adt7410_probe);

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

* Re: [lm-sensors] [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
@ 2013-02-19 12:05       ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-19 12:05 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On 02/19/2013 02:39 AM, Guenter Roeck wrote:
> On Mon, Feb 18, 2013 at 02:38:58PM +0100, Lars-Peter Clausen wrote:
>> This allows a userspace application to poll() on the alarm files to get notified
>> in case of an temperature threshold event.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>>
[...]
>>  
>> +static irqreturn_t adt7410_irq_handler(int irq, void *private)
>> +{
>> +	struct device *dev = private;
>> +	int status;
>> +
>> +	status = adt7410_read_byte(dev, ADT7410_STATUS);
>> +	if (status < 0)
>> +		return IRQ_HANDLED;
>> +
>> +	if (status & ADT7410_STAT_T_HIGH)
>> +		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
>> +	if (status & ADT7410_STAT_T_LOW)
>> +		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
>> +	if (status & ADT7410_STAT_T_CRIT)
>> +		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
>> +
> Question about semantics: Do you want a notification on all attributes each time
> one is set during an interrupt, or do you want a notification each time an
> attribute changes state ? With the above code, the 1->0 transition does not
> result in a notification. Is that on purpose ?
> 

Now that the code uses an edge triggered IRQ and comparator mode it would
actually be possible to also generate an event for a 1->0 transition, but
I'm not sure how much sense that makes. Usually you'd only want to respond
quickly to the case where a threshold event happens.

>> +	return IRQ_HANDLED;
>> +}
>> +
>>  static int adt7410_temp_ready(struct device *dev)
>>  {
>>  	int i, status;
>> @@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
>>  	.attrs = adt7410_attributes,
>>  };
>>  
>> -int adt7410_probe(struct device *dev, const char *name,
>> +int adt7410_probe(struct device *dev, const char *name, int irq,
>>  	const struct adt7410_ops *ops)
>>  {
>>  	struct adt7410_data *data;
>> @@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
>>  	data->oldconfig = ret;
>>  
>>  	/*
>> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
>> +	 * Set to 16 bit resolution and continous conversion
>>  	 */
>>  	data->config = data->oldconfig;
>> -	data->config &= ~ADT7410_MODE_MASK;
>> +	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
>> +			ADT7410_INT_POLARITY);
>>  	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
>> +
>>  	if (data->config != data->oldconfig) {
>>  		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
>>  		if (ret)
>> @@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
>>  		goto exit_remove_name;
>>  	}
>>  
>> +	if (irq > 0) {
>> +		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
>> +				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +				dev_name(dev), dev);
> 
> If you use devm_request_threaded_irq, you don't have to release it on remove.

devm_request_threaded_irq is tricky, since the IRQ will only be freed after
the drivers remove callback has been run. This can cause race conditions,
since the IRQ handler often access resources already freed in the remove
callback. In this case it will work fine since sysfs_notify handles the case
where the files are already gone gracefully and just does nothing in that
case. I'd still prefer to keep the explicit free, so things are cleaned up
in the reverse order to the way they were set up.

> 
>> +		if (ret)
>> +			goto exit_hwmon_device_unregister;
>> +	}
>> +
> 
> This code should be before hwmon registration.

Why? It doesn't make sense to signal an event before userspace can actually
discover the device.

> 
>>  	return 0;
>>  
>> +exit_hwmon_device_unregister:
>> +	hwmon_device_unregister(data->hwmon_dev);
>>  exit_remove_name:
>>  	if (name)
>>  		device_remove_file(dev, &dev_attr_name);
>> @@ -434,10 +466,13 @@ exit_restore:
>>  }
>>  EXPORT_SYMBOL_GPL(adt7410_probe);

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-19 11:57       ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-19 16:52         ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19 16:52 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Tue, Feb 19, 2013 at 12:57:43PM +0100, Lars-Peter Clausen wrote:
> On 02/19/2013 02:30 AM, Guenter Roeck wrote:
> > On Mon, Feb 18, 2013 at 02:38:57PM +0100, Lars-Peter Clausen wrote:
> [...]
> >> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> >> new file mode 100644
> >> index 0000000..2196ac3
> >> --- /dev/null
> >> +++ b/drivers/hwmon/adt7310.c
> >> @@ -0,0 +1,115 @@
> >> +/*
> >> + * ADT7310/ADT7310 digital temperature sensor driver
> >> + *
> >> + * Copyright 2010-2013 Analog Devices Inc.
> > 
> > Not really; copyright is yours, unless you are signing it off to analog or if
> > you are working for them and have permission (or the duty) to sign off the
> > copyright. Even then it should be 2013 only for this file.
> 
> I work for them. The copyright notice comes from the IIO adt7410/adt7310
> driver, where also this code comes from. Although this portion of the code
> was written in 2012, so maybe change it to 2012-2013.
> 
Ok.

> [...]
> >> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> >> new file mode 100644
> >> index 0000000..a7165e6
> >> --- /dev/null
> >> +++ b/drivers/hwmon/adt7x10.h
> >> @@ -0,0 +1,48 @@
> >> +#ifndef __HWMON_ADT7X10_H__
> >> +#define __HWMON_ADT7X10_H__
> >> +
> >> +#include <linux/types.h>
> >> +#include <linux/pm.h>
> >> +
> >> +/* ADT7410 registers definition */
> >> +#define ADT7410_TEMPERATURE		0
> >> +#define ADT7410_STATUS			2
> >> +#define ADT7410_CONFIG			3
> >> +#define ADT7410_T_ALARM_HIGH		4
> >> +#define ADT7410_T_ALARM_LOW		6
> >> +#define ADT7410_T_CRIT			8
> >> +#define ADT7410_T_HYST			0xA
> >> +#define ADT7410_ID			0xB
> >> +
> >> +/* ADT7310 registers definition */
> >> +#define ADT7310_STATUS			0
> >> +#define ADT7310_CONFIG			1
> >> +#define ADT7310_TEMPERATURE		2
> >> +#define ADT7310_ID			3
> >> +#define ADT7310_T_CRIT			4
> >> +#define ADT7310_T_HYST			5
> >> +#define ADT7310_T_ALARM_HIGH		6
> >> +#define ADT7310_T_ALARM_LOW		7
> >> +
> >> +struct device;
> >> +
> >> +struct adt7410_ops {
> >> +	int (*read_byte)(struct device *, u8 reg);
> >> +	int (*write_byte)(struct device *, u8 reg, u8 data);
> >> +	int (*read_word)(struct device *, u8 reg);
> >> +	int (*write_word)(struct device *, u8 reg, u16 data);
> >> +};
> >> +
> >> +int adt7410_probe(struct device *dev, const char *name,
> >> +	const struct adt7410_ops *ops);
> >> +int adt7410_remove(struct device *dev);
> >> +
> > 
> > I think the above should be adt7x10_ops, adt7x10_probe and adt7x10_remove, to
> > indicate that those are common functions.
> > 
> > I would actually suggest to rename all functions in adt7x10.c (not just the
> > exported ones).
> 
> I normally don't like to rename all the symbols in one file just because
> support for another part is added, since it just cause lots of noise in the
> diff. My initial plan was to call the two new modules adt7410-i2c and
> adt7410-spi, but then though it is probably going to be confusing to have
> the driver for the adt7310 called adt7410-spi. In this case the renaming
> probably doesn't hurt since we already have lots of diff noise anyway. It
> probably would have made sense to make the review easier to split this up in
> two patches, one which renames adt7410 to adt7x10 and a second one which
> adds the new functionality.
> 
In general I agree, but the diff shows the renamed file as all-new code anyway,
so I figured renaming the functions doesn't really create additional noise. I'll
leave it up to you how to handle it in detail, but the exported common functions
and defines should really not be named adt7410. After all, future developers
won't know the history.

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320
@ 2013-02-19 16:52         ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19 16:52 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Tue, Feb 19, 2013 at 12:57:43PM +0100, Lars-Peter Clausen wrote:
> On 02/19/2013 02:30 AM, Guenter Roeck wrote:
> > On Mon, Feb 18, 2013 at 02:38:57PM +0100, Lars-Peter Clausen wrote:
> [...]
> >> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> >> new file mode 100644
> >> index 0000000..2196ac3
> >> --- /dev/null
> >> +++ b/drivers/hwmon/adt7310.c
> >> @@ -0,0 +1,115 @@
> >> +/*
> >> + * ADT7310/ADT7310 digital temperature sensor driver
> >> + *
> >> + * Copyright 2010-2013 Analog Devices Inc.
> > 
> > Not really; copyright is yours, unless you are signing it off to analog or if
> > you are working for them and have permission (or the duty) to sign off the
> > copyright. Even then it should be 2013 only for this file.
> 
> I work for them. The copyright notice comes from the IIO adt7410/adt7310
> driver, where also this code comes from. Although this portion of the code
> was written in 2012, so maybe change it to 2012-2013.
> 
Ok.

> [...]
> >> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> >> new file mode 100644
> >> index 0000000..a7165e6
> >> --- /dev/null
> >> +++ b/drivers/hwmon/adt7x10.h
> >> @@ -0,0 +1,48 @@
> >> +#ifndef __HWMON_ADT7X10_H__
> >> +#define __HWMON_ADT7X10_H__
> >> +
> >> +#include <linux/types.h>
> >> +#include <linux/pm.h>
> >> +
> >> +/* ADT7410 registers definition */
> >> +#define ADT7410_TEMPERATURE		0
> >> +#define ADT7410_STATUS			2
> >> +#define ADT7410_CONFIG			3
> >> +#define ADT7410_T_ALARM_HIGH		4
> >> +#define ADT7410_T_ALARM_LOW		6
> >> +#define ADT7410_T_CRIT			8
> >> +#define ADT7410_T_HYST			0xA
> >> +#define ADT7410_ID			0xB
> >> +
> >> +/* ADT7310 registers definition */
> >> +#define ADT7310_STATUS			0
> >> +#define ADT7310_CONFIG			1
> >> +#define ADT7310_TEMPERATURE		2
> >> +#define ADT7310_ID			3
> >> +#define ADT7310_T_CRIT			4
> >> +#define ADT7310_T_HYST			5
> >> +#define ADT7310_T_ALARM_HIGH		6
> >> +#define ADT7310_T_ALARM_LOW		7
> >> +
> >> +struct device;
> >> +
> >> +struct adt7410_ops {
> >> +	int (*read_byte)(struct device *, u8 reg);
> >> +	int (*write_byte)(struct device *, u8 reg, u8 data);
> >> +	int (*read_word)(struct device *, u8 reg);
> >> +	int (*write_word)(struct device *, u8 reg, u16 data);
> >> +};
> >> +
> >> +int adt7410_probe(struct device *dev, const char *name,
> >> +	const struct adt7410_ops *ops);
> >> +int adt7410_remove(struct device *dev);
> >> +
> > 
> > I think the above should be adt7x10_ops, adt7x10_probe and adt7x10_remove, to
> > indicate that those are common functions.
> > 
> > I would actually suggest to rename all functions in adt7x10.c (not just the
> > exported ones).
> 
> I normally don't like to rename all the symbols in one file just because
> support for another part is added, since it just cause lots of noise in the
> diff. My initial plan was to call the two new modules adt7410-i2c and
> adt7410-spi, but then though it is probably going to be confusing to have
> the driver for the adt7310 called adt7410-spi. In this case the renaming
> probably doesn't hurt since we already have lots of diff noise anyway. It
> probably would have made sense to make the review easier to split this up in
> two patches, one which renames adt7410 to adt7x10 and a second one which
> adds the new functionality.
> 
In general I agree, but the diff shows the renamed file as all-new code anyway,
so I figured renaming the functions doesn't really create additional noise. I'll
leave it up to you how to handle it in detail, but the exported common functions
and defines should really not be named adt7410. After all, future developers
won't know the history.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
  2013-02-19 12:05       ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-19 17:10         ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19 17:10 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Tue, Feb 19, 2013 at 01:05:42PM +0100, Lars-Peter Clausen wrote:
> On 02/19/2013 02:39 AM, Guenter Roeck wrote:
> > On Mon, Feb 18, 2013 at 02:38:58PM +0100, Lars-Peter Clausen wrote:
> >> This allows a userspace application to poll() on the alarm files to get notified
> >> in case of an temperature threshold event.
> >>
> >> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> >>
> [...]
> >>  
> >> +static irqreturn_t adt7410_irq_handler(int irq, void *private)
> >> +{
> >> +	struct device *dev = private;
> >> +	int status;
> >> +
> >> +	status = adt7410_read_byte(dev, ADT7410_STATUS);
> >> +	if (status < 0)
> >> +		return IRQ_HANDLED;
> >> +
> >> +	if (status & ADT7410_STAT_T_HIGH)
> >> +		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
> >> +	if (status & ADT7410_STAT_T_LOW)
> >> +		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
> >> +	if (status & ADT7410_STAT_T_CRIT)
> >> +		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
> >> +
> > Question about semantics: Do you want a notification on all attributes each time
> > one is set during an interrupt, or do you want a notification each time an
> > attribute changes state ? With the above code, the 1->0 transition does not
> > result in a notification. Is that on purpose ?
> > 
> 
> Now that the code uses an edge triggered IRQ and comparator mode it would
> actually be possible to also generate an event for a 1->0 transition, but
> I'm not sure how much sense that makes. Usually you'd only want to respond
> quickly to the case where a threshold event happens.
> 
I am not sure if it would make sense either. I am fine either way - this is
quite new functionality for hwmon, and we can add support for two-way
notification later if it turns out to be needed.

> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >>  static int adt7410_temp_ready(struct device *dev)
> >>  {
> >>  	int i, status;
> >> @@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
> >>  	.attrs = adt7410_attributes,
> >>  };
> >>  
> >> -int adt7410_probe(struct device *dev, const char *name,
> >> +int adt7410_probe(struct device *dev, const char *name, int irq,
> >>  	const struct adt7410_ops *ops)
> >>  {
> >>  	struct adt7410_data *data;
> >> @@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
> >>  	data->oldconfig = ret;
> >>  
> >>  	/*
> >> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
> >> +	 * Set to 16 bit resolution and continous conversion
> >>  	 */
> >>  	data->config = data->oldconfig;
> >> -	data->config &= ~ADT7410_MODE_MASK;
> >> +	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
> >> +			ADT7410_INT_POLARITY);
> >>  	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
> >> +
> >>  	if (data->config != data->oldconfig) {
> >>  		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
> >>  		if (ret)
> >> @@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
> >>  		goto exit_remove_name;
> >>  	}
> >>  
> >> +	if (irq > 0) {
> >> +		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
> >> +				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> >> +				dev_name(dev), dev);
> > 
> > If you use devm_request_threaded_irq, you don't have to release it on remove.
> 
> devm_request_threaded_irq is tricky, since the IRQ will only be freed after
> the drivers remove callback has been run. This can cause race conditions,
> since the IRQ handler often access resources already freed in the remove
> callback. In this case it will work fine since sysfs_notify handles the case
> where the files are already gone gracefully and just does nothing in that
> case. I'd still prefer to keep the explicit free, so things are cleaned up
> in the reverse order to the way they were set up.
> 
Ok, makes sense.

> > 
> >> +		if (ret)
> >> +			goto exit_hwmon_device_unregister;
> >> +	}
> >> +
> > 
> > This code should be before hwmon registration.
> 
> Why? It doesn't make sense to signal an event before userspace can actually
> discover the device.
> 
You are right. Historically we keep hwmon registration as last operation to
avoid user-space confusion if the attributes are gone by the time user-space
tries to access them. Getting a notification prior to registration is just as
bad, and would be "normal" operation instead of error handling, so your code
makes sense.

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support
@ 2013-02-19 17:10         ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-19 17:10 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio

On Tue, Feb 19, 2013 at 01:05:42PM +0100, Lars-Peter Clausen wrote:
> On 02/19/2013 02:39 AM, Guenter Roeck wrote:
> > On Mon, Feb 18, 2013 at 02:38:58PM +0100, Lars-Peter Clausen wrote:
> >> This allows a userspace application to poll() on the alarm files to get notified
> >> in case of an temperature threshold event.
> >>
> >> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> >>
> [...]
> >>  
> >> +static irqreturn_t adt7410_irq_handler(int irq, void *private)
> >> +{
> >> +	struct device *dev = private;
> >> +	int status;
> >> +
> >> +	status = adt7410_read_byte(dev, ADT7410_STATUS);
> >> +	if (status < 0)
> >> +		return IRQ_HANDLED;
> >> +
> >> +	if (status & ADT7410_STAT_T_HIGH)
> >> +		sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
> >> +	if (status & ADT7410_STAT_T_LOW)
> >> +		sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
> >> +	if (status & ADT7410_STAT_T_CRIT)
> >> +		sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
> >> +
> > Question about semantics: Do you want a notification on all attributes each time
> > one is set during an interrupt, or do you want a notification each time an
> > attribute changes state ? With the above code, the 1->0 transition does not
> > result in a notification. Is that on purpose ?
> > 
> 
> Now that the code uses an edge triggered IRQ and comparator mode it would
> actually be possible to also generate an event for a 1->0 transition, but
> I'm not sure how much sense that makes. Usually you'd only want to respond
> quickly to the case where a threshold event happens.
> 
I am not sure if it would make sense either. I am fine either way - this is
quite new functionality for hwmon, and we can add support for two-way
notification later if it turns out to be needed.

> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >>  static int adt7410_temp_ready(struct device *dev)
> >>  {
> >>  	int i, status;
> >> @@ -357,7 +377,7 @@ static const struct attribute_group adt7410_group = {
> >>  	.attrs = adt7410_attributes,
> >>  };
> >>  
> >> -int adt7410_probe(struct device *dev, const char *name,
> >> +int adt7410_probe(struct device *dev, const char *name, int irq,
> >>  	const struct adt7410_ops *ops)
> >>  {
> >>  	struct adt7410_data *data;
> >> @@ -383,11 +403,13 @@ int adt7410_probe(struct device *dev, const char *name,
> >>  	data->oldconfig = ret;
> >>  
> >>  	/*
> >> -	 * Set to 16 bit resolution, continous conversion and comparator mode.
> >> +	 * Set to 16 bit resolution and continous conversion
> >>  	 */
> >>  	data->config = data->oldconfig;
> >> -	data->config &= ~ADT7410_MODE_MASK;
> >> +	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
> >> +			ADT7410_INT_POLARITY);
> >>  	data->config |= ADT7410_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
> >> +
> >>  	if (data->config != data->oldconfig) {
> >>  		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
> >>  		if (ret)
> >> @@ -421,8 +443,18 @@ int adt7410_probe(struct device *dev, const char *name,
> >>  		goto exit_remove_name;
> >>  	}
> >>  
> >> +	if (irq > 0) {
> >> +		ret = request_threaded_irq(irq, NULL, adt7410_irq_handler,
> >> +				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> >> +				dev_name(dev), dev);
> > 
> > If you use devm_request_threaded_irq, you don't have to release it on remove.
> 
> devm_request_threaded_irq is tricky, since the IRQ will only be freed after
> the drivers remove callback has been run. This can cause race conditions,
> since the IRQ handler often access resources already freed in the remove
> callback. In this case it will work fine since sysfs_notify handles the case
> where the files are already gone gracefully and just does nothing in that
> case. I'd still prefer to keep the explicit free, so things are cleaned up
> in the reverse order to the way they were set up.
> 
Ok, makes sense.

> > 
> >> +		if (ret)
> >> +			goto exit_hwmon_device_unregister;
> >> +	}
> >> +
> > 
> > This code should be before hwmon registration.
> 
> Why? It doesn't make sense to signal an event before userspace can actually
> discover the device.
> 
You are right. Historically we keep hwmon registration as last operation to
avoid user-space confusion if the attributes are gone by the time user-space
tries to access them. Getting a notification prior to registration is just as
bad, and would be "normal" operation instead of error handling, so your code
makes sense.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-19  1:02     ` [lm-sensors] " Guenter Roeck
@ 2013-02-19 19:39       ` Hartmut Knaack
  -1 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-19 19:39 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

Guenter Roeck schrieb:
> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>> Lars-Peter Clausen schrieb:
>>> Currently each time the temperature register is read the driver also reads the
>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>> the hysteresis change on their own, so once we've read them, we should be able
>>> to just use the cached value of the registers. This patch modifies the code
>>> accordingly and only reads the threshold and hysteresis registers once during
>>> probe.
>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> I am not concerned about that, unless there is a known issue with the chip
> and it is known to return bad data under some circumstances. I am doing the
> same in the PMBus drivers, since there are simply too many limit registers
> to read on some of the chips (there may literally be more than a hundred).
> That works fine most of the time; if it does not work, it is a chip problem,
> an i2c bus master problem, a hardware signal problem, or a combination of all.
> I actually think it is better if the problem is exposed by cached bad readings.
Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> Then there is a chance to do something about it. This would be much better
> than just re-reading the value next time and ignoring the problem.
>
> Something else - the commit window just opened (a week earlier than I guessed),
> so this part of the series is going to miss 3.9. Hartmut, if you plan to provide
> an Acked-by or Reviewed-by or Tested-by for the first part of the series, you'll
> have to do it soon, as I plan to send my push request to Linus around Thursday.
OK
>
> Thanks,
> Guenter
>


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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-19 19:39       ` Hartmut Knaack
  0 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-19 19:39 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

Guenter Roeck schrieb:
> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>> Lars-Peter Clausen schrieb:
>>> Currently each time the temperature register is read the driver also reads the
>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>> the hysteresis change on their own, so once we've read them, we should be able
>>> to just use the cached value of the registers. This patch modifies the code
>>> accordingly and only reads the threshold and hysteresis registers once during
>>> probe.
>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> I am not concerned about that, unless there is a known issue with the chip
> and it is known to return bad data under some circumstances. I am doing the
> same in the PMBus drivers, since there are simply too many limit registers
> to read on some of the chips (there may literally be more than a hundred).
> That works fine most of the time; if it does not work, it is a chip problem,
> an i2c bus master problem, a hardware signal problem, or a combination of all.
> I actually think it is better if the problem is exposed by cached bad readings.
Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> Then there is a chance to do something about it. This would be much better
> than just re-reading the value next time and ignoring the problem.
>
> Something else - the commit window just opened (a week earlier than I guessed),
> so this part of the series is going to miss 3.9. Hartmut, if you plan to provide
> an Acked-by or Reviewed-by or Tested-by for the first part of the series, you'll
> have to do it soon, as I plan to send my push request to Linus around Thursday.
OK
>
> Thanks,
> Guenter
>


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-19 19:39       ` [lm-sensors] " Hartmut Knaack
@ 2013-02-20  1:22         ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-20  1:22 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
> > On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >> Lars-Peter Clausen schrieb:
> >>> Currently each time the temperature register is read the driver also reads the
> >>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>> the hysteresis change on their own, so once we've read them, we should be able
> >>> to just use the cached value of the registers. This patch modifies the code
> >>> accordingly and only reads the threshold and hysteresis registers once during
> >>> probe.
> >> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> > I am not concerned about that, unless there is a known issue with the chip
> > and it is known to return bad data under some circumstances. I am doing the
> > same in the PMBus drivers, since there are simply too many limit registers
> > to read on some of the chips (there may literally be more than a hundred).
> > That works fine most of the time; if it does not work, it is a chip problem,
> > an i2c bus master problem, a hardware signal problem, or a combination of all.
> > I actually think it is better if the problem is exposed by cached bad readings.
> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.

Someone will actually notice it (hopefully while testing) and provide feedback.
This gives a chance to fix the problem instead of having it linger around ...
which would likely be the case if the problem is not persistent.

Guenter

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-20  1:22         ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-20  1:22 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
> > On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >> Lars-Peter Clausen schrieb:
> >>> Currently each time the temperature register is read the driver also reads the
> >>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>> the hysteresis change on their own, so once we've read them, we should be able
> >>> to just use the cached value of the registers. This patch modifies the code
> >>> accordingly and only reads the threshold and hysteresis registers once during
> >>> probe.
> >> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> > I am not concerned about that, unless there is a known issue with the chip
> > and it is known to return bad data under some circumstances. I am doing the
> > same in the PMBus drivers, since there are simply too many limit registers
> > to read on some of the chips (there may literally be more than a hundred).
> > That works fine most of the time; if it does not work, it is a chip problem,
> > an i2c bus master problem, a hardware signal problem, or a combination of all.
> > I actually think it is better if the problem is exposed by cached bad readings.
> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.

Someone will actually notice it (hopefully while testing) and provide feedback.
This gives a chance to fix the problem instead of having it linger around ...
which would likely be the case if the problem is not persistent.

Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
                   ` (5 preceding siblings ...)
  (?)
@ 2013-02-23  0:45 ` Hartmut Knaack
  2013-02-23 20:18     ` [lm-sensors] " Guenter Roeck
  2013-02-25  9:54     ` [lm-sensors] " Lars-Peter Clausen
  -1 siblings, 2 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-23  0:45 UTC (permalink / raw)
  To: lm-sensors

Guenter Roeck schrieb:
> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>> Guenter Roeck schrieb:
>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>> Lars-Peter Clausen schrieb:
>>>>> Currently each time the temperature register is read the driver also reads the
>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>> probe.
>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>> I am not concerned about that, unless there is a known issue with the chip
>>> and it is known to return bad data under some circumstances. I am doing the
>>> same in the PMBus drivers, since there are simply too many limit registers
>>> to read on some of the chips (there may literally be more than a hundred).
>>> That works fine most of the time; if it does not work, it is a chip problem,
>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>> I actually think it is better if the problem is exposed by cached bad readings.
>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> Someone will actually notice it (hopefully while testing) and provide feedback.
> This gives a chance to fix the problem instead of having it linger around ...
> which would likely be the case if the problem is not persistent.
>
> Guenter
Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
Thanks

Hartmut

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
old mode 100644
new mode 100755
index 99a7290..dc11cac
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -91,8 +91,13 @@ struct adt7410_data {
 	struct mutex		update_lock;
 	u8			config;
 	u8			oldconfig;
-	bool			valid;		/* true if registers valid */
-	unsigned long		last_updated;	/* In jiffies */
+	u8			valid;		/* true if registers valid */
+	unsigned long		last_updated[5];	/* In jiffies
+							   0 = input
+							   1 = high
+							   2 = low
+							   3 = critical
+							   4 = hysteresis */
 	s16			temp[4];	/* Register values,
 						   0 = input
 						   1 = high
@@ -119,25 +124,27 @@ static int adt7410_temp_ready(struct i2c_client *client)
 	return -ETIMEDOUT;
 }
 
-static struct adt7410_data *adt7410_update_device(struct device *dev)
+static struct adt7410_data *adt7410_update_device(struct device *dev, int i)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct adt7410_data *data = i2c_get_clientdata(client);
 	struct adt7410_data *ret = data;
 	mutex_lock(&data->update_lock);
 
-	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
-	    || !data->valid) {
-		int i, status;
+	if (time_after(jiffies, data->last_updated[i] + HZ + HZ / 2)
+	    || !(data->valid & ~(1 << i))) {
+		int status;
 
 		dev_dbg(&client->dev, "Starting update\n");
 
-		status = adt7410_temp_ready(client); /* check for new value */
-		if (unlikely(status)) {
-			ret = ERR_PTR(status);
-			goto abort;
-		}
-		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+		if (i >= 0 && i <=3) {
+			if (!i) {
+				status = adt7410_temp_ready(client); /* check for new value */
+				if (unlikely(status)) {
+					ret = ERR_PTR(status);
+					goto abort;
+				}
+			}
 			status = i2c_smbus_read_word_swapped(client,
 							ADT7410_REG_TEMP[i]);
 			if (unlikely(status < 0)) {
@@ -148,18 +155,24 @@ static struct adt7410_data *adt7410_update_device(struct device *dev)
 				goto abort;
 			}
 			data->temp[i] = status;
+			data->last_updated[i] = jiffies;
+			data->valid |= 1 << i;
 		}
-		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
-		if (unlikely(status < 0)) {
-			dev_dbg(dev,
-				"Failed to read value: reg %d, error %d\n",
-				ADT7410_T_HYST, status);
-			ret = ERR_PTR(status);
-			goto abort;
+		else if (i = 4) {
+			status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
+			if (unlikely(status < 0)) {
+				dev_dbg(dev,
+					"Failed to read value: reg %d, error %d\n",
+					ADT7410_T_HYST, status);
+				ret = ERR_PTR(status);
+				goto abort;
+			}
+			data->hyst = status;
+			data->last_updated[i] = jiffies;
+			data->valid |= 1 << i;
 		}
-		data->hyst = status;
-		data->last_updated = jiffies;
-		data->valid = true;
+		else
+			ret = ERR_PTR(-EINVAL);
 	}
 
 abort:
@@ -193,7 +206,7 @@ static ssize_t adt7410_show_temp(struct device *dev,
 				 struct device_attribute *da, char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	struct adt7410_data *data = adt7410_update_device(dev);
+	struct adt7410_data *data = adt7410_update_device(dev, attr->index);
 
 	if (IS_ERR(data))
 		return PTR_ERR(data);
@@ -236,7 +249,10 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
 	int nr = attr->index;
 	int hyst;
 
-	data = adt7410_update_device(dev);
+	data = adt7410_update_device(dev, nr);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+	data = adt7410_update_device(dev, 4);
 	if (IS_ERR(data))
 		return PTR_ERR(data);
 	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;



_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-23  0:45 ` Hartmut Knaack
@ 2013-02-23 20:18     ` Guenter Roeck
  2013-02-25  9:54     ` [lm-sensors] " Lars-Peter Clausen
  1 sibling, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-23 20:18 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Sat, Feb 23, 2013 at 01:45:32AM +0100, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
> > On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> >> Guenter Roeck schrieb:
> >>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >>>> Lars-Peter Clausen schrieb:
> >>>>> Currently each time the temperature register is read the driver also reads the
> >>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>>>> the hysteresis change on their own, so once we've read them, we should be able
> >>>>> to just use the cached value of the registers. This patch modifies the code
> >>>>> accordingly and only reads the threshold and hysteresis registers once during
> >>>>> probe.
> >>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> >>> I am not concerned about that, unless there is a known issue with the chip
> >>> and it is known to return bad data under some circumstances. I am doing the
> >>> same in the PMBus drivers, since there are simply too many limit registers
> >>> to read on some of the chips (there may literally be more than a hundred).
> >>> That works fine most of the time; if it does not work, it is a chip problem,
> >>> an i2c bus master problem, a hardware signal problem, or a combination of all.
> >>> I actually think it is better if the problem is exposed by cached bad readings.
> >> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> > Someone will actually notice it (hopefully while testing) and provide feedback.
> > This gives a chance to fix the problem instead of having it linger around ...
> > which would likely be the case if the problem is not persistent.
> >
> > Guenter
> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?

Yes, it adds a lot of complexity. Also, at least in theory it would apply to all
i2c drivers, so solving the problem for one chip doesn't help much.

A somewhat cleanly designed hardware should inform the software that a device went
offline, for example with an interrupt; otherwise its operation could be severely
affected and system behavior would be unpredictable.

So the question here is if this is a practical problem in your use case, or a
theoretical problem. If it is a theoretical problem, we can address it for
affected drivers as it is seen. If it is a practical problem, I think we'll need
to come up with a simpler solution. The only one I can think of right now would
be a module parameter. If set, it the driver could re-load the cache each time
the temperature sensor is updated.

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-23 20:18     ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-23 20:18 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Sat, Feb 23, 2013 at 01:45:32AM +0100, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
> > On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> >> Guenter Roeck schrieb:
> >>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >>>> Lars-Peter Clausen schrieb:
> >>>>> Currently each time the temperature register is read the driver also reads the
> >>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>>>> the hysteresis change on their own, so once we've read them, we should be able
> >>>>> to just use the cached value of the registers. This patch modifies the code
> >>>>> accordingly and only reads the threshold and hysteresis registers once during
> >>>>> probe.
> >>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> >>> I am not concerned about that, unless there is a known issue with the chip
> >>> and it is known to return bad data under some circumstances. I am doing the
> >>> same in the PMBus drivers, since there are simply too many limit registers
> >>> to read on some of the chips (there may literally be more than a hundred).
> >>> That works fine most of the time; if it does not work, it is a chip problem,
> >>> an i2c bus master problem, a hardware signal problem, or a combination of all.
> >>> I actually think it is better if the problem is exposed by cached bad readings.
> >> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> > Someone will actually notice it (hopefully while testing) and provide feedback.
> > This gives a chance to fix the problem instead of having it linger around ...
> > which would likely be the case if the problem is not persistent.
> >
> > Guenter
> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?

Yes, it adds a lot of complexity. Also, at least in theory it would apply to all
i2c drivers, so solving the problem for one chip doesn't help much.

A somewhat cleanly designed hardware should inform the software that a device went
offline, for example with an interrupt; otherwise its operation could be severely
affected and system behavior would be unpredictable.

So the question here is if this is a practical problem in your use case, or a
theoretical problem. If it is a theoretical problem, we can address it for
affected drivers as it is seen. If it is a practical problem, I think we'll need
to come up with a simpler solution. The only one I can think of right now would
be a module parameter. If set, it the driver could re-load the cache each time
the temperature sensor is updated.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-23  0:45 ` Hartmut Knaack
@ 2013-02-25  9:54     ` Lars-Peter Clausen
  2013-02-25  9:54     ` [lm-sensors] " Lars-Peter Clausen
  1 sibling, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-25  9:54 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Guenter Roeck, Jean Delvare, Jonathan Cameron, lm-sensors, linux-iio

On 02/23/2013 01:45 AM, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>>> Guenter Roeck schrieb:
>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>>> Lars-Peter Clausen schrieb:
>>>>>> Currently each time the temperature register is read the driver also reads the
>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>>> probe.
>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>>> I am not concerned about that, unless there is a known issue with the chip
>>>> and it is known to return bad data under some circumstances. I am doing the
>>>> same in the PMBus drivers, since there are simply too many limit registers
>>>> to read on some of the chips (there may literally be more than a hundred).
>>>> That works fine most of the time; if it does not work, it is a chip problem,
>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>>> I actually think it is better if the problem is exposed by cached bad readings.
>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
>> Someone will actually notice it (hopefully while testing) and provide feedback.
>> This gives a chance to fix the problem instead of having it linger around ...
>> which would likely be the case if the problem is not persistent.
>>
>> Guenter
> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> Thanks
> 

That's a bit of an artificially constructed situation, isn't it? Anyway,
wouldn't it be better to not cache at all in that case. For the cache to be
useful userspace would have to poll the file with period of less than 1.5
seconds. I don't think anybody is going to do this for the threshold properties.

- Lars


> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> old mode 100644
> new mode 100755
> index 99a7290..dc11cac
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -91,8 +91,13 @@ struct adt7410_data {
>  	struct mutex		update_lock;
>  	u8			config;
>  	u8			oldconfig;
> -	bool			valid;		/* true if registers valid */
> -	unsigned long		last_updated;	/* In jiffies */
> +	u8			valid;		/* true if registers valid */
> +	unsigned long		last_updated[5];	/* In jiffies
> +							   0 = input
> +							   1 = high
> +							   2 = low
> +							   3 = critical
> +							   4 = hysteresis */
>  	s16			temp[4];	/* Register values,
>  						   0 = input
>  						   1 = high
> @@ -119,25 +124,27 @@ static int adt7410_temp_ready(struct i2c_client *client)
>  	return -ETIMEDOUT;
>  }
>  
> -static struct adt7410_data *adt7410_update_device(struct device *dev)
> +static struct adt7410_data *adt7410_update_device(struct device *dev, int i)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
>  	struct adt7410_data *data = i2c_get_clientdata(client);
>  	struct adt7410_data *ret = data;
>  	mutex_lock(&data->update_lock);
>  
> -	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
> -	    || !data->valid) {
> -		int i, status;
> +	if (time_after(jiffies, data->last_updated[i] + HZ + HZ / 2)
> +	    || !(data->valid & ~(1 << i))) {
> +		int status;
>  
>  		dev_dbg(&client->dev, "Starting update\n");
>  
> -		status = adt7410_temp_ready(client); /* check for new value */
> -		if (unlikely(status)) {
> -			ret = ERR_PTR(status);
> -			goto abort;
> -		}
> -		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
> +		if (i >= 0 && i <=3) {
> +			if (!i) {
> +				status = adt7410_temp_ready(client); /* check for new value */
> +				if (unlikely(status)) {
> +					ret = ERR_PTR(status);
> +					goto abort;
> +				}
> +			}
>  			status = i2c_smbus_read_word_swapped(client,
>  							ADT7410_REG_TEMP[i]);
>  			if (unlikely(status < 0)) {
> @@ -148,18 +155,24 @@ static struct adt7410_data *adt7410_update_device(struct device *dev)
>  				goto abort;
>  			}
>  			data->temp[i] = status;
> +			data->last_updated[i] = jiffies;
> +			data->valid |= 1 << i;
>  		}
> -		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> -		if (unlikely(status < 0)) {
> -			dev_dbg(dev,
> -				"Failed to read value: reg %d, error %d\n",
> -				ADT7410_T_HYST, status);
> -			ret = ERR_PTR(status);
> -			goto abort;
> +		else if (i == 4) {
> +			status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> +			if (unlikely(status < 0)) {
> +				dev_dbg(dev,
> +					"Failed to read value: reg %d, error %d\n",
> +					ADT7410_T_HYST, status);
> +				ret = ERR_PTR(status);
> +				goto abort;
> +			}
> +			data->hyst = status;
> +			data->last_updated[i] = jiffies;
> +			data->valid |= 1 << i;
>  		}
> -		data->hyst = status;
> -		data->last_updated = jiffies;
> -		data->valid = true;
> +		else
> +			ret = ERR_PTR(-EINVAL);
>  	}
>  
>  abort:
> @@ -193,7 +206,7 @@ static ssize_t adt7410_show_temp(struct device *dev,
>  				 struct device_attribute *da, char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct adt7410_data *data = adt7410_update_device(dev);
> +	struct adt7410_data *data = adt7410_update_device(dev, attr->index);
>  
>  	if (IS_ERR(data))
>  		return PTR_ERR(data);
> @@ -236,7 +249,10 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
>  	int nr = attr->index;
>  	int hyst;
>  
> -	data = adt7410_update_device(dev);
> +	data = adt7410_update_device(dev, nr);
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +	data = adt7410_update_device(dev, 4);
>  	if (IS_ERR(data))
>  		return PTR_ERR(data);
>  	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-25  9:54     ` Lars-Peter Clausen
  0 siblings, 0 replies; 47+ messages in thread
From: Lars-Peter Clausen @ 2013-02-25  9:54 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Guenter Roeck, Jean Delvare, Jonathan Cameron, lm-sensors, linux-iio

On 02/23/2013 01:45 AM, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>>> Guenter Roeck schrieb:
>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>>> Lars-Peter Clausen schrieb:
>>>>>> Currently each time the temperature register is read the driver also reads the
>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>>> probe.
>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>>> I am not concerned about that, unless there is a known issue with the chip
>>>> and it is known to return bad data under some circumstances. I am doing the
>>>> same in the PMBus drivers, since there are simply too many limit registers
>>>> to read on some of the chips (there may literally be more than a hundred).
>>>> That works fine most of the time; if it does not work, it is a chip problem,
>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>>> I actually think it is better if the problem is exposed by cached bad readings.
>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
>> Someone will actually notice it (hopefully while testing) and provide feedback.
>> This gives a chance to fix the problem instead of having it linger around ...
>> which would likely be the case if the problem is not persistent.
>>
>> Guenter
> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> Thanks
> 

That's a bit of an artificially constructed situation, isn't it? Anyway,
wouldn't it be better to not cache at all in that case. For the cache to be
useful userspace would have to poll the file with period of less than 1.5
seconds. I don't think anybody is going to do this for the threshold properties.

- Lars


> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> old mode 100644
> new mode 100755
> index 99a7290..dc11cac
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -91,8 +91,13 @@ struct adt7410_data {
>  	struct mutex		update_lock;
>  	u8			config;
>  	u8			oldconfig;
> -	bool			valid;		/* true if registers valid */
> -	unsigned long		last_updated;	/* In jiffies */
> +	u8			valid;		/* true if registers valid */
> +	unsigned long		last_updated[5];	/* In jiffies
> +							   0 = input
> +							   1 = high
> +							   2 = low
> +							   3 = critical
> +							   4 = hysteresis */
>  	s16			temp[4];	/* Register values,
>  						   0 = input
>  						   1 = high
> @@ -119,25 +124,27 @@ static int adt7410_temp_ready(struct i2c_client *client)
>  	return -ETIMEDOUT;
>  }
>  
> -static struct adt7410_data *adt7410_update_device(struct device *dev)
> +static struct adt7410_data *adt7410_update_device(struct device *dev, int i)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
>  	struct adt7410_data *data = i2c_get_clientdata(client);
>  	struct adt7410_data *ret = data;
>  	mutex_lock(&data->update_lock);
>  
> -	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
> -	    || !data->valid) {
> -		int i, status;
> +	if (time_after(jiffies, data->last_updated[i] + HZ + HZ / 2)
> +	    || !(data->valid & ~(1 << i))) {
> +		int status;
>  
>  		dev_dbg(&client->dev, "Starting update\n");
>  
> -		status = adt7410_temp_ready(client); /* check for new value */
> -		if (unlikely(status)) {
> -			ret = ERR_PTR(status);
> -			goto abort;
> -		}
> -		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
> +		if (i >= 0 && i <=3) {
> +			if (!i) {
> +				status = adt7410_temp_ready(client); /* check for new value */
> +				if (unlikely(status)) {
> +					ret = ERR_PTR(status);
> +					goto abort;
> +				}
> +			}
>  			status = i2c_smbus_read_word_swapped(client,
>  							ADT7410_REG_TEMP[i]);
>  			if (unlikely(status < 0)) {
> @@ -148,18 +155,24 @@ static struct adt7410_data *adt7410_update_device(struct device *dev)
>  				goto abort;
>  			}
>  			data->temp[i] = status;
> +			data->last_updated[i] = jiffies;
> +			data->valid |= 1 << i;
>  		}
> -		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> -		if (unlikely(status < 0)) {
> -			dev_dbg(dev,
> -				"Failed to read value: reg %d, error %d\n",
> -				ADT7410_T_HYST, status);
> -			ret = ERR_PTR(status);
> -			goto abort;
> +		else if (i = 4) {
> +			status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
> +			if (unlikely(status < 0)) {
> +				dev_dbg(dev,
> +					"Failed to read value: reg %d, error %d\n",
> +					ADT7410_T_HYST, status);
> +				ret = ERR_PTR(status);
> +				goto abort;
> +			}
> +			data->hyst = status;
> +			data->last_updated[i] = jiffies;
> +			data->valid |= 1 << i;
>  		}
> -		data->hyst = status;
> -		data->last_updated = jiffies;
> -		data->valid = true;
> +		else
> +			ret = ERR_PTR(-EINVAL);
>  	}
>  
>  abort:
> @@ -193,7 +206,7 @@ static ssize_t adt7410_show_temp(struct device *dev,
>  				 struct device_attribute *da, char *buf)
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	struct adt7410_data *data = adt7410_update_device(dev);
> +	struct adt7410_data *data = adt7410_update_device(dev, attr->index);
>  
>  	if (IS_ERR(data))
>  		return PTR_ERR(data);
> @@ -236,7 +249,10 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
>  	int nr = attr->index;
>  	int hyst;
>  
> -	data = adt7410_update_device(dev);
> +	data = adt7410_update_device(dev, nr);
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +	data = adt7410_update_device(dev, 4);
>  	if (IS_ERR(data))
>  		return PTR_ERR(data);
>  	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-25  9:54     ` [lm-sensors] " Lars-Peter Clausen
@ 2013-02-25 21:30       ` Hartmut Knaack
  -1 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-25 21:30 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Guenter Roeck, Jean Delvare, Jonathan Cameron, lm-sensors, linux-iio

Lars-Peter Clausen schrieb:
> On 02/23/2013 01:45 AM, Hartmut Knaack wrote:
>> Guenter Roeck schrieb:
>>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>>>> Guenter Roeck schrieb:
>>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>>>> Lars-Peter Clausen schrieb:
>>>>>>> Currently each time the temperature register is read the driver also reads the
>>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>>>> probe.
>>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>>>> I am not concerned about that, unless there is a known issue with the chip
>>>>> and it is known to return bad data under some circumstances. I am doing the
>>>>> same in the PMBus drivers, since there are simply too many limit registers
>>>>> to read on some of the chips (there may literally be more than a hundred).
>>>>> That works fine most of the time; if it does not work, it is a chip problem,
>>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>>>> I actually think it is better if the problem is exposed by cached bad readings.
>>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
>>> Someone will actually notice it (hopefully while testing) and provide feedback.
>>> This gives a chance to fix the problem instead of having it linger around ...
>>> which would likely be the case if the problem is not persistent.
>>>
>>> Guenter
>> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
>> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
>> Thanks
>>
> That's a bit of an artificially constructed situation, isn't it? Anyway,
> wouldn't it be better to not cache at all in that case. For the cache to be
> useful userspace would have to poll the file with period of less than 1.5
> seconds. I don't think anybody is going to do this for the threshold properties.
>
> - Lars
I could agree with not caching those registers - nobody would probably want to read them too often, intentionally (although bugged userspace code might DoS the bus). The other few i2c drivers I know also do uncached reads.
>
>
>> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
>> old mode 100644
>> new mode 100755
>> index 99a7290..dc11cac
>> --- a/drivers/hwmon/adt7410.c
>> +++ b/drivers/hwmon/adt7410.c
>> @@ -91,8 +91,13 @@ struct adt7410_data {
>>  	struct mutex		update_lock;
>>  	u8			config;
>>  	u8			oldconfig;
>> -	bool			valid;		/* true if registers valid */
>> -	unsigned long		last_updated;	/* In jiffies */
>> +	u8			valid;		/* true if registers valid */
>> +	unsigned long		last_updated[5];	/* In jiffies
>> +							   0 = input
>> +							   1 = high
>> +							   2 = low
>> +							   3 = critical
>> +							   4 = hysteresis */
>>  	s16			temp[4];	/* Register values,
>>  						   0 = input
>>  						   1 = high
>> @@ -119,25 +124,27 @@ static int adt7410_temp_ready(struct i2c_client *client)
>>  	return -ETIMEDOUT;
>>  }
>>  
>> -static struct adt7410_data *adt7410_update_device(struct device *dev)
>> +static struct adt7410_data *adt7410_update_device(struct device *dev, int i)
>>  {
>>  	struct i2c_client *client = to_i2c_client(dev);
>>  	struct adt7410_data *data = i2c_get_clientdata(client);
>>  	struct adt7410_data *ret = data;
>>  	mutex_lock(&data->update_lock);
>>  
>> -	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
>> -	    || !data->valid) {
>> -		int i, status;
>> +	if (time_after(jiffies, data->last_updated[i] + HZ + HZ / 2)
>> +	    || !(data->valid & ~(1 << i))) {
>> +		int status;
>>  
>>  		dev_dbg(&client->dev, "Starting update\n");
>>  
>> -		status = adt7410_temp_ready(client); /* check for new value */
>> -		if (unlikely(status)) {
>> -			ret = ERR_PTR(status);
>> -			goto abort;
>> -		}
>> -		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
>> +		if (i >= 0 && i <=3) {
>> +			if (!i) {
>> +				status = adt7410_temp_ready(client); /* check for new value */
>> +				if (unlikely(status)) {
>> +					ret = ERR_PTR(status);
>> +					goto abort;
>> +				}
>> +			}
>>  			status = i2c_smbus_read_word_swapped(client,
>>  							ADT7410_REG_TEMP[i]);
>>  			if (unlikely(status < 0)) {
>> @@ -148,18 +155,24 @@ static struct adt7410_data *adt7410_update_device(struct device *dev)
>>  				goto abort;
>>  			}
>>  			data->temp[i] = status;
>> +			data->last_updated[i] = jiffies;
>> +			data->valid |= 1 << i;
>>  		}
>> -		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
>> -		if (unlikely(status < 0)) {
>> -			dev_dbg(dev,
>> -				"Failed to read value: reg %d, error %d\n",
>> -				ADT7410_T_HYST, status);
>> -			ret = ERR_PTR(status);
>> -			goto abort;
>> +		else if (i == 4) {
>> +			status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
>> +			if (unlikely(status < 0)) {
>> +				dev_dbg(dev,
>> +					"Failed to read value: reg %d, error %d\n",
>> +					ADT7410_T_HYST, status);
>> +				ret = ERR_PTR(status);
>> +				goto abort;
>> +			}
>> +			data->hyst = status;
>> +			data->last_updated[i] = jiffies;
>> +			data->valid |= 1 << i;
>>  		}
>> -		data->hyst = status;
>> -		data->last_updated = jiffies;
>> -		data->valid = true;
>> +		else
>> +			ret = ERR_PTR(-EINVAL);
>>  	}
>>  
>>  abort:
>> @@ -193,7 +206,7 @@ static ssize_t adt7410_show_temp(struct device *dev,
>>  				 struct device_attribute *da, char *buf)
>>  {
>>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> -	struct adt7410_data *data = adt7410_update_device(dev);
>> +	struct adt7410_data *data = adt7410_update_device(dev, attr->index);
>>  
>>  	if (IS_ERR(data))
>>  		return PTR_ERR(data);
>> @@ -236,7 +249,10 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
>>  	int nr = attr->index;
>>  	int hyst;
>>  
>> -	data = adt7410_update_device(dev);
>> +	data = adt7410_update_device(dev, nr);
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +	data = adt7410_update_device(dev, 4);
>>  	if (IS_ERR(data))
>>  		return PTR_ERR(data);
>>  	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-25 21:30       ` Hartmut Knaack
  0 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-25 21:30 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Guenter Roeck, Jean Delvare, Jonathan Cameron, lm-sensors, linux-iio

Lars-Peter Clausen schrieb:
> On 02/23/2013 01:45 AM, Hartmut Knaack wrote:
>> Guenter Roeck schrieb:
>>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>>>> Guenter Roeck schrieb:
>>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>>>> Lars-Peter Clausen schrieb:
>>>>>>> Currently each time the temperature register is read the driver also reads the
>>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>>>> probe.
>>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>>>> I am not concerned about that, unless there is a known issue with the chip
>>>>> and it is known to return bad data under some circumstances. I am doing the
>>>>> same in the PMBus drivers, since there are simply too many limit registers
>>>>> to read on some of the chips (there may literally be more than a hundred).
>>>>> That works fine most of the time; if it does not work, it is a chip problem,
>>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>>>> I actually think it is better if the problem is exposed by cached bad readings.
>>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
>>> Someone will actually notice it (hopefully while testing) and provide feedback.
>>> This gives a chance to fix the problem instead of having it linger around ...
>>> which would likely be the case if the problem is not persistent.
>>>
>>> Guenter
>> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
>> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
>> Thanks
>>
> That's a bit of an artificially constructed situation, isn't it? Anyway,
> wouldn't it be better to not cache at all in that case. For the cache to be
> useful userspace would have to poll the file with period of less than 1.5
> seconds. I don't think anybody is going to do this for the threshold properties.
>
> - Lars
I could agree with not caching those registers - nobody would probably want to read them too often, intentionally (although bugged userspace code might DoS the bus). The other few i2c drivers I know also do uncached reads.
>
>
>> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
>> old mode 100644
>> new mode 100755
>> index 99a7290..dc11cac
>> --- a/drivers/hwmon/adt7410.c
>> +++ b/drivers/hwmon/adt7410.c
>> @@ -91,8 +91,13 @@ struct adt7410_data {
>>  	struct mutex		update_lock;
>>  	u8			config;
>>  	u8			oldconfig;
>> -	bool			valid;		/* true if registers valid */
>> -	unsigned long		last_updated;	/* In jiffies */
>> +	u8			valid;		/* true if registers valid */
>> +	unsigned long		last_updated[5];	/* In jiffies
>> +							   0 = input
>> +							   1 = high
>> +							   2 = low
>> +							   3 = critical
>> +							   4 = hysteresis */
>>  	s16			temp[4];	/* Register values,
>>  						   0 = input
>>  						   1 = high
>> @@ -119,25 +124,27 @@ static int adt7410_temp_ready(struct i2c_client *client)
>>  	return -ETIMEDOUT;
>>  }
>>  
>> -static struct adt7410_data *adt7410_update_device(struct device *dev)
>> +static struct adt7410_data *adt7410_update_device(struct device *dev, int i)
>>  {
>>  	struct i2c_client *client = to_i2c_client(dev);
>>  	struct adt7410_data *data = i2c_get_clientdata(client);
>>  	struct adt7410_data *ret = data;
>>  	mutex_lock(&data->update_lock);
>>  
>> -	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
>> -	    || !data->valid) {
>> -		int i, status;
>> +	if (time_after(jiffies, data->last_updated[i] + HZ + HZ / 2)
>> +	    || !(data->valid & ~(1 << i))) {
>> +		int status;
>>  
>>  		dev_dbg(&client->dev, "Starting update\n");
>>  
>> -		status = adt7410_temp_ready(client); /* check for new value */
>> -		if (unlikely(status)) {
>> -			ret = ERR_PTR(status);
>> -			goto abort;
>> -		}
>> -		for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
>> +		if (i >= 0 && i <=3) {
>> +			if (!i) {
>> +				status = adt7410_temp_ready(client); /* check for new value */
>> +				if (unlikely(status)) {
>> +					ret = ERR_PTR(status);
>> +					goto abort;
>> +				}
>> +			}
>>  			status = i2c_smbus_read_word_swapped(client,
>>  							ADT7410_REG_TEMP[i]);
>>  			if (unlikely(status < 0)) {
>> @@ -148,18 +155,24 @@ static struct adt7410_data *adt7410_update_device(struct device *dev)
>>  				goto abort;
>>  			}
>>  			data->temp[i] = status;
>> +			data->last_updated[i] = jiffies;
>> +			data->valid |= 1 << i;
>>  		}
>> -		status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
>> -		if (unlikely(status < 0)) {
>> -			dev_dbg(dev,
>> -				"Failed to read value: reg %d, error %d\n",
>> -				ADT7410_T_HYST, status);
>> -			ret = ERR_PTR(status);
>> -			goto abort;
>> +		else if (i = 4) {
>> +			status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
>> +			if (unlikely(status < 0)) {
>> +				dev_dbg(dev,
>> +					"Failed to read value: reg %d, error %d\n",
>> +					ADT7410_T_HYST, status);
>> +				ret = ERR_PTR(status);
>> +				goto abort;
>> +			}
>> +			data->hyst = status;
>> +			data->last_updated[i] = jiffies;
>> +			data->valid |= 1 << i;
>>  		}
>> -		data->hyst = status;
>> -		data->last_updated = jiffies;
>> -		data->valid = true;
>> +		else
>> +			ret = ERR_PTR(-EINVAL);
>>  	}
>>  
>>  abort:
>> @@ -193,7 +206,7 @@ static ssize_t adt7410_show_temp(struct device *dev,
>>  				 struct device_attribute *da, char *buf)
>>  {
>>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> -	struct adt7410_data *data = adt7410_update_device(dev);
>> +	struct adt7410_data *data = adt7410_update_device(dev, attr->index);
>>  
>>  	if (IS_ERR(data))
>>  		return PTR_ERR(data);
>> @@ -236,7 +249,10 @@ static ssize_t adt7410_show_t_hyst(struct device *dev,
>>  	int nr = attr->index;
>>  	int hyst;
>>  
>> -	data = adt7410_update_device(dev);
>> +	data = adt7410_update_device(dev, nr);
>> +	if (IS_ERR(data))
>> +		return PTR_ERR(data);
>> +	data = adt7410_update_device(dev, 4);
>>  	if (IS_ERR(data))
>>  		return PTR_ERR(data);
>>  	hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-23 20:18     ` [lm-sensors] " Guenter Roeck
@ 2013-02-25 22:03       ` Hartmut Knaack
  -1 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-25 22:03 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

Guenter Roeck schrieb:
> On Sat, Feb 23, 2013 at 01:45:32AM +0100, Hartmut Knaack wrote:
>> Guenter Roeck schrieb:
>>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>>>> Guenter Roeck schrieb:
>>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>>>> Lars-Peter Clausen schrieb:
>>>>>>> Currently each time the temperature register is read the driver also reads the
>>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>>>> probe.
>>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>>>> I am not concerned about that, unless there is a known issue with the chip
>>>>> and it is known to return bad data under some circumstances. I am doing the
>>>>> same in the PMBus drivers, since there are simply too many limit registers
>>>>> to read on some of the chips (there may literally be more than a hundred).
>>>>> That works fine most of the time; if it does not work, it is a chip problem,
>>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>>>> I actually think it is better if the problem is exposed by cached bad readings.
>>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
>>> Someone will actually notice it (hopefully while testing) and provide feedback.
>>> This gives a chance to fix the problem instead of having it linger around ...
>>> which would likely be the case if the problem is not persistent.
>>>
>>> Guenter
>> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
>> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> Yes, it adds a lot of complexity. Also, at least in theory it would apply to all
> i2c drivers, so solving the problem for one chip doesn't help much.
>
> A somewhat cleanly designed hardware should inform the software that a device went
> offline, for example with an interrupt; otherwise its operation could be severely
> affected and system behavior would be unpredictable.
>
> So the question here is if this is a practical problem in your use case, or a
> theoretical problem. If it is a theoretical problem, we can address it for
> affected drivers as it is seen. If it is a practical problem, I think we'll need
> to come up with a simpler solution. The only one I can think of right now would
> be a module parameter. If set, it the driver could re-load the cache each time
> the temperature sensor is updated.
>
> Thanks,
> Guenter
>
My intended use for this sensor type is just temperature logging, so it is just a theoretical problem for me. But I don't trust I2C any more than really necessary (and always keeping Murphy's law in mind) and feel somehow responsible for this driver.
I can agree on having a 'paranoid' module parameter to force register reads, just for those folks who need it.

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-25 22:03       ` Hartmut Knaack
  0 siblings, 0 replies; 47+ messages in thread
From: Hartmut Knaack @ 2013-02-25 22:03 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

Guenter Roeck schrieb:
> On Sat, Feb 23, 2013 at 01:45:32AM +0100, Hartmut Knaack wrote:
>> Guenter Roeck schrieb:
>>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
>>>> Guenter Roeck schrieb:
>>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
>>>>>> Lars-Peter Clausen schrieb:
>>>>>>> Currently each time the temperature register is read the driver also reads the
>>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
>>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
>>>>>>> the hysteresis change on their own, so once we've read them, we should be able
>>>>>>> to just use the cached value of the registers. This patch modifies the code
>>>>>>> accordingly and only reads the threshold and hysteresis registers once during
>>>>>>> probe.
>>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
>>>>> I am not concerned about that, unless there is a known issue with the chip
>>>>> and it is known to return bad data under some circumstances. I am doing the
>>>>> same in the PMBus drivers, since there are simply too many limit registers
>>>>> to read on some of the chips (there may literally be more than a hundred).
>>>>> That works fine most of the time; if it does not work, it is a chip problem,
>>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
>>>>> I actually think it is better if the problem is exposed by cached bad readings.
>>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
>>> Someone will actually notice it (hopefully while testing) and provide feedback.
>>> This gives a chance to fix the problem instead of having it linger around ...
>>> which would likely be the case if the problem is not persistent.
>>>
>>> Guenter
>> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
>> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> Yes, it adds a lot of complexity. Also, at least in theory it would apply to all
> i2c drivers, so solving the problem for one chip doesn't help much.
>
> A somewhat cleanly designed hardware should inform the software that a device went
> offline, for example with an interrupt; otherwise its operation could be severely
> affected and system behavior would be unpredictable.
>
> So the question here is if this is a practical problem in your use case, or a
> theoretical problem. If it is a theoretical problem, we can address it for
> affected drivers as it is seen. If it is a practical problem, I think we'll need
> to come up with a simpler solution. The only one I can think of right now would
> be a module parameter. If set, it the driver could re-load the cache each time
> the temperature sensor is updated.
>
> Thanks,
> Guenter
>
My intended use for this sensor type is just temperature logging, so it is just a theoretical problem for me. But I don't trust I2C any more than really necessary (and always keeping Murphy's law in mind) and feel somehow responsible for this driver.
I can agree on having a 'paranoid' module parameter to force register reads, just for those folks who need it.

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-25 21:30       ` [lm-sensors] " Hartmut Knaack
@ 2013-02-26  1:39         ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-26  1:39 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Mon, Feb 25, 2013 at 10:30:30PM +0100, Hartmut Knaack wrote:
> Lars-Peter Clausen schrieb:
> > On 02/23/2013 01:45 AM, Hartmut Knaack wrote:
> >> Guenter Roeck schrieb:
> >>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> >>>> Guenter Roeck schrieb:
> >>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >>>>>> Lars-Peter Clausen schrieb:
> >>>>>>> Currently each time the temperature register is read the driver also reads the
> >>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>>>>>> the hysteresis change on their own, so once we've read them, we should be able
> >>>>>>> to just use the cached value of the registers. This patch modifies the code
> >>>>>>> accordingly and only reads the threshold and hysteresis registers once during
> >>>>>>> probe.
> >>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> >>>>> I am not concerned about that, unless there is a known issue with the chip
> >>>>> and it is known to return bad data under some circumstances. I am doing the
> >>>>> same in the PMBus drivers, since there are simply too many limit registers
> >>>>> to read on some of the chips (there may literally be more than a hundred).
> >>>>> That works fine most of the time; if it does not work, it is a chip problem,
> >>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
> >>>>> I actually think it is better if the problem is exposed by cached bad readings.
> >>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> >>> Someone will actually notice it (hopefully while testing) and provide feedback.
> >>> This gives a chance to fix the problem instead of having it linger around ...
> >>> which would likely be the case if the problem is not persistent.
> >>>
> >>> Guenter
> >> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> >> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> >> Thanks
> >>
> > That's a bit of an artificially constructed situation, isn't it? Anyway,
> > wouldn't it be better to not cache at all in that case. For the cache to be
> > useful userspace would have to poll the file with period of less than 1.5
> > seconds. I don't think anybody is going to do this for the threshold properties.
> >
> > - Lars
> I could agree with not caching those registers - nobody would probably want to read them too often, intentionally (although bugged userspace code might DoS the bus). The other few i2c drivers I know also do uncached reads.

The "sensors" program always reads all attributes.

Guenter

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-26  1:39         ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-26  1:39 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Mon, Feb 25, 2013 at 10:30:30PM +0100, Hartmut Knaack wrote:
> Lars-Peter Clausen schrieb:
> > On 02/23/2013 01:45 AM, Hartmut Knaack wrote:
> >> Guenter Roeck schrieb:
> >>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> >>>> Guenter Roeck schrieb:
> >>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >>>>>> Lars-Peter Clausen schrieb:
> >>>>>>> Currently each time the temperature register is read the driver also reads the
> >>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>>>>>> the hysteresis change on their own, so once we've read them, we should be able
> >>>>>>> to just use the cached value of the registers. This patch modifies the code
> >>>>>>> accordingly and only reads the threshold and hysteresis registers once during
> >>>>>>> probe.
> >>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> >>>>> I am not concerned about that, unless there is a known issue with the chip
> >>>>> and it is known to return bad data under some circumstances. I am doing the
> >>>>> same in the PMBus drivers, since there are simply too many limit registers
> >>>>> to read on some of the chips (there may literally be more than a hundred).
> >>>>> That works fine most of the time; if it does not work, it is a chip problem,
> >>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
> >>>>> I actually think it is better if the problem is exposed by cached bad readings.
> >>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> >>> Someone will actually notice it (hopefully while testing) and provide feedback.
> >>> This gives a chance to fix the problem instead of having it linger around ...
> >>> which would likely be the case if the problem is not persistent.
> >>>
> >>> Guenter
> >> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> >> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> >> Thanks
> >>
> > That's a bit of an artificially constructed situation, isn't it? Anyway,
> > wouldn't it be better to not cache at all in that case. For the cache to be
> > useful userspace would have to poll the file with period of less than 1.5
> > seconds. I don't think anybody is going to do this for the threshold properties.
> >
> > - Lars
> I could agree with not caching those registers - nobody would probably want to read them too often, intentionally (although bugged userspace code might DoS the bus). The other few i2c drivers I know also do uncached reads.

The "sensors" program always reads all attributes.

Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-25 22:03       ` [lm-sensors] " Hartmut Knaack
@ 2013-02-26  1:40         ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-26  1:40 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Mon, Feb 25, 2013 at 11:03:50PM +0100, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
> > On Sat, Feb 23, 2013 at 01:45:32AM +0100, Hartmut Knaack wrote:
> >> Guenter Roeck schrieb:
> >>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> >>>> Guenter Roeck schrieb:
> >>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >>>>>> Lars-Peter Clausen schrieb:
> >>>>>>> Currently each time the temperature register is read the driver also reads the
> >>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>>>>>> the hysteresis change on their own, so once we've read them, we should be able
> >>>>>>> to just use the cached value of the registers. This patch modifies the code
> >>>>>>> accordingly and only reads the threshold and hysteresis registers once during
> >>>>>>> probe.
> >>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> >>>>> I am not concerned about that, unless there is a known issue with the chip
> >>>>> and it is known to return bad data under some circumstances. I am doing the
> >>>>> same in the PMBus drivers, since there are simply too many limit registers
> >>>>> to read on some of the chips (there may literally be more than a hundred).
> >>>>> That works fine most of the time; if it does not work, it is a chip problem,
> >>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
> >>>>> I actually think it is better if the problem is exposed by cached bad readings.
> >>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> >>> Someone will actually notice it (hopefully while testing) and provide feedback.
> >>> This gives a chance to fix the problem instead of having it linger around ...
> >>> which would likely be the case if the problem is not persistent.
> >>>
> >>> Guenter
> >> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> >> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> > Yes, it adds a lot of complexity. Also, at least in theory it would apply to all
> > i2c drivers, so solving the problem for one chip doesn't help much.
> >
> > A somewhat cleanly designed hardware should inform the software that a device went
> > offline, for example with an interrupt; otherwise its operation could be severely
> > affected and system behavior would be unpredictable.
> >
> > So the question here is if this is a practical problem in your use case, or a
> > theoretical problem. If it is a theoretical problem, we can address it for
> > affected drivers as it is seen. If it is a practical problem, I think we'll need
> > to come up with a simpler solution. The only one I can think of right now would
> > be a module parameter. If set, it the driver could re-load the cache each time
> > the temperature sensor is updated.
> >
> > Thanks,
> > Guenter
> >
> My intended use for this sensor type is just temperature logging, so it is just a theoretical problem for me. But I don't trust I2C any more than really necessary (and always keeping Murphy's law in mind) and feel somehow responsible for this driver.
> I can agree on having a 'paranoid' module parameter to force register reads, just for those folks who need it.
> 
If it is a theoretic problem, let's address it once/if it ever gets a real one.

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers
@ 2013-02-26  1:40         ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-02-26  1:40 UTC (permalink / raw)
  To: Hartmut Knaack
  Cc: Lars-Peter Clausen, Jean Delvare, Jonathan Cameron, lm-sensors,
	linux-iio

On Mon, Feb 25, 2013 at 11:03:50PM +0100, Hartmut Knaack wrote:
> Guenter Roeck schrieb:
> > On Sat, Feb 23, 2013 at 01:45:32AM +0100, Hartmut Knaack wrote:
> >> Guenter Roeck schrieb:
> >>> On Tue, Feb 19, 2013 at 08:39:33PM +0100, Hartmut Knaack wrote:
> >>>> Guenter Roeck schrieb:
> >>>>> On Mon, Feb 18, 2013 at 09:22:18PM +0100, Hartmut Knaack wrote:
> >>>>>> Lars-Peter Clausen schrieb:
> >>>>>>> Currently each time the temperature register is read the driver also reads the
> >>>>>>> threshold and hysteresis registers. This increases the amount of I2C traffic and
> >>>>>>> time needed to read the temperature by a factor of ~5. Neither the threshold nor
> >>>>>>> the hysteresis change on their own, so once we've read them, we should be able
> >>>>>>> to just use the cached value of the registers. This patch modifies the code
> >>>>>>> accordingly and only reads the threshold and hysteresis registers once during
> >>>>>>> probe.
> >>>>>> I have been thinking about this a lot, and I am concerned about data integrity. From what I know about I2C, there is no data integrity verification specified in the protocol. So, what the master sends is not necessarily what the slave receives (not to mention other devices on the bus, which could potentially mess around with the slaves, or even reset of the slave). Reading back just cached values makes it pretty hard to verify, if there are issues. I think it might be better to call a read-temperature function with a parameter that indicates, which temperature register is required.
> >>>>> I am not concerned about that, unless there is a known issue with the chip
> >>>>> and it is known to return bad data under some circumstances. I am doing the
> >>>>> same in the PMBus drivers, since there are simply too many limit registers
> >>>>> to read on some of the chips (there may literally be more than a hundred).
> >>>>> That works fine most of the time; if it does not work, it is a chip problem,
> >>>>> an i2c bus master problem, a hardware signal problem, or a combination of all.
> >>>>> I actually think it is better if the problem is exposed by cached bad readings.
> >>>> Could you please outline the last sentence? I'm having trouble to understand your intention with cached bad readings.
> >>> Someone will actually notice it (hopefully while testing) and provide feedback.
> >>> This gives a chance to fix the problem instead of having it linger around ...
> >>> which would likely be the case if the problem is not persistent.
> >>>
> >>> Guenter
> >> Well, think about a use case where you optically decouple your master and slave using PCA9600 (may it be using optical fibers to cover a big distance, or just to operate the slave in a hazardous environment), where the slave is powered from a different source. Now, if the slaves Vcc drops for a certain time, all its registers get reset (especially min, max and crit) - without the master noticing anything.
> >> Therefor I would prefer something like the following solution, where unnecessary load on the bus gets avoided and the cached values get used, until they expire and get read again from the sensor. This is mainly a draft, but do you see any reason against it?
> > Yes, it adds a lot of complexity. Also, at least in theory it would apply to all
> > i2c drivers, so solving the problem for one chip doesn't help much.
> >
> > A somewhat cleanly designed hardware should inform the software that a device went
> > offline, for example with an interrupt; otherwise its operation could be severely
> > affected and system behavior would be unpredictable.
> >
> > So the question here is if this is a practical problem in your use case, or a
> > theoretical problem. If it is a theoretical problem, we can address it for
> > affected drivers as it is seen. If it is a practical problem, I think we'll need
> > to come up with a simpler solution. The only one I can think of right now would
> > be a module parameter. If set, it the driver could re-load the cache each time
> > the temperature sensor is updated.
> >
> > Thanks,
> > Guenter
> >
> My intended use for this sensor type is just temperature logging, so it is just a theoretical problem for me. But I don't trust I2C any more than really necessary (and always keeping Murphy's law in mind) and feel somehow responsible for this driver.
> I can agree on having a 'paranoid' module parameter to force register reads, just for those folks who need it.
> 
If it is a theoretic problem, let's address it once/if it ever gets a real one.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 4/4] staging:iio: Remove adt7410 driver
  2013-02-18 13:38   ` [lm-sensors] " Lars-Peter Clausen
@ 2013-03-02 16:45     ` Jonathan Cameron
  -1 siblings, 0 replies; 47+ messages in thread
From: Jonathan Cameron @ 2013-03-02 16:45 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Hartmut Knaack, Jonathan Cameron,
	lm-sensors, linux-iio

On 02/18/2013 01:38 PM, Lars-Peter Clausen wrote:
> The adt7410 hwmon driver is feature wise more or less on par with the IIO
> driver. So we can finally remove the IIO driver.
> 
I kind of lost touch on where things were with the last few patches adding support in
the hwmon driver for stuff that was in here.  It looked like there were only nitpicks
left so I'll assume it gets sorted this coming cycle and take this patch now
(before I loose it)
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
added to togreg branch of iio.git

> ---
>  drivers/staging/iio/adc/Kconfig   |    7 -
>  drivers/staging/iio/adc/Makefile  |    1 -
>  drivers/staging/iio/adc/adt7410.c | 1102 -------------------------------------
>  3 files changed, 1110 deletions(-)
>  delete mode 100644 drivers/staging/iio/adc/adt7410.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index fb8c239..029ae54 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -90,13 +90,6 @@ config AD7192
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7192.
>  
> -config ADT7410
> -	tristate "Analog Devices ADT7310/ADT7410 temperature sensor driver"
> -	depends on I2C || SPI_MASTER
> -	help
> -	  Say yes here to build support for Analog Devices ADT7310/ADT7410
> -	  temperature sensors.
> -
>  config AD7280
>  	tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
>  	depends on SPI
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index d285596..3e9fb14 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -16,7 +16,6 @@ obj-$(CONFIG_AD7291) += ad7291.o
>  obj-$(CONFIG_AD7780) += ad7780.o
>  obj-$(CONFIG_AD7816) += ad7816.o
>  obj-$(CONFIG_AD7192) += ad7192.o
> -obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
>  obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
> diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c
> deleted file mode 100644
> index 35455e1..0000000
> --- a/drivers/staging/iio/adc/adt7410.c
> +++ /dev/null
> @@ -1,1102 +0,0 @@
> -/*
> - * ADT7410 digital temperature sensor driver supporting ADT7310/ADT7410
> - *
> - * Copyright 2010 Analog Devices Inc.
> - *
> - * Licensed under the GPL-2 or later.
> - */
> -
> -#include <linux/interrupt.h>
> -#include <linux/device.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/sysfs.h>
> -#include <linux/list.h>
> -#include <linux/i2c.h>
> -#include <linux/spi/spi.h>
> -#include <linux/module.h>
> -
> -#include <linux/iio/iio.h>
> -#include <linux/iio/sysfs.h>
> -#include <linux/iio/events.h>
> -
> -/*
> - * ADT7410 registers definition
> - */
> -
> -#define ADT7410_TEMPERATURE		0
> -#define ADT7410_STATUS			2
> -#define ADT7410_CONFIG			3
> -#define ADT7410_T_ALARM_HIGH		4
> -#define ADT7410_T_ALARM_LOW		6
> -#define ADT7410_T_CRIT			8
> -#define ADT7410_T_HYST			0xA
> -#define ADT7410_ID			0xB
> -#define ADT7410_RESET			0x2F
> -
> -/*
> - * ADT7310 registers definition
> - */
> -
> -#define ADT7310_STATUS			0
> -#define ADT7310_CONFIG			1
> -#define ADT7310_TEMPERATURE		2
> -#define ADT7310_ID			3
> -#define ADT7310_T_CRIT			4
> -#define ADT7310_T_HYST			5
> -#define ADT7310_T_ALARM_HIGH		6
> -#define ADT7310_T_ALARM_LOW		7
> -
> -/*
> - * ADT7410 status
> - */
> -#define ADT7410_STAT_T_LOW		0x10
> -#define ADT7410_STAT_T_HIGH		0x20
> -#define ADT7410_STAT_T_CRIT		0x40
> -#define ADT7410_STAT_NOT_RDY		0x80
> -
> -/*
> - * ADT7410 config
> - */
> -#define ADT7410_FAULT_QUEUE_MASK	0x3
> -#define ADT7410_CT_POLARITY		0x4
> -#define ADT7410_INT_POLARITY		0x8
> -#define ADT7410_EVENT_MODE		0x10
> -#define ADT7410_MODE_MASK		0x60
> -#define ADT7410_ONESHOT			0x20
> -#define ADT7410_SPS			0x40
> -#define ADT7410_PD			0x60
> -#define ADT7410_RESOLUTION		0x80
> -
> -/*
> - * ADT7410 masks
> - */
> -#define ADT7410_T16_VALUE_SIGN			0x8000
> -#define ADT7410_T16_VALUE_FLOAT_OFFSET		7
> -#define ADT7410_T16_VALUE_FLOAT_MASK		0x7F
> -#define ADT7410_T13_VALUE_SIGN			0x1000
> -#define ADT7410_T13_VALUE_OFFSET		3
> -#define ADT7410_T13_VALUE_FLOAT_OFFSET		4
> -#define ADT7410_T13_VALUE_FLOAT_MASK		0xF
> -#define ADT7410_T_HYST_MASK			0xF
> -#define ADT7410_DEVICE_ID_MASK			0xF
> -#define ADT7410_MANUFACTORY_ID_MASK		0xF0
> -#define ADT7410_MANUFACTORY_ID_OFFSET		4
> -
> -
> -#define ADT7310_CMD_REG_MASK			0x28
> -#define ADT7310_CMD_REG_OFFSET			3
> -#define ADT7310_CMD_READ			0x40
> -#define ADT7310_CMD_CON_READ			0x4
> -
> -#define ADT7410_IRQS				2
> -
> -/*
> - * struct adt7410_chip_info - chip specifc information
> - */
> -
> -struct adt7410_chip_info;
> -
> -struct adt7410_ops {
> -	int (*read_word)(struct adt7410_chip_info *, u8 reg, u16 *data);
> -	int (*write_word)(struct adt7410_chip_info *, u8 reg, u16 data);
> -	int (*read_byte)(struct adt7410_chip_info *, u8 reg, u8 *data);
> -	int (*write_byte)(struct adt7410_chip_info *, u8 reg, u8 data);
> -};
> -
> -struct adt7410_chip_info {
> -	struct device *dev;
> -	u8  config;
> -
> -	const struct adt7410_ops *ops;
> -};
> -
> -static int adt7410_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
> -{
> -	return chip->ops->read_word(chip, reg, data);
> -}
> -
> -static int adt7410_write_word(struct adt7410_chip_info *chip, u8 reg, u16 data)
> -{
> -	return chip->ops->write_word(chip, reg, data);
> -}
> -
> -static int adt7410_read_byte(struct adt7410_chip_info *chip, u8 reg, u8 *data)
> -{
> -	return chip->ops->read_byte(chip, reg, data);
> -}
> -
> -static int adt7410_write_byte(struct adt7410_chip_info *chip, u8 reg, u8 data)
> -{
> -	return chip->ops->write_byte(chip, reg, data);
> -}
> -
> -static ssize_t adt7410_show_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u8 config;
> -
> -	config = chip->config & ADT7410_MODE_MASK;
> -
> -	switch (config) {
> -	case ADT7410_PD:
> -		return sprintf(buf, "power-down\n");
> -	case ADT7410_ONESHOT:
> -		return sprintf(buf, "one-shot\n");
> -	case ADT7410_SPS:
> -		return sprintf(buf, "sps\n");
> -	default:
> -		return sprintf(buf, "full\n");
> -	}
> -}
> -
> -static ssize_t adt7410_store_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u16 config;
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config & (~ADT7410_MODE_MASK);
> -	if (strcmp(buf, "power-down"))
> -		config |= ADT7410_PD;
> -	else if (strcmp(buf, "one-shot"))
> -		config |= ADT7410_ONESHOT;
> -	else if (strcmp(buf, "sps"))
> -		config |= ADT7410_SPS;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return len;
> -}
> -
> -static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> -		adt7410_show_mode,
> -		adt7410_store_mode,
> -		0);
> -
> -static ssize_t adt7410_show_available_modes(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
> -}
> -
> -static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7410_show_available_modes, NULL, 0);
> -
> -static ssize_t adt7410_show_resolution(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -	int bits;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	if (chip->config & ADT7410_RESOLUTION)
> -		bits = 16;
> -	else
> -		bits = 13;
> -
> -	return sprintf(buf, "%d bits\n", bits);
> -}
> -
> -static ssize_t adt7410_store_resolution(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	unsigned long data;
> -	u16 config;
> -	int ret;
> -
> -	ret = strict_strtoul(buf, 10, &data);
> -	if (ret)
> -		return -EINVAL;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config & (~ADT7410_RESOLUTION);
> -	if (data)
> -		config |= ADT7410_RESOLUTION;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return len;
> -}
> -
> -static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
> -		adt7410_show_resolution,
> -		adt7410_store_resolution,
> -		0);
> -
> -static ssize_t adt7410_show_id(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u8 id;
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_ID, &id);
> -	if (ret)
> -		return -EIO;
> -
> -	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
> -			id & ADT7410_DEVICE_ID_MASK,
> -			(id & ADT7410_MANUFACTORY_ID_MASK) >> ADT7410_MANUFACTORY_ID_OFFSET);
> -}
> -
> -static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
> -		adt7410_show_id,
> -		NULL,
> -		0);
> -
> -static ssize_t adt7410_convert_temperature(struct adt7410_chip_info *chip,
> -		u16 data, char *buf)
> -{
> -	char sign = ' ';
> -
> -	if (!(chip->config & ADT7410_RESOLUTION))
> -		data &= ~0x7;
> -
> -	if (data & ADT7410_T16_VALUE_SIGN) {
> -		/* convert supplement to positive value */
> -		data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
> -		sign = '-';
> -	}
> -	return sprintf(buf, "%c%d.%.7d\n", sign,
> -			(data >> ADT7410_T16_VALUE_FLOAT_OFFSET),
> -			(data & ADT7410_T16_VALUE_FLOAT_MASK) * 78125);
> -}
> -
> -static ssize_t adt7410_show_value(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u8 status;
> -	u16 data;
> -	int ret, i = 0;
> -
> -	do {
> -		ret = adt7410_read_byte(chip, ADT7410_STATUS, &status);
> -		if (ret)
> -			return -EIO;
> -		i++;
> -		if (i == 10000)
> -			return -EIO;
> -	} while (status & ADT7410_STAT_NOT_RDY);
> -
> -	ret = adt7410_read_word(chip, ADT7410_TEMPERATURE, &data);
> -	if (ret)
> -		return -EIO;
> -
> -	return adt7410_convert_temperature(chip, data, buf);
> -}
> -
> -static IIO_DEVICE_ATTR(value, S_IRUGO, adt7410_show_value, NULL, 0);
> -
> -static struct attribute *adt7410_attributes[] = {
> -	&iio_dev_attr_available_modes.dev_attr.attr,
> -	&iio_dev_attr_mode.dev_attr.attr,
> -	&iio_dev_attr_resolution.dev_attr.attr,
> -	&iio_dev_attr_id.dev_attr.attr,
> -	&iio_dev_attr_value.dev_attr.attr,
> -	NULL,
> -};
> -
> -static const struct attribute_group adt7410_attribute_group = {
> -	.attrs = adt7410_attributes,
> -};
> -
> -static irqreturn_t adt7410_event_handler(int irq, void *private)
> -{
> -	struct iio_dev *indio_dev = private;
> -	struct adt7410_chip_info *chip = iio_priv(indio_dev);
> -	s64 timestamp = iio_get_time_ns();
> -	u8 status;
> -
> -	if (adt7410_read_byte(chip, ADT7410_STATUS, &status))
> -		return IRQ_HANDLED;
> -
> -	if (status & ADT7410_STAT_T_HIGH)
> -		iio_push_event(indio_dev,
> -			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> -						    IIO_EV_TYPE_THRESH,
> -						    IIO_EV_DIR_RISING),
> -			       timestamp);
> -	if (status & ADT7410_STAT_T_LOW)
> -		iio_push_event(indio_dev,
> -			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> -						    IIO_EV_TYPE_THRESH,
> -						    IIO_EV_DIR_FALLING),
> -			       timestamp);
> -	if (status & ADT7410_STAT_T_CRIT)
> -		iio_push_event(indio_dev,
> -			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> -						    IIO_EV_TYPE_THRESH,
> -						    IIO_EV_DIR_RISING),
> -			       timestamp);
> -
> -	return IRQ_HANDLED;
> -}
> -
> -static ssize_t adt7410_show_event_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	if (chip->config & ADT7410_EVENT_MODE)
> -		return sprintf(buf, "interrupt\n");
> -	else
> -		return sprintf(buf, "comparator\n");
> -}
> -
> -static ssize_t adt7410_set_event_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u16 config;
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config &= ~ADT7410_EVENT_MODE;
> -	if (strcmp(buf, "comparator") != 0)
> -		config |= ADT7410_EVENT_MODE;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return ret;
> -}
> -
> -static ssize_t adt7410_show_available_event_modes(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return sprintf(buf, "comparator\ninterrupt\n");
> -}
> -
> -static ssize_t adt7410_show_fault_queue(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	return sprintf(buf, "%d\n", chip->config & ADT7410_FAULT_QUEUE_MASK);
> -}
> -
> -static ssize_t adt7410_set_fault_queue(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	unsigned long data;
> -	int ret;
> -	u8 config;
> -
> -	ret = strict_strtoul(buf, 10, &data);
> -	if (ret || data > 3)
> -		return -EINVAL;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
> -	config |= data;
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return ret;
> -}
> -
> -static inline ssize_t adt7410_show_t_bound(struct device *dev,
> -		struct device_attribute *attr,
> -		u8 bound_reg,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u16 data;
> -	int ret;
> -
> -	ret = adt7410_read_word(chip, bound_reg, &data);
> -	if (ret)
> -		return -EIO;
> -
> -	return adt7410_convert_temperature(chip, data, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_bound(struct device *dev,
> -		struct device_attribute *attr,
> -		u8 bound_reg,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	long tmp1, tmp2;
> -	u16 data;
> -	char *pos;
> -	int ret;
> -
> -	pos = strchr(buf, '.');
> -
> -	ret = strict_strtol(buf, 10, &tmp1);
> -
> -	if (ret || tmp1 > 127 || tmp1 < -128)
> -		return -EINVAL;
> -
> -	if (pos) {
> -		len = strlen(pos);
> -
> -		if (chip->config & ADT7410_RESOLUTION) {
> -			if (len > ADT7410_T16_VALUE_FLOAT_OFFSET)
> -				len = ADT7410_T16_VALUE_FLOAT_OFFSET;
> -			pos[len] = 0;
> -			ret = strict_strtol(pos, 10, &tmp2);
> -
> -			if (!ret)
> -				tmp2 = (tmp2 / 78125) * 78125;
> -		} else {
> -			if (len > ADT7410_T13_VALUE_FLOAT_OFFSET)
> -				len = ADT7410_T13_VALUE_FLOAT_OFFSET;
> -			pos[len] = 0;
> -			ret = strict_strtol(pos, 10, &tmp2);
> -
> -			if (!ret)
> -				tmp2 = (tmp2 / 625) * 625;
> -		}
> -	}
> -
> -	if (tmp1 < 0)
> -		data = (u16)(-tmp1);
> -	else
> -		data = (u16)tmp1;
> -
> -	if (chip->config & ADT7410_RESOLUTION) {
> -		data = (data << ADT7410_T16_VALUE_FLOAT_OFFSET) |
> -			(tmp2 & ADT7410_T16_VALUE_FLOAT_MASK);
> -
> -		if (tmp1 < 0)
> -			/* convert positive value to supplyment */
> -			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
> -	} else {
> -		data = (data << ADT7410_T13_VALUE_FLOAT_OFFSET) |
> -			(tmp2 & ADT7410_T13_VALUE_FLOAT_MASK);
> -
> -		if (tmp1 < 0)
> -			/* convert positive value to supplyment */
> -			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
> -		data <<= ADT7410_T13_VALUE_OFFSET;
> -	}
> -
> -	ret = adt7410_write_word(chip, bound_reg, data);
> -	if (ret)
> -		return -EIO;
> -
> -	return ret;
> -}
> -
> -static ssize_t adt7410_show_t_alarm_high(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return adt7410_show_t_bound(dev, attr,
> -			ADT7410_T_ALARM_HIGH, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_alarm_high(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	return adt7410_set_t_bound(dev, attr,
> -			ADT7410_T_ALARM_HIGH, buf, len);
> -}
> -
> -static ssize_t adt7410_show_t_alarm_low(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return adt7410_show_t_bound(dev, attr,
> -			ADT7410_T_ALARM_LOW, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_alarm_low(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	return adt7410_set_t_bound(dev, attr,
> -			ADT7410_T_ALARM_LOW, buf, len);
> -}
> -
> -static ssize_t adt7410_show_t_crit(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return adt7410_show_t_bound(dev, attr,
> -			ADT7410_T_CRIT, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_crit(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	return adt7410_set_t_bound(dev, attr,
> -			ADT7410_T_CRIT, buf, len);
> -}
> -
> -static ssize_t adt7410_show_t_hyst(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -	u8 t_hyst;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_T_HYST, &t_hyst);
> -	if (ret)
> -		return -EIO;
> -
> -	return sprintf(buf, "%d\n", t_hyst & ADT7410_T_HYST_MASK);
> -}
> -
> -static inline ssize_t adt7410_set_t_hyst(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -	unsigned long data;
> -	u8 t_hyst;
> -
> -	ret = strict_strtol(buf, 10, &data);
> -
> -	if (ret || data > ADT7410_T_HYST_MASK)
> -		return -EINVAL;
> -
> -	t_hyst = (u8)data;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_T_HYST, t_hyst);
> -	if (ret)
> -		return -EIO;
> -
> -	return ret;
> -}
> -
> -static IIO_DEVICE_ATTR(event_mode,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_event_mode, adt7410_set_event_mode, 0);
> -static IIO_DEVICE_ATTR(available_event_modes,
> -		       S_IRUGO,
> -		       adt7410_show_available_event_modes, NULL, 0);
> -static IIO_DEVICE_ATTR(fault_queue,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
> -static IIO_DEVICE_ATTR(t_alarm_high,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
> -static IIO_DEVICE_ATTR(t_alarm_low,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
> -static IIO_DEVICE_ATTR(t_crit,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_crit, adt7410_set_t_crit, 0);
> -static IIO_DEVICE_ATTR(t_hyst,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
> -
> -static struct attribute *adt7410_event_int_attributes[] = {
> -	&iio_dev_attr_event_mode.dev_attr.attr,
> -	&iio_dev_attr_available_event_modes.dev_attr.attr,
> -	&iio_dev_attr_fault_queue.dev_attr.attr,
> -	&iio_dev_attr_t_alarm_high.dev_attr.attr,
> -	&iio_dev_attr_t_alarm_low.dev_attr.attr,
> -	&iio_dev_attr_t_crit.dev_attr.attr,
> -	&iio_dev_attr_t_hyst.dev_attr.attr,
> -	NULL,
> -};
> -
> -static struct attribute_group adt7410_event_attribute_group = {
> -	.attrs = adt7410_event_int_attributes,
> -	.name = "events",
> -};
> -
> -static const struct iio_info adt7410_info = {
> -	.attrs = &adt7410_attribute_group,
> -	.event_attrs = &adt7410_event_attribute_group,
> -	.driver_module = THIS_MODULE,
> -};
> -
> -/*
> - * device probe and remove
> - */
> -
> -static int adt7410_probe(struct device *dev, int irq,
> -	const char *name, const struct adt7410_ops *ops)
> -{
> -	unsigned long *adt7410_platform_data = dev->platform_data;
> -	unsigned long local_pdata[] = {0, 0};
> -	struct adt7410_chip_info *chip;
> -	struct iio_dev *indio_dev;
> -	int ret = 0;
> -
> -	indio_dev = iio_device_alloc(sizeof(*chip));
> -	if (indio_dev == NULL) {
> -		ret = -ENOMEM;
> -		goto error_ret;
> -	}
> -	chip = iio_priv(indio_dev);
> -	/* this is only used for device removal purposes */
> -	dev_set_drvdata(dev, indio_dev);
> -
> -	chip->dev = dev;
> -	chip->ops = ops;
> -
> -	indio_dev->name = name;
> -	indio_dev->dev.parent = dev;
> -	indio_dev->info = &adt7410_info;
> -	indio_dev->modes = INDIO_DIRECT_MODE;
> -
> -	if (!adt7410_platform_data)
> -		adt7410_platform_data = local_pdata;
> -
> -	/* CT critcal temperature event. line 0 */
> -	if (irq) {
> -		ret = request_threaded_irq(irq,
> -					   NULL,
> -					   &adt7410_event_handler,
> -					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> -					   name,
> -					   indio_dev);
> -		if (ret)
> -			goto error_free_dev;
> -	}
> -
> -	/* INT bound temperature alarm event. line 1 */
> -	if (adt7410_platform_data[0]) {
> -		ret = request_threaded_irq(adt7410_platform_data[0],
> -					   NULL,
> -					   &adt7410_event_handler,
> -					   adt7410_platform_data[1] |
> -					   IRQF_ONESHOT,
> -					   name,
> -					   indio_dev);
> -		if (ret)
> -			goto error_unreg_ct_irq;
> -	}
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret) {
> -		ret = -EIO;
> -		goto error_unreg_int_irq;
> -	}
> -
> -	chip->config |= ADT7410_RESOLUTION;
> -
> -	if (irq && adt7410_platform_data[0]) {
> -
> -		/* set irq polarity low level */
> -		chip->config &= ~ADT7410_CT_POLARITY;
> -
> -		if (adt7410_platform_data[1] & IRQF_TRIGGER_HIGH)
> -			chip->config |= ADT7410_INT_POLARITY;
> -		else
> -			chip->config &= ~ADT7410_INT_POLARITY;
> -	}
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, chip->config);
> -	if (ret) {
> -		ret = -EIO;
> -		goto error_unreg_int_irq;
> -	}
> -	ret = iio_device_register(indio_dev);
> -	if (ret)
> -		goto error_unreg_int_irq;
> -
> -	dev_info(dev, "%s temperature sensor registered.\n",
> -			 name);
> -
> -	return 0;
> -
> -error_unreg_int_irq:
> -	free_irq(adt7410_platform_data[0], indio_dev);
> -error_unreg_ct_irq:
> -	free_irq(irq, indio_dev);
> -error_free_dev:
> -	iio_device_free(indio_dev);
> -error_ret:
> -	return ret;
> -}
> -
> -static int adt7410_remove(struct device *dev, int irq)
> -{
> -	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> -	unsigned long *adt7410_platform_data = dev->platform_data;
> -
> -	iio_device_unregister(indio_dev);
> -	if (adt7410_platform_data[0])
> -		free_irq(adt7410_platform_data[0], indio_dev);
> -	if (irq)
> -		free_irq(irq, indio_dev);
> -	iio_device_free(indio_dev);
> -
> -	return 0;
> -}
> -
> -#if IS_ENABLED(CONFIG_I2C)
> -
> -static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg,
> -	u16 *data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_read_word_data(client, reg);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "I2C read error\n");
> -		return ret;
> -	}
> -
> -	*data = swab16((u16)ret);
> -
> -	return 0;
> -}
> -
> -static int adt7410_i2c_write_word(struct adt7410_chip_info *chip, u8 reg,
> -	u16 data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
> -	if (ret < 0)
> -		dev_err(&client->dev, "I2C write error\n");
> -
> -	return ret;
> -}
> -
> -static int adt7410_i2c_read_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 *data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_read_byte_data(client, reg);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "I2C read error\n");
> -		return ret;
> -	}
> -
> -	*data = (u8)ret;
> -
> -	return 0;
> -}
> -
> -static int adt7410_i2c_write_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_write_byte_data(client, reg, data);
> -	if (ret < 0)
> -		dev_err(&client->dev, "I2C write error\n");
> -
> -	return ret;
> -}
> -
> -static const struct adt7410_ops adt7410_i2c_ops = {
> -	.read_word = adt7410_i2c_read_word,
> -	.write_word = adt7410_i2c_write_word,
> -	.read_byte = adt7410_i2c_read_byte,
> -	.write_byte = adt7410_i2c_write_byte,
> -};
> -
> -static int adt7410_i2c_probe(struct i2c_client *client,
> -	const struct i2c_device_id *id)
> -{
> -	return adt7410_probe(&client->dev, client->irq, id->name,
> -		&adt7410_i2c_ops);
> -}
> -
> -static int adt7410_i2c_remove(struct i2c_client *client)
> -{
> -	return adt7410_remove(&client->dev, client->irq);
> -}
> -
> -static const struct i2c_device_id adt7410_id[] = {
> -	{ "adt7410", 0 },
> -	{}
> -};
> -
> -MODULE_DEVICE_TABLE(i2c, adt7410_id);
> -
> -static struct i2c_driver adt7410_driver = {
> -	.driver = {
> -		.name = "adt7410",
> -	},
> -	.probe = adt7410_i2c_probe,
> -	.remove = adt7410_i2c_remove,
> -	.id_table = adt7410_id,
> -};
> -
> -static int __init adt7410_i2c_init(void)
> -{
> -	return i2c_add_driver(&adt7410_driver);
> -}
> -
> -static void __exit adt7410_i2c_exit(void)
> -{
> -	i2c_del_driver(&adt7410_driver);
> -}
> -
> -#else
> -
> -static int  __init adt7410_i2c_init(void) { return 0; };
> -static void __exit adt7410_i2c_exit(void) {};
> -
> -#endif
> -
> -#if IS_ENABLED(CONFIG_SPI_MASTER)
> -
> -static const u8 adt7371_reg_table[] = {
> -	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
> -	[ADT7410_STATUS]	= ADT7310_STATUS,
> -	[ADT7410_CONFIG]	= ADT7310_CONFIG,
> -	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
> -	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
> -	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
> -	[ADT7410_T_HYST]	= ADT7310_T_HYST,
> -	[ADT7410_ID]		= ADT7310_ID,
> -};
> -
> -#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> -
> -static int adt7310_spi_read_word(struct adt7410_chip_info *chip,
> -	u8 reg, u16 *data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 command = AD7310_COMMAND(reg);
> -	int ret = 0;
> -
> -	command |= ADT7310_CMD_READ;
> -	ret = spi_write(spi, &command, sizeof(command));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write command error\n");
> -		return ret;
> -	}
> -
> -	ret = spi_read(spi, (u8 *)data, sizeof(*data));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI read word error\n");
> -		return ret;
> -	}
> -
> -	*data = be16_to_cpu(*data);
> -
> -	return 0;
> -}
> -
> -static int adt7310_spi_write_word(struct adt7410_chip_info *chip, u8 reg,
> -	u16 data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 buf[3];
> -	int ret = 0;
> -
> -	buf[0] = AD7310_COMMAND(reg);
> -	buf[1] = (u8)(data >> 8);
> -	buf[2] = (u8)(data & 0xFF);
> -
> -	ret = spi_write(spi, buf, 3);
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write word error\n");
> -		return ret;
> -	}
> -
> -	return ret;
> -}
> -
> -static int adt7310_spi_read_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 *data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 command = AD7310_COMMAND(reg);
> -	int ret = 0;
> -
> -	command |= ADT7310_CMD_READ;
> -	ret = spi_write(spi, &command, sizeof(command));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write command error\n");
> -		return ret;
> -	}
> -
> -	ret = spi_read(spi, data, sizeof(*data));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI read byte error\n");
> -		return ret;
> -	}
> -
> -	return 0;
> -}
> -
> -static int adt7310_spi_write_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 buf[2];
> -	int ret = 0;
> -
> -	buf[0] = AD7310_COMMAND(reg);
> -	buf[1] = data;
> -
> -	ret = spi_write(spi, buf, 2);
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write byte error\n");
> -		return ret;
> -	}
> -
> -	return ret;
> -}
> -
> -static const struct adt7410_ops adt7310_spi_ops = {
> -	.read_word = adt7310_spi_read_word,
> -	.write_word = adt7310_spi_write_word,
> -	.read_byte = adt7310_spi_read_byte,
> -	.write_byte = adt7310_spi_write_byte,
> -};
> -
> -static int adt7310_spi_probe(struct spi_device *spi)
> -{
> -	return adt7410_probe(&spi->dev, spi->irq,
> -		spi_get_device_id(spi)->name, &adt7310_spi_ops);
> -}
> -
> -static int adt7310_spi_remove(struct spi_device *spi)
> -{
> -	return adt7410_remove(&spi->dev, spi->irq);
> -}
> -
> -static const struct spi_device_id adt7310_id[] = {
> -	{ "adt7310", 0 },
> -	{}
> -};
> -MODULE_DEVICE_TABLE(spi, adt7310_id);
> -
> -static struct spi_driver adt7310_driver = {
> -	.driver = {
> -		.name = "adt7310",
> -		.owner = THIS_MODULE,
> -	},
> -	.probe = adt7310_spi_probe,
> -	.remove = adt7310_spi_remove,
> -	.id_table = adt7310_id,
> -};
> -
> -static int __init adt7310_spi_init(void)
> -{
> -	return spi_register_driver(&adt7310_driver);
> -}
> -
> -static void adt7310_spi_exit(void)
> -{
> -	spi_unregister_driver(&adt7310_driver);
> -}
> -
> -#else
> -
> -static int __init adt7310_spi_init(void) { return 0; };
> -static void adt7310_spi_exit(void) {};
> -
> -#endif
> -
> -static int __init adt7410_init(void)
> -{
> -	int ret;
> -
> -	ret = adt7310_spi_init();
> -	if (ret)
> -		return ret;
> -
> -	ret = adt7410_i2c_init();
> -	if (ret)
> -		adt7310_spi_exit();
> -
> -	return ret;
> -}
> -module_init(adt7410_init);
> -
> -static void __exit adt7410_exit(void)
> -{
> -	adt7410_i2c_exit();
> -	adt7310_spi_exit();
> -}
> -module_exit(adt7410_exit);
> -
> -MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> -MODULE_DESCRIPTION("Analog Devices ADT7310/ADT7410 digital temperature sensor driver");
> -MODULE_LICENSE("GPL v2");
> 

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

* Re: [lm-sensors] [PATCH v2 4/4] staging:iio: Remove adt7410 driver
@ 2013-03-02 16:45     ` Jonathan Cameron
  0 siblings, 0 replies; 47+ messages in thread
From: Jonathan Cameron @ 2013-03-02 16:45 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Hartmut Knaack, Jonathan Cameron,
	lm-sensors, linux-iio

On 02/18/2013 01:38 PM, Lars-Peter Clausen wrote:
> The adt7410 hwmon driver is feature wise more or less on par with the IIO
> driver. So we can finally remove the IIO driver.
> 
I kind of lost touch on where things were with the last few patches adding support in
the hwmon driver for stuff that was in here.  It looked like there were only nitpicks
left so I'll assume it gets sorted this coming cycle and take this patch now
(before I loose it)
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
added to togreg branch of iio.git

> ---
>  drivers/staging/iio/adc/Kconfig   |    7 -
>  drivers/staging/iio/adc/Makefile  |    1 -
>  drivers/staging/iio/adc/adt7410.c | 1102 -------------------------------------
>  3 files changed, 1110 deletions(-)
>  delete mode 100644 drivers/staging/iio/adc/adt7410.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index fb8c239..029ae54 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -90,13 +90,6 @@ config AD7192
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7192.
>  
> -config ADT7410
> -	tristate "Analog Devices ADT7310/ADT7410 temperature sensor driver"
> -	depends on I2C || SPI_MASTER
> -	help
> -	  Say yes here to build support for Analog Devices ADT7310/ADT7410
> -	  temperature sensors.
> -
>  config AD7280
>  	tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
>  	depends on SPI
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index d285596..3e9fb14 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -16,7 +16,6 @@ obj-$(CONFIG_AD7291) += ad7291.o
>  obj-$(CONFIG_AD7780) += ad7780.o
>  obj-$(CONFIG_AD7816) += ad7816.o
>  obj-$(CONFIG_AD7192) += ad7192.o
> -obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
>  obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
> diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c
> deleted file mode 100644
> index 35455e1..0000000
> --- a/drivers/staging/iio/adc/adt7410.c
> +++ /dev/null
> @@ -1,1102 +0,0 @@
> -/*
> - * ADT7410 digital temperature sensor driver supporting ADT7310/ADT7410
> - *
> - * Copyright 2010 Analog Devices Inc.
> - *
> - * Licensed under the GPL-2 or later.
> - */
> -
> -#include <linux/interrupt.h>
> -#include <linux/device.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/sysfs.h>
> -#include <linux/list.h>
> -#include <linux/i2c.h>
> -#include <linux/spi/spi.h>
> -#include <linux/module.h>
> -
> -#include <linux/iio/iio.h>
> -#include <linux/iio/sysfs.h>
> -#include <linux/iio/events.h>
> -
> -/*
> - * ADT7410 registers definition
> - */
> -
> -#define ADT7410_TEMPERATURE		0
> -#define ADT7410_STATUS			2
> -#define ADT7410_CONFIG			3
> -#define ADT7410_T_ALARM_HIGH		4
> -#define ADT7410_T_ALARM_LOW		6
> -#define ADT7410_T_CRIT			8
> -#define ADT7410_T_HYST			0xA
> -#define ADT7410_ID			0xB
> -#define ADT7410_RESET			0x2F
> -
> -/*
> - * ADT7310 registers definition
> - */
> -
> -#define ADT7310_STATUS			0
> -#define ADT7310_CONFIG			1
> -#define ADT7310_TEMPERATURE		2
> -#define ADT7310_ID			3
> -#define ADT7310_T_CRIT			4
> -#define ADT7310_T_HYST			5
> -#define ADT7310_T_ALARM_HIGH		6
> -#define ADT7310_T_ALARM_LOW		7
> -
> -/*
> - * ADT7410 status
> - */
> -#define ADT7410_STAT_T_LOW		0x10
> -#define ADT7410_STAT_T_HIGH		0x20
> -#define ADT7410_STAT_T_CRIT		0x40
> -#define ADT7410_STAT_NOT_RDY		0x80
> -
> -/*
> - * ADT7410 config
> - */
> -#define ADT7410_FAULT_QUEUE_MASK	0x3
> -#define ADT7410_CT_POLARITY		0x4
> -#define ADT7410_INT_POLARITY		0x8
> -#define ADT7410_EVENT_MODE		0x10
> -#define ADT7410_MODE_MASK		0x60
> -#define ADT7410_ONESHOT			0x20
> -#define ADT7410_SPS			0x40
> -#define ADT7410_PD			0x60
> -#define ADT7410_RESOLUTION		0x80
> -
> -/*
> - * ADT7410 masks
> - */
> -#define ADT7410_T16_VALUE_SIGN			0x8000
> -#define ADT7410_T16_VALUE_FLOAT_OFFSET		7
> -#define ADT7410_T16_VALUE_FLOAT_MASK		0x7F
> -#define ADT7410_T13_VALUE_SIGN			0x1000
> -#define ADT7410_T13_VALUE_OFFSET		3
> -#define ADT7410_T13_VALUE_FLOAT_OFFSET		4
> -#define ADT7410_T13_VALUE_FLOAT_MASK		0xF
> -#define ADT7410_T_HYST_MASK			0xF
> -#define ADT7410_DEVICE_ID_MASK			0xF
> -#define ADT7410_MANUFACTORY_ID_MASK		0xF0
> -#define ADT7410_MANUFACTORY_ID_OFFSET		4
> -
> -
> -#define ADT7310_CMD_REG_MASK			0x28
> -#define ADT7310_CMD_REG_OFFSET			3
> -#define ADT7310_CMD_READ			0x40
> -#define ADT7310_CMD_CON_READ			0x4
> -
> -#define ADT7410_IRQS				2
> -
> -/*
> - * struct adt7410_chip_info - chip specifc information
> - */
> -
> -struct adt7410_chip_info;
> -
> -struct adt7410_ops {
> -	int (*read_word)(struct adt7410_chip_info *, u8 reg, u16 *data);
> -	int (*write_word)(struct adt7410_chip_info *, u8 reg, u16 data);
> -	int (*read_byte)(struct adt7410_chip_info *, u8 reg, u8 *data);
> -	int (*write_byte)(struct adt7410_chip_info *, u8 reg, u8 data);
> -};
> -
> -struct adt7410_chip_info {
> -	struct device *dev;
> -	u8  config;
> -
> -	const struct adt7410_ops *ops;
> -};
> -
> -static int adt7410_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
> -{
> -	return chip->ops->read_word(chip, reg, data);
> -}
> -
> -static int adt7410_write_word(struct adt7410_chip_info *chip, u8 reg, u16 data)
> -{
> -	return chip->ops->write_word(chip, reg, data);
> -}
> -
> -static int adt7410_read_byte(struct adt7410_chip_info *chip, u8 reg, u8 *data)
> -{
> -	return chip->ops->read_byte(chip, reg, data);
> -}
> -
> -static int adt7410_write_byte(struct adt7410_chip_info *chip, u8 reg, u8 data)
> -{
> -	return chip->ops->write_byte(chip, reg, data);
> -}
> -
> -static ssize_t adt7410_show_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u8 config;
> -
> -	config = chip->config & ADT7410_MODE_MASK;
> -
> -	switch (config) {
> -	case ADT7410_PD:
> -		return sprintf(buf, "power-down\n");
> -	case ADT7410_ONESHOT:
> -		return sprintf(buf, "one-shot\n");
> -	case ADT7410_SPS:
> -		return sprintf(buf, "sps\n");
> -	default:
> -		return sprintf(buf, "full\n");
> -	}
> -}
> -
> -static ssize_t adt7410_store_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u16 config;
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config & (~ADT7410_MODE_MASK);
> -	if (strcmp(buf, "power-down"))
> -		config |= ADT7410_PD;
> -	else if (strcmp(buf, "one-shot"))
> -		config |= ADT7410_ONESHOT;
> -	else if (strcmp(buf, "sps"))
> -		config |= ADT7410_SPS;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return len;
> -}
> -
> -static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> -		adt7410_show_mode,
> -		adt7410_store_mode,
> -		0);
> -
> -static ssize_t adt7410_show_available_modes(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
> -}
> -
> -static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7410_show_available_modes, NULL, 0);
> -
> -static ssize_t adt7410_show_resolution(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -	int bits;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	if (chip->config & ADT7410_RESOLUTION)
> -		bits = 16;
> -	else
> -		bits = 13;
> -
> -	return sprintf(buf, "%d bits\n", bits);
> -}
> -
> -static ssize_t adt7410_store_resolution(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	unsigned long data;
> -	u16 config;
> -	int ret;
> -
> -	ret = strict_strtoul(buf, 10, &data);
> -	if (ret)
> -		return -EINVAL;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config & (~ADT7410_RESOLUTION);
> -	if (data)
> -		config |= ADT7410_RESOLUTION;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return len;
> -}
> -
> -static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
> -		adt7410_show_resolution,
> -		adt7410_store_resolution,
> -		0);
> -
> -static ssize_t adt7410_show_id(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u8 id;
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_ID, &id);
> -	if (ret)
> -		return -EIO;
> -
> -	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
> -			id & ADT7410_DEVICE_ID_MASK,
> -			(id & ADT7410_MANUFACTORY_ID_MASK) >> ADT7410_MANUFACTORY_ID_OFFSET);
> -}
> -
> -static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
> -		adt7410_show_id,
> -		NULL,
> -		0);
> -
> -static ssize_t adt7410_convert_temperature(struct adt7410_chip_info *chip,
> -		u16 data, char *buf)
> -{
> -	char sign = ' ';
> -
> -	if (!(chip->config & ADT7410_RESOLUTION))
> -		data &= ~0x7;
> -
> -	if (data & ADT7410_T16_VALUE_SIGN) {
> -		/* convert supplement to positive value */
> -		data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
> -		sign = '-';
> -	}
> -	return sprintf(buf, "%c%d.%.7d\n", sign,
> -			(data >> ADT7410_T16_VALUE_FLOAT_OFFSET),
> -			(data & ADT7410_T16_VALUE_FLOAT_MASK) * 78125);
> -}
> -
> -static ssize_t adt7410_show_value(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u8 status;
> -	u16 data;
> -	int ret, i = 0;
> -
> -	do {
> -		ret = adt7410_read_byte(chip, ADT7410_STATUS, &status);
> -		if (ret)
> -			return -EIO;
> -		i++;
> -		if (i = 10000)
> -			return -EIO;
> -	} while (status & ADT7410_STAT_NOT_RDY);
> -
> -	ret = adt7410_read_word(chip, ADT7410_TEMPERATURE, &data);
> -	if (ret)
> -		return -EIO;
> -
> -	return adt7410_convert_temperature(chip, data, buf);
> -}
> -
> -static IIO_DEVICE_ATTR(value, S_IRUGO, adt7410_show_value, NULL, 0);
> -
> -static struct attribute *adt7410_attributes[] = {
> -	&iio_dev_attr_available_modes.dev_attr.attr,
> -	&iio_dev_attr_mode.dev_attr.attr,
> -	&iio_dev_attr_resolution.dev_attr.attr,
> -	&iio_dev_attr_id.dev_attr.attr,
> -	&iio_dev_attr_value.dev_attr.attr,
> -	NULL,
> -};
> -
> -static const struct attribute_group adt7410_attribute_group = {
> -	.attrs = adt7410_attributes,
> -};
> -
> -static irqreturn_t adt7410_event_handler(int irq, void *private)
> -{
> -	struct iio_dev *indio_dev = private;
> -	struct adt7410_chip_info *chip = iio_priv(indio_dev);
> -	s64 timestamp = iio_get_time_ns();
> -	u8 status;
> -
> -	if (adt7410_read_byte(chip, ADT7410_STATUS, &status))
> -		return IRQ_HANDLED;
> -
> -	if (status & ADT7410_STAT_T_HIGH)
> -		iio_push_event(indio_dev,
> -			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> -						    IIO_EV_TYPE_THRESH,
> -						    IIO_EV_DIR_RISING),
> -			       timestamp);
> -	if (status & ADT7410_STAT_T_LOW)
> -		iio_push_event(indio_dev,
> -			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> -						    IIO_EV_TYPE_THRESH,
> -						    IIO_EV_DIR_FALLING),
> -			       timestamp);
> -	if (status & ADT7410_STAT_T_CRIT)
> -		iio_push_event(indio_dev,
> -			       IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> -						    IIO_EV_TYPE_THRESH,
> -						    IIO_EV_DIR_RISING),
> -			       timestamp);
> -
> -	return IRQ_HANDLED;
> -}
> -
> -static ssize_t adt7410_show_event_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	if (chip->config & ADT7410_EVENT_MODE)
> -		return sprintf(buf, "interrupt\n");
> -	else
> -		return sprintf(buf, "comparator\n");
> -}
> -
> -static ssize_t adt7410_set_event_mode(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u16 config;
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config &= ~ADT7410_EVENT_MODE;
> -	if (strcmp(buf, "comparator") != 0)
> -		config |= ADT7410_EVENT_MODE;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return ret;
> -}
> -
> -static ssize_t adt7410_show_available_event_modes(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return sprintf(buf, "comparator\ninterrupt\n");
> -}
> -
> -static ssize_t adt7410_show_fault_queue(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	return sprintf(buf, "%d\n", chip->config & ADT7410_FAULT_QUEUE_MASK);
> -}
> -
> -static ssize_t adt7410_set_fault_queue(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	unsigned long data;
> -	int ret;
> -	u8 config;
> -
> -	ret = strict_strtoul(buf, 10, &data);
> -	if (ret || data > 3)
> -		return -EINVAL;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret)
> -		return -EIO;
> -
> -	config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
> -	config |= data;
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, config);
> -	if (ret)
> -		return -EIO;
> -
> -	chip->config = config;
> -
> -	return ret;
> -}
> -
> -static inline ssize_t adt7410_show_t_bound(struct device *dev,
> -		struct device_attribute *attr,
> -		u8 bound_reg,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	u16 data;
> -	int ret;
> -
> -	ret = adt7410_read_word(chip, bound_reg, &data);
> -	if (ret)
> -		return -EIO;
> -
> -	return adt7410_convert_temperature(chip, data, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_bound(struct device *dev,
> -		struct device_attribute *attr,
> -		u8 bound_reg,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	long tmp1, tmp2;
> -	u16 data;
> -	char *pos;
> -	int ret;
> -
> -	pos = strchr(buf, '.');
> -
> -	ret = strict_strtol(buf, 10, &tmp1);
> -
> -	if (ret || tmp1 > 127 || tmp1 < -128)
> -		return -EINVAL;
> -
> -	if (pos) {
> -		len = strlen(pos);
> -
> -		if (chip->config & ADT7410_RESOLUTION) {
> -			if (len > ADT7410_T16_VALUE_FLOAT_OFFSET)
> -				len = ADT7410_T16_VALUE_FLOAT_OFFSET;
> -			pos[len] = 0;
> -			ret = strict_strtol(pos, 10, &tmp2);
> -
> -			if (!ret)
> -				tmp2 = (tmp2 / 78125) * 78125;
> -		} else {
> -			if (len > ADT7410_T13_VALUE_FLOAT_OFFSET)
> -				len = ADT7410_T13_VALUE_FLOAT_OFFSET;
> -			pos[len] = 0;
> -			ret = strict_strtol(pos, 10, &tmp2);
> -
> -			if (!ret)
> -				tmp2 = (tmp2 / 625) * 625;
> -		}
> -	}
> -
> -	if (tmp1 < 0)
> -		data = (u16)(-tmp1);
> -	else
> -		data = (u16)tmp1;
> -
> -	if (chip->config & ADT7410_RESOLUTION) {
> -		data = (data << ADT7410_T16_VALUE_FLOAT_OFFSET) |
> -			(tmp2 & ADT7410_T16_VALUE_FLOAT_MASK);
> -
> -		if (tmp1 < 0)
> -			/* convert positive value to supplyment */
> -			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
> -	} else {
> -		data = (data << ADT7410_T13_VALUE_FLOAT_OFFSET) |
> -			(tmp2 & ADT7410_T13_VALUE_FLOAT_MASK);
> -
> -		if (tmp1 < 0)
> -			/* convert positive value to supplyment */
> -			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
> -		data <<= ADT7410_T13_VALUE_OFFSET;
> -	}
> -
> -	ret = adt7410_write_word(chip, bound_reg, data);
> -	if (ret)
> -		return -EIO;
> -
> -	return ret;
> -}
> -
> -static ssize_t adt7410_show_t_alarm_high(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return adt7410_show_t_bound(dev, attr,
> -			ADT7410_T_ALARM_HIGH, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_alarm_high(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	return adt7410_set_t_bound(dev, attr,
> -			ADT7410_T_ALARM_HIGH, buf, len);
> -}
> -
> -static ssize_t adt7410_show_t_alarm_low(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return adt7410_show_t_bound(dev, attr,
> -			ADT7410_T_ALARM_LOW, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_alarm_low(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	return adt7410_set_t_bound(dev, attr,
> -			ADT7410_T_ALARM_LOW, buf, len);
> -}
> -
> -static ssize_t adt7410_show_t_crit(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	return adt7410_show_t_bound(dev, attr,
> -			ADT7410_T_CRIT, buf);
> -}
> -
> -static inline ssize_t adt7410_set_t_crit(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	return adt7410_set_t_bound(dev, attr,
> -			ADT7410_T_CRIT, buf, len);
> -}
> -
> -static ssize_t adt7410_show_t_hyst(struct device *dev,
> -		struct device_attribute *attr,
> -		char *buf)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -	u8 t_hyst;
> -
> -	ret = adt7410_read_byte(chip, ADT7410_T_HYST, &t_hyst);
> -	if (ret)
> -		return -EIO;
> -
> -	return sprintf(buf, "%d\n", t_hyst & ADT7410_T_HYST_MASK);
> -}
> -
> -static inline ssize_t adt7410_set_t_hyst(struct device *dev,
> -		struct device_attribute *attr,
> -		const char *buf,
> -		size_t len)
> -{
> -	struct iio_dev *dev_info = dev_to_iio_dev(dev);
> -	struct adt7410_chip_info *chip = iio_priv(dev_info);
> -	int ret;
> -	unsigned long data;
> -	u8 t_hyst;
> -
> -	ret = strict_strtol(buf, 10, &data);
> -
> -	if (ret || data > ADT7410_T_HYST_MASK)
> -		return -EINVAL;
> -
> -	t_hyst = (u8)data;
> -
> -	ret = adt7410_write_byte(chip, ADT7410_T_HYST, t_hyst);
> -	if (ret)
> -		return -EIO;
> -
> -	return ret;
> -}
> -
> -static IIO_DEVICE_ATTR(event_mode,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_event_mode, adt7410_set_event_mode, 0);
> -static IIO_DEVICE_ATTR(available_event_modes,
> -		       S_IRUGO,
> -		       adt7410_show_available_event_modes, NULL, 0);
> -static IIO_DEVICE_ATTR(fault_queue,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
> -static IIO_DEVICE_ATTR(t_alarm_high,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
> -static IIO_DEVICE_ATTR(t_alarm_low,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
> -static IIO_DEVICE_ATTR(t_crit,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_crit, adt7410_set_t_crit, 0);
> -static IIO_DEVICE_ATTR(t_hyst,
> -		       S_IRUGO | S_IWUSR,
> -		       adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
> -
> -static struct attribute *adt7410_event_int_attributes[] = {
> -	&iio_dev_attr_event_mode.dev_attr.attr,
> -	&iio_dev_attr_available_event_modes.dev_attr.attr,
> -	&iio_dev_attr_fault_queue.dev_attr.attr,
> -	&iio_dev_attr_t_alarm_high.dev_attr.attr,
> -	&iio_dev_attr_t_alarm_low.dev_attr.attr,
> -	&iio_dev_attr_t_crit.dev_attr.attr,
> -	&iio_dev_attr_t_hyst.dev_attr.attr,
> -	NULL,
> -};
> -
> -static struct attribute_group adt7410_event_attribute_group = {
> -	.attrs = adt7410_event_int_attributes,
> -	.name = "events",
> -};
> -
> -static const struct iio_info adt7410_info = {
> -	.attrs = &adt7410_attribute_group,
> -	.event_attrs = &adt7410_event_attribute_group,
> -	.driver_module = THIS_MODULE,
> -};
> -
> -/*
> - * device probe and remove
> - */
> -
> -static int adt7410_probe(struct device *dev, int irq,
> -	const char *name, const struct adt7410_ops *ops)
> -{
> -	unsigned long *adt7410_platform_data = dev->platform_data;
> -	unsigned long local_pdata[] = {0, 0};
> -	struct adt7410_chip_info *chip;
> -	struct iio_dev *indio_dev;
> -	int ret = 0;
> -
> -	indio_dev = iio_device_alloc(sizeof(*chip));
> -	if (indio_dev = NULL) {
> -		ret = -ENOMEM;
> -		goto error_ret;
> -	}
> -	chip = iio_priv(indio_dev);
> -	/* this is only used for device removal purposes */
> -	dev_set_drvdata(dev, indio_dev);
> -
> -	chip->dev = dev;
> -	chip->ops = ops;
> -
> -	indio_dev->name = name;
> -	indio_dev->dev.parent = dev;
> -	indio_dev->info = &adt7410_info;
> -	indio_dev->modes = INDIO_DIRECT_MODE;
> -
> -	if (!adt7410_platform_data)
> -		adt7410_platform_data = local_pdata;
> -
> -	/* CT critcal temperature event. line 0 */
> -	if (irq) {
> -		ret = request_threaded_irq(irq,
> -					   NULL,
> -					   &adt7410_event_handler,
> -					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> -					   name,
> -					   indio_dev);
> -		if (ret)
> -			goto error_free_dev;
> -	}
> -
> -	/* INT bound temperature alarm event. line 1 */
> -	if (adt7410_platform_data[0]) {
> -		ret = request_threaded_irq(adt7410_platform_data[0],
> -					   NULL,
> -					   &adt7410_event_handler,
> -					   adt7410_platform_data[1] |
> -					   IRQF_ONESHOT,
> -					   name,
> -					   indio_dev);
> -		if (ret)
> -			goto error_unreg_ct_irq;
> -	}
> -
> -	ret = adt7410_read_byte(chip, ADT7410_CONFIG, &chip->config);
> -	if (ret) {
> -		ret = -EIO;
> -		goto error_unreg_int_irq;
> -	}
> -
> -	chip->config |= ADT7410_RESOLUTION;
> -
> -	if (irq && adt7410_platform_data[0]) {
> -
> -		/* set irq polarity low level */
> -		chip->config &= ~ADT7410_CT_POLARITY;
> -
> -		if (adt7410_platform_data[1] & IRQF_TRIGGER_HIGH)
> -			chip->config |= ADT7410_INT_POLARITY;
> -		else
> -			chip->config &= ~ADT7410_INT_POLARITY;
> -	}
> -
> -	ret = adt7410_write_byte(chip, ADT7410_CONFIG, chip->config);
> -	if (ret) {
> -		ret = -EIO;
> -		goto error_unreg_int_irq;
> -	}
> -	ret = iio_device_register(indio_dev);
> -	if (ret)
> -		goto error_unreg_int_irq;
> -
> -	dev_info(dev, "%s temperature sensor registered.\n",
> -			 name);
> -
> -	return 0;
> -
> -error_unreg_int_irq:
> -	free_irq(adt7410_platform_data[0], indio_dev);
> -error_unreg_ct_irq:
> -	free_irq(irq, indio_dev);
> -error_free_dev:
> -	iio_device_free(indio_dev);
> -error_ret:
> -	return ret;
> -}
> -
> -static int adt7410_remove(struct device *dev, int irq)
> -{
> -	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> -	unsigned long *adt7410_platform_data = dev->platform_data;
> -
> -	iio_device_unregister(indio_dev);
> -	if (adt7410_platform_data[0])
> -		free_irq(adt7410_platform_data[0], indio_dev);
> -	if (irq)
> -		free_irq(irq, indio_dev);
> -	iio_device_free(indio_dev);
> -
> -	return 0;
> -}
> -
> -#if IS_ENABLED(CONFIG_I2C)
> -
> -static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg,
> -	u16 *data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_read_word_data(client, reg);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "I2C read error\n");
> -		return ret;
> -	}
> -
> -	*data = swab16((u16)ret);
> -
> -	return 0;
> -}
> -
> -static int adt7410_i2c_write_word(struct adt7410_chip_info *chip, u8 reg,
> -	u16 data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
> -	if (ret < 0)
> -		dev_err(&client->dev, "I2C write error\n");
> -
> -	return ret;
> -}
> -
> -static int adt7410_i2c_read_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 *data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_read_byte_data(client, reg);
> -	if (ret < 0) {
> -		dev_err(&client->dev, "I2C read error\n");
> -		return ret;
> -	}
> -
> -	*data = (u8)ret;
> -
> -	return 0;
> -}
> -
> -static int adt7410_i2c_write_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 data)
> -{
> -	struct i2c_client *client = to_i2c_client(chip->dev);
> -	int ret = 0;
> -
> -	ret = i2c_smbus_write_byte_data(client, reg, data);
> -	if (ret < 0)
> -		dev_err(&client->dev, "I2C write error\n");
> -
> -	return ret;
> -}
> -
> -static const struct adt7410_ops adt7410_i2c_ops = {
> -	.read_word = adt7410_i2c_read_word,
> -	.write_word = adt7410_i2c_write_word,
> -	.read_byte = adt7410_i2c_read_byte,
> -	.write_byte = adt7410_i2c_write_byte,
> -};
> -
> -static int adt7410_i2c_probe(struct i2c_client *client,
> -	const struct i2c_device_id *id)
> -{
> -	return adt7410_probe(&client->dev, client->irq, id->name,
> -		&adt7410_i2c_ops);
> -}
> -
> -static int adt7410_i2c_remove(struct i2c_client *client)
> -{
> -	return adt7410_remove(&client->dev, client->irq);
> -}
> -
> -static const struct i2c_device_id adt7410_id[] = {
> -	{ "adt7410", 0 },
> -	{}
> -};
> -
> -MODULE_DEVICE_TABLE(i2c, adt7410_id);
> -
> -static struct i2c_driver adt7410_driver = {
> -	.driver = {
> -		.name = "adt7410",
> -	},
> -	.probe = adt7410_i2c_probe,
> -	.remove = adt7410_i2c_remove,
> -	.id_table = adt7410_id,
> -};
> -
> -static int __init adt7410_i2c_init(void)
> -{
> -	return i2c_add_driver(&adt7410_driver);
> -}
> -
> -static void __exit adt7410_i2c_exit(void)
> -{
> -	i2c_del_driver(&adt7410_driver);
> -}
> -
> -#else
> -
> -static int  __init adt7410_i2c_init(void) { return 0; };
> -static void __exit adt7410_i2c_exit(void) {};
> -
> -#endif
> -
> -#if IS_ENABLED(CONFIG_SPI_MASTER)
> -
> -static const u8 adt7371_reg_table[] = {
> -	[ADT7410_TEMPERATURE]   = ADT7310_TEMPERATURE,
> -	[ADT7410_STATUS]	= ADT7310_STATUS,
> -	[ADT7410_CONFIG]	= ADT7310_CONFIG,
> -	[ADT7410_T_ALARM_HIGH]	= ADT7310_T_ALARM_HIGH,
> -	[ADT7410_T_ALARM_LOW]	= ADT7310_T_ALARM_LOW,
> -	[ADT7410_T_CRIT]	= ADT7310_T_CRIT,
> -	[ADT7410_T_HYST]	= ADT7310_T_HYST,
> -	[ADT7410_ID]		= ADT7310_ID,
> -};
> -
> -#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> -
> -static int adt7310_spi_read_word(struct adt7410_chip_info *chip,
> -	u8 reg, u16 *data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 command = AD7310_COMMAND(reg);
> -	int ret = 0;
> -
> -	command |= ADT7310_CMD_READ;
> -	ret = spi_write(spi, &command, sizeof(command));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write command error\n");
> -		return ret;
> -	}
> -
> -	ret = spi_read(spi, (u8 *)data, sizeof(*data));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI read word error\n");
> -		return ret;
> -	}
> -
> -	*data = be16_to_cpu(*data);
> -
> -	return 0;
> -}
> -
> -static int adt7310_spi_write_word(struct adt7410_chip_info *chip, u8 reg,
> -	u16 data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 buf[3];
> -	int ret = 0;
> -
> -	buf[0] = AD7310_COMMAND(reg);
> -	buf[1] = (u8)(data >> 8);
> -	buf[2] = (u8)(data & 0xFF);
> -
> -	ret = spi_write(spi, buf, 3);
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write word error\n");
> -		return ret;
> -	}
> -
> -	return ret;
> -}
> -
> -static int adt7310_spi_read_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 *data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 command = AD7310_COMMAND(reg);
> -	int ret = 0;
> -
> -	command |= ADT7310_CMD_READ;
> -	ret = spi_write(spi, &command, sizeof(command));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write command error\n");
> -		return ret;
> -	}
> -
> -	ret = spi_read(spi, data, sizeof(*data));
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI read byte error\n");
> -		return ret;
> -	}
> -
> -	return 0;
> -}
> -
> -static int adt7310_spi_write_byte(struct adt7410_chip_info *chip, u8 reg,
> -	u8 data)
> -{
> -	struct spi_device *spi = to_spi_device(chip->dev);
> -	u8 buf[2];
> -	int ret = 0;
> -
> -	buf[0] = AD7310_COMMAND(reg);
> -	buf[1] = data;
> -
> -	ret = spi_write(spi, buf, 2);
> -	if (ret < 0) {
> -		dev_err(&spi->dev, "SPI write byte error\n");
> -		return ret;
> -	}
> -
> -	return ret;
> -}
> -
> -static const struct adt7410_ops adt7310_spi_ops = {
> -	.read_word = adt7310_spi_read_word,
> -	.write_word = adt7310_spi_write_word,
> -	.read_byte = adt7310_spi_read_byte,
> -	.write_byte = adt7310_spi_write_byte,
> -};
> -
> -static int adt7310_spi_probe(struct spi_device *spi)
> -{
> -	return adt7410_probe(&spi->dev, spi->irq,
> -		spi_get_device_id(spi)->name, &adt7310_spi_ops);
> -}
> -
> -static int adt7310_spi_remove(struct spi_device *spi)
> -{
> -	return adt7410_remove(&spi->dev, spi->irq);
> -}
> -
> -static const struct spi_device_id adt7310_id[] = {
> -	{ "adt7310", 0 },
> -	{}
> -};
> -MODULE_DEVICE_TABLE(spi, adt7310_id);
> -
> -static struct spi_driver adt7310_driver = {
> -	.driver = {
> -		.name = "adt7310",
> -		.owner = THIS_MODULE,
> -	},
> -	.probe = adt7310_spi_probe,
> -	.remove = adt7310_spi_remove,
> -	.id_table = adt7310_id,
> -};
> -
> -static int __init adt7310_spi_init(void)
> -{
> -	return spi_register_driver(&adt7310_driver);
> -}
> -
> -static void adt7310_spi_exit(void)
> -{
> -	spi_unregister_driver(&adt7310_driver);
> -}
> -
> -#else
> -
> -static int __init adt7310_spi_init(void) { return 0; };
> -static void adt7310_spi_exit(void) {};
> -
> -#endif
> -
> -static int __init adt7410_init(void)
> -{
> -	int ret;
> -
> -	ret = adt7310_spi_init();
> -	if (ret)
> -		return ret;
> -
> -	ret = adt7410_i2c_init();
> -	if (ret)
> -		adt7310_spi_exit();
> -
> -	return ret;
> -}
> -module_init(adt7410_init);
> -
> -static void __exit adt7410_exit(void)
> -{
> -	adt7410_i2c_exit();
> -	adt7310_spi_exit();
> -}
> -module_exit(adt7410_exit);
> -
> -MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> -MODULE_DESCRIPTION("Analog Devices ADT7310/ADT7410 digital temperature sensor driver");
> -MODULE_LICENSE("GPL v2");
> 

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

* Re: [PATCH v2 4/4] staging:iio: Remove adt7410 driver
  2013-03-02 16:45     ` [lm-sensors] " Jonathan Cameron
@ 2013-03-02 17:10       ` Guenter Roeck
  -1 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-03-02 17:10 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Lars-Peter Clausen, Jean Delvare, Hartmut Knaack,
	Jonathan Cameron, lm-sensors, linux-iio

On Sat, Mar 02, 2013 at 04:45:14PM +0000, Jonathan Cameron wrote:
> On 02/18/2013 01:38 PM, Lars-Peter Clausen wrote:
> > The adt7410 hwmon driver is feature wise more or less on par with the IIO
> > driver. So we can finally remove the IIO driver.
> > 
> I kind of lost touch on where things were with the last few patches adding support in
> the hwmon driver for stuff that was in here.  It looked like there were only nitpicks
> left so I'll assume it gets sorted this coming cycle and take this patch now
> (before I loose it)

I am waiting for a final rev of Lars' patches to add adt7310/adt7320 support and
alarm interrupt support to the hwmon driver, but I don't see a problem getting
it all into 3.10.

Thanks,
Guenter

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

* Re: [lm-sensors] [PATCH v2 4/4] staging:iio: Remove adt7410 driver
@ 2013-03-02 17:10       ` Guenter Roeck
  0 siblings, 0 replies; 47+ messages in thread
From: Guenter Roeck @ 2013-03-02 17:10 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Lars-Peter Clausen, Jean Delvare, Hartmut Knaack,
	Jonathan Cameron, lm-sensors, linux-iio

On Sat, Mar 02, 2013 at 04:45:14PM +0000, Jonathan Cameron wrote:
> On 02/18/2013 01:38 PM, Lars-Peter Clausen wrote:
> > The adt7410 hwmon driver is feature wise more or less on par with the IIO
> > driver. So we can finally remove the IIO driver.
> > 
> I kind of lost touch on where things were with the last few patches adding support in
> the hwmon driver for stuff that was in here.  It looked like there were only nitpicks
> left so I'll assume it gets sorted this coming cycle and take this patch now
> (before I loose it)

I am waiting for a final rev of Lars' patches to add adt7310/adt7320 support and
alarm interrupt support to the hwmon driver, but I don't see a problem getting
it all into 3.10.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

end of thread, other threads:[~2013-03-02 17:10 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-18 13:38 [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers Lars-Peter Clausen
2013-02-18 13:38 ` [lm-sensors] " Lars-Peter Clausen
2013-02-18 13:38 ` [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320 Lars-Peter Clausen
2013-02-18 13:38   ` [lm-sensors] [PATCH v2 2/4] hwmon: (adt7410) =?utf-8?q?Add_support_for_the_ad Lars-Peter Clausen
2013-02-19  1:30   ` [PATCH v2 2/4] hwmon: (adt7410) Add support for the adt7310/adt7320 Guenter Roeck
2013-02-19  1:30     ` [lm-sensors] " Guenter Roeck
2013-02-19 11:57     ` Lars-Peter Clausen
2013-02-19 11:57       ` [lm-sensors] " Lars-Peter Clausen
2013-02-19 16:52       ` Guenter Roeck
2013-02-19 16:52         ` [lm-sensors] " Guenter Roeck
2013-02-18 13:38 ` [PATCH v2 3/4] hwmon: (adt7x10) Add alarm interrupt support Lars-Peter Clausen
2013-02-18 13:38   ` [lm-sensors] " Lars-Peter Clausen
2013-02-19  1:39   ` Guenter Roeck
2013-02-19  1:39     ` [lm-sensors] " Guenter Roeck
2013-02-19 12:05     ` Lars-Peter Clausen
2013-02-19 12:05       ` [lm-sensors] " Lars-Peter Clausen
2013-02-19 17:10       ` Guenter Roeck
2013-02-19 17:10         ` [lm-sensors] " Guenter Roeck
2013-02-18 13:38 ` [PATCH v2 4/4] staging:iio: Remove adt7410 driver Lars-Peter Clausen
2013-02-18 13:38   ` [lm-sensors] " Lars-Peter Clausen
2013-03-02 16:45   ` Jonathan Cameron
2013-03-02 16:45     ` [lm-sensors] " Jonathan Cameron
2013-03-02 17:10     ` Guenter Roeck
2013-03-02 17:10       ` [lm-sensors] " Guenter Roeck
2013-02-18 20:22 ` [PATCH v2 1/4] hwmon: (adt7410) Don't re-read non-volatile registers Hartmut Knaack
2013-02-18 20:22   ` [lm-sensors] " Hartmut Knaack
2013-02-19  1:02   ` Guenter Roeck
2013-02-19  1:02     ` [lm-sensors] " Guenter Roeck
2013-02-19 19:39     ` Hartmut Knaack
2013-02-19 19:39       ` [lm-sensors] " Hartmut Knaack
2013-02-20  1:22       ` Guenter Roeck
2013-02-20  1:22         ` [lm-sensors] " Guenter Roeck
2013-02-19  1:32 ` Guenter Roeck
2013-02-19  1:32   ` [lm-sensors] " Guenter Roeck
2013-02-23  0:45 ` Hartmut Knaack
2013-02-23 20:18   ` Guenter Roeck
2013-02-23 20:18     ` [lm-sensors] " Guenter Roeck
2013-02-25 22:03     ` Hartmut Knaack
2013-02-25 22:03       ` [lm-sensors] " Hartmut Knaack
2013-02-26  1:40       ` Guenter Roeck
2013-02-26  1:40         ` [lm-sensors] " Guenter Roeck
2013-02-25  9:54   ` Lars-Peter Clausen
2013-02-25  9:54     ` [lm-sensors] " Lars-Peter Clausen
2013-02-25 21:30     ` Hartmut Knaack
2013-02-25 21:30       ` [lm-sensors] " Hartmut Knaack
2013-02-26  1:39       ` Guenter Roeck
2013-02-26  1:39         ` [lm-sensors] " 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.