Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d79e7e47 authored by Benson Leung's avatar Benson Leung Committed by Dmitry Torokhov
Browse files

Input: atmel_mxt_ts - wait for CHG assert in mxt_check_bootloader



The driver should not immediately read bootloader status when in
Application Update Mode. The CHG line will assert when the device has made
a state transition and is ready to report a new status via i2c.

This change adds a wait for completion in mxt_check_bootloader, and changes
the mxt_interrupt handler to signal the completion.

Signed-off-by: default avatarBenson Leung <bleung@chromium.org>
Signed-off-by: default avatarDaniel Kurtz <djkurtz@chromium.org>
Signed-off-by: default avatarNick Dyer <nick.dyer@itdev.co.uk>
Acked-by: default avatarYufeng Shen <miletus@chromium.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 82c2c0d6
Loading
Loading
Loading
Loading
+81 −21
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
@@ -246,16 +248,19 @@ struct mxt_data {
	const struct mxt_platform_data *pdata;
	struct mxt_object *object_table;
	struct mxt_info info;

	unsigned int irq;
	unsigned int max_x;
	unsigned int max_y;
	bool in_bootloader;

	/* Cached parameters from object table */
	u8 T6_reportid;
	u8 T9_reportid_min;
	u8 T9_reportid_max;
	u8 T19_reportid;

	/* for fw update in bootloader */
	struct completion bl_completion;
};

static size_t mxt_obj_size(const struct mxt_object *obj)
@@ -339,12 +344,50 @@ static void mxt_dump_message(struct device *dev,
		message->reportid, 7, message->message);
}

static int mxt_check_bootloader(struct i2c_client *client,
				     unsigned int state)
static int mxt_wait_for_chg(struct mxt_data *data, unsigned int timeout_ms)
{
	struct device *dev = &data->client->dev;
	struct completion *comp = &data->bl_completion;
	unsigned long timeout = msecs_to_jiffies(timeout_ms);
	long ret;

	ret = wait_for_completion_interruptible_timeout(comp, timeout);
	if (ret < 0) {
		return ret;
	} else if (ret == 0) {
		dev_err(dev, "Wait for completion timed out.\n");
		return -ETIMEDOUT;
	}
	return 0;
}

static int mxt_check_bootloader(struct mxt_data *data, unsigned int state)
{
	struct i2c_client *client = data->client;
	u8 val;
	int ret;

recheck:
	if (state != MXT_WAITING_BOOTLOAD_CMD) {
		/*
		 * 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_chg(data, 300);
		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(&client->dev, "Update wait error %d\n", ret);
			return ret;
		}
	}

	if (i2c_master_recv(client, &val, 1) != 1) {
		dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
		return -EIO;
@@ -590,9 +633,8 @@ static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg)
	return (id >= data->T9_reportid_min && id <= data->T9_reportid_max);
}

static irqreturn_t mxt_interrupt(int irq, void *dev_id)
static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)
{
	struct mxt_data *data = dev_id;
	struct mxt_message message;
	const u8 *payload = &message.message[0];
	struct device *dev = &data->client->dev;
@@ -632,6 +674,19 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
	return IRQ_HANDLED;
}

static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
	struct mxt_data *data = dev_id;

	if (data->in_bootloader) {
		/* bootloader state transition completion */
		complete(&data->bl_completion);
		return IRQ_HANDLED;
	}

	return mxt_process_messages_until_invalid(data);
}

static int mxt_check_reg_init(struct mxt_data *data)
{
	const struct mxt_platform_data *pdata = data->pdata;
@@ -947,6 +1002,8 @@ static int mxt_load_fw(struct device *dev, const char *fn)
	}

	/* Change to the bootloader mode */
	data->in_bootloader = true;

	mxt_write_object(data, MXT_GEN_COMMAND_T6,
			MXT_COMMAND_RESET, MXT_BOOT_VALUE);
	msleep(MXT_RESET_TIME);
@@ -957,18 +1014,19 @@ static int mxt_load_fw(struct device *dev, const char *fn)
	else
		client->addr = MXT_BOOT_HIGH;

	ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
	reinit_completion(&data->bl_completion);

	ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD);
	if (ret)
		goto out;
		goto disable_irq;

	/* Unlock bootloader */
	mxt_unlock_bootloader(client);

	while (pos < fw->size) {
		ret = mxt_check_bootloader(client,
						MXT_WAITING_FRAME_DATA);
		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA);
		if (ret)
			goto out;
			goto disable_irq;

		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));

@@ -980,17 +1038,19 @@ static int mxt_load_fw(struct device *dev, const char *fn)
		/* Write one frame to device */
		mxt_fw_write(client, fw->data + pos, frame_size);

		ret = mxt_check_bootloader(client,
						MXT_FRAME_CRC_PASS);
		ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS);
		if (ret)
			goto out;
			goto disable_irq;

		pos += frame_size;

		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
	}

out:
	data->in_bootloader = false;

disable_irq:
	disable_irq(data->irq);
	release_firmware(fw);

	/* Change to slave address of application */
@@ -1009,8 +1069,6 @@ static ssize_t mxt_update_fw_store(struct device *dev,
	struct mxt_data *data = dev_get_drvdata(dev);
	int error;

	disable_irq(data->irq);

	error = mxt_load_fw(dev, MXT_FW_NAME);
	if (error) {
		dev_err(dev, "The firmware update failed(%d)\n", error);
@@ -1024,13 +1082,13 @@ static ssize_t mxt_update_fw_store(struct device *dev,
		mxt_free_object_table(data);

		mxt_initialize(data);
	}

		enable_irq(data->irq);

		error = mxt_make_highchg(data);
		if (error)
			return error;
	}

	return count;
}
@@ -1120,6 +1178,8 @@ static int mxt_probe(struct i2c_client *client,
	data->pdata = pdata;
	data->irq = client->irq;

	init_completion(&data->bl_completion);

	mxt_calc_resolution(data);

	error = mxt_initialize(data);