Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 10/26/10 04:27, Zhang, Sonic wrote:
> 
> 
>> -----Original Message-----
>> From: Jonathan Cameron [mailto:jic23@xxxxxxxxx]
>> Sent: Monday, October 25, 2010 6:54 AM
>> To: Mike Frysinger
>> Cc: linux-iio@xxxxxxxxxxxxxxx;
>> device-drivers-devel@xxxxxxxxxxxxxxxxxxxx; Zhang, Sonic; Guenter Roeck
>> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>> ADT7408 temperature sensors
>>
>> On 10/23/10 21:29, Mike Frysinger wrote:
>>> From: Sonic Zhang <sonic.zhang@xxxxxxxxxx>
>> Here we enter new territory. This device is already supported
>> in hwmon.  Do we have a usecase that is not covered by that driver?
>>
> 
> I don't find a way to get event notification other than poll in hwmon
> framework. So, I move all temperature devic with interrupt available
> to IIO framework.

Whilst it isn't often done (and is a little clunky). It is possible to select
on sysfs attributes much like any other file. I'm sure Guenter can tell us
if any current hwmon devices are doing this?
> Sonic
> 
>> If this is only for hardware monitoring by Guenter (cc'd) can
>> perhaps advise on how to support everything you have here...
>>
>> I'm personally not against having drivers in IIO for devices
>> supported elsewhere, but the requirements for justification
>> are rather higher. Also care is needed to ensure no issues
>> with platform data etc.
>>
>> Guenter, for your information we have a set of temp drivers
>> coming, as a small element of a larger set, from Analog's
>> tree. Those I've reviewed so far have wanted to use IIO's
>> event infrastructure (which is much more general than hwmon's
>> handling of alarms) or have been suitably high performance
>> devices with general adc's to satisfy me that they clearly
>> have uses beyond hardware monitoring.
>>
>>
>>>
>>> Signed-off-by: Sonic Zhang <sonic.zhang@xxxxxxxxxx>
>>> Signed-off-by: Mike Frysinger <vapier@xxxxxxxxxx>
>>> ---
>>>  drivers/staging/iio/adc/Kconfig   |    7 +
>>>  drivers/staging/iio/adc/Makefile  |    1 +
>>>  drivers/staging/iio/adc/adt7408.c | 1006
>>> +++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 1014 insertions(+), 0 deletions(-)  create mode
>>> 100644 drivers/staging/iio/adc/adt7408.c
>>>
>>> diff --git a/drivers/staging/iio/adc/Kconfig
>>> b/drivers/staging/iio/adc/Kconfig index 5d13918..ea75700 100644
>>> --- a/drivers/staging/iio/adc/Kconfig
>>> +++ b/drivers/staging/iio/adc/Kconfig
>>> @@ -114,3 +114,10 @@ config ADT7408
>>>      help
>>>        Say yes here to build support for Analog Devices ADT7408
>>>        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.
>>> diff --git a/drivers/staging/iio/adc/Makefile
>>> b/drivers/staging/iio/adc/Makefile
>>> index 6c11363..dc2bdbe 100644
>>> --- a/drivers/staging/iio/adc/Makefile
>>> +++ b/drivers/staging/iio/adc/Makefile
>>> @@ -21,3 +21,4 @@ obj-$(CONFIG_AD774X) += ad774x.o
>>>  obj-$(CONFIG_AD7816) += ad7816.o
>>>  obj-$(CONFIG_ADT75) += adt75.o
>>>  obj-$(CONFIG_ADT7310) += adt7310.o
>>> +obj-$(CONFIG_ADT7408) += adt7408.o
>>> diff --git a/drivers/staging/iio/adc/adt7408.c
>>> b/drivers/staging/iio/adc/adt7408.c
>>> new file mode 100644
>>> index 0000000..25bd594
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/adc/adt7408.c
>>> @@ -0,0 +1,1006 @@
>>> +/*
>>> + * ADT7408 digital temperature sensor driver supporting ADT7408
>>> + *
>>> + * 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"
>>> +
>>> +/*
>>> + * ADT7408 registers definition
>>> + */
>>> +
>>> +#define ADT7408_CAPABILITY          0
>>> +#define ADT7408_CONFIG                      1
>>> +#define ADT7408_T_ALARM_HIGH                2
>>> +#define ADT7408_T_ALARM_LOW         3
>>> +#define ADT7408_T_CRIT                      4
>>> +#define ADT7408_TEMPERATURE         5
>>> +#define ADT7408_MANUFACTURER_ID             6
>>> +#define ADT7408_DEVICE_ID           7
>>> +
>>> +/*
>>> + * ADT7408 capability
>>> + */
>>> +#define ADT7408_CAP_ALARM_CRIT_TRIPS        0x1
>>> +#define ADT7408_CAP_HIGH_PRECISION  0x2
>>> +#define ADT7408_CAP_WIDER_RANGE             0x4
>>> +#define ADT7408_CAP_T_RESOLUTION_MASK       0x18
>>> +#define ADT7408_CAP_T_RESOLUTION_HIGH       0x18
>>> +#define ADT7408_CAP_T_RESOLUTION_LOW        0x8
>>> +
>>> +/*
>>> + * ADT7408 config
>>> + */
>>> +#define ADT7408_EVENT_MODE          0x1
>>> +#define ADT7408_EVENT_POLARITY              0x2
>>> +#define ADT7408_EVENT_CRIT_ONLY             0x4
>>> +#define ADT7408_EVENT_ENABLE                0x8
>>> +#define ADT7408_EVENT_STATUS                0x10
>>> +#define ADT7408_EVENT_CLEAR         0x20
>>> +#define ADT7408_EVENT_ALARM_LOCK    0x40
>>> +#define ADT7408_EVENT_CRIT_LOCK             0x80
>>> +#define ADT7408_PD                  0x100
>>> +#define ADT7408_HISTERESIS_MASK             0x600
>>> +#define ADT7408_HISTERESIS_1_5              0x200
>>> +#define ADT7408_HISTERESIS_3                0x400
>>> +#define ADT7408_HISTERESIS_6                0x600
>>> +
>>> +/*
>>> + * ADT7408 masks
>>> + */
>>> +#define ADT7408_BOUND_VALUE_SIGN            0x400
>>> +#define ADT7408_BOUND_VALUE_OFFSET          2
>>> +#define ADT7408_BOUND_VALUE_FLOAT_OFFSET    2
>>> +#define ADT7408_BOUND_VALUE_FLOAT_MASK              0x3
>>> +#define ADT7408_T_VALUE_SIGN                        0x1000
>>> +#define ADT7408_T_VALUE_FLOAT_OFFSET                4
>>> +#define ADT7408_T_VALUE_FLOAT_MASK          0xF
>>> +
>>> +/*
>>> + * ADT7408 event source
>>> + */
>>> +#define ADT7408_T_BELLOW_ALARM                      0x2000
>>> +#define ADT7408_T_ABOVE_ALARM                       0x4000
>>> +#define ADT7408_T_ABOVE_CRIT                        0x8000
>>> +
>>> +
>>> +/*
>>> + * struct adt7408_chip_info - chip specifc information  */
>>> +
>>> +struct adt7408_chip_info {
>>> +    const char *name;
>>> +    struct i2c_client *client;
>>> +    struct iio_dev *indio_dev;
>>> +    struct work_struct thresh_work;
>>> +    s64 last_timestamp;
>>> +    u16 config;
>>> +};
>>> +
>>> +/*
>>> + * adt7408 register access by I2C
>>> + */
>>> +
>>> +static int adt7408_i2c_read(struct adt7408_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 adt7408_i2c_write(struct adt7408_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 adt7408_is_event_locked(struct adt7408_chip_info *chip) {
>>> +    return chip->config & (ADT7408_EVENT_ALARM_LOCK |
>>> +ADT7408_EVENT_ALARM_LOCK); }
>>> +
>>> +static ssize_t adt7408_show_mode(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +
>>> +    if (chip->config & ADT7408_PD)
>>> +            return sprintf(buf, "power-save\n");
>>> +    else
>>> +            return sprintf(buf, "full\n");
>>> +}
>>> +
>>> +static ssize_t adt7408_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 adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & (~ADT7408_PD);
>>> +    if (!strcmp(buf, "full"))
>>> +            config |= ADT7408_PD;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_mode,
>>> +            adt7408_store_mode,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_available_modes(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return sprintf(buf, "full\npower-down\n"); }
>>> +
>>> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO,
>>> +adt7408_show_available_modes, NULL, 0);
>>> +
>>> +static ssize_t adt7408_show_capability(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 capability;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CAPABILITY, &capability);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "0x%x\n", capability); }
>>> +
>>> +static IIO_DEVICE_ATTR(capability, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_capability,
>>> +            NULL,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_manufactory_id(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 id;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "0x%x\n", id);
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(manufactory_id, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_manufactory_id,
>>> +            NULL,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_device_id(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 id;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "0x%x\n", id);
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(device_id, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_device_id,
>>> +            NULL,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_value(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 data;
>>> +    char sign = ' ';
>>> +    int ret;
>>> +
>>> +    if (chip->config & ADT7408_PD) {
>>> +            dev_err(dev, "Can't read value in power-down mode.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    if (data & ADT7408_T_VALUE_SIGN) {
>>> +            /* convert supplement to positive value */
>>> +            data = (ADT7408_T_VALUE_SIGN << 1) - data;
>>> +            sign = '-';
>>> +    }
>>> +
>>> +    return sprintf(buf, "%c%d.%.4d\n", sign,
>>> +            (data >> ADT7408_T_VALUE_FLOAT_OFFSET),
>>> +            (data & ADT7408_T_VALUE_FLOAT_MASK) * 625); }
>>> +
>>> +static IIO_DEVICE_ATTR(value, S_IRUGO, adt7408_show_value, NULL, 0);
>>> +
>>> +static ssize_t adt7408_show_name(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    return sprintf(buf, "%s\n", chip->name); }
>>> +
>>> +static IIO_DEVICE_ATTR(name, S_IRUGO, adt7408_show_name, NULL, 0);
>>> +
>>> +static struct attribute *adt7408_attributes[] = {
>>> +    &iio_dev_attr_available_modes.dev_attr.attr,
>>> +    &iio_dev_attr_mode.dev_attr.attr,
>>> +    &iio_dev_attr_capability.dev_attr.attr,
>>> +    &iio_dev_attr_device_id.dev_attr.attr,
>>> +    &iio_dev_attr_manufactory_id.dev_attr.attr,
>>> +    &iio_dev_attr_value.dev_attr.attr,
>>> +    &iio_dev_attr_name.dev_attr.attr,
>>> +    NULL,
>>> +};
>>> +
>>> +static const struct attribute_group adt7408_attribute_group = {
>>> +    .attrs = adt7408_attributes,
>>> +};
>>> +
>>> +/*
>>> + * temperature bound events
>>> + */
>>> +
>>> +#define IIO_EVENT_CODE_ADT7408_ABOVE_ALARM
>> (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
>>> +#define IIO_EVENT_CODE_ADT7408_BELLOW_ALARM
>> (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
>>> +#define IIO_EVENT_CODE_ADT7408_ABOVE_CRIT
>> (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
>>> +
>>> +static void adt7408_interrupt_bh(struct work_struct *work_s) {
>>> +    struct adt7408_chip_info *chip =
>>> +            container_of(work_s, struct adt7408_chip_info,
>> thresh_work);
>>> +    u16 config;
>>> +    u16 data;
>>> +
>>> +    if (adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config))
>>> +            return;
>>> +
>>> +    if (!(chip->config & ADT7408_EVENT_STATUS))
>>> +            return;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_CLEAR;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_CLEAR;
>>> +
>>> +    adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +
>>> +    if (adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data))
>>> +            goto exit;
>>> +
>>> +    if (data & ADT7408_T_ABOVE_ALARM)
>>> +            iio_push_event(chip->indio_dev, 0,
>>> +                    IIO_EVENT_CODE_ADT7408_ABOVE_ALARM,
>>> +                    chip->last_timestamp);
>>> +    if (data & ADT7408_T_BELLOW_ALARM)
>>> +            iio_push_event(chip->indio_dev, 0,
>>> +                    IIO_EVENT_CODE_ADT7408_BELLOW_ALARM,
>>> +                    chip->last_timestamp);
>>> +    if (data & ADT7408_T_ABOVE_CRIT)
>>> +            iio_push_event(chip->indio_dev, 0,
>>> +                    IIO_EVENT_CODE_ADT7408_ABOVE_CRIT,
>>> +                    chip->last_timestamp);
>>> +exit:
>>> +    enable_irq(chip->client->irq);
>>> +}
>>> +
>>> +static int adt7408_interrupt(struct iio_dev *dev_info,
>>> +            int index,
>>> +            s64 timestamp,
>>> +            int no_test)
>>> +{
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +
>>> +    chip->last_timestamp = timestamp;
>>> +    schedule_work(&chip->thresh_work);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +IIO_EVENT_SH(adt7408, &adt7408_interrupt);
>>> +
>>> +static ssize_t adt7408_show_event_mode(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    if (chip->config & ADT7408_EVENT_MODE)
>>> +            return sprintf(buf, "interrupt\n");
>>> +    else
>>> +            return sprintf(buf, "comparator\n"); }
>>> +
>>> +static ssize_t adt7408_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 adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config &= ~ADT7408_EVENT_MODE;
>>> +    if (strcmp(buf, "comparator") != 0)
>>> +            config |= ADT7408_EVENT_MODE;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_available_event_modes(struct
>> device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return sprintf(buf, "comparator\ninterrupt\n"); }
>>> +
>>> +static ssize_t adt7408_show_event_crit_only(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_CRIT_ONLY)); }
>>> +
>>> +static ssize_t adt7408_set_event_crit_only(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    unsigned long data = 0;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = strict_strtoul(buf, 10, &data);
>>> +    if (ret)
>>> +            return -EINVAL;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config &= ~ADT7408_EVENT_CRIT_ONLY;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_CRIT_ONLY;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_event_enable(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_ENABLE)); }
>>> +
>>> +static ssize_t adt7408_set_event_enable(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    unsigned long data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = strict_strtoul(buf, 10, &data);
>>> +    if (ret)
>>> +            return -EINVAL;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_ENABLE;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_ENABLE;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_alarm_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_ALARM_LOCK)); }
>>> +
>>> +static ssize_t adt7408_set_alarm_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_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 = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_ALARM_LOCK;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_ALARM_LOCK;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_crit_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_CRIT_LOCK)); }
>>> +
>>> +static ssize_t adt7408_set_crit_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_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 = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_CRIT_LOCK;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_CRIT_LOCK;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +
>>> +static inline ssize_t adt7408_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 adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 data;
>>> +    char sign = ' ';
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, bound_reg, &data);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    data >>= ADT7408_BOUND_VALUE_OFFSET;
>>> +    if (data & ADT7408_BOUND_VALUE_SIGN) {
>>> +            /* convert supplement to positive value */
>>> +            data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
>>> +            sign = '-';
>>> +    }
>>> +
>>> +    return sprintf(buf, "%c%d.%.2d\n", sign,
>>> +                    data >> ADT7408_BOUND_VALUE_FLOAT_OFFSET,
>>> +                    (data & ADT7408_BOUND_VALUE_FLOAT_MASK) * 25); }
>>> +
>>> +static inline ssize_t adt7408_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 adt7408_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 (len > ADT7408_BOUND_VALUE_FLOAT_OFFSET)
>>> +                    len = ADT7408_BOUND_VALUE_FLOAT_OFFSET;
>>> +            pos[len] = 0;
>>> +            ret = strict_strtol(pos, 10, &tmp2);
>>> +
>>> +            if (!ret)
>>> +                    tmp2 = (tmp2 / 25) * 25;
>>> +    }
>>> +
>>> +    if (tmp1 < 0)
>>> +            data = (u16)(-tmp1);
>>> +    else
>>> +            data = (u16)tmp1;
>>> +    data = (data << ADT7408_BOUND_VALUE_FLOAT_OFFSET) |
>>> +            (tmp2 & ADT7408_BOUND_VALUE_FLOAT_MASK);
>>> +    if (tmp1 < 0)
>>> +            /* convert positive value to supplyment */
>>> +            data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
>>> +    data <<= ADT7408_BOUND_VALUE_OFFSET;
>>> +
>>> +    ret = adt7408_i2c_write(chip, bound_reg, data);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_alarm_high(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return adt7408_show_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_HIGH, buf);
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_alarm_high(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    return adt7408_set_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_HIGH, buf, len);
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_alarm_low(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return adt7408_show_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_LOW, buf);
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_alarm_low(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    return adt7408_set_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_LOW, buf, len);
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_crit(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return adt7408_show_t_bound(dev, attr,
>>> +                    ADT7408_T_CRIT, buf);
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_crit(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    return adt7408_set_t_bound(dev, attr,
>>> +                    ADT7408_T_CRIT, buf, len);
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_hyst(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    /* retrive ALART status */
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    switch (chip->config & ADT7408_HISTERESIS_MASK) {
>>> +    case ADT7408_HISTERESIS_1_5:
>>> +            return sprintf(buf, "1.5\n");
>>> +    case ADT7408_HISTERESIS_3:
>>> +            return sprintf(buf, "3\n");
>>> +    case ADT7408_HISTERESIS_6:
>>> +            return sprintf(buf, "6\n");
>>> +    default:
>>> +            return sprintf(buf, "Disabled\n");
>>> +    }
>>> +}
>>> +
>>> +static inline ssize_t adt7408_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 adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +    u16 config = 0;
>>> +
>>> +    if (strcmp(buf, "disble"))
>>> +            config = ADT7408_HISTERESIS_MASK;
>>> +    else if (strcmp(buf, "1.5"))
>>> +            config = ADT7408_HISTERESIS_1_5;
>>> +    else if (len > 1 && buf[0] == '3')
>>> +            config = ADT7408_HISTERESIS_6;
>>> +    else if (len > 1 && buf[0] == '6')
>>> +            config = ADT7408_HISTERESIS_6;
>>> +
>>> +    if (!config)
>>> +            return -EINVAL;
>>> +
>>> +    /* retrive ALART status */
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config |= chip->config & ~ADT7408_HISTERESIS_MASK;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_available_t_hyst(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return sprintf(buf, "1.5\n3\n6\ndisable\n"); }
>>> +
>>> +IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7408,
>>> +            adt7408_show_event_mode, adt7408_set_event_mode, 0);
>>> +IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7408,
>>> +            adt7408_show_available_event_modes, NULL, 0);
>>> +IIO_EVENT_ATTR_SH(event_crit_only, iio_event_adt7408,
>>> +            adt7408_show_event_crit_only,
>> adt7408_set_event_crit_only, 0);
>>> +IIO_EVENT_ATTR_SH(event_enable, iio_event_adt7408,
>>> +            adt7408_show_event_enable,
>> adt7408_set_event_enable, 0);
>>> +IIO_EVENT_ATTR_SH(alarm_lock, iio_event_adt7408,
>>> +            adt7408_show_alarm_lock, adt7408_set_alarm_lock, 0);
>>> +IIO_EVENT_ATTR_SH(crit_lock, iio_event_adt7408,
>>> +            adt7408_show_crit_lock, adt7408_set_crit_lock, 0);
>>> +IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7408,
>>> +            adt7408_show_t_alarm_high,
>> adt7408_set_t_alarm_high, 0);
>>> +IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7408,
>>> +            adt7408_show_t_alarm_low, adt7408_set_t_alarm_low, 0);
>>> +IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7408,
>>> +            adt7408_show_t_crit, adt7408_set_t_crit, 0);
>>> +IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7408,
>>> +            adt7408_show_t_hyst, adt7408_set_t_hyst, 0);
>>> +IIO_EVENT_ATTR_SH(available_t_hyst, iio_event_adt7408,
>>> +            adt7408_show_available_t_hyst, NULL, 0);
>>> +
>>> +static struct attribute *adt7408_event_attributes[] = {
>>> +    &iio_event_attr_event_mode.dev_attr.attr,
>>> +    &iio_event_attr_available_event_modes.dev_attr.attr,
>>> +    &iio_event_attr_event_crit_only.dev_attr.attr,
>>> +    &iio_event_attr_event_enable.dev_attr.attr,
>>> +    &iio_event_attr_alarm_lock.dev_attr.attr,
>>> +    &iio_event_attr_crit_lock.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_crit.dev_attr.attr,
>>> +    &iio_event_attr_t_hyst.dev_attr.attr,
>>> +    &iio_event_attr_available_t_hyst.dev_attr.attr,
>>> +    NULL,
>>> +};
>>> +
>>> +static struct attribute_group adt7408_event_attribute_group = {
>>> +    .attrs = adt7408_event_attributes,
>>> +};
>>> +
>>> +/*
>>> + * device probe and remove
>>> + */
>>> +
>>> +static int __devinit adt7408_probe(struct i2c_client *client,
>>> +            const struct i2c_device_id *id)
>>> +{
>>> +    struct adt7408_chip_info *chip;
>>> +    int ret = 0;
>>> +
>>> +    chip = kzalloc(sizeof(struct adt7408_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 = &adt7408_attribute_group;
>>> +    chip->indio_dev->event_attrs = &adt7408_event_attribute_group;
>>> +    chip->indio_dev->dev_data = (void *)chip;
>>> +    chip->indio_dev->driver_module = THIS_MODULE;
>>> +    chip->indio_dev->num_interrupt_lines = 1;
>>> +    chip->indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> +    ret = iio_device_register(chip->indio_dev);
>>> +    if (ret)
>>> +            goto error_free_dev;
>>> +
>>> +    if (client->irq) {
>>> +            ret = iio_register_interrupt_line(client->irq,
>>> +                            chip->indio_dev,
>>> +                            0,
>>> +                            client->irq_flags,
>>> +                            chip->name);
>>> +            if (ret)
>>> +                    goto error_unreg_dev;
>>> +
>>> +            /*
>>> +             * The event handler list element refer to
>> iio_event_adt7408.
>>> +             * All event attributes bind to the same event handler.
>>> +             * So, only register event handler once.
>>> +             */
>>> +            iio_add_event_to_list(&iio_event_adt7408,
>>> +
>> &chip->indio_dev->interrupts[0]->ev_list);
>>> +
>>> +            INIT_WORK(&chip->thresh_work, adt7408_interrupt_bh);
>>> +
>>> +            ret = adt7408_i2c_read(chip, ADT7408_CONFIG,
>> &chip->config);
>>> +            if (ret) {
>>> +                    ret = -EIO;
>>> +                    goto error_unreg_irq;
>>> +            }
>>> +
>>> +            if (client->irq_flags & IRQF_TRIGGER_HIGH)
>>> +                    chip->config |= ADT7408_EVENT_POLARITY;
>>> +            else
>>> +                    chip->config &= ~ADT7408_EVENT_POLARITY;
>>> +
>>> +            ret = adt7408_i2c_write(chip, ADT7408_CONFIG,
>> chip->config);
>>> +            if (ret) {
>>> +                    ret = -EIO;
>>> +                    goto error_unreg_irq;
>>> +            }
>>> +    }
>>> +
>>> +    dev_info(&client->dev, "%s temperature sensor registered.\n",
>>> +                     id->name);
>>> +
>>> +    return 0;
>>> +
>>> +error_unreg_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 adt7408_remove(struct i2c_client *client) {
>>> +    struct adt7408_chip_info *chip = i2c_get_clientdata(client);
>>> +    struct iio_dev *indio_dev = chip->indio_dev;
>>> +
>>> +    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 adt7408_id[] = {
>>> +    { "adt7408", 0 },
>>> +    {}
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(i2c, adt7408_id);
>>> +
>>> +static struct i2c_driver adt7408_driver = {
>>> +    .driver = {
>>> +            .name = "adt7408",
>>> +    },
>>> +    .probe = adt7408_probe,
>>> +    .remove = __devexit_p(adt7408_remove),
>>> +    .id_table = adt7408_id,
>>> +};
>>> +
>>> +static __init int adt7408_init(void)
>>> +{
>>> +    return i2c_add_driver(&adt7408_driver); }
>>> +
>>> +static __exit void adt7408_exit(void) {
>>> +    i2c_del_driver(&adt7408_driver);
>>> +}
>>> +
>>> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@xxxxxxxxxx>");
>>> +MODULE_DESCRIPTION("Analog Devices ADT7408 digital"
>>> +                    " temperature sensor driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +
>>> +module_init(adt7408_init);
>>> +module_exit(adt7408_exit);
>>
>>
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux