Linux IIO development
 help / color / mirror / Atom feed
* [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register
@ 2013-02-15 16:57 Lars-Peter Clausen
  2013-02-15 16:57 ` [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP Lars-Peter Clausen
                   ` (9 more replies)
  0 siblings, 10 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

Make sure to clear the mode bits from the config register before setting the new
mode. Otherwise we might end up with a different mode than we want to.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/hwmon/adt7410.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 797c2b8..4ccee4f 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -364,6 +364,7 @@ static int adt7410_probe(struct i2c_client *client,
 	/*
 	 * 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) {
-- 
1.8.0


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

* [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:07   ` Guenter Roeck
  2013-02-19 23:07   ` Hartmut Knaack
  2013-02-15 16:57 ` [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro Lars-Peter Clausen
                   ` (8 subsequent siblings)
  9 siblings, 2 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

Only build the suspend/resume code if CONFIG_PM_SLEEP is selected. Currently the
code is built if CONFIG_PM is selected, but it will also be selected if only
runtime PM support is built into the kernel.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/hwmon/adt7410.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 4ccee4f..ae80815 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -415,7 +415,7 @@ static const struct i2c_device_id adt7410_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, adt7410_ids);
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int adt7410_suspend(struct device *dev)
 {
 	int ret;
-- 
1.8.0


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

* [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
  2013-02-15 16:57 ` [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:07   ` Guenter Roeck
  2013-02-19 23:13   ` Hartmut Knaack
  2013-02-15 16:57 ` [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS " Lars-Peter Clausen
                   ` (7 subsequent siblings)
  9 siblings, 2 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

Use the SIMPLE_DEV_PM_OPS macro to declare the driver's pm_ops.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/hwmon/adt7410.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index ae80815..4ae16e0 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -437,10 +437,8 @@ static int adt7410_resume(struct device *dev)
 	return ret;
 }
 
-static const struct dev_pm_ops adt7410_dev_pm_ops = {
-	.suspend	= adt7410_suspend,
-	.resume		= adt7410_resume,
-};
+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
-- 
1.8.0


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

* [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS helper macro
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
  2013-02-15 16:57 ` [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP Lars-Peter Clausen
  2013-02-15 16:57 ` [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:07   ` Guenter Roeck
  2013-02-19 23:17   ` Hartmut Knaack
  2013-02-15 16:57 ` [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420 Lars-Peter Clausen
                   ` (6 subsequent siblings)
  9 siblings, 2 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

Use the I2C_ADDRS macro to initialize the I2C device's address_list. Doing so
saves a few lines of boilerplate code.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/hwmon/adt7410.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 4ae16e0..99f6d32 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -78,10 +78,6 @@ enum adt7410_type {		/* keep sorted in alphabetical order */
 	adt7410,
 };
 
-/* Addresses scanned */
-static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
-					I2C_CLIENT_END };
-
 static const u8 ADT7410_REG_TEMP[4] = {
 	ADT7410_TEMPERATURE,		/* input */
 	ADT7410_T_ALARM_HIGH,		/* high */
@@ -453,7 +449,7 @@ static struct i2c_driver adt7410_driver = {
 	.probe		= adt7410_probe,
 	.remove		= adt7410_remove,
 	.id_table	= adt7410_ids,
-	.address_list	= normal_i2c,
+	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
 };
 
 module_i2c_driver(adt7410_driver);
-- 
1.8.0


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

* [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (2 preceding siblings ...)
  2013-02-15 16:57 ` [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS " Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:08   ` Guenter Roeck
                     ` (2 more replies)
  2013-02-15 16:57 ` [PATCH 6/9] hwmon: (adt7410) Don't re-read non-volatile registers Lars-Peter Clausen
                   ` (5 subsequent siblings)
  9 siblings, 3 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Hartmut Knaack, Jonathan Cameron, lm-sensors, linux-iio,
	Lars-Peter Clausen

The adt7420 is software compatible to the adt7410.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/hwmon/Kconfig   | 4 ++--
 drivers/hwmon/adt7410.c | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 52d5174..89ac1cb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -180,11 +180,11 @@ config SENSORS_ADM9240
 	  will be called adm9240.
 
 config SENSORS_ADT7410
-	tristate "Analog Devices ADT7410"
+	tristate "Analog Devices ADT7410/ADT7420"
 	depends on I2C
 	help
 	  If you say yes here you get support for the Analog Devices
-	  ADT7410 temperature monitoring chip.
+	  ADT7410 and ADT7420 temperature monitoring chips.
 
 	  This driver can also be built as a module. If so, the module
 	  will be called adt7410.
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 99f6d32..99a7290 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -407,6 +407,7 @@ static int adt7410_remove(struct i2c_client *client)
 
 static const struct i2c_device_id adt7410_ids[] = {
 	{ "adt7410", adt7410, },
+	{ "adt7420", adt7410, },
 	{ /* LIST END */ }
 };
 MODULE_DEVICE_TABLE(i2c, adt7410_ids);
@@ -455,5 +456,5 @@ static struct i2c_driver adt7410_driver = {
 module_i2c_driver(adt7410_driver);
 
 MODULE_AUTHOR("Hartmut Knaack");
-MODULE_DESCRIPTION("ADT7410 driver");
+MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
 MODULE_LICENSE("GPL");
-- 
1.8.0


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

* [PATCH 6/9] hwmon: (adt7410) Don't re-read non-volatile registers
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (3 preceding siblings ...)
  2013-02-15 16:57 ` [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420 Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:20   ` Guenter Roeck
  2013-02-15 16:57 ` [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320 Lars-Peter Clausen
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 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>
---
 drivers/hwmon/adt7410.c | 89 +++++++++++++++++++++++++++++++------------------
 1 file changed, 56 insertions(+), 33 deletions(-)

diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 99a7290..5de0376 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -119,45 +119,31 @@ 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;
 
 		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);
+
+		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
+		if (ret) {
+			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] = ret;
+
 		data->last_updated = jiffies;
 		data->valid = true;
 	}
@@ -167,6 +153,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 < 3; i++) {
+		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
+		if (ret) {
+			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) {
+		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 +208,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 +253,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 +390,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] 27+ messages in thread

* [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (4 preceding siblings ...)
  2013-02-15 16:57 ` [PATCH 6/9] hwmon: (adt7410) Don't re-read non-volatile registers Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:05   ` Hartmut Knaack
  2013-02-15 20:32   ` Guenter Roeck
  2013-02-15 16:57 ` [PATCH 8/9] hwmon: (adt7x10) Add alarm interrupt support Lars-Peter Clausen
                   ` (3 subsequent siblings)
  9 siblings, 2 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 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/adt7320. 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>
---
 drivers/hwmon/Kconfig   |  20 ++
 drivers/hwmon/Makefile  |   2 +
 drivers/hwmon/adt7310.c | 160 ++++++++++++++++
 drivers/hwmon/adt7410.c | 472 ++++++-----------------------------------------
 drivers/hwmon/adt7x10.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/adt7x10.h |  47 +++++
 6 files changed, 758 insertions(+), 419 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/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..0483e6c
--- /dev/null
+++ b/drivers/hwmon/adt7310.c
@@ -0,0 +1,160 @@
+/*
+ * 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 "adt7x10.h"
+
+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 ADT7310_CMD_REG_MASK			0x28
+#define ADT7310_CMD_REG_OFFSET			3
+#define ADT7310_CMD_READ			0x40
+#define ADT7310_CMD_CON_READ			0x4
+
+#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
+
+static int adt7310_spi_read_word(struct device *dev,
+	u8 reg, u16 *data)
+{
+	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi, (u8 *)data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(dev, "SPI read word error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu(*data);
+
+	return 0;
+}
+
+static int adt7310_spi_write_word(struct device *dev, u8 reg,
+	u16 data)
+{
+	struct spi_device *spi = to_spi_device(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(dev, "SPI write word error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int adt7310_spi_read_byte(struct device *dev, u8 reg,
+	u8 *data)
+{
+	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi, data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(dev, "SPI read byte error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int adt7310_spi_write_byte(struct device *dev, u8 reg,
+	u8 data)
+{
+	struct spi_device *spi = to_spi_device(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(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_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/ADT7420 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 5de0376..13734c5 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -1,483 +1,117 @@
 /*
- * 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
+#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
-
-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)
-{
-	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;
-}
-
-static int adt7410_update_temp(struct device *dev)
+static int adt7410_i2c_read_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) {
-
-		dev_dbg(&client->dev, "Starting update\n");
-
-		ret = adt7410_temp_ready(client); /* check for new value */
-		if (ret)
-			goto abort;
-
-		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
-		if (ret) {
-			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
-				ADT7410_REG_TEMP[0], ret);
-			goto abort;
-		}
-		data->temp[0] = ret;
-
-		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 < 3; i++) {
-		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
-		if (ret) {
-			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) {
-		dev_dbg(&client->dev,
-			"Failed to read value: hyst reg, error %d\n",
-			ret);
+	ret = i2c_smbus_read_word_swapped(client, reg);
+	if (ret < 0) {
+		dev_err(dev, "I2C read error\n");
 		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);
-}
+	*data = ret;
 
-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);
+	return 0;
 }
 
-/*-----------------------------------------------------------------------*/
-
-/* sysfs attributes for hwmon */
-
-static ssize_t adt7410_show_temp(struct device *dev,
-				 struct device_attribute *da, char *buf)
+static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
 {
-	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;
+	int ret = 0;
 
-		ret = adt7410_update_temp(dev);
-		if (ret)
-			return ret;
-	}
+	ret = i2c_smbus_write_word_swapped(client, reg, data);
+	if (ret < 0)
+		dev_err(dev, "I2C write error\n");
 
-	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
-		       data->temp[attr->index]));
+	return ret;
 }
 
-static ssize_t adt7410_set_temp(struct device *dev,
-				struct device_attribute *da,
-				const char *buf, size_t count)
+static int adt7410_i2c_read_byte(struct device *dev, u8 reg, u8 *data)
 {
-	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;
+	int ret = 0;
 
-	ret = kstrtol(buf, 10, &temp);
-	if (ret)
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(dev, "I2C read error\n");
 		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;
+	*data = (u8)ret;
 
-	return count;
+	return 0;
 }
 
-static ssize_t adt7410_show_alarm(struct device *dev,
-				  struct device_attribute *da,
-				  char *buf)
+static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
 {
 	struct i2c_client *client = to_i2c_client(dev);
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-	int ret;
+	int ret = 0;
 
-	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
+	ret = i2c_smbus_write_byte_data(client, reg, data);
 	if (ret < 0)
-		return ret;
+		dev_err(&client->dev, "I2C write error\n");
 
-	return sprintf(buf, "%d\n", !!(ret & attr->index));
+	return ret;
 }
 
-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 */ }
+static const struct i2c_device_id adt7410_id[] = {
+	{ "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 */
+MODULE_DEVICE_TABLE(i2c, adt7410_id);
 
 static struct i2c_driver adt7410_driver = {
-	.class		= I2C_CLASS_HWMON,
 	.driver = {
-		.name	= "adt7410",
+		.name = "adt7410",
 		.pm	= ADT7410_DEV_PM_OPS,
 	},
-	.probe		= adt7410_probe,
-	.remove		= adt7410_remove,
-	.id_table	= adt7410_ids,
-	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
+	.probe = adt7410_i2c_probe,
+	.remove = adt7410_i2c_remove,
+	.id_table = adt7410_id,
+	.address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
+	.class = I2C_CLASS_HWMON,
 };
-
 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..2598aaf
--- /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_word(struct device *dev, u8 reg, u16 *data)
+{
+	struct adt7410_data *d = dev_get_drvdata(dev);
+	return d->ops->read_word(dev, reg, data);
+}
+
+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 int adt7410_read_byte(struct device *dev, u8 reg, u8 *data)
+{
+	struct adt7410_data *d = dev_get_drvdata(dev);
+	return d->ops->read_byte(dev, reg, data);
+}
+
+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 const u8 ADT7410_REG_TEMP[4] = {
+	ADT7410_TEMPERATURE,		/* input */
+	ADT7410_T_ALARM_HIGH,		/* high */
+	ADT7410_T_ALARM_LOW,		/* low */
+	ADT7410_T_CRIT,			/* critical */
+};
+
+/*
+ * adt7410 register access by I2C
+ */
+static int adt7410_temp_ready(struct device *dev)
+{
+	int i, ret;
+	u8 status;
+
+	for (i = 0; i < 6; i++) {
+		ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
+		if (ret)
+			return ret;
+		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) {
+
+		dev_dbg(dev, "Starting update\n");
+
+		ret = adt7410_temp_ready(dev); /* check for new value */
+		if (ret)
+			goto abort;
+
+		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[0],
+					&data->temp[0]);
+		if (ret) {
+			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
+			goto abort;
+		}
+		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],
+					&data->temp[i]);
+		if (ret) {
+			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_REG_TEMP[0], ret);
+			return ret;
+		}
+	}
+
+	ret = adt7410_read_byte(dev, ADT7410_T_HYST, &data->hyst);
+	if (ret) {
+		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+				ADT7410_T_HYST, ret);
+		return 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);
+	u8 status;
+	int ret;
+
+	ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", !!(status & 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, &data->oldconfig);
+	if (ret < 0) {
+		dev_dbg(dev, "Can't read config? %d\n", ret);
+		return 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 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
new file mode 100644
index 0000000..17c8053
--- /dev/null
+++ b/drivers/hwmon/adt7x10.h
@@ -0,0 +1,47 @@
+#ifndef __HWMON_ADT7410_H__
+#define __HWMON_ADT7410_H__
+
+#include <linux/types.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_word)(struct device *, u8 reg, u16 *data);
+	int (*write_word)(struct device *, u8 reg, u16 data);
+	int (*read_byte)(struct device *, u8 reg, u8 *data);
+	int (*write_byte)(struct device *, u8 reg, u8 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] 27+ messages in thread

* [PATCH 8/9] hwmon: (adt7x10) Add alarm interrupt support
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (5 preceding siblings ...)
  2013-02-15 16:57 ` [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320 Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 16:57 ` [PATCH 9/9] staging:iio: Remove adt7410 driver Lars-Peter Clausen
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 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>
---
 drivers/hwmon/adt7310.c |  4 ++--
 drivers/hwmon/adt7410.c |  4 ++--
 drivers/hwmon/adt7x10.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
 drivers/hwmon/adt7x10.h |  4 ++--
 4 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
index 0483e6c..dcd8d5a 100644
--- a/drivers/hwmon/adt7310.c
+++ b/drivers/hwmon/adt7310.c
@@ -127,13 +127,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 13734c5..35880c6 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -84,12 +84,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_id[] = {
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
index 2598aaf..d042596 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,26 @@ 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;
+	u8 status;
+	int ret;
+
+	ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
+	if (ret)
+		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;
+}
+
 /*
  * adt7410 register access by I2C
  */
@@ -359,7 +380,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 +404,17 @@ int adt7410_probe(struct device *dev, const char *name,
 		return 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_FULL | ADT7410_RESOLUTION | ADT7410_EVENT_MODE;
+	data->config &= ~(ADT7410_MODE_MASK | ADT7410_CT_POLARITY |
+			ADT7410_INT_POLARITY);
+	data->config |= ADT7410_FULL | ADT7410_RESOLUTION;
+
+	/* If we are not using interrupts use comperator mode */
+	if (irq <= 0)
+		data->config |= ADT7410_EVENT_MODE;
+
 	if (data->config != data->oldconfig) {
 		ret = adt7410_write_byte(dev, ADT7410_CONFIG, data->config);
 		if (ret)
@@ -421,8 +448,17 @@ 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_LOW | IRQF_ONESHOT, name, 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 +470,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 17c8053..173b094 100644
--- a/drivers/hwmon/adt7x10.h
+++ b/drivers/hwmon/adt7x10.h
@@ -32,9 +32,9 @@ struct adt7410_ops {
 	int (*write_byte)(struct device *, u8 reg, u8 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] 27+ messages in thread

* [PATCH 9/9] staging:iio: Remove adt7410 driver
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (6 preceding siblings ...)
  2013-02-15 16:57 ` [PATCH 8/9] hwmon: (adt7x10) Add alarm interrupt support Lars-Peter Clausen
@ 2013-02-15 16:57 ` Lars-Peter Clausen
  2013-02-15 20:06 ` [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Guenter Roeck
  2013-02-19 23:03 ` Hartmut Knaack
  9 siblings, 0 replies; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-15 16:57 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] 27+ messages in thread

* Re: [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-15 16:57 ` [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320 Lars-Peter Clausen
@ 2013-02-15 20:05   ` Hartmut Knaack
  2013-02-16 15:54     ` Lars-Peter Clausen
  2013-02-15 20:32   ` Guenter Roeck
  1 sibling, 1 reply; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-15 20:05 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

I had a quick look over your patches and found some minor typo, see below. I will apply all the patches to see the result.
Also keep in mind to add some changes to Documentation/hwmon/adt7410 (and add an adt7310 as well?).

Lars-Peter Clausen schrieb:
<...>
> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
> new file mode 100644
> index 0000000..0483e6c
> --- /dev/null
> +++ b/drivers/hwmon/adt7310.c
> @@ -0,0 +1,160 @@
> +/*
> + * 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 "adt7x10.h"
> +
> +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,
> +};
Just out of curiousity, whats the reason for the name adt7371 here?
> +
> +#define ADT7310_CMD_REG_MASK			0x28
> +#define ADT7310_CMD_REG_OFFSET			3
> +#define ADT7310_CMD_READ			0x40
> +#define ADT7310_CMD_CON_READ			0x4
> +
> +#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> +
> +static int adt7310_spi_read_word(struct device *dev,
> +	u8 reg, u16 *data)
> +{
> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
> +		return ret;
> +	}
> +
> +	ret = spi_read(spi, (u8 *)data, sizeof(*data));
> +	if (ret < 0) {
> +		dev_err(dev, "SPI read word error\n");
> +		return ret;
> +	}
> +
> +	*data = be16_to_cpu(*data);
> +
> +	return 0;
> +}
> +
> +static int adt7310_spi_write_word(struct device *dev, u8 reg,
> +	u16 data)
> +{
> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write word error\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int adt7310_spi_read_byte(struct device *dev, u8 reg,
> +	u8 *data)
> +{
> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
> +		return ret;
> +	}
> +
> +	ret = spi_read(spi, data, sizeof(*data));
> +	if (ret < 0) {
> +		dev_err(dev, "SPI read byte error\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int adt7310_spi_write_byte(struct device *dev, u8 reg,
> +	u8 data)
> +{
> +	struct spi_device *spi = to_spi_device(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(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_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/ADT7420 driver");
This should be ADT7310/ADT7320, right?
> +MODULE_LICENSE("GPL");
<...>
> --- /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_word(struct device *dev, u8 reg, u16 *data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_word(dev, reg, data);
> +}
> +
> +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 int adt7410_read_byte(struct device *dev, u8 reg, u8 *data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_byte(dev, reg, data);
> +}
> +
> +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 const u8 ADT7410_REG_TEMP[4] = {
> +	ADT7410_TEMPERATURE,		/* input */
> +	ADT7410_T_ALARM_HIGH,		/* high */
> +	ADT7410_T_ALARM_LOW,		/* low */
> +	ADT7410_T_CRIT,			/* critical */
> +};
> +
> +/*
> + * adt7410 register access by I2C
> + */
> +static int adt7410_temp_ready(struct device *dev)
> +{
> +	int i, ret;
> +	u8 status;
> +
> +	for (i = 0; i < 6; i++) {
> +		ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
> +		if (ret)
> +			return ret;
> +		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) {
> +
> +		dev_dbg(dev, "Starting update\n");
> +
> +		ret = adt7410_temp_ready(dev); /* check for new value */
> +		if (ret)
> +			goto abort;
> +
> +		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[0],
> +					&data->temp[0]);
> +		if (ret) {
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			goto abort;
> +		}
> +		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],
> +					&data->temp[i]);
> +		if (ret) {
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			return ret;
> +		}
> +	}
> +
> +	ret = adt7410_read_byte(dev, ADT7410_T_HYST, &data->hyst);
> +	if (ret) {
> +		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_T_HYST, ret);
> +		return 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);
> +	u8 status;
> +	int ret;
> +
> +	ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", !!(status & 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, &data->oldconfig);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Can't read config? %d\n", ret);
> +		return 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 driver");
This description does not seem appropriate any longer.
> +MODULE_LICENSE("GPL");
<...>


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

* Re: [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (7 preceding siblings ...)
  2013-02-15 16:57 ` [PATCH 9/9] staging:iio: Remove adt7410 driver Lars-Peter Clausen
@ 2013-02-15 20:06 ` Guenter Roeck
  2013-02-19 23:03 ` Hartmut Knaack
  9 siblings, 0 replies; 27+ messages in thread
From: Guenter Roeck @ 2013-02-15 20:06 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On Fri, Feb 15, 2013 at 05:57:10PM +0100, Lars-Peter Clausen wrote:
> Make sure to clear the mode bits from the config register before setting the new
> mode. Otherwise we might end up with a different mode than we want to.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied to hwmon-next.

Thanks,
Guenter

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

* Re: [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP
  2013-02-15 16:57 ` [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP Lars-Peter Clausen
@ 2013-02-15 20:07   ` Guenter Roeck
  2013-02-19 23:07   ` Hartmut Knaack
  1 sibling, 0 replies; 27+ messages in thread
From: Guenter Roeck @ 2013-02-15 20:07 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On Fri, Feb 15, 2013 at 05:57:11PM +0100, Lars-Peter Clausen wrote:
> Only build the suspend/resume code if CONFIG_PM_SLEEP is selected. Currently the
> code is built if CONFIG_PM is selected, but it will also be selected if only
> runtime PM support is built into the kernel.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied to hwmon-next.

Thanks,
Guenter

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

* Re: [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro
  2013-02-15 16:57 ` [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro Lars-Peter Clausen
@ 2013-02-15 20:07   ` Guenter Roeck
  2013-02-19 23:13   ` Hartmut Knaack
  1 sibling, 0 replies; 27+ messages in thread
From: Guenter Roeck @ 2013-02-15 20:07 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On Fri, Feb 15, 2013 at 05:57:12PM +0100, Lars-Peter Clausen wrote:
> Use the SIMPLE_DEV_PM_OPS macro to declare the driver's pm_ops.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied to hwmon-next.

Thanks,
Guenter

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

* Re: [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS helper macro
  2013-02-15 16:57 ` [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS " Lars-Peter Clausen
@ 2013-02-15 20:07   ` Guenter Roeck
  2013-02-19 23:17   ` Hartmut Knaack
  1 sibling, 0 replies; 27+ messages in thread
From: Guenter Roeck @ 2013-02-15 20:07 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On Fri, Feb 15, 2013 at 05:57:13PM +0100, Lars-Peter Clausen wrote:
> Use the I2C_ADDRS macro to initialize the I2C device's address_list. Doing so
> saves a few lines of boilerplate code.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied to hwmon-next.

Thanks,
Guenter

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

* Re: [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420
  2013-02-15 16:57 ` [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420 Lars-Peter Clausen
@ 2013-02-15 20:08   ` Guenter Roeck
  2013-02-19 23:21   ` Hartmut Knaack
  2013-02-21  0:10   ` Hartmut Knaack
  2 siblings, 0 replies; 27+ messages in thread
From: Guenter Roeck @ 2013-02-15 20:08 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On Fri, Feb 15, 2013 at 05:57:14PM +0100, Lars-Peter Clausen wrote:
> The adt7420 is software compatible to the adt7410.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

Applied to hwmon-next.

Thanks,
Guenter

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

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

On Fri, Feb 15, 2013 at 05:57:15PM +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>
> ---
>  drivers/hwmon/adt7410.c | 89 +++++++++++++++++++++++++++++++------------------
>  1 file changed, 56 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 99a7290..5de0376 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -119,45 +119,31 @@ 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;
>  
>  		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);
> +
> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
> +		if (ret) {

Should be
		if (ret < 0) {

or non-zero temperatures always create an error condition.

> +			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] = ret;
> +
>  		data->last_updated = jiffies;
>  		data->valid = true;
>  	}
> @@ -167,6 +153,35 @@ abort:
>  	return ret;

	ret can be > 0 as no-error return. So you'll either have to
	change the no-error return case to always return 0, or change the
	return value check when the function is called.
>  }
>  
> +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 < 3; i++) {

I think using ARRAY_SIZE would be better here. With "i < 3" you don't read the
critical temperature, which proves the point (unless I am missing something).

> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> +		if (ret) {

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

	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 +208,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)

Problematic; see above.

> +			return ret;
> +	}
>  
>  	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
>  		       data->temp[attr->index]));
> @@ -232,13 +253,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 +390,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	[flat|nested] 27+ messages in thread

* Re: [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-15 16:57 ` [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320 Lars-Peter Clausen
  2013-02-15 20:05   ` Hartmut Knaack
@ 2013-02-15 20:32   ` Guenter Roeck
  2013-02-16 16:04     ` Lars-Peter Clausen
  1 sibling, 1 reply; 27+ messages in thread
From: Guenter Roeck @ 2013-02-15 20:32 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On Fri, Feb 15, 2013 at 05:57:16PM +0100, Lars-Peter Clausen wrote:
> The adt7310/adt7320 is the SPI version of the adt7410/adt7320. 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>
> ---
>  drivers/hwmon/Kconfig   |  20 ++
>  drivers/hwmon/Makefile  |   2 +
>  drivers/hwmon/adt7310.c | 160 ++++++++++++++++
>  drivers/hwmon/adt7410.c | 472 ++++++-----------------------------------------
>  drivers/hwmon/adt7x10.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/adt7x10.h |  47 +++++
>  6 files changed, 758 insertions(+), 419 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/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..0483e6c
> --- /dev/null
> +++ b/drivers/hwmon/adt7310.c
> @@ -0,0 +1,160 @@
> +/*
> + * 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 "adt7x10.h"
> +
> +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 ADT7310_CMD_REG_MASK			0x28
> +#define ADT7310_CMD_REG_OFFSET			3
> +#define ADT7310_CMD_READ			0x40
> +#define ADT7310_CMD_CON_READ			0x4
> +
> +#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> +
> +static int adt7310_spi_read_word(struct device *dev,
> +	u8 reg, u16 *data)

I don't really like the approach of separating the return value from the error
code, if both can be returned directly. There are two reasons: First, the code
gets more complex, and second, the compiler tends to get confused under some
conditions abd believe that the return value is not set. And I really don't want
to have to deal with the resulting build warnings, after getting rid of pretty
much all such warnings in the hwmon subsystem. So I'd really appreciate if you
could rewrite this and the subsequent patches to return both error and value as
function return values.

Thanks,
Guenter

> +{
> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
> +		return ret;
> +	}
> +
> +	ret = spi_read(spi, (u8 *)data, sizeof(*data));
> +	if (ret < 0) {
> +		dev_err(dev, "SPI read word error\n");
> +		return ret;
> +	}
> +
> +	*data = be16_to_cpu(*data);
> +
> +	return 0;
> +}
> +
> +static int adt7310_spi_write_word(struct device *dev, u8 reg,
> +	u16 data)
> +{
> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write word error\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int adt7310_spi_read_byte(struct device *dev, u8 reg,
> +	u8 *data)
> +{
> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
> +		return ret;
> +	}
> +
> +	ret = spi_read(spi, data, sizeof(*data));
> +	if (ret < 0) {
> +		dev_err(dev, "SPI read byte error\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int adt7310_spi_write_byte(struct device *dev, u8 reg,
> +	u8 data)
> +{
> +	struct spi_device *spi = to_spi_device(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(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_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/ADT7420 driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 5de0376..13734c5 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -1,483 +1,117 @@
>  /*
> - * 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
> +#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
> -
> -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)
> -{
> -	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;
> -}
> -
> -static int adt7410_update_temp(struct device *dev)
> +static int adt7410_i2c_read_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) {
> -
> -		dev_dbg(&client->dev, "Starting update\n");
> -
> -		ret = adt7410_temp_ready(client); /* check for new value */
> -		if (ret)
> -			goto abort;
> -
> -		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
> -		if (ret) {
> -			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> -				ADT7410_REG_TEMP[0], ret);
> -			goto abort;
> -		}
> -		data->temp[0] = ret;
> -
> -		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 < 3; i++) {
> -		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
> -		if (ret) {
> -			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) {
> -		dev_dbg(&client->dev,
> -			"Failed to read value: hyst reg, error %d\n",
> -			ret);
> +	ret = i2c_smbus_read_word_swapped(client, reg);
> +	if (ret < 0) {
> +		dev_err(dev, "I2C read error\n");
>  		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);
> -}
> +	*data = ret;
>  
> -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);
> +	return 0;
>  }
>  
> -/*-----------------------------------------------------------------------*/
> -
> -/* sysfs attributes for hwmon */
> -
> -static ssize_t adt7410_show_temp(struct device *dev,
> -				 struct device_attribute *da, char *buf)
> +static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
>  {
> -	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;
> +	int ret = 0;
>  
> -		ret = adt7410_update_temp(dev);
> -		if (ret)
> -			return ret;
> -	}
> +	ret = i2c_smbus_write_word_swapped(client, reg, data);
> +	if (ret < 0)
> +		dev_err(dev, "I2C write error\n");
>  
> -	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
> -		       data->temp[attr->index]));
> +	return ret;
>  }
>  
> -static ssize_t adt7410_set_temp(struct device *dev,
> -				struct device_attribute *da,
> -				const char *buf, size_t count)
> +static int adt7410_i2c_read_byte(struct device *dev, u8 reg, u8 *data)
>  {
> -	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;
> +	int ret = 0;
>  
> -	ret = kstrtol(buf, 10, &temp);
> -	if (ret)
> +	ret = i2c_smbus_read_byte_data(client, reg);
> +	if (ret < 0) {
> +		dev_err(dev, "I2C read error\n");
>  		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;
> +	*data = (u8)ret;
>  
> -	return count;
> +	return 0;
>  }
>  
> -static ssize_t adt7410_show_alarm(struct device *dev,
> -				  struct device_attribute *da,
> -				  char *buf)
> +static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> -	int ret;
> +	int ret = 0;
>  
> -	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
> +	ret = i2c_smbus_write_byte_data(client, reg, data);
>  	if (ret < 0)
> -		return ret;
> +		dev_err(&client->dev, "I2C write error\n");
>  
> -	return sprintf(buf, "%d\n", !!(ret & attr->index));
> +	return ret;
>  }
>  
> -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 */ }
> +static const struct i2c_device_id adt7410_id[] = {
> +	{ "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 */
> +MODULE_DEVICE_TABLE(i2c, adt7410_id);
>  
>  static struct i2c_driver adt7410_driver = {
> -	.class		= I2C_CLASS_HWMON,
>  	.driver = {
> -		.name	= "adt7410",
> +		.name = "adt7410",
>  		.pm	= ADT7410_DEV_PM_OPS,
>  	},
> -	.probe		= adt7410_probe,
> -	.remove		= adt7410_remove,
> -	.id_table	= adt7410_ids,
> -	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
> +	.probe = adt7410_i2c_probe,
> +	.remove = adt7410_i2c_remove,
> +	.id_table = adt7410_id,
> +	.address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
> +	.class = I2C_CLASS_HWMON,
>  };
> -
>  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..2598aaf
> --- /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_word(struct device *dev, u8 reg, u16 *data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_word(dev, reg, data);
> +}
> +
> +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 int adt7410_read_byte(struct device *dev, u8 reg, u8 *data)
> +{
> +	struct adt7410_data *d = dev_get_drvdata(dev);
> +	return d->ops->read_byte(dev, reg, data);
> +}
> +
> +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 const u8 ADT7410_REG_TEMP[4] = {
> +	ADT7410_TEMPERATURE,		/* input */
> +	ADT7410_T_ALARM_HIGH,		/* high */
> +	ADT7410_T_ALARM_LOW,		/* low */
> +	ADT7410_T_CRIT,			/* critical */
> +};
> +
> +/*
> + * adt7410 register access by I2C
> + */
> +static int adt7410_temp_ready(struct device *dev)
> +{
> +	int i, ret;
> +	u8 status;
> +
> +	for (i = 0; i < 6; i++) {
> +		ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
> +		if (ret)
> +			return ret;
> +		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) {
> +
> +		dev_dbg(dev, "Starting update\n");
> +
> +		ret = adt7410_temp_ready(dev); /* check for new value */
> +		if (ret)
> +			goto abort;
> +
> +		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[0],
> +					&data->temp[0]);
> +		if (ret) {
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			goto abort;
> +		}
> +		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],
> +					&data->temp[i]);
> +		if (ret) {
> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_REG_TEMP[0], ret);
> +			return ret;
> +		}
> +	}
> +
> +	ret = adt7410_read_byte(dev, ADT7410_T_HYST, &data->hyst);
> +	if (ret) {
> +		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
> +				ADT7410_T_HYST, ret);
> +		return 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);
> +	u8 status;
> +	int ret;
> +
> +	ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", !!(status & 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, &data->oldconfig);
> +	if (ret < 0) {
> +		dev_dbg(dev, "Can't read config? %d\n", ret);
> +		return 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 driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
> new file mode 100644
> index 0000000..17c8053
> --- /dev/null
> +++ b/drivers/hwmon/adt7x10.h
> @@ -0,0 +1,47 @@
> +#ifndef __HWMON_ADT7410_H__
> +#define __HWMON_ADT7410_H__
> +
> +#include <linux/types.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_word)(struct device *, u8 reg, u16 *data);
> +	int (*write_word)(struct device *, u8 reg, u16 data);
> +	int (*read_byte)(struct device *, u8 reg, u8 *data);
> +	int (*write_byte)(struct device *, u8 reg, u8 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	[flat|nested] 27+ messages in thread

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

On 02/15/2013 09:05 PM, Hartmut Knaack wrote:
> I had a quick look over your patches and found some minor typo, see below. I will apply all the patches to see the result.
> Also keep in mind to add some changes to Documentation/hwmon/adt7410 (and add an adt7310 as well?).

yep, forgot about the documentation, will do this in v2. But I don't think it
makes sense to have to separate documents for the adt7410 and adt7310, the
devices really only differ in their interface and all of the real driver logic
is implemented in the common code.

> 
> Lars-Peter Clausen schrieb:
> <...>
>> diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
>> new file mode 100644
>> index 0000000..0483e6c
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7310.c
>> @@ -0,0 +1,160 @@
>> +/*
>> + * 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 "adt7x10.h"
>> +
>> +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,
>> +};
> Just out of curiousity, whats the reason for the name adt7371 here?

That's also a typo. And don't ask we how I got from adt7310 to adt7371, I have
no idea.

[...]
>> +
>> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
>> +MODULE_DESCRIPTION("ADT7310/ADT7420 driver");
> This should be ADT7310/ADT7320, right?

Yes, fixed.

>> +MODULE_LICENSE("GPL");
> <...>
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7x10.c
>> @@ -0,0 +1,476 @@
[...]
>> +
>> +MODULE_AUTHOR("Hartmut Knaack");
>> +MODULE_DESCRIPTION("ADT7410 driver");
> This description does not seem appropriate any longer.

Changed it to "ADT7410/ADT7420, ADT7310/ADT7320 common code".

Thanks,
- Lars

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

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

On 02/15/2013 09:20 PM, Guenter Roeck wrote:
> On Fri, Feb 15, 2013 at 05:57:15PM +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>
>> ---
>>  drivers/hwmon/adt7410.c | 89 +++++++++++++++++++++++++++++++------------------
>>  1 file changed, 56 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
>> index 99a7290..5de0376 100644
>> --- a/drivers/hwmon/adt7410.c
>> +++ b/drivers/hwmon/adt7410.c
>> @@ -119,45 +119,31 @@ 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;
>>  
>>  		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);
>> +
>> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
>> +		if (ret) {
> 
> Should be
> 		if (ret < 0) {
> 
> or non-zero temperatures always create an error condition.
> 
>> +			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] = ret;
>> +
>>  		data->last_updated = jiffies;
>>  		data->valid = true;
>>  	}
>> @@ -167,6 +153,35 @@ abort:
>>  	return ret;
> 
> 	ret can be > 0 as no-error return. So you'll either have to
> 	change the no-error return case to always return 0, or change the
> 	return value check when the function is called.
>>  }
>>  
>> +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 < 3; i++) {
> 
> I think using ARRAY_SIZE would be better here. With "i < 3" you don't read the
> critical temperature, which proves the point (unless I am missing something).
> 
>> +		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
>> +		if (ret) {
> 
> 		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) {
> 
> 	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 +208,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)
> 
> Problematic; see above.
> 

Yep all correct, I somehow mixed up a few things between patch 6 and 7. Will
fix it for v2.

Thanks,
- Lars

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

* Re: [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320
  2013-02-15 20:32   ` Guenter Roeck
@ 2013-02-16 16:04     ` Lars-Peter Clausen
  2013-02-16 19:15       ` Guenter Roeck
  0 siblings, 1 reply; 27+ messages in thread
From: Lars-Peter Clausen @ 2013-02-16 16:04 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Jean Delvare, Hartmut Knaack, Jonathan Cameron, lm-sensors,
	linux-iio

On 02/15/2013 09:32 PM, Guenter Roeck wrote:
> On Fri, Feb 15, 2013 at 05:57:16PM +0100, Lars-Peter Clausen wrote:
>> The adt7310/adt7320 is the SPI version of the adt7410/adt7320. 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>
>> ---
>>  drivers/hwmon/Kconfig   |  20 ++
>>  drivers/hwmon/Makefile  |   2 +
>>  drivers/hwmon/adt7310.c | 160 ++++++++++++++++
>>  drivers/hwmon/adt7410.c | 472 ++++++-----------------------------------------
>>  drivers/hwmon/adt7x10.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  drivers/hwmon/adt7x10.h |  47 +++++
>>  6 files changed, 758 insertions(+), 419 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/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..0483e6c
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7310.c
>> @@ -0,0 +1,160 @@
>> +/*
>> + * 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 "adt7x10.h"
>> +
>> +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 ADT7310_CMD_REG_MASK			0x28
>> +#define ADT7310_CMD_REG_OFFSET			3
>> +#define ADT7310_CMD_READ			0x40
>> +#define ADT7310_CMD_CON_READ			0x4
>> +
>> +#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
>> +
>> +static int adt7310_spi_read_word(struct device *dev,
>> +	u8 reg, u16 *data)
> 
> I don't really like the approach of separating the return value from the error
> code, if both can be returned directly. There are two reasons: First, the code
> gets more complex, and second, the compiler tends to get confused under some
> conditions abd believe that the return value is not set. And I really don't want
> to have to deal with the resulting build warnings, after getting rid of pretty
> much all such warnings in the hwmon subsystem. So I'd really appreciate if you
> could rewrite this and the subsequent patches to return both error and value as
> function return values.
> 

I can change it, but I don't think that the current code will confuse the
compiler, the return value is set under all circumstances. And the code
actually gets slightly more complex by rewriting it. Most of the time we don't
want to store the result of the read operation on stack but rather somewhere else.

	ret = adt7410_read_word(dev, TEMP);
	if (ret < 0) {
		...
	}
	data->temp[0] = ret;

Without the change we can write it as:

	ret = adt7410_read_word(dev, TEMP, &data->temp[0])
	if (ret) {
		...
	}

The difference is not that big, so I won't argue over it if you prefer the
first version.

Thanks,
- Lars

>> +{
>> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = spi_read(spi, (u8 *)data, sizeof(*data));
>> +	if (ret < 0) {
>> +		dev_err(dev, "SPI read word error\n");
>> +		return ret;
>> +	}
>> +
>> +	*data = be16_to_cpu(*data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int adt7310_spi_write_word(struct device *dev, u8 reg,
>> +	u16 data)
>> +{
>> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write word error\n");
>> +		return ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int adt7310_spi_read_byte(struct device *dev, u8 reg,
>> +	u8 *data)
>> +{
>> +	struct spi_device *spi = to_spi_device(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(dev, "SPI write command error\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = spi_read(spi, data, sizeof(*data));
>> +	if (ret < 0) {
>> +		dev_err(dev, "SPI read byte error\n");
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int adt7310_spi_write_byte(struct device *dev, u8 reg,
>> +	u8 data)
>> +{
>> +	struct spi_device *spi = to_spi_device(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(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_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/ADT7420 driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
>> index 5de0376..13734c5 100644
>> --- a/drivers/hwmon/adt7410.c
>> +++ b/drivers/hwmon/adt7410.c
>> @@ -1,483 +1,117 @@
>>  /*
>> - * 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
>> +#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
>> -
>> -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)
>> -{
>> -	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;
>> -}
>> -
>> -static int adt7410_update_temp(struct device *dev)
>> +static int adt7410_i2c_read_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) {
>> -
>> -		dev_dbg(&client->dev, "Starting update\n");
>> -
>> -		ret = adt7410_temp_ready(client); /* check for new value */
>> -		if (ret)
>> -			goto abort;
>> -
>> -		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
>> -		if (ret) {
>> -			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
>> -				ADT7410_REG_TEMP[0], ret);
>> -			goto abort;
>> -		}
>> -		data->temp[0] = ret;
>> -
>> -		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 < 3; i++) {
>> -		ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
>> -		if (ret) {
>> -			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) {
>> -		dev_dbg(&client->dev,
>> -			"Failed to read value: hyst reg, error %d\n",
>> -			ret);
>> +	ret = i2c_smbus_read_word_swapped(client, reg);
>> +	if (ret < 0) {
>> +		dev_err(dev, "I2C read error\n");
>>  		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);
>> -}
>> +	*data = ret;
>>  
>> -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);
>> +	return 0;
>>  }
>>  
>> -/*-----------------------------------------------------------------------*/
>> -
>> -/* sysfs attributes for hwmon */
>> -
>> -static ssize_t adt7410_show_temp(struct device *dev,
>> -				 struct device_attribute *da, char *buf)
>> +static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
>>  {
>> -	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;
>> +	int ret = 0;
>>  
>> -		ret = adt7410_update_temp(dev);
>> -		if (ret)
>> -			return ret;
>> -	}
>> +	ret = i2c_smbus_write_word_swapped(client, reg, data);
>> +	if (ret < 0)
>> +		dev_err(dev, "I2C write error\n");
>>  
>> -	return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
>> -		       data->temp[attr->index]));
>> +	return ret;
>>  }
>>  
>> -static ssize_t adt7410_set_temp(struct device *dev,
>> -				struct device_attribute *da,
>> -				const char *buf, size_t count)
>> +static int adt7410_i2c_read_byte(struct device *dev, u8 reg, u8 *data)
>>  {
>> -	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;
>> +	int ret = 0;
>>  
>> -	ret = kstrtol(buf, 10, &temp);
>> -	if (ret)
>> +	ret = i2c_smbus_read_byte_data(client, reg);
>> +	if (ret < 0) {
>> +		dev_err(dev, "I2C read error\n");
>>  		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;
>> +	*data = (u8)ret;
>>  
>> -	return count;
>> +	return 0;
>>  }
>>  
>> -static ssize_t adt7410_show_alarm(struct device *dev,
>> -				  struct device_attribute *da,
>> -				  char *buf)
>> +static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
>>  {
>>  	struct i2c_client *client = to_i2c_client(dev);
>> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> -	int ret;
>> +	int ret = 0;
>>  
>> -	ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
>> +	ret = i2c_smbus_write_byte_data(client, reg, data);
>>  	if (ret < 0)
>> -		return ret;
>> +		dev_err(&client->dev, "I2C write error\n");
>>  
>> -	return sprintf(buf, "%d\n", !!(ret & attr->index));
>> +	return ret;
>>  }
>>  
>> -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 */ }
>> +static const struct i2c_device_id adt7410_id[] = {
>> +	{ "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 */
>> +MODULE_DEVICE_TABLE(i2c, adt7410_id);
>>  
>>  static struct i2c_driver adt7410_driver = {
>> -	.class		= I2C_CLASS_HWMON,
>>  	.driver = {
>> -		.name	= "adt7410",
>> +		.name = "adt7410",
>>  		.pm	= ADT7410_DEV_PM_OPS,
>>  	},
>> -	.probe		= adt7410_probe,
>> -	.remove		= adt7410_remove,
>> -	.id_table	= adt7410_ids,
>> -	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
>> +	.probe = adt7410_i2c_probe,
>> +	.remove = adt7410_i2c_remove,
>> +	.id_table = adt7410_id,
>> +	.address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
>> +	.class = I2C_CLASS_HWMON,
>>  };
>> -
>>  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..2598aaf
>> --- /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_word(struct device *dev, u8 reg, u16 *data)
>> +{
>> +	struct adt7410_data *d = dev_get_drvdata(dev);
>> +	return d->ops->read_word(dev, reg, data);
>> +}
>> +
>> +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 int adt7410_read_byte(struct device *dev, u8 reg, u8 *data)
>> +{
>> +	struct adt7410_data *d = dev_get_drvdata(dev);
>> +	return d->ops->read_byte(dev, reg, data);
>> +}
>> +
>> +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 const u8 ADT7410_REG_TEMP[4] = {
>> +	ADT7410_TEMPERATURE,		/* input */
>> +	ADT7410_T_ALARM_HIGH,		/* high */
>> +	ADT7410_T_ALARM_LOW,		/* low */
>> +	ADT7410_T_CRIT,			/* critical */
>> +};
>> +
>> +/*
>> + * adt7410 register access by I2C
>> + */
>> +static int adt7410_temp_ready(struct device *dev)
>> +{
>> +	int i, ret;
>> +	u8 status;
>> +
>> +	for (i = 0; i < 6; i++) {
>> +		ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
>> +		if (ret)
>> +			return ret;
>> +		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) {
>> +
>> +		dev_dbg(dev, "Starting update\n");
>> +
>> +		ret = adt7410_temp_ready(dev); /* check for new value */
>> +		if (ret)
>> +			goto abort;
>> +
>> +		ret = adt7410_read_word(dev, ADT7410_REG_TEMP[0],
>> +					&data->temp[0]);
>> +		if (ret) {
>> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
>> +				ADT7410_REG_TEMP[0], ret);
>> +			goto abort;
>> +		}
>> +		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],
>> +					&data->temp[i]);
>> +		if (ret) {
>> +			dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
>> +				ADT7410_REG_TEMP[0], ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	ret = adt7410_read_byte(dev, ADT7410_T_HYST, &data->hyst);
>> +	if (ret) {
>> +		dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
>> +				ADT7410_T_HYST, ret);
>> +		return 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);
>> +	u8 status;
>> +	int ret;
>> +
>> +	ret = adt7410_read_byte(dev, ADT7410_STATUS, &status);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return sprintf(buf, "%d\n", !!(status & 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, &data->oldconfig);
>> +	if (ret < 0) {
>> +		dev_dbg(dev, "Can't read config? %d\n", ret);
>> +		return 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 driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
>> new file mode 100644
>> index 0000000..17c8053
>> --- /dev/null
>> +++ b/drivers/hwmon/adt7x10.h
>> @@ -0,0 +1,47 @@
>> +#ifndef __HWMON_ADT7410_H__
>> +#define __HWMON_ADT7410_H__
>> +
>> +#include <linux/types.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_word)(struct device *, u8 reg, u16 *data);
>> +	int (*write_word)(struct device *, u8 reg, u16 data);
>> +	int (*read_byte)(struct device *, u8 reg, u8 *data);
>> +	int (*write_byte)(struct device *, u8 reg, u8 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
>>
>>
> --
> 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] 27+ messages in thread

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

On Sat, Feb 16, 2013 at 05:04:15PM +0100, Lars-Peter Clausen wrote:
> On 02/15/2013 09:32 PM, Guenter Roeck wrote:
> > On Fri, Feb 15, 2013 at 05:57:16PM +0100, Lars-Peter Clausen wrote:
> >> The adt7310/adt7320 is the SPI version of the adt7410/adt7320. 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>
> >> ---
> >>  drivers/hwmon/Kconfig   |  20 ++
> >>  drivers/hwmon/Makefile  |   2 +
> >>  drivers/hwmon/adt7310.c | 160 ++++++++++++++++
> >>  drivers/hwmon/adt7410.c | 472 ++++++-----------------------------------------
> >>  drivers/hwmon/adt7x10.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>  drivers/hwmon/adt7x10.h |  47 +++++
> >>  6 files changed, 758 insertions(+), 419 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/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..0483e6c
> >> --- /dev/null
> >> +++ b/drivers/hwmon/adt7310.c
> >> @@ -0,0 +1,160 @@
> >> +/*
> >> + * 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 "adt7x10.h"
> >> +
> >> +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 ADT7310_CMD_REG_MASK			0x28
> >> +#define ADT7310_CMD_REG_OFFSET			3
> >> +#define ADT7310_CMD_READ			0x40
> >> +#define ADT7310_CMD_CON_READ			0x4
> >> +
> >> +#define AD7310_COMMAND(reg) (adt7371_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
> >> +
> >> +static int adt7310_spi_read_word(struct device *dev,
> >> +	u8 reg, u16 *data)
> > 
> > I don't really like the approach of separating the return value from the error
> > code, if both can be returned directly. There are two reasons: First, the code
> > gets more complex, and second, the compiler tends to get confused under some
> > conditions abd believe that the return value is not set. And I really don't want
> > to have to deal with the resulting build warnings, after getting rid of pretty
> > much all such warnings in the hwmon subsystem. So I'd really appreciate if you
> > could rewrite this and the subsequent patches to return both error and value as
> > function return values.
> > 
> 
> I can change it, but I don't think that the current code will confuse the
> compiler, the return value is set under all circumstances. And the code

There still tend to be conditions (mostly hit with randconfig configurations or
with some oddball configurations) where it gets confused; at least that is what
tends to happen.

> actually gets slightly more complex by rewriting it. Most of the time we don't
> want to store the result of the read operation on stack but rather somewhere else.
> 
> 	ret = adt7410_read_word(dev, TEMP);
> 	if (ret < 0) {
> 		...
> 	}
> 	data->temp[0] = ret;
> 
> Without the change we can write it as:
> 
> 	ret = adt7410_read_word(dev, TEMP, &data->temp[0])
> 	if (ret) {
> 		...
> 	}
> 
> The difference is not that big, so I won't argue over it if you prefer the
> first version.
> 
On the other side, you can then use functions such as spi_w8r16 and spi_w8r8,
and you don't need the conversion from one method to another in the i2c code.

So, yes, I would appreciate if you can make that change.

Thanks,
Guenter

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

* Re: [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register
  2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
                   ` (8 preceding siblings ...)
  2013-02-15 20:06 ` [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Guenter Roeck
@ 2013-02-19 23:03 ` Hartmut Knaack
  9 siblings, 0 replies; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-19 23:03 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

Lars-Peter Clausen schrieb:
> Make sure to clear the mode bits from the config register before setting the new
> mode. Otherwise we might end up with a different mode than we want to.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Reviewed-by: Hartmut Knaack <knaack.h@gmx.de>
> ---
>  drivers/hwmon/adt7410.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 797c2b8..4ccee4f 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -364,6 +364,7 @@ static int adt7410_probe(struct i2c_client *client,
>  	/*
>  	 * 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) {


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

* Re: [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP
  2013-02-15 16:57 ` [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP Lars-Peter Clausen
  2013-02-15 20:07   ` Guenter Roeck
@ 2013-02-19 23:07   ` Hartmut Knaack
  1 sibling, 0 replies; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-19 23:07 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

Lars-Peter Clausen schrieb:
> Only build the suspend/resume code if CONFIG_PM_SLEEP is selected. Currently the
> code is built if CONFIG_PM is selected, but it will also be selected if only
> runtime PM support is built into the kernel.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Hartmut Knaack <knaack.h@gmx.de>
> ---
>  drivers/hwmon/adt7410.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 4ccee4f..ae80815 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -415,7 +415,7 @@ static const struct i2c_device_id adt7410_ids[] = {
>  };
>  MODULE_DEVICE_TABLE(i2c, adt7410_ids);
>  
> -#ifdef CONFIG_PM
> +#ifdef CONFIG_PM_SLEEP
>  static int adt7410_suspend(struct device *dev)
>  {
>  	int ret;


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

* Re: [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro
  2013-02-15 16:57 ` [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro Lars-Peter Clausen
  2013-02-15 20:07   ` Guenter Roeck
@ 2013-02-19 23:13   ` Hartmut Knaack
  1 sibling, 0 replies; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-19 23:13 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

Lars-Peter Clausen schrieb:
> Use the SIMPLE_DEV_PM_OPS macro to declare the driver's pm_ops.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Reviewed-by: Hartmut Knaack <knaack.h@gmx.de>
> ---
>  drivers/hwmon/adt7410.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index ae80815..4ae16e0 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -437,10 +437,8 @@ static int adt7410_resume(struct device *dev)
>  	return ret;
>  }
>  
> -static const struct dev_pm_ops adt7410_dev_pm_ops = {
> -	.suspend	= adt7410_suspend,
> -	.resume		= adt7410_resume,
> -};
> +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


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

* Re: [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS helper macro
  2013-02-15 16:57 ` [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS " Lars-Peter Clausen
  2013-02-15 20:07   ` Guenter Roeck
@ 2013-02-19 23:17   ` Hartmut Knaack
  1 sibling, 0 replies; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-19 23:17 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

Lars-Peter Clausen schrieb:
> Use the I2C_ADDRS macro to initialize the I2C device's address_list. Doing so
> saves a few lines of boilerplate code.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Reviewed-by: Hartmut Knaack <knaack.h@gmx.de>
> ---
>  drivers/hwmon/adt7410.c | 6 +-----
>  1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 4ae16e0..99f6d32 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -78,10 +78,6 @@ enum adt7410_type {		/* keep sorted in alphabetical order */
>  	adt7410,
>  };
>  
> -/* Addresses scanned */
> -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
> -					I2C_CLIENT_END };
> -
>  static const u8 ADT7410_REG_TEMP[4] = {
>  	ADT7410_TEMPERATURE,		/* input */
>  	ADT7410_T_ALARM_HIGH,		/* high */
> @@ -453,7 +449,7 @@ static struct i2c_driver adt7410_driver = {
>  	.probe		= adt7410_probe,
>  	.remove		= adt7410_remove,
>  	.id_table	= adt7410_ids,
> -	.address_list	= normal_i2c,
> +	.address_list	= I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
>  };
>  
>  module_i2c_driver(adt7410_driver);


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

* Re: [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420
  2013-02-15 16:57 ` [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420 Lars-Peter Clausen
  2013-02-15 20:08   ` Guenter Roeck
@ 2013-02-19 23:21   ` Hartmut Knaack
  2013-02-21  0:10   ` Hartmut Knaack
  2 siblings, 0 replies; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-19 23:21 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

Lars-Peter Clausen schrieb:
> The adt7420 is software compatible to the adt7410.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Reviewed-by: Hartmut Knaack <knaack.h@gmx.de>
> ---
>  drivers/hwmon/Kconfig   | 4 ++--
>  drivers/hwmon/adt7410.c | 3 ++-
>  2 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 52d5174..89ac1cb 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -180,11 +180,11 @@ config SENSORS_ADM9240
>  	  will be called adm9240.
>  
>  config SENSORS_ADT7410
> -	tristate "Analog Devices ADT7410"
> +	tristate "Analog Devices ADT7410/ADT7420"
>  	depends on I2C
>  	help
>  	  If you say yes here you get support for the Analog Devices
> -	  ADT7410 temperature monitoring chip.
> +	  ADT7410 and ADT7420 temperature monitoring chips.
>  
>  	  This driver can also be built as a module. If so, the module
>  	  will be called adt7410.
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 99f6d32..99a7290 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -407,6 +407,7 @@ static int adt7410_remove(struct i2c_client *client)
>  
>  static const struct i2c_device_id adt7410_ids[] = {
>  	{ "adt7410", adt7410, },
> +	{ "adt7420", adt7410, },
>  	{ /* LIST END */ }
>  };
>  MODULE_DEVICE_TABLE(i2c, adt7410_ids);
> @@ -455,5 +456,5 @@ static struct i2c_driver adt7410_driver = {
>  module_i2c_driver(adt7410_driver);
>  
>  MODULE_AUTHOR("Hartmut Knaack");
> -MODULE_DESCRIPTION("ADT7410 driver");
> +MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
>  MODULE_LICENSE("GPL");


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

* Re: [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420
  2013-02-15 16:57 ` [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420 Lars-Peter Clausen
  2013-02-15 20:08   ` Guenter Roeck
  2013-02-19 23:21   ` Hartmut Knaack
@ 2013-02-21  0:10   ` Hartmut Knaack
  2 siblings, 0 replies; 27+ messages in thread
From: Hartmut Knaack @ 2013-02-21  0:10 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jean Delvare, Guenter Roeck, Jonathan Cameron, lm-sensors,
	linux-iio

Lars-Peter Clausen schrieb:
> The adt7420 is software compatible to the adt7410.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

I did some basic testing of the patch-set until this one (instantiation, removal, get/set min/max/crit/hyst, monitor temperatures and alarm triggers while changing temperatures) on an ADT7410, all went fine. No suspend/resume tested.
Tested-by: Hartmut Knaack <knaack.h@gmx.de>
> ---
>  drivers/hwmon/Kconfig   | 4 ++--
>  drivers/hwmon/adt7410.c | 3 ++-
>  2 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 52d5174..89ac1cb 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -180,11 +180,11 @@ config SENSORS_ADM9240
>  	  will be called adm9240.
>  
>  config SENSORS_ADT7410
> -	tristate "Analog Devices ADT7410"
> +	tristate "Analog Devices ADT7410/ADT7420"
>  	depends on I2C
>  	help
>  	  If you say yes here you get support for the Analog Devices
> -	  ADT7410 temperature monitoring chip.
> +	  ADT7410 and ADT7420 temperature monitoring chips.
>  
>  	  This driver can also be built as a module. If so, the module
>  	  will be called adt7410.
> diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
> index 99f6d32..99a7290 100644
> --- a/drivers/hwmon/adt7410.c
> +++ b/drivers/hwmon/adt7410.c
> @@ -407,6 +407,7 @@ static int adt7410_remove(struct i2c_client *client)
>  
>  static const struct i2c_device_id adt7410_ids[] = {
>  	{ "adt7410", adt7410, },
> +	{ "adt7420", adt7410, },
>  	{ /* LIST END */ }
>  };
>  MODULE_DEVICE_TABLE(i2c, adt7410_ids);
> @@ -455,5 +456,5 @@ static struct i2c_driver adt7410_driver = {
>  module_i2c_driver(adt7410_driver);
>  
>  MODULE_AUTHOR("Hartmut Knaack");
> -MODULE_DESCRIPTION("ADT7410 driver");
> +MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
>  MODULE_LICENSE("GPL");


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

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

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-15 16:57 [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Lars-Peter Clausen
2013-02-15 16:57 ` [PATCH 2/9] hwmon: (adt7410) Let suspend/resume depend on CONFIG_PM_SLEEP Lars-Peter Clausen
2013-02-15 20:07   ` Guenter Roeck
2013-02-19 23:07   ` Hartmut Knaack
2013-02-15 16:57 ` [PATCH 3/9] hwmon: (adt7410) Use the SIMPLE_DEV_PM_OPS helper macro Lars-Peter Clausen
2013-02-15 20:07   ` Guenter Roeck
2013-02-19 23:13   ` Hartmut Knaack
2013-02-15 16:57 ` [PATCH 4/9] hwmon: (adt7410) Use I2C_ADDRS " Lars-Peter Clausen
2013-02-15 20:07   ` Guenter Roeck
2013-02-19 23:17   ` Hartmut Knaack
2013-02-15 16:57 ` [PATCH 5/9] hwmon: (adt7410) Add device table entry for the adt7420 Lars-Peter Clausen
2013-02-15 20:08   ` Guenter Roeck
2013-02-19 23:21   ` Hartmut Knaack
2013-02-21  0:10   ` Hartmut Knaack
2013-02-15 16:57 ` [PATCH 6/9] hwmon: (adt7410) Don't re-read non-volatile registers Lars-Peter Clausen
2013-02-15 20:20   ` Guenter Roeck
2013-02-16 15:56     ` Lars-Peter Clausen
2013-02-15 16:57 ` [PATCH 7/9] hwmon: (adt7410) Add support for the adt7310/adt7320 Lars-Peter Clausen
2013-02-15 20:05   ` Hartmut Knaack
2013-02-16 15:54     ` Lars-Peter Clausen
2013-02-15 20:32   ` Guenter Roeck
2013-02-16 16:04     ` Lars-Peter Clausen
2013-02-16 19:15       ` Guenter Roeck
2013-02-15 16:57 ` [PATCH 8/9] hwmon: (adt7x10) Add alarm interrupt support Lars-Peter Clausen
2013-02-15 16:57 ` [PATCH 9/9] staging:iio: Remove adt7410 driver Lars-Peter Clausen
2013-02-15 20:06 ` [PATCH 1/9] hwmon: (adt7410) Clear unwanted bits in the config register Guenter Roeck
2013-02-19 23:03 ` Hartmut Knaack

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox