diff options
author | Jingle Wu <jingle.wu@emc.com.tw> | 2020-07-17 07:32:23 +0300 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2020-07-18 03:36:00 +0300 |
commit | 059d6c2de6fd521d4d4a39d127eece3de114b767 (patch) | |
tree | 7abff2a94e327458808773b158274543eb568798 | |
parent | df10cc8db1e13cfcbd851e02881e2ef815d21b37 (diff) | |
download | linux-059d6c2de6fd521d4d4a39d127eece3de114b767.tar.xz |
Input: elan_i2c - add support for different firmware page sizes
Prepare driver for devices that use different sizes of firmware pages.
Signed-off-by: Jingle Wu <jingle.wu@emc.com.tw>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/mouse/elan_i2c.h | 4 | ||||
-rw-r--r-- | drivers/input/mouse/elan_i2c_core.c | 24 | ||||
-rw-r--r-- | drivers/input/mouse/elan_i2c_i2c.c | 30 | ||||
-rw-r--r-- | drivers/input/mouse/elan_i2c_smbus.c | 8 |
4 files changed, 41 insertions, 25 deletions
diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index a9074ac9364f..b504f560a666 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -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); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index f1dade60f407..46f334bcfc0e 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -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; diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 317166a264e6..4dfc3c2b4706 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -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, diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index 8c3185d54c73..59eec1a2ed5c 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -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); |