To compile this driver as a module, choose M here: the
          module will be called tsc2007.
 
+config TOUCHSCREEN_TSC2007_IIO
+       bool "IIO interface for external ADC input and temperature"
+       depends on TOUCHSCREEN_TSC2007
+       depends on IIO=y || IIO=TOUCHSCREEN_TSC2007
+       help
+         Saying Y here adds an iio interface to the tsc2007 which
+         provides values for the AUX input (used for e.g. battery
+         or ambient light monitoring), temperature and raw input
+         values.
+
 config TOUCHSCREEN_W90X900
        tristate "W90P910 touchscreen driver"
        depends on ARCH_W90X900
 
 obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2004)      += tsc2004.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2005)      += tsc2005.o
+tsc2007-y := tsc2007_core.o
+tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO)      += tsc2007_iio.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2007)      += tsc2007.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
 obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)  += wacom_w8001.o
 
--- /dev/null
+
+/*
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ *     Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ *  - ads7846.c
+ *     Copyright (c) 2005 David Brownell
+ *     Copyright (c) 2006 Nokia Corporation
+ *  - corgi_ts.c
+ *     Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *     Copyright (C) 2002 MontaVista Software
+ *     Copyright (C) 2004 Texas Instruments
+ *     Copyright (C) 2005 Dirk Behme
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#ifndef _TSC2007_H
+#define _TSC2007_H
+
+#define TSC2007_MEASURE_TEMP0          (0x0 << 4)
+#define TSC2007_MEASURE_AUX            (0x2 << 4)
+#define TSC2007_MEASURE_TEMP1          (0x4 << 4)
+#define TSC2007_ACTIVATE_XN            (0x8 << 4)
+#define TSC2007_ACTIVATE_YN            (0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN         (0xa << 4)
+#define TSC2007_SETUP                  (0xb << 4)
+#define TSC2007_MEASURE_X              (0xc << 4)
+#define TSC2007_MEASURE_Y              (0xd << 4)
+#define TSC2007_MEASURE_Z1             (0xe << 4)
+#define TSC2007_MEASURE_Z2             (0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN       (0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0                (0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN         (0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1                (0x3 << 2)
+
+#define TSC2007_12BIT                  (0x0 << 1)
+#define TSC2007_8BIT                   (0x1 << 1)
+
+#define MAX_12BIT                      ((1 << 12) - 1)
+
+#define ADC_ON_12BIT   (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y         (ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1                (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2                (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X         (ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN                (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+       u16     x;
+       u16     y;
+       u16     z1, z2;
+};
+
+struct tsc2007 {
+       struct input_dev        *input;
+       char                    phys[32];
+
+       struct i2c_client       *client;
+
+       u16                     model;
+       u16                     x_plate_ohms;
+       u16                     max_rt;
+       unsigned long           poll_period; /* in jiffies */
+       int                     fuzzx;
+       int                     fuzzy;
+       int                     fuzzz;
+
+       unsigned int            gpio;
+       int                     irq;
+
+       wait_queue_head_t       wait;
+       bool                    stopped;
+
+       int                     (*get_pendown_state)(struct device *);
+       void                    (*clear_penirq)(void);
+
+       struct mutex            mlock;
+};
+
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
+u32 tsc2007_calculate_pressure(struct tsc2007 *tsc,
+                                       struct ts_event *tc);
+bool tsc2007_is_pen_down(struct tsc2007 *ts);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2007_IIO)
+/* defined in tsc2007_iio.c */
+int tsc2007_iio_configure(struct tsc2007 *ts);
+#else
+static inline int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+       return 0;
+}
+#endif /* CONFIG_TOUCHSCREEN_TSC2007_IIO */
+
+#endif /* _TSC2007_H */
 
 #include <linux/i2c.h>
 #include <linux/i2c/tsc2007.h>
 #include <linux/of_device.h>
-#include <linux/of.h>
 #include <linux/of_gpio.h>
+#include "tsc2007.h"
 
-#define TSC2007_MEASURE_TEMP0          (0x0 << 4)
-#define TSC2007_MEASURE_AUX            (0x2 << 4)
-#define TSC2007_MEASURE_TEMP1          (0x4 << 4)
-#define TSC2007_ACTIVATE_XN            (0x8 << 4)
-#define TSC2007_ACTIVATE_YN            (0x9 << 4)
-#define TSC2007_ACTIVATE_YP_XN         (0xa << 4)
-#define TSC2007_SETUP                  (0xb << 4)
-#define TSC2007_MEASURE_X              (0xc << 4)
-#define TSC2007_MEASURE_Y              (0xd << 4)
-#define TSC2007_MEASURE_Z1             (0xe << 4)
-#define TSC2007_MEASURE_Z2             (0xf << 4)
-
-#define TSC2007_POWER_OFF_IRQ_EN       (0x0 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS0                (0x1 << 2)
-#define TSC2007_ADC_OFF_IRQ_EN         (0x2 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS1                (0x3 << 2)
-
-#define TSC2007_12BIT                  (0x0 << 1)
-#define TSC2007_8BIT                   (0x1 << 1)
-
-#define        MAX_12BIT                       ((1 << 12) - 1)
-
-#define ADC_ON_12BIT   (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
-
-#define READ_Y         (ADC_ON_12BIT | TSC2007_MEASURE_Y)
-#define READ_Z1                (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
-#define READ_Z2                (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
-#define READ_X         (ADC_ON_12BIT | TSC2007_MEASURE_X)
-#define PWRDOWN                (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
-
-struct ts_event {
-       u16     x;
-       u16     y;
-       u16     z1, z2;
-};
-
-struct tsc2007 {
-       struct input_dev        *input;
-       char                    phys[32];
-
-       struct i2c_client       *client;
-
-       u16                     model;
-       u16                     x_plate_ohms;
-       u16                     max_rt;
-       unsigned long           poll_period; /* in jiffies */
-       int                     fuzzx;
-       int                     fuzzy;
-       int                     fuzzz;
-
-       unsigned                gpio;
-       int                     irq;
-
-       wait_queue_head_t       wait;
-       bool                    stopped;
-
-       int                     (*get_pendown_state)(struct device *);
-       void                    (*clear_penirq)(void);
-};
-
-static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
 {
        s32 data;
        u16 val;
        tsc2007_xfer(tsc, PWRDOWN);
 }
 
-static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
 {
        u32 rt = 0;
 
        return rt;
 }
 
-static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+bool tsc2007_is_pen_down(struct tsc2007 *ts)
 {
        /*
         * NOTE: We can't rely on the pressure to determine the pen down
        while (!ts->stopped && tsc2007_is_pen_down(ts)) {
 
                /* pen is down, continue with the measurement */
+
+               mutex_lock(&ts->mlock);
                tsc2007_read_values(ts, &tc);
+               mutex_unlock(&ts->mlock);
 
                rt = tsc2007_calculate_pressure(ts, &tc);
 
 static int tsc2007_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
-       const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
+       const struct tsc2007_platform_data *pdata =
+               dev_get_platdata(&client->dev);
        struct tsc2007 *ts;
        struct input_dev *input_dev;
        int err;
        ts->client = client;
        ts->irq = client->irq;
        ts->input = input_dev;
+
        init_waitqueue_head(&ts->wait);
+       mutex_init(&ts->mlock);
 
        snprintf(ts->phys, sizeof(ts->phys),
                 "%s/input0", dev_name(&client->dev));
        if (err < 0) {
                dev_err(&client->dev,
                        "Failed to setup chip: %d\n", err);
-               return err;     /* usually, chip does not respond */
+               return err;     /* chip does not respond */
        }
 
        err = input_register_device(input_dev);
                return err;
        }
 
+       err =  tsc2007_iio_configure(ts);
+       if (err) {
+               dev_err(&client->dev,
+                       "Failed to register with IIO: %d\n", err);
+               return err;
+       }
+
        return 0;
 }
 
 
--- /dev/null
+/*
+ * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
+ *     Nikolaus Schaller <hns@goldelico.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include "tsc2007.h"
+
+struct tsc2007_iio {
+       struct tsc2007 *ts;
+};
+
+#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
+{ \
+       .datasheet_name = _name, \
+       .type = _type, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+                       BIT(_chan_info), \
+       .indexed = 1, \
+       .channel = _chan, \
+}
+
+static const struct iio_chan_spec tsc2007_iio_channel[] = {
+       TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
+       TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
+};
+
+static int tsc2007_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct tsc2007_iio *iio = iio_priv(indio_dev);
+       struct tsc2007 *tsc = iio->ts;
+       int adc_chan = chan->channel;
+       int ret = 0;
+
+       if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
+               return -EINVAL;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       mutex_lock(&tsc->mlock);
+
+       switch (chan->channel) {
+       case 0:
+               *val = tsc2007_xfer(tsc, READ_X);
+               break;
+       case 1:
+               *val = tsc2007_xfer(tsc, READ_Y);
+               break;
+       case 2:
+               *val = tsc2007_xfer(tsc, READ_Z1);
+               break;
+       case 3:
+               *val = tsc2007_xfer(tsc, READ_Z2);
+               break;
+       case 4:
+               *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
+               break;
+       case 5: {
+               struct ts_event tc;
+
+               tc.x = tsc2007_xfer(tsc, READ_X);
+               tc.z1 = tsc2007_xfer(tsc, READ_Z1);
+               tc.z2 = tsc2007_xfer(tsc, READ_Z2);
+               *val = tsc2007_calculate_pressure(tsc, &tc);
+               break;
+       }
+       case 6:
+               *val = tsc2007_is_pen_down(tsc);
+               break;
+       case 7:
+               *val = tsc2007_xfer(tsc,
+                                   (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
+               break;
+       case 8:
+               *val = tsc2007_xfer(tsc,
+                                   (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
+               break;
+       }
+
+       /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+       tsc2007_xfer(tsc, PWRDOWN);
+
+       mutex_unlock(&tsc->mlock);
+
+       ret = IIO_VAL_INT;
+
+       return ret;
+}
+
+static const struct iio_info tsc2007_iio_info = {
+       .read_raw = tsc2007_read_raw,
+       .driver_module = THIS_MODULE,
+};
+
+int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+       struct iio_dev *indio_dev;
+       struct tsc2007_iio *iio;
+       int error;
+
+       indio_dev = devm_iio_device_alloc(&ts->client->dev, sizeof(*iio));
+       if (!indio_dev) {
+               dev_err(&ts->client->dev, "iio_device_alloc failed\n");
+               return -ENOMEM;
+       }
+
+       iio = iio_priv(indio_dev);
+       iio->ts = ts;
+
+       indio_dev->name = "tsc2007";
+       indio_dev->dev.parent = &ts->client->dev;
+       indio_dev->info = &tsc2007_iio_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = tsc2007_iio_channel;
+       indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
+
+       error = devm_iio_device_register(&ts->client->dev, indio_dev);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "iio_device_register() failed: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}