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]

 




>-----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.

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