summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/atmel_mxt_ts.c
diff options
context:
space:
mode:
authorNick Dyer <nick.dyer@itdev.co.uk>2011-11-01 16:26:10 +0000
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:37:38 -0700
commit336467629d7026e85a3ead8fb6c1b2954ef9dd2c (patch)
treedb890e0af6edd92b89e3e2b8242697b4fd2bba59 /drivers/input/touchscreen/atmel_mxt_ts.c
parent052cc961afc8de2990b3aee8da7d25080b004031 (diff)
Input: atmel-mxt-ts - Improve bootloader support
Add additional bootloader addresses Read and report bootloader version Increase fw reset time for greater reliability Further bootloader improvements Handle APP_CRC_FAIL on startup Handle bootloader frame CRC failure Recover gracefully from flash aborted halfway through Signed-off-by: Nick Dyer <nick.dyer@itdev.co.uk> Rebase-Id: R35ad9aa057af76183190b2851fed20c21ea6ef0c
Diffstat (limited to 'drivers/input/touchscreen/atmel_mxt_ts.c')
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c324
1 files changed, 249 insertions, 75 deletions
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 0656484afe80..81c5818db996 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -27,12 +27,6 @@
#define MXT_VER_21 21
#define MXT_VER_22 22
-/* Slave addresses */
-#define MXT_APP_LOW 0x4a
-#define MXT_APP_HIGH 0x4b
-#define MXT_BOOT_LOW 0x24
-#define MXT_BOOT_HIGH 0x25
-
/* Firmware */
#define MXT_FW_NAME "maxtouch.fw"
@@ -203,6 +197,8 @@
#define MXT_FRAME_CRC_PASS 0x04
#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
#define MXT_BOOT_STATUS_MASK 0x3f
+#define MXT_BOOT_EXTENDED_ID (1 << 5)
+#define MXT_BOOT_ID_MASK 0x1f
/* Touch status */
#define MXT_SUPPRESS (1 << 1)
@@ -258,11 +254,14 @@ struct mxt_finger {
int pressure;
};
+enum mxt_device_state { INIT, APPMODE, BOOTLOADER, FAILED };
+
/* Each client has this additional data */
struct mxt_data {
struct i2c_client *client;
struct input_dev *input_dev;
const struct mxt_platform_data *pdata;
+ enum mxt_device_state state;
struct mxt_object *object_table;
u16 mem_size;
struct mxt_info info;
@@ -273,11 +272,27 @@ struct mxt_data {
struct bin_attribute mem_access_attr;
bool debug_enabled;
bool driver_paused;
+ u8 bootloader_addr;
u8 actv_cycle_time;
u8 idle_cycle_time;
u8 is_stopped;
};
+/* I2C slave address pairs */
+struct mxt_i2c_address_pair {
+ u8 bootloader;
+ u8 application;
+};
+
+static const struct mxt_i2c_address_pair mxt_i2c_addresses[] = {
+ { 0x24, 0x4a },
+ { 0x25, 0x4b },
+ { 0x26, 0x4c },
+ { 0x27, 0x4d },
+ { 0x34, 0x5a },
+ { 0x35, 0x5b },
+};
+
static bool mxt_object_writable(unsigned int type)
{
switch (type) {
@@ -309,59 +324,166 @@ static bool mxt_object_writable(unsigned int type)
}
}
-static int mxt_check_bootloader(struct i2c_client *client,
- unsigned int state)
+static int mxt_bootloader_read(struct mxt_data *data, u8 *val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.flags |= I2C_M_RD;
+ msg.len = count;
+ msg.buf = val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int mxt_bootloader_write(struct mxt_data *data, const u8 * const val,
+ unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.len = count;
+ msg.buf = (u8 *)val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int mxt_get_bootloader_address(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxt_i2c_addresses); i++) {
+ if (mxt_i2c_addresses[i].application == client->addr) {
+ data->bootloader_addr = mxt_i2c_addresses[i].bootloader;
+
+ dev_info(&client->dev, "Bootloader i2c addr: 0x%02x\n",
+ data->bootloader_addr);
+
+ return 0;
+ }
+ }
+
+ dev_err(&client->dev, "Address 0x%02x not found in address table\n",
+ client->addr);
+ return -EINVAL;
+}
+
+static int mxt_probe_bootloader(struct mxt_data *data)
{
+ struct device *dev = &data->client->dev;
+ int ret;
u8 val;
+ bool crc_failure;
-recheck:
- if (i2c_master_recv(client, &val, 1) != 1) {
- dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+ ret = mxt_get_bootloader_address(data);
+ if (ret)
+ return ret;
+
+ ret = mxt_bootloader_read(data, &val, 1);
+ if (ret) {
+ dev_err(dev, "%s: i2c recv failed\n", __func__);
return -EIO;
}
+ /* Check app crc fail mode */
+ crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
+
+ dev_err(dev, "Detected bootloader, status:%02X%s\n",
+ val, crc_failure ? ", APP_CRC_FAIL" : "");
+
+ return 0;
+}
+
+static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
+{
+ struct device *dev = &data->client->dev;
+ u8 buf[3];
+
+ if (val & MXT_BOOT_EXTENDED_ID) {
+ if (mxt_bootloader_read(data, &buf[0], 3) != 0) {
+ dev_err(dev, "%s: i2c failure\n", __func__);
+ return -EIO;
+ }
+
+ dev_info(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
+
+ return buf[0];
+ } else {
+ dev_info(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
+
+ return val;
+ }
+}
+
+static int mxt_check_bootloader(struct mxt_data *data,
+ unsigned int state)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ u8 val;
+
+recheck:
+ ret = mxt_bootloader_read(data, &val, 1);
+ if (ret) {
+ dev_err(dev, "%s: i2c recv failed, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (state == MXT_WAITING_BOOTLOAD_CMD) {
+ val = mxt_get_bootloader_version(data, val);
+ }
+
switch (state) {
case MXT_WAITING_BOOTLOAD_CMD:
+ val &= ~MXT_BOOT_STATUS_MASK;
+ break;
case MXT_WAITING_FRAME_DATA:
+ case MXT_APP_CRC_FAIL:
val &= ~MXT_BOOT_STATUS_MASK;
break;
case MXT_FRAME_CRC_PASS:
if (val == MXT_FRAME_CRC_CHECK)
goto recheck;
+ if (val == MXT_FRAME_CRC_FAIL) {
+ dev_err(dev, "Bootloader CRC fail\n");
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
}
if (val != state) {
- dev_err(&client->dev, "Invalid bootloader mode state\n");
+ dev_err(dev, "Invalid bootloader mode state 0x%02X\n", val);
return -EINVAL;
}
return 0;
}
-static int mxt_unlock_bootloader(struct i2c_client *client)
+static int mxt_unlock_bootloader(struct mxt_data *data)
{
+ int ret;
u8 buf[2];
buf[0] = MXT_UNLOCK_CMD_LSB;
buf[1] = MXT_UNLOCK_CMD_MSB;
- if (i2c_master_send(client, buf, 2) != 2) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int mxt_fw_write(struct i2c_client *client,
- const u8 *data, unsigned int frame_size)
-{
- if (i2c_master_send(client, data, frame_size) != frame_size) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
+ ret = mxt_bootloader_write(data, buf, 2);
+ if (ret) {
+ dev_err(&data->client->dev, "%s: i2c send failed, ret=%d\n",
+ __func__, ret);
+ return ret;
}
return 0;
@@ -839,6 +961,11 @@ static int mxt_set_power_cfg(struct mxt_data *data, u8 sleep)
u8 actv_cycle_time = 0;
u8 idle_cycle_time = 0;
+ if (data->state != APPMODE) {
+ dev_err(dev, "Not in APPMODE\n");
+ return -EINVAL;
+ }
+
if (!sleep) {
actv_cycle_time = data->actv_cycle_time;
idle_cycle_time = data->idle_cycle_time;
@@ -987,8 +1114,18 @@ static int mxt_initialize(struct mxt_data *data)
u8 val;
error = mxt_get_info(data);
- if (error)
- return error;
+ if (error) {
+ error = mxt_probe_bootloader(data);
+
+ if (error) {
+ return error;
+ } else {
+ data->state = BOOTLOADER;
+ return 0;
+ }
+ }
+
+ data->state = APPMODE;
data->object_table = kcalloc(info->object_num,
sizeof(struct mxt_object),
@@ -1062,71 +1199,99 @@ static void mxt_calc_resolution(struct mxt_data *data)
static int mxt_load_fw(struct device *dev, const char *fn)
{
struct mxt_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
const struct firmware *fw = NULL;
unsigned int frame_size;
unsigned int pos = 0;
+ unsigned int retry = 0;
+ unsigned int frame = 0;
int ret;
ret = request_firmware(&fw, fn, dev);
- if (ret) {
+ if (ret < 0) {
dev_err(dev, "Unable to open firmware %s\n", fn);
return ret;
}
- /* Change to the bootloader mode */
- mxt_write_object(data, MXT_GEN_COMMAND_T6,
- MXT_COMMAND_RESET, MXT_BOOT_VALUE);
- msleep(MXT_RESET_TIME);
+ if (data->state != BOOTLOADER) {
+ /* Change to the bootloader mode */
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
+ MXT_COMMAND_RESET, MXT_BOOT_VALUE);
+ msleep(MXT_RESET_TIME);
- /* Change to slave address of bootloader */
- if (client->addr == MXT_APP_LOW)
- client->addr = MXT_BOOT_LOW;
- else
- client->addr = MXT_BOOT_HIGH;
+ ret = mxt_get_bootloader_address(data);
+ if (ret)
+ goto release_firmware;
- ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
- if (ret)
- goto out;
+ data->state = BOOTLOADER;
+ }
- /* Unlock bootloader */
- mxt_unlock_bootloader(client);
+ ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD);
+ if (ret) {
+ /* Bootloader may still be unlocked from previous update
+ * attempt */
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA);
+ if (ret) {
+ data->state = FAILED;
+ goto release_firmware;
+ }
+ } else {
+ dev_info(dev, "Unlocking bootloader\n");
+
+ /* Unlock bootloader */
+ ret = mxt_unlock_bootloader(data);
+ if (ret) {
+ data->state = FAILED;
+ goto release_firmware;
+ }
+ }
while (pos < fw->size) {
- ret = mxt_check_bootloader(client,
- MXT_WAITING_FRAME_DATA);
- if (ret)
- goto out;
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA);
+ if (ret) {
+ data->state = FAILED;
+ goto release_firmware;
+ }
frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
- /* We should add 2 at frame size as the the firmware data is not
- * included the CRC bytes.
- */
+ /* Take account of CRC bytes */
frame_size += 2;
/* Write one frame to device */
- mxt_fw_write(client, fw->data + pos, frame_size);
+ ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
+ if (ret) {
+ data->state = FAILED;
+ goto release_firmware;
+ }
- ret = mxt_check_bootloader(client,
- MXT_FRAME_CRC_PASS);
- if (ret)
- goto out;
+ ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS);
+ if (ret) {
+ retry++;
+
+ /* Back off by 20ms per retry */
+ msleep(retry * 20);
- pos += frame_size;
+ if (retry > 20) {
+ data->state = FAILED;
+ goto release_firmware;
+ }
+ } else {
+ retry = 0;
+ pos += frame_size;
+ frame++;
+ }
- dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+ if (frame % 10 == 0)
+ dev_info(dev, "Updated %d frames, %d/%zd bytes\n",
+ frame, pos, fw->size);
}
-out:
- release_firmware(fw);
+ dev_info(dev, "Finished, sent %d frames, %zd bytes\n", frame, pos);
- /* Change to slave address of application */
- if (client->addr == MXT_BOOT_LOW)
- client->addr = MXT_APP_LOW;
- else
- client->addr = MXT_APP_HIGH;
+ data->state = INIT;
+release_firmware:
+ release_firmware(fw);
return ret;
}
@@ -1144,7 +1309,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
dev_err(dev, "The firmware update failed(%d)\n", error);
count = error;
} else {
- dev_dbg(dev, "The firmware update succeeded\n");
+ dev_info(dev, "The firmware update succeeded\n");
/* Wait for reset */
msleep(MXT_FWRESET_TIME);
@@ -1155,11 +1320,13 @@ static ssize_t mxt_update_fw_store(struct device *dev,
mxt_initialize(data);
}
- enable_irq(data->irq);
+ if (data->state == APPMODE) {
+ enable_irq(data->irq);
- error = mxt_make_highchg(data);
- if (error)
- return error;
+ error = mxt_make_highchg(data);
+ if (error)
+ return error;
+ }
return count;
}
@@ -1226,6 +1393,11 @@ static ssize_t mxt_debug_enable_store(struct device *dev,
static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off,
size_t *count)
{
+ if (data->state != APPMODE) {
+ dev_err(&data->client->dev, "Not in APPMODE\n");
+ return -EINVAL;
+ }
+
if (off >= data->mem_size)
return -EIO;
@@ -1362,6 +1534,8 @@ static int mxt_probe(struct i2c_client *client,
goto err_free_mem;
}
+ data->state = INIT;
+
input_dev->name = "Atmel maXTouch Touchscreen";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
@@ -1412,10 +1586,10 @@ static int mxt_probe(struct i2c_client *client,
goto err_free_object;
}
- error = mxt_make_highchg(data);
- if (error) {
- dev_err(&client->dev, "Error %d clearing messages\n", error);
- goto err_free_irq;
+ if (data->state == APPMODE) {
+ error = mxt_make_highchg(data);
+ if (error)
+ goto err_free_irq;
}
error = input_register_device(input_dev);