[PATCH] USB TYPEC: RT1711H Type-C Chip Driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: ShuFanLee <shufan_lee@xxxxxxxxxxx>

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_lee@xxxxxxxxxxx>
---
 .../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_huang@xxxxxxxxxxx>");
+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

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux