diff mbox series

[v2,4/6] usb: typec: ucsi: add cmd used for fw flashing

Message ID 20190128203731.12681-5-ajayg@nvidia.com (mailing list archive)
State Superseded
Headers show
Series Add support for firmware update on Cypres CCGx | expand

Commit Message

Ajay Gupta Jan. 28, 2019, 8:37 p.m. UTC
Adding support for below commands which will be used
during firmware flashing.
	- ENTER_FLASHING
	- RESET
	- PDPORT_ENABLE
	- JUMP_TO_BOOT
	- FLASH_ROW_RW
	- VALIDATE_FW
I command specific mutex lock is also added to sync
between driver and user threads.

Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
---
Changes from v1:
	- Updated commit message
	- Used mask and shift instead of bit fields

 drivers/usb/typec/ucsi/ucsi_ccg.c | 216 ++++++++++++++++++++++++++++++
 1 file changed, 216 insertions(+)
diff mbox series

Patch

diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index f71387a21693..b1fadec9172b 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -23,13 +23,34 @@ 
 #define  PORT0_INT				BIT(1)
 #define  PORT1_INT				BIT(2)
 #define  UCSI_READ_INT				BIT(7)
+#define CCGX_RAB_JUMP_TO_BOOT			0x0007
+#define  TO_BOOT				'J'
+#define  TO_ALT_FW				'A'
+#define CCGX_RAB_RESET_REQ			0x0008
+#define  RESET_SIG				'R'
+#define  CMD_RESET_I2C				0x0
+#define  CMD_RESET_DEV				0x1
+#define CCGX_RAB_ENTER_FLASHING			0x000A
+#define  FLASH_ENTER_SIG			'P'
+#define CCGX_RAB_VALIDATE_FW			0x000B
+#define CCGX_RAB_FLASH_ROW_RW			0x000C
+#define  FLASH_SIG				'F'
+#define  FLASH_RD_CMD				0x0
+#define  FLASH_WR_CMD				0x1
+#define  FLASH_FWCT1_WR_CMD			0x2
+#define  FLASH_FWCT2_WR_CMD			0x3
+#define  FLASH_FWCT_SIG_WR_CMD			0x4
 #define CCGX_RAB_READ_ALL_VER			0x0010
 #define CCGX_RAB_READ_FW2_VER			0x0020
 #define CCGX_RAB_UCSI_CONTROL			0x0039
 #define CCGX_RAB_UCSI_CONTROL_START		BIT(0)
 #define CCGX_RAB_UCSI_CONTROL_STOP		BIT(1)
 #define CCGX_RAB_UCSI_DATA_BLOCK(offset)	(0xf000 | ((offset) & 0xff))
+#define REG_FLASH_RW_MEM        0x0200
 #define DEV_REG_IDX				CCGX_RAB_DEVICE_MODE
+#define CCGX_RAB_PDPORT_ENABLE			0x002C
+#define  PDPORT_1		BIT(0)
+#define  PDPORT_2		BIT(1)
 #define CCGX_RAB_RESPONSE			0x007E
 #define  ASYNC_EVENT				BIT(7)
 
@@ -40,6 +61,13 @@ 
 #define PORT_DISCONNECT_DET	0x85
 #define ROLE_SWAP_COMPELETE	0x87
 
+/* ccg firmware */
+#define CYACD_LINE_SIZE         527
+#define CCG4_ROW_SIZE           256
+#define FW1_METADATA_ROW        0x1FF
+#define FW2_METADATA_ROW        0x1FE
+#define FW_CFG_TABLE_SIG_SIZE	256
+
 struct ccg_dev_info {
 #define CCG_DEVINFO_FWMODE_SHIFT (0)
 #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT)
@@ -173,6 +201,7 @@  struct ucsi_ccg {
 	struct ccg_resp dev_resp;
 	u8 cmd_resp;
 	int port_num;
+	struct mutex lock; /* to sync between user and driver thread */
 };
 
 static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
@@ -493,6 +522,193 @@  static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd)
 	return uc->cmd_resp;
 }
 
+static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc)
+{
+	struct ccg_cmd cmd;
+	int ret;
+
+	cmd.reg = CCGX_RAB_ENTER_FLASHING;
+	cmd.data = FLASH_ENTER_SIG;
+	cmd.len = 1;
+	cmd.delay = 50;
+
+	mutex_lock(&uc->lock);
+
+	ret = ccg_send_command(uc, &cmd);
+
+	mutex_unlock(&uc->lock);
+
+	if (ret != CMD_SUCCESS) {
+		dev_err(uc->dev, "enter flashing failed ret=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccg_cmd_reset(struct ucsi_ccg *uc, bool extra_delay)
+{
+	struct ccg_cmd cmd;
+	u8 *p;
+	int ret;
+
+	p = (u8 *)&cmd.data;
+	cmd.reg = CCGX_RAB_RESET_REQ;
+	p[0] = RESET_SIG;
+	p[1] = CMD_RESET_DEV;
+	cmd.len = 2;
+	cmd.delay = 2000 + (extra_delay ? 3000 : 0);
+
+	mutex_lock(&uc->lock);
+
+	set_bit(RESET_PENDING, &uc->flags);
+
+	ret = ccg_send_command(uc, &cmd);
+	if (ret != RESET_COMPLETE)
+		goto err_clear_flag;
+
+	ret = 0;
+
+err_clear_flag:
+	clear_bit(RESET_PENDING, &uc->flags);
+
+	mutex_unlock(&uc->lock);
+
+	return ret;
+}
+
+static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable)
+{
+	struct ccg_cmd cmd;
+	int ret;
+
+	cmd.reg = CCGX_RAB_PDPORT_ENABLE;
+	if (enable)
+		cmd.data = (uc->port_num == 1) ?
+			    PDPORT_1 : (PDPORT_1 | PDPORT_2);
+	else
+		cmd.data = 0x0;
+	cmd.len = 1;
+	cmd.delay = 10;
+
+	mutex_lock(&uc->lock);
+
+	ret = ccg_send_command(uc, &cmd);
+
+	mutex_unlock(&uc->lock);
+
+	if (ret != CMD_SUCCESS) {
+		dev_err(uc->dev, "port control failed ret=%d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode)
+{
+	struct ccg_cmd cmd;
+	int ret;
+
+	cmd.reg = CCGX_RAB_JUMP_TO_BOOT;
+
+	if (bl_mode)
+		cmd.data = TO_BOOT;
+	else
+		cmd.data = TO_ALT_FW;
+
+	cmd.len = 1;
+	cmd.delay = 100;
+
+	mutex_lock(&uc->lock);
+
+	set_bit(RESET_PENDING, &uc->flags);
+
+	ret = ccg_send_command(uc, &cmd);
+	if (ret != RESET_COMPLETE)
+		goto err_clear_flag;
+
+	ret = 0;
+
+err_clear_flag:
+	clear_bit(RESET_PENDING, &uc->flags);
+
+	mutex_unlock(&uc->lock);
+
+	return ret;
+}
+
+static int
+ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row,
+			const void *data, u8 fcmd)
+{
+	struct i2c_client *client = uc->client;
+	struct ccg_cmd cmd;
+	u8 buf[CCG4_ROW_SIZE + 2];
+	u8 *p;
+	int ret;
+
+	/* Copy the data into the flash read/write memory. */
+	buf[0] = REG_FLASH_RW_MEM & 0xFF;
+	buf[1] = REG_FLASH_RW_MEM >> 8;
+
+	memcpy(buf + 2, data, CCG4_ROW_SIZE);
+
+	mutex_lock(&uc->lock);
+
+	ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2);
+	if (ret != CCG4_ROW_SIZE + 2) {
+		dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	/* Use the FLASH_ROW_READ_WRITE register to trigger */
+	/* writing of data to the desired flash row */
+	p = (u8 *)&cmd.data;
+	cmd.reg = CCGX_RAB_FLASH_ROW_RW;
+	p[0] = FLASH_SIG;
+	p[1] = fcmd;
+	p[2] = row & 0xFF;
+	p[3] = row >> 8;
+	cmd.len = 4;
+	cmd.delay = 50;
+	if (fcmd == FLASH_FWCT_SIG_WR_CMD)
+		cmd.delay += 400;
+	if (row == 510)
+		cmd.delay += 220;
+	ret = ccg_send_command(uc, &cmd);
+
+	mutex_unlock(&uc->lock);
+
+	if (ret != CMD_SUCCESS) {
+		dev_err(uc->dev, "write flash row failed ret=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
+{
+	struct ccg_cmd cmd;
+	int ret;
+
+	cmd.reg = CCGX_RAB_VALIDATE_FW;
+	cmd.data = fwid;
+	cmd.len = 1;
+	cmd.delay = 500;
+
+	mutex_lock(&uc->lock);
+
+	ret = ccg_send_command(uc, &cmd);
+
+	mutex_unlock(&uc->lock);
+
+	if (ret != CMD_SUCCESS)
+		return ret;
+
+	return 0;
+}
+
 static int ucsi_ccg_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
 {