[prev in list] [next in list] [prev in thread] [next in thread] 

List:       lm-sensors
Subject:    [lm-sensors] [RFC][PATCH 1/1] add adt7411 hwmon driver
From:       Rini van Zetten <rini () arvoo ! nl>
Date:       2009-02-20 10:22:36
Message-ID: 499E846C.8050105 () arvoo ! nl
[Download RAW message or body]



Signed-off-by: Rini van Zetten <rini@arvoo.nl>
---
  drivers/hwmon/Kconfig   |   10 +
  drivers/hwmon/Makefile  |    1 +
  drivers/hwmon/adt7411.c |  424 
+++++++++++++++++++++++++++++++++++++++++++++++
  3 files changed, 435 insertions(+), 0 deletions(-)
  create mode 100644 drivers/hwmon/adt7411.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b84bf06..1356ff9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -159,6 +159,16 @@ config SENSORS_ADM9240
  	  This driver can also be built as a module.  If so, the module
  	  will be called adm9240.

+config SENSORS_ADT7411
+	tristate "Analog Devices ADT7411"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes here you get support for the Analog Devices
+	  ADT7411 temperature monitoring chip.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called adt7411.
+
  config SENSORS_ADT7462
  	tristate "Analog Devices ADT7462"
  	depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2e80f37..ac9fb0a 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
  obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
  obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
  obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
+obj-$(CONFIG_SENSORS_AD7414)	+= adt7411.o
  obj-$(CONFIG_SENSORS_ADT7462)	+= adt7462.o
  obj-$(CONFIG_SENSORS_ADT7470)	+= adt7470.o
  obj-$(CONFIG_SENSORS_ADT7473)	+= adt7473.o
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
new file mode 100644
index 0000000..e073c72
--- /dev/null
+++ b/drivers/hwmon/adt7411.c
@@ -0,0 +1,424 @@
+/*
+ * An hwmon driver for the Analog Devices ADT7411
+ *
+ * Copyright (C) 2009 Rini van Zetten <rini@arvoo.nl> ARVOO Engineering 
B.V.
+ *
+ * Based on ad7414.c
+ *
+ * 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.
+ */
+
+#include <linux/module.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/sysfs.h>
+
+/*
+ * The ADT7411 registers
+ * Manufacturer ID is 0x41 for Analog Devices.
+ */
+
+#define ADT7411_REG_ISR1                0x00
+#define ADT7411_REG_ISR2                0x01
+#define ADT7411_REG_INT_TEMP_VDD_LSB    0x03
+#define ADT7411_REG_EXT_TEMP_AIN1_LSB   0x04
+#define ADT7411_REG_AIN5_LSB            0x05
+#define ADT7411_REG_VDD_MSB             0x06
+#define ADT7411_REG_INT_TEMP_MSB        0x07
+#define ADT7411_REG_EXT_TEMP_AIN1_MSB   0x08
+#define ADT7411_REG_AIN2_MSB            0x09
+#define ADT7411_REG_AIN3_MSB            0x0A
+#define ADT7411_REG_AIN4_MSB            0x0B
+#define ADT7411_REG_AIN5_MSB            0x0C
+#define ADT7411_REG_AIN6_MSB            0x0D
+#define ADT7411_REG_AIN7_MSB            0x0E
+#define ADT7411_REG_AIN8_MSB            0x0F
+#define ADT7411_REG_CONFIG1             0x18
+#define ADT7411_REG_CONFIG2             0x19
+#define ADT7411_REG_CONFIG3             0x1A
+#define ADT7411_REG_INT_MASK1           0x1D
+#define ADT7411_REG_INT_MASK2           0x1E
+#define ADT7411_REG_INT_TEMP_OFFSET     0x1F
+#define ADT7411_REG_EXT_TEMP_OFFSET     0x20
+#define ADT7411_REG_VDD_HIGH_LIMIT      0x23
+#define ADT7411_REG_VDD_LOW_LIMIT       0x24
+#define ADT7411_REG_INT_THIGH_LIMIT     0x25
+#define ADT7411_REG_INT_TLOW_LIMIT      0x26
+#define ADT7411_REG_EXT_THIGH_AIN1_VHIGH_LIMITS 0x27
+#define ADT7411_REG_EXT_TLOW_AIN1_VLOW_LIMITS   0x28
+#define ADT7411_REG_AIN2_VHIGH_LIMIT            0x2B
+#define ADT7411_REG_AIN2_VLOW_LIMIT             0x2C
+#define ADT7411_REG_AIN3_VHIGH_LIMIT            0x2D
+#define ADT7411_REG_AIN3_VLOW_LIMIT             0x2E
+#define ADT7411_REG_AIN4_VHIGH_LIMIT            0x2F
+#define ADT7411_REG_AIN4_VLOW_LIMIT             0x30
+#define ADT7411_REG_AIN5_VHIGH_LIMIT            0x31
+#define ADT7411_REG_AIN5_VLOW_LIMIT             0x32
+#define ADT7411_REG_AIN6_VHIGH_LIMIT            0x33
+#define ADT7411_REG_AIN6_VLOW_LIMIT             0x34
+#define ADT7411_REG_AIN7_VHIGH_LIMIT            0x35
+#define ADT7411_REG_AIN7_VLOW_LIMIT             0x36
+#define ADT7411_REG_AIN8_VHIGH_LIMIT            0x37
+#define ADT7411_REG_AIN8_VLOW_LIMIT             0x38
+#define ADT7411_REG_DEVID                       0x4D
+#define ADT7411_REG_MANID                       0x4E
+#define ADT7411_REG_REVISION                    0x4F
+#define ADT7411_REG_SPI_LOCK_STATUS             0x7F
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct adt7411_data {
+	struct device *hwmon_dev;
+	struct mutex lock; /* atomic read data updates */
+	char valid; /* !=0 if following fields are valid */
+	unsigned long next_update; /* In jiffies */
+
+	/* registers values */
+	s16 temp_int;
+	s16 vdd;
+	s16 ain[8]; /* AIN1..AIN8 */
+};
+
+/*
+ * Conversions
+ *
+ */
+#define ADV7411_1LSB_mV 2197 /* (mV) Vref/1024 = 2.25/1024 */
+
+s32 reg_to_volt(s16 reg)
+{
+	return (s32)(reg * ADV7411_1LSB_mV / 1000);
+}
+
+s16 reg_to_temp(s16 t)
+{
+	s16 res;
+
+	res = 0;
+	if (t & 0x200) {
+		/* negative */
+		res = -512 + (t & 0x1ff);
+	}
+	res += (t & 0x1ff);
+	res /= 4;
+
+	return res;
+}
+
+static struct adt7411_data *adt7411_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adt7411_data *data = i2c_get_clientdata(client);
+	u8 reg;
+
+	mutex_lock(&data->lock);
+
+	if (time_after(jiffies, data->next_update) || !data->valid) {
+
+		dev_dbg(&client->dev, "starting adt7411 update.\n");
+
+		/* read all measurements
+		 first LSB, next MSB!
+		 */
+
+		reg = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_INT_TEMP_VDD_LSB);/* lsb's */
+		data->vdd = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_VDD_MSB);/* Vdd msb */
+		data->vdd <<= 2;
+		data->vdd |= (reg >> 2);
+
+		data->temp_int = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_INT_TEMP_MSB);/* Tint msb */
+		data->temp_int <<= 2;
+		data->temp_int |= (reg & 0x3);
+
+		/* lsb's AIN4..1 */
+		reg = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_EXT_TEMP_AIN1_LSB);
+
+		data->ain[0] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_EXT_TEMP_AIN1_MSB);
+		data->ain[0] <<= 2;
+		data->ain[0] |= (reg & 0x3);
+
+		data->ain[1] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN2_MSB);
+		data->ain[1] <<= 2;
+		data->ain[1] |= ((reg & 0xc) >> 2);
+
+		data->ain[2] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN3_MSB);
+		data->ain[2] <<= 2;
+		data->ain[2] |= ((reg & 0x30) >> 4);/* lsb AIN3 */
+
+		data->ain[3] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN4_MSB);
+		data->ain[3] <<= 2;
+		data->ain[3] |= ((reg & 0xc0) >> 6);/* lsb AIN4 */
+
+		reg = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN5_LSB);/* lsb's AIN8..5 */
+
+		data->ain[4] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN5_MSB);
+		data->ain[4] <<= 2;
+		data->ain[4] |= (reg & 0x3);/* lsb AIN5 */
+
+		data->ain[5] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN6_MSB);
+		data->ain[5] <<= 2;
+		data->ain[5] |= ((reg & 0xc) >> 2);/* lsb AIN6 */
+
+		data->ain[6] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN7_MSB);
+		data->ain[6] <<= 2;
+		data->ain[6] |= ((reg & 0x30) >> 4);/* lsb AIN7 */
+
+		data->ain[7] = i2c_smbus_read_byte_data(client,
+				ADT7411_REG_AIN8_MSB);
+		data->ain[7] <<= 2;
+		data->ain[7] |= ((reg & 0xc0) >> 6);/* lsb AIN8 */
+
+		data->next_update = jiffies + HZ + HZ / 2;
+
+		data->valid = 1;
+
+	}
+
+	mutex_unlock(&data->lock);
+
+	return data;
+}
+
+static int adt7411_initialize(struct i2c_client *client)
+{
+	struct adt7411_data *data = i2c_get_clientdata(client);
+
+	mutex_lock(&data->lock);
+
+	/* Control Config 2: reset */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_CONFIG2, 0x80);
+
+	/* Control Config 2  rrobin, en aver */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_CONFIG2, 0x00);
+	/* Control Config 3 clk:22.5khz, int_Vref */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_CONFIG3, 0x09);
+	/* Interr Mask 1 */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_INT_MASK1, 0x00);
+	/* Interr Mask 2 */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_INT_MASK2, 0x00);
+	/* Int T Offset */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_INT_TEMP_OFFSET, 0x00);
+	/* Ext T Offset */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_EXT_TEMP_OFFSET, 0x00);
+	/* Vdd Vhigh Limit */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_VDD_HIGH_LIMIT, 0xc0);
+	/* Vdd Vlow Limit: 2.7V*/
+	i2c_smbus_write_byte_data(client, ADT7411_REG_VDD_LOW_LIMIT, 0x62);
+	/* Int Thigh: +100 */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_INT_THIGH_LIMIT, 0x64);
+	/* Int Tlow: -55 */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_INT_TLOW_LIMIT, 0xc9);
+	/* ExtT/AIN1 Vhigh */
+	i2c_smbus_write_byte_data(client,
+			ADT7411_REG_EXT_THIGH_AIN1_VHIGH_LIMITS, 0xff);
+	/* ExtTlow/AIN Vlow */
+	i2c_smbus_write_byte_data(client,
+			ADT7411_REG_EXT_TLOW_AIN1_VLOW_LIMITS, 0x00);
+	/* AIN2 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN2_VHIGH_LIMIT, 0xff);
+	/* AIN2 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN2_VLOW_LIMIT, 0x00);
+	/* AIN3 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN3_VHIGH_LIMIT, 0xff);
+	/* AIN3 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN3_VLOW_LIMIT, 0x00);
+	/* AIN4 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN4_VHIGH_LIMIT, 0xff);
+	/* AIN4 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN4_VLOW_LIMIT, 0x00);
+	/* AIN5 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN5_VHIGH_LIMIT, 0xff);
+	/* AIN5 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN5_VLOW_LIMIT, 0x00);
+	/* AIN6 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN6_VHIGH_LIMIT, 0xff);
+	/* AIN6 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN6_VLOW_LIMIT, 0x00);
+	/* AIN7 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN7_VHIGH_LIMIT, 0xff);
+	/* AIN7 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN7_VLOW_LIMIT, 0x00);
+	/* AIN8 Vhigh */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN8_VHIGH_LIMIT, 0xff);
+	/* AIN8 Vlow */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_AIN8_VLOW_LIMIT, 0x00);
+
+	/* Control Config 1
+	 start, AIN1-AIN2, dis INT */
+	i2c_smbus_write_byte_data(client, ADT7411_REG_CONFIG1, 0x29);
+
+	mutex_unlock(&data->lock);
+
+	return 1;
+}
+
+/*
+ * Sysfs stuff
+ */
+static ssize_t show_inttemp(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct adt7411_data *data = adt7411_update_device(dev);
+	return sprintf(buf, "%d\n", reg_to_temp(data->temp_int));
+}
+
+static ssize_t show_ain(struct device *dev, struct device_attribute 
*devattr,
+		char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct adt7411_data *data = adt7411_update_device(dev);
+
+	return sprintf(buf, "%d\n", reg_to_volt(data->ain[attr->index]));
+}
+
+static ssize_t show_vdd(struct device *dev, struct device_attribute 
*devattr,
+		char *buf)
+{
+	struct adt7411_data *data = adt7411_update_device(dev);
+
+	return sprintf(buf, "%d\n", reg_to_volt(data->vdd) * 311 / 100);
+}
+
+static SENSOR_DEVICE_ATTR(int_temp_input, S_IRUGO, show_inttemp, NULL, 0);
+static SENSOR_DEVICE_ATTR(ain1_input, S_IRUGO, show_ain, NULL, 0);
+static SENSOR_DEVICE_ATTR(ain2_input, S_IRUGO, show_ain, NULL, 1);
+static SENSOR_DEVICE_ATTR(ain3_input, S_IRUGO, show_ain, NULL, 2);
+static SENSOR_DEVICE_ATTR(ain4_input, S_IRUGO, show_ain, NULL, 3);
+static SENSOR_DEVICE_ATTR(ain5_input, S_IRUGO, show_ain, NULL, 4);
+static SENSOR_DEVICE_ATTR(ain6_input, S_IRUGO, show_ain, NULL, 5);
+static SENSOR_DEVICE_ATTR(ain7_input, S_IRUGO, show_ain, NULL, 6);
+static SENSOR_DEVICE_ATTR(ain8_input, S_IRUGO, show_ain, NULL, 7);
+static SENSOR_DEVICE_ATTR(vdd_input, S_IRUGO, show_vdd, NULL, 0);
+
+static struct attribute *adt7411_attributes[] = {
+		&sensor_dev_attr_int_temp_input.dev_attr.attr,
+		&sensor_dev_attr_ain1_input.dev_attr.attr,
+		&sensor_dev_attr_ain2_input.dev_attr.attr,
+		&sensor_dev_attr_ain3_input.dev_attr.attr,
+		&sensor_dev_attr_ain4_input.dev_attr.attr,
+		&sensor_dev_attr_ain5_input.dev_attr.attr,
+		&sensor_dev_attr_ain6_input.dev_attr.attr,
+		&sensor_dev_attr_ain7_input.dev_attr.attr,
+		&sensor_dev_attr_ain8_input.dev_attr.attr,
+		&sensor_dev_attr_vdd_input.dev_attr.attr, NULL };
+
+static const struct attribute_group adt7411_group = {
+		.attrs = adt7411_attributes, };
+
+static int adt7411_probe(struct i2c_client *client,
+		const struct i2c_device_id *dev_id)
+{
+	struct adt7411_data *data;
+	int err = 0;
+	u8 man_id, chip_id;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto exit;
+
+	data = kzalloc(sizeof(struct adt7411_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	i2c_set_clientdata(client, data);
+
+	mutex_init(&data->lock);
+
+	/* check whether it is the right chip */
+	man_id = i2c_smbus_read_byte_data(client, ADT7411_REG_MANID);
+	chip_id = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVID);
+
+	if ((man_id != 0x41) || (chip_id != 0x02)) {
+		dev_warn(&client->dev, "adt7411_probe unknown chip.\n");
+		goto exit_free;
+	}
+
+	dev_info(&client->dev, "chip found\n");
+
+	/* initialize chip */
+	adt7411_initialize(client);
+
+	/* Register sysfs hooks */
+	err = sysfs_create_group(&client->dev.kobj, &adt7411_group);
+	if (err)
+		goto exit_free;
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove;
+	}
+	return 0;
+
+exit_remove:
+	sysfs_remove_group(&client->dev.kobj, &adt7411_group);
+exit_free:
+	kfree(data);
+exit:
+	return err;
+}
+
+static int __devexit adt7411_remove(struct i2c_client *client)
+{
+	struct adt7411_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &adt7411_group);
+	kfree(data);
+	return 0;
+}
+
+static const struct i2c_device_id adt7411_id[] = {
+		{ "adt7411", 0 },
+		{ }
+};
+
+static struct i2c_driver adt7411_driver = {
+		.driver = {
+			.owner = THIS_MODULE,
+			.name = "adt7411",
+		},
+		.probe = adt7411_probe,
+		.remove = __devexit_p(adt7411_remove),
+		.id_table = adt7411_id,
+};
+
+static int __init adt7411_init(void)
+{
+	return i2c_add_driver(&adt7411_driver);
+}
+
+module_init(adt7411_init);
+
+static void __exit adt7411_exit(void)
+{
+	i2c_del_driver(&adt7411_driver);
+}
+module_exit(adt7411_exit);
+
+MODULE_AUTHOR("Rini van Zetten : <rini at arvoo.com> ");
+
+MODULE_DESCRIPTION("adt7411 driver");
+MODULE_LICENSE("GPL");
-- 
1.5.6.3

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic