Add Holt descrete ADC driver for HI-8435/8436/8437 chips Signed-off-by: Vladimir Barinov <vladimir.barinov@xxxxxxxxxxxxxxxxxx> --- 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, ®, 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, ®, 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, ®, 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html