root/drivers/usb/typec/ucsi/ucsi_ccg.c
// SPDX-License-Identifier: GPL-2.0
/*
 * UCSI driver for Cypress CCGx Type-C controller
 *
 * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved.
 * Author: Ajay Gupta <ajayg@nvidia.com>
 *
 * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c
 */
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/hex.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/usb/typec_dp.h>

#include <linux/unaligned.h>
#include "ucsi.h"

enum enum_fw_mode {
        BOOT,   /* bootloader */
        FW1,    /* FW partition-1 (contains secondary fw) */
        FW2,    /* FW partition-2 (contains primary fw) */
        FW_INVALID,
};

#define CCGX_RAB_DEVICE_MODE                    0x0000
#define CCGX_RAB_INTR_REG                       0x0006
#define  DEV_INT                                BIT(0)
#define  PORT0_INT                              BIT(1)
#define  PORT1_INT                              BIT(2)
#define  UCSI_READ_INT                          BIT(7)
#define CCGX_RAB_JUMP_TO_BOOT                   0x0007
#define  TO_BOOT                                'J'
#define  TO_ALT_FW                              'A'
#define CCGX_RAB_RESET_REQ                      0x0008
#define  RESET_SIG                              'R'
#define  CMD_RESET_I2C                          0x0
#define  CMD_RESET_DEV                          0x1
#define CCGX_RAB_ENTER_FLASHING                 0x000A
#define  FLASH_ENTER_SIG                        'P'
#define CCGX_RAB_VALIDATE_FW                    0x000B
#define CCGX_RAB_FLASH_ROW_RW                   0x000C
#define  FLASH_SIG                              'F'
#define  FLASH_RD_CMD                           0x0
#define  FLASH_WR_CMD                           0x1
#define  FLASH_FWCT1_WR_CMD                     0x2
#define  FLASH_FWCT2_WR_CMD                     0x3
#define  FLASH_FWCT_SIG_WR_CMD                  0x4
#define CCGX_RAB_READ_ALL_VER                   0x0010
#define CCGX_RAB_READ_FW2_VER                   0x0020
#define CCGX_RAB_UCSI_CONTROL                   0x0039
#define CCGX_RAB_UCSI_CONTROL_START             BIT(0)
#define CCGX_RAB_UCSI_CONTROL_STOP              BIT(1)
#define CCGX_RAB_UCSI_DATA_BLOCK(offset)        (0xf000 | ((offset) & 0xff))
#define REG_FLASH_RW_MEM        0x0200
#define DEV_REG_IDX                             CCGX_RAB_DEVICE_MODE
#define CCGX_RAB_PDPORT_ENABLE                  0x002C
#define  PDPORT_1               BIT(0)
#define  PDPORT_2               BIT(1)
#define CCGX_RAB_RESPONSE                       0x007E
#define  ASYNC_EVENT                            BIT(7)

/* CCGx events & async msg codes */
#define RESET_COMPLETE          0x80
#define EVENT_INDEX             RESET_COMPLETE
#define PORT_CONNECT_DET        0x84
#define PORT_DISCONNECT_DET     0x85
#define ROLE_SWAP_COMPELETE     0x87

/* ccg firmware */
#define CYACD_LINE_SIZE         527
#define CCG4_ROW_SIZE           256
#define FW1_METADATA_ROW        0x1FF
#define FW2_METADATA_ROW        0x1FE
#define FW_CFG_TABLE_SIG_SIZE   256

static int secondary_fw_min_ver = 41;

enum enum_flash_mode {
        SECONDARY_BL,   /* update secondary using bootloader */
        PRIMARY,        /* update primary using secondary */
        SECONDARY,      /* update secondary using primary */
        FLASH_NOT_NEEDED,       /* update not required */
        FLASH_INVALID,
};

static const char * const ccg_fw_names[] = {
        "ccg_boot.cyacd",
        "ccg_primary.cyacd",
        "ccg_secondary.cyacd"
};

struct ccg_dev_info {
#define CCG_DEVINFO_FWMODE_SHIFT (0)
#define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT)
#define CCG_DEVINFO_PDPORTS_SHIFT (2)
#define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT)
        u8 mode;
        u8 bl_mode;
        __le16 silicon_id;
        __le16 bl_last_row;
} __packed;

struct version_format {
        __le16 build;
        u8 patch;
        u8 ver;
#define CCG_VERSION_PATCH(x) ((x) << 16)
#define CCG_VERSION(x)  ((x) << 24)
#define CCG_VERSION_MIN_SHIFT (0)
#define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT)
#define CCG_VERSION_MAJ_SHIFT (4)
#define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT)
} __packed;

/*
 * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
 * of missing interrupt when a device is connected for runtime resume
 */
#define CCG_FW_BUILD_NVIDIA     (('n' << 8) | 'v')
#define CCG_OLD_FW_VERSION      (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))

/* Firmware for Tegra doesn't support UCSI ALT command, built
 * for NVIDIA has known issue of reporting wrong capability info
 */
#define CCG_FW_BUILD_NVIDIA_TEGRA       (('g' << 8) | 'n')

/* Altmode offset for NVIDIA Function Test Board (FTB) */
#define NVIDIA_FTB_DP_OFFSET    (2)
#define NVIDIA_FTB_DBG_OFFSET   (3)

struct version_info {
        struct version_format base;
        struct version_format app;
};

struct fw_config_table {
        u32 identity;
        u16 table_size;
        u8 fwct_version;
        u8 is_key_change;
        u8 guid[16];
        struct version_format base;
        struct version_format app;
        u8 primary_fw_digest[32];
        u32 key_exp_length;
        u8 key_modulus[256];
        u8 key_exp[4];
};

/* CCGx response codes */
enum ccg_resp_code {
        CMD_NO_RESP             = 0x00,
        CMD_SUCCESS             = 0x02,
        FLASH_DATA_AVAILABLE    = 0x03,
        CMD_INVALID             = 0x05,
        FLASH_UPDATE_FAIL       = 0x07,
        INVALID_FW              = 0x08,
        INVALID_ARG             = 0x09,
        CMD_NOT_SUPPORT         = 0x0A,
        TRANSACTION_FAIL        = 0x0C,
        PD_CMD_FAIL             = 0x0D,
        UNDEF_ERROR             = 0x0F,
        INVALID_RESP            = 0x10,
};

#define CCG_EVENT_MAX   (EVENT_INDEX + 43)

struct ccg_cmd {
        u16 reg;
        u32 data;
        int len;
        u32 delay; /* ms delay for cmd timeout  */
};

struct ccg_resp {
        u8 code;
        u8 length;
};

struct ucsi_ccg_altmode {
        u16 svid;
        u32 mid;
        u8 linked_idx;
        u8 active_idx;
#define UCSI_MULTI_DP_INDEX     (0xff)
        bool checked;
} __packed;

#define CCGX_MESSAGE_IN_MAX 4
struct op_region {
        __le32 cci;
        __le32 message_in[CCGX_MESSAGE_IN_MAX];
};

struct ucsi_ccg {
        struct device *dev;
        struct ucsi *ucsi;
        struct i2c_client *client;

        struct ccg_dev_info info;
        /* version info for boot, primary and secondary */
        struct version_info version[FW2 + 1];
        u32 fw_version;
        /* CCG HPI communication flags */
        unsigned long flags;
#define RESET_PENDING   0
#define DEV_CMD_PENDING 1
        struct ccg_resp dev_resp;
        u8 cmd_resp;
        int port_num;
        int irq;
        struct work_struct work;
        struct mutex lock; /* to sync between user and driver thread */

        /* fw build with vendor information */
        u16 fw_build;
        struct work_struct pm_work;

        bool has_multiple_dp;
        struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
        struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];

        /*
         * This spinlock protects op_data which includes CCI and MESSAGE_IN that
         * will be updated in ISR
         */
        spinlock_t op_lock;
        struct op_region op_data;
};

static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
{
        struct i2c_client *client = uc->client;
        const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
        unsigned char buf[2];
        struct i2c_msg msgs[] = {
                {
                        .addr   = client->addr,
                        .flags  = 0x0,
                        .len    = sizeof(buf),
                        .buf    = buf,
                },
                {
                        .addr   = client->addr,
                        .flags  = I2C_M_RD,
                        .buf    = data,
                },
        };
        u32 rlen, rem_len = len, max_read_len = len;
        int status;

        /* check any max_read_len limitation on i2c adapter */
        if (quirks && quirks->max_read_len)
                max_read_len = quirks->max_read_len;

        pm_runtime_get_sync(uc->dev);
        while (rem_len > 0) {
                msgs[1].buf = &data[len - rem_len];
                rlen = min_t(u16, rem_len, max_read_len);
                msgs[1].len = rlen;
                put_unaligned_le16(rab, buf);
                status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
                if (status < 0) {
                        dev_err(uc->dev, "i2c_transfer failed %d\n", status);
                        pm_runtime_put_sync(uc->dev);
                        return status;
                }
                rab += rlen;
                rem_len -= rlen;
        }

        pm_runtime_put_sync(uc->dev);
        return 0;
}

static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
{
        struct i2c_client *client = uc->client;
        unsigned char *buf;
        struct i2c_msg msgs[] = {
                {
                        .addr   = client->addr,
                        .flags  = 0x0,
                }
        };
        int status;

        buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
        if (!buf)
                return -ENOMEM;

        put_unaligned_le16(rab, buf);
        memcpy(buf + sizeof(rab), data, len);

        msgs[0].len = len + sizeof(rab);
        msgs[0].buf = buf;

        pm_runtime_get_sync(uc->dev);
        status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
        if (status < 0) {
                dev_err(uc->dev, "i2c_transfer failed %d\n", status);
                pm_runtime_put_sync(uc->dev);
                kfree(buf);
                return status;
        }

        pm_runtime_put_sync(uc->dev);
        kfree(buf);
        return 0;
}

static int ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
{
        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN);
        struct op_region *data = &uc->op_data;
        unsigned char *buf;
        size_t size = sizeof(data->message_in);

        buf = kzalloc(size, GFP_ATOMIC);
        if (!buf)
                return -ENOMEM;
        if (UCSI_CCI_LENGTH(cci)) {
                int ret = ccg_read(uc, reg, (void *)buf, size);

                if (ret) {
                        kfree(buf);
                        return ret;
                }
        }

        spin_lock(&uc->op_lock);
        data->cci = cpu_to_le32(cci);
        if (UCSI_CCI_LENGTH(cci))
                memcpy(&data->message_in, buf, size);
        spin_unlock(&uc->op_lock);
        kfree(buf);
        return 0;
}

static int ucsi_ccg_init(struct ucsi_ccg *uc)
{
        unsigned int count = 10;
        u8 data;
        int status;

        spin_lock_init(&uc->op_lock);

        data = CCGX_RAB_UCSI_CONTROL_STOP;
        status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
        if (status < 0)
                return status;

        data = CCGX_RAB_UCSI_CONTROL_START;
        status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
        if (status < 0)
                return status;

        /*
         * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
         * register write will push response which must be cleared.
         */
        do {
                status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
                if (status < 0)
                        return status;

                if (!(data & DEV_INT))
                        return 0;

                status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
                if (status < 0)
                        return status;

                usleep_range(10000, 11000);
        } while (--count);

        return -ETIMEDOUT;
}

static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
{
        u8 cam, new_cam;

        cam = data[0];
        new_cam = uc->orig[cam].linked_idx;
        uc->updated[new_cam].active_idx = cam;
        data[0] = new_cam;
}

static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
                                     u8 recipient,
                                     struct ucsi_altmode *orig,
                                     struct ucsi_altmode *updated)
{
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
        struct ucsi_ccg_altmode *alt, *new_alt;
        int i, j, k = 0;
        bool found = false;

        if (recipient != UCSI_RECIPIENT_CON)
                return false;

        alt = uc->orig;
        new_alt = uc->updated;
        memset(uc->updated, 0, sizeof(uc->updated));

        /*
         * Copy original connector altmodes to new structure.
         * We need this before second loop since second loop
         * checks for duplicate altmodes.
         */
        for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
                alt[i].svid = orig[i].svid;
                alt[i].mid = orig[i].mid;
                if (!alt[i].svid)
                        break;
        }

        for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
                if (!alt[i].svid)
                        break;

                /* already checked and considered */
                if (alt[i].checked)
                        continue;

                if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) {
                        /* Found Non DP altmode */
                        new_alt[k].svid = alt[i].svid;
                        new_alt[k].mid |= alt[i].mid;
                        new_alt[k].linked_idx = i;
                        alt[i].linked_idx = k;
                        updated[k].svid = new_alt[k].svid;
                        updated[k].mid = new_alt[k].mid;
                        k++;
                        continue;
                }

                for (j = i + 1; j < UCSI_MAX_ALTMODES; j++) {
                        if (alt[i].svid != alt[j].svid ||
                            !DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) {
                                continue;
                        } else {
                                /* Found duplicate DP mode */
                                new_alt[k].svid = alt[i].svid;
                                new_alt[k].mid |= alt[i].mid | alt[j].mid;
                                new_alt[k].linked_idx = UCSI_MULTI_DP_INDEX;
                                alt[i].linked_idx = k;
                                alt[j].linked_idx = k;
                                alt[j].checked = true;
                                found = true;
                        }
                }
                if (found) {
                        uc->has_multiple_dp = true;
                } else {
                        /* Didn't find any duplicate DP altmode */
                        new_alt[k].svid = alt[i].svid;
                        new_alt[k].mid |= alt[i].mid;
                        new_alt[k].linked_idx = i;
                        alt[i].linked_idx = k;
                }
                updated[k].svid = new_alt[k].svid;
                updated[k].mid = new_alt[k].mid;
                k++;
        }
        return found;
}

static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
                                            struct ucsi_connector *con,
                                            u64 *cmd)
{
        struct ucsi_ccg_altmode *new_port, *port;
        struct typec_altmode *alt = NULL;
        u8 new_cam, cam, pin;
        bool enter_new_mode;
        int i, j, k = 0xff;

        port = uc->orig;
        new_cam = UCSI_SET_NEW_CAM_GET_AM(*cmd);
        if (new_cam >= ARRAY_SIZE(uc->updated))
                return;
        new_port = &uc->updated[new_cam];
        cam = new_port->linked_idx;
        enter_new_mode = UCSI_SET_NEW_CAM_ENTER(*cmd);

        /*
         * If CAM is UCSI_MULTI_DP_INDEX then this is DP altmode
         * with multiple DP mode. Find out CAM for best pin assignment
         * among all DP mode. Priorite pin E->D->C after making sure
         * the partner supports that pin.
         */
        if (cam == UCSI_MULTI_DP_INDEX) {
                if (enter_new_mode) {
                        for (i = 0; con->partner_altmode[i]; i++) {
                                alt = con->partner_altmode[i];
                                if (alt->svid == new_port->svid)
                                        break;
                        }
                        /*
                         * alt will always be non NULL since this is
                         * UCSI_SET_NEW_CAM command and so there will be
                         * at least one con->partner_altmode[i] with svid
                         * matching with new_port->svid.
                         */
                        for (j = 0; port[j].svid; j++) {
                                pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid);
                                if (alt && port[j].svid == alt->svid &&
                                    (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) {
                                        /* prioritize pin E->D->C */
                                        if (k == 0xff || (k != 0xff && pin >
                                            DP_CONF_GET_PIN_ASSIGN(port[k].mid))
                                            ) {
                                                k = j;
                                        }
                                }
                        }
                        cam = k;
                        new_port->active_idx = cam;
                } else {
                        cam = new_port->active_idx;
                }
        }
        *cmd &= ~UCSI_SET_NEW_CAM_AM_MASK;
        *cmd |= UCSI_SET_NEW_CAM_SET_AM(cam);
}

/*
 * Change the order of vdo values of NVIDIA test device FTB
 * (Function Test Board) which reports altmode list with vdo=0x3
 * first and then vdo=0x. Current logic to assign mode value is
 * based on order in altmode list and it causes a mismatch of CON
 * and SOP altmodes since NVIDIA GPU connector has order of vdo=0x1
 * first and then vdo=0x3
 */
static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
                                    struct ucsi_altmode *alt,
                                    u64 command)
{
        switch (UCSI_ALTMODE_OFFSET(command)) {
        case NVIDIA_FTB_DP_OFFSET:
                if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
                        alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
                                     DP_CAP_DP_SIGNALLING(0) | DP_CAP_USB |
                                     DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_E));
                break;
        case NVIDIA_FTB_DBG_OFFSET:
                if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DP_VDO)
                        alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DBG_VDO;
                break;
        default:
                break;
        }
}

static int ucsi_ccg_read_version(struct ucsi *ucsi, u16 *version)
{
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_VERSION);

        return ccg_read(uc, reg, (u8 *)version, sizeof(*version));
}

static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci)
{
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);

        spin_lock(&uc->op_lock);
        *cci = uc->op_data.cci;
        spin_unlock(&uc->op_lock);

        return 0;
}

static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);

        spin_lock(&uc->op_lock);
        memcpy(val, uc->op_data.message_in, val_len);
        spin_unlock(&uc->op_lock);

        return 0;
}

static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
{
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CONTROL);

        /*
         * UCSI may read CCI instantly after async_control,
         * clear CCI to avoid caller getting wrong data before we get CCI from ISR
         */
        spin_lock(&uc->op_lock);
        uc->op_data.cci = 0;
        spin_unlock(&uc->op_lock);

        return ccg_write(uc, reg, (u8 *)&command, sizeof(command));
}

static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
                                 void *data, size_t size)
{
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
        struct ucsi_connector *con;
        int con_index;
        int ret;

        mutex_lock(&uc->lock);
        pm_runtime_get_sync(uc->dev);

        if (UCSI_COMMAND(command) == UCSI_SET_NEW_CAM &&
            uc->has_multiple_dp) {
                con_index = (command >> 16) &
                        UCSI_CMD_CONNECTOR_MASK;
                if (con_index == 0) {
                        ret = -EINVAL;
                        goto err_put;
                }
                con = &uc->ucsi->connector[con_index - 1];
                ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
        }

        ret = ucsi_sync_control_common(ucsi, command, cci, data, size);

        switch (UCSI_COMMAND(command)) {
        case UCSI_GET_CURRENT_CAM:
                if (uc->has_multiple_dp)
                        ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data);
                break;
        case UCSI_GET_ALTERNATE_MODES:
                if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) {
                        struct ucsi_altmode *alt = data;

                        if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
                                ucsi_ccg_nvidia_altmode(uc, alt, command);
                }
                break;
        case UCSI_GET_CAPABILITY:
                if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
                        struct ucsi_capability *cap = data;

                        cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
                }
                break;
        default:
                break;
        }

err_put:
        pm_runtime_put_sync(uc->dev);
        mutex_unlock(&uc->lock);

        return ret;
}

static const struct ucsi_operations ucsi_ccg_ops = {
        .read_version = ucsi_ccg_read_version,
        .read_cci = ucsi_ccg_read_cci,
        .poll_cci = ucsi_ccg_read_cci,
        .read_message_in = ucsi_ccg_read_message_in,
        .sync_control = ucsi_ccg_sync_control,
        .async_control = ucsi_ccg_async_control,
        .update_altmodes = ucsi_ccg_update_altmodes
};

static irqreturn_t ccg_irq_handler(int irq, void *data)
{
        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI);
        struct ucsi_ccg *uc = data;
        u8 intr_reg;
        u32 cci = 0;
        int ret = 0;

        ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
        if (ret)
                return ret;

        if (!intr_reg)
                return IRQ_HANDLED;
        else if (!(intr_reg & UCSI_READ_INT))
                goto err_clear_irq;

        ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
        if (ret)
                goto err_clear_irq;

        /*
         * As per CCGx UCSI interface guide, copy CCI and MESSAGE_IN
         * to the OpRegion before clear the UCSI interrupt
         */
        ret = ccg_op_region_update(uc, cci);
        if (ret)
                goto err_clear_irq;

err_clear_irq:
        ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));

        if (!ret)
                ucsi_notify_common(uc->ucsi, cci);

        return IRQ_HANDLED;
}

static int ccg_request_irq(struct ucsi_ccg *uc)
{
        unsigned long flags = IRQF_ONESHOT;

        if (!dev_fwnode(uc->dev))
                flags |= IRQF_TRIGGER_HIGH;

        return request_threaded_irq(uc->irq, NULL, ccg_irq_handler, flags, dev_name(uc->dev), uc);
}

static void ccg_pm_workaround_work(struct work_struct *pm_work)
{
        ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work));
}

static int get_fw_info(struct ucsi_ccg *uc)
{
        int err;

        err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version),
                       sizeof(uc->version));
        if (err < 0)
                return err;

        uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) |
                        CCG_VERSION_PATCH(uc->version[FW2].app.patch);

        err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
                       sizeof(uc->info));
        if (err < 0)
                return err;

        return 0;
}

static inline bool invalid_async_evt(int code)
{
        return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX);
}

static void ccg_process_response(struct ucsi_ccg *uc)
{
        struct device *dev = uc->dev;

        if (uc->dev_resp.code & ASYNC_EVENT) {
                if (uc->dev_resp.code == RESET_COMPLETE) {
                        if (test_bit(RESET_PENDING, &uc->flags))
                                uc->cmd_resp = uc->dev_resp.code;
                        get_fw_info(uc);
                }
                if (invalid_async_evt(uc->dev_resp.code))
                        dev_err(dev, "invalid async evt %d\n",
                                uc->dev_resp.code);
        } else {
                if (test_bit(DEV_CMD_PENDING, &uc->flags)) {
                        uc->cmd_resp = uc->dev_resp.code;
                        clear_bit(DEV_CMD_PENDING, &uc->flags);
                } else {
                        dev_err(dev, "dev resp 0x%04x but no cmd pending\n",
                                uc->dev_resp.code);
                }
        }
}

static int ccg_read_response(struct ucsi_ccg *uc)
{
        unsigned long target = jiffies + msecs_to_jiffies(1000);
        struct device *dev = uc->dev;
        u8 intval;
        int status;

        /* wait for interrupt status to get updated */
        do {
                status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval,
                                  sizeof(intval));
                if (status < 0)
                        return status;

                if (intval & DEV_INT)
                        break;
                usleep_range(500, 600);
        } while (time_is_after_jiffies(target));

        if (time_is_before_jiffies(target)) {
                dev_err(dev, "response timeout error\n");
                return -ETIME;
        }

        status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp,
                          sizeof(uc->dev_resp));
        if (status < 0)
                return status;

        status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval));
        if (status < 0)
                return status;

        return 0;
}

/* Caller must hold uc->lock */
static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd)
{
        struct device *dev = uc->dev;
        int ret;

        switch (cmd->reg & 0xF000) {
        case DEV_REG_IDX:
                set_bit(DEV_CMD_PENDING, &uc->flags);
                break;
        default:
                dev_err(dev, "invalid cmd register\n");
                break;
        }

        ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len);
        if (ret < 0)
                return ret;

        msleep(cmd->delay);

        ret = ccg_read_response(uc);
        if (ret < 0) {
                dev_err(dev, "response read error\n");
                switch (cmd->reg & 0xF000) {
                case DEV_REG_IDX:
                        clear_bit(DEV_CMD_PENDING, &uc->flags);
                        break;
                default:
                        dev_err(dev, "invalid cmd register\n");
                        break;
                }
                return -EIO;
        }
        ccg_process_response(uc);

        return uc->cmd_resp;
}

static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc)
{
        struct ccg_cmd cmd;
        int ret;

        cmd.reg = CCGX_RAB_ENTER_FLASHING;
        cmd.data = FLASH_ENTER_SIG;
        cmd.len = 1;
        cmd.delay = 50;

        mutex_lock(&uc->lock);

        ret = ccg_send_command(uc, &cmd);

        mutex_unlock(&uc->lock);

        if (ret != CMD_SUCCESS) {
                dev_err(uc->dev, "enter flashing failed ret=%d\n", ret);
                return ret;
        }

        return 0;
}

static int ccg_cmd_reset(struct ucsi_ccg *uc)
{
        struct ccg_cmd cmd;
        u8 *p;
        int ret;

        p = (u8 *)&cmd.data;
        cmd.reg = CCGX_RAB_RESET_REQ;
        p[0] = RESET_SIG;
        p[1] = CMD_RESET_DEV;
        cmd.len = 2;
        cmd.delay = 5000;

        mutex_lock(&uc->lock);

        set_bit(RESET_PENDING, &uc->flags);

        ret = ccg_send_command(uc, &cmd);
        if (ret != RESET_COMPLETE)
                goto err_clear_flag;

        ret = 0;

err_clear_flag:
        clear_bit(RESET_PENDING, &uc->flags);

        mutex_unlock(&uc->lock);

        return ret;
}

static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable)
{
        struct ccg_cmd cmd;
        int ret;

        cmd.reg = CCGX_RAB_PDPORT_ENABLE;
        if (enable)
                cmd.data = (uc->port_num == 1) ?
                            PDPORT_1 : (PDPORT_1 | PDPORT_2);
        else
                cmd.data = 0x0;
        cmd.len = 1;
        cmd.delay = 10;

        mutex_lock(&uc->lock);

        ret = ccg_send_command(uc, &cmd);

        mutex_unlock(&uc->lock);

        if (ret != CMD_SUCCESS) {
                dev_err(uc->dev, "port control failed ret=%d\n", ret);
                return ret;
        }
        return 0;
}

static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode)
{
        struct ccg_cmd cmd;
        int ret;

        cmd.reg = CCGX_RAB_JUMP_TO_BOOT;

        if (bl_mode)
                cmd.data = TO_BOOT;
        else
                cmd.data = TO_ALT_FW;

        cmd.len = 1;
        cmd.delay = 100;

        mutex_lock(&uc->lock);

        set_bit(RESET_PENDING, &uc->flags);

        ret = ccg_send_command(uc, &cmd);
        if (ret != RESET_COMPLETE)
                goto err_clear_flag;

        ret = 0;

err_clear_flag:
        clear_bit(RESET_PENDING, &uc->flags);

        mutex_unlock(&uc->lock);

        return ret;
}

static int
ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row,
                        const void *data, u8 fcmd)
{
        struct i2c_client *client = uc->client;
        struct ccg_cmd cmd;
        u8 buf[CCG4_ROW_SIZE + 2];
        u8 *p;
        int ret;

        /* Copy the data into the flash read/write memory. */
        put_unaligned_le16(REG_FLASH_RW_MEM, buf);

        memcpy(buf + 2, data, CCG4_ROW_SIZE);

        mutex_lock(&uc->lock);

        ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2);
        if (ret != CCG4_ROW_SIZE + 2) {
                dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret);
                mutex_unlock(&uc->lock);
                return ret < 0 ? ret : -EIO;
        }

        /* Use the FLASH_ROW_READ_WRITE register to trigger */
        /* writing of data to the desired flash row */
        p = (u8 *)&cmd.data;
        cmd.reg = CCGX_RAB_FLASH_ROW_RW;
        p[0] = FLASH_SIG;
        p[1] = fcmd;
        put_unaligned_le16(row, &p[2]);
        cmd.len = 4;
        cmd.delay = 50;
        if (fcmd == FLASH_FWCT_SIG_WR_CMD)
                cmd.delay += 400;
        if (row == 510)
                cmd.delay += 220;
        ret = ccg_send_command(uc, &cmd);

        mutex_unlock(&uc->lock);

        if (ret != CMD_SUCCESS) {
                dev_err(uc->dev, "write flash row failed ret=%d\n", ret);
                return ret;
        }

        return 0;
}

static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid)
{
        struct ccg_cmd cmd;
        int ret;

        cmd.reg = CCGX_RAB_VALIDATE_FW;
        cmd.data = fwid;
        cmd.len = 1;
        cmd.delay = 500;

        mutex_lock(&uc->lock);

        ret = ccg_send_command(uc, &cmd);

        mutex_unlock(&uc->lock);

        if (ret != CMD_SUCCESS)
                return ret;

        return 0;
}

static bool ccg_check_vendor_version(struct ucsi_ccg *uc,
                                     struct version_format *app,
                                     struct fw_config_table *fw_cfg)
{
        struct device *dev = uc->dev;

        /* Check if the fw build is for supported vendors */
        if (le16_to_cpu(app->build) != uc->fw_build) {
                dev_info(dev, "current fw is not from supported vendor\n");
                return false;
        }

        /* Check if the new fw build is for supported vendors */
        if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) {
                dev_info(dev, "new fw is not from supported vendor\n");
                return false;
        }
        return true;
}

static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
                                 struct version_format *app)
{
        const struct firmware *fw = NULL;
        struct device *dev = uc->dev;
        struct fw_config_table fw_cfg;
        u32 cur_version, new_version;
        bool is_later = false;

        if (request_firmware(&fw, fw_name, dev) != 0) {
                dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name);
                return false;
        }

        /*
         * check if signed fw
         * last part of fw image is fw cfg table and signature
         */
        if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE)
                goto out_release_firmware;

        memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
               sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg));

        if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) {
                dev_info(dev, "not a signed image\n");
                goto out_release_firmware;
        }

        /* compare input version with FWCT version */
        cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) |
                        CCG_VERSION(app->ver);

        new_version = le16_to_cpu(fw_cfg.app.build) |
                        CCG_VERSION_PATCH(fw_cfg.app.patch) |
                        CCG_VERSION(fw_cfg.app.ver);

        if (!ccg_check_vendor_version(uc, app, &fw_cfg))
                goto out_release_firmware;

        if (new_version > cur_version)
                is_later = true;

out_release_firmware:
        release_firmware(fw);
        return is_later;
}

static int ccg_fw_update_needed(struct ucsi_ccg *uc,
                                enum enum_flash_mode *mode)
{
        struct device *dev = uc->dev;
        int err;
        struct version_info version[3];

        err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
                       sizeof(uc->info));
        if (err) {
                dev_err(dev, "read device mode failed\n");
                return err;
        }

        err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version,
                       sizeof(version));
        if (err) {
                dev_err(dev, "read device mode failed\n");
                return err;
        }

        if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0",
                   sizeof(struct version_info)) == 0) {
                dev_info(dev, "secondary fw is not flashed\n");
                *mode = SECONDARY_BL;
        } else if (le16_to_cpu(version[FW1].base.build) <
                secondary_fw_min_ver) {
                dev_info(dev, "secondary fw version is too low (< %d)\n",
                         secondary_fw_min_ver);
                *mode = SECONDARY;
        } else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0",
                   sizeof(struct version_info)) == 0) {
                dev_info(dev, "primary fw is not flashed\n");
                *mode = PRIMARY;
        } else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY],
                   &version[FW2].app)) {
                dev_info(dev, "found primary fw with later version\n");
                *mode = PRIMARY;
        } else {
                dev_info(dev, "secondary and primary fw are the latest\n");
                *mode = FLASH_NOT_NEEDED;
        }
        return 0;
}

static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
{
        struct device *dev = uc->dev;
        const struct firmware *fw = NULL;
        const char *p, *s;
        const char *eof;
        int err, row, len, line_sz, line_cnt = 0;
        unsigned long start_time = jiffies;
        struct fw_config_table  fw_cfg;
        u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE];
        u8 *wr_buf;

        err = request_firmware(&fw, ccg_fw_names[mode], dev);
        if (err) {
                dev_err(dev, "request %s failed err=%d\n",
                        ccg_fw_names[mode], err);
                return err;
        }

        if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >>
                        CCG_DEVINFO_FWMODE_SHIFT) == FW2) {
                err = ccg_cmd_port_control(uc, false);
                if (err < 0)
                        goto release_fw;
                err = ccg_cmd_jump_boot_mode(uc, 0);
                if (err < 0)
                        goto release_fw;
        }

        eof = fw->data + fw->size;

        /*
         * check if signed fw
         * last part of fw image is fw cfg table and signature
         */
        if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig))
                goto not_signed_fw;

        memcpy((uint8_t *)&fw_cfg, fw->data + fw->size -
               sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg));

        if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) {
                dev_info(dev, "not a signed image\n");
                goto not_signed_fw;
        }
        eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig);

        memcpy((uint8_t *)&fw_cfg_sig,
               fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig));

        /* flash fw config table and signature first */
        err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg,
                                      FLASH_FWCT1_WR_CMD);
        if (err)
                goto release_fw;

        err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE,
                                      FLASH_FWCT2_WR_CMD);
        if (err)
                goto release_fw;

        err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig,
                                      FLASH_FWCT_SIG_WR_CMD);
        if (err)
                goto release_fw;

not_signed_fw:
        wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL);
        if (!wr_buf) {
                err = -ENOMEM;
                goto release_fw;
        }

        err = ccg_cmd_enter_flashing(uc);
        if (err)
                goto release_mem;

        /*****************************************************************
         * CCG firmware image (.cyacd) file line format
         *
         * :00rrrrllll[dd....]cc/r/n
         *
         * :00   header
         * rrrr is row number to flash                          (4 char)
         * llll is data len to flash                            (4 char)
         * dd   is a data field represents one byte of data     (512 char)
         * cc   is checksum                                     (2 char)
         * \r\n newline
         *
         * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527
         *
         *****************************************************************/

        p = strnchr(fw->data, fw->size, ':');
        while (p < eof) {
                s = strnchr(p + 1, eof - p - 1, ':');

                if (!s)
                        s = eof;

                line_sz = s - p;

                if (line_sz != CYACD_LINE_SIZE) {
                        dev_err(dev, "Bad FW format line_sz=%d\n", line_sz);
                        err =  -EINVAL;
                        goto release_mem;
                }

                if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) {
                        err =  -EINVAL;
                        goto release_mem;
                }

                row = get_unaligned_be16(wr_buf);
                len = get_unaligned_be16(&wr_buf[2]);

                if (len != CCG4_ROW_SIZE) {
                        err =  -EINVAL;
                        goto release_mem;
                }

                err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4,
                                              FLASH_WR_CMD);
                if (err)
                        goto release_mem;

                line_cnt++;
                p = s;
        }

        dev_info(dev, "total %d row flashed. time: %dms\n",
                 line_cnt, jiffies_to_msecs(jiffies - start_time));

        err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 :  FW1);
        if (err)
                dev_err(dev, "%s validation failed err=%d\n",
                        (mode == PRIMARY) ? "FW2" :  "FW1", err);
        else
                dev_info(dev, "%s validated\n",
                         (mode == PRIMARY) ? "FW2" :  "FW1");

        err = ccg_cmd_port_control(uc, false);
        if (err < 0)
                goto release_mem;

        err = ccg_cmd_reset(uc);
        if (err < 0)
                goto release_mem;

        err = ccg_cmd_port_control(uc, true);
        if (err < 0)
                goto release_mem;

release_mem:
        kfree(wr_buf);

release_fw:
        release_firmware(fw);
        return err;
}

/*******************************************************************************
 * CCG4 has two copies of the firmware in addition to the bootloader.
 * If the device is running FW1, FW2 can be updated with the new version.
 * Dual firmware mode allows the CCG device to stay in a PD contract and support
 * USB PD and Type-C functionality while a firmware update is in progress.
 ******************************************************************************/
static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode)
{
        int err = 0;

        while (flash_mode != FLASH_NOT_NEEDED) {
                err = do_flash(uc, flash_mode);
                if (err < 0)
                        return err;
                err = ccg_fw_update_needed(uc, &flash_mode);
                if (err < 0)
                        return err;
        }
        dev_info(uc->dev, "CCG FW update successful\n");

        return err;
}

static int ccg_restart(struct ucsi_ccg *uc)
{
        struct device *dev = uc->dev;
        int status;

        status = ucsi_ccg_init(uc);
        if (status < 0) {
                dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status);
                return status;
        }

        status = ccg_request_irq(uc);
        if (status < 0) {
                dev_err(dev, "request_threaded_irq failed - %d\n", status);
                return status;
        }

        status = ucsi_register(uc->ucsi);
        if (status) {
                dev_err(uc->dev, "failed to register the interface\n");
                return status;
        }

        pm_runtime_enable(uc->dev);
        return 0;
}

static void ccg_update_firmware(struct work_struct *work)
{
        struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work);
        enum enum_flash_mode flash_mode;
        int status;

        status = ccg_fw_update_needed(uc, &flash_mode);
        if (status < 0)
                return;

        if (flash_mode != FLASH_NOT_NEEDED) {
                ucsi_unregister(uc->ucsi);
                pm_runtime_disable(uc->dev);
                free_irq(uc->irq, uc);

                ccg_fw_update(uc, flash_mode);
                ccg_restart(uc);
        }
}

static ssize_t do_flash_store(struct device *dev,
                              struct device_attribute *attr,
                              const char *buf, size_t n)
{
        struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
        bool flash;

        if (kstrtobool(buf, &flash))
                return -EINVAL;

        if (!flash)
                return n;

        schedule_work(&uc->work);
        return n;
}

static umode_t ucsi_ccg_attrs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
        struct device *dev = kobj_to_dev(kobj);
        struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));

        if (!uc->fw_build)
                return 0;

        return attr->mode;
}

static DEVICE_ATTR_WO(do_flash);

static struct attribute *ucsi_ccg_attrs[] = {
        &dev_attr_do_flash.attr,
        NULL,
};
static struct attribute_group ucsi_ccg_attr_group = {
        .attrs = ucsi_ccg_attrs,
        .is_visible = ucsi_ccg_attrs_is_visible,
};
static const struct attribute_group *ucsi_ccg_groups[] = {
        &ucsi_ccg_attr_group,
        NULL,
};

static int ucsi_ccg_probe(struct i2c_client *client)
{
        struct device *dev = &client->dev;
        struct ucsi_ccg *uc;
        const char *fw_name;
        int status;

        uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
        if (!uc)
                return -ENOMEM;

        uc->dev = dev;
        uc->client = client;
        uc->irq = client->irq;
        mutex_init(&uc->lock);
        INIT_WORK(&uc->work, ccg_update_firmware);
        INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);

        /* Only fail FW flashing when FW build information is not provided */
        status = device_property_read_string(dev, "firmware-name", &fw_name);
        if (!status) {
                if (!strcmp(fw_name, "nvidia,jetson-agx-xavier"))
                        uc->fw_build = CCG_FW_BUILD_NVIDIA_TEGRA;
                else if (!strcmp(fw_name, "nvidia,gpu"))
                        uc->fw_build = CCG_FW_BUILD_NVIDIA;
                if (!uc->fw_build)
                        dev_err(uc->dev, "failed to get FW build information\n");
        }

        /* reset ccg device and initialize ucsi */
        status = ucsi_ccg_init(uc);
        if (status < 0) {
                dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
                return status;
        }

        status = get_fw_info(uc);
        if (status < 0) {
                dev_err(uc->dev, "get_fw_info failed - %d\n", status);
                return status;
        }

        uc->port_num = 1;

        if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
                uc->port_num++;

        uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops);
        if (IS_ERR(uc->ucsi))
                return PTR_ERR(uc->ucsi);

        ucsi_set_drvdata(uc->ucsi, uc);

        status = ccg_request_irq(uc);
        if (status < 0) {
                dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
                goto out_ucsi_destroy;
        }

        status = ucsi_register(uc->ucsi);
        if (status)
                goto out_free_irq;

        i2c_set_clientdata(client, uc);

        device_disable_async_suspend(uc->dev);

        pm_runtime_set_active(uc->dev);
        pm_runtime_enable(uc->dev);
        pm_runtime_use_autosuspend(uc->dev);
        pm_runtime_set_autosuspend_delay(uc->dev, 5000);
        pm_runtime_idle(uc->dev);

        return 0;

out_free_irq:
        free_irq(uc->irq, uc);
out_ucsi_destroy:
        ucsi_destroy(uc->ucsi);

        return status;
}

static void ucsi_ccg_remove(struct i2c_client *client)
{
        struct ucsi_ccg *uc = i2c_get_clientdata(client);

        cancel_work_sync(&uc->pm_work);
        cancel_work_sync(&uc->work);
        pm_runtime_disable(uc->dev);
        ucsi_unregister(uc->ucsi);
        ucsi_destroy(uc->ucsi);
        free_irq(uc->irq, uc);
}

static const struct of_device_id ucsi_ccg_of_match_table[] = {
                { .compatible = "cypress,cypd4226", },
                { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ucsi_ccg_of_match_table);

static const struct i2c_device_id ucsi_ccg_device_id[] = {
        { "ccgx-ucsi" },
        {}
};
MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);

static const struct acpi_device_id amd_i2c_ucsi_match[] = {
        {"AMDI0042"},
        {}
};
MODULE_DEVICE_TABLE(acpi, amd_i2c_ucsi_match);

static int ucsi_ccg_resume(struct device *dev)
{
        struct i2c_client *client = to_i2c_client(dev);
        struct ucsi_ccg *uc = i2c_get_clientdata(client);

        return ucsi_resume(uc->ucsi);
}

static int ucsi_ccg_runtime_suspend(struct device *dev)
{
        return 0;
}

static int ucsi_ccg_runtime_resume(struct device *dev)
{
        struct i2c_client *client = to_i2c_client(dev);
        struct ucsi_ccg *uc = i2c_get_clientdata(client);

        /*
         * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
         * of missing interrupt when a device is connected for runtime resume.
         * Schedule a work to call ISR as a workaround.
         */
        if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
            uc->fw_version <= CCG_OLD_FW_VERSION)
                schedule_work(&uc->pm_work);

        return 0;
}

static const struct dev_pm_ops ucsi_ccg_pm = {
        .resume = ucsi_ccg_resume,
        .runtime_suspend = ucsi_ccg_runtime_suspend,
        .runtime_resume = ucsi_ccg_runtime_resume,
};

static struct i2c_driver ucsi_ccg_driver = {
        .driver = {
                .name = "ucsi_ccg",
                .pm = &ucsi_ccg_pm,
                .dev_groups = ucsi_ccg_groups,
                .acpi_match_table = amd_i2c_ucsi_match,
                .of_match_table = ucsi_ccg_of_match_table,
        },
        .probe = ucsi_ccg_probe,
        .remove = ucsi_ccg_remove,
        .id_table = ucsi_ccg_device_id,
};

module_i2c_driver(ucsi_ccg_driver);

MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
MODULE_LICENSE("GPL v2");