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