Commit 06b86a75 authored by Sonic Zhang's avatar Sonic Zhang Committed by Greg Kroah-Hartman

staging: iio: adc: new driver for ADT7410 temperature sensors

Signed-off-by: default avatarSonic Zhang <sonic.zhang@analog.com>
Signed-off-by: default avatarMichael Hennerich <michael.hennerich@analog.com>
Acked-by: default avatarJonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent a5d8c6bc
...@@ -127,3 +127,10 @@ config ADT7310 ...@@ -127,3 +127,10 @@ config ADT7310
help help
Say yes here to build support for Analog Devices ADT7310 Say yes here to build support for Analog Devices ADT7310
temperature sensors. temperature sensors.
config ADT7410
tristate "Analog Devices ADT7410 temperature sensor driver"
depends on I2C
help
Say yes here to build support for Analog Devices ADT7410
temperature sensors.
...@@ -24,3 +24,4 @@ obj-$(CONFIG_AD7745) += ad7745.o ...@@ -24,3 +24,4 @@ obj-$(CONFIG_AD7745) += ad7745.o
obj-$(CONFIG_AD7816) += ad7816.o obj-$(CONFIG_AD7816) += ad7816.o
obj-$(CONFIG_ADT75) += adt75.o obj-$(CONFIG_ADT75) += adt75.o
obj-$(CONFIG_ADT7310) += adt7310.o obj-$(CONFIG_ADT7310) += adt7310.o
obj-$(CONFIG_ADT7410) += adt7410.o
/*
* ADT7410 digital temperature sensor driver supporting ADT7410
*
* Copyright 2010 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.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/rtc.h>
#include "../iio.h"
#include "../sysfs.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
/*
* 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 ADT7410_IRQS 2
/*
* struct adt7410_chip_info - chip specifc information
*/
struct adt7410_chip_info {
const char *name;
struct i2c_client *client;
struct iio_dev *indio_dev;
struct work_struct thresh_work;
s64 last_timestamp;
u8 config;
};
/*
* adt7410 register access by I2C
*/
static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
{
struct i2c_client *client = chip->client;
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 = chip->client;
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 = chip->client;
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 = chip->client;
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 ssize_t adt7410_show_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *dev_info = dev_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
u16 config;
int ret;
ret = adt7410_i2c_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_i2c_write_byte(chip, ADT7410_CONFIG, config);
if (ret)
return -EIO;
chip->config = config;
return ret;
}
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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
int ret;
int bits;
ret = adt7410_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
unsigned long data;
u16 config;
int ret;
ret = strict_strtoul(buf, 10, &data);
if (ret)
return -EINVAL;
ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
if (ret)
return -EIO;
config = chip->config & (~ADT7410_RESOLUTION);
if (data)
config |= ADT7410_RESOLUTION;
ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
if (ret)
return -EIO;
chip->config = config;
return ret;
}
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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
u8 id;
int ret;
ret = adt7410_i2c_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) {
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);
} else {
if (data & ADT7410_T13_VALUE_SIGN) {
/* convert supplement to positive value */
data >>= ADT7410_T13_VALUE_OFFSET;
data = (ADT7410_T13_VALUE_SIGN << 1) - data;
sign = '-';
}
return sprintf(buf, "%c%d.%.4d\n", sign,
(data >> ADT7410_T13_VALUE_FLOAT_OFFSET),
(data & ADT7410_T13_VALUE_FLOAT_MASK) * 625);
}
}
static ssize_t adt7410_show_value(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *dev_info = dev_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
u8 status;
u16 data;
int ret, i = 0;
do {
ret = adt7410_i2c_read_byte(chip, ADT7410_STATUS, &status);
if (ret)
return -EIO;
i++;
if (i == 10000)
return -EIO;
} while (status & ADT7410_STAT_NOT_RDY);
ret = adt7410_i2c_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 ssize_t adt7410_show_name(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *dev_info = dev_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
return sprintf(buf, "%s\n", chip->name);
}
static IIO_DEVICE_ATTR(name, S_IRUGO, adt7410_show_name, 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,
&iio_dev_attr_name.dev_attr.attr,
NULL,
};
static const struct attribute_group adt7410_attribute_group = {
.attrs = adt7410_attributes,
};
/*
* temperature bound events
*/
#define IIO_EVENT_CODE_ADT7410_ABOVE_ALARM IIO_BUFFER_EVENT_CODE(0)
#define IIO_EVENT_CODE_ADT7410_BELLOW_ALARM IIO_BUFFER_EVENT_CODE(1)
#define IIO_EVENT_CODE_ADT7410_ABOVE_CRIT IIO_BUFFER_EVENT_CODE(2)
static void adt7410_interrupt_bh(struct work_struct *work_s)
{
struct adt7410_chip_info *chip =
container_of(work_s, struct adt7410_chip_info, thresh_work);
u8 status;
if (adt7410_i2c_read_byte(chip, ADT7410_STATUS, &status))
return;
enable_irq(chip->client->irq);
if (status & ADT7410_STAT_T_HIGH)
iio_push_event(chip->indio_dev, 0,
IIO_EVENT_CODE_ADT7410_ABOVE_ALARM,
chip->last_timestamp);
if (status & ADT7410_STAT_T_LOW)
iio_push_event(chip->indio_dev, 0,
IIO_EVENT_CODE_ADT7410_BELLOW_ALARM,
chip->last_timestamp);
if (status & ADT7410_STAT_T_CRIT)
iio_push_event(chip->indio_dev, 0,
IIO_EVENT_CODE_ADT7410_ABOVE_CRIT,
chip->last_timestamp);
}
static int adt7410_interrupt(struct iio_dev *dev_info,
int index,
s64 timestamp,
int no_test)
{
struct adt7410_chip_info *chip = dev_info->dev_data;
chip->last_timestamp = timestamp;
schedule_work(&chip->thresh_work);
return 0;
}
IIO_EVENT_SH(adt7410, &adt7410_interrupt);
IIO_EVENT_SH(adt7410_ct, &adt7410_interrupt);
static ssize_t adt7410_show_event_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *dev_info = dev_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
int ret;
ret = adt7410_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
u16 config;
int ret;
ret = adt7410_i2c_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_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
int ret;
ret = adt7410_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
unsigned long data;
int ret;
u8 config;
ret = strict_strtoul(buf, 10, &data);
if (ret || data > 3)
return -EINVAL;
ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
if (ret)
return -EIO;
config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
config |= data;
ret = adt7410_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
u16 data;
int ret;
ret = adt7410_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
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_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
int ret;
u8 t_hyst;
ret = adt7410_i2c_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_get_drvdata(dev);
struct adt7410_chip_info *chip = dev_info->dev_data;
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_i2c_write_byte(chip, ADT7410_T_HYST, t_hyst);
if (ret)
return -EIO;
return ret;
}
IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7410,
adt7410_show_event_mode, adt7410_set_event_mode, 0);
IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7410,
adt7410_show_available_event_modes, NULL, 0);
IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt7410,
adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7410,
adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7410,
adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7410_ct,
adt7410_show_t_crit, adt7410_set_t_crit, 0);
IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7410,
adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
static struct attribute *adt7410_event_int_attributes[] = {
&iio_event_attr_event_mode.dev_attr.attr,
&iio_event_attr_available_event_modes.dev_attr.attr,
&iio_event_attr_fault_queue.dev_attr.attr,
&iio_event_attr_t_alarm_high.dev_attr.attr,
&iio_event_attr_t_alarm_low.dev_attr.attr,
&iio_event_attr_t_hyst.dev_attr.attr,
NULL,
};
static struct attribute *adt7410_event_ct_attributes[] = {
&iio_event_attr_event_mode.dev_attr.attr,
&iio_event_attr_available_event_modes.dev_attr.attr,
&iio_event_attr_fault_queue.dev_attr.attr,
&iio_event_attr_t_crit.dev_attr.attr,
&iio_event_attr_t_hyst.dev_attr.attr,
NULL,
};
static struct attribute_group adt7410_event_attribute_group[ADT7410_IRQS] = {
{
.attrs = adt7410_event_int_attributes,
},
{
.attrs = adt7410_event_ct_attributes,
}
};
/*
* device probe and remove
*/
static int __devinit adt7410_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adt7410_chip_info *chip;
int ret = 0;
unsigned long *adt7410_platform_data = client->dev.platform_data;
chip = kzalloc(sizeof(struct adt7410_chip_info), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
/* this is only used for device removal purposes */
i2c_set_clientdata(client, chip);
chip->client = client;
chip->name = id->name;
chip->indio_dev = iio_allocate_device();
if (chip->indio_dev == NULL) {
ret = -ENOMEM;
goto error_free_chip;
}
chip->indio_dev->dev.parent = &client->dev;
chip->indio_dev->attrs = &adt7410_attribute_group;
chip->indio_dev->event_attrs = adt7410_event_attribute_group;
chip->indio_dev->dev_data = (void *)chip;
chip->indio_dev->driver_module = THIS_MODULE;
chip->indio_dev->num_interrupt_lines = ADT7410_IRQS;
chip->indio_dev->modes = INDIO_DIRECT_MODE;
ret = iio_device_register(chip->indio_dev);
if (ret)
goto error_free_dev;
/* CT critcal temperature event. line 0 */
if (client->irq) {
ret = iio_register_interrupt_line(client->irq,
chip->indio_dev,
0,
IRQF_TRIGGER_LOW,
chip->name);
if (ret)
goto error_unreg_dev;
/*
* The event handler list element refer to iio_event_adt7410.
* All event attributes bind to the same event handler.
* One event handler can only be added to one event list.
*/
iio_add_event_to_list(&iio_event_adt7410,
&chip->indio_dev->interrupts[0]->ev_list);
}
/* INT bound temperature alarm event. line 1 */
if (adt7410_platform_data[0]) {
ret = iio_register_interrupt_line(adt7410_platform_data[0],
chip->indio_dev,
1,
adt7410_platform_data[1],
chip->name);
if (ret)
goto error_unreg_ct_irq;
/*
* The event handler list element refer to iio_event_adt7410.
* All event attributes bind to the same event handler.
* One event handler can only be added to one event list.
*/
iio_add_event_to_list(&iio_event_adt7410_ct,
&chip->indio_dev->interrupts[1]->ev_list);
}
if (client->irq && adt7410_platform_data[0]) {
INIT_WORK(&chip->thresh_work, adt7410_interrupt_bh);
ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
if (ret) {
ret = -EIO;
goto error_unreg_int_irq;
}
/* 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_i2c_write_byte(chip, ADT7410_CONFIG, chip->config);
if (ret) {
ret = -EIO;
goto error_unreg_int_irq;
}
}
dev_info(&client->dev, "%s temperature sensor registered.\n",
id->name);
return 0;
error_unreg_int_irq:
iio_unregister_interrupt_line(chip->indio_dev, 1);
error_unreg_ct_irq:
iio_unregister_interrupt_line(chip->indio_dev, 0);
error_unreg_dev:
iio_device_unregister(chip->indio_dev);
error_free_dev:
iio_free_device(chip->indio_dev);
error_free_chip:
kfree(chip);
return ret;
}
static int __devexit adt7410_remove(struct i2c_client *client)
{
struct adt7410_chip_info *chip = i2c_get_clientdata(client);
struct iio_dev *indio_dev = chip->indio_dev;
unsigned long *adt7410_platform_data = client->dev.platform_data;
if (adt7410_platform_data[0])
iio_unregister_interrupt_line(indio_dev, 1);
if (client->irq)
iio_unregister_interrupt_line(indio_dev, 0);
iio_device_unregister(indio_dev);
iio_free_device(chip->indio_dev);
kfree(chip);
return 0;
}
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_probe,
.remove = __devexit_p(adt7410_remove),
.id_table = adt7410_id,
};
static __init int adt7410_init(void)
{
return i2c_add_driver(&adt7410_driver);
}
static __exit void adt7410_exit(void)
{
i2c_del_driver(&adt7410_driver);
}
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADT7410 digital"
" temperature sensor driver");
MODULE_LICENSE("GPL v2");
module_init(adt7410_init);
module_exit(adt7410_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment