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 128706f4bbfSAjay Gupta /* Altmode offset for NVIDIA Function Test Board (FTB) */ 129706f4bbfSAjay Gupta #define NVIDIA_FTB_DP_OFFSET (2) 130706f4bbfSAjay Gupta #define NVIDIA_FTB_DBG_OFFSET (3) 131706f4bbfSAjay Gupta 1325d438e20SAjay Gupta struct version_info { 1335d438e20SAjay Gupta struct version_format base; 1345d438e20SAjay Gupta struct version_format app; 1355d438e20SAjay Gupta }; 1365d438e20SAjay Gupta 1375c9ae5a8SAjay Gupta struct fw_config_table { 1385c9ae5a8SAjay Gupta u32 identity; 1395c9ae5a8SAjay Gupta u16 table_size; 1405c9ae5a8SAjay Gupta u8 fwct_version; 1415c9ae5a8SAjay Gupta u8 is_key_change; 1425c9ae5a8SAjay Gupta u8 guid[16]; 1435c9ae5a8SAjay Gupta struct version_format base; 1445c9ae5a8SAjay Gupta struct version_format app; 1455c9ae5a8SAjay Gupta u8 primary_fw_digest[32]; 1465c9ae5a8SAjay Gupta u32 key_exp_length; 1475c9ae5a8SAjay Gupta u8 key_modulus[256]; 1485c9ae5a8SAjay Gupta u8 key_exp[4]; 1495c9ae5a8SAjay Gupta }; 1505c9ae5a8SAjay Gupta 1515c9ae5a8SAjay Gupta /* CCGx response codes */ 1525c9ae5a8SAjay Gupta enum ccg_resp_code { 1535c9ae5a8SAjay Gupta CMD_NO_RESP = 0x00, 1545c9ae5a8SAjay Gupta CMD_SUCCESS = 0x02, 1555c9ae5a8SAjay Gupta FLASH_DATA_AVAILABLE = 0x03, 1565c9ae5a8SAjay Gupta CMD_INVALID = 0x05, 1575c9ae5a8SAjay Gupta FLASH_UPDATE_FAIL = 0x07, 1585c9ae5a8SAjay Gupta INVALID_FW = 0x08, 1595c9ae5a8SAjay Gupta INVALID_ARG = 0x09, 1605c9ae5a8SAjay Gupta CMD_NOT_SUPPORT = 0x0A, 1615c9ae5a8SAjay Gupta TRANSACTION_FAIL = 0x0C, 1625c9ae5a8SAjay Gupta PD_CMD_FAIL = 0x0D, 1635c9ae5a8SAjay Gupta UNDEF_ERROR = 0x0F, 1645c9ae5a8SAjay Gupta INVALID_RESP = 0x10, 1655c9ae5a8SAjay Gupta }; 1665c9ae5a8SAjay Gupta 1675c9ae5a8SAjay Gupta #define CCG_EVENT_MAX (EVENT_INDEX + 43) 1685c9ae5a8SAjay Gupta 1695c9ae5a8SAjay Gupta struct ccg_cmd { 1705c9ae5a8SAjay Gupta u16 reg; 1715c9ae5a8SAjay Gupta u32 data; 1725c9ae5a8SAjay Gupta int len; 1735c9ae5a8SAjay Gupta u32 delay; /* ms delay for cmd timeout */ 1745c9ae5a8SAjay Gupta }; 1755c9ae5a8SAjay Gupta 1765c9ae5a8SAjay Gupta struct ccg_resp { 1775c9ae5a8SAjay Gupta u8 code; 1785c9ae5a8SAjay Gupta u8 length; 1795c9ae5a8SAjay Gupta }; 1805c9ae5a8SAjay Gupta 181170a6726SAjay Gupta struct ucsi_ccg_altmode { 182170a6726SAjay Gupta u16 svid; 183170a6726SAjay Gupta u32 mid; 184170a6726SAjay Gupta u8 linked_idx; 185170a6726SAjay Gupta u8 active_idx; 186170a6726SAjay Gupta #define UCSI_MULTI_DP_INDEX (0xff) 187170a6726SAjay Gupta bool checked; 188170a6726SAjay Gupta } __packed; 189170a6726SAjay Gupta 190247c554aSAjay Gupta struct ucsi_ccg { 191247c554aSAjay Gupta struct device *dev; 192247c554aSAjay Gupta struct ucsi *ucsi; 193247c554aSAjay Gupta struct i2c_client *client; 194e32fd989SHeikki Krogerus 1955d438e20SAjay Gupta struct ccg_dev_info info; 1965d438e20SAjay Gupta /* version info for boot, primary and secondary */ 1975d438e20SAjay Gupta struct version_info version[FW2 + 1]; 198f0e4cd94SAjay Gupta u32 fw_version; 1995c9ae5a8SAjay Gupta /* CCG HPI communication flags */ 2005c9ae5a8SAjay Gupta unsigned long flags; 2015c9ae5a8SAjay Gupta #define RESET_PENDING 0 2025c9ae5a8SAjay Gupta #define DEV_CMD_PENDING 1 2035c9ae5a8SAjay Gupta struct ccg_resp dev_resp; 2045c9ae5a8SAjay Gupta u8 cmd_resp; 2055c9ae5a8SAjay Gupta int port_num; 2065c9ae5a8SAjay Gupta int irq; 2075c9ae5a8SAjay Gupta struct work_struct work; 2085c9ae5a8SAjay Gupta struct mutex lock; /* to sync between user and driver thread */ 209247c554aSAjay Gupta 2105c9ae5a8SAjay Gupta /* fw build with vendor information */ 2115c9ae5a8SAjay Gupta u16 fw_build; 212f0e4cd94SAjay Gupta struct work_struct pm_work; 213e32fd989SHeikki Krogerus 214e32fd989SHeikki Krogerus struct completion complete; 215170a6726SAjay Gupta 216170a6726SAjay Gupta u64 last_cmd_sent; 217170a6726SAjay Gupta bool has_multiple_dp; 218170a6726SAjay Gupta struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES]; 219170a6726SAjay Gupta struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES]; 2205c9ae5a8SAjay Gupta }; 221247c554aSAjay Gupta 222247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 223247c554aSAjay Gupta { 224247c554aSAjay Gupta struct i2c_client *client = uc->client; 225247c554aSAjay Gupta const struct i2c_adapter_quirks *quirks = client->adapter->quirks; 226247c554aSAjay Gupta unsigned char buf[2]; 227247c554aSAjay Gupta struct i2c_msg msgs[] = { 228247c554aSAjay Gupta { 229247c554aSAjay Gupta .addr = client->addr, 230247c554aSAjay Gupta .flags = 0x0, 231247c554aSAjay Gupta .len = sizeof(buf), 232247c554aSAjay Gupta .buf = buf, 233247c554aSAjay Gupta }, 234247c554aSAjay Gupta { 235247c554aSAjay Gupta .addr = client->addr, 236247c554aSAjay Gupta .flags = I2C_M_RD, 237247c554aSAjay Gupta .buf = data, 238247c554aSAjay Gupta }, 239247c554aSAjay Gupta }; 240247c554aSAjay Gupta u32 rlen, rem_len = len, max_read_len = len; 241247c554aSAjay Gupta int status; 242247c554aSAjay Gupta 243247c554aSAjay Gupta /* check any max_read_len limitation on i2c adapter */ 244247c554aSAjay Gupta if (quirks && quirks->max_read_len) 245247c554aSAjay Gupta max_read_len = quirks->max_read_len; 246247c554aSAjay Gupta 247a94ecde4SAjay Gupta pm_runtime_get_sync(uc->dev); 248247c554aSAjay Gupta while (rem_len > 0) { 249247c554aSAjay Gupta msgs[1].buf = &data[len - rem_len]; 250247c554aSAjay Gupta rlen = min_t(u16, rem_len, max_read_len); 251247c554aSAjay Gupta msgs[1].len = rlen; 252247c554aSAjay Gupta put_unaligned_le16(rab, buf); 253247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 254247c554aSAjay Gupta if (status < 0) { 255247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 256a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 257247c554aSAjay Gupta return status; 258247c554aSAjay Gupta } 259247c554aSAjay Gupta rab += rlen; 260247c554aSAjay Gupta rem_len -= rlen; 261247c554aSAjay Gupta } 262247c554aSAjay Gupta 263a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 264247c554aSAjay Gupta return 0; 265247c554aSAjay Gupta } 266247c554aSAjay Gupta 267e32fd989SHeikki Krogerus static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len) 268247c554aSAjay Gupta { 269247c554aSAjay Gupta struct i2c_client *client = uc->client; 270247c554aSAjay Gupta unsigned char *buf; 271247c554aSAjay Gupta struct i2c_msg msgs[] = { 272247c554aSAjay Gupta { 273247c554aSAjay Gupta .addr = client->addr, 274247c554aSAjay Gupta .flags = 0x0, 275247c554aSAjay Gupta } 276247c554aSAjay Gupta }; 277247c554aSAjay Gupta int status; 278247c554aSAjay Gupta 279247c554aSAjay Gupta buf = kzalloc(len + sizeof(rab), GFP_KERNEL); 280247c554aSAjay Gupta if (!buf) 281247c554aSAjay Gupta return -ENOMEM; 282247c554aSAjay Gupta 283247c554aSAjay Gupta put_unaligned_le16(rab, buf); 284247c554aSAjay Gupta memcpy(buf + sizeof(rab), data, len); 285247c554aSAjay Gupta 286247c554aSAjay Gupta msgs[0].len = len + sizeof(rab); 287247c554aSAjay Gupta msgs[0].buf = buf; 288247c554aSAjay Gupta 289a94ecde4SAjay Gupta pm_runtime_get_sync(uc->dev); 290247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 291247c554aSAjay Gupta if (status < 0) { 292247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 293a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 294247c554aSAjay Gupta kfree(buf); 295247c554aSAjay Gupta return status; 296247c554aSAjay Gupta } 297247c554aSAjay Gupta 298a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 299247c554aSAjay Gupta kfree(buf); 300247c554aSAjay Gupta return 0; 301247c554aSAjay Gupta } 302247c554aSAjay Gupta 303247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc) 304247c554aSAjay Gupta { 305247c554aSAjay Gupta unsigned int count = 10; 306247c554aSAjay Gupta u8 data; 307247c554aSAjay Gupta int status; 308247c554aSAjay Gupta 309247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_STOP; 310247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 311247c554aSAjay Gupta if (status < 0) 312247c554aSAjay Gupta return status; 313247c554aSAjay Gupta 314247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_START; 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 /* 320247c554aSAjay Gupta * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control 321247c554aSAjay Gupta * register write will push response which must be cleared. 322247c554aSAjay Gupta */ 323247c554aSAjay Gupta do { 324247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 325247c554aSAjay Gupta if (status < 0) 326247c554aSAjay Gupta return status; 327247c554aSAjay Gupta 32882591149SSing-Han Chen if (!(data & DEV_INT)) 329247c554aSAjay Gupta return 0; 330247c554aSAjay Gupta 331247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 332247c554aSAjay Gupta if (status < 0) 333247c554aSAjay Gupta return status; 334247c554aSAjay Gupta 335247c554aSAjay Gupta usleep_range(10000, 11000); 336247c554aSAjay Gupta } while (--count); 337247c554aSAjay Gupta 338247c554aSAjay Gupta return -ETIMEDOUT; 339247c554aSAjay Gupta } 340247c554aSAjay Gupta 341170a6726SAjay Gupta static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data) 342170a6726SAjay Gupta { 343170a6726SAjay Gupta u8 cam, new_cam; 344170a6726SAjay Gupta 345170a6726SAjay Gupta cam = data[0]; 346170a6726SAjay Gupta new_cam = uc->orig[cam].linked_idx; 347170a6726SAjay Gupta uc->updated[new_cam].active_idx = cam; 348170a6726SAjay Gupta data[0] = new_cam; 349170a6726SAjay Gupta } 350170a6726SAjay Gupta 351170a6726SAjay Gupta static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi, 352170a6726SAjay Gupta struct ucsi_altmode *orig, 353170a6726SAjay Gupta struct ucsi_altmode *updated) 354170a6726SAjay Gupta { 355170a6726SAjay Gupta struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); 356170a6726SAjay Gupta struct ucsi_ccg_altmode *alt, *new_alt; 357170a6726SAjay Gupta int i, j, k = 0; 358170a6726SAjay Gupta bool found = false; 359170a6726SAjay Gupta 360170a6726SAjay Gupta alt = uc->orig; 361170a6726SAjay Gupta new_alt = uc->updated; 362170a6726SAjay Gupta memset(uc->updated, 0, sizeof(uc->updated)); 363170a6726SAjay Gupta 364170a6726SAjay Gupta /* 365170a6726SAjay Gupta * Copy original connector altmodes to new structure. 366170a6726SAjay Gupta * We need this before second loop since second loop 367170a6726SAjay Gupta * checks for duplicate altmodes. 368170a6726SAjay Gupta */ 369170a6726SAjay Gupta for (i = 0; i < UCSI_MAX_ALTMODES; i++) { 370170a6726SAjay Gupta alt[i].svid = orig[i].svid; 371170a6726SAjay Gupta alt[i].mid = orig[i].mid; 372170a6726SAjay Gupta if (!alt[i].svid) 373170a6726SAjay Gupta break; 374170a6726SAjay Gupta } 375170a6726SAjay Gupta 376170a6726SAjay Gupta for (i = 0; i < UCSI_MAX_ALTMODES; i++) { 377170a6726SAjay Gupta if (!alt[i].svid) 378170a6726SAjay Gupta break; 379170a6726SAjay Gupta 380170a6726SAjay Gupta /* already checked and considered */ 381170a6726SAjay Gupta if (alt[i].checked) 382170a6726SAjay Gupta continue; 383170a6726SAjay Gupta 384170a6726SAjay Gupta if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) { 385170a6726SAjay Gupta /* Found Non DP altmode */ 386170a6726SAjay Gupta new_alt[k].svid = alt[i].svid; 387170a6726SAjay Gupta new_alt[k].mid |= alt[i].mid; 388170a6726SAjay Gupta new_alt[k].linked_idx = i; 389170a6726SAjay Gupta alt[i].linked_idx = k; 390170a6726SAjay Gupta updated[k].svid = new_alt[k].svid; 391170a6726SAjay Gupta updated[k].mid = new_alt[k].mid; 392170a6726SAjay Gupta k++; 393170a6726SAjay Gupta continue; 394170a6726SAjay Gupta } 395170a6726SAjay Gupta 396170a6726SAjay Gupta for (j = i + 1; j < UCSI_MAX_ALTMODES; j++) { 397170a6726SAjay Gupta if (alt[i].svid != alt[j].svid || 398170a6726SAjay Gupta !DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) { 399170a6726SAjay Gupta continue; 400170a6726SAjay Gupta } else { 401170a6726SAjay Gupta /* Found duplicate DP mode */ 402170a6726SAjay Gupta new_alt[k].svid = alt[i].svid; 403170a6726SAjay Gupta new_alt[k].mid |= alt[i].mid | alt[j].mid; 404170a6726SAjay Gupta new_alt[k].linked_idx = UCSI_MULTI_DP_INDEX; 405170a6726SAjay Gupta alt[i].linked_idx = k; 406170a6726SAjay Gupta alt[j].linked_idx = k; 407170a6726SAjay Gupta alt[j].checked = true; 408170a6726SAjay Gupta found = true; 409170a6726SAjay Gupta } 410170a6726SAjay Gupta } 411170a6726SAjay Gupta if (found) { 412170a6726SAjay Gupta uc->has_multiple_dp = true; 413170a6726SAjay Gupta } else { 414170a6726SAjay Gupta /* Didn't find any duplicate DP altmode */ 415170a6726SAjay Gupta new_alt[k].svid = alt[i].svid; 416170a6726SAjay Gupta new_alt[k].mid |= alt[i].mid; 417170a6726SAjay Gupta new_alt[k].linked_idx = i; 418170a6726SAjay Gupta alt[i].linked_idx = k; 419170a6726SAjay Gupta } 420170a6726SAjay Gupta updated[k].svid = new_alt[k].svid; 421170a6726SAjay Gupta updated[k].mid = new_alt[k].mid; 422170a6726SAjay Gupta k++; 423170a6726SAjay Gupta } 424170a6726SAjay Gupta return found; 425170a6726SAjay Gupta } 426170a6726SAjay Gupta 427170a6726SAjay Gupta static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc, 428170a6726SAjay Gupta struct ucsi_connector *con, 429170a6726SAjay Gupta u64 *cmd) 430170a6726SAjay Gupta { 431170a6726SAjay Gupta struct ucsi_ccg_altmode *new_port, *port; 432170a6726SAjay Gupta struct typec_altmode *alt = NULL; 433170a6726SAjay Gupta u8 new_cam, cam, pin; 434170a6726SAjay Gupta bool enter_new_mode; 435170a6726SAjay Gupta int i, j, k = 0xff; 436170a6726SAjay Gupta 437170a6726SAjay Gupta port = uc->orig; 438170a6726SAjay Gupta new_cam = UCSI_SET_NEW_CAM_GET_AM(*cmd); 439170a6726SAjay Gupta new_port = &uc->updated[new_cam]; 440170a6726SAjay Gupta cam = new_port->linked_idx; 441170a6726SAjay Gupta enter_new_mode = UCSI_SET_NEW_CAM_ENTER(*cmd); 442170a6726SAjay Gupta 443170a6726SAjay Gupta /* 444170a6726SAjay Gupta * If CAM is UCSI_MULTI_DP_INDEX then this is DP altmode 445170a6726SAjay Gupta * with multiple DP mode. Find out CAM for best pin assignment 446170a6726SAjay Gupta * among all DP mode. Priorite pin E->D->C after making sure 447170a6726SAjay Gupta * the partner supports that pin. 448170a6726SAjay Gupta */ 449170a6726SAjay Gupta if (cam == UCSI_MULTI_DP_INDEX) { 450170a6726SAjay Gupta if (enter_new_mode) { 451170a6726SAjay Gupta for (i = 0; con->partner_altmode[i]; i++) { 452170a6726SAjay Gupta alt = con->partner_altmode[i]; 453170a6726SAjay Gupta if (alt->svid == new_port->svid) 454170a6726SAjay Gupta break; 455170a6726SAjay Gupta } 456170a6726SAjay Gupta /* 457170a6726SAjay Gupta * alt will always be non NULL since this is 458170a6726SAjay Gupta * UCSI_SET_NEW_CAM command and so there will be 459170a6726SAjay Gupta * at least one con->partner_altmode[i] with svid 460170a6726SAjay Gupta * matching with new_port->svid. 461170a6726SAjay Gupta */ 462170a6726SAjay Gupta for (j = 0; port[j].svid; j++) { 463170a6726SAjay Gupta pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid); 464170a6726SAjay Gupta if (alt && port[j].svid == alt->svid && 465170a6726SAjay Gupta (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) { 466170a6726SAjay Gupta /* prioritize pin E->D->C */ 467170a6726SAjay Gupta if (k == 0xff || (k != 0xff && pin > 468170a6726SAjay Gupta DP_CONF_GET_PIN_ASSIGN(port[k].mid)) 469170a6726SAjay Gupta ) { 470170a6726SAjay Gupta k = j; 471170a6726SAjay Gupta } 472170a6726SAjay Gupta } 473170a6726SAjay Gupta } 474170a6726SAjay Gupta cam = k; 475170a6726SAjay Gupta new_port->active_idx = cam; 476170a6726SAjay Gupta } else { 477170a6726SAjay Gupta cam = new_port->active_idx; 478170a6726SAjay Gupta } 479170a6726SAjay Gupta } 480170a6726SAjay Gupta *cmd &= ~UCSI_SET_NEW_CAM_AM_MASK; 481170a6726SAjay Gupta *cmd |= UCSI_SET_NEW_CAM_SET_AM(cam); 482170a6726SAjay Gupta } 483170a6726SAjay Gupta 484706f4bbfSAjay Gupta /* 485706f4bbfSAjay Gupta * Change the order of vdo values of NVIDIA test device FTB 486706f4bbfSAjay Gupta * (Function Test Board) which reports altmode list with vdo=0x3 487706f4bbfSAjay Gupta * first and then vdo=0x. Current logic to assign mode value is 488706f4bbfSAjay Gupta * based on order in altmode list and it causes a mismatch of CON 489706f4bbfSAjay Gupta * and SOP altmodes since NVIDIA GPU connector has order of vdo=0x1 490706f4bbfSAjay Gupta * first and then vdo=0x3 491706f4bbfSAjay Gupta */ 492706f4bbfSAjay Gupta static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc, 493706f4bbfSAjay Gupta struct ucsi_altmode *alt) 494706f4bbfSAjay Gupta { 495706f4bbfSAjay Gupta switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) { 496706f4bbfSAjay Gupta case NVIDIA_FTB_DP_OFFSET: 497706f4bbfSAjay Gupta if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO) 498706f4bbfSAjay Gupta alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO | 499706f4bbfSAjay Gupta DP_CAP_DP_SIGNALING | DP_CAP_USB | 500706f4bbfSAjay Gupta DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_E)); 501706f4bbfSAjay Gupta break; 502706f4bbfSAjay Gupta case NVIDIA_FTB_DBG_OFFSET: 503706f4bbfSAjay Gupta if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DP_VDO) 504706f4bbfSAjay Gupta alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DBG_VDO; 505706f4bbfSAjay Gupta break; 506706f4bbfSAjay Gupta default: 507706f4bbfSAjay Gupta break; 508706f4bbfSAjay Gupta } 509706f4bbfSAjay Gupta } 510706f4bbfSAjay Gupta 511e32fd989SHeikki Krogerus static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset, 512e32fd989SHeikki Krogerus void *val, size_t val_len) 513247c554aSAjay Gupta { 514170a6726SAjay Gupta struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); 515e32fd989SHeikki Krogerus u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); 516706f4bbfSAjay Gupta struct ucsi_altmode *alt; 517706f4bbfSAjay Gupta int ret; 518247c554aSAjay Gupta 519170a6726SAjay Gupta ret = ccg_read(uc, reg, val, val_len); 520170a6726SAjay Gupta if (ret) 521170a6726SAjay Gupta return ret; 522170a6726SAjay Gupta 523706f4bbfSAjay Gupta if (offset != UCSI_MESSAGE_IN) 524706f4bbfSAjay Gupta return ret; 525706f4bbfSAjay Gupta 526706f4bbfSAjay Gupta switch (UCSI_COMMAND(uc->last_cmd_sent)) { 527706f4bbfSAjay Gupta case UCSI_GET_CURRENT_CAM: 528706f4bbfSAjay Gupta if (uc->has_multiple_dp) 529170a6726SAjay Gupta ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val); 530706f4bbfSAjay Gupta break; 531706f4bbfSAjay Gupta case UCSI_GET_ALTERNATE_MODES: 532706f4bbfSAjay Gupta if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) == 533706f4bbfSAjay Gupta UCSI_RECIPIENT_SOP) { 534706f4bbfSAjay Gupta alt = val; 535706f4bbfSAjay Gupta if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID) 536706f4bbfSAjay Gupta ucsi_ccg_nvidia_altmode(uc, alt); 537706f4bbfSAjay Gupta } 538706f4bbfSAjay Gupta break; 539706f4bbfSAjay Gupta default: 540706f4bbfSAjay Gupta break; 541170a6726SAjay Gupta } 542170a6726SAjay Gupta uc->last_cmd_sent = 0; 543170a6726SAjay Gupta 544170a6726SAjay Gupta return ret; 545247c554aSAjay Gupta } 546247c554aSAjay Gupta 547e32fd989SHeikki Krogerus static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset, 548e32fd989SHeikki Krogerus const void *val, size_t val_len) 549247c554aSAjay Gupta { 550e32fd989SHeikki Krogerus u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); 551247c554aSAjay Gupta 552e32fd989SHeikki Krogerus return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len); 553247c554aSAjay Gupta } 554247c554aSAjay Gupta 555e32fd989SHeikki Krogerus static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset, 556e32fd989SHeikki Krogerus const void *val, size_t val_len) 557247c554aSAjay Gupta { 558e32fd989SHeikki Krogerus struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); 559170a6726SAjay Gupta struct ucsi_connector *con; 560170a6726SAjay Gupta int con_index; 561e32fd989SHeikki Krogerus int ret; 562247c554aSAjay Gupta 563e32fd989SHeikki Krogerus mutex_lock(&uc->lock); 564e32fd989SHeikki Krogerus pm_runtime_get_sync(uc->dev); 565e32fd989SHeikki Krogerus set_bit(DEV_CMD_PENDING, &uc->flags); 566247c554aSAjay Gupta 567170a6726SAjay Gupta if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) { 568170a6726SAjay Gupta uc->last_cmd_sent = *(u64 *)val; 569170a6726SAjay Gupta 570170a6726SAjay Gupta if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM && 571170a6726SAjay Gupta uc->has_multiple_dp) { 572170a6726SAjay Gupta con_index = (uc->last_cmd_sent >> 16) & 573170a6726SAjay Gupta UCSI_CMD_CONNECTOR_MASK; 574170a6726SAjay Gupta con = &uc->ucsi->connector[con_index - 1]; 575170a6726SAjay Gupta ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val); 576170a6726SAjay Gupta } 577170a6726SAjay Gupta } 578170a6726SAjay Gupta 579e32fd989SHeikki Krogerus ret = ucsi_ccg_async_write(ucsi, offset, val, val_len); 580e32fd989SHeikki Krogerus if (ret) 581e32fd989SHeikki Krogerus goto err_clear_bit; 582e32fd989SHeikki Krogerus 583e32fd989SHeikki Krogerus if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000))) 584e32fd989SHeikki Krogerus ret = -ETIMEDOUT; 585e32fd989SHeikki Krogerus 586e32fd989SHeikki Krogerus err_clear_bit: 587e32fd989SHeikki Krogerus clear_bit(DEV_CMD_PENDING, &uc->flags); 588e32fd989SHeikki Krogerus pm_runtime_put_sync(uc->dev); 589e32fd989SHeikki Krogerus mutex_unlock(&uc->lock); 590e32fd989SHeikki Krogerus 591e32fd989SHeikki Krogerus return ret; 592247c554aSAjay Gupta } 593247c554aSAjay Gupta 594e32fd989SHeikki Krogerus static const struct ucsi_operations ucsi_ccg_ops = { 595e32fd989SHeikki Krogerus .read = ucsi_ccg_read, 596e32fd989SHeikki Krogerus .sync_write = ucsi_ccg_sync_write, 597170a6726SAjay Gupta .async_write = ucsi_ccg_async_write, 598170a6726SAjay Gupta .update_altmodes = ucsi_ccg_update_altmodes 599e32fd989SHeikki Krogerus }; 600247c554aSAjay Gupta 601247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data) 602247c554aSAjay Gupta { 603e32fd989SHeikki Krogerus u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI); 604247c554aSAjay Gupta struct ucsi_ccg *uc = data; 605e32fd989SHeikki Krogerus u8 intr_reg; 606e32fd989SHeikki Krogerus u32 cci; 607e32fd989SHeikki Krogerus int ret; 608247c554aSAjay Gupta 609e32fd989SHeikki Krogerus ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg)); 610e32fd989SHeikki Krogerus if (ret) 611e32fd989SHeikki Krogerus return ret; 612e32fd989SHeikki Krogerus 613e32fd989SHeikki Krogerus ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci)); 614e32fd989SHeikki Krogerus if (ret) 615e32fd989SHeikki Krogerus goto err_clear_irq; 616e32fd989SHeikki Krogerus 617e32fd989SHeikki Krogerus if (UCSI_CCI_CONNECTOR(cci)) 618e32fd989SHeikki Krogerus ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci)); 619e32fd989SHeikki Krogerus 620e32fd989SHeikki Krogerus if (test_bit(DEV_CMD_PENDING, &uc->flags) && 621e32fd989SHeikki Krogerus cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) 622e32fd989SHeikki Krogerus complete(&uc->complete); 623e32fd989SHeikki Krogerus 624e32fd989SHeikki Krogerus err_clear_irq: 625e32fd989SHeikki Krogerus ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg)); 626247c554aSAjay Gupta 627247c554aSAjay Gupta return IRQ_HANDLED; 628247c554aSAjay Gupta } 629247c554aSAjay Gupta 6305767f400SSanket Goswami static int ccg_request_irq(struct ucsi_ccg *uc) 6315767f400SSanket Goswami { 6325767f400SSanket Goswami unsigned long flags = IRQF_ONESHOT; 6335767f400SSanket Goswami 6345767f400SSanket Goswami if (!has_acpi_companion(uc->dev)) 6355767f400SSanket Goswami flags |= IRQF_TRIGGER_HIGH; 6365767f400SSanket Goswami 6375767f400SSanket Goswami return request_threaded_irq(uc->irq, NULL, ccg_irq_handler, flags, dev_name(uc->dev), uc); 6385767f400SSanket Goswami } 6395767f400SSanket Goswami 640f0e4cd94SAjay Gupta static void ccg_pm_workaround_work(struct work_struct *pm_work) 641f0e4cd94SAjay Gupta { 642e32fd989SHeikki Krogerus ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work)); 643f0e4cd94SAjay Gupta } 644f0e4cd94SAjay Gupta 6455d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc) 6465d438e20SAjay Gupta { 6475d438e20SAjay Gupta int err; 6485d438e20SAjay Gupta 6495d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), 6505d438e20SAjay Gupta sizeof(uc->version)); 6515d438e20SAjay Gupta if (err < 0) 6525d438e20SAjay Gupta return err; 6535d438e20SAjay Gupta 654f0e4cd94SAjay Gupta uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | 655f0e4cd94SAjay Gupta CCG_VERSION_PATCH(uc->version[FW2].app.patch); 656f0e4cd94SAjay Gupta 6575d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 6585d438e20SAjay Gupta sizeof(uc->info)); 6595d438e20SAjay Gupta if (err < 0) 6605d438e20SAjay Gupta return err; 6615d438e20SAjay Gupta 6625d438e20SAjay Gupta return 0; 6635d438e20SAjay Gupta } 6645d438e20SAjay Gupta 6655c9ae5a8SAjay Gupta static inline bool invalid_async_evt(int code) 6665c9ae5a8SAjay Gupta { 6675c9ae5a8SAjay Gupta return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX); 6685c9ae5a8SAjay Gupta } 6695c9ae5a8SAjay Gupta 6705c9ae5a8SAjay Gupta static void ccg_process_response(struct ucsi_ccg *uc) 6715c9ae5a8SAjay Gupta { 6725c9ae5a8SAjay Gupta struct device *dev = uc->dev; 6735c9ae5a8SAjay Gupta 6745c9ae5a8SAjay Gupta if (uc->dev_resp.code & ASYNC_EVENT) { 6755c9ae5a8SAjay Gupta if (uc->dev_resp.code == RESET_COMPLETE) { 6765c9ae5a8SAjay Gupta if (test_bit(RESET_PENDING, &uc->flags)) 6775c9ae5a8SAjay Gupta uc->cmd_resp = uc->dev_resp.code; 6785c9ae5a8SAjay Gupta get_fw_info(uc); 6795c9ae5a8SAjay Gupta } 6805c9ae5a8SAjay Gupta if (invalid_async_evt(uc->dev_resp.code)) 6815c9ae5a8SAjay Gupta dev_err(dev, "invalid async evt %d\n", 6825c9ae5a8SAjay Gupta uc->dev_resp.code); 6835c9ae5a8SAjay Gupta } else { 6845c9ae5a8SAjay Gupta if (test_bit(DEV_CMD_PENDING, &uc->flags)) { 6855c9ae5a8SAjay Gupta uc->cmd_resp = uc->dev_resp.code; 6865c9ae5a8SAjay Gupta clear_bit(DEV_CMD_PENDING, &uc->flags); 6875c9ae5a8SAjay Gupta } else { 6885c9ae5a8SAjay Gupta dev_err(dev, "dev resp 0x%04x but no cmd pending\n", 6895c9ae5a8SAjay Gupta uc->dev_resp.code); 6905c9ae5a8SAjay Gupta } 6915c9ae5a8SAjay Gupta } 6925c9ae5a8SAjay Gupta } 6935c9ae5a8SAjay Gupta 6945c9ae5a8SAjay Gupta static int ccg_read_response(struct ucsi_ccg *uc) 6955c9ae5a8SAjay Gupta { 6965c9ae5a8SAjay Gupta unsigned long target = jiffies + msecs_to_jiffies(1000); 6975c9ae5a8SAjay Gupta struct device *dev = uc->dev; 6985c9ae5a8SAjay Gupta u8 intval; 6995c9ae5a8SAjay Gupta int status; 7005c9ae5a8SAjay Gupta 7015c9ae5a8SAjay Gupta /* wait for interrupt status to get updated */ 7025c9ae5a8SAjay Gupta do { 7035c9ae5a8SAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval, 7045c9ae5a8SAjay Gupta sizeof(intval)); 7055c9ae5a8SAjay Gupta if (status < 0) 7065c9ae5a8SAjay Gupta return status; 7075c9ae5a8SAjay Gupta 7085c9ae5a8SAjay Gupta if (intval & DEV_INT) 7095c9ae5a8SAjay Gupta break; 7105c9ae5a8SAjay Gupta usleep_range(500, 600); 7115c9ae5a8SAjay Gupta } while (time_is_after_jiffies(target)); 7125c9ae5a8SAjay Gupta 7135c9ae5a8SAjay Gupta if (time_is_before_jiffies(target)) { 7145c9ae5a8SAjay Gupta dev_err(dev, "response timeout error\n"); 7155c9ae5a8SAjay Gupta return -ETIME; 7165c9ae5a8SAjay Gupta } 7175c9ae5a8SAjay Gupta 7185c9ae5a8SAjay Gupta status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp, 7195c9ae5a8SAjay Gupta sizeof(uc->dev_resp)); 7205c9ae5a8SAjay Gupta if (status < 0) 7215c9ae5a8SAjay Gupta return status; 7225c9ae5a8SAjay Gupta 7235c9ae5a8SAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval)); 7245c9ae5a8SAjay Gupta if (status < 0) 7255c9ae5a8SAjay Gupta return status; 7265c9ae5a8SAjay Gupta 7275c9ae5a8SAjay Gupta return 0; 7285c9ae5a8SAjay Gupta } 7295c9ae5a8SAjay Gupta 7305c9ae5a8SAjay Gupta /* Caller must hold uc->lock */ 7315c9ae5a8SAjay Gupta static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd) 7325c9ae5a8SAjay Gupta { 7335c9ae5a8SAjay Gupta struct device *dev = uc->dev; 7345c9ae5a8SAjay Gupta int ret; 7355c9ae5a8SAjay Gupta 7365c9ae5a8SAjay Gupta switch (cmd->reg & 0xF000) { 7375c9ae5a8SAjay Gupta case DEV_REG_IDX: 7385c9ae5a8SAjay Gupta set_bit(DEV_CMD_PENDING, &uc->flags); 7395c9ae5a8SAjay Gupta break; 7405c9ae5a8SAjay Gupta default: 7415c9ae5a8SAjay Gupta dev_err(dev, "invalid cmd register\n"); 7425c9ae5a8SAjay Gupta break; 7435c9ae5a8SAjay Gupta } 7445c9ae5a8SAjay Gupta 7455c9ae5a8SAjay Gupta ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len); 7465c9ae5a8SAjay Gupta if (ret < 0) 7475c9ae5a8SAjay Gupta return ret; 7485c9ae5a8SAjay Gupta 7495c9ae5a8SAjay Gupta msleep(cmd->delay); 7505c9ae5a8SAjay Gupta 7515c9ae5a8SAjay Gupta ret = ccg_read_response(uc); 7525c9ae5a8SAjay Gupta if (ret < 0) { 7535c9ae5a8SAjay Gupta dev_err(dev, "response read error\n"); 7545c9ae5a8SAjay Gupta switch (cmd->reg & 0xF000) { 7555c9ae5a8SAjay Gupta case DEV_REG_IDX: 7565c9ae5a8SAjay Gupta clear_bit(DEV_CMD_PENDING, &uc->flags); 7575c9ae5a8SAjay Gupta break; 7585c9ae5a8SAjay Gupta default: 7595c9ae5a8SAjay Gupta dev_err(dev, "invalid cmd register\n"); 7605c9ae5a8SAjay Gupta break; 7615c9ae5a8SAjay Gupta } 7625c9ae5a8SAjay Gupta return -EIO; 7635c9ae5a8SAjay Gupta } 7645c9ae5a8SAjay Gupta ccg_process_response(uc); 7655c9ae5a8SAjay Gupta 7665c9ae5a8SAjay Gupta return uc->cmd_resp; 7675c9ae5a8SAjay Gupta } 7685c9ae5a8SAjay Gupta 7695c9ae5a8SAjay Gupta static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc) 7705c9ae5a8SAjay Gupta { 7715c9ae5a8SAjay Gupta struct ccg_cmd cmd; 7725c9ae5a8SAjay Gupta int ret; 7735c9ae5a8SAjay Gupta 7745c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_ENTER_FLASHING; 7755c9ae5a8SAjay Gupta cmd.data = FLASH_ENTER_SIG; 7765c9ae5a8SAjay Gupta cmd.len = 1; 7775c9ae5a8SAjay Gupta cmd.delay = 50; 7785c9ae5a8SAjay Gupta 7795c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 7805c9ae5a8SAjay Gupta 7815c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 7825c9ae5a8SAjay Gupta 7835c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 7845c9ae5a8SAjay Gupta 7855c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 7865c9ae5a8SAjay Gupta dev_err(uc->dev, "enter flashing failed ret=%d\n", ret); 7875c9ae5a8SAjay Gupta return ret; 7885c9ae5a8SAjay Gupta } 7895c9ae5a8SAjay Gupta 7905c9ae5a8SAjay Gupta return 0; 7915c9ae5a8SAjay Gupta } 7925c9ae5a8SAjay Gupta 7935c9ae5a8SAjay Gupta static int ccg_cmd_reset(struct ucsi_ccg *uc) 7945c9ae5a8SAjay Gupta { 7955c9ae5a8SAjay Gupta struct ccg_cmd cmd; 7965c9ae5a8SAjay Gupta u8 *p; 7975c9ae5a8SAjay Gupta int ret; 7985c9ae5a8SAjay Gupta 7995c9ae5a8SAjay Gupta p = (u8 *)&cmd.data; 8005c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_RESET_REQ; 8015c9ae5a8SAjay Gupta p[0] = RESET_SIG; 8025c9ae5a8SAjay Gupta p[1] = CMD_RESET_DEV; 8035c9ae5a8SAjay Gupta cmd.len = 2; 8045c9ae5a8SAjay Gupta cmd.delay = 5000; 8055c9ae5a8SAjay Gupta 8065c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 8075c9ae5a8SAjay Gupta 8085c9ae5a8SAjay Gupta set_bit(RESET_PENDING, &uc->flags); 8095c9ae5a8SAjay Gupta 8105c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 8115c9ae5a8SAjay Gupta if (ret != RESET_COMPLETE) 8125c9ae5a8SAjay Gupta goto err_clear_flag; 8135c9ae5a8SAjay Gupta 8145c9ae5a8SAjay Gupta ret = 0; 8155c9ae5a8SAjay Gupta 8165c9ae5a8SAjay Gupta err_clear_flag: 8175c9ae5a8SAjay Gupta clear_bit(RESET_PENDING, &uc->flags); 8185c9ae5a8SAjay Gupta 8195c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 8205c9ae5a8SAjay Gupta 8215c9ae5a8SAjay Gupta return ret; 8225c9ae5a8SAjay Gupta } 8235c9ae5a8SAjay Gupta 8245c9ae5a8SAjay Gupta static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable) 8255c9ae5a8SAjay Gupta { 8265c9ae5a8SAjay Gupta struct ccg_cmd cmd; 8275c9ae5a8SAjay Gupta int ret; 8285c9ae5a8SAjay Gupta 8295c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_PDPORT_ENABLE; 8305c9ae5a8SAjay Gupta if (enable) 8315c9ae5a8SAjay Gupta cmd.data = (uc->port_num == 1) ? 8325c9ae5a8SAjay Gupta PDPORT_1 : (PDPORT_1 | PDPORT_2); 8335c9ae5a8SAjay Gupta else 8345c9ae5a8SAjay Gupta cmd.data = 0x0; 8355c9ae5a8SAjay Gupta cmd.len = 1; 8365c9ae5a8SAjay Gupta cmd.delay = 10; 8375c9ae5a8SAjay Gupta 8385c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 8395c9ae5a8SAjay Gupta 8405c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 8415c9ae5a8SAjay Gupta 8425c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 8435c9ae5a8SAjay Gupta 8445c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 8455c9ae5a8SAjay Gupta dev_err(uc->dev, "port control failed ret=%d\n", ret); 8465c9ae5a8SAjay Gupta return ret; 8475c9ae5a8SAjay Gupta } 8485c9ae5a8SAjay Gupta return 0; 8495c9ae5a8SAjay Gupta } 8505c9ae5a8SAjay Gupta 8515c9ae5a8SAjay Gupta static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode) 8525c9ae5a8SAjay Gupta { 8535c9ae5a8SAjay Gupta struct ccg_cmd cmd; 8545c9ae5a8SAjay Gupta int ret; 8555c9ae5a8SAjay Gupta 8565c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_JUMP_TO_BOOT; 8575c9ae5a8SAjay Gupta 8585c9ae5a8SAjay Gupta if (bl_mode) 8595c9ae5a8SAjay Gupta cmd.data = TO_BOOT; 8605c9ae5a8SAjay Gupta else 8615c9ae5a8SAjay Gupta cmd.data = TO_ALT_FW; 8625c9ae5a8SAjay Gupta 8635c9ae5a8SAjay Gupta cmd.len = 1; 8645c9ae5a8SAjay Gupta cmd.delay = 100; 8655c9ae5a8SAjay Gupta 8665c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 8675c9ae5a8SAjay Gupta 8685c9ae5a8SAjay Gupta set_bit(RESET_PENDING, &uc->flags); 8695c9ae5a8SAjay Gupta 8705c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 8715c9ae5a8SAjay Gupta if (ret != RESET_COMPLETE) 8725c9ae5a8SAjay Gupta goto err_clear_flag; 8735c9ae5a8SAjay Gupta 8745c9ae5a8SAjay Gupta ret = 0; 8755c9ae5a8SAjay Gupta 8765c9ae5a8SAjay Gupta err_clear_flag: 8775c9ae5a8SAjay Gupta clear_bit(RESET_PENDING, &uc->flags); 8785c9ae5a8SAjay Gupta 8795c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 8805c9ae5a8SAjay Gupta 8815c9ae5a8SAjay Gupta return ret; 8825c9ae5a8SAjay Gupta } 8835c9ae5a8SAjay Gupta 8845c9ae5a8SAjay Gupta static int 8855c9ae5a8SAjay Gupta ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row, 8865c9ae5a8SAjay Gupta const void *data, u8 fcmd) 8875c9ae5a8SAjay Gupta { 8885c9ae5a8SAjay Gupta struct i2c_client *client = uc->client; 8895c9ae5a8SAjay Gupta struct ccg_cmd cmd; 8905c9ae5a8SAjay Gupta u8 buf[CCG4_ROW_SIZE + 2]; 8915c9ae5a8SAjay Gupta u8 *p; 8925c9ae5a8SAjay Gupta int ret; 8935c9ae5a8SAjay Gupta 8945c9ae5a8SAjay Gupta /* Copy the data into the flash read/write memory. */ 8955c9ae5a8SAjay Gupta put_unaligned_le16(REG_FLASH_RW_MEM, buf); 8965c9ae5a8SAjay Gupta 8975c9ae5a8SAjay Gupta memcpy(buf + 2, data, CCG4_ROW_SIZE); 8985c9ae5a8SAjay Gupta 8995c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 9005c9ae5a8SAjay Gupta 9015c9ae5a8SAjay Gupta ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2); 9025c9ae5a8SAjay Gupta if (ret != CCG4_ROW_SIZE + 2) { 9035c9ae5a8SAjay Gupta dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret); 904c2d18126SWei Yongjun mutex_unlock(&uc->lock); 9055c9ae5a8SAjay Gupta return ret < 0 ? ret : -EIO; 9065c9ae5a8SAjay Gupta } 9075c9ae5a8SAjay Gupta 9085c9ae5a8SAjay Gupta /* Use the FLASH_ROW_READ_WRITE register to trigger */ 9095c9ae5a8SAjay Gupta /* writing of data to the desired flash row */ 9105c9ae5a8SAjay Gupta p = (u8 *)&cmd.data; 9115c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_FLASH_ROW_RW; 9125c9ae5a8SAjay Gupta p[0] = FLASH_SIG; 9135c9ae5a8SAjay Gupta p[1] = fcmd; 9145c9ae5a8SAjay Gupta put_unaligned_le16(row, &p[2]); 9155c9ae5a8SAjay Gupta cmd.len = 4; 9165c9ae5a8SAjay Gupta cmd.delay = 50; 9175c9ae5a8SAjay Gupta if (fcmd == FLASH_FWCT_SIG_WR_CMD) 9185c9ae5a8SAjay Gupta cmd.delay += 400; 9195c9ae5a8SAjay Gupta if (row == 510) 9205c9ae5a8SAjay Gupta cmd.delay += 220; 9215c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 9225c9ae5a8SAjay Gupta 9235c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 9245c9ae5a8SAjay Gupta 9255c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 9265c9ae5a8SAjay Gupta dev_err(uc->dev, "write flash row failed ret=%d\n", ret); 9275c9ae5a8SAjay Gupta return ret; 9285c9ae5a8SAjay Gupta } 9295c9ae5a8SAjay Gupta 9305c9ae5a8SAjay Gupta return 0; 9315c9ae5a8SAjay Gupta } 9325c9ae5a8SAjay Gupta 9335c9ae5a8SAjay Gupta static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid) 9345c9ae5a8SAjay Gupta { 9355c9ae5a8SAjay Gupta struct ccg_cmd cmd; 9365c9ae5a8SAjay Gupta int ret; 9375c9ae5a8SAjay Gupta 9385c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_VALIDATE_FW; 9395c9ae5a8SAjay Gupta cmd.data = fwid; 9405c9ae5a8SAjay Gupta cmd.len = 1; 9415c9ae5a8SAjay Gupta cmd.delay = 500; 9425c9ae5a8SAjay Gupta 9435c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 9445c9ae5a8SAjay Gupta 9455c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 9465c9ae5a8SAjay Gupta 9475c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 9485c9ae5a8SAjay Gupta 9495c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) 9505c9ae5a8SAjay Gupta return ret; 9515c9ae5a8SAjay Gupta 9525c9ae5a8SAjay Gupta return 0; 9535c9ae5a8SAjay Gupta } 9545c9ae5a8SAjay Gupta 9555c9ae5a8SAjay Gupta static bool ccg_check_vendor_version(struct ucsi_ccg *uc, 9565c9ae5a8SAjay Gupta struct version_format *app, 9575c9ae5a8SAjay Gupta struct fw_config_table *fw_cfg) 9585c9ae5a8SAjay Gupta { 9595c9ae5a8SAjay Gupta struct device *dev = uc->dev; 9605c9ae5a8SAjay Gupta 9615c9ae5a8SAjay Gupta /* Check if the fw build is for supported vendors */ 9625c9ae5a8SAjay Gupta if (le16_to_cpu(app->build) != uc->fw_build) { 9635c9ae5a8SAjay Gupta dev_info(dev, "current fw is not from supported vendor\n"); 9645c9ae5a8SAjay Gupta return false; 9655c9ae5a8SAjay Gupta } 9665c9ae5a8SAjay Gupta 9675c9ae5a8SAjay Gupta /* Check if the new fw build is for supported vendors */ 9685c9ae5a8SAjay Gupta if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) { 9695c9ae5a8SAjay Gupta dev_info(dev, "new fw is not from supported vendor\n"); 9705c9ae5a8SAjay Gupta return false; 9715c9ae5a8SAjay Gupta } 9725c9ae5a8SAjay Gupta return true; 9735c9ae5a8SAjay Gupta } 9745c9ae5a8SAjay Gupta 9755c9ae5a8SAjay Gupta static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, 9765c9ae5a8SAjay Gupta struct version_format *app) 9775c9ae5a8SAjay Gupta { 9785c9ae5a8SAjay Gupta const struct firmware *fw = NULL; 9795c9ae5a8SAjay Gupta struct device *dev = uc->dev; 9805c9ae5a8SAjay Gupta struct fw_config_table fw_cfg; 9815c9ae5a8SAjay Gupta u32 cur_version, new_version; 9825c9ae5a8SAjay Gupta bool is_later = false; 9835c9ae5a8SAjay Gupta 9845c9ae5a8SAjay Gupta if (request_firmware(&fw, fw_name, dev) != 0) { 9855c9ae5a8SAjay Gupta dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name); 9865c9ae5a8SAjay Gupta return false; 9875c9ae5a8SAjay Gupta } 9885c9ae5a8SAjay Gupta 9895c9ae5a8SAjay Gupta /* 9905c9ae5a8SAjay Gupta * check if signed fw 9915c9ae5a8SAjay Gupta * last part of fw image is fw cfg table and signature 9925c9ae5a8SAjay Gupta */ 9935c9ae5a8SAjay Gupta if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE) 9945c9ae5a8SAjay Gupta goto out_release_firmware; 9955c9ae5a8SAjay Gupta 9965c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - 9975c9ae5a8SAjay Gupta sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg)); 9985c9ae5a8SAjay Gupta 9995c9ae5a8SAjay Gupta if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) { 10005c9ae5a8SAjay Gupta dev_info(dev, "not a signed image\n"); 10015c9ae5a8SAjay Gupta goto out_release_firmware; 10025c9ae5a8SAjay Gupta } 10035c9ae5a8SAjay Gupta 10045c9ae5a8SAjay Gupta /* compare input version with FWCT version */ 1005f0e4cd94SAjay Gupta cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) | 1006f0e4cd94SAjay Gupta CCG_VERSION(app->ver); 10075c9ae5a8SAjay Gupta 1008f0e4cd94SAjay Gupta new_version = le16_to_cpu(fw_cfg.app.build) | 1009f0e4cd94SAjay Gupta CCG_VERSION_PATCH(fw_cfg.app.patch) | 1010f0e4cd94SAjay Gupta CCG_VERSION(fw_cfg.app.ver); 10115c9ae5a8SAjay Gupta 10125c9ae5a8SAjay Gupta if (!ccg_check_vendor_version(uc, app, &fw_cfg)) 10135c9ae5a8SAjay Gupta goto out_release_firmware; 10145c9ae5a8SAjay Gupta 10155c9ae5a8SAjay Gupta if (new_version > cur_version) 10165c9ae5a8SAjay Gupta is_later = true; 10175c9ae5a8SAjay Gupta 10185c9ae5a8SAjay Gupta out_release_firmware: 10195c9ae5a8SAjay Gupta release_firmware(fw); 10205c9ae5a8SAjay Gupta return is_later; 10215c9ae5a8SAjay Gupta } 10225c9ae5a8SAjay Gupta 10235c9ae5a8SAjay Gupta static int ccg_fw_update_needed(struct ucsi_ccg *uc, 10245c9ae5a8SAjay Gupta enum enum_flash_mode *mode) 10255c9ae5a8SAjay Gupta { 10265c9ae5a8SAjay Gupta struct device *dev = uc->dev; 10275c9ae5a8SAjay Gupta int err; 10285c9ae5a8SAjay Gupta struct version_info version[3]; 10295c9ae5a8SAjay Gupta 10305c9ae5a8SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 10315c9ae5a8SAjay Gupta sizeof(uc->info)); 10325c9ae5a8SAjay Gupta if (err) { 10335c9ae5a8SAjay Gupta dev_err(dev, "read device mode failed\n"); 10345c9ae5a8SAjay Gupta return err; 10355c9ae5a8SAjay Gupta } 10365c9ae5a8SAjay Gupta 10375c9ae5a8SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version, 10385c9ae5a8SAjay Gupta sizeof(version)); 10395c9ae5a8SAjay Gupta if (err) { 10405c9ae5a8SAjay Gupta dev_err(dev, "read device mode failed\n"); 10415c9ae5a8SAjay Gupta return err; 10425c9ae5a8SAjay Gupta } 10435c9ae5a8SAjay Gupta 10445c9ae5a8SAjay Gupta if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0", 10455c9ae5a8SAjay Gupta sizeof(struct version_info)) == 0) { 10465c9ae5a8SAjay Gupta dev_info(dev, "secondary fw is not flashed\n"); 10475c9ae5a8SAjay Gupta *mode = SECONDARY_BL; 10485c9ae5a8SAjay Gupta } else if (le16_to_cpu(version[FW1].base.build) < 10495c9ae5a8SAjay Gupta secondary_fw_min_ver) { 10505c9ae5a8SAjay Gupta dev_info(dev, "secondary fw version is too low (< %d)\n", 10515c9ae5a8SAjay Gupta secondary_fw_min_ver); 10525c9ae5a8SAjay Gupta *mode = SECONDARY; 10535c9ae5a8SAjay Gupta } else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0", 10545c9ae5a8SAjay Gupta sizeof(struct version_info)) == 0) { 10555c9ae5a8SAjay Gupta dev_info(dev, "primary fw is not flashed\n"); 10565c9ae5a8SAjay Gupta *mode = PRIMARY; 10575c9ae5a8SAjay Gupta } else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY], 10585c9ae5a8SAjay Gupta &version[FW2].app)) { 10595c9ae5a8SAjay Gupta dev_info(dev, "found primary fw with later version\n"); 10605c9ae5a8SAjay Gupta *mode = PRIMARY; 10615c9ae5a8SAjay Gupta } else { 10625c9ae5a8SAjay Gupta dev_info(dev, "secondary and primary fw are the latest\n"); 10635c9ae5a8SAjay Gupta *mode = FLASH_NOT_NEEDED; 10645c9ae5a8SAjay Gupta } 10655c9ae5a8SAjay Gupta return 0; 10665c9ae5a8SAjay Gupta } 10675c9ae5a8SAjay Gupta 10685c9ae5a8SAjay Gupta static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) 10695c9ae5a8SAjay Gupta { 10705c9ae5a8SAjay Gupta struct device *dev = uc->dev; 10715c9ae5a8SAjay Gupta const struct firmware *fw = NULL; 10725c9ae5a8SAjay Gupta const char *p, *s; 10735c9ae5a8SAjay Gupta const char *eof; 10745c9ae5a8SAjay Gupta int err, row, len, line_sz, line_cnt = 0; 10755c9ae5a8SAjay Gupta unsigned long start_time = jiffies; 10765c9ae5a8SAjay Gupta struct fw_config_table fw_cfg; 10775c9ae5a8SAjay Gupta u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE]; 10785c9ae5a8SAjay Gupta u8 *wr_buf; 10795c9ae5a8SAjay Gupta 10805c9ae5a8SAjay Gupta err = request_firmware(&fw, ccg_fw_names[mode], dev); 10815c9ae5a8SAjay Gupta if (err) { 10825c9ae5a8SAjay Gupta dev_err(dev, "request %s failed err=%d\n", 10835c9ae5a8SAjay Gupta ccg_fw_names[mode], err); 10845c9ae5a8SAjay Gupta return err; 10855c9ae5a8SAjay Gupta } 10865c9ae5a8SAjay Gupta 10875c9ae5a8SAjay Gupta if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >> 10885c9ae5a8SAjay Gupta CCG_DEVINFO_FWMODE_SHIFT) == FW2) { 10895c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, false); 10905c9ae5a8SAjay Gupta if (err < 0) 10915c9ae5a8SAjay Gupta goto release_fw; 10925c9ae5a8SAjay Gupta err = ccg_cmd_jump_boot_mode(uc, 0); 10935c9ae5a8SAjay Gupta if (err < 0) 10945c9ae5a8SAjay Gupta goto release_fw; 10955c9ae5a8SAjay Gupta } 10965c9ae5a8SAjay Gupta 10975c9ae5a8SAjay Gupta eof = fw->data + fw->size; 10985c9ae5a8SAjay Gupta 10995c9ae5a8SAjay Gupta /* 11005c9ae5a8SAjay Gupta * check if signed fw 11015c9ae5a8SAjay Gupta * last part of fw image is fw cfg table and signature 11025c9ae5a8SAjay Gupta */ 11035c9ae5a8SAjay Gupta if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig)) 11045c9ae5a8SAjay Gupta goto not_signed_fw; 11055c9ae5a8SAjay Gupta 11065c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - 11075c9ae5a8SAjay Gupta sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg)); 11085c9ae5a8SAjay Gupta 11095c9ae5a8SAjay Gupta if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) { 11105c9ae5a8SAjay Gupta dev_info(dev, "not a signed image\n"); 11115c9ae5a8SAjay Gupta goto not_signed_fw; 11125c9ae5a8SAjay Gupta } 11135c9ae5a8SAjay Gupta eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig); 11145c9ae5a8SAjay Gupta 11155c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg_sig, 11165c9ae5a8SAjay Gupta fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig)); 11175c9ae5a8SAjay Gupta 11185c9ae5a8SAjay Gupta /* flash fw config table and signature first */ 11195c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg, 11205c9ae5a8SAjay Gupta FLASH_FWCT1_WR_CMD); 11215c9ae5a8SAjay Gupta if (err) 11225c9ae5a8SAjay Gupta goto release_fw; 11235c9ae5a8SAjay Gupta 11245c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE, 11255c9ae5a8SAjay Gupta FLASH_FWCT2_WR_CMD); 11265c9ae5a8SAjay Gupta if (err) 11275c9ae5a8SAjay Gupta goto release_fw; 11285c9ae5a8SAjay Gupta 11295c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig, 11305c9ae5a8SAjay Gupta FLASH_FWCT_SIG_WR_CMD); 11315c9ae5a8SAjay Gupta if (err) 11325c9ae5a8SAjay Gupta goto release_fw; 11335c9ae5a8SAjay Gupta 11345c9ae5a8SAjay Gupta not_signed_fw: 11355c9ae5a8SAjay Gupta wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL); 11362649939aSGustavo A. R. Silva if (!wr_buf) { 11372649939aSGustavo A. R. Silva err = -ENOMEM; 11382649939aSGustavo A. R. Silva goto release_fw; 11392649939aSGustavo A. R. Silva } 11405c9ae5a8SAjay Gupta 11415c9ae5a8SAjay Gupta err = ccg_cmd_enter_flashing(uc); 11425c9ae5a8SAjay Gupta if (err) 11435c9ae5a8SAjay Gupta goto release_mem; 11445c9ae5a8SAjay Gupta 11455c9ae5a8SAjay Gupta /***************************************************************** 11465c9ae5a8SAjay Gupta * CCG firmware image (.cyacd) file line format 11475c9ae5a8SAjay Gupta * 11485c9ae5a8SAjay Gupta * :00rrrrllll[dd....]cc/r/n 11495c9ae5a8SAjay Gupta * 11505c9ae5a8SAjay Gupta * :00 header 11515c9ae5a8SAjay Gupta * rrrr is row number to flash (4 char) 11525c9ae5a8SAjay Gupta * llll is data len to flash (4 char) 11535c9ae5a8SAjay Gupta * dd is a data field represents one byte of data (512 char) 11545c9ae5a8SAjay Gupta * cc is checksum (2 char) 11555c9ae5a8SAjay Gupta * \r\n newline 11565c9ae5a8SAjay Gupta * 11575c9ae5a8SAjay Gupta * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527 11585c9ae5a8SAjay Gupta * 11595c9ae5a8SAjay Gupta *****************************************************************/ 11605c9ae5a8SAjay Gupta 11615c9ae5a8SAjay Gupta p = strnchr(fw->data, fw->size, ':'); 11625c9ae5a8SAjay Gupta while (p < eof) { 11635c9ae5a8SAjay Gupta s = strnchr(p + 1, eof - p - 1, ':'); 11645c9ae5a8SAjay Gupta 11655c9ae5a8SAjay Gupta if (!s) 11665c9ae5a8SAjay Gupta s = eof; 11675c9ae5a8SAjay Gupta 11685c9ae5a8SAjay Gupta line_sz = s - p; 11695c9ae5a8SAjay Gupta 11705c9ae5a8SAjay Gupta if (line_sz != CYACD_LINE_SIZE) { 11715c9ae5a8SAjay Gupta dev_err(dev, "Bad FW format line_sz=%d\n", line_sz); 11725c9ae5a8SAjay Gupta err = -EINVAL; 11735c9ae5a8SAjay Gupta goto release_mem; 11745c9ae5a8SAjay Gupta } 11755c9ae5a8SAjay Gupta 11765c9ae5a8SAjay Gupta if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) { 11775c9ae5a8SAjay Gupta err = -EINVAL; 11785c9ae5a8SAjay Gupta goto release_mem; 11795c9ae5a8SAjay Gupta } 11805c9ae5a8SAjay Gupta 11815c9ae5a8SAjay Gupta row = get_unaligned_be16(wr_buf); 11825c9ae5a8SAjay Gupta len = get_unaligned_be16(&wr_buf[2]); 11835c9ae5a8SAjay Gupta 11845c9ae5a8SAjay Gupta if (len != CCG4_ROW_SIZE) { 11855c9ae5a8SAjay Gupta err = -EINVAL; 11865c9ae5a8SAjay Gupta goto release_mem; 11875c9ae5a8SAjay Gupta } 11885c9ae5a8SAjay Gupta 11895c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4, 11905c9ae5a8SAjay Gupta FLASH_WR_CMD); 11915c9ae5a8SAjay Gupta if (err) 11925c9ae5a8SAjay Gupta goto release_mem; 11935c9ae5a8SAjay Gupta 11945c9ae5a8SAjay Gupta line_cnt++; 11955c9ae5a8SAjay Gupta p = s; 11965c9ae5a8SAjay Gupta } 11975c9ae5a8SAjay Gupta 11985c9ae5a8SAjay Gupta dev_info(dev, "total %d row flashed. time: %dms\n", 11995c9ae5a8SAjay Gupta line_cnt, jiffies_to_msecs(jiffies - start_time)); 12005c9ae5a8SAjay Gupta 12015c9ae5a8SAjay Gupta err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 : FW1); 12025c9ae5a8SAjay Gupta if (err) 12035c9ae5a8SAjay Gupta dev_err(dev, "%s validation failed err=%d\n", 12045c9ae5a8SAjay Gupta (mode == PRIMARY) ? "FW2" : "FW1", err); 12055c9ae5a8SAjay Gupta else 12065c9ae5a8SAjay Gupta dev_info(dev, "%s validated\n", 12075c9ae5a8SAjay Gupta (mode == PRIMARY) ? "FW2" : "FW1"); 12085c9ae5a8SAjay Gupta 12095c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, false); 12105c9ae5a8SAjay Gupta if (err < 0) 12115c9ae5a8SAjay Gupta goto release_mem; 12125c9ae5a8SAjay Gupta 12135c9ae5a8SAjay Gupta err = ccg_cmd_reset(uc); 12145c9ae5a8SAjay Gupta if (err < 0) 12155c9ae5a8SAjay Gupta goto release_mem; 12165c9ae5a8SAjay Gupta 12175c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, true); 12185c9ae5a8SAjay Gupta if (err < 0) 12195c9ae5a8SAjay Gupta goto release_mem; 12205c9ae5a8SAjay Gupta 12215c9ae5a8SAjay Gupta release_mem: 12225c9ae5a8SAjay Gupta kfree(wr_buf); 12235c9ae5a8SAjay Gupta 12245c9ae5a8SAjay Gupta release_fw: 12255c9ae5a8SAjay Gupta release_firmware(fw); 12265c9ae5a8SAjay Gupta return err; 12275c9ae5a8SAjay Gupta } 12285c9ae5a8SAjay Gupta 12295c9ae5a8SAjay Gupta /******************************************************************************* 12305c9ae5a8SAjay Gupta * CCG4 has two copies of the firmware in addition to the bootloader. 12315c9ae5a8SAjay Gupta * If the device is running FW1, FW2 can be updated with the new version. 12325c9ae5a8SAjay Gupta * Dual firmware mode allows the CCG device to stay in a PD contract and support 12335c9ae5a8SAjay Gupta * USB PD and Type-C functionality while a firmware update is in progress. 12345c9ae5a8SAjay Gupta ******************************************************************************/ 12355c9ae5a8SAjay Gupta static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode) 12365c9ae5a8SAjay Gupta { 1237a29d56c2SHeikki Krogerus int err = 0; 12385c9ae5a8SAjay Gupta 12395c9ae5a8SAjay Gupta while (flash_mode != FLASH_NOT_NEEDED) { 12405c9ae5a8SAjay Gupta err = do_flash(uc, flash_mode); 12415c9ae5a8SAjay Gupta if (err < 0) 12425c9ae5a8SAjay Gupta return err; 12435c9ae5a8SAjay Gupta err = ccg_fw_update_needed(uc, &flash_mode); 12445c9ae5a8SAjay Gupta if (err < 0) 12455c9ae5a8SAjay Gupta return err; 12465c9ae5a8SAjay Gupta } 12475c9ae5a8SAjay Gupta dev_info(uc->dev, "CCG FW update successful\n"); 12485c9ae5a8SAjay Gupta 12495c9ae5a8SAjay Gupta return err; 12505c9ae5a8SAjay Gupta } 12515c9ae5a8SAjay Gupta 12525c9ae5a8SAjay Gupta static int ccg_restart(struct ucsi_ccg *uc) 12535c9ae5a8SAjay Gupta { 12545c9ae5a8SAjay Gupta struct device *dev = uc->dev; 12555c9ae5a8SAjay Gupta int status; 12565c9ae5a8SAjay Gupta 12575c9ae5a8SAjay Gupta status = ucsi_ccg_init(uc); 12585c9ae5a8SAjay Gupta if (status < 0) { 12595c9ae5a8SAjay Gupta dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status); 12605c9ae5a8SAjay Gupta return status; 12615c9ae5a8SAjay Gupta } 12625c9ae5a8SAjay Gupta 12635767f400SSanket Goswami status = ccg_request_irq(uc); 12645c9ae5a8SAjay Gupta if (status < 0) { 12655c9ae5a8SAjay Gupta dev_err(dev, "request_threaded_irq failed - %d\n", status); 12665c9ae5a8SAjay Gupta return status; 12675c9ae5a8SAjay Gupta } 12685c9ae5a8SAjay Gupta 1269e32fd989SHeikki Krogerus status = ucsi_register(uc->ucsi); 1270e32fd989SHeikki Krogerus if (status) { 1271e32fd989SHeikki Krogerus dev_err(uc->dev, "failed to register the interface\n"); 1272e32fd989SHeikki Krogerus return status; 12735c9ae5a8SAjay Gupta } 12745c9ae5a8SAjay Gupta 127557a5e5f9SAjay Gupta pm_runtime_enable(uc->dev); 12765c9ae5a8SAjay Gupta return 0; 12775c9ae5a8SAjay Gupta } 12785c9ae5a8SAjay Gupta 12795c9ae5a8SAjay Gupta static void ccg_update_firmware(struct work_struct *work) 12805c9ae5a8SAjay Gupta { 12815c9ae5a8SAjay Gupta struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work); 12825c9ae5a8SAjay Gupta enum enum_flash_mode flash_mode; 12835c9ae5a8SAjay Gupta int status; 12845c9ae5a8SAjay Gupta 12855c9ae5a8SAjay Gupta status = ccg_fw_update_needed(uc, &flash_mode); 12865c9ae5a8SAjay Gupta if (status < 0) 12875c9ae5a8SAjay Gupta return; 12885c9ae5a8SAjay Gupta 12895c9ae5a8SAjay Gupta if (flash_mode != FLASH_NOT_NEEDED) { 1290e32fd989SHeikki Krogerus ucsi_unregister(uc->ucsi); 129157a5e5f9SAjay Gupta pm_runtime_disable(uc->dev); 12925c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 12935c9ae5a8SAjay Gupta 12945c9ae5a8SAjay Gupta ccg_fw_update(uc, flash_mode); 12955c9ae5a8SAjay Gupta ccg_restart(uc); 12965c9ae5a8SAjay Gupta } 12975c9ae5a8SAjay Gupta } 12985c9ae5a8SAjay Gupta 12995c9ae5a8SAjay Gupta static ssize_t do_flash_store(struct device *dev, 13005c9ae5a8SAjay Gupta struct device_attribute *attr, 13015c9ae5a8SAjay Gupta const char *buf, size_t n) 13025c9ae5a8SAjay Gupta { 13035c9ae5a8SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev)); 13045c9ae5a8SAjay Gupta bool flash; 13055c9ae5a8SAjay Gupta 13065c9ae5a8SAjay Gupta if (kstrtobool(buf, &flash)) 13075c9ae5a8SAjay Gupta return -EINVAL; 13085c9ae5a8SAjay Gupta 13095c9ae5a8SAjay Gupta if (!flash) 13105c9ae5a8SAjay Gupta return n; 13115c9ae5a8SAjay Gupta 13125c9ae5a8SAjay Gupta if (uc->fw_build == 0x0) { 13135c9ae5a8SAjay Gupta dev_err(dev, "fail to flash FW due to missing FW build info\n"); 13145c9ae5a8SAjay Gupta return -EINVAL; 13155c9ae5a8SAjay Gupta } 13165c9ae5a8SAjay Gupta 13175c9ae5a8SAjay Gupta schedule_work(&uc->work); 13185c9ae5a8SAjay Gupta return n; 13195c9ae5a8SAjay Gupta } 13205c9ae5a8SAjay Gupta 13215c9ae5a8SAjay Gupta static DEVICE_ATTR_WO(do_flash); 13225c9ae5a8SAjay Gupta 13232e18b14eSGreg Kroah-Hartman static struct attribute *ucsi_ccg_attrs[] = { 13245c9ae5a8SAjay Gupta &dev_attr_do_flash.attr, 13255c9ae5a8SAjay Gupta NULL, 13265c9ae5a8SAjay Gupta }; 13272e18b14eSGreg Kroah-Hartman ATTRIBUTE_GROUPS(ucsi_ccg); 13285c9ae5a8SAjay Gupta 1329247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client, 1330247c554aSAjay Gupta const struct i2c_device_id *id) 1331247c554aSAjay Gupta { 1332247c554aSAjay Gupta struct device *dev = &client->dev; 1333247c554aSAjay Gupta struct ucsi_ccg *uc; 1334247c554aSAjay Gupta int status; 1335247c554aSAjay Gupta 1336247c554aSAjay Gupta uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); 1337247c554aSAjay Gupta if (!uc) 1338247c554aSAjay Gupta return -ENOMEM; 1339247c554aSAjay Gupta 1340247c554aSAjay Gupta uc->dev = dev; 1341247c554aSAjay Gupta uc->client = client; 13425767f400SSanket Goswami uc->irq = client->irq; 13435c9ae5a8SAjay Gupta mutex_init(&uc->lock); 1344e32fd989SHeikki Krogerus init_completion(&uc->complete); 13455c9ae5a8SAjay Gupta INIT_WORK(&uc->work, ccg_update_firmware); 1346f0e4cd94SAjay Gupta INIT_WORK(&uc->pm_work, ccg_pm_workaround_work); 13475c9ae5a8SAjay Gupta 13485c9ae5a8SAjay Gupta /* Only fail FW flashing when FW build information is not provided */ 13495c9ae5a8SAjay Gupta status = device_property_read_u16(dev, "ccgx,firmware-build", 13505c9ae5a8SAjay Gupta &uc->fw_build); 13515c9ae5a8SAjay Gupta if (status) 13525c9ae5a8SAjay Gupta dev_err(uc->dev, "failed to get FW build information\n"); 1353247c554aSAjay Gupta 1354247c554aSAjay Gupta /* reset ccg device and initialize ucsi */ 1355247c554aSAjay Gupta status = ucsi_ccg_init(uc); 1356247c554aSAjay Gupta if (status < 0) { 1357247c554aSAjay Gupta dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); 1358247c554aSAjay Gupta return status; 1359247c554aSAjay Gupta } 1360247c554aSAjay Gupta 13615d438e20SAjay Gupta status = get_fw_info(uc); 13625d438e20SAjay Gupta if (status < 0) { 13635d438e20SAjay Gupta dev_err(uc->dev, "get_fw_info failed - %d\n", status); 13645d438e20SAjay Gupta return status; 13655d438e20SAjay Gupta } 13665d438e20SAjay Gupta 13675c9ae5a8SAjay Gupta uc->port_num = 1; 13685c9ae5a8SAjay Gupta 13695c9ae5a8SAjay Gupta if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK) 13705c9ae5a8SAjay Gupta uc->port_num++; 13715c9ae5a8SAjay Gupta 1372e32fd989SHeikki Krogerus uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops); 1373e32fd989SHeikki Krogerus if (IS_ERR(uc->ucsi)) 1374e32fd989SHeikki Krogerus return PTR_ERR(uc->ucsi); 1375e32fd989SHeikki Krogerus 1376e32fd989SHeikki Krogerus ucsi_set_drvdata(uc->ucsi, uc); 1377e32fd989SHeikki Krogerus 13785767f400SSanket Goswami status = ccg_request_irq(uc); 1379247c554aSAjay Gupta if (status < 0) { 1380247c554aSAjay Gupta dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); 1381e32fd989SHeikki Krogerus goto out_ucsi_destroy; 1382247c554aSAjay Gupta } 1383247c554aSAjay Gupta 1384e32fd989SHeikki Krogerus status = ucsi_register(uc->ucsi); 1385e32fd989SHeikki Krogerus if (status) 1386e32fd989SHeikki Krogerus goto out_free_irq; 1387247c554aSAjay Gupta 1388247c554aSAjay Gupta i2c_set_clientdata(client, uc); 13895c9ae5a8SAjay Gupta 1390a94ecde4SAjay Gupta pm_runtime_set_active(uc->dev); 1391a94ecde4SAjay Gupta pm_runtime_enable(uc->dev); 13928530e4e2SHeikki Krogerus pm_runtime_use_autosuspend(uc->dev); 13938530e4e2SHeikki Krogerus pm_runtime_set_autosuspend_delay(uc->dev, 5000); 1394a94ecde4SAjay Gupta pm_runtime_idle(uc->dev); 1395a94ecde4SAjay Gupta 1396247c554aSAjay Gupta return 0; 1397e32fd989SHeikki Krogerus 1398e32fd989SHeikki Krogerus out_free_irq: 1399e32fd989SHeikki Krogerus free_irq(uc->irq, uc); 1400e32fd989SHeikki Krogerus out_ucsi_destroy: 1401e32fd989SHeikki Krogerus ucsi_destroy(uc->ucsi); 1402e32fd989SHeikki Krogerus 1403e32fd989SHeikki Krogerus return status; 1404247c554aSAjay Gupta } 1405247c554aSAjay Gupta 1406247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client) 1407247c554aSAjay Gupta { 1408247c554aSAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1409247c554aSAjay Gupta 1410f0e4cd94SAjay Gupta cancel_work_sync(&uc->pm_work); 14115c9ae5a8SAjay Gupta cancel_work_sync(&uc->work); 1412a94ecde4SAjay Gupta pm_runtime_disable(uc->dev); 1413e32fd989SHeikki Krogerus ucsi_unregister(uc->ucsi); 1414e32fd989SHeikki Krogerus ucsi_destroy(uc->ucsi); 14155c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 1416247c554aSAjay Gupta 1417247c554aSAjay Gupta return 0; 1418247c554aSAjay Gupta } 1419247c554aSAjay Gupta 1420247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = { 1421247c554aSAjay Gupta {"ccgx-ucsi", 0}, 1422247c554aSAjay Gupta {} 1423247c554aSAjay Gupta }; 1424247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); 1425247c554aSAjay Gupta 1426*5fd6c4f0SSanket Goswami static const struct acpi_device_id amd_i2c_ucsi_match[] = { 1427*5fd6c4f0SSanket Goswami {"AMDI0042"}, 1428*5fd6c4f0SSanket Goswami {} 1429*5fd6c4f0SSanket Goswami }; 1430*5fd6c4f0SSanket Goswami MODULE_DEVICE_TABLE(acpi, amd_i2c_ucsi_match); 1431*5fd6c4f0SSanket Goswami 1432a94ecde4SAjay Gupta static int ucsi_ccg_resume(struct device *dev) 1433a94ecde4SAjay Gupta { 1434a94ecde4SAjay Gupta struct i2c_client *client = to_i2c_client(dev); 1435a94ecde4SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1436a94ecde4SAjay Gupta 1437a94ecde4SAjay Gupta return ucsi_resume(uc->ucsi); 1438a94ecde4SAjay Gupta } 1439a94ecde4SAjay Gupta 1440a94ecde4SAjay Gupta static int ucsi_ccg_runtime_suspend(struct device *dev) 1441a94ecde4SAjay Gupta { 1442a94ecde4SAjay Gupta return 0; 1443a94ecde4SAjay Gupta } 1444a94ecde4SAjay Gupta 1445a94ecde4SAjay Gupta static int ucsi_ccg_runtime_resume(struct device *dev) 1446a94ecde4SAjay Gupta { 1447f0e4cd94SAjay Gupta struct i2c_client *client = to_i2c_client(dev); 1448f0e4cd94SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1449f0e4cd94SAjay Gupta 1450f0e4cd94SAjay Gupta /* 1451f0e4cd94SAjay Gupta * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue 1452f0e4cd94SAjay Gupta * of missing interrupt when a device is connected for runtime resume. 1453f0e4cd94SAjay Gupta * Schedule a work to call ISR as a workaround. 1454f0e4cd94SAjay Gupta */ 1455f0e4cd94SAjay Gupta if (uc->fw_build == CCG_FW_BUILD_NVIDIA && 14568530e4e2SHeikki Krogerus uc->fw_version <= CCG_OLD_FW_VERSION) 1457f0e4cd94SAjay Gupta schedule_work(&uc->pm_work); 1458f0e4cd94SAjay Gupta 1459a94ecde4SAjay Gupta return 0; 1460a94ecde4SAjay Gupta } 1461a94ecde4SAjay Gupta 1462a94ecde4SAjay Gupta static const struct dev_pm_ops ucsi_ccg_pm = { 1463a94ecde4SAjay Gupta .resume = ucsi_ccg_resume, 1464a94ecde4SAjay Gupta .runtime_suspend = ucsi_ccg_runtime_suspend, 1465a94ecde4SAjay Gupta .runtime_resume = ucsi_ccg_runtime_resume, 1466a94ecde4SAjay Gupta }; 1467a94ecde4SAjay Gupta 1468247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = { 1469247c554aSAjay Gupta .driver = { 1470247c554aSAjay Gupta .name = "ucsi_ccg", 1471a94ecde4SAjay Gupta .pm = &ucsi_ccg_pm, 14722e18b14eSGreg Kroah-Hartman .dev_groups = ucsi_ccg_groups, 1473*5fd6c4f0SSanket Goswami .acpi_match_table = amd_i2c_ucsi_match, 1474247c554aSAjay Gupta }, 1475247c554aSAjay Gupta .probe = ucsi_ccg_probe, 1476247c554aSAjay Gupta .remove = ucsi_ccg_remove, 1477247c554aSAjay Gupta .id_table = ucsi_ccg_device_id, 1478247c554aSAjay Gupta }; 1479247c554aSAjay Gupta 1480247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver); 1481247c554aSAjay Gupta 1482247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); 1483247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); 1484247c554aSAjay Gupta MODULE_LICENSE("GPL v2"); 1485