lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1433161211-22034-1-git-send-email-vladimir.barinov@cogentembedded.com>
Date:	Mon,  1 Jun 2015 15:20:11 +0300
From:	Vladimir Barinov <vladimir.barinov@...entembedded.com>
To:	Jonathan Cameron <jic23@...nel.org>
Cc:	Hartmut Knaack <knaack.h@....de>,
	Lars-Peter Clausen <lars@...afoo.de>,
	Peter Meerwald <pmeerw@...erw.net>,
	Rob Herring <robh+dt@...nel.org>,
	Pawel Moll <pawel.moll@....com>,
	Mark Rutland <mark.rutland@....com>,
	Ian Campbell <ijc+devicetree@...lion.org.uk>,
	Kumar Gala <galak@...eaurora.org>,
	linux-kernel@...r.kernel.org, linux-iio@...r.kernel.org,
	devicetree@...r.kernel.org
Subject: [PATCH 1/3] iio: adc: hi-843x: Holt HI-8435/8436/8437 descrete ADC

Add Holt descrete ADC driver for HI-8435/8436/8437 chips

Signed-off-by: Vladimir Barinov <vladimir.barinov@...entembedded.com>
---
 drivers/iio/adc/Kconfig   |  12 +
 drivers/iio/adc/Makefile  |   1 +
 drivers/iio/adc/hi-843x.c | 777 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 790 insertions(+)
 create mode 100644 drivers/iio/adc/hi-843x.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e36a73e..71b0efc 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -164,6 +164,18 @@ config EXYNOS_ADC
 	  of SoCs for drivers such as the touchscreen and hwmon to use to share
 	  this resource.
 
+config HI_843X
+	tristate "Holt Integrated Circuits HI-8435/8436/8437"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	depends on SPI
+	help
+	  If you say yes here you get support for Holt Integrated Circuits
+	  HI-8435/8436/8437 chip.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called hi-843x.
+
 config LP8788_ADC
 	tristate "LP8788 ADC driver"
 	depends on MFD_LP8788
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 3930e63..65f54c2 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_HI_843X) += hi-843x.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
 obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/iio/adc/hi-843x.c b/drivers/iio/adc/hi-843x.c
new file mode 100644
index 0000000..ccc46e7
--- /dev/null
+++ b/drivers/iio/adc/hi-843x.c
@@ -0,0 +1,777 @@
+/*
+ * Holt Integrated Circuits HI-8435/8436/8437 discrete ADC driver
+ *
+ * Copyright (C) 2015 Zodiac Inflight Innovations
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+
+#include <linux/interrupt.h>
+
+#define DRV_NAME "hi-843x"
+
+/* Register offsets for HI-843X */
+#define HI843X_CTRL_REG			0x02
+#define HI843X_PSEN_REG			0x04
+#define HI843X_TMDATA_REG		0x1E
+#define HI843X_GOCENHYS_REG		0x3A
+#define HI843X_SOCENHYS_REG		0x3C
+#define HI843X_SO7_0_REG		0x10
+#define HI843X_SO15_8_REG		0x12
+#define HI843X_SO23_16_REG		0x14
+#define HI843X_SO31_24_REG		0x16
+#define HI843X_SO31_0_REG		0x78
+
+#define HI843X_WRITE_OPCODE		0x00
+#define HI843X_READ_OPCODE		0x80
+
+/* THRESHOLD mask */
+#define HI843X_THRESHOLD_MAX		0x3f
+#define HI843X_THRESHOLD_MASK		0xff
+
+/* CTRL register bits */
+#define HI843X_CTRL_TEST		0x01
+#define HI843X_CTRL_SRST		0x02
+
+#define HI843X_DEBOUNCE_SOFT_DELAY_MAX	1000	/* ms */
+#define HI843X_DEBOUNCE_SOFT_DELAY_DEF	100	/* ms */
+
+struct hi843x_priv {
+	struct spi_device *spi;
+	struct mutex lock;
+	struct delayed_work work;
+
+	int mr_gpio;
+	bool debounce_soft;
+	int debounce_soft_delay;
+	int debounce_soft_val;
+
+	void *iio_buffer;
+	u8 reg_buffer[4] ____cacheline_aligned;
+};
+
+static int hi843x_readb(struct hi843x_priv *priv, u8 reg, u8 *val)
+{
+	reg |= HI843X_READ_OPCODE;
+	return spi_write_then_read(priv->spi, &reg, 1, val, 1);
+}
+
+static int hi843x_readw(struct hi843x_priv *priv, u8 reg, u16 *val)
+{
+	int ret;
+
+	reg |= HI843X_READ_OPCODE;
+	ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
+	*val = swab16p(val);
+
+	return ret;
+}
+
+static int hi843x_readl(struct hi843x_priv *priv, u8 reg, u32 *val)
+{
+	int ret;
+
+	reg |= HI843X_READ_OPCODE;
+	ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
+	*val = swab32p(val);
+
+	return ret;
+}
+
+static int hi843x_writeb(struct hi843x_priv *priv, u8 reg, u8 val)
+{
+	priv->reg_buffer[0] = reg | HI843X_WRITE_OPCODE;
+	priv->reg_buffer[1] = val;
+
+	return spi_write(priv->spi, priv->reg_buffer, 2);
+}
+
+static int hi843x_writew(struct hi843x_priv *priv, u8 reg, u16 val)
+{
+	priv->reg_buffer[0] = reg | HI843X_WRITE_OPCODE;
+	priv->reg_buffer[1] = (val >> 8) & 0xff;
+	priv->reg_buffer[2] = val & 0xff;
+
+	return spi_write(priv->spi, priv->reg_buffer, 3);
+}
+
+ssize_t hi843x_debounce_soft_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sprintf(buf, "%d\n", priv->debounce_soft);
+}
+
+ssize_t hi843x_debounce_soft_delay_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sprintf(buf, "%d\n", priv->debounce_soft_delay);
+}
+
+ssize_t hi843x_sensing_mode_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u8 reg;
+
+	ret = hi843x_readb(priv, HI843X_PSEN_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg);
+}
+
+ssize_t hi843x_test_enable_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u8 reg;
+
+	ret = hi843x_readb(priv, HI843X_CTRL_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg & HI843X_CTRL_TEST);
+}
+
+ssize_t hi843x_test_mode_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u8 reg;
+
+	ret = hi843x_readb(priv, HI843X_TMDATA_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg);
+}
+
+ssize_t hi843x_debounce_soft_store(struct device *dev,
+				   struct device_attribute *attr,
+				    const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	priv->debounce_soft = !!val;
+
+	return len;
+}
+
+ssize_t hi843x_debounce_soft_delay_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val > HI843X_DEBOUNCE_SOFT_DELAY_MAX)
+		val = HI843X_DEBOUNCE_SOFT_DELAY_MAX;
+
+	priv->debounce_soft_delay = val;
+
+	return len;
+}
+
+ssize_t hi843x_sensing_mode_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	hi843x_writeb(priv, HI843X_PSEN_REG, val & 0xf);
+
+	return len;
+}
+
+ssize_t hi843x_test_enable_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	hi843x_writeb(priv, HI843X_CTRL_REG, val ? HI843X_CTRL_TEST : 0);
+
+	return len;
+}
+
+ssize_t hi843x_test_mode_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	hi843x_writeb(priv, HI843X_TMDATA_REG, val & 0xf);
+
+	return len;
+}
+
+ssize_t hi843x_threshold_gohys_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u16 reg;
+
+	ret = hi843x_readw(priv, HI843X_GOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", (reg >> 8) & HI843X_THRESHOLD_MASK);
+}
+
+ssize_t hi843x_threshold_gocval_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u16 reg;
+
+	ret = hi843x_readw(priv, HI843X_GOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg & HI843X_THRESHOLD_MASK);
+}
+
+ssize_t hi843x_threshold_sohys_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u16 reg;
+
+	ret = hi843x_readw(priv, HI843X_SOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", (reg >> 8) & HI843X_THRESHOLD_MASK);
+}
+
+ssize_t hi843x_threshold_socval_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u16 reg;
+
+	ret = hi843x_readw(priv, HI843X_SOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg & HI843X_THRESHOLD_MASK);
+}
+
+ssize_t hi843x_threshold_gohys_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val, ret;
+	u16 reg;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val > HI843X_THRESHOLD_MAX)
+		return -EINVAL;
+
+	mutex_lock(&priv->lock);
+
+	ret = hi843x_readw(priv, HI843X_GOCENHYS_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+	reg &= ~(HI843X_THRESHOLD_MASK << 8);
+	reg |= (val << 8);
+	hi843x_writew(priv, HI843X_GOCENHYS_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return len;
+}
+
+ssize_t hi843x_threshold_gocval_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val, ret;
+	u16 reg;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val > HI843X_THRESHOLD_MAX)
+		return -EINVAL;
+
+	mutex_lock(&priv->lock);
+
+	ret = hi843x_readw(priv, HI843X_GOCENHYS_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+	reg &= ~HI843X_THRESHOLD_MASK;
+	reg |= val;
+	hi843x_writew(priv, HI843X_GOCENHYS_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return len;
+}
+
+ssize_t hi843x_threshold_sohys_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val, ret;
+	u16 reg;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val > HI843X_THRESHOLD_MAX)
+		return -EINVAL;
+
+	mutex_lock(&priv->lock);
+
+	ret = hi843x_readw(priv, HI843X_SOCENHYS_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+	reg &= ~(HI843X_THRESHOLD_MASK << 8);
+	reg |= (val << 8);
+	hi843x_writew(priv, HI843X_SOCENHYS_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return len;
+}
+
+ssize_t hi843x_threshold_socval_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t len)
+{
+	struct hi843x_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val, ret;
+	u16 reg;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val > HI843X_THRESHOLD_MAX)
+		return -EINVAL;
+
+	mutex_lock(&priv->lock);
+
+	ret = hi843x_readw(priv, HI843X_SOCENHYS_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+	reg &= ~HI843X_THRESHOLD_MASK;
+	reg |= val;
+	hi843x_writew(priv, HI843X_SOCENHYS_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(debounce_soft, S_IRUGO | S_IWUSR,
+	hi843x_debounce_soft_show, hi843x_debounce_soft_store, 0);
+static IIO_DEVICE_ATTR(debounce_soft_delay, S_IRUGO | S_IWUSR,
+	hi843x_debounce_soft_delay_show, hi843x_debounce_soft_delay_store, 0);
+static IIO_DEVICE_ATTR(sensing_mode, S_IRUGO | S_IWUSR,
+	hi843x_sensing_mode_show, hi843x_sensing_mode_store, 0);
+static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
+	hi843x_test_enable_show, hi843x_test_enable_store, 0);
+static IIO_DEVICE_ATTR(test_mode, S_IRUGO | S_IWUSR,
+	hi843x_test_mode_show, hi843x_test_mode_store, 0);
+static IIO_DEVICE_ATTR(threshold_gohys, S_IRUGO | S_IWUSR,
+	hi843x_threshold_gohys_show, hi843x_threshold_gohys_store, 0);
+static IIO_DEVICE_ATTR(threshold_gocval, S_IRUGO | S_IWUSR,
+	hi843x_threshold_gocval_show, hi843x_threshold_gocval_store, 0);
+static IIO_DEVICE_ATTR(threshold_sohys, S_IRUGO | S_IWUSR,
+	hi843x_threshold_sohys_show, hi843x_threshold_sohys_store, 0);
+static IIO_DEVICE_ATTR(threshold_socval, S_IRUGO | S_IWUSR,
+	hi843x_threshold_socval_show, hi843x_threshold_socval_store, 0);
+
+static struct attribute *hi843x_attributes[] = {
+	&iio_dev_attr_debounce_soft.dev_attr.attr,
+	&iio_dev_attr_debounce_soft_delay.dev_attr.attr,
+	&iio_dev_attr_sensing_mode.dev_attr.attr,
+	&iio_dev_attr_test_enable.dev_attr.attr,
+	&iio_dev_attr_test_mode.dev_attr.attr,
+	&iio_dev_attr_threshold_gohys.dev_attr.attr,
+	&iio_dev_attr_threshold_gocval.dev_attr.attr,
+	&iio_dev_attr_threshold_sohys.dev_attr.attr,
+	&iio_dev_attr_threshold_socval.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group hi843x_attribute_group = {
+	.attrs = hi843x_attributes,
+};
+
+static int hi843x_read_raw(struct iio_dev *idev,
+			   struct iio_chan_spec const *channel, int *val,
+			   int *val2, long mask)
+{
+	struct hi843x_priv *priv = iio_priv(idev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+	case IIO_CHAN_INFO_RAW:
+		ret = hi843x_readl(priv, HI843X_SO31_0_REG, val);
+		if (ret < 0)
+			return ret;
+
+		if (mask == IIO_CHAN_INFO_RAW)
+			*val = !!(*val & BIT(channel->channel));
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+#define HI843X_VOLTAGE_CHANNEL(num)				\
+	{							\
+		.type = IIO_VOLTAGE,				\
+		.indexed = 1,					\
+		.channel = num,					\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+		.scan_index = num,				\
+		.scan_type = {					\
+			.sign = 'u',				\
+			.realbits = 1,				\
+			.storagebits = 8,			\
+		},						\
+	}
+
+static const struct iio_chan_spec hi843x_channels[] = {
+	HI843X_VOLTAGE_CHANNEL(0),
+	HI843X_VOLTAGE_CHANNEL(1),
+	HI843X_VOLTAGE_CHANNEL(2),
+	HI843X_VOLTAGE_CHANNEL(3),
+	HI843X_VOLTAGE_CHANNEL(4),
+	HI843X_VOLTAGE_CHANNEL(5),
+	HI843X_VOLTAGE_CHANNEL(6),
+	HI843X_VOLTAGE_CHANNEL(7),
+	HI843X_VOLTAGE_CHANNEL(8),
+	HI843X_VOLTAGE_CHANNEL(9),
+	HI843X_VOLTAGE_CHANNEL(10),
+	HI843X_VOLTAGE_CHANNEL(11),
+	HI843X_VOLTAGE_CHANNEL(12),
+	HI843X_VOLTAGE_CHANNEL(13),
+	HI843X_VOLTAGE_CHANNEL(14),
+	HI843X_VOLTAGE_CHANNEL(15),
+	HI843X_VOLTAGE_CHANNEL(16),
+	HI843X_VOLTAGE_CHANNEL(17),
+	HI843X_VOLTAGE_CHANNEL(18),
+	HI843X_VOLTAGE_CHANNEL(19),
+	HI843X_VOLTAGE_CHANNEL(20),
+	HI843X_VOLTAGE_CHANNEL(21),
+	HI843X_VOLTAGE_CHANNEL(22),
+	HI843X_VOLTAGE_CHANNEL(23),
+	HI843X_VOLTAGE_CHANNEL(24),
+	HI843X_VOLTAGE_CHANNEL(25),
+	HI843X_VOLTAGE_CHANNEL(26),
+	HI843X_VOLTAGE_CHANNEL(27),
+	HI843X_VOLTAGE_CHANNEL(28),
+	HI843X_VOLTAGE_CHANNEL(29),
+	HI843X_VOLTAGE_CHANNEL(30),
+	HI843X_VOLTAGE_CHANNEL(31),
+	{
+		.type = IIO_ALTVOLTAGE,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.scan_index = 32,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 32,
+			.storagebits = 32,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(33),
+};
+
+static const struct iio_info hi843x_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &hi843x_attribute_group,
+	.read_raw = hi843x_read_raw,
+};
+
+static void h843x_iio_push_to_buffers(struct iio_dev *idev, int val)
+{
+	struct hi843x_priv *priv = iio_priv(idev);
+	u8 *pbuffer = priv->iio_buffer;
+	int i;
+
+	for_each_set_bit(i, idev->active_scan_mask, idev->masklength) {
+		if (idev->channels[i].type == IIO_ALTVOLTAGE) {
+			*(u32 *)pbuffer = val;
+			pbuffer += 4;
+		 } else {
+			*pbuffer = !!(val & BIT(i));
+			pbuffer++;
+		}
+	}
+	iio_push_to_buffers_with_timestamp(idev, priv->iio_buffer,
+					   iio_get_time_ns());
+}
+
+static void hi843x_debounce_soft_work(struct work_struct *work)
+{
+	struct hi843x_priv *priv = container_of(work, struct hi843x_priv,
+						work.work);
+	struct iio_dev *idev = spi_get_drvdata(priv->spi);
+	int val, ret;
+
+	ret = hi843x_readl(priv, HI843X_SO31_0_REG, &val);
+	if (ret < 0)
+		return;
+
+	if (val == priv->debounce_soft_val)
+		h843x_iio_push_to_buffers(idev, val);
+	else
+		dev_warn(&priv->spi->dev, "filtered by soft debounce");
+}
+
+static irqreturn_t hi843x_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *idev = pf->indio_dev;
+	struct hi843x_priv *priv = iio_priv(idev);
+	int val, ret;
+
+	ret = hi843x_readl(priv, HI843X_SO31_0_REG, &val);
+	if (ret < 0)
+		goto err_read;
+
+	if (priv->debounce_soft) {
+		priv->debounce_soft_val = val;
+		schedule_delayed_work(&priv->work,
+				msecs_to_jiffies(priv->debounce_soft_delay));
+	} else
+		h843x_iio_push_to_buffers(idev, val);
+
+err_read:
+	iio_trigger_notify_done(idev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int hi843x_buffer_postenable(struct iio_dev *idev)
+{
+	struct hi843x_priv *priv = iio_priv(idev);
+
+	priv->iio_buffer = kmalloc(idev->scan_bytes, GFP_KERNEL);
+	if (!priv->iio_buffer)
+		return -ENOMEM;
+
+	return iio_triggered_buffer_postenable(idev);
+}
+
+static int hi843x_buffer_predisable(struct iio_dev *idev)
+{
+	struct hi843x_priv *priv = iio_priv(idev);
+	int ret;
+
+	ret = iio_triggered_buffer_predisable(idev);
+	if (!ret)
+		kfree(priv->iio_buffer);
+
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops hi843x_buffer_setup_ops = {
+	.postenable = &hi843x_buffer_postenable,
+	.predisable = &hi843x_buffer_predisable,
+};
+
+static const struct iio_trigger_ops hi843x_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static void hi843x_parse_dt(struct hi843x_priv *priv)
+{
+	struct device_node *np = priv->spi->dev.of_node;
+	int ret;
+
+	ret = of_get_named_gpio(np, "holt,mr-gpio", 0);
+	priv->mr_gpio = ret < 0 ? 0 : ret;
+
+	if (of_find_property(np, "holt,debounce-soft", NULL))
+		priv->debounce_soft = 1;
+
+	ret = of_property_read_u32(np, "holt,debounce-soft-delay",
+				   &priv->debounce_soft_delay);
+	if (ret)
+		priv->debounce_soft_delay = HI843X_DEBOUNCE_SOFT_DELAY_DEF;
+}
+
+static int hi843x_probe(struct spi_device *spi)
+{
+	struct iio_dev *idev;
+	struct hi843x_priv *priv;
+	int ret;
+
+	idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+	if (!idev)
+		return -ENOMEM;
+
+	priv = iio_priv(idev);
+	priv->spi = spi;
+
+	if (spi->dev.of_node)
+		hi843x_parse_dt(priv);
+
+	spi_set_drvdata(spi, idev);
+	mutex_init(&priv->lock);
+	INIT_DELAYED_WORK(&priv->work, hi843x_debounce_soft_work);
+
+	idev->dev.parent	= &spi->dev;
+	idev->name		= spi_get_device_id(spi)->name;
+	idev->modes		= INDIO_DIRECT_MODE;
+	idev->info		= &hi843x_info;
+	idev->channels		= hi843x_channels;
+	idev->num_channels	= ARRAY_SIZE(hi843x_channels);
+
+	if (priv->mr_gpio) {
+		ret = devm_gpio_request(&spi->dev, priv->mr_gpio, idev->name);
+		if (!ret) {
+			/* chip hardware reset */
+			gpio_direction_output(priv->mr_gpio, 0);
+			udelay(5);
+			gpio_direction_output(priv->mr_gpio, 1);
+		}
+	} else {
+		/* chip software reset */
+		hi843x_writeb(priv, HI843X_CTRL_REG, HI843X_CTRL_SRST);
+		/* get out from reset state */
+		hi843x_writeb(priv, HI843X_CTRL_REG, 0);
+	}
+
+	ret = iio_triggered_buffer_setup(idev, NULL, hi843x_trigger_handler,
+					 &hi843x_buffer_setup_ops);
+	if (ret)
+		return ret;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&spi->dev, "unable to register device\n");
+		goto unregister_buffer;
+	}
+
+	return 0;
+
+unregister_buffer:
+	iio_triggered_buffer_cleanup(idev);
+	return ret;
+}
+
+static int hi843x_remove(struct spi_device *spi)
+{
+	struct iio_dev *idev = spi_get_drvdata(spi);
+	struct hi843x_priv *priv = iio_priv(idev);
+
+	cancel_delayed_work_sync(&priv->work);
+	iio_device_unregister(idev);
+	iio_triggered_buffer_cleanup(idev);
+
+	return 0;
+}
+
+static const struct of_device_id hi843x_dt_ids[] = {
+	{ .compatible = "holt,hi-8435" },
+	{ .compatible = "holt,hi-8436" },
+	{ .compatible = "holt,hi-8437" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hi843x_dt_ids);
+
+static const struct spi_device_id hi843x_id[] = {
+	{ "hi-8435", 0},
+	{ "hi-8436", 0},
+	{ "hi-8437", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, hi843x_id);
+
+static struct spi_driver hi843x_driver = {
+	.driver	= {
+		.name		= DRV_NAME,
+		.of_match_table	= of_match_ptr(hi843x_dt_ids),
+	},
+	.probe		= hi843x_probe,
+	.remove		= hi843x_remove,
+	.id_table	= hi843x_id,
+};
+module_spi_driver(hi843x_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("HI-8435/8436/8437 discrete ADC");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ