diff mbox series

[5/7] usb: typec: ucsi: add fw update needed check

Message ID 20190118011238.16210-1-ajayg@nvidia.com (mailing list archive)
State New, archived
Headers show
Series Add support for firmware update on Cypres CCGx | expand

Commit Message

Ajay Gupta Jan. 18, 2019, 1:12 a.m. UTC
This will be needed to check if latest fw is already flashed.

Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
---
 drivers/usb/typec/ucsi/ucsi_ccg.c | 139 ++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

Comments

Greg KH Jan. 18, 2019, 7:06 a.m. UTC | #1
On Thu, Jan 17, 2019 at 05:12:38PM -0800, Ajay Gupta wrote:
> This will be needed to check if latest fw is already flashed.

Very odd changelog text, please make this more detailed.

And your email threading broke here, did your email client change?

thanks,

greg k-h
Greg KH Jan. 18, 2019, 7:07 a.m. UTC | #2
On Thu, Jan 17, 2019 at 05:12:38PM -0800, Ajay Gupta wrote:
> This will be needed to check if latest fw is already flashed.
> 
> Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
> ---
>  drivers/usb/typec/ucsi/ucsi_ccg.c | 139 ++++++++++++++++++++++++++++++
>  1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
> index 5f341934a5af..6069a9f60d1e 100644
> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> @@ -9,6 +9,7 @@
>   */
>  #include <linux/acpi.h>
>  #include <linux/delay.h>
> +#include <linux/firmware.h>
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  #include <linux/pci.h>
> @@ -17,6 +18,8 @@
>  #include <asm/unaligned.h>
>  #include "ucsi.h"
>  
> +static int secondary_fw_min_ver = 41;
> +module_param(secondary_fw_min_ver, int, 0660);

Ick, never add new module parameters, this isn't the 1990's :)
Heikki Krogerus Jan. 18, 2019, 3:37 p.m. UTC | #3
On Thu, Jan 17, 2019 at 05:12:38PM -0800, Ajay Gupta wrote:
> This will be needed to check if latest fw is already flashed.
> 
> Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
> ---
>  drivers/usb/typec/ucsi/ucsi_ccg.c | 139 ++++++++++++++++++++++++++++++
>  1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
> index 5f341934a5af..6069a9f60d1e 100644
> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> @@ -9,6 +9,7 @@
>   */
>  #include <linux/acpi.h>
>  #include <linux/delay.h>
> +#include <linux/firmware.h>
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  #include <linux/pci.h>
> @@ -17,6 +18,8 @@
>  #include <asm/unaligned.h>
>  #include "ucsi.h"
>  
> +static int secondary_fw_min_ver = 41;
> +module_param(secondary_fw_min_ver, int, 0660);
>  #define CCGX_RAB_DEVICE_MODE			0x0000
>  #define CCGX_RAB_INTR_REG			0x0006
>  #define  DEV_INT				BIT(0)
> @@ -68,6 +71,27 @@
>  #define FW2_METADATA_ROW        0x1FE
>  #define FW_CFG_TABLE_SIG_SIZE	256
>  
> +enum enum_fw_mode {
> +	BOOT,   /* bootloader */
> +	FW1,    /* FW partition-1 */
> +	FW2,    /* FW partition-2 */
> +	FW_INVALID,
> +};
> +
> +enum enum_flash_mode {
> +	SECONDARY_BL,	/* update secondary using bootloader */
> +	SECONDARY,	/* update secondary using primary */
> +	PRIMARY,	/* update primary */
> +	FLASH_NOT_NEEDED,	/* update not required */
> +	FLASH_INVALID,
> +};
> +
> +static const char * const ccg_fw_names[] = {
> +	/* 0x00 */ "ccg_boot.cyacd",
> +	/* 0x01 */ "ccg_2.cyacd",
> +	/* 0x02 */ "ccg_1.cyacd",
> +};
> +
>  struct ccg_dev_info {
>  	u8 fw_mode:2;
>  	u8 two_pd_ports:2;
> @@ -95,6 +119,20 @@ struct version_info {
>  	struct version_format app;
>  };
>  
> +struct fw_config_table {
> +	u32 identity;
> +	u16 table_size;
> +	u8 fwct_version;
> +	u8 is_key_change;
> +	u8 guid[16];
> +	struct version_format base;
> +	struct version_format app;
> +	u8 primary_fw_digest[32];
> +	u32 key_exp_length;
> +	u8 key_modulus[256];
> +	u8 key_exp[4];
> +};
> +
>  /* CCGx response codes */
>  enum ccg_resp_code {
>  	CMD_NO_RESP             = 0x00,
> @@ -737,6 +775,107 @@ static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
>  	return 0;
>  }
>  
> +static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
> +				 struct version_format *app)
> +{
> +	const struct firmware *fw = NULL;
> +	struct device *dev = uc->dev;
> +	struct fw_config_table fw_cfg;
> +	u32 cur_version, new_version;
> +	bool is_later = false;
> +
> +	if (request_firmware(&fw, fw_name, dev) != 0) {
> +		dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
> +		return false;
> +	}
> +
> +	/*
> +	 * check if signed fw
> +	 * last part of fw image is fw cfg table and signature
> +	 */
> +	if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
> +		goto not_signed_fw;
> +
> +	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
> +	       sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));
> +
> +	if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
> +		dev_info(dev, "not a signed image\n");
> +		goto not_signed_fw;
> +	}
> +
> +	/* compare input version with FWCT version */
> +	cur_version = app->build | (app->patch << 16) |
> +			((app->min | (app->maj << 4)) << 24);
> +
> +	new_version = fw_cfg.app.build | (fw_cfg.app.patch << 16) |
> +			((fw_cfg.app.min | (fw_cfg.app.maj << 4)) << 24);
> +
> +	dev_dbg(dev, "compare current %08x and new version %08x\n",
> +		cur_version, new_version);
> +
> +	if (new_version > cur_version) {
> +		dev_dbg(dev, "new firmware file version is later\n");
> +		is_later = true;
> +	} else {
> +		dev_dbg(dev, "new firmware file version is same or earlier\n");
> +	}
> +
> +not_signed_fw:
> +	release_firmware(fw);
> +	return is_later;
> +}
> +
> +static int ccg_fw_update_needed(struct ucsi_ccg *uc,
> +				enum enum_flash_mode *mode)
> +{
> +	struct device *dev = uc->dev;
> +	int err;
> +	struct version_info version[3];
> +
> +	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
> +		       sizeof(uc->info));
> +	if (err) {
> +		dev_err(dev, "read device mode failed\n");
> +		return err;
> +	}
> +
> +	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
> +		       sizeof(version));
> +	if (err) {
> +		dev_err(dev, "read device mode failed\n");
> +		return err;
> +	}
> +
> +	dev_dbg(dev, "check if fw upgrade required %x %x %x %x %x %x %x %x\n",
> +		version[FW1].base.build, version[FW1].base.patch,
> +		version[FW1].base.min, version[FW1].base.maj,
> +		version[FW2].app.build, version[FW2].app.patch,
> +		version[FW2].app.min, version[FW2].app.maj);
> +
> +	if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
> +		   sizeof(struct version_info)) == 0) {
> +		dev_info(dev, "secondary fw is not flashed\n");
> +		*mode = SECONDARY_BL;
> +	} else if (version[FW1].base.build < secondary_fw_min_ver) {
> +		dev_info(dev, "secondary fw version is too low (< %d)\n",
> +			 secondary_fw_min_ver);
> +		*mode = SECONDARY;
> +	} else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
> +		   sizeof(struct version_info)) == 0) {
> +		dev_info(dev, "primary fw is not flashed\n");
> +		*mode = PRIMARY;
> +	} else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
> +		   &version[FW2].app)) {
> +		dev_info(dev, "found primary fw with later version\n");
> +		*mode = PRIMARY;
> +	} else {
> +		dev_info(dev, "secondary and primary fw are the latest\n");
> +		*mode = FLASH_NOT_NEEDED;
> +	}
> +	return 0;
> +}

Before sending a new version, can you first explain what are you
trying to do here?

I would assume that you just want to read the version of your current
fw, and if there is a newer version of the firmware available, you
upgrade it, and that's it.

Perhaps I'm missing something?


thanks,
Ajay Gupta Jan. 24, 2019, 5:47 p.m. UTC | #4
Hi Greg

-off-by: Ajay Gupta <ajayg@nvidia.com>
> > ---
> >  drivers/usb/typec/ucsi/ucsi_ccg.c | 139
> > ++++++++++++++++++++++++++++++
> >  1 file changed, 139 insertions(+)
> >
> > diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c
> > b/drivers/usb/typec/ucsi/ucsi_ccg.c
> > index 5f341934a5af..6069a9f60d1e 100644
> > --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> > +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> > @@ -9,6 +9,7 @@
> >   */
> >  #include <linux/acpi.h>
> >  #include <linux/delay.h>
> > +#include <linux/firmware.h>
> >  #include <linux/i2c.h>
> >  #include <linux/module.h>
> >  #include <linux/pci.h>
> > @@ -17,6 +18,8 @@
> >  #include <asm/unaligned.h>
> >  #include "ucsi.h"
> >
> > +static int secondary_fw_min_ver = 41;
> > +module_param(secondary_fw_min_ver, int, 0660);
> 
> Ick, never add new module parameters, this isn't the 1990's :)
Ok 
Ajay Gupta Jan. 24, 2019, 5:54 p.m. UTC | #5
Hi Heikki,

> > Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
> > ---
> >  drivers/usb/typec/ucsi/ucsi_ccg.c | 139
> > ++++++++++++++++++++++++++++++
> >  1 file changed, 139 insertions(+)
> >
> > diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c
> > b/drivers/usb/typec/ucsi/ucsi_ccg.c
> > index 5f341934a5af..6069a9f60d1e 100644
> > --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> > +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> > @@ -9,6 +9,7 @@
> >   */
> >  #include <linux/acpi.h>
> >  #include <linux/delay.h>
> > +#include <linux/firmware.h>
> >  #include <linux/i2c.h>
> >  #include <linux/module.h>
> >  #include <linux/pci.h>
> > @@ -17,6 +18,8 @@
> >  #include <asm/unaligned.h>
> >  #include "ucsi.h"
> >
> > +static int secondary_fw_min_ver = 41;
> > +module_param(secondary_fw_min_ver, int, 0660);
> >  #define CCGX_RAB_DEVICE_MODE			0x0000
> >  #define CCGX_RAB_INTR_REG			0x0006
> >  #define  DEV_INT				BIT(0)
> > @@ -68,6 +71,27 @@
> >  #define FW2_METADATA_ROW        0x1FE
> >  #define FW_CFG_TABLE_SIG_SIZE	256
> >
> > +enum enum_fw_mode {
> > +	BOOT,   /* bootloader */
> > +	FW1,    /* FW partition-1 */
> > +	FW2,    /* FW partition-2 */
> > +	FW_INVALID,
> > +};
> > +
> > +enum enum_flash_mode {
> > +	SECONDARY_BL,	/* update secondary using bootloader */
> > +	SECONDARY,	/* update secondary using primary */
> > +	PRIMARY,	/* update primary */
> > +	FLASH_NOT_NEEDED,	/* update not required */
> > +	FLASH_INVALID,
> > +};
> > +
> > +static const char * const ccg_fw_names[] = {
> > +	/* 0x00 */ "ccg_boot.cyacd",
> > +	/* 0x01 */ "ccg_2.cyacd",
> > +	/* 0x02 */ "ccg_1.cyacd",
> > +};
> > +
> >  struct ccg_dev_info {
> >  	u8 fw_mode:2;
> >  	u8 two_pd_ports:2;
> > @@ -95,6 +119,20 @@ struct version_info {
> >  	struct version_format app;
> >  };
> >
> > +struct fw_config_table {
> > +	u32 identity;
> > +	u16 table_size;
> > +	u8 fwct_version;
> > +	u8 is_key_change;
> > +	u8 guid[16];
> > +	struct version_format base;
> > +	struct version_format app;
> > +	u8 primary_fw_digest[32];
> > +	u32 key_exp_length;
> > +	u8 key_modulus[256];
> > +	u8 key_exp[4];
> > +};
> > +
> >  /* CCGx response codes */
> >  enum ccg_resp_code {
> >  	CMD_NO_RESP             = 0x00,
> > @@ -737,6 +775,107 @@ static int ccg_cmd_validate_fw(struct ucsi_ccg *uc,
> unsigned int fwid)
> >  	return 0;
> >  }
> >
> > +static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
> > +				 struct version_format *app)
> > +{
> > +	const struct firmware *fw = NULL;
> > +	struct device *dev = uc->dev;
> > +	struct fw_config_table fw_cfg;
> > +	u32 cur_version, new_version;
> > +	bool is_later = false;
> > +
> > +	if (request_firmware(&fw, fw_name, dev) != 0) {
> > +		dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
> > +		return false;
> > +	}
> > +
> > +	/*
> > +	 * check if signed fw
> > +	 * last part of fw image is fw cfg table and signature
> > +	 */
> > +	if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
> > +		goto not_signed_fw;
> > +
> > +	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
> > +	       sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));
> > +
> > +	if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
> > +		dev_info(dev, "not a signed image\n");
> > +		goto not_signed_fw;
> > +	}
> > +
> > +	/* compare input version with FWCT version */
> > +	cur_version = app->build | (app->patch << 16) |
> > +			((app->min | (app->maj << 4)) << 24);
> > +
> > +	new_version = fw_cfg.app.build | (fw_cfg.app.patch << 16) |
> > +			((fw_cfg.app.min | (fw_cfg.app.maj << 4)) << 24);
> > +
> > +	dev_dbg(dev, "compare current %08x and new version %08x\n",
> > +		cur_version, new_version);
> > +
> > +	if (new_version > cur_version) {
> > +		dev_dbg(dev, "new firmware file version is later\n");
> > +		is_later = true;
> > +	} else {
> > +		dev_dbg(dev, "new firmware file version is same or earlier\n");
> > +	}
> > +
> > +not_signed_fw:
> > +	release_firmware(fw);
> > +	return is_later;
> > +}
> > +
> > +static int ccg_fw_update_needed(struct ucsi_ccg *uc,
> > +				enum enum_flash_mode *mode)
> > +{
> > +	struct device *dev = uc->dev;
> > +	int err;
> > +	struct version_info version[3];
> > +
> > +	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
> > +		       sizeof(uc->info));
> > +	if (err) {
> > +		dev_err(dev, "read device mode failed\n");
> > +		return err;
> > +	}
> > +
> > +	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
> > +		       sizeof(version));
> > +	if (err) {
> > +		dev_err(dev, "read device mode failed\n");
> > +		return err;
> > +	}
> > +
> > +	dev_dbg(dev, "check if fw upgrade required %x %x %x %x %x %x %x
> %x\n",
> > +		version[FW1].base.build, version[FW1].base.patch,
> > +		version[FW1].base.min, version[FW1].base.maj,
> > +		version[FW2].app.build, version[FW2].app.patch,
> > +		version[FW2].app.min, version[FW2].app.maj);
> > +
> > +	if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
> > +		   sizeof(struct version_info)) == 0) {
> > +		dev_info(dev, "secondary fw is not flashed\n");
> > +		*mode = SECONDARY_BL;
> > +	} else if (version[FW1].base.build < secondary_fw_min_ver) {
> > +		dev_info(dev, "secondary fw version is too low (< %d)\n",
> > +			 secondary_fw_min_ver);
> > +		*mode = SECONDARY;
> > +	} else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
> > +		   sizeof(struct version_info)) == 0) {
> > +		dev_info(dev, "primary fw is not flashed\n");
> > +		*mode = PRIMARY;
> > +	} else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
> > +		   &version[FW2].app)) {
> > +		dev_info(dev, "found primary fw with later version\n");
> > +		*mode = PRIMARY;
> > +	} else {
> > +		dev_info(dev, "secondary and primary fw are the latest\n");
> > +		*mode = FLASH_NOT_NEEDED;
> > +	}
> > +	return 0;
> > +}
> 
> Before sending a new version, can you first explain what are you trying to do
> here?
We are reading the current flashed firmware version of both primary and secondary
and comparing it against the new firmware files. If the new firmware versions are later
than the currently flashed then only we update the firmware.
 
> I would assume that you just want to read the version of your current fw, and if
> there is a newer version of the firmware available, you upgrade it, and that's it.
That's correct.

> Perhaps I'm missing something?
I think you missed that there are two firmware, primary and secondary. 

Thanks
> nvpublic 
> 
> 
> thanks,
> 
> --
> heikki
Ajay Gupta Jan. 24, 2019, 6:01 p.m. UTC | #6
Hi Greg,

> On Thu, Jan 17, 2019 at 05:12:38PM -0800, Ajay Gupta wrote:
> > This will be needed to check if latest fw is already flashed.
> 
> Very odd changelog text, please make this more detailed.
Sure.

> And your email threading broke here, did your email client change?
There was some error from my email client and I had to resend few changes.
It has been resolved now.

Thanks
>  nvpublic
> thanks,
> 
> greg k-h
diff mbox series

Patch

diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 5f341934a5af..6069a9f60d1e 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -9,6 +9,7 @@ 
  */
 #include <linux/acpi.h>
 #include <linux/delay.h>
+#include <linux/firmware.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/pci.h>
@@ -17,6 +18,8 @@ 
 #include <asm/unaligned.h>
 #include "ucsi.h"
 
+static int secondary_fw_min_ver = 41;
+module_param(secondary_fw_min_ver, int, 0660);
 #define CCGX_RAB_DEVICE_MODE			0x0000
 #define CCGX_RAB_INTR_REG			0x0006
 #define  DEV_INT				BIT(0)
@@ -68,6 +71,27 @@ 
 #define FW2_METADATA_ROW        0x1FE
 #define FW_CFG_TABLE_SIG_SIZE	256
 
+enum enum_fw_mode {
+	BOOT,   /* bootloader */
+	FW1,    /* FW partition-1 */
+	FW2,    /* FW partition-2 */
+	FW_INVALID,
+};
+
+enum enum_flash_mode {
+	SECONDARY_BL,	/* update secondary using bootloader */
+	SECONDARY,	/* update secondary using primary */
+	PRIMARY,	/* update primary */
+	FLASH_NOT_NEEDED,	/* update not required */
+	FLASH_INVALID,
+};
+
+static const char * const ccg_fw_names[] = {
+	/* 0x00 */ "ccg_boot.cyacd",
+	/* 0x01 */ "ccg_2.cyacd",
+	/* 0x02 */ "ccg_1.cyacd",
+};
+
 struct ccg_dev_info {
 	u8 fw_mode:2;
 	u8 two_pd_ports:2;
@@ -95,6 +119,20 @@  struct version_info {
 	struct version_format app;
 };
 
+struct fw_config_table {
+	u32 identity;
+	u16 table_size;
+	u8 fwct_version;
+	u8 is_key_change;
+	u8 guid[16];
+	struct version_format base;
+	struct version_format app;
+	u8 primary_fw_digest[32];
+	u32 key_exp_length;
+	u8 key_modulus[256];
+	u8 key_exp[4];
+};
+
 /* CCGx response codes */
 enum ccg_resp_code {
 	CMD_NO_RESP             = 0x00,
@@ -737,6 +775,107 @@  static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
 	return 0;
 }
 
+static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
+				 struct version_format *app)
+{
+	const struct firmware *fw = NULL;
+	struct device *dev = uc->dev;
+	struct fw_config_table fw_cfg;
+	u32 cur_version, new_version;
+	bool is_later = false;
+
+	if (request_firmware(&fw, fw_name, dev) != 0) {
+		dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
+		return false;
+	}
+
+	/*
+	 * check if signed fw
+	 * last part of fw image is fw cfg table and signature
+	 */
+	if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
+		goto not_signed_fw;
+
+	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
+	       sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));
+
+	if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
+		dev_info(dev, "not a signed image\n");
+		goto not_signed_fw;
+	}
+
+	/* compare input version with FWCT version */
+	cur_version = app->build | (app->patch << 16) |
+			((app->min | (app->maj << 4)) << 24);
+
+	new_version = fw_cfg.app.build | (fw_cfg.app.patch << 16) |
+			((fw_cfg.app.min | (fw_cfg.app.maj << 4)) << 24);
+
+	dev_dbg(dev, "compare current %08x and new version %08x\n",
+		cur_version, new_version);
+
+	if (new_version > cur_version) {
+		dev_dbg(dev, "new firmware file version is later\n");
+		is_later = true;
+	} else {
+		dev_dbg(dev, "new firmware file version is same or earlier\n");
+	}
+
+not_signed_fw:
+	release_firmware(fw);
+	return is_later;
+}
+
+static int ccg_fw_update_needed(struct ucsi_ccg *uc,
+				enum enum_flash_mode *mode)
+{
+	struct device *dev = uc->dev;
+	int err;
+	struct version_info version[3];
+
+	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
+		       sizeof(uc->info));
+	if (err) {
+		dev_err(dev, "read device mode failed\n");
+		return err;
+	}
+
+	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
+		       sizeof(version));
+	if (err) {
+		dev_err(dev, "read device mode failed\n");
+		return err;
+	}
+
+	dev_dbg(dev, "check if fw upgrade required %x %x %x %x %x %x %x %x\n",
+		version[FW1].base.build, version[FW1].base.patch,
+		version[FW1].base.min, version[FW1].base.maj,
+		version[FW2].app.build, version[FW2].app.patch,
+		version[FW2].app.min, version[FW2].app.maj);
+
+	if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
+		   sizeof(struct version_info)) == 0) {
+		dev_info(dev, "secondary fw is not flashed\n");
+		*mode = SECONDARY_BL;
+	} else if (version[FW1].base.build < secondary_fw_min_ver) {
+		dev_info(dev, "secondary fw version is too low (< %d)\n",
+			 secondary_fw_min_ver);
+		*mode = SECONDARY;
+	} else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
+		   sizeof(struct version_info)) == 0) {
+		dev_info(dev, "primary fw is not flashed\n");
+		*mode = PRIMARY;
+	} else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
+		   &version[FW2].app)) {
+		dev_info(dev, "found primary fw with later version\n");
+		*mode = PRIMARY;
+	} else {
+		dev_info(dev, "secondary and primary fw are the latest\n");
+		*mode = FLASH_NOT_NEEDED;
+	}
+	return 0;
+}
+
 static int ucsi_ccg_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
 {