diff mbox

[PATCHv1] Input: atmel_mxt_ts - fix the firmware update

Message ID 20180323194736.7fhgb6qjkwbivsvx@hairyalien (mailing list archive)
State New, archived
Headers show

Commit Message

Nick Dyer March 23, 2018, 7:47 p.m. UTC
On Thu, Mar 22, 2018 at 05:43:30PM +0100, Sebastian Reichel wrote:
> The automatic update mechanism will trigger an update if the
> info block CRCs are different between maxtouch configuration
> file (maxtouch.cfg) and chip.
> 
> The driver compared the CRCs without retrieving the chip CRC,
> resulting always in a failure and firmware flashing action
> triggered. The patch will fix this issue by retrieving the
> chip info block CRC before the check.

Thanks for raising this, I agree it's definitely something we want to
fix.

However, I'm not convinced you're solving the problem in the best way.
You've attached it to the read_t9_resolution() code path, whereas the
info block is common between T9 and T100 and works in the same way.

Would you mind trying the below patch? I've dusted it off from some
work that I did back in 2012 and it should solve your issue.

It also has the benefit that by reading the information block and the
object table into a contiguous region of memory, we can verify the
checksum at probe time. This means we make sure that we are indeed
talking to a chip that supports object protocol correctly.

Signed-off-by: Nick Dyer <nick.dyer@shmanahar.org>
Acked-by: Benson Leung <bleung@chromium.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 193 +++++++++++++++++++------------
 1 file changed, 117 insertions(+), 76 deletions(-)

Comments

Sebastian Reichel April 4, 2018, 3:05 p.m. UTC | #1
Hi Nick,

On Fri, Mar 23, 2018 at 07:47:36PM +0000, Nick Dyer wrote:
> On Thu, Mar 22, 2018 at 05:43:30PM +0100, Sebastian Reichel wrote:
> > The automatic update mechanism will trigger an update if the
> > info block CRCs are different between maxtouch configuration
> > file (maxtouch.cfg) and chip.
> > 
> > The driver compared the CRCs without retrieving the chip CRC,
> > resulting always in a failure and firmware flashing action
> > triggered. The patch will fix this issue by retrieving the
> > chip info block CRC before the check.
> 
> Thanks for raising this, I agree it's definitely something we want to
> fix.
> 
> However, I'm not convinced you're solving the problem in the best way.
> You've attached it to the read_t9_resolution() code path, whereas the
> info block is common between T9 and T100 and works in the same way.
> 
> Would you mind trying the below patch? I've dusted it off from some
> work that I did back in 2012 and it should solve your issue.
> 
> It also has the benefit that by reading the information block and the
> object table into a contiguous region of memory, we can verify the
> checksum at probe time. This means we make sure that we are indeed
> talking to a chip that supports object protocol correctly.
> 
> Signed-off-by: Nick Dyer <nick.dyer@shmanahar.org>
> Acked-by: Benson Leung <bleung@chromium.org>

I currently have an unrelated issue that breaks boot on that board,
so I can't test it, but the patch looks mostly good to me. I
noticed, two useless error messages for -ENOMEM. After fixing those
the patch is

Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>

> ---
>  drivers/input/touchscreen/atmel_mxt_ts.c | 193 +++++++++++++++++++------------
>  1 file changed, 117 insertions(+), 76 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 7659bc48f1db..8a60d91d49a6 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -275,7 +275,8 @@ struct mxt_data {
>  	char phys[64];		/* device physical location */
>  	const struct mxt_platform_data *pdata;
>  	struct mxt_object *object_table;
> -	struct mxt_info info;
> +	struct mxt_info *info;
> +	void *raw_info_block;
>  	unsigned int irq;
>  	unsigned int max_x;
>  	unsigned int max_y;
> @@ -450,12 +451,13 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
>  {
>  	u8 appmode = data->client->addr;
>  	u8 bootloader;
> +	u8 family_id = data->info ? data->info->family_id : 0;
>  
>  	switch (appmode) {
>  	case 0x4a:
>  	case 0x4b:
>  		/* Chips after 1664S use different scheme */
> -		if (retry || data->info.family_id >= 0xa2) {
> +		if (retry || family_id >= 0xa2) {
>  			bootloader = appmode - 0x24;
>  			break;
>  		}
> @@ -682,7 +684,7 @@ mxt_get_object(struct mxt_data *data, u8 type)
>  	struct mxt_object *object;
>  	int i;
>  
> -	for (i = 0; i < data->info.object_num; i++) {
> +	for (i = 0; i < data->info->object_num; i++) {
>  		object = data->object_table + i;
>  		if (object->type == type)
>  			return object;
> @@ -1453,12 +1455,12 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
>  		data_pos += offset;
>  	}
>  
> -	if (cfg_info.family_id != data->info.family_id) {
> +	if (cfg_info.family_id != data->info->family_id) {
>  		dev_err(dev, "Family ID mismatch!\n");
>  		return -EINVAL;
>  	}
>  
> -	if (cfg_info.variant_id != data->info.variant_id) {
> +	if (cfg_info.variant_id != data->info->variant_id) {
>  		dev_err(dev, "Variant ID mismatch!\n");
>  		return -EINVAL;
>  	}
> @@ -1503,7 +1505,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
>  
>  	/* Malloc memory to store configuration */
>  	cfg_start_ofs = MXT_OBJECT_START +
> -			data->info.object_num * sizeof(struct mxt_object) +
> +			data->info->object_num * sizeof(struct mxt_object) +
>  			MXT_INFO_CHECKSUM_SIZE;
>  	config_mem_size = data->mem_size - cfg_start_ofs;
>  	config_mem = kzalloc(config_mem_size, GFP_KERNEL);
> @@ -1554,20 +1556,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
>  	return ret;
>  }
>  
> -static int mxt_get_info(struct mxt_data *data)
> -{
> -	struct i2c_client *client = data->client;
> -	struct mxt_info *info = &data->info;
> -	int error;
> -
> -	/* Read 7-byte info block starting at address 0 */
> -	error = __mxt_read_reg(client, 0, sizeof(*info), info);
> -	if (error)
> -		return error;
> -
> -	return 0;
> -}
> -
>  static void mxt_free_input_device(struct mxt_data *data)
>  {
>  	if (data->input_dev) {
> @@ -1582,9 +1570,10 @@ static void mxt_free_object_table(struct mxt_data *data)
>  	video_unregister_device(&data->dbg.vdev);
>  	v4l2_device_unregister(&data->dbg.v4l2);
>  #endif
> -
> -	kfree(data->object_table);
>  	data->object_table = NULL;
> +	data->info = NULL;
> +	kfree(data->raw_info_block);
> +	data->raw_info_block = NULL;
>  	kfree(data->msg_buf);
>  	data->msg_buf = NULL;
>  	data->T5_address = 0;
> @@ -1600,34 +1589,18 @@ static void mxt_free_object_table(struct mxt_data *data)
>  	data->max_reportid = 0;
>  }
>  
> -static int mxt_get_object_table(struct mxt_data *data)
> +static int mxt_parse_object_table(struct mxt_data *data,
> +				  struct mxt_object *object_table)
>  {
>  	struct i2c_client *client = data->client;
> -	size_t table_size;
> -	struct mxt_object *object_table;
> -	int error;
>  	int i;
>  	u8 reportid;
>  	u16 end_address;
>  
> -	table_size = data->info.object_num * sizeof(struct mxt_object);
> -	object_table = kzalloc(table_size, GFP_KERNEL);
> -	if (!object_table) {
> -		dev_err(&data->client->dev, "Failed to allocate memory\n");
> -		return -ENOMEM;
> -	}
> -
> -	error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
> -			object_table);
> -	if (error) {
> -		kfree(object_table);
> -		return error;
> -	}
> -
>  	/* Valid Report IDs start counting from 1 */
>  	reportid = 1;
>  	data->mem_size = 0;
> -	for (i = 0; i < data->info.object_num; i++) {
> +	for (i = 0; i < data->info->object_num; i++) {
>  		struct mxt_object *object = object_table + i;
>  		u8 min_id, max_id;
>  
> @@ -1651,8 +1624,8 @@ static int mxt_get_object_table(struct mxt_data *data)
>  
>  		switch (object->type) {
>  		case MXT_GEN_MESSAGE_T5:
> -			if (data->info.family_id == 0x80 &&
> -			    data->info.version < 0x20) {
> +			if (data->info->family_id == 0x80 &&
> +			    data->info->version < 0x20) {
>  				/*
>  				 * On mXT224 firmware versions prior to V2.0
>  				 * read and discard unused CRC byte otherwise
> @@ -1707,24 +1680,108 @@ static int mxt_get_object_table(struct mxt_data *data)
>  	/* If T44 exists, T5 position has to be directly after */
>  	if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
>  		dev_err(&client->dev, "Invalid T44 position\n");
> -		error = -EINVAL;
> -		goto free_object_table;
> +		return -EINVAL;
>  	}
>  
>  	data->msg_buf = kcalloc(data->max_reportid,
>  				data->T5_msg_size, GFP_KERNEL);
>  	if (!data->msg_buf) {
>  		dev_err(&client->dev, "Failed to allocate message buffer\n");
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mxt_read_info_block(struct mxt_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	int error;
> +	size_t size;
> +	void *id_buf, *buf;
> +	uint8_t num_objects;
> +	u32 calculated_crc;
> +	u8 *crc_ptr;
> +
> +	/* If info block already allocated, free it */
> +	if (data->raw_info_block != NULL)
> +		mxt_free_object_table(data);
> +
> +	/* Read 7-byte ID information block starting at address 0 */
> +	size = sizeof(struct mxt_info);
> +	id_buf = kzalloc(size, GFP_KERNEL);
> +	if (!id_buf) {
> +		dev_err(&client->dev, "Failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	error = __mxt_read_reg(client, 0, size, id_buf);
> +	if (error) {
> +		kfree(id_buf);
> +		return error;
> +	}
> +
> +	/* Resize buffer to give space for rest of info block */
> +	num_objects = ((struct mxt_info *)id_buf)->object_num;
> +	size += (num_objects * sizeof(struct mxt_object))
> +		+ MXT_INFO_CHECKSUM_SIZE;
> +
> +	buf = krealloc(id_buf, size, GFP_KERNEL);
> +	if (!buf) {
> +		dev_err(&client->dev, "Failed to allocate memory\n");
>  		error = -ENOMEM;
> -		goto free_object_table;
> +		goto err_free_mem;
> +	}
> +
> +	/* Read rest of info block */
> +	error = __mxt_read_reg(client, MXT_OBJECT_START,
> +			       size - MXT_OBJECT_START,
> +			       buf + MXT_OBJECT_START);
> +	if (error)
> +		goto err_free_mem;
> +
> +	/* Extract & calculate checksum */
> +	crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE;
> +	data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
> +
> +	calculated_crc = mxt_calculate_crc(buf, 0,
> +					   size - MXT_INFO_CHECKSUM_SIZE);
> +
> +	/*
> +	 * CRC mismatch can be caused by data corruption due to I2C comms
> +	 * issue or else device is not using Object Based Protocol (eg i2c-hid)
> +	 */
> +	if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
> +		dev_err(&client->dev,
> +			"Info Block CRC error calculated=0x%06X read=0x%06X\n",
> +			calculated_crc, data->info_crc);
> +		error = -EIO;
> +		goto err_free_mem;
>  	}
>  
> -	data->object_table = object_table;
> +	data->raw_info_block = buf;
> +	data->info = (struct mxt_info *)buf;
> +
> +	dev_info(&client->dev,
> +		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
> +		 data->info->family_id, data->info->variant_id,
> +		 data->info->version >> 4, data->info->version & 0xf,
> +		 data->info->build, data->info->object_num);
> +
> +	/* Parse object table information */
> +	error = mxt_parse_object_table(data, buf + MXT_OBJECT_START);
> +	if (error) {
> +		dev_err(&client->dev, "Error %d parsing object table\n", error);
> +		mxt_free_object_table(data);
> +		return error;
> +	}
> +
> +	data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
>  
>  	return 0;
>  
> -free_object_table:
> -	mxt_free_object_table(data);
> +err_free_mem:
> +	kfree(buf);
>  	return error;
>  }
>  
> @@ -2039,7 +2096,7 @@ static int mxt_initialize(struct mxt_data *data)
>  	int error;
>  
>  	while (1) {
> -		error = mxt_get_info(data);
> +		error = mxt_read_info_block(data);
>  		if (!error)
>  			break;
>  
> @@ -2070,13 +2127,6 @@ static int mxt_initialize(struct mxt_data *data)
>  		msleep(MXT_FW_RESET_TIME);
>  	}
>  
> -	/* Get object table information */
> -	error = mxt_get_object_table(data);
> -	if (error) {
> -		dev_err(&client->dev, "Error %d reading object table\n", error);
> -		return error;
> -	}
> -
>  	error = mxt_acquire_irq(data);
>  	if (error)
>  		goto err_free_object_table;
> @@ -2155,25 +2205,24 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data)
>  static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
>  			       unsigned int y)
>  {
> -	struct mxt_info *info = &data->info;
>  	struct mxt_dbg *dbg = &data->dbg;
>  	unsigned int ofs, page;
>  	unsigned int col = 0;
>  	unsigned int col_width;
>  
> -	if (info->family_id == MXT_FAMILY_1386) {
> -		col_width = info->matrix_ysize / MXT1386_COLUMNS;
> +	if (data->info->family_id == MXT_FAMILY_1386) {
> +		col_width = data->info->matrix_ysize / MXT1386_COLUMNS;
>  		col = y / col_width;
>  		y = y % col_width;
>  	} else {
> -		col_width = info->matrix_ysize;
> +		col_width = data->info->matrix_ysize;
>  	}
>  
>  	ofs = (y + (x * col_width)) * sizeof(u16);
>  	page = ofs / MXT_DIAGNOSTIC_SIZE;
>  	ofs %= MXT_DIAGNOSTIC_SIZE;
>  
> -	if (info->family_id == MXT_FAMILY_1386)
> +	if (data->info->family_id == MXT_FAMILY_1386)
>  		page += col * MXT1386_PAGES_PER_COLUMN;
>  
>  	return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
> @@ -2483,7 +2532,6 @@ static const struct video_device mxt_video_device = {
>  
>  static void mxt_debug_init(struct mxt_data *data)
>  {
> -	struct mxt_info *info = &data->info;
>  	struct mxt_dbg *dbg = &data->dbg;
>  	struct mxt_object *object;
>  	int error;
> @@ -2508,11 +2556,11 @@ static void mxt_debug_init(struct mxt_data *data)
>  	/* Calculate size of data and allocate buffer */
>  	dbg->t37_nodes = data->xsize * data->ysize;
>  
> -	if (info->family_id == MXT_FAMILY_1386)
> +	if (data->info->family_id == MXT_FAMILY_1386)
>  		dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
>  	else
>  		dbg->t37_pages = DIV_ROUND_UP(data->xsize *
> -					      info->matrix_ysize *
> +					      data->info->matrix_ysize *
>  					      sizeof(u16),
>  					      sizeof(dbg->t37_buf->data));
>  
> @@ -2569,7 +2617,6 @@ static int mxt_configure_objects(struct mxt_data *data,
>  				 const struct firmware *cfg)
>  {
>  	struct device *dev = &data->client->dev;
> -	struct mxt_info *info = &data->info;
>  	int error;
>  
>  	error = mxt_init_t7_power_cfg(data);
> @@ -2594,11 +2641,6 @@ static int mxt_configure_objects(struct mxt_data *data,
>  
>  	mxt_debug_init(data);
>  
> -	dev_info(dev,
> -		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
> -		 info->family_id, info->variant_id, info->version >> 4,
> -		 info->version & 0xf, info->build, info->object_num);
> -
>  	return 0;
>  }
>  
> @@ -2607,9 +2649,9 @@ static ssize_t mxt_fw_version_show(struct device *dev,
>  				   struct device_attribute *attr, char *buf)
>  {
>  	struct mxt_data *data = dev_get_drvdata(dev);
> -	struct mxt_info *info = &data->info;
>  	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
> -			 info->version >> 4, info->version & 0xf, info->build);
> +			 data->info->version >> 4, data->info->version & 0xf,
> +			 data->info->build);
>  }
>  
>  /* Hardware Version is returned as FamilyID.VariantID */
> @@ -2617,9 +2659,8 @@ static ssize_t mxt_hw_version_show(struct device *dev,
>  				   struct device_attribute *attr, char *buf)
>  {
>  	struct mxt_data *data = dev_get_drvdata(dev);
> -	struct mxt_info *info = &data->info;
>  	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
> -			 info->family_id, info->variant_id);
> +			data->info->family_id, data->info->variant_id);
>  }
>  
>  static ssize_t mxt_show_instance(char *buf, int count,
> @@ -2656,7 +2697,7 @@ static ssize_t mxt_object_show(struct device *dev,
>  		return -ENOMEM;
>  
>  	error = 0;
> -	for (i = 0; i < data->info.object_num; i++) {
> +	for (i = 0; i < data->info->object_num; i++) {
>  		object = data->object_table + i;
>  
>  		if (!mxt_object_readable(object->type))
> -- 
> 2.14.1
>
Nandor Han April 17, 2018, 6:37 a.m. UTC | #2
On 23/03/18 21:47, Nick Dyer wrote:
> On Thu, Mar 22, 2018 at 05:43:30PM +0100, Sebastian Reichel wrote:
>> The automatic update mechanism will trigger an update if the
>> info block CRCs are different between maxtouch configuration
>> file (maxtouch.cfg) and chip.
>>
>> The driver compared the CRCs without retrieving the chip CRC,
>> resulting always in a failure and firmware flashing action
>> triggered. The patch will fix this issue by retrieving the
>> chip info block CRC before the check.
> 
> Thanks for raising this, I agree it's definitely something we want to
> fix.
> 
> However, I'm not convinced you're solving the problem in the best way.
> You've attached it to the read_t9_resolution() code path, whereas the
> info block is common between T9 and T100 and works in the same way.
> 
> Would you mind trying the below patch? I've dusted it off from some
> work that I did back in 2012 and it should solve your issue.
> 
> It also has the benefit that by reading the information block and the
> object table into a contiguous region of memory, we can verify the
> checksum at probe time. This means we make sure that we are indeed
> talking to a chip that supports object protocol correctly.
> 

Thanks Nick. This looks like a better solution.
Sebastian will test this and we can see the results.


Nandor
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 7659bc48f1db..8a60d91d49a6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -275,7 +275,8 @@  struct mxt_data {
 	char phys[64];		/* device physical location */
 	const struct mxt_platform_data *pdata;
 	struct mxt_object *object_table;
-	struct mxt_info info;
+	struct mxt_info *info;
+	void *raw_info_block;
 	unsigned int irq;
 	unsigned int max_x;
 	unsigned int max_y;
@@ -450,12 +451,13 @@  static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
 {
 	u8 appmode = data->client->addr;
 	u8 bootloader;
+	u8 family_id = data->info ? data->info->family_id : 0;
 
 	switch (appmode) {
 	case 0x4a:
 	case 0x4b:
 		/* Chips after 1664S use different scheme */
-		if (retry || data->info.family_id >= 0xa2) {
+		if (retry || family_id >= 0xa2) {
 			bootloader = appmode - 0x24;
 			break;
 		}
@@ -682,7 +684,7 @@  mxt_get_object(struct mxt_data *data, u8 type)
 	struct mxt_object *object;
 	int i;
 
-	for (i = 0; i < data->info.object_num; i++) {
+	for (i = 0; i < data->info->object_num; i++) {
 		object = data->object_table + i;
 		if (object->type == type)
 			return object;
@@ -1453,12 +1455,12 @@  static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
 		data_pos += offset;
 	}
 
-	if (cfg_info.family_id != data->info.family_id) {
+	if (cfg_info.family_id != data->info->family_id) {
 		dev_err(dev, "Family ID mismatch!\n");
 		return -EINVAL;
 	}
 
-	if (cfg_info.variant_id != data->info.variant_id) {
+	if (cfg_info.variant_id != data->info->variant_id) {
 		dev_err(dev, "Variant ID mismatch!\n");
 		return -EINVAL;
 	}
@@ -1503,7 +1505,7 @@  static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
 
 	/* Malloc memory to store configuration */
 	cfg_start_ofs = MXT_OBJECT_START +
-			data->info.object_num * sizeof(struct mxt_object) +
+			data->info->object_num * sizeof(struct mxt_object) +
 			MXT_INFO_CHECKSUM_SIZE;
 	config_mem_size = data->mem_size - cfg_start_ofs;
 	config_mem = kzalloc(config_mem_size, GFP_KERNEL);
@@ -1554,20 +1556,6 @@  static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
 	return ret;
 }
 
-static int mxt_get_info(struct mxt_data *data)
-{
-	struct i2c_client *client = data->client;
-	struct mxt_info *info = &data->info;
-	int error;
-
-	/* Read 7-byte info block starting at address 0 */
-	error = __mxt_read_reg(client, 0, sizeof(*info), info);
-	if (error)
-		return error;
-
-	return 0;
-}
-
 static void mxt_free_input_device(struct mxt_data *data)
 {
 	if (data->input_dev) {
@@ -1582,9 +1570,10 @@  static void mxt_free_object_table(struct mxt_data *data)
 	video_unregister_device(&data->dbg.vdev);
 	v4l2_device_unregister(&data->dbg.v4l2);
 #endif
-
-	kfree(data->object_table);
 	data->object_table = NULL;
+	data->info = NULL;
+	kfree(data->raw_info_block);
+	data->raw_info_block = NULL;
 	kfree(data->msg_buf);
 	data->msg_buf = NULL;
 	data->T5_address = 0;
@@ -1600,34 +1589,18 @@  static void mxt_free_object_table(struct mxt_data *data)
 	data->max_reportid = 0;
 }
 
-static int mxt_get_object_table(struct mxt_data *data)
+static int mxt_parse_object_table(struct mxt_data *data,
+				  struct mxt_object *object_table)
 {
 	struct i2c_client *client = data->client;
-	size_t table_size;
-	struct mxt_object *object_table;
-	int error;
 	int i;
 	u8 reportid;
 	u16 end_address;
 
-	table_size = data->info.object_num * sizeof(struct mxt_object);
-	object_table = kzalloc(table_size, GFP_KERNEL);
-	if (!object_table) {
-		dev_err(&data->client->dev, "Failed to allocate memory\n");
-		return -ENOMEM;
-	}
-
-	error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
-			object_table);
-	if (error) {
-		kfree(object_table);
-		return error;
-	}
-
 	/* Valid Report IDs start counting from 1 */
 	reportid = 1;
 	data->mem_size = 0;
-	for (i = 0; i < data->info.object_num; i++) {
+	for (i = 0; i < data->info->object_num; i++) {
 		struct mxt_object *object = object_table + i;
 		u8 min_id, max_id;
 
@@ -1651,8 +1624,8 @@  static int mxt_get_object_table(struct mxt_data *data)
 
 		switch (object->type) {
 		case MXT_GEN_MESSAGE_T5:
-			if (data->info.family_id == 0x80 &&
-			    data->info.version < 0x20) {
+			if (data->info->family_id == 0x80 &&
+			    data->info->version < 0x20) {
 				/*
 				 * On mXT224 firmware versions prior to V2.0
 				 * read and discard unused CRC byte otherwise
@@ -1707,24 +1680,108 @@  static int mxt_get_object_table(struct mxt_data *data)
 	/* If T44 exists, T5 position has to be directly after */
 	if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
 		dev_err(&client->dev, "Invalid T44 position\n");
-		error = -EINVAL;
-		goto free_object_table;
+		return -EINVAL;
 	}
 
 	data->msg_buf = kcalloc(data->max_reportid,
 				data->T5_msg_size, GFP_KERNEL);
 	if (!data->msg_buf) {
 		dev_err(&client->dev, "Failed to allocate message buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int mxt_read_info_block(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+	size_t size;
+	void *id_buf, *buf;
+	uint8_t num_objects;
+	u32 calculated_crc;
+	u8 *crc_ptr;
+
+	/* If info block already allocated, free it */
+	if (data->raw_info_block != NULL)
+		mxt_free_object_table(data);
+
+	/* Read 7-byte ID information block starting at address 0 */
+	size = sizeof(struct mxt_info);
+	id_buf = kzalloc(size, GFP_KERNEL);
+	if (!id_buf) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	error = __mxt_read_reg(client, 0, size, id_buf);
+	if (error) {
+		kfree(id_buf);
+		return error;
+	}
+
+	/* Resize buffer to give space for rest of info block */
+	num_objects = ((struct mxt_info *)id_buf)->object_num;
+	size += (num_objects * sizeof(struct mxt_object))
+		+ MXT_INFO_CHECKSUM_SIZE;
+
+	buf = krealloc(id_buf, size, GFP_KERNEL);
+	if (!buf) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
 		error = -ENOMEM;
-		goto free_object_table;
+		goto err_free_mem;
+	}
+
+	/* Read rest of info block */
+	error = __mxt_read_reg(client, MXT_OBJECT_START,
+			       size - MXT_OBJECT_START,
+			       buf + MXT_OBJECT_START);
+	if (error)
+		goto err_free_mem;
+
+	/* Extract & calculate checksum */
+	crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE;
+	data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
+
+	calculated_crc = mxt_calculate_crc(buf, 0,
+					   size - MXT_INFO_CHECKSUM_SIZE);
+
+	/*
+	 * CRC mismatch can be caused by data corruption due to I2C comms
+	 * issue or else device is not using Object Based Protocol (eg i2c-hid)
+	 */
+	if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
+		dev_err(&client->dev,
+			"Info Block CRC error calculated=0x%06X read=0x%06X\n",
+			calculated_crc, data->info_crc);
+		error = -EIO;
+		goto err_free_mem;
 	}
 
-	data->object_table = object_table;
+	data->raw_info_block = buf;
+	data->info = (struct mxt_info *)buf;
+
+	dev_info(&client->dev,
+		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
+		 data->info->family_id, data->info->variant_id,
+		 data->info->version >> 4, data->info->version & 0xf,
+		 data->info->build, data->info->object_num);
+
+	/* Parse object table information */
+	error = mxt_parse_object_table(data, buf + MXT_OBJECT_START);
+	if (error) {
+		dev_err(&client->dev, "Error %d parsing object table\n", error);
+		mxt_free_object_table(data);
+		return error;
+	}
+
+	data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
 
 	return 0;
 
-free_object_table:
-	mxt_free_object_table(data);
+err_free_mem:
+	kfree(buf);
 	return error;
 }
 
@@ -2039,7 +2096,7 @@  static int mxt_initialize(struct mxt_data *data)
 	int error;
 
 	while (1) {
-		error = mxt_get_info(data);
+		error = mxt_read_info_block(data);
 		if (!error)
 			break;
 
@@ -2070,13 +2127,6 @@  static int mxt_initialize(struct mxt_data *data)
 		msleep(MXT_FW_RESET_TIME);
 	}
 
-	/* Get object table information */
-	error = mxt_get_object_table(data);
-	if (error) {
-		dev_err(&client->dev, "Error %d reading object table\n", error);
-		return error;
-	}
-
 	error = mxt_acquire_irq(data);
 	if (error)
 		goto err_free_object_table;
@@ -2155,25 +2205,24 @@  static int mxt_init_t7_power_cfg(struct mxt_data *data)
 static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
 			       unsigned int y)
 {
-	struct mxt_info *info = &data->info;
 	struct mxt_dbg *dbg = &data->dbg;
 	unsigned int ofs, page;
 	unsigned int col = 0;
 	unsigned int col_width;
 
-	if (info->family_id == MXT_FAMILY_1386) {
-		col_width = info->matrix_ysize / MXT1386_COLUMNS;
+	if (data->info->family_id == MXT_FAMILY_1386) {
+		col_width = data->info->matrix_ysize / MXT1386_COLUMNS;
 		col = y / col_width;
 		y = y % col_width;
 	} else {
-		col_width = info->matrix_ysize;
+		col_width = data->info->matrix_ysize;
 	}
 
 	ofs = (y + (x * col_width)) * sizeof(u16);
 	page = ofs / MXT_DIAGNOSTIC_SIZE;
 	ofs %= MXT_DIAGNOSTIC_SIZE;
 
-	if (info->family_id == MXT_FAMILY_1386)
+	if (data->info->family_id == MXT_FAMILY_1386)
 		page += col * MXT1386_PAGES_PER_COLUMN;
 
 	return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
@@ -2483,7 +2532,6 @@  static const struct video_device mxt_video_device = {
 
 static void mxt_debug_init(struct mxt_data *data)
 {
-	struct mxt_info *info = &data->info;
 	struct mxt_dbg *dbg = &data->dbg;
 	struct mxt_object *object;
 	int error;
@@ -2508,11 +2556,11 @@  static void mxt_debug_init(struct mxt_data *data)
 	/* Calculate size of data and allocate buffer */
 	dbg->t37_nodes = data->xsize * data->ysize;
 
-	if (info->family_id == MXT_FAMILY_1386)
+	if (data->info->family_id == MXT_FAMILY_1386)
 		dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
 	else
 		dbg->t37_pages = DIV_ROUND_UP(data->xsize *
-					      info->matrix_ysize *
+					      data->info->matrix_ysize *
 					      sizeof(u16),
 					      sizeof(dbg->t37_buf->data));
 
@@ -2569,7 +2617,6 @@  static int mxt_configure_objects(struct mxt_data *data,
 				 const struct firmware *cfg)
 {
 	struct device *dev = &data->client->dev;
-	struct mxt_info *info = &data->info;
 	int error;
 
 	error = mxt_init_t7_power_cfg(data);
@@ -2594,11 +2641,6 @@  static int mxt_configure_objects(struct mxt_data *data,
 
 	mxt_debug_init(data);
 
-	dev_info(dev,
-		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
-		 info->family_id, info->variant_id, info->version >> 4,
-		 info->version & 0xf, info->build, info->object_num);
-
 	return 0;
 }
 
@@ -2607,9 +2649,9 @@  static ssize_t mxt_fw_version_show(struct device *dev,
 				   struct device_attribute *attr, char *buf)
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
-	struct mxt_info *info = &data->info;
 	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
-			 info->version >> 4, info->version & 0xf, info->build);
+			 data->info->version >> 4, data->info->version & 0xf,
+			 data->info->build);
 }
 
 /* Hardware Version is returned as FamilyID.VariantID */
@@ -2617,9 +2659,8 @@  static ssize_t mxt_hw_version_show(struct device *dev,
 				   struct device_attribute *attr, char *buf)
 {
 	struct mxt_data *data = dev_get_drvdata(dev);
-	struct mxt_info *info = &data->info;
 	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
-			 info->family_id, info->variant_id);
+			data->info->family_id, data->info->variant_id);
 }
 
 static ssize_t mxt_show_instance(char *buf, int count,
@@ -2656,7 +2697,7 @@  static ssize_t mxt_object_show(struct device *dev,
 		return -ENOMEM;
 
 	error = 0;
-	for (i = 0; i < data->info.object_num; i++) {
+	for (i = 0; i < data->info->object_num; i++) {
 		object = data->object_table + i;
 
 		if (!mxt_object_readable(object->type))