Title: [8267] trunk: taske [#5868], New IIO driver for ad715x CDC devices
Revision
8267
Author
bhsong
Date
2010-02-03 04:15:31 -0500 (Wed, 03 Feb 2010)

Log Message

taske [#5868], New IIO driver for ad715x CDC devices
 
Basic test is done by checking:
 
(1)char device node
root:/> cat /proc/devices
Character devices:
         253 iio
 
(2)sysfs nodes
root:/sys/devices/platform/i2c-bfin-twi.0/i2c-0/0-0090/iio/device0> ls
available_threshold_modes  device
ch1_sensitivity            event_line0
ch1_threshold              event_line0_sources
ch1_timeout                name
ch1_value                  power
ch2_sensitivity            subsystem
ch2_threshold              threshold_mode
ch2_timeout                uevent
ch2_value

Modified Paths

Added Paths

Diff

Modified: trunk/arch/blackfin/mach-bf537/boards/stamp.c (8266 => 8267)


--- trunk/arch/blackfin/mach-bf537/boards/stamp.c	2010-02-03 06:48:58 UTC (rev 8266)
+++ trunk/arch/blackfin/mach-bf537/boards/stamp.c	2010-02-03 09:15:31 UTC (rev 8267)
@@ -1643,6 +1643,14 @@
 		.platform_data = (void *)&ad7142_i2c_platform_data,
 	},
 #endif
+
+#if defined(CONFIG_AD715X) || defined(CONFIG_AD715X_MODULE)
+	{
+		I2C_BOARD_INFO("ad7150", 0x90),
+		.irq = IRQ_PG5, /* fixme: use real interrupt number */
+	},
+#endif
+
 #if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_BFIN_TWI_LCD_MODULE)
 	{
 		I2C_BOARD_INFO("pcf8574_lcd", 0x22),

Modified: trunk/drivers/staging/iio/adc/Kconfig (8266 => 8267)


--- trunk/drivers/staging/iio/adc/Kconfig	2010-02-03 06:48:58 UTC (rev 8266)
+++ trunk/drivers/staging/iio/adc/Kconfig	2010-02-03 09:15:31 UTC (rev 8267)
@@ -11,3 +11,10 @@
 	  convertors (ADC). (max1361, max1362, max1363, max1364, max1136,
 	  max1136, max1137, max1138, max1139, max1236, max1237, max11238,
 	  max1239) Provides direct access via sysfs.
+
+config AD715X
+	tristate "Analog Devices ad715x capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (ad7150, ad7152, ad7153, ad7156) Provides direct access via sysfs.

Modified: trunk/drivers/staging/iio/adc/Makefile (8266 => 8267)


--- trunk/drivers/staging/iio/adc/Makefile	2010-02-03 06:48:58 UTC (rev 8266)
+++ trunk/drivers/staging/iio/adc/Makefile	2010-02-03 09:15:31 UTC (rev 8267)
@@ -6,3 +6,4 @@
 max1363-$(CONFIG_IIO_RING_BUFFER) += max1363_ring.o
 
 obj-$(CONFIG_MAX1363) += max1363.o
+obj-$(CONFIG_AD715X) += ad715x.o

Added: trunk/drivers/staging/iio/adc/ad715x.c (0 => 8267)


--- trunk/drivers/staging/iio/adc/ad715x.c	                        (rev 0)
+++ trunk/drivers/staging/iio/adc/ad715x.c	2010-02-03 09:15:31 UTC (rev 8267)
@@ -0,0 +1,693 @@
+/*
+ * AD715X capacitive sensor driver supporting AD7150/2/3/6
+ *
+ * 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/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD715X registers definition
+ */
+
+#define AD715X_STATUS              0
+#define AD715X_STATUS_OUT1         (1 << 3)
+#define AD715X_STATUS_OUT2         (1 << 5)
+#define AD715X_CH1_DATA_HIGH       1
+#define AD715X_CH1_DATA_LOW        2
+#define AD715X_CH2_DATA_HIGH       3
+#define AD715X_CH2_DATA_LOW        4
+#define AD715X_CH1_AVG_HIGH        5
+#define AD715X_CH1_AVG_LOW         6
+#define AD715X_CH2_AVG_HIGH        7
+#define AD715X_CH2_AVG_LOW         8
+#define AD715X_CH1_SENSITIVITY     9
+#define AD715X_CH1_THR_HOLD_H      9
+#define AD715X_CH1_TIMEOUT         10
+#define AD715X_CH1_THR_HOLD_L      10
+#define AD715X_CH1_SETUP           11
+#define AD715X_CH2_SENSITIVITY     12
+#define AD715X_CH2_THR_HOLD_H      12
+#define AD715X_CH2_TIMEOUT         13
+#define AD715X_CH2_THR_HOLD_L      13
+#define AD715X_CH2_SETUP           14
+#define AD715X_CFG                 15
+#define AD715X_CFG_FIX             (1 << 7)
+#define AD715X_PD_TIMER            16
+#define AD715X_CH1_CAPDAC          17
+#define AD715X_CH2_CAPDAC          18
+#define AD715X_SN3                 19
+#define AD715X_SN2                 20
+#define AD715X_SN1                 21
+#define AD715X_SN0                 22
+#define AD715X_ID                  23
+
+
+/*
+ * struct ad715x_chip_info - chip specifc information
+ */
+
+struct ad715x_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct iio_work_cont		work_cont_thresh;
+	bool				inter;
+	s64				last_timestamp;
+	u16 ch1_threshold;     /* Ch1 Threshold (in fixed threshold mode) */
+	u8  ch1_sensitivity;   /* Ch1 Sensitivity (in adaptive threshold mode) */
+	u8  ch1_timeout;       /* Ch1 Timeout (in adaptive threshold mode) */
+	u16 ch2_threshold;     /* Ch2 Threshold (in fixed threshold mode) */
+	u8  ch2_sensitivity;   /* Ch1 Sensitivity (in adaptive threshold mode) */
+	u8  ch2_timeout;       /* Ch1 Timeout (in adaptive threshold mode) */
+	char current_mode[10]; /* adaptive/fixed threshold mode */
+	int old_state;
+};
+
+/*
+ * ad715x register access by I2C
+ */
+
+static int ad715x_i2c_read(struct ad715x_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+	u8 rx;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, &rx, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	*data = ""
+
+	return ret;
+}
+
+static int ad715x_i2c_write(struct ad715x_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	u8 tx[2] = {
+		reg,
+		data,
+	};
+
+	ret = i2c_master_send(client, tx, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/*
+ * sysfs nodes
+ */
+
+#define IIO_DEV_ATTR_AVAIL_THRESHOLD_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_threshold_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_THRESHOLD_MODE(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(threshold_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_THRESHOLD(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch1_threshold, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_THRESHOLD(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch2_threshold, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_SENSITIVITY(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_sensitivity, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_SENSITIVITY(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_sensitivity, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_TIMEOUT(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_timeout, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_TIMEOUT(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_timeout, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch1_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH2_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch2_value, S_IRUGO, _show, NULL, 0)
+
+static ssize_t ad715x_show_threshold_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "adaptive\nfixed\n");
+}
+
+IIO_DEV_ATTR_AVAIL_THRESHOLD_MODES(ad715x_show_threshold_modes);
+
+static ssize_t ad715x_show_ch1_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	u8 data_l, data_h;
+
+	ad715x_i2c_read(chip, AD715X_CH1_DATA_HIGH, &data_h);
+	ad715x_i2c_read(chip, AD715X_CH1_DATA_LOW, &data_l);
+	return sprintf(buf, "%d\n", (data_h << 8) | data_l);
+}
+
+IIO_DEV_ATTR_CH1_VALUE(ad715x_show_ch1_value);
+
+static ssize_t ad715x_show_ch2_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	u8 data_l, data_h;
+
+	ad715x_i2c_read(chip, AD715X_CH2_DATA_HIGH, &data_h);
+	ad715x_i2c_read(chip, AD715X_CH2_DATA_LOW, &data_l);
+	return sprintf(buf, "%d\n", (data_h << 8) | data_l);
+}
+
+IIO_DEV_ATTR_CH2_VALUE(ad715x_show_ch2_value);
+
+
+static ssize_t ad715x_show_threshold_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->current_mode);
+}
+
+static ssize_t ad715x_store_threshold_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+
+	ad715x_i2c_read(chip, AD715X_CFG, &cfg);
+
+	if (strncmp(buf, "fixed", 5) == 0) {
+		strcpy(chip->current_mode, "fixed");
+		cfg |= AD715X_CFG_FIX;
+		ad715x_i2c_write(chip, AD715X_CFG, cfg);
+
+		return len;
+	} else if (strncmp(buf, "adaptive", 8) == 0) {
+		strcpy(chip->current_mode, "adaptive");
+		cfg &= ~AD715X_CFG_FIX;
+		ad715x_i2c_write(chip, AD715X_CFG, cfg);
+
+		return len;
+	}
+
+	dev_err(dev, "not supported threshold mode\n");
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_THRESHOLD_MODE(S_IRUGO | S_IWUSR,
+		ad715x_show_threshold_mode,
+		ad715x_store_threshold_mode);
+
+static ssize_t ad715x_show_ch1_threshold(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_threshold);
+}
+
+static ssize_t ad715x_store_ch1_threshold(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad715x_i2c_write(chip, AD715X_CH1_THR_HOLD_H, data >> 8);
+		ad715x_i2c_write(chip, AD715X_CH1_THR_HOLD_L, data);
+		chip->ch1_threshold = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_CH1_THRESHOLD(S_IRUGO | S_IWUSR,
+		ad715x_show_ch1_threshold,
+		ad715x_store_ch1_threshold);
+
+static ssize_t ad715x_show_ch2_threshold(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_threshold);
+}
+
+static ssize_t ad715x_store_ch2_threshold(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad715x_i2c_write(chip, AD715X_CH2_THR_HOLD_H, data >> 8);
+		ad715x_i2c_write(chip, AD715X_CH2_THR_HOLD_L, data);
+		chip->ch2_threshold = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_CH2_THRESHOLD(S_IRUGO | S_IWUSR,
+		ad715x_show_ch2_threshold,
+		ad715x_store_ch2_threshold);
+
+static ssize_t ad715x_show_ch1_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_sensitivity);
+}
+
+static ssize_t ad715x_store_ch1_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad715x_i2c_write(chip, AD715X_CH1_SENSITIVITY, data);
+		chip->ch1_sensitivity = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_CH1_SENSITIVITY(S_IRUGO | S_IWUSR,
+		ad715x_show_ch1_sensitivity,
+		ad715x_store_ch1_sensitivity);
+
+static ssize_t ad715x_show_ch2_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_sensitivity);
+}
+
+static ssize_t ad715x_store_ch2_sensitivity(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad715x_i2c_write(chip, AD715X_CH2_SENSITIVITY, data);
+		chip->ch2_sensitivity = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_CH2_SENSITIVITY(S_IRUGO | S_IWUSR,
+		ad715x_show_ch2_sensitivity,
+		ad715x_store_ch2_sensitivity);
+
+static ssize_t ad715x_show_ch1_timeout(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_timeout);
+}
+
+static ssize_t ad715x_store_ch1_timeout(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad715x_i2c_write(chip, AD715X_CH1_TIMEOUT, data);
+		chip->ch1_timeout = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_CH1_TIMEOUT(S_IRUGO | S_IWUSR,
+		ad715x_show_ch1_timeout,
+		ad715x_store_ch1_timeout);
+
+static ssize_t ad715x_show_ch2_timeout(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_timeout);
+}
+
+static ssize_t ad715x_store_ch2_timeout(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad715x_i2c_write(chip, AD715X_CH2_TIMEOUT, data);
+		chip->ch2_timeout = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+IIO_DEV_ATTR_CH2_TIMEOUT(S_IRUGO | S_IWUSR,
+		ad715x_show_ch2_timeout,
+		ad715x_store_ch2_timeout);
+
+static ssize_t ad715x_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+IIO_DEVICE_ATTR(name, S_IRUGO, ad715x_show_name, NULL, 0);
+
+static struct attribute *ad715x_attributes[] = {
+	&iio_dev_attr_available_threshold_modes.dev_attr.attr,
+	&iio_dev_attr_threshold_mode.dev_attr.attr,
+	&iio_dev_attr_ch1_threshold.dev_attr.attr,
+	&iio_dev_attr_ch2_threshold.dev_attr.attr,
+	&iio_dev_attr_ch1_timeout.dev_attr.attr,
+	&iio_dev_attr_ch2_timeout.dev_attr.attr,
+	&iio_dev_attr_ch1_sensitivity.dev_attr.attr,
+	&iio_dev_attr_ch2_sensitivity.dev_attr.attr,
+	&iio_dev_attr_ch1_value.dev_attr.attr,
+	&iio_dev_attr_ch2_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad715x_attribute_group = {
+	.attrs = ad715x_attributes,
+};
+
+/*
+ * threshold events
+ */
+
+#define IIO_EVENT_CODE_CH1_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_CH1_LOW     (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+#define IIO_EVENT_CODE_CH2_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
+#define IIO_EVENT_CODE_CH2_LOW     (IIO_EVENT_CODE_DEVICE_SPECIFIC + 4)
+
+#define IIO_EVENT_ATTR_CH1_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch1_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_CH2_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch2_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_CH1_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch1_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_CH2_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ch2_low, _evlist, _show, _store, _mask)
+
+static void ad715x_interrupt_handler_bh(struct work_struct *work_s)
+{
+	struct iio_work_cont *wc
+		= container_of(work_s, struct iio_work_cont, ws_nocheck);
+	struct ad715x_chip_info *chip = wc->st;
+	u8 int_status;
+
+	ad715x_i2c_read(chip, AD715X_STATUS, &int_status);
+
+	if ((int_status & AD715X_STATUS_OUT1) && !(chip->old_state & AD715X_STATUS_OUT1))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH1_HIGH,
+				chip->last_timestamp);
+	else if ((!(int_status & AD715X_STATUS_OUT1)) && (chip->old_state & AD715X_STATUS_OUT1))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH1_LOW,
+				chip->last_timestamp);
+
+	if ((int_status & AD715X_STATUS_OUT2) && !(chip->old_state & AD715X_STATUS_OUT2))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH2_HIGH,
+				chip->last_timestamp);
+	else if ((!(int_status & AD715X_STATUS_OUT2)) && (chip->old_state & AD715X_STATUS_OUT2))
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CH2_LOW,
+				chip->last_timestamp);
+}
+
+static int ad715x_interrupt_handler_th(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad715x_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->work_cont_thresh.ws);
+
+	return 0;
+}
+
+IIO_EVENT_SH(threshold, &ad715x_interrupt_handler_th);
+
+static ssize_t ad715x_query_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	/*
+	 * AD715X provides two logic output channels, which can be used as interrupt
+	 * but the pins are not configurable
+	 */
+	return sprintf(buf, "1\n");
+}
+
+static ssize_t ad715x_set_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return len;
+}
+
+IIO_EVENT_ATTR_CH1_HIGH_SH(iio_event_threshold, ad715x_query_out_mode, ad715x_set_out_mode, 0);
+IIO_EVENT_ATTR_CH2_HIGH_SH(iio_event_threshold, ad715x_query_out_mode, ad715x_set_out_mode, 0);
+IIO_EVENT_ATTR_CH1_LOW_SH(iio_event_threshold, ad715x_query_out_mode, ad715x_set_out_mode, 0);
+IIO_EVENT_ATTR_CH2_LOW_SH(iio_event_threshold, ad715x_query_out_mode, ad715x_set_out_mode, 0);
+
+static struct attribute *ad715x_event_attributes[] = {
+	&iio_event_attr_ch1_high.dev_attr.attr,
+	&iio_event_attr_ch2_high.dev_attr.attr,
+	&iio_event_attr_ch1_low.dev_attr.attr,
+	&iio_event_attr_ch2_low.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad715x_event_attribute_group = {
+	.attrs = ad715x_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad715x_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret = 0, regdone = 0;
+	struct ad715x_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* 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;
+	}
+
+	/* Echipabilish that the iio_dev is a child of the i2c device */
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad715x_attribute_group;
+	chip->indio_dev->event_attrs = &ad715x_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;
+	regdone = 1;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0) {
+		iio_init_work_cont(&chip->work_cont_thresh,
+				ad715x_interrupt_handler_bh,
+				ad715x_interrupt_handler_bh,
+				AD715X_STATUS,
+				0,
+				chip);
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"ad715x");
+		if (ret)
+			goto error_free_dev;
+	}
+
+	dev_err(&client->dev, "%s capacitive sensor registered, irq :%d\n", id->name, client->irq);
+
+	return 0;
+
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(chip->indio_dev);
+	else
+		iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+error_ret:
+	return ret;
+}
+
+static int ad715x_remove(struct i2c_client *client)
+{
+	struct ad715x_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad715x_id[] = {
+	{ "ad7150", 0 },
+	{ "ad7151", 0 },
+	{ "ad7152", 0 },
+	{ "ad7153", 0 },
+	{ "ad7156", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad715x_id);
+
+static struct i2c_driver ad715x_driver = {
+	.driver = {
+		.name = "ad715x",
+	},
+	.probe = ad715x_probe,
+	.remove = ad715x_remove,
+	.id_table = ad715x_id,
+};
+
+static __init int ad715x_init(void)
+{
+	return i2c_add_driver(&ad715x_driver);
+}
+
+static __exit void ad715x_exit(void)
+{
+	i2c_del_driver(&ad715x_driver);
+}
+
+MODULE_AUTHOR("Barry Song <[email protected]>");
+MODULE_DESCRIPTION("Analog Device ad715x capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad715x_init);
+module_exit(ad715x_exit);
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to