lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID:
 <TY0PR06MB5611D36BFF6DD29506A807EC9E3E2@TY0PR06MB5611.apcprd06.prod.outlook.com>
Date: Tue,  2 Apr 2024 18:49:30 +0800
From: Allen_Lin <allencl_lin@...mail.com>
To: dmitry.torokhov@...il.com,
	robh@...nel.org,
	krzysztof.kozlowski+dt@...aro.org,
	conor@...nel.org,
	jikos@...nel.org,
	benjamin.tissoires@...hat.com,
	linux-input@...r.kernel.org,
	devicetree@...r.kernel.org,
	linux-kernel@...r.kernel.org
Cc: Allen_Lin <allencl_lin@...mail.com>
Subject: [PATCH v2 4/4] HID: Load firmware directly from file to IC

For HX83102J, often shipped without flash for costdown purpose.
This patch implement the function to load firmware file from
user-space, write into IC sram and make it running.

The procedure as following:
1. Check OF to get FW PID specified: himax_parse_dt
2. Load PID specified FW: i_get_FW, upload process use workqueue
   himax_boot_upgrade
3. Write FW into IC sram: i_update_FW
3-1. FW file contain a mapping table to indicate various section,
     such code, data, version info and HID info
3-2. Call update process himax_mcu_firmware_update_0f, which stop
     the internal MCU: i. system reset. ii. hx83102j_sense_off
3-3. Parsing FW and upload file: himax_zf_part_info. info[0] contain
     the code section and will upload to ISRAM. Other info[x] are
     data sections will be combined and upload to DSRAM separately.
     After upload FW, driver will use crc to check integrity:
     himax_sram_write_crc_check for code(HW CRC result must be 0),
     himax_mcu_calculate_crc_with_ap for pre-calculate data CRC[1],
     Upload data section by himax_mcu_register_write,
     himax_mcu_check_crc use HW calculate CRC and compared with [1]
4. Disable HW reload FW from flash function before MCU start running:
   himax_disable_fw_reload
5. Start running the FW: himax_mcu_power_on_init
5-1. Setting varies FW settings before FW start running
5-2. Start FW: hx83102j_sense_on
5-3. Wait until FW initial process complete: Read 0x72C0 from IC
6. Read HID info from FW
7. Read TP properties from IC: himax_mcu_tp_info_check
8. Read FW information fromIC: himax_mcu_read_FW_ver
9. Release FW, IC initial complete

The resume procedure do part of boot process, it also need to reload
FW from user-space into IC sram: himax_0f_op_file_dirly
1. Request FW
2. Upload FW: himax_mcu_firmware_update_0f
3. Disable HW reload FW from flash function: himax_disable_fw_reload
4. Start running the FW: himax_mcu_power_on_init

Signed-off-by: Allen_Lin <allencl_lin@...mail.com>
---
 drivers/hid/hid-himax-83102j.c | 1515 +++++++++++++++++++++++++++-----
 drivers/hid/hid-himax-83102j.h |  168 +++-
 2 files changed, 1483 insertions(+), 200 deletions(-)

diff --git a/drivers/hid/hid-himax-83102j.c b/drivers/hid/hid-himax-83102j.c
index 0a2be071a6c4..5cae979a3052 100644
--- a/drivers/hid/hid-himax-83102j.c
+++ b/drivers/hid/hid-himax-83102j.c
@@ -532,6 +532,92 @@ static void himax_mcu_ic_reset(struct himax_ts_data *ts, bool int_off)
 		himax_int_enable(ts, true);
 }
 
+/**
+ * hx83102j_reload_to_active() - Reload to active mode
+ * @ts: Himax touch screen data
+ *
+ * This function is used to write a flag to the IC register to make MCU restart without
+ * reload the firmware.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int hx83102j_reload_to_active(struct himax_ts_data *ts)
+{
+	int ret;
+	u32 retry_cnt;
+	const u32 addr = HIMAX_REG_ADDR_RELOAD_TO_ACTIVE;
+	const u32 reload_to_active_cmd = 0xec;
+	const u32 reload_to_active_done = 0x01ec;
+	const u32 retry_limit = 5;
+	union himax_dword_data data;
+
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		data.dword = cpu_to_le32(reload_to_active_cmd);
+		ret = himax_mcu_register_write(ts, addr, data.byte, 4);
+		if (ret < 0)
+			return ret;
+		usleep_range(1000, 1100);
+		ret = himax_mcu_register_read(ts, addr, data.byte, 4);
+		if (ret < 0)
+			return ret;
+		data.dword = le32_to_cpu(data.dword);
+		if (data.word[0] == reload_to_active_done)
+			break;
+	}
+
+	if (data.word[0] != reload_to_active_done) {
+		dev_err(ts->dev, "%s: Reload to active failed!\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * hx83102j_en_hw_crc() - Enable/Disable HW CRC
+ * @ts: Himax touch screen data
+ * @en: true for enable, false for disable
+ *
+ * This function is used to enable or disable the HW CRC. The HW CRC
+ * is used to protect the SRAM data.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int hx83102j_en_hw_crc(struct himax_ts_data *ts, bool en)
+{
+	int ret;
+	u32 retry_cnt;
+	const u32 addr = HIMAX_HX83102J_REG_ADDR_HW_CRC;
+	const u32 retry_limit = 5;
+	union himax_dword_data data, wrt_data;
+
+	if (en)
+		data.dword = cpu_to_le32(HIMAX_HX83102J_REG_DATA_HW_CRC);
+	else
+		data.dword = cpu_to_le32(HIMAX_HX83102J_REG_DATA_HW_CRC_DISABLE);
+
+	wrt_data.dword = data.dword;
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		ret = himax_mcu_register_write(ts, addr, data.byte, 4);
+		if (ret < 0)
+			return ret;
+		usleep_range(1000, 1100);
+		ret = himax_mcu_register_read(ts, addr, data.byte, 4);
+		if (ret < 0)
+			return ret;
+
+		if (data.word[0] == wrt_data.word[0])
+			break;
+	}
+
+	if (data.word[0] != wrt_data.word[0]) {
+		dev_err(ts->dev, "%s: ECC fail!\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /**
  * hx83102j_sense_off() - Stop MCU and enter safe mode
  * @ts: Himax touch screen data
@@ -641,6 +727,56 @@ static int hx83102j_sense_off(struct himax_ts_data *ts, bool check_en)
 	return -EIO;
 }
 
+/**
+ * hx83102j_sense_on() - Sense on the touch chip
+ * @ts: Himax touch screen data
+ * @sw_reset: true for software reset, false for hardware reset
+ *
+ * This function is used to sense on the touch chip, which means to start running the
+ * FW. The process begin with wakeup the IC bus interface, then write a flag to the IC
+ * register to make MCU restart running the FW. When sw_reset is true, the function will
+ * send a command to the IC to leave safe mode. Otherwise, the function will call
+ * himax_mcu_ic_reset() to reset the touch chip by hardware pin.
+ * Then enable the HW CRC to protect sram data, and reload to active to make the MCU
+ * start running without reload the firmware.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int hx83102j_sense_on(struct himax_ts_data *ts, bool sw_reset)
+{
+	int ret;
+	const union himax_dword_data re_init = {
+		.dword = cpu_to_le32(HIMAX_REG_DATA_FW_RE_INIT)
+	};
+	union himax_dword_data data;
+
+	dev_info(ts->dev, "%s: software reset %s\n", __func__, sw_reset ? "true" : "false");
+	ret = himax_mcu_interface_on(ts);
+	if (ret < 0)
+		return ret;
+
+	ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_CTRL_FW, re_init.byte, 4);
+	if (ret < 0)
+		return ret;
+	usleep_range(10000, 11000);
+	if (!sw_reset) {
+		himax_mcu_ic_reset(ts, false);
+	} else {
+		data.word[0] = cpu_to_le16(HIMAX_AHB_CMD_LEAVE_SAFE_MODE);
+		ret = himax_write(ts, HIMAX_AHB_ADDR_PSW_LB, NULL, data.byte, 2);
+		if (ret < 0)
+			return ret;
+	}
+	ret = hx83102j_en_hw_crc(ts, true);
+	if (ret < 0)
+		return ret;
+	ret = hx83102j_reload_to_active(ts);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 /**
  * hx83102j_chip_detect() - Check if the touch chip is HX83102J
  * @ts: Himax touch screen data
@@ -783,166 +919,980 @@ static int hx83102j_read_event_stack(struct himax_ts_data *ts, u8 *buf, u32 leng
 	int ret;
 	const u32 max_trunk_sz = ts->spi_xfer_max_sz - HIMAX_BUS_R_HLEN;
 
-	for (i = 0; i < length; i += max_trunk_sz) {
-		ret = himax_read(ts, HIMAX_AHB_ADDR_EVENT_STACK, buf + i,
-				 min(length - i, max_trunk_sz));
+	for (i = 0; i < length; i += max_trunk_sz) {
+		ret = himax_read(ts, HIMAX_AHB_ADDR_EVENT_STACK, buf + i,
+				 min(length - i, max_trunk_sz));
+		if (ret) {
+			dev_err(ts->dev, "%s: read event stack error!\n", __func__);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * hx83102j_chip_init_data() - Initialize the touch chip data
+ * @ts: Himax touch screen data
+ *
+ * This function is used to initialize hx83102j touch specific data in himax_ts_data.
+ * The chip_max_dsram_size is the maximum size of the DSRAM of hx83102j.
+ *
+ * Return: None
+ */
+static void hx83102j_chip_init_data(struct himax_ts_data *ts)
+{
+	ts->chip_max_dsram_size = HIMAX_HX83102J_DSRAM_SZ;
+}
+
+/**
+ * himax_touch_get() - Get touch data from touch chip
+ * @ts: Himax touch screen data
+ * @buf: Buffer to store the data
+ *
+ * This function is a wrapper to call hx83102j_read_event_stack() to read the touch
+ * data from the touch chip. The touch_data_sz is the size of the touch data to read,
+ * which is calculated by hid report descriptor provided by the firmware.
+ *
+ * Return: HIMAX_TS_SUCCESS on success, negative error code on failure. We categorize
+ * the error code into HIMAX_TS_GET_DATA_FAIL when the read fails, and HIMAX_TS_SUCCESS
+ * when the read is successful. The reason is that the may need special handling when
+ * the read fails.
+ */
+static int himax_touch_get(struct himax_ts_data *ts, u8 *buf)
+{
+	if (hx83102j_read_event_stack(ts, buf, ts->touch_data_sz)) {
+		dev_err(ts->dev, "can't read data from chip!");
+		return HIMAX_TS_GET_DATA_FAIL;
+	}
+
+	return HIMAX_TS_SUCCESS;
+}
+
+/**
+ * himax_mcu_assign_sorting_mode() - Write sorting mode to dsram and verify
+ * @ts: Himax touch screen data
+ * @tmp_data_in: password to write
+ *
+ * This function is used to write the sorting mode password to dsram and verify the
+ * password is written correctly. The sorting mode password is used as a flag to
+ * FW to let it know which mode the touch chip is working on.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_mcu_assign_sorting_mode(struct himax_ts_data *ts, u8 *tmp_data_in)
+{
+	int ret;
+	u8 rdata[4];
+	u32 retry_cnt;
+	const u32 retry_limit = 3;
+
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_SORTING_MODE_EN,
+					       tmp_data_in, HIMAX_REG_SZ);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: write sorting mode fail\n", __func__);
+			return ret;
+		}
+		usleep_range(1000, 1100);
+		ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_SORTING_MODE_EN,
+					      rdata, HIMAX_REG_SZ);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: read sorting mode fail\n", __func__);
+			return ret;
+		}
+
+		if (!memcmp(tmp_data_in, rdata, HIMAX_REG_SZ))
+			return 0;
+	}
+	dev_err(ts->dev, "%s: fail to write sorting mode\n", __func__);
+
+	return -EINVAL;
+}
+
+/**
+ * himax_mcu_read_FW_status() - Read FW status from touch chip
+ * @ts: Himax touch screen data
+ *
+ * This function is used to read the FW status from the touch chip. The FW status is
+ * values from dsram and register from TPIC. Which shows the FW vital working status
+ * for developer debug.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_mcu_read_FW_status(struct himax_ts_data *ts)
+{
+	int i;
+	int ret;
+	size_t len;
+	u8 data[4];
+	const char * const reg_name[] = {
+		"DBG_MSG",
+		"FW_STATUS",
+		"DD_STATUS",
+		"RESET_FLAG"
+	};
+	const u32 dbg_reg_array[] = {
+		HIMAX_DSRAM_ADDR_DBG_MSG,
+		HIMAX_REG_ADDR_FW_STATUS,
+		HIMAX_REG_ADDR_DD_STATUS,
+		HIMAX_REG_ADDR_RESET_FLAG
+	};
+
+	len = ARRAY_SIZE(dbg_reg_array);
+
+	for (i = 0; i < len; i++) {
+		ret = himax_mcu_register_read(ts, dbg_reg_array[i], data, HIMAX_REG_SZ);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: read FW status fail\n", __func__);
+			return ret;
+		}
+
+		dev_info(ts->dev, "%s: %10s(0x%08X) = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+			 __func__, reg_name[i], dbg_reg_array[i],
+			 data[0], data[1], data[2], data[3]);
+	}
+
+	return 0;
+}
+
+/**
+ * himax_mcu_power_on_init() - Power on initialization
+ * @ts: Himax touch screen data
+ *
+ * This function is used to do the power on initialization after firmware has been
+ * loaded to sram. The process initialize varies IC register and dsram to make sure
+ * FW start running correctly. When all set, sense on the touch chip to make the FW
+ * start running and wait for the FW reload done password.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_mcu_power_on_init(struct himax_ts_data *ts)
+{
+	int ret;
+	u32 retry_cnt;
+	const u32 retry_limit = 30;
+	union himax_dword_data data;
+
+	/* RawOut select initial */
+	data.dword = cpu_to_le32(HIMAX_DATA_CLEAR);
+	ret = himax_mcu_register_write(ts, HIMAX_HX83102J_DSRAM_ADDR_RAW_OUT_SEL, data.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: set RawOut select fail\n", __func__);
+		return ret;
+	}
+	/* Initial sorting mode password to normal mode */
+	ret = himax_mcu_assign_sorting_mode(ts, data.byte);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: assign sorting mode fail\n", __func__);
+		return ret;
+	}
+	/* N frame initial */
+	/* reset N frame back to default value 1 for normal mode */
+	data.dword = cpu_to_le32(1);
+	ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_SET_NFRAME, data.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: set N frame fail\n", __func__);
+		return ret;
+	}
+	/* Initial FW reload status */
+	data.dword = cpu_to_le32(HIMAX_DATA_CLEAR);
+	ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_2ND_FLASH_RELOAD, data.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: initial FW reload status fail\n", __func__);
+		return ret;
+	}
+
+	ret = hx83102j_sense_on(ts, false);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: sense on fail\n", __func__);
+		return ret;
+	}
+
+	dev_info(ts->dev, "%s: waiting for FW reload data\n", __func__);
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_2ND_FLASH_RELOAD, data.byte, 4);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: read FW reload status fail\n", __func__);
+			return ret;
+		}
+
+		/* use all 4 bytes to compare */
+		if (le32_to_cpu(data.dword) == HIMAX_DSRAM_DATA_FW_RELOAD_DONE) {
+			dev_info(ts->dev, "%s: FW reload done\n", __func__);
+			break;
+		}
+		dev_info(ts->dev, "%s: wait FW reload %u times\n", __func__, retry_cnt);
+		ret = himax_mcu_read_FW_status(ts);
+		if (ret < 0)
+			dev_err(ts->dev, "%s: read FW status fail\n", __func__);
+
+		usleep_range(10000, 11000);
+	}
+
+	if (retry_cnt == retry_limit) {
+		dev_err(ts->dev, "%s: FW reload fail!\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * himax_mcu_calculate_crc() - Calculate CRC-32 of given data
+ * @data: Data to calculate CRC
+ * @len: Length of data
+ *
+ * This function is used to calculate the CRC-32 of the given data. The function
+ * calculate the CRC-32 value by the polynomial 0x82f63b78.
+ *
+ * Return: CRC-32 value
+ */
+static u32 himax_mcu_calculate_crc(const u8 *data, int len)
+{
+	int i, j, length;
+	u32 crc = GENMASK(31, 0);
+	u32 current_data;
+	u32 tmp;
+	const u32 mask = GENMASK(30, 0);
+
+	length = len / 4;
+
+	for (i = 0; i < length; i++) {
+		current_data = data[i * 4];
+
+		for (j = 1; j < 4; j++) {
+			tmp = data[i * 4 + j];
+			current_data += (tmp) << (8 * j);
+		}
+		crc = current_data ^ crc;
+		for (j = 0; j < 32; j++) {
+			if ((crc % 2) != 0)
+				crc = ((crc >> 1) & mask) ^ CRC32C_POLY_LE;
+			else
+				crc = (((crc >> 1) & mask));
+		}
+	}
+
+	return crc;
+}
+
+/**
+ * himax_mcu_check_crc() - Let TPIC check CRC itself
+ * @ts: Himax touch screen data
+ * @start_addr: Start address of the data in sram to check
+ * @reload_length: Length of the data to check
+ * @crc_result: CRC result for return
+ *
+ * This function is used to let TPIC check the CRC of the given data in sram. The
+ * function write the start address and length of the data to the TPIC, and wait for
+ * the TPIC to finish the CRC check. When the CRC check is done, the function read
+ * the CRC result from the TPIC.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_mcu_check_crc(struct himax_ts_data *ts, u32 start_addr,
+			       int reload_length, u32 *crc_result)
+{
+	int ret;
+	int length = reload_length / HIMAX_REG_SZ;
+	u32 retry_cnt;
+	const u32 retry_limit = 100;
+	union himax_dword_data data, addr;
+
+	addr.dword = cpu_to_le32(start_addr);
+	ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_RELOAD_ADDR_FROM, addr.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: write reload start address fail\n", __func__);
+		return ret;
+	}
+
+	data.word[1] = cpu_to_le16(HIMAX_REG_DATA_RELOAD_PASSWORD);
+	data.word[0] = cpu_to_le16(length);
+	ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_RELOAD_ADDR_CMD_BEAT, data.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: write reload length and password fail!\n", __func__);
+		return ret;
+	}
+
+	ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RELOAD_ADDR_CMD_BEAT, data.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read reload length and password fail!\n", __func__);
+		return ret;
+	}
+
+	if (le16_to_cpu(data.word[0]) != length) {
+		dev_err(ts->dev, "%s: length verify failed!\n", __func__);
+		return -EINVAL;
+	}
+
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RELOAD_STATUS, data.byte, 4);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: read reload status fail!\n", __func__);
+			return ret;
+		}
+
+		data.dword = le32_to_cpu(data.dword);
+		if ((data.byte[0] & HIMAX_REG_DATA_RELOAD_DONE) != HIMAX_REG_DATA_RELOAD_DONE) {
+			ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RELOAD_CRC32_RESULT,
+						      data.byte, HIMAX_REG_SZ);
+			if (ret < 0) {
+				dev_err(ts->dev, "%s: read crc32 result fail!\n", __func__);
+				return ret;
+			}
+			*crc_result = le32_to_cpu(data.dword);
+			return 0;
+		}
+
+		dev_info(ts->dev, "%s: Waiting for HW ready!\n", __func__);
+		usleep_range(1000, 1100);
+	}
+
+	if (retry_cnt == retry_limit) {
+		ret = himax_mcu_read_FW_status(ts);
+		if (ret < 0)
+			dev_err(ts->dev, "%s: read FW status fail\n", __func__);
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * himax_mcu_read_FW_ver() - Read varies version from touch chip
+ * @ts: Himax touch screen data
+ *
+ * This function is used to read the firmware version, config version, touch config
+ * version, display config version, customer ID, customer info, and project info from
+ * the touch chip. The function will call himax_mcu_register_read() to read the data
+ * from the TPIC, and store the data to the IC data in himax_ts_data.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_mcu_read_FW_ver(struct himax_ts_data *ts)
+{
+	int ret;
+	u8 data[HIMAX_TP_INFO_STR_LEN];
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_FW_VER, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read FW version fail\n", __func__);
+		return ret;
+	}
+	ts->ic_data.vendor_panel_ver =  data[0];
+	ts->ic_data.vendor_fw_ver = data[1] << 8 | data[2];
+	dev_info(ts->dev, "%s: PANEL_VER: %X\n", __func__, ts->ic_data.vendor_panel_ver);
+	dev_info(ts->dev, "%s: FW_VER: %X\n", __func__, ts->ic_data.vendor_fw_ver);
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_CFG, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read CFG version fail\n", __func__);
+		return ret;
+	}
+	ts->ic_data.vendor_config_ver = data[2] << 8 | data[3];
+	ts->ic_data.vendor_touch_cfg_ver = data[2];
+	dev_info(ts->dev, "%s: TOUCH_VER: %X\n", __func__, ts->ic_data.vendor_touch_cfg_ver);
+	ts->ic_data.vendor_display_cfg_ver = data[3];
+	dev_info(ts->dev, "%s: DISPLAY_VER: %X\n", __func__, ts->ic_data.vendor_display_cfg_ver);
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_VENDOR, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read customer ID fail\n", __func__);
+		return ret;
+	}
+	ts->ic_data.vendor_cid_maj_ver = data[2];
+	ts->ic_data.vendor_cid_min_ver = data[3];
+	dev_info(ts->dev, "%s: CID_VER: %X\n", __func__, (ts->ic_data.vendor_cid_maj_ver << 8
+		 | ts->ic_data.vendor_cid_min_ver));
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_CUS_INFO, data, HIMAX_TP_INFO_STR_LEN);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read customer info fail\n", __func__);
+		return ret;
+	}
+	memcpy(ts->ic_data.vendor_cus_info, data, HIMAX_TP_INFO_STR_LEN);
+	dev_info(ts->dev, "%s: Cusomer ID : %s\n", __func__, ts->ic_data.vendor_cus_info);
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_PROJ_INFO, data, HIMAX_TP_INFO_STR_LEN);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read project info fail\n", __func__);
+		return ret;
+	}
+	memcpy(ts->ic_data.vendor_proj_info, data, HIMAX_TP_INFO_STR_LEN);
+	dev_info(ts->dev, "%s: Project ID : %s\n", __func__, ts->ic_data.vendor_proj_info);
+
+	return 0;
+}
+
+/**
+ * himax_bin_desc_data_get() - Parse descriptor data from firmware token
+ * @ts: Himax touch screen data
+ * @addr: Address of the data in firmware image
+ * @descript_buf: token for parsing
+ *
+ * This function is used to parse the descriptor data from the firmware token. The
+ * descriptors are mappings of information in the firmware image. The function will
+ * check checksum of each token first, and then parse the token to get the related
+ * data. The data includes CID version, FW version, CFG version, touch config table,
+ * HID table, HID descriptor, and HID read descriptor.
+ *
+ * Return: true on success, false on failure
+ */
+static bool himax_bin_desc_data_get(struct himax_ts_data *ts, u32 addr, u8 *descript_buf)
+{
+	u16 chk_end;
+	u16 chk_sum;
+	u32 hid_table_addr;
+	u32 i, j;
+	u32 image_offset;
+	u32 map_code;
+	const u32 data_sz = 16;
+	const u32 report_desc_offset = 24;
+	union {
+		u8 *buf;
+		u32 *word;
+	} map_data;
+
+	/* looking for mapping in page, each mapping is 16 bytes */
+	for (i = 0; i < HIMAX_HX83102J_PAGE_SIZE; i = i + data_sz) {
+		chk_end = 0;
+		chk_sum = 0;
+		for (j = i; j < (i + data_sz); j++) {
+			chk_end |= descript_buf[j];
+			chk_sum += descript_buf[j];
+		}
+		if (!chk_end) { /* 1. Check all zero */
+			return false;
+		} else if (chk_sum % 0x100) { /* 2. Check sum */
+			dev_warn(ts->dev, "%s: chk sum failed in %X\n", __func__, i + addr);
+		} else { /* 3. get data */
+			map_data.buf = &descript_buf[i];
+			map_code = le32_to_cpup(map_data.word);
+			map_data.buf = &descript_buf[i + 4];
+			image_offset = le32_to_cpup(map_data.word);
+			/* 4. load info from FW image by specified mapping offset */
+			switch (map_code) {
+			/* Config ID */
+			case HIMAX_FW_CID:
+				ts->fw_info_table.addr_cid_ver_major = image_offset;
+				ts->fw_info_table.addr_cid_ver_minor = image_offset + 1;
+				break;
+			/* FW version */
+			case HIMAX_FW_VER:
+				ts->fw_info_table.addr_fw_ver_major = image_offset;
+				ts->fw_info_table.addr_fw_ver_minor = image_offset + 1;
+				break;
+			/* Config version */
+			case HIMAX_CFG_VER:
+				ts->fw_info_table.addr_cfg_ver_major = image_offset;
+				ts->fw_info_table.addr_cfg_ver_minor = image_offset + 1;
+				break;
+			/* Touch config table */
+			case HIMAX_TP_CONFIG_TABLE:
+				ts->fw_info_table.addr_cfg_table = image_offset;
+				break;
+			/* HID table */
+			case HIMAX_HID_TABLE:
+				ts->fw_info_table.addr_hid_table = image_offset;
+				hid_table_addr = image_offset;
+				ts->fw_info_table.addr_hid_desc = hid_table_addr;
+				ts->fw_info_table.addr_hid_rd_desc =
+					hid_table_addr + report_desc_offset;
+				break;
+			}
+		}
+	}
+
+	return true;
+}
+
+/**
+ * himax_mcu_bin_desc_get() - Check and get the bin description from the data
+ * @fw: Firmware data
+ * @ts: Himax touch screen data
+ * @max_sz: Maximum size to check
+ *
+ * This function is used to check and get the bin description from the firmware data.
+ * It will check the given data to see if it match the bin description format, and
+ * call himax_bin_desc_data_get() to get the related data.
+ *
+ * Return: true on mapping_count > 0, false on otherwise
+ */
+static bool himax_mcu_bin_desc_get(unsigned char *fw, struct himax_ts_data *ts, u32 max_sz)
+{
+	bool keep_on_flag;
+	u32 addr;
+	u32 mapping_count;
+	unsigned char *fw_buf;
+	const u8 header_id = 0x87;
+	const u8 header_id_loc = 0x0e;
+	const u8 header_sz = 8;
+	const u8 header[8] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+	/* Check bin is with description table or not */
+	if (!(memcmp(fw, header, header_sz) == 0 && fw[header_id_loc] == header_id)) {
+		dev_err(ts->dev, "%s: No description table\n", __func__);
+		return false;
+	}
+
+	for (addr = 0, mapping_count = 0; addr < max_sz; addr += HIMAX_HX83102J_PAGE_SIZE) {
+		fw_buf = &fw[addr];
+		/* Get related data */
+		keep_on_flag = himax_bin_desc_data_get(ts, addr, fw_buf);
+		if (keep_on_flag)
+			mapping_count++;
+		else
+			break;
+	}
+
+	return mapping_count > 0;
+}
+
+/**
+ * himax_mcu_tp_info_check() - Read touch information from touch chip
+ * @ts: Himax touch screen data
+ *
+ * This function is used to read the touch information from the touch chip. The
+ * information includes the touch resolution, touch point number, interrupt type,
+ * button number, stylus function, stylus version, and stylus ratio. These information
+ * is filled by FW after the FW initialized, so it must be called after FW finish
+ * loading.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_mcu_tp_info_check(struct himax_ts_data *ts)
+{
+	int ret;
+	char data[HIMAX_REG_SZ];
+	u8 stylus_ratio;
+	u32 button_num;
+	u32 max_pt;
+	u32 rx_num;
+	u32 tx_num;
+	u32 x_res;
+	u32 y_res;
+	const u32 button_num_mask = 0x03;
+	const u32 interrupt_type_mask = 0x01;
+	const u32 interrupt_type_edge = 0x01;
+	bool int_is_edge;
+	bool stylus_func;
+	bool stylus_id_v2;
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_RXNUM_TXNUM, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read rx/tx num fail\n", __func__);
+		return ret;
+	}
+	rx_num = data[2];
+	tx_num = data[3];
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_MAXPT_XYRVS, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read max touch point fail\n", __func__);
+		return ret;
+	}
+	max_pt = data[0];
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_X_Y_RES, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read x/y resolution fail\n", __func__);
+		return ret;
+	}
+	y_res = be16_to_cpup((u16 *)&data[0]);
+	x_res = be16_to_cpup((u16 *)&data[2]);
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_INT_IS_EDGE, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read interrupt type fail\n", __func__);
+		return ret;
+	}
+	if ((data[1] & interrupt_type_mask) == interrupt_type_edge)
+		int_is_edge = true;
+	else
+		int_is_edge = false;
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_MKEY, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read button number fail\n", __func__);
+		return ret;
+	}
+	button_num = data[0] & button_num_mask;
+
+	ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_FUNCTION, data, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: read stylus function fail\n", __func__);
+		return ret;
+	}
+	stylus_func = data[3] ? true : false;
+
+	if (stylus_func) {
+		ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_VERSION, data, 4);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: read stylus version fail\n", __func__);
+			return ret;
+		}
+		/* dsram_addr_stylus_version + 2 : 0=off 1=on */
+		stylus_id_v2 = data[2] ? true : false;
+		/* dsram_addr_stylus_version + 3 : 0=ratio_1 10=ratio_10 */
+		stylus_ratio = data[3];
+	}
+
+	ts->ic_data.button_num = button_num;
+	ts->ic_data.stylus_function = stylus_func;
+	ts->ic_data.rx_num = rx_num;
+	ts->ic_data.tx_num = tx_num;
+	ts->ic_data.x_res = x_res;
+	ts->ic_data.y_res = y_res;
+	ts->ic_data.max_point = max_pt;
+	ts->ic_data.interrupt_is_edge = int_is_edge;
+	if (stylus_func) {
+		ts->ic_data.stylus_v2 = stylus_id_v2;
+		ts->ic_data.stylus_ratio = stylus_ratio;
+	} else {
+		ts->ic_data.stylus_v2 = false;
+		ts->ic_data.stylus_ratio = 0;
+	}
+
+	dev_info(ts->dev, "%s: rx_num = %u, tx_num = %u\n", __func__,
+		 ts->ic_data.rx_num, ts->ic_data.tx_num);
+	dev_info(ts->dev, "%s: max_point = %u\n", __func__, ts->ic_data.max_point);
+	dev_info(ts->dev, "%s: interrupt_is_edge = %s, stylus_function = %s\n", __func__,
+		 ts->ic_data.interrupt_is_edge ? "true" : "false",
+		 ts->ic_data.stylus_function ? "true" : "false");
+	dev_info(ts->dev, "%s: stylus_v2 = %s, stylus_ratio = %u\n", __func__,
+		 ts->ic_data.stylus_v2 ? "true" : "false", ts->ic_data.stylus_ratio);
+	dev_info(ts->dev, "%s: TOUCH INFO updated\n", __func__);
+
+	return 0;
+}
+
+/**
+ * himax_disable_fw_reload() - Disable the FW reload data from flash
+ * @ts: Himax touch screen data
+ *
+ * This function is used to tell FW not to reload data from flash. It needs to be
+ * set before FW start running.
+ *
+ * return: 0 on success, negative error code on failure
+ */
+static int himax_disable_fw_reload(struct himax_ts_data *ts)
+{
+	union himax_dword_data data = {
+		/*
+		 * HIMAX_DSRAM_ADDR_FLASH_RELOAD: 0x10007f00
+		 * 0x10007f00 <= 0x9aa9, let FW know there's no flash
+		 *	      <= 0x5aa5, there has flash, but not reload
+		 *	      <= 0x0000, there has flash, and reload
+		 */
+		.dword = cpu_to_le32(HIMAX_DSRAM_DATA_DISABLE_FLASH_RELOAD)
+	};
+
+	/* Disable Flash Reload */
+	return himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_FLASH_RELOAD, data.byte, 4);
+}
+
+/**
+ * himax_sram_write_crc_check() - Write the data to SRAM and check the CRC by hardware
+ * @ts: Himax touch screen data
+ * @addr: Address to write to
+ * @data: Data to write
+ * @len: Length of data
+ *
+ * This function is use to write FW code/data to SRAM and check the CRC by hardware to make
+ * sure the written data is correct. The FW code is designed to be CRC result 0, so if the
+ * CRC result is not 0, it means the written data is not correct.
+ *
+ * return: 0 on success, negative error code on failure
+ */
+static int himax_sram_write_crc_check(struct himax_ts_data *ts, u32 addr, const u8 *data, u32 len)
+{
+	int ret;
+	u32 crc;
+	u32 retry_cnt;
+	const u32 retry_limit = 3;
+
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		dev_info(ts->dev, "%s: Write FW to SRAM - total write size = %u\n", __func__, len);
+		ret = himax_mcu_register_write(ts, addr, data, len);
+		if (ret) {
+			dev_err(ts->dev, "%s: write FW to SRAM fail\n", __func__);
+			return ret;
+		}
+		ret = himax_mcu_check_crc(ts, addr, len, &crc);
+		if (ret) {
+			dev_err(ts->dev, "%s: check CRC fail\n", __func__);
+			return ret;
+		}
+		dev_info(ts->dev, "%s: HW CRC %s in %u time\n", __func__,
+			 crc == 0 ? "OK" : "Fail", retry_cnt);
+
+		if (crc == 0)
+			break;
+	}
+
+	if (crc != 0) {
+		dev_err(ts->dev, "%s: HW CRC fail\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * himax_zf_part_info() - Get and write the partition from the firmware to SRAM
+ * @fw: Firmware data
+ * @ts: Himax touch screen data
+ *
+ * This function is used to get the partition information from the firmware and write
+ * the partition to SRAM. The partition information includes the DSRAM address, the
+ * firmware offset, and the write size. The function will get the partition information
+ * into a table, and then write the partition to SRAM according to the table. After
+ * writing the partition to SRAM, the function will check the CRC by hardware to make
+ * sure the written data is correct.
+ *
+ * return: 0 on success, negative error code on failure
+ */
+static int himax_zf_part_info(const struct firmware *fw, struct himax_ts_data *ts)
+{
+	int i;
+	int i_max = -1;
+	int i_min = -1;
+	int pnum;
+	int ret;
+	u8 buf[HIMAX_ZF_PARTITION_DESC_SZ];
+	u32 cfg_crc_sw;
+	u32 cfg_crc_hw;
+	u32 cfg_sz;
+	u32 dsram_base = 0xffffffff;
+	u32 dsram_max = 0;
+	u32 retry_cnt = 0;
+	u32 sram_min;
+	const u32 retry_limit = 3;
+	const u32 table_addr = ts->fw_info_table.addr_cfg_table;
+	struct himax_zf_info *info;
+
+	/* 1. initial check */
+	ret = hx83102j_en_hw_crc(ts, true);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: Failed to enable HW CRC\n", __func__);
+		return ret;
+	}
+	pnum = fw->data[table_addr + HIMAX_ZF_PARTITION_AMOUNT_OFFSET];
+	if (pnum < 2) {
+		dev_err(ts->dev, "%s: partition number is not correct\n", __func__);
+		return -EINVAL;
+	}
+
+	info = kcalloc(pnum, sizeof(struct himax_zf_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	memset(info, 0, pnum * sizeof(struct himax_zf_info));
+
+	/*
+	 * 2. record partition information:
+	 * partition 0: FW main code
+	 */
+	memcpy(buf, &fw->data[table_addr], HIMAX_ZF_PARTITION_DESC_SZ);
+	memcpy(info[0].sram_addr, buf, 4);
+	info[0].write_size = le32_to_cpup((u32 *)&buf[4]);
+	info[0].fw_addr = le32_to_cpup((u32 *)&buf[8]);
+
+	/* partition 1 ~ n: config data */
+	for (i = 1; i < pnum; i++) {
+		memcpy(buf, &fw->data[i * HIMAX_ZF_PARTITION_DESC_SZ + table_addr],
+		       HIMAX_ZF_PARTITION_DESC_SZ);
+		memcpy(info[i].sram_addr, buf, 4);
+		info[i].write_size = le32_to_cpup((u32 *)&buf[4]);
+		info[i].fw_addr = le32_to_cpup((u32 *)&buf[8]);
+		info[i].cfg_addr = le32_to_cpup((u32 *)&info[i].sram_addr[0]);
+
+		/* Write address must be multiple of 4 */
+		if (info[i].cfg_addr % 4 != 0) {
+			info[i].cfg_addr -= (info[i].cfg_addr % 4);
+			info[i].fw_addr -= (info[i].cfg_addr % 4);
+			info[i].write_size += (info[i].cfg_addr % 4);
+		}
+
+		if (dsram_base > info[i].cfg_addr) {
+			dsram_base = info[i].cfg_addr;
+			i_min = i;
+		}
+		if (dsram_max < info[i].cfg_addr) {
+			dsram_max = info[i].cfg_addr;
+			i_max = i;
+		}
+	}
+
+	if (i_min < 0 || i_max < 0) {
+		dev_err(ts->dev, "%s: DSRAM address invalid!\n", __func__);
+		return -EINVAL;
+	}
+
+	/* 3. prepare data to update */
+	sram_min = info[i_min].cfg_addr;
+
+	cfg_sz = (dsram_max - dsram_base) + info[i_max].write_size;
+	/* Wrtie size must be multiple of 4 */
+	if (cfg_sz % 4 != 0)
+		cfg_sz = cfg_sz + 4 - (cfg_sz % 4);
+
+	dev_info(ts->dev, "%s: main code sz = %d, config sz = %d\n", __func__,
+		 info[0].write_size, cfg_sz);
+	/* config size should be smaller than DSRAM size */
+	if (cfg_sz > ts->chip_max_dsram_size) {
+		dev_err(ts->dev, "%s: config size error[%d, %u]!!\n", __func__,
+			cfg_sz, ts->chip_max_dsram_size);
+		ret = -EINVAL;
+		goto alloc_cfg_buffer_failed;
+	}
+
+	memset(ts->zf_update_cfg_buffer, 0x00,
+	       ts->chip_max_dsram_size * sizeof(u8));
+
+	/* Collect all partition in FW for DSRAM in a cfg buffer */
+	for (i = 1; i < pnum; i++)
+		memcpy(&ts->zf_update_cfg_buffer[info[i].cfg_addr - dsram_base],
+		       &fw->data[info[i].fw_addr], info[i].write_size);
+
+	/*
+	 * 4. write to sram
+	 * First, write FW main code and check CRC by HW
+	 */
+	ret = himax_sram_write_crc_check(ts, le32_to_cpup((u32 *)info[0].sram_addr),
+					 &fw->data[info[0].fw_addr], info[0].write_size);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: HW CRC fail\n", __func__);
+		goto write_main_code_failed;
+	}
+
+	/*
+	 * Second, FW config data: Calculate CRC of CFG data which is going to write.
+	 * CFG data don't have CRC pre-defined in FW and need to be calculated by driver.
+	 */
+	cfg_crc_sw = himax_mcu_calculate_crc(ts->zf_update_cfg_buffer, cfg_sz);
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		/* Write hole cfg data to DSRAM */
+		dev_info(ts->dev, "%s: Write cfg to SRAM - total write size = %d\n",
+			 __func__, cfg_sz);
+		ret = himax_mcu_register_write(ts, sram_min, ts->zf_update_cfg_buffer, cfg_sz);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: write cfg to SRAM fail\n", __func__);
+			goto write_cfg_failed;
+		}
+		/*
+		 * Check CRC: Tell HW to calculate CRC from CFG start address in SRAM and check
+		 * size is equal to size of CFG buffer written. Then we compare the two CRC data
+		 * make sure data written is correct.
+		 */
+		ret = himax_mcu_check_crc(ts, sram_min, cfg_sz, &cfg_crc_hw);
 		if (ret) {
-			dev_err(ts->dev, "%s: read event stack error!\n", __func__);
-			return ret;
+			dev_err(ts->dev, "%s: check CRC failed!\n", __func__);
+			goto crc_failed;
 		}
-	}
 
-	return 0;
-}
+		if (cfg_crc_hw != cfg_crc_sw)
+			dev_err(ts->dev, "%s: Cfg CRC FAIL, HWCRC = %X, SWCRC = %X, retry = %u\n",
+				__func__, cfg_crc_hw, cfg_crc_sw, retry_cnt);
+		else
+			break;
+	}
 
-/**
- * himax_touch_get() - Get touch data from touch chip
- * @ts: Himax touch screen data
- * @buf: Buffer to store the data
- *
- * This function is a wrapper to call hx83102j_read_event_stack() to read the touch
- * data from the touch chip. The touch_data_sz is the size of the touch data to read,
- * which is calculated by hid report descriptor provided by the firmware.
- *
- * Return: HIMAX_TS_SUCCESS on success, negative error code on failure. We categorize
- * the error code into HIMAX_TS_GET_DATA_FAIL when the read fails, and HIMAX_TS_SUCCESS
- * when the read is successful. The reason is that the may need special handling when
- * the read fails.
- */
-static int himax_touch_get(struct himax_ts_data *ts, u8 *buf)
-{
-	if (hx83102j_read_event_stack(ts, buf, ts->touch_data_sz)) {
-		dev_err(ts->dev, "can't read data from chip!");
-		return HIMAX_TS_GET_DATA_FAIL;
+	if (retry_cnt == retry_limit && cfg_crc_hw != cfg_crc_sw) {
+		dev_err(ts->dev, "%s: Write cfg to SRAM fail\n", __func__);
+		ret = -EINVAL;
+		goto crc_not_match;
 	}
 
-	return HIMAX_TS_SUCCESS;
+crc_not_match:
+crc_failed:
+write_cfg_failed:
+write_main_code_failed:
+alloc_cfg_buffer_failed:
+	kfree(info);
+
+	return ret;
 }
 
 /**
- * himax_bin_desc_data_get() - Parse descriptor data from firmware token
+ * himax_mcu_firmware_update_zf() - Update the firmware to the touch chip
+ * @fw: Firmware data
  * @ts: Himax touch screen data
- * @addr: Address of the data in firmware image
- * @descript_buf: token for parsing
  *
- * This function is used to parse the descriptor data from the firmware token. The
- * descriptors are mappings of information in the firmware image. The function will
- * check checksum of each token first, and then parse the token to get the related
- * data. The data includes CID version, FW version, CFG version, touch config table,
- * HID table, HID descriptor, and HID read descriptor.
+ * This function is used to update the firmware to the touch chip. The first step is
+ * to reset the touch chip, stop the MCU and then write the firmware to the touch chip.
  *
- * Return: true on success, false on failure
+ * return: 0 on success, negative error code on failure
  */
-static bool himax_bin_desc_data_get(struct himax_ts_data *ts, u32 addr, u8 *descript_buf)
+static int himax_mcu_firmware_update_zf(const struct firmware *fw, struct himax_ts_data *ts)
 {
-	u16 chk_end;
-	u16 chk_sum;
-	u32 hid_table_addr;
-	u32 i, j;
-	u32 image_offset;
-	u32 map_code;
-	const u32 data_sz = 16;
-	const u32 report_desc_offset = 24;
-	union {
-		u8 *buf;
-		u32 *word;
-	} map_data;
+	int ret;
+	union himax_dword_data data_system_reset = {
+		.dword = cpu_to_le32(HIMAX_REG_DATA_SYSTEM_RESET)
+	};
 
-	/* looking for mapping in page, each mapping is 16 bytes */
-	for (i = 0; i < HIMAX_HX83102J_PAGE_SIZE; i = i + data_sz) {
-		chk_end = 0;
-		chk_sum = 0;
-		for (j = i; j < (i + data_sz); j++) {
-			chk_end |= descript_buf[j];
-			chk_sum += descript_buf[j];
-		}
-		if (!chk_end) { /* 1. Check all zero */
-			return false;
-		} else if (chk_sum % 0x100) { /* 2. Check sum */
-			dev_warn(ts->dev, "%s: chk sum failed in %X\n", __func__, i + addr);
-		} else { /* 3. get data */
-			map_data.buf = &descript_buf[i];
-			map_code = le32_to_cpup(map_data.word);
-			map_data.buf = &descript_buf[i + 4];
-			image_offset = le32_to_cpup(map_data.word);
-			/* 4. load info from FW image by specified mapping offset */
-			switch (map_code) {
-			/* Config ID */
-			case HIMAX_FW_CID:
-				ts->fw_info_table.addr_cid_ver_major = image_offset;
-				ts->fw_info_table.addr_cid_ver_minor = image_offset + 1;
-				break;
-			/* FW version */
-			case HIMAX_FW_VER:
-				ts->fw_info_table.addr_fw_ver_major = image_offset;
-				ts->fw_info_table.addr_fw_ver_minor = image_offset + 1;
-				break;
-			/* Config version */
-			case HIMAX_CFG_VER:
-				ts->fw_info_table.addr_cfg_ver_major = image_offset;
-				ts->fw_info_table.addr_cfg_ver_minor = image_offset + 1;
-				break;
-			/* Touch config table */
-			case HIMAX_TP_CONFIG_TABLE:
-				ts->fw_info_table.addr_cfg_table = image_offset;
-				break;
-			/* HID table */
-			case HIMAX_HID_TABLE:
-				ts->fw_info_table.addr_hid_table = image_offset;
-				hid_table_addr = image_offset;
-				ts->fw_info_table.addr_hid_desc = hid_table_addr;
-				ts->fw_info_table.addr_hid_rd_desc =
-					hid_table_addr + report_desc_offset;
-				break;
-			}
-		}
+	dev_info(ts->dev, "%s: Updating FW - total FW size = %u\n", __func__, (u32)fw->size);
+	ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_SYSTEM_RESET, data_system_reset.byte, 4);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: system reset fail\n", __func__);
+		return ret;
 	}
 
-	return true;
+	ret = hx83102j_sense_off(ts, false);
+	if (ret)
+		return ret;
+
+	ret = himax_zf_part_info(fw, ts);
+
+	return ret;
 }
 
 /**
- * himax_mcu_bin_desc_get() - Check and get the bin description from the data
- * @fw: Firmware data
+ * himax_zf_reload_from_file() - Complete firmware update sequence
+ * @file_name: File name of the firmware
  * @ts: Himax touch screen data
- * @max_sz: Maximum size to check
  *
- * This function is used to check and get the bin description from the firmware data.
- * It will check the given data to see if it match the bin description format, and
- * call himax_bin_desc_data_get() to get the related data.
+ * This function process the full sequence of updating the firmware to the touch chip.
+ * It will first check if the other thread is updating now, if not, it will request the
+ * firmware from user space and then call himax_mcu_firmware_update_zf() to update the
+ * firmware, and then tell firmware not to reload data from flash and initial the touch
+ * chip by calling himax_mcu_power_on_init().
  *
- * Return: true on mapping_count > 0, false on otherwise
+ * return: 0 on success, negative error code on failure
  */
-static bool himax_mcu_bin_desc_get(unsigned char *fw, struct himax_ts_data *ts, u32 max_sz)
+static int himax_zf_reload_from_file(char *file_name, struct himax_ts_data *ts)
 {
-	bool keep_on_flag;
-	u32 addr;
-	u32 mapping_count;
-	unsigned char *fw_buf;
-	const u8 header_id = 0x87;
-	const u8 header_id_loc = 0x0e;
-	const u8 header_sz = 8;
-	const u8 header[8] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-	};
+	int ret;
+	const struct firmware *fw;
 
-	/* Check bin is with description table or not */
-	if (!(memcmp(fw, header, header_sz) == 0 && fw[header_id_loc] == header_id)) {
-		dev_err(ts->dev, "%s: No description table\n", __func__);
-		return false;
+	if (!mutex_trylock(&ts->zf_update_lock)) {
+		dev_warn(ts->dev, "%s: Other thread is updating now!\n", __func__);
+		return 0;
 	}
+	dev_info(ts->dev, "%s: Preparing to update %s!\n", __func__, file_name);
 
-	for (addr = 0, mapping_count = 0; addr < max_sz; addr += HIMAX_HX83102J_PAGE_SIZE) {
-		fw_buf = &fw[addr];
-		/* Get related data */
-		keep_on_flag = himax_bin_desc_data_get(ts, addr, fw_buf);
-		if (keep_on_flag)
-			mapping_count++;
-		else
-			break;
+	ret = request_firmware(&fw, file_name, ts->dev);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: request firmware fail, code[%d]!!\n", __func__, ret);
+		goto load_firmware_error;
 	}
 
-	return mapping_count > 0;
+	ret = himax_mcu_firmware_update_zf(fw, ts);
+	release_firmware(fw);
+	if (ret < 0)
+		goto load_firmware_error;
+
+	ret = himax_disable_fw_reload(ts);
+	if (ret < 0)
+		goto load_firmware_error;
+	ret = himax_mcu_power_on_init(ts);
+
+load_firmware_error:
+	mutex_unlock(&ts->zf_update_lock);
+
+	return ret;
 }
 
 /**
@@ -1238,6 +2188,7 @@ static int himax_ts_operation(struct himax_ts_data *ts)
  * This function is used to handle interrupt bottom half work. It will
  * call the himax_ts_operation() to get the touch data, dispatch the data
  * to HID core. If the touch data is not valid, it will reset the TPIC.
+ * It will also call the hx83102j_reload_to_active() after the reset action.
  *
  * Return: void
  */
@@ -1246,7 +2197,38 @@ static void himax_ts_work(struct himax_ts_data *ts)
 	if (himax_ts_operation(ts) == HIMAX_TS_GET_DATA_FAIL) {
 		dev_info(ts->dev, "%s: Now reset the Touch chip\n", __func__);
 		himax_mcu_ic_reset(ts, true);
+		if (hx83102j_reload_to_active(ts))
+			dev_warn(ts->dev, "%s: Reload to active failed\n", __func__);
+	}
+}
+
+/**
+ * himax_update_fw() - update firmware using firmware structure
+ * @ts: Himax touch screen data
+ *
+ * This function use already initialize firmware structure in ts to update
+ * firmware.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_update_fw(struct himax_ts_data *ts)
+{
+	int ret;
+	u32 retry_cnt;
+	const u32 retry_limit = 3;
+
+	for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
+		ret = himax_mcu_firmware_update_zf(ts->himax_fw, ts);
+		if (ret < 0) {
+			dev_err(ts->dev, "%s: TP upgrade error, upgrade_times = %d\n", __func__,
+				retry_cnt);
+		} else {
+			dev_info(ts->dev, "%s: TP FW upgrade OK\n", __func__);
+			return 0;
+		}
 	}
+
+	return -EIO;
 }
 
 /**
@@ -1279,9 +2261,8 @@ static int himax_hid_rd_init(struct himax_ts_data *ts)
 			if (!ts->hid_rd_data.rd_data)
 				return -ENOMEM;
 		}
-		/* Copy the base RD from firmware table */
 		memcpy((void *)ts->hid_rd_data.rd_data,
-		       &ts->himax_fw_data[ts->fw_info_table.addr_hid_rd_desc],
+		       &ts->himax_fw->data[ts->fw_info_table.addr_hid_rd_desc],
 		       ts->hid_desc.report_desc_length);
 		ts->hid_rd_data.rd_length = ts->hid_desc.report_desc_length;
 	}
@@ -1343,81 +2324,105 @@ static int himax_hid_report_data_init(struct himax_ts_data *ts)
 	return 0;
 }
 
-/* load firmware data from flash, parse HID info and register HID */
 /**
- * himax_load_config() - Load the firmware from the flash
- * @ts: Himax touch screen data
+ * himax_initial_work() - Initial work for the touch screen
+ * @work: Work structure
+ *
+ * This function is used to do the initial work for the touch screen. It will
+ * call the request_firmware() to get the firmware from the file system, and parse the
+ * mapping table in 1k header. If the headers are parsed successfully, it will
+ * call the himax_update_fw() to update the firmware and power on the touch screen.
+ * If the power on action is successful, it will load the hid descriptor and
+ * check the touch panel information. If the touch panel information is correct,
+ * it will call the himax_hid_rd_init() to initialize the HID report descriptor,
+ * and call the himax_hid_register() to register the HID device. After all is done,
+ * it will release the firmware and enable the interrupt.
  *
- * This function is used to load the firmware from the flash. It will read
- * the firmware from the flash and parse the HID info. If the HID info is
- * valid, it will initialize the HID report descriptor and register the HID
- * device. If the HID device is probed, it will initialize the report data
- * and enable the interrupt.
- *
- * Return: 0 on success, negative error code on failure
+ * Return: None
  */
-static int himax_load_config(struct himax_ts_data *ts)
+static void himax_initial_work(struct work_struct *work)
 {
+	struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
+						initial_work.work);
 	int ret;
-	s32 i;
-	s32 page_sz = (s32)HIMAX_HX83102J_PAGE_SIZE;
-	s32 flash_sz = (s32)HIMAX_HX83102J_FLASH_SIZE;
-	bool fw_load_status = false;
+	bool fw_load_status;
 	const u32 fw_bin_header_sz = 1024;
 
 	ts->ic_boot_done = false;
-
-	ts->himax_fw_data = devm_kzalloc(ts->dev, HIMAX_HX83102J_FLASH_SIZE, GFP_KERNEL);
-	if (!ts->himax_fw_data)
-		return -ENOMEM;
-
-	for (i = 0; i < flash_sz; i += page_sz) {
-		ret = himax_mcu_register_read(ts, i, ts->himax_fw_data + i,
-					      (flash_sz - i) > page_sz ? page_sz : (flash_sz - i));
-		if (ret < 0) {
-			dev_err(ts->dev, "%s: read FW from flash fail!\n", __func__);
-			return ret;
-		}
+	dev_info(ts->dev, "%s: request file %s\n", __func__, ts->firmware_name);
+	ret = request_firmware(&ts->himax_fw, ts->firmware_name, ts->dev);
+	if (ret < 0) {
+		dev_err(ts->dev, "%s: request firmware failed, error code = %d\n", __func__, ret);
+		return;
 	}
-	/* Search mapping table in 1k header */
-	fw_load_status = himax_mcu_bin_desc_get((unsigned char *)ts->himax_fw_data,
+	/* Parse the mapping table in 1k header */
+	fw_load_status = himax_mcu_bin_desc_get((unsigned char *)ts->himax_fw->data,
 						ts, fw_bin_header_sz);
 	if (!fw_load_status) {
-		dev_err(ts->dev, "%s: FW load status fail!\n", __func__);
-		return -EINVAL;
+		dev_err(ts->dev, "%s: Failed to parse the mapping table!\n", __func__);
+		goto err_load_bin_descriptor;
 	}
 
-	if (ts->fw_info_table.addr_hid_desc != 0) {
-		memcpy(&ts->hid_desc,
-		       &ts->himax_fw_data[ts->fw_info_table.addr_hid_desc],
-		       sizeof(struct himax_hid_desc));
-		ts->hid_desc.desc_length =
-			le16_to_cpu(ts->hid_desc.desc_length);
-		ts->hid_desc.bcd_version =
-			le16_to_cpu(ts->hid_desc.bcd_version);
-		ts->hid_desc.report_desc_length =
-			le16_to_cpu(ts->hid_desc.report_desc_length);
-		ts->hid_desc.max_input_length =
-			le16_to_cpu(ts->hid_desc.max_input_length);
-		ts->hid_desc.max_output_length =
-			le16_to_cpu(ts->hid_desc.max_output_length);
-		ts->hid_desc.max_fragment_length =
-			le16_to_cpu(ts->hid_desc.max_fragment_length);
-		ts->hid_desc.vendor_id =
-			le16_to_cpu(ts->hid_desc.vendor_id);
-		ts->hid_desc.product_id =
-			le16_to_cpu(ts->hid_desc.product_id);
-		ts->hid_desc.version_id =
-			le16_to_cpu(ts->hid_desc.version_id);
-		ts->hid_desc.flags =
-			le16_to_cpu(ts->hid_desc.flags);
+	if (himax_update_fw(ts)) {
+		dev_err(ts->dev, "%s: Update FW fail\n", __func__);
+		goto err_update_fw_failed;
 	}
 
+	dev_info(ts->dev, "%s: Update FW success\n", __func__);
+	/* write flag to sram to stop fw reload again. */
+	if (himax_disable_fw_reload(ts))
+		goto err_disable_fw_reload;
+	if (himax_mcu_power_on_init(ts))
+		goto err_power_on_init;
+	/* get hid descriptors */
+	if (!ts->fw_info_table.addr_hid_desc) {
+		dev_err(ts->dev, "%s: No HID descriptor! Wrong FW!\n", __func__);
+		goto err_wrong_firmware;
+	}
+	memcpy(&ts->hid_desc,
+	       &ts->himax_fw->data[ts->fw_info_table.addr_hid_desc],
+	       sizeof(struct himax_hid_desc));
+	ts->hid_desc.desc_length =
+		le16_to_cpu(ts->hid_desc.desc_length);
+	ts->hid_desc.bcd_version =
+		le16_to_cpu(ts->hid_desc.bcd_version);
+	ts->hid_desc.report_desc_length =
+		le16_to_cpu(ts->hid_desc.report_desc_length);
+	ts->hid_desc.max_input_length =
+		le16_to_cpu(ts->hid_desc.max_input_length);
+	ts->hid_desc.max_output_length =
+		le16_to_cpu(ts->hid_desc.max_output_length);
+	ts->hid_desc.max_fragment_length =
+		le16_to_cpu(ts->hid_desc.max_fragment_length);
+	ts->hid_desc.vendor_id =
+		le16_to_cpu(ts->hid_desc.vendor_id);
+	ts->hid_desc.product_id =
+		le16_to_cpu(ts->hid_desc.product_id);
+	ts->hid_desc.version_id =
+		le16_to_cpu(ts->hid_desc.version_id);
+	ts->hid_desc.flags =
+		le16_to_cpu(ts->hid_desc.flags);
+
+	if (himax_mcu_tp_info_check(ts))
+		goto err_tp_info_failed;
+	if (himax_mcu_read_FW_ver(ts))
+		goto err_read_fw_ver;
+	if (ts->pdata.pid) {
+		if (ts->pdata.pid != ts->hid_desc.product_id) {
+			dev_err(ts->dev, "%s: PID mismatch, dtsi PID = 0x%x, fw PID = 0x%x\n",
+				__func__, ts->pdata.pid, ts->hid_desc.product_id);
+			goto err_pid_match_failed;
+		} else {
+			dev_info(ts->dev, "%s: PID match, dtsi PID = 0x%x, fw PID = 0x%x\n",
+				 __func__, ts->pdata.pid, ts->hid_desc.product_id);
+		}
+	}
 	if (himax_hid_rd_init(ts)) {
 		dev_err(ts->dev, "%s: hid rd init fail\n", __func__);
 		goto err_hid_rd_init_failed;
 	}
 
+	usleep_range(1000000, 1000100);
 	himax_hid_register(ts);
 	if (!ts->hid_probed) {
 		goto err_hid_probe_failed;
@@ -1428,19 +2433,29 @@ static int himax_load_config(struct himax_ts_data *ts)
 		}
 	}
 
-	ts->himax_fw_data = NULL;
+	release_firmware(ts->himax_fw);
+	ts->himax_fw = NULL;
+
 	ts->ic_boot_done = true;
 	himax_int_enable(ts, true);
 
-	return 0;
+	return;
 
 err_report_data_init_failed:
 	himax_hid_remove(ts);
 	ts->hid_probed = false;
 err_hid_probe_failed:
 err_hid_rd_init_failed:
-
-	return -EINVAL;
+err_pid_match_failed:
+err_read_fw_ver:
+err_tp_info_failed:
+err_wrong_firmware:
+err_power_on_init:
+err_disable_fw_reload:
+err_update_fw_failed:
+err_load_bin_descriptor:
+	release_firmware(ts->himax_fw);
+	ts->himax_fw = NULL;
 }
 
 /**
@@ -1492,12 +2507,22 @@ static void himax_ap_notify_fw_suspend(struct himax_ts_data *ts, bool suspend)
  * @ts: Himax touch screen data
  *
  * This function is used to resume the touch screen. It will call the
+ * himax_zf_reload_from_file() to reload the firmware. And call the
  * himax_ap_notify_fw_suspend() to notify the FW of AP resume status.
  *
  * Return: None
  */
 static void himax_resume_proc(struct himax_ts_data *ts)
 {
+	int ret;
+
+	ret = himax_zf_reload_from_file(ts->firmware_name, ts);
+	if (ret) {
+		dev_err(ts->dev, "%s: update FW fail, code[%d]!!\n", __func__, ret);
+		return;
+	}
+	ts->resume_succeeded = true;
+
 	himax_ap_notify_fw_suspend(ts, false);
 }
 
@@ -1527,15 +2552,23 @@ static int himax_chip_suspend(struct himax_ts_data *ts)
  * This function is used to resume the touch screen. It will set the resume
  * success flag to false, and disable reset pin. Then call the himax_resume_proc()
  * to process detailed resume procedure.
+ * If the resume action is succeeded, it will call the himax_hid_probe() to restore
+ * the HID device and enable the interrupt.
  *
  * Return: 0 on success, negative error code on failure
  */
 static int himax_chip_resume(struct himax_ts_data *ts)
 {
+	ts->resume_succeeded = false;
 	gpiod_set_value(ts->pdata.gpiod_rst, 0);
 	himax_resume_proc(ts);
-	himax_hid_probe(ts);
-	himax_int_enable(ts, true);
+	if (ts->resume_succeeded) {
+		himax_hid_probe(ts);
+		himax_int_enable(ts, true);
+	} else {
+		dev_err(ts->dev, "%s: resume failed!\n", __func__);
+		return -ECANCELED;
+	}
 
 	return 0;
 }
@@ -1596,8 +2629,7 @@ static int himax_resume(struct device *dev)
  * initialize interrupt lock, register the interrupt, and disable the
  * interrupt. If later part of initialization succeed, then interrupt will
  * be enabled.
- * It will also load the firmware from the flash, parse the HID info, and
- * register the HID device by calling the himax_load_config().
+ * And initialize varies flags, workqueue and delayed work for later use.
  *
  * Return: 0 on success, negative error code on failure
  */
@@ -1605,18 +2637,96 @@ static int himax_chip_init(struct himax_ts_data *ts)
 {
 	int ret;
 
+	hx83102j_chip_init_data(ts);
 	if (himax_ts_register_interrupt(ts)) {
 		dev_err(ts->dev, "%s: register interrupt failed\n", __func__);
 		return -EIO;
 	}
 	himax_int_enable(ts, false);
-	ret = himax_load_config(ts);
-	if (ret < 0)
-		return ret;
+	ts->zf_update_cfg_buffer = devm_kzalloc(ts->dev, ts->chip_max_dsram_size, GFP_KERNEL);
+	if (!ts->zf_update_cfg_buffer) {
+		ret = -ENOMEM;
+		goto err_update_cfg_buf_alloc_failed;
+	}
+	INIT_DELAYED_WORK(&ts->initial_work, himax_initial_work);
+	schedule_delayed_work(&ts->initial_work, msecs_to_jiffies(HIMAX_DELAY_BOOT_UPDATE_MS));
 	ts->initialized = true;
 
+	return 0;
+	cancel_delayed_work_sync(&ts->initial_work);
+err_update_cfg_buf_alloc_failed:
+
+	return ret;
+}
+
+/**
+ * himax_chip_deinit() - Deinitialize the Himax touch screen
+ * @ts: Himax touch screen data
+ *
+ * This function is used to deinitialize the Himax touch screen.
+ *
+ * Return: None
+ */
+static void himax_chip_deinit(struct himax_ts_data *ts)
+{
+	cancel_delayed_work_sync(&ts->initial_work);
+}
+
+#if defined(CONFIG_OF)
+/**
+ * himax_parse_dt() - Parse the device tree
+ * @dt: Device node
+ * @pdata: Himax platform data
+ *
+ * This function is used to parse the device tree. If "himax,pid" is found,
+ * it will parse the PID value and set it to the platform data. The firmware
+ * name will set to himax_i2chid_$PID.bin if the PID is found, or
+ * himax_i2chid.bin if the PID is not found.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int himax_parse_dt(struct device_node *dt, struct himax_platform_data *pdata)
+{
+	u32 data;
+	const char default_fw_name[] = HIMAX_BOOT_UPGRADE_FWNAME;
+	/*
+	 * Maximum length of a firmware name size:
+	 * (default_name) + _XXXX(PID) + .bin + null terminator
+	 */
+	static char pid_fw_name[ARRAY_SIZE(default_fw_name) + 5 + 4 + 1] = {0};
+	struct himax_ts_data *ts;
+
+	if (!dt || !pdata)
+		return -EINVAL;
+
+	ts = container_of(pdata, struct himax_ts_data, pdata);
+	/* Set default firmware name, without PID */
+	strscpy(ts->firmware_name, HIMAX_BOOT_UPGRADE_FWNAME HIMAX_FW_EXT_NAME,
+		sizeof(HIMAX_BOOT_UPGRADE_FWNAME HIMAX_FW_EXT_NAME));
+
+	/*
+	 * check himax,pid first, if exist then get the value.
+	 * himax,pid = <0x1002>; 0x1002 is PID value
+	 */
+	if (of_get_property(dt, "himax,pid", &data)) {
+		if (of_property_read_u32(dt, "himax,pid", &data)) {
+			pdata->pid = 0;
+			return -EINVAL;
+		}
+
+		pdata->pid = data;
+		snprintf(pid_fw_name, sizeof(pid_fw_name), "%s_%04X%s", HIMAX_BOOT_UPGRADE_FWNAME,
+			 pdata->pid, HIMAX_FW_EXT_NAME);
+		dev_info(ts->dev, "%s: DT:himax,pid = %04X, fw_name = %s\n", __func__,
+			 pdata->pid, pid_fw_name);
+		strscpy(ts->firmware_name, pid_fw_name, sizeof(pid_fw_name));
+	} else {
+		pdata->pid = 0;
+	}
+
 	return 0;
 }
+#endif
 
 /**
  * __himax_initial_power_up() - Initial power up of the Himax touch screen
@@ -1820,6 +2930,13 @@ static int himax_spi_drv_probe(struct spi_device *spi)
 		dev_err(ts->dev, "%s: gpio-rst value is not valid\n", __func__);
 		return -EIO;
 	}
+#if defined(CONFIG_OF)
+	if (himax_parse_dt(spi->dev.of_node, pdata) < 0) {
+		dev_err(ts->dev, "%s:  parse OF data failed!\n", __func__);
+		ts->dev = NULL;
+		return -ENODEV;
+	}
+#endif
 
 	spi->bits_per_word = 8;
 	spi->mode = SPI_MODE_3;
@@ -1850,6 +2967,7 @@ static int himax_spi_drv_probe(struct spi_device *spi)
 	spin_lock_init(&ts->irq_lock);
 	mutex_init(&ts->rw_lock);
 	mutex_init(&ts->reg_lock);
+	mutex_init(&ts->zf_update_lock);
 	dev_set_drvdata(&spi->dev, ts);
 	spi_set_drvdata(spi, ts);
 
@@ -1885,6 +3003,7 @@ static void himax_spi_drv_remove(struct spi_device *spi)
 			if (ts->hid_probed)
 				himax_hid_remove(ts);
 		}
+		himax_chip_deinit(ts);
 		himax_platform_deinit(ts);
 	}
 }
diff --git a/drivers/hid/hid-himax-83102j.h b/drivers/hid/hid-himax-83102j.h
index eef55c45b1d4..6a41ff680478 100644
--- a/drivers/hid/hid-himax-83102j.h
+++ b/drivers/hid/hid-himax-83102j.h
@@ -10,7 +10,9 @@
 // #define HX_PWR_CONFIG
 
 #include <drm/drm_panel.h>
+#include <linux/crc32poly.h>
 #include <linux/delay.h>
+#include <linux/firmware.h>
 #include <linux/hid.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -41,6 +43,13 @@
 							 HIMAX_BUS_W_HLEN + HIMAX_REG_SZ)
 /* SPI CS setup time */
 #define HIMAX_SPI_CS_SETUP_TIME				300
+/* Clear 4 bytes data */
+#define HIMAX_DATA_CLEAR				0x00000000
+/* boot update start delay */
+#define HIMAX_DELAY_BOOT_UPDATE_MS			2000
+#define HIMAX_TP_INFO_STR_LEN				12U
+#define HIMAX_ZF_PARTITION_AMOUNT_OFFSET		12
+#define HIMAX_ZF_PARTITION_DESC_SZ			16U
 /* HIDRAW report header size */
 #define HIMAX_HID_REPORT_HDR_SZ				2U
 /* hx83102j IC parameters */
@@ -71,17 +80,48 @@
 #define HIMAX_AHB_CMD_INCR4_ADD_4_BYTE			0x01
 #define HIMAX_AHB_CMD_LEAVE_SAFE_MODE			0x0000
 /* DSRAM flag addresses */
+#define HIMAX_DSRAM_ADDR_VENDOR				0x10007000
+#define HIMAX_DSRAM_ADDR_FW_VER				0x10007004
+#define HIMAX_DSRAM_ADDR_CUS_INFO			0x10007008
+#define HIMAX_DSRAM_ADDR_PROJ_INFO			0x10007014
+#define HIMAX_DSRAM_ADDR_CFG				0x10007084
+#define HIMAX_DSRAM_ADDR_INT_IS_EDGE			0x10007088
+#define HIMAX_DSRAM_ADDR_MKEY				0x100070e8
+#define HIMAX_DSRAM_ADDR_RXNUM_TXNUM			0x100070f4
+#define HIMAX_DSRAM_ADDR_MAXPT_XYRVS			0x100070f8
+#define HIMAX_DSRAM_ADDR_X_Y_RES			0x100070fc
+#define HIMAX_DSRAM_ADDR_STYLUS_FUNCTION		0x1000719c
+#define HIMAX_DSRAM_ADDR_STYLUS_VERSION			0x100071fc
+#define HIMAX_DSRAM_ADDR_SET_NFRAME			0x10007294
+#define HIMAX_DSRAM_ADDR_2ND_FLASH_RELOAD		0x100072c0
+#define HIMAX_DSRAM_ADDR_FLASH_RELOAD			0x10007f00
+#define HIMAX_DSRAM_ADDR_SORTING_MODE_EN		0x10007f04
+#define HIMAX_DSRAM_ADDR_DBG_MSG			0x10007f40
 #define HIMAX_DSRAM_ADDR_AP_NOTIFY_FW_SUSPEND		0x10007fd0
 /* dsram flag data */
 #define HIMAX_DSRAM_DATA_AP_NOTIFY_FW_SUSPEND		0xa55aa55a
 #define HIMAX_DSRAM_DATA_AP_NOTIFY_FW_RESUME		0x00000000
+#define HIMAX_DSRAM_DATA_DISABLE_FLASH_RELOAD		0x00009aa9
+#define HIMAX_DSRAM_DATA_FW_RELOAD_DONE			0x000072c0
 /* hx83102j-specific register/dsram flags/data */
+#define HIMAX_HX83102J_DSRAM_ADDR_RAW_OUT_SEL		0x100072ec
+#define HIMAX_HX83102J_REG_ADDR_HW_CRC			0x80010000
 #define HIMAX_HX83102J_REG_ADDR_TCON_RST		0x80020004
+#define HIMAX_HX83102J_REG_DATA_HW_CRC			0x0000ecce
+#define HIMAX_HX83102J_REG_DATA_HW_CRC_DISABLE		0x00000000
 /* hardware register addresses */
 #define HIMAX_REG_ADDR_SPI200_DATA			0x8000002c
+#define HIMAX_REG_ADDR_RELOAD_STATUS			0x80050000
+#define HIMAX_REG_ADDR_RELOAD_CRC32_RESULT		0x80050018
+#define HIMAX_REG_ADDR_RELOAD_ADDR_FROM			0x80050020
+#define HIMAX_REG_ADDR_RELOAD_ADDR_CMD_BEAT		0x80050028
+#define HIMAX_REG_ADDR_SYSTEM_RESET			0x90000018
+#define HIMAX_REG_ADDR_RELOAD_TO_ACTIVE			0x90000048
 #define HIMAX_REG_ADDR_CTRL_FW				0x9000005c
 #define HIMAX_REG_ADDR_FW_STATUS			0x900000a8
 #define HIMAX_REG_ADDR_ICID				0x900000d0
+#define HIMAX_REG_ADDR_RESET_FLAG			0x900000e4
+#define HIMAX_REG_ADDR_DD_STATUS			0x900000e8
 /* hardware reg data/flags */
 #define HIMAX_REG_DATA_FW_STATE_RUNNING			0x05
 #define HIMAX_REG_DATA_FW_STATE_SAFE_MODE		0x0c
@@ -89,6 +129,9 @@
 #define HIMAX_REG_DATA_FW_GO_SAFEMODE			0xa5
 #define HIMAX_REG_DATA_FW_IN_SAFEMODE			0x87
 #define HIMAX_REG_DATA_ICID				0x83102900
+#define HIMAX_REG_DATA_RELOAD_DONE			0x01
+#define HIMAX_REG_DATA_RELOAD_PASSWORD			0x99
+#define HIMAX_REG_DATA_SYSTEM_RESET			0x00000055
 #define HIMAX_REG_DATA_TCON_RST				0x00000000
 /* HIMAX SPI function select, 1st byte of any SPI command sequence */
 #define HIMAX_SPI_FUNCTION_READ				0xf3
@@ -100,6 +143,8 @@
 #define HIMAX_CFG_VER					0x10000600
 #define HIMAX_HID_TABLE					0x30000100
 #define HIMAX_FW_BIN_DESC				0x10000000
+#define HIMAX_BOOT_UPGRADE_FWNAME			"himax_i2chid"
+#define HIMAX_FW_EXT_NAME				".bin"
 
 /**
  * enum himax_hidraw_id_function - HIDRAW report IDs
@@ -119,6 +164,20 @@ enum himax_touch_report_status {
 	HIMAX_TS_SUCCESS = 0,
 };
 
+/**
+ * struct himax_zf_info - Zero flash update information
+ * @sram_addr: SRAM address byte array buffer
+ * @write_size: Write size of each chunk
+ * @fw_addr: Offset in firmware file
+ * @cfg_addr: target sram address
+ */
+struct himax_zf_info {
+	u8 sram_addr[4];
+	int write_size;
+	u32 fw_addr;
+	u32 cfg_addr;
+};
+
 /**
  * struct himax_fw_address_table - address/offset in firmware image
  * @addr_fw_ver_major: Address to Major version of firmware
@@ -170,9 +229,21 @@ union himax_dword_data {
 /**
  * struct himax_ic_data - IC information holder
  * @stylus_ratio: Stylus ratio
+ * @vendor_cus_info: Vendor customer information
+ * @vendor_proj_info: Vendor project information
+ * @vendor_fw_ver: Vendor firmware version
+ * @vendor_config_ver: Vendor config version
+ * @vendor_touch_cfg_ver: Vendor touch config version
+ * @vendor_display_cfg_ver: Vendor display config version
+ * @vendor_cid_maj_ver: Vendor CID major version
+ * @vendor_cid_min_ver: Vendor CID minor version
+ * @vendor_panel_ver: Vendor panel version
+ * @vendor_sensor_id: Vendor sensor ID
  * @rx_num: Number of RX
  * @tx_num: Number of TX
  * @button_num: Number of buttons
+ * @x_res: X resolution
+ * @y_res: Y resolution
  * @max_point: Maximum touch point
  * @icid: IC ID
  * @interrupt_is_edge: Interrupt is edge otherwise level
@@ -181,9 +252,21 @@ union himax_dword_data {
  */
 struct himax_ic_data {
 	u8 stylus_ratio;
+	u8 vendor_cus_info[12];
+	u8 vendor_proj_info[12];
+	int vendor_fw_ver;
+	int vendor_config_ver;
+	int vendor_touch_cfg_ver;
+	int vendor_display_cfg_ver;
+	int vendor_cid_maj_ver;
+	int vendor_cid_min_ver;
+	int vendor_panel_ver;
+	int vendor_sensor_id;
 	u32 rx_num;
 	u32 tx_num;
 	u32 button_num;
+	u32 x_res;
+	u32 y_res;
 	u32 max_point;
 	u32 icid;
 	bool interrupt_is_edge;
@@ -191,6 +274,38 @@ struct himax_ic_data {
 	bool stylus_v2;
 };
 
+/**
+ * struct himax_bin_desc - Firmware binary descriptor
+ * @passwd: Password to indicate the binary is valid
+ * @cid: Customer ID
+ * @panel_ver: Panel version
+ * @fw_ver: Firmware version
+ * @ic_sign: IC signature
+ * @customer: Customer name
+ * @project: Project name
+ * @fw_major: Major version of firmware
+ * @fw_minor: Minor version of firmware
+ * @date: Generate date of firmware
+ * @ic_sign_2: IC signature 2
+ *
+ * This structure is used to hold the firmware binary descriptor.
+ * It directly maps to a sequence of bytes in firmware image,
+ * thus need to be packed.
+ */
+struct himax_bin_desc {
+	u16 passwd;
+	u16 cid;
+	u8 panel_ver;
+	u16 fw_ver;
+	u8 ic_sign;
+	char customer[12];
+	char project[12];
+	char fw_major[12];
+	char fw_minor[12];
+	char date[12];
+	char ic_sign_2[12];
+} __packed;
+
 /**
  * struct himax_hid_desc - HID descriptor
  * @desc_length: Length of HID descriptor
@@ -223,8 +338,42 @@ struct himax_hid_desc {
 	u32 reserved;
 } __packed;
 
+/**
+ * struct himax_hid_info - IC information holder for HIDRAW function
+ * @vid: Vendor ID
+ * @pid: Product ID
+ * @cfg_info: Configuration information
+ * @cfg_version: Configuration version
+ * @disp_version: Display version
+ * @rx: Number of RX
+ * @tx: Number of TX
+ * @y_res: Y resolution
+ * @x_res: X resolution
+ * @pt_num: Number of touch points
+ * @mkey_num: Number of mkey
+ * @debug_info: Debug information
+ *
+ * This structure is used to hold the IC config information for HIDRAW.
+ * The format is binary fixed, thus need to be packed.
+ */
+struct himax_hid_info {
+	u16 vid;
+	u16 pid;
+	u8 cfg_info[32];
+	u8 cfg_version;
+	u8 disp_version;
+	u8 rx;
+	u8 tx;
+	u16 y_res;
+	u16 x_res;
+	u8 pt_num;
+	u8 mkey_num;
+	u8 debug_info[78];
+} __packed;
+
 /**
  * struct himax_platform_data - Platform data holder
+ * @pid: Product ID
  * @is_panel_follower: Is panel follower enabled
  * @panel_follower: DRM panel follower
  * @gpiod_rst: GPIO reset
@@ -232,6 +381,7 @@ struct himax_hid_desc {
  * This structure is used to hold the platform related data.
  */
 struct himax_platform_data {
+	u16 pid;
 	bool is_panel_follower;
 	struct drm_panel_follower panel_follower;
 	struct gpio_desc *gpiod_rst;
@@ -242,8 +392,9 @@ struct himax_platform_data {
  * @xfer_buf: Interrupt data buffer
  * @xfer_rx_data: SPI Transfer receive data buffer
  * @xfer_tx_data: SPI Transfer transmit data buffer
- * @himax_fw_data: Firmware data holder from flash
+ * @zf_update_cfg_buffer: Zero flash update configuration buffer
  * @himax_irq: IRQ number
+ * @chip_max_dsram_size: Maximum size of DSRAM
  * @spi_xfer_max_sz: Size of SPI controller max transfer size
  * @xfer_buf_sz: Size of interrupt data buffer
  * @irq_state: IRQ state
@@ -252,24 +403,30 @@ struct himax_platform_data {
  * @probe_finish: Indicate the driver probe is finished
  * @ic_boot_done: Indicate the IC boot is done
  * @hid_probed: Indicate the HID device is probed
+ * @resume_succeeded: Indicate the resume is succeeded
+ * @firmware_name: Firmware name
  * @touch_data_sz: Size of each interrupt data from IC
+ * @himax_fw: Firmware data holder from user space
  * @dev: Device pointer
  * @spi: SPI device pointer
  * @hid: HID device pointer
  * @reg_lock: Mutex lock for reg access
  * @rw_lock: Mutex lock for read/write action
+ * @zf_update_lock: Mutex lock for zero-flash FW update
  * @ic_data: IC information holder
  * @pdata: Platform data holder
  * @fw_info_table: Firmware information address table of firmware image
  * @hid_desc: HID descriptor
  * @hid_rd_data: HID report descriptor data
+ * @initial_work: Delayed work for TP initialization
  */
 struct himax_ts_data {
 	u8 *xfer_buf;
 	u8 *xfer_rx_data;
 	u8 *xfer_tx_data;
-	u8 *himax_fw_data;
+	u8 *zf_update_cfg_buffer;
 	s32 himax_irq;
+	u32 chip_max_dsram_size;
 	u32 spi_xfer_max_sz;
 	u32 xfer_buf_sz;
 	atomic_t irq_state;
@@ -279,7 +436,11 @@ struct himax_ts_data {
 	bool probe_finish;
 	bool ic_boot_done;
 	bool hid_probed;
+	bool resume_succeeded;
+	bool zf_update_flag;
+	char firmware_name[64];
 	int touch_data_sz;
+	const struct firmware *himax_fw;
 	struct device *dev;
 	struct spi_device *spi;
 	struct hid_device *hid;
@@ -287,10 +448,13 @@ struct himax_ts_data {
 	struct mutex reg_lock;
 	/* lock for bus read/write action */
 	struct mutex rw_lock;
+	/* lock for zero-flash FW update */
+	struct mutex zf_update_lock;
 	struct himax_ic_data ic_data;
 	struct himax_platform_data pdata;
 	struct himax_fw_address_table fw_info_table;
 	struct himax_hid_desc hid_desc;
 	struct himax_hid_rd_data hid_rd_data;
+	struct delayed_work initial_work;
 };
 #endif
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ