diff options
author | Benson Leung <bleung@chromium.org> | 2014-05-19 02:02:52 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-05-19 02:27:24 -0400 |
commit | d79e7e47a9442abfcc252c8363521fe84c6b5783 (patch) | |
tree | f7b78e6367482baff48077ecbf4916ce6020649a | |
parent | 82c2c0d6296526c27379f47194caf26e543b766f (diff) |
Input: atmel_mxt_ts - wait for CHG assert in mxt_check_bootloader
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 <bleung@chromium.org>
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Signed-off-by: Nick Dyer <nick.dyer@itdev.co.uk>
Acked-by: Yufeng Shen <miletus@chromium.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 102 |
1 files changed, 81 insertions, 21 deletions
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 40af02c26113..7f51d39ce2fb 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c | |||
@@ -14,6 +14,8 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/init.h> | ||
18 | #include <linux/completion.h> | ||
17 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
18 | #include <linux/firmware.h> | 20 | #include <linux/firmware.h> |
19 | #include <linux/i2c.h> | 21 | #include <linux/i2c.h> |
@@ -246,16 +248,19 @@ struct mxt_data { | |||
246 | const struct mxt_platform_data *pdata; | 248 | const struct mxt_platform_data *pdata; |
247 | struct mxt_object *object_table; | 249 | struct mxt_object *object_table; |
248 | struct mxt_info info; | 250 | struct mxt_info info; |
249 | |||
250 | unsigned int irq; | 251 | unsigned int irq; |
251 | unsigned int max_x; | 252 | unsigned int max_x; |
252 | unsigned int max_y; | 253 | unsigned int max_y; |
254 | bool in_bootloader; | ||
253 | 255 | ||
254 | /* Cached parameters from object table */ | 256 | /* Cached parameters from object table */ |
255 | u8 T6_reportid; | 257 | u8 T6_reportid; |
256 | u8 T9_reportid_min; | 258 | u8 T9_reportid_min; |
257 | u8 T9_reportid_max; | 259 | u8 T9_reportid_max; |
258 | u8 T19_reportid; | 260 | u8 T19_reportid; |
261 | |||
262 | /* for fw update in bootloader */ | ||
263 | struct completion bl_completion; | ||
259 | }; | 264 | }; |
260 | 265 | ||
261 | static size_t mxt_obj_size(const struct mxt_object *obj) | 266 | static size_t mxt_obj_size(const struct mxt_object *obj) |
@@ -339,12 +344,50 @@ static void mxt_dump_message(struct device *dev, | |||
339 | message->reportid, 7, message->message); | 344 | message->reportid, 7, message->message); |
340 | } | 345 | } |
341 | 346 | ||
342 | static int mxt_check_bootloader(struct i2c_client *client, | 347 | static int mxt_wait_for_chg(struct mxt_data *data, unsigned int timeout_ms) |
343 | unsigned int state) | ||
344 | { | 348 | { |
349 | struct device *dev = &data->client->dev; | ||
350 | struct completion *comp = &data->bl_completion; | ||
351 | unsigned long timeout = msecs_to_jiffies(timeout_ms); | ||
352 | long ret; | ||
353 | |||
354 | ret = wait_for_completion_interruptible_timeout(comp, timeout); | ||
355 | if (ret < 0) { | ||
356 | return ret; | ||
357 | } else if (ret == 0) { | ||
358 | dev_err(dev, "Wait for completion timed out.\n"); | ||
359 | return -ETIMEDOUT; | ||
360 | } | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) | ||
365 | { | ||
366 | struct i2c_client *client = data->client; | ||
345 | u8 val; | 367 | u8 val; |
368 | int ret; | ||
346 | 369 | ||
347 | recheck: | 370 | recheck: |
371 | if (state != MXT_WAITING_BOOTLOAD_CMD) { | ||
372 | /* | ||
373 | * In application update mode, the interrupt | ||
374 | * line signals state transitions. We must wait for the | ||
375 | * CHG assertion before reading the status byte. | ||
376 | * Once the status byte has been read, the line is deasserted. | ||
377 | */ | ||
378 | ret = mxt_wait_for_chg(data, 300); | ||
379 | if (ret) { | ||
380 | /* | ||
381 | * TODO: handle -ERESTARTSYS better by terminating | ||
382 | * fw update process before returning to userspace | ||
383 | * by writing length 0x000 to device (iff we are in | ||
384 | * WAITING_FRAME_DATA state). | ||
385 | */ | ||
386 | dev_err(&client->dev, "Update wait error %d\n", ret); | ||
387 | return ret; | ||
388 | } | ||
389 | } | ||
390 | |||
348 | if (i2c_master_recv(client, &val, 1) != 1) { | 391 | if (i2c_master_recv(client, &val, 1) != 1) { |
349 | dev_err(&client->dev, "%s: i2c recv failed\n", __func__); | 392 | dev_err(&client->dev, "%s: i2c recv failed\n", __func__); |
350 | return -EIO; | 393 | return -EIO; |
@@ -590,9 +633,8 @@ static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) | |||
590 | return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); | 633 | return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); |
591 | } | 634 | } |
592 | 635 | ||
593 | static irqreturn_t mxt_interrupt(int irq, void *dev_id) | 636 | static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) |
594 | { | 637 | { |
595 | struct mxt_data *data = dev_id; | ||
596 | struct mxt_message message; | 638 | struct mxt_message message; |
597 | const u8 *payload = &message.message[0]; | 639 | const u8 *payload = &message.message[0]; |
598 | struct device *dev = &data->client->dev; | 640 | struct device *dev = &data->client->dev; |
@@ -632,6 +674,19 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) | |||
632 | return IRQ_HANDLED; | 674 | return IRQ_HANDLED; |
633 | } | 675 | } |
634 | 676 | ||
677 | static irqreturn_t mxt_interrupt(int irq, void *dev_id) | ||
678 | { | ||
679 | struct mxt_data *data = dev_id; | ||
680 | |||
681 | if (data->in_bootloader) { | ||
682 | /* bootloader state transition completion */ | ||
683 | complete(&data->bl_completion); | ||
684 | return IRQ_HANDLED; | ||
685 | } | ||
686 | |||
687 | return mxt_process_messages_until_invalid(data); | ||
688 | } | ||
689 | |||
635 | static int mxt_check_reg_init(struct mxt_data *data) | 690 | static int mxt_check_reg_init(struct mxt_data *data) |
636 | { | 691 | { |
637 | const struct mxt_platform_data *pdata = data->pdata; | 692 | const struct mxt_platform_data *pdata = data->pdata; |
@@ -947,6 +1002,8 @@ static int mxt_load_fw(struct device *dev, const char *fn) | |||
947 | } | 1002 | } |
948 | 1003 | ||
949 | /* Change to the bootloader mode */ | 1004 | /* Change to the bootloader mode */ |
1005 | data->in_bootloader = true; | ||
1006 | |||
950 | mxt_write_object(data, MXT_GEN_COMMAND_T6, | 1007 | mxt_write_object(data, MXT_GEN_COMMAND_T6, |
951 | MXT_COMMAND_RESET, MXT_BOOT_VALUE); | 1008 | MXT_COMMAND_RESET, MXT_BOOT_VALUE); |
952 | msleep(MXT_RESET_TIME); | 1009 | msleep(MXT_RESET_TIME); |
@@ -957,18 +1014,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) | |||
957 | else | 1014 | else |
958 | client->addr = MXT_BOOT_HIGH; | 1015 | client->addr = MXT_BOOT_HIGH; |
959 | 1016 | ||
960 | ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); | 1017 | reinit_completion(&data->bl_completion); |
1018 | |||
1019 | ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); | ||
961 | if (ret) | 1020 | if (ret) |
962 | goto out; | 1021 | goto disable_irq; |
963 | 1022 | ||
964 | /* Unlock bootloader */ | 1023 | /* Unlock bootloader */ |
965 | mxt_unlock_bootloader(client); | 1024 | mxt_unlock_bootloader(client); |
966 | 1025 | ||
967 | while (pos < fw->size) { | 1026 | while (pos < fw->size) { |
968 | ret = mxt_check_bootloader(client, | 1027 | ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); |
969 | MXT_WAITING_FRAME_DATA); | ||
970 | if (ret) | 1028 | if (ret) |
971 | goto out; | 1029 | goto disable_irq; |
972 | 1030 | ||
973 | frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); | 1031 | frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); |
974 | 1032 | ||
@@ -980,17 +1038,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) | |||
980 | /* Write one frame to device */ | 1038 | /* Write one frame to device */ |
981 | mxt_fw_write(client, fw->data + pos, frame_size); | 1039 | mxt_fw_write(client, fw->data + pos, frame_size); |
982 | 1040 | ||
983 | ret = mxt_check_bootloader(client, | 1041 | ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); |
984 | MXT_FRAME_CRC_PASS); | ||
985 | if (ret) | 1042 | if (ret) |
986 | goto out; | 1043 | goto disable_irq; |
987 | 1044 | ||
988 | pos += frame_size; | 1045 | pos += frame_size; |
989 | 1046 | ||
990 | dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); | 1047 | dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); |
991 | } | 1048 | } |
992 | 1049 | ||
993 | out: | 1050 | data->in_bootloader = false; |
1051 | |||
1052 | disable_irq: | ||
1053 | disable_irq(data->irq); | ||
994 | release_firmware(fw); | 1054 | release_firmware(fw); |
995 | 1055 | ||
996 | /* Change to slave address of application */ | 1056 | /* Change to slave address of application */ |
@@ -1009,8 +1069,6 @@ static ssize_t mxt_update_fw_store(struct device *dev, | |||
1009 | struct mxt_data *data = dev_get_drvdata(dev); | 1069 | struct mxt_data *data = dev_get_drvdata(dev); |
1010 | int error; | 1070 | int error; |
1011 | 1071 | ||
1012 | disable_irq(data->irq); | ||
1013 | |||
1014 | error = mxt_load_fw(dev, MXT_FW_NAME); | 1072 | error = mxt_load_fw(dev, MXT_FW_NAME); |
1015 | if (error) { | 1073 | if (error) { |
1016 | dev_err(dev, "The firmware update failed(%d)\n", error); | 1074 | dev_err(dev, "The firmware update failed(%d)\n", error); |
@@ -1024,13 +1082,13 @@ static ssize_t mxt_update_fw_store(struct device *dev, | |||
1024 | mxt_free_object_table(data); | 1082 | mxt_free_object_table(data); |
1025 | 1083 | ||
1026 | mxt_initialize(data); | 1084 | mxt_initialize(data); |
1027 | } | ||
1028 | 1085 | ||
1029 | enable_irq(data->irq); | 1086 | enable_irq(data->irq); |
1030 | 1087 | ||
1031 | error = mxt_make_highchg(data); | 1088 | error = mxt_make_highchg(data); |
1032 | if (error) | 1089 | if (error) |
1033 | return error; | 1090 | return error; |
1091 | } | ||
1034 | 1092 | ||
1035 | return count; | 1093 | return count; |
1036 | } | 1094 | } |
@@ -1120,6 +1178,8 @@ static int mxt_probe(struct i2c_client *client, | |||
1120 | data->pdata = pdata; | 1178 | data->pdata = pdata; |
1121 | data->irq = client->irq; | 1179 | data->irq = client->irq; |
1122 | 1180 | ||
1181 | init_completion(&data->bl_completion); | ||
1182 | |||
1123 | mxt_calc_resolution(data); | 1183 | mxt_calc_resolution(data); |
1124 | 1184 | ||
1125 | error = mxt_initialize(data); | 1185 | error = mxt_initialize(data); |