From: ShuFanLee <shufan_...@richtek.com>

Richtek RT1711H Type-C chip driver that works with
Type-C Port Controller Manager to provide USB PD and
USB Type-C functionalities.

Signed-off-by: ShuFanLee <shufan_...@richtek.com>
---
 .../devicetree/bindings/usb/richtek,rt1711h.txt    |   38 +
 arch/arm64/boot/dts/hisilicon/rt1711h.dtsi         |   11 +
 drivers/usb/typec/Kconfig                          |    2 +
 drivers/usb/typec/Makefile                         |    1 +
 drivers/usb/typec/rt1711h/Kconfig                  |    7 +
 drivers/usb/typec/rt1711h/Makefile                 |    2 +
 drivers/usb/typec/rt1711h/rt1711h.c                | 2241 ++++++++++++++++++++
 drivers/usb/typec/rt1711h/rt1711h.h                |  300 +++
 8 files changed, 2602 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/richtek,rt1711h.txt
 create mode 100644 arch/arm64/boot/dts/hisilicon/rt1711h.dtsi
 create mode 100644 drivers/usb/typec/rt1711h/Kconfig
 create mode 100644 drivers/usb/typec/rt1711h/Makefile
 create mode 100644 drivers/usb/typec/rt1711h/rt1711h.c
 create mode 100644 drivers/usb/typec/rt1711h/rt1711h.h

diff --git a/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt 
b/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt
new file mode 100644
index 0000000..c28299c
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt
@@ -0,0 +1,38 @@
+Richtek RT1711H Type-C Port Controller.
+
+Required properties:
+- compatible : Must be "richtek,typec_rt1711h";
+- reg : Must be 0x4e, it's default slave address of RT1711H.
+- rt,intr_gpio : IRQ GPIO pin that's connected to RT1711H interrupt.
+
+Optional node:
+- rt,name : Name used for registering IRQ and creating kthread.
+           If this property is not specified, "default" will be applied.
+- rt,def_role : Default port role (TYPEC_SINK(0) or TYPEC_SOURCE(1)).
+               Set to TYPEC_NO_PREFERRED_ROLE(-1) if no default role.
+               If this property is not specified, TYPEC_SINK will be applied.
+- rt,port_type : Port type (TYPEC_PORT_DFP(0), TYPEC_PORT_UFP(1),
+                or TYPEC_PORT_DRP(2)). If this property is not specified,
+                TYPEC_PORT_DRP will be applied.
+- rt,max_snk_mv : Maximum acceptable sink voltage in mV.
+                 If this property is not specified, 5000mV will be applied.
+- rt,max_snk_ma : Maximum sink current in mA.
+                 If this property is not specified, 3000mA will be applied.
+- rt,max_snk_mw : Maximum required sink power in mW.
+                 If this property is not specified, 15000mW will be applied.
+- rt,operating_snk_mw : Required operating sink power in mW.
+                       If this property is not specified,
+                       2500mW will be applied.
+- rt,try_role_hw : True if try.{Src,Snk} is implemented in hardware.
+                  If this property is not specified, False will be applied.
+
+Example:
+rt1711h@4e {
+       status = "ok";
+       compatible = "richtek,typec_rt1711h";
+       reg = <0x4e>;
+       rt,intr_gpio = <&gpio26 0 0x0>;
+       rt,name = "rt1711h";
+       rt,port_type = <2>; /* 0: DFP, 1: UFP, 2: DRP */
+       rt,def_role = <0>; /* 0: SNK, 1: SRC, -1: TYPEC_NO_PREFERRED_ROLE */
+};
diff --git a/arch/arm64/boot/dts/hisilicon/rt1711h.dtsi 
b/arch/arm64/boot/dts/hisilicon/rt1711h.dtsi
new file mode 100644
index 0000000..4196cc0
--- /dev/null
+++ b/arch/arm64/boot/dts/hisilicon/rt1711h.dtsi
@@ -0,0 +1,11 @@
+&i2c7 {
+       rt1711h@4e {
+               status = "ok";
+               compatible = "richtek,typec_rt1711h";
+               reg = <0x4e>;
+               rt,intr_gpio = <&gpio26 0 0x0>;
+               rt,name = "rt1711h";
+               rt,port_type = <2>; /* 0: DFP, 1: UFP, 2: DRP */
+               rt,def_role = <0>; /* 0: SNK, 1: SRC */
+       };
+};
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index bcb2744..7bede0b 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -56,6 +56,8 @@ if TYPEC_TCPM
 
 source "drivers/usb/typec/fusb302/Kconfig"
 
+source "drivers/usb/typec/rt1711h/Kconfig"
+
 config TYPEC_WCOVE
        tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
        depends on ACPI
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index bb3138a..e3aaf3c 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -2,6 +2,7 @@
 obj-$(CONFIG_TYPEC)            += typec.o
 obj-$(CONFIG_TYPEC_TCPM)       += tcpm.o
 obj-y                          += fusb302/
+obj-$(CONFIG_TYPEC_RT1711H)    += rt1711h/
 obj-$(CONFIG_TYPEC_WCOVE)      += typec_wcove.o
 obj-$(CONFIG_TYPEC_UCSI)       += ucsi/
 obj-$(CONFIG_TYPEC_TPS6598X)   += tps6598x.o
diff --git a/drivers/usb/typec/rt1711h/Kconfig 
b/drivers/usb/typec/rt1711h/Kconfig
new file mode 100644
index 0000000..2fbfff5
--- /dev/null
+++ b/drivers/usb/typec/rt1711h/Kconfig
@@ -0,0 +1,7 @@
+config TYPEC_RT1711H
+       tristate "Richtek RT1711H Type-C chip driver"
+       depends on I2C && POWER_SUPPLY
+       help
+         The Richtek RT1711H   Type-C chip driver that works with
+         Type-C Port Controller Manager to provide USB PD and USB
+         Type-C functionalities.
diff --git a/drivers/usb/typec/rt1711h/Makefile 
b/drivers/usb/typec/rt1711h/Makefile
new file mode 100644
index 0000000..5fab8ae
--- /dev/null
+++ b/drivers/usb/typec/rt1711h/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_TYPEC_RT1711H)    += rt1711h.o
diff --git a/drivers/usb/typec/rt1711h/rt1711h.c 
b/drivers/usb/typec/rt1711h/rt1711h.c
new file mode 100644
index 0000000..1aef3e8
--- /dev/null
+++ b/drivers/usb/typec/rt1711h/rt1711h.c
@@ -0,0 +1,2241 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017 Richtek Technologh Corp.
+ *
+ * Richtek RT1711H Type-C Chip Driver
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/i2c.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/pd.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/power_supply.h>
+#include <linux/extcon.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/cpu.h>
+#include <linux/alarmtimer.h>
+#include <linux/sched/clock.h>
+#include <uapi/linux/sched/types.h>
+
+#include "rt1711h.h"
+
+#define RT1711H_DRV_VERSION    "1.0.3"
+
+#define LOG_BUFFER_ENTRIES     1024
+#define LOG_BUFFER_ENTRY_SIZE  128 /* 128 char per line */
+
+enum {
+       RT1711H_DBG_LOG = 0,
+       RT1711H_DBG_REGS,
+       RT1711H_DBG_REG_ADDR,
+       RT1711H_DBG_DATA,
+       RT1711H_DBG_MAX,
+};
+
+struct rt1711h_dbg_info {
+       struct rt1711h_chip *chip;
+       int id;
+};
+
+
+struct rt1711h_chip {
+       struct i2c_client *i2c;
+       struct device *dev;
+       uint16_t did;
+       int irq_gpio;
+       int irq;
+       char *name;
+       struct tcpc_dev tcpc_dev;
+       struct tcpc_config tcpc_cfg;
+       struct tcpm_port *tcpm_port;
+       struct regulator *vbus;
+       struct extcon_dev *extcon;
+
+       /* IRQ */
+       struct kthread_worker irq_worker;
+       struct kthread_work irq_work;
+       struct task_struct *irq_worker_task;
+       atomic_t poll_count;
+       struct delayed_work poll_work;
+
+       /* LPM */
+       struct delayed_work wakeup_work;
+       struct alarm wakeup_timer;
+       struct mutex wakeup_lock;
+       enum typec_cc_pull lpm_pull;
+       bool wakeup_once;
+       bool low_rp_duty_cntdown;
+       bool cable_only;
+       bool lpm;
+
+       /* I2C */
+       atomic_t i2c_busy;
+       atomic_t pm_suspend;
+
+       /* psy + psy status */
+       struct power_supply *psy;
+       u32 current_limit;
+       u32 supply_voltage;
+
+       /* lock for sharing chip states */
+       struct mutex lock;
+
+       /* port status */
+       bool vconn_on;
+       bool vbus_on;
+       bool charge_on;
+       bool vbus_present;
+       enum typec_cc_polarity polarity;
+       enum typec_cc_status cc1;
+       enum typec_cc_status cc2;
+       enum typec_role pwr_role;
+       bool drp_toggling;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *dbgdir;
+       struct rt1711h_dbg_info dbg_info[RT1711H_DBG_MAX];
+       struct dentry *dbg_files[RT1711H_DBG_MAX];
+       int dbg_regidx;
+       struct mutex dbgops_lock;
+       /* lock for log buffer access */
+       struct mutex logbuffer_lock;
+       int logbuffer_head;
+       int logbuffer_tail;
+       u8 *logbuffer[LOG_BUFFER_ENTRIES];
+#endif /* CONFIG_DEBUG_FS */
+};
+
+/*
+ * Logging & debugging
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+static int rt1711h_reg_block_read(struct rt1711h_chip *chip, uint8_t reg,
+       int len, uint8_t *data);
+static int rt1711h_reg_block_write(struct rt1711h_chip *chip, uint8_t reg,
+       int len, const uint8_t *data);
+
+struct reg_desc {
+       uint8_t addr;
+       uint8_t size;
+};
+#define DECL_REG(_addr, _size) {.addr = _addr, .size = _size}
+
+static struct reg_desc rt1711h_reg_desc[] = {
+       DECL_REG(RT1711H_REG_VID, 2),
+       DECL_REG(RT1711H_REG_PID, 2),
+       DECL_REG(RT1711H_REG_DID, 2),
+       DECL_REG(RT1711H_REG_TYPEC_REV, 2),
+       DECL_REG(RT1711H_REG_PD_REV, 2),
+       DECL_REG(RT1711H_REG_PDIF_REV, 2),
+       DECL_REG(RT1711H_REG_ALERT, 2),
+       DECL_REG(RT1711H_REG_ALERT_MASK, 2),
+       DECL_REG(RT1711H_REG_POWER_STATUS_MASK, 1),
+       DECL_REG(RT1711H_REG_FAULT_STATUS_MASK, 1),
+       DECL_REG(RT1711H_REG_TCPC_CTRL, 1),
+       DECL_REG(RT1711H_REG_ROLE_CTRL, 1),
+       DECL_REG(RT1711H_REG_FAULT_CTRL, 1),
+       DECL_REG(RT1711H_REG_POWER_CTRL, 1),
+       DECL_REG(RT1711H_REG_CC_STATUS, 1),
+       DECL_REG(RT1711H_REG_POWER_STATUS, 1),
+       DECL_REG(RT1711H_REG_FAULT_STATUS, 1),
+       DECL_REG(RT1711H_REG_COMMAND, 1),
+       DECL_REG(RT1711H_REG_MSG_HDR_INFO, 1),
+       DECL_REG(RT1711H_REG_RX_DETECT, 1),
+       DECL_REG(RT1711H_REG_RX_BYTE_CNT, 1),
+       DECL_REG(RT1711H_REG_RX_BUF_FRAME_TYPE, 1),
+       DECL_REG(RT1711H_REG_RX_HDR, 2),
+       DECL_REG(RT1711H_REG_RX_DATA, 1),
+       DECL_REG(RT1711H_REG_TRANSMIT, 1),
+       DECL_REG(RT1711H_REG_TX_BYTE_CNT, 1),
+       DECL_REG(RT1711H_REG_TX_HDR, 2),
+       DECL_REG(RT1711H_REG_TX_DATA, 1),
+       DECL_REG(RT1711H_REG_CLK_CTRL2, 1),
+       DECL_REG(RT1711H_REG_CLK_CTRL3, 1),
+       DECL_REG(RT1711H_REG_BMC_CTRL, 1),
+       DECL_REG(RT1711H_REG_BMCIO_RXDZSEL, 1),
+       DECL_REG(RT1711H_REG_VCONN_CLIMITEN, 1),
+       DECL_REG(RT1711H_REG_RT_STATUS, 1),
+       DECL_REG(RT1711H_REG_RT_INT, 1),
+       DECL_REG(RT1711H_REG_RT_MASK, 1),
+       DECL_REG(RT1711H_REG_IDLE_CTRL, 1),
+       DECL_REG(RT1711H_REG_INTRST_CTRL, 1),
+       DECL_REG(RT1711H_REG_WATCHDOG_CTRL, 1),
+       DECL_REG(RT1711H_REG_I2CRST_CTRL, 1),
+       DECL_REG(RT1711H_REG_SWRESET, 1),
+       DECL_REG(RT1711H_REG_TTCPC_FILTER, 1),
+       DECL_REG(RT1711H_REG_DRP_TOGGLE_CYCLE, 1),
+       DECL_REG(RT1711H_REG_DRP_DUTY_CTRL, 1),
+       DECL_REG(RT1711H_REG_BMCIO_RXDZEN, 1),
+};
+
+static const char *rt1711h_dbg_filename[RT1711H_DBG_MAX] = {
+       "log", "regs", "reg_addr", "data",
+};
+
+static bool rt1711h_log_full(struct rt1711h_chip *chip)
+{
+       return chip->logbuffer_tail ==
+               (chip->logbuffer_head + 1) % LOG_BUFFER_ENTRIES;
+}
+
+static void _rt1711h_log(struct rt1711h_chip *chip, const char *fmt,
+                        va_list args)
+{
+       char tmpbuffer[LOG_BUFFER_ENTRY_SIZE];
+       u64 ts_nsec = local_clock();
+       unsigned long rem_nsec;
+
+       if (!chip->logbuffer[chip->logbuffer_head]) {
+               chip->logbuffer[chip->logbuffer_head] =
+               devm_kzalloc(chip->dev, LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL);
+               if (!chip->logbuffer[chip->logbuffer_head])
+                       return;
+       }
+
+       vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args);
+
+       mutex_lock(&chip->logbuffer_lock);
+
+       if (rt1711h_log_full(chip)) {
+               chip->logbuffer_head = max(chip->logbuffer_head - 1, 0);
+               strlcpy(tmpbuffer, "overflow", sizeof(tmpbuffer));
+       }
+
+       if (chip->logbuffer_head < 0 ||
+               chip->logbuffer_head >= LOG_BUFFER_ENTRIES) {
+               dev_warn(chip->dev, "%s bad log buffer index %d\n", __func__,
+                       chip->logbuffer_head);
+               goto abort;
+       }
+
+       if (!chip->logbuffer[chip->logbuffer_head]) {
+               dev_warn(chip->dev, "%s log buffer index %d is NULL\n",
+                       __func__, chip->logbuffer_head);
+               goto abort;
+       }
+
+       rem_nsec = do_div(ts_nsec, 1000000000);
+       scnprintf(chip->logbuffer[chip->logbuffer_head], LOG_BUFFER_ENTRY_SIZE,
+               "[%5lu.%06lu] %s", (unsigned long)ts_nsec, rem_nsec / 1000,
+                 tmpbuffer);
+       chip->logbuffer_head = (chip->logbuffer_head + 1) % LOG_BUFFER_ENTRIES;
+
+abort:
+       mutex_unlock(&chip->logbuffer_lock);
+}
+
+static void rt1711h_log(struct rt1711h_chip *chip,
+       const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       _rt1711h_log(chip, fmt, args);
+       va_end(args);
+}
+
+static int rt1711h_log_show(struct rt1711h_chip *chip, struct seq_file *s)
+{
+       int tail;
+
+       mutex_lock(&chip->logbuffer_lock);
+       tail = chip->logbuffer_tail;
+       while (tail != chip->logbuffer_head) {
+               seq_printf(s, "%s", chip->logbuffer[tail]);
+               tail = (tail + 1) % LOG_BUFFER_ENTRIES;
+       }
+       if (!seq_has_overflowed(s))
+               chip->logbuffer_tail = tail;
+       mutex_unlock(&chip->logbuffer_lock);
+
+       return 0;
+}
+
+static int rt1711h_regs_show(struct rt1711h_chip *chip, struct seq_file *s)
+{
+       int ret = 0;
+       int i = 0, j = 0;
+       struct reg_desc *desc = NULL;
+       uint8_t regval[2] = {0};
+
+       for (i = 0; i < ARRAY_SIZE(rt1711h_reg_desc); i++) {
+               desc = &rt1711h_reg_desc[i];
+               ret = rt1711h_reg_block_read(chip, desc->addr, desc->size,
+                       regval);
+               if (ret < 0) {
+                       dev_err(chip->dev, "%s read reg0x%02X fail\n",
+                               __func__, desc->addr);
+                       continue;
+               }
+
+               seq_printf(s, "reg0x%02x:0x", desc->addr);
+               for (j = 0; j < desc->size; j++)
+                       seq_printf(s, "%02x,", regval[j]);
+               seq_puts(s, "\n");
+       }
+
+       return 0;
+}
+
+static inline int rt1711h_reg_addr_show(struct rt1711h_chip *chip,
+       struct seq_file *s)
+{
+       struct reg_desc *desc = &rt1711h_reg_desc[chip->dbg_regidx];
+
+       seq_printf(s, "0x%02x\n", desc->addr);
+       return 0;
+}
+
+static inline int rt1711h_data_show(struct rt1711h_chip *chip,
+       struct seq_file *s)
+{
+       int ret = 0, i = 0;
+       struct reg_desc *desc = &rt1711h_reg_desc[chip->dbg_regidx];
+       uint8_t regval[2] = {0};
+
+       ret = rt1711h_reg_block_read(chip, desc->addr, desc->size, regval);
+       if (ret < 0)
+               return ret;
+
+       seq_printf(s, "reg0x%02x=0x", desc->addr);
+       for (i = 0; i < desc->size; i++)
+               seq_printf(s, "%02x,", regval[i]);
+       seq_puts(s, "\n");
+       return 0;
+}
+
+static int rt1711h_dbg_show(struct seq_file *s, void *v)
+{
+       int ret = 0;
+       struct rt1711h_dbg_info *info = (struct rt1711h_dbg_info *)s->private;
+       struct rt1711h_chip *chip = info->chip;
+
+       mutex_lock(&chip->dbgops_lock);
+       switch (info->id) {
+       case RT1711H_DBG_LOG:
+               ret = rt1711h_log_show(chip, s);
+               break;
+       case RT1711H_DBG_REGS:
+               ret = rt1711h_regs_show(chip, s);
+               break;
+       case RT1711H_DBG_REG_ADDR:
+               ret = rt1711h_reg_addr_show(chip, s);
+               break;
+       case RT1711H_DBG_DATA:
+               ret = rt1711h_data_show(chip, s);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       mutex_unlock(&chip->dbgops_lock);
+       return ret;
+}
+
+static int rt1711h_dbg_open(struct inode *inode, struct file *file)
+{
+       if (file->f_mode & FMODE_READ)
+               return single_open(file, rt1711h_dbg_show, inode->i_private);
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+       char *token;
+       int base, cnt;
+
+       token = strsep(&buf, " ");
+
+       for (cnt = 0; cnt < num_of_par; cnt++) {
+               if (token != NULL) {
+                       if ((token[1] == 'x') || (token[1] == 'X'))
+                               base = 16;
+                       else
+                               base = 10;
+
+                       if (kstrtoul(token, base, &param1[cnt]) != 0)
+                               return -EINVAL;
+
+                       token = strsep(&buf, " ");
+               } else
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static int get_datas(const char *buf, const int length,
+       unsigned char *data_buffer, unsigned char data_length)
+{
+       int i, ptr;
+       long int value;
+       char token[5];
+
+       token[0] = '0';
+       token[1] = 'x';
+       token[4] = 0;
+       if (buf[0] != '0' || buf[1] != 'x')
+               return -EINVAL;
+
+       ptr = 2;
+       for (i = 0; (i < data_length) && (ptr + 2 <= length); i++) {
+               token[2] = buf[ptr++];
+               token[3] = buf[ptr++];
+               ptr++;
+               if (kstrtoul(token, 16, &value) != 0)
+                       return -EINVAL;
+               data_buffer[i] = value;
+       }
+       return 0;
+}
+
+static int rt1711h_regaddr2idx(uint8_t reg_addr)
+{
+       int i = 0;
+       struct reg_desc *desc = NULL;
+
+       for (i = 0; i < ARRAY_SIZE(rt1711h_reg_desc); i++) {
+               desc = &rt1711h_reg_desc[i];
+               if (desc->addr == reg_addr)
+                       return i;
+       }
+       return -EINVAL;
+}
+
+static ssize_t rt1711h_dbg_write(struct file *file, const char __user *ubuf,
+       size_t count, loff_t *ppos)
+{
+       int ret = 0;
+       struct rt1711h_dbg_info *info =
+               (struct rt1711h_dbg_info *)file->private_data;
+       struct rt1711h_chip *chip = info->chip;
+       struct reg_desc *desc = NULL;
+       char lbuf[128];
+       long int param[5];
+       unsigned char reg_data[2] = {0};
+
+       if (count > sizeof(lbuf) - 1)
+               return -EFAULT;
+
+       ret = copy_from_user(lbuf, ubuf, count);
+       if (ret)
+               return -EFAULT;
+       lbuf[count] = '\0';
+
+       mutex_lock(&chip->dbgops_lock);
+       switch (info->id) {
+       case RT1711H_DBG_REG_ADDR:
+               ret = get_parameters(lbuf, param, 1);
+               if (ret < 0) {
+                       dev_err(chip->dev, "%s get param fail\n", __func__);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               ret = rt1711h_regaddr2idx(param[0]);
+               if (ret < 0) {
+                       dev_err(chip->dev, "%s addr2idx fail\n", __func__);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               chip->dbg_regidx = ret;
+               break;
+       case RT1711H_DBG_DATA:
+               desc = &rt1711h_reg_desc[chip->dbg_regidx];
+               if ((desc->size - 1) * 3 + 5 != count) {
+                       dev_err(chip->dev, "%s incorrect input length\n",
+                               __func__);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               ret = get_datas((char *)ubuf, count, reg_data, desc->size);
+               if (ret < 0) {
+                       dev_err(chip->dev, "%s get data fail\n", __func__);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               ret = rt1711h_reg_block_write(chip, desc->addr, desc->size,
+                       reg_data);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+out:
+       mutex_unlock(&chip->dbgops_lock);
+       return ret < 0 ? ret : count;
+}
+
+static int rt1711h_dbg_release(struct inode *inode, struct file *file)
+{
+       if (file->f_mode & FMODE_READ)
+               return single_release(inode, file);
+       return 0;
+}
+
+static const struct file_operations rt1711h_dbg_ops = {
+       .open           = rt1711h_dbg_open,
+       .llseek         = seq_lseek,
+       .read           = seq_read,
+       .write          = rt1711h_dbg_write,
+       .release        = rt1711h_dbg_release,
+};
+
+
+static int rt1711h_debugfs_init(struct rt1711h_chip *chip)
+{
+       int ret = 0, i = 0;
+       struct rt1711h_dbg_info *info = NULL;
+       int len = 0;
+       char *dirname = NULL;
+
+       mutex_init(&chip->logbuffer_lock);
+       mutex_init(&chip->dbgops_lock);
+       len = strlen(dev_name(chip->dev));
+       dirname = devm_kzalloc(chip->dev, len + 9, GFP_KERNEL);
+       if (!dirname)
+               return -ENOMEM;
+       snprintf(dirname, len + 9, "rt1711h-%s", dev_name(chip->dev));
+       if (!chip->dbgdir) {
+               chip->dbgdir = debugfs_create_dir(dirname, NULL);
+               if (!chip->dbgdir)
+                       return -ENOMEM;
+       }
+
+       for (i = 0; i < RT1711H_DBG_MAX; i++) {
+               info = &chip->dbg_info[i];
+               info->chip = chip;
+               info->id = i;
+               chip->dbg_files[i] = debugfs_create_file(
+                       rt1711h_dbg_filename[i], S_IFREG | 0444,
+                       chip->dbgdir, info, &rt1711h_dbg_ops);
+               if (!chip->dbg_files[i]) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
+       return 0;
+err:
+       debugfs_remove_recursive(chip->dbgdir);
+       return ret;
+}
+
+static void rt1711h_debugfs_exit(struct rt1711h_chip *chip)
+{
+       debugfs_remove_recursive(chip->dbgdir);
+}
+
+#else
+
+static void rt1711h_log(const struct rt1711h_chip *chip, const char *fmt, ...)
+{
+}
+
+static int rt1711h_debugfs_init(const struct rt1711h_chip *chip)
+{
+       return 0;
+}
+
+static void rt1711h_debugfs_exit(const struct rt1711h_chip *chip)
+{
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+static const char * const typec_cc_status_name[] = {
+       [TYPEC_CC_OPEN]         = "Open",
+       [TYPEC_CC_RA]           = "Ra",
+       [TYPEC_CC_RD]           = "Rd",
+       [TYPEC_CC_RP_DEF]       = "Rp-def",
+       [TYPEC_CC_RP_1_5]       = "Rp-1.5",
+       [TYPEC_CC_RP_3_0]       = "Rp-3.0",
+};
+
+static const char * const cc_polarity_name[] = {
+       [TYPEC_POLARITY_CC1]    = "Polarity_CC1",
+       [TYPEC_POLARITY_CC2]    = "Polarity_CC2",
+};
+
+static const char * const transmit_type_name[] = {
+       [TCPC_TX_SOP]                   = "SOP",
+       [TCPC_TX_SOP_PRIME]             = "SOP'",
+       [TCPC_TX_SOP_PRIME_PRIME]       = "SOP''",
+       [TCPC_TX_SOP_DEBUG_PRIME]       = "DEBUG'",
+       [TCPC_TX_SOP_DEBUG_PRIME_PRIME] = "DEBUG''",
+       [TCPC_TX_HARD_RESET]            = "HARD_RESET",
+       [TCPC_TX_CABLE_RESET]           = "CABLE_RESET",
+       [TCPC_TX_BIST_MODE_2]           = "BIST_MODE_2",
+};
+
+static const char * const typec_role_name[] = {
+       [TYPEC_SINK]            = "Sink",
+       [TYPEC_SOURCE]          = "Source",
+};
+
+static const char * const typec_data_role_name[] = {
+       [TYPEC_DEVICE]          = "Device",
+       [TYPEC_HOST]            = "Host",
+};
+
+static const enum typec_cc_pull typec_cc_status_pull_mapping[] = {
+       [TYPEC_CC_OPEN] = TYPEC_CC_PULL_OPEN,
+       [TYPEC_CC_RA] = TYPEC_CC_PULL_RA,
+       [TYPEC_CC_RD] = TYPEC_CC_PULL_RD,
+       [TYPEC_CC_RP_DEF] = TYPEC_CC_PULL_RP_DEF,
+       [TYPEC_CC_RP_1_5] = TYPEC_CC_PULL_RP_1_5,
+       [TYPEC_CC_RP_3_0] = TYPEC_CC_PULL_RP_3_0,
+};
+
+static inline enum typec_cc_pull rt1711h_cc_status2pull(enum typec_cc_status 
cc)
+{
+       return typec_cc_status_pull_mapping[cc];
+}
+
+#define PDO_FIXED_FLAGS \
+       (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
+
+static const u32 src_pdo[] = {
+       PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
+};
+
+static const u32 snk_pdo[] = {
+       PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
+};
+
+static const struct tcpc_config rt1711h_tcpc_config = {
+       .src_pdo = src_pdo,
+       .nr_src_pdo = ARRAY_SIZE(src_pdo),
+       .snk_pdo = snk_pdo,
+       .nr_snk_pdo = ARRAY_SIZE(snk_pdo),
+       .max_snk_mv = 5000,
+       .max_snk_ma = 3000,
+       .max_snk_mw = 15000,
+       .operating_snk_mw = 2500,
+       .type = TYPEC_PORT_DRP,
+       .default_role = TYPEC_SINK,
+       .alt_modes = NULL,
+};
+
+#define RT1711H_RESUME_RETRY 10
+#define RT1711H_RESUME_RETRY_SLEEP 50
+
+static inline bool rt1711h_is_suspended(struct rt1711h_chip *chip)
+{
+       int retry_cnt = 0;
+
+       for (retry_cnt = 0; retry_cnt < RT1711H_RESUME_RETRY; retry_cnt++) {
+               if (atomic_read(&chip->pm_suspend)) {
+                       rt1711h_log(chip, "%s retry %d/%d\n", __func__,
+                               retry_cnt + 1, RT1711H_RESUME_RETRY);
+                       msleep(RT1711H_RESUME_RETRY_SLEEP);
+               } else
+                       return false;
+       }
+
+       return true;
+}
+
+static int rt1711h_reg_read(struct rt1711h_chip *chip, uint8_t reg,
+       uint8_t *data)
+{
+       int ret = 0;
+
+       atomic_set(&chip->i2c_busy, 1);
+       if (rt1711h_is_suspended(chip)) {
+               atomic_set(&chip->i2c_busy, 0);
+               return -ETIMEDOUT;
+       }
+
+       ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, 1, data);
+       if (ret < 0)
+               rt1711h_log(chip, "%s reg%02X fail(%d)\n", __func__, reg, ret);
+       atomic_set(&chip->i2c_busy, 0);
+
+       return ret;
+}
+
+static int rt1711h_reg_write(struct rt1711h_chip *chip, uint8_t reg,
+       uint8_t data)
+{
+       int ret = 0;
+
+       atomic_set(&chip->i2c_busy, 1);
+       if (rt1711h_is_suspended(chip)) {
+               atomic_set(&chip->i2c_busy, 0);
+               return -ETIMEDOUT;
+       }
+
+       ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, 1, &data);
+       if (ret < 0)
+               rt1711h_log(chip, "%s reg%02X = %02X fail(%d)\n", __func__, reg,
+                       data, ret);
+       atomic_set(&chip->i2c_busy, 0);
+
+       return ret;
+}
+
+static int rt1711h_reg_block_write(struct rt1711h_chip *chip, uint8_t reg,
+       int len, const uint8_t *data)
+{
+       int ret = 0;
+
+       atomic_set(&chip->i2c_busy, 1);
+       if (rt1711h_is_suspended(chip)) {
+               atomic_set(&chip->i2c_busy, 0);
+               return -ETIMEDOUT;
+       }
+
+       ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, len, data);
+       if (ret < 0)
+               rt1711h_log(chip, "%s reg%02X, len = %d fail(%d)\n", __func__,
+                       reg, len, ret);
+       atomic_set(&chip->i2c_busy, 0);
+
+       return ret;
+}
+
+static int rt1711h_reg_block_read(struct rt1711h_chip *chip, uint8_t reg,
+       int len, uint8_t *data)
+{
+       int ret = 0;
+
+       atomic_set(&chip->i2c_busy, 1);
+       if (rt1711h_is_suspended(chip)) {
+               atomic_set(&chip->i2c_busy, 0);
+               return -ETIMEDOUT;
+       }
+
+       ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, len, data);
+       if (ret < 0)
+               rt1711h_log(chip, "%s reg%02X, len = %d fail(%d)\n", __func__,
+                       reg, len, ret);
+       atomic_set(&chip->i2c_busy, 0);
+
+       return ret;
+}
+
+static inline int rt1711h_reg_write_word(struct rt1711h_chip *chip, uint8_t 
reg,
+       uint16_t data)
+{
+       data = cpu_to_le16(data);
+       return rt1711h_reg_block_write(chip, reg, 2, (uint8_t *)&data);
+}
+
+static inline int rt1711h_reg_read_word(struct rt1711h_chip *chip, uint8_t reg,
+       uint16_t *data)
+{
+       int ret = 0;
+
+       ret = rt1711h_reg_block_read(chip, reg, 2, (uint8_t *)data);
+       if (ret < 0)
+               return ret;
+       *data = le16_to_cpu(*data);
+       return 0;
+}
+
+static int rt1711h_psy_get_property(struct power_supply *psy,
+       enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct rt1711h_chip *chip = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = chip->charge_on;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = chip->supply_voltage * 1000; /* mV -> µV */
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               val->intval = chip->current_limit * 1000; /* mA -> µA */
+               break;
+       default:
+               return -ENODATA;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property rt1711h_psy_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static const struct power_supply_desc rt1711h_psy_desc = {
+       .name           = "rt1711h-typec-source",
+       .type           = POWER_SUPPLY_TYPE_USB_TYPE_C,
+       .properties     = rt1711h_psy_properties,
+       .num_properties = ARRAY_SIZE(rt1711h_psy_properties),
+       .get_property   = rt1711h_psy_get_property,
+};
+
+static inline int rt1711h_software_reset(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_SWRESET, 0x01);
+       if (ret < 0)
+               return ret;
+
+       usleep_range(1000, 2000);
+       return 0;
+}
+
+static inline int rt1711h_command(struct rt1711h_chip *chip, uint8_t cmd)
+{
+       return rt1711h_reg_write(chip, RT1711H_REG_COMMAND, cmd);
+}
+
+static inline int rt1711h_init_cc_params(struct rt1711h_chip *chip,
+       const enum typec_cc_status *cc)
+{
+       int ret = 0;
+       uint8_t en = 0, sel = 0;
+
+       if (*cc == TYPEC_CC_RP_DEF) { /* 0.55 */
+               en = 0;
+               sel = 0x81;
+       } else if (chip->did >= RT1711H_DID_D) { /* 0.35 & 0.75 */
+               en = 1;
+               sel = 0x81;
+       } else { /* 0.4 & 0.7 */
+               en = 1;
+               sel = 0x80;
+       }
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_BMCIO_RXDZEN, en);
+       if (ret < 0)
+               return ret;
+
+       return rt1711h_reg_write(chip, RT1711H_REG_BMCIO_RXDZSEL, sel);
+}
+
+static int rt1711h_alert_status_clear(struct rt1711h_chip *chip, uint32_t mask)
+{
+       int ret = 0;
+       uint16_t mask_t1 = 0;
+       uint8_t mask_t2 = 0;
+
+       /* Write 1 clear */
+       mask_t1 = (uint16_t)mask;
+       ret = rt1711h_reg_write_word(chip, RT1711H_REG_ALERT, mask_t1);
+       if (ret < 0)
+               return ret;
+
+       mask_t2 = mask >> 16;
+       if (mask_t2) {
+               ret = rt1711h_reg_write(chip, RT1711H_REG_RT_INT, mask_t2);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int rt1711h_init_alert_mask(struct rt1711h_chip *chip)
+{
+       uint16_t mask = 0;
+
+       mask = RT1711H_REG_ALERT_CC_STATUS | RT1711H_REG_ALERT_POWER_STATUS;
+
+       mask |= RT1711H_REG_ALERT_TX_SUCCESS | RT1711H_REG_ALERT_TX_DISCARDED
+               | RT1711H_REG_ALERT_TX_FAILED | RT1711H_REG_ALERT_RX_HARD_RST
+               | RT1711H_REG_ALERT_RX_STATUS | RT1711H_REG_ALERT_RX_BUF_OVF;
+
+       mask |= RT1711H_REG_ALERT_FAULT;
+
+       return rt1711h_reg_write_word(chip, RT1711H_REG_ALERT_MASK, mask);
+}
+
+static int rt1711h_init_power_status_mask(struct rt1711h_chip *chip)
+{
+       uint8_t mask = RT1711H_REG_POWER_STATUS_VBUS_PRES;
+
+       return rt1711h_reg_write(chip, RT1711H_REG_POWER_STATUS_MASK, mask);
+}
+
+static int rt1711h_init_fault_mask(struct rt1711h_chip *chip)
+{
+       const uint8_t mask = RT1711H_REG_FAULT_STATUS_VCONN_OV
+               | RT1711H_REG_FAULT_STATUS_VCONN_OC;
+
+       return rt1711h_reg_write(chip, RT1711H_REG_FAULT_STATUS_MASK, mask);
+}
+
+static int rt1711h_init_rt_mask(struct rt1711h_chip *chip)
+{
+       uint8_t rt_mask = RT1711H_REG_M_VBUS_80;
+
+       if (chip->did < RT1711H_DID_D)
+               rt_mask |= (RT1711H_REG_M_WAKEUP | RT1711H_REG_M_RA_DETACH);
+
+       return rt1711h_reg_write(chip, RT1711H_REG_RT_MASK, rt_mask);
+}
+
+#define RT1711H_WAKEUP_WORK_TIME       (1000)
+static enum alarmtimer_restart
+       rt1711h_alarm_wakeup_handler(struct alarm *alarm, ktime_t now)
+{
+       struct rt1711h_chip *chip =
+               container_of(alarm, struct rt1711h_chip, wakeup_timer);
+
+       rt1711h_log(chip, "%s\n", __func__);
+       pm_wakeup_event(chip->dev, RT1711H_WAKEUP_WORK_TIME);
+       schedule_delayed_work(&chip->wakeup_work, 0);
+       return ALARMTIMER_NORESTART;
+}
+
+static void rt1711h_enable_wakeup_timer(struct rt1711h_chip *chip, bool en)
+{
+       int tout = 300; /* s */
+
+       rt1711h_log(chip, "%s %d\n", __func__, en);
+       if (en) {
+               if (!chip->wakeup_once)
+                       tout = (chip->low_rp_duty_cntdown) ? 5 : 20;
+               alarm_start_relative(&chip->wakeup_timer, ktime_set(tout, 0));
+       } else
+               alarm_cancel(&chip->wakeup_timer);
+}
+
+static inline bool rt1711h_is_cc_open(struct rt1711h_chip *chip)
+{
+       if (!chip->drp_toggling && chip->cc1 == TYPEC_CC_OPEN &&
+               chip->cc2 == TYPEC_CC_OPEN)
+               return true;
+       return false;
+}
+
+static int rt1711h_set_low_rp_duty(struct rt1711h_chip *chip, bool low_rp)
+{
+       uint16_t duty = low_rp ? RT1711H_LOW_RP_DUTY : RT1711H_NORMAL_RP_DUTY;
+
+       return rt1711h_reg_write_word(chip, RT1711H_REG_DRP_DUTY_CTRL, duty);
+}
+
+/*
+ * rt1711h_check_false_ra_detach
+ *
+ * Check single Ra resistance (eMark) exists or not when
+ *     1) ra detach int triggered
+ *     2) wakeup timer triggered
+ *
+ * If reentering low-power mode and eMark still exists,
+ * it may cause an infinite loop.
+ *
+ * If cc status is both open, return true; otherwise return false
+ */
+static int __tcpm_get_cc(struct rt1711h_chip *chip);
+static int __tcpm_set_cc(struct rt1711h_chip *chip, enum typec_cc_status cc);
+static inline int __tcpm_start_drp_toggling(struct rt1711h_chip *chip);
+static inline bool rt1711h_check_false_ra_detach(struct rt1711h_chip *chip)
+{
+       bool drp = (chip->tcpc_cfg.type == TYPEC_PORT_DRP) ? true : false;
+
+       rt1711h_log(chip, "%s\n", __func__);
+
+       /*
+        * If the DUT is DRP and current CC status has stopped toggling,
+        * let cc_handler to handle it later.
+        *
+        * If CC is toggling, force CC to present Rp
+        */
+       if (drp) {
+               __tcpm_get_cc(chip);
+
+               if (!chip->drp_toggling) {
+                       rt1711h_log(chip, "%s 1(%s, %s)\n", __func__,
+                               typec_cc_status_name[chip->cc1],
+                               typec_cc_status_name[chip->cc2]);
+                       return true;
+               }
+               __tcpm_set_cc(chip, TYPEC_CC_RP_DEF);
+               usleep_range(1000, 2000);
+       }
+
+       /*
+        * Check CC status
+        * Rd (device) -> let cc_handler to handle it later
+        * eMark only -> Reschedule wakeup timer
+        * Open -> (true condition)
+        * Read to reenter low-power mode.
+        * If we repeatedly enter this situation,
+        * it will trigger low rp duty protection
+        */
+       __tcpm_get_cc(chip);
+       if (rt1711h_is_cc_open(chip))
+               chip->cable_only = false;
+       else if ((chip->cc1 + chip->cc2) == TYPEC_CC_RA) {
+               chip->cable_only = true;
+               rt1711h_log(chip, "%s 2(emark)\n", __func__);
+       } else {
+               chip->cable_only = false;
+               rt1711h_log(chip, "%s 3(%s %s)\n", __func__,
+                       typec_cc_status_name[chip->cc1],
+                       typec_cc_status_name[chip->cc2]);
+               return true;
+       }
+
+       if (chip->cable_only)
+               rt1711h_enable_wakeup_timer(chip, true);
+       else {
+               if (chip->low_rp_duty_cntdown)
+                       rt1711h_set_low_rp_duty(chip, true);
+               else {
+                       chip->wakeup_once = false;
+                       chip->low_rp_duty_cntdown = true;
+               }
+       }
+
+       /* If DUP is DRP, force CC to toggle again */
+       if (drp) {
+               __tcpm_start_drp_toggling(chip);
+               rt1711h_alert_status_clear(chip,
+                       RT1711H_REG_ALERT_EXT_RA_DETACH);
+       }
+
+       return chip->cable_only;
+}
+
+static int rt1711h_set_low_power_mode(struct rt1711h_chip *chip, bool en,
+       enum typec_cc_pull pull)
+{
+       uint8_t data = 0;
+
+       rt1711h_log(chip, "%s %d\n", __func__, en);
+
+       if (en) {
+               data = RT1711H_REG_BMCIO_LPEN;
+
+               if (pull & TYPEC_CC_PULL_RP)
+                       data |= RT1711H_REG_BMCIO_LPRPRD;
+       } else
+               data = RT1711H_REG_BMCIO_BG_EN |
+                       RT1711H_REG_VBUS_DET_EN | RT1711H_REG_BMCIO_OSC_EN;
+
+       return rt1711h_reg_write(chip, RT1711H_REG_BMC_CTRL, data);
+}
+
+static int rt1711h_enter_lpm_again(struct rt1711h_chip *chip)
+{
+       bool check_ra = (chip->lpm) || (chip->cable_only);
+
+       if (check_ra && rt1711h_check_false_ra_detach(chip))
+               return 0;
+
+       rt1711h_log(chip, "%s retry lpm\n", __func__);
+       chip->lpm = true;
+
+       rt1711h_set_low_power_mode(chip, true,
+               (chip->pwr_role != TYPEC_SOURCE) ?
+               TYPEC_CC_PULL_DRP : TYPEC_CC_PULL_RP);
+
+       return 0;
+}
+
+static void rt1711h_wakeup_work(struct work_struct *work)
+{
+       struct rt1711h_chip *chip =
+               container_of(work, struct rt1711h_chip, wakeup_work.work);
+
+       mutex_lock(&chip->wakeup_lock);
+       mutex_lock(&chip->lock);
+       rt1711h_log(chip, "%s\n", __func__);
+       chip->wakeup_once = true;
+       rt1711h_enter_lpm_again(chip);
+       mutex_unlock(&chip->lock);
+       mutex_unlock(&chip->wakeup_lock);
+       pm_relax(chip->dev);
+}
+
+static inline int rt1711h_try_low_power_mode(struct rt1711h_chip *chip)
+{
+       return rt1711h_set_low_power_mode(chip, true, chip->lpm_pull);
+}
+
+static inline int rt1711h_enter_low_power_mode(struct rt1711h_chip *chip)
+{
+       return rt1711h_try_low_power_mode(chip);
+}
+
+static inline int rt1711h_enable_low_power_mode(struct rt1711h_chip *chip,
+       enum typec_cc_pull pull)
+{
+       if (chip->cable_only) {
+               rt1711h_log(chip, "%s ra only\n", __func__);
+               rt1711h_enable_wakeup_timer(chip, true);
+               return 0;
+       }
+
+       if (chip->lpm != true) {
+               chip->lpm = true;
+               chip->lpm_pull = pull;
+               return rt1711h_enter_low_power_mode(chip);
+       }
+
+       return 0;
+}
+
+static inline int rt1711h_disable_low_power_mode(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+
+       if (chip->lpm != false) {
+               chip->lpm = false;
+               rt1711h_set_low_rp_duty(chip, false);
+               ret = rt1711h_set_low_power_mode(chip, false,
+                       TYPEC_CC_PULL_DRP);
+       }
+
+       chip->wakeup_once = false;
+       chip->low_rp_duty_cntdown = false;
+       return ret;
+}
+
+static int tcpm_init(struct tcpc_dev *dev)
+{
+       int ret = 0;
+       struct rt1711h_chip *chip = container_of(dev,
+               struct rt1711h_chip, tcpc_dev);
+
+       rt1711h_log(chip, "%s\n", __func__);
+
+       /* CK 300K from 320K, shipping off, auto_idle enable, tout = 32ms */
+       ret = rt1711h_reg_write(chip, RT1711H_REG_IDLE_CTRL,
+               RT1711H_REG_IDLE_SET(0, 1, 1, 2));
+       if (ret < 0) {
+               rt1711h_log(chip, "%s set idle ctrl fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_I2CRST_CTRL,
+               RT1711H_REG_I2CRST_SET(true, 0x0F));
+       if (ret < 0) {
+               rt1711h_log(chip, "%s set i2crst fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       /* UFP Both RD setting */
+       /* DRP = 0, RpVal = 0 (Default), Rd, Rd */
+       ret = rt1711h_reg_write(chip, RT1711H_REG_ROLE_CTRL,
+               RT1711H_REG_ROLE_CTRL_RES_SET(0, 0, TYPEC_CC_PULL_RD,
+               TYPEC_CC_PULL_RD));
+       if (ret < 0) {
+               rt1711h_log(chip, "%s set role ctrl fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       /*
+        * CC Detect Debounce : (26.7 * val) us
+        * Transition window count : spec 12~20us, based on 2.4MHz
+        */
+       ret = rt1711h_reg_write(chip, RT1711H_REG_TTCPC_FILTER, 0x0F);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s set cc deb fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       /*  DRP Toggle Cycle : (51.2 + 6.4 * val) ms */
+       rt1711h_reg_write(chip, RT1711H_REG_DRP_TOGGLE_CYCLE, 4);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s set tog cyc fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       /* DRP Duty Ctrl: 33% */
+       ret = rt1711h_reg_write_word(chip, RT1711H_REG_DRP_DUTY_CTRL,
+               RT1711H_NORMAL_RP_DUTY);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s set drp duty fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       /* Vconn OC */
+       ret = rt1711h_reg_write(chip, RT1711H_REG_VCONN_CLIMITEN, 1);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s en vconn oc fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       /* Alert & Mask */
+       ret = rt1711h_alert_status_clear(chip, 0xffffffff);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s clear alert fail(%d)\n", __func__, ret);
+               return ret;
+       }
+       ret = rt1711h_init_power_status_mask(chip);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s init pwr mask fail(%d)\n", __func__, ret);
+               return ret;
+       }
+       ret = rt1711h_init_alert_mask(chip);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s init alert mask fail(%d)\n", __func__,
+                       ret);
+               return ret;
+       }
+       ret = rt1711h_init_fault_mask(chip);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s init fault mask fail(%d)\n", __func__,
+                       ret);
+               return ret;
+       }
+       ret = rt1711h_init_rt_mask(chip);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s init rt mask fail(%d)\n", __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int tcpm_get_vbus(struct tcpc_dev *dev)
+{
+       int ret = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       rt1711h_log(chip, "%s\n", __func__);
+       mutex_lock(&chip->lock);
+       ret = chip->vbus_present ? 1 : 0;
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+static int tcpm_get_current_limit(struct tcpc_dev *dev)
+{
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+       int current_limit = 0;
+       unsigned long timeout;
+
+       rt1711h_log(chip, "%s\n", __func__);
+       if (!chip->extcon)
+               return 0;
+
+       /*
+        * USB2 Charger detection may still be in progress when we get here,
+        * this can take upto 600ms, wait 800ms max.
+        */
+       timeout = jiffies + msecs_to_jiffies(800);
+       do {
+               if (extcon_get_state(chip->extcon, EXTCON_CHG_USB_SDP) == 1)
+                       current_limit = 500;
+
+               if (extcon_get_state(chip->extcon, EXTCON_CHG_USB_CDP) == 1 ||
+                   extcon_get_state(chip->extcon, EXTCON_CHG_USB_ACA) == 1)
+                       current_limit = 1500;
+
+               if (extcon_get_state(chip->extcon, EXTCON_CHG_USB_DCP) == 1)
+                       current_limit = 2000;
+
+               msleep(50);
+       } while (current_limit == 0 && time_before(jiffies, timeout));
+
+       return current_limit;
+}
+
+static int __tcpm_set_cc(struct rt1711h_chip *chip, enum typec_cc_status cc)
+{
+       uint8_t data = 0, pull = 0, rp_lvl = 0;
+
+       rt1711h_log(chip, "%s %s\n", __func__, typec_cc_status_name[cc]);
+       switch (cc) {
+       case TYPEC_CC_OPEN:
+       case TYPEC_CC_RD:
+       case TYPEC_CC_RP_DEF:
+       case TYPEC_CC_RP_1_5:
+       case TYPEC_CC_RP_3_0:
+               pull = rt1711h_cc_status2pull(cc);
+               rp_lvl = RT1711H_TYPEC_CC_PULL_GET_RP_LVL(pull);
+               pull = RT1711H_TYPEC_CC_PULL_GET_RES(pull);
+               data = RT1711H_REG_ROLE_CTRL_RES_SET(0, rp_lvl, pull, pull);
+               break;
+       default:
+               rt1711h_log(chip, "%s unsupported cc value %s\n", __func__,
+                       typec_cc_status_name[cc]);
+               return -EINVAL;
+       }
+
+       return rt1711h_reg_write(chip, RT1711H_REG_ROLE_CTRL, data);
+}
+
+static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
+{
+       int ret = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       ret = __tcpm_set_cc(chip, cc);
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+static inline enum typec_cc_status rt1711h_cc2enum(enum typec_cc_status cc,
+       bool act_as_sink)
+{
+       return act_as_sink ? cc + 2 : cc;
+}
+
+static int __tcpm_get_cc(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+       uint8_t status = 0, role_ctrl = 0, cc_role = 0;
+       bool act_as_sink, act_as_drp;
+
+       ret = rt1711h_reg_read(chip, RT1711H_REG_CC_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       ret = rt1711h_reg_read(chip, RT1711H_REG_ROLE_CTRL, &role_ctrl);
+       if (ret < 0)
+               return ret;
+
+       if (status & RT1711H_REG_CC_STATUS_DRP_TOGGLING) {
+               /* during toggling, consider cc as Open */
+               chip->cc1 = TYPEC_CC_OPEN;
+               chip->cc2 = TYPEC_CC_OPEN;
+               rt1711h_log(chip, "%s drp toggling\n", __func__);
+               return 0;
+       }
+       chip->drp_toggling = false;
+
+       act_as_drp = RT1711H_REG_ROLE_CTRL_DRP & role_ctrl;
+
+       if (act_as_drp)
+               act_as_sink = RT1711H_REG_CC_STATUS_DRP_RESULT(status);
+       else {
+               cc_role = RT1711H_REG_ROLE_CTRL_CC1(role_ctrl);
+               act_as_sink = (cc_role == TYPEC_CC_PULL_RP) ? false : true;
+       }
+
+       chip->cc1 = RT1711H_REG_CC_STATUS_CC1(status);
+       chip->cc2 = RT1711H_REG_CC_STATUS_CC2(status);
+       if (chip->cc1 != TYPEC_CC_OPEN)
+               chip->cc1 = rt1711h_cc2enum(chip->cc1, act_as_sink);
+       if (chip->cc2 != TYPEC_CC_OPEN)
+               chip->cc2 = rt1711h_cc2enum(chip->cc2, act_as_sink);
+
+       ret = rt1711h_init_cc_params(chip, chip->polarity ?
+               &chip->cc2 : &chip->cc1);
+       if (ret < 0)
+               rt1711h_log(chip, "%s init cc param fail(%d)\n", __func__, ret);
+
+       rt1711h_log(chip, "%s cc1 = %s, cc2 = %s\n", __func__,
+               typec_cc_status_name[chip->cc1],
+               typec_cc_status_name[chip->cc2]);
+
+       return 0;
+}
+
+static int tcpm_get_cc(struct tcpc_dev *dev,
+       enum typec_cc_status *cc1, enum typec_cc_status *cc2)
+{
+       int ret = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       ret = __tcpm_get_cc(chip);
+       if (ret < 0)
+               goto out;
+       *cc1 = chip->cc1;
+       *cc2 = chip->cc2;
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int tcpm_set_polarity(struct tcpc_dev *dev,
+       enum typec_cc_polarity polarity)
+{
+       int ret = 0;
+       uint8_t data = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       rt1711h_log(chip, "%s %s\n", __func__, cc_polarity_name[polarity]);
+       ret = rt1711h_init_cc_params(chip, polarity ? &chip->cc2 : &chip->cc1);
+       if (ret < 0)
+               goto out;
+
+       ret = rt1711h_reg_read(chip, RT1711H_REG_TCPC_CTRL, &data);
+       if (ret < 0)
+               goto out;
+
+       data &= ~RT1711H_REG_TCPC_CTRL_PLUG_ORIENT;
+       data |= polarity ? RT1711H_REG_TCPC_CTRL_PLUG_ORIENT : 0;
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_TCPC_CTRL, data);
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int tcpm_set_vconn(struct tcpc_dev *dev, bool on)
+{
+       int ret = 0;
+       uint8_t data = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       if (chip->vconn_on == on) {
+               rt1711h_log(chip, "%s vconn is already %d\n", __func__, on);
+               goto out;
+       }
+       ret = rt1711h_reg_read(chip, RT1711H_REG_POWER_CTRL, &data);
+       if (ret < 0)
+               goto out;
+
+       data &= ~RT1711H_REG_POWER_CTRL_VCONN;
+       data |= on ? RT1711H_REG_POWER_CTRL_VCONN : 0;
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_POWER_CTRL, data);
+       if (ret < 0)
+               goto out;
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_IDLE_CTRL,
+               RT1711H_REG_IDLE_SET(0, 1, on ? 0 : 1, 2));
+       if (ret < 0)
+               goto out;
+
+       chip->vconn_on = on;
+       rt1711h_log(chip, "%s vconn = %d\n", __func__, on);
+
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
+{
+       int ret = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       if (chip->vbus_on == on)
+               rt1711h_log(chip, "%s vbus is already %d\n", __func__, on);
+       else {
+               ret = (on ? regulator_enable : regulator_disable)(chip->vbus);
+               if (ret < 0) {
+                       rt1711h_log(chip, "%s cannot %s vbus regulator(%d)\n",
+                               __func__, on ? "enable" : "disable", ret);
+                       goto out;
+               }
+               chip->vbus_on = on;
+               rt1711h_log(chip, "%s vbus = %d\n", __func__, on);
+       }
+       if (chip->charge_on == charge)
+               rt1711h_log(chip, "%s chg is already %d\n", __func__, charge);
+       else {
+               chip->charge_on = charge;
+               power_supply_changed(chip->psy);
+       }
+
+out:
+       mutex_unlock(&chip->lock);
+       return 0;
+}
+
+static int tcpm_set_current_limit(struct tcpc_dev *dev, u32 max_ma,
+       u32 mv)
+{
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       rt1711h_log(chip, "%s %d ma, %d mv (not implemented)\n", __func__,
+               max_ma, mv);
+
+       mutex_lock(&chip->lock);
+       chip->supply_voltage = mv;
+       chip->current_limit = max_ma;
+       mutex_unlock(&chip->lock);
+
+       power_supply_changed(chip->psy);
+       return 0;
+}
+
+static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
+{
+       int ret = 0;
+       uint8_t rx_en = 0x00;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       rt1711h_log(chip, "%s %d\n", __func__, on);
+       if (on)
+               rx_en = BIT(TCPC_TX_SOP) | BIT(TCPC_TX_HARD_RESET);
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_RX_DETECT, rx_en);
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int tcpm_set_roles(struct tcpc_dev *dev, bool attached,
+       enum typec_role pwr, enum typec_data_role data)
+{
+       int ret = 0;
+       uint8_t msg_hdr = RT1711H_REG_MSG_HDR_INFO_SET(data, pwr);
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       rt1711h_log(chip, "%s %s %s\n", __func__, typec_role_name[pwr],
+               typec_data_role_name[data]);
+       ret = rt1711h_reg_write(chip, RT1711H_REG_MSG_HDR_INFO, msg_hdr);
+       if (ret < 0)
+               goto out;
+       chip->pwr_role = pwr;
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static inline int __tcpm_start_drp_toggling(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+       uint8_t data = 0;
+       uint8_t rp_def = RT1711H_TYPEC_CC_PULL_GET_RP_LVL(TYPEC_CC_PULL_RP_DEF);
+       uint8_t cc_role = TYPEC_CC_PULL_RD;
+
+       data = RT1711H_REG_ROLE_CTRL_RES_SET(0, rp_def, cc_role, cc_role);
+       ret = rt1711h_reg_write(chip, RT1711H_REG_ROLE_CTRL, data);
+       if (ret < 0)
+               return ret;
+       mdelay(1);
+       data = RT1711H_REG_ROLE_CTRL_RES_SET(1, rp_def, cc_role, cc_role);
+       ret = rt1711h_reg_write(chip, RT1711H_REG_ROLE_CTRL, data);
+       if (ret < 0)
+               return ret;
+       ret = rt1711h_command(chip, RT1711H_CMD_LOOK_CONNECTION);
+       if (ret < 0)
+               return ret;
+       chip->drp_toggling = true;
+
+       return 0;
+}
+
+static int tcpm_start_drp_toggling(struct tcpc_dev *dev,
+       enum typec_cc_status cc)
+{
+       int ret = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+
+       mutex_lock(&chip->lock);
+       rt1711h_log(chip, "%s\n", __func__);
+       ret = __tcpm_start_drp_toggling(chip);
+       if (ret < 0)
+               goto out;
+       if (chip->did < RT1711H_DID_D)
+               ret = rt1711h_enable_low_power_mode(chip, TYPEC_CC_PULL_DRP);
+
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+#pragma pack(push, 1)
+struct tcpc_transmit_packet {
+       uint8_t cnt;
+       uint16_t msg_header;
+       uint8_t data[sizeof(uint32_t) * PD_MAX_PAYLOAD];
+};
+#pragma pack(pop)
+
+static int tcpm_pd_transmit(struct tcpc_dev *dev,
+       enum tcpm_transmit_type type, const struct pd_message *msg)
+{
+       int ret = 0;
+       int data_cnt = 0;
+       struct rt1711h_chip *chip = container_of(dev, struct rt1711h_chip,
+               tcpc_dev);
+       struct tcpc_transmit_packet packet;
+
+       rt1711h_log(chip, "%s %s\n", __func__, transmit_type_name[type]);
+       mutex_lock(&chip->lock);
+       switch (type) {
+       case TCPC_TX_SOP:
+               data_cnt = sizeof(uint32_t) * pd_header_cnt_le(msg->header);
+               packet.cnt = data_cnt + sizeof(uint16_t);
+               packet.msg_header = msg->header;
+               if (data_cnt > 0)
+                       memcpy(packet.data, (uint8_t *)msg->payload, data_cnt);
+
+               ret = rt1711h_reg_block_write(chip, RT1711H_REG_TX_BYTE_CNT,
+                       packet.cnt + 1, (uint8_t *)&packet);
+               if (ret < 0) {
+                       rt1711h_log(chip, "%s fail (%d)\n", __func__, ret);
+                       goto out;
+               }
+               break;
+       case TCPC_TX_HARD_RESET:
+               break;
+       default:
+               rt1711h_log(chip, "type %s not supported\n",
+                       transmit_type_name[type]);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = rt1711h_reg_write(chip, RT1711H_REG_TRANSMIT,
+               RT1711H_REG_TRANSMIT_SET(3, type));
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int rt1711h_parse_dt(struct rt1711h_chip *chip)
+{
+       int ret = 0, len = 0;
+       uint32_t val = 0;
+       struct device_node *np = chip->dev->of_node;
+       struct tcpc_config *cfg = &chip->tcpc_cfg;
+       const char *name = "default";
+
+       if (!np)
+               return -EINVAL;
+
+       dev_info(chip->dev, "%s\n", __func__);
+
+       memcpy(cfg, &rt1711h_tcpc_config, sizeof(struct tcpc_config));
+
+       ret = of_get_named_gpio(np, "rt,intr_gpio", 0);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s get int gpio fail(%d)\n",
+                       __func__, ret);
+               return ret;
+       }
+       chip->irq_gpio = ret;
+       dev_info(chip->dev, "%s irq_gpio = %d\n", __func__, chip->irq_gpio);
+
+       of_property_read_string(np, "rt,name", &name);
+
+       len = strlen(name);
+       chip->name = devm_kzalloc(chip->dev, len + 1, GFP_KERNEL);
+       if (!chip->name)
+               return -ENOMEM;
+       strlcpy(chip->name, name, strlen(name) + 1);
+
+       if (of_property_read_u32(np, "rt,def_role", &val) == 0)
+               cfg->default_role = val;
+
+       if (of_property_read_u32(np, "rt,port_type", &val) == 0)
+               cfg->type = val;
+
+       if (of_property_read_u32(np, "rt,max_snk_mv", &val) == 0)
+               cfg->max_snk_mv = val;
+
+       if (of_property_read_u32(np, "rt,max_snk_ma", &val) == 0)
+               cfg->max_snk_ma = val;
+
+       if (of_property_read_u32(np, "rt,max_snk_mw", &val) == 0)
+               cfg->max_snk_mw = val;
+
+       if (of_property_read_u32(np, "rt,operating_snk_mw", &val) == 0)
+               cfg->operating_snk_mw = val;
+
+       cfg->try_role_hw = of_property_read_bool(np, "rt,try_role_hw");
+
+       return 0;
+}
+
+static void rt1711h_init_tcpc_dev(struct rt1711h_chip *chip)
+{
+       chip->tcpc_dev.config = &chip->tcpc_cfg;
+       chip->tcpc_dev.init = tcpm_init;
+       chip->tcpc_dev.get_vbus = tcpm_get_vbus;
+       chip->tcpc_dev.get_current_limit = tcpm_get_current_limit;
+       chip->tcpc_dev.set_cc = tcpm_set_cc;
+       chip->tcpc_dev.get_cc = tcpm_get_cc;
+       chip->tcpc_dev.set_polarity = tcpm_set_polarity;
+       chip->tcpc_dev.set_vconn = tcpm_set_vconn;
+       chip->tcpc_dev.set_vbus = tcpm_set_vbus;
+       chip->tcpc_dev.set_current_limit = tcpm_set_current_limit;
+       chip->tcpc_dev.set_pd_rx = tcpm_set_pd_rx;
+       chip->tcpc_dev.set_roles = tcpm_set_roles;
+       chip->tcpc_dev.start_drp_toggling = tcpm_start_drp_toggling;
+       chip->tcpc_dev.pd_transmit = tcpm_pd_transmit;
+       chip->tcpc_dev.mux = NULL;
+}
+
+static int rt1711h_get_alert_status(struct rt1711h_chip *chip,
+       uint32_t *alert)
+{
+       int ret = 0;
+       uint16_t data = 0;
+       uint8_t rt_int = 0;
+
+       ret = rt1711h_reg_read_word(chip, RT1711H_REG_ALERT, &data);
+       if (ret < 0)
+               return ret;
+       *alert = data;
+
+       ret = rt1711h_reg_read(chip, RT1711H_REG_RT_INT, &rt_int);
+       if (ret < 0)
+               return ret;
+       *alert |= rt_int << 16;
+
+       return 0;
+}
+
+static int rt1711h_get_fault_status(struct rt1711h_chip *chip, uint8_t *status)
+{
+       return rt1711h_reg_read(chip, RT1711H_REG_FAULT_STATUS, status);
+}
+
+static inline int rt1711h_fault_status_vconn_ov(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+       uint8_t data = 0;
+
+       ret = rt1711h_reg_read(chip, RT1711H_REG_BMC_CTRL, &data);
+       if (ret < 0)
+               return ret;
+
+       data &= ~RT1711H_REG_DISCHARGE_EN;
+       return rt1711h_reg_write(chip, RT1711H_REG_BMC_CTRL, data);
+}
+
+static int rt1711h_fault_status_clear(struct rt1711h_chip *chip, uint8_t 
status)
+{
+       if (status & RT1711H_REG_FAULT_STATUS_VCONN_OV)
+               rt1711h_fault_status_vconn_ov(chip);
+
+       return rt1711h_reg_write(chip, RT1711H_REG_FAULT_STATUS, status);
+}
+
+/* Alert handlers */
+
+static int rt1711h_alert_cc_changed(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+
+       ret = __tcpm_get_cc(chip);
+       if (ret < 0)
+               return ret;
+
+       if (chip->drp_toggling) {
+               rt1711h_log(chip, "%s DRP toggling\n", __func__);
+               if (chip->did < RT1711H_DID_D && chip->lpm && !chip->cable_only)
+                       rt1711h_enter_low_power_mode(chip);
+               return 0;
+       }
+       if (chip->did < RT1711H_DID_D)
+               rt1711h_disable_low_power_mode(chip);
+
+       tcpm_cc_change(chip->tcpm_port);
+       return 0;
+}
+
+static int rt1711h_alert_power_status_changed(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+       bool vbus_pres = false;
+       uint8_t data = 0;
+
+       ret = rt1711h_reg_read(chip, RT1711H_REG_POWER_STATUS, &data);
+       if (ret < 0)
+               goto out;
+
+       vbus_pres = (data & RT1711H_REG_POWER_STATUS_VBUS_PRES) ? true : false;
+       if (vbus_pres != chip->vbus_present) {
+               chip->vbus_present = vbus_pres;
+               rt1711h_log(chip, "%s vbus = %d\n", __func__, vbus_pres);
+               tcpm_vbus_change(chip->tcpm_port);
+       }
+
+out:
+       return ret;
+}
+
+static int rt1711h_alert_recv_msg(struct rt1711h_chip *chip)
+{
+       int ret = 0, len = 0;
+       uint8_t buf[2];
+       struct pd_message msg;
+       const uint32_t alert_rx =
+               RT1711H_REG_ALERT_RX_STATUS | RT1711H_REG_ALERT_RX_BUF_OVF;
+
+       rt1711h_log(chip, "%s\n", __func__);
+       ret = rt1711h_reg_block_read(chip, RT1711H_REG_RX_HDR, 2, buf);
+       if (ret < 0)
+               return ret;
+
+       memcpy(&(msg.header), buf, 2);
+
+       len = pd_header_cnt_le(msg.header) * 4;
+       if (len > PD_MAX_PAYLOAD * 4) {
+               rt1711h_log(chip, "%s PD message too long %d\n", __func__, len);
+               return -EINVAL;
+       }
+       if (len > 0)
+               ret = rt1711h_reg_block_read(chip, RT1711H_REG_RX_DATA, len,
+                       (uint8_t *)msg.payload);
+
+       /* Read complete, clear RX status alert bit */
+       rt1711h_alert_status_clear(chip, alert_rx);
+
+       tcpm_pd_receive(chip->tcpm_port, &msg);
+       return ret;
+}
+
+static int rt1711h_alert_recv_hard_reset(struct rt1711h_chip *chip)
+{
+       tcpm_pd_hard_reset(chip->tcpm_port);
+       return 0;
+}
+
+static int rt1711h_alert_tx_failed(struct rt1711h_chip *chip)
+{
+       tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_FAILED);
+       return 0;
+}
+
+static int rt1711h_alert_tx_discard(struct rt1711h_chip *chip)
+{
+       tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_DISCARDED);
+       return 0;
+}
+
+static int rt1711h_alert_tx_success(struct rt1711h_chip *chip)
+{
+       tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS);
+       return 0;
+}
+
+static int rt1711h_alert_fault(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+       uint8_t status = 0;
+
+       ret = rt1711h_get_fault_status(chip, &status);
+       if (ret < 0)
+               return ret;
+
+       rt1711h_log(chip, "%s 0x%02X\n", __func__, status);
+       rt1711h_fault_status_clear(chip, status);
+       return 0;
+}
+
+static int rt1711h_alert_rx_overflow(struct rt1711h_chip *chip)
+{
+       int ret = 0;
+       uint32_t alert_status = 0;
+
+       rt1711h_log(chip, "%s\n", __func__);
+
+       ret = rt1711h_get_alert_status(chip, &alert_status);
+       if (ret < 0)
+               return ret;
+
+       if (alert_status & RT1711H_REG_ALERT_RX_STATUS)
+               return rt1711h_alert_recv_msg(chip);
+
+       return 0;
+}
+
+static int rt1711h_alert_wakeup(struct rt1711h_chip *chip)
+{
+       rt1711h_log(chip, "%s\n", __func__);
+       if (chip->drp_toggling)
+               rt1711h_enable_wakeup_timer(chip, true);
+       return 0;
+}
+
+static int rt1711h_alert_ra_detach(struct rt1711h_chip *chip)
+{
+       rt1711h_log(chip, "%s\n", __func__);
+       if (chip->drp_toggling)
+               rt1711h_enter_lpm_again(chip);
+
+       return 0;
+}
+
+struct rt1711h_alert_handler {
+       uint32_t bit_mask;
+       int (*handler)(struct rt1711h_chip *chip);
+};
+
+#define RT1711H_DECL_ALERT_HANDLER(xbit, xhandler) { \
+       .bit_mask = 1 << xbit, \
+       .handler = xhandler, \
+}
+
+static const struct rt1711h_alert_handler rt1711h_alert_handlers[] = {
+       RT1711H_DECL_ALERT_HANDLER(4, rt1711h_alert_tx_failed),
+       RT1711H_DECL_ALERT_HANDLER(5, rt1711h_alert_tx_discard),
+       RT1711H_DECL_ALERT_HANDLER(6, rt1711h_alert_tx_success),
+       RT1711H_DECL_ALERT_HANDLER(2, rt1711h_alert_recv_msg),
+       RT1711H_DECL_ALERT_HANDLER(7, NULL),
+       RT1711H_DECL_ALERT_HANDLER(8, NULL),
+       RT1711H_DECL_ALERT_HANDLER(3, rt1711h_alert_recv_hard_reset),
+       RT1711H_DECL_ALERT_HANDLER(10, rt1711h_alert_rx_overflow),
+       RT1711H_DECL_ALERT_HANDLER(16, rt1711h_alert_wakeup),
+       RT1711H_DECL_ALERT_HANDLER(21, rt1711h_alert_ra_detach),
+       RT1711H_DECL_ALERT_HANDLER(9, rt1711h_alert_fault),
+       RT1711H_DECL_ALERT_HANDLER(0, rt1711h_alert_cc_changed),
+       RT1711H_DECL_ALERT_HANDLER(1, rt1711h_alert_power_status_changed),
+};
+
+static int __rt1711h_irq_handler(struct rt1711h_chip *chip)
+{
+       int i = 0, ret = 0;
+       uint32_t alert_status = 0;
+
+       ret = rt1711h_get_alert_status(chip, &alert_status);
+       if (ret < 0) {
+               rt1711h_log(chip, "%s get alert status fail(%d)\n",
+                       __func__, ret);
+               goto out;
+       }
+
+       rt1711h_alert_status_clear(chip,
+               alert_status & (~RT1711H_REG_ALERT_RX_MASK));
+
+       if (alert_status)
+               rt1711h_log(chip, "%s 0x%04X\n", __func__, alert_status);
+
+       if (alert_status & RT1711H_REG_ALERT_EXT_VBUS_80)
+               alert_status |= RT1711H_REG_ALERT_POWER_STATUS;
+
+       for (i = 0; i < ARRAY_SIZE(rt1711h_alert_handlers); i++) {
+               if (rt1711h_alert_handlers[i].bit_mask & alert_status) {
+                       if (rt1711h_alert_handlers[i].handler != 0)
+                               rt1711h_alert_handlers[i].handler(chip);
+               }
+       }
+
+out:
+       return ret;
+}
+
+static inline void rt1711h_poll_ctrl(struct rt1711h_chip *chip)
+{
+       cancel_delayed_work_sync(&chip->poll_work);
+
+       if (atomic_read(&chip->poll_count) == 0) {
+               atomic_inc(&chip->poll_count);
+               cpu_idle_poll_ctrl(true);
+       }
+
+       schedule_delayed_work(&chip->poll_work, msecs_to_jiffies(40));
+}
+
+static void rt1711h_irq_work_handler(struct kthread_work *work)
+{
+       struct rt1711h_chip *chip =
+               container_of(work, struct rt1711h_chip, irq_work);
+       int ret = 0, gpio_val = 0;
+
+       rt1711h_poll_ctrl(chip);
+       mutex_lock(&chip->wakeup_lock);
+       mutex_lock(&chip->lock);
+       do {
+               ret = __rt1711h_irq_handler(chip);
+               if (ret < 0)
+                       break;
+               gpio_val = gpio_get_value(chip->irq_gpio);
+       } while (gpio_val == 0);
+       mutex_unlock(&chip->lock);
+       mutex_unlock(&chip->wakeup_lock);
+}
+
+static void rt1711h_poll_work(struct work_struct *work)
+{
+       struct rt1711h_chip *chip = container_of(work, struct rt1711h_chip,
+               poll_work.work);
+
+       if  (atomic_dec_and_test(&chip->poll_count))
+               cpu_idle_poll_ctrl(false);
+}
+#define RT1711H_IRQ_WAKE_TIME  (500) /* ms */
+
+static irqreturn_t rt1711h_intr_handler(int irq, void *data)
+{
+       struct rt1711h_chip *chip = data;
+
+       pm_wakeup_event(chip->dev, RT1711H_IRQ_WAKE_TIME);
+       kthread_queue_work(&chip->irq_worker, &chip->irq_work);
+
+       return IRQ_HANDLED;
+}
+
+static int rt1711h_init_alert(struct rt1711h_chip *chip)
+{
+       int ret = 0, len = 0;
+       char *name = NULL;
+       struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
+
+       rt1711h_reg_write_word(chip, RT1711H_REG_ALERT_MASK, 0);
+       rt1711h_reg_write_word(chip, RT1711H_REG_ALERT, 0xffff);
+
+       len = strlen(chip->name);
+       name = devm_kzalloc(chip->dev, len + 5, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       snprintf(name, len, "%s-IRQ", chip->name);
+
+       dev_info(chip->dev, "%s name = %s, gpio = %d\n", __func__, chip->name,
+               chip->irq_gpio);
+
+       ret = devm_gpio_request_one(chip->dev, chip->irq_gpio,
+               GPIOF_IN, name);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s request gpio fail(%d)\n",
+                       __func__, ret);
+               goto err_init_alert;
+       }
+
+       chip->irq = gpio_to_irq(chip->irq_gpio);
+       if (chip->irq <= 0) {
+               dev_err(chip->dev, "%s gpio2irq fail(%d)\n",
+                       __func__, chip->irq);
+               ret = -EINVAL;
+               goto err_init_alert;
+       }
+       dev_info(chip->dev, "%s irq = %d\n", __func__, chip->irq);
+
+       kthread_init_worker(&chip->irq_worker);
+       chip->irq_worker_task = kthread_run(kthread_worker_fn,
+               &chip->irq_worker, chip->name);
+       if (IS_ERR(chip->irq_worker_task)) {
+               dev_err(chip->dev, "%s could not create tcpc task\n", __func__);
+               goto err_init_alert;
+       }
+
+       sched_setscheduler(chip->irq_worker_task, SCHED_FIFO, &param);
+       kthread_init_work(&chip->irq_work, rt1711h_irq_work_handler);
+
+       ret = devm_request_irq(chip->dev, chip->irq, rt1711h_intr_handler,
+               IRQF_TRIGGER_FALLING | IRQF_NO_THREAD | IRQF_NO_SUSPEND, name,
+               chip);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s request irq%d fail(%d)\n",
+                       __func__, chip->irq, ret);
+               goto err_init_alert;
+       }
+       enable_irq_wake(chip->irq);
+       return 0;
+
+err_init_alert:
+       devm_kfree(chip->dev, name);
+       return ret;
+}
+
+static int rt1711h_check_revision(struct i2c_client *i2c)
+{
+       int ret = 0;
+
+       ret = i2c_smbus_read_word_data(i2c, 0x00);
+       if (ret < 0)
+               return ret;
+       if (ret != 0x29cf) {
+               dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret);
+               return -ENODEV;
+       }
+       ret = i2c_smbus_read_word_data(i2c, 0x02);
+       if (ret < 0)
+               return ret;
+       if (ret != 0x1711) {
+               dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret);
+               return -ENODEV;
+       }
+       ret = i2c_smbus_read_word_data(i2c, 0x04);
+       if (ret < 0)
+               return ret;
+       /* return did */
+       return ret;
+}
+
+static int rt1711h_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       int ret = 0;
+       uint16_t did = 0;
+       struct rt1711h_chip *chip = NULL;
+       struct power_supply_config cfg = {};
+
+       pr_info("%s %s\n", __func__, RT1711H_DRV_VERSION);
+
+       if (!i2c_check_functionality(client->adapter,
+               I2C_FUNC_SMBUS_I2C_BLOCK)) {
+               dev_err(&client->dev,
+                       "I2C/SMBusyy block functionality not supported!\n");
+               return -ENODEV;
+       }
+       ret = rt1711h_check_revision(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "check vid/pid/did fail\n");
+               return ret;
+       }
+       did = (uint16_t)ret;
+       dev_info(&client->dev, "did = 0x%04x\n", did);
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+       chip->i2c = client;
+       chip->dev = &client->dev;
+       chip->did = did;
+       mutex_init(&chip->lock);
+       mutex_init(&chip->wakeup_lock);
+       INIT_DELAYED_WORK(&chip->poll_work, rt1711h_poll_work);
+       INIT_DELAYED_WORK(&chip->wakeup_work, rt1711h_wakeup_work);
+       alarm_init(&chip->wakeup_timer, ALARM_REALTIME,
+               rt1711h_alarm_wakeup_handler);
+       i2c_set_clientdata(client, chip);
+
+       ret = rt1711h_parse_dt(chip);
+       if (ret < 0)
+               goto out_parse_dt;
+
+       cfg.drv_data = chip;
+       chip->psy = devm_power_supply_register(chip->dev, &rt1711h_psy_desc,
+               &cfg);
+       if (IS_ERR(chip->psy)) {
+               ret = PTR_ERR(chip->psy);
+               dev_err(chip->dev, "%s register psy fail(%d)\n", __func__, ret);
+               goto out_psy_reg;
+       }
+
+       chip->vbus = devm_regulator_get(chip->dev, "vbus");
+       if (IS_ERR(chip->vbus)) {
+               ret = PTR_ERR(chip->vbus);
+               goto out_reg_get;
+       }
+
+       ret = rt1711h_debugfs_init(chip);
+       if (ret < 0)
+               goto out_dbgfs_init;
+
+       ret = rt1711h_software_reset(chip);
+       if (ret < 0)
+               goto out_sw_reset;
+
+       ret = rt1711h_init_alert(chip);
+       if (ret < 0)
+               goto out_init_alert;
+
+       rt1711h_init_tcpc_dev(chip);
+
+       chip->tcpm_port = tcpm_register_port(chip->dev,
+               &chip->tcpc_dev);
+       if (IS_ERR(chip->tcpm_port)) {
+               ret = PTR_ERR(chip->tcpm_port);
+               dev_err(chip->dev, "%s register tcpm port fail(%d)",
+                       __func__, ret);
+               goto out_tcpm_reg;
+       }
+
+       pm_runtime_set_active(&client->dev);
+       pm_runtime_enable(&client->dev);
+       dev_info(chip->dev, "%s: successfully\n", __func__);
+       return 0;
+
+out_tcpm_reg:
+out_init_alert:
+out_sw_reset:
+       rt1711h_debugfs_exit(chip);
+out_dbgfs_init:
+out_reg_get:
+out_psy_reg:
+out_parse_dt:
+       mutex_destroy(&chip->lock);
+       devm_kfree(&client->dev, chip);
+       return 0;
+}
+
+static int rt1711h_i2c_remove(struct i2c_client *client)
+{
+       struct rt1711h_chip *chip = i2c_get_clientdata(client);
+
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       if (chip) {
+               rt1711h_debugfs_exit(chip);
+               mutex_destroy(&chip->lock);
+       }
+       dev_info(chip->dev, "%s: successfully\n", __func__);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rt1711h_i2c_pm_suspend(struct device *dev)
+{
+       struct rt1711h_chip *chip = dev_get_drvdata(dev);
+
+       if (chip) {
+               if (atomic_read(&chip->i2c_busy))
+                       return -EBUSY;
+               atomic_set(&chip->pm_suspend, 1);
+       }
+       return 0;
+}
+
+static int rt1711h_i2c_pm_resume(struct device *dev)
+{
+       struct rt1711h_chip *chip = dev_get_drvdata(dev);
+
+       if (chip)
+               atomic_set(&chip->pm_suspend, 0);
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops rt1711h_i2c_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(rt1711h_i2c_pm_suspend, rt1711h_i2c_pm_resume)
+};
+
+static const struct of_device_id rt1711h_of_device_id[] = {
+       { .compatible = "richtek,typec_rt1711h",},
+       { },
+};
+MODULE_DEVICE_TABLE(of, rt1711h_of_device_id);
+
+static const struct i2c_device_id rt1711h_i2c_device_id[] = {
+       { "typec_rt1711h", 0},
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, rt1711h_i2c_device_id);
+
+static struct i2c_driver rt1711h_i2c_driver = {
+       .driver = {
+               .name = "typec_rt1711h",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(rt1711h_of_device_id),
+               .pm = &rt1711h_i2c_pm_ops,
+       },
+       .probe = rt1711h_i2c_probe,
+       .remove = rt1711h_i2c_remove,
+       .id_table = rt1711h_i2c_device_id,
+};
+module_i2c_driver(rt1711h_i2c_driver);
+
+MODULE_AUTHOR("cy_huang <cy_hu...@richtek.com>");
+MODULE_DESCRIPTION("rt1711h typec driver");
+MODULE_VERSION(RT1711H_DRV_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/rt1711h/rt1711h.h 
b/drivers/usb/typec/rt1711h/rt1711h.h
new file mode 100644
index 0000000..8b67464
--- /dev/null
+++ b/drivers/usb/typec/rt1711h/rt1711h.h
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017 Richtek Technologh Corp.
+ *
+ * Richtek RT1711H Type-C Chip Driver
+ */
+
+#ifndef __LINUX_RT1711H_H
+#define __LINUX_RT1711H_H
+
+/* Device ID */
+#define RT1711H_DID_A  0x2170
+#define RT1711H_DID_B  0x2171
+#define RT1711H_DID_C  0x2172
+#define RT1711H_DID_D  0x2173
+
+/* Registers */
+#define RT1711H_REG_VID                                (0x00)
+#define RT1711H_REG_PID                                (0x02)
+#define RT1711H_REG_DID                                (0x04)
+#define RT1711H_REG_TYPEC_REV                  (0x06)
+#define RT1711H_REG_PD_REV                     (0x08)
+#define RT1711H_REG_PDIF_REV                   (0x0A)
+#define RT1711H_REG_ALERT                      (0x10)
+#define RT1711H_REG_ALERT_MASK                 (0x12)
+#define RT1711H_REG_POWER_STATUS_MASK          (0x14)
+#define RT1711H_REG_FAULT_STATUS_MASK          (0x15)
+#define RT1711H_REG_TCPC_CTRL                  (0x19)
+#define RT1711H_REG_ROLE_CTRL                  (0x1A)
+#define RT1711H_REG_FAULT_CTRL                 (0x1B)
+#define RT1711H_REG_POWER_CTRL                 (0x1C)
+#define RT1711H_REG_CC_STATUS                  (0x1D)
+#define RT1711H_REG_POWER_STATUS               (0x1E)
+#define RT1711H_REG_FAULT_STATUS               (0x1F)
+#define RT1711H_REG_COMMAND                    (0x23)
+#define RT1711H_REG_MSG_HDR_INFO               (0x2e)
+#define RT1711H_REG_RX_DETECT                  (0x2f)
+#define RT1711H_REG_RX_BYTE_CNT                        (0x30)
+#define RT1711H_REG_RX_BUF_FRAME_TYPE          (0x31)
+#define RT1711H_REG_RX_HDR                     (0x32)
+#define RT1711H_REG_RX_DATA                    (0x34)
+#define RT1711H_REG_TRANSMIT                   (0x50)
+#define RT1711H_REG_TX_BYTE_CNT                        (0x51)
+#define RT1711H_REG_TX_HDR                     (0x52)
+#define RT1711H_REG_TX_DATA                    (0x54)
+
+#define RT1711H_REG_CLK_CTRL2                  (0x87)
+#define RT1711H_REG_CLK_CTRL3                  (0x88)
+#define RT1711H_REG_BMC_CTRL                   (0x90)
+#define RT1711H_REG_BMCIO_RXDZSEL              (0x93)
+#define RT1711H_REG_VCONN_CLIMITEN             (0x95)
+#define RT1711H_REG_RT_STATUS                  (0x97)
+#define RT1711H_REG_RT_INT                     (0x98)
+#define RT1711H_REG_RT_MASK                    (0x99)
+#define RT1711H_REG_IDLE_CTRL                  (0x9B)
+#define RT1711H_REG_INTRST_CTRL                        (0x9C)
+#define RT1711H_REG_WATCHDOG_CTRL              (0x9D)
+#define RT1711H_REG_I2CRST_CTRL                        (0X9E)
+#define RT1711H_REG_SWRESET                    (0xA0)
+#define RT1711H_REG_TTCPC_FILTER               (0xA1)
+#define RT1711H_REG_DRP_TOGGLE_CYCLE           (0xA2)
+#define RT1711H_REG_DRP_DUTY_CTRL              (0xA3)
+#define RT1711H_REG_BMCIO_RXDZEN               (0xAF)
+
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+/*
+ * RT1711H_REG_ALERT                           (0x10)
+ * RT1711H_REG_ALERT_MASK                      (0x12)
+ */
+#define RT1711H_REG_VBUS_SINK_DISCONNECT       BIT(11)
+#define RT1711H_REG_ALERT_RX_BUF_OVF           BIT(10)
+#define RT1711H_REG_ALERT_FAULT                        BIT(9)
+#define RT1711H_REG_ALERT_LO_VOLT              BIT(8)
+#define RT1711H_REG_ALERT_HI_VOLT              BIT(7)
+#define RT1711H_REG_ALERT_TX_SUCCESS           BIT(6)
+#define RT1711H_REG_ALERT_TX_DISCARDED         BIT(5)
+#define RT1711H_REG_ALERT_TX_FAILED            BIT(4)
+#define RT1711H_REG_ALERT_RX_HARD_RST          BIT(3)
+#define RT1711H_REG_ALERT_RX_STATUS            BIT(2)
+#define RT1711H_REG_ALERT_POWER_STATUS         BIT(1)
+#define RT1711H_REG_ALERT_CC_STATUS            BIT(0)
+#define RT1711H_REG_ALERT_RX_MASK \
+       (RT1711H_REG_ALERT_RX_STATUS | RT1711H_REG_ALERT_RX_BUF_OVF)
+
+/*
+ * RT1711H_REG_POWER_STATUS_MASK               (0x14)
+ * RT1711H_REG_POWER_STATUS                    (0x1E)
+ */
+#define RT1711H_REG_POWER_STATUS_TCPC_INITIAL  BIT(6)
+#define RT1711H_REG_POWER_STATUS_SRC_HV                BIT(5)
+#define RT1711H_REG_POWER_STATUS_SRC_VBUS      BIT(4)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRES_DET BIT(3)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRES     BIT(2)
+#define RT1711H_REG_POWER_STATUS_VCONN_PRES    BIT(1)
+#define RT1711H_REG_POWER_STATUS_SINK_VBUS     BIT(0)
+
+/*
+ * RT1711H_REG_FAULT_STATUS_MASK               (0x15)
+ * RT1711H_REG_FAULT_STATUS                    (0x1F)
+ */
+#define RT1711H_REG_FAULT_STATUS_VCONN_OV              BIT(7)
+#define RT1711H_REG_FAULT_STATUS_FORCE_OFF_VBUS                BIT(6)
+#define RT1711H_REG_FAULT_STATUS_AUTO_DISC_FAIL                BIT(5)
+#define RT1711H_REG_FAULT_STATUS_FORCE_DISC_FAIL       BIT(4)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OC               BIT(3)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OV               BIT(2)
+#define RT1711H_REG_FAULT_STATUS_VCONN_OC              BIT(1)
+#define RT1711H_REG_FAULT_STATUS_I2C_ERROR             BIT(0)
+
+/*
+ * RT1711H_REG_ROLE_CTRL                       (0x1A)
+ */
+#define RT1711H_REG_ROLE_CTRL_DRP              BIT(6)
+#define RT1711H_REG_ROLE_CTRL_RES_SET(drp, rp, cc1, cc2) \
+       ((drp) << 6 | (rp) << 4 | (cc2) << 2 | (cc1))
+#define RT1711H_REG_ROLE_CTRL_CC2(reg)                 (((reg) & 0x0C) >> 2)
+#define RT1711H_REG_ROLE_CTRL_CC1(reg)                 ((reg) & 0x03)
+#define RT1711H_TYPEC_CC_PULL_GET_RP_LVL(pull)         ((pull & 0x18) >> 3)
+#define RT1711H_TYPEC_CC_PULL_GET_RES(pull)            (pull & 0x07)
+
+enum typec_cc_pull {
+       TYPEC_CC_PULL_RA = 0,
+       TYPEC_CC_PULL_RP,
+       TYPEC_CC_PULL_RD,
+       TYPEC_CC_PULL_OPEN,
+       TYPEC_CC_PULL_DRP,      /* from rd */
+
+       TYPEC_CC_PULL_RP_DEF = 1,       /* 0x00 + 1 */
+       TYPEC_CC_PULL_RP_1_5 = 9,       /* 0x08 + 1 */
+       TYPEC_CC_PULL_RP_3_0 = 17,      /* 0x10 + 1 */
+
+       TYPEC_CC_PULL_DRP_DEF = 4,      /* 0x00 + 4 */
+       TYPEC_CC_PULL_DRP_1_5 = 12,     /* 0x08 + 4 */
+       TYPEC_CC_PULL_DRP_3_0 = 20,     /* 0x10 + 4 */
+};
+
+/*
+ * RT1711H_REG_TCPC_CTRL                       (0x19)
+ */
+#define RT1711H_REG_TCPC_CTRL_BIST_TEST_MODE   BIT(1)
+#define RT1711H_REG_TCPC_CTRL_PLUG_ORIENT      BIT(0)
+
+/*
+ * RT1711H_REG_FAULT_CTRL                      (0x1B)
+ */
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OV    BIT(7)
+#define RT1711H_REG_FAULT_CTRL_DIS_SNK_VBUS_OC BIT(2)
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OC    BIT(0)
+
+/*
+ * RT1711H_REG_POWER_CTRL                      (0x1C)
+ */
+#define RT1711H_REG_POWER_CTRL_VCONN           BIT(0)
+
+/*
+ * RT1711H_REG_CC_STATUS                       (0x1D)
+ */
+#define RT1711H_REG_CC_STATUS_DRP_TOGGLING     BIT(5)
+#define RT1711H_REG_CC_STATUS_DRP_RESULT(reg)  (((reg) & 0x10) >> 4)
+#define RT1711H_REG_CC_STATUS_CC2(reg)         (((reg) & 0x0C) >> 2)
+#define RT1711H_REG_CC_STATUS_CC1(reg)         ((reg) & 0x03)
+#define RT1711H_REG_CC_STATUS_RD2ENUM(cc)      ((cc) + 2)
+
+/*
+ * RT1711H_REG_COMMAND                         (0x23)
+ */
+enum rt1711h_command {
+       RT1711H_CMD_WAKE_I2C = 0x11,
+       RT1711H_CMD_DISABLE_VBUS_DETECT = 0x22,
+       RT1711H_CMD_ENABLE_VBUS_DETECT = 0x33,
+       RT1711H_CMD_DISABLE_SINK_VBUS = 0x44,
+       RT1711H_CMD_ENABLE_SINK_VBUS = 0x55,
+       RT1711H_CMD_DISABLE_SOURCE_VBUS = 0x66,
+       RT1711H_CMD_ENABLE_SOURCE_VBUS = 0x77,
+       RT1711H_CMD_SOURCE_VBUS_HV = 0x88,
+       RT1711H_CMD_LOOK_CONNECTION = 0x99,
+       RT1711H_CMD_RX_ONE_MODE = 0xAA,
+       RT1711H_CMD_I2C_IDLE = 0xFF,
+};
+
+
+/*
+ * RT1711H_REG_MSG_HDR_INFO                    (0x2E)
+ */
+#define RT1711H_REG_MSG_HDR_INFO_SET(drole, prole) \
+       ((drole) << 3 | (PD_REV20 << 1) | (prole))
+#define RT1711H_REG_MSG_HDR_INFO_DROLE(reg)    (((reg) & 0x08) >> 3)
+#define RT1711H_REG_MSG_HDR_INFO_PROLE(reg)    ((reg) & 0x01)
+
+/*
+ * RT1711H_REG_TRANSMIT                                (0x50)
+ */
+#define RT1711H_REG_TRANSMIT_SET(retry, type)  ((retry) << 4 | (type))
+
+
+/*
+ * RT1711H_REG_CLK_CTRL2                       (0x87)
+ */
+#define RT1711H_REG_CLK_DIV_600K_EN            BIT(7)
+#define RT1711H_REG_CLK_BCLK2_EN               BIT(6)
+#define RT1711H_REG_CLK_BCLK2_TG_EN            BIT(5)
+#define RT1711H_REG_CLK_DIV_300K_EN            BIT(3)
+#define RT1711H_REG_CLK_CK_300K_EN             BIT(2)
+#define RT1711H_REG_CLK_BCLK_EN                        BIT(1)
+#define RT1711H_REG_CLK_BCLK_TH_EN             BIT(0)
+
+/*
+ * RT1711H_REG_CLK_CTRL3                       (0x88)
+ */
+#define RT1711H_REG_CLK_OSCMUX_RG_EN           BIT(7)
+#define RT1711H_REG_CLK_CK_24M_EN              BIT(6)
+#define RT1711H_REG_CLK_OSC_RG_EN              BIT(5)
+#define RT1711H_REG_CLK_DIV_2P4M_EN            BIT(4)
+#define RT1711H_REG_CLK_CK_2P4M_EN             BIT(3)
+#define RT1711H_REG_CLK_PCLK_EN                        BIT(2)
+#define RT1711H_REG_CLK_PCLK_RG_EN             BIT(1)
+#define RT1711H_REG_CLK_PCLK_TG_EN             BIT(0)
+
+/*
+ * RT1711H_REG_BMC_CTRL                                (0x90)
+ */
+#define RT1711H_REG_IDLE_EN                    BIT(6)
+#define RT1711H_REG_DISCHARGE_EN               BIT(5)
+#define RT1711H_REG_BMCIO_LPRPRD               BIT(4)
+#define RT1711H_REG_BMCIO_LPEN                 BIT(3)
+#define RT1711H_REG_BMCIO_BG_EN                        BIT(2)
+#define RT1711H_REG_VBUS_DET_EN                        BIT(1)
+#define RT1711H_REG_BMCIO_OSC_EN               BIT(0)
+
+/*
+ * RT1711H_REG_RT_STATUS                       (0x97)
+ */
+#define RT1711H_REG_RA_DETACH                  BIT(5)
+#define RT1711H_REG_VBUS_80                    BIT(1)
+
+/*
+ * RT1711H_REG_RT_INT                          (0x98)
+ */
+#define RT1711H_REG_INT_RA_DETACH              BIT(5)
+#define RT1711H_REG_INT_WATCHDOG               BIT(2)
+#define RT1711H_REG_INT_VBUS_80                        BIT(1)
+#define RT1711H_REG_INT_WAKEUP                 BIT(0)
+
+/*
+ * RT1711H_REG_RT_MASK                         (0x99)
+ */
+#define RT1711H_REG_M_RA_DETACH                        BIT(5)
+#define RT1711H_REG_M_WATCHDOG                 BIT(2)
+#define RT1711H_REG_M_VBUS_80                  BIT(1)
+#define RT1711H_REG_M_WAKEUP                   BIT(0)
+#define RT1711H_REG_ALERT_EXT_RA_DETACH                (1 << (16 + 5))
+#define RT1711H_REG_ALERT_EXT_VBUS_80          (1 << (16 + 1))
+
+/*
+ * RT1711H_REG_IDLE_CTRL                       (0x9B)
+ */
+#define RT1711H_REG_CK_300K_SEL                        BIT(7)
+#define RT1711H_REG_SHIPPING_OFF               BIT(5)
+#define RT1711H_REG_AUTOIDLE_EN                        BIT(3)
+
+/* timeout = (tout*2+1) * 6.4ms */
+#define RT1711H_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \
+       (((ck300) << 7) | ((ship_dis) << 5) | \
+        ((auto_idle) << 3) | ((tout) & 0x07))
+
+/*
+ * RT1711H_REG_INTRST_CTRL                     (0x9C)
+ */
+#define RT1711H_REG_INTRST_EN                  BIT(7)
+
+/* timeout = (tout+1) * 0.2sec */
+#define RT1711H_REG_INTRST_SET(en, tout)       (((en) << 7) | ((tout) & 0x03))
+
+/*
+ * RT1711H_REG_WATCHDOG_CTRL                   (0x9D)
+ */
+#define RT1711H_REG_WATCHDOG_EN                        BIT(7)
+
+/* timeout = (tout+1) * 0.4sec */
+#define RT1711H_REG_WATCHDOG_CTRL_SET(en, tout)        (((en) << 7) | ((tout) 
& 0x07))
+
+/*
+ * RT1711H_REG_I2CRST_CTRL                     (0x9E)
+ */
+#define RT1711H_REG_I2CRST_EN                  BIT(7)
+
+/* timeout = (tout+1) * 12.5ms */
+#define RT1711H_REG_I2CRST_SET(en, tout)       ((en << 7) | (tout & 0x0F))
+
+/*
+ * RT1711H_REG_DRP_DUTY_CTRL                   (0xA3)
+ */
+#define RT1711H_LOW_RP_DUTY                    (100)   /* 10% */
+#define RT1711H_NORMAL_RP_DUTY                 (330)   /* 33% */
+
+#endif /* __LINUX_RT1711H_H */
-- 
1.9.1

Reply via email to