Commit 8efaa5e5 authored by Nick Dyer's avatar Nick Dyer Committed by Dmitry Torokhov

Input: atmel_mxt_ts - recover from bootloader on probe

The MXT device may be in bootloader mode on probe, due to:
1) APP CRC failure, either:
  a) flash corruption
  b) bad power or other intermittent problem while checking CRC
2) If the device has been reset 10 or more times without accessing comms
3) Warm probe, device was in bootloader mode already

This code attempts to recover from 1(b) and 3.

There is an additional complexity: we have to try two possible bootloader
addresses because the mapping is not one-to-one and we don't know the exact
model yet.
Signed-off-by: default avatarNick Dyer <nick.dyer@itdev.co.uk>
Acked-by: default avatarYufeng Shen <miletus@chromium.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 44a0bab2
...@@ -379,7 +379,7 @@ static int mxt_bootloader_write(struct mxt_data *data, ...@@ -379,7 +379,7 @@ static int mxt_bootloader_write(struct mxt_data *data,
return ret; return ret;
} }
static int mxt_lookup_bootloader_address(struct mxt_data *data) static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
{ {
u8 appmode = data->client->addr; u8 appmode = data->client->addr;
u8 bootloader; u8 bootloader;
...@@ -388,7 +388,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) ...@@ -388,7 +388,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data)
case 0x4a: case 0x4a:
case 0x4b: case 0x4b:
/* Chips after 1664S use different scheme */ /* Chips after 1664S use different scheme */
if (data->info.family_id >= 0xa2) { if (retry || data->info.family_id >= 0xa2) {
bootloader = appmode - 0x24; bootloader = appmode - 0x24;
break; break;
} }
...@@ -410,14 +410,14 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) ...@@ -410,14 +410,14 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data)
return 0; return 0;
} }
static int mxt_probe_bootloader(struct mxt_data *data) static int mxt_probe_bootloader(struct mxt_data *data, bool retry)
{ {
struct device *dev = &data->client->dev; struct device *dev = &data->client->dev;
int ret; int ret;
u8 val; u8 val;
bool crc_failure; bool crc_failure;
ret = mxt_lookup_bootloader_address(data); ret = mxt_lookup_bootloader_address(data, retry);
if (ret) if (ret)
return ret; return ret;
...@@ -518,13 +518,18 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, ...@@ -518,13 +518,18 @@ static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
return 0; return 0;
} }
static int mxt_unlock_bootloader(struct mxt_data *data) static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
{ {
int ret; int ret;
u8 buf[2]; u8 buf[2];
if (unlock) {
buf[0] = MXT_UNLOCK_CMD_LSB; buf[0] = MXT_UNLOCK_CMD_LSB;
buf[1] = MXT_UNLOCK_CMD_MSB; buf[1] = MXT_UNLOCK_CMD_MSB;
} else {
buf[0] = 0x01;
buf[1] = 0x01;
}
ret = mxt_bootloader_write(data, buf, 2); ret = mxt_bootloader_write(data, buf, 2);
if (ret) if (ret)
...@@ -1481,17 +1486,42 @@ static int mxt_initialize(struct mxt_data *data) ...@@ -1481,17 +1486,42 @@ static int mxt_initialize(struct mxt_data *data)
{ {
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
int error; int error;
bool alt_bootloader_addr = false;
bool retry = false;
retry_info:
error = mxt_get_info(data); error = mxt_get_info(data);
if (error) { if (error) {
error = mxt_probe_bootloader(data); retry_bootloader:
if (error) error = mxt_probe_bootloader(data, alt_bootloader_addr);
if (error) {
if (alt_bootloader_addr) {
/* Chip is not in appmode or bootloader mode */
return error; return error;
}
dev_info(&client->dev, "Trying alternate bootloader address\n");
alt_bootloader_addr = true;
goto retry_bootloader;
} else {
if (retry) {
dev_err(&client->dev, "Could not recover from bootloader mode\n");
/*
* We can reflash from this state, so do not
* abort init
*/
data->in_bootloader = true; data->in_bootloader = true;
return 0; return 0;
} }
/* Attempt to exit bootloader into app mode */
mxt_send_bootloader_cmd(data, false);
msleep(MXT_FW_RESET_TIME);
retry = true;
goto retry_info;
}
}
/* Get object table information */ /* Get object table information */
error = mxt_get_object_table(data); error = mxt_get_object_table(data);
if (error) { if (error) {
...@@ -1664,10 +1694,6 @@ static int mxt_load_fw(struct device *dev, const char *fn) ...@@ -1664,10 +1694,6 @@ static int mxt_load_fw(struct device *dev, const char *fn)
if (ret) if (ret)
goto release_firmware; goto release_firmware;
ret = mxt_lookup_bootloader_address(data);
if (ret)
goto release_firmware;
if (!data->in_bootloader) { if (!data->in_bootloader) {
/* Change to the bootloader mode */ /* Change to the bootloader mode */
data->in_bootloader = true; data->in_bootloader = true;
...@@ -1678,6 +1704,13 @@ static int mxt_load_fw(struct device *dev, const char *fn) ...@@ -1678,6 +1704,13 @@ static int mxt_load_fw(struct device *dev, const char *fn)
goto release_firmware; goto release_firmware;
msleep(MXT_RESET_TIME); msleep(MXT_RESET_TIME);
/* Do not need to scan since we know family ID */
ret = mxt_lookup_bootloader_address(data, 0);
if (ret)
goto release_firmware;
} else {
enable_irq(data->irq);
} }
mxt_free_object_table(data); mxt_free_object_table(data);
...@@ -1693,7 +1726,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) ...@@ -1693,7 +1726,7 @@ static int mxt_load_fw(struct device *dev, const char *fn)
dev_info(dev, "Unlocking bootloader\n"); dev_info(dev, "Unlocking bootloader\n");
/* Unlock bootloader */ /* Unlock bootloader */
ret = mxt_unlock_bootloader(data); ret = mxt_send_bootloader_cmd(data, true);
if (ret) if (ret)
goto disable_irq; goto disable_irq;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment