From: Benson Leung <ble...@chromium.org>

The driver should not immediately read bootloader status when
in Application Update Mode. The CHG line will assert when the device
has made a state transition and is ready to report a new status
via i2c.

This change adds a wait for completion in mxt_check_bootloader,
and changes the mxt_interrupt handler to signal the completion.

Signed-off-by: Benson Leung <ble...@chromium.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 63 ++++++++++++++++++++++++++++----
 1 file changed, 55 insertions(+), 8 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c 
b/drivers/input/touchscreen/atmel_mxt_ts.c
index be96be3..d0f91ff 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -13,6 +13,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/i2c.h>
@@ -251,6 +252,9 @@ struct mxt_data {
        u8 T6_reportid;
        u8 T9_reportid_min;
        u8 T9_reportid_max;
+
+       /* for fw update in bootloader */
+       struct completion bl_completion;
 };
 
 static void mxt_free_object_table(struct mxt_data *data);
@@ -381,19 +385,58 @@ static int mxt_i2c_transfer(struct i2c_client *client, 
struct i2c_msg *msgs,
        return ret;
 }
 
-static int mxt_check_bootloader(struct i2c_client *client,
-                                    unsigned int state)
+static int mxt_wait_for_chg(struct mxt_data *data, unsigned int timeout_ms)
 {
+       struct device *dev = &data->client->dev;
+       struct completion *comp = &data->bl_completion;
+       unsigned long timeout = msecs_to_jiffies(timeout_ms);
+       long ret;
+
+       ret = wait_for_completion_interruptible_timeout(comp, timeout);
+       if (ret < 0) {
+               dev_err(dev, "Wait for completion interrupted.\n");
+               /*
+                * TODO: handle -EINTR better by terminating fw update process
+                * before returning to userspace by writing length 0x000 to
+                * device (iff we are in WAITING_FRAME_DATA state).
+                */
+               return -EINTR;
+       } else if (ret == 0) {
+               dev_err(dev, "Wait for completion timed out.\n");
+               return -ETIMEDOUT;
+       }
+       return 0;
+}
+
+static int mxt_check_bootloader(struct mxt_data *data, unsigned int state)
+{
+       struct i2c_client *client = data->client;
        u8 val;
        int ret;
 
 recheck:
+       if (state != MXT_WAITING_BOOTLOAD_CMD) {
+               /*
+                * In application update mode, the interrupt
+                * line signals state transitions. We must wait for the
+                * CHG assertion before reading the status byte.
+                * Once the status byte has been read, the line is deasserted.
+                */
+               int ret = mxt_wait_for_chg(data, 300);
+               if (ret) {
+                       dev_err(&client->dev, "Update wait error %d\n", ret);
+                       return ret;
+               }
+       }
+
        ret = mxt_i2c_recv(client, &val, 1);
        if (ret)
                return ret;
 
        switch (state) {
        case MXT_WAITING_BOOTLOAD_CMD:
+               dev_info(&client->dev, "bootloader version: %d\n",
+                        val & MXT_BOOT_STATUS_MASK);
        case MXT_WAITING_FRAME_DATA:
                val &= ~MXT_BOOT_STATUS_MASK;
                break;
@@ -672,8 +715,11 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
        u8 reportid;
        bool update_input = false;
 
-       if (mxt_in_bootloader(data))
+       if (mxt_in_bootloader(data)) {
+               /* bootloader state transition completion */
+               complete(&data->bl_completion);
                goto end;
+       }
 
        do {
                if (mxt_read_message(data, &message)) {
@@ -1072,18 +1118,18 @@ static int mxt_load_fw(struct device *dev, const char 
*fn)
                goto out;
        }
 
-       ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
+       ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD);
        if (ret)
                goto out;
 
+       INIT_COMPLETION(data->bl_completion);
        /* Unlock bootloader */
        ret = mxt_unlock_bootloader(client);
        if (ret)
                goto out;
 
        while (pos < fw->size) {
-               ret = mxt_check_bootloader(client,
-                                               MXT_WAITING_FRAME_DATA);
+               ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA);
                if (ret)
                        goto out;
 
@@ -1099,8 +1145,7 @@ static int mxt_load_fw(struct device *dev, const char *fn)
                if (ret)
                        goto out;
 
-               ret = mxt_check_bootloader(client,
-                                               MXT_FRAME_CRC_PASS);
+               ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS);
                if (ret)
                        goto out;
 
@@ -1264,6 +1309,8 @@ static int __devinit mxt_probe(struct i2c_client *client,
        data->pdata = pdata;
        data->irq = client->irq;
 
+       init_completion(&data->bl_completion);
+
        mxt_calc_resolution(data);
 
        if (mxt_in_bootloader(data)) {
-- 
1.8.1

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

Reply via email to