Input: elan_i2c - handle firmware updated on newer ICs
authorJingle Wu <jingle.wu@emc.com.tw>
Fri, 17 Jul 2020 05:49:09 +0000 (22:49 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 18 Jul 2020 00:36:01 +0000 (17:36 -0700)
Newer ICs with IC type value starting with 0x0D and newer bootloader code
use 128-byte firmware pages. Their bootloader also needs to be switched to
proper mode before executing firmware update.

Signed-off-by: Jingle Wu <jingle.wu@emc.com.tw>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/mouse/elan_i2c.h
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elan_i2c_i2c.c
drivers/input/mouse/elan_i2c_smbus.c

index b504f56..de10a78 100644 (file)
@@ -74,7 +74,8 @@ struct elan_transport_ops {
        int (*iap_get_mode)(struct i2c_client *client, enum tp_mode *mode);
        int (*iap_reset)(struct i2c_client *client);
 
-       int (*prepare_fw_update)(struct i2c_client *client);
+       int (*prepare_fw_update)(struct i2c_client *client, u16 ic_type,
+                                u8 iap_version);
        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,
index 46f334b..e0f42de 100644 (file)
@@ -101,7 +101,7 @@ struct elan_tp_data {
        bool                    middle_button;
 };
 
-static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
+static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count,
                           u32 *signature_address, u16 *page_size)
 {
        switch (ic_type) {
@@ -138,7 +138,12 @@ static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
        *signature_address =
                (*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
 
-       *page_size = ETP_FW_PAGE_SIZE;
+       if (ic_type >= 0x0D && iap_version >= 1) {
+               *validpage_count /= 2;
+               *page_size = ETP_FW_PAGE_SIZE_128;
+       } else {
+               *page_size = ETP_FW_PAGE_SIZE;
+       }
 
        return 0;
 }
@@ -339,7 +344,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
-       error = elan_get_fwinfo(data->ic_type, &data->fw_validpage_count,
+       error = elan_get_fwinfo(data->ic_type, data->iap_version,
+                               &data->fw_validpage_count,
                                &data->fw_signature_address,
                                &data->fw_page_size);
        if (error)
@@ -459,7 +465,8 @@ static int __elan_update_firmware(struct elan_tp_data *data,
        u16 boot_page_count;
        u16 sw_checksum = 0, fw_checksum = 0;
 
-       error = data->ops->prepare_fw_update(client);
+       error = data->ops->prepare_fw_update(client, data->ic_type,
+                                            data->iap_version);
        if (error)
                return error;
 
index 4dfc3c2..4dfac2e 100644 (file)
@@ -56,6 +56,8 @@
 #define ETP_I2C_CALIBRATE_CMD          0x0316
 #define ETP_I2C_MAX_BASELINE_CMD       0x0317
 #define ETP_I2C_MIN_BASELINE_CMD       0x0318
+#define ETP_I2C_IAP_TYPE_REG           0x0040
+#define ETP_I2C_IAP_TYPE_CMD           0x0304
 
 #define ETP_I2C_REPORT_LEN             34
 #define ETP_I2C_DESC_LENGTH            30
@@ -528,7 +530,43 @@ static int elan_i2c_set_flash_key(struct i2c_client *client)
        return 0;
 }
 
-static int elan_i2c_prepare_fw_update(struct i2c_client *client)
+static int elan_read_write_iap_type(struct i2c_client *client)
+{
+       int error;
+       u16 constant;
+       u8 val[3];
+       int retry = 3;
+
+       do {
+               error = elan_i2c_write_cmd(client, ETP_I2C_IAP_TYPE_CMD,
+                                          ETP_I2C_IAP_TYPE_REG);
+               if (error) {
+                       dev_err(&client->dev,
+                               "cannot write iap type: %d\n", error);
+                       return error;
+               }
+
+               error = elan_i2c_read_cmd(client, ETP_I2C_IAP_TYPE_CMD, val);
+               if (error) {
+                       dev_err(&client->dev,
+                               "failed to read iap type register: %d\n",
+                               error);
+                       return error;
+               }
+               constant = le16_to_cpup((__le16 *)val);
+               dev_dbg(&client->dev, "iap type reg: 0x%04x\n", constant);
+
+               if (constant == ETP_I2C_IAP_TYPE_REG)
+                       return 0;
+
+       } while (--retry > 0);
+
+       dev_err(&client->dev, "cannot set iap type\n");
+       return -EIO;
+}
+
+static int elan_i2c_prepare_fw_update(struct i2c_client *client, u16 ic_type,
+                                     u8 iap_version)
 {
        struct device *dev = &client->dev;
        int error;
@@ -568,6 +606,12 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client)
                return -EIO;
        }
 
+       if (ic_type >= 0x0D && iap_version >= 1) {
+               error = elan_read_write_iap_type(client);
+               if (error)
+                       return error;
+       }
+
        /* Set flash key again */
        error = elan_i2c_set_flash_key(client);
        if (error)
index 59eec1a..97c2c46 100644 (file)
@@ -340,7 +340,8 @@ static int elan_smbus_set_flash_key(struct i2c_client *client)
        return 0;
 }
 
-static int elan_smbus_prepare_fw_update(struct i2c_client *client)
+static int elan_smbus_prepare_fw_update(struct i2c_client *client, u16 ic_type,
+                                       u8 iap_version)
 {
        struct device *dev = &client->dev;
        int len;