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> 12247c554aSAjay Gupta #include <linux/i2c.h> 13247c554aSAjay Gupta #include <linux/module.h> 14247c554aSAjay Gupta #include <linux/pci.h> 15247c554aSAjay Gupta #include <linux/platform_device.h> 16247c554aSAjay Gupta 17247c554aSAjay Gupta #include <asm/unaligned.h> 18247c554aSAjay Gupta #include "ucsi.h" 19247c554aSAjay Gupta 205d438e20SAjay Gupta enum enum_fw_mode { 215d438e20SAjay Gupta BOOT, /* bootloader */ 225d438e20SAjay Gupta FW1, /* FW partition-1 (contains secondary fw) */ 235d438e20SAjay Gupta FW2, /* FW partition-2 (contains primary fw) */ 245d438e20SAjay Gupta FW_INVALID, 255d438e20SAjay Gupta }; 265d438e20SAjay Gupta 275d438e20SAjay Gupta struct ccg_dev_info { 285d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0) 295d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) 305d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2) 315d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) 325d438e20SAjay Gupta u8 mode; 335d438e20SAjay Gupta u8 bl_mode; 345d438e20SAjay Gupta __le16 silicon_id; 355d438e20SAjay Gupta __le16 bl_last_row; 365d438e20SAjay Gupta } __packed; 375d438e20SAjay Gupta 385d438e20SAjay Gupta struct version_format { 395d438e20SAjay Gupta __le16 build; 405d438e20SAjay Gupta u8 patch; 415d438e20SAjay Gupta u8 ver; 425d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0) 435d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) 445d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4) 455d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) 465d438e20SAjay Gupta } __packed; 475d438e20SAjay Gupta 485d438e20SAjay Gupta struct version_info { 495d438e20SAjay Gupta struct version_format base; 505d438e20SAjay Gupta struct version_format app; 515d438e20SAjay Gupta }; 525d438e20SAjay Gupta 53247c554aSAjay Gupta struct ucsi_ccg { 54247c554aSAjay Gupta struct device *dev; 55247c554aSAjay Gupta struct ucsi *ucsi; 56247c554aSAjay Gupta struct ucsi_ppm ppm; 57247c554aSAjay Gupta struct i2c_client *client; 585d438e20SAjay Gupta struct ccg_dev_info info; 595d438e20SAjay Gupta /* version info for boot, primary and secondary */ 605d438e20SAjay Gupta struct version_info version[FW2 + 1]; 61247c554aSAjay Gupta }; 62247c554aSAjay Gupta 635d438e20SAjay Gupta #define CCGX_RAB_DEVICE_MODE 0x0000 645d438e20SAjay Gupta #define CCGX_RAB_INTR_REG 0x0006 655d438e20SAjay Gupta #define CCGX_RAB_READ_ALL_VER 0x0010 665d438e20SAjay Gupta #define CCGX_RAB_READ_FW2_VER 0x0020 675d438e20SAjay Gupta #define CCGX_RAB_UCSI_CONTROL 0x0039 68247c554aSAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START BIT(0) 69247c554aSAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) 70247c554aSAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) 71247c554aSAjay Gupta 72247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 73247c554aSAjay Gupta { 74247c554aSAjay Gupta struct i2c_client *client = uc->client; 75247c554aSAjay Gupta const struct i2c_adapter_quirks *quirks = client->adapter->quirks; 76247c554aSAjay Gupta unsigned char buf[2]; 77247c554aSAjay Gupta struct i2c_msg msgs[] = { 78247c554aSAjay Gupta { 79247c554aSAjay Gupta .addr = client->addr, 80247c554aSAjay Gupta .flags = 0x0, 81247c554aSAjay Gupta .len = sizeof(buf), 82247c554aSAjay Gupta .buf = buf, 83247c554aSAjay Gupta }, 84247c554aSAjay Gupta { 85247c554aSAjay Gupta .addr = client->addr, 86247c554aSAjay Gupta .flags = I2C_M_RD, 87247c554aSAjay Gupta .buf = data, 88247c554aSAjay Gupta }, 89247c554aSAjay Gupta }; 90247c554aSAjay Gupta u32 rlen, rem_len = len, max_read_len = len; 91247c554aSAjay Gupta int status; 92247c554aSAjay Gupta 93247c554aSAjay Gupta /* check any max_read_len limitation on i2c adapter */ 94247c554aSAjay Gupta if (quirks && quirks->max_read_len) 95247c554aSAjay Gupta max_read_len = quirks->max_read_len; 96247c554aSAjay Gupta 97247c554aSAjay Gupta while (rem_len > 0) { 98247c554aSAjay Gupta msgs[1].buf = &data[len - rem_len]; 99247c554aSAjay Gupta rlen = min_t(u16, rem_len, max_read_len); 100247c554aSAjay Gupta msgs[1].len = rlen; 101247c554aSAjay Gupta put_unaligned_le16(rab, buf); 102247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 103247c554aSAjay Gupta if (status < 0) { 104247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 105247c554aSAjay Gupta return status; 106247c554aSAjay Gupta } 107247c554aSAjay Gupta rab += rlen; 108247c554aSAjay Gupta rem_len -= rlen; 109247c554aSAjay Gupta } 110247c554aSAjay Gupta 111247c554aSAjay Gupta return 0; 112247c554aSAjay Gupta } 113247c554aSAjay Gupta 114247c554aSAjay Gupta static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) 115247c554aSAjay Gupta { 116247c554aSAjay Gupta struct i2c_client *client = uc->client; 117247c554aSAjay Gupta unsigned char *buf; 118247c554aSAjay Gupta struct i2c_msg msgs[] = { 119247c554aSAjay Gupta { 120247c554aSAjay Gupta .addr = client->addr, 121247c554aSAjay Gupta .flags = 0x0, 122247c554aSAjay Gupta } 123247c554aSAjay Gupta }; 124247c554aSAjay Gupta int status; 125247c554aSAjay Gupta 126247c554aSAjay Gupta buf = kzalloc(len + sizeof(rab), GFP_KERNEL); 127247c554aSAjay Gupta if (!buf) 128247c554aSAjay Gupta return -ENOMEM; 129247c554aSAjay Gupta 130247c554aSAjay Gupta put_unaligned_le16(rab, buf); 131247c554aSAjay Gupta memcpy(buf + sizeof(rab), data, len); 132247c554aSAjay Gupta 133247c554aSAjay Gupta msgs[0].len = len + sizeof(rab); 134247c554aSAjay Gupta msgs[0].buf = buf; 135247c554aSAjay Gupta 136247c554aSAjay Gupta status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 137247c554aSAjay Gupta if (status < 0) { 138247c554aSAjay Gupta dev_err(uc->dev, "i2c_transfer failed %d\n", status); 139247c554aSAjay Gupta kfree(buf); 140247c554aSAjay Gupta return status; 141247c554aSAjay Gupta } 142247c554aSAjay Gupta 143247c554aSAjay Gupta kfree(buf); 144247c554aSAjay Gupta return 0; 145247c554aSAjay Gupta } 146247c554aSAjay Gupta 147247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc) 148247c554aSAjay Gupta { 149247c554aSAjay Gupta unsigned int count = 10; 150247c554aSAjay Gupta u8 data; 151247c554aSAjay Gupta int status; 152247c554aSAjay Gupta 153247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_STOP; 154247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 155247c554aSAjay Gupta if (status < 0) 156247c554aSAjay Gupta return status; 157247c554aSAjay Gupta 158247c554aSAjay Gupta data = CCGX_RAB_UCSI_CONTROL_START; 159247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); 160247c554aSAjay Gupta if (status < 0) 161247c554aSAjay Gupta return status; 162247c554aSAjay Gupta 163247c554aSAjay Gupta /* 164247c554aSAjay Gupta * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control 165247c554aSAjay Gupta * register write will push response which must be cleared. 166247c554aSAjay Gupta */ 167247c554aSAjay Gupta do { 168247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 169247c554aSAjay Gupta if (status < 0) 170247c554aSAjay Gupta return status; 171247c554aSAjay Gupta 172247c554aSAjay Gupta if (!data) 173247c554aSAjay Gupta return 0; 174247c554aSAjay Gupta 175247c554aSAjay Gupta status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 176247c554aSAjay Gupta if (status < 0) 177247c554aSAjay Gupta return status; 178247c554aSAjay Gupta 179247c554aSAjay Gupta usleep_range(10000, 11000); 180247c554aSAjay Gupta } while (--count); 181247c554aSAjay Gupta 182247c554aSAjay Gupta return -ETIMEDOUT; 183247c554aSAjay Gupta } 184247c554aSAjay Gupta 185247c554aSAjay Gupta static int ucsi_ccg_send_data(struct ucsi_ccg *uc) 186247c554aSAjay Gupta { 187247c554aSAjay Gupta u8 *ppm = (u8 *)uc->ppm.data; 188247c554aSAjay Gupta int status; 189247c554aSAjay Gupta u16 rab; 190247c554aSAjay Gupta 191247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out)); 192247c554aSAjay Gupta status = ccg_write(uc, rab, ppm + 193247c554aSAjay Gupta offsetof(struct ucsi_data, message_out), 194247c554aSAjay Gupta sizeof(uc->ppm.data->message_out)); 195247c554aSAjay Gupta if (status < 0) 196247c554aSAjay Gupta return status; 197247c554aSAjay Gupta 198247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl)); 199247c554aSAjay Gupta return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl), 200247c554aSAjay Gupta sizeof(uc->ppm.data->ctrl)); 201247c554aSAjay Gupta } 202247c554aSAjay Gupta 203247c554aSAjay Gupta static int ucsi_ccg_recv_data(struct ucsi_ccg *uc) 204247c554aSAjay Gupta { 205247c554aSAjay Gupta u8 *ppm = (u8 *)uc->ppm.data; 206247c554aSAjay Gupta int status; 207247c554aSAjay Gupta u16 rab; 208247c554aSAjay Gupta 209247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci)); 210247c554aSAjay Gupta status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci), 211247c554aSAjay Gupta sizeof(uc->ppm.data->cci)); 212247c554aSAjay Gupta if (status < 0) 213247c554aSAjay Gupta return status; 214247c554aSAjay Gupta 215247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in)); 216247c554aSAjay Gupta return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in), 217247c554aSAjay Gupta sizeof(uc->ppm.data->message_in)); 218247c554aSAjay Gupta } 219247c554aSAjay Gupta 220247c554aSAjay Gupta static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc) 221247c554aSAjay Gupta { 222247c554aSAjay Gupta int status; 223247c554aSAjay Gupta unsigned char data; 224247c554aSAjay Gupta 225247c554aSAjay Gupta status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 226247c554aSAjay Gupta if (status < 0) 227247c554aSAjay Gupta return status; 228247c554aSAjay Gupta 229247c554aSAjay Gupta return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); 230247c554aSAjay Gupta } 231247c554aSAjay Gupta 232247c554aSAjay Gupta static int ucsi_ccg_sync(struct ucsi_ppm *ppm) 233247c554aSAjay Gupta { 234247c554aSAjay Gupta struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); 235247c554aSAjay Gupta int status; 236247c554aSAjay Gupta 237247c554aSAjay Gupta status = ucsi_ccg_recv_data(uc); 238247c554aSAjay Gupta if (status < 0) 239247c554aSAjay Gupta return status; 240247c554aSAjay Gupta 241247c554aSAjay Gupta /* ack interrupt to allow next command to run */ 242247c554aSAjay Gupta return ucsi_ccg_ack_interrupt(uc); 243247c554aSAjay Gupta } 244247c554aSAjay Gupta 245247c554aSAjay Gupta static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl) 246247c554aSAjay Gupta { 247247c554aSAjay Gupta struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); 248247c554aSAjay Gupta 249247c554aSAjay Gupta ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; 250247c554aSAjay Gupta return ucsi_ccg_send_data(uc); 251247c554aSAjay Gupta } 252247c554aSAjay Gupta 253247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data) 254247c554aSAjay Gupta { 255247c554aSAjay Gupta struct ucsi_ccg *uc = data; 256247c554aSAjay Gupta 257247c554aSAjay Gupta ucsi_notify(uc->ucsi); 258247c554aSAjay Gupta 259247c554aSAjay Gupta return IRQ_HANDLED; 260247c554aSAjay Gupta } 261247c554aSAjay Gupta 2625d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc) 2635d438e20SAjay Gupta { 2645d438e20SAjay Gupta int err; 2655d438e20SAjay Gupta 2665d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), 2675d438e20SAjay Gupta sizeof(uc->version)); 2685d438e20SAjay Gupta if (err < 0) 2695d438e20SAjay Gupta return err; 2705d438e20SAjay Gupta 2715d438e20SAjay Gupta err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), 2725d438e20SAjay Gupta sizeof(uc->info)); 2735d438e20SAjay Gupta if (err < 0) 2745d438e20SAjay Gupta return err; 2755d438e20SAjay Gupta 2765d438e20SAjay Gupta return 0; 2775d438e20SAjay Gupta } 2785d438e20SAjay Gupta 279247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client, 280247c554aSAjay Gupta const struct i2c_device_id *id) 281247c554aSAjay Gupta { 282247c554aSAjay Gupta struct device *dev = &client->dev; 283247c554aSAjay Gupta struct ucsi_ccg *uc; 284247c554aSAjay Gupta int status; 285247c554aSAjay Gupta u16 rab; 286247c554aSAjay Gupta 287247c554aSAjay Gupta uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); 288247c554aSAjay Gupta if (!uc) 289247c554aSAjay Gupta return -ENOMEM; 290247c554aSAjay Gupta 291247c554aSAjay Gupta uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL); 292247c554aSAjay Gupta if (!uc->ppm.data) 293247c554aSAjay Gupta return -ENOMEM; 294247c554aSAjay Gupta 295247c554aSAjay Gupta uc->ppm.cmd = ucsi_ccg_cmd; 296247c554aSAjay Gupta uc->ppm.sync = ucsi_ccg_sync; 297247c554aSAjay Gupta uc->dev = dev; 298247c554aSAjay Gupta uc->client = client; 299247c554aSAjay Gupta 300247c554aSAjay Gupta /* reset ccg device and initialize ucsi */ 301247c554aSAjay Gupta status = ucsi_ccg_init(uc); 302247c554aSAjay Gupta if (status < 0) { 303247c554aSAjay Gupta dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); 304247c554aSAjay Gupta return status; 305247c554aSAjay Gupta } 306247c554aSAjay Gupta 3075d438e20SAjay Gupta status = get_fw_info(uc); 3085d438e20SAjay Gupta if (status < 0) { 3095d438e20SAjay Gupta dev_err(uc->dev, "get_fw_info failed - %d\n", status); 3105d438e20SAjay Gupta return status; 3115d438e20SAjay Gupta } 3125d438e20SAjay Gupta 313247c554aSAjay Gupta status = devm_request_threaded_irq(dev, client->irq, NULL, 314247c554aSAjay Gupta ccg_irq_handler, 315247c554aSAjay Gupta IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 316247c554aSAjay Gupta dev_name(dev), uc); 317247c554aSAjay Gupta if (status < 0) { 318247c554aSAjay Gupta dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); 319247c554aSAjay Gupta return status; 320247c554aSAjay Gupta } 321247c554aSAjay Gupta 322247c554aSAjay Gupta uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); 323247c554aSAjay Gupta if (IS_ERR(uc->ucsi)) { 324247c554aSAjay Gupta dev_err(uc->dev, "ucsi_register_ppm failed\n"); 325247c554aSAjay Gupta return PTR_ERR(uc->ucsi); 326247c554aSAjay Gupta } 327247c554aSAjay Gupta 328247c554aSAjay Gupta rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version)); 329247c554aSAjay Gupta status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) + 330247c554aSAjay Gupta offsetof(struct ucsi_data, version), 331247c554aSAjay Gupta sizeof(uc->ppm.data->version)); 332247c554aSAjay Gupta if (status < 0) { 333247c554aSAjay Gupta ucsi_unregister_ppm(uc->ucsi); 334247c554aSAjay Gupta return status; 335247c554aSAjay Gupta } 336247c554aSAjay Gupta 337247c554aSAjay Gupta i2c_set_clientdata(client, uc); 338247c554aSAjay Gupta return 0; 339247c554aSAjay Gupta } 340247c554aSAjay Gupta 341247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client) 342247c554aSAjay Gupta { 343247c554aSAjay Gupta struct ucsi_ccg *uc = i2c_get_clientdata(client); 344247c554aSAjay Gupta 345247c554aSAjay Gupta ucsi_unregister_ppm(uc->ucsi); 346247c554aSAjay Gupta 347247c554aSAjay Gupta return 0; 348247c554aSAjay Gupta } 349247c554aSAjay Gupta 350247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = { 351247c554aSAjay Gupta {"ccgx-ucsi", 0}, 352247c554aSAjay Gupta {} 353247c554aSAjay Gupta }; 354247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); 355247c554aSAjay Gupta 356247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = { 357247c554aSAjay Gupta .driver = { 358247c554aSAjay Gupta .name = "ucsi_ccg", 359247c554aSAjay Gupta }, 360247c554aSAjay Gupta .probe = ucsi_ccg_probe, 361247c554aSAjay Gupta .remove = ucsi_ccg_remove, 362247c554aSAjay Gupta .id_table = ucsi_ccg_device_id, 363247c554aSAjay Gupta }; 364247c554aSAjay Gupta 365247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver); 366247c554aSAjay Gupta 367247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); 368247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller"); 369247c554aSAjay Gupta MODULE_LICENSE("GPL v2"); 370