Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-block.git] / drivers / input / touchscreen / eeti_ts.c
index c6b85ba7f991fe5b43ec1d4b901df92fca31d817..2e1404cd09ec9f62491f082d9598b8d1c71e1ac8 100644 (file)
@@ -28,6 +28,7 @@ struct eeti_ts {
        struct input_dev *input;
        struct gpio_desc *attn_gpio;
        struct touchscreen_properties props;
+       struct mutex mutex;
        bool running;
 };
 
@@ -62,42 +63,80 @@ static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf)
        input_sync(eeti->input);
 }
 
+static int eeti_ts_read(struct eeti_ts *eeti)
+{
+       int len, error;
+       char buf[6];
+
+       len = i2c_master_recv(eeti->client, buf, sizeof(buf));
+       if (len != sizeof(buf)) {
+               error = len < 0 ? len : -EIO;
+               dev_err(&eeti->client->dev,
+                       "failed to read touchscreen data: %d\n",
+                       error);
+               return error;
+       }
+
+       /* Motion packet */
+       if (buf[0] & 0x80)
+               eeti_ts_report_event(eeti, buf);
+
+       return 0;
+}
+
 static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
 {
        struct eeti_ts *eeti = dev_id;
-       int len;
        int error;
-       char buf[6];
+
+       mutex_lock(&eeti->mutex);
 
        do {
-               len = i2c_master_recv(eeti->client, buf, sizeof(buf));
-               if (len != sizeof(buf)) {
-                       error = len < 0 ? len : -EIO;
-                       dev_err(&eeti->client->dev,
-                               "failed to read touchscreen data: %d\n",
-                               error);
+               /*
+                * If we have attention GPIO, trust it. Otherwise we'll read
+                * once and exit. We assume that in this case we are using
+                * level triggered interrupt and it will get raised again
+                * if/when there is more data.
+                */
+               if (eeti->attn_gpio &&
+                   !gpiod_get_value_cansleep(eeti->attn_gpio)) {
                        break;
                }
 
-               if (buf[0] & 0x80) {
-                       /* Motion packet */
-                       eeti_ts_report_event(eeti, buf);
-               }
-       } while (eeti->running &&
-                eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio));
+               error = eeti_ts_read(eeti);
+               if (error)
+                       break;
+
+       } while (eeti->running && eeti->attn_gpio);
 
+       mutex_unlock(&eeti->mutex);
        return IRQ_HANDLED;
 }
 
 static void eeti_ts_start(struct eeti_ts *eeti)
 {
+       mutex_lock(&eeti->mutex);
+
        eeti->running = true;
-       wmb();
        enable_irq(eeti->client->irq);
+
+       /*
+        * Kick the controller in case we are using edge interrupt and
+        * we missed our edge while interrupt was disabled. We expect
+        * the attention GPIO to be wired in this case.
+        */
+       if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio))
+               eeti_ts_read(eeti);
+
+       mutex_unlock(&eeti->mutex);
 }
 
 static void eeti_ts_stop(struct eeti_ts *eeti)
 {
+       /*
+        * Not locking here, just setting a flag and expect that the
+        * interrupt thread will notice the flag eventually.
+        */
        eeti->running = false;
        wmb();
        disable_irq(eeti->client->irq);
@@ -140,6 +179,8 @@ static int eeti_ts_probe(struct i2c_client *client,
                return -ENOMEM;
        }
 
+       mutex_init(&eeti->mutex);
+
        input = devm_input_allocate_device(dev);
        if (!input) {
                dev_err(dev, "Failed to allocate input device.\n");