Skip to content

Commit

Permalink
Input: atmel_mxt_ts - improve bootloader state machine handling
Browse files Browse the repository at this point in the history
The code is much clearer if we switch on the actual state the bootloader
is in, rather than the state we want it to be in, and allows the removal
of a goto retry tangle.

Signed-off-by: Nick Dyer <[email protected]>
  • Loading branch information
ndyer committed Apr 26, 2016
1 parent a2d141f commit 463e15e
Showing 1 changed file with 99 additions and 86 deletions.
185 changes: 99 additions & 86 deletions drivers/input/touchscreen/atmel_mxt_ts.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ struct mxt_flash {
size_t frame_size;
unsigned int count;
unsigned int retry;
u8 previous;
bool complete;
};

/* Each client has this additional data */
Expand Down Expand Up @@ -514,67 +516,124 @@ static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
return 0;
}

static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
bool wait)
static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock);

static int mxt_write_firmware_frame(struct mxt_data *data, struct mxt_flash *f)
{
f->frame = (struct mxt_fw_frame *)(f->fw->data + f->pos);

/* Take account of CRC bytes */
f->frame_size = __be16_to_cpu(f->frame->size) + 2U;

/* Write one frame to device */
return mxt_bootloader_write(data, f->fw->data + f->pos,
f->frame_size);
}

static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f)
{
struct device *dev = &data->client->dev;
u8 val;
u8 state;
int ret;

recheck:
if (wait) {
/*
* In application update mode, the interrupt
* line signals state transitions. We must wait for the
* CHG assertion before reading the status byte.
* Once the status byte has been read, the line is deasserted.
*/
ret = mxt_wait_for_completion(data, &data->bl_completion,
MXT_FW_CHG_TIMEOUT);
if (ret) {
/*
* In application update mode, the interrupt
* line signals state transitions. We must wait for the
* CHG assertion before reading the status byte.
* Once the status byte has been read, the line is deasserted.
* TODO: handle -ERESTARTSYS better by terminating
* fw update process before returning to userspace
* by writing length 0x000 to device (iff we are in
* WAITING_FRAME_DATA state).
*/
ret = mxt_wait_for_completion(data, &data->bl_completion,
MXT_FW_CHG_TIMEOUT);
if (ret) {
/*
* TODO: handle -ERESTARTSYS better by terminating
* fw update process before returning to userspace
* by writing length 0x000 to device (iff we are in
* WAITING_FRAME_DATA state).
*/
dev_err(dev, "Update wait error %d\n", ret);
return ret;
}
dev_warn(dev, "Update wait error %d\n", ret);
}

ret = mxt_bootloader_read(data, &val, 1);
ret = mxt_bootloader_read(data, &state, 1);
if (ret)
return ret;

/* Remove don't care bits */
if (state & ~MXT_BOOT_STATUS_MASK)
state &= ~MXT_BOOT_STATUS_MASK;

switch (state) {
case MXT_WAITING_BOOTLOAD_CMD:
dev_info(dev, "Unlocking bootloader\n");
ret = mxt_send_bootloader_cmd(data, true);
if (ret)
return ret;

break;

case MXT_WAITING_FRAME_DATA:
case MXT_APP_CRC_FAIL:
val &= ~MXT_BOOT_STATUS_MASK;
if ((f->previous != MXT_WAITING_BOOTLOAD_CMD)
&& (f->previous != MXT_FRAME_CRC_PASS)
&& (f->previous != MXT_FRAME_CRC_FAIL))
goto unexpected;

ret = mxt_write_firmware_frame(data, f);
if (ret)
return ret;

break;

case MXT_FRAME_CRC_CHECK:
if (f->previous != MXT_WAITING_FRAME_DATA)
goto unexpected;
break;

case MXT_FRAME_CRC_PASS:
if (val == MXT_FRAME_CRC_CHECK) {
goto recheck;
} else if (val == MXT_FRAME_CRC_FAIL) {
dev_err(dev, "Bootloader CRC fail\n");
return -EINVAL;
if (f->previous != MXT_FRAME_CRC_CHECK)
goto unexpected;

/* Next frame */
f->retry = 0;
f->pos += f->frame_size;
f->count++;

if (f->pos >= f->fw->size) {
f->complete = true;
dev_info(dev, "Sent %u frames, %zu bytes\n",
f->count, f->fw->size);
} else if (f->count % 50 == 0) {
dev_dbg(dev, "Sent %u frames, %lld/%zu bytes\n",
f->count, f->pos, f->fw->size);
}

break;

case MXT_FRAME_CRC_FAIL:
if (f->retry > 20) {
dev_err(dev, "Retry count exceeded\n");
return -EIO;
}

/* Back off by 20ms per retry */
dev_dbg(dev, "Bootloader frame CRC failure\n");
f->retry++;
msleep(f->retry * 20);
break;

default:
return -EINVAL;
}

if (val != state) {
dev_err(dev, "Invalid bootloader state %02X != %02X\n",
val, state);
return -EINVAL;
}
f->previous = state;

return 0;

unexpected:
dev_err(dev, "Unexpected state transition\n");
return -EINVAL;
}

static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
{
int ret;
u8 buf[2];
Expand Down Expand Up @@ -2767,57 +2826,13 @@ static int mxt_load_fw(struct device *dev)
if (ret)
goto release_firmware;

ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
if (ret) {
/* Bootloader may still be unlocked from previous attempt */
ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
if (ret)
goto disable_irq;
} else {
dev_info(dev, "Unlocking bootloader\n");

/* Unlock bootloader */
ret = mxt_send_bootloader_cmd(data, true);
while (true) {
ret = mxt_check_bootloader(data, &f);
if (ret)
goto disable_irq;
}

while (f.pos < f.fw->size) {
f.frame = (struct mxt_fw_frame *)(f.fw->data + f.pos);

ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
if (ret)
goto disable_irq;

/* Take account of CRC bytes */
f.frame_size = __be16_to_cpu(f.frame->size) + 2U;

/* Write one frame to device */
ret = mxt_bootloader_write(data, f.fw->data + f.pos,
f.frame_size);
if (ret)
goto disable_irq;

ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
if (ret) {
f.retry++;

/* Back off by 20ms per retry */
msleep(f.retry * 20);

if (f.retry > 20) {
dev_err(dev, "Retry count exceeded\n");
goto disable_irq;
}
} else {
f.retry = 0;
f.pos += f.frame_size;
f.count++;
}
return ret;

if (f.count % 50 == 0)
dev_dbg(dev, "Sent %u frames, %lld/%zu bytes\n",
f.count, f.pos, f.fw->size);
if (f.complete)
break;
}

/* Wait for flash. */
Expand All @@ -2826,7 +2841,6 @@ static int mxt_load_fw(struct device *dev)
if (ret)
goto disable_irq;

dev_dbg(dev, "Sent %u frames, %lld bytes\n", f.count, f.pos);

/*
* Wait for device to reset. Some bootloader versions do not assert
Expand All @@ -2836,7 +2850,6 @@ static int mxt_load_fw(struct device *dev)
mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME);

data->in_bootloader = false;

disable_irq:
disable_irq(data->irq);
release_firmware:
Expand Down

0 comments on commit 463e15e

Please sign in to comment.