Skip to content

Commit

Permalink
Input: atmel_mxt_ts - make bootloader interrupt driven
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Dyer <nick.dyer@itdev.co.uk>
  • Loading branch information
ndyer committed Apr 26, 2016
1 parent dda8453 commit 67a3eea
Showing 1 changed file with 61 additions and 59 deletions.
120 changes: 61 additions & 59 deletions drivers/input/touchscreen/atmel_mxt_ts.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <asm/unaligned.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>

/* Configuration file */
#define MXT_CFG_MAGIC "OBP_RAW V1"
Expand Down Expand Up @@ -190,6 +191,7 @@ enum t100_type {
#define MXT_REGULATOR_DELAY 150 /* msec */
#define MXT_CHG_DELAY 100 /* msec */
#define MXT_POWERON_DELAY 150 /* msec */
#define MXT_BOOTLOADER_WAIT 36E5 /* 1 minute */

/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
Expand Down Expand Up @@ -250,14 +252,16 @@ struct mxt_fw_frame {

/* Firmware update context */
struct mxt_flash {
struct mxt_data *data;
const struct firmware *fw;
struct mxt_fw_frame *frame;
loff_t pos;
size_t frame_size;
unsigned int count;
unsigned int retry;
u8 previous;
bool complete;
struct completion flash_completion;
struct delayed_work work;
};

/* Each client has this additional data */
Expand Down Expand Up @@ -297,6 +301,7 @@ struct mxt_data {
struct regulator *reg_avdd;
char *fw_name;
char *cfg_name;
struct mxt_flash *flash;

/* Cached parameters from object table */
u16 T5_address;
Expand Down Expand Up @@ -530,28 +535,17 @@ static int mxt_write_firmware_frame(struct mxt_data *data, struct mxt_flash *f)
f->frame_size);
}

static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f)
static int mxt_check_bootloader(struct mxt_data *data)
{
struct device *dev = &data->client->dev;
struct mxt_flash *f = data->flash;
u8 state;
int 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.
*/
ret = mxt_wait_for_completion(data, &data->chg_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_warn(dev, "Update wait error %d\n", ret);
/* Handle interrupt after download/flash process */
if (f->pos >= f->fw->size) {
complete(&f->flash_completion);
return 0;
}

ret = mxt_bootloader_read(data, &state, 1);
Expand Down Expand Up @@ -597,14 +591,12 @@ static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f)
f->pos += f->frame_size;
f->count++;

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

break;

Expand All @@ -626,6 +618,9 @@ static int mxt_check_bootloader(struct mxt_data *data, struct mxt_flash *f)

f->previous = state;

/* Poll after 0.1s if no interrupt received */
schedule_delayed_work(&f->work, HZ / 10);

return 0;

unexpected:
Expand Down Expand Up @@ -1335,8 +1330,12 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)

complete(&data->chg_completion);

if (data->in_bootloader)
return IRQ_HANDLED;
if (data->in_bootloader) {
if (data->flash && &data->flash->work)
cancel_delayed_work_sync(&data->flash->work);

return IRQ_RETVAL(mxt_check_bootloader(data));
}

if (!data->object_table)
return IRQ_HANDLED;
Expand Down Expand Up @@ -2770,16 +2769,13 @@ static int mxt_enter_bootloader(struct mxt_data *data)
if (data->pdata->suspend_mode == MXT_SUSPEND_REGULATOR)
mxt_regulator_enable(data);

if (data->pdata->suspend_mode == MXT_SUSPEND_DEEP_SLEEP)
enable_irq(data->irq);

data->suspended = false;
}

if (!data->in_bootloader) {
/* Change to the bootloader mode */
data->in_bootloader = true;
disable_irq(data->irq);

/* Change to the bootloader mode */
ret = mxt_t6_command(data, MXT_COMMAND_RESET,
MXT_BOOT_VALUE, false);
if (ret)
Expand All @@ -2792,67 +2788,73 @@ static int mxt_enter_bootloader(struct mxt_data *data)
if (ret)
return ret;

data->in_bootloader = true;
mxt_sysfs_remove(data);
mxt_free_input_device(data);
mxt_free_object_table(data);
} else {
enable_irq(data->irq);
}

reinit_completion(&data->chg_completion);
dev_dbg(&data->client->dev, "Entered bootloader\n");

return 0;
}

static void mxt_fw_work(struct work_struct *work)
{
struct mxt_flash *f =
container_of(work, struct mxt_flash, work.work);

mxt_check_bootloader(f->data);
}

static int mxt_load_fw(struct device *dev)
{
struct mxt_data *data = dev_get_drvdata(dev);
struct mxt_flash f = { 0, };
int ret;

ret = request_firmware(&f.fw, data->fw_name, dev);
data->flash = devm_kzalloc(dev, sizeof(struct mxt_flash), GFP_KERNEL);
if (!data->flash)
return -ENOMEM;

data->flash->data = data;

ret = request_firmware(&data->flash->fw, data->fw_name, dev);
if (ret) {
dev_err(dev, "Unable to open firmware %s\n", data->fw_name);
return ret;
goto free;
}

/* Check for incorrect enc file */
ret = mxt_check_firmware_format(dev, f.fw);
ret = mxt_check_firmware_format(dev, data->flash->fw);
if (ret)
goto release_firmware;

ret = mxt_enter_bootloader(data);
if (ret)
goto release_firmware;
init_completion(&data->flash->flash_completion);
INIT_DELAYED_WORK(&data->flash->work, mxt_fw_work);
reinit_completion(&data->flash->flash_completion);

while (true) {
ret = mxt_check_bootloader(data, &f);
if (!data->in_bootloader) {
ret = mxt_enter_bootloader(data);
if (ret)
return ret;

if (f.complete)
break;
goto release_firmware;
}

/* Wait for flash. */
ret = mxt_wait_for_completion(data, &data->chg_completion,
MXT_FW_RESET_TIME);
if (ret)
goto disable_irq;
enable_irq(data->irq);

/* Poll after 0.1s if no interrupt received */
schedule_delayed_work(&data->flash->work, HZ / 10);

/*
* Wait for device to reset. Some bootloader versions do not assert
* the CHG line after bootloading has finished, so ignore potential
* errors.
*/
mxt_wait_for_completion(data, &data->chg_completion, MXT_FW_RESET_TIME);
/* Wait for flash. */
ret = mxt_wait_for_completion(data, &data->flash->flash_completion,
MXT_BOOTLOADER_WAIT);

data->in_bootloader = false;
disable_irq:
disable_irq(data->irq);
cancel_delayed_work_sync(&data->flash->work);
data->in_bootloader = false;
release_firmware:
release_firmware(f.fw);
release_firmware(data->flash->fw);
free:
devm_kfree(dev, data->flash);
return ret;
}

Expand Down

0 comments on commit 67a3eea

Please sign in to comment.