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> 17247c554aSAjay Gupta 18247c554aSAjay Gupta #include <asm/unaligned.h> 19247c554aSAjay Gupta #include "ucsi.h" 20247c554aSAjay Gupta 215d438e20SAjay Gupta enum enum_fw_mode { 225d438e20SAjay Gupta BOOT, /* bootloader */ 235d438e20SAjay Gupta FW1, /* FW partition-1 (contains secondary fw) */ 245d438e20SAjay Gupta FW2, /* FW partition-2 (contains primary fw) */ 255d438e20SAjay Gupta FW_INVALID, 265d438e20SAjay Gupta }; 275d438e20SAjay Gupta 285c9ae5a8SAjay Gupta #define CCGX_RAB_DEVICE_MODE 0x0000 295c9ae5a8SAjay Gupta #define CCGX_RAB_INTR_REG 0x0006 305c9ae5a8SAjay Gupta #define DEV_INT BIT(0) 315c9ae5a8SAjay Gupta #define PORT0_INT BIT(1) 325c9ae5a8SAjay Gupta #define PORT1_INT BIT(2) 335c9ae5a8SAjay Gupta #define UCSI_READ_INT BIT(7) 345c9ae5a8SAjay Gupta #define CCGX_RAB_JUMP_TO_BOOT 0x0007 355c9ae5a8SAjay Gupta #define TO_BOOT 'J' 365c9ae5a8SAjay Gupta #define TO_ALT_FW 'A' 375c9ae5a8SAjay Gupta #define CCGX_RAB_RESET_REQ 0x0008 385c9ae5a8SAjay Gupta #define RESET_SIG 'R' 395c9ae5a8SAjay Gupta #define CMD_RESET_I2C 0x0 405c9ae5a8SAjay Gupta #define CMD_RESET_DEV 0x1 415c9ae5a8SAjay Gupta #define CCGX_RAB_ENTER_FLASHING 0x000A 425c9ae5a8SAjay Gupta #define FLASH_ENTER_SIG 'P' 435c9ae5a8SAjay Gupta #define CCGX_RAB_VALIDATE_FW 0x000B 445c9ae5a8SAjay Gupta #define CCGX_RAB_FLASH_ROW_RW 0x000C 455c9ae5a8SAjay Gupta #define FLASH_SIG 'F' 465c9ae5a8SAjay Gupta #define FLASH_RD_CMD 0x0 475c9ae5a8SAjay Gupta #define FLASH_WR_CMD 0x1 485c9ae5a8SAjay Gupta #define FLASH_FWCT1_WR_CMD 0x2 495c9ae5a8SAjay Gupta #define FLASH_FWCT2_WR_CMD 0x3 505c9ae5a8SAjay Gupta #define FLASH_FWCT_SIG_WR_CMD 0x4 515c9ae5a8SAjay Gupta #define CCGX_RAB_READ_ALL_VER 0x0010 525c9ae5a8SAjay Gupta #define CCGX_RAB_READ_FW2_VER 0x0020 535c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL 0x0039 545c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START BIT(0) 555c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) 565c9ae5a8SAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) 575c9ae5a8SAjay Gupta #define REG_FLASH_RW_MEM 0x0200 585c9ae5a8SAjay Gupta #define DEV_REG_IDX CCGX_RAB_DEVICE_MODE 595c9ae5a8SAjay Gupta #define CCGX_RAB_PDPORT_ENABLE 0x002C 605c9ae5a8SAjay Gupta #define PDPORT_1 BIT(0) 615c9ae5a8SAjay Gupta #define PDPORT_2 BIT(1) 625c9ae5a8SAjay Gupta #define CCGX_RAB_RESPONSE 0x007E 635c9ae5a8SAjay Gupta #define ASYNC_EVENT BIT(7) 645c9ae5a8SAjay Gupta 655c9ae5a8SAjay Gupta /* CCGx events & async msg codes */ 665c9ae5a8SAjay Gupta #define RESET_COMPLETE 0x80 675c9ae5a8SAjay Gupta #define EVENT_INDEX RESET_COMPLETE 685c9ae5a8SAjay Gupta #define PORT_CONNECT_DET 0x84 695c9ae5a8SAjay Gupta #define PORT_DISCONNECT_DET 0x85 705c9ae5a8SAjay Gupta #define ROLE_SWAP_COMPELETE 0x87 715c9ae5a8SAjay Gupta 725c9ae5a8SAjay Gupta /* ccg firmware */ 735c9ae5a8SAjay Gupta #define CYACD_LINE_SIZE 527 745c9ae5a8SAjay Gupta #define CCG4_ROW_SIZE 256 755c9ae5a8SAjay Gupta #define FW1_METADATA_ROW 0x1FF 765c9ae5a8SAjay Gupta #define FW2_METADATA_ROW 0x1FE 775c9ae5a8SAjay Gupta #define FW_CFG_TABLE_SIG_SIZE 256 785c9ae5a8SAjay Gupta 795c9ae5a8SAjay Gupta static int secondary_fw_min_ver = 41; 805c9ae5a8SAjay Gupta 815c9ae5a8SAjay Gupta enum enum_flash_mode { 825c9ae5a8SAjay Gupta SECONDARY_BL, /* update secondary using bootloader */ 835c9ae5a8SAjay Gupta PRIMARY, /* update primary using secondary */ 845c9ae5a8SAjay Gupta SECONDARY, /* update secondary using primary */ 855c9ae5a8SAjay Gupta FLASH_NOT_NEEDED, /* update not required */ 865c9ae5a8SAjay Gupta FLASH_INVALID, 875c9ae5a8SAjay Gupta }; 885c9ae5a8SAjay Gupta 895c9ae5a8SAjay Gupta static const char * const ccg_fw_names[] = { 905c9ae5a8SAjay Gupta "ccg_boot.cyacd", 915c9ae5a8SAjay Gupta "ccg_primary.cyacd", 925c9ae5a8SAjay Gupta "ccg_secondary.cyacd" 935c9ae5a8SAjay Gupta }; 945c9ae5a8SAjay Gupta 955d438e20SAjay Gupta struct ccg_dev_info { 965d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0) 975d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) 985d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2) 995d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) 1005d438e20SAjay Gupta u8 mode; 1015d438e20SAjay Gupta u8 bl_mode; 1025d438e20SAjay Gupta __le16 silicon_id; 1035d438e20SAjay Gupta __le16 bl_last_row; 1045d438e20SAjay Gupta } __packed; 1055d438e20SAjay Gupta 1065d438e20SAjay Gupta struct version_format { 1075d438e20SAjay Gupta __le16 build; 1085d438e20SAjay Gupta u8 patch; 1095d438e20SAjay Gupta u8 ver; 1105d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0) 1115d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) 1125d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4) 1135d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) 1145d438e20SAjay Gupta } __packed; 1155d438e20SAjay Gupta 1165d438e20SAjay Gupta struct version_info { 1175d438e20SAjay Gupta struct version_format base; 1185d438e20SAjay Gupta struct version_format app; 1195d438e20SAjay Gupta }; 1205d438e20SAjay Gupta 1215c9ae5a8SAjay Gupta struct fw_config_table { 1225c9ae5a8SAjay Gupta u32 identity; 1235c9ae5a8SAjay Gupta u16 table_size; 1245c9ae5a8SAjay Gupta u8 fwct_version; 1255c9ae5a8SAjay Gupta u8 is_key_change; 1265c9ae5a8SAjay Gupta u8 guid[16]; 1275c9ae5a8SAjay Gupta struct version_format base; 1285c9ae5a8SAjay Gupta struct version_format app; 1295c9ae5a8SAjay Gupta u8 primary_fw_digest[32]; 1305c9ae5a8SAjay Gupta u32 key_exp_length; 1315c9ae5a8SAjay Gupta u8 key_modulus[256]; 1325c9ae5a8SAjay Gupta u8 key_exp[4]; 1335c9ae5a8SAjay Gupta }; 1345c9ae5a8SAjay Gupta 1355c9ae5a8SAjay Gupta /* CCGx response codes */ 1365c9ae5a8SAjay Gupta enum ccg_resp_code { 1375c9ae5a8SAjay Gupta CMD_NO_RESP = 0x00, 1385c9ae5a8SAjay Gupta CMD_SUCCESS = 0x02, 1395c9ae5a8SAjay Gupta FLASH_DATA_AVAILABLE = 0x03, 1405c9ae5a8SAjay Gupta CMD_INVALID = 0x05, 1415c9ae5a8SAjay Gupta FLASH_UPDATE_FAIL = 0x07, 1425c9ae5a8SAjay Gupta INVALID_FW = 0x08, 1435c9ae5a8SAjay Gupta INVALID_ARG = 0x09, 1445c9ae5a8SAjay Gupta CMD_NOT_SUPPORT = 0x0A, 1455c9ae5a8SAjay Gupta TRANSACTION_FAIL = 0x0C, 1465c9ae5a8SAjay Gupta PD_CMD_FAIL = 0x0D, 1475c9ae5a8SAjay Gupta UNDEF_ERROR = 0x0F, 1485c9ae5a8SAjay Gupta INVALID_RESP = 0x10, 1495c9ae5a8SAjay Gupta }; 1505c9ae5a8SAjay Gupta 1515c9ae5a8SAjay Gupta #define CCG_EVENT_MAX (EVENT_INDEX + 43) 1525c9ae5a8SAjay Gupta 1535c9ae5a8SAjay Gupta struct ccg_cmd { 1545c9ae5a8SAjay Gupta u16 reg; 1555c9ae5a8SAjay Gupta u32 data; 1565c9ae5a8SAjay Gupta int len; 1575c9ae5a8SAjay Gupta u32 delay; /* ms delay for cmd timeout */ 1585c9ae5a8SAjay Gupta }; 1595c9ae5a8SAjay Gupta 1605c9ae5a8SAjay Gupta struct ccg_resp { 1615c9ae5a8SAjay Gupta u8 code; 1625c9ae5a8SAjay Gupta u8 length; 1635c9ae5a8SAjay Gupta }; 1645c9ae5a8SAjay Gupta 165247c554aSAjay Gupta struct ucsi_ccg { 166247c554aSAjay Gupta struct device *dev; 167247c554aSAjay Gupta struct ucsi *ucsi; 168247c554aSAjay Gupta struct ucsi_ppm ppm; 169247c554aSAjay Gupta struct i2c_client *client; 1705d438e20SAjay Gupta struct ccg_dev_info info; 1715d438e20SAjay Gupta /* version info for boot, primary and secondary */ 1725d438e20SAjay Gupta struct version_info version[FW2 + 1]; 1735c9ae5a8SAjay Gupta /* CCG HPI communication flags */ 1745c9ae5a8SAjay Gupta unsigned long flags; 1755c9ae5a8SAjay Gupta #define RESET_PENDING 0 1765c9ae5a8SAjay Gupta #define DEV_CMD_PENDING 1 1775c9ae5a8SAjay Gupta struct ccg_resp dev_resp; 1785c9ae5a8SAjay Gupta u8 cmd_resp; 1795c9ae5a8SAjay Gupta int port_num; 1805c9ae5a8SAjay Gupta int irq; 1815c9ae5a8SAjay Gupta struct work_struct work; 1825c9ae5a8SAjay Gupta struct mutex lock; /* to sync between user and driver thread */ 183247c554aSAjay Gupta 1845c9ae5a8SAjay Gupta /* fw build with vendor information */ 1855c9ae5a8SAjay Gupta u16 fw_build; 1865c9ae5a8SAjay Gupta }; 187247c554aSAjay Gupta 188247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 189247c554aSAjay Gupta { 190247c554aSAjay Gupta struct i2c_client *client = uc->client; 191247c554aSAjay Gupta const struct i2c_adapter_quirks *quirks = client->adapter->quirks; 192247c554aSAjay Gupta unsigned char buf[2]; 193247c554aSAjay Gupta struct i2c_msg msgs[] = { 194247c554aSAjay Gupta { 195247c554aSAjay Gupta .addr = client->addr, 196247c554aSAjay Gupta .flags = 0x0, 197247c554aSAjay Gupta .len = sizeof(buf), 198247c554aSAjay Gupta .buf = buf, 199247c554aSAjay Gupta }, 200247c554aSAjay Gupta { 201247c554aSAjay Gupta .addr = client->addr, 202247c554aSAjay Gupta .flags = I2C_M_RD, 203247c554aSAjay Gupta .buf = data, 204247c554aSAjay Gupta }, 205247c554aSAjay Gupta }; 206247c554aSAjay Gupta u32 rlen, rem_len = len, max_read_len = len; 207247c554aSAjay Gupta int status; 208247c554aSAjay Gupta 209247c554aSAjay Gupta /* check any max_read_len limitation on i2c adapter */ 210247c554aSAjay Gupta if (quirks && quirks->max_read_len) 211247c554aSAjay Gupta max_read_len = quirks->max_read_len; 212247c554aSAjay Gupta 213247c554aSAjay Gupta while (rem_len > 0) { 214247c554aSAjay Gupta msgs[1].buf = &data[len - rem_len]; 215247c554aSAjay Gupta rlen = min_t(u16, rem_len, max_read_len); 216247c554aSAjay Gupta msgs[1].len = rlen; 217247c554aSAjay Gupta put_unaligned_le16(rab, buf); 218247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 219247c554aSAjay Gupta if (status < 0) { 220247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 221247c554aSAjay Gupta return status; 222247c554aSAjay Gupta } 223247c554aSAjay Gupta rab += rlen; 224247c554aSAjay Gupta rem_len -= rlen; 225247c554aSAjay Gupta } 226247c554aSAjay Gupta 227247c554aSAjay Gupta return 0; 228247c554aSAjay Gupta } 229247c554aSAjay Gupta 230247c554aSAjay Gupta static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 231247c554aSAjay Gupta { 232247c554aSAjay Gupta struct i2c_client *client = uc->client; 233247c554aSAjay Gupta unsigned char *buf; 234247c554aSAjay Gupta struct i2c_msg msgs[] = { 235247c554aSAjay Gupta { 236247c554aSAjay Gupta .addr = client->addr, 237247c554aSAjay Gupta .flags = 0x0, 238247c554aSAjay Gupta } 239247c554aSAjay Gupta }; 240247c554aSAjay Gupta int status; 241247c554aSAjay Gupta 242247c554aSAjay Gupta buf = kzalloc(len + sizeof(rab), GFP_KERNEL); 243247c554aSAjay Gupta if (!buf) 244247c554aSAjay Gupta return -ENOMEM; 245247c554aSAjay Gupta 246247c554aSAjay Gupta put_unaligned_le16(rab, buf); 247247c554aSAjay Gupta memcpy(buf + sizeof(rab), data, len); 248247c554aSAjay Gupta 249247c554aSAjay Gupta msgs[0].len = len + sizeof(rab); 250247c554aSAjay Gupta msgs[0].buf = buf; 251247c554aSAjay Gupta 252247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 253247c554aSAjay Gupta if (status < 0) { 254247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 255247c554aSAjay Gupta kfree(buf); 256247c554aSAjay Gupta return status; 257247c554aSAjay Gupta } 258247c554aSAjay Gupta 259247c554aSAjay Gupta kfree(buf); 260247c554aSAjay Gupta return 0; 261247c554aSAjay Gupta } 262247c554aSAjay Gupta 263247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc) 264247c554aSAjay Gupta { 265247c554aSAjay Gupta unsigned int count = 10; 266247c554aSAjay Gupta u8 data; 267247c554aSAjay Gupta int status; 268247c554aSAjay Gupta 269247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_STOP; 270247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 271247c554aSAjay Gupta if (status < 0) 272247c554aSAjay Gupta return status; 273247c554aSAjay Gupta 274247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_START; 275247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 276247c554aSAjay Gupta if (status < 0) 277247c554aSAjay Gupta return status; 278247c554aSAjay Gupta 279247c554aSAjay Gupta /* 280247c554aSAjay Gupta * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control 281247c554aSAjay Gupta * register write will push response which must be cleared. 282247c554aSAjay Gupta */ 283247c554aSAjay Gupta do { 284247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 285247c554aSAjay Gupta if (status < 0) 286247c554aSAjay Gupta return status; 287247c554aSAjay Gupta 288247c554aSAjay Gupta if (!data) 289247c554aSAjay Gupta return 0; 290247c554aSAjay Gupta 291247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 292247c554aSAjay Gupta if (status < 0) 293247c554aSAjay Gupta return status; 294247c554aSAjay Gupta 295247c554aSAjay Gupta usleep_range(10000, 11000); 296247c554aSAjay Gupta } while (--count); 297247c554aSAjay Gupta 298247c554aSAjay Gupta return -ETIMEDOUT; 299247c554aSAjay Gupta } 300247c554aSAjay Gupta 301247c554aSAjay Gupta static int ucsi_ccg_send_data(struct ucsi_ccg *uc) 302247c554aSAjay Gupta { 303247c554aSAjay Gupta u8 *ppm = (u8 *)uc->ppm.data; 304247c554aSAjay Gupta int status; 305247c554aSAjay Gupta u16 rab; 306247c554aSAjay Gupta 307247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out)); 308247c554aSAjay Gupta status = ccg_write(uc, rab, ppm + 309247c554aSAjay Gupta offsetof(struct ucsi_data, message_out), 310247c554aSAjay Gupta sizeof(uc->ppm.data->message_out)); 311247c554aSAjay Gupta if (status < 0) 312247c554aSAjay Gupta return status; 313247c554aSAjay Gupta 314247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl)); 315247c554aSAjay Gupta return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl), 316247c554aSAjay Gupta sizeof(uc->ppm.data->ctrl)); 317247c554aSAjay Gupta } 318247c554aSAjay Gupta 319247c554aSAjay Gupta static int ucsi_ccg_recv_data(struct ucsi_ccg *uc) 320247c554aSAjay Gupta { 321247c554aSAjay Gupta u8 *ppm = (u8 *)uc->ppm.data; 322247c554aSAjay Gupta int status; 323247c554aSAjay Gupta u16 rab; 324247c554aSAjay Gupta 325247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci)); 326247c554aSAjay Gupta status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci), 327247c554aSAjay Gupta sizeof(uc->ppm.data->cci)); 328247c554aSAjay Gupta if (status < 0) 329247c554aSAjay Gupta return status; 330247c554aSAjay Gupta 331247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in)); 332247c554aSAjay Gupta return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in), 333247c554aSAjay Gupta sizeof(uc->ppm.data->message_in)); 334247c554aSAjay Gupta } 335247c554aSAjay Gupta 336247c554aSAjay Gupta static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc) 337247c554aSAjay Gupta { 338247c554aSAjay Gupta int status; 339247c554aSAjay Gupta unsigned char data; 340247c554aSAjay Gupta 341247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 342247c554aSAjay Gupta if (status < 0) 343247c554aSAjay Gupta return status; 344247c554aSAjay Gupta 345247c554aSAjay Gupta return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 346247c554aSAjay Gupta } 347247c554aSAjay Gupta 348247c554aSAjay Gupta static int ucsi_ccg_sync(struct ucsi_ppm *ppm) 349247c554aSAjay Gupta { 350247c554aSAjay Gupta struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); 351247c554aSAjay Gupta int status; 352247c554aSAjay Gupta 353247c554aSAjay Gupta status = ucsi_ccg_recv_data(uc); 354247c554aSAjay Gupta if (status < 0) 355247c554aSAjay Gupta return status; 356247c554aSAjay Gupta 357247c554aSAjay Gupta /* ack interrupt to allow next command to run */ 358247c554aSAjay Gupta return ucsi_ccg_ack_interrupt(uc); 359247c554aSAjay Gupta } 360247c554aSAjay Gupta 361247c554aSAjay Gupta static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl) 362247c554aSAjay Gupta { 363247c554aSAjay Gupta struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); 364247c554aSAjay Gupta 365247c554aSAjay Gupta ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; 366247c554aSAjay Gupta return ucsi_ccg_send_data(uc); 367247c554aSAjay Gupta } 368247c554aSAjay Gupta 369247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data) 370247c554aSAjay Gupta { 371247c554aSAjay Gupta struct ucsi_ccg *uc = data; 372247c554aSAjay Gupta 373247c554aSAjay Gupta ucsi_notify(uc->ucsi); 374247c554aSAjay Gupta 375247c554aSAjay Gupta return IRQ_HANDLED; 376247c554aSAjay Gupta } 377247c554aSAjay Gupta 3785d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc) 3795d438e20SAjay Gupta { 3805d438e20SAjay Gupta int err; 3815d438e20SAjay Gupta 3825d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), 3835d438e20SAjay Gupta sizeof(uc->version)); 3845d438e20SAjay Gupta if (err < 0) 3855d438e20SAjay Gupta return err; 3865d438e20SAjay Gupta 3875d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 3885d438e20SAjay Gupta sizeof(uc->info)); 3895d438e20SAjay Gupta if (err < 0) 3905d438e20SAjay Gupta return err; 3915d438e20SAjay Gupta 3925d438e20SAjay Gupta return 0; 3935d438e20SAjay Gupta } 3945d438e20SAjay Gupta 3955c9ae5a8SAjay Gupta static inline bool invalid_async_evt(int code) 3965c9ae5a8SAjay Gupta { 3975c9ae5a8SAjay Gupta return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX); 3985c9ae5a8SAjay Gupta } 3995c9ae5a8SAjay Gupta 4005c9ae5a8SAjay Gupta static void ccg_process_response(struct ucsi_ccg *uc) 4015c9ae5a8SAjay Gupta { 4025c9ae5a8SAjay Gupta struct device *dev = uc->dev; 4035c9ae5a8SAjay Gupta 4045c9ae5a8SAjay Gupta if (uc->dev_resp.code & ASYNC_EVENT) { 4055c9ae5a8SAjay Gupta if (uc->dev_resp.code == RESET_COMPLETE) { 4065c9ae5a8SAjay Gupta if (test_bit(RESET_PENDING, &uc->flags)) 4075c9ae5a8SAjay Gupta uc->cmd_resp = uc->dev_resp.code; 4085c9ae5a8SAjay Gupta get_fw_info(uc); 4095c9ae5a8SAjay Gupta } 4105c9ae5a8SAjay Gupta if (invalid_async_evt(uc->dev_resp.code)) 4115c9ae5a8SAjay Gupta dev_err(dev, "invalid async evt %d\n", 4125c9ae5a8SAjay Gupta uc->dev_resp.code); 4135c9ae5a8SAjay Gupta } else { 4145c9ae5a8SAjay Gupta if (test_bit(DEV_CMD_PENDING, &uc->flags)) { 4155c9ae5a8SAjay Gupta uc->cmd_resp = uc->dev_resp.code; 4165c9ae5a8SAjay Gupta clear_bit(DEV_CMD_PENDING, &uc->flags); 4175c9ae5a8SAjay Gupta } else { 4185c9ae5a8SAjay Gupta dev_err(dev, "dev resp 0x%04x but no cmd pending\n", 4195c9ae5a8SAjay Gupta uc->dev_resp.code); 4205c9ae5a8SAjay Gupta } 4215c9ae5a8SAjay Gupta } 4225c9ae5a8SAjay Gupta } 4235c9ae5a8SAjay Gupta 4245c9ae5a8SAjay Gupta static int ccg_read_response(struct ucsi_ccg *uc) 4255c9ae5a8SAjay Gupta { 4265c9ae5a8SAjay Gupta unsigned long target = jiffies + msecs_to_jiffies(1000); 4275c9ae5a8SAjay Gupta struct device *dev = uc->dev; 4285c9ae5a8SAjay Gupta u8 intval; 4295c9ae5a8SAjay Gupta int status; 4305c9ae5a8SAjay Gupta 4315c9ae5a8SAjay Gupta /* wait for interrupt status to get updated */ 4325c9ae5a8SAjay Gupta do { 4335c9ae5a8SAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval, 4345c9ae5a8SAjay Gupta sizeof(intval)); 4355c9ae5a8SAjay Gupta if (status < 0) 4365c9ae5a8SAjay Gupta return status; 4375c9ae5a8SAjay Gupta 4385c9ae5a8SAjay Gupta if (intval & DEV_INT) 4395c9ae5a8SAjay Gupta break; 4405c9ae5a8SAjay Gupta usleep_range(500, 600); 4415c9ae5a8SAjay Gupta } while (time_is_after_jiffies(target)); 4425c9ae5a8SAjay Gupta 4435c9ae5a8SAjay Gupta if (time_is_before_jiffies(target)) { 4445c9ae5a8SAjay Gupta dev_err(dev, "response timeout error\n"); 4455c9ae5a8SAjay Gupta return -ETIME; 4465c9ae5a8SAjay Gupta } 4475c9ae5a8SAjay Gupta 4485c9ae5a8SAjay Gupta status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp, 4495c9ae5a8SAjay Gupta sizeof(uc->dev_resp)); 4505c9ae5a8SAjay Gupta if (status < 0) 4515c9ae5a8SAjay Gupta return status; 4525c9ae5a8SAjay Gupta 4535c9ae5a8SAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval)); 4545c9ae5a8SAjay Gupta if (status < 0) 4555c9ae5a8SAjay Gupta return status; 4565c9ae5a8SAjay Gupta 4575c9ae5a8SAjay Gupta return 0; 4585c9ae5a8SAjay Gupta } 4595c9ae5a8SAjay Gupta 4605c9ae5a8SAjay Gupta /* Caller must hold uc->lock */ 4615c9ae5a8SAjay Gupta static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd) 4625c9ae5a8SAjay Gupta { 4635c9ae5a8SAjay Gupta struct device *dev = uc->dev; 4645c9ae5a8SAjay Gupta int ret; 4655c9ae5a8SAjay Gupta 4665c9ae5a8SAjay Gupta switch (cmd->reg & 0xF000) { 4675c9ae5a8SAjay Gupta case DEV_REG_IDX: 4685c9ae5a8SAjay Gupta set_bit(DEV_CMD_PENDING, &uc->flags); 4695c9ae5a8SAjay Gupta break; 4705c9ae5a8SAjay Gupta default: 4715c9ae5a8SAjay Gupta dev_err(dev, "invalid cmd register\n"); 4725c9ae5a8SAjay Gupta break; 4735c9ae5a8SAjay Gupta } 4745c9ae5a8SAjay Gupta 4755c9ae5a8SAjay Gupta ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len); 4765c9ae5a8SAjay Gupta if (ret < 0) 4775c9ae5a8SAjay Gupta return ret; 4785c9ae5a8SAjay Gupta 4795c9ae5a8SAjay Gupta msleep(cmd->delay); 4805c9ae5a8SAjay Gupta 4815c9ae5a8SAjay Gupta ret = ccg_read_response(uc); 4825c9ae5a8SAjay Gupta if (ret < 0) { 4835c9ae5a8SAjay Gupta dev_err(dev, "response read error\n"); 4845c9ae5a8SAjay Gupta switch (cmd->reg & 0xF000) { 4855c9ae5a8SAjay Gupta case DEV_REG_IDX: 4865c9ae5a8SAjay Gupta clear_bit(DEV_CMD_PENDING, &uc->flags); 4875c9ae5a8SAjay Gupta break; 4885c9ae5a8SAjay Gupta default: 4895c9ae5a8SAjay Gupta dev_err(dev, "invalid cmd register\n"); 4905c9ae5a8SAjay Gupta break; 4915c9ae5a8SAjay Gupta } 4925c9ae5a8SAjay Gupta return -EIO; 4935c9ae5a8SAjay Gupta } 4945c9ae5a8SAjay Gupta ccg_process_response(uc); 4955c9ae5a8SAjay Gupta 4965c9ae5a8SAjay Gupta return uc->cmd_resp; 4975c9ae5a8SAjay Gupta } 4985c9ae5a8SAjay Gupta 4995c9ae5a8SAjay Gupta static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc) 5005c9ae5a8SAjay Gupta { 5015c9ae5a8SAjay Gupta struct ccg_cmd cmd; 5025c9ae5a8SAjay Gupta int ret; 5035c9ae5a8SAjay Gupta 5045c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_ENTER_FLASHING; 5055c9ae5a8SAjay Gupta cmd.data = FLASH_ENTER_SIG; 5065c9ae5a8SAjay Gupta cmd.len = 1; 5075c9ae5a8SAjay Gupta cmd.delay = 50; 5085c9ae5a8SAjay Gupta 5095c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 5105c9ae5a8SAjay Gupta 5115c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 5125c9ae5a8SAjay Gupta 5135c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 5145c9ae5a8SAjay Gupta 5155c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 5165c9ae5a8SAjay Gupta dev_err(uc->dev, "enter flashing failed ret=%d\n", ret); 5175c9ae5a8SAjay Gupta return ret; 5185c9ae5a8SAjay Gupta } 5195c9ae5a8SAjay Gupta 5205c9ae5a8SAjay Gupta return 0; 5215c9ae5a8SAjay Gupta } 5225c9ae5a8SAjay Gupta 5235c9ae5a8SAjay Gupta static int ccg_cmd_reset(struct ucsi_ccg *uc) 5245c9ae5a8SAjay Gupta { 5255c9ae5a8SAjay Gupta struct ccg_cmd cmd; 5265c9ae5a8SAjay Gupta u8 *p; 5275c9ae5a8SAjay Gupta int ret; 5285c9ae5a8SAjay Gupta 5295c9ae5a8SAjay Gupta p = (u8 *)&cmd.data; 5305c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_RESET_REQ; 5315c9ae5a8SAjay Gupta p[0] = RESET_SIG; 5325c9ae5a8SAjay Gupta p[1] = CMD_RESET_DEV; 5335c9ae5a8SAjay Gupta cmd.len = 2; 5345c9ae5a8SAjay Gupta cmd.delay = 5000; 5355c9ae5a8SAjay Gupta 5365c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 5375c9ae5a8SAjay Gupta 5385c9ae5a8SAjay Gupta set_bit(RESET_PENDING, &uc->flags); 5395c9ae5a8SAjay Gupta 5405c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 5415c9ae5a8SAjay Gupta if (ret != RESET_COMPLETE) 5425c9ae5a8SAjay Gupta goto err_clear_flag; 5435c9ae5a8SAjay Gupta 5445c9ae5a8SAjay Gupta ret = 0; 5455c9ae5a8SAjay Gupta 5465c9ae5a8SAjay Gupta err_clear_flag: 5475c9ae5a8SAjay Gupta clear_bit(RESET_PENDING, &uc->flags); 5485c9ae5a8SAjay Gupta 5495c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 5505c9ae5a8SAjay Gupta 5515c9ae5a8SAjay Gupta return ret; 5525c9ae5a8SAjay Gupta } 5535c9ae5a8SAjay Gupta 5545c9ae5a8SAjay Gupta static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable) 5555c9ae5a8SAjay Gupta { 5565c9ae5a8SAjay Gupta struct ccg_cmd cmd; 5575c9ae5a8SAjay Gupta int ret; 5585c9ae5a8SAjay Gupta 5595c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_PDPORT_ENABLE; 5605c9ae5a8SAjay Gupta if (enable) 5615c9ae5a8SAjay Gupta cmd.data = (uc->port_num == 1) ? 5625c9ae5a8SAjay Gupta PDPORT_1 : (PDPORT_1 | PDPORT_2); 5635c9ae5a8SAjay Gupta else 5645c9ae5a8SAjay Gupta cmd.data = 0x0; 5655c9ae5a8SAjay Gupta cmd.len = 1; 5665c9ae5a8SAjay Gupta cmd.delay = 10; 5675c9ae5a8SAjay Gupta 5685c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 5695c9ae5a8SAjay Gupta 5705c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 5715c9ae5a8SAjay Gupta 5725c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 5735c9ae5a8SAjay Gupta 5745c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 5755c9ae5a8SAjay Gupta dev_err(uc->dev, "port control failed ret=%d\n", ret); 5765c9ae5a8SAjay Gupta return ret; 5775c9ae5a8SAjay Gupta } 5785c9ae5a8SAjay Gupta return 0; 5795c9ae5a8SAjay Gupta } 5805c9ae5a8SAjay Gupta 5815c9ae5a8SAjay Gupta static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode) 5825c9ae5a8SAjay Gupta { 5835c9ae5a8SAjay Gupta struct ccg_cmd cmd; 5845c9ae5a8SAjay Gupta int ret; 5855c9ae5a8SAjay Gupta 5865c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_JUMP_TO_BOOT; 5875c9ae5a8SAjay Gupta 5885c9ae5a8SAjay Gupta if (bl_mode) 5895c9ae5a8SAjay Gupta cmd.data = TO_BOOT; 5905c9ae5a8SAjay Gupta else 5915c9ae5a8SAjay Gupta cmd.data = TO_ALT_FW; 5925c9ae5a8SAjay Gupta 5935c9ae5a8SAjay Gupta cmd.len = 1; 5945c9ae5a8SAjay Gupta cmd.delay = 100; 5955c9ae5a8SAjay Gupta 5965c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 5975c9ae5a8SAjay Gupta 5985c9ae5a8SAjay Gupta set_bit(RESET_PENDING, &uc->flags); 5995c9ae5a8SAjay Gupta 6005c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 6015c9ae5a8SAjay Gupta if (ret != RESET_COMPLETE) 6025c9ae5a8SAjay Gupta goto err_clear_flag; 6035c9ae5a8SAjay Gupta 6045c9ae5a8SAjay Gupta ret = 0; 6055c9ae5a8SAjay Gupta 6065c9ae5a8SAjay Gupta err_clear_flag: 6075c9ae5a8SAjay Gupta clear_bit(RESET_PENDING, &uc->flags); 6085c9ae5a8SAjay Gupta 6095c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 6105c9ae5a8SAjay Gupta 6115c9ae5a8SAjay Gupta return ret; 6125c9ae5a8SAjay Gupta } 6135c9ae5a8SAjay Gupta 6145c9ae5a8SAjay Gupta static int 6155c9ae5a8SAjay Gupta ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row, 6165c9ae5a8SAjay Gupta const void *data, u8 fcmd) 6175c9ae5a8SAjay Gupta { 6185c9ae5a8SAjay Gupta struct i2c_client *client = uc->client; 6195c9ae5a8SAjay Gupta struct ccg_cmd cmd; 6205c9ae5a8SAjay Gupta u8 buf[CCG4_ROW_SIZE + 2]; 6215c9ae5a8SAjay Gupta u8 *p; 6225c9ae5a8SAjay Gupta int ret; 6235c9ae5a8SAjay Gupta 6245c9ae5a8SAjay Gupta /* Copy the data into the flash read/write memory. */ 6255c9ae5a8SAjay Gupta put_unaligned_le16(REG_FLASH_RW_MEM, buf); 6265c9ae5a8SAjay Gupta 6275c9ae5a8SAjay Gupta memcpy(buf + 2, data, CCG4_ROW_SIZE); 6285c9ae5a8SAjay Gupta 6295c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 6305c9ae5a8SAjay Gupta 6315c9ae5a8SAjay Gupta ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2); 6325c9ae5a8SAjay Gupta if (ret != CCG4_ROW_SIZE + 2) { 6335c9ae5a8SAjay Gupta dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret); 6345c9ae5a8SAjay Gupta return ret < 0 ? ret : -EIO; 6355c9ae5a8SAjay Gupta } 6365c9ae5a8SAjay Gupta 6375c9ae5a8SAjay Gupta /* Use the FLASH_ROW_READ_WRITE register to trigger */ 6385c9ae5a8SAjay Gupta /* writing of data to the desired flash row */ 6395c9ae5a8SAjay Gupta p = (u8 *)&cmd.data; 6405c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_FLASH_ROW_RW; 6415c9ae5a8SAjay Gupta p[0] = FLASH_SIG; 6425c9ae5a8SAjay Gupta p[1] = fcmd; 6435c9ae5a8SAjay Gupta put_unaligned_le16(row, &p[2]); 6445c9ae5a8SAjay Gupta cmd.len = 4; 6455c9ae5a8SAjay Gupta cmd.delay = 50; 6465c9ae5a8SAjay Gupta if (fcmd == FLASH_FWCT_SIG_WR_CMD) 6475c9ae5a8SAjay Gupta cmd.delay += 400; 6485c9ae5a8SAjay Gupta if (row == 510) 6495c9ae5a8SAjay Gupta cmd.delay += 220; 6505c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 6515c9ae5a8SAjay Gupta 6525c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 6535c9ae5a8SAjay Gupta 6545c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) { 6555c9ae5a8SAjay Gupta dev_err(uc->dev, "write flash row failed ret=%d\n", ret); 6565c9ae5a8SAjay Gupta return ret; 6575c9ae5a8SAjay Gupta } 6585c9ae5a8SAjay Gupta 6595c9ae5a8SAjay Gupta return 0; 6605c9ae5a8SAjay Gupta } 6615c9ae5a8SAjay Gupta 6625c9ae5a8SAjay Gupta static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid) 6635c9ae5a8SAjay Gupta { 6645c9ae5a8SAjay Gupta struct ccg_cmd cmd; 6655c9ae5a8SAjay Gupta int ret; 6665c9ae5a8SAjay Gupta 6675c9ae5a8SAjay Gupta cmd.reg = CCGX_RAB_VALIDATE_FW; 6685c9ae5a8SAjay Gupta cmd.data = fwid; 6695c9ae5a8SAjay Gupta cmd.len = 1; 6705c9ae5a8SAjay Gupta cmd.delay = 500; 6715c9ae5a8SAjay Gupta 6725c9ae5a8SAjay Gupta mutex_lock(&uc->lock); 6735c9ae5a8SAjay Gupta 6745c9ae5a8SAjay Gupta ret = ccg_send_command(uc, &cmd); 6755c9ae5a8SAjay Gupta 6765c9ae5a8SAjay Gupta mutex_unlock(&uc->lock); 6775c9ae5a8SAjay Gupta 6785c9ae5a8SAjay Gupta if (ret != CMD_SUCCESS) 6795c9ae5a8SAjay Gupta return ret; 6805c9ae5a8SAjay Gupta 6815c9ae5a8SAjay Gupta return 0; 6825c9ae5a8SAjay Gupta } 6835c9ae5a8SAjay Gupta 6845c9ae5a8SAjay Gupta static bool ccg_check_vendor_version(struct ucsi_ccg *uc, 6855c9ae5a8SAjay Gupta struct version_format *app, 6865c9ae5a8SAjay Gupta struct fw_config_table *fw_cfg) 6875c9ae5a8SAjay Gupta { 6885c9ae5a8SAjay Gupta struct device *dev = uc->dev; 6895c9ae5a8SAjay Gupta 6905c9ae5a8SAjay Gupta /* Check if the fw build is for supported vendors */ 6915c9ae5a8SAjay Gupta if (le16_to_cpu(app->build) != uc->fw_build) { 6925c9ae5a8SAjay Gupta dev_info(dev, "current fw is not from supported vendor\n"); 6935c9ae5a8SAjay Gupta return false; 6945c9ae5a8SAjay Gupta } 6955c9ae5a8SAjay Gupta 6965c9ae5a8SAjay Gupta /* Check if the new fw build is for supported vendors */ 6975c9ae5a8SAjay Gupta if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) { 6985c9ae5a8SAjay Gupta dev_info(dev, "new fw is not from supported vendor\n"); 6995c9ae5a8SAjay Gupta return false; 7005c9ae5a8SAjay Gupta } 7015c9ae5a8SAjay Gupta return true; 7025c9ae5a8SAjay Gupta } 7035c9ae5a8SAjay Gupta 7045c9ae5a8SAjay Gupta static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, 7055c9ae5a8SAjay Gupta struct version_format *app) 7065c9ae5a8SAjay Gupta { 7075c9ae5a8SAjay Gupta const struct firmware *fw = NULL; 7085c9ae5a8SAjay Gupta struct device *dev = uc->dev; 7095c9ae5a8SAjay Gupta struct fw_config_table fw_cfg; 7105c9ae5a8SAjay Gupta u32 cur_version, new_version; 7115c9ae5a8SAjay Gupta bool is_later = false; 7125c9ae5a8SAjay Gupta 7135c9ae5a8SAjay Gupta if (request_firmware(&fw, fw_name, dev) != 0) { 7145c9ae5a8SAjay Gupta dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name); 7155c9ae5a8SAjay Gupta return false; 7165c9ae5a8SAjay Gupta } 7175c9ae5a8SAjay Gupta 7185c9ae5a8SAjay Gupta /* 7195c9ae5a8SAjay Gupta * check if signed fw 7205c9ae5a8SAjay Gupta * last part of fw image is fw cfg table and signature 7215c9ae5a8SAjay Gupta */ 7225c9ae5a8SAjay Gupta if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE) 7235c9ae5a8SAjay Gupta goto out_release_firmware; 7245c9ae5a8SAjay Gupta 7255c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - 7265c9ae5a8SAjay Gupta sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg)); 7275c9ae5a8SAjay Gupta 7285c9ae5a8SAjay Gupta if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) { 7295c9ae5a8SAjay Gupta dev_info(dev, "not a signed image\n"); 7305c9ae5a8SAjay Gupta goto out_release_firmware; 7315c9ae5a8SAjay Gupta } 7325c9ae5a8SAjay Gupta 7335c9ae5a8SAjay Gupta /* compare input version with FWCT version */ 7345c9ae5a8SAjay Gupta cur_version = le16_to_cpu(app->build) | app->patch << 16 | 7355c9ae5a8SAjay Gupta app->ver << 24; 7365c9ae5a8SAjay Gupta 7375c9ae5a8SAjay Gupta new_version = le16_to_cpu(fw_cfg.app.build) | fw_cfg.app.patch << 16 | 7385c9ae5a8SAjay Gupta fw_cfg.app.ver << 24; 7395c9ae5a8SAjay Gupta 7405c9ae5a8SAjay Gupta if (!ccg_check_vendor_version(uc, app, &fw_cfg)) 7415c9ae5a8SAjay Gupta goto out_release_firmware; 7425c9ae5a8SAjay Gupta 7435c9ae5a8SAjay Gupta if (new_version > cur_version) 7445c9ae5a8SAjay Gupta is_later = true; 7455c9ae5a8SAjay Gupta 7465c9ae5a8SAjay Gupta out_release_firmware: 7475c9ae5a8SAjay Gupta release_firmware(fw); 7485c9ae5a8SAjay Gupta return is_later; 7495c9ae5a8SAjay Gupta } 7505c9ae5a8SAjay Gupta 7515c9ae5a8SAjay Gupta static int ccg_fw_update_needed(struct ucsi_ccg *uc, 7525c9ae5a8SAjay Gupta enum enum_flash_mode *mode) 7535c9ae5a8SAjay Gupta { 7545c9ae5a8SAjay Gupta struct device *dev = uc->dev; 7555c9ae5a8SAjay Gupta int err; 7565c9ae5a8SAjay Gupta struct version_info version[3]; 7575c9ae5a8SAjay Gupta 7585c9ae5a8SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 7595c9ae5a8SAjay Gupta sizeof(uc->info)); 7605c9ae5a8SAjay Gupta if (err) { 7615c9ae5a8SAjay Gupta dev_err(dev, "read device mode failed\n"); 7625c9ae5a8SAjay Gupta return err; 7635c9ae5a8SAjay Gupta } 7645c9ae5a8SAjay Gupta 7655c9ae5a8SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version, 7665c9ae5a8SAjay Gupta sizeof(version)); 7675c9ae5a8SAjay Gupta if (err) { 7685c9ae5a8SAjay Gupta dev_err(dev, "read device mode failed\n"); 7695c9ae5a8SAjay Gupta return err; 7705c9ae5a8SAjay Gupta } 7715c9ae5a8SAjay Gupta 7725c9ae5a8SAjay Gupta if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0", 7735c9ae5a8SAjay Gupta sizeof(struct version_info)) == 0) { 7745c9ae5a8SAjay Gupta dev_info(dev, "secondary fw is not flashed\n"); 7755c9ae5a8SAjay Gupta *mode = SECONDARY_BL; 7765c9ae5a8SAjay Gupta } else if (le16_to_cpu(version[FW1].base.build) < 7775c9ae5a8SAjay Gupta secondary_fw_min_ver) { 7785c9ae5a8SAjay Gupta dev_info(dev, "secondary fw version is too low (< %d)\n", 7795c9ae5a8SAjay Gupta secondary_fw_min_ver); 7805c9ae5a8SAjay Gupta *mode = SECONDARY; 7815c9ae5a8SAjay Gupta } else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0", 7825c9ae5a8SAjay Gupta sizeof(struct version_info)) == 0) { 7835c9ae5a8SAjay Gupta dev_info(dev, "primary fw is not flashed\n"); 7845c9ae5a8SAjay Gupta *mode = PRIMARY; 7855c9ae5a8SAjay Gupta } else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY], 7865c9ae5a8SAjay Gupta &version[FW2].app)) { 7875c9ae5a8SAjay Gupta dev_info(dev, "found primary fw with later version\n"); 7885c9ae5a8SAjay Gupta *mode = PRIMARY; 7895c9ae5a8SAjay Gupta } else { 7905c9ae5a8SAjay Gupta dev_info(dev, "secondary and primary fw are the latest\n"); 7915c9ae5a8SAjay Gupta *mode = FLASH_NOT_NEEDED; 7925c9ae5a8SAjay Gupta } 7935c9ae5a8SAjay Gupta return 0; 7945c9ae5a8SAjay Gupta } 7955c9ae5a8SAjay Gupta 7965c9ae5a8SAjay Gupta static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) 7975c9ae5a8SAjay Gupta { 7985c9ae5a8SAjay Gupta struct device *dev = uc->dev; 7995c9ae5a8SAjay Gupta const struct firmware *fw = NULL; 8005c9ae5a8SAjay Gupta const char *p, *s; 8015c9ae5a8SAjay Gupta const char *eof; 8025c9ae5a8SAjay Gupta int err, row, len, line_sz, line_cnt = 0; 8035c9ae5a8SAjay Gupta unsigned long start_time = jiffies; 8045c9ae5a8SAjay Gupta struct fw_config_table fw_cfg; 8055c9ae5a8SAjay Gupta u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE]; 8065c9ae5a8SAjay Gupta u8 *wr_buf; 8075c9ae5a8SAjay Gupta 8085c9ae5a8SAjay Gupta err = request_firmware(&fw, ccg_fw_names[mode], dev); 8095c9ae5a8SAjay Gupta if (err) { 8105c9ae5a8SAjay Gupta dev_err(dev, "request %s failed err=%d\n", 8115c9ae5a8SAjay Gupta ccg_fw_names[mode], err); 8125c9ae5a8SAjay Gupta return err; 8135c9ae5a8SAjay Gupta } 8145c9ae5a8SAjay Gupta 8155c9ae5a8SAjay Gupta if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >> 8165c9ae5a8SAjay Gupta CCG_DEVINFO_FWMODE_SHIFT) == FW2) { 8175c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, false); 8185c9ae5a8SAjay Gupta if (err < 0) 8195c9ae5a8SAjay Gupta goto release_fw; 8205c9ae5a8SAjay Gupta err = ccg_cmd_jump_boot_mode(uc, 0); 8215c9ae5a8SAjay Gupta if (err < 0) 8225c9ae5a8SAjay Gupta goto release_fw; 8235c9ae5a8SAjay Gupta } 8245c9ae5a8SAjay Gupta 8255c9ae5a8SAjay Gupta eof = fw->data + fw->size; 8265c9ae5a8SAjay Gupta 8275c9ae5a8SAjay Gupta /* 8285c9ae5a8SAjay Gupta * check if signed fw 8295c9ae5a8SAjay Gupta * last part of fw image is fw cfg table and signature 8305c9ae5a8SAjay Gupta */ 8315c9ae5a8SAjay Gupta if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig)) 8325c9ae5a8SAjay Gupta goto not_signed_fw; 8335c9ae5a8SAjay Gupta 8345c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - 8355c9ae5a8SAjay Gupta sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg)); 8365c9ae5a8SAjay Gupta 8375c9ae5a8SAjay Gupta if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) { 8385c9ae5a8SAjay Gupta dev_info(dev, "not a signed image\n"); 8395c9ae5a8SAjay Gupta goto not_signed_fw; 8405c9ae5a8SAjay Gupta } 8415c9ae5a8SAjay Gupta eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig); 8425c9ae5a8SAjay Gupta 8435c9ae5a8SAjay Gupta memcpy((uint8_t *)&fw_cfg_sig, 8445c9ae5a8SAjay Gupta fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig)); 8455c9ae5a8SAjay Gupta 8465c9ae5a8SAjay Gupta /* flash fw config table and signature first */ 8475c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg, 8485c9ae5a8SAjay Gupta FLASH_FWCT1_WR_CMD); 8495c9ae5a8SAjay Gupta if (err) 8505c9ae5a8SAjay Gupta goto release_fw; 8515c9ae5a8SAjay Gupta 8525c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE, 8535c9ae5a8SAjay Gupta FLASH_FWCT2_WR_CMD); 8545c9ae5a8SAjay Gupta if (err) 8555c9ae5a8SAjay Gupta goto release_fw; 8565c9ae5a8SAjay Gupta 8575c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig, 8585c9ae5a8SAjay Gupta FLASH_FWCT_SIG_WR_CMD); 8595c9ae5a8SAjay Gupta if (err) 8605c9ae5a8SAjay Gupta goto release_fw; 8615c9ae5a8SAjay Gupta 8625c9ae5a8SAjay Gupta not_signed_fw: 8635c9ae5a8SAjay Gupta wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL); 8645c9ae5a8SAjay Gupta if (!wr_buf) 8655c9ae5a8SAjay Gupta return -ENOMEM; 8665c9ae5a8SAjay Gupta 8675c9ae5a8SAjay Gupta err = ccg_cmd_enter_flashing(uc); 8685c9ae5a8SAjay Gupta if (err) 8695c9ae5a8SAjay Gupta goto release_mem; 8705c9ae5a8SAjay Gupta 8715c9ae5a8SAjay Gupta /***************************************************************** 8725c9ae5a8SAjay Gupta * CCG firmware image (.cyacd) file line format 8735c9ae5a8SAjay Gupta * 8745c9ae5a8SAjay Gupta * :00rrrrllll[dd....]cc/r/n 8755c9ae5a8SAjay Gupta * 8765c9ae5a8SAjay Gupta * :00 header 8775c9ae5a8SAjay Gupta * rrrr is row number to flash (4 char) 8785c9ae5a8SAjay Gupta * llll is data len to flash (4 char) 8795c9ae5a8SAjay Gupta * dd is a data field represents one byte of data (512 char) 8805c9ae5a8SAjay Gupta * cc is checksum (2 char) 8815c9ae5a8SAjay Gupta * \r\n newline 8825c9ae5a8SAjay Gupta * 8835c9ae5a8SAjay Gupta * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527 8845c9ae5a8SAjay Gupta * 8855c9ae5a8SAjay Gupta *****************************************************************/ 8865c9ae5a8SAjay Gupta 8875c9ae5a8SAjay Gupta p = strnchr(fw->data, fw->size, ':'); 8885c9ae5a8SAjay Gupta while (p < eof) { 8895c9ae5a8SAjay Gupta s = strnchr(p + 1, eof - p - 1, ':'); 8905c9ae5a8SAjay Gupta 8915c9ae5a8SAjay Gupta if (!s) 8925c9ae5a8SAjay Gupta s = eof; 8935c9ae5a8SAjay Gupta 8945c9ae5a8SAjay Gupta line_sz = s - p; 8955c9ae5a8SAjay Gupta 8965c9ae5a8SAjay Gupta if (line_sz != CYACD_LINE_SIZE) { 8975c9ae5a8SAjay Gupta dev_err(dev, "Bad FW format line_sz=%d\n", line_sz); 8985c9ae5a8SAjay Gupta err = -EINVAL; 8995c9ae5a8SAjay Gupta goto release_mem; 9005c9ae5a8SAjay Gupta } 9015c9ae5a8SAjay Gupta 9025c9ae5a8SAjay Gupta if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) { 9035c9ae5a8SAjay Gupta err = -EINVAL; 9045c9ae5a8SAjay Gupta goto release_mem; 9055c9ae5a8SAjay Gupta } 9065c9ae5a8SAjay Gupta 9075c9ae5a8SAjay Gupta row = get_unaligned_be16(wr_buf); 9085c9ae5a8SAjay Gupta len = get_unaligned_be16(&wr_buf[2]); 9095c9ae5a8SAjay Gupta 9105c9ae5a8SAjay Gupta if (len != CCG4_ROW_SIZE) { 9115c9ae5a8SAjay Gupta err = -EINVAL; 9125c9ae5a8SAjay Gupta goto release_mem; 9135c9ae5a8SAjay Gupta } 9145c9ae5a8SAjay Gupta 9155c9ae5a8SAjay Gupta err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4, 9165c9ae5a8SAjay Gupta FLASH_WR_CMD); 9175c9ae5a8SAjay Gupta if (err) 9185c9ae5a8SAjay Gupta goto release_mem; 9195c9ae5a8SAjay Gupta 9205c9ae5a8SAjay Gupta line_cnt++; 9215c9ae5a8SAjay Gupta p = s; 9225c9ae5a8SAjay Gupta } 9235c9ae5a8SAjay Gupta 9245c9ae5a8SAjay Gupta dev_info(dev, "total %d row flashed. time: %dms\n", 9255c9ae5a8SAjay Gupta line_cnt, jiffies_to_msecs(jiffies - start_time)); 9265c9ae5a8SAjay Gupta 9275c9ae5a8SAjay Gupta err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 : FW1); 9285c9ae5a8SAjay Gupta if (err) 9295c9ae5a8SAjay Gupta dev_err(dev, "%s validation failed err=%d\n", 9305c9ae5a8SAjay Gupta (mode == PRIMARY) ? "FW2" : "FW1", err); 9315c9ae5a8SAjay Gupta else 9325c9ae5a8SAjay Gupta dev_info(dev, "%s validated\n", 9335c9ae5a8SAjay Gupta (mode == PRIMARY) ? "FW2" : "FW1"); 9345c9ae5a8SAjay Gupta 9355c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, false); 9365c9ae5a8SAjay Gupta if (err < 0) 9375c9ae5a8SAjay Gupta goto release_mem; 9385c9ae5a8SAjay Gupta 9395c9ae5a8SAjay Gupta err = ccg_cmd_reset(uc); 9405c9ae5a8SAjay Gupta if (err < 0) 9415c9ae5a8SAjay Gupta goto release_mem; 9425c9ae5a8SAjay Gupta 9435c9ae5a8SAjay Gupta err = ccg_cmd_port_control(uc, true); 9445c9ae5a8SAjay Gupta if (err < 0) 9455c9ae5a8SAjay Gupta goto release_mem; 9465c9ae5a8SAjay Gupta 9475c9ae5a8SAjay Gupta release_mem: 9485c9ae5a8SAjay Gupta kfree(wr_buf); 9495c9ae5a8SAjay Gupta 9505c9ae5a8SAjay Gupta release_fw: 9515c9ae5a8SAjay Gupta release_firmware(fw); 9525c9ae5a8SAjay Gupta return err; 9535c9ae5a8SAjay Gupta } 9545c9ae5a8SAjay Gupta 9555c9ae5a8SAjay Gupta /******************************************************************************* 9565c9ae5a8SAjay Gupta * CCG4 has two copies of the firmware in addition to the bootloader. 9575c9ae5a8SAjay Gupta * If the device is running FW1, FW2 can be updated with the new version. 9585c9ae5a8SAjay Gupta * Dual firmware mode allows the CCG device to stay in a PD contract and support 9595c9ae5a8SAjay Gupta * USB PD and Type-C functionality while a firmware update is in progress. 9605c9ae5a8SAjay Gupta ******************************************************************************/ 9615c9ae5a8SAjay Gupta static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode) 9625c9ae5a8SAjay Gupta { 9635c9ae5a8SAjay Gupta int err; 9645c9ae5a8SAjay Gupta 9655c9ae5a8SAjay Gupta while (flash_mode != FLASH_NOT_NEEDED) { 9665c9ae5a8SAjay Gupta err = do_flash(uc, flash_mode); 9675c9ae5a8SAjay Gupta if (err < 0) 9685c9ae5a8SAjay Gupta return err; 9695c9ae5a8SAjay Gupta err = ccg_fw_update_needed(uc, &flash_mode); 9705c9ae5a8SAjay Gupta if (err < 0) 9715c9ae5a8SAjay Gupta return err; 9725c9ae5a8SAjay Gupta } 9735c9ae5a8SAjay Gupta dev_info(uc->dev, "CCG FW update successful\n"); 9745c9ae5a8SAjay Gupta 9755c9ae5a8SAjay Gupta return err; 9765c9ae5a8SAjay Gupta } 9775c9ae5a8SAjay Gupta 9785c9ae5a8SAjay Gupta static int ccg_restart(struct ucsi_ccg *uc) 9795c9ae5a8SAjay Gupta { 9805c9ae5a8SAjay Gupta struct device *dev = uc->dev; 9815c9ae5a8SAjay Gupta int status; 9825c9ae5a8SAjay Gupta 9835c9ae5a8SAjay Gupta status = ucsi_ccg_init(uc); 9845c9ae5a8SAjay Gupta if (status < 0) { 9855c9ae5a8SAjay Gupta dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status); 9865c9ae5a8SAjay Gupta return status; 9875c9ae5a8SAjay Gupta } 9885c9ae5a8SAjay Gupta 9895c9ae5a8SAjay Gupta status = request_threaded_irq(uc->irq, NULL, ccg_irq_handler, 9905c9ae5a8SAjay Gupta IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 9915c9ae5a8SAjay Gupta dev_name(dev), uc); 9925c9ae5a8SAjay Gupta if (status < 0) { 9935c9ae5a8SAjay Gupta dev_err(dev, "request_threaded_irq failed - %d\n", status); 9945c9ae5a8SAjay Gupta return status; 9955c9ae5a8SAjay Gupta } 9965c9ae5a8SAjay Gupta 9975c9ae5a8SAjay Gupta uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); 9985c9ae5a8SAjay Gupta if (IS_ERR(uc->ucsi)) { 9995c9ae5a8SAjay Gupta dev_err(uc->dev, "ucsi_register_ppm failed\n"); 10005c9ae5a8SAjay Gupta return PTR_ERR(uc->ucsi); 10015c9ae5a8SAjay Gupta } 10025c9ae5a8SAjay Gupta 10035c9ae5a8SAjay Gupta return 0; 10045c9ae5a8SAjay Gupta } 10055c9ae5a8SAjay Gupta 10065c9ae5a8SAjay Gupta static void ccg_update_firmware(struct work_struct *work) 10075c9ae5a8SAjay Gupta { 10085c9ae5a8SAjay Gupta struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work); 10095c9ae5a8SAjay Gupta enum enum_flash_mode flash_mode; 10105c9ae5a8SAjay Gupta int status; 10115c9ae5a8SAjay Gupta 10125c9ae5a8SAjay Gupta status = ccg_fw_update_needed(uc, &flash_mode); 10135c9ae5a8SAjay Gupta if (status < 0) 10145c9ae5a8SAjay Gupta return; 10155c9ae5a8SAjay Gupta 10165c9ae5a8SAjay Gupta if (flash_mode != FLASH_NOT_NEEDED) { 10175c9ae5a8SAjay Gupta ucsi_unregister_ppm(uc->ucsi); 10185c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 10195c9ae5a8SAjay Gupta 10205c9ae5a8SAjay Gupta ccg_fw_update(uc, flash_mode); 10215c9ae5a8SAjay Gupta ccg_restart(uc); 10225c9ae5a8SAjay Gupta } 10235c9ae5a8SAjay Gupta } 10245c9ae5a8SAjay Gupta 10255c9ae5a8SAjay Gupta static ssize_t do_flash_store(struct device *dev, 10265c9ae5a8SAjay Gupta struct device_attribute *attr, 10275c9ae5a8SAjay Gupta const char *buf, size_t n) 10285c9ae5a8SAjay Gupta { 10295c9ae5a8SAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev)); 10305c9ae5a8SAjay Gupta bool flash; 10315c9ae5a8SAjay Gupta 10325c9ae5a8SAjay Gupta if (kstrtobool(buf, &flash)) 10335c9ae5a8SAjay Gupta return -EINVAL; 10345c9ae5a8SAjay Gupta 10355c9ae5a8SAjay Gupta if (!flash) 10365c9ae5a8SAjay Gupta return n; 10375c9ae5a8SAjay Gupta 10385c9ae5a8SAjay Gupta if (uc->fw_build == 0x0) { 10395c9ae5a8SAjay Gupta dev_err(dev, "fail to flash FW due to missing FW build info\n"); 10405c9ae5a8SAjay Gupta return -EINVAL; 10415c9ae5a8SAjay Gupta } 10425c9ae5a8SAjay Gupta 10435c9ae5a8SAjay Gupta schedule_work(&uc->work); 10445c9ae5a8SAjay Gupta return n; 10455c9ae5a8SAjay Gupta } 10465c9ae5a8SAjay Gupta 10475c9ae5a8SAjay Gupta static DEVICE_ATTR_WO(do_flash); 10485c9ae5a8SAjay Gupta 10495c9ae5a8SAjay Gupta static struct attribute *ucsi_ccg_sysfs_attrs[] = { 10505c9ae5a8SAjay Gupta &dev_attr_do_flash.attr, 10515c9ae5a8SAjay Gupta NULL, 10525c9ae5a8SAjay Gupta }; 10535c9ae5a8SAjay Gupta 10545c9ae5a8SAjay Gupta static struct attribute_group ucsi_ccg_attr_group = { 10555c9ae5a8SAjay Gupta .attrs = ucsi_ccg_sysfs_attrs, 10565c9ae5a8SAjay Gupta }; 10575c9ae5a8SAjay Gupta 1058247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client, 1059247c554aSAjay Gupta const struct i2c_device_id *id) 1060247c554aSAjay Gupta { 1061247c554aSAjay Gupta struct device *dev = &client->dev; 1062247c554aSAjay Gupta struct ucsi_ccg *uc; 1063247c554aSAjay Gupta int status; 1064247c554aSAjay Gupta u16 rab; 1065247c554aSAjay Gupta 1066247c554aSAjay Gupta uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); 1067247c554aSAjay Gupta if (!uc) 1068247c554aSAjay Gupta return -ENOMEM; 1069247c554aSAjay Gupta 1070247c554aSAjay Gupta uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL); 1071247c554aSAjay Gupta if (!uc->ppm.data) 1072247c554aSAjay Gupta return -ENOMEM; 1073247c554aSAjay Gupta 1074247c554aSAjay Gupta uc->ppm.cmd = ucsi_ccg_cmd; 1075247c554aSAjay Gupta uc->ppm.sync = ucsi_ccg_sync; 1076247c554aSAjay Gupta uc->dev = dev; 1077247c554aSAjay Gupta uc->client = client; 10785c9ae5a8SAjay Gupta mutex_init(&uc->lock); 10795c9ae5a8SAjay Gupta INIT_WORK(&uc->work, ccg_update_firmware); 10805c9ae5a8SAjay Gupta 10815c9ae5a8SAjay Gupta /* Only fail FW flashing when FW build information is not provided */ 10825c9ae5a8SAjay Gupta status = device_property_read_u16(dev, "ccgx,firmware-build", 10835c9ae5a8SAjay Gupta &uc->fw_build); 10845c9ae5a8SAjay Gupta if (status) 10855c9ae5a8SAjay Gupta dev_err(uc->dev, "failed to get FW build information\n"); 1086247c554aSAjay Gupta 1087247c554aSAjay Gupta /* reset ccg device and initialize ucsi */ 1088247c554aSAjay Gupta status = ucsi_ccg_init(uc); 1089247c554aSAjay Gupta if (status < 0) { 1090247c554aSAjay Gupta dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); 1091247c554aSAjay Gupta return status; 1092247c554aSAjay Gupta } 1093247c554aSAjay Gupta 10945d438e20SAjay Gupta status = get_fw_info(uc); 10955d438e20SAjay Gupta if (status < 0) { 10965d438e20SAjay Gupta dev_err(uc->dev, "get_fw_info failed - %d\n", status); 10975d438e20SAjay Gupta return status; 10985d438e20SAjay Gupta } 10995d438e20SAjay Gupta 11005c9ae5a8SAjay Gupta uc->port_num = 1; 11015c9ae5a8SAjay Gupta 11025c9ae5a8SAjay Gupta if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK) 11035c9ae5a8SAjay Gupta uc->port_num++; 11045c9ae5a8SAjay Gupta 11055c9ae5a8SAjay Gupta status = request_threaded_irq(client->irq, NULL, ccg_irq_handler, 1106247c554aSAjay Gupta IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 1107247c554aSAjay Gupta dev_name(dev), uc); 1108247c554aSAjay Gupta if (status < 0) { 1109247c554aSAjay Gupta dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); 1110247c554aSAjay Gupta return status; 1111247c554aSAjay Gupta } 1112247c554aSAjay Gupta 11135c9ae5a8SAjay Gupta uc->irq = client->irq; 11145c9ae5a8SAjay Gupta 1115247c554aSAjay Gupta uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); 1116247c554aSAjay Gupta if (IS_ERR(uc->ucsi)) { 1117247c554aSAjay Gupta dev_err(uc->dev, "ucsi_register_ppm failed\n"); 1118247c554aSAjay Gupta return PTR_ERR(uc->ucsi); 1119247c554aSAjay Gupta } 1120247c554aSAjay Gupta 1121247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version)); 1122247c554aSAjay Gupta status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) + 1123247c554aSAjay Gupta offsetof(struct ucsi_data, version), 1124247c554aSAjay Gupta sizeof(uc->ppm.data->version)); 1125247c554aSAjay Gupta if (status < 0) { 1126247c554aSAjay Gupta ucsi_unregister_ppm(uc->ucsi); 1127247c554aSAjay Gupta return status; 1128247c554aSAjay Gupta } 1129247c554aSAjay Gupta 1130247c554aSAjay Gupta i2c_set_clientdata(client, uc); 11315c9ae5a8SAjay Gupta 11325c9ae5a8SAjay Gupta status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group); 11335c9ae5a8SAjay Gupta if (status) 11345c9ae5a8SAjay Gupta dev_err(uc->dev, "cannot create sysfs group: %d\n", status); 11355c9ae5a8SAjay Gupta 1136247c554aSAjay Gupta return 0; 1137247c554aSAjay Gupta } 1138247c554aSAjay Gupta 1139247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client) 1140247c554aSAjay Gupta { 1141247c554aSAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 1142247c554aSAjay Gupta 11435c9ae5a8SAjay Gupta cancel_work_sync(&uc->work); 1144247c554aSAjay Gupta ucsi_unregister_ppm(uc->ucsi); 11455c9ae5a8SAjay Gupta free_irq(uc->irq, uc); 11465c9ae5a8SAjay Gupta sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); 1147247c554aSAjay Gupta 1148247c554aSAjay Gupta return 0; 1149247c554aSAjay Gupta } 1150247c554aSAjay Gupta 1151247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = { 1152247c554aSAjay Gupta {"ccgx-ucsi", 0}, 1153247c554aSAjay Gupta {} 1154247c554aSAjay Gupta }; 1155247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); 1156247c554aSAjay Gupta 1157247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = { 1158247c554aSAjay Gupta .driver = { 1159247c554aSAjay Gupta .name = "ucsi_ccg", 1160247c554aSAjay Gupta }, 1161247c554aSAjay Gupta .probe = ucsi_ccg_probe, 1162247c554aSAjay Gupta .remove = ucsi_ccg_remove, 1163247c554aSAjay Gupta .id_table = ucsi_ccg_device_id, 1164247c554aSAjay Gupta }; 1165247c554aSAjay Gupta 1166247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver); 1167247c554aSAjay Gupta 1168247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); 1169247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); 1170247c554aSAjay Gupta MODULE_LICENSE("GPL v2"); 1171