|
@@ -359,7 +359,6 @@ static int mxt_bootloader_read(struct mxt_data *data,
|
|
|
msg.buf = val;
|
|
|
|
|
|
ret = i2c_transfer(data->client->adapter, &msg, 1);
|
|
|
-
|
|
|
if (ret == 1) {
|
|
|
ret = 0;
|
|
|
} else {
|
|
@@ -414,6 +413,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
|
|
|
case 0x5b:
|
|
|
bootloader = appmode - 0x26;
|
|
|
break;
|
|
|
+
|
|
|
default:
|
|
|
dev_err(&data->client->dev,
|
|
|
"Appmode i2c address 0x%02x not found\n",
|
|
@@ -425,20 +425,20 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int mxt_probe_bootloader(struct mxt_data *data, bool retry)
|
|
|
+static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
|
|
|
{
|
|
|
struct device *dev = &data->client->dev;
|
|
|
- int ret;
|
|
|
+ int error;
|
|
|
u8 val;
|
|
|
bool crc_failure;
|
|
|
|
|
|
- ret = mxt_lookup_bootloader_address(data, retry);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ error = mxt_lookup_bootloader_address(data, alt_address);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
|
|
|
- ret = mxt_bootloader_read(data, &val, 1);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ error = mxt_bootloader_read(data, &val, 1);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
|
|
|
/* Check app crc fail mode */
|
|
|
crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
|
|
@@ -1064,6 +1064,137 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
|
|
|
return crc;
|
|
|
}
|
|
|
|
|
|
+static int mxt_prepare_cfg_mem(struct mxt_data *data,
|
|
|
+ const struct firmware *cfg,
|
|
|
+ unsigned int data_pos,
|
|
|
+ unsigned int cfg_start_ofs,
|
|
|
+ u8 *config_mem,
|
|
|
+ size_t config_mem_size)
|
|
|
+{
|
|
|
+ struct device *dev = &data->client->dev;
|
|
|
+ struct mxt_object *object;
|
|
|
+ unsigned int type, instance, size, byte_offset;
|
|
|
+ int offset;
|
|
|
+ int ret;
|
|
|
+ int i;
|
|
|
+ u16 reg;
|
|
|
+ u8 val;
|
|
|
+
|
|
|
+ while (data_pos < cfg->size) {
|
|
|
+ /* Read type, instance, length */
|
|
|
+ ret = sscanf(cfg->data + data_pos, "%x %x %x%n",
|
|
|
+ &type, &instance, &size, &offset);
|
|
|
+ if (ret == 0) {
|
|
|
+ /* EOF */
|
|
|
+ break;
|
|
|
+ } else if (ret != 3) {
|
|
|
+ dev_err(dev, "Bad format: failed to parse object\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ data_pos += offset;
|
|
|
+
|
|
|
+ object = mxt_get_object(data, type);
|
|
|
+ if (!object) {
|
|
|
+ /* Skip object */
|
|
|
+ for (i = 0; i < size; i++) {
|
|
|
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
|
|
+ &val, &offset);
|
|
|
+ if (ret != 1) {
|
|
|
+ dev_err(dev, "Bad format in T%d at %d\n",
|
|
|
+ type, i);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ data_pos += offset;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (size > mxt_obj_size(object)) {
|
|
|
+ /*
|
|
|
+ * Either we are in fallback mode due to wrong
|
|
|
+ * config or config from a later fw version,
|
|
|
+ * or the file is corrupt or hand-edited.
|
|
|
+ */
|
|
|
+ dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
|
|
|
+ size - mxt_obj_size(object), type);
|
|
|
+ } else if (mxt_obj_size(object) > size) {
|
|
|
+ /*
|
|
|
+ * If firmware is upgraded, new bytes may be added to
|
|
|
+ * end of objects. It is generally forward compatible
|
|
|
+ * to zero these bytes - previous behaviour will be
|
|
|
+ * retained. However this does invalidate the CRC and
|
|
|
+ * will force fallback mode until the configuration is
|
|
|
+ * updated. We warn here but do nothing else - the
|
|
|
+ * malloc has zeroed the entire configuration.
|
|
|
+ */
|
|
|
+ dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
|
|
|
+ mxt_obj_size(object) - size, type);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (instance >= mxt_obj_instances(object)) {
|
|
|
+ dev_err(dev, "Object instances exceeded!\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ reg = object->start_address + mxt_obj_size(object) * instance;
|
|
|
+
|
|
|
+ for (i = 0; i < size; i++) {
|
|
|
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
|
|
+ &val,
|
|
|
+ &offset);
|
|
|
+ if (ret != 1) {
|
|
|
+ dev_err(dev, "Bad format in T%d at %d\n",
|
|
|
+ type, i);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ data_pos += offset;
|
|
|
+
|
|
|
+ if (i > mxt_obj_size(object))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ byte_offset = reg + i - cfg_start_ofs;
|
|
|
+
|
|
|
+ if (byte_offset >= 0 && byte_offset < config_mem_size) {
|
|
|
+ *(config_mem + byte_offset) = val;
|
|
|
+ } else {
|
|
|
+ dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
|
|
|
+ reg, object->type, byte_offset);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
|
|
|
+ u8 *config_mem, size_t config_mem_size)
|
|
|
+{
|
|
|
+ unsigned int byte_offset = 0;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ /* Write configuration as blocks */
|
|
|
+ while (byte_offset < config_mem_size) {
|
|
|
+ unsigned int size = config_mem_size - byte_offset;
|
|
|
+
|
|
|
+ if (size > MXT_MAX_BLOCK_WRITE)
|
|
|
+ size = MXT_MAX_BLOCK_WRITE;
|
|
|
+
|
|
|
+ error = __mxt_write_reg(data->client,
|
|
|
+ cfg_start + byte_offset,
|
|
|
+ size, config_mem + byte_offset);
|
|
|
+ if (error) {
|
|
|
+ dev_err(&data->client->dev,
|
|
|
+ "Config write error, ret=%d\n", error);
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte_offset += size;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* mxt_update_cfg - download configuration to chip
|
|
|
*
|
|
@@ -1087,26 +1218,20 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
{
|
|
|
struct device *dev = &data->client->dev;
|
|
|
struct mxt_info cfg_info;
|
|
|
- struct mxt_object *object;
|
|
|
int ret;
|
|
|
int offset;
|
|
|
int data_pos;
|
|
|
- int byte_offset;
|
|
|
int i;
|
|
|
int cfg_start_ofs;
|
|
|
u32 info_crc, config_crc, calculated_crc;
|
|
|
u8 *config_mem;
|
|
|
size_t config_mem_size;
|
|
|
- unsigned int type, instance, size;
|
|
|
- u8 val;
|
|
|
- u16 reg;
|
|
|
|
|
|
mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
|
|
|
|
|
|
if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
|
|
|
dev_err(dev, "Unrecognised config file\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
data_pos = strlen(MXT_CFG_MAGIC);
|
|
@@ -1118,8 +1243,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
&offset);
|
|
|
if (ret != 1) {
|
|
|
dev_err(dev, "Bad format\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
data_pos += offset;
|
|
@@ -1127,30 +1251,26 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
|
|
|
if (cfg_info.family_id != data->info.family_id) {
|
|
|
dev_err(dev, "Family ID mismatch!\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
if (cfg_info.variant_id != data->info.variant_id) {
|
|
|
dev_err(dev, "Variant ID mismatch!\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
/* Read CRCs */
|
|
|
ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
|
|
|
if (ret != 1) {
|
|
|
dev_err(dev, "Bad format: failed to parse Info CRC\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
data_pos += offset;
|
|
|
|
|
|
ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
|
|
|
if (ret != 1) {
|
|
|
dev_err(dev, "Bad format: failed to parse Config CRC\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
data_pos += offset;
|
|
|
|
|
@@ -1166,8 +1286,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
} else if (config_crc == data->config_crc) {
|
|
|
dev_dbg(dev, "Config CRC 0x%06X: OK\n",
|
|
|
data->config_crc);
|
|
|
- ret = 0;
|
|
|
- goto release;
|
|
|
+ return 0;
|
|
|
} else {
|
|
|
dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
|
|
|
data->config_crc, config_crc);
|
|
@@ -1186,93 +1305,13 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
config_mem = kzalloc(config_mem_size, GFP_KERNEL);
|
|
|
if (!config_mem) {
|
|
|
dev_err(dev, "Failed to allocate memory\n");
|
|
|
- ret = -ENOMEM;
|
|
|
- goto release;
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
- while (data_pos < cfg->size) {
|
|
|
- /* Read type, instance, length */
|
|
|
- ret = sscanf(cfg->data + data_pos, "%x %x %x%n",
|
|
|
- &type, &instance, &size, &offset);
|
|
|
- if (ret == 0) {
|
|
|
- /* EOF */
|
|
|
- break;
|
|
|
- } else if (ret != 3) {
|
|
|
- dev_err(dev, "Bad format: failed to parse object\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release_mem;
|
|
|
- }
|
|
|
- data_pos += offset;
|
|
|
-
|
|
|
- object = mxt_get_object(data, type);
|
|
|
- if (!object) {
|
|
|
- /* Skip object */
|
|
|
- for (i = 0; i < size; i++) {
|
|
|
- ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
|
|
- &val,
|
|
|
- &offset);
|
|
|
- data_pos += offset;
|
|
|
- }
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (size > mxt_obj_size(object)) {
|
|
|
- /*
|
|
|
- * Either we are in fallback mode due to wrong
|
|
|
- * config or config from a later fw version,
|
|
|
- * or the file is corrupt or hand-edited.
|
|
|
- */
|
|
|
- dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
|
|
|
- size - mxt_obj_size(object), type);
|
|
|
- } else if (mxt_obj_size(object) > size) {
|
|
|
- /*
|
|
|
- * If firmware is upgraded, new bytes may be added to
|
|
|
- * end of objects. It is generally forward compatible
|
|
|
- * to zero these bytes - previous behaviour will be
|
|
|
- * retained. However this does invalidate the CRC and
|
|
|
- * will force fallback mode until the configuration is
|
|
|
- * updated. We warn here but do nothing else - the
|
|
|
- * malloc has zeroed the entire configuration.
|
|
|
- */
|
|
|
- dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
|
|
|
- mxt_obj_size(object) - size, type);
|
|
|
- }
|
|
|
-
|
|
|
- if (instance >= mxt_obj_instances(object)) {
|
|
|
- dev_err(dev, "Object instances exceeded!\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto release_mem;
|
|
|
- }
|
|
|
-
|
|
|
- reg = object->start_address + mxt_obj_size(object) * instance;
|
|
|
-
|
|
|
- for (i = 0; i < size; i++) {
|
|
|
- ret = sscanf(cfg->data + data_pos, "%hhx%n",
|
|
|
- &val,
|
|
|
- &offset);
|
|
|
- if (ret != 1) {
|
|
|
- dev_err(dev, "Bad format in T%d\n", type);
|
|
|
- ret = -EINVAL;
|
|
|
- goto release_mem;
|
|
|
- }
|
|
|
- data_pos += offset;
|
|
|
-
|
|
|
- if (i > mxt_obj_size(object))
|
|
|
- continue;
|
|
|
-
|
|
|
- byte_offset = reg + i - cfg_start_ofs;
|
|
|
-
|
|
|
- if ((byte_offset >= 0)
|
|
|
- && (byte_offset <= config_mem_size)) {
|
|
|
- *(config_mem + byte_offset) = val;
|
|
|
- } else {
|
|
|
- dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
|
|
|
- reg, object->type, byte_offset);
|
|
|
- ret = -EINVAL;
|
|
|
- goto release_mem;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs,
|
|
|
+ config_mem, config_mem_size);
|
|
|
+ if (ret)
|
|
|
+ goto release_mem;
|
|
|
|
|
|
/* Calculate crc of the received configs (not the raw config file) */
|
|
|
if (data->T7_address < cfg_start_ofs) {
|
|
@@ -1286,28 +1325,14 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
data->T7_address - cfg_start_ofs,
|
|
|
config_mem_size);
|
|
|
|
|
|
- if (config_crc > 0 && (config_crc != calculated_crc))
|
|
|
+ if (config_crc > 0 && config_crc != calculated_crc)
|
|
|
dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n",
|
|
|
calculated_crc, config_crc);
|
|
|
|
|
|
- /* Write configuration as blocks */
|
|
|
- byte_offset = 0;
|
|
|
- while (byte_offset < config_mem_size) {
|
|
|
- size = config_mem_size - byte_offset;
|
|
|
-
|
|
|
- if (size > MXT_MAX_BLOCK_WRITE)
|
|
|
- size = MXT_MAX_BLOCK_WRITE;
|
|
|
-
|
|
|
- ret = __mxt_write_reg(data->client,
|
|
|
- cfg_start_ofs + byte_offset,
|
|
|
- size, config_mem + byte_offset);
|
|
|
- if (ret != 0) {
|
|
|
- dev_err(dev, "Config write error, ret=%d\n", ret);
|
|
|
- goto release_mem;
|
|
|
- }
|
|
|
-
|
|
|
- byte_offset += size;
|
|
|
- }
|
|
|
+ ret = mxt_upload_cfg_mem(data, cfg_start_ofs,
|
|
|
+ config_mem, config_mem_size);
|
|
|
+ if (ret)
|
|
|
+ goto release_mem;
|
|
|
|
|
|
mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
|
|
|
|
|
@@ -1319,8 +1344,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
|
|
|
|
|
release_mem:
|
|
|
kfree(config_mem);
|
|
|
-release:
|
|
|
- release_firmware(cfg);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1422,10 +1445,12 @@ static int mxt_get_object_table(struct mxt_data *data)
|
|
|
|
|
|
switch (object->type) {
|
|
|
case MXT_GEN_MESSAGE_T5:
|
|
|
- if (data->info.family_id == 0x80) {
|
|
|
+ if (data->info.family_id == 0x80 &&
|
|
|
+ data->info.version < 0x20) {
|
|
|
/*
|
|
|
- * On mXT224 read and discard unused CRC byte
|
|
|
- * otherwise DMA reads are misaligned
|
|
|
+ * On mXT224 firmware versions prior to V2.0
|
|
|
+ * read and discard unused CRC byte otherwise
|
|
|
+ * DMA reads are misaligned.
|
|
|
*/
|
|
|
data->T5_msg_size = mxt_obj_size(object);
|
|
|
} else {
|
|
@@ -1433,6 +1458,7 @@ static int mxt_get_object_table(struct mxt_data *data)
|
|
|
data->T5_msg_size = mxt_obj_size(object) - 1;
|
|
|
}
|
|
|
data->T5_address = object->start_address;
|
|
|
+ break;
|
|
|
case MXT_GEN_COMMAND_T6:
|
|
|
data->T6_reportid = min_id;
|
|
|
data->T6_address = object->start_address;
|
|
@@ -1638,46 +1664,45 @@ static int mxt_configure_objects(struct mxt_data *data,
|
|
|
static void mxt_config_cb(const struct firmware *cfg, void *ctx)
|
|
|
{
|
|
|
mxt_configure_objects(ctx, cfg);
|
|
|
+ release_firmware(cfg);
|
|
|
}
|
|
|
|
|
|
static int mxt_initialize(struct mxt_data *data)
|
|
|
{
|
|
|
struct i2c_client *client = data->client;
|
|
|
+ int recovery_attempts = 0;
|
|
|
int error;
|
|
|
- bool alt_bootloader_addr = false;
|
|
|
- bool retry = false;
|
|
|
|
|
|
-retry_info:
|
|
|
- error = mxt_get_info(data);
|
|
|
- if (error) {
|
|
|
-retry_bootloader:
|
|
|
- error = mxt_probe_bootloader(data, alt_bootloader_addr);
|
|
|
+ while (1) {
|
|
|
+ error = mxt_get_info(data);
|
|
|
+ if (!error)
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* Check bootloader state */
|
|
|
+ error = mxt_probe_bootloader(data, false);
|
|
|
if (error) {
|
|
|
- if (alt_bootloader_addr) {
|
|
|
+ dev_info(&client->dev, "Trying alternate bootloader address\n");
|
|
|
+ error = mxt_probe_bootloader(data, true);
|
|
|
+ if (error) {
|
|
|
/* Chip is not in appmode or bootloader mode */
|
|
|
return error;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- dev_info(&client->dev, "Trying alternate bootloader address\n");
|
|
|
- alt_bootloader_addr = true;
|
|
|
- goto retry_bootloader;
|
|
|
- } else {
|
|
|
- if (retry) {
|
|
|
- dev_err(&client->dev, "Could not recover from bootloader mode\n");
|
|
|
- /*
|
|
|
- * We can reflash from this state, so do not
|
|
|
- * abort init
|
|
|
- */
|
|
|
- data->in_bootloader = true;
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* Attempt to exit bootloader into app mode */
|
|
|
- mxt_send_bootloader_cmd(data, false);
|
|
|
- msleep(MXT_FW_RESET_TIME);
|
|
|
- retry = true;
|
|
|
- goto retry_info;
|
|
|
+ /* OK, we are in bootloader, see if we can recover */
|
|
|
+ if (++recovery_attempts > 1) {
|
|
|
+ dev_err(&client->dev, "Could not recover from bootloader mode\n");
|
|
|
+ /*
|
|
|
+ * We can reflash from this state, so do not
|
|
|
+ * abort initialization.
|
|
|
+ */
|
|
|
+ data->in_bootloader = true;
|
|
|
+ return 0;
|
|
|
}
|
|
|
+
|
|
|
+ /* Attempt to exit bootloader into app mode */
|
|
|
+ mxt_send_bootloader_cmd(data, false);
|
|
|
+ msleep(MXT_FW_RESET_TIME);
|
|
|
}
|
|
|
|
|
|
/* Get object table information */
|
|
@@ -1687,13 +1712,18 @@ retry_bootloader:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
- mxt_acquire_irq(data);
|
|
|
+ error = mxt_acquire_irq(data);
|
|
|
if (error)
|
|
|
goto err_free_object_table;
|
|
|
|
|
|
- request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
|
|
|
- &data->client->dev, GFP_KERNEL, data,
|
|
|
- mxt_config_cb);
|
|
|
+ error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
|
|
|
+ &client->dev, GFP_KERNEL, data,
|
|
|
+ mxt_config_cb);
|
|
|
+ if (error) {
|
|
|
+ dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
|
|
|
+ error);
|
|
|
+ goto err_free_object_table;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
|