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 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 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 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 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 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 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 */ 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 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 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 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 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 6425767f400SSanket Goswami static int ccg_request_irq(struct ucsi_ccg *uc) 6435767f400SSanket Goswami { 6445767f400SSanket Goswami unsigned long flags = IRQF_ONESHOT; 6455767f400SSanket Goswami 6465767f400SSanket Goswami if (!has_acpi_companion(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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 ******************************************************************************/ 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 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 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 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 1341*d24182b1SUwe 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; 1345247c554aSAjay Gupta int status; 1346247c554aSAjay Gupta 1347247c554aSAjay Gupta uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); 1348247c554aSAjay Gupta if (!uc) 1349247c554aSAjay Gupta return -ENOMEM; 1350247c554aSAjay Gupta 1351247c554aSAjay Gupta uc->dev = dev; 1352247c554aSAjay Gupta uc->client = client; 13535767f400SSanket Goswami uc->irq = client->irq; 13545c9ae5a8SAjay Gupta mutex_init(&uc->lock); 1355e32fd989SHeikki Krogerus init_completion(&uc->complete); 13565c9ae5a8SAjay Gupta INIT_WORK(&uc->work, ccg_update_firmware); 1357f0e4cd94SAjay Gupta INIT_WORK(&uc->pm_work, ccg_pm_workaround_work); 13585c9ae5a8SAjay Gupta 13595c9ae5a8SAjay Gupta /* Only fail FW flashing when FW build information is not provided */ 13605c9ae5a8SAjay Gupta status = device_property_read_u16(dev, "ccgx,firmware-build", 13615c9ae5a8SAjay Gupta &uc->fw_build); 13625c9ae5a8SAjay Gupta if (status) 13635c9ae5a8SAjay Gupta dev_err(uc->dev, "failed to get FW build information\n"); 1364247c554aSAjay Gupta 1365247c554aSAjay Gupta /* reset ccg device and initialize ucsi */ 1366247c554aSAjay Gupta status = ucsi_ccg_init(uc); 1367247c554aSAjay Gupta if (status < 0) { 1368247c554aSAjay Gupta dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); 1369247c554aSAjay Gupta return status; 1370247c554aSAjay Gupta } 1371247c554aSAjay Gupta 13725d438e20SAjay Gupta status = get_fw_info(uc); 13735d438e20SAjay Gupta if (status < 0) { 13745d438e20SAjay Gupta dev_err(uc->dev, "get_fw_info failed - %d\n", status); 13755d438e20SAjay Gupta return status; 13765d438e20SAjay Gupta } 13775d438e20SAjay Gupta 13785c9ae5a8SAjay Gupta uc->port_num = 1; 13795c9ae5a8SAjay Gupta 13805c9ae5a8SAjay Gupta if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK) 13815c9ae5a8SAjay Gupta uc->port_num++; 13825c9ae5a8SAjay Gupta 1383e32fd989SHeikki Krogerus uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops); 1384e32fd989SHeikki Krogerus if (IS_ERR(uc->ucsi)) 1385e32fd989SHeikki Krogerus return PTR_ERR(uc->ucsi); 1386e32fd989SHeikki Krogerus 1387e32fd989SHeikki Krogerus ucsi_set_drvdata(uc->ucsi, uc); 1388e32fd989SHeikki Krogerus 13895767f400SSanket Goswami status = ccg_request_irq(uc); 1390247c554aSAjay Gupta if (status < 0) { 1391247c554aSAjay Gupta dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); 1392e32fd989SHeikki Krogerus goto out_ucsi_destroy; 1393247c554aSAjay Gupta } 1394247c554aSAjay Gupta 1395e32fd989SHeikki Krogerus status = ucsi_register(uc->ucsi); 1396e32fd989SHeikki Krogerus if (status) 1397e32fd989SHeikki Krogerus goto out_free_irq; 1398247c554aSAjay Gupta 1399247c554aSAjay Gupta i2c_set_clientdata(client, uc); 14005c9ae5a8SAjay Gupta 1401a94ecde4SAjay Gupta pm_runtime_set_active(uc->dev); 1402a94ecde4SAjay Gupta pm_runtime_enable(uc->dev); 14038530e4e2SHeikki Krogerus pm_runtime_use_autosuspend(uc->dev); 14048530e4e2SHeikki Krogerus pm_runtime_set_autosuspend_delay(uc->dev, 5000); 1405a94ecde4SAjay Gupta pm_runtime_idle(uc->dev); 1406a94ecde4SAjay Gupta 1407247c554aSAjay Gupta return 0; 1408e32fd989SHeikki Krogerus 1409e32fd989SHeikki Krogerus out_free_irq: 1410e32fd989SHeikki Krogerus free_irq(uc->irq, uc); 1411e32fd989SHeikki Krogerus out_ucsi_destroy: 1412e32fd989SHeikki Krogerus ucsi_destroy(uc->ucsi); 1413e32fd989SHeikki Krogerus 1414e32fd989SHeikki Krogerus return status; 1415247c554aSAjay Gupta } 1416247c554aSAjay Gupta 1417ed5c2f5fSUwe Kleine-König static void ucsi_ccg_remove(struct i2c_client *client) 1418247c554aSAjay Gupta { 1419247c554aSAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1420247c554aSAjay Gupta 1421f0e4cd94SAjay Gupta cancel_work_sync(&uc->pm_work); 14225c9ae5a8SAjay Gupta cancel_work_sync(&uc->work); 1423a94ecde4SAjay Gupta pm_runtime_disable(uc->dev); 1424e32fd989SHeikki Krogerus ucsi_unregister(uc->ucsi); 1425e32fd989SHeikki Krogerus ucsi_destroy(uc->ucsi); 14265c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 1427247c554aSAjay Gupta } 1428247c554aSAjay Gupta 1429247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = { 1430247c554aSAjay Gupta {"ccgx-ucsi", 0}, 1431247c554aSAjay Gupta {} 1432247c554aSAjay Gupta }; 1433247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); 1434247c554aSAjay Gupta 14355fd6c4f0SSanket Goswami static const struct acpi_device_id amd_i2c_ucsi_match[] = { 14365fd6c4f0SSanket Goswami {"AMDI0042"}, 14375fd6c4f0SSanket Goswami {} 14385fd6c4f0SSanket Goswami }; 14395fd6c4f0SSanket Goswami MODULE_DEVICE_TABLE(acpi, amd_i2c_ucsi_match); 14405fd6c4f0SSanket Goswami 1441a94ecde4SAjay Gupta static int ucsi_ccg_resume(struct device *dev) 1442a94ecde4SAjay Gupta { 1443a94ecde4SAjay Gupta struct i2c_client *client = to_i2c_client(dev); 1444a94ecde4SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1445a94ecde4SAjay Gupta 1446a94ecde4SAjay Gupta return ucsi_resume(uc->ucsi); 1447a94ecde4SAjay Gupta } 1448a94ecde4SAjay Gupta 1449a94ecde4SAjay Gupta static int ucsi_ccg_runtime_suspend(struct device *dev) 1450a94ecde4SAjay Gupta { 1451a94ecde4SAjay Gupta return 0; 1452a94ecde4SAjay Gupta } 1453a94ecde4SAjay Gupta 1454a94ecde4SAjay Gupta static int ucsi_ccg_runtime_resume(struct device *dev) 1455a94ecde4SAjay Gupta { 1456f0e4cd94SAjay Gupta struct i2c_client *client = to_i2c_client(dev); 1457f0e4cd94SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1458f0e4cd94SAjay Gupta 1459f0e4cd94SAjay Gupta /* 1460f0e4cd94SAjay Gupta * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue 1461f0e4cd94SAjay Gupta * of missing interrupt when a device is connected for runtime resume. 1462f0e4cd94SAjay Gupta * Schedule a work to call ISR as a workaround. 1463f0e4cd94SAjay Gupta */ 1464f0e4cd94SAjay Gupta if (uc->fw_build == CCG_FW_BUILD_NVIDIA && 14658530e4e2SHeikki Krogerus uc->fw_version <= CCG_OLD_FW_VERSION) 1466f0e4cd94SAjay Gupta schedule_work(&uc->pm_work); 1467f0e4cd94SAjay Gupta 1468a94ecde4SAjay Gupta return 0; 1469a94ecde4SAjay Gupta } 1470a94ecde4SAjay Gupta 1471a94ecde4SAjay Gupta static const struct dev_pm_ops ucsi_ccg_pm = { 1472a94ecde4SAjay Gupta .resume = ucsi_ccg_resume, 1473a94ecde4SAjay Gupta .runtime_suspend = ucsi_ccg_runtime_suspend, 1474a94ecde4SAjay Gupta .runtime_resume = ucsi_ccg_runtime_resume, 1475a94ecde4SAjay Gupta }; 1476a94ecde4SAjay Gupta 1477247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = { 1478247c554aSAjay Gupta .driver = { 1479247c554aSAjay Gupta .name = "ucsi_ccg", 1480a94ecde4SAjay Gupta .pm = &ucsi_ccg_pm, 14812e18b14eSGreg Kroah-Hartman .dev_groups = ucsi_ccg_groups, 14825fd6c4f0SSanket Goswami .acpi_match_table = amd_i2c_ucsi_match, 1483247c554aSAjay Gupta }, 1484*d24182b1SUwe Kleine-König .probe_new = ucsi_ccg_probe, 1485247c554aSAjay Gupta .remove = ucsi_ccg_remove, 1486247c554aSAjay Gupta .id_table = ucsi_ccg_device_id, 1487247c554aSAjay Gupta }; 1488247c554aSAjay Gupta 1489247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver); 1490247c554aSAjay Gupta 1491247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); 1492247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); 1493247c554aSAjay Gupta MODULE_LICENSE("GPL v2"); 1494