input: synaptics_fw_update: Add firmware ID reading

- Fix issue of converting config id
- Support "Option 1 (bit0)" in image file header
  -- Query firmware build id from image file header

Change-Id: Iabd24d662762b67de7163ad7321f95f1c58f1459
Git-commit: 7e811dcd8eaf3fc0df765335d0ef14926f325f64
Git-repo: git://github.com/synaptics-touch/synaptics-dsx-i2c.git
[amaloche@codeaurora.org: only portion of patch taken.
 Custom changes were necessary to add for integration
 with mainline]
Signed-off-by: Amy Maloche <amaloche@codeaurora.org>
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index 986c062..dbe378c 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -25,6 +25,7 @@
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/firmware.h>
+#include <linux/string.h>
 #include <linux/input/synaptics_dsx.h>
 #include "synaptics_i2c_rmi4.h"
 
@@ -34,14 +35,7 @@
 #define FORCE_UPDATE false
 #define INSIDE_FIRMWARE_UPDATE
 
-#define CHECKSUM_OFFSET 0x00
-#define BOOTLOADER_VERSION_OFFSET 0x07
-#define IMAGE_SIZE_OFFSET 0x08
-#define CONFIG_SIZE_OFFSET 0x0C
-#define PRODUCT_ID_OFFSET 0x10
-#define PRODUCT_INFO_OFFSET 0x1E
 #define FW_IMAGE_OFFSET 0x100
-#define PRODUCT_ID_SIZE 10
 
 #define BOOTLOADER_ID_OFFSET 0
 #define FLASH_PROPERTIES_OFFSET 2
@@ -59,10 +53,14 @@
 #define BLOCK_NUMBER_OFFSET 0
 #define BLOCK_DATA_OFFSET 2
 
-#define UI_CONFIG_AREA 0x00
-#define PERM_CONFIG_AREA 0x01
-#define BL_CONFIG_AREA 0x02
-#define DISP_CONFIG_AREA 0x03
+#define NAME_BUFFER_SIZE 128
+
+enum falsh_config_area {
+	UI_CONFIG_AREA = 0x00,
+	PERM_CONFIG_AREA = 0x01,
+	BL_CONFIG_AREA = 0x02,
+	DISP_CONFIG_AREA = 0x03
+};
 
 enum flash_command {
 	CMD_WRITE_FW_BLOCK		= 0x2,
@@ -70,9 +68,10 @@
 	CMD_READ_CONFIG_BLOCK	= 0x5,
 	CMD_WRITE_CONFIG_BLOCK	= 0x6,
 	CMD_ERASE_CONFIG		= 0x7,
+	CMD_READ_SENSOR_ID		= 0x8,
 	CMD_ERASE_BL_CONFIG		= 0x9,
 	CMD_ERASE_DISP_CONFIG	= 0xA,
-	CMD_ENABLE_FLASH_PROG	= 0xF,
+	CMD_ENABLE_FLASH_PROG	= 0xF
 };
 
 enum flash_area {
@@ -81,6 +80,11 @@
 	CONFIG_AREA
 };
 
+enum image_file_option {
+	OPTION_BUILD_INFO = 0,
+	OPTION_CONTAIN_BOOTLOADER = 1,
+};
+
 #define SLEEP_MODE_NORMAL (0x00)
 #define SLEEP_MODE_SENSOR_SLEEP (0x01)
 #define SLEEP_MODE_RESERVED0 (0x02)
@@ -91,6 +95,8 @@
 #define ERASE_WAIT_MS (5 * 1000)
 #define RESET_WAIT_MS (500)
 
+#define POLLING_MODE 0
+
 #define SLEEP_TIME_US 50
 
 static ssize_t fwu_sysfs_show_image(struct file *data_file,
@@ -139,6 +145,40 @@
 
 static int fwu_wait_for_idle(int timeout_ms);
 
+struct image_header_data {
+	union {
+		struct {
+			/* 0x00-0x0F */
+			unsigned char file_checksum[4];
+			unsigned char reserved_04;
+			unsigned char reserved_05;
+			unsigned char options_firmware_id:1;
+			unsigned char options_contain_bootloader:1;
+			unsigned char options_reserved:6;
+			unsigned char bootloader_version;
+			unsigned char firmware_size[4];
+			unsigned char config_size[4];
+			/* 0x10-0x1F */
+			unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE];
+			unsigned char reserved_1a;
+			unsigned char reserved_1b;
+			unsigned char reserved_1c;
+			unsigned char reserved_1d;
+			unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+			/* 0x20-0x2F */
+			unsigned char reserved_20_2f[0x10];
+			/* 0x30-0x3F */
+			unsigned char ds_firmware_id[0x10];
+			/* 0x40-0x4F */
+			unsigned char ds_customize_info[10];
+			unsigned char reserved_4a_4f[6];
+			/* 0x50-0x53*/
+			unsigned char firmware_id[4];
+		} __packed;
+		unsigned char data[54];
+	};
+};
+
 struct image_header {
 	unsigned int checksum;
 	unsigned int image_size;
@@ -147,6 +187,8 @@
 	unsigned char bootloader_version;
 	unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
 	unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+	unsigned int firmware_id;
+	bool is_contain_build_info;
 };
 
 struct pdt_properties {
@@ -245,6 +287,7 @@
 	struct f34_flash_properties flash_properties;
 	struct workqueue_struct *fwu_workqueue;
 	struct delayed_work fwu_work;
+	char *firmware_name;
 };
 
 static struct bin_attribute dev_attr_data = {
@@ -308,18 +351,36 @@
 			(unsigned int)ptr[3] * 0x1000000;
 }
 
+static unsigned int extract_uint_be(const unsigned char *ptr)
+{
+	return (unsigned int)ptr[3] +
+			(unsigned int)ptr[2] * 0x100 +
+			(unsigned int)ptr[1] * 0x10000 +
+			(unsigned int)ptr[0] * 0x1000000;
+}
+
 static void parse_header(struct image_header *header,
 		const unsigned char *fw_image)
 {
-	header->checksum = extract_uint(&fw_image[CHECKSUM_OFFSET]);
-	header->bootloader_version = fw_image[BOOTLOADER_VERSION_OFFSET];
-	header->image_size = extract_uint(&fw_image[IMAGE_SIZE_OFFSET]);
-	header->config_size = extract_uint(&fw_image[CONFIG_SIZE_OFFSET]);
-	memcpy(header->product_id, &fw_image[PRODUCT_ID_OFFSET],
-			SYNAPTICS_RMI4_PRODUCT_ID_SIZE);
-	header->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0;
-	memcpy(header->product_info, &fw_image[PRODUCT_INFO_OFFSET],
-			SYNAPTICS_RMI4_PRODUCT_INFO_SIZE);
+	struct image_header_data *data = (struct image_header_data *)fw_image;
+	header->checksum = extract_uint(data->file_checksum);
+	header->bootloader_version = data->bootloader_version;
+	header->image_size = extract_uint(data->firmware_size);
+	header->config_size = extract_uint(data->config_size);
+	memcpy(header->product_id, data->product_id,
+		sizeof(data->product_id));
+	header->product_id[sizeof(data->product_info)] = 0;
+	memcpy(header->product_info, data->product_info,
+		sizeof(data->product_info));
+
+	header->is_contain_build_info =
+		(data->options_firmware_id == (1 << OPTION_BUILD_INFO));
+	if (header->is_contain_build_info) {
+		header->firmware_id = extract_uint(data->firmware_id);
+		dev_info(&fwu->rmi4_data->i2c_client->dev,
+			"%s Firwmare build id %d\n", __func__,
+			header->firmware_id);
+	}
 
 #ifdef DEBUG_FW_UPDATE
 	dev_info(&fwu->rmi4_data->i2c_client->dev,
@@ -516,6 +577,9 @@
 	int count = 0;
 	int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1;
 	do {
+		#if POLLING_MODE
+		fwu_read_f34_flash_status();
+		#endif
 		if (fwu->flash_control.command == 0x00)
 			return 0;
 
@@ -533,7 +597,7 @@
 	return -ETIMEDOUT;
 }
 
-static enum flash_area fwu_go_nogo(void)
+static enum flash_area fwu_go_nogo(struct image_header *header)
 {
 	int retval = 0;
 	int index = 0;
@@ -569,7 +633,6 @@
 		goto exit;
 	}
 
-
 	/* device firmware id */
 	retval = fwu->fn_ptr->read(fwu->rmi4_data,
 				fwu->f01_fd.query_base_addr + 18,
@@ -577,37 +640,45 @@
 				sizeof(firmware_id));
 	if (retval < 0) {
 		dev_err(&i2c_client->dev,
-			"Failed to read firmware ID (code %d).\n", retval);
+			"%s: Failed to read firmware ID (code %d).\n",
+			__func__, retval);
 		goto exit;
 	}
 	firmware_id[3] = 0;
 	deviceFirmwareID = extract_uint(firmware_id);
 
 	/* .img firmware id */
-	strptr = strnstr(fwu->rmi4_data->fw_image_name, "PR",
-			sizeof(fwu->rmi4_data->fw_image_name));
-	if (!strptr) {
+	if (header->is_contain_build_info) {
 		dev_err(&i2c_client->dev,
-			"No valid PR number (PRxxxxxxx)" \
-			"found in image file name...\n");
-		goto exit;
+			"%s: Image option contains build info.\n",
+			__func__);
+		imageFirmwareID = header->firmware_id;
+	} else {
+		strptr = strnstr(fwu->firmware_name, "PR",
+				sizeof(fwu->firmware_name));
+		if (!strptr) {
+			dev_err(&i2c_client->dev,
+				"No valid PR number (PRxxxxxxx)" \
+				"found in image file name...\n");
+			goto exit;
+		}
+
+		strptr += 2;
+		while (strptr[index] >= '0' && strptr[index] <= '9') {
+			imagePR[index] = strptr[index];
+			index++;
+		}
+		imagePR[index] = 0;
+
+		retval = kstrtoul(imagePR, 10, &imageFirmwareID);
+		if (retval ==  -EINVAL) {
+			dev_err(&i2c_client->dev,
+				"invalid image firmware id...\n");
+			goto exit;
+		}
 	}
 
-	strptr += 2;
-	while (strptr[index] >= '0' && strptr[index] <= '9') {
-		imagePR[index] = strptr[index];
-		index++;
-	}
-	imagePR[index] = 0;
-
-	retval = kstrtoul(imagePR, 10, &imageFirmwareID);
-	if (retval ==  -EINVAL) {
-		dev_err(&i2c_client->dev,
-			"invalid image firmware id...\n");
-		goto exit;
-	}
-
-	dev_info(&i2c_client->dev,
+	dev_dbg(&i2c_client->dev,
 			"Device firmware id %d, .img firmware id %d\n",
 			deviceFirmwareID,
 			(unsigned int)imageFirmwareID);
@@ -617,7 +688,8 @@
 	} else if (imageFirmwareID < deviceFirmwareID) {
 		flash_area = NONE;
 		dev_info(&i2c_client->dev,
-			"Img fw is older than device fw.  Skip fw update.\n");
+			"%s: Img fw is older than device fw. Skip fw update.\n",
+			__func__);
 		goto exit;
 	}
 
@@ -628,24 +700,29 @@
 				sizeof(config_id));
 	if (retval < 0) {
 		dev_err(&i2c_client->dev,
-			"Failed to read config ID (code %d).\n", retval);
+			"%s: Failed to read config ID (code %d).\n",
+			__func__, retval);
 		flash_area = NONE;
 		goto exit;
 	}
-	deviceConfigID =  extract_uint(config_id);
+	deviceConfigID =  extract_uint_be(config_id);
 
-	dev_info(&i2c_client->dev,
+	dev_dbg(&i2c_client->dev,
 		"Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
 		config_id[0], config_id[1], config_id[2], config_id[3]);
 
 	/* .img config id */
-	dev_info(&i2c_client->dev,
+	dev_dbg(&i2c_client->dev,
 			".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
 			fwu->config_data[0],
 			fwu->config_data[1],
 			fwu->config_data[2],
 			fwu->config_data[3]);
-	imageConfigID =  extract_uint(fwu->config_data);
+	imageConfigID =  extract_uint_be(fwu->config_data);
+
+	dev_dbg(&i2c_client->dev,
+		"%s: Device config ID %d, .img config ID %d\n",
+		__func__, deviceConfigID, imageConfigID);
 
 	if (imageConfigID > deviceConfigID) {
 		flash_area = CONFIG_AREA;
@@ -656,10 +733,10 @@
 	kfree(imagePR);
 	if (flash_area == NONE)
 		dev_info(&i2c_client->dev,
-			"Nothing needs to be updated\n");
+			"%s: Nothing needs to be updated\n", __func__);
 	else
 		dev_info(&i2c_client->dev,
-			"Update %s block\n",
+			"%s: Update %s block\n", __func__,
 			flash_area == UI_FIRMWARE ? "UI FW" : "CONFIG");
 	return flash_area;
 }
@@ -1210,7 +1287,7 @@
 
 static int fwu_start_reflash(void)
 {
-	int retval;
+	int retval = 0;
 	struct image_header header;
 	const unsigned char *fw_image;
 	const struct firmware *fw_entry = NULL;
@@ -1229,18 +1306,30 @@
 	if (fwu->ext_data_source)
 		fw_image = fwu->ext_data_source;
 	else {
-		dev_dbg(&fwu->rmi4_data->i2c_client->dev,
-				"%s: Requesting firmware image %s\n",
-				__func__, fwu->rmi4_data->fw_image_name);
+		fwu->firmware_name = kcalloc(NAME_BUFFER_SIZE,
+			sizeof(char), GFP_KERNEL);
+		if (!fwu->firmware_name) {
+			dev_err(&fwu->rmi4_data->i2c_client->dev,
+				"%s Failed to allocate firmware name (%d).\n",
+				__func__, NAME_BUFFER_SIZE);
+			retval = -ENOMEM;
+			goto memory_exit;
+		}
+
+		snprintf(fwu->firmware_name, NAME_BUFFER_SIZE, "%s",
+			fwu->rmi4_data->fw_image_name);
+		dev_info(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Requesting firmware image %s\n",
+			__func__, fwu->firmware_name);
 
 		retval = request_firmware(&fw_entry,
-				fwu->rmi4_data->fw_image_name,
+				fwu->firmware_name,
 				&fwu->rmi4_data->i2c_client->dev);
 		if (retval != 0) {
 			dev_err(&fwu->rmi4_data->i2c_client->dev,
 					"%s: Firmware image %s not available\n",
 					__func__,
-					fwu->rmi4_data->fw_image_name);
+					fwu->firmware_name);
 			retval = -EINVAL;
 			goto exit;
 		}
@@ -1264,7 +1353,7 @@
 	if (fwu->ext_data_source)
 		flash_area = UI_FIRMWARE;
 	else
-		flash_area = fwu_go_nogo();
+		flash_area = fwu_go_nogo(&header);
 
 	switch (flash_area) {
 	case NONE:
@@ -1318,6 +1407,8 @@
 
 	pr_notice("%s: End of reflash process\n", __func__);
 exit:
+	kfree(fwu->firmware_name);
+memory_exit:
 	return retval;
 }