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