xref: /openbmc/linux/drivers/usb/typec/ucsi/ucsi_ccg.c (revision 5c9ae5a8)
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>
17247c554aSAjay Gupta 
18247c554aSAjay Gupta #include <asm/unaligned.h>
19247c554aSAjay Gupta #include "ucsi.h"
20247c554aSAjay Gupta 
215d438e20SAjay Gupta enum enum_fw_mode {
225d438e20SAjay Gupta 	BOOT,   /* bootloader */
235d438e20SAjay Gupta 	FW1,    /* FW partition-1 (contains secondary fw) */
245d438e20SAjay Gupta 	FW2,    /* FW partition-2 (contains primary fw) */
255d438e20SAjay Gupta 	FW_INVALID,
265d438e20SAjay Gupta };
275d438e20SAjay Gupta 
285c9ae5a8SAjay Gupta #define CCGX_RAB_DEVICE_MODE			0x0000
295c9ae5a8SAjay Gupta #define CCGX_RAB_INTR_REG			0x0006
305c9ae5a8SAjay Gupta #define  DEV_INT				BIT(0)
315c9ae5a8SAjay Gupta #define  PORT0_INT				BIT(1)
325c9ae5a8SAjay Gupta #define  PORT1_INT				BIT(2)
335c9ae5a8SAjay Gupta #define  UCSI_READ_INT				BIT(7)
345c9ae5a8SAjay Gupta #define CCGX_RAB_JUMP_TO_BOOT			0x0007
355c9ae5a8SAjay Gupta #define  TO_BOOT				'J'
365c9ae5a8SAjay Gupta #define  TO_ALT_FW				'A'
375c9ae5a8SAjay Gupta #define CCGX_RAB_RESET_REQ			0x0008
385c9ae5a8SAjay Gupta #define  RESET_SIG				'R'
395c9ae5a8SAjay Gupta #define  CMD_RESET_I2C				0x0
405c9ae5a8SAjay Gupta #define  CMD_RESET_DEV				0x1
415c9ae5a8SAjay Gupta #define CCGX_RAB_ENTER_FLASHING			0x000A
425c9ae5a8SAjay Gupta #define  FLASH_ENTER_SIG			'P'
435c9ae5a8SAjay Gupta #define CCGX_RAB_VALIDATE_FW			0x000B
445c9ae5a8SAjay Gupta #define CCGX_RAB_FLASH_ROW_RW			0x000C
455c9ae5a8SAjay Gupta #define  FLASH_SIG				'F'
465c9ae5a8SAjay Gupta #define  FLASH_RD_CMD				0x0
475c9ae5a8SAjay Gupta #define  FLASH_WR_CMD				0x1
485c9ae5a8SAjay Gupta #define  FLASH_FWCT1_WR_CMD			0x2
495c9ae5a8SAjay Gupta #define  FLASH_FWCT2_WR_CMD			0x3
505c9ae5a8SAjay Gupta #define  FLASH_FWCT_SIG_WR_CMD			0x4
515c9ae5a8SAjay Gupta #define CCGX_RAB_READ_ALL_VER			0x0010
525c9ae5a8SAjay Gupta #define CCGX_RAB_READ_FW2_VER			0x0020
535c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL			0x0039
545c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START		BIT(0)
555c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP		BIT(1)
565c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset)	(0xf000 | ((offset) & 0xff))
575c9ae5a8SAjay Gupta #define REG_FLASH_RW_MEM        0x0200
585c9ae5a8SAjay Gupta #define DEV_REG_IDX				CCGX_RAB_DEVICE_MODE
595c9ae5a8SAjay Gupta #define CCGX_RAB_PDPORT_ENABLE			0x002C
605c9ae5a8SAjay Gupta #define  PDPORT_1		BIT(0)
615c9ae5a8SAjay Gupta #define  PDPORT_2		BIT(1)
625c9ae5a8SAjay Gupta #define CCGX_RAB_RESPONSE			0x007E
635c9ae5a8SAjay Gupta #define  ASYNC_EVENT				BIT(7)
645c9ae5a8SAjay Gupta 
655c9ae5a8SAjay Gupta /* CCGx events & async msg codes */
665c9ae5a8SAjay Gupta #define RESET_COMPLETE		0x80
675c9ae5a8SAjay Gupta #define EVENT_INDEX		RESET_COMPLETE
685c9ae5a8SAjay Gupta #define PORT_CONNECT_DET	0x84
695c9ae5a8SAjay Gupta #define PORT_DISCONNECT_DET	0x85
705c9ae5a8SAjay Gupta #define ROLE_SWAP_COMPELETE	0x87
715c9ae5a8SAjay Gupta 
725c9ae5a8SAjay Gupta /* ccg firmware */
735c9ae5a8SAjay Gupta #define CYACD_LINE_SIZE         527
745c9ae5a8SAjay Gupta #define CCG4_ROW_SIZE           256
755c9ae5a8SAjay Gupta #define FW1_METADATA_ROW        0x1FF
765c9ae5a8SAjay Gupta #define FW2_METADATA_ROW        0x1FE
775c9ae5a8SAjay Gupta #define FW_CFG_TABLE_SIG_SIZE	256
785c9ae5a8SAjay Gupta 
795c9ae5a8SAjay Gupta static int secondary_fw_min_ver = 41;
805c9ae5a8SAjay Gupta 
815c9ae5a8SAjay Gupta enum enum_flash_mode {
825c9ae5a8SAjay Gupta 	SECONDARY_BL,	/* update secondary using bootloader */
835c9ae5a8SAjay Gupta 	PRIMARY,	/* update primary using secondary */
845c9ae5a8SAjay Gupta 	SECONDARY,	/* update secondary using primary */
855c9ae5a8SAjay Gupta 	FLASH_NOT_NEEDED,	/* update not required */
865c9ae5a8SAjay Gupta 	FLASH_INVALID,
875c9ae5a8SAjay Gupta };
885c9ae5a8SAjay Gupta 
895c9ae5a8SAjay Gupta static const char * const ccg_fw_names[] = {
905c9ae5a8SAjay Gupta 	"ccg_boot.cyacd",
915c9ae5a8SAjay Gupta 	"ccg_primary.cyacd",
925c9ae5a8SAjay Gupta 	"ccg_secondary.cyacd"
935c9ae5a8SAjay Gupta };
945c9ae5a8SAjay Gupta 
955d438e20SAjay Gupta struct ccg_dev_info {
965d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0)
975d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT)
985d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2)
995d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT)
1005d438e20SAjay Gupta 	u8 mode;
1015d438e20SAjay Gupta 	u8 bl_mode;
1025d438e20SAjay Gupta 	__le16 silicon_id;
1035d438e20SAjay Gupta 	__le16 bl_last_row;
1045d438e20SAjay Gupta } __packed;
1055d438e20SAjay Gupta 
1065d438e20SAjay Gupta struct version_format {
1075d438e20SAjay Gupta 	__le16 build;
1085d438e20SAjay Gupta 	u8 patch;
1095d438e20SAjay Gupta 	u8 ver;
1105d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0)
1115d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT)
1125d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4)
1135d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT)
1145d438e20SAjay Gupta } __packed;
1155d438e20SAjay Gupta 
1165d438e20SAjay Gupta struct version_info {
1175d438e20SAjay Gupta 	struct version_format base;
1185d438e20SAjay Gupta 	struct version_format app;
1195d438e20SAjay Gupta };
1205d438e20SAjay Gupta 
1215c9ae5a8SAjay Gupta struct fw_config_table {
1225c9ae5a8SAjay Gupta 	u32 identity;
1235c9ae5a8SAjay Gupta 	u16 table_size;
1245c9ae5a8SAjay Gupta 	u8 fwct_version;
1255c9ae5a8SAjay Gupta 	u8 is_key_change;
1265c9ae5a8SAjay Gupta 	u8 guid[16];
1275c9ae5a8SAjay Gupta 	struct version_format base;
1285c9ae5a8SAjay Gupta 	struct version_format app;
1295c9ae5a8SAjay Gupta 	u8 primary_fw_digest[32];
1305c9ae5a8SAjay Gupta 	u32 key_exp_length;
1315c9ae5a8SAjay Gupta 	u8 key_modulus[256];
1325c9ae5a8SAjay Gupta 	u8 key_exp[4];
1335c9ae5a8SAjay Gupta };
1345c9ae5a8SAjay Gupta 
1355c9ae5a8SAjay Gupta /* CCGx response codes */
1365c9ae5a8SAjay Gupta enum ccg_resp_code {
1375c9ae5a8SAjay Gupta 	CMD_NO_RESP             = 0x00,
1385c9ae5a8SAjay Gupta 	CMD_SUCCESS             = 0x02,
1395c9ae5a8SAjay Gupta 	FLASH_DATA_AVAILABLE    = 0x03,
1405c9ae5a8SAjay Gupta 	CMD_INVALID             = 0x05,
1415c9ae5a8SAjay Gupta 	FLASH_UPDATE_FAIL       = 0x07,
1425c9ae5a8SAjay Gupta 	INVALID_FW              = 0x08,
1435c9ae5a8SAjay Gupta 	INVALID_ARG             = 0x09,
1445c9ae5a8SAjay Gupta 	CMD_NOT_SUPPORT         = 0x0A,
1455c9ae5a8SAjay Gupta 	TRANSACTION_FAIL        = 0x0C,
1465c9ae5a8SAjay Gupta 	PD_CMD_FAIL             = 0x0D,
1475c9ae5a8SAjay Gupta 	UNDEF_ERROR             = 0x0F,
1485c9ae5a8SAjay Gupta 	INVALID_RESP		= 0x10,
1495c9ae5a8SAjay Gupta };
1505c9ae5a8SAjay Gupta 
1515c9ae5a8SAjay Gupta #define CCG_EVENT_MAX	(EVENT_INDEX + 43)
1525c9ae5a8SAjay Gupta 
1535c9ae5a8SAjay Gupta struct ccg_cmd {
1545c9ae5a8SAjay Gupta 	u16 reg;
1555c9ae5a8SAjay Gupta 	u32 data;
1565c9ae5a8SAjay Gupta 	int len;
1575c9ae5a8SAjay Gupta 	u32 delay; /* ms delay for cmd timeout  */
1585c9ae5a8SAjay Gupta };
1595c9ae5a8SAjay Gupta 
1605c9ae5a8SAjay Gupta struct ccg_resp {
1615c9ae5a8SAjay Gupta 	u8 code;
1625c9ae5a8SAjay Gupta 	u8 length;
1635c9ae5a8SAjay Gupta };
1645c9ae5a8SAjay Gupta 
165247c554aSAjay Gupta struct ucsi_ccg {
166247c554aSAjay Gupta 	struct device *dev;
167247c554aSAjay Gupta 	struct ucsi *ucsi;
168247c554aSAjay Gupta 	struct ucsi_ppm ppm;
169247c554aSAjay Gupta 	struct i2c_client *client;
1705d438e20SAjay Gupta 	struct ccg_dev_info info;
1715d438e20SAjay Gupta 	/* version info for boot, primary and secondary */
1725d438e20SAjay Gupta 	struct version_info version[FW2 + 1];
1735c9ae5a8SAjay Gupta 	/* CCG HPI communication flags */
1745c9ae5a8SAjay Gupta 	unsigned long flags;
1755c9ae5a8SAjay Gupta #define RESET_PENDING	0
1765c9ae5a8SAjay Gupta #define DEV_CMD_PENDING	1
1775c9ae5a8SAjay Gupta 	struct ccg_resp dev_resp;
1785c9ae5a8SAjay Gupta 	u8 cmd_resp;
1795c9ae5a8SAjay Gupta 	int port_num;
1805c9ae5a8SAjay Gupta 	int irq;
1815c9ae5a8SAjay Gupta 	struct work_struct work;
1825c9ae5a8SAjay Gupta 	struct mutex lock; /* to sync between user and driver thread */
183247c554aSAjay Gupta 
1845c9ae5a8SAjay Gupta 	/* fw build with vendor information */
1855c9ae5a8SAjay Gupta 	u16 fw_build;
1865c9ae5a8SAjay Gupta };
187247c554aSAjay Gupta 
188247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
189247c554aSAjay Gupta {
190247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
191247c554aSAjay Gupta 	const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
192247c554aSAjay Gupta 	unsigned char buf[2];
193247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
194247c554aSAjay Gupta 		{
195247c554aSAjay Gupta 			.addr	= client->addr,
196247c554aSAjay Gupta 			.flags  = 0x0,
197247c554aSAjay Gupta 			.len	= sizeof(buf),
198247c554aSAjay Gupta 			.buf	= buf,
199247c554aSAjay Gupta 		},
200247c554aSAjay Gupta 		{
201247c554aSAjay Gupta 			.addr	= client->addr,
202247c554aSAjay Gupta 			.flags  = I2C_M_RD,
203247c554aSAjay Gupta 			.buf	= data,
204247c554aSAjay Gupta 		},
205247c554aSAjay Gupta 	};
206247c554aSAjay Gupta 	u32 rlen, rem_len = len, max_read_len = len;
207247c554aSAjay Gupta 	int status;
208247c554aSAjay Gupta 
209247c554aSAjay Gupta 	/* check any max_read_len limitation on i2c adapter */
210247c554aSAjay Gupta 	if (quirks && quirks->max_read_len)
211247c554aSAjay Gupta 		max_read_len = quirks->max_read_len;
212247c554aSAjay Gupta 
213247c554aSAjay Gupta 	while (rem_len > 0) {
214247c554aSAjay Gupta 		msgs[1].buf = &data[len - rem_len];
215247c554aSAjay Gupta 		rlen = min_t(u16, rem_len, max_read_len);
216247c554aSAjay Gupta 		msgs[1].len = rlen;
217247c554aSAjay Gupta 		put_unaligned_le16(rab, buf);
218247c554aSAjay Gupta 		status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
219247c554aSAjay Gupta 		if (status < 0) {
220247c554aSAjay Gupta 			dev_err(uc->dev, "i2c_transfer failed %d\n", status);
221247c554aSAjay Gupta 			return status;
222247c554aSAjay Gupta 		}
223247c554aSAjay Gupta 		rab += rlen;
224247c554aSAjay Gupta 		rem_len -= rlen;
225247c554aSAjay Gupta 	}
226247c554aSAjay Gupta 
227247c554aSAjay Gupta 	return 0;
228247c554aSAjay Gupta }
229247c554aSAjay Gupta 
230247c554aSAjay Gupta static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
231247c554aSAjay Gupta {
232247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
233247c554aSAjay Gupta 	unsigned char *buf;
234247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
235247c554aSAjay Gupta 		{
236247c554aSAjay Gupta 			.addr	= client->addr,
237247c554aSAjay Gupta 			.flags  = 0x0,
238247c554aSAjay Gupta 		}
239247c554aSAjay Gupta 	};
240247c554aSAjay Gupta 	int status;
241247c554aSAjay Gupta 
242247c554aSAjay Gupta 	buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
243247c554aSAjay Gupta 	if (!buf)
244247c554aSAjay Gupta 		return -ENOMEM;
245247c554aSAjay Gupta 
246247c554aSAjay Gupta 	put_unaligned_le16(rab, buf);
247247c554aSAjay Gupta 	memcpy(buf + sizeof(rab), data, len);
248247c554aSAjay Gupta 
249247c554aSAjay Gupta 	msgs[0].len = len + sizeof(rab);
250247c554aSAjay Gupta 	msgs[0].buf = buf;
251247c554aSAjay Gupta 
252247c554aSAjay Gupta 	status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
253247c554aSAjay Gupta 	if (status < 0) {
254247c554aSAjay Gupta 		dev_err(uc->dev, "i2c_transfer failed %d\n", status);
255247c554aSAjay Gupta 		kfree(buf);
256247c554aSAjay Gupta 		return status;
257247c554aSAjay Gupta 	}
258247c554aSAjay Gupta 
259247c554aSAjay Gupta 	kfree(buf);
260247c554aSAjay Gupta 	return 0;
261247c554aSAjay Gupta }
262247c554aSAjay Gupta 
263247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc)
264247c554aSAjay Gupta {
265247c554aSAjay Gupta 	unsigned int count = 10;
266247c554aSAjay Gupta 	u8 data;
267247c554aSAjay Gupta 	int status;
268247c554aSAjay Gupta 
269247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_STOP;
270247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
271247c554aSAjay Gupta 	if (status < 0)
272247c554aSAjay Gupta 		return status;
273247c554aSAjay Gupta 
274247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_START;
275247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
276247c554aSAjay Gupta 	if (status < 0)
277247c554aSAjay Gupta 		return status;
278247c554aSAjay Gupta 
279247c554aSAjay Gupta 	/*
280247c554aSAjay Gupta 	 * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
281247c554aSAjay Gupta 	 * register write will push response which must be cleared.
282247c554aSAjay Gupta 	 */
283247c554aSAjay Gupta 	do {
284247c554aSAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
285247c554aSAjay Gupta 		if (status < 0)
286247c554aSAjay Gupta 			return status;
287247c554aSAjay Gupta 
288247c554aSAjay Gupta 		if (!data)
289247c554aSAjay Gupta 			return 0;
290247c554aSAjay Gupta 
291247c554aSAjay Gupta 		status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
292247c554aSAjay Gupta 		if (status < 0)
293247c554aSAjay Gupta 			return status;
294247c554aSAjay Gupta 
295247c554aSAjay Gupta 		usleep_range(10000, 11000);
296247c554aSAjay Gupta 	} while (--count);
297247c554aSAjay Gupta 
298247c554aSAjay Gupta 	return -ETIMEDOUT;
299247c554aSAjay Gupta }
300247c554aSAjay Gupta 
301247c554aSAjay Gupta static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
302247c554aSAjay Gupta {
303247c554aSAjay Gupta 	u8 *ppm = (u8 *)uc->ppm.data;
304247c554aSAjay Gupta 	int status;
305247c554aSAjay Gupta 	u16 rab;
306247c554aSAjay Gupta 
307247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
308247c554aSAjay Gupta 	status = ccg_write(uc, rab, ppm +
309247c554aSAjay Gupta 			   offsetof(struct ucsi_data, message_out),
310247c554aSAjay Gupta 			   sizeof(uc->ppm.data->message_out));
311247c554aSAjay Gupta 	if (status < 0)
312247c554aSAjay Gupta 		return status;
313247c554aSAjay Gupta 
314247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
315247c554aSAjay Gupta 	return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
316247c554aSAjay Gupta 			 sizeof(uc->ppm.data->ctrl));
317247c554aSAjay Gupta }
318247c554aSAjay Gupta 
319247c554aSAjay Gupta static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
320247c554aSAjay Gupta {
321247c554aSAjay Gupta 	u8 *ppm = (u8 *)uc->ppm.data;
322247c554aSAjay Gupta 	int status;
323247c554aSAjay Gupta 	u16 rab;
324247c554aSAjay Gupta 
325247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
326247c554aSAjay Gupta 	status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
327247c554aSAjay Gupta 			  sizeof(uc->ppm.data->cci));
328247c554aSAjay Gupta 	if (status < 0)
329247c554aSAjay Gupta 		return status;
330247c554aSAjay Gupta 
331247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
332247c554aSAjay Gupta 	return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
333247c554aSAjay Gupta 			sizeof(uc->ppm.data->message_in));
334247c554aSAjay Gupta }
335247c554aSAjay Gupta 
336247c554aSAjay Gupta static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
337247c554aSAjay Gupta {
338247c554aSAjay Gupta 	int status;
339247c554aSAjay Gupta 	unsigned char data;
340247c554aSAjay Gupta 
341247c554aSAjay Gupta 	status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
342247c554aSAjay Gupta 	if (status < 0)
343247c554aSAjay Gupta 		return status;
344247c554aSAjay Gupta 
345247c554aSAjay Gupta 	return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
346247c554aSAjay Gupta }
347247c554aSAjay Gupta 
348247c554aSAjay Gupta static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
349247c554aSAjay Gupta {
350247c554aSAjay Gupta 	struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
351247c554aSAjay Gupta 	int status;
352247c554aSAjay Gupta 
353247c554aSAjay Gupta 	status = ucsi_ccg_recv_data(uc);
354247c554aSAjay Gupta 	if (status < 0)
355247c554aSAjay Gupta 		return status;
356247c554aSAjay Gupta 
357247c554aSAjay Gupta 	/* ack interrupt to allow next command to run */
358247c554aSAjay Gupta 	return ucsi_ccg_ack_interrupt(uc);
359247c554aSAjay Gupta }
360247c554aSAjay Gupta 
361247c554aSAjay Gupta static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
362247c554aSAjay Gupta {
363247c554aSAjay Gupta 	struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
364247c554aSAjay Gupta 
365247c554aSAjay Gupta 	ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
366247c554aSAjay Gupta 	return ucsi_ccg_send_data(uc);
367247c554aSAjay Gupta }
368247c554aSAjay Gupta 
369247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data)
370247c554aSAjay Gupta {
371247c554aSAjay Gupta 	struct ucsi_ccg *uc = data;
372247c554aSAjay Gupta 
373247c554aSAjay Gupta 	ucsi_notify(uc->ucsi);
374247c554aSAjay Gupta 
375247c554aSAjay Gupta 	return IRQ_HANDLED;
376247c554aSAjay Gupta }
377247c554aSAjay Gupta 
3785d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc)
3795d438e20SAjay Gupta {
3805d438e20SAjay Gupta 	int err;
3815d438e20SAjay Gupta 
3825d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version),
3835d438e20SAjay Gupta 		       sizeof(uc->version));
3845d438e20SAjay Gupta 	if (err < 0)
3855d438e20SAjay Gupta 		return err;
3865d438e20SAjay Gupta 
3875d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
3885d438e20SAjay Gupta 		       sizeof(uc->info));
3895d438e20SAjay Gupta 	if (err < 0)
3905d438e20SAjay Gupta 		return err;
3915d438e20SAjay Gupta 
3925d438e20SAjay Gupta 	return 0;
3935d438e20SAjay Gupta }
3945d438e20SAjay Gupta 
3955c9ae5a8SAjay Gupta static inline bool invalid_async_evt(int code)
3965c9ae5a8SAjay Gupta {
3975c9ae5a8SAjay Gupta 	return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX);
3985c9ae5a8SAjay Gupta }
3995c9ae5a8SAjay Gupta 
4005c9ae5a8SAjay Gupta static void ccg_process_response(struct ucsi_ccg *uc)
4015c9ae5a8SAjay Gupta {
4025c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
4035c9ae5a8SAjay Gupta 
4045c9ae5a8SAjay Gupta 	if (uc->dev_resp.code & ASYNC_EVENT) {
4055c9ae5a8SAjay Gupta 		if (uc->dev_resp.code == RESET_COMPLETE) {
4065c9ae5a8SAjay Gupta 			if (test_bit(RESET_PENDING, &uc->flags))
4075c9ae5a8SAjay Gupta 				uc->cmd_resp = uc->dev_resp.code;
4085c9ae5a8SAjay Gupta 			get_fw_info(uc);
4095c9ae5a8SAjay Gupta 		}
4105c9ae5a8SAjay Gupta 		if (invalid_async_evt(uc->dev_resp.code))
4115c9ae5a8SAjay Gupta 			dev_err(dev, "invalid async evt %d\n",
4125c9ae5a8SAjay Gupta 				uc->dev_resp.code);
4135c9ae5a8SAjay Gupta 	} else {
4145c9ae5a8SAjay Gupta 		if (test_bit(DEV_CMD_PENDING, &uc->flags)) {
4155c9ae5a8SAjay Gupta 			uc->cmd_resp = uc->dev_resp.code;
4165c9ae5a8SAjay Gupta 			clear_bit(DEV_CMD_PENDING, &uc->flags);
4175c9ae5a8SAjay Gupta 		} else {
4185c9ae5a8SAjay Gupta 			dev_err(dev, "dev resp 0x%04x but no cmd pending\n",
4195c9ae5a8SAjay Gupta 				uc->dev_resp.code);
4205c9ae5a8SAjay Gupta 		}
4215c9ae5a8SAjay Gupta 	}
4225c9ae5a8SAjay Gupta }
4235c9ae5a8SAjay Gupta 
4245c9ae5a8SAjay Gupta static int ccg_read_response(struct ucsi_ccg *uc)
4255c9ae5a8SAjay Gupta {
4265c9ae5a8SAjay Gupta 	unsigned long target = jiffies + msecs_to_jiffies(1000);
4275c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
4285c9ae5a8SAjay Gupta 	u8 intval;
4295c9ae5a8SAjay Gupta 	int status;
4305c9ae5a8SAjay Gupta 
4315c9ae5a8SAjay Gupta 	/* wait for interrupt status to get updated */
4325c9ae5a8SAjay Gupta 	do {
4335c9ae5a8SAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval,
4345c9ae5a8SAjay Gupta 				  sizeof(intval));
4355c9ae5a8SAjay Gupta 		if (status < 0)
4365c9ae5a8SAjay Gupta 			return status;
4375c9ae5a8SAjay Gupta 
4385c9ae5a8SAjay Gupta 		if (intval & DEV_INT)
4395c9ae5a8SAjay Gupta 			break;
4405c9ae5a8SAjay Gupta 		usleep_range(500, 600);
4415c9ae5a8SAjay Gupta 	} while (time_is_after_jiffies(target));
4425c9ae5a8SAjay Gupta 
4435c9ae5a8SAjay Gupta 	if (time_is_before_jiffies(target)) {
4445c9ae5a8SAjay Gupta 		dev_err(dev, "response timeout error\n");
4455c9ae5a8SAjay Gupta 		return -ETIME;
4465c9ae5a8SAjay Gupta 	}
4475c9ae5a8SAjay Gupta 
4485c9ae5a8SAjay Gupta 	status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp,
4495c9ae5a8SAjay Gupta 			  sizeof(uc->dev_resp));
4505c9ae5a8SAjay Gupta 	if (status < 0)
4515c9ae5a8SAjay Gupta 		return status;
4525c9ae5a8SAjay Gupta 
4535c9ae5a8SAjay Gupta 	status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval));
4545c9ae5a8SAjay Gupta 	if (status < 0)
4555c9ae5a8SAjay Gupta 		return status;
4565c9ae5a8SAjay Gupta 
4575c9ae5a8SAjay Gupta 	return 0;
4585c9ae5a8SAjay Gupta }
4595c9ae5a8SAjay Gupta 
4605c9ae5a8SAjay Gupta /* Caller must hold uc->lock */
4615c9ae5a8SAjay Gupta static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd)
4625c9ae5a8SAjay Gupta {
4635c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
4645c9ae5a8SAjay Gupta 	int ret;
4655c9ae5a8SAjay Gupta 
4665c9ae5a8SAjay Gupta 	switch (cmd->reg & 0xF000) {
4675c9ae5a8SAjay Gupta 	case DEV_REG_IDX:
4685c9ae5a8SAjay Gupta 		set_bit(DEV_CMD_PENDING, &uc->flags);
4695c9ae5a8SAjay Gupta 		break;
4705c9ae5a8SAjay Gupta 	default:
4715c9ae5a8SAjay Gupta 		dev_err(dev, "invalid cmd register\n");
4725c9ae5a8SAjay Gupta 		break;
4735c9ae5a8SAjay Gupta 	}
4745c9ae5a8SAjay Gupta 
4755c9ae5a8SAjay Gupta 	ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len);
4765c9ae5a8SAjay Gupta 	if (ret < 0)
4775c9ae5a8SAjay Gupta 		return ret;
4785c9ae5a8SAjay Gupta 
4795c9ae5a8SAjay Gupta 	msleep(cmd->delay);
4805c9ae5a8SAjay Gupta 
4815c9ae5a8SAjay Gupta 	ret = ccg_read_response(uc);
4825c9ae5a8SAjay Gupta 	if (ret < 0) {
4835c9ae5a8SAjay Gupta 		dev_err(dev, "response read error\n");
4845c9ae5a8SAjay Gupta 		switch (cmd->reg & 0xF000) {
4855c9ae5a8SAjay Gupta 		case DEV_REG_IDX:
4865c9ae5a8SAjay Gupta 			clear_bit(DEV_CMD_PENDING, &uc->flags);
4875c9ae5a8SAjay Gupta 			break;
4885c9ae5a8SAjay Gupta 		default:
4895c9ae5a8SAjay Gupta 			dev_err(dev, "invalid cmd register\n");
4905c9ae5a8SAjay Gupta 			break;
4915c9ae5a8SAjay Gupta 		}
4925c9ae5a8SAjay Gupta 		return -EIO;
4935c9ae5a8SAjay Gupta 	}
4945c9ae5a8SAjay Gupta 	ccg_process_response(uc);
4955c9ae5a8SAjay Gupta 
4965c9ae5a8SAjay Gupta 	return uc->cmd_resp;
4975c9ae5a8SAjay Gupta }
4985c9ae5a8SAjay Gupta 
4995c9ae5a8SAjay Gupta static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc)
5005c9ae5a8SAjay Gupta {
5015c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
5025c9ae5a8SAjay Gupta 	int ret;
5035c9ae5a8SAjay Gupta 
5045c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_ENTER_FLASHING;
5055c9ae5a8SAjay Gupta 	cmd.data = FLASH_ENTER_SIG;
5065c9ae5a8SAjay Gupta 	cmd.len = 1;
5075c9ae5a8SAjay Gupta 	cmd.delay = 50;
5085c9ae5a8SAjay Gupta 
5095c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
5105c9ae5a8SAjay Gupta 
5115c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
5125c9ae5a8SAjay Gupta 
5135c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
5145c9ae5a8SAjay Gupta 
5155c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
5165c9ae5a8SAjay Gupta 		dev_err(uc->dev, "enter flashing failed ret=%d\n", ret);
5175c9ae5a8SAjay Gupta 		return ret;
5185c9ae5a8SAjay Gupta 	}
5195c9ae5a8SAjay Gupta 
5205c9ae5a8SAjay Gupta 	return 0;
5215c9ae5a8SAjay Gupta }
5225c9ae5a8SAjay Gupta 
5235c9ae5a8SAjay Gupta static int ccg_cmd_reset(struct ucsi_ccg *uc)
5245c9ae5a8SAjay Gupta {
5255c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
5265c9ae5a8SAjay Gupta 	u8 *p;
5275c9ae5a8SAjay Gupta 	int ret;
5285c9ae5a8SAjay Gupta 
5295c9ae5a8SAjay Gupta 	p = (u8 *)&cmd.data;
5305c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_RESET_REQ;
5315c9ae5a8SAjay Gupta 	p[0] = RESET_SIG;
5325c9ae5a8SAjay Gupta 	p[1] = CMD_RESET_DEV;
5335c9ae5a8SAjay Gupta 	cmd.len = 2;
5345c9ae5a8SAjay Gupta 	cmd.delay = 5000;
5355c9ae5a8SAjay Gupta 
5365c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
5375c9ae5a8SAjay Gupta 
5385c9ae5a8SAjay Gupta 	set_bit(RESET_PENDING, &uc->flags);
5395c9ae5a8SAjay Gupta 
5405c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
5415c9ae5a8SAjay Gupta 	if (ret != RESET_COMPLETE)
5425c9ae5a8SAjay Gupta 		goto err_clear_flag;
5435c9ae5a8SAjay Gupta 
5445c9ae5a8SAjay Gupta 	ret = 0;
5455c9ae5a8SAjay Gupta 
5465c9ae5a8SAjay Gupta err_clear_flag:
5475c9ae5a8SAjay Gupta 	clear_bit(RESET_PENDING, &uc->flags);
5485c9ae5a8SAjay Gupta 
5495c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
5505c9ae5a8SAjay Gupta 
5515c9ae5a8SAjay Gupta 	return ret;
5525c9ae5a8SAjay Gupta }
5535c9ae5a8SAjay Gupta 
5545c9ae5a8SAjay Gupta static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable)
5555c9ae5a8SAjay Gupta {
5565c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
5575c9ae5a8SAjay Gupta 	int ret;
5585c9ae5a8SAjay Gupta 
5595c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_PDPORT_ENABLE;
5605c9ae5a8SAjay Gupta 	if (enable)
5615c9ae5a8SAjay Gupta 		cmd.data = (uc->port_num == 1) ?
5625c9ae5a8SAjay Gupta 			    PDPORT_1 : (PDPORT_1 | PDPORT_2);
5635c9ae5a8SAjay Gupta 	else
5645c9ae5a8SAjay Gupta 		cmd.data = 0x0;
5655c9ae5a8SAjay Gupta 	cmd.len = 1;
5665c9ae5a8SAjay Gupta 	cmd.delay = 10;
5675c9ae5a8SAjay Gupta 
5685c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
5695c9ae5a8SAjay Gupta 
5705c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
5715c9ae5a8SAjay Gupta 
5725c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
5735c9ae5a8SAjay Gupta 
5745c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
5755c9ae5a8SAjay Gupta 		dev_err(uc->dev, "port control failed ret=%d\n", ret);
5765c9ae5a8SAjay Gupta 		return ret;
5775c9ae5a8SAjay Gupta 	}
5785c9ae5a8SAjay Gupta 	return 0;
5795c9ae5a8SAjay Gupta }
5805c9ae5a8SAjay Gupta 
5815c9ae5a8SAjay Gupta static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode)
5825c9ae5a8SAjay Gupta {
5835c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
5845c9ae5a8SAjay Gupta 	int ret;
5855c9ae5a8SAjay Gupta 
5865c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_JUMP_TO_BOOT;
5875c9ae5a8SAjay Gupta 
5885c9ae5a8SAjay Gupta 	if (bl_mode)
5895c9ae5a8SAjay Gupta 		cmd.data = TO_BOOT;
5905c9ae5a8SAjay Gupta 	else
5915c9ae5a8SAjay Gupta 		cmd.data = TO_ALT_FW;
5925c9ae5a8SAjay Gupta 
5935c9ae5a8SAjay Gupta 	cmd.len = 1;
5945c9ae5a8SAjay Gupta 	cmd.delay = 100;
5955c9ae5a8SAjay Gupta 
5965c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
5975c9ae5a8SAjay Gupta 
5985c9ae5a8SAjay Gupta 	set_bit(RESET_PENDING, &uc->flags);
5995c9ae5a8SAjay Gupta 
6005c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
6015c9ae5a8SAjay Gupta 	if (ret != RESET_COMPLETE)
6025c9ae5a8SAjay Gupta 		goto err_clear_flag;
6035c9ae5a8SAjay Gupta 
6045c9ae5a8SAjay Gupta 	ret = 0;
6055c9ae5a8SAjay Gupta 
6065c9ae5a8SAjay Gupta err_clear_flag:
6075c9ae5a8SAjay Gupta 	clear_bit(RESET_PENDING, &uc->flags);
6085c9ae5a8SAjay Gupta 
6095c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
6105c9ae5a8SAjay Gupta 
6115c9ae5a8SAjay Gupta 	return ret;
6125c9ae5a8SAjay Gupta }
6135c9ae5a8SAjay Gupta 
6145c9ae5a8SAjay Gupta static int
6155c9ae5a8SAjay Gupta ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row,
6165c9ae5a8SAjay Gupta 			const void *data, u8 fcmd)
6175c9ae5a8SAjay Gupta {
6185c9ae5a8SAjay Gupta 	struct i2c_client *client = uc->client;
6195c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
6205c9ae5a8SAjay Gupta 	u8 buf[CCG4_ROW_SIZE + 2];
6215c9ae5a8SAjay Gupta 	u8 *p;
6225c9ae5a8SAjay Gupta 	int ret;
6235c9ae5a8SAjay Gupta 
6245c9ae5a8SAjay Gupta 	/* Copy the data into the flash read/write memory. */
6255c9ae5a8SAjay Gupta 	put_unaligned_le16(REG_FLASH_RW_MEM, buf);
6265c9ae5a8SAjay Gupta 
6275c9ae5a8SAjay Gupta 	memcpy(buf + 2, data, CCG4_ROW_SIZE);
6285c9ae5a8SAjay Gupta 
6295c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
6305c9ae5a8SAjay Gupta 
6315c9ae5a8SAjay Gupta 	ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2);
6325c9ae5a8SAjay Gupta 	if (ret != CCG4_ROW_SIZE + 2) {
6335c9ae5a8SAjay Gupta 		dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret);
6345c9ae5a8SAjay Gupta 		return ret < 0 ? ret : -EIO;
6355c9ae5a8SAjay Gupta 	}
6365c9ae5a8SAjay Gupta 
6375c9ae5a8SAjay Gupta 	/* Use the FLASH_ROW_READ_WRITE register to trigger */
6385c9ae5a8SAjay Gupta 	/* writing of data to the desired flash row */
6395c9ae5a8SAjay Gupta 	p = (u8 *)&cmd.data;
6405c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_FLASH_ROW_RW;
6415c9ae5a8SAjay Gupta 	p[0] = FLASH_SIG;
6425c9ae5a8SAjay Gupta 	p[1] = fcmd;
6435c9ae5a8SAjay Gupta 	put_unaligned_le16(row, &p[2]);
6445c9ae5a8SAjay Gupta 	cmd.len = 4;
6455c9ae5a8SAjay Gupta 	cmd.delay = 50;
6465c9ae5a8SAjay Gupta 	if (fcmd == FLASH_FWCT_SIG_WR_CMD)
6475c9ae5a8SAjay Gupta 		cmd.delay += 400;
6485c9ae5a8SAjay Gupta 	if (row == 510)
6495c9ae5a8SAjay Gupta 		cmd.delay += 220;
6505c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
6515c9ae5a8SAjay Gupta 
6525c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
6535c9ae5a8SAjay Gupta 
6545c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS) {
6555c9ae5a8SAjay Gupta 		dev_err(uc->dev, "write flash row failed ret=%d\n", ret);
6565c9ae5a8SAjay Gupta 		return ret;
6575c9ae5a8SAjay Gupta 	}
6585c9ae5a8SAjay Gupta 
6595c9ae5a8SAjay Gupta 	return 0;
6605c9ae5a8SAjay Gupta }
6615c9ae5a8SAjay Gupta 
6625c9ae5a8SAjay Gupta static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
6635c9ae5a8SAjay Gupta {
6645c9ae5a8SAjay Gupta 	struct ccg_cmd cmd;
6655c9ae5a8SAjay Gupta 	int ret;
6665c9ae5a8SAjay Gupta 
6675c9ae5a8SAjay Gupta 	cmd.reg = CCGX_RAB_VALIDATE_FW;
6685c9ae5a8SAjay Gupta 	cmd.data = fwid;
6695c9ae5a8SAjay Gupta 	cmd.len = 1;
6705c9ae5a8SAjay Gupta 	cmd.delay = 500;
6715c9ae5a8SAjay Gupta 
6725c9ae5a8SAjay Gupta 	mutex_lock(&uc->lock);
6735c9ae5a8SAjay Gupta 
6745c9ae5a8SAjay Gupta 	ret = ccg_send_command(uc, &cmd);
6755c9ae5a8SAjay Gupta 
6765c9ae5a8SAjay Gupta 	mutex_unlock(&uc->lock);
6775c9ae5a8SAjay Gupta 
6785c9ae5a8SAjay Gupta 	if (ret != CMD_SUCCESS)
6795c9ae5a8SAjay Gupta 		return ret;
6805c9ae5a8SAjay Gupta 
6815c9ae5a8SAjay Gupta 	return 0;
6825c9ae5a8SAjay Gupta }
6835c9ae5a8SAjay Gupta 
6845c9ae5a8SAjay Gupta static bool ccg_check_vendor_version(struct ucsi_ccg *uc,
6855c9ae5a8SAjay Gupta 				     struct version_format *app,
6865c9ae5a8SAjay Gupta 				     struct fw_config_table *fw_cfg)
6875c9ae5a8SAjay Gupta {
6885c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
6895c9ae5a8SAjay Gupta 
6905c9ae5a8SAjay Gupta 	/* Check if the fw build is for supported vendors */
6915c9ae5a8SAjay Gupta 	if (le16_to_cpu(app->build) != uc->fw_build) {
6925c9ae5a8SAjay Gupta 		dev_info(dev, "current fw is not from supported vendor\n");
6935c9ae5a8SAjay Gupta 		return false;
6945c9ae5a8SAjay Gupta 	}
6955c9ae5a8SAjay Gupta 
6965c9ae5a8SAjay Gupta 	/* Check if the new fw build is for supported vendors */
6975c9ae5a8SAjay Gupta 	if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) {
6985c9ae5a8SAjay Gupta 		dev_info(dev, "new fw is not from supported vendor\n");
6995c9ae5a8SAjay Gupta 		return false;
7005c9ae5a8SAjay Gupta 	}
7015c9ae5a8SAjay Gupta 	return true;
7025c9ae5a8SAjay Gupta }
7035c9ae5a8SAjay Gupta 
7045c9ae5a8SAjay Gupta static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
7055c9ae5a8SAjay Gupta 				 struct version_format *app)
7065c9ae5a8SAjay Gupta {
7075c9ae5a8SAjay Gupta 	const struct firmware *fw = NULL;
7085c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7095c9ae5a8SAjay Gupta 	struct fw_config_table fw_cfg;
7105c9ae5a8SAjay Gupta 	u32 cur_version, new_version;
7115c9ae5a8SAjay Gupta 	bool is_later = false;
7125c9ae5a8SAjay Gupta 
7135c9ae5a8SAjay Gupta 	if (request_firmware(&fw, fw_name, dev) != 0) {
7145c9ae5a8SAjay Gupta 		dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
7155c9ae5a8SAjay Gupta 		return false;
7165c9ae5a8SAjay Gupta 	}
7175c9ae5a8SAjay Gupta 
7185c9ae5a8SAjay Gupta 	/*
7195c9ae5a8SAjay Gupta 	 * check if signed fw
7205c9ae5a8SAjay Gupta 	 * last part of fw image is fw cfg table and signature
7215c9ae5a8SAjay Gupta 	 */
7225c9ae5a8SAjay Gupta 	if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
7235c9ae5a8SAjay Gupta 		goto out_release_firmware;
7245c9ae5a8SAjay Gupta 
7255c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
7265c9ae5a8SAjay Gupta 	       sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));
7275c9ae5a8SAjay Gupta 
7285c9ae5a8SAjay Gupta 	if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) {
7295c9ae5a8SAjay Gupta 		dev_info(dev, "not a signed image\n");
7305c9ae5a8SAjay Gupta 		goto out_release_firmware;
7315c9ae5a8SAjay Gupta 	}
7325c9ae5a8SAjay Gupta 
7335c9ae5a8SAjay Gupta 	/* compare input version with FWCT version */
7345c9ae5a8SAjay Gupta 	cur_version = le16_to_cpu(app->build) | app->patch << 16 |
7355c9ae5a8SAjay Gupta 			app->ver << 24;
7365c9ae5a8SAjay Gupta 
7375c9ae5a8SAjay Gupta 	new_version = le16_to_cpu(fw_cfg.app.build) | fw_cfg.app.patch << 16 |
7385c9ae5a8SAjay Gupta 			fw_cfg.app.ver << 24;
7395c9ae5a8SAjay Gupta 
7405c9ae5a8SAjay Gupta 	if (!ccg_check_vendor_version(uc, app, &fw_cfg))
7415c9ae5a8SAjay Gupta 		goto out_release_firmware;
7425c9ae5a8SAjay Gupta 
7435c9ae5a8SAjay Gupta 	if (new_version > cur_version)
7445c9ae5a8SAjay Gupta 		is_later = true;
7455c9ae5a8SAjay Gupta 
7465c9ae5a8SAjay Gupta out_release_firmware:
7475c9ae5a8SAjay Gupta 	release_firmware(fw);
7485c9ae5a8SAjay Gupta 	return is_later;
7495c9ae5a8SAjay Gupta }
7505c9ae5a8SAjay Gupta 
7515c9ae5a8SAjay Gupta static int ccg_fw_update_needed(struct ucsi_ccg *uc,
7525c9ae5a8SAjay Gupta 				enum enum_flash_mode *mode)
7535c9ae5a8SAjay Gupta {
7545c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7555c9ae5a8SAjay Gupta 	int err;
7565c9ae5a8SAjay Gupta 	struct version_info version[3];
7575c9ae5a8SAjay Gupta 
7585c9ae5a8SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
7595c9ae5a8SAjay Gupta 		       sizeof(uc->info));
7605c9ae5a8SAjay Gupta 	if (err) {
7615c9ae5a8SAjay Gupta 		dev_err(dev, "read device mode failed\n");
7625c9ae5a8SAjay Gupta 		return err;
7635c9ae5a8SAjay Gupta 	}
7645c9ae5a8SAjay Gupta 
7655c9ae5a8SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
7665c9ae5a8SAjay Gupta 		       sizeof(version));
7675c9ae5a8SAjay Gupta 	if (err) {
7685c9ae5a8SAjay Gupta 		dev_err(dev, "read device mode failed\n");
7695c9ae5a8SAjay Gupta 		return err;
7705c9ae5a8SAjay Gupta 	}
7715c9ae5a8SAjay Gupta 
7725c9ae5a8SAjay Gupta 	if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
7735c9ae5a8SAjay Gupta 		   sizeof(struct version_info)) == 0) {
7745c9ae5a8SAjay Gupta 		dev_info(dev, "secondary fw is not flashed\n");
7755c9ae5a8SAjay Gupta 		*mode = SECONDARY_BL;
7765c9ae5a8SAjay Gupta 	} else if (le16_to_cpu(version[FW1].base.build) <
7775c9ae5a8SAjay Gupta 		secondary_fw_min_ver) {
7785c9ae5a8SAjay Gupta 		dev_info(dev, "secondary fw version is too low (< %d)\n",
7795c9ae5a8SAjay Gupta 			 secondary_fw_min_ver);
7805c9ae5a8SAjay Gupta 		*mode = SECONDARY;
7815c9ae5a8SAjay Gupta 	} else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
7825c9ae5a8SAjay Gupta 		   sizeof(struct version_info)) == 0) {
7835c9ae5a8SAjay Gupta 		dev_info(dev, "primary fw is not flashed\n");
7845c9ae5a8SAjay Gupta 		*mode = PRIMARY;
7855c9ae5a8SAjay Gupta 	} else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
7865c9ae5a8SAjay Gupta 		   &version[FW2].app)) {
7875c9ae5a8SAjay Gupta 		dev_info(dev, "found primary fw with later version\n");
7885c9ae5a8SAjay Gupta 		*mode = PRIMARY;
7895c9ae5a8SAjay Gupta 	} else {
7905c9ae5a8SAjay Gupta 		dev_info(dev, "secondary and primary fw are the latest\n");
7915c9ae5a8SAjay Gupta 		*mode = FLASH_NOT_NEEDED;
7925c9ae5a8SAjay Gupta 	}
7935c9ae5a8SAjay Gupta 	return 0;
7945c9ae5a8SAjay Gupta }
7955c9ae5a8SAjay Gupta 
7965c9ae5a8SAjay Gupta static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
7975c9ae5a8SAjay Gupta {
7985c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
7995c9ae5a8SAjay Gupta 	const struct firmware *fw = NULL;
8005c9ae5a8SAjay Gupta 	const char *p, *s;
8015c9ae5a8SAjay Gupta 	const char *eof;
8025c9ae5a8SAjay Gupta 	int err, row, len, line_sz, line_cnt = 0;
8035c9ae5a8SAjay Gupta 	unsigned long start_time = jiffies;
8045c9ae5a8SAjay Gupta 	struct fw_config_table  fw_cfg;
8055c9ae5a8SAjay Gupta 	u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE];
8065c9ae5a8SAjay Gupta 	u8 *wr_buf;
8075c9ae5a8SAjay Gupta 
8085c9ae5a8SAjay Gupta 	err = request_firmware(&fw, ccg_fw_names[mode], dev);
8095c9ae5a8SAjay Gupta 	if (err) {
8105c9ae5a8SAjay Gupta 		dev_err(dev, "request %s failed err=%d\n",
8115c9ae5a8SAjay Gupta 			ccg_fw_names[mode], err);
8125c9ae5a8SAjay Gupta 		return err;
8135c9ae5a8SAjay Gupta 	}
8145c9ae5a8SAjay Gupta 
8155c9ae5a8SAjay Gupta 	if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >>
8165c9ae5a8SAjay Gupta 			CCG_DEVINFO_FWMODE_SHIFT) == FW2) {
8175c9ae5a8SAjay Gupta 		err = ccg_cmd_port_control(uc, false);
8185c9ae5a8SAjay Gupta 		if (err < 0)
8195c9ae5a8SAjay Gupta 			goto release_fw;
8205c9ae5a8SAjay Gupta 		err = ccg_cmd_jump_boot_mode(uc, 0);
8215c9ae5a8SAjay Gupta 		if (err < 0)
8225c9ae5a8SAjay Gupta 			goto release_fw;
8235c9ae5a8SAjay Gupta 	}
8245c9ae5a8SAjay Gupta 
8255c9ae5a8SAjay Gupta 	eof = fw->data + fw->size;
8265c9ae5a8SAjay Gupta 
8275c9ae5a8SAjay Gupta 	/*
8285c9ae5a8SAjay Gupta 	 * check if signed fw
8295c9ae5a8SAjay Gupta 	 * last part of fw image is fw cfg table and signature
8305c9ae5a8SAjay Gupta 	 */
8315c9ae5a8SAjay Gupta 	if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig))
8325c9ae5a8SAjay Gupta 		goto not_signed_fw;
8335c9ae5a8SAjay Gupta 
8345c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
8355c9ae5a8SAjay Gupta 	       sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg));
8365c9ae5a8SAjay Gupta 
8375c9ae5a8SAjay Gupta 	if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
8385c9ae5a8SAjay Gupta 		dev_info(dev, "not a signed image\n");
8395c9ae5a8SAjay Gupta 		goto not_signed_fw;
8405c9ae5a8SAjay Gupta 	}
8415c9ae5a8SAjay Gupta 	eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig);
8425c9ae5a8SAjay Gupta 
8435c9ae5a8SAjay Gupta 	memcpy((uint8_t *)&fw_cfg_sig,
8445c9ae5a8SAjay Gupta 	       fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig));
8455c9ae5a8SAjay Gupta 
8465c9ae5a8SAjay Gupta 	/* flash fw config table and signature first */
8475c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg,
8485c9ae5a8SAjay Gupta 				      FLASH_FWCT1_WR_CMD);
8495c9ae5a8SAjay Gupta 	if (err)
8505c9ae5a8SAjay Gupta 		goto release_fw;
8515c9ae5a8SAjay Gupta 
8525c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE,
8535c9ae5a8SAjay Gupta 				      FLASH_FWCT2_WR_CMD);
8545c9ae5a8SAjay Gupta 	if (err)
8555c9ae5a8SAjay Gupta 		goto release_fw;
8565c9ae5a8SAjay Gupta 
8575c9ae5a8SAjay Gupta 	err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig,
8585c9ae5a8SAjay Gupta 				      FLASH_FWCT_SIG_WR_CMD);
8595c9ae5a8SAjay Gupta 	if (err)
8605c9ae5a8SAjay Gupta 		goto release_fw;
8615c9ae5a8SAjay Gupta 
8625c9ae5a8SAjay Gupta not_signed_fw:
8635c9ae5a8SAjay Gupta 	wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL);
8645c9ae5a8SAjay Gupta 	if (!wr_buf)
8655c9ae5a8SAjay Gupta 		return -ENOMEM;
8665c9ae5a8SAjay Gupta 
8675c9ae5a8SAjay Gupta 	err = ccg_cmd_enter_flashing(uc);
8685c9ae5a8SAjay Gupta 	if (err)
8695c9ae5a8SAjay Gupta 		goto release_mem;
8705c9ae5a8SAjay Gupta 
8715c9ae5a8SAjay Gupta 	/*****************************************************************
8725c9ae5a8SAjay Gupta 	 * CCG firmware image (.cyacd) file line format
8735c9ae5a8SAjay Gupta 	 *
8745c9ae5a8SAjay Gupta 	 * :00rrrrllll[dd....]cc/r/n
8755c9ae5a8SAjay Gupta 	 *
8765c9ae5a8SAjay Gupta 	 * :00   header
8775c9ae5a8SAjay Gupta 	 * rrrr is row number to flash				(4 char)
8785c9ae5a8SAjay Gupta 	 * llll is data len to flash				(4 char)
8795c9ae5a8SAjay Gupta 	 * dd   is a data field represents one byte of data	(512 char)
8805c9ae5a8SAjay Gupta 	 * cc   is checksum					(2 char)
8815c9ae5a8SAjay Gupta 	 * \r\n newline
8825c9ae5a8SAjay Gupta 	 *
8835c9ae5a8SAjay Gupta 	 * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527
8845c9ae5a8SAjay Gupta 	 *
8855c9ae5a8SAjay Gupta 	 *****************************************************************/
8865c9ae5a8SAjay Gupta 
8875c9ae5a8SAjay Gupta 	p = strnchr(fw->data, fw->size, ':');
8885c9ae5a8SAjay Gupta 	while (p < eof) {
8895c9ae5a8SAjay Gupta 		s = strnchr(p + 1, eof - p - 1, ':');
8905c9ae5a8SAjay Gupta 
8915c9ae5a8SAjay Gupta 		if (!s)
8925c9ae5a8SAjay Gupta 			s = eof;
8935c9ae5a8SAjay Gupta 
8945c9ae5a8SAjay Gupta 		line_sz = s - p;
8955c9ae5a8SAjay Gupta 
8965c9ae5a8SAjay Gupta 		if (line_sz != CYACD_LINE_SIZE) {
8975c9ae5a8SAjay Gupta 			dev_err(dev, "Bad FW format line_sz=%d\n", line_sz);
8985c9ae5a8SAjay Gupta 			err =  -EINVAL;
8995c9ae5a8SAjay Gupta 			goto release_mem;
9005c9ae5a8SAjay Gupta 		}
9015c9ae5a8SAjay Gupta 
9025c9ae5a8SAjay Gupta 		if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) {
9035c9ae5a8SAjay Gupta 			err =  -EINVAL;
9045c9ae5a8SAjay Gupta 			goto release_mem;
9055c9ae5a8SAjay Gupta 		}
9065c9ae5a8SAjay Gupta 
9075c9ae5a8SAjay Gupta 		row = get_unaligned_be16(wr_buf);
9085c9ae5a8SAjay Gupta 		len = get_unaligned_be16(&wr_buf[2]);
9095c9ae5a8SAjay Gupta 
9105c9ae5a8SAjay Gupta 		if (len != CCG4_ROW_SIZE) {
9115c9ae5a8SAjay Gupta 			err =  -EINVAL;
9125c9ae5a8SAjay Gupta 			goto release_mem;
9135c9ae5a8SAjay Gupta 		}
9145c9ae5a8SAjay Gupta 
9155c9ae5a8SAjay Gupta 		err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4,
9165c9ae5a8SAjay Gupta 					      FLASH_WR_CMD);
9175c9ae5a8SAjay Gupta 		if (err)
9185c9ae5a8SAjay Gupta 			goto release_mem;
9195c9ae5a8SAjay Gupta 
9205c9ae5a8SAjay Gupta 		line_cnt++;
9215c9ae5a8SAjay Gupta 		p = s;
9225c9ae5a8SAjay Gupta 	}
9235c9ae5a8SAjay Gupta 
9245c9ae5a8SAjay Gupta 	dev_info(dev, "total %d row flashed. time: %dms\n",
9255c9ae5a8SAjay Gupta 		 line_cnt, jiffies_to_msecs(jiffies - start_time));
9265c9ae5a8SAjay Gupta 
9275c9ae5a8SAjay Gupta 	err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 :  FW1);
9285c9ae5a8SAjay Gupta 	if (err)
9295c9ae5a8SAjay Gupta 		dev_err(dev, "%s validation failed err=%d\n",
9305c9ae5a8SAjay Gupta 			(mode == PRIMARY) ? "FW2" :  "FW1", err);
9315c9ae5a8SAjay Gupta 	else
9325c9ae5a8SAjay Gupta 		dev_info(dev, "%s validated\n",
9335c9ae5a8SAjay Gupta 			 (mode == PRIMARY) ? "FW2" :  "FW1");
9345c9ae5a8SAjay Gupta 
9355c9ae5a8SAjay Gupta 	err = ccg_cmd_port_control(uc, false);
9365c9ae5a8SAjay Gupta 	if (err < 0)
9375c9ae5a8SAjay Gupta 		goto release_mem;
9385c9ae5a8SAjay Gupta 
9395c9ae5a8SAjay Gupta 	err = ccg_cmd_reset(uc);
9405c9ae5a8SAjay Gupta 	if (err < 0)
9415c9ae5a8SAjay Gupta 		goto release_mem;
9425c9ae5a8SAjay Gupta 
9435c9ae5a8SAjay Gupta 	err = ccg_cmd_port_control(uc, true);
9445c9ae5a8SAjay Gupta 	if (err < 0)
9455c9ae5a8SAjay Gupta 		goto release_mem;
9465c9ae5a8SAjay Gupta 
9475c9ae5a8SAjay Gupta release_mem:
9485c9ae5a8SAjay Gupta 	kfree(wr_buf);
9495c9ae5a8SAjay Gupta 
9505c9ae5a8SAjay Gupta release_fw:
9515c9ae5a8SAjay Gupta 	release_firmware(fw);
9525c9ae5a8SAjay Gupta 	return err;
9535c9ae5a8SAjay Gupta }
9545c9ae5a8SAjay Gupta 
9555c9ae5a8SAjay Gupta /*******************************************************************************
9565c9ae5a8SAjay Gupta  * CCG4 has two copies of the firmware in addition to the bootloader.
9575c9ae5a8SAjay Gupta  * If the device is running FW1, FW2 can be updated with the new version.
9585c9ae5a8SAjay Gupta  * Dual firmware mode allows the CCG device to stay in a PD contract and support
9595c9ae5a8SAjay Gupta  * USB PD and Type-C functionality while a firmware update is in progress.
9605c9ae5a8SAjay Gupta  ******************************************************************************/
9615c9ae5a8SAjay Gupta static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode)
9625c9ae5a8SAjay Gupta {
9635c9ae5a8SAjay Gupta 	int err;
9645c9ae5a8SAjay Gupta 
9655c9ae5a8SAjay Gupta 	while (flash_mode != FLASH_NOT_NEEDED) {
9665c9ae5a8SAjay Gupta 		err = do_flash(uc, flash_mode);
9675c9ae5a8SAjay Gupta 		if (err < 0)
9685c9ae5a8SAjay Gupta 			return err;
9695c9ae5a8SAjay Gupta 		err = ccg_fw_update_needed(uc, &flash_mode);
9705c9ae5a8SAjay Gupta 		if (err < 0)
9715c9ae5a8SAjay Gupta 			return err;
9725c9ae5a8SAjay Gupta 	}
9735c9ae5a8SAjay Gupta 	dev_info(uc->dev, "CCG FW update successful\n");
9745c9ae5a8SAjay Gupta 
9755c9ae5a8SAjay Gupta 	return err;
9765c9ae5a8SAjay Gupta }
9775c9ae5a8SAjay Gupta 
9785c9ae5a8SAjay Gupta static int ccg_restart(struct ucsi_ccg *uc)
9795c9ae5a8SAjay Gupta {
9805c9ae5a8SAjay Gupta 	struct device *dev = uc->dev;
9815c9ae5a8SAjay Gupta 	int status;
9825c9ae5a8SAjay Gupta 
9835c9ae5a8SAjay Gupta 	status = ucsi_ccg_init(uc);
9845c9ae5a8SAjay Gupta 	if (status < 0) {
9855c9ae5a8SAjay Gupta 		dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status);
9865c9ae5a8SAjay Gupta 		return status;
9875c9ae5a8SAjay Gupta 	}
9885c9ae5a8SAjay Gupta 
9895c9ae5a8SAjay Gupta 	status = request_threaded_irq(uc->irq, NULL, ccg_irq_handler,
9905c9ae5a8SAjay Gupta 				      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
9915c9ae5a8SAjay Gupta 				      dev_name(dev), uc);
9925c9ae5a8SAjay Gupta 	if (status < 0) {
9935c9ae5a8SAjay Gupta 		dev_err(dev, "request_threaded_irq failed - %d\n", status);
9945c9ae5a8SAjay Gupta 		return status;
9955c9ae5a8SAjay Gupta 	}
9965c9ae5a8SAjay Gupta 
9975c9ae5a8SAjay Gupta 	uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
9985c9ae5a8SAjay Gupta 	if (IS_ERR(uc->ucsi)) {
9995c9ae5a8SAjay Gupta 		dev_err(uc->dev, "ucsi_register_ppm failed\n");
10005c9ae5a8SAjay Gupta 		return PTR_ERR(uc->ucsi);
10015c9ae5a8SAjay Gupta 	}
10025c9ae5a8SAjay Gupta 
10035c9ae5a8SAjay Gupta 	return 0;
10045c9ae5a8SAjay Gupta }
10055c9ae5a8SAjay Gupta 
10065c9ae5a8SAjay Gupta static void ccg_update_firmware(struct work_struct *work)
10075c9ae5a8SAjay Gupta {
10085c9ae5a8SAjay Gupta 	struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work);
10095c9ae5a8SAjay Gupta 	enum enum_flash_mode flash_mode;
10105c9ae5a8SAjay Gupta 	int status;
10115c9ae5a8SAjay Gupta 
10125c9ae5a8SAjay Gupta 	status = ccg_fw_update_needed(uc, &flash_mode);
10135c9ae5a8SAjay Gupta 	if (status < 0)
10145c9ae5a8SAjay Gupta 		return;
10155c9ae5a8SAjay Gupta 
10165c9ae5a8SAjay Gupta 	if (flash_mode != FLASH_NOT_NEEDED) {
10175c9ae5a8SAjay Gupta 		ucsi_unregister_ppm(uc->ucsi);
10185c9ae5a8SAjay Gupta 		free_irq(uc->irq, uc);
10195c9ae5a8SAjay Gupta 
10205c9ae5a8SAjay Gupta 		ccg_fw_update(uc, flash_mode);
10215c9ae5a8SAjay Gupta 		ccg_restart(uc);
10225c9ae5a8SAjay Gupta 	}
10235c9ae5a8SAjay Gupta }
10245c9ae5a8SAjay Gupta 
10255c9ae5a8SAjay Gupta static ssize_t do_flash_store(struct device *dev,
10265c9ae5a8SAjay Gupta 			      struct device_attribute *attr,
10275c9ae5a8SAjay Gupta 			      const char *buf, size_t n)
10285c9ae5a8SAjay Gupta {
10295c9ae5a8SAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
10305c9ae5a8SAjay Gupta 	bool flash;
10315c9ae5a8SAjay Gupta 
10325c9ae5a8SAjay Gupta 	if (kstrtobool(buf, &flash))
10335c9ae5a8SAjay Gupta 		return -EINVAL;
10345c9ae5a8SAjay Gupta 
10355c9ae5a8SAjay Gupta 	if (!flash)
10365c9ae5a8SAjay Gupta 		return n;
10375c9ae5a8SAjay Gupta 
10385c9ae5a8SAjay Gupta 	if (uc->fw_build == 0x0) {
10395c9ae5a8SAjay Gupta 		dev_err(dev, "fail to flash FW due to missing FW build info\n");
10405c9ae5a8SAjay Gupta 		return -EINVAL;
10415c9ae5a8SAjay Gupta 	}
10425c9ae5a8SAjay Gupta 
10435c9ae5a8SAjay Gupta 	schedule_work(&uc->work);
10445c9ae5a8SAjay Gupta 	return n;
10455c9ae5a8SAjay Gupta }
10465c9ae5a8SAjay Gupta 
10475c9ae5a8SAjay Gupta static DEVICE_ATTR_WO(do_flash);
10485c9ae5a8SAjay Gupta 
10495c9ae5a8SAjay Gupta static struct attribute *ucsi_ccg_sysfs_attrs[] = {
10505c9ae5a8SAjay Gupta 	&dev_attr_do_flash.attr,
10515c9ae5a8SAjay Gupta 	NULL,
10525c9ae5a8SAjay Gupta };
10535c9ae5a8SAjay Gupta 
10545c9ae5a8SAjay Gupta static struct attribute_group ucsi_ccg_attr_group = {
10555c9ae5a8SAjay Gupta 	.attrs = ucsi_ccg_sysfs_attrs,
10565c9ae5a8SAjay Gupta };
10575c9ae5a8SAjay Gupta 
1058247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client,
1059247c554aSAjay Gupta 			  const struct i2c_device_id *id)
1060247c554aSAjay Gupta {
1061247c554aSAjay Gupta 	struct device *dev = &client->dev;
1062247c554aSAjay Gupta 	struct ucsi_ccg *uc;
1063247c554aSAjay Gupta 	int status;
1064247c554aSAjay Gupta 	u16 rab;
1065247c554aSAjay Gupta 
1066247c554aSAjay Gupta 	uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
1067247c554aSAjay Gupta 	if (!uc)
1068247c554aSAjay Gupta 		return -ENOMEM;
1069247c554aSAjay Gupta 
1070247c554aSAjay Gupta 	uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
1071247c554aSAjay Gupta 	if (!uc->ppm.data)
1072247c554aSAjay Gupta 		return -ENOMEM;
1073247c554aSAjay Gupta 
1074247c554aSAjay Gupta 	uc->ppm.cmd = ucsi_ccg_cmd;
1075247c554aSAjay Gupta 	uc->ppm.sync = ucsi_ccg_sync;
1076247c554aSAjay Gupta 	uc->dev = dev;
1077247c554aSAjay Gupta 	uc->client = client;
10785c9ae5a8SAjay Gupta 	mutex_init(&uc->lock);
10795c9ae5a8SAjay Gupta 	INIT_WORK(&uc->work, ccg_update_firmware);
10805c9ae5a8SAjay Gupta 
10815c9ae5a8SAjay Gupta 	/* Only fail FW flashing when FW build information is not provided */
10825c9ae5a8SAjay Gupta 	status = device_property_read_u16(dev, "ccgx,firmware-build",
10835c9ae5a8SAjay Gupta 					  &uc->fw_build);
10845c9ae5a8SAjay Gupta 	if (status)
10855c9ae5a8SAjay Gupta 		dev_err(uc->dev, "failed to get FW build information\n");
1086247c554aSAjay Gupta 
1087247c554aSAjay Gupta 	/* reset ccg device and initialize ucsi */
1088247c554aSAjay Gupta 	status = ucsi_ccg_init(uc);
1089247c554aSAjay Gupta 	if (status < 0) {
1090247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
1091247c554aSAjay Gupta 		return status;
1092247c554aSAjay Gupta 	}
1093247c554aSAjay Gupta 
10945d438e20SAjay Gupta 	status = get_fw_info(uc);
10955d438e20SAjay Gupta 	if (status < 0) {
10965d438e20SAjay Gupta 		dev_err(uc->dev, "get_fw_info failed - %d\n", status);
10975d438e20SAjay Gupta 		return status;
10985d438e20SAjay Gupta 	}
10995d438e20SAjay Gupta 
11005c9ae5a8SAjay Gupta 	uc->port_num = 1;
11015c9ae5a8SAjay Gupta 
11025c9ae5a8SAjay Gupta 	if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
11035c9ae5a8SAjay Gupta 		uc->port_num++;
11045c9ae5a8SAjay Gupta 
11055c9ae5a8SAjay Gupta 	status = request_threaded_irq(client->irq, NULL, ccg_irq_handler,
1106247c554aSAjay Gupta 				      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
1107247c554aSAjay Gupta 				      dev_name(dev), uc);
1108247c554aSAjay Gupta 	if (status < 0) {
1109247c554aSAjay Gupta 		dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
1110247c554aSAjay Gupta 		return status;
1111247c554aSAjay Gupta 	}
1112247c554aSAjay Gupta 
11135c9ae5a8SAjay Gupta 	uc->irq = client->irq;
11145c9ae5a8SAjay Gupta 
1115247c554aSAjay Gupta 	uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
1116247c554aSAjay Gupta 	if (IS_ERR(uc->ucsi)) {
1117247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_register_ppm failed\n");
1118247c554aSAjay Gupta 		return PTR_ERR(uc->ucsi);
1119247c554aSAjay Gupta 	}
1120247c554aSAjay Gupta 
1121247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
1122247c554aSAjay Gupta 	status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
1123247c554aSAjay Gupta 			  offsetof(struct ucsi_data, version),
1124247c554aSAjay Gupta 			  sizeof(uc->ppm.data->version));
1125247c554aSAjay Gupta 	if (status < 0) {
1126247c554aSAjay Gupta 		ucsi_unregister_ppm(uc->ucsi);
1127247c554aSAjay Gupta 		return status;
1128247c554aSAjay Gupta 	}
1129247c554aSAjay Gupta 
1130247c554aSAjay Gupta 	i2c_set_clientdata(client, uc);
11315c9ae5a8SAjay Gupta 
11325c9ae5a8SAjay Gupta 	status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
11335c9ae5a8SAjay Gupta 	if (status)
11345c9ae5a8SAjay Gupta 		dev_err(uc->dev, "cannot create sysfs group: %d\n", status);
11355c9ae5a8SAjay Gupta 
1136247c554aSAjay Gupta 	return 0;
1137247c554aSAjay Gupta }
1138247c554aSAjay Gupta 
1139247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client)
1140247c554aSAjay Gupta {
1141247c554aSAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
1142247c554aSAjay Gupta 
11435c9ae5a8SAjay Gupta 	cancel_work_sync(&uc->work);
1144247c554aSAjay Gupta 	ucsi_unregister_ppm(uc->ucsi);
11455c9ae5a8SAjay Gupta 	free_irq(uc->irq, uc);
11465c9ae5a8SAjay Gupta 	sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
1147247c554aSAjay Gupta 
1148247c554aSAjay Gupta 	return 0;
1149247c554aSAjay Gupta }
1150247c554aSAjay Gupta 
1151247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = {
1152247c554aSAjay Gupta 	{"ccgx-ucsi", 0},
1153247c554aSAjay Gupta 	{}
1154247c554aSAjay Gupta };
1155247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
1156247c554aSAjay Gupta 
1157247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = {
1158247c554aSAjay Gupta 	.driver = {
1159247c554aSAjay Gupta 		.name = "ucsi_ccg",
1160247c554aSAjay Gupta 	},
1161247c554aSAjay Gupta 	.probe = ucsi_ccg_probe,
1162247c554aSAjay Gupta 	.remove = ucsi_ccg_remove,
1163247c554aSAjay Gupta 	.id_table = ucsi_ccg_device_id,
1164247c554aSAjay Gupta };
1165247c554aSAjay Gupta 
1166247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver);
1167247c554aSAjay Gupta 
1168247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
1169247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
1170247c554aSAjay Gupta MODULE_LICENSE("GPL v2");
1171