#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/irq.h>
 #include <linux/module.h>
 
 #include <linux/iio/iio.h>
 /**
  * struct ad7150_chip_info - instance specific chip data
  * @client: i2c client for this device
- * @current_event: device always has one type of event enabled.
- *     This element stores the event code of the current one.
  * @threshold: thresholds for simple capacitance value events
  * @thresh_sensitivity: threshold for simple capacitance offset
  *     from 'average' value.
  *     value jumps to current value.  Note made up of two fields,
  *      3:0 are for timeout receding - applies if below lower threshold
  *      7:4 are for timeout approaching - applies if above upper threshold
- * @old_state: store state from previous event, allowing confirmation
- *     of new condition.
- * @conversion_mode: the current conversion mode.
  * @state_lock: ensure consistent state of this structure wrt the
  *     hardware.
+ * @interrupts: one or two interrupt numbers depending on device type.
+ * @int_enabled: is a given interrupt currently enabled.
+ * @type: threshold type
+ * @dir: threshold direction
  */
 struct ad7150_chip_info {
        struct i2c_client *client;
-       u64 current_event;
        u16 threshold[2][2];
        u8 thresh_sensitivity[2][2];
        u8 thresh_timeout[2][2];
-       int old_state;
-       char *conversion_mode;
        struct mutex state_lock;
+       int interrupts[2];
+       bool int_enabled[2];
+       enum iio_event_type type;
+       enum iio_event_direction dir;
 };
 
 /*
        switch (type) {
        case IIO_EV_TYPE_THRESH_ADAPTIVE:
                if (dir == IIO_EV_DIR_RISING)
-                       return !thrfixed && (threshtype == 0x3);
-               return !thrfixed && (threshtype == 0x2);
+                       return !thrfixed && (threshtype == 0x1);
+               return !thrfixed && (threshtype == 0x0);
        case IIO_EV_TYPE_THRESH:
                if (dir == IIO_EV_DIR_RISING)
                        return thrfixed && (threshtype == 0x1);
 {
        struct ad7150_chip_info *chip = iio_priv(indio_dev);
        int rising = (dir == IIO_EV_DIR_RISING);
-       u64 event_code;
 
-       event_code = IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, chan, type, dir);
-
-       if (event_code != chip->current_event)
+       /* Only update value live, if parameter is in use */
+       if ((type != chip->type) || (dir != chip->dir))
                return 0;
 
        switch (type) {
        int ret;
        struct ad7150_chip_info *chip = iio_priv(indio_dev);
        int rising = (dir == IIO_EV_DIR_RISING);
-       u64 event_code;
-
-       /* Something must always be turned on */
-       if (!state)
-               return -EINVAL;
 
-       event_code = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, type, dir);
-       if (event_code == chip->current_event)
+       /*
+        * There is only a single shared control and no on chip
+        * interrupt disables for the two interrupt lines.
+        * So, enabling will switch the events configured to enable
+        * whatever was most recently requested and if necessary enable_irq()
+        * the interrupt and any disable will disable_irq() for that
+        * channels interrupt.
+        */
+       if (!state) {
+               if ((chip->int_enabled[chan->channel]) &&
+                   (type == chip->type) && (dir == chip->dir)) {
+                       disable_irq(chip->interrupts[chan->channel]);
+                       chip->int_enabled[chan->channel] = false;
+               }
                return 0;
+       }
+
        mutex_lock(&chip->state_lock);
-       ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG);
-       if (ret < 0)
-               goto error_ret;
+       if ((type != chip->type) || (dir != chip->dir)) {
 
-       cfg = ret & ~((0x03 << 5) | BIT(7));
+               /*
+                * Need to temporarily disable both interrupts if
+                * enabled - this is to avoid races around changing
+                * config and thresholds.
+                * Note enable/disable_irq() are reference counted so
+                * no need to check if already enabled.
+                */
+               disable_irq(chip->interrupts[0]);
+               disable_irq(chip->interrupts[1]);
 
-       switch (type) {
-       case IIO_EV_TYPE_THRESH_ADAPTIVE:
-               adaptive = 1;
-               if (rising)
-                       thresh_type = 0x3;
-               else
-                       thresh_type = 0x2;
-               break;
-       case IIO_EV_TYPE_THRESH:
-               adaptive = 0;
-               if (rising)
-                       thresh_type = 0x1;
-               else
-                       thresh_type = 0x0;
-               break;
-       default:
-               ret = -EINVAL;
-               goto error_ret;
-       }
+               ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG);
+               if (ret < 0)
+                       goto error_ret;
 
-       cfg |= (!adaptive << 7) | (thresh_type << 5);
+               cfg = ret & ~((0x03 << 5) | BIT(7));
 
-       ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG, cfg);
-       if (ret < 0)
-               goto error_ret;
+               switch (type) {
+               case IIO_EV_TYPE_THRESH_ADAPTIVE:
+                       adaptive = 1;
+                       if (rising)
+                               thresh_type = 0x1;
+                       else
+                               thresh_type = 0x0;
+                       break;
+               case IIO_EV_TYPE_THRESH:
+                       adaptive = 0;
+                       if (rising)
+                               thresh_type = 0x1;
+                       else
+                               thresh_type = 0x0;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto error_ret;
+               }
 
-       chip->current_event = event_code;
+               cfg |= (!adaptive << 7) | (thresh_type << 5);
+
+               ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG, cfg);
+               if (ret < 0)
+                       goto error_ret;
+
+               /*
+                * There is a potential race condition here, but not easy
+                * to close given we can't disable the interrupt at the
+                * chip side of things. Rely on the status bit.
+                */
+               chip->type = type;
+               chip->dir = dir;
+
+               /* update control attributes */
+               ret = ad7150_write_event_params(indio_dev, chan->channel, type,
+                                               dir);
+               if (ret)
+                       goto error_ret;
+               /* reenable any irq's we disabled whilst changing mode */
+               enable_irq(chip->interrupts[0]);
+               enable_irq(chip->interrupts[1]);
+       }
+       if (!chip->int_enabled[chan->channel]) {
+               enable_irq(chip->interrupts[chan->channel]);
+               chip->int_enabled[chan->channel] = true;
+       }
 
-       /* update control attributes */
-       ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir);
 error_ret:
        mutex_unlock(&chip->state_lock);
 
        AD7150_CAPACITANCE_CHAN_NO_IRQ(0),
 };
 
-static irqreturn_t ad7150_event_handler(int irq, void *private)
+static irqreturn_t __ad7150_event_handler(void *private, u8 status_mask,
+                                         int channel)
 {
        struct iio_dev *indio_dev = private;
        struct ad7150_chip_info *chip = iio_priv(indio_dev);
-       u8 int_status;
        s64 timestamp = iio_get_time_ns(indio_dev);
-       int ret;
+       int int_status;
 
-       ret = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS);
-       if (ret < 0)
+       int_status = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS);
+       if (int_status < 0)
                return IRQ_HANDLED;
 
-       int_status = ret;
-
-       if ((int_status & AD7150_STATUS_OUT1) &&
-           !(chip->old_state & AD7150_STATUS_OUT1))
-               iio_push_event(indio_dev,
-                              IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
-                                                   0,
-                                                   IIO_EV_TYPE_THRESH,
-                                                   IIO_EV_DIR_RISING),
-                               timestamp);
-       else if ((!(int_status & AD7150_STATUS_OUT1)) &&
-                (chip->old_state & AD7150_STATUS_OUT1))
-               iio_push_event(indio_dev,
-                              IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
-                                                   0,
-                                                   IIO_EV_TYPE_THRESH,
-                                                   IIO_EV_DIR_FALLING),
-                              timestamp);
-
-       if ((int_status & AD7150_STATUS_OUT2) &&
-           !(chip->old_state & AD7150_STATUS_OUT2))
-               iio_push_event(indio_dev,
-                              IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
-                                                   1,
-                                                   IIO_EV_TYPE_THRESH,
-                                                   IIO_EV_DIR_RISING),
-                              timestamp);
-       else if ((!(int_status & AD7150_STATUS_OUT2)) &&
-                (chip->old_state & AD7150_STATUS_OUT2))
-               iio_push_event(indio_dev,
-                              IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
-                                                   1,
-                                                   IIO_EV_TYPE_THRESH,
-                                                   IIO_EV_DIR_FALLING),
-                              timestamp);
-       /* store the status to avoid repushing same events */
-       chip->old_state = int_status;
+       if (!(int_status & status_mask))
+               return IRQ_HANDLED;
+
+       iio_push_event(indio_dev,
+                      IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, channel,
+                                           chip->type, chip->dir),
+                      timestamp);
 
        return IRQ_HANDLED;
 }
 
+static irqreturn_t ad7150_event_handler_ch1(int irq, void *private)
+{
+       return __ad7150_event_handler(private, AD7150_STATUS_OUT1, 0);
+}
+
+static irqreturn_t ad7150_event_handler_ch2(int irq, void *private)
+{
+       return __ad7150_event_handler(private, AD7150_STATUS_OUT2, 1);
+}
+
 static IIO_CONST_ATTR(in_capacitance_thresh_adaptive_timeout_available,
                      "[0 0.01 0.15]");
 
 
        indio_dev->modes = INDIO_DIRECT_MODE;
 
-       if (client->irq) {
+       chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0);
+       if (chip->interrupts[0] < 0)
+               return chip->interrupts[0];
+       if (id->driver_data == AD7150) {
+               chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1);
+               if (chip->interrupts[1] < 0)
+                       return chip->interrupts[1];
+       }
+       if (chip->interrupts[0] &&
+           (id->driver_data == AD7151 || chip->interrupts[1])) {
+               irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN);
+               ret = devm_request_threaded_irq(&client->dev,
+                                               chip->interrupts[0],
+                                               NULL,
+                                               &ad7150_event_handler_ch1,
+                                               IRQF_TRIGGER_RISING |
+                                               IRQF_ONESHOT,
+                                               "ad7150_irq1",
+                                               indio_dev);
+               if (ret)
+                       return ret;
+
                indio_dev->info = &ad7150_info;
                switch (id->driver_data) {
                case AD7150:
                        indio_dev->channels = ad7150_channels;
                        indio_dev->num_channels = ARRAY_SIZE(ad7150_channels);
+                       irq_set_status_flags(chip->interrupts[1], IRQ_NOAUTOEN);
+                       ret = devm_request_threaded_irq(&client->dev,
+                                                       chip->interrupts[1],
+                                                       NULL,
+                                                       &ad7150_event_handler_ch2,
+                                                       IRQF_TRIGGER_RISING |
+                                                       IRQF_ONESHOT,
+                                                       "ad7150_irq2",
+                                                       indio_dev);
+                       if (ret)
+                               return ret;
                        break;
                case AD7151:
                        indio_dev->channels = ad7151_channels;
                        return -EINVAL;
                }
 
-               ret = devm_request_threaded_irq(&client->dev, client->irq,
-                                               NULL,
-                                               &ad7150_event_handler,
-                                               IRQF_TRIGGER_RISING |
-                                               IRQF_ONESHOT,
-                                               "ad7150_irq1",
-                                               indio_dev);
-               if (ret)
-                       return ret;
        } else {
                indio_dev->info = &ad7150_info_no_irq;
                switch (id->driver_data) {
                case AD7150:
                        indio_dev->channels = ad7150_channels_no_irq;
-                       indio_dev->num_channels = ARRAY_SIZE(ad7150_channels_no_irq);
+                       indio_dev->num_channels =
+                               ARRAY_SIZE(ad7150_channels_no_irq);
                        break;
                case AD7151:
                        indio_dev->channels = ad7151_channels_no_irq;
-                       indio_dev->num_channels = ARRAY_SIZE(ad7151_channels_no_irq);
+                       indio_dev->num_channels =
+                               ARRAY_SIZE(ad7151_channels_no_irq);
                        break;
                default:
                        return -EINVAL;