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> 19247c554aSAjay Gupta 20247c554aSAjay Gupta #include <asm/unaligned.h> 21247c554aSAjay Gupta #include "ucsi.h" 22247c554aSAjay Gupta 235d438e20SAjay Gupta enum enum_fw_mode { 245d438e20SAjay Gupta BOOT, /* bootloader */ 255d438e20SAjay Gupta FW1, /* FW partition-1 (contains secondary fw) */ 265d438e20SAjay Gupta FW2, /* FW partition-2 (contains primary fw) */ 275d438e20SAjay Gupta FW_INVALID, 285d438e20SAjay Gupta }; 295d438e20SAjay Gupta 305c9ae5a8SAjay Gupta #define CCGX_RAB_DEVICE_MODE 0x0000 315c9ae5a8SAjay Gupta #define CCGX_RAB_INTR_REG 0x0006 325c9ae5a8SAjay Gupta #define DEV_INT BIT(0) 335c9ae5a8SAjay Gupta #define PORT0_INT BIT(1) 345c9ae5a8SAjay Gupta #define PORT1_INT BIT(2) 355c9ae5a8SAjay Gupta #define UCSI_READ_INT BIT(7) 365c9ae5a8SAjay Gupta #define CCGX_RAB_JUMP_TO_BOOT 0x0007 375c9ae5a8SAjay Gupta #define TO_BOOT 'J' 385c9ae5a8SAjay Gupta #define TO_ALT_FW 'A' 395c9ae5a8SAjay Gupta #define CCGX_RAB_RESET_REQ 0x0008 405c9ae5a8SAjay Gupta #define RESET_SIG 'R' 415c9ae5a8SAjay Gupta #define CMD_RESET_I2C 0x0 425c9ae5a8SAjay Gupta #define CMD_RESET_DEV 0x1 435c9ae5a8SAjay Gupta #define CCGX_RAB_ENTER_FLASHING 0x000A 445c9ae5a8SAjay Gupta #define FLASH_ENTER_SIG 'P' 455c9ae5a8SAjay Gupta #define CCGX_RAB_VALIDATE_FW 0x000B 465c9ae5a8SAjay Gupta #define CCGX_RAB_FLASH_ROW_RW 0x000C 475c9ae5a8SAjay Gupta #define FLASH_SIG 'F' 485c9ae5a8SAjay Gupta #define FLASH_RD_CMD 0x0 495c9ae5a8SAjay Gupta #define FLASH_WR_CMD 0x1 505c9ae5a8SAjay Gupta #define FLASH_FWCT1_WR_CMD 0x2 515c9ae5a8SAjay Gupta #define FLASH_FWCT2_WR_CMD 0x3 525c9ae5a8SAjay Gupta #define FLASH_FWCT_SIG_WR_CMD 0x4 535c9ae5a8SAjay Gupta #define CCGX_RAB_READ_ALL_VER 0x0010 545c9ae5a8SAjay Gupta #define CCGX_RAB_READ_FW2_VER 0x0020 555c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL 0x0039 565c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START BIT(0) 575c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) 585c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) 595c9ae5a8SAjay Gupta #define REG_FLASH_RW_MEM 0x0200 605c9ae5a8SAjay Gupta #define DEV_REG_IDX CCGX_RAB_DEVICE_MODE 615c9ae5a8SAjay Gupta #define CCGX_RAB_PDPORT_ENABLE 0x002C 625c9ae5a8SAjay Gupta #define PDPORT_1 BIT(0) 635c9ae5a8SAjay Gupta #define PDPORT_2 BIT(1) 645c9ae5a8SAjay Gupta #define CCGX_RAB_RESPONSE 0x007E 655c9ae5a8SAjay Gupta #define ASYNC_EVENT BIT(7) 665c9ae5a8SAjay Gupta 675c9ae5a8SAjay Gupta /* CCGx events & async msg codes */ 685c9ae5a8SAjay Gupta #define RESET_COMPLETE 0x80 695c9ae5a8SAjay Gupta #define EVENT_INDEX RESET_COMPLETE 705c9ae5a8SAjay Gupta #define PORT_CONNECT_DET 0x84 715c9ae5a8SAjay Gupta #define PORT_DISCONNECT_DET 0x85 725c9ae5a8SAjay Gupta #define ROLE_SWAP_COMPELETE 0x87 735c9ae5a8SAjay Gupta 745c9ae5a8SAjay Gupta /* ccg firmware */ 755c9ae5a8SAjay Gupta #define CYACD_LINE_SIZE 527 765c9ae5a8SAjay Gupta #define CCG4_ROW_SIZE 256 775c9ae5a8SAjay Gupta #define FW1_METADATA_ROW 0x1FF 785c9ae5a8SAjay Gupta #define FW2_METADATA_ROW 0x1FE 795c9ae5a8SAjay Gupta #define FW_CFG_TABLE_SIG_SIZE 256 805c9ae5a8SAjay Gupta 815c9ae5a8SAjay Gupta static int secondary_fw_min_ver = 41; 825c9ae5a8SAjay Gupta 835c9ae5a8SAjay Gupta enum enum_flash_mode { 845c9ae5a8SAjay Gupta SECONDARY_BL, /* update secondary using bootloader */ 855c9ae5a8SAjay Gupta PRIMARY, /* update primary using secondary */ 865c9ae5a8SAjay Gupta SECONDARY, /* update secondary using primary */ 875c9ae5a8SAjay Gupta FLASH_NOT_NEEDED, /* update not required */ 885c9ae5a8SAjay Gupta FLASH_INVALID, 895c9ae5a8SAjay Gupta }; 905c9ae5a8SAjay Gupta 915c9ae5a8SAjay Gupta static const char * const ccg_fw_names[] = { 925c9ae5a8SAjay Gupta "ccg_boot.cyacd", 935c9ae5a8SAjay Gupta "ccg_primary.cyacd", 945c9ae5a8SAjay Gupta "ccg_secondary.cyacd" 955c9ae5a8SAjay Gupta }; 965c9ae5a8SAjay Gupta 975d438e20SAjay Gupta struct ccg_dev_info { 985d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0) 995d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) 1005d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2) 1015d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) 1025d438e20SAjay Gupta u8 mode; 1035d438e20SAjay Gupta u8 bl_mode; 1045d438e20SAjay Gupta __le16 silicon_id; 1055d438e20SAjay Gupta __le16 bl_last_row; 1065d438e20SAjay Gupta } __packed; 1075d438e20SAjay Gupta 1085d438e20SAjay Gupta struct version_format { 1095d438e20SAjay Gupta __le16 build; 1105d438e20SAjay Gupta u8 patch; 1115d438e20SAjay Gupta u8 ver; 112f0e4cd94SAjay Gupta #define CCG_VERSION_PATCH(x) ((x) << 16) 113f0e4cd94SAjay Gupta #define CCG_VERSION(x) ((x) << 24) 1145d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0) 1155d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) 1165d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4) 1175d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) 1185d438e20SAjay Gupta } __packed; 1195d438e20SAjay Gupta 120f0e4cd94SAjay Gupta /* 121f0e4cd94SAjay Gupta * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue 122f0e4cd94SAjay Gupta * of missing interrupt when a device is connected for runtime resume 123f0e4cd94SAjay Gupta */ 124f0e4cd94SAjay Gupta #define CCG_FW_BUILD_NVIDIA (('n' << 8) | 'v') 125f0e4cd94SAjay Gupta #define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10)) 126f0e4cd94SAjay Gupta 1275d438e20SAjay Gupta struct version_info { 1285d438e20SAjay Gupta struct version_format base; 1295d438e20SAjay Gupta struct version_format app; 1305d438e20SAjay Gupta }; 1315d438e20SAjay Gupta 1325c9ae5a8SAjay Gupta struct fw_config_table { 1335c9ae5a8SAjay Gupta u32 identity; 1345c9ae5a8SAjay Gupta u16 table_size; 1355c9ae5a8SAjay Gupta u8 fwct_version; 1365c9ae5a8SAjay Gupta u8 is_key_change; 1375c9ae5a8SAjay Gupta u8 guid[16]; 1385c9ae5a8SAjay Gupta struct version_format base; 1395c9ae5a8SAjay Gupta struct version_format app; 1405c9ae5a8SAjay Gupta u8 primary_fw_digest[32]; 1415c9ae5a8SAjay Gupta u32 key_exp_length; 1425c9ae5a8SAjay Gupta u8 key_modulus[256]; 1435c9ae5a8SAjay Gupta u8 key_exp[4]; 1445c9ae5a8SAjay Gupta }; 1455c9ae5a8SAjay Gupta 1465c9ae5a8SAjay Gupta /* CCGx response codes */ 1475c9ae5a8SAjay Gupta enum ccg_resp_code { 1485c9ae5a8SAjay Gupta CMD_NO_RESP = 0x00, 1495c9ae5a8SAjay Gupta CMD_SUCCESS = 0x02, 1505c9ae5a8SAjay Gupta FLASH_DATA_AVAILABLE = 0x03, 1515c9ae5a8SAjay Gupta CMD_INVALID = 0x05, 1525c9ae5a8SAjay Gupta FLASH_UPDATE_FAIL = 0x07, 1535c9ae5a8SAjay Gupta INVALID_FW = 0x08, 1545c9ae5a8SAjay Gupta INVALID_ARG = 0x09, 1555c9ae5a8SAjay Gupta CMD_NOT_SUPPORT = 0x0A, 1565c9ae5a8SAjay Gupta TRANSACTION_FAIL = 0x0C, 1575c9ae5a8SAjay Gupta PD_CMD_FAIL = 0x0D, 1585c9ae5a8SAjay Gupta UNDEF_ERROR = 0x0F, 1595c9ae5a8SAjay Gupta INVALID_RESP = 0x10, 1605c9ae5a8SAjay Gupta }; 1615c9ae5a8SAjay Gupta 1625c9ae5a8SAjay Gupta #define CCG_EVENT_MAX (EVENT_INDEX + 43) 1635c9ae5a8SAjay Gupta 1645c9ae5a8SAjay Gupta struct ccg_cmd { 1655c9ae5a8SAjay Gupta u16 reg; 1665c9ae5a8SAjay Gupta u32 data; 1675c9ae5a8SAjay Gupta int len; 1685c9ae5a8SAjay Gupta u32 delay; /* ms delay for cmd timeout */ 1695c9ae5a8SAjay Gupta }; 1705c9ae5a8SAjay Gupta 1715c9ae5a8SAjay Gupta struct ccg_resp { 1725c9ae5a8SAjay Gupta u8 code; 1735c9ae5a8SAjay Gupta u8 length; 1745c9ae5a8SAjay Gupta }; 1755c9ae5a8SAjay Gupta 176247c554aSAjay Gupta struct ucsi_ccg { 177247c554aSAjay Gupta struct device *dev; 178247c554aSAjay Gupta struct ucsi *ucsi; 179247c554aSAjay Gupta struct ucsi_ppm ppm; 180247c554aSAjay Gupta struct i2c_client *client; 1815d438e20SAjay Gupta struct ccg_dev_info info; 1825d438e20SAjay Gupta /* version info for boot, primary and secondary */ 1835d438e20SAjay Gupta struct version_info version[FW2 + 1]; 184f0e4cd94SAjay Gupta u32 fw_version; 1855c9ae5a8SAjay Gupta /* CCG HPI communication flags */ 1865c9ae5a8SAjay Gupta unsigned long flags; 1875c9ae5a8SAjay Gupta #define RESET_PENDING 0 1885c9ae5a8SAjay Gupta #define DEV_CMD_PENDING 1 1895c9ae5a8SAjay Gupta struct ccg_resp dev_resp; 1905c9ae5a8SAjay Gupta u8 cmd_resp; 1915c9ae5a8SAjay Gupta int port_num; 1925c9ae5a8SAjay Gupta int irq; 1935c9ae5a8SAjay Gupta struct work_struct work; 1945c9ae5a8SAjay Gupta struct mutex lock; /* to sync between user and driver thread */ 195247c554aSAjay Gupta 1965c9ae5a8SAjay Gupta /* fw build with vendor information */ 1975c9ae5a8SAjay Gupta u16 fw_build; 198f0e4cd94SAjay Gupta bool run_isr; /* flag to call ISR routine during resume */ 199f0e4cd94SAjay Gupta struct work_struct pm_work; 2005c9ae5a8SAjay Gupta }; 201247c554aSAjay Gupta 202247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 203247c554aSAjay Gupta { 204247c554aSAjay Gupta struct i2c_client *client = uc->client; 205247c554aSAjay Gupta const struct i2c_adapter_quirks *quirks = client->adapter->quirks; 206247c554aSAjay Gupta unsigned char buf[2]; 207247c554aSAjay Gupta struct i2c_msg msgs[] = { 208247c554aSAjay Gupta { 209247c554aSAjay Gupta .addr = client->addr, 210247c554aSAjay Gupta .flags = 0x0, 211247c554aSAjay Gupta .len = sizeof(buf), 212247c554aSAjay Gupta .buf = buf, 213247c554aSAjay Gupta }, 214247c554aSAjay Gupta { 215247c554aSAjay Gupta .addr = client->addr, 216247c554aSAjay Gupta .flags = I2C_M_RD, 217247c554aSAjay Gupta .buf = data, 218247c554aSAjay Gupta }, 219247c554aSAjay Gupta }; 220247c554aSAjay Gupta u32 rlen, rem_len = len, max_read_len = len; 221247c554aSAjay Gupta int status; 222247c554aSAjay Gupta 223247c554aSAjay Gupta /* check any max_read_len limitation on i2c adapter */ 224247c554aSAjay Gupta if (quirks && quirks->max_read_len) 225247c554aSAjay Gupta max_read_len = quirks->max_read_len; 226247c554aSAjay Gupta 227f0e4cd94SAjay Gupta if (uc->fw_build == CCG_FW_BUILD_NVIDIA && 228f0e4cd94SAjay Gupta uc->fw_version <= CCG_OLD_FW_VERSION) { 229f0e4cd94SAjay Gupta mutex_lock(&uc->lock); 230f0e4cd94SAjay Gupta /* 231f0e4cd94SAjay Gupta * Do not schedule pm_work to run ISR in 232f0e4cd94SAjay Gupta * ucsi_ccg_runtime_resume() after pm_runtime_get_sync() 233f0e4cd94SAjay Gupta * since we are already in ISR path. 234f0e4cd94SAjay Gupta */ 235f0e4cd94SAjay Gupta uc->run_isr = false; 236f0e4cd94SAjay Gupta mutex_unlock(&uc->lock); 237f0e4cd94SAjay Gupta } 238f0e4cd94SAjay Gupta 239a94ecde4SAjay Gupta pm_runtime_get_sync(uc->dev); 240247c554aSAjay Gupta while (rem_len > 0) { 241247c554aSAjay Gupta msgs[1].buf = &data[len - rem_len]; 242247c554aSAjay Gupta rlen = min_t(u16, rem_len, max_read_len); 243247c554aSAjay Gupta msgs[1].len = rlen; 244247c554aSAjay Gupta put_unaligned_le16(rab, buf); 245247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 246247c554aSAjay Gupta if (status < 0) { 247247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 248a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 249247c554aSAjay Gupta return status; 250247c554aSAjay Gupta } 251247c554aSAjay Gupta rab += rlen; 252247c554aSAjay Gupta rem_len -= rlen; 253247c554aSAjay Gupta } 254247c554aSAjay Gupta 255a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 256247c554aSAjay Gupta return 0; 257247c554aSAjay Gupta } 258247c554aSAjay Gupta 259247c554aSAjay Gupta static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 260247c554aSAjay Gupta { 261247c554aSAjay Gupta struct i2c_client *client = uc->client; 262247c554aSAjay Gupta unsigned char *buf; 263247c554aSAjay Gupta struct i2c_msg msgs[] = { 264247c554aSAjay Gupta { 265247c554aSAjay Gupta .addr = client->addr, 266247c554aSAjay Gupta .flags = 0x0, 267247c554aSAjay Gupta } 268247c554aSAjay Gupta }; 269247c554aSAjay Gupta int status; 270247c554aSAjay Gupta 271247c554aSAjay Gupta buf = kzalloc(len + sizeof(rab), GFP_KERNEL); 272247c554aSAjay Gupta if (!buf) 273247c554aSAjay Gupta return -ENOMEM; 274247c554aSAjay Gupta 275247c554aSAjay Gupta put_unaligned_le16(rab, buf); 276247c554aSAjay Gupta memcpy(buf + sizeof(rab), data, len); 277247c554aSAjay Gupta 278247c554aSAjay Gupta msgs[0].len = len + sizeof(rab); 279247c554aSAjay Gupta msgs[0].buf = buf; 280247c554aSAjay Gupta 281f0e4cd94SAjay Gupta if (uc->fw_build == CCG_FW_BUILD_NVIDIA && 282f0e4cd94SAjay Gupta uc->fw_version <= CCG_OLD_FW_VERSION) { 283f0e4cd94SAjay Gupta mutex_lock(&uc->lock); 284f0e4cd94SAjay Gupta /* 285f0e4cd94SAjay Gupta * Do not schedule pm_work to run ISR in 286f0e4cd94SAjay Gupta * ucsi_ccg_runtime_resume() after pm_runtime_get_sync() 287f0e4cd94SAjay Gupta * since we are already in ISR path. 288f0e4cd94SAjay Gupta */ 289f0e4cd94SAjay Gupta uc->run_isr = false; 290f0e4cd94SAjay Gupta mutex_unlock(&uc->lock); 291f0e4cd94SAjay Gupta } 292f0e4cd94SAjay Gupta 293a94ecde4SAjay Gupta pm_runtime_get_sync(uc->dev); 294247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 295247c554aSAjay Gupta if (status < 0) { 296247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 297a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 298247c554aSAjay Gupta kfree(buf); 299247c554aSAjay Gupta return status; 300247c554aSAjay Gupta } 301247c554aSAjay Gupta 302a94ecde4SAjay Gupta pm_runtime_put_sync(uc->dev); 303247c554aSAjay Gupta kfree(buf); 304247c554aSAjay Gupta return 0; 305247c554aSAjay Gupta } 306247c554aSAjay Gupta 307247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc) 308247c554aSAjay Gupta { 309247c554aSAjay Gupta unsigned int count = 10; 310247c554aSAjay Gupta u8 data; 311247c554aSAjay Gupta int status; 312247c554aSAjay Gupta 313247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_STOP; 314247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 315247c554aSAjay Gupta if (status < 0) 316247c554aSAjay Gupta return status; 317247c554aSAjay Gupta 318247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_START; 319247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 320247c554aSAjay Gupta if (status < 0) 321247c554aSAjay Gupta return status; 322247c554aSAjay Gupta 323247c554aSAjay Gupta /* 324247c554aSAjay Gupta * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control 325247c554aSAjay Gupta * register write will push response which must be cleared. 326247c554aSAjay Gupta */ 327247c554aSAjay Gupta do { 328247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 329247c554aSAjay Gupta if (status < 0) 330247c554aSAjay Gupta return status; 331247c554aSAjay Gupta 332247c554aSAjay Gupta if (!data) 333247c554aSAjay Gupta return 0; 334247c554aSAjay Gupta 335247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 336247c554aSAjay Gupta if (status < 0) 337247c554aSAjay Gupta return status; 338247c554aSAjay Gupta 339247c554aSAjay Gupta usleep_range(10000, 11000); 340247c554aSAjay Gupta } while (--count); 341247c554aSAjay Gupta 342247c554aSAjay Gupta return -ETIMEDOUT; 343247c554aSAjay Gupta } 344247c554aSAjay Gupta 345247c554aSAjay Gupta static int ucsi_ccg_send_data(struct ucsi_ccg *uc) 346247c554aSAjay Gupta { 347247c554aSAjay Gupta u8 *ppm = (u8 *)uc->ppm.data; 348247c554aSAjay Gupta int status; 349247c554aSAjay Gupta u16 rab; 350247c554aSAjay Gupta 351247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out)); 352247c554aSAjay Gupta status = ccg_write(uc, rab, ppm + 353247c554aSAjay Gupta offsetof(struct ucsi_data, message_out), 354247c554aSAjay Gupta sizeof(uc->ppm.data->message_out)); 355247c554aSAjay Gupta if (status < 0) 356247c554aSAjay Gupta return status; 357247c554aSAjay Gupta 358247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl)); 359247c554aSAjay Gupta return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl), 360247c554aSAjay Gupta sizeof(uc->ppm.data->ctrl)); 361247c554aSAjay Gupta } 362247c554aSAjay Gupta 363247c554aSAjay Gupta static int ucsi_ccg_recv_data(struct ucsi_ccg *uc) 364247c554aSAjay Gupta { 365247c554aSAjay Gupta u8 *ppm = (u8 *)uc->ppm.data; 366247c554aSAjay Gupta int status; 367247c554aSAjay Gupta u16 rab; 368247c554aSAjay Gupta 369247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci)); 370247c554aSAjay Gupta status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci), 371247c554aSAjay Gupta sizeof(uc->ppm.data->cci)); 372247c554aSAjay Gupta if (status < 0) 373247c554aSAjay Gupta return status; 374247c554aSAjay Gupta 375247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in)); 376247c554aSAjay Gupta return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in), 377247c554aSAjay Gupta sizeof(uc->ppm.data->message_in)); 378247c554aSAjay Gupta } 379247c554aSAjay Gupta 380247c554aSAjay Gupta static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc) 381247c554aSAjay Gupta { 382247c554aSAjay Gupta int status; 383247c554aSAjay Gupta unsigned char data; 384247c554aSAjay Gupta 385247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 386247c554aSAjay Gupta if (status < 0) 387247c554aSAjay Gupta return status; 388247c554aSAjay Gupta 389247c554aSAjay Gupta return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 390247c554aSAjay Gupta } 391247c554aSAjay Gupta 392247c554aSAjay Gupta static int ucsi_ccg_sync(struct ucsi_ppm *ppm) 393247c554aSAjay Gupta { 394247c554aSAjay Gupta struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); 395247c554aSAjay Gupta int status; 396247c554aSAjay Gupta 397247c554aSAjay Gupta status = ucsi_ccg_recv_data(uc); 398247c554aSAjay Gupta if (status < 0) 399247c554aSAjay Gupta return status; 400247c554aSAjay Gupta 401247c554aSAjay Gupta /* ack interrupt to allow next command to run */ 402247c554aSAjay Gupta return ucsi_ccg_ack_interrupt(uc); 403247c554aSAjay Gupta } 404247c554aSAjay Gupta 405247c554aSAjay Gupta static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl) 406247c554aSAjay Gupta { 407247c554aSAjay Gupta struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); 408247c554aSAjay Gupta 409247c554aSAjay Gupta ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; 410247c554aSAjay Gupta return ucsi_ccg_send_data(uc); 411247c554aSAjay Gupta } 412247c554aSAjay Gupta 413247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data) 414247c554aSAjay Gupta { 415247c554aSAjay Gupta struct ucsi_ccg *uc = data; 416247c554aSAjay Gupta 417247c554aSAjay Gupta ucsi_notify(uc->ucsi); 418247c554aSAjay Gupta 419247c554aSAjay Gupta return IRQ_HANDLED; 420247c554aSAjay Gupta } 421247c554aSAjay Gupta 422f0e4cd94SAjay Gupta static void ccg_pm_workaround_work(struct work_struct *pm_work) 423f0e4cd94SAjay Gupta { 424f0e4cd94SAjay Gupta struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); 425f0e4cd94SAjay Gupta 426f0e4cd94SAjay Gupta ucsi_notify(uc->ucsi); 427f0e4cd94SAjay Gupta } 428f0e4cd94SAjay Gupta 4295d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc) 4305d438e20SAjay Gupta { 4315d438e20SAjay Gupta int err; 4325d438e20SAjay Gupta 4335d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), 4345d438e20SAjay Gupta sizeof(uc->version)); 4355d438e20SAjay Gupta if (err < 0) 4365d438e20SAjay Gupta return err; 4375d438e20SAjay Gupta 438f0e4cd94SAjay Gupta uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) | 439f0e4cd94SAjay Gupta CCG_VERSION_PATCH(uc->version[FW2].app.patch); 440f0e4cd94SAjay Gupta 4415d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 4425d438e20SAjay Gupta sizeof(uc->info)); 4435d438e20SAjay Gupta if (err < 0) 4445d438e20SAjay Gupta return err; 4455d438e20SAjay Gupta 4465d438e20SAjay Gupta return 0; 4475d438e20SAjay Gupta } 4485d438e20SAjay Gupta 4495c9ae5a8SAjay Gupta static inline bool invalid_async_evt(int code) 4505c9ae5a8SAjay Gupta { 4515c9ae5a8SAjay Gupta return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX); 4525c9ae5a8SAjay Gupta } 4535c9ae5a8SAjay Gupta 4545c9ae5a8SAjay Gupta static void ccg_process_response(struct ucsi_ccg *uc) 4555c9ae5a8SAjay Gupta { 4565c9ae5a8SAjay Gupta struct device *dev = uc->dev; 4575c9ae5a8SAjay Gupta 4585c9ae5a8SAjay Gupta if (uc->dev_resp.code & ASYNC_EVENT) { 4595c9ae5a8SAjay Gupta if (uc->dev_resp.code == RESET_COMPLETE) { 4605c9ae5a8SAjay Gupta if (test_bit(RESET_PENDING, &uc->flags)) 4615c9ae5a8SAjay Gupta uc->cmd_resp = uc->dev_resp.code; 4625c9ae5a8SAjay Gupta get_fw_info(uc); 4635c9ae5a8SAjay Gupta } 4645c9ae5a8SAjay Gupta if (invalid_async_evt(uc->dev_resp.code)) 4655c9ae5a8SAjay Gupta dev_err(dev, "invalid async evt %d\n", 4665c9ae5a8SAjay Gupta uc->dev_resp.code); 4675c9ae5a8SAjay Gupta } else { 4685c9ae5a8SAjay Gupta if (test_bit(DEV_CMD_PENDING, &uc->flags)) { 4695c9ae5a8SAjay Gupta uc->cmd_resp = uc->dev_resp.code; 4705c9ae5a8SAjay Gupta clear_bit(DEV_CMD_PENDING, &uc->flags); 4715c9ae5a8SAjay Gupta } else { 4725c9ae5a8SAjay Gupta dev_err(dev, "dev resp 0x%04x but no cmd pending\n", 4735c9ae5a8SAjay Gupta uc->dev_resp.code); 4745c9ae5a8SAjay Gupta } 4755c9ae5a8SAjay Gupta } 4765c9ae5a8SAjay Gupta } 4775c9ae5a8SAjay Gupta 4785c9ae5a8SAjay Gupta static int ccg_read_response(struct ucsi_ccg *uc) 4795c9ae5a8SAjay Gupta { 4805c9ae5a8SAjay Gupta unsigned long target = jiffies + msecs_to_jiffies(1000); 4815c9ae5a8SAjay Gupta struct device *dev = uc->dev; 4825c9ae5a8SAjay Gupta u8 intval; 4835c9ae5a8SAjay Gupta int status; 4845c9ae5a8SAjay Gupta 4855c9ae5a8SAjay Gupta /* wait for interrupt status to get updated */ 4865c9ae5a8SAjay Gupta do { 4875c9ae5a8SAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval, 4885c9ae5a8SAjay Gupta sizeof(intval)); 4895c9ae5a8SAjay Gupta if (status < 0) 4905c9ae5a8SAjay Gupta return status; 4915c9ae5a8SAjay Gupta 4925c9ae5a8SAjay Gupta if (intval & DEV_INT) 4935c9ae5a8SAjay Gupta break; 4945c9ae5a8SAjay Gupta usleep_range(500, 600); 4955c9ae5a8SAjay Gupta } while (time_is_after_jiffies(target)); 4965c9ae5a8SAjay Gupta 4975c9ae5a8SAjay Gupta if (time_is_before_jiffies(target)) { 4985c9ae5a8SAjay Gupta dev_err(dev, "response timeout error\n"); 4995c9ae5a8SAjay Gupta return -ETIME; 5005c9ae5a8SAjay Gupta } 5015c9ae5a8SAjay Gupta 5025c9ae5a8SAjay Gupta status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp, 5035c9ae5a8SAjay Gupta sizeof(uc->dev_resp)); 5045c9ae5a8SAjay Gupta if (status < 0) 5055c9ae5a8SAjay Gupta return status; 5065c9ae5a8SAjay Gupta 5075c9ae5a8SAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval)); 5085c9ae5a8SAjay Gupta if (status < 0) 5095c9ae5a8SAjay Gupta return status; 5105c9ae5a8SAjay Gupta 5115c9ae5a8SAjay Gupta return 0; 5125c9ae5a8SAjay Gupta } 5135c9ae5a8SAjay Gupta 5145c9ae5a8SAjay Gupta /* Caller must hold uc->lock */ 5155c9ae5a8SAjay Gupta static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd) 5165c9ae5a8SAjay Gupta { 5175c9ae5a8SAjay Gupta struct device *dev = uc->dev; 5185c9ae5a8SAjay Gupta int ret; 5195c9ae5a8SAjay Gupta 5205c9ae5a8SAjay Gupta switch (cmd->reg & 0xF000) { 5215c9ae5a8SAjay Gupta case DEV_REG_IDX: 5225c9ae5a8SAjay Gupta set_bit(DEV_CMD_PENDING, &uc->flags); 5235c9ae5a8SAjay Gupta break; 5245c9ae5a8SAjay Gupta default: 5255c9ae5a8SAjay Gupta dev_err(dev, "invalid cmd register\n"); 5265c9ae5a8SAjay Gupta break; 5275c9ae5a8SAjay Gupta } 5285c9ae5a8SAjay Gupta 5295c9ae5a8SAjay Gupta ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len); 5305c9ae5a8SAjay Gupta if (ret < 0) 5315c9ae5a8SAjay Gupta return ret; 5325c9ae5a8SAjay Gupta 5335c9ae5a8SAjay Gupta msleep(cmd->delay); 5345c9ae5a8SAjay Gupta 5355c9ae5a8SAjay Gupta ret = ccg_read_response(uc); 5365c9ae5a8SAjay Gupta if (ret < 0) { 5375c9ae5a8SAjay Gupta dev_err(dev, "response read error\n"); 5385c9ae5a8SAjay Gupta switch (cmd->reg & 0xF000) { 5395c9ae5a8SAjay Gupta case DEV_REG_IDX: 5405c9ae5a8SAjay Gupta clear_bit(DEV_CMD_PENDING, &uc->flags); 5415c9ae5a8SAjay Gupta break; 5425c9ae5a8SAjay Gupta default: 5435c9ae5a8SAjay Gupta dev_err(dev, "invalid cmd register\n"); 5445c9ae5a8SAjay Gupta break; 5455c9ae5a8SAjay Gupta } 5465c9ae5a8SAjay Gupta return -EIO; 5475c9ae5a8SAjay Gupta } 5485c9ae5a8SAjay Gupta ccg_process_response(uc); 5495c9ae5a8SAjay Gupta 5505c9ae5a8SAjay Gupta return uc->cmd_resp; 5515c9ae5a8SAjay Gupta } 5525c9ae5a8SAjay Gupta 5535c9ae5a8SAjay Gupta static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc) 5545c9ae5a8SAjay Gupta { 5555c9ae5a8SAjay Gupta struct ccg_cmd cmd; 5565c9ae5a8SAjay Gupta int ret; 5575c9ae5a8SAjay Gupta 5585c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_ENTER_FLASHING; 5595c9ae5a8SAjay Gupta cmd.data = FLASH_ENTER_SIG; 5605c9ae5a8SAjay Gupta cmd.len = 1; 5615c9ae5a8SAjay Gupta cmd.delay = 50; 5625c9ae5a8SAjay Gupta 5635c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 5645c9ae5a8SAjay Gupta 5655c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 5665c9ae5a8SAjay Gupta 5675c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 5685c9ae5a8SAjay Gupta 5695c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 5705c9ae5a8SAjay Gupta dev_err(uc->dev, "enter flashing failed ret=%d\n", ret); 5715c9ae5a8SAjay Gupta return ret; 5725c9ae5a8SAjay Gupta } 5735c9ae5a8SAjay Gupta 5745c9ae5a8SAjay Gupta return 0; 5755c9ae5a8SAjay Gupta } 5765c9ae5a8SAjay Gupta 5775c9ae5a8SAjay Gupta static int ccg_cmd_reset(struct ucsi_ccg *uc) 5785c9ae5a8SAjay Gupta { 5795c9ae5a8SAjay Gupta struct ccg_cmd cmd; 5805c9ae5a8SAjay Gupta u8 *p; 5815c9ae5a8SAjay Gupta int ret; 5825c9ae5a8SAjay Gupta 5835c9ae5a8SAjay Gupta p = (u8 *)&cmd.data; 5845c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_RESET_REQ; 5855c9ae5a8SAjay Gupta p[0] = RESET_SIG; 5865c9ae5a8SAjay Gupta p[1] = CMD_RESET_DEV; 5875c9ae5a8SAjay Gupta cmd.len = 2; 5885c9ae5a8SAjay Gupta cmd.delay = 5000; 5895c9ae5a8SAjay Gupta 5905c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 5915c9ae5a8SAjay Gupta 5925c9ae5a8SAjay Gupta set_bit(RESET_PENDING, &uc->flags); 5935c9ae5a8SAjay Gupta 5945c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 5955c9ae5a8SAjay Gupta if (ret != RESET_COMPLETE) 5965c9ae5a8SAjay Gupta goto err_clear_flag; 5975c9ae5a8SAjay Gupta 5985c9ae5a8SAjay Gupta ret = 0; 5995c9ae5a8SAjay Gupta 6005c9ae5a8SAjay Gupta err_clear_flag: 6015c9ae5a8SAjay Gupta clear_bit(RESET_PENDING, &uc->flags); 6025c9ae5a8SAjay Gupta 6035c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 6045c9ae5a8SAjay Gupta 6055c9ae5a8SAjay Gupta return ret; 6065c9ae5a8SAjay Gupta } 6075c9ae5a8SAjay Gupta 6085c9ae5a8SAjay Gupta static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable) 6095c9ae5a8SAjay Gupta { 6105c9ae5a8SAjay Gupta struct ccg_cmd cmd; 6115c9ae5a8SAjay Gupta int ret; 6125c9ae5a8SAjay Gupta 6135c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_PDPORT_ENABLE; 6145c9ae5a8SAjay Gupta if (enable) 6155c9ae5a8SAjay Gupta cmd.data = (uc->port_num == 1) ? 6165c9ae5a8SAjay Gupta PDPORT_1 : (PDPORT_1 | PDPORT_2); 6175c9ae5a8SAjay Gupta else 6185c9ae5a8SAjay Gupta cmd.data = 0x0; 6195c9ae5a8SAjay Gupta cmd.len = 1; 6205c9ae5a8SAjay Gupta cmd.delay = 10; 6215c9ae5a8SAjay Gupta 6225c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 6235c9ae5a8SAjay Gupta 6245c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 6255c9ae5a8SAjay Gupta 6265c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 6275c9ae5a8SAjay Gupta 6285c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 6295c9ae5a8SAjay Gupta dev_err(uc->dev, "port control failed ret=%d\n", ret); 6305c9ae5a8SAjay Gupta return ret; 6315c9ae5a8SAjay Gupta } 6325c9ae5a8SAjay Gupta return 0; 6335c9ae5a8SAjay Gupta } 6345c9ae5a8SAjay Gupta 6355c9ae5a8SAjay Gupta static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode) 6365c9ae5a8SAjay Gupta { 6375c9ae5a8SAjay Gupta struct ccg_cmd cmd; 6385c9ae5a8SAjay Gupta int ret; 6395c9ae5a8SAjay Gupta 6405c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_JUMP_TO_BOOT; 6415c9ae5a8SAjay Gupta 6425c9ae5a8SAjay Gupta if (bl_mode) 6435c9ae5a8SAjay Gupta cmd.data = TO_BOOT; 6445c9ae5a8SAjay Gupta else 6455c9ae5a8SAjay Gupta cmd.data = TO_ALT_FW; 6465c9ae5a8SAjay Gupta 6475c9ae5a8SAjay Gupta cmd.len = 1; 6485c9ae5a8SAjay Gupta cmd.delay = 100; 6495c9ae5a8SAjay Gupta 6505c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 6515c9ae5a8SAjay Gupta 6525c9ae5a8SAjay Gupta set_bit(RESET_PENDING, &uc->flags); 6535c9ae5a8SAjay Gupta 6545c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 6555c9ae5a8SAjay Gupta if (ret != RESET_COMPLETE) 6565c9ae5a8SAjay Gupta goto err_clear_flag; 6575c9ae5a8SAjay Gupta 6585c9ae5a8SAjay Gupta ret = 0; 6595c9ae5a8SAjay Gupta 6605c9ae5a8SAjay Gupta err_clear_flag: 6615c9ae5a8SAjay Gupta clear_bit(RESET_PENDING, &uc->flags); 6625c9ae5a8SAjay Gupta 6635c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 6645c9ae5a8SAjay Gupta 6655c9ae5a8SAjay Gupta return ret; 6665c9ae5a8SAjay Gupta } 6675c9ae5a8SAjay Gupta 6685c9ae5a8SAjay Gupta static int 6695c9ae5a8SAjay Gupta ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row, 6705c9ae5a8SAjay Gupta const void *data, u8 fcmd) 6715c9ae5a8SAjay Gupta { 6725c9ae5a8SAjay Gupta struct i2c_client *client = uc->client; 6735c9ae5a8SAjay Gupta struct ccg_cmd cmd; 6745c9ae5a8SAjay Gupta u8 buf[CCG4_ROW_SIZE + 2]; 6755c9ae5a8SAjay Gupta u8 *p; 6765c9ae5a8SAjay Gupta int ret; 6775c9ae5a8SAjay Gupta 6785c9ae5a8SAjay Gupta /* Copy the data into the flash read/write memory. */ 6795c9ae5a8SAjay Gupta put_unaligned_le16(REG_FLASH_RW_MEM, buf); 6805c9ae5a8SAjay Gupta 6815c9ae5a8SAjay Gupta memcpy(buf + 2, data, CCG4_ROW_SIZE); 6825c9ae5a8SAjay Gupta 6835c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 6845c9ae5a8SAjay Gupta 6855c9ae5a8SAjay Gupta ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2); 6865c9ae5a8SAjay Gupta if (ret != CCG4_ROW_SIZE + 2) { 6875c9ae5a8SAjay Gupta dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret); 688c2d18126SWei Yongjun mutex_unlock(&uc->lock); 6895c9ae5a8SAjay Gupta return ret < 0 ? ret : -EIO; 6905c9ae5a8SAjay Gupta } 6915c9ae5a8SAjay Gupta 6925c9ae5a8SAjay Gupta /* Use the FLASH_ROW_READ_WRITE register to trigger */ 6935c9ae5a8SAjay Gupta /* writing of data to the desired flash row */ 6945c9ae5a8SAjay Gupta p = (u8 *)&cmd.data; 6955c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_FLASH_ROW_RW; 6965c9ae5a8SAjay Gupta p[0] = FLASH_SIG; 6975c9ae5a8SAjay Gupta p[1] = fcmd; 6985c9ae5a8SAjay Gupta put_unaligned_le16(row, &p[2]); 6995c9ae5a8SAjay Gupta cmd.len = 4; 7005c9ae5a8SAjay Gupta cmd.delay = 50; 7015c9ae5a8SAjay Gupta if (fcmd == FLASH_FWCT_SIG_WR_CMD) 7025c9ae5a8SAjay Gupta cmd.delay += 400; 7035c9ae5a8SAjay Gupta if (row == 510) 7045c9ae5a8SAjay Gupta cmd.delay += 220; 7055c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 7065c9ae5a8SAjay Gupta 7075c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 7085c9ae5a8SAjay Gupta 7095c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 7105c9ae5a8SAjay Gupta dev_err(uc->dev, "write flash row failed ret=%d\n", ret); 7115c9ae5a8SAjay Gupta return ret; 7125c9ae5a8SAjay Gupta } 7135c9ae5a8SAjay Gupta 7145c9ae5a8SAjay Gupta return 0; 7155c9ae5a8SAjay Gupta } 7165c9ae5a8SAjay Gupta 7175c9ae5a8SAjay Gupta static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid) 7185c9ae5a8SAjay Gupta { 7195c9ae5a8SAjay Gupta struct ccg_cmd cmd; 7205c9ae5a8SAjay Gupta int ret; 7215c9ae5a8SAjay Gupta 7225c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_VALIDATE_FW; 7235c9ae5a8SAjay Gupta cmd.data = fwid; 7245c9ae5a8SAjay Gupta cmd.len = 1; 7255c9ae5a8SAjay Gupta cmd.delay = 500; 7265c9ae5a8SAjay Gupta 7275c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 7285c9ae5a8SAjay Gupta 7295c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 7305c9ae5a8SAjay Gupta 7315c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 7325c9ae5a8SAjay Gupta 7335c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) 7345c9ae5a8SAjay Gupta return ret; 7355c9ae5a8SAjay Gupta 7365c9ae5a8SAjay Gupta return 0; 7375c9ae5a8SAjay Gupta } 7385c9ae5a8SAjay Gupta 7395c9ae5a8SAjay Gupta static bool ccg_check_vendor_version(struct ucsi_ccg *uc, 7405c9ae5a8SAjay Gupta struct version_format *app, 7415c9ae5a8SAjay Gupta struct fw_config_table *fw_cfg) 7425c9ae5a8SAjay Gupta { 7435c9ae5a8SAjay Gupta struct device *dev = uc->dev; 7445c9ae5a8SAjay Gupta 7455c9ae5a8SAjay Gupta /* Check if the fw build is for supported vendors */ 7465c9ae5a8SAjay Gupta if (le16_to_cpu(app->build) != uc->fw_build) { 7475c9ae5a8SAjay Gupta dev_info(dev, "current fw is not from supported vendor\n"); 7485c9ae5a8SAjay Gupta return false; 7495c9ae5a8SAjay Gupta } 7505c9ae5a8SAjay Gupta 7515c9ae5a8SAjay Gupta /* Check if the new fw build is for supported vendors */ 7525c9ae5a8SAjay Gupta if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) { 7535c9ae5a8SAjay Gupta dev_info(dev, "new fw is not from supported vendor\n"); 7545c9ae5a8SAjay Gupta return false; 7555c9ae5a8SAjay Gupta } 7565c9ae5a8SAjay Gupta return true; 7575c9ae5a8SAjay Gupta } 7585c9ae5a8SAjay Gupta 7595c9ae5a8SAjay Gupta static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, 7605c9ae5a8SAjay Gupta struct version_format *app) 7615c9ae5a8SAjay Gupta { 7625c9ae5a8SAjay Gupta const struct firmware *fw = NULL; 7635c9ae5a8SAjay Gupta struct device *dev = uc->dev; 7645c9ae5a8SAjay Gupta struct fw_config_table fw_cfg; 7655c9ae5a8SAjay Gupta u32 cur_version, new_version; 7665c9ae5a8SAjay Gupta bool is_later = false; 7675c9ae5a8SAjay Gupta 7685c9ae5a8SAjay Gupta if (request_firmware(&fw, fw_name, dev) != 0) { 7695c9ae5a8SAjay Gupta dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name); 7705c9ae5a8SAjay Gupta return false; 7715c9ae5a8SAjay Gupta } 7725c9ae5a8SAjay Gupta 7735c9ae5a8SAjay Gupta /* 7745c9ae5a8SAjay Gupta * check if signed fw 7755c9ae5a8SAjay Gupta * last part of fw image is fw cfg table and signature 7765c9ae5a8SAjay Gupta */ 7775c9ae5a8SAjay Gupta if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE) 7785c9ae5a8SAjay Gupta goto out_release_firmware; 7795c9ae5a8SAjay Gupta 7805c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - 7815c9ae5a8SAjay Gupta sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg)); 7825c9ae5a8SAjay Gupta 7835c9ae5a8SAjay Gupta if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) { 7845c9ae5a8SAjay Gupta dev_info(dev, "not a signed image\n"); 7855c9ae5a8SAjay Gupta goto out_release_firmware; 7865c9ae5a8SAjay Gupta } 7875c9ae5a8SAjay Gupta 7885c9ae5a8SAjay Gupta /* compare input version with FWCT version */ 789f0e4cd94SAjay Gupta cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) | 790f0e4cd94SAjay Gupta CCG_VERSION(app->ver); 7915c9ae5a8SAjay Gupta 792f0e4cd94SAjay Gupta new_version = le16_to_cpu(fw_cfg.app.build) | 793f0e4cd94SAjay Gupta CCG_VERSION_PATCH(fw_cfg.app.patch) | 794f0e4cd94SAjay Gupta CCG_VERSION(fw_cfg.app.ver); 7955c9ae5a8SAjay Gupta 7965c9ae5a8SAjay Gupta if (!ccg_check_vendor_version(uc, app, &fw_cfg)) 7975c9ae5a8SAjay Gupta goto out_release_firmware; 7985c9ae5a8SAjay Gupta 7995c9ae5a8SAjay Gupta if (new_version > cur_version) 8005c9ae5a8SAjay Gupta is_later = true; 8015c9ae5a8SAjay Gupta 8025c9ae5a8SAjay Gupta out_release_firmware: 8035c9ae5a8SAjay Gupta release_firmware(fw); 8045c9ae5a8SAjay Gupta return is_later; 8055c9ae5a8SAjay Gupta } 8065c9ae5a8SAjay Gupta 8075c9ae5a8SAjay Gupta static int ccg_fw_update_needed(struct ucsi_ccg *uc, 8085c9ae5a8SAjay Gupta enum enum_flash_mode *mode) 8095c9ae5a8SAjay Gupta { 8105c9ae5a8SAjay Gupta struct device *dev = uc->dev; 8115c9ae5a8SAjay Gupta int err; 8125c9ae5a8SAjay Gupta struct version_info version[3]; 8135c9ae5a8SAjay Gupta 8145c9ae5a8SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 8155c9ae5a8SAjay Gupta sizeof(uc->info)); 8165c9ae5a8SAjay Gupta if (err) { 8175c9ae5a8SAjay Gupta dev_err(dev, "read device mode failed\n"); 8185c9ae5a8SAjay Gupta return err; 8195c9ae5a8SAjay Gupta } 8205c9ae5a8SAjay Gupta 8215c9ae5a8SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version, 8225c9ae5a8SAjay Gupta sizeof(version)); 8235c9ae5a8SAjay Gupta if (err) { 8245c9ae5a8SAjay Gupta dev_err(dev, "read device mode failed\n"); 8255c9ae5a8SAjay Gupta return err; 8265c9ae5a8SAjay Gupta } 8275c9ae5a8SAjay Gupta 8285c9ae5a8SAjay Gupta if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0", 8295c9ae5a8SAjay Gupta sizeof(struct version_info)) == 0) { 8305c9ae5a8SAjay Gupta dev_info(dev, "secondary fw is not flashed\n"); 8315c9ae5a8SAjay Gupta *mode = SECONDARY_BL; 8325c9ae5a8SAjay Gupta } else if (le16_to_cpu(version[FW1].base.build) < 8335c9ae5a8SAjay Gupta secondary_fw_min_ver) { 8345c9ae5a8SAjay Gupta dev_info(dev, "secondary fw version is too low (< %d)\n", 8355c9ae5a8SAjay Gupta secondary_fw_min_ver); 8365c9ae5a8SAjay Gupta *mode = SECONDARY; 8375c9ae5a8SAjay Gupta } else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0", 8385c9ae5a8SAjay Gupta sizeof(struct version_info)) == 0) { 8395c9ae5a8SAjay Gupta dev_info(dev, "primary fw is not flashed\n"); 8405c9ae5a8SAjay Gupta *mode = PRIMARY; 8415c9ae5a8SAjay Gupta } else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY], 8425c9ae5a8SAjay Gupta &version[FW2].app)) { 8435c9ae5a8SAjay Gupta dev_info(dev, "found primary fw with later version\n"); 8445c9ae5a8SAjay Gupta *mode = PRIMARY; 8455c9ae5a8SAjay Gupta } else { 8465c9ae5a8SAjay Gupta dev_info(dev, "secondary and primary fw are the latest\n"); 8475c9ae5a8SAjay Gupta *mode = FLASH_NOT_NEEDED; 8485c9ae5a8SAjay Gupta } 8495c9ae5a8SAjay Gupta return 0; 8505c9ae5a8SAjay Gupta } 8515c9ae5a8SAjay Gupta 8525c9ae5a8SAjay Gupta static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) 8535c9ae5a8SAjay Gupta { 8545c9ae5a8SAjay Gupta struct device *dev = uc->dev; 8555c9ae5a8SAjay Gupta const struct firmware *fw = NULL; 8565c9ae5a8SAjay Gupta const char *p, *s; 8575c9ae5a8SAjay Gupta const char *eof; 8585c9ae5a8SAjay Gupta int err, row, len, line_sz, line_cnt = 0; 8595c9ae5a8SAjay Gupta unsigned long start_time = jiffies; 8605c9ae5a8SAjay Gupta struct fw_config_table fw_cfg; 8615c9ae5a8SAjay Gupta u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE]; 8625c9ae5a8SAjay Gupta u8 *wr_buf; 8635c9ae5a8SAjay Gupta 8645c9ae5a8SAjay Gupta err = request_firmware(&fw, ccg_fw_names[mode], dev); 8655c9ae5a8SAjay Gupta if (err) { 8665c9ae5a8SAjay Gupta dev_err(dev, "request %s failed err=%d\n", 8675c9ae5a8SAjay Gupta ccg_fw_names[mode], err); 8685c9ae5a8SAjay Gupta return err; 8695c9ae5a8SAjay Gupta } 8705c9ae5a8SAjay Gupta 8715c9ae5a8SAjay Gupta if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >> 8725c9ae5a8SAjay Gupta CCG_DEVINFO_FWMODE_SHIFT) == FW2) { 8735c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, false); 8745c9ae5a8SAjay Gupta if (err < 0) 8755c9ae5a8SAjay Gupta goto release_fw; 8765c9ae5a8SAjay Gupta err = ccg_cmd_jump_boot_mode(uc, 0); 8775c9ae5a8SAjay Gupta if (err < 0) 8785c9ae5a8SAjay Gupta goto release_fw; 8795c9ae5a8SAjay Gupta } 8805c9ae5a8SAjay Gupta 8815c9ae5a8SAjay Gupta eof = fw->data + fw->size; 8825c9ae5a8SAjay Gupta 8835c9ae5a8SAjay Gupta /* 8845c9ae5a8SAjay Gupta * check if signed fw 8855c9ae5a8SAjay Gupta * last part of fw image is fw cfg table and signature 8865c9ae5a8SAjay Gupta */ 8875c9ae5a8SAjay Gupta if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig)) 8885c9ae5a8SAjay Gupta goto not_signed_fw; 8895c9ae5a8SAjay Gupta 8905c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - 8915c9ae5a8SAjay Gupta sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg)); 8925c9ae5a8SAjay Gupta 8935c9ae5a8SAjay Gupta if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) { 8945c9ae5a8SAjay Gupta dev_info(dev, "not a signed image\n"); 8955c9ae5a8SAjay Gupta goto not_signed_fw; 8965c9ae5a8SAjay Gupta } 8975c9ae5a8SAjay Gupta eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig); 8985c9ae5a8SAjay Gupta 8995c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg_sig, 9005c9ae5a8SAjay Gupta fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig)); 9015c9ae5a8SAjay Gupta 9025c9ae5a8SAjay Gupta /* flash fw config table and signature first */ 9035c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg, 9045c9ae5a8SAjay Gupta FLASH_FWCT1_WR_CMD); 9055c9ae5a8SAjay Gupta if (err) 9065c9ae5a8SAjay Gupta goto release_fw; 9075c9ae5a8SAjay Gupta 9085c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE, 9095c9ae5a8SAjay Gupta FLASH_FWCT2_WR_CMD); 9105c9ae5a8SAjay Gupta if (err) 9115c9ae5a8SAjay Gupta goto release_fw; 9125c9ae5a8SAjay Gupta 9135c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig, 9145c9ae5a8SAjay Gupta FLASH_FWCT_SIG_WR_CMD); 9155c9ae5a8SAjay Gupta if (err) 9165c9ae5a8SAjay Gupta goto release_fw; 9175c9ae5a8SAjay Gupta 9185c9ae5a8SAjay Gupta not_signed_fw: 9195c9ae5a8SAjay Gupta wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL); 9205c9ae5a8SAjay Gupta if (!wr_buf) 9215c9ae5a8SAjay Gupta return -ENOMEM; 9225c9ae5a8SAjay Gupta 9235c9ae5a8SAjay Gupta err = ccg_cmd_enter_flashing(uc); 9245c9ae5a8SAjay Gupta if (err) 9255c9ae5a8SAjay Gupta goto release_mem; 9265c9ae5a8SAjay Gupta 9275c9ae5a8SAjay Gupta /***************************************************************** 9285c9ae5a8SAjay Gupta * CCG firmware image (.cyacd) file line format 9295c9ae5a8SAjay Gupta * 9305c9ae5a8SAjay Gupta * :00rrrrllll[dd....]cc/r/n 9315c9ae5a8SAjay Gupta * 9325c9ae5a8SAjay Gupta * :00 header 9335c9ae5a8SAjay Gupta * rrrr is row number to flash (4 char) 9345c9ae5a8SAjay Gupta * llll is data len to flash (4 char) 9355c9ae5a8SAjay Gupta * dd is a data field represents one byte of data (512 char) 9365c9ae5a8SAjay Gupta * cc is checksum (2 char) 9375c9ae5a8SAjay Gupta * \r\n newline 9385c9ae5a8SAjay Gupta * 9395c9ae5a8SAjay Gupta * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527 9405c9ae5a8SAjay Gupta * 9415c9ae5a8SAjay Gupta *****************************************************************/ 9425c9ae5a8SAjay Gupta 9435c9ae5a8SAjay Gupta p = strnchr(fw->data, fw->size, ':'); 9445c9ae5a8SAjay Gupta while (p < eof) { 9455c9ae5a8SAjay Gupta s = strnchr(p + 1, eof - p - 1, ':'); 9465c9ae5a8SAjay Gupta 9475c9ae5a8SAjay Gupta if (!s) 9485c9ae5a8SAjay Gupta s = eof; 9495c9ae5a8SAjay Gupta 9505c9ae5a8SAjay Gupta line_sz = s - p; 9515c9ae5a8SAjay Gupta 9525c9ae5a8SAjay Gupta if (line_sz != CYACD_LINE_SIZE) { 9535c9ae5a8SAjay Gupta dev_err(dev, "Bad FW format line_sz=%d\n", line_sz); 9545c9ae5a8SAjay Gupta err = -EINVAL; 9555c9ae5a8SAjay Gupta goto release_mem; 9565c9ae5a8SAjay Gupta } 9575c9ae5a8SAjay Gupta 9585c9ae5a8SAjay Gupta if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) { 9595c9ae5a8SAjay Gupta err = -EINVAL; 9605c9ae5a8SAjay Gupta goto release_mem; 9615c9ae5a8SAjay Gupta } 9625c9ae5a8SAjay Gupta 9635c9ae5a8SAjay Gupta row = get_unaligned_be16(wr_buf); 9645c9ae5a8SAjay Gupta len = get_unaligned_be16(&wr_buf[2]); 9655c9ae5a8SAjay Gupta 9665c9ae5a8SAjay Gupta if (len != CCG4_ROW_SIZE) { 9675c9ae5a8SAjay Gupta err = -EINVAL; 9685c9ae5a8SAjay Gupta goto release_mem; 9695c9ae5a8SAjay Gupta } 9705c9ae5a8SAjay Gupta 9715c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4, 9725c9ae5a8SAjay Gupta FLASH_WR_CMD); 9735c9ae5a8SAjay Gupta if (err) 9745c9ae5a8SAjay Gupta goto release_mem; 9755c9ae5a8SAjay Gupta 9765c9ae5a8SAjay Gupta line_cnt++; 9775c9ae5a8SAjay Gupta p = s; 9785c9ae5a8SAjay Gupta } 9795c9ae5a8SAjay Gupta 9805c9ae5a8SAjay Gupta dev_info(dev, "total %d row flashed. time: %dms\n", 9815c9ae5a8SAjay Gupta line_cnt, jiffies_to_msecs(jiffies - start_time)); 9825c9ae5a8SAjay Gupta 9835c9ae5a8SAjay Gupta err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 : FW1); 9845c9ae5a8SAjay Gupta if (err) 9855c9ae5a8SAjay Gupta dev_err(dev, "%s validation failed err=%d\n", 9865c9ae5a8SAjay Gupta (mode == PRIMARY) ? "FW2" : "FW1", err); 9875c9ae5a8SAjay Gupta else 9885c9ae5a8SAjay Gupta dev_info(dev, "%s validated\n", 9895c9ae5a8SAjay Gupta (mode == PRIMARY) ? "FW2" : "FW1"); 9905c9ae5a8SAjay Gupta 9915c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, false); 9925c9ae5a8SAjay Gupta if (err < 0) 9935c9ae5a8SAjay Gupta goto release_mem; 9945c9ae5a8SAjay Gupta 9955c9ae5a8SAjay Gupta err = ccg_cmd_reset(uc); 9965c9ae5a8SAjay Gupta if (err < 0) 9975c9ae5a8SAjay Gupta goto release_mem; 9985c9ae5a8SAjay Gupta 9995c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, true); 10005c9ae5a8SAjay Gupta if (err < 0) 10015c9ae5a8SAjay Gupta goto release_mem; 10025c9ae5a8SAjay Gupta 10035c9ae5a8SAjay Gupta release_mem: 10045c9ae5a8SAjay Gupta kfree(wr_buf); 10055c9ae5a8SAjay Gupta 10065c9ae5a8SAjay Gupta release_fw: 10075c9ae5a8SAjay Gupta release_firmware(fw); 10085c9ae5a8SAjay Gupta return err; 10095c9ae5a8SAjay Gupta } 10105c9ae5a8SAjay Gupta 10115c9ae5a8SAjay Gupta /******************************************************************************* 10125c9ae5a8SAjay Gupta * CCG4 has two copies of the firmware in addition to the bootloader. 10135c9ae5a8SAjay Gupta * If the device is running FW1, FW2 can be updated with the new version. 10145c9ae5a8SAjay Gupta * Dual firmware mode allows the CCG device to stay in a PD contract and support 10155c9ae5a8SAjay Gupta * USB PD and Type-C functionality while a firmware update is in progress. 10165c9ae5a8SAjay Gupta ******************************************************************************/ 10175c9ae5a8SAjay Gupta static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode) 10185c9ae5a8SAjay Gupta { 10195c9ae5a8SAjay Gupta int err; 10205c9ae5a8SAjay Gupta 10215c9ae5a8SAjay Gupta while (flash_mode != FLASH_NOT_NEEDED) { 10225c9ae5a8SAjay Gupta err = do_flash(uc, flash_mode); 10235c9ae5a8SAjay Gupta if (err < 0) 10245c9ae5a8SAjay Gupta return err; 10255c9ae5a8SAjay Gupta err = ccg_fw_update_needed(uc, &flash_mode); 10265c9ae5a8SAjay Gupta if (err < 0) 10275c9ae5a8SAjay Gupta return err; 10285c9ae5a8SAjay Gupta } 10295c9ae5a8SAjay Gupta dev_info(uc->dev, "CCG FW update successful\n"); 10305c9ae5a8SAjay Gupta 10315c9ae5a8SAjay Gupta return err; 10325c9ae5a8SAjay Gupta } 10335c9ae5a8SAjay Gupta 10345c9ae5a8SAjay Gupta static int ccg_restart(struct ucsi_ccg *uc) 10355c9ae5a8SAjay Gupta { 10365c9ae5a8SAjay Gupta struct device *dev = uc->dev; 10375c9ae5a8SAjay Gupta int status; 10385c9ae5a8SAjay Gupta 10395c9ae5a8SAjay Gupta status = ucsi_ccg_init(uc); 10405c9ae5a8SAjay Gupta if (status < 0) { 10415c9ae5a8SAjay Gupta dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status); 10425c9ae5a8SAjay Gupta return status; 10435c9ae5a8SAjay Gupta } 10445c9ae5a8SAjay Gupta 10455c9ae5a8SAjay Gupta status = request_threaded_irq(uc->irq, NULL, ccg_irq_handler, 10465c9ae5a8SAjay Gupta IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 10475c9ae5a8SAjay Gupta dev_name(dev), uc); 10485c9ae5a8SAjay Gupta if (status < 0) { 10495c9ae5a8SAjay Gupta dev_err(dev, "request_threaded_irq failed - %d\n", status); 10505c9ae5a8SAjay Gupta return status; 10515c9ae5a8SAjay Gupta } 10525c9ae5a8SAjay Gupta 10535c9ae5a8SAjay Gupta uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); 10545c9ae5a8SAjay Gupta if (IS_ERR(uc->ucsi)) { 10555c9ae5a8SAjay Gupta dev_err(uc->dev, "ucsi_register_ppm failed\n"); 10565c9ae5a8SAjay Gupta return PTR_ERR(uc->ucsi); 10575c9ae5a8SAjay Gupta } 10585c9ae5a8SAjay Gupta 10595c9ae5a8SAjay Gupta return 0; 10605c9ae5a8SAjay Gupta } 10615c9ae5a8SAjay Gupta 10625c9ae5a8SAjay Gupta static void ccg_update_firmware(struct work_struct *work) 10635c9ae5a8SAjay Gupta { 10645c9ae5a8SAjay Gupta struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work); 10655c9ae5a8SAjay Gupta enum enum_flash_mode flash_mode; 10665c9ae5a8SAjay Gupta int status; 10675c9ae5a8SAjay Gupta 10685c9ae5a8SAjay Gupta status = ccg_fw_update_needed(uc, &flash_mode); 10695c9ae5a8SAjay Gupta if (status < 0) 10705c9ae5a8SAjay Gupta return; 10715c9ae5a8SAjay Gupta 10725c9ae5a8SAjay Gupta if (flash_mode != FLASH_NOT_NEEDED) { 10735c9ae5a8SAjay Gupta ucsi_unregister_ppm(uc->ucsi); 10745c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 10755c9ae5a8SAjay Gupta 10765c9ae5a8SAjay Gupta ccg_fw_update(uc, flash_mode); 10775c9ae5a8SAjay Gupta ccg_restart(uc); 10785c9ae5a8SAjay Gupta } 10795c9ae5a8SAjay Gupta } 10805c9ae5a8SAjay Gupta 10815c9ae5a8SAjay Gupta static ssize_t do_flash_store(struct device *dev, 10825c9ae5a8SAjay Gupta struct device_attribute *attr, 10835c9ae5a8SAjay Gupta const char *buf, size_t n) 10845c9ae5a8SAjay Gupta { 10855c9ae5a8SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev)); 10865c9ae5a8SAjay Gupta bool flash; 10875c9ae5a8SAjay Gupta 10885c9ae5a8SAjay Gupta if (kstrtobool(buf, &flash)) 10895c9ae5a8SAjay Gupta return -EINVAL; 10905c9ae5a8SAjay Gupta 10915c9ae5a8SAjay Gupta if (!flash) 10925c9ae5a8SAjay Gupta return n; 10935c9ae5a8SAjay Gupta 10945c9ae5a8SAjay Gupta if (uc->fw_build == 0x0) { 10955c9ae5a8SAjay Gupta dev_err(dev, "fail to flash FW due to missing FW build info\n"); 10965c9ae5a8SAjay Gupta return -EINVAL; 10975c9ae5a8SAjay Gupta } 10985c9ae5a8SAjay Gupta 10995c9ae5a8SAjay Gupta schedule_work(&uc->work); 11005c9ae5a8SAjay Gupta return n; 11015c9ae5a8SAjay Gupta } 11025c9ae5a8SAjay Gupta 11035c9ae5a8SAjay Gupta static DEVICE_ATTR_WO(do_flash); 11045c9ae5a8SAjay Gupta 11055c9ae5a8SAjay Gupta static struct attribute *ucsi_ccg_sysfs_attrs[] = { 11065c9ae5a8SAjay Gupta &dev_attr_do_flash.attr, 11075c9ae5a8SAjay Gupta NULL, 11085c9ae5a8SAjay Gupta }; 11095c9ae5a8SAjay Gupta 11105c9ae5a8SAjay Gupta static struct attribute_group ucsi_ccg_attr_group = { 11115c9ae5a8SAjay Gupta .attrs = ucsi_ccg_sysfs_attrs, 11125c9ae5a8SAjay Gupta }; 11135c9ae5a8SAjay Gupta 1114247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client, 1115247c554aSAjay Gupta const struct i2c_device_id *id) 1116247c554aSAjay Gupta { 1117247c554aSAjay Gupta struct device *dev = &client->dev; 1118247c554aSAjay Gupta struct ucsi_ccg *uc; 1119247c554aSAjay Gupta int status; 1120247c554aSAjay Gupta u16 rab; 1121247c554aSAjay Gupta 1122247c554aSAjay Gupta uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); 1123247c554aSAjay Gupta if (!uc) 1124247c554aSAjay Gupta return -ENOMEM; 1125247c554aSAjay Gupta 1126247c554aSAjay Gupta uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL); 1127247c554aSAjay Gupta if (!uc->ppm.data) 1128247c554aSAjay Gupta return -ENOMEM; 1129247c554aSAjay Gupta 1130247c554aSAjay Gupta uc->ppm.cmd = ucsi_ccg_cmd; 1131247c554aSAjay Gupta uc->ppm.sync = ucsi_ccg_sync; 1132247c554aSAjay Gupta uc->dev = dev; 1133247c554aSAjay Gupta uc->client = client; 1134f0e4cd94SAjay Gupta uc->run_isr = true; 11355c9ae5a8SAjay Gupta mutex_init(&uc->lock); 11365c9ae5a8SAjay Gupta INIT_WORK(&uc->work, ccg_update_firmware); 1137f0e4cd94SAjay Gupta INIT_WORK(&uc->pm_work, ccg_pm_workaround_work); 11385c9ae5a8SAjay Gupta 11395c9ae5a8SAjay Gupta /* Only fail FW flashing when FW build information is not provided */ 11405c9ae5a8SAjay Gupta status = device_property_read_u16(dev, "ccgx,firmware-build", 11415c9ae5a8SAjay Gupta &uc->fw_build); 11425c9ae5a8SAjay Gupta if (status) 11435c9ae5a8SAjay Gupta dev_err(uc->dev, "failed to get FW build information\n"); 1144247c554aSAjay Gupta 1145247c554aSAjay Gupta /* reset ccg device and initialize ucsi */ 1146247c554aSAjay Gupta status = ucsi_ccg_init(uc); 1147247c554aSAjay Gupta if (status < 0) { 1148247c554aSAjay Gupta dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); 1149247c554aSAjay Gupta return status; 1150247c554aSAjay Gupta } 1151247c554aSAjay Gupta 11525d438e20SAjay Gupta status = get_fw_info(uc); 11535d438e20SAjay Gupta if (status < 0) { 11545d438e20SAjay Gupta dev_err(uc->dev, "get_fw_info failed - %d\n", status); 11555d438e20SAjay Gupta return status; 11565d438e20SAjay Gupta } 11575d438e20SAjay Gupta 11585c9ae5a8SAjay Gupta uc->port_num = 1; 11595c9ae5a8SAjay Gupta 11605c9ae5a8SAjay Gupta if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK) 11615c9ae5a8SAjay Gupta uc->port_num++; 11625c9ae5a8SAjay Gupta 11635c9ae5a8SAjay Gupta status = request_threaded_irq(client->irq, NULL, ccg_irq_handler, 1164247c554aSAjay Gupta IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 1165247c554aSAjay Gupta dev_name(dev), uc); 1166247c554aSAjay Gupta if (status < 0) { 1167247c554aSAjay Gupta dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); 1168247c554aSAjay Gupta return status; 1169247c554aSAjay Gupta } 1170247c554aSAjay Gupta 11715c9ae5a8SAjay Gupta uc->irq = client->irq; 11725c9ae5a8SAjay Gupta 1173247c554aSAjay Gupta uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); 1174247c554aSAjay Gupta if (IS_ERR(uc->ucsi)) { 1175247c554aSAjay Gupta dev_err(uc->dev, "ucsi_register_ppm failed\n"); 1176247c554aSAjay Gupta return PTR_ERR(uc->ucsi); 1177247c554aSAjay Gupta } 1178247c554aSAjay Gupta 1179247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version)); 1180247c554aSAjay Gupta status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) + 1181247c554aSAjay Gupta offsetof(struct ucsi_data, version), 1182247c554aSAjay Gupta sizeof(uc->ppm.data->version)); 1183247c554aSAjay Gupta if (status < 0) { 1184247c554aSAjay Gupta ucsi_unregister_ppm(uc->ucsi); 1185247c554aSAjay Gupta return status; 1186247c554aSAjay Gupta } 1187247c554aSAjay Gupta 1188247c554aSAjay Gupta i2c_set_clientdata(client, uc); 11895c9ae5a8SAjay Gupta 11905c9ae5a8SAjay Gupta status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group); 11915c9ae5a8SAjay Gupta if (status) 11925c9ae5a8SAjay Gupta dev_err(uc->dev, "cannot create sysfs group: %d\n", status); 11935c9ae5a8SAjay Gupta 1194a94ecde4SAjay Gupta pm_runtime_set_active(uc->dev); 1195a94ecde4SAjay Gupta pm_runtime_enable(uc->dev); 1196a94ecde4SAjay Gupta pm_runtime_idle(uc->dev); 1197a94ecde4SAjay Gupta 1198247c554aSAjay Gupta return 0; 1199247c554aSAjay Gupta } 1200247c554aSAjay Gupta 1201247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client) 1202247c554aSAjay Gupta { 1203247c554aSAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1204247c554aSAjay Gupta 1205f0e4cd94SAjay Gupta cancel_work_sync(&uc->pm_work); 12065c9ae5a8SAjay Gupta cancel_work_sync(&uc->work); 1207247c554aSAjay Gupta ucsi_unregister_ppm(uc->ucsi); 1208a94ecde4SAjay Gupta pm_runtime_disable(uc->dev); 12095c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 12105c9ae5a8SAjay Gupta sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); 1211247c554aSAjay Gupta 1212247c554aSAjay Gupta return 0; 1213247c554aSAjay Gupta } 1214247c554aSAjay Gupta 1215247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = { 1216247c554aSAjay Gupta {"ccgx-ucsi", 0}, 1217247c554aSAjay Gupta {} 1218247c554aSAjay Gupta }; 1219247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); 1220247c554aSAjay Gupta 1221a94ecde4SAjay Gupta static int ucsi_ccg_resume(struct device *dev) 1222a94ecde4SAjay Gupta { 1223a94ecde4SAjay Gupta struct i2c_client *client = to_i2c_client(dev); 1224a94ecde4SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1225a94ecde4SAjay Gupta 1226a94ecde4SAjay Gupta return ucsi_resume(uc->ucsi); 1227a94ecde4SAjay Gupta } 1228a94ecde4SAjay Gupta 1229a94ecde4SAjay Gupta static int ucsi_ccg_runtime_suspend(struct device *dev) 1230a94ecde4SAjay Gupta { 1231a94ecde4SAjay Gupta return 0; 1232a94ecde4SAjay Gupta } 1233a94ecde4SAjay Gupta 1234a94ecde4SAjay Gupta static int ucsi_ccg_runtime_resume(struct device *dev) 1235a94ecde4SAjay Gupta { 1236f0e4cd94SAjay Gupta struct i2c_client *client = to_i2c_client(dev); 1237f0e4cd94SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1238f0e4cd94SAjay Gupta bool schedule = true; 1239f0e4cd94SAjay Gupta 1240f0e4cd94SAjay Gupta /* 1241f0e4cd94SAjay Gupta * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue 1242f0e4cd94SAjay Gupta * of missing interrupt when a device is connected for runtime resume. 1243f0e4cd94SAjay Gupta * Schedule a work to call ISR as a workaround. 1244f0e4cd94SAjay Gupta */ 1245f0e4cd94SAjay Gupta if (uc->fw_build == CCG_FW_BUILD_NVIDIA && 1246f0e4cd94SAjay Gupta uc->fw_version <= CCG_OLD_FW_VERSION) { 1247f0e4cd94SAjay Gupta mutex_lock(&uc->lock); 1248f0e4cd94SAjay Gupta if (!uc->run_isr) { 1249f0e4cd94SAjay Gupta uc->run_isr = true; 1250f0e4cd94SAjay Gupta schedule = false; 1251f0e4cd94SAjay Gupta } 1252f0e4cd94SAjay Gupta mutex_unlock(&uc->lock); 1253f0e4cd94SAjay Gupta 1254f0e4cd94SAjay Gupta if (schedule) 1255f0e4cd94SAjay Gupta schedule_work(&uc->pm_work); 1256f0e4cd94SAjay Gupta } 1257f0e4cd94SAjay Gupta 1258a94ecde4SAjay Gupta return 0; 1259a94ecde4SAjay Gupta } 1260a94ecde4SAjay Gupta 1261a94ecde4SAjay Gupta static const struct dev_pm_ops ucsi_ccg_pm = { 1262a94ecde4SAjay Gupta .resume = ucsi_ccg_resume, 1263a94ecde4SAjay Gupta .runtime_suspend = ucsi_ccg_runtime_suspend, 1264a94ecde4SAjay Gupta .runtime_resume = ucsi_ccg_runtime_resume, 1265a94ecde4SAjay Gupta }; 1266a94ecde4SAjay Gupta 1267247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = { 1268247c554aSAjay Gupta .driver = { 1269247c554aSAjay Gupta .name = "ucsi_ccg", 1270a94ecde4SAjay Gupta .pm = &ucsi_ccg_pm, 1271247c554aSAjay Gupta }, 1272247c554aSAjay Gupta .probe = ucsi_ccg_probe, 1273247c554aSAjay Gupta .remove = ucsi_ccg_remove, 1274247c554aSAjay Gupta .id_table = ucsi_ccg_device_id, 1275247c554aSAjay Gupta }; 1276247c554aSAjay Gupta 1277247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver); 1278247c554aSAjay Gupta 1279247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); 1280247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); 1281247c554aSAjay Gupta MODULE_LICENSE("GPL v2"); 1282