Commit 059d6c2d authored by Jingle Wu's avatar Jingle Wu Committed by Dmitry Torokhov
Browse files

Input: elan_i2c - add support for different firmware page sizes



Prepare driver for devices that use different sizes of firmware pages.

Signed-off-by: default avatarJingle Wu <jingle.wu@emc.com.tw>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent df10cc8d
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@
#define ETP_FW_IAP_PAGE_ERR	(1 << 5)
#define ETP_FW_IAP_INTF_ERR	(1 << 4)
#define ETP_FW_PAGE_SIZE	64
#define ETP_FW_PAGE_SIZE_128	128
#define ETP_FW_PAGE_SIZE_512	512
#define ETP_FW_SIGNATURE_SIZE	6

struct i2c_client;
@@ -73,7 +75,7 @@ struct elan_transport_ops {
	int (*iap_reset)(struct i2c_client *client);

	int (*prepare_fw_update)(struct i2c_client *client);
	int (*write_fw_block)(struct i2c_client *client,
	int (*write_fw_block)(struct i2c_client *client, u16 fw_page_size,
			      const u8 *page, u16 checksum, int idx);
	int (*finish_fw_update)(struct i2c_client *client,
				struct completion *reset_done);
+15 −9
Original line number Diff line number Diff line
@@ -89,7 +89,8 @@ struct elan_tp_data {
	u8			mode;
	u16			ic_type;
	u16			fw_validpage_count;
	u16			fw_signature_address;
	u16			fw_page_size;
	u32			fw_signature_address;

	bool			irq_wake;

@@ -101,7 +102,7 @@ struct elan_tp_data {
};

static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
			   u16 *signature_address)
			   u32 *signature_address, u16 *page_size)
{
	switch (ic_type) {
	case 0x00:
@@ -130,12 +131,15 @@ static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
		/* unknown ic type clear value */
		*validpage_count = 0;
		*signature_address = 0;
		*page_size = 0;
		return -ENXIO;
	}

	*signature_address =
		(*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;

	*page_size = ETP_FW_PAGE_SIZE;

	return 0;
}

@@ -336,7 +340,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
		return error;

	error = elan_get_fwinfo(data->ic_type, &data->fw_validpage_count,
				&data->fw_signature_address);
				&data->fw_signature_address,
				&data->fw_page_size);
	if (error)
		dev_warn(&data->client->dev,
			 "unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n",
@@ -424,14 +429,14 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
 * IAP firmware updater related routines
 **********************************************************
 */
static int elan_write_fw_block(struct elan_tp_data *data,
static int elan_write_fw_block(struct elan_tp_data *data, u16 page_size,
			       const u8 *page, u16 checksum, int idx)
{
	int retry = ETP_RETRY_COUNT;
	int error;

	do {
		error = data->ops->write_fw_block(data->client,
		error = data->ops->write_fw_block(data->client, page_size,
						  page, checksum, idx);
		if (!error)
			return 0;
@@ -460,15 +465,16 @@ static int __elan_update_firmware(struct elan_tp_data *data,

	iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);

	boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
	boot_page_count = (iap_start_addr * 2) / data->fw_page_size;
	for (i = boot_page_count; i < data->fw_validpage_count; i++) {
		u16 checksum = 0;
		const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
		const u8 *page = &fw->data[i * data->fw_page_size];

		for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
		for (j = 0; j < data->fw_page_size; j += 2)
			checksum += ((page[j + 1] << 8) | page[j]);

		error = elan_write_fw_block(data, page, checksum, i);
		error = elan_write_fw_block(data, data->fw_page_size,
					    page, checksum, i);
		if (error) {
			dev_err(dev, "write page %d fail: %d\n", i, error);
			return error;
+19 −11
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <asm/unaligned.h>

@@ -592,45 +593,52 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client)
	return 0;
}

static int elan_i2c_write_fw_block(struct i2c_client *client,
static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size,
				   const u8 *page, u16 checksum, int idx)
{
	struct device *dev = &client->dev;
	u8 page_store[ETP_FW_PAGE_SIZE + 4];
	u8 *page_store;
	u8 val[3];
	u16 result;
	int ret, error;

	page_store = kmalloc(fw_page_size + 4, GFP_KERNEL);
	if (!page_store)
		return -ENOMEM;

	page_store[0] = ETP_I2C_IAP_REG_L;
	page_store[1] = ETP_I2C_IAP_REG_H;
	memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
	memcpy(&page_store[2], page, fw_page_size);
	/* recode checksum at last two bytes */
	put_unaligned_le16(checksum, &page_store[ETP_FW_PAGE_SIZE + 2]);
	put_unaligned_le16(checksum, &page_store[fw_page_size + 2]);

	ret = i2c_master_send(client, page_store, sizeof(page_store));
	if (ret != sizeof(page_store)) {
	ret = i2c_master_send(client, page_store, fw_page_size + 4);
	if (ret != fw_page_size + 4) {
		error = ret < 0 ? ret : -EIO;
		dev_err(dev, "Failed to write page %d: %d\n", idx, error);
		return error;
		goto exit;
	}

	/* Wait for F/W to update one page ROM data. */
	msleep(35);
	msleep(fw_page_size == ETP_FW_PAGE_SIZE_512 ? 50 : 35);

	error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
	if (error) {
		dev_err(dev, "Failed to read IAP write result: %d\n", error);
		return error;
		goto exit;
	}

	result = le16_to_cpup((__le16 *)val);
	if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
		dev_err(dev, "IAP reports failed write: %04hx\n",
			result);
		return -EIO;
		error = -EIO;
		goto exit;
	}

	return 0;
exit:
	kfree(page_store);
	return error;
}

static int elan_i2c_finish_fw_update(struct i2c_client *client,
+4 −4
Original line number Diff line number Diff line
@@ -414,7 +414,7 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client)
}


static int elan_smbus_write_fw_block(struct i2c_client *client,
static int elan_smbus_write_fw_block(struct i2c_client *client, u16 fw_page_size,
				     const u8 *page, u16 checksum, int idx)
{
	struct device *dev = &client->dev;
@@ -429,7 +429,7 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
	 */
	error = i2c_smbus_write_block_data(client,
					   ETP_SMBUS_WRITE_FW_BLOCK,
					   ETP_FW_PAGE_SIZE / 2,
					   fw_page_size / 2,
					   page);
	if (error) {
		dev_err(dev, "Failed to write page %d (part %d): %d\n",
@@ -439,8 +439,8 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,

	error = i2c_smbus_write_block_data(client,
					   ETP_SMBUS_WRITE_FW_BLOCK,
					   ETP_FW_PAGE_SIZE / 2,
					   page + ETP_FW_PAGE_SIZE / 2);
					   fw_page_size / 2,
					   page + fw_page_size / 2);
	if (error) {
		dev_err(dev, "Failed to write page %d (part %d): %d\n",
			idx, 2, error);