Commit ac7e0839 authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

Input: ili210x - switch to using cleanup functions in firmware code

Start using __free() attributes to simplify the code and error handling.

Link: https://lore.kernel.org/r/20240609234757.610273-2-dmitry.torokhov@gmail.com


Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 17f5eebf
Loading
Loading
Loading
Loading
+63 −60
Original line number Diff line number Diff line
@@ -582,14 +582,12 @@ static ssize_t ili210x_calibrate(struct device *dev,
}
static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate);

static int ili251x_firmware_to_buffer(const struct firmware *fw,
				      u8 **buf, u16 *ac_end, u16 *df_end)
static const u8 *ili251x_firmware_to_buffer(const struct firmware *fw,
					    u16 *ac_end, u16 *df_end)
{
	const struct ihex_binrec *rec;
	u32 fw_addr, fw_last_addr = 0;
	u16 fw_len;
	u8 *fw_buf;
	int error;

	/*
	 * The firmware ihex blob can never be bigger than 64 kiB, so make this
@@ -597,9 +595,9 @@ static int ili251x_firmware_to_buffer(const struct firmware *fw,
	 * once, copy them all into this buffer at the right locations, and then
	 * do all operations on this linear buffer.
	 */
	fw_buf = kvmalloc(SZ_64K, GFP_KERNEL);
	u8* fw_buf __free(kvfree) = kvmalloc(SZ_64K, GFP_KERNEL);
	if (!fw_buf)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	rec = (const struct ihex_binrec *)fw->data;
	while (rec) {
@@ -607,10 +605,8 @@ static int ili251x_firmware_to_buffer(const struct firmware *fw,
		fw_len = be16_to_cpu(rec->len);

		/* The last 32 Byte firmware block can be 0xffe0 */
		if (fw_addr + fw_len > SZ_64K || fw_addr > SZ_64K - 32) {
			error = -EFBIG;
			goto err_big;
		}
		if (fw_addr + fw_len > SZ_64K || fw_addr > SZ_64K - 32)
			return ERR_PTR(-EFBIG);

		/* Find the last address before DF start address, that is AC end */
		if (fw_addr == 0xf000)
@@ -623,12 +619,8 @@ static int ili251x_firmware_to_buffer(const struct firmware *fw,

	/* DF end address is the last address in the firmware blob */
	*df_end = fw_addr + fw_len;
	*buf = fw_buf;
	return 0;

err_big:
	kvfree(fw_buf);
	return error;
	return_ptr(fw_buf);
}

/* Switch mode between Application and BootLoader */
@@ -691,7 +683,7 @@ static int ili251x_firmware_busy(struct i2c_client *client)
	return 0;
}

static int ili251x_firmware_write_to_ic(struct device *dev, u8 *fwbuf,
static int ili251x_firmware_write_to_ic(struct device *dev, const u8 *fwbuf,
					u16 start, u16 end, u8 dataflash)
{
	struct i2c_client *client = to_i2c_client(dev);
@@ -776,47 +768,17 @@ static void ili210x_hardware_reset(struct gpio_desc *reset_gpio)
	msleep(300);
}

static ssize_t ili210x_firmware_update_store(struct device *dev,
					     struct device_attribute *attr,
					     const char *buf, size_t count)
static int ili210x_do_firmware_update(struct ili210x *priv,
				      const u8 *fwbuf, u16 ac_end, u16 df_end)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct ili210x *priv = i2c_get_clientdata(client);
	const char *fwname = ILI251X_FW_FILENAME;
	const struct firmware *fw;
	u16 ac_end, df_end;
	u8 *fwbuf;
	struct i2c_client *client = priv->client;
	struct device *dev = &client->dev;
	int error;
	int i;

	error = request_ihex_firmware(&fw, fwname, dev);
	if (error) {
		dev_err(dev, "Failed to request firmware %s, error=%d\n",
			fwname, error);
		return error;
	}

	error = ili251x_firmware_to_buffer(fw, &fwbuf, &ac_end, &df_end);
	release_firmware(fw);
	if (error)
		return error;

	/*
	 * Disable touchscreen IRQ, so that we would not get spurious touch
	 * interrupt during firmware update, and so that the IRQ handler won't
	 * trigger and interfere with the firmware update. There is no bit in
	 * the touch controller to disable the IRQs during update, so we have
	 * to do it this way here.
	 */
	disable_irq(client->irq);

	dev_dbg(dev, "Firmware update started, firmware=%s\n", fwname);

	ili210x_hardware_reset(priv->reset_gpio);

	error = ili251x_firmware_reset(client);
	if (error)
		goto exit;
		return error;

	/* This may not succeed on first try, so re-try a few times. */
	for (i = 0; i < 5; i++) {
@@ -826,7 +788,7 @@ static ssize_t ili210x_firmware_update_store(struct device *dev,
	}

	if (error)
		goto exit;
		return error;

	dev_dbg(dev, "IC is now in BootLoader mode\n");

@@ -835,7 +797,7 @@ static ssize_t ili210x_firmware_update_store(struct device *dev,
	error = ili251x_firmware_write_to_ic(dev, fwbuf, 0xf000, df_end, 1);
	if (error) {
		dev_err(dev, "DF firmware update failed, error=%d\n", error);
		goto exit;
		return error;
	}

	dev_dbg(dev, "DataFlash firmware written\n");
@@ -843,7 +805,7 @@ static ssize_t ili210x_firmware_update_store(struct device *dev,
	error = ili251x_firmware_write_to_ic(dev, fwbuf, 0x2000, ac_end, 0);
	if (error) {
		dev_err(dev, "AC firmware update failed, error=%d\n", error);
		goto exit;
		return error;
	}

	dev_dbg(dev, "Application firmware written\n");
@@ -856,22 +818,63 @@ static ssize_t ili210x_firmware_update_store(struct device *dev,
	}

	if (error)
		goto exit;
		return error;

	dev_dbg(dev, "IC is now in Application mode\n");

	error = ili251x_firmware_update_cached_state(dev);
	if (error)
		goto exit;
		return error;

	error = count;
	return 0;
}

static ssize_t ili210x_firmware_update_store(struct device *dev,
					     struct device_attribute *attr,
					     const char *buf, size_t count)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct ili210x *priv = i2c_get_clientdata(client);
	const char *fwname = ILI251X_FW_FILENAME;
	u16 ac_end, df_end;
	int error;

	const struct firmware *fw __free(firmware) = NULL;
	error = request_ihex_firmware(&fw, fwname, dev);
	if (error) {
		dev_err(dev, "Failed to request firmware %s, error=%d\n",
			fwname, error);
		return error;
	}

	const u8* fwbuf __free(kvfree) =
			ili251x_firmware_to_buffer(fw, &ac_end, &df_end);
	error = PTR_ERR_OR_ZERO(fwbuf);
	if (error)
		return error;

	/*
	 * Disable touchscreen IRQ, so that we would not get spurious touch
	 * interrupt during firmware update, and so that the IRQ handler won't
	 * trigger and interfere with the firmware update. There is no bit in
	 * the touch controller to disable the IRQs during update, so we have
	 * to do it this way here.
	 */
	disable_irq(client->irq);

	dev_dbg(dev, "Firmware update started, firmware=%s\n", fwname);

	ili210x_hardware_reset(priv->reset_gpio);

	error = ili210x_do_firmware_update(priv, fwbuf, ac_end, df_end);

exit:
	ili210x_hardware_reset(priv->reset_gpio);

	dev_dbg(dev, "Firmware update ended, error=%i\n", error);

	enable_irq(client->irq);
	kvfree(fwbuf);
	return error;

	return error ?: count;
}

static DEVICE_ATTR(firmware_update, 0200, NULL, ili210x_firmware_update_store);