xref: /openbmc/linux/drivers/usb/typec/ucsi/ucsi_ccg.c (revision 7126a2ae)
1247c554aSAjay Gupta // SPDX-License-Identifier: GPL-2.0
2247c554aSAjay Gupta /*
3247c554aSAjay Gupta  * UCSI driver for Cypress CCGx Type-C controller
4247c554aSAjay Gupta  *
5247c554aSAjay Gupta  * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved.
6247c554aSAjay Gupta  * Author: Ajay Gupta <ajayg@nvidia.com>
7247c554aSAjay Gupta  *
8247c554aSAjay Gupta  * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c
9247c554aSAjay Gupta  */
10247c554aSAjay Gupta #include <linux/acpi.h>
11247c554aSAjay Gupta #include <linux/delay.h>
125c9ae5a8SAjay Gupta #include <linux/firmware.h>
13247c554aSAjay Gupta #include <linux/i2c.h>
14247c554aSAjay Gupta #include <linux/module.h>
15247c554aSAjay Gupta #include <linux/pci.h>
16247c554aSAjay Gupta #include <linux/platform_device.h>
17a94ecde4SAjay Gupta #include <linux/pm.h>
18a94ecde4SAjay Gupta #include <linux/pm_runtime.h>
19170a6726SAjay Gupta #include <linux/usb/typec_dp.h>
20247c554aSAjay Gupta 
21247c554aSAjay Gupta #include <asm/unaligned.h>
22247c554aSAjay Gupta #include "ucsi.h"
23247c554aSAjay Gupta 
245d438e20SAjay Gupta enum enum_fw_mode {
255d438e20SAjay Gupta 	BOOT,   /* bootloader */
265d438e20SAjay Gupta 	FW1,    /* FW partition-1 (contains secondary fw) */
275d438e20SAjay Gupta 	FW2,    /* FW partition-2 (contains primary fw) */
285d438e20SAjay Gupta 	FW_INVALID,
295d438e20SAjay Gupta };
305d438e20SAjay Gupta 
315c9ae5a8SAjay Gupta #define CCGX_RAB_DEVICE_MODE			0x0000
325c9ae5a8SAjay Gupta #define CCGX_RAB_INTR_REG			0x0006
335c9ae5a8SAjay Gupta #define  DEV_INT				BIT(0)
345c9ae5a8SAjay Gupta #define  PORT0_INT				BIT(1)
355c9ae5a8SAjay Gupta #define  PORT1_INT				BIT(2)
365c9ae5a8SAjay Gupta #define  UCSI_READ_INT				BIT(7)
375c9ae5a8SAjay Gupta #define CCGX_RAB_JUMP_TO_BOOT			0x0007
385c9ae5a8SAjay Gupta #define  TO_BOOT				'J'
395c9ae5a8SAjay Gupta #define  TO_ALT_FW				'A'
405c9ae5a8SAjay Gupta #define CCGX_RAB_RESET_REQ			0x0008
415c9ae5a8SAjay Gupta #define  RESET_SIG				'R'
425c9ae5a8SAjay Gupta #define  CMD_RESET_I2C				0x0
435c9ae5a8SAjay Gupta #define  CMD_RESET_DEV				0x1
445c9ae5a8SAjay Gupta #define CCGX_RAB_ENTER_FLASHING			0x000A
455c9ae5a8SAjay Gupta #define  FLASH_ENTER_SIG			'P'
465c9ae5a8SAjay Gupta #define CCGX_RAB_VALIDATE_FW			0x000B
475c9ae5a8SAjay Gupta #define CCGX_RAB_FLASH_ROW_RW			0x000C
485c9ae5a8SAjay Gupta #define  FLASH_SIG				'F'
495c9ae5a8SAjay Gupta #define  FLASH_RD_CMD				0x0
505c9ae5a8SAjay Gupta #define  FLASH_WR_CMD				0x1
515c9ae5a8SAjay Gupta #define  FLASH_FWCT1_WR_CMD			0x2
525c9ae5a8SAjay Gupta #define  FLASH_FWCT2_WR_CMD			0x3
535c9ae5a8SAjay Gupta #define  FLASH_FWCT_SIG_WR_CMD			0x4
545c9ae5a8SAjay Gupta #define CCGX_RAB_READ_ALL_VER			0x0010
555c9ae5a8SAjay Gupta #define CCGX_RAB_READ_FW2_VER			0x0020
565c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL			0x0039
575c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START		BIT(0)
585c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP		BIT(1)
595c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset)	(0xf000 | ((offset) & 0xff))
605c9ae5a8SAjay Gupta #define REG_FLASH_RW_MEM        0x0200
615c9ae5a8SAjay Gupta #define DEV_REG_IDX				CCGX_RAB_DEVICE_MODE
625c9ae5a8SAjay Gupta #define CCGX_RAB_PDPORT_ENABLE			0x002C
635c9ae5a8SAjay Gupta #define  PDPORT_1		BIT(0)
645c9ae5a8SAjay Gupta #define  PDPORT_2		BIT(1)
655c9ae5a8SAjay Gupta #define CCGX_RAB_RESPONSE			0x007E
665c9ae5a8SAjay Gupta #define  ASYNC_EVENT				BIT(7)
675c9ae5a8SAjay Gupta 
685c9ae5a8SAjay Gupta /* CCGx events & async msg codes */
695c9ae5a8SAjay Gupta #define RESET_COMPLETE		0x80
705c9ae5a8SAjay Gupta #define EVENT_INDEX		RESET_COMPLETE
715c9ae5a8SAjay Gupta #define PORT_CONNECT_DET	0x84
725c9ae5a8SAjay Gupta #define PORT_DISCONNECT_DET	0x85
735c9ae5a8SAjay Gupta #define ROLE_SWAP_COMPELETE	0x87
745c9ae5a8SAjay Gupta 
755c9ae5a8SAjay Gupta /* ccg firmware */
765c9ae5a8SAjay Gupta #define CYACD_LINE_SIZE         527
775c9ae5a8SAjay Gupta #define CCG4_ROW_SIZE           256
785c9ae5a8SAjay Gupta #define FW1_METADATA_ROW        0x1FF
795c9ae5a8SAjay Gupta #define FW2_METADATA_ROW        0x1FE
805c9ae5a8SAjay Gupta #define FW_CFG_TABLE_SIG_SIZE	256
815c9ae5a8SAjay Gupta 
825c9ae5a8SAjay Gupta static int secondary_fw_min_ver = 41;
835c9ae5a8SAjay Gupta 
845c9ae5a8SAjay Gupta enum enum_flash_mode {
855c9ae5a8SAjay Gupta 	SECONDARY_BL,	/* update secondary using bootloader */
865c9ae5a8SAjay Gupta 	PRIMARY,	/* update primary using secondary */
875c9ae5a8SAjay Gupta 	SECONDARY,	/* update secondary using primary */
885c9ae5a8SAjay Gupta 	FLASH_NOT_NEEDED,	/* update not required */
895c9ae5a8SAjay Gupta 	FLASH_INVALID,
905c9ae5a8SAjay Gupta };
915c9ae5a8SAjay Gupta 
925c9ae5a8SAjay Gupta static const char * const ccg_fw_names[] = {
935c9ae5a8SAjay Gupta 	"ccg_boot.cyacd",
945c9ae5a8SAjay Gupta 	"ccg_primary.cyacd",
955c9ae5a8SAjay Gupta 	"ccg_secondary.cyacd"
965c9ae5a8SAjay Gupta };
975c9ae5a8SAjay Gupta 
985d438e20SAjay Gupta struct ccg_dev_info {
995d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0)
1005d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT)
1015d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2)
1025d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT)
1035d438e20SAjay Gupta 	u8 mode;
1045d438e20SAjay Gupta 	u8 bl_mode;
1055d438e20SAjay Gupta 	__le16 silicon_id;
1065d438e20SAjay Gupta 	__le16 bl_last_row;
1075d438e20SAjay Gupta } __packed;
1085d438e20SAjay Gupta 
1095d438e20SAjay Gupta struct version_format {
1105d438e20SAjay Gupta 	__le16 build;
1115d438e20SAjay Gupta 	u8 patch;
1125d438e20SAjay Gupta 	u8 ver;
113f0e4cd94SAjay Gupta #define CCG_VERSION_PATCH(x) ((x) << 16)
114f0e4cd94SAjay Gupta #define CCG_VERSION(x)	((x) << 24)
1155d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0)
1165d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT)
1175d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4)
1185d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT)
1195d438e20SAjay Gupta } __packed;
1205d438e20SAjay Gupta 
121f0e4cd94SAjay Gupta /*
122f0e4cd94SAjay Gupta  * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
123f0e4cd94SAjay Gupta  * of missing interrupt when a device is connected for runtime resume
124f0e4cd94SAjay Gupta  */
125f0e4cd94SAjay Gupta #define CCG_FW_BUILD_NVIDIA	(('n' << 8) | 'v')
126f0e4cd94SAjay Gupta #define CCG_OLD_FW_VERSION	(CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))
127f0e4cd94SAjay Gupta 
128691f43cbSSing-Han Chen /* Firmware for Tegra doesn't support UCSI ALT command, built
129691f43cbSSing-Han Chen  * for NVIDIA has known issue of reporting wrong capability info
130691f43cbSSing-Han Chen  */
131691f43cbSSing-Han Chen #define CCG_FW_BUILD_NVIDIA_TEGRA	(('g' << 8) | 'n')
132691f43cbSSing-Han Chen 
133706f4bbfSAjay Gupta /* Altmode offset for NVIDIA Function Test Board (FTB) */
134706f4bbfSAjay Gupta #define NVIDIA_FTB_DP_OFFSET	(2)
135706f4bbfSAjay Gupta #define NVIDIA_FTB_DBG_OFFSET	(3)
136706f4bbfSAjay Gupta 
1375d438e20SAjay Gupta struct version_info {
1385d438e20SAjay Gupta 	struct version_format base;
1395d438e20SAjay Gupta 	struct version_format app;
1405d438e20SAjay Gupta };
1415d438e20SAjay Gupta 
1425c9ae5a8SAjay Gupta struct fw_config_table {
1435c9ae5a8SAjay Gupta 	u32 identity;
1445c9ae5a8SAjay Gupta 	u16 table_size;
1455c9ae5a8SAjay Gupta 	u8 fwct_version;
1465c9ae5a8SAjay Gupta 	u8 is_key_change;
1475c9ae5a8SAjay Gupta 	u8 guid[16];
1485c9ae5a8SAjay Gupta 	struct version_format base;
1495c9ae5a8SAjay Gupta 	struct version_format app;
1505c9ae5a8SAjay Gupta 	u8 primary_fw_digest[32];
1515c9ae5a8SAjay Gupta 	u32 key_exp_length;
1525c9ae5a8SAjay Gupta 	u8 key_modulus[256];
1535c9ae5a8SAjay Gupta 	u8 key_exp[4];
1545c9ae5a8SAjay Gupta };
1555c9ae5a8SAjay Gupta 
1565c9ae5a8SAjay Gupta /* CCGx response codes */
1575c9ae5a8SAjay Gupta enum ccg_resp_code {
1585c9ae5a8SAjay Gupta 	CMD_NO_RESP             = 0x00,
1595c9ae5a8SAjay Gupta 	CMD_SUCCESS             = 0x02,
1605c9ae5a8SAjay Gupta 	FLASH_DATA_AVAILABLE    = 0x03,
1615c9ae5a8SAjay Gupta 	CMD_INVALID             = 0x05,
1625c9ae5a8SAjay Gupta 	FLASH_UPDATE_FAIL       = 0x07,
1635c9ae5a8SAjay Gupta 	INVALID_FW              = 0x08,
1645c9ae5a8SAjay Gupta 	INVALID_ARG             = 0x09,
1655c9ae5a8SAjay Gupta 	CMD_NOT_SUPPORT         = 0x0A,
1665c9ae5a8SAjay Gupta 	TRANSACTION_FAIL        = 0x0C,
1675c9ae5a8SAjay Gupta 	PD_CMD_FAIL             = 0x0D,
1685c9ae5a8SAjay Gupta 	UNDEF_ERROR             = 0x0F,
1695c9ae5a8SAjay Gupta 	INVALID_RESP		= 0x10,
1705c9ae5a8SAjay Gupta };
1715c9ae5a8SAjay Gupta 
1725c9ae5a8SAjay Gupta #define CCG_EVENT_MAX	(EVENT_INDEX + 43)
1735c9ae5a8SAjay Gupta 
1745c9ae5a8SAjay Gupta struct ccg_cmd {
1755c9ae5a8SAjay Gupta 	u16 reg;
1765c9ae5a8SAjay Gupta 	u32 data;
1775c9ae5a8SAjay Gupta 	int len;
1785c9ae5a8SAjay Gupta 	u32 delay; /* ms delay for cmd timeout  */
1795c9ae5a8SAjay Gupta };
1805c9ae5a8SAjay Gupta 
1815c9ae5a8SAjay Gupta struct ccg_resp {
1825c9ae5a8SAjay Gupta 	u8 code;
1835c9ae5a8SAjay Gupta 	u8 length;
1845c9ae5a8SAjay Gupta };
1855c9ae5a8SAjay Gupta 
186170a6726SAjay Gupta struct ucsi_ccg_altmode {
187170a6726SAjay Gupta 	u16 svid;
188170a6726SAjay Gupta 	u32 mid;
189170a6726SAjay Gupta 	u8 linked_idx;
190170a6726SAjay Gupta 	u8 active_idx;
191170a6726SAjay Gupta #define UCSI_MULTI_DP_INDEX	(0xff)
192170a6726SAjay Gupta 	bool checked;
193170a6726SAjay Gupta } __packed;
194170a6726SAjay Gupta 
195247c554aSAjay Gupta struct ucsi_ccg {
196247c554aSAjay Gupta 	struct device *dev;
197247c554aSAjay Gupta 	struct ucsi *ucsi;
198247c554aSAjay Gupta 	struct i2c_client *client;
199e32fd989SHeikki Krogerus 
2005d438e20SAjay Gupta 	struct ccg_dev_info info;
2015d438e20SAjay Gupta 	/* version info for boot, primary and secondary */
2025d438e20SAjay Gupta 	struct version_info version[FW2 + 1];
203f0e4cd94SAjay Gupta 	u32 fw_version;
2045c9ae5a8SAjay Gupta 	/* CCG HPI communication flags */
2055c9ae5a8SAjay Gupta 	unsigned long flags;
2065c9ae5a8SAjay Gupta #define RESET_PENDING	0
2075c9ae5a8SAjay Gupta #define DEV_CMD_PENDING	1
2085c9ae5a8SAjay Gupta 	struct ccg_resp dev_resp;
2095c9ae5a8SAjay Gupta 	u8 cmd_resp;
2105c9ae5a8SAjay Gupta 	int port_num;
2115c9ae5a8SAjay Gupta 	int irq;
2125c9ae5a8SAjay Gupta 	struct work_struct work;
2135c9ae5a8SAjay Gupta 	struct mutex lock; /* to sync between user and driver thread */
214247c554aSAjay Gupta 
2155c9ae5a8SAjay Gupta 	/* fw build with vendor information */
2165c9ae5a8SAjay Gupta 	u16 fw_build;
217f0e4cd94SAjay Gupta 	struct work_struct pm_work;
218e32fd989SHeikki Krogerus 
219e32fd989SHeikki Krogerus 	struct completion complete;
220170a6726SAjay Gupta 
221170a6726SAjay Gupta 	u64 last_cmd_sent;
222170a6726SAjay Gupta 	bool has_multiple_dp;
223170a6726SAjay Gupta 	struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
224170a6726SAjay Gupta 	struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
2255c9ae5a8SAjay Gupta };
226247c554aSAjay Gupta 
ccg_read(struct ucsi_ccg * uc,u16 rab,u8 * data,u32 len)227247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
228247c554aSAjay Gupta {
229247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
230247c554aSAjay Gupta 	const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
231247c554aSAjay Gupta 	unsigned char buf[2];
232247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
233247c554aSAjay Gupta 		{
234247c554aSAjay Gupta 			.addr	= client->addr,
235247c554aSAjay Gupta 			.flags  = 0x0,
236247c554aSAjay Gupta 			.len	= sizeof(buf),
237247c554aSAjay Gupta 			.buf	= buf,
238247c554aSAjay Gupta 		},
239247c554aSAjay Gupta 		{
240247c554aSAjay Gupta 			.addr	= client->addr,
241247c554aSAjay Gupta 			.flags  = I2C_M_RD,
242247c554aSAjay Gupta 			.buf	= data,
243247c554aSAjay Gupta 		},
244247c554aSAjay Gupta 	};
245247c554aSAjay Gupta 	u32 rlen, rem_len = len, max_read_len = len;
246247c554aSAjay Gupta 	int status;
247247c554aSAjay Gupta 
248247c554aSAjay Gupta 	/* check any max_read_len limitation on i2c adapter */
249247c554aSAjay Gupta 	if (quirks && quirks->max_read_len)
250247c554aSAjay Gupta 		max_read_len = quirks->max_read_len;
251247c554aSAjay Gupta 
252a94ecde4SAjay Gupta 	pm_runtime_get_sync(uc->dev);
253247c554aSAjay Gupta 	while (rem_len > 0) {
254247c554aSAjay Gupta 		msgs[1].buf = &data[len - rem_len];
255247c554aSAjay Gupta 		rlen = min_t(u16, rem_len, max_read_len);
256247c554aSAjay Gupta 		msgs[1].len = rlen;
257247c554aSAjay Gupta 		put_unaligned_le16(rab, buf);
258247c554aSAjay Gupta 		status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
259247c554aSAjay Gupta 		if (status < 0) {
260247c554aSAjay Gupta 			dev_err(uc->dev, "i2c_transfer failed %d\n", status);
261a94ecde4SAjay Gupta 			pm_runtime_put_sync(uc->dev);
262247c554aSAjay Gupta 			return status;
263247c554aSAjay Gupta 		}
264247c554aSAjay Gupta 		rab += rlen;
265247c554aSAjay Gupta 		rem_len -= rlen;
266247c554aSAjay Gupta 	}
267247c554aSAjay Gupta 
268a94ecde4SAjay Gupta 	pm_runtime_put_sync(uc->dev);
269247c554aSAjay Gupta 	return 0;
270247c554aSAjay Gupta }
271247c554aSAjay Gupta 
ccg_write(struct ucsi_ccg * uc,u16 rab,const u8 * data,u32 len)272e32fd989SHeikki Krogerus static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
273247c554aSAjay Gupta {
274247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
275247c554aSAjay Gupta 	unsigned char *buf;
276247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
277247c554aSAjay Gupta 		{
278247c554aSAjay Gupta 			.addr	= client->addr,
279247c554aSAjay Gupta 			.flags  = 0x0,
280247c554aSAjay Gupta 		}
281247c554aSAjay Gupta 	};
282247c554aSAjay Gupta 	int status;
283247c554aSAjay Gupta 
284247c554aSAjay Gupta 	buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
285247c554aSAjay Gupta 	if (!buf)
286247c554aSAjay Gupta 		return -ENOMEM;
287247c554aSAjay Gupta 
288247c554aSAjay Gupta 	put_unaligned_le16(rab, buf);
289247c554aSAjay Gupta 	memcpy(buf + sizeof(rab), data, len);
290247c554aSAjay Gupta 
291247c554aSAjay Gupta 	msgs[0].len = len + sizeof(rab);
292247c554aSAjay Gupta 	msgs[0].buf = buf;
293247c554aSAjay Gupta 
294a94ecde4SAjay Gupta 	pm_runtime_get_sync(uc->dev);
295247c554aSAjay Gupta 	status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
296247c554aSAjay Gupta 	if (status < 0) {
297247c554aSAjay Gupta 		dev_err(uc->dev, "i2c_transfer failed %d\n", status);
298a94ecde4SAjay Gupta 		pm_runtime_put_sync(uc->dev);
299247c554aSAjay Gupta 		kfree(buf);
300247c554aSAjay Gupta 		return status;
301247c554aSAjay Gupta 	}
302247c554aSAjay Gupta 
303a94ecde4SAjay Gupta 	pm_runtime_put_sync(uc->dev);
304247c554aSAjay Gupta 	kfree(buf);
305247c554aSAjay Gupta 	return 0;
306247c554aSAjay Gupta }
307247c554aSAjay Gupta 
ucsi_ccg_init(struct ucsi_ccg * uc)308247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc)
309247c554aSAjay Gupta {
310247c554aSAjay Gupta 	unsigned int count = 10;
311247c554aSAjay Gupta 	u8 data;
312247c554aSAjay Gupta 	int status;
313247c554aSAjay Gupta 
314247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_STOP;
315247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
316247c554aSAjay Gupta 	if (status < 0)
317247c554aSAjay Gupta 		return status;
318247c554aSAjay Gupta 
319247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_START;
320247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
321247c554aSAjay Gupta 	if (status < 0)
322247c554aSAjay Gupta 		return status;
323247c554aSAjay Gupta 
324247c554aSAjay Gupta 	/*
325247c554aSAjay Gupta 	 * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
326247c554aSAjay Gupta 	 * register write will push response which must be cleared.
327247c554aSAjay Gupta 	 */
328247c554aSAjay Gupta 	do {
329247c554aSAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
330247c554aSAjay Gupta 		if (status < 0)
331247c554aSAjay Gupta 			return status;
332247c554aSAjay Gupta 
33382591149SSing-Han Chen 		if (!(data & DEV_INT))
334247c554aSAjay Gupta 			return 0;
335247c554aSAjay Gupta 
336247c554aSAjay Gupta 		status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
337247c554aSAjay Gupta 		if (status < 0)
338247c554aSAjay Gupta 			return status;
339247c554aSAjay Gupta 
340247c554aSAjay Gupta 		usleep_range(10000, 11000);
341247c554aSAjay Gupta 	} while (--count);
342247c554aSAjay Gupta 
343247c554aSAjay Gupta 	return -ETIMEDOUT;
344247c554aSAjay Gupta }
345247c554aSAjay Gupta 
ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg * uc,u8 * data)346170a6726SAjay Gupta static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
347170a6726SAjay Gupta {
348170a6726SAjay Gupta 	u8 cam, new_cam;
349170a6726SAjay Gupta 
350170a6726SAjay Gupta 	cam = data[0];
351170a6726SAjay Gupta 	new_cam = uc->orig[cam].linked_idx;
352170a6726SAjay Gupta 	uc->updated[new_cam].active_idx = cam;
353170a6726SAjay Gupta 	data[0] = new_cam;
354170a6726SAjay Gupta }
355170a6726SAjay Gupta 
ucsi_ccg_update_altmodes(struct ucsi * ucsi,struct ucsi_altmode * orig,struct ucsi_altmode * updated)356170a6726SAjay Gupta static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
357170a6726SAjay Gupta 				     struct ucsi_altmode *orig,
358170a6726SAjay Gupta 				     struct ucsi_altmode *updated)
359170a6726SAjay Gupta {
360170a6726SAjay Gupta 	struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
361170a6726SAjay Gupta 	struct ucsi_ccg_altmode *alt, *new_alt;
362170a6726SAjay Gupta 	int i, j, k = 0;
363170a6726SAjay Gupta 	bool found = false;
364170a6726SAjay Gupta 
365170a6726SAjay Gupta 	alt = uc->orig;
366170a6726SAjay Gupta 	new_alt = uc->updated;
367170a6726SAjay Gupta 	memset(uc->updated, 0, sizeof(uc->updated));
368170a6726SAjay Gupta 
369170a6726SAjay Gupta 	/*
370170a6726SAjay Gupta 	 * Copy original connector altmodes to new structure.
371170a6726SAjay Gupta 	 * We need this before second loop since second loop
372170a6726SAjay Gupta 	 * checks for duplicate altmodes.
373170a6726SAjay Gupta 	 */
374170a6726SAjay Gupta 	for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
375170a6726SAjay Gupta 		alt[i].svid = orig[i].svid;
376170a6726SAjay Gupta 		alt[i].mid = orig[i].mid;
377170a6726SAjay Gupta 		if (!alt[i].svid)
378170a6726SAjay Gupta 			break;
379170a6726SAjay Gupta 	}
380170a6726SAjay Gupta 
381170a6726SAjay Gupta 	for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
382170a6726SAjay Gupta 		if (!alt[i].svid)
383170a6726SAjay Gupta 			break;
384170a6726SAjay Gupta 
385170a6726SAjay Gupta 		/* already checked and considered */
386170a6726SAjay Gupta 		if (alt[i].checked)
387170a6726SAjay Gupta 			continue;
388170a6726SAjay Gupta 
389170a6726SAjay Gupta 		if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) {
390170a6726SAjay Gupta 			/* Found Non DP altmode */
391170a6726SAjay Gupta 			new_alt[k].svid = alt[i].svid;
392170a6726SAjay Gupta 			new_alt[k].mid |= alt[i].mid;
393170a6726SAjay Gupta 			new_alt[k].linked_idx = i;
394170a6726SAjay Gupta 			alt[i].linked_idx = k;
395170a6726SAjay Gupta 			updated[k].svid = new_alt[k].svid;
396170a6726SAjay Gupta 			updated[k].mid = new_alt[k].mid;
397170a6726SAjay Gupta 			k++;
398170a6726SAjay Gupta 			continue;
399170a6726SAjay Gupta 		}
400170a6726SAjay Gupta 
401170a6726SAjay Gupta 		for (j = i + 1; j < UCSI_MAX_ALTMODES; j++) {
402170a6726SAjay Gupta 			if (alt[i].svid != alt[j].svid ||
403170a6726SAjay Gupta 			    !DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) {
404170a6726SAjay Gupta 				continue;
405170a6726SAjay Gupta 			} else {
406170a6726SAjay Gupta 				/* Found duplicate DP mode */
407170a6726SAjay Gupta 				new_alt[k].svid = alt[i].svid;
408170a6726SAjay Gupta 				new_alt[k].mid |= alt[i].mid | alt[j].mid;
409170a6726SAjay Gupta 				new_alt[k].linked_idx = UCSI_MULTI_DP_INDEX;
410170a6726SAjay Gupta 				alt[i].linked_idx = k;
411170a6726SAjay Gupta 				alt[j].linked_idx = k;
412170a6726SAjay Gupta 				alt[j].checked = true;
413170a6726SAjay Gupta 				found = true;
414170a6726SAjay Gupta 			}
415170a6726SAjay Gupta 		}
416170a6726SAjay Gupta 		if (found) {
417170a6726SAjay Gupta 			uc->has_multiple_dp = true;
418170a6726SAjay Gupta 		} else {
419170a6726SAjay Gupta 			/* Didn't find any duplicate DP altmode */
420170a6726SAjay Gupta 			new_alt[k].svid = alt[i].svid;
421170a6726SAjay Gupta 			new_alt[k].mid |= alt[i].mid;
422170a6726SAjay Gupta 			new_alt[k].linked_idx = i;
423170a6726SAjay Gupta 			alt[i].linked_idx = k;
424170a6726SAjay Gupta 		}
425170a6726SAjay Gupta 		updated[k].svid = new_alt[k].svid;
426170a6726SAjay Gupta 		updated[k].mid = new_alt[k].mid;
427170a6726SAjay Gupta 		k++;
428170a6726SAjay Gupta 	}
429170a6726SAjay Gupta 	return found;
430170a6726SAjay Gupta }
431170a6726SAjay Gupta 
ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg * uc,struct ucsi_connector * con,u64 * cmd)432170a6726SAjay Gupta static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
433170a6726SAjay Gupta 					    struct ucsi_connector *con,
434170a6726SAjay Gupta 					    u64 *cmd)
435170a6726SAjay Gupta {
436170a6726SAjay Gupta 	struct ucsi_ccg_altmode *new_port, *port;
437170a6726SAjay Gupta 	struct typec_altmode *alt = NULL;
438170a6726SAjay Gupta 	u8 new_cam, cam, pin;
439170a6726SAjay Gupta 	bool enter_new_mode;
440170a6726SAjay Gupta 	int i, j, k = 0xff;
441170a6726SAjay Gupta 
442170a6726SAjay Gupta 	port = uc->orig;
443170a6726SAjay Gupta 	new_cam = UCSI_SET_NEW_CAM_GET_AM(*cmd);
444170a6726SAjay Gupta 	new_port = &uc->updated[new_cam];
445170a6726SAjay Gupta 	cam = new_port->linked_idx;
446170a6726SAjay Gupta 	enter_new_mode = UCSI_SET_NEW_CAM_ENTER(*cmd);
447170a6726SAjay Gupta 
448170a6726SAjay Gupta 	/*
449170a6726SAjay Gupta 	 * If CAM is UCSI_MULTI_DP_INDEX then this is DP altmode
450170a6726SAjay Gupta 	 * with multiple DP mode. Find out CAM for best pin assignment
451170a6726SAjay Gupta 	 * among all DP mode. Priorite pin E->D->C after making sure
452170a6726SAjay Gupta 	 * the partner supports that pin.
453170a6726SAjay Gupta 	 */
454170a6726SAjay Gupta 	if (cam == UCSI_MULTI_DP_INDEX) {
455170a6726SAjay Gupta 		if (enter_new_mode) {
456170a6726SAjay Gupta 			for (i = 0; con->partner_altmode[i]; i++) {
457170a6726SAjay Gupta 				alt = con->partner_altmode[i];
458170a6726SAjay Gupta 				if (alt->svid == new_port->svid)
459170a6726SAjay Gupta 					break;
460170a6726SAjay Gupta 			}
461170a6726SAjay Gupta 			/*
462170a6726SAjay Gupta 			 * alt will always be non NULL since this is
463170a6726SAjay Gupta 			 * UCSI_SET_NEW_CAM command and so there will be
464170a6726SAjay Gupta 			 * at least one con->partner_altmode[i] with svid
465170a6726SAjay Gupta 			 * matching with new_port->svid.
466170a6726SAjay Gupta 			 */
467170a6726SAjay Gupta 			for (j = 0; port[j].svid; j++) {
468170a6726SAjay Gupta 				pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid);
469170a6726SAjay Gupta 				if (alt && port[j].svid == alt->svid &&
470170a6726SAjay Gupta 				    (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) {
471170a6726SAjay Gupta 					/* prioritize pin E->D->C */
472170a6726SAjay Gupta 					if (k == 0xff || (k != 0xff && pin >
473170a6726SAjay Gupta 					    DP_CONF_GET_PIN_ASSIGN(port[k].mid))
474170a6726SAjay Gupta 					    ) {
475170a6726SAjay Gupta 						k = j;
476170a6726SAjay Gupta 					}
477170a6726SAjay Gupta 				}
478170a6726SAjay Gupta 			}
479170a6726SAjay Gupta 			cam = k;
480170a6726SAjay Gupta 			new_port->active_idx = cam;
481170a6726SAjay Gupta 		} else {
482170a6726SAjay Gupta 			cam = new_port->active_idx;
483170a6726SAjay Gupta 		}
484170a6726SAjay Gupta 	}
485170a6726SAjay Gupta 	*cmd &= ~UCSI_SET_NEW_CAM_AM_MASK;
486170a6726SAjay Gupta 	*cmd |= UCSI_SET_NEW_CAM_SET_AM(cam);
487170a6726SAjay Gupta }
488170a6726SAjay Gupta 
489706f4bbfSAjay Gupta /*
490706f4bbfSAjay Gupta  * Change the order of vdo values of NVIDIA test device FTB
491706f4bbfSAjay Gupta  * (Function Test Board) which reports altmode list with vdo=0x3
492706f4bbfSAjay Gupta  * first and then vdo=0x. Current logic to assign mode value is
493706f4bbfSAjay Gupta  * based on order in altmode list and it causes a mismatch of CON
494706f4bbfSAjay Gupta  * and SOP altmodes since NVIDIA GPU connector has order of vdo=0x1
495706f4bbfSAjay Gupta  * first and then vdo=0x3
496706f4bbfSAjay Gupta  */
ucsi_ccg_nvidia_altmode(struct ucsi_ccg * uc,struct ucsi_altmode * alt)497706f4bbfSAjay Gupta static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
498706f4bbfSAjay Gupta 				    struct ucsi_altmode *alt)
499706f4bbfSAjay Gupta {
500706f4bbfSAjay Gupta 	switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) {
501706f4bbfSAjay Gupta 	case NVIDIA_FTB_DP_OFFSET:
502706f4bbfSAjay Gupta 		if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
503706f4bbfSAjay Gupta 			alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
504706f4bbfSAjay Gupta 				DP_CAP_DP_SIGNALING | DP_CAP_USB |
505706f4bbfSAjay Gupta 				DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_E));
506706f4bbfSAjay Gupta 		break;
507706f4bbfSAjay Gupta 	case NVIDIA_FTB_DBG_OFFSET:
508706f4bbfSAjay Gupta 		if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DP_VDO)
509706f4bbfSAjay Gupta 			alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DBG_VDO;
510706f4bbfSAjay Gupta 		break;
511706f4bbfSAjay Gupta 	default:
512706f4bbfSAjay Gupta 		break;
513706f4bbfSAjay Gupta 	}
514706f4bbfSAjay Gupta }
515706f4bbfSAjay Gupta 
ucsi_ccg_read(struct ucsi * ucsi,unsigned int offset,void * val,size_t val_len)516e32fd989SHeikki Krogerus static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
517e32fd989SHeikki Krogerus 			 void *val, size_t val_len)
518247c554aSAjay Gupta {
519170a6726SAjay Gupta 	struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
520e32fd989SHeikki Krogerus 	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
521691f43cbSSing-Han Chen 	struct ucsi_capability *cap;
522706f4bbfSAjay Gupta 	struct ucsi_altmode *alt;
523706f4bbfSAjay Gupta 	int ret;
524247c554aSAjay Gupta 
525170a6726SAjay Gupta 	ret = ccg_read(uc, reg, val, val_len);
526170a6726SAjay Gupta 	if (ret)
527170a6726SAjay Gupta 		return ret;
528170a6726SAjay Gupta 
529706f4bbfSAjay Gupta 	if (offset != UCSI_MESSAGE_IN)
530706f4bbfSAjay Gupta 		return ret;
531706f4bbfSAjay Gupta 
532706f4bbfSAjay Gupta 	switch (UCSI_COMMAND(uc->last_cmd_sent)) {
533706f4bbfSAjay Gupta 	case UCSI_GET_CURRENT_CAM:
534706f4bbfSAjay Gupta 		if (uc->has_multiple_dp)
535170a6726SAjay Gupta 			ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
536706f4bbfSAjay Gupta 		break;
537706f4bbfSAjay Gupta 	case UCSI_GET_ALTERNATE_MODES:
538706f4bbfSAjay Gupta 		if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) ==
539706f4bbfSAjay Gupta 		    UCSI_RECIPIENT_SOP) {
540706f4bbfSAjay Gupta 			alt = val;
541706f4bbfSAjay Gupta 			if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
542706f4bbfSAjay Gupta 				ucsi_ccg_nvidia_altmode(uc, alt);
543706f4bbfSAjay Gupta 		}
544706f4bbfSAjay Gupta 		break;
545691f43cbSSing-Han Chen 	case UCSI_GET_CAPABILITY:
546691f43cbSSing-Han Chen 		if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
547691f43cbSSing-Han Chen 			cap = val;
548691f43cbSSing-Han Chen 			cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
549691f43cbSSing-Han Chen 		}
550691f43cbSSing-Han Chen 		break;
551706f4bbfSAjay Gupta 	default:
552706f4bbfSAjay Gupta 		break;
553170a6726SAjay Gupta 	}
554170a6726SAjay Gupta 	uc->last_cmd_sent = 0;
555170a6726SAjay Gupta 
556170a6726SAjay Gupta 	return ret;
557247c554aSAjay Gupta }
558247c554aSAjay Gupta 
ucsi_ccg_async_write(struct ucsi * ucsi,unsigned int offset,const void * val,size_t val_len)559e32fd989SHeikki Krogerus static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
560e32fd989SHeikki Krogerus 				const void *val, size_t val_len)
561247c554aSAjay Gupta {
562e32fd989SHeikki Krogerus 	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
563247c554aSAjay Gupta 
564e32fd989SHeikki Krogerus 	return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
565247c554aSAjay Gupta }
566247c554aSAjay Gupta 
ucsi_ccg_sync_write(struct ucsi * ucsi,unsigned int offset,const void * val,size_t val_len)567e32fd989SHeikki Krogerus static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
568e32fd989SHeikki Krogerus 			       const void *val, size_t val_len)
569247c554aSAjay Gupta {
570e32fd989SHeikki Krogerus 	struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
571170a6726SAjay Gupta 	struct ucsi_connector *con;
572170a6726SAjay Gupta 	int con_index;
573e32fd989SHeikki Krogerus 	int ret;
574247c554aSAjay Gupta 
575e32fd989SHeikki Krogerus 	mutex_lock(&uc->lock);
576e32fd989SHeikki Krogerus 	pm_runtime_get_sync(uc->dev);
577e32fd989SHeikki Krogerus 	set_bit(DEV_CMD_PENDING, &uc->flags);
578247c554aSAjay Gupta 
579170a6726SAjay Gupta 	if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) {
580170a6726SAjay Gupta 		uc->last_cmd_sent = *(u64 *)val;
581170a6726SAjay Gupta 
582170a6726SAjay Gupta 		if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
583170a6726SAjay Gupta 		    uc->has_multiple_dp) {
584170a6726SAjay Gupta 			con_index = (uc->last_cmd_sent >> 16) &
585170a6726SAjay Gupta 				    UCSI_CMD_CONNECTOR_MASK;
586170a6726SAjay Gupta 			con = &uc->ucsi->connector[con_index - 1];
587170a6726SAjay Gupta 			ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val);
588170a6726SAjay Gupta 		}
589170a6726SAjay Gupta 	}
590170a6726SAjay Gupta 
591e32fd989SHeikki Krogerus 	ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
592e32fd989SHeikki Krogerus 	if (ret)
593e32fd989SHeikki Krogerus 		goto err_clear_bit;
594e32fd989SHeikki Krogerus 
595e32fd989SHeikki Krogerus 	if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000)))
596e32fd989SHeikki Krogerus 		ret = -ETIMEDOUT;
597e32fd989SHeikki Krogerus 
598e32fd989SHeikki Krogerus err_clear_bit:
599e32fd989SHeikki Krogerus 	clear_bit(DEV_CMD_PENDING, &uc->flags);
600e32fd989SHeikki Krogerus 	pm_runtime_put_sync(uc->dev);
601e32fd989SHeikki Krogerus 	mutex_unlock(&uc->lock);
602e32fd989SHeikki Krogerus 
603e32fd989SHeikki Krogerus 	return ret;
604247c554aSAjay Gupta }
605247c554aSAjay Gupta 
606e32fd989SHeikki Krogerus static const struct ucsi_operations ucsi_ccg_ops = {
607e32fd989SHeikki Krogerus 	.read = ucsi_ccg_read,
608e32fd989SHeikki Krogerus 	.sync_write = ucsi_ccg_sync_write,
609170a6726SAjay Gupta 	.async_write = ucsi_ccg_async_write,
610170a6726SAjay Gupta 	.update_altmodes = ucsi_ccg_update_altmodes
611e32fd989SHeikki Krogerus };
612247c554aSAjay Gupta 
ccg_irq_handler(int irq,void * data)613247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data)
614247c554aSAjay Gupta {
615e32fd989SHeikki Krogerus 	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI);
616247c554aSAjay Gupta 	struct ucsi_ccg *uc = data;
617e32fd989SHeikki Krogerus 	u8 intr_reg;
618e32fd989SHeikki Krogerus 	u32 cci;
619e32fd989SHeikki Krogerus 	int ret;
620247c554aSAjay Gupta 
621e32fd989SHeikki Krogerus 	ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
622e32fd989SHeikki Krogerus 	if (ret)
623e32fd989SHeikki Krogerus 		return ret;
624e32fd989SHeikki Krogerus 
625e32fd989SHeikki Krogerus 	ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
626e32fd989SHeikki Krogerus 	if (ret)
627e32fd989SHeikki Krogerus 		goto err_clear_irq;
628e32fd989SHeikki Krogerus 
629e32fd989SHeikki Krogerus 	if (UCSI_CCI_CONNECTOR(cci))
630e32fd989SHeikki Krogerus 		ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
631e32fd989SHeikki Krogerus 
632e32fd989SHeikki Krogerus 	if (test_bit(DEV_CMD_PENDING, &uc->flags) &&
633e32fd989SHeikki Krogerus 	    cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
634e32fd989SHeikki Krogerus 		complete(&uc->complete);
635e32fd989SHeikki Krogerus 
636e32fd989SHeikki Krogerus err_clear_irq:
637e32fd989SHeikki Krogerus 	ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
638247c554aSAjay Gupta 
639247c554aSAjay Gupta 	return IRQ_HANDLED;
640247c554aSAjay Gupta }
641247c554aSAjay Gupta 
ccg_request_irq(struct ucsi_ccg * uc)6425767f400SSanket Goswami static int ccg_request_irq(struct ucsi_ccg *uc)
6435767f400SSanket Goswami {
6445767f400SSanket Goswami 	unsigned long flags = IRQF_ONESHOT;
6455767f400SSanket Goswami 
6466d9e0669SWayne Chang 	if (!dev_fwnode(uc->dev))
6475767f400SSanket Goswami 		flags |= IRQF_TRIGGER_HIGH;
6485767f400SSanket Goswami 
6495767f400SSanket Goswami 	return request_threaded_irq(uc->irq, NULL, ccg_irq_handler, flags, dev_name(uc->dev), uc);
6505767f400SSanket Goswami }
6515767f400SSanket Goswami 
ccg_pm_workaround_work(struct work_struct * pm_work)652f0e4cd94SAjay Gupta static void ccg_pm_workaround_work(struct work_struct *pm_work)
653f0e4cd94SAjay Gupta {
654e32fd989SHeikki Krogerus 	ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work));
655f0e4cd94SAjay Gupta }
656f0e4cd94SAjay Gupta 
get_fw_info(struct ucsi_ccg * uc)6575d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc)
6585d438e20SAjay Gupta {
6595d438e20SAjay Gupta 	int err;
6605d438e20SAjay Gupta 
6615d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version),
6625d438e20SAjay Gupta 		       sizeof(uc->version));
6635d438e20SAjay Gupta 	if (err < 0)
6645d438e20SAjay Gupta 		return err;
6655d438e20SAjay Gupta 
666f0e4cd94SAjay Gupta 	uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) |
667f0e4cd94SAjay Gupta 			CCG_VERSION_PATCH(uc->version[FW2].app.patch);
668f0e4cd94SAjay Gupta 
6695d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
6705d438e20SAjay Gupta 		       sizeof(uc->info));
6715d438e20SAjay Gupta 	if (err < 0)
6725d438e20SAjay Gupta 		return err;
6735d438e20SAjay Gupta 
6745d438e20SAjay Gupta 	return 0;
6755d438e20SAjay Gupta }
6765d438e20SAjay Gupta 
invalid_async_evt(int code)6775c9ae5a8SAjay Gupta static inline bool invalid_async_evt(int code)
6785c9ae5a8SAjay Gupta {
6795c9ae5a8SAjay Gupta 	return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX);
6805c9ae5a8SAjay Gupta }
6815c9ae5a8SAjay Gupta 
ccg_process_response(struct ucsi_ccg * uc)6825c9ae5a8SAjay Gupta static void ccg_process_response(struct ucsi_ccg *uc)
6835c9ae5a8SAjay Gupta {
6845c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
6855c9ae5a8SAjay Gupta 
6865c9ae5a8SAjay Gupta 	if (uc->dev_resp.code & ASYNC_EVENT) {
6875c9ae5a8SAjay Gupta 		if (uc->dev_resp.code == RESET_COMPLETE) {
6885c9ae5a8SAjay Gupta 			if (test_bit(RESET_PENDING, &uc->flags))
6895c9ae5a8SAjay Gupta 				uc->cmd_resp = uc->dev_resp.code;
6905c9ae5a8SAjay Gupta 			get_fw_info(uc);
6915c9ae5a8SAjay Gupta 		}
6925c9ae5a8SAjay Gupta 		if (invalid_async_evt(uc->dev_resp.code))
6935c9ae5a8SAjay Gupta 			dev_err(dev, "invalid async evt %d\n",
6945c9ae5a8SAjay Gupta 				uc->dev_resp.code);
6955c9ae5a8SAjay Gupta 	} else {
6965c9ae5a8SAjay Gupta 		if (test_bit(DEV_CMD_PENDING, &uc->flags)) {
6975c9ae5a8SAjay Gupta 			uc->cmd_resp = uc->dev_resp.code;
6985c9ae5a8SAjay Gupta 			clear_bit(DEV_CMD_PENDING, &uc->flags);
6995c9ae5a8SAjay Gupta 		} else {
7005c9ae5a8SAjay Gupta 			dev_err(dev, "dev resp 0x%04x but no cmd pending\n",
7015c9ae5a8SAjay Gupta 				uc->dev_resp.code);
7025c9ae5a8SAjay Gupta 		}
7035c9ae5a8SAjay Gupta 	}
7045c9ae5a8SAjay Gupta }
7055c9ae5a8SAjay Gupta 
ccg_read_response(struct ucsi_ccg * uc)7065c9ae5a8SAjay Gupta static int ccg_read_response(struct ucsi_ccg *uc)
7075c9ae5a8SAjay Gupta {
7085c9ae5a8SAjay Gupta 	unsigned long target = jiffies + msecs_to_jiffies(1000);
7095c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7105c9ae5a8SAjay Gupta 	u8 intval;
7115c9ae5a8SAjay Gupta 	int status;
7125c9ae5a8SAjay Gupta 
7135c9ae5a8SAjay Gupta 	/* wait for interrupt status to get updated */
7145c9ae5a8SAjay Gupta 	do {
7155c9ae5a8SAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval,
7165c9ae5a8SAjay Gupta 				  sizeof(intval));
7175c9ae5a8SAjay Gupta 		if (status < 0)
7185c9ae5a8SAjay Gupta 			return status;
7195c9ae5a8SAjay Gupta 
7205c9ae5a8SAjay Gupta 		if (intval & DEV_INT)
7215c9ae5a8SAjay Gupta 			break;
7225c9ae5a8SAjay Gupta 		usleep_range(500, 600);
7235c9ae5a8SAjay Gupta 	} while (time_is_after_jiffies(target));
7245c9ae5a8SAjay Gupta 
7255c9ae5a8SAjay Gupta 	if (time_is_before_jiffies(target)) {
7265c9ae5a8SAjay Gupta 		dev_err(dev, "response timeout error\n");
7275c9ae5a8SAjay Gupta 		return -ETIME;
7285c9ae5a8SAjay Gupta 	}
7295c9ae5a8SAjay Gupta 
7305c9ae5a8SAjay Gupta 	status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp,
7315c9ae5a8SAjay Gupta 			  sizeof(uc->dev_resp));
7325c9ae5a8SAjay Gupta 	if (status < 0)
7335c9ae5a8SAjay Gupta 		return status;
7345c9ae5a8SAjay Gupta 
7355c9ae5a8SAjay Gupta 	status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval));
7365c9ae5a8SAjay Gupta 	if (status < 0)
7375c9ae5a8SAjay Gupta 		return status;
7385c9ae5a8SAjay Gupta 
7395c9ae5a8SAjay Gupta 	return 0;
7405c9ae5a8SAjay Gupta }
7415c9ae5a8SAjay Gupta 
7425c9ae5a8SAjay Gupta /* Caller must hold uc->lock */
ccg_send_command(struct ucsi_ccg * uc,struct ccg_cmd * cmd)7435c9ae5a8SAjay Gupta static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd)
7445c9ae5a8SAjay Gupta {
7455c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7465c9ae5a8SAjay Gupta 	int ret;
7475c9ae5a8SAjay Gupta 
7485c9ae5a8SAjay Gupta 	switch (cmd->reg & 0xF000) {
7495c9ae5a8SAjay Gupta 	case DEV_REG_IDX:
7505c9ae5a8SAjay Gupta 		set_bit(DEV_CMD_PENDING, &uc->flags);
7515c9ae5a8SAjay Gupta 		break;
7525c9ae5a8SAjay Gupta 	default:
7535c9ae5a8SAjay Gupta 		dev_err(dev, "invalid cmd register\n");
7545c9ae5a8SAjay Gupta 		break;
7555c9ae5a8SAjay Gupta 	}
7565c9ae5a8SAjay Gupta 
7575c9ae5a8SAjay Gupta 	ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len);
7585c9ae5a8SAjay Gupta 	if (ret < 0)
7595c9ae5a8SAjay Gupta 		return ret;
7605c9ae5a8SAjay Gupta 
7615c9ae5a8SAjay Gupta 	msleep(cmd->delay);
7625c9ae5a8SAjay Gupta 
7635c9ae5a8SAjay Gupta 	ret = ccg_read_response(uc);
7645c9ae5a8SAjay Gupta 	if (ret < 0) {
7655c9ae5a8SAjay Gupta 		dev_err(dev, "response read error\n");
7665c9ae5a8SAjay Gupta 		switch (cmd->reg & 0xF000) {
7675c9ae5a8SAjay Gupta 		case DEV_REG_IDX:
7685c9ae5a8SAjay Gupta 			clear_bit(DEV_CMD_PENDING, &uc->flags);
7695c9ae5a8SAjay Gupta 			break;
7705c9ae5a8SAjay Gupta 		default:
7715c9ae5a8SAjay Gupta 			dev_err(dev, "invalid cmd register\n");
7725c9ae5a8SAjay Gupta 			break;
7735c9ae5a8SAjay Gupta 		}
7745c9ae5a8SAjay Gupta 		return -EIO;
7755c9ae5a8SAjay Gupta 	}
7765c9ae5a8SAjay Gupta 	ccg_process_response(uc);
7775c9ae5a8SAjay Gupta 
7785c9ae5a8SAjay Gupta 	return uc->cmd_resp;
7795c9ae5a8SAjay Gupta }
7805c9ae5a8SAjay Gupta 
ccg_cmd_enter_flashing(struct ucsi_ccg * uc)7815c9ae5a8SAjay Gupta static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc)
7825c9ae5a8SAjay Gupta {
7835c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
7845c9ae5a8SAjay Gupta 	int ret;
7855c9ae5a8SAjay Gupta 
7865c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_ENTER_FLASHING;
7875c9ae5a8SAjay Gupta 	cmd.data = FLASH_ENTER_SIG;
7885c9ae5a8SAjay Gupta 	cmd.len = 1;
7895c9ae5a8SAjay Gupta 	cmd.delay = 50;
7905c9ae5a8SAjay Gupta 
7915c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
7925c9ae5a8SAjay Gupta 
7935c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
7945c9ae5a8SAjay Gupta 
7955c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
7965c9ae5a8SAjay Gupta 
7975c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
7985c9ae5a8SAjay Gupta 		dev_err(uc->dev, "enter flashing failed ret=%d\n", ret);
7995c9ae5a8SAjay Gupta 		return ret;
8005c9ae5a8SAjay Gupta 	}
8015c9ae5a8SAjay Gupta 
8025c9ae5a8SAjay Gupta 	return 0;
8035c9ae5a8SAjay Gupta }
8045c9ae5a8SAjay Gupta 
ccg_cmd_reset(struct ucsi_ccg * uc)8055c9ae5a8SAjay Gupta static int ccg_cmd_reset(struct ucsi_ccg *uc)
8065c9ae5a8SAjay Gupta {
8075c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
8085c9ae5a8SAjay Gupta 	u8 *p;
8095c9ae5a8SAjay Gupta 	int ret;
8105c9ae5a8SAjay Gupta 
8115c9ae5a8SAjay Gupta 	p = (u8 *)&cmd.data;
8125c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_RESET_REQ;
8135c9ae5a8SAjay Gupta 	p[0] = RESET_SIG;
8145c9ae5a8SAjay Gupta 	p[1] = CMD_RESET_DEV;
8155c9ae5a8SAjay Gupta 	cmd.len = 2;
8165c9ae5a8SAjay Gupta 	cmd.delay = 5000;
8175c9ae5a8SAjay Gupta 
8185c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
8195c9ae5a8SAjay Gupta 
8205c9ae5a8SAjay Gupta 	set_bit(RESET_PENDING, &uc->flags);
8215c9ae5a8SAjay Gupta 
8225c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
8235c9ae5a8SAjay Gupta 	if (ret != RESET_COMPLETE)
8245c9ae5a8SAjay Gupta 		goto err_clear_flag;
8255c9ae5a8SAjay Gupta 
8265c9ae5a8SAjay Gupta 	ret = 0;
8275c9ae5a8SAjay Gupta 
8285c9ae5a8SAjay Gupta err_clear_flag:
8295c9ae5a8SAjay Gupta 	clear_bit(RESET_PENDING, &uc->flags);
8305c9ae5a8SAjay Gupta 
8315c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
8325c9ae5a8SAjay Gupta 
8335c9ae5a8SAjay Gupta 	return ret;
8345c9ae5a8SAjay Gupta }
8355c9ae5a8SAjay Gupta 
ccg_cmd_port_control(struct ucsi_ccg * uc,bool enable)8365c9ae5a8SAjay Gupta static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable)
8375c9ae5a8SAjay Gupta {
8385c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
8395c9ae5a8SAjay Gupta 	int ret;
8405c9ae5a8SAjay Gupta 
8415c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_PDPORT_ENABLE;
8425c9ae5a8SAjay Gupta 	if (enable)
8435c9ae5a8SAjay Gupta 		cmd.data = (uc->port_num == 1) ?
8445c9ae5a8SAjay Gupta 			    PDPORT_1 : (PDPORT_1 | PDPORT_2);
8455c9ae5a8SAjay Gupta 	else
8465c9ae5a8SAjay Gupta 		cmd.data = 0x0;
8475c9ae5a8SAjay Gupta 	cmd.len = 1;
8485c9ae5a8SAjay Gupta 	cmd.delay = 10;
8495c9ae5a8SAjay Gupta 
8505c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
8515c9ae5a8SAjay Gupta 
8525c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
8535c9ae5a8SAjay Gupta 
8545c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
8555c9ae5a8SAjay Gupta 
8565c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
8575c9ae5a8SAjay Gupta 		dev_err(uc->dev, "port control failed ret=%d\n", ret);
8585c9ae5a8SAjay Gupta 		return ret;
8595c9ae5a8SAjay Gupta 	}
8605c9ae5a8SAjay Gupta 	return 0;
8615c9ae5a8SAjay Gupta }
8625c9ae5a8SAjay Gupta 
ccg_cmd_jump_boot_mode(struct ucsi_ccg * uc,int bl_mode)8635c9ae5a8SAjay Gupta static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode)
8645c9ae5a8SAjay Gupta {
8655c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
8665c9ae5a8SAjay Gupta 	int ret;
8675c9ae5a8SAjay Gupta 
8685c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_JUMP_TO_BOOT;
8695c9ae5a8SAjay Gupta 
8705c9ae5a8SAjay Gupta 	if (bl_mode)
8715c9ae5a8SAjay Gupta 		cmd.data = TO_BOOT;
8725c9ae5a8SAjay Gupta 	else
8735c9ae5a8SAjay Gupta 		cmd.data = TO_ALT_FW;
8745c9ae5a8SAjay Gupta 
8755c9ae5a8SAjay Gupta 	cmd.len = 1;
8765c9ae5a8SAjay Gupta 	cmd.delay = 100;
8775c9ae5a8SAjay Gupta 
8785c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
8795c9ae5a8SAjay Gupta 
8805c9ae5a8SAjay Gupta 	set_bit(RESET_PENDING, &uc->flags);
8815c9ae5a8SAjay Gupta 
8825c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
8835c9ae5a8SAjay Gupta 	if (ret != RESET_COMPLETE)
8845c9ae5a8SAjay Gupta 		goto err_clear_flag;
8855c9ae5a8SAjay Gupta 
8865c9ae5a8SAjay Gupta 	ret = 0;
8875c9ae5a8SAjay Gupta 
8885c9ae5a8SAjay Gupta err_clear_flag:
8895c9ae5a8SAjay Gupta 	clear_bit(RESET_PENDING, &uc->flags);
8905c9ae5a8SAjay Gupta 
8915c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
8925c9ae5a8SAjay Gupta 
8935c9ae5a8SAjay Gupta 	return ret;
8945c9ae5a8SAjay Gupta }
8955c9ae5a8SAjay Gupta 
8965c9ae5a8SAjay Gupta static int
ccg_cmd_write_flash_row(struct ucsi_ccg * uc,u16 row,const void * data,u8 fcmd)8975c9ae5a8SAjay Gupta ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row,
8985c9ae5a8SAjay Gupta 			const void *data, u8 fcmd)
8995c9ae5a8SAjay Gupta {
9005c9ae5a8SAjay Gupta 	struct i2c_client *client = uc->client;
9015c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
9025c9ae5a8SAjay Gupta 	u8 buf[CCG4_ROW_SIZE + 2];
9035c9ae5a8SAjay Gupta 	u8 *p;
9045c9ae5a8SAjay Gupta 	int ret;
9055c9ae5a8SAjay Gupta 
9065c9ae5a8SAjay Gupta 	/* Copy the data into the flash read/write memory. */
9075c9ae5a8SAjay Gupta 	put_unaligned_le16(REG_FLASH_RW_MEM, buf);
9085c9ae5a8SAjay Gupta 
9095c9ae5a8SAjay Gupta 	memcpy(buf + 2, data, CCG4_ROW_SIZE);
9105c9ae5a8SAjay Gupta 
9115c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
9125c9ae5a8SAjay Gupta 
9135c9ae5a8SAjay Gupta 	ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2);
9145c9ae5a8SAjay Gupta 	if (ret != CCG4_ROW_SIZE + 2) {
9155c9ae5a8SAjay Gupta 		dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret);
916c2d18126SWei Yongjun 		mutex_unlock(&uc->lock);
9175c9ae5a8SAjay Gupta 		return ret < 0 ? ret : -EIO;
9185c9ae5a8SAjay Gupta 	}
9195c9ae5a8SAjay Gupta 
9205c9ae5a8SAjay Gupta 	/* Use the FLASH_ROW_READ_WRITE register to trigger */
9215c9ae5a8SAjay Gupta 	/* writing of data to the desired flash row */
9225c9ae5a8SAjay Gupta 	p = (u8 *)&cmd.data;
9235c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_FLASH_ROW_RW;
9245c9ae5a8SAjay Gupta 	p[0] = FLASH_SIG;
9255c9ae5a8SAjay Gupta 	p[1] = fcmd;
9265c9ae5a8SAjay Gupta 	put_unaligned_le16(row, &p[2]);
9275c9ae5a8SAjay Gupta 	cmd.len = 4;
9285c9ae5a8SAjay Gupta 	cmd.delay = 50;
9295c9ae5a8SAjay Gupta 	if (fcmd == FLASH_FWCT_SIG_WR_CMD)
9305c9ae5a8SAjay Gupta 		cmd.delay += 400;
9315c9ae5a8SAjay Gupta 	if (row == 510)
9325c9ae5a8SAjay Gupta 		cmd.delay += 220;
9335c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
9345c9ae5a8SAjay Gupta 
9355c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
9365c9ae5a8SAjay Gupta 
9375c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
9385c9ae5a8SAjay Gupta 		dev_err(uc->dev, "write flash row failed ret=%d\n", ret);
9395c9ae5a8SAjay Gupta 		return ret;
9405c9ae5a8SAjay Gupta 	}
9415c9ae5a8SAjay Gupta 
9425c9ae5a8SAjay Gupta 	return 0;
9435c9ae5a8SAjay Gupta }
9445c9ae5a8SAjay Gupta 
ccg_cmd_validate_fw(struct ucsi_ccg * uc,unsigned int fwid)9455c9ae5a8SAjay Gupta static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
9465c9ae5a8SAjay Gupta {
9475c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
9485c9ae5a8SAjay Gupta 	int ret;
9495c9ae5a8SAjay Gupta 
9505c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_VALIDATE_FW;
9515c9ae5a8SAjay Gupta 	cmd.data = fwid;
9525c9ae5a8SAjay Gupta 	cmd.len = 1;
9535c9ae5a8SAjay Gupta 	cmd.delay = 500;
9545c9ae5a8SAjay Gupta 
9555c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
9565c9ae5a8SAjay Gupta 
9575c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
9585c9ae5a8SAjay Gupta 
9595c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
9605c9ae5a8SAjay Gupta 
9615c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS)
9625c9ae5a8SAjay Gupta 		return ret;
9635c9ae5a8SAjay Gupta 
9645c9ae5a8SAjay Gupta 	return 0;
9655c9ae5a8SAjay Gupta }
9665c9ae5a8SAjay Gupta 
ccg_check_vendor_version(struct ucsi_ccg * uc,struct version_format * app,struct fw_config_table * fw_cfg)9675c9ae5a8SAjay Gupta static bool ccg_check_vendor_version(struct ucsi_ccg *uc,
9685c9ae5a8SAjay Gupta 				     struct version_format *app,
9695c9ae5a8SAjay Gupta 				     struct fw_config_table *fw_cfg)
9705c9ae5a8SAjay Gupta {
9715c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
9725c9ae5a8SAjay Gupta 
9735c9ae5a8SAjay Gupta 	/* Check if the fw build is for supported vendors */
9745c9ae5a8SAjay Gupta 	if (le16_to_cpu(app->build) != uc->fw_build) {
9755c9ae5a8SAjay Gupta 		dev_info(dev, "current fw is not from supported vendor\n");
9765c9ae5a8SAjay Gupta 		return false;
9775c9ae5a8SAjay Gupta 	}
9785c9ae5a8SAjay Gupta 
9795c9ae5a8SAjay Gupta 	/* Check if the new fw build is for supported vendors */
9805c9ae5a8SAjay Gupta 	if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) {
9815c9ae5a8SAjay Gupta 		dev_info(dev, "new fw is not from supported vendor\n");
9825c9ae5a8SAjay Gupta 		return false;
9835c9ae5a8SAjay Gupta 	}
9845c9ae5a8SAjay Gupta 	return true;
9855c9ae5a8SAjay Gupta }
9865c9ae5a8SAjay Gupta 
ccg_check_fw_version(struct ucsi_ccg * uc,const char * fw_name,struct version_format * app)9875c9ae5a8SAjay Gupta static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
9885c9ae5a8SAjay Gupta 				 struct version_format *app)
9895c9ae5a8SAjay Gupta {
9905c9ae5a8SAjay Gupta 	const struct firmware *fw = NULL;
9915c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
9925c9ae5a8SAjay Gupta 	struct fw_config_table fw_cfg;
9935c9ae5a8SAjay Gupta 	u32 cur_version, new_version;
9945c9ae5a8SAjay Gupta 	bool is_later = false;
9955c9ae5a8SAjay Gupta 
9965c9ae5a8SAjay Gupta 	if (request_firmware(&fw, fw_name, dev) != 0) {
9975c9ae5a8SAjay Gupta 		dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
9985c9ae5a8SAjay Gupta 		return false;
9995c9ae5a8SAjay Gupta 	}
10005c9ae5a8SAjay Gupta 
10015c9ae5a8SAjay Gupta 	/*
10025c9ae5a8SAjay Gupta 	 * check if signed fw
10035c9ae5a8SAjay Gupta 	 * last part of fw image is fw cfg table and signature
10045c9ae5a8SAjay Gupta 	 */
10055c9ae5a8SAjay Gupta 	if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
10065c9ae5a8SAjay Gupta 		goto out_release_firmware;
10075c9ae5a8SAjay Gupta 
10085c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
10095c9ae5a8SAjay Gupta 	       sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));
10105c9ae5a8SAjay Gupta 
10115c9ae5a8SAjay Gupta 	if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) {
10125c9ae5a8SAjay Gupta 		dev_info(dev, "not a signed image\n");
10135c9ae5a8SAjay Gupta 		goto out_release_firmware;
10145c9ae5a8SAjay Gupta 	}
10155c9ae5a8SAjay Gupta 
10165c9ae5a8SAjay Gupta 	/* compare input version with FWCT version */
1017f0e4cd94SAjay Gupta 	cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) |
1018f0e4cd94SAjay Gupta 			CCG_VERSION(app->ver);
10195c9ae5a8SAjay Gupta 
1020f0e4cd94SAjay Gupta 	new_version = le16_to_cpu(fw_cfg.app.build) |
1021f0e4cd94SAjay Gupta 			CCG_VERSION_PATCH(fw_cfg.app.patch) |
1022f0e4cd94SAjay Gupta 			CCG_VERSION(fw_cfg.app.ver);
10235c9ae5a8SAjay Gupta 
10245c9ae5a8SAjay Gupta 	if (!ccg_check_vendor_version(uc, app, &fw_cfg))
10255c9ae5a8SAjay Gupta 		goto out_release_firmware;
10265c9ae5a8SAjay Gupta 
10275c9ae5a8SAjay Gupta 	if (new_version > cur_version)
10285c9ae5a8SAjay Gupta 		is_later = true;
10295c9ae5a8SAjay Gupta 
10305c9ae5a8SAjay Gupta out_release_firmware:
10315c9ae5a8SAjay Gupta 	release_firmware(fw);
10325c9ae5a8SAjay Gupta 	return is_later;
10335c9ae5a8SAjay Gupta }
10345c9ae5a8SAjay Gupta 
ccg_fw_update_needed(struct ucsi_ccg * uc,enum enum_flash_mode * mode)10355c9ae5a8SAjay Gupta static int ccg_fw_update_needed(struct ucsi_ccg *uc,
10365c9ae5a8SAjay Gupta 				enum enum_flash_mode *mode)
10375c9ae5a8SAjay Gupta {
10385c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
10395c9ae5a8SAjay Gupta 	int err;
10405c9ae5a8SAjay Gupta 	struct version_info version[3];
10415c9ae5a8SAjay Gupta 
10425c9ae5a8SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
10435c9ae5a8SAjay Gupta 		       sizeof(uc->info));
10445c9ae5a8SAjay Gupta 	if (err) {
10455c9ae5a8SAjay Gupta 		dev_err(dev, "read device mode failed\n");
10465c9ae5a8SAjay Gupta 		return err;
10475c9ae5a8SAjay Gupta 	}
10485c9ae5a8SAjay Gupta 
10495c9ae5a8SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
10505c9ae5a8SAjay Gupta 		       sizeof(version));
10515c9ae5a8SAjay Gupta 	if (err) {
10525c9ae5a8SAjay Gupta 		dev_err(dev, "read device mode failed\n");
10535c9ae5a8SAjay Gupta 		return err;
10545c9ae5a8SAjay Gupta 	}
10555c9ae5a8SAjay Gupta 
10565c9ae5a8SAjay Gupta 	if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
10575c9ae5a8SAjay Gupta 		   sizeof(struct version_info)) == 0) {
10585c9ae5a8SAjay Gupta 		dev_info(dev, "secondary fw is not flashed\n");
10595c9ae5a8SAjay Gupta 		*mode = SECONDARY_BL;
10605c9ae5a8SAjay Gupta 	} else if (le16_to_cpu(version[FW1].base.build) <
10615c9ae5a8SAjay Gupta 		secondary_fw_min_ver) {
10625c9ae5a8SAjay Gupta 		dev_info(dev, "secondary fw version is too low (< %d)\n",
10635c9ae5a8SAjay Gupta 			 secondary_fw_min_ver);
10645c9ae5a8SAjay Gupta 		*mode = SECONDARY;
10655c9ae5a8SAjay Gupta 	} else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
10665c9ae5a8SAjay Gupta 		   sizeof(struct version_info)) == 0) {
10675c9ae5a8SAjay Gupta 		dev_info(dev, "primary fw is not flashed\n");
10685c9ae5a8SAjay Gupta 		*mode = PRIMARY;
10695c9ae5a8SAjay Gupta 	} else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
10705c9ae5a8SAjay Gupta 		   &version[FW2].app)) {
10715c9ae5a8SAjay Gupta 		dev_info(dev, "found primary fw with later version\n");
10725c9ae5a8SAjay Gupta 		*mode = PRIMARY;
10735c9ae5a8SAjay Gupta 	} else {
10745c9ae5a8SAjay Gupta 		dev_info(dev, "secondary and primary fw are the latest\n");
10755c9ae5a8SAjay Gupta 		*mode = FLASH_NOT_NEEDED;
10765c9ae5a8SAjay Gupta 	}
10775c9ae5a8SAjay Gupta 	return 0;
10785c9ae5a8SAjay Gupta }
10795c9ae5a8SAjay Gupta 
do_flash(struct ucsi_ccg * uc,enum enum_flash_mode mode)10805c9ae5a8SAjay Gupta static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
10815c9ae5a8SAjay Gupta {
10825c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
10835c9ae5a8SAjay Gupta 	const struct firmware *fw = NULL;
10845c9ae5a8SAjay Gupta 	const char *p, *s;
10855c9ae5a8SAjay Gupta 	const char *eof;
10865c9ae5a8SAjay Gupta 	int err, row, len, line_sz, line_cnt = 0;
10875c9ae5a8SAjay Gupta 	unsigned long start_time = jiffies;
10885c9ae5a8SAjay Gupta 	struct fw_config_table  fw_cfg;
10895c9ae5a8SAjay Gupta 	u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE];
10905c9ae5a8SAjay Gupta 	u8 *wr_buf;
10915c9ae5a8SAjay Gupta 
10925c9ae5a8SAjay Gupta 	err = request_firmware(&fw, ccg_fw_names[mode], dev);
10935c9ae5a8SAjay Gupta 	if (err) {
10945c9ae5a8SAjay Gupta 		dev_err(dev, "request %s failed err=%d\n",
10955c9ae5a8SAjay Gupta 			ccg_fw_names[mode], err);
10965c9ae5a8SAjay Gupta 		return err;
10975c9ae5a8SAjay Gupta 	}
10985c9ae5a8SAjay Gupta 
10995c9ae5a8SAjay Gupta 	if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >>
11005c9ae5a8SAjay Gupta 			CCG_DEVINFO_FWMODE_SHIFT) == FW2) {
11015c9ae5a8SAjay Gupta 		err = ccg_cmd_port_control(uc, false);
11025c9ae5a8SAjay Gupta 		if (err < 0)
11035c9ae5a8SAjay Gupta 			goto release_fw;
11045c9ae5a8SAjay Gupta 		err = ccg_cmd_jump_boot_mode(uc, 0);
11055c9ae5a8SAjay Gupta 		if (err < 0)
11065c9ae5a8SAjay Gupta 			goto release_fw;
11075c9ae5a8SAjay Gupta 	}
11085c9ae5a8SAjay Gupta 
11095c9ae5a8SAjay Gupta 	eof = fw->data + fw->size;
11105c9ae5a8SAjay Gupta 
11115c9ae5a8SAjay Gupta 	/*
11125c9ae5a8SAjay Gupta 	 * check if signed fw
11135c9ae5a8SAjay Gupta 	 * last part of fw image is fw cfg table and signature
11145c9ae5a8SAjay Gupta 	 */
11155c9ae5a8SAjay Gupta 	if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig))
11165c9ae5a8SAjay Gupta 		goto not_signed_fw;
11175c9ae5a8SAjay Gupta 
11185c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
11195c9ae5a8SAjay Gupta 	       sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg));
11205c9ae5a8SAjay Gupta 
11215c9ae5a8SAjay Gupta 	if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
11225c9ae5a8SAjay Gupta 		dev_info(dev, "not a signed image\n");
11235c9ae5a8SAjay Gupta 		goto not_signed_fw;
11245c9ae5a8SAjay Gupta 	}
11255c9ae5a8SAjay Gupta 	eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig);
11265c9ae5a8SAjay Gupta 
11275c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg_sig,
11285c9ae5a8SAjay Gupta 	       fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig));
11295c9ae5a8SAjay Gupta 
11305c9ae5a8SAjay Gupta 	/* flash fw config table and signature first */
11315c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg,
11325c9ae5a8SAjay Gupta 				      FLASH_FWCT1_WR_CMD);
11335c9ae5a8SAjay Gupta 	if (err)
11345c9ae5a8SAjay Gupta 		goto release_fw;
11355c9ae5a8SAjay Gupta 
11365c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE,
11375c9ae5a8SAjay Gupta 				      FLASH_FWCT2_WR_CMD);
11385c9ae5a8SAjay Gupta 	if (err)
11395c9ae5a8SAjay Gupta 		goto release_fw;
11405c9ae5a8SAjay Gupta 
11415c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig,
11425c9ae5a8SAjay Gupta 				      FLASH_FWCT_SIG_WR_CMD);
11435c9ae5a8SAjay Gupta 	if (err)
11445c9ae5a8SAjay Gupta 		goto release_fw;
11455c9ae5a8SAjay Gupta 
11465c9ae5a8SAjay Gupta not_signed_fw:
11475c9ae5a8SAjay Gupta 	wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL);
11482649939aSGustavo A. R. Silva 	if (!wr_buf) {
11492649939aSGustavo A. R. Silva 		err = -ENOMEM;
11502649939aSGustavo A. R. Silva 		goto release_fw;
11512649939aSGustavo A. R. Silva 	}
11525c9ae5a8SAjay Gupta 
11535c9ae5a8SAjay Gupta 	err = ccg_cmd_enter_flashing(uc);
11545c9ae5a8SAjay Gupta 	if (err)
11555c9ae5a8SAjay Gupta 		goto release_mem;
11565c9ae5a8SAjay Gupta 
11575c9ae5a8SAjay Gupta 	/*****************************************************************
11585c9ae5a8SAjay Gupta 	 * CCG firmware image (.cyacd) file line format
11595c9ae5a8SAjay Gupta 	 *
11605c9ae5a8SAjay Gupta 	 * :00rrrrllll[dd....]cc/r/n
11615c9ae5a8SAjay Gupta 	 *
11625c9ae5a8SAjay Gupta 	 * :00   header
11635c9ae5a8SAjay Gupta 	 * rrrr is row number to flash				(4 char)
11645c9ae5a8SAjay Gupta 	 * llll is data len to flash				(4 char)
11655c9ae5a8SAjay Gupta 	 * dd   is a data field represents one byte of data	(512 char)
11665c9ae5a8SAjay Gupta 	 * cc   is checksum					(2 char)
11675c9ae5a8SAjay Gupta 	 * \r\n newline
11685c9ae5a8SAjay Gupta 	 *
11695c9ae5a8SAjay Gupta 	 * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527
11705c9ae5a8SAjay Gupta 	 *
11715c9ae5a8SAjay Gupta 	 *****************************************************************/
11725c9ae5a8SAjay Gupta 
11735c9ae5a8SAjay Gupta 	p = strnchr(fw->data, fw->size, ':');
11745c9ae5a8SAjay Gupta 	while (p < eof) {
11755c9ae5a8SAjay Gupta 		s = strnchr(p + 1, eof - p - 1, ':');
11765c9ae5a8SAjay Gupta 
11775c9ae5a8SAjay Gupta 		if (!s)
11785c9ae5a8SAjay Gupta 			s = eof;
11795c9ae5a8SAjay Gupta 
11805c9ae5a8SAjay Gupta 		line_sz = s - p;
11815c9ae5a8SAjay Gupta 
11825c9ae5a8SAjay Gupta 		if (line_sz != CYACD_LINE_SIZE) {
11835c9ae5a8SAjay Gupta 			dev_err(dev, "Bad FW format line_sz=%d\n", line_sz);
11845c9ae5a8SAjay Gupta 			err =  -EINVAL;
11855c9ae5a8SAjay Gupta 			goto release_mem;
11865c9ae5a8SAjay Gupta 		}
11875c9ae5a8SAjay Gupta 
11885c9ae5a8SAjay Gupta 		if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) {
11895c9ae5a8SAjay Gupta 			err =  -EINVAL;
11905c9ae5a8SAjay Gupta 			goto release_mem;
11915c9ae5a8SAjay Gupta 		}
11925c9ae5a8SAjay Gupta 
11935c9ae5a8SAjay Gupta 		row = get_unaligned_be16(wr_buf);
11945c9ae5a8SAjay Gupta 		len = get_unaligned_be16(&wr_buf[2]);
11955c9ae5a8SAjay Gupta 
11965c9ae5a8SAjay Gupta 		if (len != CCG4_ROW_SIZE) {
11975c9ae5a8SAjay Gupta 			err =  -EINVAL;
11985c9ae5a8SAjay Gupta 			goto release_mem;
11995c9ae5a8SAjay Gupta 		}
12005c9ae5a8SAjay Gupta 
12015c9ae5a8SAjay Gupta 		err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4,
12025c9ae5a8SAjay Gupta 					      FLASH_WR_CMD);
12035c9ae5a8SAjay Gupta 		if (err)
12045c9ae5a8SAjay Gupta 			goto release_mem;
12055c9ae5a8SAjay Gupta 
12065c9ae5a8SAjay Gupta 		line_cnt++;
12075c9ae5a8SAjay Gupta 		p = s;
12085c9ae5a8SAjay Gupta 	}
12095c9ae5a8SAjay Gupta 
12105c9ae5a8SAjay Gupta 	dev_info(dev, "total %d row flashed. time: %dms\n",
12115c9ae5a8SAjay Gupta 		 line_cnt, jiffies_to_msecs(jiffies - start_time));
12125c9ae5a8SAjay Gupta 
12135c9ae5a8SAjay Gupta 	err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 :  FW1);
12145c9ae5a8SAjay Gupta 	if (err)
12155c9ae5a8SAjay Gupta 		dev_err(dev, "%s validation failed err=%d\n",
12165c9ae5a8SAjay Gupta 			(mode == PRIMARY) ? "FW2" :  "FW1", err);
12175c9ae5a8SAjay Gupta 	else
12185c9ae5a8SAjay Gupta 		dev_info(dev, "%s validated\n",
12195c9ae5a8SAjay Gupta 			 (mode == PRIMARY) ? "FW2" :  "FW1");
12205c9ae5a8SAjay Gupta 
12215c9ae5a8SAjay Gupta 	err = ccg_cmd_port_control(uc, false);
12225c9ae5a8SAjay Gupta 	if (err < 0)
12235c9ae5a8SAjay Gupta 		goto release_mem;
12245c9ae5a8SAjay Gupta 
12255c9ae5a8SAjay Gupta 	err = ccg_cmd_reset(uc);
12265c9ae5a8SAjay Gupta 	if (err < 0)
12275c9ae5a8SAjay Gupta 		goto release_mem;
12285c9ae5a8SAjay Gupta 
12295c9ae5a8SAjay Gupta 	err = ccg_cmd_port_control(uc, true);
12305c9ae5a8SAjay Gupta 	if (err < 0)
12315c9ae5a8SAjay Gupta 		goto release_mem;
12325c9ae5a8SAjay Gupta 
12335c9ae5a8SAjay Gupta release_mem:
12345c9ae5a8SAjay Gupta 	kfree(wr_buf);
12355c9ae5a8SAjay Gupta 
12365c9ae5a8SAjay Gupta release_fw:
12375c9ae5a8SAjay Gupta 	release_firmware(fw);
12385c9ae5a8SAjay Gupta 	return err;
12395c9ae5a8SAjay Gupta }
12405c9ae5a8SAjay Gupta 
12415c9ae5a8SAjay Gupta /*******************************************************************************
12425c9ae5a8SAjay Gupta  * CCG4 has two copies of the firmware in addition to the bootloader.
12435c9ae5a8SAjay Gupta  * If the device is running FW1, FW2 can be updated with the new version.
12445c9ae5a8SAjay Gupta  * Dual firmware mode allows the CCG device to stay in a PD contract and support
12455c9ae5a8SAjay Gupta  * USB PD and Type-C functionality while a firmware update is in progress.
12465c9ae5a8SAjay Gupta  ******************************************************************************/
ccg_fw_update(struct ucsi_ccg * uc,enum enum_flash_mode flash_mode)12475c9ae5a8SAjay Gupta static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode)
12485c9ae5a8SAjay Gupta {
1249a29d56c2SHeikki Krogerus 	int err = 0;
12505c9ae5a8SAjay Gupta 
12515c9ae5a8SAjay Gupta 	while (flash_mode != FLASH_NOT_NEEDED) {
12525c9ae5a8SAjay Gupta 		err = do_flash(uc, flash_mode);
12535c9ae5a8SAjay Gupta 		if (err < 0)
12545c9ae5a8SAjay Gupta 			return err;
12555c9ae5a8SAjay Gupta 		err = ccg_fw_update_needed(uc, &flash_mode);
12565c9ae5a8SAjay Gupta 		if (err < 0)
12575c9ae5a8SAjay Gupta 			return err;
12585c9ae5a8SAjay Gupta 	}
12595c9ae5a8SAjay Gupta 	dev_info(uc->dev, "CCG FW update successful\n");
12605c9ae5a8SAjay Gupta 
12615c9ae5a8SAjay Gupta 	return err;
12625c9ae5a8SAjay Gupta }
12635c9ae5a8SAjay Gupta 
ccg_restart(struct ucsi_ccg * uc)12645c9ae5a8SAjay Gupta static int ccg_restart(struct ucsi_ccg *uc)
12655c9ae5a8SAjay Gupta {
12665c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
12675c9ae5a8SAjay Gupta 	int status;
12685c9ae5a8SAjay Gupta 
12695c9ae5a8SAjay Gupta 	status = ucsi_ccg_init(uc);
12705c9ae5a8SAjay Gupta 	if (status < 0) {
12715c9ae5a8SAjay Gupta 		dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status);
12725c9ae5a8SAjay Gupta 		return status;
12735c9ae5a8SAjay Gupta 	}
12745c9ae5a8SAjay Gupta 
12755767f400SSanket Goswami 	status = ccg_request_irq(uc);
12765c9ae5a8SAjay Gupta 	if (status < 0) {
12775c9ae5a8SAjay Gupta 		dev_err(dev, "request_threaded_irq failed - %d\n", status);
12785c9ae5a8SAjay Gupta 		return status;
12795c9ae5a8SAjay Gupta 	}
12805c9ae5a8SAjay Gupta 
1281e32fd989SHeikki Krogerus 	status = ucsi_register(uc->ucsi);
1282e32fd989SHeikki Krogerus 	if (status) {
1283e32fd989SHeikki Krogerus 		dev_err(uc->dev, "failed to register the interface\n");
1284e32fd989SHeikki Krogerus 		return status;
12855c9ae5a8SAjay Gupta 	}
12865c9ae5a8SAjay Gupta 
128757a5e5f9SAjay Gupta 	pm_runtime_enable(uc->dev);
12885c9ae5a8SAjay Gupta 	return 0;
12895c9ae5a8SAjay Gupta }
12905c9ae5a8SAjay Gupta 
ccg_update_firmware(struct work_struct * work)12915c9ae5a8SAjay Gupta static void ccg_update_firmware(struct work_struct *work)
12925c9ae5a8SAjay Gupta {
12935c9ae5a8SAjay Gupta 	struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work);
12945c9ae5a8SAjay Gupta 	enum enum_flash_mode flash_mode;
12955c9ae5a8SAjay Gupta 	int status;
12965c9ae5a8SAjay Gupta 
12975c9ae5a8SAjay Gupta 	status = ccg_fw_update_needed(uc, &flash_mode);
12985c9ae5a8SAjay Gupta 	if (status < 0)
12995c9ae5a8SAjay Gupta 		return;
13005c9ae5a8SAjay Gupta 
13015c9ae5a8SAjay Gupta 	if (flash_mode != FLASH_NOT_NEEDED) {
1302e32fd989SHeikki Krogerus 		ucsi_unregister(uc->ucsi);
130357a5e5f9SAjay Gupta 		pm_runtime_disable(uc->dev);
13045c9ae5a8SAjay Gupta 		free_irq(uc->irq, uc);
13055c9ae5a8SAjay Gupta 
13065c9ae5a8SAjay Gupta 		ccg_fw_update(uc, flash_mode);
13075c9ae5a8SAjay Gupta 		ccg_restart(uc);
13085c9ae5a8SAjay Gupta 	}
13095c9ae5a8SAjay Gupta }
13105c9ae5a8SAjay Gupta 
do_flash_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t n)13115c9ae5a8SAjay Gupta static ssize_t do_flash_store(struct device *dev,
13125c9ae5a8SAjay Gupta 			      struct device_attribute *attr,
13135c9ae5a8SAjay Gupta 			      const char *buf, size_t n)
13145c9ae5a8SAjay Gupta {
13155c9ae5a8SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
13165c9ae5a8SAjay Gupta 	bool flash;
13175c9ae5a8SAjay Gupta 
13185c9ae5a8SAjay Gupta 	if (kstrtobool(buf, &flash))
13195c9ae5a8SAjay Gupta 		return -EINVAL;
13205c9ae5a8SAjay Gupta 
13215c9ae5a8SAjay Gupta 	if (!flash)
13225c9ae5a8SAjay Gupta 		return n;
13235c9ae5a8SAjay Gupta 
13245c9ae5a8SAjay Gupta 	if (uc->fw_build == 0x0) {
13255c9ae5a8SAjay Gupta 		dev_err(dev, "fail to flash FW due to missing FW build info\n");
13265c9ae5a8SAjay Gupta 		return -EINVAL;
13275c9ae5a8SAjay Gupta 	}
13285c9ae5a8SAjay Gupta 
13295c9ae5a8SAjay Gupta 	schedule_work(&uc->work);
13305c9ae5a8SAjay Gupta 	return n;
13315c9ae5a8SAjay Gupta }
13325c9ae5a8SAjay Gupta 
13335c9ae5a8SAjay Gupta static DEVICE_ATTR_WO(do_flash);
13345c9ae5a8SAjay Gupta 
13352e18b14eSGreg Kroah-Hartman static struct attribute *ucsi_ccg_attrs[] = {
13365c9ae5a8SAjay Gupta 	&dev_attr_do_flash.attr,
13375c9ae5a8SAjay Gupta 	NULL,
13385c9ae5a8SAjay Gupta };
13392e18b14eSGreg Kroah-Hartman ATTRIBUTE_GROUPS(ucsi_ccg);
13405c9ae5a8SAjay Gupta 
ucsi_ccg_probe(struct i2c_client * client)1341d24182b1SUwe Kleine-König static int ucsi_ccg_probe(struct i2c_client *client)
1342247c554aSAjay Gupta {
1343247c554aSAjay Gupta 	struct device *dev = &client->dev;
1344247c554aSAjay Gupta 	struct ucsi_ccg *uc;
13456d9e0669SWayne Chang 	const char *fw_name;
1346247c554aSAjay Gupta 	int status;
1347247c554aSAjay Gupta 
1348247c554aSAjay Gupta 	uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
1349247c554aSAjay Gupta 	if (!uc)
1350247c554aSAjay Gupta 		return -ENOMEM;
1351247c554aSAjay Gupta 
1352247c554aSAjay Gupta 	uc->dev = dev;
1353247c554aSAjay Gupta 	uc->client = client;
13545767f400SSanket Goswami 	uc->irq = client->irq;
13555c9ae5a8SAjay Gupta 	mutex_init(&uc->lock);
1356e32fd989SHeikki Krogerus 	init_completion(&uc->complete);
13575c9ae5a8SAjay Gupta 	INIT_WORK(&uc->work, ccg_update_firmware);
1358f0e4cd94SAjay Gupta 	INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
13595c9ae5a8SAjay Gupta 
13605c9ae5a8SAjay Gupta 	/* Only fail FW flashing when FW build information is not provided */
13616d9e0669SWayne Chang 	status = device_property_read_string(dev, "firmware-name", &fw_name);
13626d9e0669SWayne Chang 	if (!status) {
13636d9e0669SWayne Chang 		if (!strcmp(fw_name, "nvidia,jetson-agx-xavier"))
13646d9e0669SWayne Chang 			uc->fw_build = CCG_FW_BUILD_NVIDIA_TEGRA;
13656d9e0669SWayne Chang 		else if (!strcmp(fw_name, "nvidia,gpu"))
13666d9e0669SWayne Chang 			uc->fw_build = CCG_FW_BUILD_NVIDIA;
13676d9e0669SWayne Chang 	}
13686d9e0669SWayne Chang 
13696d9e0669SWayne Chang 	if (!uc->fw_build)
13705c9ae5a8SAjay Gupta 		dev_err(uc->dev, "failed to get FW build information\n");
1371247c554aSAjay Gupta 
1372247c554aSAjay Gupta 	/* reset ccg device and initialize ucsi */
1373247c554aSAjay Gupta 	status = ucsi_ccg_init(uc);
1374247c554aSAjay Gupta 	if (status < 0) {
1375247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
1376247c554aSAjay Gupta 		return status;
1377247c554aSAjay Gupta 	}
1378247c554aSAjay Gupta 
13795d438e20SAjay Gupta 	status = get_fw_info(uc);
13805d438e20SAjay Gupta 	if (status < 0) {
13815d438e20SAjay Gupta 		dev_err(uc->dev, "get_fw_info failed - %d\n", status);
13825d438e20SAjay Gupta 		return status;
13835d438e20SAjay Gupta 	}
13845d438e20SAjay Gupta 
13855c9ae5a8SAjay Gupta 	uc->port_num = 1;
13865c9ae5a8SAjay Gupta 
13875c9ae5a8SAjay Gupta 	if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
13885c9ae5a8SAjay Gupta 		uc->port_num++;
13895c9ae5a8SAjay Gupta 
1390e32fd989SHeikki Krogerus 	uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops);
1391e32fd989SHeikki Krogerus 	if (IS_ERR(uc->ucsi))
1392e32fd989SHeikki Krogerus 		return PTR_ERR(uc->ucsi);
1393e32fd989SHeikki Krogerus 
1394e32fd989SHeikki Krogerus 	ucsi_set_drvdata(uc->ucsi, uc);
1395e32fd989SHeikki Krogerus 
13965767f400SSanket Goswami 	status = ccg_request_irq(uc);
1397247c554aSAjay Gupta 	if (status < 0) {
1398247c554aSAjay Gupta 		dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
1399e32fd989SHeikki Krogerus 		goto out_ucsi_destroy;
1400247c554aSAjay Gupta 	}
1401247c554aSAjay Gupta 
1402e32fd989SHeikki Krogerus 	status = ucsi_register(uc->ucsi);
1403e32fd989SHeikki Krogerus 	if (status)
1404e32fd989SHeikki Krogerus 		goto out_free_irq;
1405247c554aSAjay Gupta 
1406247c554aSAjay Gupta 	i2c_set_clientdata(client, uc);
14075c9ae5a8SAjay Gupta 
1408a94ecde4SAjay Gupta 	pm_runtime_set_active(uc->dev);
1409a94ecde4SAjay Gupta 	pm_runtime_enable(uc->dev);
14108530e4e2SHeikki Krogerus 	pm_runtime_use_autosuspend(uc->dev);
14118530e4e2SHeikki Krogerus 	pm_runtime_set_autosuspend_delay(uc->dev, 5000);
1412a94ecde4SAjay Gupta 	pm_runtime_idle(uc->dev);
1413a94ecde4SAjay Gupta 
1414247c554aSAjay Gupta 	return 0;
1415e32fd989SHeikki Krogerus 
1416e32fd989SHeikki Krogerus out_free_irq:
1417e32fd989SHeikki Krogerus 	free_irq(uc->irq, uc);
1418e32fd989SHeikki Krogerus out_ucsi_destroy:
1419e32fd989SHeikki Krogerus 	ucsi_destroy(uc->ucsi);
1420e32fd989SHeikki Krogerus 
1421e32fd989SHeikki Krogerus 	return status;
1422247c554aSAjay Gupta }
1423247c554aSAjay Gupta 
ucsi_ccg_remove(struct i2c_client * client)1424ed5c2f5fSUwe Kleine-König static void ucsi_ccg_remove(struct i2c_client *client)
1425247c554aSAjay Gupta {
1426247c554aSAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1427247c554aSAjay Gupta 
1428f0e4cd94SAjay Gupta 	cancel_work_sync(&uc->pm_work);
14295c9ae5a8SAjay Gupta 	cancel_work_sync(&uc->work);
1430a94ecde4SAjay Gupta 	pm_runtime_disable(uc->dev);
1431e32fd989SHeikki Krogerus 	ucsi_unregister(uc->ucsi);
1432e32fd989SHeikki Krogerus 	ucsi_destroy(uc->ucsi);
14335c9ae5a8SAjay Gupta 	free_irq(uc->irq, uc);
1434247c554aSAjay Gupta }
1435247c554aSAjay Gupta 
14366d9e0669SWayne Chang static const struct of_device_id ucsi_ccg_of_match_table[] = {
14376d9e0669SWayne Chang 		{ .compatible = "cypress,cypd4226", },
14386d9e0669SWayne Chang 		{ /* sentinel */ }
14396d9e0669SWayne Chang };
14406d9e0669SWayne Chang MODULE_DEVICE_TABLE(of, ucsi_ccg_of_match_table);
14416d9e0669SWayne Chang 
1442247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = {
1443247c554aSAjay Gupta 	{"ccgx-ucsi", 0},
1444247c554aSAjay Gupta 	{}
1445247c554aSAjay Gupta };
1446247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
1447247c554aSAjay Gupta 
14485fd6c4f0SSanket Goswami static const struct acpi_device_id amd_i2c_ucsi_match[] = {
14495fd6c4f0SSanket Goswami 	{"AMDI0042"},
14505fd6c4f0SSanket Goswami 	{}
14515fd6c4f0SSanket Goswami };
14525fd6c4f0SSanket Goswami MODULE_DEVICE_TABLE(acpi, amd_i2c_ucsi_match);
14535fd6c4f0SSanket Goswami 
ucsi_ccg_resume(struct device * dev)1454a94ecde4SAjay Gupta static int ucsi_ccg_resume(struct device *dev)
1455a94ecde4SAjay Gupta {
1456a94ecde4SAjay Gupta 	struct i2c_client *client = to_i2c_client(dev);
1457a94ecde4SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1458a94ecde4SAjay Gupta 
1459a94ecde4SAjay Gupta 	return ucsi_resume(uc->ucsi);
1460a94ecde4SAjay Gupta }
1461a94ecde4SAjay Gupta 
ucsi_ccg_runtime_suspend(struct device * dev)1462a94ecde4SAjay Gupta static int ucsi_ccg_runtime_suspend(struct device *dev)
1463a94ecde4SAjay Gupta {
1464a94ecde4SAjay Gupta 	return 0;
1465a94ecde4SAjay Gupta }
1466a94ecde4SAjay Gupta 
ucsi_ccg_runtime_resume(struct device * dev)1467a94ecde4SAjay Gupta static int ucsi_ccg_runtime_resume(struct device *dev)
1468a94ecde4SAjay Gupta {
1469f0e4cd94SAjay Gupta 	struct i2c_client *client = to_i2c_client(dev);
1470f0e4cd94SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1471f0e4cd94SAjay Gupta 
1472f0e4cd94SAjay Gupta 	/*
1473f0e4cd94SAjay Gupta 	 * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
1474f0e4cd94SAjay Gupta 	 * of missing interrupt when a device is connected for runtime resume.
1475f0e4cd94SAjay Gupta 	 * Schedule a work to call ISR as a workaround.
1476f0e4cd94SAjay Gupta 	 */
1477f0e4cd94SAjay Gupta 	if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
14788530e4e2SHeikki Krogerus 	    uc->fw_version <= CCG_OLD_FW_VERSION)
1479f0e4cd94SAjay Gupta 		schedule_work(&uc->pm_work);
1480f0e4cd94SAjay Gupta 
1481a94ecde4SAjay Gupta 	return 0;
1482a94ecde4SAjay Gupta }
1483a94ecde4SAjay Gupta 
1484a94ecde4SAjay Gupta static const struct dev_pm_ops ucsi_ccg_pm = {
1485a94ecde4SAjay Gupta 	.resume = ucsi_ccg_resume,
1486a94ecde4SAjay Gupta 	.runtime_suspend = ucsi_ccg_runtime_suspend,
1487a94ecde4SAjay Gupta 	.runtime_resume = ucsi_ccg_runtime_resume,
1488a94ecde4SAjay Gupta };
1489a94ecde4SAjay Gupta 
1490247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = {
1491247c554aSAjay Gupta 	.driver = {
1492247c554aSAjay Gupta 		.name = "ucsi_ccg",
1493a94ecde4SAjay Gupta 		.pm = &ucsi_ccg_pm,
14942e18b14eSGreg Kroah-Hartman 		.dev_groups = ucsi_ccg_groups,
14955fd6c4f0SSanket Goswami 		.acpi_match_table = amd_i2c_ucsi_match,
14966d9e0669SWayne Chang 		.of_match_table = ucsi_ccg_of_match_table,
1497247c554aSAjay Gupta 	},
1498*7126a2aeSUwe Kleine-König 	.probe = ucsi_ccg_probe,
1499247c554aSAjay Gupta 	.remove = ucsi_ccg_remove,
1500247c554aSAjay Gupta 	.id_table = ucsi_ccg_device_id,
1501247c554aSAjay Gupta };
1502247c554aSAjay Gupta 
1503247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver);
1504247c554aSAjay Gupta 
1505247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
1506247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
1507247c554aSAjay Gupta MODULE_LICENSE("GPL v2");
1508