xref: /openbmc/linux/drivers/usb/typec/ucsi/ucsi_ccg.c (revision f0e4cd94)
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>
19247c554aSAjay Gupta 
20247c554aSAjay Gupta #include <asm/unaligned.h>
21247c554aSAjay Gupta #include "ucsi.h"
22247c554aSAjay Gupta 
235d438e20SAjay Gupta enum enum_fw_mode {
245d438e20SAjay Gupta 	BOOT,   /* bootloader */
255d438e20SAjay Gupta 	FW1,    /* FW partition-1 (contains secondary fw) */
265d438e20SAjay Gupta 	FW2,    /* FW partition-2 (contains primary fw) */
275d438e20SAjay Gupta 	FW_INVALID,
285d438e20SAjay Gupta };
295d438e20SAjay Gupta 
305c9ae5a8SAjay Gupta #define CCGX_RAB_DEVICE_MODE			0x0000
315c9ae5a8SAjay Gupta #define CCGX_RAB_INTR_REG			0x0006
325c9ae5a8SAjay Gupta #define  DEV_INT				BIT(0)
335c9ae5a8SAjay Gupta #define  PORT0_INT				BIT(1)
345c9ae5a8SAjay Gupta #define  PORT1_INT				BIT(2)
355c9ae5a8SAjay Gupta #define  UCSI_READ_INT				BIT(7)
365c9ae5a8SAjay Gupta #define CCGX_RAB_JUMP_TO_BOOT			0x0007
375c9ae5a8SAjay Gupta #define  TO_BOOT				'J'
385c9ae5a8SAjay Gupta #define  TO_ALT_FW				'A'
395c9ae5a8SAjay Gupta #define CCGX_RAB_RESET_REQ			0x0008
405c9ae5a8SAjay Gupta #define  RESET_SIG				'R'
415c9ae5a8SAjay Gupta #define  CMD_RESET_I2C				0x0
425c9ae5a8SAjay Gupta #define  CMD_RESET_DEV				0x1
435c9ae5a8SAjay Gupta #define CCGX_RAB_ENTER_FLASHING			0x000A
445c9ae5a8SAjay Gupta #define  FLASH_ENTER_SIG			'P'
455c9ae5a8SAjay Gupta #define CCGX_RAB_VALIDATE_FW			0x000B
465c9ae5a8SAjay Gupta #define CCGX_RAB_FLASH_ROW_RW			0x000C
475c9ae5a8SAjay Gupta #define  FLASH_SIG				'F'
485c9ae5a8SAjay Gupta #define  FLASH_RD_CMD				0x0
495c9ae5a8SAjay Gupta #define  FLASH_WR_CMD				0x1
505c9ae5a8SAjay Gupta #define  FLASH_FWCT1_WR_CMD			0x2
515c9ae5a8SAjay Gupta #define  FLASH_FWCT2_WR_CMD			0x3
525c9ae5a8SAjay Gupta #define  FLASH_FWCT_SIG_WR_CMD			0x4
535c9ae5a8SAjay Gupta #define CCGX_RAB_READ_ALL_VER			0x0010
545c9ae5a8SAjay Gupta #define CCGX_RAB_READ_FW2_VER			0x0020
555c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL			0x0039
565c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START		BIT(0)
575c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP		BIT(1)
585c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset)	(0xf000 | ((offset) & 0xff))
595c9ae5a8SAjay Gupta #define REG_FLASH_RW_MEM        0x0200
605c9ae5a8SAjay Gupta #define DEV_REG_IDX				CCGX_RAB_DEVICE_MODE
615c9ae5a8SAjay Gupta #define CCGX_RAB_PDPORT_ENABLE			0x002C
625c9ae5a8SAjay Gupta #define  PDPORT_1		BIT(0)
635c9ae5a8SAjay Gupta #define  PDPORT_2		BIT(1)
645c9ae5a8SAjay Gupta #define CCGX_RAB_RESPONSE			0x007E
655c9ae5a8SAjay Gupta #define  ASYNC_EVENT				BIT(7)
665c9ae5a8SAjay Gupta 
675c9ae5a8SAjay Gupta /* CCGx events & async msg codes */
685c9ae5a8SAjay Gupta #define RESET_COMPLETE		0x80
695c9ae5a8SAjay Gupta #define EVENT_INDEX		RESET_COMPLETE
705c9ae5a8SAjay Gupta #define PORT_CONNECT_DET	0x84
715c9ae5a8SAjay Gupta #define PORT_DISCONNECT_DET	0x85
725c9ae5a8SAjay Gupta #define ROLE_SWAP_COMPELETE	0x87
735c9ae5a8SAjay Gupta 
745c9ae5a8SAjay Gupta /* ccg firmware */
755c9ae5a8SAjay Gupta #define CYACD_LINE_SIZE         527
765c9ae5a8SAjay Gupta #define CCG4_ROW_SIZE           256
775c9ae5a8SAjay Gupta #define FW1_METADATA_ROW        0x1FF
785c9ae5a8SAjay Gupta #define FW2_METADATA_ROW        0x1FE
795c9ae5a8SAjay Gupta #define FW_CFG_TABLE_SIG_SIZE	256
805c9ae5a8SAjay Gupta 
815c9ae5a8SAjay Gupta static int secondary_fw_min_ver = 41;
825c9ae5a8SAjay Gupta 
835c9ae5a8SAjay Gupta enum enum_flash_mode {
845c9ae5a8SAjay Gupta 	SECONDARY_BL,	/* update secondary using bootloader */
855c9ae5a8SAjay Gupta 	PRIMARY,	/* update primary using secondary */
865c9ae5a8SAjay Gupta 	SECONDARY,	/* update secondary using primary */
875c9ae5a8SAjay Gupta 	FLASH_NOT_NEEDED,	/* update not required */
885c9ae5a8SAjay Gupta 	FLASH_INVALID,
895c9ae5a8SAjay Gupta };
905c9ae5a8SAjay Gupta 
915c9ae5a8SAjay Gupta static const char * const ccg_fw_names[] = {
925c9ae5a8SAjay Gupta 	"ccg_boot.cyacd",
935c9ae5a8SAjay Gupta 	"ccg_primary.cyacd",
945c9ae5a8SAjay Gupta 	"ccg_secondary.cyacd"
955c9ae5a8SAjay Gupta };
965c9ae5a8SAjay Gupta 
975d438e20SAjay Gupta struct ccg_dev_info {
985d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0)
995d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT)
1005d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2)
1015d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT)
1025d438e20SAjay Gupta 	u8 mode;
1035d438e20SAjay Gupta 	u8 bl_mode;
1045d438e20SAjay Gupta 	__le16 silicon_id;
1055d438e20SAjay Gupta 	__le16 bl_last_row;
1065d438e20SAjay Gupta } __packed;
1075d438e20SAjay Gupta 
1085d438e20SAjay Gupta struct version_format {
1095d438e20SAjay Gupta 	__le16 build;
1105d438e20SAjay Gupta 	u8 patch;
1115d438e20SAjay Gupta 	u8 ver;
112f0e4cd94SAjay Gupta #define CCG_VERSION_PATCH(x) ((x) << 16)
113f0e4cd94SAjay Gupta #define CCG_VERSION(x)	((x) << 24)
1145d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0)
1155d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT)
1165d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4)
1175d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT)
1185d438e20SAjay Gupta } __packed;
1195d438e20SAjay Gupta 
120f0e4cd94SAjay Gupta /*
121f0e4cd94SAjay Gupta  * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
122f0e4cd94SAjay Gupta  * of missing interrupt when a device is connected for runtime resume
123f0e4cd94SAjay Gupta  */
124f0e4cd94SAjay Gupta #define CCG_FW_BUILD_NVIDIA	(('n' << 8) | 'v')
125f0e4cd94SAjay Gupta #define CCG_OLD_FW_VERSION	(CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))
126f0e4cd94SAjay Gupta 
1275d438e20SAjay Gupta struct version_info {
1285d438e20SAjay Gupta 	struct version_format base;
1295d438e20SAjay Gupta 	struct version_format app;
1305d438e20SAjay Gupta };
1315d438e20SAjay Gupta 
1325c9ae5a8SAjay Gupta struct fw_config_table {
1335c9ae5a8SAjay Gupta 	u32 identity;
1345c9ae5a8SAjay Gupta 	u16 table_size;
1355c9ae5a8SAjay Gupta 	u8 fwct_version;
1365c9ae5a8SAjay Gupta 	u8 is_key_change;
1375c9ae5a8SAjay Gupta 	u8 guid[16];
1385c9ae5a8SAjay Gupta 	struct version_format base;
1395c9ae5a8SAjay Gupta 	struct version_format app;
1405c9ae5a8SAjay Gupta 	u8 primary_fw_digest[32];
1415c9ae5a8SAjay Gupta 	u32 key_exp_length;
1425c9ae5a8SAjay Gupta 	u8 key_modulus[256];
1435c9ae5a8SAjay Gupta 	u8 key_exp[4];
1445c9ae5a8SAjay Gupta };
1455c9ae5a8SAjay Gupta 
1465c9ae5a8SAjay Gupta /* CCGx response codes */
1475c9ae5a8SAjay Gupta enum ccg_resp_code {
1485c9ae5a8SAjay Gupta 	CMD_NO_RESP             = 0x00,
1495c9ae5a8SAjay Gupta 	CMD_SUCCESS             = 0x02,
1505c9ae5a8SAjay Gupta 	FLASH_DATA_AVAILABLE    = 0x03,
1515c9ae5a8SAjay Gupta 	CMD_INVALID             = 0x05,
1525c9ae5a8SAjay Gupta 	FLASH_UPDATE_FAIL       = 0x07,
1535c9ae5a8SAjay Gupta 	INVALID_FW              = 0x08,
1545c9ae5a8SAjay Gupta 	INVALID_ARG             = 0x09,
1555c9ae5a8SAjay Gupta 	CMD_NOT_SUPPORT         = 0x0A,
1565c9ae5a8SAjay Gupta 	TRANSACTION_FAIL        = 0x0C,
1575c9ae5a8SAjay Gupta 	PD_CMD_FAIL             = 0x0D,
1585c9ae5a8SAjay Gupta 	UNDEF_ERROR             = 0x0F,
1595c9ae5a8SAjay Gupta 	INVALID_RESP		= 0x10,
1605c9ae5a8SAjay Gupta };
1615c9ae5a8SAjay Gupta 
1625c9ae5a8SAjay Gupta #define CCG_EVENT_MAX	(EVENT_INDEX + 43)
1635c9ae5a8SAjay Gupta 
1645c9ae5a8SAjay Gupta struct ccg_cmd {
1655c9ae5a8SAjay Gupta 	u16 reg;
1665c9ae5a8SAjay Gupta 	u32 data;
1675c9ae5a8SAjay Gupta 	int len;
1685c9ae5a8SAjay Gupta 	u32 delay; /* ms delay for cmd timeout  */
1695c9ae5a8SAjay Gupta };
1705c9ae5a8SAjay Gupta 
1715c9ae5a8SAjay Gupta struct ccg_resp {
1725c9ae5a8SAjay Gupta 	u8 code;
1735c9ae5a8SAjay Gupta 	u8 length;
1745c9ae5a8SAjay Gupta };
1755c9ae5a8SAjay Gupta 
176247c554aSAjay Gupta struct ucsi_ccg {
177247c554aSAjay Gupta 	struct device *dev;
178247c554aSAjay Gupta 	struct ucsi *ucsi;
179247c554aSAjay Gupta 	struct ucsi_ppm ppm;
180247c554aSAjay Gupta 	struct i2c_client *client;
1815d438e20SAjay Gupta 	struct ccg_dev_info info;
1825d438e20SAjay Gupta 	/* version info for boot, primary and secondary */
1835d438e20SAjay Gupta 	struct version_info version[FW2 + 1];
184f0e4cd94SAjay Gupta 	u32 fw_version;
1855c9ae5a8SAjay Gupta 	/* CCG HPI communication flags */
1865c9ae5a8SAjay Gupta 	unsigned long flags;
1875c9ae5a8SAjay Gupta #define RESET_PENDING	0
1885c9ae5a8SAjay Gupta #define DEV_CMD_PENDING	1
1895c9ae5a8SAjay Gupta 	struct ccg_resp dev_resp;
1905c9ae5a8SAjay Gupta 	u8 cmd_resp;
1915c9ae5a8SAjay Gupta 	int port_num;
1925c9ae5a8SAjay Gupta 	int irq;
1935c9ae5a8SAjay Gupta 	struct work_struct work;
1945c9ae5a8SAjay Gupta 	struct mutex lock; /* to sync between user and driver thread */
195247c554aSAjay Gupta 
1965c9ae5a8SAjay Gupta 	/* fw build with vendor information */
1975c9ae5a8SAjay Gupta 	u16 fw_build;
198f0e4cd94SAjay Gupta 	bool run_isr; /* flag to call ISR routine during resume */
199f0e4cd94SAjay Gupta 	struct work_struct pm_work;
2005c9ae5a8SAjay Gupta };
201247c554aSAjay Gupta 
202247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
203247c554aSAjay Gupta {
204247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
205247c554aSAjay Gupta 	const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
206247c554aSAjay Gupta 	unsigned char buf[2];
207247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
208247c554aSAjay Gupta 		{
209247c554aSAjay Gupta 			.addr	= client->addr,
210247c554aSAjay Gupta 			.flags  = 0x0,
211247c554aSAjay Gupta 			.len	= sizeof(buf),
212247c554aSAjay Gupta 			.buf	= buf,
213247c554aSAjay Gupta 		},
214247c554aSAjay Gupta 		{
215247c554aSAjay Gupta 			.addr	= client->addr,
216247c554aSAjay Gupta 			.flags  = I2C_M_RD,
217247c554aSAjay Gupta 			.buf	= data,
218247c554aSAjay Gupta 		},
219247c554aSAjay Gupta 	};
220247c554aSAjay Gupta 	u32 rlen, rem_len = len, max_read_len = len;
221247c554aSAjay Gupta 	int status;
222247c554aSAjay Gupta 
223247c554aSAjay Gupta 	/* check any max_read_len limitation on i2c adapter */
224247c554aSAjay Gupta 	if (quirks && quirks->max_read_len)
225247c554aSAjay Gupta 		max_read_len = quirks->max_read_len;
226247c554aSAjay Gupta 
227f0e4cd94SAjay Gupta 	if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
228f0e4cd94SAjay Gupta 	    uc->fw_version <= CCG_OLD_FW_VERSION) {
229f0e4cd94SAjay Gupta 		mutex_lock(&uc->lock);
230f0e4cd94SAjay Gupta 		/*
231f0e4cd94SAjay Gupta 		 * Do not schedule pm_work to run ISR in
232f0e4cd94SAjay Gupta 		 * ucsi_ccg_runtime_resume() after pm_runtime_get_sync()
233f0e4cd94SAjay Gupta 		 * since we are already in ISR path.
234f0e4cd94SAjay Gupta 		 */
235f0e4cd94SAjay Gupta 		uc->run_isr = false;
236f0e4cd94SAjay Gupta 		mutex_unlock(&uc->lock);
237f0e4cd94SAjay Gupta 	}
238f0e4cd94SAjay Gupta 
239a94ecde4SAjay Gupta 	pm_runtime_get_sync(uc->dev);
240247c554aSAjay Gupta 	while (rem_len > 0) {
241247c554aSAjay Gupta 		msgs[1].buf = &data[len - rem_len];
242247c554aSAjay Gupta 		rlen = min_t(u16, rem_len, max_read_len);
243247c554aSAjay Gupta 		msgs[1].len = rlen;
244247c554aSAjay Gupta 		put_unaligned_le16(rab, buf);
245247c554aSAjay Gupta 		status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
246247c554aSAjay Gupta 		if (status < 0) {
247247c554aSAjay Gupta 			dev_err(uc->dev, "i2c_transfer failed %d\n", status);
248a94ecde4SAjay Gupta 			pm_runtime_put_sync(uc->dev);
249247c554aSAjay Gupta 			return status;
250247c554aSAjay Gupta 		}
251247c554aSAjay Gupta 		rab += rlen;
252247c554aSAjay Gupta 		rem_len -= rlen;
253247c554aSAjay Gupta 	}
254247c554aSAjay Gupta 
255a94ecde4SAjay Gupta 	pm_runtime_put_sync(uc->dev);
256247c554aSAjay Gupta 	return 0;
257247c554aSAjay Gupta }
258247c554aSAjay Gupta 
259247c554aSAjay Gupta static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
260247c554aSAjay Gupta {
261247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
262247c554aSAjay Gupta 	unsigned char *buf;
263247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
264247c554aSAjay Gupta 		{
265247c554aSAjay Gupta 			.addr	= client->addr,
266247c554aSAjay Gupta 			.flags  = 0x0,
267247c554aSAjay Gupta 		}
268247c554aSAjay Gupta 	};
269247c554aSAjay Gupta 	int status;
270247c554aSAjay Gupta 
271247c554aSAjay Gupta 	buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
272247c554aSAjay Gupta 	if (!buf)
273247c554aSAjay Gupta 		return -ENOMEM;
274247c554aSAjay Gupta 
275247c554aSAjay Gupta 	put_unaligned_le16(rab, buf);
276247c554aSAjay Gupta 	memcpy(buf + sizeof(rab), data, len);
277247c554aSAjay Gupta 
278247c554aSAjay Gupta 	msgs[0].len = len + sizeof(rab);
279247c554aSAjay Gupta 	msgs[0].buf = buf;
280247c554aSAjay Gupta 
281f0e4cd94SAjay Gupta 	if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
282f0e4cd94SAjay Gupta 	    uc->fw_version <= CCG_OLD_FW_VERSION) {
283f0e4cd94SAjay Gupta 		mutex_lock(&uc->lock);
284f0e4cd94SAjay Gupta 		/*
285f0e4cd94SAjay Gupta 		 * Do not schedule pm_work to run ISR in
286f0e4cd94SAjay Gupta 		 * ucsi_ccg_runtime_resume() after pm_runtime_get_sync()
287f0e4cd94SAjay Gupta 		 * since we are already in ISR path.
288f0e4cd94SAjay Gupta 		 */
289f0e4cd94SAjay Gupta 		uc->run_isr = false;
290f0e4cd94SAjay Gupta 		mutex_unlock(&uc->lock);
291f0e4cd94SAjay Gupta 	}
292f0e4cd94SAjay Gupta 
293a94ecde4SAjay Gupta 	pm_runtime_get_sync(uc->dev);
294247c554aSAjay Gupta 	status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
295247c554aSAjay Gupta 	if (status < 0) {
296247c554aSAjay Gupta 		dev_err(uc->dev, "i2c_transfer failed %d\n", status);
297a94ecde4SAjay Gupta 		pm_runtime_put_sync(uc->dev);
298247c554aSAjay Gupta 		kfree(buf);
299247c554aSAjay Gupta 		return status;
300247c554aSAjay Gupta 	}
301247c554aSAjay Gupta 
302a94ecde4SAjay Gupta 	pm_runtime_put_sync(uc->dev);
303247c554aSAjay Gupta 	kfree(buf);
304247c554aSAjay Gupta 	return 0;
305247c554aSAjay Gupta }
306247c554aSAjay Gupta 
307247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc)
308247c554aSAjay Gupta {
309247c554aSAjay Gupta 	unsigned int count = 10;
310247c554aSAjay Gupta 	u8 data;
311247c554aSAjay Gupta 	int status;
312247c554aSAjay Gupta 
313247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_STOP;
314247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
315247c554aSAjay Gupta 	if (status < 0)
316247c554aSAjay Gupta 		return status;
317247c554aSAjay Gupta 
318247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_START;
319247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
320247c554aSAjay Gupta 	if (status < 0)
321247c554aSAjay Gupta 		return status;
322247c554aSAjay Gupta 
323247c554aSAjay Gupta 	/*
324247c554aSAjay Gupta 	 * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
325247c554aSAjay Gupta 	 * register write will push response which must be cleared.
326247c554aSAjay Gupta 	 */
327247c554aSAjay Gupta 	do {
328247c554aSAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
329247c554aSAjay Gupta 		if (status < 0)
330247c554aSAjay Gupta 			return status;
331247c554aSAjay Gupta 
332247c554aSAjay Gupta 		if (!data)
333247c554aSAjay Gupta 			return 0;
334247c554aSAjay Gupta 
335247c554aSAjay Gupta 		status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
336247c554aSAjay Gupta 		if (status < 0)
337247c554aSAjay Gupta 			return status;
338247c554aSAjay Gupta 
339247c554aSAjay Gupta 		usleep_range(10000, 11000);
340247c554aSAjay Gupta 	} while (--count);
341247c554aSAjay Gupta 
342247c554aSAjay Gupta 	return -ETIMEDOUT;
343247c554aSAjay Gupta }
344247c554aSAjay Gupta 
345247c554aSAjay Gupta static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
346247c554aSAjay Gupta {
347247c554aSAjay Gupta 	u8 *ppm = (u8 *)uc->ppm.data;
348247c554aSAjay Gupta 	int status;
349247c554aSAjay Gupta 	u16 rab;
350247c554aSAjay Gupta 
351247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
352247c554aSAjay Gupta 	status = ccg_write(uc, rab, ppm +
353247c554aSAjay Gupta 			   offsetof(struct ucsi_data, message_out),
354247c554aSAjay Gupta 			   sizeof(uc->ppm.data->message_out));
355247c554aSAjay Gupta 	if (status < 0)
356247c554aSAjay Gupta 		return status;
357247c554aSAjay Gupta 
358247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
359247c554aSAjay Gupta 	return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
360247c554aSAjay Gupta 			 sizeof(uc->ppm.data->ctrl));
361247c554aSAjay Gupta }
362247c554aSAjay Gupta 
363247c554aSAjay Gupta static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
364247c554aSAjay Gupta {
365247c554aSAjay Gupta 	u8 *ppm = (u8 *)uc->ppm.data;
366247c554aSAjay Gupta 	int status;
367247c554aSAjay Gupta 	u16 rab;
368247c554aSAjay Gupta 
369247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
370247c554aSAjay Gupta 	status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
371247c554aSAjay Gupta 			  sizeof(uc->ppm.data->cci));
372247c554aSAjay Gupta 	if (status < 0)
373247c554aSAjay Gupta 		return status;
374247c554aSAjay Gupta 
375247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
376247c554aSAjay Gupta 	return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
377247c554aSAjay Gupta 			sizeof(uc->ppm.data->message_in));
378247c554aSAjay Gupta }
379247c554aSAjay Gupta 
380247c554aSAjay Gupta static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
381247c554aSAjay Gupta {
382247c554aSAjay Gupta 	int status;
383247c554aSAjay Gupta 	unsigned char data;
384247c554aSAjay Gupta 
385247c554aSAjay Gupta 	status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
386247c554aSAjay Gupta 	if (status < 0)
387247c554aSAjay Gupta 		return status;
388247c554aSAjay Gupta 
389247c554aSAjay Gupta 	return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
390247c554aSAjay Gupta }
391247c554aSAjay Gupta 
392247c554aSAjay Gupta static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
393247c554aSAjay Gupta {
394247c554aSAjay Gupta 	struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
395247c554aSAjay Gupta 	int status;
396247c554aSAjay Gupta 
397247c554aSAjay Gupta 	status = ucsi_ccg_recv_data(uc);
398247c554aSAjay Gupta 	if (status < 0)
399247c554aSAjay Gupta 		return status;
400247c554aSAjay Gupta 
401247c554aSAjay Gupta 	/* ack interrupt to allow next command to run */
402247c554aSAjay Gupta 	return ucsi_ccg_ack_interrupt(uc);
403247c554aSAjay Gupta }
404247c554aSAjay Gupta 
405247c554aSAjay Gupta static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
406247c554aSAjay Gupta {
407247c554aSAjay Gupta 	struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
408247c554aSAjay Gupta 
409247c554aSAjay Gupta 	ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
410247c554aSAjay Gupta 	return ucsi_ccg_send_data(uc);
411247c554aSAjay Gupta }
412247c554aSAjay Gupta 
413247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data)
414247c554aSAjay Gupta {
415247c554aSAjay Gupta 	struct ucsi_ccg *uc = data;
416247c554aSAjay Gupta 
417247c554aSAjay Gupta 	ucsi_notify(uc->ucsi);
418247c554aSAjay Gupta 
419247c554aSAjay Gupta 	return IRQ_HANDLED;
420247c554aSAjay Gupta }
421247c554aSAjay Gupta 
422f0e4cd94SAjay Gupta static void ccg_pm_workaround_work(struct work_struct *pm_work)
423f0e4cd94SAjay Gupta {
424f0e4cd94SAjay Gupta 	struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work);
425f0e4cd94SAjay Gupta 
426f0e4cd94SAjay Gupta 	ucsi_notify(uc->ucsi);
427f0e4cd94SAjay Gupta }
428f0e4cd94SAjay Gupta 
4295d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc)
4305d438e20SAjay Gupta {
4315d438e20SAjay Gupta 	int err;
4325d438e20SAjay Gupta 
4335d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version),
4345d438e20SAjay Gupta 		       sizeof(uc->version));
4355d438e20SAjay Gupta 	if (err < 0)
4365d438e20SAjay Gupta 		return err;
4375d438e20SAjay Gupta 
438f0e4cd94SAjay Gupta 	uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) |
439f0e4cd94SAjay Gupta 			CCG_VERSION_PATCH(uc->version[FW2].app.patch);
440f0e4cd94SAjay Gupta 
4415d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
4425d438e20SAjay Gupta 		       sizeof(uc->info));
4435d438e20SAjay Gupta 	if (err < 0)
4445d438e20SAjay Gupta 		return err;
4455d438e20SAjay Gupta 
4465d438e20SAjay Gupta 	return 0;
4475d438e20SAjay Gupta }
4485d438e20SAjay Gupta 
4495c9ae5a8SAjay Gupta static inline bool invalid_async_evt(int code)
4505c9ae5a8SAjay Gupta {
4515c9ae5a8SAjay Gupta 	return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX);
4525c9ae5a8SAjay Gupta }
4535c9ae5a8SAjay Gupta 
4545c9ae5a8SAjay Gupta static void ccg_process_response(struct ucsi_ccg *uc)
4555c9ae5a8SAjay Gupta {
4565c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
4575c9ae5a8SAjay Gupta 
4585c9ae5a8SAjay Gupta 	if (uc->dev_resp.code & ASYNC_EVENT) {
4595c9ae5a8SAjay Gupta 		if (uc->dev_resp.code == RESET_COMPLETE) {
4605c9ae5a8SAjay Gupta 			if (test_bit(RESET_PENDING, &uc->flags))
4615c9ae5a8SAjay Gupta 				uc->cmd_resp = uc->dev_resp.code;
4625c9ae5a8SAjay Gupta 			get_fw_info(uc);
4635c9ae5a8SAjay Gupta 		}
4645c9ae5a8SAjay Gupta 		if (invalid_async_evt(uc->dev_resp.code))
4655c9ae5a8SAjay Gupta 			dev_err(dev, "invalid async evt %d\n",
4665c9ae5a8SAjay Gupta 				uc->dev_resp.code);
4675c9ae5a8SAjay Gupta 	} else {
4685c9ae5a8SAjay Gupta 		if (test_bit(DEV_CMD_PENDING, &uc->flags)) {
4695c9ae5a8SAjay Gupta 			uc->cmd_resp = uc->dev_resp.code;
4705c9ae5a8SAjay Gupta 			clear_bit(DEV_CMD_PENDING, &uc->flags);
4715c9ae5a8SAjay Gupta 		} else {
4725c9ae5a8SAjay Gupta 			dev_err(dev, "dev resp 0x%04x but no cmd pending\n",
4735c9ae5a8SAjay Gupta 				uc->dev_resp.code);
4745c9ae5a8SAjay Gupta 		}
4755c9ae5a8SAjay Gupta 	}
4765c9ae5a8SAjay Gupta }
4775c9ae5a8SAjay Gupta 
4785c9ae5a8SAjay Gupta static int ccg_read_response(struct ucsi_ccg *uc)
4795c9ae5a8SAjay Gupta {
4805c9ae5a8SAjay Gupta 	unsigned long target = jiffies + msecs_to_jiffies(1000);
4815c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
4825c9ae5a8SAjay Gupta 	u8 intval;
4835c9ae5a8SAjay Gupta 	int status;
4845c9ae5a8SAjay Gupta 
4855c9ae5a8SAjay Gupta 	/* wait for interrupt status to get updated */
4865c9ae5a8SAjay Gupta 	do {
4875c9ae5a8SAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval,
4885c9ae5a8SAjay Gupta 				  sizeof(intval));
4895c9ae5a8SAjay Gupta 		if (status < 0)
4905c9ae5a8SAjay Gupta 			return status;
4915c9ae5a8SAjay Gupta 
4925c9ae5a8SAjay Gupta 		if (intval & DEV_INT)
4935c9ae5a8SAjay Gupta 			break;
4945c9ae5a8SAjay Gupta 		usleep_range(500, 600);
4955c9ae5a8SAjay Gupta 	} while (time_is_after_jiffies(target));
4965c9ae5a8SAjay Gupta 
4975c9ae5a8SAjay Gupta 	if (time_is_before_jiffies(target)) {
4985c9ae5a8SAjay Gupta 		dev_err(dev, "response timeout error\n");
4995c9ae5a8SAjay Gupta 		return -ETIME;
5005c9ae5a8SAjay Gupta 	}
5015c9ae5a8SAjay Gupta 
5025c9ae5a8SAjay Gupta 	status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp,
5035c9ae5a8SAjay Gupta 			  sizeof(uc->dev_resp));
5045c9ae5a8SAjay Gupta 	if (status < 0)
5055c9ae5a8SAjay Gupta 		return status;
5065c9ae5a8SAjay Gupta 
5075c9ae5a8SAjay Gupta 	status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval));
5085c9ae5a8SAjay Gupta 	if (status < 0)
5095c9ae5a8SAjay Gupta 		return status;
5105c9ae5a8SAjay Gupta 
5115c9ae5a8SAjay Gupta 	return 0;
5125c9ae5a8SAjay Gupta }
5135c9ae5a8SAjay Gupta 
5145c9ae5a8SAjay Gupta /* Caller must hold uc->lock */
5155c9ae5a8SAjay Gupta static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd)
5165c9ae5a8SAjay Gupta {
5175c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
5185c9ae5a8SAjay Gupta 	int ret;
5195c9ae5a8SAjay Gupta 
5205c9ae5a8SAjay Gupta 	switch (cmd->reg & 0xF000) {
5215c9ae5a8SAjay Gupta 	case DEV_REG_IDX:
5225c9ae5a8SAjay Gupta 		set_bit(DEV_CMD_PENDING, &uc->flags);
5235c9ae5a8SAjay Gupta 		break;
5245c9ae5a8SAjay Gupta 	default:
5255c9ae5a8SAjay Gupta 		dev_err(dev, "invalid cmd register\n");
5265c9ae5a8SAjay Gupta 		break;
5275c9ae5a8SAjay Gupta 	}
5285c9ae5a8SAjay Gupta 
5295c9ae5a8SAjay Gupta 	ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len);
5305c9ae5a8SAjay Gupta 	if (ret < 0)
5315c9ae5a8SAjay Gupta 		return ret;
5325c9ae5a8SAjay Gupta 
5335c9ae5a8SAjay Gupta 	msleep(cmd->delay);
5345c9ae5a8SAjay Gupta 
5355c9ae5a8SAjay Gupta 	ret = ccg_read_response(uc);
5365c9ae5a8SAjay Gupta 	if (ret < 0) {
5375c9ae5a8SAjay Gupta 		dev_err(dev, "response read error\n");
5385c9ae5a8SAjay Gupta 		switch (cmd->reg & 0xF000) {
5395c9ae5a8SAjay Gupta 		case DEV_REG_IDX:
5405c9ae5a8SAjay Gupta 			clear_bit(DEV_CMD_PENDING, &uc->flags);
5415c9ae5a8SAjay Gupta 			break;
5425c9ae5a8SAjay Gupta 		default:
5435c9ae5a8SAjay Gupta 			dev_err(dev, "invalid cmd register\n");
5445c9ae5a8SAjay Gupta 			break;
5455c9ae5a8SAjay Gupta 		}
5465c9ae5a8SAjay Gupta 		return -EIO;
5475c9ae5a8SAjay Gupta 	}
5485c9ae5a8SAjay Gupta 	ccg_process_response(uc);
5495c9ae5a8SAjay Gupta 
5505c9ae5a8SAjay Gupta 	return uc->cmd_resp;
5515c9ae5a8SAjay Gupta }
5525c9ae5a8SAjay Gupta 
5535c9ae5a8SAjay Gupta static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc)
5545c9ae5a8SAjay Gupta {
5555c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
5565c9ae5a8SAjay Gupta 	int ret;
5575c9ae5a8SAjay Gupta 
5585c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_ENTER_FLASHING;
5595c9ae5a8SAjay Gupta 	cmd.data = FLASH_ENTER_SIG;
5605c9ae5a8SAjay Gupta 	cmd.len = 1;
5615c9ae5a8SAjay Gupta 	cmd.delay = 50;
5625c9ae5a8SAjay Gupta 
5635c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
5645c9ae5a8SAjay Gupta 
5655c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
5665c9ae5a8SAjay Gupta 
5675c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
5685c9ae5a8SAjay Gupta 
5695c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
5705c9ae5a8SAjay Gupta 		dev_err(uc->dev, "enter flashing failed ret=%d\n", ret);
5715c9ae5a8SAjay Gupta 		return ret;
5725c9ae5a8SAjay Gupta 	}
5735c9ae5a8SAjay Gupta 
5745c9ae5a8SAjay Gupta 	return 0;
5755c9ae5a8SAjay Gupta }
5765c9ae5a8SAjay Gupta 
5775c9ae5a8SAjay Gupta static int ccg_cmd_reset(struct ucsi_ccg *uc)
5785c9ae5a8SAjay Gupta {
5795c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
5805c9ae5a8SAjay Gupta 	u8 *p;
5815c9ae5a8SAjay Gupta 	int ret;
5825c9ae5a8SAjay Gupta 
5835c9ae5a8SAjay Gupta 	p = (u8 *)&cmd.data;
5845c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_RESET_REQ;
5855c9ae5a8SAjay Gupta 	p[0] = RESET_SIG;
5865c9ae5a8SAjay Gupta 	p[1] = CMD_RESET_DEV;
5875c9ae5a8SAjay Gupta 	cmd.len = 2;
5885c9ae5a8SAjay Gupta 	cmd.delay = 5000;
5895c9ae5a8SAjay Gupta 
5905c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
5915c9ae5a8SAjay Gupta 
5925c9ae5a8SAjay Gupta 	set_bit(RESET_PENDING, &uc->flags);
5935c9ae5a8SAjay Gupta 
5945c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
5955c9ae5a8SAjay Gupta 	if (ret != RESET_COMPLETE)
5965c9ae5a8SAjay Gupta 		goto err_clear_flag;
5975c9ae5a8SAjay Gupta 
5985c9ae5a8SAjay Gupta 	ret = 0;
5995c9ae5a8SAjay Gupta 
6005c9ae5a8SAjay Gupta err_clear_flag:
6015c9ae5a8SAjay Gupta 	clear_bit(RESET_PENDING, &uc->flags);
6025c9ae5a8SAjay Gupta 
6035c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
6045c9ae5a8SAjay Gupta 
6055c9ae5a8SAjay Gupta 	return ret;
6065c9ae5a8SAjay Gupta }
6075c9ae5a8SAjay Gupta 
6085c9ae5a8SAjay Gupta static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable)
6095c9ae5a8SAjay Gupta {
6105c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
6115c9ae5a8SAjay Gupta 	int ret;
6125c9ae5a8SAjay Gupta 
6135c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_PDPORT_ENABLE;
6145c9ae5a8SAjay Gupta 	if (enable)
6155c9ae5a8SAjay Gupta 		cmd.data = (uc->port_num == 1) ?
6165c9ae5a8SAjay Gupta 			    PDPORT_1 : (PDPORT_1 | PDPORT_2);
6175c9ae5a8SAjay Gupta 	else
6185c9ae5a8SAjay Gupta 		cmd.data = 0x0;
6195c9ae5a8SAjay Gupta 	cmd.len = 1;
6205c9ae5a8SAjay Gupta 	cmd.delay = 10;
6215c9ae5a8SAjay Gupta 
6225c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
6235c9ae5a8SAjay Gupta 
6245c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
6255c9ae5a8SAjay Gupta 
6265c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
6275c9ae5a8SAjay Gupta 
6285c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
6295c9ae5a8SAjay Gupta 		dev_err(uc->dev, "port control failed ret=%d\n", ret);
6305c9ae5a8SAjay Gupta 		return ret;
6315c9ae5a8SAjay Gupta 	}
6325c9ae5a8SAjay Gupta 	return 0;
6335c9ae5a8SAjay Gupta }
6345c9ae5a8SAjay Gupta 
6355c9ae5a8SAjay Gupta static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode)
6365c9ae5a8SAjay Gupta {
6375c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
6385c9ae5a8SAjay Gupta 	int ret;
6395c9ae5a8SAjay Gupta 
6405c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_JUMP_TO_BOOT;
6415c9ae5a8SAjay Gupta 
6425c9ae5a8SAjay Gupta 	if (bl_mode)
6435c9ae5a8SAjay Gupta 		cmd.data = TO_BOOT;
6445c9ae5a8SAjay Gupta 	else
6455c9ae5a8SAjay Gupta 		cmd.data = TO_ALT_FW;
6465c9ae5a8SAjay Gupta 
6475c9ae5a8SAjay Gupta 	cmd.len = 1;
6485c9ae5a8SAjay Gupta 	cmd.delay = 100;
6495c9ae5a8SAjay Gupta 
6505c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
6515c9ae5a8SAjay Gupta 
6525c9ae5a8SAjay Gupta 	set_bit(RESET_PENDING, &uc->flags);
6535c9ae5a8SAjay Gupta 
6545c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
6555c9ae5a8SAjay Gupta 	if (ret != RESET_COMPLETE)
6565c9ae5a8SAjay Gupta 		goto err_clear_flag;
6575c9ae5a8SAjay Gupta 
6585c9ae5a8SAjay Gupta 	ret = 0;
6595c9ae5a8SAjay Gupta 
6605c9ae5a8SAjay Gupta err_clear_flag:
6615c9ae5a8SAjay Gupta 	clear_bit(RESET_PENDING, &uc->flags);
6625c9ae5a8SAjay Gupta 
6635c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
6645c9ae5a8SAjay Gupta 
6655c9ae5a8SAjay Gupta 	return ret;
6665c9ae5a8SAjay Gupta }
6675c9ae5a8SAjay Gupta 
6685c9ae5a8SAjay Gupta static int
6695c9ae5a8SAjay Gupta ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row,
6705c9ae5a8SAjay Gupta 			const void *data, u8 fcmd)
6715c9ae5a8SAjay Gupta {
6725c9ae5a8SAjay Gupta 	struct i2c_client *client = uc->client;
6735c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
6745c9ae5a8SAjay Gupta 	u8 buf[CCG4_ROW_SIZE + 2];
6755c9ae5a8SAjay Gupta 	u8 *p;
6765c9ae5a8SAjay Gupta 	int ret;
6775c9ae5a8SAjay Gupta 
6785c9ae5a8SAjay Gupta 	/* Copy the data into the flash read/write memory. */
6795c9ae5a8SAjay Gupta 	put_unaligned_le16(REG_FLASH_RW_MEM, buf);
6805c9ae5a8SAjay Gupta 
6815c9ae5a8SAjay Gupta 	memcpy(buf + 2, data, CCG4_ROW_SIZE);
6825c9ae5a8SAjay Gupta 
6835c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
6845c9ae5a8SAjay Gupta 
6855c9ae5a8SAjay Gupta 	ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2);
6865c9ae5a8SAjay Gupta 	if (ret != CCG4_ROW_SIZE + 2) {
6875c9ae5a8SAjay Gupta 		dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret);
688c2d18126SWei Yongjun 		mutex_unlock(&uc->lock);
6895c9ae5a8SAjay Gupta 		return ret < 0 ? ret : -EIO;
6905c9ae5a8SAjay Gupta 	}
6915c9ae5a8SAjay Gupta 
6925c9ae5a8SAjay Gupta 	/* Use the FLASH_ROW_READ_WRITE register to trigger */
6935c9ae5a8SAjay Gupta 	/* writing of data to the desired flash row */
6945c9ae5a8SAjay Gupta 	p = (u8 *)&cmd.data;
6955c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_FLASH_ROW_RW;
6965c9ae5a8SAjay Gupta 	p[0] = FLASH_SIG;
6975c9ae5a8SAjay Gupta 	p[1] = fcmd;
6985c9ae5a8SAjay Gupta 	put_unaligned_le16(row, &p[2]);
6995c9ae5a8SAjay Gupta 	cmd.len = 4;
7005c9ae5a8SAjay Gupta 	cmd.delay = 50;
7015c9ae5a8SAjay Gupta 	if (fcmd == FLASH_FWCT_SIG_WR_CMD)
7025c9ae5a8SAjay Gupta 		cmd.delay += 400;
7035c9ae5a8SAjay Gupta 	if (row == 510)
7045c9ae5a8SAjay Gupta 		cmd.delay += 220;
7055c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
7065c9ae5a8SAjay Gupta 
7075c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
7085c9ae5a8SAjay Gupta 
7095c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
7105c9ae5a8SAjay Gupta 		dev_err(uc->dev, "write flash row failed ret=%d\n", ret);
7115c9ae5a8SAjay Gupta 		return ret;
7125c9ae5a8SAjay Gupta 	}
7135c9ae5a8SAjay Gupta 
7145c9ae5a8SAjay Gupta 	return 0;
7155c9ae5a8SAjay Gupta }
7165c9ae5a8SAjay Gupta 
7175c9ae5a8SAjay Gupta static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
7185c9ae5a8SAjay Gupta {
7195c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
7205c9ae5a8SAjay Gupta 	int ret;
7215c9ae5a8SAjay Gupta 
7225c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_VALIDATE_FW;
7235c9ae5a8SAjay Gupta 	cmd.data = fwid;
7245c9ae5a8SAjay Gupta 	cmd.len = 1;
7255c9ae5a8SAjay Gupta 	cmd.delay = 500;
7265c9ae5a8SAjay Gupta 
7275c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
7285c9ae5a8SAjay Gupta 
7295c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
7305c9ae5a8SAjay Gupta 
7315c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
7325c9ae5a8SAjay Gupta 
7335c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS)
7345c9ae5a8SAjay Gupta 		return ret;
7355c9ae5a8SAjay Gupta 
7365c9ae5a8SAjay Gupta 	return 0;
7375c9ae5a8SAjay Gupta }
7385c9ae5a8SAjay Gupta 
7395c9ae5a8SAjay Gupta static bool ccg_check_vendor_version(struct ucsi_ccg *uc,
7405c9ae5a8SAjay Gupta 				     struct version_format *app,
7415c9ae5a8SAjay Gupta 				     struct fw_config_table *fw_cfg)
7425c9ae5a8SAjay Gupta {
7435c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7445c9ae5a8SAjay Gupta 
7455c9ae5a8SAjay Gupta 	/* Check if the fw build is for supported vendors */
7465c9ae5a8SAjay Gupta 	if (le16_to_cpu(app->build) != uc->fw_build) {
7475c9ae5a8SAjay Gupta 		dev_info(dev, "current fw is not from supported vendor\n");
7485c9ae5a8SAjay Gupta 		return false;
7495c9ae5a8SAjay Gupta 	}
7505c9ae5a8SAjay Gupta 
7515c9ae5a8SAjay Gupta 	/* Check if the new fw build is for supported vendors */
7525c9ae5a8SAjay Gupta 	if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) {
7535c9ae5a8SAjay Gupta 		dev_info(dev, "new fw is not from supported vendor\n");
7545c9ae5a8SAjay Gupta 		return false;
7555c9ae5a8SAjay Gupta 	}
7565c9ae5a8SAjay Gupta 	return true;
7575c9ae5a8SAjay Gupta }
7585c9ae5a8SAjay Gupta 
7595c9ae5a8SAjay Gupta static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
7605c9ae5a8SAjay Gupta 				 struct version_format *app)
7615c9ae5a8SAjay Gupta {
7625c9ae5a8SAjay Gupta 	const struct firmware *fw = NULL;
7635c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7645c9ae5a8SAjay Gupta 	struct fw_config_table fw_cfg;
7655c9ae5a8SAjay Gupta 	u32 cur_version, new_version;
7665c9ae5a8SAjay Gupta 	bool is_later = false;
7675c9ae5a8SAjay Gupta 
7685c9ae5a8SAjay Gupta 	if (request_firmware(&fw, fw_name, dev) != 0) {
7695c9ae5a8SAjay Gupta 		dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
7705c9ae5a8SAjay Gupta 		return false;
7715c9ae5a8SAjay Gupta 	}
7725c9ae5a8SAjay Gupta 
7735c9ae5a8SAjay Gupta 	/*
7745c9ae5a8SAjay Gupta 	 * check if signed fw
7755c9ae5a8SAjay Gupta 	 * last part of fw image is fw cfg table and signature
7765c9ae5a8SAjay Gupta 	 */
7775c9ae5a8SAjay Gupta 	if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
7785c9ae5a8SAjay Gupta 		goto out_release_firmware;
7795c9ae5a8SAjay Gupta 
7805c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
7815c9ae5a8SAjay Gupta 	       sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));
7825c9ae5a8SAjay Gupta 
7835c9ae5a8SAjay Gupta 	if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) {
7845c9ae5a8SAjay Gupta 		dev_info(dev, "not a signed image\n");
7855c9ae5a8SAjay Gupta 		goto out_release_firmware;
7865c9ae5a8SAjay Gupta 	}
7875c9ae5a8SAjay Gupta 
7885c9ae5a8SAjay Gupta 	/* compare input version with FWCT version */
789f0e4cd94SAjay Gupta 	cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) |
790f0e4cd94SAjay Gupta 			CCG_VERSION(app->ver);
7915c9ae5a8SAjay Gupta 
792f0e4cd94SAjay Gupta 	new_version = le16_to_cpu(fw_cfg.app.build) |
793f0e4cd94SAjay Gupta 			CCG_VERSION_PATCH(fw_cfg.app.patch) |
794f0e4cd94SAjay Gupta 			CCG_VERSION(fw_cfg.app.ver);
7955c9ae5a8SAjay Gupta 
7965c9ae5a8SAjay Gupta 	if (!ccg_check_vendor_version(uc, app, &fw_cfg))
7975c9ae5a8SAjay Gupta 		goto out_release_firmware;
7985c9ae5a8SAjay Gupta 
7995c9ae5a8SAjay Gupta 	if (new_version > cur_version)
8005c9ae5a8SAjay Gupta 		is_later = true;
8015c9ae5a8SAjay Gupta 
8025c9ae5a8SAjay Gupta out_release_firmware:
8035c9ae5a8SAjay Gupta 	release_firmware(fw);
8045c9ae5a8SAjay Gupta 	return is_later;
8055c9ae5a8SAjay Gupta }
8065c9ae5a8SAjay Gupta 
8075c9ae5a8SAjay Gupta static int ccg_fw_update_needed(struct ucsi_ccg *uc,
8085c9ae5a8SAjay Gupta 				enum enum_flash_mode *mode)
8095c9ae5a8SAjay Gupta {
8105c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
8115c9ae5a8SAjay Gupta 	int err;
8125c9ae5a8SAjay Gupta 	struct version_info version[3];
8135c9ae5a8SAjay Gupta 
8145c9ae5a8SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
8155c9ae5a8SAjay Gupta 		       sizeof(uc->info));
8165c9ae5a8SAjay Gupta 	if (err) {
8175c9ae5a8SAjay Gupta 		dev_err(dev, "read device mode failed\n");
8185c9ae5a8SAjay Gupta 		return err;
8195c9ae5a8SAjay Gupta 	}
8205c9ae5a8SAjay Gupta 
8215c9ae5a8SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
8225c9ae5a8SAjay Gupta 		       sizeof(version));
8235c9ae5a8SAjay Gupta 	if (err) {
8245c9ae5a8SAjay Gupta 		dev_err(dev, "read device mode failed\n");
8255c9ae5a8SAjay Gupta 		return err;
8265c9ae5a8SAjay Gupta 	}
8275c9ae5a8SAjay Gupta 
8285c9ae5a8SAjay Gupta 	if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
8295c9ae5a8SAjay Gupta 		   sizeof(struct version_info)) == 0) {
8305c9ae5a8SAjay Gupta 		dev_info(dev, "secondary fw is not flashed\n");
8315c9ae5a8SAjay Gupta 		*mode = SECONDARY_BL;
8325c9ae5a8SAjay Gupta 	} else if (le16_to_cpu(version[FW1].base.build) <
8335c9ae5a8SAjay Gupta 		secondary_fw_min_ver) {
8345c9ae5a8SAjay Gupta 		dev_info(dev, "secondary fw version is too low (< %d)\n",
8355c9ae5a8SAjay Gupta 			 secondary_fw_min_ver);
8365c9ae5a8SAjay Gupta 		*mode = SECONDARY;
8375c9ae5a8SAjay Gupta 	} else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
8385c9ae5a8SAjay Gupta 		   sizeof(struct version_info)) == 0) {
8395c9ae5a8SAjay Gupta 		dev_info(dev, "primary fw is not flashed\n");
8405c9ae5a8SAjay Gupta 		*mode = PRIMARY;
8415c9ae5a8SAjay Gupta 	} else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
8425c9ae5a8SAjay Gupta 		   &version[FW2].app)) {
8435c9ae5a8SAjay Gupta 		dev_info(dev, "found primary fw with later version\n");
8445c9ae5a8SAjay Gupta 		*mode = PRIMARY;
8455c9ae5a8SAjay Gupta 	} else {
8465c9ae5a8SAjay Gupta 		dev_info(dev, "secondary and primary fw are the latest\n");
8475c9ae5a8SAjay Gupta 		*mode = FLASH_NOT_NEEDED;
8485c9ae5a8SAjay Gupta 	}
8495c9ae5a8SAjay Gupta 	return 0;
8505c9ae5a8SAjay Gupta }
8515c9ae5a8SAjay Gupta 
8525c9ae5a8SAjay Gupta static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
8535c9ae5a8SAjay Gupta {
8545c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
8555c9ae5a8SAjay Gupta 	const struct firmware *fw = NULL;
8565c9ae5a8SAjay Gupta 	const char *p, *s;
8575c9ae5a8SAjay Gupta 	const char *eof;
8585c9ae5a8SAjay Gupta 	int err, row, len, line_sz, line_cnt = 0;
8595c9ae5a8SAjay Gupta 	unsigned long start_time = jiffies;
8605c9ae5a8SAjay Gupta 	struct fw_config_table  fw_cfg;
8615c9ae5a8SAjay Gupta 	u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE];
8625c9ae5a8SAjay Gupta 	u8 *wr_buf;
8635c9ae5a8SAjay Gupta 
8645c9ae5a8SAjay Gupta 	err = request_firmware(&fw, ccg_fw_names[mode], dev);
8655c9ae5a8SAjay Gupta 	if (err) {
8665c9ae5a8SAjay Gupta 		dev_err(dev, "request %s failed err=%d\n",
8675c9ae5a8SAjay Gupta 			ccg_fw_names[mode], err);
8685c9ae5a8SAjay Gupta 		return err;
8695c9ae5a8SAjay Gupta 	}
8705c9ae5a8SAjay Gupta 
8715c9ae5a8SAjay Gupta 	if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >>
8725c9ae5a8SAjay Gupta 			CCG_DEVINFO_FWMODE_SHIFT) == FW2) {
8735c9ae5a8SAjay Gupta 		err = ccg_cmd_port_control(uc, false);
8745c9ae5a8SAjay Gupta 		if (err < 0)
8755c9ae5a8SAjay Gupta 			goto release_fw;
8765c9ae5a8SAjay Gupta 		err = ccg_cmd_jump_boot_mode(uc, 0);
8775c9ae5a8SAjay Gupta 		if (err < 0)
8785c9ae5a8SAjay Gupta 			goto release_fw;
8795c9ae5a8SAjay Gupta 	}
8805c9ae5a8SAjay Gupta 
8815c9ae5a8SAjay Gupta 	eof = fw->data + fw->size;
8825c9ae5a8SAjay Gupta 
8835c9ae5a8SAjay Gupta 	/*
8845c9ae5a8SAjay Gupta 	 * check if signed fw
8855c9ae5a8SAjay Gupta 	 * last part of fw image is fw cfg table and signature
8865c9ae5a8SAjay Gupta 	 */
8875c9ae5a8SAjay Gupta 	if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig))
8885c9ae5a8SAjay Gupta 		goto not_signed_fw;
8895c9ae5a8SAjay Gupta 
8905c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
8915c9ae5a8SAjay Gupta 	       sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg));
8925c9ae5a8SAjay Gupta 
8935c9ae5a8SAjay Gupta 	if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
8945c9ae5a8SAjay Gupta 		dev_info(dev, "not a signed image\n");
8955c9ae5a8SAjay Gupta 		goto not_signed_fw;
8965c9ae5a8SAjay Gupta 	}
8975c9ae5a8SAjay Gupta 	eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig);
8985c9ae5a8SAjay Gupta 
8995c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg_sig,
9005c9ae5a8SAjay Gupta 	       fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig));
9015c9ae5a8SAjay Gupta 
9025c9ae5a8SAjay Gupta 	/* flash fw config table and signature first */
9035c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg,
9045c9ae5a8SAjay Gupta 				      FLASH_FWCT1_WR_CMD);
9055c9ae5a8SAjay Gupta 	if (err)
9065c9ae5a8SAjay Gupta 		goto release_fw;
9075c9ae5a8SAjay Gupta 
9085c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE,
9095c9ae5a8SAjay Gupta 				      FLASH_FWCT2_WR_CMD);
9105c9ae5a8SAjay Gupta 	if (err)
9115c9ae5a8SAjay Gupta 		goto release_fw;
9125c9ae5a8SAjay Gupta 
9135c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig,
9145c9ae5a8SAjay Gupta 				      FLASH_FWCT_SIG_WR_CMD);
9155c9ae5a8SAjay Gupta 	if (err)
9165c9ae5a8SAjay Gupta 		goto release_fw;
9175c9ae5a8SAjay Gupta 
9185c9ae5a8SAjay Gupta not_signed_fw:
9195c9ae5a8SAjay Gupta 	wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL);
9205c9ae5a8SAjay Gupta 	if (!wr_buf)
9215c9ae5a8SAjay Gupta 		return -ENOMEM;
9225c9ae5a8SAjay Gupta 
9235c9ae5a8SAjay Gupta 	err = ccg_cmd_enter_flashing(uc);
9245c9ae5a8SAjay Gupta 	if (err)
9255c9ae5a8SAjay Gupta 		goto release_mem;
9265c9ae5a8SAjay Gupta 
9275c9ae5a8SAjay Gupta 	/*****************************************************************
9285c9ae5a8SAjay Gupta 	 * CCG firmware image (.cyacd) file line format
9295c9ae5a8SAjay Gupta 	 *
9305c9ae5a8SAjay Gupta 	 * :00rrrrllll[dd....]cc/r/n
9315c9ae5a8SAjay Gupta 	 *
9325c9ae5a8SAjay Gupta 	 * :00   header
9335c9ae5a8SAjay Gupta 	 * rrrr is row number to flash				(4 char)
9345c9ae5a8SAjay Gupta 	 * llll is data len to flash				(4 char)
9355c9ae5a8SAjay Gupta 	 * dd   is a data field represents one byte of data	(512 char)
9365c9ae5a8SAjay Gupta 	 * cc   is checksum					(2 char)
9375c9ae5a8SAjay Gupta 	 * \r\n newline
9385c9ae5a8SAjay Gupta 	 *
9395c9ae5a8SAjay Gupta 	 * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527
9405c9ae5a8SAjay Gupta 	 *
9415c9ae5a8SAjay Gupta 	 *****************************************************************/
9425c9ae5a8SAjay Gupta 
9435c9ae5a8SAjay Gupta 	p = strnchr(fw->data, fw->size, ':');
9445c9ae5a8SAjay Gupta 	while (p < eof) {
9455c9ae5a8SAjay Gupta 		s = strnchr(p + 1, eof - p - 1, ':');
9465c9ae5a8SAjay Gupta 
9475c9ae5a8SAjay Gupta 		if (!s)
9485c9ae5a8SAjay Gupta 			s = eof;
9495c9ae5a8SAjay Gupta 
9505c9ae5a8SAjay Gupta 		line_sz = s - p;
9515c9ae5a8SAjay Gupta 
9525c9ae5a8SAjay Gupta 		if (line_sz != CYACD_LINE_SIZE) {
9535c9ae5a8SAjay Gupta 			dev_err(dev, "Bad FW format line_sz=%d\n", line_sz);
9545c9ae5a8SAjay Gupta 			err =  -EINVAL;
9555c9ae5a8SAjay Gupta 			goto release_mem;
9565c9ae5a8SAjay Gupta 		}
9575c9ae5a8SAjay Gupta 
9585c9ae5a8SAjay Gupta 		if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) {
9595c9ae5a8SAjay Gupta 			err =  -EINVAL;
9605c9ae5a8SAjay Gupta 			goto release_mem;
9615c9ae5a8SAjay Gupta 		}
9625c9ae5a8SAjay Gupta 
9635c9ae5a8SAjay Gupta 		row = get_unaligned_be16(wr_buf);
9645c9ae5a8SAjay Gupta 		len = get_unaligned_be16(&wr_buf[2]);
9655c9ae5a8SAjay Gupta 
9665c9ae5a8SAjay Gupta 		if (len != CCG4_ROW_SIZE) {
9675c9ae5a8SAjay Gupta 			err =  -EINVAL;
9685c9ae5a8SAjay Gupta 			goto release_mem;
9695c9ae5a8SAjay Gupta 		}
9705c9ae5a8SAjay Gupta 
9715c9ae5a8SAjay Gupta 		err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4,
9725c9ae5a8SAjay Gupta 					      FLASH_WR_CMD);
9735c9ae5a8SAjay Gupta 		if (err)
9745c9ae5a8SAjay Gupta 			goto release_mem;
9755c9ae5a8SAjay Gupta 
9765c9ae5a8SAjay Gupta 		line_cnt++;
9775c9ae5a8SAjay Gupta 		p = s;
9785c9ae5a8SAjay Gupta 	}
9795c9ae5a8SAjay Gupta 
9805c9ae5a8SAjay Gupta 	dev_info(dev, "total %d row flashed. time: %dms\n",
9815c9ae5a8SAjay Gupta 		 line_cnt, jiffies_to_msecs(jiffies - start_time));
9825c9ae5a8SAjay Gupta 
9835c9ae5a8SAjay Gupta 	err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 :  FW1);
9845c9ae5a8SAjay Gupta 	if (err)
9855c9ae5a8SAjay Gupta 		dev_err(dev, "%s validation failed err=%d\n",
9865c9ae5a8SAjay Gupta 			(mode == PRIMARY) ? "FW2" :  "FW1", err);
9875c9ae5a8SAjay Gupta 	else
9885c9ae5a8SAjay Gupta 		dev_info(dev, "%s validated\n",
9895c9ae5a8SAjay Gupta 			 (mode == PRIMARY) ? "FW2" :  "FW1");
9905c9ae5a8SAjay Gupta 
9915c9ae5a8SAjay Gupta 	err = ccg_cmd_port_control(uc, false);
9925c9ae5a8SAjay Gupta 	if (err < 0)
9935c9ae5a8SAjay Gupta 		goto release_mem;
9945c9ae5a8SAjay Gupta 
9955c9ae5a8SAjay Gupta 	err = ccg_cmd_reset(uc);
9965c9ae5a8SAjay Gupta 	if (err < 0)
9975c9ae5a8SAjay Gupta 		goto release_mem;
9985c9ae5a8SAjay Gupta 
9995c9ae5a8SAjay Gupta 	err = ccg_cmd_port_control(uc, true);
10005c9ae5a8SAjay Gupta 	if (err < 0)
10015c9ae5a8SAjay Gupta 		goto release_mem;
10025c9ae5a8SAjay Gupta 
10035c9ae5a8SAjay Gupta release_mem:
10045c9ae5a8SAjay Gupta 	kfree(wr_buf);
10055c9ae5a8SAjay Gupta 
10065c9ae5a8SAjay Gupta release_fw:
10075c9ae5a8SAjay Gupta 	release_firmware(fw);
10085c9ae5a8SAjay Gupta 	return err;
10095c9ae5a8SAjay Gupta }
10105c9ae5a8SAjay Gupta 
10115c9ae5a8SAjay Gupta /*******************************************************************************
10125c9ae5a8SAjay Gupta  * CCG4 has two copies of the firmware in addition to the bootloader.
10135c9ae5a8SAjay Gupta  * If the device is running FW1, FW2 can be updated with the new version.
10145c9ae5a8SAjay Gupta  * Dual firmware mode allows the CCG device to stay in a PD contract and support
10155c9ae5a8SAjay Gupta  * USB PD and Type-C functionality while a firmware update is in progress.
10165c9ae5a8SAjay Gupta  ******************************************************************************/
10175c9ae5a8SAjay Gupta static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode)
10185c9ae5a8SAjay Gupta {
10195c9ae5a8SAjay Gupta 	int err;
10205c9ae5a8SAjay Gupta 
10215c9ae5a8SAjay Gupta 	while (flash_mode != FLASH_NOT_NEEDED) {
10225c9ae5a8SAjay Gupta 		err = do_flash(uc, flash_mode);
10235c9ae5a8SAjay Gupta 		if (err < 0)
10245c9ae5a8SAjay Gupta 			return err;
10255c9ae5a8SAjay Gupta 		err = ccg_fw_update_needed(uc, &flash_mode);
10265c9ae5a8SAjay Gupta 		if (err < 0)
10275c9ae5a8SAjay Gupta 			return err;
10285c9ae5a8SAjay Gupta 	}
10295c9ae5a8SAjay Gupta 	dev_info(uc->dev, "CCG FW update successful\n");
10305c9ae5a8SAjay Gupta 
10315c9ae5a8SAjay Gupta 	return err;
10325c9ae5a8SAjay Gupta }
10335c9ae5a8SAjay Gupta 
10345c9ae5a8SAjay Gupta static int ccg_restart(struct ucsi_ccg *uc)
10355c9ae5a8SAjay Gupta {
10365c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
10375c9ae5a8SAjay Gupta 	int status;
10385c9ae5a8SAjay Gupta 
10395c9ae5a8SAjay Gupta 	status = ucsi_ccg_init(uc);
10405c9ae5a8SAjay Gupta 	if (status < 0) {
10415c9ae5a8SAjay Gupta 		dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status);
10425c9ae5a8SAjay Gupta 		return status;
10435c9ae5a8SAjay Gupta 	}
10445c9ae5a8SAjay Gupta 
10455c9ae5a8SAjay Gupta 	status = request_threaded_irq(uc->irq, NULL, ccg_irq_handler,
10465c9ae5a8SAjay Gupta 				      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
10475c9ae5a8SAjay Gupta 				      dev_name(dev), uc);
10485c9ae5a8SAjay Gupta 	if (status < 0) {
10495c9ae5a8SAjay Gupta 		dev_err(dev, "request_threaded_irq failed - %d\n", status);
10505c9ae5a8SAjay Gupta 		return status;
10515c9ae5a8SAjay Gupta 	}
10525c9ae5a8SAjay Gupta 
10535c9ae5a8SAjay Gupta 	uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
10545c9ae5a8SAjay Gupta 	if (IS_ERR(uc->ucsi)) {
10555c9ae5a8SAjay Gupta 		dev_err(uc->dev, "ucsi_register_ppm failed\n");
10565c9ae5a8SAjay Gupta 		return PTR_ERR(uc->ucsi);
10575c9ae5a8SAjay Gupta 	}
10585c9ae5a8SAjay Gupta 
10595c9ae5a8SAjay Gupta 	return 0;
10605c9ae5a8SAjay Gupta }
10615c9ae5a8SAjay Gupta 
10625c9ae5a8SAjay Gupta static void ccg_update_firmware(struct work_struct *work)
10635c9ae5a8SAjay Gupta {
10645c9ae5a8SAjay Gupta 	struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work);
10655c9ae5a8SAjay Gupta 	enum enum_flash_mode flash_mode;
10665c9ae5a8SAjay Gupta 	int status;
10675c9ae5a8SAjay Gupta 
10685c9ae5a8SAjay Gupta 	status = ccg_fw_update_needed(uc, &flash_mode);
10695c9ae5a8SAjay Gupta 	if (status < 0)
10705c9ae5a8SAjay Gupta 		return;
10715c9ae5a8SAjay Gupta 
10725c9ae5a8SAjay Gupta 	if (flash_mode != FLASH_NOT_NEEDED) {
10735c9ae5a8SAjay Gupta 		ucsi_unregister_ppm(uc->ucsi);
10745c9ae5a8SAjay Gupta 		free_irq(uc->irq, uc);
10755c9ae5a8SAjay Gupta 
10765c9ae5a8SAjay Gupta 		ccg_fw_update(uc, flash_mode);
10775c9ae5a8SAjay Gupta 		ccg_restart(uc);
10785c9ae5a8SAjay Gupta 	}
10795c9ae5a8SAjay Gupta }
10805c9ae5a8SAjay Gupta 
10815c9ae5a8SAjay Gupta static ssize_t do_flash_store(struct device *dev,
10825c9ae5a8SAjay Gupta 			      struct device_attribute *attr,
10835c9ae5a8SAjay Gupta 			      const char *buf, size_t n)
10845c9ae5a8SAjay Gupta {
10855c9ae5a8SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
10865c9ae5a8SAjay Gupta 	bool flash;
10875c9ae5a8SAjay Gupta 
10885c9ae5a8SAjay Gupta 	if (kstrtobool(buf, &flash))
10895c9ae5a8SAjay Gupta 		return -EINVAL;
10905c9ae5a8SAjay Gupta 
10915c9ae5a8SAjay Gupta 	if (!flash)
10925c9ae5a8SAjay Gupta 		return n;
10935c9ae5a8SAjay Gupta 
10945c9ae5a8SAjay Gupta 	if (uc->fw_build == 0x0) {
10955c9ae5a8SAjay Gupta 		dev_err(dev, "fail to flash FW due to missing FW build info\n");
10965c9ae5a8SAjay Gupta 		return -EINVAL;
10975c9ae5a8SAjay Gupta 	}
10985c9ae5a8SAjay Gupta 
10995c9ae5a8SAjay Gupta 	schedule_work(&uc->work);
11005c9ae5a8SAjay Gupta 	return n;
11015c9ae5a8SAjay Gupta }
11025c9ae5a8SAjay Gupta 
11035c9ae5a8SAjay Gupta static DEVICE_ATTR_WO(do_flash);
11045c9ae5a8SAjay Gupta 
11055c9ae5a8SAjay Gupta static struct attribute *ucsi_ccg_sysfs_attrs[] = {
11065c9ae5a8SAjay Gupta 	&dev_attr_do_flash.attr,
11075c9ae5a8SAjay Gupta 	NULL,
11085c9ae5a8SAjay Gupta };
11095c9ae5a8SAjay Gupta 
11105c9ae5a8SAjay Gupta static struct attribute_group ucsi_ccg_attr_group = {
11115c9ae5a8SAjay Gupta 	.attrs = ucsi_ccg_sysfs_attrs,
11125c9ae5a8SAjay Gupta };
11135c9ae5a8SAjay Gupta 
1114247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client,
1115247c554aSAjay Gupta 			  const struct i2c_device_id *id)
1116247c554aSAjay Gupta {
1117247c554aSAjay Gupta 	struct device *dev = &client->dev;
1118247c554aSAjay Gupta 	struct ucsi_ccg *uc;
1119247c554aSAjay Gupta 	int status;
1120247c554aSAjay Gupta 	u16 rab;
1121247c554aSAjay Gupta 
1122247c554aSAjay Gupta 	uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
1123247c554aSAjay Gupta 	if (!uc)
1124247c554aSAjay Gupta 		return -ENOMEM;
1125247c554aSAjay Gupta 
1126247c554aSAjay Gupta 	uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
1127247c554aSAjay Gupta 	if (!uc->ppm.data)
1128247c554aSAjay Gupta 		return -ENOMEM;
1129247c554aSAjay Gupta 
1130247c554aSAjay Gupta 	uc->ppm.cmd = ucsi_ccg_cmd;
1131247c554aSAjay Gupta 	uc->ppm.sync = ucsi_ccg_sync;
1132247c554aSAjay Gupta 	uc->dev = dev;
1133247c554aSAjay Gupta 	uc->client = client;
1134f0e4cd94SAjay Gupta 	uc->run_isr = true;
11355c9ae5a8SAjay Gupta 	mutex_init(&uc->lock);
11365c9ae5a8SAjay Gupta 	INIT_WORK(&uc->work, ccg_update_firmware);
1137f0e4cd94SAjay Gupta 	INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
11385c9ae5a8SAjay Gupta 
11395c9ae5a8SAjay Gupta 	/* Only fail FW flashing when FW build information is not provided */
11405c9ae5a8SAjay Gupta 	status = device_property_read_u16(dev, "ccgx,firmware-build",
11415c9ae5a8SAjay Gupta 					  &uc->fw_build);
11425c9ae5a8SAjay Gupta 	if (status)
11435c9ae5a8SAjay Gupta 		dev_err(uc->dev, "failed to get FW build information\n");
1144247c554aSAjay Gupta 
1145247c554aSAjay Gupta 	/* reset ccg device and initialize ucsi */
1146247c554aSAjay Gupta 	status = ucsi_ccg_init(uc);
1147247c554aSAjay Gupta 	if (status < 0) {
1148247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
1149247c554aSAjay Gupta 		return status;
1150247c554aSAjay Gupta 	}
1151247c554aSAjay Gupta 
11525d438e20SAjay Gupta 	status = get_fw_info(uc);
11535d438e20SAjay Gupta 	if (status < 0) {
11545d438e20SAjay Gupta 		dev_err(uc->dev, "get_fw_info failed - %d\n", status);
11555d438e20SAjay Gupta 		return status;
11565d438e20SAjay Gupta 	}
11575d438e20SAjay Gupta 
11585c9ae5a8SAjay Gupta 	uc->port_num = 1;
11595c9ae5a8SAjay Gupta 
11605c9ae5a8SAjay Gupta 	if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
11615c9ae5a8SAjay Gupta 		uc->port_num++;
11625c9ae5a8SAjay Gupta 
11635c9ae5a8SAjay Gupta 	status = request_threaded_irq(client->irq, NULL, ccg_irq_handler,
1164247c554aSAjay Gupta 				      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
1165247c554aSAjay Gupta 				      dev_name(dev), uc);
1166247c554aSAjay Gupta 	if (status < 0) {
1167247c554aSAjay Gupta 		dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
1168247c554aSAjay Gupta 		return status;
1169247c554aSAjay Gupta 	}
1170247c554aSAjay Gupta 
11715c9ae5a8SAjay Gupta 	uc->irq = client->irq;
11725c9ae5a8SAjay Gupta 
1173247c554aSAjay Gupta 	uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
1174247c554aSAjay Gupta 	if (IS_ERR(uc->ucsi)) {
1175247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_register_ppm failed\n");
1176247c554aSAjay Gupta 		return PTR_ERR(uc->ucsi);
1177247c554aSAjay Gupta 	}
1178247c554aSAjay Gupta 
1179247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
1180247c554aSAjay Gupta 	status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
1181247c554aSAjay Gupta 			  offsetof(struct ucsi_data, version),
1182247c554aSAjay Gupta 			  sizeof(uc->ppm.data->version));
1183247c554aSAjay Gupta 	if (status < 0) {
1184247c554aSAjay Gupta 		ucsi_unregister_ppm(uc->ucsi);
1185247c554aSAjay Gupta 		return status;
1186247c554aSAjay Gupta 	}
1187247c554aSAjay Gupta 
1188247c554aSAjay Gupta 	i2c_set_clientdata(client, uc);
11895c9ae5a8SAjay Gupta 
11905c9ae5a8SAjay Gupta 	status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
11915c9ae5a8SAjay Gupta 	if (status)
11925c9ae5a8SAjay Gupta 		dev_err(uc->dev, "cannot create sysfs group: %d\n", status);
11935c9ae5a8SAjay Gupta 
1194a94ecde4SAjay Gupta 	pm_runtime_set_active(uc->dev);
1195a94ecde4SAjay Gupta 	pm_runtime_enable(uc->dev);
1196a94ecde4SAjay Gupta 	pm_runtime_idle(uc->dev);
1197a94ecde4SAjay Gupta 
1198247c554aSAjay Gupta 	return 0;
1199247c554aSAjay Gupta }
1200247c554aSAjay Gupta 
1201247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client)
1202247c554aSAjay Gupta {
1203247c554aSAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1204247c554aSAjay Gupta 
1205f0e4cd94SAjay Gupta 	cancel_work_sync(&uc->pm_work);
12065c9ae5a8SAjay Gupta 	cancel_work_sync(&uc->work);
1207247c554aSAjay Gupta 	ucsi_unregister_ppm(uc->ucsi);
1208a94ecde4SAjay Gupta 	pm_runtime_disable(uc->dev);
12095c9ae5a8SAjay Gupta 	free_irq(uc->irq, uc);
12105c9ae5a8SAjay Gupta 	sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
1211247c554aSAjay Gupta 
1212247c554aSAjay Gupta 	return 0;
1213247c554aSAjay Gupta }
1214247c554aSAjay Gupta 
1215247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = {
1216247c554aSAjay Gupta 	{"ccgx-ucsi", 0},
1217247c554aSAjay Gupta 	{}
1218247c554aSAjay Gupta };
1219247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
1220247c554aSAjay Gupta 
1221a94ecde4SAjay Gupta static int ucsi_ccg_resume(struct device *dev)
1222a94ecde4SAjay Gupta {
1223a94ecde4SAjay Gupta 	struct i2c_client *client = to_i2c_client(dev);
1224a94ecde4SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1225a94ecde4SAjay Gupta 
1226a94ecde4SAjay Gupta 	return ucsi_resume(uc->ucsi);
1227a94ecde4SAjay Gupta }
1228a94ecde4SAjay Gupta 
1229a94ecde4SAjay Gupta static int ucsi_ccg_runtime_suspend(struct device *dev)
1230a94ecde4SAjay Gupta {
1231a94ecde4SAjay Gupta 	return 0;
1232a94ecde4SAjay Gupta }
1233a94ecde4SAjay Gupta 
1234a94ecde4SAjay Gupta static int ucsi_ccg_runtime_resume(struct device *dev)
1235a94ecde4SAjay Gupta {
1236f0e4cd94SAjay Gupta 	struct i2c_client *client = to_i2c_client(dev);
1237f0e4cd94SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1238f0e4cd94SAjay Gupta 	bool schedule = true;
1239f0e4cd94SAjay Gupta 
1240f0e4cd94SAjay Gupta 	/*
1241f0e4cd94SAjay Gupta 	 * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
1242f0e4cd94SAjay Gupta 	 * of missing interrupt when a device is connected for runtime resume.
1243f0e4cd94SAjay Gupta 	 * Schedule a work to call ISR as a workaround.
1244f0e4cd94SAjay Gupta 	 */
1245f0e4cd94SAjay Gupta 	if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
1246f0e4cd94SAjay Gupta 	    uc->fw_version <= CCG_OLD_FW_VERSION) {
1247f0e4cd94SAjay Gupta 		mutex_lock(&uc->lock);
1248f0e4cd94SAjay Gupta 		if (!uc->run_isr) {
1249f0e4cd94SAjay Gupta 			uc->run_isr = true;
1250f0e4cd94SAjay Gupta 			schedule = false;
1251f0e4cd94SAjay Gupta 		}
1252f0e4cd94SAjay Gupta 		mutex_unlock(&uc->lock);
1253f0e4cd94SAjay Gupta 
1254f0e4cd94SAjay Gupta 		if (schedule)
1255f0e4cd94SAjay Gupta 			schedule_work(&uc->pm_work);
1256f0e4cd94SAjay Gupta 	}
1257f0e4cd94SAjay Gupta 
1258a94ecde4SAjay Gupta 	return 0;
1259a94ecde4SAjay Gupta }
1260a94ecde4SAjay Gupta 
1261a94ecde4SAjay Gupta static const struct dev_pm_ops ucsi_ccg_pm = {
1262a94ecde4SAjay Gupta 	.resume = ucsi_ccg_resume,
1263a94ecde4SAjay Gupta 	.runtime_suspend = ucsi_ccg_runtime_suspend,
1264a94ecde4SAjay Gupta 	.runtime_resume = ucsi_ccg_runtime_resume,
1265a94ecde4SAjay Gupta };
1266a94ecde4SAjay Gupta 
1267247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = {
1268247c554aSAjay Gupta 	.driver = {
1269247c554aSAjay Gupta 		.name = "ucsi_ccg",
1270a94ecde4SAjay Gupta 		.pm = &ucsi_ccg_pm,
1271247c554aSAjay Gupta 	},
1272247c554aSAjay Gupta 	.probe = ucsi_ccg_probe,
1273247c554aSAjay Gupta 	.remove = ucsi_ccg_remove,
1274247c554aSAjay Gupta 	.id_table = ucsi_ccg_device_id,
1275247c554aSAjay Gupta };
1276247c554aSAjay Gupta 
1277247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver);
1278247c554aSAjay Gupta 
1279247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
1280247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
1281247c554aSAjay Gupta MODULE_LICENSE("GPL v2");
1282