All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-10  6:59 ` ShuFanLee
  0 siblings, 0 replies; 51+ messages in thread
From: ShuFanLee @ 2018-01-10  6:59 UTC (permalink / raw)
  To: heikki.krogerus; +Cc: cy_huang, shufan_lee, linux-kernel, linux-usb

From: ShuFanLee <shufan_lee@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_lee@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_huang@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

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-10  6:59 ` ShuFanLee
  0 siblings, 0 replies; 51+ messages in thread
From: ShuFanLee @ 2018-01-10  6:59 UTC (permalink / raw)
  To: heikki.krogerus; +Cc: cy_huang, shufan_lee, linux-kernel, linux-usb

From: ShuFanLee <shufan_lee@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_lee@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_huang@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 */

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17  9:30   ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-17  9:30 UTC (permalink / raw)
  To: 'ShuFanLee', heikki.krogerus
  Cc: cy_huang(黃啟原), linux-kernel, linux-usb

Dear Heikki,

  Sorry for bothering.

  Just want to check is there anything we need to modify?

  Thank you!

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: ShuFanLee [mailto:leechu729@gmail.com]
Sent: Wednesday, January 10, 2018 2:59 PM
To: heikki.krogerus@linux.intel.com
Cc: cy_huang(黃啟原); shufan_lee(李書帆); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

From: ShuFanLee <shufan_lee@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_lee@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_ENTRIES1024
+#define LOG_BUFFER_ENTRY_SIZE128 /* 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@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_A0x2170
+#define RT1711H_DID_B0x2171
+#define RT1711H_DID_C0x2172
+#define RT1711H_DID_D0x2173
+
+/* 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_DISCONNECTBIT(11)
+#define RT1711H_REG_ALERT_RX_BUF_OVFBIT(10)
+#define RT1711H_REG_ALERT_FAULTBIT(9)
+#define RT1711H_REG_ALERT_LO_VOLTBIT(8)
+#define RT1711H_REG_ALERT_HI_VOLTBIT(7)
+#define RT1711H_REG_ALERT_TX_SUCCESSBIT(6)
+#define RT1711H_REG_ALERT_TX_DISCARDEDBIT(5)
+#define RT1711H_REG_ALERT_TX_FAILEDBIT(4)
+#define RT1711H_REG_ALERT_RX_HARD_RSTBIT(3)
+#define RT1711H_REG_ALERT_RX_STATUSBIT(2)
+#define RT1711H_REG_ALERT_POWER_STATUSBIT(1)
+#define RT1711H_REG_ALERT_CC_STATUSBIT(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_INITIALBIT(6)
+#define RT1711H_REG_POWER_STATUS_SRC_HVBIT(5)
+#define RT1711H_REG_POWER_STATUS_SRC_VBUSBIT(4)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRES_DETBIT(3)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRESBIT(2)
+#define RT1711H_REG_POWER_STATUS_VCONN_PRESBIT(1)
+#define RT1711H_REG_POWER_STATUS_SINK_VBUSBIT(0)
+
+/*
+ * RT1711H_REG_FAULT_STATUS_MASK(0x15)
+ * RT1711H_REG_FAULT_STATUS(0x1F)
+ */
+#define RT1711H_REG_FAULT_STATUS_VCONN_OVBIT(7)
+#define RT1711H_REG_FAULT_STATUS_FORCE_OFF_VBUSBIT(6)
+#define RT1711H_REG_FAULT_STATUS_AUTO_DISC_FAILBIT(5)
+#define RT1711H_REG_FAULT_STATUS_FORCE_DISC_FAILBIT(4)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OCBIT(3)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OVBIT(2)
+#define RT1711H_REG_FAULT_STATUS_VCONN_OCBIT(1)
+#define RT1711H_REG_FAULT_STATUS_I2C_ERRORBIT(0)
+
+/*
+ * RT1711H_REG_ROLE_CTRL(0x1A)
+ */
+#define RT1711H_REG_ROLE_CTRL_DRPBIT(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_MODEBIT(1)
+#define RT1711H_REG_TCPC_CTRL_PLUG_ORIENTBIT(0)
+
+/*
+ * RT1711H_REG_FAULT_CTRL(0x1B)
+ */
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OVBIT(7)
+#define RT1711H_REG_FAULT_CTRL_DIS_SNK_VBUS_OCBIT(2)
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OCBIT(0)
+
+/*
+ * RT1711H_REG_POWER_CTRL(0x1C)
+ */
+#define RT1711H_REG_POWER_CTRL_VCONNBIT(0)
+
+/*
+ * RT1711H_REG_CC_STATUS(0x1D)
+ */
+#define RT1711H_REG_CC_STATUS_DRP_TOGGLINGBIT(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_ENBIT(7)
+#define RT1711H_REG_CLK_BCLK2_ENBIT(6)
+#define RT1711H_REG_CLK_BCLK2_TG_ENBIT(5)
+#define RT1711H_REG_CLK_DIV_300K_ENBIT(3)
+#define RT1711H_REG_CLK_CK_300K_ENBIT(2)
+#define RT1711H_REG_CLK_BCLK_ENBIT(1)
+#define RT1711H_REG_CLK_BCLK_TH_ENBIT(0)
+
+/*
+ * RT1711H_REG_CLK_CTRL3(0x88)
+ */
+#define RT1711H_REG_CLK_OSCMUX_RG_ENBIT(7)
+#define RT1711H_REG_CLK_CK_24M_ENBIT(6)
+#define RT1711H_REG_CLK_OSC_RG_ENBIT(5)
+#define RT1711H_REG_CLK_DIV_2P4M_ENBIT(4)
+#define RT1711H_REG_CLK_CK_2P4M_ENBIT(3)
+#define RT1711H_REG_CLK_PCLK_ENBIT(2)
+#define RT1711H_REG_CLK_PCLK_RG_ENBIT(1)
+#define RT1711H_REG_CLK_PCLK_TG_ENBIT(0)
+
+/*
+ * RT1711H_REG_BMC_CTRL(0x90)
+ */
+#define RT1711H_REG_IDLE_ENBIT(6)
+#define RT1711H_REG_DISCHARGE_ENBIT(5)
+#define RT1711H_REG_BMCIO_LPRPRDBIT(4)
+#define RT1711H_REG_BMCIO_LPENBIT(3)
+#define RT1711H_REG_BMCIO_BG_ENBIT(2)
+#define RT1711H_REG_VBUS_DET_ENBIT(1)
+#define RT1711H_REG_BMCIO_OSC_ENBIT(0)
+
+/*
+ * RT1711H_REG_RT_STATUS(0x97)
+ */
+#define RT1711H_REG_RA_DETACHBIT(5)
+#define RT1711H_REG_VBUS_80BIT(1)
+
+/*
+ * RT1711H_REG_RT_INT(0x98)
+ */
+#define RT1711H_REG_INT_RA_DETACHBIT(5)
+#define RT1711H_REG_INT_WATCHDOGBIT(2)
+#define RT1711H_REG_INT_VBUS_80BIT(1)
+#define RT1711H_REG_INT_WAKEUPBIT(0)
+
+/*
+ * RT1711H_REG_RT_MASK(0x99)
+ */
+#define RT1711H_REG_M_RA_DETACHBIT(5)
+#define RT1711H_REG_M_WATCHDOGBIT(2)
+#define RT1711H_REG_M_VBUS_80BIT(1)
+#define RT1711H_REG_M_WAKEUPBIT(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_SELBIT(7)
+#define RT1711H_REG_SHIPPING_OFFBIT(5)
+#define RT1711H_REG_AUTOIDLE_ENBIT(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_ENBIT(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_ENBIT(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_ENBIT(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

************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17  9:30   ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-17  9:30 UTC (permalink / raw)
  To: 'ShuFanLee', heikki.krogerus
  Cc: cy_huang(黃啟原), linux-kernel, linux-usb

Dear Heikki,

  Sorry for bothering.

  Just want to check is there anything we need to modify?

  Thank you!

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: ShuFanLee [mailto:leechu729@gmail.com]
Sent: Wednesday, January 10, 2018 2:59 PM
To: heikki.krogerus@linux.intel.com
Cc: cy_huang(黃啟原); shufan_lee(李書帆); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

From: ShuFanLee <shufan_lee@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_lee@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

--
1.9.1

************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

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_ENTRIES1024
+#define LOG_BUFFER_ENTRY_SIZE128 /* 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@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_A0x2170
+#define RT1711H_DID_B0x2171
+#define RT1711H_DID_C0x2172
+#define RT1711H_DID_D0x2173
+
+/* 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_DISCONNECTBIT(11)
+#define RT1711H_REG_ALERT_RX_BUF_OVFBIT(10)
+#define RT1711H_REG_ALERT_FAULTBIT(9)
+#define RT1711H_REG_ALERT_LO_VOLTBIT(8)
+#define RT1711H_REG_ALERT_HI_VOLTBIT(7)
+#define RT1711H_REG_ALERT_TX_SUCCESSBIT(6)
+#define RT1711H_REG_ALERT_TX_DISCARDEDBIT(5)
+#define RT1711H_REG_ALERT_TX_FAILEDBIT(4)
+#define RT1711H_REG_ALERT_RX_HARD_RSTBIT(3)
+#define RT1711H_REG_ALERT_RX_STATUSBIT(2)
+#define RT1711H_REG_ALERT_POWER_STATUSBIT(1)
+#define RT1711H_REG_ALERT_CC_STATUSBIT(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_INITIALBIT(6)
+#define RT1711H_REG_POWER_STATUS_SRC_HVBIT(5)
+#define RT1711H_REG_POWER_STATUS_SRC_VBUSBIT(4)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRES_DETBIT(3)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRESBIT(2)
+#define RT1711H_REG_POWER_STATUS_VCONN_PRESBIT(1)
+#define RT1711H_REG_POWER_STATUS_SINK_VBUSBIT(0)
+
+/*
+ * RT1711H_REG_FAULT_STATUS_MASK(0x15)
+ * RT1711H_REG_FAULT_STATUS(0x1F)
+ */
+#define RT1711H_REG_FAULT_STATUS_VCONN_OVBIT(7)
+#define RT1711H_REG_FAULT_STATUS_FORCE_OFF_VBUSBIT(6)
+#define RT1711H_REG_FAULT_STATUS_AUTO_DISC_FAILBIT(5)
+#define RT1711H_REG_FAULT_STATUS_FORCE_DISC_FAILBIT(4)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OCBIT(3)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OVBIT(2)
+#define RT1711H_REG_FAULT_STATUS_VCONN_OCBIT(1)
+#define RT1711H_REG_FAULT_STATUS_I2C_ERRORBIT(0)
+
+/*
+ * RT1711H_REG_ROLE_CTRL(0x1A)
+ */
+#define RT1711H_REG_ROLE_CTRL_DRPBIT(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_MODEBIT(1)
+#define RT1711H_REG_TCPC_CTRL_PLUG_ORIENTBIT(0)
+
+/*
+ * RT1711H_REG_FAULT_CTRL(0x1B)
+ */
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OVBIT(7)
+#define RT1711H_REG_FAULT_CTRL_DIS_SNK_VBUS_OCBIT(2)
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OCBIT(0)
+
+/*
+ * RT1711H_REG_POWER_CTRL(0x1C)
+ */
+#define RT1711H_REG_POWER_CTRL_VCONNBIT(0)
+
+/*
+ * RT1711H_REG_CC_STATUS(0x1D)
+ */
+#define RT1711H_REG_CC_STATUS_DRP_TOGGLINGBIT(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_ENBIT(7)
+#define RT1711H_REG_CLK_BCLK2_ENBIT(6)
+#define RT1711H_REG_CLK_BCLK2_TG_ENBIT(5)
+#define RT1711H_REG_CLK_DIV_300K_ENBIT(3)
+#define RT1711H_REG_CLK_CK_300K_ENBIT(2)
+#define RT1711H_REG_CLK_BCLK_ENBIT(1)
+#define RT1711H_REG_CLK_BCLK_TH_ENBIT(0)
+
+/*
+ * RT1711H_REG_CLK_CTRL3(0x88)
+ */
+#define RT1711H_REG_CLK_OSCMUX_RG_ENBIT(7)
+#define RT1711H_REG_CLK_CK_24M_ENBIT(6)
+#define RT1711H_REG_CLK_OSC_RG_ENBIT(5)
+#define RT1711H_REG_CLK_DIV_2P4M_ENBIT(4)
+#define RT1711H_REG_CLK_CK_2P4M_ENBIT(3)
+#define RT1711H_REG_CLK_PCLK_ENBIT(2)
+#define RT1711H_REG_CLK_PCLK_RG_ENBIT(1)
+#define RT1711H_REG_CLK_PCLK_TG_ENBIT(0)
+
+/*
+ * RT1711H_REG_BMC_CTRL(0x90)
+ */
+#define RT1711H_REG_IDLE_ENBIT(6)
+#define RT1711H_REG_DISCHARGE_ENBIT(5)
+#define RT1711H_REG_BMCIO_LPRPRDBIT(4)
+#define RT1711H_REG_BMCIO_LPENBIT(3)
+#define RT1711H_REG_BMCIO_BG_ENBIT(2)
+#define RT1711H_REG_VBUS_DET_ENBIT(1)
+#define RT1711H_REG_BMCIO_OSC_ENBIT(0)
+
+/*
+ * RT1711H_REG_RT_STATUS(0x97)
+ */
+#define RT1711H_REG_RA_DETACHBIT(5)
+#define RT1711H_REG_VBUS_80BIT(1)
+
+/*
+ * RT1711H_REG_RT_INT(0x98)
+ */
+#define RT1711H_REG_INT_RA_DETACHBIT(5)
+#define RT1711H_REG_INT_WATCHDOGBIT(2)
+#define RT1711H_REG_INT_VBUS_80BIT(1)
+#define RT1711H_REG_INT_WAKEUPBIT(0)
+
+/*
+ * RT1711H_REG_RT_MASK(0x99)
+ */
+#define RT1711H_REG_M_RA_DETACHBIT(5)
+#define RT1711H_REG_M_WATCHDOGBIT(2)
+#define RT1711H_REG_M_VBUS_80BIT(1)
+#define RT1711H_REG_M_WAKEUPBIT(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_SELBIT(7)
+#define RT1711H_REG_SHIPPING_OFFBIT(5)
+#define RT1711H_REG_AUTOIDLE_ENBIT(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_ENBIT(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_ENBIT(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_ENBIT(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 */

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 11:08     ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-17 11:08 UTC (permalink / raw)
  To: shufan_lee(?????????)
  Cc: 'ShuFanLee', cy_huang(?????????), linux-kernel, linux-usb

Hi,

On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> Dear Heikki,
> 
>   Sorry for bothering.
> 
>   Just want to check is there anything we need to modify?

I'll check the patch this week, but please note that we are -rc8, so
nothing is going to happen before -rc1 is out.


Br,

-- 
heikki

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 11:08     ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-17 11:08 UTC (permalink / raw)
  To: shufan_lee(?????????)
  Cc: 'ShuFanLee', cy_huang(?????????), linux-kernel, linux-usb

Hi,

On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> Dear Heikki,
> 
>   Sorry for bothering.
> 
>   Just want to check is there anything we need to modify?

I'll check the patch this week, but please note that we are -rc8, so
nothing is going to happen before -rc1 is out.


Br,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 11:14       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 11:14 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: shufan_lee(?????????), 'ShuFanLee', cy_huang(?????????),
	linux-kernel, linux-usb

On Wed, Jan 17, 2018 at 01:08:58PM +0200, Heikki Krogerus wrote:
> Hi,
> 
> On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> > Dear Heikki,
> > 
> >   Sorry for bothering.
> > 
> >   Just want to check is there anything we need to modify?
> 
> I'll check the patch this week, but please note that we are -rc8, so
> nothing is going to happen before -rc1 is out.

If you ack it today, I could queue it up for -rc1 as it is a stand-alone
driver...

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 11:14       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 51+ messages in thread
From: Greg Kroah-Hartman @ 2018-01-17 11:14 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: shufan_lee(?????????), 'ShuFanLee', cy_huang(?????????),
	linux-kernel, linux-usb

On Wed, Jan 17, 2018 at 01:08:58PM +0200, Heikki Krogerus wrote:
> Hi,
> 
> On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> > Dear Heikki,
> > 
> >   Sorry for bothering.
> > 
> >   Just want to check is there anything we need to modify?
> 
> I'll check the patch this week, but please note that we are -rc8, so
> nothing is going to happen before -rc1 is out.

If you ack it today, I could queue it up for -rc1 as it is a stand-alone
driver...
---
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 12:00         ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-17 12:00 UTC (permalink / raw)
  To: Greg KH
  Cc: shufan_lee(?????????), 'ShuFanLee', cy_huang(?????????),
	linux-kernel, linux-usb

Hi Greg,

On Wed, Jan 17, 2018 at 12:14:02PM +0100, Greg KH wrote:
> On Wed, Jan 17, 2018 at 01:08:58PM +0200, Heikki Krogerus wrote:
> > Hi,
> > 
> > On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> > > Dear Heikki,
> > > 
> > >   Sorry for bothering.
> > > 
> > >   Just want to check is there anything we need to modify?
> > 
> > I'll check the patch this week, but please note that we are -rc8, so
> > nothing is going to happen before -rc1 is out.
> 
> If you ack it today, I could queue it up for -rc1 as it is a stand-alone
> driver...

The driver does not compile as module:

ERROR: "cpu_idle_poll_ctrl" [drivers/usb/typec/rt1711h/rt1711h.ko] undefined!


Br,

-- 
heikki

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 12:00         ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-17 12:00 UTC (permalink / raw)
  To: Greg KH
  Cc: shufan_lee(?????????), 'ShuFanLee', cy_huang(?????????),
	linux-kernel, linux-usb

Hi Greg,

On Wed, Jan 17, 2018 at 12:14:02PM +0100, Greg KH wrote:
> On Wed, Jan 17, 2018 at 01:08:58PM +0200, Heikki Krogerus wrote:
> > Hi,
> > 
> > On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> > > Dear Heikki,
> > > 
> > >   Sorry for bothering.
> > > 
> > >   Just want to check is there anything we need to modify?
> > 
> > I'll check the patch this week, but please note that we are -rc8, so
> > nothing is going to happen before -rc1 is out.
> 
> If you ack it today, I could queue it up for -rc1 as it is a stand-alone
> driver...

The driver does not compile as module:

ERROR: "cpu_idle_poll_ctrl" [drivers/usb/typec/rt1711h/rt1711h.ko] undefined!


Br,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:31           ` Greg Kroah-Hartman
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:31 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: shufan_lee(?????????), 'ShuFanLee', cy_huang(?????????),
	linux-kernel, linux-usb

On Wed, Jan 17, 2018 at 02:00:28PM +0200, Heikki Krogerus wrote:
> Hi Greg,
> 
> On Wed, Jan 17, 2018 at 12:14:02PM +0100, Greg KH wrote:
> > On Wed, Jan 17, 2018 at 01:08:58PM +0200, Heikki Krogerus wrote:
> > > Hi,
> > > 
> > > On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> > > > Dear Heikki,
> > > > 
> > > >   Sorry for bothering.
> > > > 
> > > >   Just want to check is there anything we need to modify?
> > > 
> > > I'll check the patch this week, but please note that we are -rc8, so
> > > nothing is going to happen before -rc1 is out.
> > 
> > If you ack it today, I could queue it up for -rc1 as it is a stand-alone
> > driver...
> 
> The driver does not compile as module:
> 
> ERROR: "cpu_idle_poll_ctrl" [drivers/usb/typec/rt1711h/rt1711h.ko] undefined!

Well of course it has to pass review :)

Why wouuld a driver be calling that function?  That's not right at all,
ick...

Shufan, what are you doing there?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:31           ` Greg Kroah-Hartman
  0 siblings, 0 replies; 51+ messages in thread
From: Greg Kroah-Hartman @ 2018-01-17 13:31 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: shufan_lee(?????????), 'ShuFanLee', cy_huang(?????????),
	linux-kernel, linux-usb

On Wed, Jan 17, 2018 at 02:00:28PM +0200, Heikki Krogerus wrote:
> Hi Greg,
> 
> On Wed, Jan 17, 2018 at 12:14:02PM +0100, Greg KH wrote:
> > On Wed, Jan 17, 2018 at 01:08:58PM +0200, Heikki Krogerus wrote:
> > > Hi,
> > > 
> > > On Wed, Jan 17, 2018 at 09:30:45AM +0000, shufan_lee(?????????) wrote:
> > > > Dear Heikki,
> > > > 
> > > >   Sorry for bothering.
> > > > 
> > > >   Just want to check is there anything we need to modify?
> > > 
> > > I'll check the patch this week, but please note that we are -rc8, so
> > > nothing is going to happen before -rc1 is out.
> > 
> > If you ack it today, I could queue it up for -rc1 as it is a stand-alone
> > driver...
> 
> The driver does not compile as module:
> 
> ERROR: "cpu_idle_poll_ctrl" [drivers/usb/typec/rt1711h/rt1711h.ko] undefined!

Well of course it has to pass review :)

Why wouuld a driver be calling that function?  That's not right at all,
ick...

Shufan, what are you doing there?

thanks,

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:33   ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:33 UTC (permalink / raw)
  To: ShuFanLee; +Cc: heikki.krogerus, cy_huang, shufan_lee, linux-kernel, linux-usb

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> +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));
> +}

This is very odd, and not good.  What are you trying to do here?  And
why are you thinking that poll_count should be an atomic variable?

This feels really strange, and not something you should be doing in an
irq handler, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:33   ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:33 UTC (permalink / raw)
  To: ShuFanLee; +Cc: heikki.krogerus, cy_huang, shufan_lee, linux-kernel, linux-usb

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> +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));
> +}

This is very odd, and not good.  What are you trying to do here?  And
why are you thinking that poll_count should be an atomic variable?

This feels really strange, and not something you should be doing in an
irq handler, right?

thanks,

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:33   ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:33 UTC (permalink / raw)
  To: ShuFanLee; +Cc: heikki.krogerus, cy_huang, shufan_lee, linux-kernel, linux-usb

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> +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);
> +}

Did you run sparse on this code?  What are you doing casting the types
all over the place for data?  That does not seem correct at all.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:33   ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:33 UTC (permalink / raw)
  To: ShuFanLee; +Cc: heikki.krogerus, cy_huang, shufan_lee, linux-kernel, linux-usb

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> +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);
> +}

Did you run sparse on this code?  What are you doing casting the types
all over the place for data?  That does not seem correct at all.

thanks,

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:42   ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:42 UTC (permalink / raw)
  To: ShuFanLee; +Cc: heikki.krogerus, cy_huang, shufan_lee, linux-kernel, linux-usb

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> From: ShuFanLee <shufan_lee@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_lee@richtek.com>

Minor review of your main structure and your debugfs code and other
stuff, all of which need work:

> ---
>  .../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 */
> +};

dts stuff needs to always be in a separate file so the DT maintainers
can review/ack it.  Split this patch up into smaller pieces please.


> 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/

Why do you need a whole directory for one file?


>  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>

This last #include should not be needed.  If it does, you are doing
something really wrong...

> +
> +#include "rt1711h.h"

Why a .h file for a single .c file?

> +
> +#define RT1711H_DRV_VERSION	"1.0.3"

When code is in the kernel tree, versions mean nothing, you will note
that no other USB driver has them, right?  Please remove.


> +
> +#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;

kernel types are u16, u32, u8, and the like, not uint16_t, those are for
userspace code only.

Yeah, other drivers do it, but you shouldn't :)


> +	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;

3 things for an irq handler?  That feels wrong.

> +	atomic_t poll_count;

Like I said before, why is this an atomic?

> +	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;

Why are these atomic?  You know that doesn't mean they do not need
locking, right?

> +
> +	/* psy + psy status */
> +	struct power_supply *psy;
> +	u32 current_limit;
> +	u32 supply_voltage;
> +
> +	/* lock for sharing chip states */
> +	struct mutex lock;

How many locks do you have in this structure?  You should only need 1.

> +
> +	/* 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 */

That's a lot of stuff jsut for debugfs.  Why do you care about #define
at all?  The code should not.

And another 2 locks?  Ick, no.


> +};
> +
> +/*
> + * 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;
> +}

What is this function doing?  What is your debugfs files for?

> +
> +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;

No need to ever check the return value of debugfs_ calls, you should not
care and can always use the value to any future debugfs calls, if you
really need it.

> +	}
> +
> +	for (i = 0; i < RT1711H_DBG_MAX; i++) {
> +		info = &chip->dbg_info[i];

static array of debug info?  That feels odd.

> +		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;

Like here, you don't need this, and you don't need to care about the
return value.

> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +err:
> +	debugfs_remove_recursive(chip->dbgdir);
> +	return ret;

Why do you care about an error here?  Your code should not do anything
different if debugfs stuff does not work or if it does.  It's debugging
only.

> +}
> +
> +static void rt1711h_debugfs_exit(struct rt1711h_chip *chip)
> +{
> +	debugfs_remove_recursive(chip->dbgdir);

See, you didn't need those file handles :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-17 13:42   ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-17 13:42 UTC (permalink / raw)
  To: ShuFanLee; +Cc: heikki.krogerus, cy_huang, shufan_lee, linux-kernel, linux-usb

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> From: ShuFanLee <shufan_lee@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_lee@richtek.com>

Minor review of your main structure and your debugfs code and other
stuff, all of which need work:

> ---
>  .../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 */
> +};

dts stuff needs to always be in a separate file so the DT maintainers
can review/ack it.  Split this patch up into smaller pieces please.


> 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/

Why do you need a whole directory for one file?


>  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>

This last #include should not be needed.  If it does, you are doing
something really wrong...

> +
> +#include "rt1711h.h"

Why a .h file for a single .c file?

> +
> +#define RT1711H_DRV_VERSION	"1.0.3"

When code is in the kernel tree, versions mean nothing, you will note
that no other USB driver has them, right?  Please remove.


> +
> +#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;

kernel types are u16, u32, u8, and the like, not uint16_t, those are for
userspace code only.

Yeah, other drivers do it, but you shouldn't :)


> +	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;

3 things for an irq handler?  That feels wrong.

> +	atomic_t poll_count;

Like I said before, why is this an atomic?

> +	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;

Why are these atomic?  You know that doesn't mean they do not need
locking, right?

> +
> +	/* psy + psy status */
> +	struct power_supply *psy;
> +	u32 current_limit;
> +	u32 supply_voltage;
> +
> +	/* lock for sharing chip states */
> +	struct mutex lock;

How many locks do you have in this structure?  You should only need 1.

> +
> +	/* 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 */

That's a lot of stuff jsut for debugfs.  Why do you care about #define
at all?  The code should not.

And another 2 locks?  Ick, no.


> +};
> +
> +/*
> + * 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;
> +}

What is this function doing?  What is your debugfs files for?

> +
> +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;

No need to ever check the return value of debugfs_ calls, you should not
care and can always use the value to any future debugfs calls, if you
really need it.

> +	}
> +
> +	for (i = 0; i < RT1711H_DBG_MAX; i++) {
> +		info = &chip->dbg_info[i];

static array of debug info?  That feels odd.

> +		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;

Like here, you don't need this, and you don't need to care about the
return value.

> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +err:
> +	debugfs_remove_recursive(chip->dbgdir);
> +	return ret;

Why do you care about an error here?  Your code should not do anything
different if debugfs stuff does not work or if it does.  It's debugging
only.

> +}
> +
> +static void rt1711h_debugfs_exit(struct rt1711h_chip *chip)
> +{
> +	debugfs_remove_recursive(chip->dbgdir);

See, you didn't need those file handles :)

thanks,

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-18 13:13     ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-18 13:13 UTC (permalink / raw)
  To: 'Greg KH', ShuFanLee
  Cc: heikki.krogerus, cy_huang(黃啟原),
	linux-kernel, linux-usb

Dear Gerg,

  Many thanks to your comment.
  I've checked all of them and here are some questions need your help.

> +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 */
> +};

dts stuff needs to always be in a separate file so the DT maintainers can review/ack it.  Split this patch up into smaller pieces please.

Ok, I'll split it into two patches, one for source code and once for dts related files.
========================================================================

> 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/

Why do you need a whole directory for one file?

Is the suggestion to move rt1711h.c to the same directory level as tcpm? i.e. drivers/usb/typec/rt1711h.c
========================================================================

> +<uapi/linux/sched/types.h>

This last #include should not be needed.  If it does, you are doing something really wrong...

Ok, this #include will be removed
========================================================================

> +
> +#include "rt1711h.h"

Why a .h file for a single .c file?

Is the suggestion to move all content in rt1711h.h into rt1711h.c?
========================================================================

> +
> +#define RT1711H_DRV_VERSION"1.0.3"

When code is in the kernel tree, versions mean nothing, you will note that no other USB driver has them, right?  Please remove.

Ok, this will be removed.
========================================================================

kernel types are u16, u32, u8, and the like, not uint16_t, those are for userspace code only.
Yeah, other drivers do it, but you shouldn't :)

Ok, I'll use u16, u32 and u8 instead of uint16_t, uint32_t and uint8_t
========================================================================

> +/* IRQ */
> +struct kthread_worker irq_worker;
> +struct kthread_work irq_work;
> +struct task_struct *irq_worker_task;

3 things for an irq handler?  That feels wrong.

> +atomic_t poll_count;

Like I said before, why is this an atomic?

I'll use threaded_irq instead, the above things will be removed.
========================================================================

> +/* I2C */
> +atomic_t i2c_busy;
> +atomic_t pm_suspend;

Why are these atomic?  You know that doesn't mean they do not need locking, right?

For my understanding, a single operation on atomic_t doesn't need lock, like a single atomic_set.
But two consecutive operations doesn't guarantee anything. Like atomic_set followed by an atomic_read.
This part is referenced from fusb302 used to make sure I2C is idle before system suspends.
It only needs to guarantee a single read/write on these variable is atomic operation, so atomic is used.
========================================================================

> +};
> +
> +/*
> + * 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;
> +}

What is this function doing?  What is your debugfs files for?

There are 4 debug files.
First(log) is for logging which needs a lock for log buffer. The way to log is referenced from fusb302 and tcpm.
Second(regs) is used to dump all register of rt1711h.
Third(reg_addr)&Forth(data) are used to write/read a register specified in reg_addr.
The reason to create these debug files is to make issue support easier.
========================================================================

> +#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 */

That's a lot of stuff jsut for debugfs.  Why do you care about #define at all?  The code should not.

Is the suggestion to remove #ifdef CONFIG_DEBUG_FS?

And another 2 locks?  Ick, no.

dbgops_lock is used to prevent user from accessing different debug files simultaneously.
Is the suggestion to use the lock of the following one?
> +/* lock for sharing chip states */
> +struct mutex lock;
========================================================================

> +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;

No need to ever check the return value of debugfs_ calls, you should not care and can always use the value to any future debugfs calls, if you really need it.

If it is NULL without checking and we use it in debugfs_create_file, all the debug files will be created in the root of the debugfs filesystem.
Is this correct?
========================================================================

> +for (i = 0; i < RT1711H_DBG_MAX; i++) {
> +info = &chip->dbg_info[i];

static array of debug info?  That feels odd.

Is the suggestion to use pointer of array and dynamically allocated it?
========================================================================

Like here, you don't need this, and you don't need to care about the return value.

> +goto err;
> +}
> +}
> +
> +return 0;
> +err:
> +debugfs_remove_recursive(chip->dbgdir);
> +return ret;

Why do you care about an error here?  Your code should not do anything different if debugfs stuff does not work or if it does.  It's debugging only.
Ok, this will be removed.
========================================================================

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Greg KH [mailto:greg@kroah.com]
Sent: Wednesday, January 17, 2018 9:42 PM
To: ShuFanLee
Cc: heikki.krogerus@linux.intel.com; cy_huang(黃啟原); shufan_lee(李書帆); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On Wed, Jan 10, 2018 at 02:59:12PM +0800, ShuFanLee wrote:
> From: ShuFanLee <shufan_lee@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_lee@richtek.com>

Minor review of your main structure and your debugfs code and other stuff, all of which need work:

> ---
>  .../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 */
> +};

dts stuff needs to always be in a separate file so the DT maintainers can review/ack it.  Split this patch up into smaller pieces please.


> 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/

Why do you need a whole directory for one file?


>  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>

This last #include should not be needed.  If it does, you are doing something really wrong...

> +
> +#include "rt1711h.h"

Why a .h file for a single .c file?

> +
> +#define RT1711H_DRV_VERSION"1.0.3"

When code is in the kernel tree, versions mean nothing, you will note that no other USB driver has them, right?  Please remove.


> +
> +#define LOG_BUFFER_ENTRIES1024
> +#define LOG_BUFFER_ENTRY_SIZE128 /* 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;

kernel types are u16, u32, u8, and the like, not uint16_t, those are for userspace code only.

Yeah, other drivers do it, but you shouldn't :)


> +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;

3 things for an irq handler?  That feels wrong.

> +atomic_t poll_count;

Like I said before, why is this an atomic?

> +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;

Why are these atomic?  You know that doesn't mean they do not need locking, right?

> +
> +/* psy + psy status */
> +struct power_supply *psy;
> +u32 current_limit;
> +u32 supply_voltage;
> +
> +/* lock for sharing chip states */
> +struct mutex lock;

How many locks do you have in this structure?  You should only need 1.

> +
> +/* 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 */

That's a lot of stuff jsut for debugfs.  Why do you care about #define at all?  The code should not.

And another 2 locks?  Ick, no.


> +};
> +
> +/*
> + * 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;
> +}

What is this function doing?  What is your debugfs files for?

> +
> +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;

No need to ever check the return value of debugfs_ calls, you should not care and can always use the value to any future debugfs calls, if you really need it.

> +}
> +
> +for (i = 0; i < RT1711H_DBG_MAX; i++) {
> +info = &chip->dbg_info[i];

static array of debug info?  That feels odd.

> +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;

Like here, you don't need this, and you don't need to care about the return value.

> +goto err;
> +}
> +}
> +
> +return 0;
> +err:
> +debugfs_remove_recursive(chip->dbgdir);
> +return ret;

Why do you care about an error here?  Your code should not do anything different if debugfs stuff does not work or if it does.  It's debugging only.

> +}
> +
> +static void rt1711h_debugfs_exit(struct rt1711h_chip *chip) {
> +debugfs_remove_recursive(chip->dbgdir);

See, you didn't need those file handles :)

thanks,

greg k-h
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-18 13:13     ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-18 13:13 UTC (permalink / raw)
  To: 'Greg KH', ShuFanLee
  Cc: heikki.krogerus, cy_huang(黃啟原),
	linux-kernel, linux-usb

RGVhciBHZXJnLA0KDQogIE1hbnkgdGhhbmtzIHRvIHlvdXIgY29tbWVudC4NCiAgSSd2ZSBjaGVj
a2VkIGFsbCBvZiB0aGVtIGFuZCBoZXJlIGFyZSBzb21lIHF1ZXN0aW9ucyBuZWVkIHlvdXIgaGVs
cC4NCg0KPiArRXhhbXBsZToNCj4gK3J0MTcxMWhANGUgew0KPiArc3RhdHVzID0gIm9rIjsNCj4g
K2NvbXBhdGlibGUgPSAicmljaHRlayx0eXBlY19ydDE3MTFoIjsNCj4gK3JlZyA9IDwweDRlPjsN
Cj4gK3J0LGludHJfZ3BpbyA9IDwmZ3BpbzI2IDAgMHgwPjsNCj4gK3J0LG5hbWUgPSAicnQxNzEx
aCI7DQo+ICtydCxwb3J0X3R5cGUgPSA8Mj47IC8qIDA6IERGUCwgMTogVUZQLCAyOiBEUlAgKi8N
Cj4gK3J0LGRlZl9yb2xlID0gPDA+OyAvKiAwOiBTTkssIDE6IFNSQywgLTE6IFRZUEVDX05PX1BS
RUZFUlJFRF9ST0xFICovDQo+ICt9Ow0KDQpkdHMgc3R1ZmYgbmVlZHMgdG8gYWx3YXlzIGJlIGlu
IGEgc2VwYXJhdGUgZmlsZSBzbyB0aGUgRFQgbWFpbnRhaW5lcnMgY2FuIHJldmlldy9hY2sgaXQu
ICBTcGxpdCB0aGlzIHBhdGNoIHVwIGludG8gc21hbGxlciBwaWVjZXMgcGxlYXNlLg0KDQpPaywg
SSdsbCBzcGxpdCBpdCBpbnRvIHR3byBwYXRjaGVzLCBvbmUgZm9yIHNvdXJjZSBjb2RlIGFuZCBv
bmNlIGZvciBkdHMgcmVsYXRlZCBmaWxlcy4NCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQo+IGRpZmYgLS1n
aXQgYS9kcml2ZXJzL3VzYi90eXBlYy9NYWtlZmlsZSBiL2RyaXZlcnMvdXNiL3R5cGVjL01ha2Vm
aWxlDQo+IGluZGV4IGJiMzEzOGEuLmUzYWFmM2MgMTAwNjQ0DQo+IC0tLSBhL2RyaXZlcnMvdXNi
L3R5cGVjL01ha2VmaWxlDQo+ICsrKyBiL2RyaXZlcnMvdXNiL3R5cGVjL01ha2VmaWxlDQo+IEBA
IC0yLDYgKzIsNyBAQA0KPiAgb2JqLSQoQ09ORklHX1RZUEVDKSs9IHR5cGVjLm8NCj4gIG9iai0k
KENPTkZJR19UWVBFQ19UQ1BNKSs9IHRjcG0ubw0KPiAgb2JqLXkrPSBmdXNiMzAyLw0KPiArb2Jq
LSQoQ09ORklHX1RZUEVDX1JUMTcxMUgpKz0gcnQxNzExaC8NCg0KV2h5IGRvIHlvdSBuZWVkIGEg
d2hvbGUgZGlyZWN0b3J5IGZvciBvbmUgZmlsZT8NCg0KSXMgdGhlIHN1Z2dlc3Rpb24gdG8gbW92
ZSBydDE3MTFoLmMgdG8gdGhlIHNhbWUgZGlyZWN0b3J5IGxldmVsIGFzIHRjcG0/IGkuZS4gZHJp
dmVycy91c2IvdHlwZWMvcnQxNzExaC5jDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KPiArPHVhcGkvbGlu
dXgvc2NoZWQvdHlwZXMuaD4NCg0KVGhpcyBsYXN0ICNpbmNsdWRlIHNob3VsZCBub3QgYmUgbmVl
ZGVkLiAgSWYgaXQgZG9lcywgeW91IGFyZSBkb2luZyBzb21ldGhpbmcgcmVhbGx5IHdyb25nLi4u
DQoNCk9rLCB0aGlzICNpbmNsdWRlIHdpbGwgYmUgcmVtb3ZlZA0KPT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoN
Cj4gKw0KPiArI2luY2x1ZGUgInJ0MTcxMWguaCINCg0KV2h5IGEgLmggZmlsZSBmb3IgYSBzaW5n
bGUgLmMgZmlsZT8NCg0KSXMgdGhlIHN1Z2dlc3Rpb24gdG8gbW92ZSBhbGwgY29udGVudCBpbiBy
dDE3MTFoLmggaW50byBydDE3MTFoLmM/DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KPiArDQo+ICsjZGVm
aW5lIFJUMTcxMUhfRFJWX1ZFUlNJT04iMS4wLjMiDQoNCldoZW4gY29kZSBpcyBpbiB0aGUga2Vy
bmVsIHRyZWUsIHZlcnNpb25zIG1lYW4gbm90aGluZywgeW91IHdpbGwgbm90ZSB0aGF0IG5vIG90
aGVyIFVTQiBkcml2ZXIgaGFzIHRoZW0sIHJpZ2h0PyAgUGxlYXNlIHJlbW92ZS4NCg0KT2ssIHRo
aXMgd2lsbCBiZSByZW1vdmVkLg0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmtlcm5lbCB0eXBlcyBhcmUg
dTE2LCB1MzIsIHU4LCBhbmQgdGhlIGxpa2UsIG5vdCB1aW50MTZfdCwgdGhvc2UgYXJlIGZvciB1
c2Vyc3BhY2UgY29kZSBvbmx5Lg0KWWVhaCwgb3RoZXIgZHJpdmVycyBkbyBpdCwgYnV0IHlvdSBz
aG91bGRuJ3QgOikNCg0KT2ssIEknbGwgdXNlIHUxNiwgdTMyIGFuZCB1OCBpbnN0ZWFkIG9mIHVp
bnQxNl90LCB1aW50MzJfdCBhbmQgdWludDhfdA0KPT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCj4gKy8qIElS
USAqLw0KPiArc3RydWN0IGt0aHJlYWRfd29ya2VyIGlycV93b3JrZXI7DQo+ICtzdHJ1Y3Qga3Ro
cmVhZF93b3JrIGlycV93b3JrOw0KPiArc3RydWN0IHRhc2tfc3RydWN0ICppcnFfd29ya2VyX3Rh
c2s7DQoNCjMgdGhpbmdzIGZvciBhbiBpcnEgaGFuZGxlcj8gIFRoYXQgZmVlbHMgd3JvbmcuDQoN
Cj4gK2F0b21pY190IHBvbGxfY291bnQ7DQoNCkxpa2UgSSBzYWlkIGJlZm9yZSwgd2h5IGlzIHRo
aXMgYW4gYXRvbWljPw0KDQpJJ2xsIHVzZSB0aHJlYWRlZF9pcnEgaW5zdGVhZCwgdGhlIGFib3Zl
IHRoaW5ncyB3aWxsIGJlIHJlbW92ZWQuDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KPiArLyogSTJDICov
DQo+ICthdG9taWNfdCBpMmNfYnVzeTsNCj4gK2F0b21pY190IHBtX3N1c3BlbmQ7DQoNCldoeSBh
cmUgdGhlc2UgYXRvbWljPyAgWW91IGtub3cgdGhhdCBkb2Vzbid0IG1lYW4gdGhleSBkbyBub3Qg
bmVlZCBsb2NraW5nLCByaWdodD8NCg0KRm9yIG15IHVuZGVyc3RhbmRpbmcsIGEgc2luZ2xlIG9w
ZXJhdGlvbiBvbiBhdG9taWNfdCBkb2Vzbid0IG5lZWQgbG9jaywgbGlrZSBhIHNpbmdsZSBhdG9t
aWNfc2V0Lg0KQnV0IHR3byBjb25zZWN1dGl2ZSBvcGVyYXRpb25zIGRvZXNuJ3QgZ3VhcmFudGVl
IGFueXRoaW5nLiBMaWtlIGF0b21pY19zZXQgZm9sbG93ZWQgYnkgYW4gYXRvbWljX3JlYWQuDQpU
aGlzIHBhcnQgaXMgcmVmZXJlbmNlZCBmcm9tIGZ1c2IzMDIgdXNlZCB0byBtYWtlIHN1cmUgSTJD
IGlzIGlkbGUgYmVmb3JlIHN5c3RlbSBzdXNwZW5kcy4NCkl0IG9ubHkgbmVlZHMgdG8gZ3VhcmFu
dGVlIGEgc2luZ2xlIHJlYWQvd3JpdGUgb24gdGhlc2UgdmFyaWFibGUgaXMgYXRvbWljIG9wZXJh
dGlvbiwgc28gYXRvbWljIGlzIHVzZWQuDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KPiArfTsNCj4gKw0K
PiArLyoNCj4gKyAqIExvZ2dpbmcgJiBkZWJ1Z2dpbmcNCj4gKyAqLw0KPiArDQo+ICsjaWZkZWYg
Q09ORklHX0RFQlVHX0ZTDQo+ICsNCj4gK3N0YXRpYyBpbnQgcnQxNzExaF9yZWdfYmxvY2tfcmVh
ZChzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLCB1aW50OF90IHJlZywNCj4gK2ludCBsZW4sIHVp
bnQ4X3QgKmRhdGEpOw0KPiArc3RhdGljIGludCBydDE3MTFoX3JlZ19ibG9ja193cml0ZShzdHJ1
Y3QgcnQxNzExaF9jaGlwICpjaGlwLCB1aW50OF90IHJlZywNCj4gK2ludCBsZW4sIGNvbnN0IHVp
bnQ4X3QgKmRhdGEpOw0KPiArDQo+ICtzdHJ1Y3QgcmVnX2Rlc2Mgew0KPiArdWludDhfdCBhZGRy
Ow0KPiArdWludDhfdCBzaXplOw0KPiArfTsNCj4gKyNkZWZpbmUgREVDTF9SRUcoX2FkZHIsIF9z
aXplKSB7LmFkZHIgPSBfYWRkciwgLnNpemUgPSBfc2l6ZX0NCj4gKw0KPiArc3RhdGljIHN0cnVj
dCByZWdfZGVzYyBydDE3MTFoX3JlZ19kZXNjW10gPSB7DQo+ICtERUNMX1JFRyhSVDE3MTFIX1JF
R19WSUQsIDIpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfUElELCAyKSwNCj4gK0RFQ0xfUkVH
KFJUMTcxMUhfUkVHX0RJRCwgMiksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19UWVBFQ19SRVYs
IDIpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfUERfUkVWLCAyKSwNCj4gK0RFQ0xfUkVHKFJU
MTcxMUhfUkVHX1BESUZfUkVWLCAyKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0FMRVJULCAy
KSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0FMRVJUX01BU0ssIDIpLA0KPiArREVDTF9SRUco
UlQxNzExSF9SRUdfUE9XRVJfU1RBVFVTX01BU0ssIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9S
RUdfRkFVTFRfU1RBVFVTX01BU0ssIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfVENQQ19D
VFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JPTEVfQ1RSTCwgMSksDQo+ICtERUNM
X1JFRyhSVDE3MTFIX1JFR19GQVVMVF9DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVH
X1BPV0VSX0NUUkwsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfQ0NfU1RBVFVTLCAxKSwN
Cj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1BPV0VSX1NUQVRVUywgMSksDQo+ICtERUNMX1JFRyhS
VDE3MTFIX1JFR19GQVVMVF9TVEFUVVMsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfQ09N
TUFORCwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19NU0dfSERSX0lORk8sIDEpLA0KPiAr
REVDTF9SRUcoUlQxNzExSF9SRUdfUlhfREVURUNULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhf
UkVHX1JYX0JZVEVfQ05ULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JYX0JVRl9GUkFN
RV9UWVBFLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JYX0hEUiwgMiksDQo+ICtERUNM
X1JFRyhSVDE3MTFIX1JFR19SWF9EQVRBLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1RS
QU5TTUlULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1RYX0JZVEVfQ05ULCAxKSwNCj4g
K0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1RYX0hEUiwgMiksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JF
R19UWF9EQVRBLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0NMS19DVFJMMiwgMSksDQo+
ICtERUNMX1JFRyhSVDE3MTFIX1JFR19DTEtfQ1RSTDMsIDEpLA0KPiArREVDTF9SRUcoUlQxNzEx
SF9SRUdfQk1DX0NUUkwsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfQk1DSU9fUlhEWlNF
TCwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19WQ09OTl9DTElNSVRFTiwgMSksDQo+ICtE
RUNMX1JFRyhSVDE3MTFIX1JFR19SVF9TVEFUVVMsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9S
RUdfUlRfSU5ULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JUX01BU0ssIDEpLA0KPiAr
REVDTF9SRUcoUlQxNzExSF9SRUdfSURMRV9DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhf
UkVHX0lOVFJTVF9DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1dBVENIRE9HX0NU
UkwsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfSTJDUlNUX0NUUkwsIDEpLA0KPiArREVD
TF9SRUcoUlQxNzExSF9SRUdfU1dSRVNFVCwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19U
VENQQ19GSUxURVIsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfRFJQX1RPR0dMRV9DWUNM
RSwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19EUlBfRFVUWV9DVFJMLCAxKSwNCj4gK0RF
Q0xfUkVHKFJUMTcxMUhfUkVHX0JNQ0lPX1JYRFpFTiwgMSksIH07DQo+ICsNCj4gK3N0YXRpYyBj
b25zdCBjaGFyICpydDE3MTFoX2RiZ19maWxlbmFtZVtSVDE3MTFIX0RCR19NQVhdID0gew0KPiAr
ImxvZyIsICJyZWdzIiwgInJlZ19hZGRyIiwgImRhdGEiLA0KPiArfTsNCj4gKw0KPiArc3RhdGlj
IGJvb2wgcnQxNzExaF9sb2dfZnVsbChzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwKSB7DQo+ICty
ZXR1cm4gY2hpcC0+bG9nYnVmZmVyX3RhaWwgPT0NCj4gKyhjaGlwLT5sb2didWZmZXJfaGVhZCAr
IDEpICUgTE9HX0JVRkZFUl9FTlRSSUVTOyB9DQo+ICsNCj4gK3N0YXRpYyB2b2lkIF9ydDE3MTFo
X2xvZyhzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLCBjb25zdCBjaGFyICpmbXQsDQo+ICsgdmFf
bGlzdCBhcmdzKQ0KPiArew0KPiArY2hhciB0bXBidWZmZXJbTE9HX0JVRkZFUl9FTlRSWV9TSVpF
XTsNCj4gK3U2NCB0c19uc2VjID0gbG9jYWxfY2xvY2soKTsNCj4gK3Vuc2lnbmVkIGxvbmcgcmVt
X25zZWM7DQo+ICsNCj4gK2lmICghY2hpcC0+bG9nYnVmZmVyW2NoaXAtPmxvZ2J1ZmZlcl9oZWFk
XSkgew0KPiArY2hpcC0+bG9nYnVmZmVyW2NoaXAtPmxvZ2J1ZmZlcl9oZWFkXSA9DQo+ICtkZXZt
X2t6YWxsb2MoY2hpcC0+ZGV2LCBMT0dfQlVGRkVSX0VOVFJZX1NJWkUsIEdGUF9LRVJORUwpOw0K
PiAraWYgKCFjaGlwLT5sb2didWZmZXJbY2hpcC0+bG9nYnVmZmVyX2hlYWRdKQ0KPiArcmV0dXJu
Ow0KPiArfQ0KPiArDQo+ICt2c25wcmludGYodG1wYnVmZmVyLCBzaXplb2YodG1wYnVmZmVyKSwg
Zm10LCBhcmdzKTsNCj4gKw0KPiArbXV0ZXhfbG9jaygmY2hpcC0+bG9nYnVmZmVyX2xvY2spOw0K
PiArDQo+ICtpZiAocnQxNzExaF9sb2dfZnVsbChjaGlwKSkgew0KPiArY2hpcC0+bG9nYnVmZmVy
X2hlYWQgPSBtYXgoY2hpcC0+bG9nYnVmZmVyX2hlYWQgLSAxLCAwKTsNCj4gK3N0cmxjcHkodG1w
YnVmZmVyLCAib3ZlcmZsb3ciLCBzaXplb2YodG1wYnVmZmVyKSk7DQo+ICt9DQo+ICsNCj4gK2lm
IChjaGlwLT5sb2didWZmZXJfaGVhZCA8IDAgfHwNCj4gK2NoaXAtPmxvZ2J1ZmZlcl9oZWFkID49
IExPR19CVUZGRVJfRU5UUklFUykgew0KPiArZGV2X3dhcm4oY2hpcC0+ZGV2LCAiJXMgYmFkIGxv
ZyBidWZmZXIgaW5kZXggJWRcbiIsIF9fZnVuY19fLA0KPiArY2hpcC0+bG9nYnVmZmVyX2hlYWQp
Ow0KPiArZ290byBhYm9ydDsNCj4gK30NCj4gKw0KPiAraWYgKCFjaGlwLT5sb2didWZmZXJbY2hp
cC0+bG9nYnVmZmVyX2hlYWRdKSB7DQo+ICtkZXZfd2FybihjaGlwLT5kZXYsICIlcyBsb2cgYnVm
ZmVyIGluZGV4ICVkIGlzIE5VTExcbiIsDQo+ICtfX2Z1bmNfXywgY2hpcC0+bG9nYnVmZmVyX2hl
YWQpOw0KPiArZ290byBhYm9ydDsNCj4gK30NCj4gKw0KPiArcmVtX25zZWMgPSBkb19kaXYodHNf
bnNlYywgMTAwMDAwMDAwMCk7DQo+ICtzY25wcmludGYoY2hpcC0+bG9nYnVmZmVyW2NoaXAtPmxv
Z2J1ZmZlcl9oZWFkXSwgTE9HX0JVRkZFUl9FTlRSWV9TSVpFLA0KPiArIlslNWx1LiUwNmx1XSAl
cyIsICh1bnNpZ25lZCBsb25nKXRzX25zZWMsIHJlbV9uc2VjIC8gMTAwMCwNCj4gKyAgdG1wYnVm
ZmVyKTsNCj4gK2NoaXAtPmxvZ2J1ZmZlcl9oZWFkID0gKGNoaXAtPmxvZ2J1ZmZlcl9oZWFkICsg
MSkgJQ0KPiArTE9HX0JVRkZFUl9FTlRSSUVTOw0KPiArDQo+ICthYm9ydDoNCj4gK211dGV4X3Vu
bG9jaygmY2hpcC0+bG9nYnVmZmVyX2xvY2spOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgdm9pZCBy
dDE3MTFoX2xvZyhzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLA0KPiArY29uc3QgY2hhciAqZm10
LCAuLi4pDQo+ICt7DQo+ICt2YV9saXN0IGFyZ3M7DQo+ICsNCj4gK3ZhX3N0YXJ0KGFyZ3MsIGZt
dCk7DQo+ICtfcnQxNzExaF9sb2coY2hpcCwgZm10LCBhcmdzKTsNCj4gK3ZhX2VuZChhcmdzKTsN
Cj4gK30NCj4gKw0KPiArc3RhdGljIGludCBydDE3MTFoX2xvZ19zaG93KHN0cnVjdCBydDE3MTFo
X2NoaXAgKmNoaXAsIHN0cnVjdA0KPiArc2VxX2ZpbGUgKnMpIHsNCj4gK2ludCB0YWlsOw0KPiAr
DQo+ICttdXRleF9sb2NrKCZjaGlwLT5sb2didWZmZXJfbG9jayk7DQo+ICt0YWlsID0gY2hpcC0+
bG9nYnVmZmVyX3RhaWw7DQo+ICt3aGlsZSAodGFpbCAhPSBjaGlwLT5sb2didWZmZXJfaGVhZCkg
ew0KPiArc2VxX3ByaW50ZihzLCAiJXMiLCBjaGlwLT5sb2didWZmZXJbdGFpbF0pOw0KPiArdGFp
bCA9ICh0YWlsICsgMSkgJSBMT0dfQlVGRkVSX0VOVFJJRVM7DQo+ICt9DQo+ICtpZiAoIXNlcV9o
YXNfb3ZlcmZsb3dlZChzKSkNCj4gK2NoaXAtPmxvZ2J1ZmZlcl90YWlsID0gdGFpbDsNCj4gK211
dGV4X3VubG9jaygmY2hpcC0+bG9nYnVmZmVyX2xvY2spOw0KPiArDQo+ICtyZXR1cm4gMDsNCj4g
K30NCj4gKw0KPiArc3RhdGljIGludCBydDE3MTFoX3JlZ3Nfc2hvdyhzdHJ1Y3QgcnQxNzExaF9j
aGlwICpjaGlwLCBzdHJ1Y3QNCj4gK3NlcV9maWxlICpzKSB7DQo+ICtpbnQgcmV0ID0gMDsNCj4g
K2ludCBpID0gMCwgaiA9IDA7DQo+ICtzdHJ1Y3QgcmVnX2Rlc2MgKmRlc2MgPSBOVUxMOw0KPiAr
dWludDhfdCByZWd2YWxbMl0gPSB7MH07DQo+ICsNCj4gK2ZvciAoaSA9IDA7IGkgPCBBUlJBWV9T
SVpFKHJ0MTcxMWhfcmVnX2Rlc2MpOyBpKyspIHsNCj4gK2Rlc2MgPSAmcnQxNzExaF9yZWdfZGVz
Y1tpXTsNCj4gK3JldCA9IHJ0MTcxMWhfcmVnX2Jsb2NrX3JlYWQoY2hpcCwgZGVzYy0+YWRkciwg
ZGVzYy0+c2l6ZSwNCj4gK3JlZ3ZhbCk7DQo+ICtpZiAocmV0IDwgMCkgew0KPiArZGV2X2Vycihj
aGlwLT5kZXYsICIlcyByZWFkIHJlZzB4JTAyWCBmYWlsXG4iLA0KPiArX19mdW5jX18sIGRlc2Mt
PmFkZHIpOw0KPiArY29udGludWU7DQo+ICt9DQo+ICsNCj4gK3NlcV9wcmludGYocywgInJlZzB4
JTAyeDoweCIsIGRlc2MtPmFkZHIpOw0KPiArZm9yIChqID0gMDsgaiA8IGRlc2MtPnNpemU7IGor
KykNCj4gK3NlcV9wcmludGYocywgIiUwMngsIiwgcmVndmFsW2pdKTsNCj4gK3NlcV9wdXRzKHMs
ICJcbiIpOw0KPiArfQ0KPiArDQo+ICtyZXR1cm4gMDsNCj4gK30NCj4gKw0KPiArc3RhdGljIGlu
bGluZSBpbnQgcnQxNzExaF9yZWdfYWRkcl9zaG93KHN0cnVjdCBydDE3MTFoX2NoaXAgKmNoaXAs
DQo+ICtzdHJ1Y3Qgc2VxX2ZpbGUgKnMpDQo+ICt7DQo+ICtzdHJ1Y3QgcmVnX2Rlc2MgKmRlc2Mg
PSAmcnQxNzExaF9yZWdfZGVzY1tjaGlwLT5kYmdfcmVnaWR4XTsNCj4gKw0KPiArc2VxX3ByaW50
ZihzLCAiMHglMDJ4XG4iLCBkZXNjLT5hZGRyKTsNCj4gK3JldHVybiAwOw0KPiArfQ0KPiArDQo+
ICtzdGF0aWMgaW5saW5lIGludCBydDE3MTFoX2RhdGFfc2hvdyhzdHJ1Y3QgcnQxNzExaF9jaGlw
ICpjaGlwLA0KPiArc3RydWN0IHNlcV9maWxlICpzKQ0KPiArew0KPiAraW50IHJldCA9IDAsIGkg
PSAwOw0KPiArc3RydWN0IHJlZ19kZXNjICpkZXNjID0gJnJ0MTcxMWhfcmVnX2Rlc2NbY2hpcC0+
ZGJnX3JlZ2lkeF07DQo+ICt1aW50OF90IHJlZ3ZhbFsyXSA9IHswfTsNCj4gKw0KPiArcmV0ID0g
cnQxNzExaF9yZWdfYmxvY2tfcmVhZChjaGlwLCBkZXNjLT5hZGRyLCBkZXNjLT5zaXplLCByZWd2
YWwpOw0KPiAraWYgKHJldCA8IDApDQo+ICtyZXR1cm4gcmV0Ow0KPiArDQo+ICtzZXFfcHJpbnRm
KHMsICJyZWcweCUwMng9MHgiLCBkZXNjLT5hZGRyKTsNCj4gK2ZvciAoaSA9IDA7IGkgPCBkZXNj
LT5zaXplOyBpKyspDQo+ICtzZXFfcHJpbnRmKHMsICIlMDJ4LCIsIHJlZ3ZhbFtpXSk7DQo+ICtz
ZXFfcHV0cyhzLCAiXG4iKTsNCj4gK3JldHVybiAwOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW50
IHJ0MTcxMWhfZGJnX3Nob3coc3RydWN0IHNlcV9maWxlICpzLCB2b2lkICp2KSB7DQo+ICtpbnQg
cmV0ID0gMDsNCj4gK3N0cnVjdCBydDE3MTFoX2RiZ19pbmZvICppbmZvID0gKHN0cnVjdCBydDE3
MTFoX2RiZ19pbmZvICopcy0+cHJpdmF0ZTsNCj4gK3N0cnVjdCBydDE3MTFoX2NoaXAgKmNoaXAg
PSBpbmZvLT5jaGlwOw0KPiArDQo+ICttdXRleF9sb2NrKCZjaGlwLT5kYmdvcHNfbG9jayk7DQo+
ICtzd2l0Y2ggKGluZm8tPmlkKSB7DQo+ICtjYXNlIFJUMTcxMUhfREJHX0xPRzoNCj4gK3JldCA9
IHJ0MTcxMWhfbG9nX3Nob3coY2hpcCwgcyk7DQo+ICticmVhazsNCj4gK2Nhc2UgUlQxNzExSF9E
QkdfUkVHUzoNCj4gK3JldCA9IHJ0MTcxMWhfcmVnc19zaG93KGNoaXAsIHMpOw0KPiArYnJlYWs7
DQo+ICtjYXNlIFJUMTcxMUhfREJHX1JFR19BRERSOg0KPiArcmV0ID0gcnQxNzExaF9yZWdfYWRk
cl9zaG93KGNoaXAsIHMpOw0KPiArYnJlYWs7DQo+ICtjYXNlIFJUMTcxMUhfREJHX0RBVEE6DQo+
ICtyZXQgPSBydDE3MTFoX2RhdGFfc2hvdyhjaGlwLCBzKTsNCj4gK2JyZWFrOw0KPiArZGVmYXVs
dDoNCj4gK3JldCA9IC1FSU5WQUw7DQo+ICticmVhazsNCj4gK30NCj4gKw0KPiArbXV0ZXhfdW5s
b2NrKCZjaGlwLT5kYmdvcHNfbG9jayk7DQo+ICtyZXR1cm4gcmV0Ow0KPiArfQ0KPiArDQo+ICtz
dGF0aWMgaW50IHJ0MTcxMWhfZGJnX29wZW4oc3RydWN0IGlub2RlICppbm9kZSwgc3RydWN0IGZp
bGUgKmZpbGUpIHsNCj4gK2lmIChmaWxlLT5mX21vZGUgJiBGTU9ERV9SRUFEKQ0KPiArcmV0dXJu
IHNpbmdsZV9vcGVuKGZpbGUsIHJ0MTcxMWhfZGJnX3Nob3csIGlub2RlLT5pX3ByaXZhdGUpOw0K
PiArZmlsZS0+cHJpdmF0ZV9kYXRhID0gaW5vZGUtPmlfcHJpdmF0ZTsNCj4gK3JldHVybiAwOw0K
PiArfQ0KPiArDQo+ICtzdGF0aWMgaW50IGdldF9wYXJhbWV0ZXJzKGNoYXIgKmJ1ZiwgbG9uZyBp
bnQgKnBhcmFtMSwgaW50DQo+ICtudW1fb2ZfcGFyKSB7DQo+ICtjaGFyICp0b2tlbjsNCj4gK2lu
dCBiYXNlLCBjbnQ7DQo+ICsNCj4gK3Rva2VuID0gc3Ryc2VwKCZidWYsICIgIik7DQo+ICsNCj4g
K2ZvciAoY250ID0gMDsgY250IDwgbnVtX29mX3BhcjsgY250KyspIHsNCj4gK2lmICh0b2tlbiAh
PSBOVUxMKSB7DQo+ICtpZiAoKHRva2VuWzFdID09ICd4JykgfHwgKHRva2VuWzFdID09ICdYJykp
DQo+ICtiYXNlID0gMTY7DQo+ICtlbHNlDQo+ICtiYXNlID0gMTA7DQo+ICsNCj4gK2lmIChrc3Ry
dG91bCh0b2tlbiwgYmFzZSwgJnBhcmFtMVtjbnRdKSAhPSAwKQ0KPiArcmV0dXJuIC1FSU5WQUw7
DQo+ICsNCj4gK3Rva2VuID0gc3Ryc2VwKCZidWYsICIgIik7DQo+ICt9IGVsc2UNCj4gK3JldHVy
biAtRUlOVkFMOw0KPiArfQ0KPiArcmV0dXJuIDA7DQo+ICt9DQoNCldoYXQgaXMgdGhpcyBmdW5j
dGlvbiBkb2luZz8gIFdoYXQgaXMgeW91ciBkZWJ1Z2ZzIGZpbGVzIGZvcj8NCg0KVGhlcmUgYXJl
IDQgZGVidWcgZmlsZXMuDQpGaXJzdChsb2cpIGlzIGZvciBsb2dnaW5nIHdoaWNoIG5lZWRzIGEg
bG9jayBmb3IgbG9nIGJ1ZmZlci4gVGhlIHdheSB0byBsb2cgaXMgcmVmZXJlbmNlZCBmcm9tIGZ1
c2IzMDIgYW5kIHRjcG0uDQpTZWNvbmQocmVncykgaXMgdXNlZCB0byBkdW1wIGFsbCByZWdpc3Rl
ciBvZiBydDE3MTFoLg0KVGhpcmQocmVnX2FkZHIpJkZvcnRoKGRhdGEpIGFyZSB1c2VkIHRvIHdy
aXRlL3JlYWQgYSByZWdpc3RlciBzcGVjaWZpZWQgaW4gcmVnX2FkZHIuDQpUaGUgcmVhc29uIHRv
IGNyZWF0ZSB0aGVzZSBkZWJ1ZyBmaWxlcyBpcyB0byBtYWtlIGlzc3VlIHN1cHBvcnQgZWFzaWVy
Lg0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09DQoNCj4gKyNpZmRlZiBDT05GSUdfREVCVUdfRlMNCj4gK3N0cnVj
dCBkZW50cnkgKmRiZ2RpcjsNCj4gK3N0cnVjdCBydDE3MTFoX2RiZ19pbmZvIGRiZ19pbmZvW1JU
MTcxMUhfREJHX01BWF07DQo+ICtzdHJ1Y3QgZGVudHJ5ICpkYmdfZmlsZXNbUlQxNzExSF9EQkdf
TUFYXTsNCj4gK2ludCBkYmdfcmVnaWR4Ow0KPiArc3RydWN0IG11dGV4IGRiZ29wc19sb2NrOw0K
PiArLyogbG9jayBmb3IgbG9nIGJ1ZmZlciBhY2Nlc3MgKi8NCj4gK3N0cnVjdCBtdXRleCBsb2di
dWZmZXJfbG9jazsNCj4gK2ludCBsb2didWZmZXJfaGVhZDsNCj4gK2ludCBsb2didWZmZXJfdGFp
bDsNCj4gK3U4ICpsb2didWZmZXJbTE9HX0JVRkZFUl9FTlRSSUVTXTsNCj4gKyNlbmRpZiAvKiBD
T05GSUdfREVCVUdfRlMgKi8NCg0KVGhhdCdzIGEgbG90IG9mIHN0dWZmIGpzdXQgZm9yIGRlYnVn
ZnMuICBXaHkgZG8geW91IGNhcmUgYWJvdXQgI2RlZmluZSBhdCBhbGw/ICBUaGUgY29kZSBzaG91
bGQgbm90Lg0KDQpJcyB0aGUgc3VnZ2VzdGlvbiB0byByZW1vdmUgI2lmZGVmIENPTkZJR19ERUJV
R19GUz8NCg0KQW5kIGFub3RoZXIgMiBsb2Nrcz8gIEljaywgbm8uDQoNCmRiZ29wc19sb2NrIGlz
IHVzZWQgdG8gcHJldmVudCB1c2VyIGZyb20gYWNjZXNzaW5nIGRpZmZlcmVudCBkZWJ1ZyBmaWxl
cyBzaW11bHRhbmVvdXNseS4NCklzIHRoZSBzdWdnZXN0aW9uIHRvIHVzZSB0aGUgbG9jayBvZiB0
aGUgZm9sbG93aW5nIG9uZT8NCj4gKy8qIGxvY2sgZm9yIHNoYXJpbmcgY2hpcCBzdGF0ZXMgKi8N
Cj4gK3N0cnVjdCBtdXRleCBsb2NrOw0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCj4gK3NucHJpbnRmKGRp
cm5hbWUsIGxlbiArIDksICJydDE3MTFoLSVzIiwgZGV2X25hbWUoY2hpcC0+ZGV2KSk7DQo+ICtp
ZiAoIWNoaXAtPmRiZ2Rpcikgew0KPiArY2hpcC0+ZGJnZGlyID0gZGVidWdmc19jcmVhdGVfZGly
KGRpcm5hbWUsIE5VTEwpOw0KPiAraWYgKCFjaGlwLT5kYmdkaXIpDQo+ICtyZXR1cm4gLUVOT01F
TTsNCg0KTm8gbmVlZCB0byBldmVyIGNoZWNrIHRoZSByZXR1cm4gdmFsdWUgb2YgZGVidWdmc18g
Y2FsbHMsIHlvdSBzaG91bGQgbm90IGNhcmUgYW5kIGNhbiBhbHdheXMgdXNlIHRoZSB2YWx1ZSB0
byBhbnkgZnV0dXJlIGRlYnVnZnMgY2FsbHMsIGlmIHlvdSByZWFsbHkgbmVlZCBpdC4NCg0KSWYg
aXQgaXMgTlVMTCB3aXRob3V0IGNoZWNraW5nIGFuZCB3ZSB1c2UgaXQgaW4gZGVidWdmc19jcmVh
dGVfZmlsZSwgYWxsIHRoZSBkZWJ1ZyBmaWxlcyB3aWxsIGJlIGNyZWF0ZWQgaW4gdGhlIHJvb3Qg
b2YgdGhlIGRlYnVnZnMgZmlsZXN5c3RlbS4NCklzIHRoaXMgY29ycmVjdD8NCj09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PQ0KDQo+ICtmb3IgKGkgPSAwOyBpIDwgUlQxNzExSF9EQkdfTUFYOyBpKyspIHsNCj4gK2lu
Zm8gPSAmY2hpcC0+ZGJnX2luZm9baV07DQoNCnN0YXRpYyBhcnJheSBvZiBkZWJ1ZyBpbmZvPyAg
VGhhdCBmZWVscyBvZGQuDQoNCklzIHRoZSBzdWdnZXN0aW9uIHRvIHVzZSBwb2ludGVyIG9mIGFy
cmF5IGFuZCBkeW5hbWljYWxseSBhbGxvY2F0ZWQgaXQ/DQo9PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KTGlr
ZSBoZXJlLCB5b3UgZG9uJ3QgbmVlZCB0aGlzLCBhbmQgeW91IGRvbid0IG5lZWQgdG8gY2FyZSBh
Ym91dCB0aGUgcmV0dXJuIHZhbHVlLg0KDQo+ICtnb3RvIGVycjsNCj4gK30NCj4gK30NCj4gKw0K
PiArcmV0dXJuIDA7DQo+ICtlcnI6DQo+ICtkZWJ1Z2ZzX3JlbW92ZV9yZWN1cnNpdmUoY2hpcC0+
ZGJnZGlyKTsNCj4gK3JldHVybiByZXQ7DQoNCldoeSBkbyB5b3UgY2FyZSBhYm91dCBhbiBlcnJv
ciBoZXJlPyAgWW91ciBjb2RlIHNob3VsZCBub3QgZG8gYW55dGhpbmcgZGlmZmVyZW50IGlmIGRl
YnVnZnMgc3R1ZmYgZG9lcyBub3Qgd29yayBvciBpZiBpdCBkb2VzLiAgSXQncyBkZWJ1Z2dpbmcg
b25seS4NCk9rLCB0aGlzIHdpbGwgYmUgcmVtb3ZlZC4NCj09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpCZXN0
IFJlZ2FyZHMsDQoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KU2h1LUZhbiBMZWUNClJp
Y2h0ZWsgVGVjaG5vbG9neSBDb3Jwb3JhdGlvbg0KVEVMOiArODg2LTMtNTUyNjc4OSAjMjM1OQ0K
RkFYOiArODg2LTMtNTUyNjYxMg0KKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCg0KLS0t
LS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCkZyb206IEdyZWcgS0ggW21haWx0bzpncmVnQGtyb2Fo
LmNvbV0NClNlbnQ6IFdlZG5lc2RheSwgSmFudWFyeSAxNywgMjAxOCA5OjQyIFBNDQpUbzogU2h1
RmFuTGVlDQpDYzogaGVpa2tpLmtyb2dlcnVzQGxpbnV4LmludGVsLmNvbTsgY3lfaHVhbmcotsCx
0q3sKTsgc2h1ZmFuX2xlZSin9a7RpnwpOyBsaW51eC1rZXJuZWxAdmdlci5rZXJuZWwub3JnOyBs
aW51eC11c2JAdmdlci5rZXJuZWwub3JnDQpTdWJqZWN0OiBSZTogW1BBVENIXSBVU0IgVFlQRUM6
IFJUMTcxMUggVHlwZS1DIENoaXAgRHJpdmVyDQoNCk9uIFdlZCwgSmFuIDEwLCAyMDE4IGF0IDAy
OjU5OjEyUE0gKzA4MDAsIFNodUZhbkxlZSB3cm90ZToNCj4gRnJvbTogU2h1RmFuTGVlIDxzaHVm
YW5fbGVlQHJpY2h0ZWsuY29tPg0KPg0KPiBSaWNodGVrIFJUMTcxMUggVHlwZS1DIGNoaXAgZHJp
dmVyIHRoYXQgd29ya3Mgd2l0aCBUeXBlLUMgUG9ydA0KPiBDb250cm9sbGVyIE1hbmFnZXIgdG8g
cHJvdmlkZSBVU0IgUEQgYW5kIFVTQiBUeXBlLUMgZnVuY3Rpb25hbGl0aWVzLg0KPg0KPiBTaWdu
ZWQtb2ZmLWJ5OiBTaHVGYW5MZWUgPHNodWZhbl9sZWVAcmljaHRlay5jb20+DQoNCk1pbm9yIHJl
dmlldyBvZiB5b3VyIG1haW4gc3RydWN0dXJlIGFuZCB5b3VyIGRlYnVnZnMgY29kZSBhbmQgb3Ro
ZXIgc3R1ZmYsIGFsbCBvZiB3aGljaCBuZWVkIHdvcms6DQoNCj4gLS0tDQo+ICAuLi4vZGV2aWNl
dHJlZS9iaW5kaW5ncy91c2IvcmljaHRlayxydDE3MTFoLnR4dCAgICB8ICAgMzggKw0KPiAgYXJj
aC9hcm02NC9ib290L2R0cy9oaXNpbGljb24vcnQxNzExaC5kdHNpICAgICAgICAgfCAgIDExICsN
Cj4gIGRyaXZlcnMvdXNiL3R5cGVjL0tjb25maWcgICAgICAgICAgICAgICAgICAgICAgICAgIHwg
ICAgMiArDQo+ICBkcml2ZXJzL3VzYi90eXBlYy9NYWtlZmlsZSAgICAgICAgICAgICAgICAgICAg
ICAgICB8ICAgIDEgKw0KPiAgZHJpdmVycy91c2IvdHlwZWMvcnQxNzExaC9LY29uZmlnICAgICAg
ICAgICAgICAgICAgfCAgICA3ICsNCj4gIGRyaXZlcnMvdXNiL3R5cGVjL3J0MTcxMWgvTWFrZWZp
bGUgICAgICAgICAgICAgICAgIHwgICAgMiArDQo+ICBkcml2ZXJzL3VzYi90eXBlYy9ydDE3MTFo
L3J0MTcxMWguYyAgICAgICAgICAgICAgICB8IDIyNDEgKysrKysrKysrKysrKysrKysrKysNCj4g
IGRyaXZlcnMvdXNiL3R5cGVjL3J0MTcxMWgvcnQxNzExaC5oICAgICAgICAgICAgICAgIHwgIDMw
MCArKysNCj4gIDggZmlsZXMgY2hhbmdlZCwgMjYwMiBpbnNlcnRpb25zKCspDQo+ICBjcmVhdGUg
bW9kZSAxMDA2NDQNCj4gRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdzL3VzYi9yaWNo
dGVrLHJ0MTcxMWgudHh0DQo+ICBjcmVhdGUgbW9kZSAxMDA2NDQgYXJjaC9hcm02NC9ib290L2R0
cy9oaXNpbGljb24vcnQxNzExaC5kdHNpDQo+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy91
c2IvdHlwZWMvcnQxNzExaC9LY29uZmlnICBjcmVhdGUgbW9kZQ0KPiAxMDA2NDQgZHJpdmVycy91
c2IvdHlwZWMvcnQxNzExaC9NYWtlZmlsZQ0KPiAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMv
dXNiL3R5cGVjL3J0MTcxMWgvcnQxNzExaC5jDQo+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVy
cy91c2IvdHlwZWMvcnQxNzExaC9ydDE3MTFoLmgNCj4NCj4gZGlmZiAtLWdpdCBhL0RvY3VtZW50
YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy91c2IvcmljaHRlayxydDE3MTFoLnR4dA0KPiBiL0Rv
Y3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy91c2IvcmljaHRlayxydDE3MTFoLnR4dA0K
PiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiBpbmRleCAwMDAwMDAwLi5jMjgyOTljDQo+IC0tLSAv
ZGV2L251bGwNCj4gKysrIGIvRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdzL3VzYi9y
aWNodGVrLHJ0MTcxMWgudHh0DQo+IEBAIC0wLDAgKzEsMzggQEANCj4gK1JpY2h0ZWsgUlQxNzEx
SCBUeXBlLUMgUG9ydCBDb250cm9sbGVyLg0KPiArDQo+ICtSZXF1aXJlZCBwcm9wZXJ0aWVzOg0K
PiArLSBjb21wYXRpYmxlIDogTXVzdCBiZSAicmljaHRlayx0eXBlY19ydDE3MTFoIjsNCj4gKy0g
cmVnIDogTXVzdCBiZSAweDRlLCBpdCdzIGRlZmF1bHQgc2xhdmUgYWRkcmVzcyBvZiBSVDE3MTFI
Lg0KPiArLSBydCxpbnRyX2dwaW8gOiBJUlEgR1BJTyBwaW4gdGhhdCdzIGNvbm5lY3RlZCB0byBS
VDE3MTFIIGludGVycnVwdC4NCj4gKw0KPiArT3B0aW9uYWwgbm9kZToNCj4gKy0gcnQsbmFtZSA6
IE5hbWUgdXNlZCBmb3IgcmVnaXN0ZXJpbmcgSVJRIGFuZCBjcmVhdGluZyBrdGhyZWFkLg0KPiAr
ICAgIElmIHRoaXMgcHJvcGVydHkgaXMgbm90IHNwZWNpZmllZCwgImRlZmF1bHQiIHdpbGwgYmUg
YXBwbGllZC4NCj4gKy0gcnQsZGVmX3JvbGUgOiBEZWZhdWx0IHBvcnQgcm9sZSAoVFlQRUNfU0lO
SygwKSBvciBUWVBFQ19TT1VSQ0UoMSkpLg0KPiArU2V0IHRvIFRZUEVDX05PX1BSRUZFUlJFRF9S
T0xFKC0xKSBpZiBubyBkZWZhdWx0IHJvbGUuDQo+ICtJZiB0aGlzIHByb3BlcnR5IGlzIG5vdCBz
cGVjaWZpZWQsIFRZUEVDX1NJTksgd2lsbCBiZSBhcHBsaWVkLg0KPiArLSBydCxwb3J0X3R5cGUg
OiBQb3J0IHR5cGUgKFRZUEVDX1BPUlRfREZQKDApLCBUWVBFQ19QT1JUX1VGUCgxKSwNCj4gKyBv
ciBUWVBFQ19QT1JUX0RSUCgyKSkuIElmIHRoaXMgcHJvcGVydHkgaXMgbm90IHNwZWNpZmllZCwN
Cj4gKyBUWVBFQ19QT1JUX0RSUCB3aWxsIGJlIGFwcGxpZWQuDQo+ICstIHJ0LG1heF9zbmtfbXYg
OiBNYXhpbXVtIGFjY2VwdGFibGUgc2luayB2b2x0YWdlIGluIG1WLg0KPiArICBJZiB0aGlzIHBy
b3BlcnR5IGlzIG5vdCBzcGVjaWZpZWQsIDUwMDBtViB3aWxsIGJlIGFwcGxpZWQuDQo+ICstIHJ0
LG1heF9zbmtfbWEgOiBNYXhpbXVtIHNpbmsgY3VycmVudCBpbiBtQS4NCj4gKyAgSWYgdGhpcyBw
cm9wZXJ0eSBpcyBub3Qgc3BlY2lmaWVkLCAzMDAwbUEgd2lsbCBiZSBhcHBsaWVkLg0KPiArLSBy
dCxtYXhfc25rX213IDogTWF4aW11bSByZXF1aXJlZCBzaW5rIHBvd2VyIGluIG1XLg0KPiArICBJ
ZiB0aGlzIHByb3BlcnR5IGlzIG5vdCBzcGVjaWZpZWQsIDE1MDAwbVcgd2lsbCBiZSBhcHBsaWVk
Lg0KPiArLSBydCxvcGVyYXRpbmdfc25rX213IDogUmVxdWlyZWQgb3BlcmF0aW5nIHNpbmsgcG93
ZXIgaW4gbVcuDQo+ICtJZiB0aGlzIHByb3BlcnR5IGlzIG5vdCBzcGVjaWZpZWQsDQo+ICsyNTAw
bVcgd2lsbCBiZSBhcHBsaWVkLg0KPiArLSBydCx0cnlfcm9sZV9odyA6IFRydWUgaWYgdHJ5LntT
cmMsU25rfSBpcyBpbXBsZW1lbnRlZCBpbiBoYXJkd2FyZS4NCj4gKyAgIElmIHRoaXMgcHJvcGVy
dHkgaXMgbm90IHNwZWNpZmllZCwgRmFsc2Ugd2lsbCBiZSBhcHBsaWVkLg0KPiArDQo+ICtFeGFt
cGxlOg0KPiArcnQxNzExaEA0ZSB7DQo+ICtzdGF0dXMgPSAib2siOw0KPiArY29tcGF0aWJsZSA9
ICJyaWNodGVrLHR5cGVjX3J0MTcxMWgiOw0KPiArcmVnID0gPDB4NGU+Ow0KPiArcnQsaW50cl9n
cGlvID0gPCZncGlvMjYgMCAweDA+Ow0KPiArcnQsbmFtZSA9ICJydDE3MTFoIjsNCj4gK3J0LHBv
cnRfdHlwZSA9IDwyPjsgLyogMDogREZQLCAxOiBVRlAsIDI6IERSUCAqLw0KPiArcnQsZGVmX3Jv
bGUgPSA8MD47IC8qIDA6IFNOSywgMTogU1JDLCAtMTogVFlQRUNfTk9fUFJFRkVSUkVEX1JPTEUg
Ki8NCj4gK307DQoNCmR0cyBzdHVmZiBuZWVkcyB0byBhbHdheXMgYmUgaW4gYSBzZXBhcmF0ZSBm
aWxlIHNvIHRoZSBEVCBtYWludGFpbmVycyBjYW4gcmV2aWV3L2FjayBpdC4gIFNwbGl0IHRoaXMg
cGF0Y2ggdXAgaW50byBzbWFsbGVyIHBpZWNlcyBwbGVhc2UuDQoNCg0KPiBkaWZmIC0tZ2l0IGEv
YXJjaC9hcm02NC9ib290L2R0cy9oaXNpbGljb24vcnQxNzExaC5kdHNpDQo+IGIvYXJjaC9hcm02
NC9ib290L2R0cy9oaXNpbGljb24vcnQxNzExaC5kdHNpDQo+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0
DQo+IGluZGV4IDAwMDAwMDAuLjQxOTZjYzANCj4gLS0tIC9kZXYvbnVsbA0KPiArKysgYi9hcmNo
L2FybTY0L2Jvb3QvZHRzL2hpc2lsaWNvbi9ydDE3MTFoLmR0c2kNCj4gQEAgLTAsMCArMSwxMSBA
QA0KPiArJmkyYzcgew0KPiArcnQxNzExaEA0ZSB7DQo+ICtzdGF0dXMgPSAib2siOw0KPiArY29t
cGF0aWJsZSA9ICJyaWNodGVrLHR5cGVjX3J0MTcxMWgiOw0KPiArcmVnID0gPDB4NGU+Ow0KPiAr
cnQsaW50cl9ncGlvID0gPCZncGlvMjYgMCAweDA+Ow0KPiArcnQsbmFtZSA9ICJydDE3MTFoIjsN
Cj4gK3J0LHBvcnRfdHlwZSA9IDwyPjsgLyogMDogREZQLCAxOiBVRlAsIDI6IERSUCAqLw0KPiAr
cnQsZGVmX3JvbGUgPSA8MD47IC8qIDA6IFNOSywgMTogU1JDICovDQo+ICt9Ow0KPiArfTsNCj4g
ZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL3R5cGVjL0tjb25maWcgYi9kcml2ZXJzL3VzYi90eXBl
Yy9LY29uZmlnDQo+IGluZGV4IGJjYjI3NDQuLjdiZWRlMGIgMTAwNjQ0DQo+IC0tLSBhL2RyaXZl
cnMvdXNiL3R5cGVjL0tjb25maWcNCj4gKysrIGIvZHJpdmVycy91c2IvdHlwZWMvS2NvbmZpZw0K
PiBAQCAtNTYsNiArNTYsOCBAQCBpZiBUWVBFQ19UQ1BNDQo+DQo+ICBzb3VyY2UgImRyaXZlcnMv
dXNiL3R5cGVjL2Z1c2IzMDIvS2NvbmZpZyINCj4NCj4gK3NvdXJjZSAiZHJpdmVycy91c2IvdHlw
ZWMvcnQxNzExaC9LY29uZmlnIg0KPiArDQo+ICBjb25maWcgVFlQRUNfV0NPVkUNCj4gIHRyaXN0
YXRlICJJbnRlbCBXaGlza2V5Q292ZSBQTUlDIFVTQiBUeXBlLUMgUEhZIGRyaXZlciINCj4gIGRl
cGVuZHMgb24gQUNQSQ0KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvdHlwZWMvTWFrZWZpbGUg
Yi9kcml2ZXJzL3VzYi90eXBlYy9NYWtlZmlsZQ0KPiBpbmRleCBiYjMxMzhhLi5lM2FhZjNjIDEw
MDY0NA0KPiAtLS0gYS9kcml2ZXJzL3VzYi90eXBlYy9NYWtlZmlsZQ0KPiArKysgYi9kcml2ZXJz
L3VzYi90eXBlYy9NYWtlZmlsZQ0KPiBAQCAtMiw2ICsyLDcgQEANCj4gIG9iai0kKENPTkZJR19U
WVBFQykrPSB0eXBlYy5vDQo+ICBvYmotJChDT05GSUdfVFlQRUNfVENQTSkrPSB0Y3BtLm8NCj4g
IG9iai15Kz0gZnVzYjMwMi8NCj4gK29iai0kKENPTkZJR19UWVBFQ19SVDE3MTFIKSs9IHJ0MTcx
MWgvDQoNCldoeSBkbyB5b3UgbmVlZCBhIHdob2xlIGRpcmVjdG9yeSBmb3Igb25lIGZpbGU/DQoN
Cg0KPiAgb2JqLSQoQ09ORklHX1RZUEVDX1dDT1ZFKSs9IHR5cGVjX3djb3ZlLm8NCj4gIG9iai0k
KENPTkZJR19UWVBFQ19VQ1NJKSs9IHVjc2kvDQo+ICBvYmotJChDT05GSUdfVFlQRUNfVFBTNjU5
OFgpKz0gdHBzNjU5OHgubw0KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvdHlwZWMvcnQxNzEx
aC9LY29uZmlnDQo+IGIvZHJpdmVycy91c2IvdHlwZWMvcnQxNzExaC9LY29uZmlnDQo+IG5ldyBm
aWxlIG1vZGUgMTAwNjQ0DQo+IGluZGV4IDAwMDAwMDAuLjJmYmZmZjUNCj4gLS0tIC9kZXYvbnVs
bA0KPiArKysgYi9kcml2ZXJzL3VzYi90eXBlYy9ydDE3MTFoL0tjb25maWcNCj4gQEAgLTAsMCAr
MSw3IEBADQo+ICtjb25maWcgVFlQRUNfUlQxNzExSA0KPiArdHJpc3RhdGUgIlJpY2h0ZWsgUlQx
NzExSCBUeXBlLUMgY2hpcCBkcml2ZXIiDQo+ICtkZXBlbmRzIG9uIEkyQyAmJiBQT1dFUl9TVVBQ
TFkNCj4gK2hlbHANCj4gKyAgVGhlIFJpY2h0ZWsgUlQxNzExSCAgIFR5cGUtQyBjaGlwIGRyaXZl
ciB0aGF0IHdvcmtzIHdpdGgNCj4gKyAgVHlwZS1DIFBvcnQgQ29udHJvbGxlciBNYW5hZ2VyIHRv
IHByb3ZpZGUgVVNCIFBEIGFuZCBVU0INCj4gKyAgVHlwZS1DIGZ1bmN0aW9uYWxpdGllcy4NCj4g
ZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL3R5cGVjL3J0MTcxMWgvTWFrZWZpbGUNCj4gYi9kcml2
ZXJzL3VzYi90eXBlYy9ydDE3MTFoL01ha2VmaWxlDQo+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+
IGluZGV4IDAwMDAwMDAuLjVmYWI4YWUNCj4gLS0tIC9kZXYvbnVsbA0KPiArKysgYi9kcml2ZXJz
L3VzYi90eXBlYy9ydDE3MTFoL01ha2VmaWxlDQo+IEBAIC0wLDAgKzEsMiBAQA0KPiArIyBTUERY
LUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIuMA0KPiArb2JqLSQoQ09ORklHX1RZUEVDX1JUMTcx
MUgpKz0gcnQxNzExaC5vDQo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3VzYi90eXBlYy9ydDE3MTFo
L3J0MTcxMWguYw0KPiBiL2RyaXZlcnMvdXNiL3R5cGVjL3J0MTcxMWgvcnQxNzExaC5jDQo+IG5l
dyBmaWxlIG1vZGUgMTAwNjQ0DQo+IGluZGV4IDAwMDAwMDAuLjFhZWYzZTgNCj4gLS0tIC9kZXYv
bnVsbA0KPiArKysgYi9kcml2ZXJzL3VzYi90eXBlYy9ydDE3MTFoL3J0MTcxMWguYw0KPiBAQCAt
MCwwICsxLDIyNDEgQEANCj4gKy8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wKw0K
PiArLyoNCj4gKyAqIENvcHlyaWdodCAyMDE3IFJpY2h0ZWsgVGVjaG5vbG9naCBDb3JwLg0KPiAr
ICoNCj4gKyAqIFJpY2h0ZWsgUlQxNzExSCBUeXBlLUMgQ2hpcCBEcml2ZXIgICovDQo+ICsNCj4g
KyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9rZXJuZWwuaD4N
Cj4gKyNpbmNsdWRlIDxsaW51eC92ZXJzaW9uLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvZXJyLmg+
DQo+ICsjaW5jbHVkZSA8bGludXgvZGVidWdmcy5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L3BtX3J1
bnRpbWUuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9pMmMuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC91
c2IvdHlwZWMuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC91c2IvdGNwbS5oPg0KPiArI2luY2x1ZGUg
PGxpbnV4L3VzYi9wZC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L29mX2dwaW8uaD4NCj4gKyNpbmNs
dWRlIDxsaW51eC9vZi5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2RlbGF5Lmg+DQo+ICsjaW5jbHVk
ZSA8bGludXgvaW50ZXJydXB0Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgvcmVndWxhdG9yL2NvbnN1
bWVyLmg+ICNpbmNsdWRlIDxsaW51eC9wb3dlcl9zdXBwbHkuaD4NCj4gKyNpbmNsdWRlIDxsaW51
eC9leHRjb24uaD4gI2luY2x1ZGUgPGxpbnV4L3dvcmtxdWV1ZS5oPiAjaW5jbHVkZQ0KPiArPGxp
bnV4L2t0aHJlYWQuaD4gI2luY2x1ZGUgPGxpbnV4L2NwdS5oPiAjaW5jbHVkZQ0KPiArPGxpbnV4
L2FsYXJtdGltZXIuaD4gI2luY2x1ZGUgPGxpbnV4L3NjaGVkL2Nsb2NrLmg+ICNpbmNsdWRlDQo+
ICs8dWFwaS9saW51eC9zY2hlZC90eXBlcy5oPg0KDQpUaGlzIGxhc3QgI2luY2x1ZGUgc2hvdWxk
IG5vdCBiZSBuZWVkZWQuICBJZiBpdCBkb2VzLCB5b3UgYXJlIGRvaW5nIHNvbWV0aGluZyByZWFs
bHkgd3JvbmcuLi4NCg0KPiArDQo+ICsjaW5jbHVkZSAicnQxNzExaC5oIg0KDQpXaHkgYSAuaCBm
aWxlIGZvciBhIHNpbmdsZSAuYyBmaWxlPw0KDQo+ICsNCj4gKyNkZWZpbmUgUlQxNzExSF9EUlZf
VkVSU0lPTiIxLjAuMyINCg0KV2hlbiBjb2RlIGlzIGluIHRoZSBrZXJuZWwgdHJlZSwgdmVyc2lv
bnMgbWVhbiBub3RoaW5nLCB5b3Ugd2lsbCBub3RlIHRoYXQgbm8gb3RoZXIgVVNCIGRyaXZlciBo
YXMgdGhlbSwgcmlnaHQ/ICBQbGVhc2UgcmVtb3ZlLg0KDQoNCj4gKw0KPiArI2RlZmluZSBMT0df
QlVGRkVSX0VOVFJJRVMxMDI0DQo+ICsjZGVmaW5lIExPR19CVUZGRVJfRU5UUllfU0laRTEyOCAv
KiAxMjggY2hhciBwZXIgbGluZSAqLw0KPiArDQo+ICtlbnVtIHsNCj4gK1JUMTcxMUhfREJHX0xP
RyA9IDAsDQo+ICtSVDE3MTFIX0RCR19SRUdTLA0KPiArUlQxNzExSF9EQkdfUkVHX0FERFIsDQo+
ICtSVDE3MTFIX0RCR19EQVRBLA0KPiArUlQxNzExSF9EQkdfTUFYLA0KPiArfTsNCj4gKw0KPiAr
c3RydWN0IHJ0MTcxMWhfZGJnX2luZm8gew0KPiArc3RydWN0IHJ0MTcxMWhfY2hpcCAqY2hpcDsN
Cj4gK2ludCBpZDsNCj4gK307DQo+ICsNCj4gKw0KPiArc3RydWN0IHJ0MTcxMWhfY2hpcCB7DQo+
ICtzdHJ1Y3QgaTJjX2NsaWVudCAqaTJjOw0KPiArc3RydWN0IGRldmljZSAqZGV2Ow0KPiArdWlu
dDE2X3QgZGlkOw0KDQprZXJuZWwgdHlwZXMgYXJlIHUxNiwgdTMyLCB1OCwgYW5kIHRoZSBsaWtl
LCBub3QgdWludDE2X3QsIHRob3NlIGFyZSBmb3IgdXNlcnNwYWNlIGNvZGUgb25seS4NCg0KWWVh
aCwgb3RoZXIgZHJpdmVycyBkbyBpdCwgYnV0IHlvdSBzaG91bGRuJ3QgOikNCg0KDQo+ICtpbnQg
aXJxX2dwaW87DQo+ICtpbnQgaXJxOw0KPiArY2hhciAqbmFtZTsNCj4gK3N0cnVjdCB0Y3BjX2Rl
diB0Y3BjX2RldjsNCj4gK3N0cnVjdCB0Y3BjX2NvbmZpZyB0Y3BjX2NmZzsNCj4gK3N0cnVjdCB0
Y3BtX3BvcnQgKnRjcG1fcG9ydDsNCj4gK3N0cnVjdCByZWd1bGF0b3IgKnZidXM7DQo+ICtzdHJ1
Y3QgZXh0Y29uX2RldiAqZXh0Y29uOw0KPiArDQo+ICsvKiBJUlEgKi8NCj4gK3N0cnVjdCBrdGhy
ZWFkX3dvcmtlciBpcnFfd29ya2VyOw0KPiArc3RydWN0IGt0aHJlYWRfd29yayBpcnFfd29yazsN
Cj4gK3N0cnVjdCB0YXNrX3N0cnVjdCAqaXJxX3dvcmtlcl90YXNrOw0KDQozIHRoaW5ncyBmb3Ig
YW4gaXJxIGhhbmRsZXI/ICBUaGF0IGZlZWxzIHdyb25nLg0KDQo+ICthdG9taWNfdCBwb2xsX2Nv
dW50Ow0KDQpMaWtlIEkgc2FpZCBiZWZvcmUsIHdoeSBpcyB0aGlzIGFuIGF0b21pYz8NCg0KPiAr
c3RydWN0IGRlbGF5ZWRfd29yayBwb2xsX3dvcms7DQo+ICsNCj4gKy8qIExQTSAqLw0KPiArc3Ry
dWN0IGRlbGF5ZWRfd29yayB3YWtldXBfd29yazsNCj4gK3N0cnVjdCBhbGFybSB3YWtldXBfdGlt
ZXI7DQo+ICtzdHJ1Y3QgbXV0ZXggd2FrZXVwX2xvY2s7DQo+ICtlbnVtIHR5cGVjX2NjX3B1bGwg
bHBtX3B1bGw7DQo+ICtib29sIHdha2V1cF9vbmNlOw0KPiArYm9vbCBsb3dfcnBfZHV0eV9jbnRk
b3duOw0KPiArYm9vbCBjYWJsZV9vbmx5Ow0KPiArYm9vbCBscG07DQo+ICsNCj4gKy8qIEkyQyAq
Lw0KPiArYXRvbWljX3QgaTJjX2J1c3k7DQo+ICthdG9taWNfdCBwbV9zdXNwZW5kOw0KDQpXaHkg
YXJlIHRoZXNlIGF0b21pYz8gIFlvdSBrbm93IHRoYXQgZG9lc24ndCBtZWFuIHRoZXkgZG8gbm90
IG5lZWQgbG9ja2luZywgcmlnaHQ/DQoNCj4gKw0KPiArLyogcHN5ICsgcHN5IHN0YXR1cyAqLw0K
PiArc3RydWN0IHBvd2VyX3N1cHBseSAqcHN5Ow0KPiArdTMyIGN1cnJlbnRfbGltaXQ7DQo+ICt1
MzIgc3VwcGx5X3ZvbHRhZ2U7DQo+ICsNCj4gKy8qIGxvY2sgZm9yIHNoYXJpbmcgY2hpcCBzdGF0
ZXMgKi8NCj4gK3N0cnVjdCBtdXRleCBsb2NrOw0KDQpIb3cgbWFueSBsb2NrcyBkbyB5b3UgaGF2
ZSBpbiB0aGlzIHN0cnVjdHVyZT8gIFlvdSBzaG91bGQgb25seSBuZWVkIDEuDQoNCj4gKw0KPiAr
LyogcG9ydCBzdGF0dXMgKi8NCj4gK2Jvb2wgdmNvbm5fb247DQo+ICtib29sIHZidXNfb247DQo+
ICtib29sIGNoYXJnZV9vbjsNCj4gK2Jvb2wgdmJ1c19wcmVzZW50Ow0KPiArZW51bSB0eXBlY19j
Y19wb2xhcml0eSBwb2xhcml0eTsNCj4gK2VudW0gdHlwZWNfY2Nfc3RhdHVzIGNjMTsNCj4gK2Vu
dW0gdHlwZWNfY2Nfc3RhdHVzIGNjMjsNCj4gK2VudW0gdHlwZWNfcm9sZSBwd3Jfcm9sZTsNCj4g
K2Jvb2wgZHJwX3RvZ2dsaW5nOw0KPiArDQo+ICsjaWZkZWYgQ09ORklHX0RFQlVHX0ZTDQo+ICtz
dHJ1Y3QgZGVudHJ5ICpkYmdkaXI7DQo+ICtzdHJ1Y3QgcnQxNzExaF9kYmdfaW5mbyBkYmdfaW5m
b1tSVDE3MTFIX0RCR19NQVhdOw0KPiArc3RydWN0IGRlbnRyeSAqZGJnX2ZpbGVzW1JUMTcxMUhf
REJHX01BWF07DQo+ICtpbnQgZGJnX3JlZ2lkeDsNCj4gK3N0cnVjdCBtdXRleCBkYmdvcHNfbG9j
azsNCj4gKy8qIGxvY2sgZm9yIGxvZyBidWZmZXIgYWNjZXNzICovDQo+ICtzdHJ1Y3QgbXV0ZXgg
bG9nYnVmZmVyX2xvY2s7DQo+ICtpbnQgbG9nYnVmZmVyX2hlYWQ7DQo+ICtpbnQgbG9nYnVmZmVy
X3RhaWw7DQo+ICt1OCAqbG9nYnVmZmVyW0xPR19CVUZGRVJfRU5UUklFU107DQo+ICsjZW5kaWYg
LyogQ09ORklHX0RFQlVHX0ZTICovDQoNClRoYXQncyBhIGxvdCBvZiBzdHVmZiBqc3V0IGZvciBk
ZWJ1Z2ZzLiAgV2h5IGRvIHlvdSBjYXJlIGFib3V0ICNkZWZpbmUgYXQgYWxsPyAgVGhlIGNvZGUg
c2hvdWxkIG5vdC4NCg0KQW5kIGFub3RoZXIgMiBsb2Nrcz8gIEljaywgbm8uDQoNCg0KPiArfTsN
Cj4gKw0KPiArLyoNCj4gKyAqIExvZ2dpbmcgJiBkZWJ1Z2dpbmcNCj4gKyAqLw0KPiArDQo+ICsj
aWZkZWYgQ09ORklHX0RFQlVHX0ZTDQo+ICsNCj4gK3N0YXRpYyBpbnQgcnQxNzExaF9yZWdfYmxv
Y2tfcmVhZChzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLCB1aW50OF90IHJlZywNCj4gK2ludCBs
ZW4sIHVpbnQ4X3QgKmRhdGEpOw0KPiArc3RhdGljIGludCBydDE3MTFoX3JlZ19ibG9ja193cml0
ZShzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLCB1aW50OF90IHJlZywNCj4gK2ludCBsZW4sIGNv
bnN0IHVpbnQ4X3QgKmRhdGEpOw0KPiArDQo+ICtzdHJ1Y3QgcmVnX2Rlc2Mgew0KPiArdWludDhf
dCBhZGRyOw0KPiArdWludDhfdCBzaXplOw0KPiArfTsNCj4gKyNkZWZpbmUgREVDTF9SRUcoX2Fk
ZHIsIF9zaXplKSB7LmFkZHIgPSBfYWRkciwgLnNpemUgPSBfc2l6ZX0NCj4gKw0KPiArc3RhdGlj
IHN0cnVjdCByZWdfZGVzYyBydDE3MTFoX3JlZ19kZXNjW10gPSB7DQo+ICtERUNMX1JFRyhSVDE3
MTFIX1JFR19WSUQsIDIpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfUElELCAyKSwNCj4gK0RF
Q0xfUkVHKFJUMTcxMUhfUkVHX0RJRCwgMiksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19UWVBF
Q19SRVYsIDIpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfUERfUkVWLCAyKSwNCj4gK0RFQ0xf
UkVHKFJUMTcxMUhfUkVHX1BESUZfUkVWLCAyKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0FM
RVJULCAyKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0FMRVJUX01BU0ssIDIpLA0KPiArREVD
TF9SRUcoUlQxNzExSF9SRUdfUE9XRVJfU1RBVFVTX01BU0ssIDEpLA0KPiArREVDTF9SRUcoUlQx
NzExSF9SRUdfRkFVTFRfU1RBVFVTX01BU0ssIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdf
VENQQ19DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JPTEVfQ1RSTCwgMSksDQo+
ICtERUNMX1JFRyhSVDE3MTFIX1JFR19GQVVMVF9DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcx
MUhfUkVHX1BPV0VSX0NUUkwsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfQ0NfU1RBVFVT
LCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1BPV0VSX1NUQVRVUywgMSksDQo+ICtERUNM
X1JFRyhSVDE3MTFIX1JFR19GQVVMVF9TVEFUVVMsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9S
RUdfQ09NTUFORCwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19NU0dfSERSX0lORk8sIDEp
LA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfUlhfREVURUNULCAxKSwNCj4gK0RFQ0xfUkVHKFJU
MTcxMUhfUkVHX1JYX0JZVEVfQ05ULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JYX0JV
Rl9GUkFNRV9UWVBFLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JYX0hEUiwgMiksDQo+
ICtERUNMX1JFRyhSVDE3MTFIX1JFR19SWF9EQVRBLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhf
UkVHX1RSQU5TTUlULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1RYX0JZVEVfQ05ULCAx
KSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1RYX0hEUiwgMiksDQo+ICtERUNMX1JFRyhSVDE3
MTFIX1JFR19UWF9EQVRBLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0NMS19DVFJMMiwg
MSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19DTEtfQ1RSTDMsIDEpLA0KPiArREVDTF9SRUco
UlQxNzExSF9SRUdfQk1DX0NUUkwsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfQk1DSU9f
UlhEWlNFTCwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19WQ09OTl9DTElNSVRFTiwgMSks
DQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19SVF9TVEFUVVMsIDEpLA0KPiArREVDTF9SRUcoUlQx
NzExSF9SRUdfUlRfSU5ULCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1JUX01BU0ssIDEp
LA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfSURMRV9DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJU
MTcxMUhfUkVHX0lOVFJTVF9DVFJMLCAxKSwNCj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX1dBVENI
RE9HX0NUUkwsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfSTJDUlNUX0NUUkwsIDEpLA0K
PiArREVDTF9SRUcoUlQxNzExSF9SRUdfU1dSRVNFVCwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFI
X1JFR19UVENQQ19GSUxURVIsIDEpLA0KPiArREVDTF9SRUcoUlQxNzExSF9SRUdfRFJQX1RPR0dM
RV9DWUNMRSwgMSksDQo+ICtERUNMX1JFRyhSVDE3MTFIX1JFR19EUlBfRFVUWV9DVFJMLCAxKSwN
Cj4gK0RFQ0xfUkVHKFJUMTcxMUhfUkVHX0JNQ0lPX1JYRFpFTiwgMSksIH07DQo+ICsNCj4gK3N0
YXRpYyBjb25zdCBjaGFyICpydDE3MTFoX2RiZ19maWxlbmFtZVtSVDE3MTFIX0RCR19NQVhdID0g
ew0KPiArImxvZyIsICJyZWdzIiwgInJlZ19hZGRyIiwgImRhdGEiLA0KPiArfTsNCj4gKw0KPiAr
c3RhdGljIGJvb2wgcnQxNzExaF9sb2dfZnVsbChzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwKSB7
DQo+ICtyZXR1cm4gY2hpcC0+bG9nYnVmZmVyX3RhaWwgPT0NCj4gKyhjaGlwLT5sb2didWZmZXJf
aGVhZCArIDEpICUgTE9HX0JVRkZFUl9FTlRSSUVTOyB9DQo+ICsNCj4gK3N0YXRpYyB2b2lkIF9y
dDE3MTFoX2xvZyhzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLCBjb25zdCBjaGFyICpmbXQsDQo+
ICsgdmFfbGlzdCBhcmdzKQ0KPiArew0KPiArY2hhciB0bXBidWZmZXJbTE9HX0JVRkZFUl9FTlRS
WV9TSVpFXTsNCj4gK3U2NCB0c19uc2VjID0gbG9jYWxfY2xvY2soKTsNCj4gK3Vuc2lnbmVkIGxv
bmcgcmVtX25zZWM7DQo+ICsNCj4gK2lmICghY2hpcC0+bG9nYnVmZmVyW2NoaXAtPmxvZ2J1ZmZl
cl9oZWFkXSkgew0KPiArY2hpcC0+bG9nYnVmZmVyW2NoaXAtPmxvZ2J1ZmZlcl9oZWFkXSA9DQo+
ICtkZXZtX2t6YWxsb2MoY2hpcC0+ZGV2LCBMT0dfQlVGRkVSX0VOVFJZX1NJWkUsIEdGUF9LRVJO
RUwpOw0KPiAraWYgKCFjaGlwLT5sb2didWZmZXJbY2hpcC0+bG9nYnVmZmVyX2hlYWRdKQ0KPiAr
cmV0dXJuOw0KPiArfQ0KPiArDQo+ICt2c25wcmludGYodG1wYnVmZmVyLCBzaXplb2YodG1wYnVm
ZmVyKSwgZm10LCBhcmdzKTsNCj4gKw0KPiArbXV0ZXhfbG9jaygmY2hpcC0+bG9nYnVmZmVyX2xv
Y2spOw0KPiArDQo+ICtpZiAocnQxNzExaF9sb2dfZnVsbChjaGlwKSkgew0KPiArY2hpcC0+bG9n
YnVmZmVyX2hlYWQgPSBtYXgoY2hpcC0+bG9nYnVmZmVyX2hlYWQgLSAxLCAwKTsNCj4gK3N0cmxj
cHkodG1wYnVmZmVyLCAib3ZlcmZsb3ciLCBzaXplb2YodG1wYnVmZmVyKSk7DQo+ICt9DQo+ICsN
Cj4gK2lmIChjaGlwLT5sb2didWZmZXJfaGVhZCA8IDAgfHwNCj4gK2NoaXAtPmxvZ2J1ZmZlcl9o
ZWFkID49IExPR19CVUZGRVJfRU5UUklFUykgew0KPiArZGV2X3dhcm4oY2hpcC0+ZGV2LCAiJXMg
YmFkIGxvZyBidWZmZXIgaW5kZXggJWRcbiIsIF9fZnVuY19fLA0KPiArY2hpcC0+bG9nYnVmZmVy
X2hlYWQpOw0KPiArZ290byBhYm9ydDsNCj4gK30NCj4gKw0KPiAraWYgKCFjaGlwLT5sb2didWZm
ZXJbY2hpcC0+bG9nYnVmZmVyX2hlYWRdKSB7DQo+ICtkZXZfd2FybihjaGlwLT5kZXYsICIlcyBs
b2cgYnVmZmVyIGluZGV4ICVkIGlzIE5VTExcbiIsDQo+ICtfX2Z1bmNfXywgY2hpcC0+bG9nYnVm
ZmVyX2hlYWQpOw0KPiArZ290byBhYm9ydDsNCj4gK30NCj4gKw0KPiArcmVtX25zZWMgPSBkb19k
aXYodHNfbnNlYywgMTAwMDAwMDAwMCk7DQo+ICtzY25wcmludGYoY2hpcC0+bG9nYnVmZmVyW2No
aXAtPmxvZ2J1ZmZlcl9oZWFkXSwgTE9HX0JVRkZFUl9FTlRSWV9TSVpFLA0KPiArIlslNWx1LiUw
Nmx1XSAlcyIsICh1bnNpZ25lZCBsb25nKXRzX25zZWMsIHJlbV9uc2VjIC8gMTAwMCwNCj4gKyAg
dG1wYnVmZmVyKTsNCj4gK2NoaXAtPmxvZ2J1ZmZlcl9oZWFkID0gKGNoaXAtPmxvZ2J1ZmZlcl9o
ZWFkICsgMSkgJQ0KPiArTE9HX0JVRkZFUl9FTlRSSUVTOw0KPiArDQo+ICthYm9ydDoNCj4gK211
dGV4X3VubG9jaygmY2hpcC0+bG9nYnVmZmVyX2xvY2spOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMg
dm9pZCBydDE3MTFoX2xvZyhzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwLA0KPiArY29uc3QgY2hh
ciAqZm10LCAuLi4pDQo+ICt7DQo+ICt2YV9saXN0IGFyZ3M7DQo+ICsNCj4gK3ZhX3N0YXJ0KGFy
Z3MsIGZtdCk7DQo+ICtfcnQxNzExaF9sb2coY2hpcCwgZm10LCBhcmdzKTsNCj4gK3ZhX2VuZChh
cmdzKTsNCj4gK30NCj4gKw0KPiArc3RhdGljIGludCBydDE3MTFoX2xvZ19zaG93KHN0cnVjdCBy
dDE3MTFoX2NoaXAgKmNoaXAsIHN0cnVjdA0KPiArc2VxX2ZpbGUgKnMpIHsNCj4gK2ludCB0YWls
Ow0KPiArDQo+ICttdXRleF9sb2NrKCZjaGlwLT5sb2didWZmZXJfbG9jayk7DQo+ICt0YWlsID0g
Y2hpcC0+bG9nYnVmZmVyX3RhaWw7DQo+ICt3aGlsZSAodGFpbCAhPSBjaGlwLT5sb2didWZmZXJf
aGVhZCkgew0KPiArc2VxX3ByaW50ZihzLCAiJXMiLCBjaGlwLT5sb2didWZmZXJbdGFpbF0pOw0K
PiArdGFpbCA9ICh0YWlsICsgMSkgJSBMT0dfQlVGRkVSX0VOVFJJRVM7DQo+ICt9DQo+ICtpZiAo
IXNlcV9oYXNfb3ZlcmZsb3dlZChzKSkNCj4gK2NoaXAtPmxvZ2J1ZmZlcl90YWlsID0gdGFpbDsN
Cj4gK211dGV4X3VubG9jaygmY2hpcC0+bG9nYnVmZmVyX2xvY2spOw0KPiArDQo+ICtyZXR1cm4g
MDsNCj4gK30NCj4gKw0KPiArc3RhdGljIGludCBydDE3MTFoX3JlZ3Nfc2hvdyhzdHJ1Y3QgcnQx
NzExaF9jaGlwICpjaGlwLCBzdHJ1Y3QNCj4gK3NlcV9maWxlICpzKSB7DQo+ICtpbnQgcmV0ID0g
MDsNCj4gK2ludCBpID0gMCwgaiA9IDA7DQo+ICtzdHJ1Y3QgcmVnX2Rlc2MgKmRlc2MgPSBOVUxM
Ow0KPiArdWludDhfdCByZWd2YWxbMl0gPSB7MH07DQo+ICsNCj4gK2ZvciAoaSA9IDA7IGkgPCBB
UlJBWV9TSVpFKHJ0MTcxMWhfcmVnX2Rlc2MpOyBpKyspIHsNCj4gK2Rlc2MgPSAmcnQxNzExaF9y
ZWdfZGVzY1tpXTsNCj4gK3JldCA9IHJ0MTcxMWhfcmVnX2Jsb2NrX3JlYWQoY2hpcCwgZGVzYy0+
YWRkciwgZGVzYy0+c2l6ZSwNCj4gK3JlZ3ZhbCk7DQo+ICtpZiAocmV0IDwgMCkgew0KPiArZGV2
X2VycihjaGlwLT5kZXYsICIlcyByZWFkIHJlZzB4JTAyWCBmYWlsXG4iLA0KPiArX19mdW5jX18s
IGRlc2MtPmFkZHIpOw0KPiArY29udGludWU7DQo+ICt9DQo+ICsNCj4gK3NlcV9wcmludGYocywg
InJlZzB4JTAyeDoweCIsIGRlc2MtPmFkZHIpOw0KPiArZm9yIChqID0gMDsgaiA8IGRlc2MtPnNp
emU7IGorKykNCj4gK3NlcV9wcmludGYocywgIiUwMngsIiwgcmVndmFsW2pdKTsNCj4gK3NlcV9w
dXRzKHMsICJcbiIpOw0KPiArfQ0KPiArDQo+ICtyZXR1cm4gMDsNCj4gK30NCj4gKw0KPiArc3Rh
dGljIGlubGluZSBpbnQgcnQxNzExaF9yZWdfYWRkcl9zaG93KHN0cnVjdCBydDE3MTFoX2NoaXAg
KmNoaXAsDQo+ICtzdHJ1Y3Qgc2VxX2ZpbGUgKnMpDQo+ICt7DQo+ICtzdHJ1Y3QgcmVnX2Rlc2Mg
KmRlc2MgPSAmcnQxNzExaF9yZWdfZGVzY1tjaGlwLT5kYmdfcmVnaWR4XTsNCj4gKw0KPiArc2Vx
X3ByaW50ZihzLCAiMHglMDJ4XG4iLCBkZXNjLT5hZGRyKTsNCj4gK3JldHVybiAwOw0KPiArfQ0K
PiArDQo+ICtzdGF0aWMgaW5saW5lIGludCBydDE3MTFoX2RhdGFfc2hvdyhzdHJ1Y3QgcnQxNzEx
aF9jaGlwICpjaGlwLA0KPiArc3RydWN0IHNlcV9maWxlICpzKQ0KPiArew0KPiAraW50IHJldCA9
IDAsIGkgPSAwOw0KPiArc3RydWN0IHJlZ19kZXNjICpkZXNjID0gJnJ0MTcxMWhfcmVnX2Rlc2Nb
Y2hpcC0+ZGJnX3JlZ2lkeF07DQo+ICt1aW50OF90IHJlZ3ZhbFsyXSA9IHswfTsNCj4gKw0KPiAr
cmV0ID0gcnQxNzExaF9yZWdfYmxvY2tfcmVhZChjaGlwLCBkZXNjLT5hZGRyLCBkZXNjLT5zaXpl
LCByZWd2YWwpOw0KPiAraWYgKHJldCA8IDApDQo+ICtyZXR1cm4gcmV0Ow0KPiArDQo+ICtzZXFf
cHJpbnRmKHMsICJyZWcweCUwMng9MHgiLCBkZXNjLT5hZGRyKTsNCj4gK2ZvciAoaSA9IDA7IGkg
PCBkZXNjLT5zaXplOyBpKyspDQo+ICtzZXFfcHJpbnRmKHMsICIlMDJ4LCIsIHJlZ3ZhbFtpXSk7
DQo+ICtzZXFfcHV0cyhzLCAiXG4iKTsNCj4gK3JldHVybiAwOw0KPiArfQ0KPiArDQo+ICtzdGF0
aWMgaW50IHJ0MTcxMWhfZGJnX3Nob3coc3RydWN0IHNlcV9maWxlICpzLCB2b2lkICp2KSB7DQo+
ICtpbnQgcmV0ID0gMDsNCj4gK3N0cnVjdCBydDE3MTFoX2RiZ19pbmZvICppbmZvID0gKHN0cnVj
dCBydDE3MTFoX2RiZ19pbmZvICopcy0+cHJpdmF0ZTsNCj4gK3N0cnVjdCBydDE3MTFoX2NoaXAg
KmNoaXAgPSBpbmZvLT5jaGlwOw0KPiArDQo+ICttdXRleF9sb2NrKCZjaGlwLT5kYmdvcHNfbG9j
ayk7DQo+ICtzd2l0Y2ggKGluZm8tPmlkKSB7DQo+ICtjYXNlIFJUMTcxMUhfREJHX0xPRzoNCj4g
K3JldCA9IHJ0MTcxMWhfbG9nX3Nob3coY2hpcCwgcyk7DQo+ICticmVhazsNCj4gK2Nhc2UgUlQx
NzExSF9EQkdfUkVHUzoNCj4gK3JldCA9IHJ0MTcxMWhfcmVnc19zaG93KGNoaXAsIHMpOw0KPiAr
YnJlYWs7DQo+ICtjYXNlIFJUMTcxMUhfREJHX1JFR19BRERSOg0KPiArcmV0ID0gcnQxNzExaF9y
ZWdfYWRkcl9zaG93KGNoaXAsIHMpOw0KPiArYnJlYWs7DQo+ICtjYXNlIFJUMTcxMUhfREJHX0RB
VEE6DQo+ICtyZXQgPSBydDE3MTFoX2RhdGFfc2hvdyhjaGlwLCBzKTsNCj4gK2JyZWFrOw0KPiAr
ZGVmYXVsdDoNCj4gK3JldCA9IC1FSU5WQUw7DQo+ICticmVhazsNCj4gK30NCj4gKw0KPiArbXV0
ZXhfdW5sb2NrKCZjaGlwLT5kYmdvcHNfbG9jayk7DQo+ICtyZXR1cm4gcmV0Ow0KPiArfQ0KPiAr
DQo+ICtzdGF0aWMgaW50IHJ0MTcxMWhfZGJnX29wZW4oc3RydWN0IGlub2RlICppbm9kZSwgc3Ry
dWN0IGZpbGUgKmZpbGUpIHsNCj4gK2lmIChmaWxlLT5mX21vZGUgJiBGTU9ERV9SRUFEKQ0KPiAr
cmV0dXJuIHNpbmdsZV9vcGVuKGZpbGUsIHJ0MTcxMWhfZGJnX3Nob3csIGlub2RlLT5pX3ByaXZh
dGUpOw0KPiArZmlsZS0+cHJpdmF0ZV9kYXRhID0gaW5vZGUtPmlfcHJpdmF0ZTsNCj4gK3JldHVy
biAwOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW50IGdldF9wYXJhbWV0ZXJzKGNoYXIgKmJ1Ziwg
bG9uZyBpbnQgKnBhcmFtMSwgaW50DQo+ICtudW1fb2ZfcGFyKSB7DQo+ICtjaGFyICp0b2tlbjsN
Cj4gK2ludCBiYXNlLCBjbnQ7DQo+ICsNCj4gK3Rva2VuID0gc3Ryc2VwKCZidWYsICIgIik7DQo+
ICsNCj4gK2ZvciAoY250ID0gMDsgY250IDwgbnVtX29mX3BhcjsgY250KyspIHsNCj4gK2lmICh0
b2tlbiAhPSBOVUxMKSB7DQo+ICtpZiAoKHRva2VuWzFdID09ICd4JykgfHwgKHRva2VuWzFdID09
ICdYJykpDQo+ICtiYXNlID0gMTY7DQo+ICtlbHNlDQo+ICtiYXNlID0gMTA7DQo+ICsNCj4gK2lm
IChrc3RydG91bCh0b2tlbiwgYmFzZSwgJnBhcmFtMVtjbnRdKSAhPSAwKQ0KPiArcmV0dXJuIC1F
SU5WQUw7DQo+ICsNCj4gK3Rva2VuID0gc3Ryc2VwKCZidWYsICIgIik7DQo+ICt9IGVsc2UNCj4g
K3JldHVybiAtRUlOVkFMOw0KPiArfQ0KPiArcmV0dXJuIDA7DQo+ICt9DQoNCldoYXQgaXMgdGhp
cyBmdW5jdGlvbiBkb2luZz8gIFdoYXQgaXMgeW91ciBkZWJ1Z2ZzIGZpbGVzIGZvcj8NCg0KPiAr
DQo+ICtzdGF0aWMgaW50IGdldF9kYXRhcyhjb25zdCBjaGFyICpidWYsIGNvbnN0IGludCBsZW5n
dGgsDQo+ICt1bnNpZ25lZCBjaGFyICpkYXRhX2J1ZmZlciwgdW5zaWduZWQgY2hhciBkYXRhX2xl
bmd0aCkgew0KPiAraW50IGksIHB0cjsNCj4gK2xvbmcgaW50IHZhbHVlOw0KPiArY2hhciB0b2tl
bls1XTsNCj4gKw0KPiArdG9rZW5bMF0gPSAnMCc7DQo+ICt0b2tlblsxXSA9ICd4JzsNCj4gK3Rv
a2VuWzRdID0gMDsNCj4gK2lmIChidWZbMF0gIT0gJzAnIHx8IGJ1ZlsxXSAhPSAneCcpDQo+ICty
ZXR1cm4gLUVJTlZBTDsNCj4gKw0KPiArcHRyID0gMjsNCj4gK2ZvciAoaSA9IDA7IChpIDwgZGF0
YV9sZW5ndGgpICYmIChwdHIgKyAyIDw9IGxlbmd0aCk7IGkrKykgew0KPiArdG9rZW5bMl0gPSBi
dWZbcHRyKytdOw0KPiArdG9rZW5bM10gPSBidWZbcHRyKytdOw0KPiArcHRyKys7DQo+ICtpZiAo
a3N0cnRvdWwodG9rZW4sIDE2LCAmdmFsdWUpICE9IDApDQo+ICtyZXR1cm4gLUVJTlZBTDsNCj4g
K2RhdGFfYnVmZmVyW2ldID0gdmFsdWU7DQo+ICt9DQo+ICtyZXR1cm4gMDsNCj4gK30NCj4gKw0K
PiArc3RhdGljIGludCBydDE3MTFoX3JlZ2FkZHIyaWR4KHVpbnQ4X3QgcmVnX2FkZHIpIHsNCj4g
K2ludCBpID0gMDsNCj4gK3N0cnVjdCByZWdfZGVzYyAqZGVzYyA9IE5VTEw7DQo+ICsNCj4gK2Zv
ciAoaSA9IDA7IGkgPCBBUlJBWV9TSVpFKHJ0MTcxMWhfcmVnX2Rlc2MpOyBpKyspIHsNCj4gK2Rl
c2MgPSAmcnQxNzExaF9yZWdfZGVzY1tpXTsNCj4gK2lmIChkZXNjLT5hZGRyID09IHJlZ19hZGRy
KQ0KPiArcmV0dXJuIGk7DQo+ICt9DQo+ICtyZXR1cm4gLUVJTlZBTDsNCj4gK30NCj4gKw0KPiAr
c3RhdGljIHNzaXplX3QgcnQxNzExaF9kYmdfd3JpdGUoc3RydWN0IGZpbGUgKmZpbGUsIGNvbnN0
IGNoYXIgX191c2VyICp1YnVmLA0KPiArc2l6ZV90IGNvdW50LCBsb2ZmX3QgKnBwb3MpDQo+ICt7
DQo+ICtpbnQgcmV0ID0gMDsNCj4gK3N0cnVjdCBydDE3MTFoX2RiZ19pbmZvICppbmZvID0NCj4g
KyhzdHJ1Y3QgcnQxNzExaF9kYmdfaW5mbyAqKWZpbGUtPnByaXZhdGVfZGF0YTsNCj4gK3N0cnVj
dCBydDE3MTFoX2NoaXAgKmNoaXAgPSBpbmZvLT5jaGlwOw0KPiArc3RydWN0IHJlZ19kZXNjICpk
ZXNjID0gTlVMTDsNCj4gK2NoYXIgbGJ1ZlsxMjhdOw0KPiArbG9uZyBpbnQgcGFyYW1bNV07DQo+
ICt1bnNpZ25lZCBjaGFyIHJlZ19kYXRhWzJdID0gezB9Ow0KPiArDQo+ICtpZiAoY291bnQgPiBz
aXplb2YobGJ1ZikgLSAxKQ0KPiArcmV0dXJuIC1FRkFVTFQ7DQo+ICsNCj4gK3JldCA9IGNvcHlf
ZnJvbV91c2VyKGxidWYsIHVidWYsIGNvdW50KTsNCj4gK2lmIChyZXQpDQo+ICtyZXR1cm4gLUVG
QVVMVDsNCj4gK2xidWZbY291bnRdID0gJ1wwJzsNCj4gKw0KPiArbXV0ZXhfbG9jaygmY2hpcC0+
ZGJnb3BzX2xvY2spOw0KPiArc3dpdGNoIChpbmZvLT5pZCkgew0KPiArY2FzZSBSVDE3MTFIX0RC
R19SRUdfQUREUjoNCj4gK3JldCA9IGdldF9wYXJhbWV0ZXJzKGxidWYsIHBhcmFtLCAxKTsNCj4g
K2lmIChyZXQgPCAwKSB7DQo+ICtkZXZfZXJyKGNoaXAtPmRldiwgIiVzIGdldCBwYXJhbSBmYWls
XG4iLCBfX2Z1bmNfXyk7DQo+ICtyZXQgPSAtRUlOVkFMOw0KPiArZ290byBvdXQ7DQo+ICt9DQo+
ICtyZXQgPSBydDE3MTFoX3JlZ2FkZHIyaWR4KHBhcmFtWzBdKTsNCj4gK2lmIChyZXQgPCAwKSB7
DQo+ICtkZXZfZXJyKGNoaXAtPmRldiwgIiVzIGFkZHIyaWR4IGZhaWxcbiIsIF9fZnVuY19fKTsN
Cj4gK3JldCA9IC1FSU5WQUw7DQo+ICtnb3RvIG91dDsNCj4gK30NCj4gK2NoaXAtPmRiZ19yZWdp
ZHggPSByZXQ7DQo+ICticmVhazsNCj4gK2Nhc2UgUlQxNzExSF9EQkdfREFUQToNCj4gK2Rlc2Mg
PSAmcnQxNzExaF9yZWdfZGVzY1tjaGlwLT5kYmdfcmVnaWR4XTsNCj4gK2lmICgoZGVzYy0+c2l6
ZSAtIDEpICogMyArIDUgIT0gY291bnQpIHsNCj4gK2Rldl9lcnIoY2hpcC0+ZGV2LCAiJXMgaW5j
b3JyZWN0IGlucHV0IGxlbmd0aFxuIiwNCj4gK19fZnVuY19fKTsNCj4gK3JldCA9IC1FSU5WQUw7
DQo+ICtnb3RvIG91dDsNCj4gK30NCj4gK3JldCA9IGdldF9kYXRhcygoY2hhciAqKXVidWYsIGNv
dW50LCByZWdfZGF0YSwgZGVzYy0+c2l6ZSk7DQo+ICtpZiAocmV0IDwgMCkgew0KPiArZGV2X2Vy
cihjaGlwLT5kZXYsICIlcyBnZXQgZGF0YSBmYWlsXG4iLCBfX2Z1bmNfXyk7DQo+ICtyZXQgPSAt
RUlOVkFMOw0KPiArZ290byBvdXQ7DQo+ICt9DQo+ICtyZXQgPSBydDE3MTFoX3JlZ19ibG9ja193
cml0ZShjaGlwLCBkZXNjLT5hZGRyLCBkZXNjLT5zaXplLA0KPiArcmVnX2RhdGEpOw0KPiArYnJl
YWs7DQo+ICtkZWZhdWx0Og0KPiArcmV0ID0gLUVJTlZBTDsNCj4gK2JyZWFrOw0KPiArfQ0KPiAr
DQo+ICtvdXQ6DQo+ICttdXRleF91bmxvY2soJmNoaXAtPmRiZ29wc19sb2NrKTsNCj4gK3JldHVy
biByZXQgPCAwID8gcmV0IDogY291bnQ7DQo+ICt9DQo+ICsNCj4gK3N0YXRpYyBpbnQgcnQxNzEx
aF9kYmdfcmVsZWFzZShzdHJ1Y3QgaW5vZGUgKmlub2RlLCBzdHJ1Y3QgZmlsZQ0KPiArKmZpbGUp
IHsNCj4gK2lmIChmaWxlLT5mX21vZGUgJiBGTU9ERV9SRUFEKQ0KPiArcmV0dXJuIHNpbmdsZV9y
ZWxlYXNlKGlub2RlLCBmaWxlKTsNCj4gK3JldHVybiAwOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMg
Y29uc3Qgc3RydWN0IGZpbGVfb3BlcmF0aW9ucyBydDE3MTFoX2RiZ19vcHMgPSB7DQo+ICsub3Bl
bj0gcnQxNzExaF9kYmdfb3BlbiwNCj4gKy5sbHNlZWs9IHNlcV9sc2VlaywNCj4gKy5yZWFkPSBz
ZXFfcmVhZCwNCj4gKy53cml0ZT0gcnQxNzExaF9kYmdfd3JpdGUsDQo+ICsucmVsZWFzZT0gcnQx
NzExaF9kYmdfcmVsZWFzZSwNCj4gK307DQo+ICsNCj4gKw0KPiArc3RhdGljIGludCBydDE3MTFo
X2RlYnVnZnNfaW5pdChzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwKSB7DQo+ICtpbnQgcmV0ID0g
MCwgaSA9IDA7DQo+ICtzdHJ1Y3QgcnQxNzExaF9kYmdfaW5mbyAqaW5mbyA9IE5VTEw7DQo+ICtp
bnQgbGVuID0gMDsNCj4gK2NoYXIgKmRpcm5hbWUgPSBOVUxMOw0KPiArDQo+ICttdXRleF9pbml0
KCZjaGlwLT5sb2didWZmZXJfbG9jayk7DQo+ICttdXRleF9pbml0KCZjaGlwLT5kYmdvcHNfbG9j
ayk7DQo+ICtsZW4gPSBzdHJsZW4oZGV2X25hbWUoY2hpcC0+ZGV2KSk7DQo+ICtkaXJuYW1lID0g
ZGV2bV9remFsbG9jKGNoaXAtPmRldiwgbGVuICsgOSwgR0ZQX0tFUk5FTCk7DQo+ICtpZiAoIWRp
cm5hbWUpDQo+ICtyZXR1cm4gLUVOT01FTTsNCj4gK3NucHJpbnRmKGRpcm5hbWUsIGxlbiArIDks
ICJydDE3MTFoLSVzIiwgZGV2X25hbWUoY2hpcC0+ZGV2KSk7DQo+ICtpZiAoIWNoaXAtPmRiZ2Rp
cikgew0KPiArY2hpcC0+ZGJnZGlyID0gZGVidWdmc19jcmVhdGVfZGlyKGRpcm5hbWUsIE5VTEwp
Ow0KPiAraWYgKCFjaGlwLT5kYmdkaXIpDQo+ICtyZXR1cm4gLUVOT01FTTsNCg0KTm8gbmVlZCB0
byBldmVyIGNoZWNrIHRoZSByZXR1cm4gdmFsdWUgb2YgZGVidWdmc18gY2FsbHMsIHlvdSBzaG91
bGQgbm90IGNhcmUgYW5kIGNhbiBhbHdheXMgdXNlIHRoZSB2YWx1ZSB0byBhbnkgZnV0dXJlIGRl
YnVnZnMgY2FsbHMsIGlmIHlvdSByZWFsbHkgbmVlZCBpdC4NCg0KPiArfQ0KPiArDQo+ICtmb3Ig
KGkgPSAwOyBpIDwgUlQxNzExSF9EQkdfTUFYOyBpKyspIHsNCj4gK2luZm8gPSAmY2hpcC0+ZGJn
X2luZm9baV07DQoNCnN0YXRpYyBhcnJheSBvZiBkZWJ1ZyBpbmZvPyAgVGhhdCBmZWVscyBvZGQu
DQoNCj4gK2luZm8tPmNoaXAgPSBjaGlwOw0KPiAraW5mby0+aWQgPSBpOw0KPiArY2hpcC0+ZGJn
X2ZpbGVzW2ldID0gZGVidWdmc19jcmVhdGVfZmlsZSgNCj4gK3J0MTcxMWhfZGJnX2ZpbGVuYW1l
W2ldLCBTX0lGUkVHIHwgMDQ0NCwNCj4gK2NoaXAtPmRiZ2RpciwgaW5mbywgJnJ0MTcxMWhfZGJn
X29wcyk7DQo+ICtpZiAoIWNoaXAtPmRiZ19maWxlc1tpXSkgew0KPiArcmV0ID0gLUVJTlZBTDsN
Cg0KTGlrZSBoZXJlLCB5b3UgZG9uJ3QgbmVlZCB0aGlzLCBhbmQgeW91IGRvbid0IG5lZWQgdG8g
Y2FyZSBhYm91dCB0aGUgcmV0dXJuIHZhbHVlLg0KDQo+ICtnb3RvIGVycjsNCj4gK30NCj4gK30N
Cj4gKw0KPiArcmV0dXJuIDA7DQo+ICtlcnI6DQo+ICtkZWJ1Z2ZzX3JlbW92ZV9yZWN1cnNpdmUo
Y2hpcC0+ZGJnZGlyKTsNCj4gK3JldHVybiByZXQ7DQoNCldoeSBkbyB5b3UgY2FyZSBhYm91dCBh
biBlcnJvciBoZXJlPyAgWW91ciBjb2RlIHNob3VsZCBub3QgZG8gYW55dGhpbmcgZGlmZmVyZW50
IGlmIGRlYnVnZnMgc3R1ZmYgZG9lcyBub3Qgd29yayBvciBpZiBpdCBkb2VzLiAgSXQncyBkZWJ1
Z2dpbmcgb25seS4NCg0KPiArfQ0KPiArDQo+ICtzdGF0aWMgdm9pZCBydDE3MTFoX2RlYnVnZnNf
ZXhpdChzdHJ1Y3QgcnQxNzExaF9jaGlwICpjaGlwKSB7DQo+ICtkZWJ1Z2ZzX3JlbW92ZV9yZWN1
cnNpdmUoY2hpcC0+ZGJnZGlyKTsNCg0KU2VlLCB5b3UgZGlkbid0IG5lZWQgdGhvc2UgZmlsZSBo
YW5kbGVzIDopDQoNCnRoYW5rcywNCg0KZ3JlZyBrLWgNCioqKioqKioqKioqKiogRW1haWwgQ29u
ZmlkZW50aWFsaXR5IE5vdGljZSAqKioqKioqKioqKioqKioqKioqKg0KDQpUaGUgaW5mb3JtYXRp
b24gY29udGFpbmVkIGluIHRoaXMgZS1tYWlsIG1lc3NhZ2UgKGluY2x1ZGluZyBhbnkgYXR0YWNo
bWVudHMpIG1heSBiZSBjb25maWRlbnRpYWwsIHByb3ByaWV0YXJ5LCBwcml2aWxlZ2VkLCBvciBv
dGhlcndpc2UgZXhlbXB0IGZyb20gZGlzY2xvc3VyZSB1bmRlciBhcHBsaWNhYmxlIGxhd3MuIEl0
IGlzIGludGVuZGVkIHRvIGJlIGNvbnZleWVkIG9ubHkgdG8gdGhlIGRlc2lnbmF0ZWQgcmVjaXBp
ZW50KHMpLiBBbnkgdXNlLCBkaXNzZW1pbmF0aW9uLCBkaXN0cmlidXRpb24sIHByaW50aW5nLCBy
ZXRhaW5pbmcgb3IgY29weWluZyBvZiB0aGlzIGUtbWFpbCAoaW5jbHVkaW5nIGl0cyBhdHRhY2ht
ZW50cykgYnkgdW5pbnRlbmRlZCByZWNpcGllbnQocykgaXMgc3RyaWN0bHkgcHJvaGliaXRlZCBh
bmQgbWF5IGJlIHVubGF3ZnVsLiBJZiB5b3UgYXJlIG5vdCBhbiBpbnRlbmRlZCByZWNpcGllbnQg
b2YgdGhpcyBlLW1haWwsIG9yIGJlbGlldmUgdGhhdCB5b3UgaGF2ZSByZWNlaXZlZCB0aGlzIGUt
bWFpbCBpbiBlcnJvciwgcGxlYXNlIG5vdGlmeSB0aGUgc2VuZGVyIGltbWVkaWF0ZWx5IChieSBy
ZXBseWluZyB0byB0aGlzIGUtbWFpbCksIGRlbGV0ZSBhbnkgYW5kIGFsbCBjb3BpZXMgb2YgdGhp
cyBlLW1haWwgKGluY2x1ZGluZyBhbnkgYXR0YWNobWVudHMpIGZyb20geW91ciBzeXN0ZW0sIGFu
ZCBkbyBub3QgZGlzY2xvc2UgdGhlIGNvbnRlbnQgb2YgdGhpcyBlLW1haWwgdG8gYW55IG90aGVy
IHBlcnNvbi4gVGhhbmsgeW91IQ0K
---
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  3:09   ` Jun Li
  0 siblings, 0 replies; 51+ messages in thread
From: Jun Li @ 2018-01-19  3:09 UTC (permalink / raw)
  To: ShuFanLee, heikki.krogerus
  Cc: cy_huang, shufan_lee, linux-kernel, linux-usb, Guenter Roeck

Hi
> -----Original Message-----
> From: linux-usb-owner@vger.kernel.org [mailto:linux-usb-
> owner@vger.kernel.org] On Behalf Of ShuFanLee
> Sent: Wednesday, January 10, 2018 2:59 PM
> To: heikki.krogerus@linux.intel.com
> Cc: cy_huang@richtek.com; shufan_lee@richtek.com; linux-
> kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
> 
> From: ShuFanLee <shufan_lee@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.

A general question, is this Rt1711h type-c chip compatible with TCPCI
(Universal Serial Bus Type-C Port Controller Interface Specification)?
looks like it has the same register map and has some extension, can
the existing ./drivers/staging/typec/tcpic.c basically work for you?

+Guenter

Li Jun 

> 
> Signed-off-by: ShuFanLee <shufan_lee@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
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  3:09   ` Jun Li
  0 siblings, 0 replies; 51+ messages in thread
From: Jun Li @ 2018-01-19  3:09 UTC (permalink / raw)
  To: ShuFanLee, heikki.krogerus
  Cc: cy_huang, shufan_lee, linux-kernel, linux-usb, Guenter Roeck

Hi
> -----Original Message-----
> From: linux-usb-owner@vger.kernel.org [mailto:linux-usb-
> owner@vger.kernel.org] On Behalf Of ShuFanLee
> Sent: Wednesday, January 10, 2018 2:59 PM
> To: heikki.krogerus@linux.intel.com
> Cc: cy_huang@richtek.com; shufan_lee@richtek.com; linux-
> kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
> 
> From: ShuFanLee <shufan_lee@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.

A general question, is this Rt1711h type-c chip compatible with TCPCI
(Universal Serial Bus Type-C Port Controller Interface Specification)?
looks like it has the same register map and has some extension, can
the existing ./drivers/staging/typec/tcpic.c basically work for you?

+Guenter

Li Jun 

> 
> Signed-off-by: ShuFanLee <shufan_lee@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
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  5:48     ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-19  5:48 UTC (permalink / raw)
  To: 'Jun Li', ShuFanLee, heikki.krogerus
  Cc: cy_huang(黃啟原),
	linux-kernel, linux-usb, Guenter Roeck

Hi Jun,

  For now, RT1711H is not fully compatible with TCPCI. So the existing tcpci.c may not work for it.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Jun Li [mailto:jun.li@nxp.com]
Sent: Friday, January 19, 2018 11:10 AM
To: ShuFanLee; heikki.krogerus@linux.intel.com
Cc: cy_huang(黃啟原); shufan_lee(李書帆); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org; Guenter Roeck
Subject: RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

Hi
> -----Original Message-----
> From: linux-usb-owner@vger.kernel.org [mailto:linux-usb-
> owner@vger.kernel.org] On Behalf Of ShuFanLee
> Sent: Wednesday, January 10, 2018 2:59 PM
> To: heikki.krogerus@linux.intel.com
> Cc: cy_huang@richtek.com; shufan_lee@richtek.com; linux-
> kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
>
> From: ShuFanLee <shufan_lee@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.

A general question, is this Rt1711h type-c chip compatible with TCPCI (Universal Serial Bus Type-C Port Controller Interface Specification)?
looks like it has the same register map and has some extension, can the existing ./drivers/staging/typec/tcpic.c basically work for you?

+Guenter

Li Jun

>
> Signed-off-by: ShuFanLee <shufan_lee@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
>
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  5:48     ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-19  5:48 UTC (permalink / raw)
  To: 'Jun Li', ShuFanLee, heikki.krogerus
  Cc: cy_huang(黃啟原),
	linux-kernel, linux-usb, Guenter Roeck

Hi Jun,

  For now, RT1711H is not fully compatible with TCPCI. So the existing tcpci.c may not work for it.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Jun Li [mailto:jun.li@nxp.com]
Sent: Friday, January 19, 2018 11:10 AM
To: ShuFanLee; heikki.krogerus@linux.intel.com
Cc: cy_huang(黃啟原); shufan_lee(李書帆); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org; Guenter Roeck
Subject: RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

Hi
> -----Original Message-----
> From: linux-usb-owner@vger.kernel.org [mailto:linux-usb-
> owner@vger.kernel.org] On Behalf Of ShuFanLee
> Sent: Wednesday, January 10, 2018 2:59 PM
> To: heikki.krogerus@linux.intel.com
> Cc: cy_huang@richtek.com; shufan_lee@richtek.com; linux-
> kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
>
> From: ShuFanLee <shufan_lee@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.

A general question, is this Rt1711h type-c chip compatible with TCPCI (Universal Serial Bus Type-C Port Controller Interface Specification)?
looks like it has the same register map and has some extension, can the existing ./drivers/staging/typec/tcpic.c basically work for you?

+Guenter

Li Jun

>
> Signed-off-by: ShuFanLee <shufan_lee@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
>
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  8:03       ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: 'Greg KH' @ 2018-01-19  8:03 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: ShuFanLee, heikki.krogerus, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Thu, Jan 18, 2018 at 01:13:15PM +0000, shufan_lee(李書帆) wrote:
> > +
> > +#include "rt1711h.h"
> 
> Why a .h file for a single .c file?
> 
> Is the suggestion to move all content in rt1711h.h into rt1711h.c?

If it can be, sure, you only need a .h file for things that are shared
among other .c files.

> > +/* I2C */
> > +atomic_t i2c_busy;
> > +atomic_t pm_suspend;
> 
> Why are these atomic?  You know that doesn't mean they do not need locking, right?
> 
> For my understanding, a single operation on atomic_t doesn't need lock, like a single atomic_set.
> But two consecutive operations doesn't guarantee anything. Like atomic_set followed by an atomic_read.
> This part is referenced from fusb302 used to make sure I2C is idle before system suspends.
> It only needs to guarantee a single read/write on these variable is atomic operation, so atomic is used.

It's atomic for read/write, yes, but that does not mean it can not be
instantly changed after the value is read, right?  So you might need to
look and ensure you are not doing something wrong that can race.  A
single lock should be simpler than this type of thing, and will be
correct.

> > +#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 */
> 
> That's a lot of stuff jsut for debugfs.  Why do you care about #define at all?  The code should not.
> 
> Is the suggestion to remove #ifdef CONFIG_DEBUG_FS?

Yes.  Or just move it all to another structure that you can dynamically
add to this one if needed.

> And another 2 locks?  Ick, no.
> 
> dbgops_lock is used to prevent user from accessing different debug files simultaneously.
> Is the suggestion to use the lock of the following one?
> > +/* lock for sharing chip states */
> > +struct mutex lock;

Sure, why not?

> ========================================================================
> 
> > +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;
> 
> No need to ever check the return value of debugfs_ calls, you should not care and can always use the value to any future debugfs calls, if you really need it.
> 
> If it is NULL without checking and we use it in debugfs_create_file, all the debug files will be created in the root of the debugfs filesystem.
> Is this correct?

If it returns NULL then any future calls to debugfs will also not be
working, so all will be fine.  So there is no need to check this.

> ========================================================================
> 
> > +for (i = 0; i < RT1711H_DBG_MAX; i++) {
> > +info = &chip->dbg_info[i];
> 
> static array of debug info?  That feels odd.
> 
> Is the suggestion to use pointer of array and dynamically allocated it?

If that makes more sense, it's up to you.  Just a suggestion.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  8:03       ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2018-01-19  8:03 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: ShuFanLee, heikki.krogerus, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Thu, Jan 18, 2018 at 01:13:15PM +0000, shufan_lee(李書帆) wrote:
> > +
> > +#include "rt1711h.h"
> 
> Why a .h file for a single .c file?
> 
> Is the suggestion to move all content in rt1711h.h into rt1711h.c?

If it can be, sure, you only need a .h file for things that are shared
among other .c files.

> > +/* I2C */
> > +atomic_t i2c_busy;
> > +atomic_t pm_suspend;
> 
> Why are these atomic?  You know that doesn't mean they do not need locking, right?
> 
> For my understanding, a single operation on atomic_t doesn't need lock, like a single atomic_set.
> But two consecutive operations doesn't guarantee anything. Like atomic_set followed by an atomic_read.
> This part is referenced from fusb302 used to make sure I2C is idle before system suspends.
> It only needs to guarantee a single read/write on these variable is atomic operation, so atomic is used.

It's atomic for read/write, yes, but that does not mean it can not be
instantly changed after the value is read, right?  So you might need to
look and ensure you are not doing something wrong that can race.  A
single lock should be simpler than this type of thing, and will be
correct.

> > +#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 */
> 
> That's a lot of stuff jsut for debugfs.  Why do you care about #define at all?  The code should not.
> 
> Is the suggestion to remove #ifdef CONFIG_DEBUG_FS?

Yes.  Or just move it all to another structure that you can dynamically
add to this one if needed.

> And another 2 locks?  Ick, no.
> 
> dbgops_lock is used to prevent user from accessing different debug files simultaneously.
> Is the suggestion to use the lock of the following one?
> > +/* lock for sharing chip states */
> > +struct mutex lock;

Sure, why not?

> ========================================================================
> 
> > +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;
> 
> No need to ever check the return value of debugfs_ calls, you should not care and can always use the value to any future debugfs calls, if you really need it.
> 
> If it is NULL without checking and we use it in debugfs_create_file, all the debug files will be created in the root of the debugfs filesystem.
> Is this correct?

If it returns NULL then any future calls to debugfs will also not be
working, so all will be fine.  So there is no need to check this.

> ========================================================================
> 
> > +for (i = 0; i < RT1711H_DBG_MAX; i++) {
> > +info = &chip->dbg_info[i];
> 
> static array of debug info?  That feels odd.
> 
> Is the suggestion to use pointer of array and dynamically allocated it?

If that makes more sense, it's up to you.  Just a suggestion.

thanks,

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  8:22       ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-19  8:22 UTC (permalink / raw)
  To: shufan_lee(?????????)
  Cc: 'Jun Li', ShuFanLee, cy_huang(?????????),
	linux-kernel, linux-usb, Guenter Roeck

Hi Shu-Fan,

On Fri, Jan 19, 2018 at 05:48:02AM +0000, shufan_lee(?????????) wrote:
> Hi Jun,
> 
>   For now, RT1711H is not fully compatible with TCPCI. So the existing tcpci.c
>   may not work for it.

The datasheet for RT1711H does talk about TCPCi and TCPM+TCPC [1].
What are the differences that justify a separate driver?

[1] http://www.richtek.com/assets/product_file/RT1711H/DS1711H-02.pdf


Br,

-- 
heikki

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  8:22       ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-19  8:22 UTC (permalink / raw)
  To: shufan_lee(?????????)
  Cc: 'Jun Li', ShuFanLee, cy_huang(?????????),
	linux-kernel, linux-usb, Guenter Roeck

Hi Shu-Fan,

On Fri, Jan 19, 2018 at 05:48:02AM +0000, shufan_lee(?????????) wrote:
> Hi Jun,
> 
>   For now, RT1711H is not fully compatible with TCPCI. So the existing tcpci.c
>   may not work for it.

The datasheet for RT1711H does talk about TCPCi and TCPM+TCPC [1].
What are the differences that justify a separate driver?

[1] http://www.richtek.com/assets/product_file/RT1711H/DS1711H-02.pdf


Br,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  9:01         ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-19  9:01 UTC (permalink / raw)
  To: 'Heikki Krogerus'
  Cc: 'Jun Li', ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb, Guenter Roeck

Hi Heikki,

  For example, the flow of tcpci_init is a little bit different.
  In tcpci_init, there are more parameters need to be set for RT1711H.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Heikki Krogerus [mailto:heikki.krogerus@linux.intel.com]
Sent: Friday, January 19, 2018 4:22 PM
To: shufan_lee(李書帆)
Cc: 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org; Guenter Roeck
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

Hi Shu-Fan,

On Fri, Jan 19, 2018 at 05:48:02AM +0000, shufan_lee(?????????) wrote:
> Hi Jun,
>
>   For now, RT1711H is not fully compatible with TCPCI. So the existing tcpci.c
>   may not work for it.

The datasheet for RT1711H does talk about TCPCi and TCPM+TCPC [1].
What are the differences that justify a separate driver?

[1] http://www.richtek.com/assets/product_file/RT1711H/DS1711H-02.pdf


Br,

--
heikki
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  9:01         ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-19  9:01 UTC (permalink / raw)
  To: 'Heikki Krogerus'
  Cc: 'Jun Li', ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb, Guenter Roeck

Hi Heikki,

  For example, the flow of tcpci_init is a little bit different.
  In tcpci_init, there are more parameters need to be set for RT1711H.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Heikki Krogerus [mailto:heikki.krogerus@linux.intel.com]
Sent: Friday, January 19, 2018 4:22 PM
To: shufan_lee(李書帆)
Cc: 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org; Guenter Roeck
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

Hi Shu-Fan,

On Fri, Jan 19, 2018 at 05:48:02AM +0000, shufan_lee(?????????) wrote:
> Hi Jun,
>
>   For now, RT1711H is not fully compatible with TCPCI. So the existing tcpci.c
>   may not work for it.

The datasheet for RT1711H does talk about TCPCi and TCPM+TCPC [1].
What are the differences that justify a separate driver?

[1] http://www.richtek.com/assets/product_file/RT1711H/DS1711H-02.pdf


Br,

--
heikki
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  9:24           ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-19  9:24 UTC (permalink / raw)
  To: shufan_lee(?????|)
  Cc: 'Jun Li', ShuFanLee, cy_huang(??????),
	linux-kernel, linux-usb, Guenter Roeck

Hi,

On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> Hi Heikki,
> 
>   For example, the flow of tcpci_init is a little bit different.
>   In tcpci_init, there are more parameters need to be set for RT1711H.

Different init parameters is really not a reason for a fork of the
driver. The configuration of the TCPC will depend on the platform and
TCPC vendor most cases.


Thanks,

-- 
heikki

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19  9:24           ` Heikki Krogerus
  0 siblings, 0 replies; 51+ messages in thread
From: Heikki Krogerus @ 2018-01-19  9:24 UTC (permalink / raw)
  To: shufan_lee(?????|)
  Cc: 'Jun Li', ShuFanLee, cy_huang(??????),
	linux-kernel, linux-usb, Guenter Roeck

Hi,

On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> Hi Heikki,
> 
>   For example, the flow of tcpci_init is a little bit different.
>   In tcpci_init, there are more parameters need to be set for RT1711H.

Different init parameters is really not a reason for a fork of the
driver. The configuration of the TCPC will depend on the platform and
TCPC vendor most cases.


Thanks,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19 16:02             ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-19 16:02 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: shufan_lee(?????|), 'Jun Li', ShuFanLee, cy_huang(??????),
	linux-kernel, linux-usb

On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> Hi,
> 
> On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > Hi Heikki,
> > 
> >   For example, the flow of tcpci_init is a little bit different.
> >   In tcpci_init, there are more parameters need to be set for RT1711H.
> 
> Different init parameters is really not a reason for a fork of the
> driver. The configuration of the TCPC will depend on the platform and
> TCPC vendor most cases.
> 
Agreed. dwc3 usb support is an excellent example on how to handle this
kind of variation.

Guenter

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-19 16:02             ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-19 16:02 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: shufan_lee(?????|), 'Jun Li', ShuFanLee, cy_huang(??????),
	linux-kernel, linux-usb

On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> Hi,
> 
> On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > Hi Heikki,
> > 
> >   For example, the flow of tcpci_init is a little bit different.
> >   In tcpci_init, there are more parameters need to be set for RT1711H.
> 
> Different init parameters is really not a reason for a fork of the
> driver. The configuration of the TCPC will depend on the platform and
> TCPC vendor most cases.
> 
Agreed. dwc3 usb support is an excellent example on how to handle this
kind of variation.

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-22  2:01               ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-22  2:01 UTC (permalink / raw)
  To: 'Guenter Roeck', Heikki Krogerus
  Cc: 'Jun Li', ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

Dear Heikki and Guenter,

  Because there are still other controls of RT1711H that are different from standard TCPCI, e.x. flow of drp toggling.

  Is the suggestion to customize the difference based on tcpci.c for RT1711H?

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
Sent: Saturday, January 20, 2018 12:03 AM
To: Heikki Krogerus
Cc: shufan_lee(李書帆); 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> Hi,
>
> On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > Hi Heikki,
> >
> >   For example, the flow of tcpci_init is a little bit different.
> >   In tcpci_init, there are more parameters need to be set for RT1711H.
>
> Different init parameters is really not a reason for a fork of the
> driver. The configuration of the TCPC will depend on the platform and
> TCPC vendor most cases.
>
Agreed. dwc3 usb support is an excellent example on how to handle this kind of variation.

Guenter
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-22  2:01               ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-22  2:01 UTC (permalink / raw)
  To: 'Guenter Roeck', Heikki Krogerus
  Cc: 'Jun Li', ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

Dear Heikki and Guenter,

  Because there are still other controls of RT1711H that are different from standard TCPCI, e.x. flow of drp toggling.

  Is the suggestion to customize the difference based on tcpci.c for RT1711H?

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
Sent: Saturday, January 20, 2018 12:03 AM
To: Heikki Krogerus
Cc: shufan_lee(李書帆); 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> Hi,
>
> On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > Hi Heikki,
> >
> >   For example, the flow of tcpci_init is a little bit different.
> >   In tcpci_init, there are more parameters need to be set for RT1711H.
>
> Different init parameters is really not a reason for a fork of the
> driver. The configuration of the TCPC will depend on the platform and
> TCPC vendor most cases.
>
Agreed. dwc3 usb support is an excellent example on how to handle this kind of variation.

Guenter
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-22 18:50                 ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-22 18:50 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Mon, Jan 22, 2018 at 02:01:13AM +0000, shufan_lee(李書帆) wrote:
> Dear Heikki and Guenter,
> 
>   Because there are still other controls of RT1711H that are different from standard TCPCI, e.x. flow of drp toggling.
> 
>   Is the suggestion to customize the difference based on tcpci.c for RT1711H?
> 

In general, I would say yes. However, I won't have ime to review 
the differences between tcpci and the RT1711H. On a high level,
if RT1711H claims to suport TCPCI, it should use (or, rather, extend)
the TCPCI driver.

Note that the TCPCI driver does not claim to be complete; there is
a reason why it is still in staging. However, I would prefer if new
devices claiming to support TCPCI would use it instead of going
their own way. I don't have problems extending it with chip specific
details if needed. Such extensions may be implemented in tcpci.c,
or maybe better in a chip specific file.

Even if you don't use the existing driver, I don't really see why
it would make sense to redeclare all its defines.

Either case, you might want to run checkpatch --strict on your
driver. Most of that it reports is really unnecessary.
Also, some of the code, such as

+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif

is _really_ odd and, at least in this case, simply wrong.

Guenter

> Best Regards,
> *****************************
> Shu-Fan Lee
> Richtek Technology Corporation
> TEL: +886-3-5526789 #2359
> FAX: +886-3-5526612
> *****************************
> 
> -----Original Message-----
> From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
> Sent: Saturday, January 20, 2018 12:03 AM
> To: Heikki Krogerus
> Cc: shufan_lee(李書帆); 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
> 
> On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> > Hi,
> >
> > On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > > Hi Heikki,
> > >
> > >   For example, the flow of tcpci_init is a little bit different.
> > >   In tcpci_init, there are more parameters need to be set for RT1711H.
> >
> > Different init parameters is really not a reason for a fork of the
> > driver. The configuration of the TCPC will depend on the platform and
> > TCPC vendor most cases.
> >
> Agreed. dwc3 usb support is an excellent example on how to handle this kind of variation.
> 
> Guenter
> ************* Email Confidentiality Notice ********************
> 
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-22 18:50                 ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-22 18:50 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Mon, Jan 22, 2018 at 02:01:13AM +0000, shufan_lee(李書帆) wrote:
> Dear Heikki and Guenter,
> 
>   Because there are still other controls of RT1711H that are different from standard TCPCI, e.x. flow of drp toggling.
> 
>   Is the suggestion to customize the difference based on tcpci.c for RT1711H?
> 

In general, I would say yes. However, I won't have ime to review 
the differences between tcpci and the RT1711H. On a high level,
if RT1711H claims to suport TCPCI, it should use (or, rather, extend)
the TCPCI driver.

Note that the TCPCI driver does not claim to be complete; there is
a reason why it is still in staging. However, I would prefer if new
devices claiming to support TCPCI would use it instead of going
their own way. I don't have problems extending it with chip specific
details if needed. Such extensions may be implemented in tcpci.c,
or maybe better in a chip specific file.

Even if you don't use the existing driver, I don't really see why
it would make sense to redeclare all its defines.

Either case, you might want to run checkpatch --strict on your
driver. Most of that it reports is really unnecessary.
Also, some of the code, such as

+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif

is _really_ odd and, at least in this case, simply wrong.

Guenter

> Best Regards,
> *****************************
> Shu-Fan Lee
> Richtek Technology Corporation
> TEL: +886-3-5526789 #2359
> FAX: +886-3-5526612
> *****************************
> 
> -----Original Message-----
> From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
> Sent: Saturday, January 20, 2018 12:03 AM
> To: Heikki Krogerus
> Cc: shufan_lee(李書帆); 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
> 
> On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> > Hi,
> >
> > On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > > Hi Heikki,
> > >
> > >   For example, the flow of tcpci_init is a little bit different.
> > >   In tcpci_init, there are more parameters need to be set for RT1711H.
> >
> > Different init parameters is really not a reason for a fork of the
> > driver. The configuration of the TCPC will depend on the platform and
> > TCPC vendor most cases.
> >
> Agreed. dwc3 usb support is an excellent example on how to handle this kind of variation.
> 
> Guenter
> ************* Email Confidentiality Notice ********************
> 
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
---
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-29  7:19                   ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-29  7:19 UTC (permalink / raw)
  To: 'Guenter Roeck'
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

Hi Guenter,

  We try to use the TCPCI driver on RT1711H and here are some questions.

  Q1. Is current TCPCI driver written according to TypeC Port Controller Interface Specification Revision 1.0 & Version 1.2?
  Q2. Because 0x80~0xFF are vendor defined registers. Some of them are needed to be initialized in tcpci_init for RT1711H (or other chips also).
In the future TCPCI driver, will an initial interface that is called in tcpci_init be released for different vendors to implement.
Or, we should directly copy tcpci.c to tcpci_rt1711h.c and add the different parts?
  Q3. If there are IRQs defined in vendor defined registers, will an interface that is called in tcpci_irq be released for different vendors to implement.
So that they can handle their own IRQs first?
If the suggestion of Q2 is to copy tcpci.c to tcpci_rt1711h.c, then Q3 will not be a problem.
  Q4. According to TCPCI Specification Revision 1.0, we should set DRP = 1 and role to Rp/Rp or Rd/Rd and set LOOK4CONNECTION command to start toggle.
So we modify the tcpci_start_drp_toggling in TCPCI driver as following. Here we write Rd/Rd and DRP = 0 simultaneously so that Rd/Rd takes effect.
Then we write DRP = 1 and set LOOK4CONNECTION command to start toggling.

static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
     enum typec_cc_status cc)
 {
+int ret = 0;
 struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
-unsigned int reg = TCPC_ROLE_CTRL_DRP;
+u32 reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);

 switch (cc) {
 default:
@@ -125,8 +672,19 @@ static int tcpci_start_drp_toggling(stru
 TCPC_ROLE_CTRL_RP_VAL_SHIFT);
 break;
 }
-
-return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+if (ret < 0)
+return ret;
+mdelay(1);
+reg |= TCPC_ROLE_CTRL_DRP;
+ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+if (ret < 0)
+return ret;
+ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+TCPC_CMD_LOOK4CONNECTION);
+if (ret < 0)
+return ret;
+return 0;
 }

  Q5. The tcpci_set_vbus in TCPCI driver uses command to control Sink/Source VBUS.
If our chip does not support power path, i.e. Sink & Source are controlled by other charger IC. Our chip will do nothing while setting these commands.
In the future TCPCI driver, will a framework be applied to notify these events. i.g. power_supply or notifier.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
Sent: Tuesday, January 23, 2018 2:51 AM
To: shufan_lee(李書帆)
Cc: Heikki Krogerus; 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On Mon, Jan 22, 2018 at 02:01:13AM +0000, shufan_lee(李書帆) wrote:
> Dear Heikki and Guenter,
>
>   Because there are still other controls of RT1711H that are different from standard TCPCI, e.x. flow of drp toggling.
>
>   Is the suggestion to customize the difference based on tcpci.c for RT1711H?
>

In general, I would say yes. However, I won't have ime to review the differences between tcpci and the RT1711H. On a high level, if RT1711H claims to suport TCPCI, it should use (or, rather, extend) the TCPCI driver.

Note that the TCPCI driver does not claim to be complete; there is a reason why it is still in staging. However, I would prefer if new devices claiming to support TCPCI would use it instead of going their own way. I don't have problems extending it with chip specific details if needed. Such extensions may be implemented in tcpci.c, or maybe better in a chip specific file.

Even if you don't use the existing driver, I don't really see why it would make sense to redeclare all its defines.

Either case, you might want to run checkpatch --strict on your driver. Most of that it reports is really unnecessary.
Also, some of the code, such as

+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif

is _really_ odd and, at least in this case, simply wrong.

Guenter

> Best Regards,
> *****************************
> Shu-Fan Lee
> Richtek Technology Corporation
> TEL: +886-3-5526789 #2359
> FAX: +886-3-5526612
> *****************************
>
> -----Original Message-----
> From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter
> Roeck
> Sent: Saturday, January 20, 2018 12:03 AM
> To: Heikki Krogerus
> Cc: shufan_lee(李書帆); 'Jun Li'; ShuFanLee; cy_huang(黃啟原);
> linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
>
> On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> > Hi,
> >
> > On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > > Hi Heikki,
> > >
> > >   For example, the flow of tcpci_init is a little bit different.
> > >   In tcpci_init, there are more parameters need to be set for RT1711H.
> >
> > Different init parameters is really not a reason for a fork of the
> > driver. The configuration of the TCPC will depend on the platform
> > and TCPC vendor most cases.
> >
> Agreed. dwc3 usb support is an excellent example on how to handle this kind of variation.
>
> Guenter
> ************* Email Confidentiality Notice ********************
>
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-29  7:19                   ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-29  7:19 UTC (permalink / raw)
  To: 'Guenter Roeck'
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

Hi Guenter,

  We try to use the TCPCI driver on RT1711H and here are some questions.

  Q1. Is current TCPCI driver written according to TypeC Port Controller Interface Specification Revision 1.0 & Version 1.2?
  Q2. Because 0x80~0xFF are vendor defined registers. Some of them are needed to be initialized in tcpci_init for RT1711H (or other chips also).
In the future TCPCI driver, will an initial interface that is called in tcpci_init be released for different vendors to implement.
Or, we should directly copy tcpci.c to tcpci_rt1711h.c and add the different parts?
  Q3. If there are IRQs defined in vendor defined registers, will an interface that is called in tcpci_irq be released for different vendors to implement.
So that they can handle their own IRQs first?
If the suggestion of Q2 is to copy tcpci.c to tcpci_rt1711h.c, then Q3 will not be a problem.
  Q4. According to TCPCI Specification Revision 1.0, we should set DRP = 1 and role to Rp/Rp or Rd/Rd and set LOOK4CONNECTION command to start toggle.
So we modify the tcpci_start_drp_toggling in TCPCI driver as following. Here we write Rd/Rd and DRP = 0 simultaneously so that Rd/Rd takes effect.
Then we write DRP = 1 and set LOOK4CONNECTION command to start toggling.

static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
     enum typec_cc_status cc)
 {
+int ret = 0;
 struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
-unsigned int reg = TCPC_ROLE_CTRL_DRP;
+u32 reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);

 switch (cc) {
 default:
@@ -125,8 +672,19 @@ static int tcpci_start_drp_toggling(stru
 TCPC_ROLE_CTRL_RP_VAL_SHIFT);
 break;
 }
-
-return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+if (ret < 0)
+return ret;
+mdelay(1);
+reg |= TCPC_ROLE_CTRL_DRP;
+ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+if (ret < 0)
+return ret;
+ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+TCPC_CMD_LOOK4CONNECTION);
+if (ret < 0)
+return ret;
+return 0;
 }

  Q5. The tcpci_set_vbus in TCPCI driver uses command to control Sink/Source VBUS.
If our chip does not support power path, i.e. Sink & Source are controlled by other charger IC. Our chip will do nothing while setting these commands.
In the future TCPCI driver, will a framework be applied to notify these events. i.g. power_supply or notifier.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
Sent: Tuesday, January 23, 2018 2:51 AM
To: shufan_lee(李書帆)
Cc: Heikki Krogerus; 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On Mon, Jan 22, 2018 at 02:01:13AM +0000, shufan_lee(李書帆) wrote:
> Dear Heikki and Guenter,
>
>   Because there are still other controls of RT1711H that are different from standard TCPCI, e.x. flow of drp toggling.
>
>   Is the suggestion to customize the difference based on tcpci.c for RT1711H?
>

In general, I would say yes. However, I won't have ime to review the differences between tcpci and the RT1711H. On a high level, if RT1711H claims to suport TCPCI, it should use (or, rather, extend) the TCPCI driver.

Note that the TCPCI driver does not claim to be complete; there is a reason why it is still in staging. However, I would prefer if new devices claiming to support TCPCI would use it instead of going their own way. I don't have problems extending it with chip specific details if needed. Such extensions may be implemented in tcpci.c, or maybe better in a chip specific file.

Even if you don't use the existing driver, I don't really see why it would make sense to redeclare all its defines.

Either case, you might want to run checkpatch --strict on your driver. Most of that it reports is really unnecessary.
Also, some of the code, such as

+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif

is _really_ odd and, at least in this case, simply wrong.

Guenter

> Best Regards,
> *****************************
> Shu-Fan Lee
> Richtek Technology Corporation
> TEL: +886-3-5526789 #2359
> FAX: +886-3-5526612
> *****************************
>
> -----Original Message-----
> From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter
> Roeck
> Sent: Saturday, January 20, 2018 12:03 AM
> To: Heikki Krogerus
> Cc: shufan_lee(李書帆); 'Jun Li'; ShuFanLee; cy_huang(黃啟原);
> linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
> Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
>
> On Fri, Jan 19, 2018 at 11:24:13AM +0200, Heikki Krogerus wrote:
> > Hi,
> >
> > On Fri, Jan 19, 2018 at 09:01:24AM +0000, shufan_lee(?????|) wrote:
> > > Hi Heikki,
> > >
> > >   For example, the flow of tcpci_init is a little bit different.
> > >   In tcpci_init, there are more parameters need to be set for RT1711H.
> >
> > Different init parameters is really not a reason for a fork of the
> > driver. The configuration of the TCPC will depend on the platform
> > and TCPC vendor most cases.
> >
> Agreed. dwc3 usb support is an excellent example on how to handle this kind of variation.
>
> Guenter
> ************* Email Confidentiality Notice ********************
>
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-29 19:57                     ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-29 19:57 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Mon, Jan 29, 2018 at 07:19:06AM +0000, shufan_lee(李書帆) wrote:
> Hi Guenter,
> 
>   We try to use the TCPCI driver on RT1711H and here are some questions.
> 
>   Q1. Is current TCPCI driver written according to TypeC Port Controller Interface Specification Revision 1.0 & Version 1.2?

Revision 1.0. Note that I did not find revision 1.2, only
Revision 1.0 and Revision 2.0 version 1.0.

>   Q2. Because 0x80~0xFF are vendor defined registers. Some of them are needed to be initialized in tcpci_init for RT1711H (or other chips also).
> In the future TCPCI driver, will an initial interface that is called in tcpci_init be released for different vendors to implement.

My suggestion would be to provide an API for vendor specific code.

> Or, we should directly copy tcpci.c to tcpci_rt1711h.c and add the different parts?

That would defeat the purpose. It would be better to implement vendor
specific code in tcpci_rt1711h.c and call it from tcpci.c

Possible example:

struct tcpci_vendor_data {
	int (*init)(...);
	int (*irq_handler)(...);
	...
}

static irqreturn_t tcpci_irq(...)
{
	struct tcpci *tcpci = dev_id;

	...
	if (tcpci->vendor_data->irq_handler) {
		ret = (*tcpci->vendor_data->irq_handler)(...);
		...
	}
	...
}

tcpci_init()
{
	struct tcpci_vendor_data *vendor_data = &tcpci_rt1711h_data;
				// eg from devicetree compatible property

	...
	if (vendor_data->init) {
		ret = (*vendor_data->init)(...);
		if (ret)
			return ret;
	}
	...
}

>   Q3. If there are IRQs defined in vendor defined registers, will an interface that is called in tcpci_irq be released for different vendors to implement.
> So that they can handle their own IRQs first?

If there are vendor specific interrupts, I would assume that vendor specific
code will have to be called. Either the generic interrupt handler can call
vendor specific code, or the vendor specific code handles the interrupt and
calls the generic handler. I don't know at this point which one is better.

> If the suggestion of Q2 is to copy tcpci.c to tcpci_rt1711h.c, then Q3 will not be a problem.
>   Q4. According to TCPCI Specification Revision 1.0, we should set DRP = 1 and role to Rp/Rp or Rd/Rd and set LOOK4CONNECTION command to start toggle.
> So we modify the tcpci_start_drp_toggling in TCPCI driver as following. Here we write Rd/Rd and DRP = 0 simultaneously so that Rd/Rd takes effect.
> Then we write DRP = 1 and set LOOK4CONNECTION command to start toggling.
> 
SGTM.

> static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
>      enum typec_cc_status cc)
>  {
> +int ret = 0;
>  struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
> -unsigned int reg = TCPC_ROLE_CTRL_DRP;
> +u32 reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
> +(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
> 
>  switch (cc) {
>  default:
> @@ -125,8 +672,19 @@ static int tcpci_start_drp_toggling(stru
>  TCPC_ROLE_CTRL_RP_VAL_SHIFT);
>  break;
>  }
> -
> -return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +if (ret < 0)
> +return ret;
> +mdelay(1);

That is bad; you don't want to hold up teh system for that much.
Try to use usleep_range().

> +reg |= TCPC_ROLE_CTRL_DRP;
> +ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +if (ret < 0)
> +return ret;
> +ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
> +TCPC_CMD_LOOK4CONNECTION);
> +if (ret < 0)
> +return ret;
> +return 0;
>  }
> 
>   Q5. The tcpci_set_vbus in TCPCI driver uses command to control Sink/Source VBUS.
> If our chip does not support power path, i.e. Sink & Source are controlled by other charger IC. Our chip will do nothing while setting these commands.
> In the future TCPCI driver, will a framework be applied to notify these events. i.g. power_supply or notifier.
> 
I would think so.

Note that the driver is, at this point, fair game to change to
make it work with your chip. The only condition is that a standard
chip should still work, ie you should not make any changes which
would cause the driver to _only_ work with your chip. Everything else
is fair game.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-29 19:57                     ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-29 19:57 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Mon, Jan 29, 2018 at 07:19:06AM +0000, shufan_lee(李書帆) wrote:
> Hi Guenter,
> 
>   We try to use the TCPCI driver on RT1711H and here are some questions.
> 
>   Q1. Is current TCPCI driver written according to TypeC Port Controller Interface Specification Revision 1.0 & Version 1.2?

Revision 1.0. Note that I did not find revision 1.2, only
Revision 1.0 and Revision 2.0 version 1.0.

>   Q2. Because 0x80~0xFF are vendor defined registers. Some of them are needed to be initialized in tcpci_init for RT1711H (or other chips also).
> In the future TCPCI driver, will an initial interface that is called in tcpci_init be released for different vendors to implement.

My suggestion would be to provide an API for vendor specific code.

> Or, we should directly copy tcpci.c to tcpci_rt1711h.c and add the different parts?

That would defeat the purpose. It would be better to implement vendor
specific code in tcpci_rt1711h.c and call it from tcpci.c

Possible example:

struct tcpci_vendor_data {
	int (*init)(...);
	int (*irq_handler)(...);
	...
}

static irqreturn_t tcpci_irq(...)
{
	struct tcpci *tcpci = dev_id;

	...
	if (tcpci->vendor_data->irq_handler) {
		ret = (*tcpci->vendor_data->irq_handler)(...);
		...
	}
	...
}

tcpci_init()
{
	struct tcpci_vendor_data *vendor_data = &tcpci_rt1711h_data;
				// eg from devicetree compatible property

	...
	if (vendor_data->init) {
		ret = (*vendor_data->init)(...);
		if (ret)
			return ret;
	}
	...
}

>   Q3. If there are IRQs defined in vendor defined registers, will an interface that is called in tcpci_irq be released for different vendors to implement.
> So that they can handle their own IRQs first?

If there are vendor specific interrupts, I would assume that vendor specific
code will have to be called. Either the generic interrupt handler can call
vendor specific code, or the vendor specific code handles the interrupt and
calls the generic handler. I don't know at this point which one is better.

> If the suggestion of Q2 is to copy tcpci.c to tcpci_rt1711h.c, then Q3 will not be a problem.
>   Q4. According to TCPCI Specification Revision 1.0, we should set DRP = 1 and role to Rp/Rp or Rd/Rd and set LOOK4CONNECTION command to start toggle.
> So we modify the tcpci_start_drp_toggling in TCPCI driver as following. Here we write Rd/Rd and DRP = 0 simultaneously so that Rd/Rd takes effect.
> Then we write DRP = 1 and set LOOK4CONNECTION command to start toggling.
> 
SGTM.

> static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
>      enum typec_cc_status cc)
>  {
> +int ret = 0;
>  struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
> -unsigned int reg = TCPC_ROLE_CTRL_DRP;
> +u32 reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
> +(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
> 
>  switch (cc) {
>  default:
> @@ -125,8 +672,19 @@ static int tcpci_start_drp_toggling(stru
>  TCPC_ROLE_CTRL_RP_VAL_SHIFT);
>  break;
>  }
> -
> -return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +if (ret < 0)
> +return ret;
> +mdelay(1);

That is bad; you don't want to hold up teh system for that much.
Try to use usleep_range().

> +reg |= TCPC_ROLE_CTRL_DRP;
> +ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +if (ret < 0)
> +return ret;
> +ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
> +TCPC_CMD_LOOK4CONNECTION);
> +if (ret < 0)
> +return ret;
> +return 0;
>  }
> 
>   Q5. The tcpci_set_vbus in TCPCI driver uses command to control Sink/Source VBUS.
> If our chip does not support power path, i.e. Sink & Source are controlled by other charger IC. Our chip will do nothing while setting these commands.
> In the future TCPCI driver, will a framework be applied to notify these events. i.g. power_supply or notifier.
> 
I would think so.

Note that the driver is, at this point, fair game to change to
make it work with your chip. The only condition is that a standard
chip should still work, ie you should not make any changes which
would cause the driver to _only_ work with your chip. Everything else
is fair game.

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-30 13:21                       ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-30 13:21 UTC (permalink / raw)
  To: 'Guenter Roeck'
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

Hi Guenter,

  For now, it looks like there are two ways to implement vendor data. It would be nice to hear your suggestion.

  1. Set vendor data in the data field of of_device_id.
If I understand correctly, this would be the one more like you mentioned before.
In this case, tcpci_rt1711h_data needs to be defined inside tcpci.c or defined by other file(tcpci_rt1711h.c) but extern in tcpci.c.

For example:
static struct tcpci_vendor_data tcpci_rt1711h_data = {
.init = rt1711h_init;
.irq_handler = rt1711h_irq_handler
};
OR
extern struct tcpci_vendor_data tcpci_rt1711h_data;

Then, put this structure here
static const struct of_device_id tcpci_of_match[] = {
{ .compatible = "usb,tcpci", },
{ .compatible = "richtek,rt1711h", .data = (void *)&tcpci_rt1711h_data },
{},
};

For other vendors who want to handle vendor data also need to add these code inside tcpci.c.
We are not sure that's what you expect or not.


2. In tcpci.c, create a static list_head used to maintain vendor data.
TCPCI driver provides an API for those vendors to add their vendor data in the list.
Then, we could find vendor data in the list according to compatible string.

For example:
In tcpci.h
struct tcpci_vendor_data {
const char *compatible;
int (*init)(...);
int (*irq_handler)(...);
struct list_head list;
};
/* This function adds tcpci_vendor_data to the list */
extern int tcpci_register_vendor_data(struct tcpci_vendor_data *data);

In tcpci.c
static LIST_HEAD(tcpci_vendor_data_list);
int tcpci_register_vendor_data(...)
{
...
list_add(...);
...
}

tcpci_init()
{
...
/* Find correct vendor data */
list_for_each(...)
...
}

In this case, vendor needs to guarantee that vendor data is added to the list before tcpci starts to work.

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Guenter Roeck [mailto:groeck7@gmail.com] On Behalf Of Guenter Roeck
Sent: Tuesday, January 30, 2018 3:58 AM
To: shufan_lee(李書帆)
Cc: Heikki Krogerus; 'Jun Li'; ShuFanLee; cy_huang(黃啟原); linux-kernel@vger.kernel.org; linux-usb@vger.kernel.org
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On Mon, Jan 29, 2018 at 07:19:06AM +0000, shufan_lee(李書帆) wrote:
> Hi Guenter,
>
>   We try to use the TCPCI driver on RT1711H and here are some questions.
>
>   Q1. Is current TCPCI driver written according to TypeC Port Controller Interface Specification Revision 1.0 & Version 1.2?

Revision 1.0. Note that I did not find revision 1.2, only Revision 1.0 and Revision 2.0 version 1.0.

>   Q2. Because 0x80~0xFF are vendor defined registers. Some of them are needed to be initialized in tcpci_init for RT1711H (or other chips also).
> In the future TCPCI driver, will an initial interface that is called in tcpci_init be released for different vendors to implement.

My suggestion would be to provide an API for vendor specific code.

> Or, we should directly copy tcpci.c to tcpci_rt1711h.c and add the different parts?

That would defeat the purpose. It would be better to implement vendor specific code in tcpci_rt1711h.c and call it from tcpci.c

Possible example:

struct tcpci_vendor_data {
int (*init)(...);
int (*irq_handler)(...);
...
}

static irqreturn_t tcpci_irq(...)
{
struct tcpci *tcpci = dev_id;

...
if (tcpci->vendor_data->irq_handler) {
ret = (*tcpci->vendor_data->irq_handler)(...);
...
}
...
}

tcpci_init()
{
struct tcpci_vendor_data *vendor_data = &tcpci_rt1711h_data;
// eg from devicetree compatible property

...
if (vendor_data->init) {
ret = (*vendor_data->init)(...);
if (ret)
return ret;
}
...
}

>   Q3. If there are IRQs defined in vendor defined registers, will an interface that is called in tcpci_irq be released for different vendors to implement.
> So that they can handle their own IRQs first?

If there are vendor specific interrupts, I would assume that vendor specific code will have to be called. Either the generic interrupt handler can call vendor specific code, or the vendor specific code handles the interrupt and calls the generic handler. I don't know at this point which one is better.

> If the suggestion of Q2 is to copy tcpci.c to tcpci_rt1711h.c, then Q3 will not be a problem.
>   Q4. According to TCPCI Specification Revision 1.0, we should set DRP = 1 and role to Rp/Rp or Rd/Rd and set LOOK4CONNECTION command to start toggle.
> So we modify the tcpci_start_drp_toggling in TCPCI driver as following. Here we write Rd/Rd and DRP = 0 simultaneously so that Rd/Rd takes effect.
> Then we write DRP = 1 and set LOOK4CONNECTION command to start toggling.
>
SGTM.

> static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
>      enum typec_cc_status cc)
>  {
> +int ret = 0;
>  struct tcpci *tcpci = tcpc_to_tcpci(tcpc); -unsigned int reg =
> TCPC_ROLE_CTRL_DRP;
> +u32 reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
> +(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
>
>  switch (cc) {
>  default:
> @@ -125,8 +672,19 @@ static int tcpci_start_drp_toggling(stru
> TCPC_ROLE_CTRL_RP_VAL_SHIFT);  break;  }
> -
> -return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
> +ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); if (ret < 0)
> +return ret; mdelay(1);

That is bad; you don't want to hold up teh system for that much.
Try to use usleep_range().

> +reg |= TCPC_ROLE_CTRL_DRP;
> +ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); if (ret < 0)
> +return ret; ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
> +TCPC_CMD_LOOK4CONNECTION); if (ret < 0) return ret; return 0;
>  }
>
>   Q5. The tcpci_set_vbus in TCPCI driver uses command to control Sink/Source VBUS.
> If our chip does not support power path, i.e. Sink & Source are controlled by other charger IC. Our chip will do nothing while setting these commands.
> In the future TCPCI driver, will a framework be applied to notify these events. i.g. power_supply or notifier.
>
I would think so.

Note that the driver is, at this point, fair game to change to make it work with your chip. The only condition is that a standard chip should still work, ie you should not make any changes which would cause the driver to _only_ work with your chip. Everything else is fair game.

Thanks,
Guenter
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-30 13:21                       ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-30 13:21 UTC (permalink / raw)
  To: 'Guenter Roeck'
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

SGkgR3VlbnRlciwNCg0KICBGb3Igbm93LCBpdCBsb29rcyBsaWtlIHRoZXJlIGFyZSB0d28gd2F5
cyB0byBpbXBsZW1lbnQgdmVuZG9yIGRhdGEuIEl0IHdvdWxkIGJlIG5pY2UgdG8gaGVhciB5b3Vy
IHN1Z2dlc3Rpb24uDQoNCiAgMS4gU2V0IHZlbmRvciBkYXRhIGluIHRoZSBkYXRhIGZpZWxkIG9m
IG9mX2RldmljZV9pZC4NCklmIEkgdW5kZXJzdGFuZCBjb3JyZWN0bHksIHRoaXMgd291bGQgYmUg
dGhlIG9uZSBtb3JlIGxpa2UgeW91IG1lbnRpb25lZCBiZWZvcmUuDQpJbiB0aGlzIGNhc2UsIHRj
cGNpX3J0MTcxMWhfZGF0YSBuZWVkcyB0byBiZSBkZWZpbmVkIGluc2lkZSB0Y3BjaS5jIG9yIGRl
ZmluZWQgYnkgb3RoZXIgZmlsZSh0Y3BjaV9ydDE3MTFoLmMpIGJ1dCBleHRlcm4gaW4gdGNwY2ku
Yy4NCg0KRm9yIGV4YW1wbGU6DQpzdGF0aWMgc3RydWN0IHRjcGNpX3ZlbmRvcl9kYXRhIHRjcGNp
X3J0MTcxMWhfZGF0YSA9IHsNCi5pbml0ID0gcnQxNzExaF9pbml0Ow0KLmlycV9oYW5kbGVyID0g
cnQxNzExaF9pcnFfaGFuZGxlcg0KfTsNCk9SDQpleHRlcm4gc3RydWN0IHRjcGNpX3ZlbmRvcl9k
YXRhIHRjcGNpX3J0MTcxMWhfZGF0YTsNCg0KVGhlbiwgcHV0IHRoaXMgc3RydWN0dXJlIGhlcmUN
CnN0YXRpYyBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkIHRjcGNpX29mX21hdGNoW10gPSB7DQp7
IC5jb21wYXRpYmxlID0gInVzYix0Y3BjaSIsIH0sDQp7IC5jb21wYXRpYmxlID0gInJpY2h0ZWss
cnQxNzExaCIsIC5kYXRhID0gKHZvaWQgKikmdGNwY2lfcnQxNzExaF9kYXRhIH0sDQp7fSwNCn07
DQoNCkZvciBvdGhlciB2ZW5kb3JzIHdobyB3YW50IHRvIGhhbmRsZSB2ZW5kb3IgZGF0YSBhbHNv
IG5lZWQgdG8gYWRkIHRoZXNlIGNvZGUgaW5zaWRlIHRjcGNpLmMuDQpXZSBhcmUgbm90IHN1cmUg
dGhhdCdzIHdoYXQgeW91IGV4cGVjdCBvciBub3QuDQoNCg0KMi4gSW4gdGNwY2kuYywgY3JlYXRl
IGEgc3RhdGljIGxpc3RfaGVhZCB1c2VkIHRvIG1haW50YWluIHZlbmRvciBkYXRhLg0KVENQQ0kg
ZHJpdmVyIHByb3ZpZGVzIGFuIEFQSSBmb3IgdGhvc2UgdmVuZG9ycyB0byBhZGQgdGhlaXIgdmVu
ZG9yIGRhdGEgaW4gdGhlIGxpc3QuDQpUaGVuLCB3ZSBjb3VsZCBmaW5kIHZlbmRvciBkYXRhIGlu
IHRoZSBsaXN0IGFjY29yZGluZyB0byBjb21wYXRpYmxlIHN0cmluZy4NCg0KRm9yIGV4YW1wbGU6
DQpJbiB0Y3BjaS5oDQpzdHJ1Y3QgdGNwY2lfdmVuZG9yX2RhdGEgew0KY29uc3QgY2hhciAqY29t
cGF0aWJsZTsNCmludCAoKmluaXQpKC4uLik7DQppbnQgKCppcnFfaGFuZGxlcikoLi4uKTsNCnN0
cnVjdCBsaXN0X2hlYWQgbGlzdDsNCn07DQovKiBUaGlzIGZ1bmN0aW9uIGFkZHMgdGNwY2lfdmVu
ZG9yX2RhdGEgdG8gdGhlIGxpc3QgKi8NCmV4dGVybiBpbnQgdGNwY2lfcmVnaXN0ZXJfdmVuZG9y
X2RhdGEoc3RydWN0IHRjcGNpX3ZlbmRvcl9kYXRhICpkYXRhKTsNCg0KSW4gdGNwY2kuYw0Kc3Rh
dGljIExJU1RfSEVBRCh0Y3BjaV92ZW5kb3JfZGF0YV9saXN0KTsNCmludCB0Y3BjaV9yZWdpc3Rl
cl92ZW5kb3JfZGF0YSguLi4pDQp7DQouLi4NCmxpc3RfYWRkKC4uLik7DQouLi4NCn0NCg0KdGNw
Y2lfaW5pdCgpDQp7DQouLi4NCi8qIEZpbmQgY29ycmVjdCB2ZW5kb3IgZGF0YSAqLw0KbGlzdF9m
b3JfZWFjaCguLi4pDQouLi4NCn0NCg0KSW4gdGhpcyBjYXNlLCB2ZW5kb3IgbmVlZHMgdG8gZ3Vh
cmFudGVlIHRoYXQgdmVuZG9yIGRhdGEgaXMgYWRkZWQgdG8gdGhlIGxpc3QgYmVmb3JlIHRjcGNp
IHN0YXJ0cyB0byB3b3JrLg0KDQpCZXN0IFJlZ2FyZHMsDQoqKioqKioqKioqKioqKioqKioqKioq
KioqKioqKg0KU2h1LUZhbiBMZWUNClJpY2h0ZWsgVGVjaG5vbG9neSBDb3Jwb3JhdGlvbg0KVEVM
OiArODg2LTMtNTUyNjc4OSAjMjM1OQ0KRkFYOiArODg2LTMtNTUyNjYxMg0KKioqKioqKioqKioq
KioqKioqKioqKioqKioqKioNCg0KLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCkZyb206IEd1
ZW50ZXIgUm9lY2sgW21haWx0bzpncm9lY2s3QGdtYWlsLmNvbV0gT24gQmVoYWxmIE9mIEd1ZW50
ZXIgUm9lY2sNClNlbnQ6IFR1ZXNkYXksIEphbnVhcnkgMzAsIDIwMTggMzo1OCBBTQ0KVG86IHNo
dWZhbl9sZWUo5p2O5pu45biGKQ0KQ2M6IEhlaWtraSBLcm9nZXJ1czsgJ0p1biBMaSc7IFNodUZh
bkxlZTsgY3lfaHVhbmco6buD5ZWf5Y6fKTsgbGludXgta2VybmVsQHZnZXIua2VybmVsLm9yZzsg
bGludXgtdXNiQHZnZXIua2VybmVsLm9yZw0KU3ViamVjdDogUmU6IFtQQVRDSF0gVVNCIFRZUEVD
OiBSVDE3MTFIIFR5cGUtQyBDaGlwIERyaXZlcg0KDQpPbiBNb24sIEphbiAyOSwgMjAxOCBhdCAw
NzoxOTowNkFNICswMDAwLCBzaHVmYW5fbGVlKOadjuabuOW4hikgd3JvdGU6DQo+IEhpIEd1ZW50
ZXIsDQo+DQo+ICAgV2UgdHJ5IHRvIHVzZSB0aGUgVENQQ0kgZHJpdmVyIG9uIFJUMTcxMUggYW5k
IGhlcmUgYXJlIHNvbWUgcXVlc3Rpb25zLg0KPg0KPiAgIFExLiBJcyBjdXJyZW50IFRDUENJIGRy
aXZlciB3cml0dGVuIGFjY29yZGluZyB0byBUeXBlQyBQb3J0IENvbnRyb2xsZXIgSW50ZXJmYWNl
IFNwZWNpZmljYXRpb24gUmV2aXNpb24gMS4wICYgVmVyc2lvbiAxLjI/DQoNClJldmlzaW9uIDEu
MC4gTm90ZSB0aGF0IEkgZGlkIG5vdCBmaW5kIHJldmlzaW9uIDEuMiwgb25seSBSZXZpc2lvbiAx
LjAgYW5kIFJldmlzaW9uIDIuMCB2ZXJzaW9uIDEuMC4NCg0KPiAgIFEyLiBCZWNhdXNlIDB4ODB+
MHhGRiBhcmUgdmVuZG9yIGRlZmluZWQgcmVnaXN0ZXJzLiBTb21lIG9mIHRoZW0gYXJlIG5lZWRl
ZCB0byBiZSBpbml0aWFsaXplZCBpbiB0Y3BjaV9pbml0IGZvciBSVDE3MTFIIChvciBvdGhlciBj
aGlwcyBhbHNvKS4NCj4gSW4gdGhlIGZ1dHVyZSBUQ1BDSSBkcml2ZXIsIHdpbGwgYW4gaW5pdGlh
bCBpbnRlcmZhY2UgdGhhdCBpcyBjYWxsZWQgaW4gdGNwY2lfaW5pdCBiZSByZWxlYXNlZCBmb3Ig
ZGlmZmVyZW50IHZlbmRvcnMgdG8gaW1wbGVtZW50Lg0KDQpNeSBzdWdnZXN0aW9uIHdvdWxkIGJl
IHRvIHByb3ZpZGUgYW4gQVBJIGZvciB2ZW5kb3Igc3BlY2lmaWMgY29kZS4NCg0KPiBPciwgd2Ug
c2hvdWxkIGRpcmVjdGx5IGNvcHkgdGNwY2kuYyB0byB0Y3BjaV9ydDE3MTFoLmMgYW5kIGFkZCB0
aGUgZGlmZmVyZW50IHBhcnRzPw0KDQpUaGF0IHdvdWxkIGRlZmVhdCB0aGUgcHVycG9zZS4gSXQg
d291bGQgYmUgYmV0dGVyIHRvIGltcGxlbWVudCB2ZW5kb3Igc3BlY2lmaWMgY29kZSBpbiB0Y3Bj
aV9ydDE3MTFoLmMgYW5kIGNhbGwgaXQgZnJvbSB0Y3BjaS5jDQoNClBvc3NpYmxlIGV4YW1wbGU6
DQoNCnN0cnVjdCB0Y3BjaV92ZW5kb3JfZGF0YSB7DQppbnQgKCppbml0KSguLi4pOw0KaW50ICgq
aXJxX2hhbmRsZXIpKC4uLik7DQouLi4NCn0NCg0Kc3RhdGljIGlycXJldHVybl90IHRjcGNpX2ly
cSguLi4pDQp7DQpzdHJ1Y3QgdGNwY2kgKnRjcGNpID0gZGV2X2lkOw0KDQouLi4NCmlmICh0Y3Bj
aS0+dmVuZG9yX2RhdGEtPmlycV9oYW5kbGVyKSB7DQpyZXQgPSAoKnRjcGNpLT52ZW5kb3JfZGF0
YS0+aXJxX2hhbmRsZXIpKC4uLik7DQouLi4NCn0NCi4uLg0KfQ0KDQp0Y3BjaV9pbml0KCkNCnsN
CnN0cnVjdCB0Y3BjaV92ZW5kb3JfZGF0YSAqdmVuZG9yX2RhdGEgPSAmdGNwY2lfcnQxNzExaF9k
YXRhOw0KLy8gZWcgZnJvbSBkZXZpY2V0cmVlIGNvbXBhdGlibGUgcHJvcGVydHkNCg0KLi4uDQpp
ZiAodmVuZG9yX2RhdGEtPmluaXQpIHsNCnJldCA9ICgqdmVuZG9yX2RhdGEtPmluaXQpKC4uLik7
DQppZiAocmV0KQ0KcmV0dXJuIHJldDsNCn0NCi4uLg0KfQ0KDQo+ICAgUTMuIElmIHRoZXJlIGFy
ZSBJUlFzIGRlZmluZWQgaW4gdmVuZG9yIGRlZmluZWQgcmVnaXN0ZXJzLCB3aWxsIGFuIGludGVy
ZmFjZSB0aGF0IGlzIGNhbGxlZCBpbiB0Y3BjaV9pcnEgYmUgcmVsZWFzZWQgZm9yIGRpZmZlcmVu
dCB2ZW5kb3JzIHRvIGltcGxlbWVudC4NCj4gU28gdGhhdCB0aGV5IGNhbiBoYW5kbGUgdGhlaXIg
b3duIElSUXMgZmlyc3Q/DQoNCklmIHRoZXJlIGFyZSB2ZW5kb3Igc3BlY2lmaWMgaW50ZXJydXB0
cywgSSB3b3VsZCBhc3N1bWUgdGhhdCB2ZW5kb3Igc3BlY2lmaWMgY29kZSB3aWxsIGhhdmUgdG8g
YmUgY2FsbGVkLiBFaXRoZXIgdGhlIGdlbmVyaWMgaW50ZXJydXB0IGhhbmRsZXIgY2FuIGNhbGwg
dmVuZG9yIHNwZWNpZmljIGNvZGUsIG9yIHRoZSB2ZW5kb3Igc3BlY2lmaWMgY29kZSBoYW5kbGVz
IHRoZSBpbnRlcnJ1cHQgYW5kIGNhbGxzIHRoZSBnZW5lcmljIGhhbmRsZXIuIEkgZG9uJ3Qga25v
dyBhdCB0aGlzIHBvaW50IHdoaWNoIG9uZSBpcyBiZXR0ZXIuDQoNCj4gSWYgdGhlIHN1Z2dlc3Rp
b24gb2YgUTIgaXMgdG8gY29weSB0Y3BjaS5jIHRvIHRjcGNpX3J0MTcxMWguYywgdGhlbiBRMyB3
aWxsIG5vdCBiZSBhIHByb2JsZW0uDQo+ICAgUTQuIEFjY29yZGluZyB0byBUQ1BDSSBTcGVjaWZp
Y2F0aW9uIFJldmlzaW9uIDEuMCwgd2Ugc2hvdWxkIHNldCBEUlAgPSAxIGFuZCByb2xlIHRvIFJw
L1JwIG9yIFJkL1JkIGFuZCBzZXQgTE9PSzRDT05ORUNUSU9OIGNvbW1hbmQgdG8gc3RhcnQgdG9n
Z2xlLg0KPiBTbyB3ZSBtb2RpZnkgdGhlIHRjcGNpX3N0YXJ0X2RycF90b2dnbGluZyBpbiBUQ1BD
SSBkcml2ZXIgYXMgZm9sbG93aW5nLiBIZXJlIHdlIHdyaXRlIFJkL1JkIGFuZCBEUlAgPSAwIHNp
bXVsdGFuZW91c2x5IHNvIHRoYXQgUmQvUmQgdGFrZXMgZWZmZWN0Lg0KPiBUaGVuIHdlIHdyaXRl
IERSUCA9IDEgYW5kIHNldCBMT09LNENPTk5FQ1RJT04gY29tbWFuZCB0byBzdGFydCB0b2dnbGlu
Zy4NCj4NClNHVE0uDQoNCj4gc3RhdGljIGludCB0Y3BjaV9zdGFydF9kcnBfdG9nZ2xpbmcoc3Ry
dWN0IHRjcGNfZGV2ICp0Y3BjLA0KPiAgICAgIGVudW0gdHlwZWNfY2Nfc3RhdHVzIGNjKQ0KPiAg
ew0KPiAraW50IHJldCA9IDA7DQo+ICBzdHJ1Y3QgdGNwY2kgKnRjcGNpID0gdGNwY190b190Y3Bj
aSh0Y3BjKTsgLXVuc2lnbmVkIGludCByZWcgPQ0KPiBUQ1BDX1JPTEVfQ1RSTF9EUlA7DQo+ICt1
MzIgcmVnID0gKFRDUENfUk9MRV9DVFJMX0NDX1JEIDw8IFRDUENfUk9MRV9DVFJMX0NDMV9TSElG
VCkgfA0KPiArKFRDUENfUk9MRV9DVFJMX0NDX1JEIDw8IFRDUENfUk9MRV9DVFJMX0NDMl9TSElG
VCk7DQo+DQo+ICBzd2l0Y2ggKGNjKSB7DQo+ICBkZWZhdWx0Og0KPiBAQCAtMTI1LDggKzY3Miwx
OSBAQCBzdGF0aWMgaW50IHRjcGNpX3N0YXJ0X2RycF90b2dnbGluZyhzdHJ1DQo+IFRDUENfUk9M
RV9DVFJMX1JQX1ZBTF9TSElGVCk7ICBicmVhazsgIH0NCj4gLQ0KPiAtcmV0dXJuIHJlZ21hcF93
cml0ZSh0Y3BjaS0+cmVnbWFwLCBUQ1BDX1JPTEVfQ1RSTCwgcmVnKTsNCj4gK3JldCA9IHJlZ21h
cF93cml0ZSh0Y3BjaS0+cmVnbWFwLCBUQ1BDX1JPTEVfQ1RSTCwgcmVnKTsgaWYgKHJldCA8IDAp
DQo+ICtyZXR1cm4gcmV0OyBtZGVsYXkoMSk7DQoNClRoYXQgaXMgYmFkOyB5b3UgZG9uJ3Qgd2Fu
dCB0byBob2xkIHVwIHRlaCBzeXN0ZW0gZm9yIHRoYXQgbXVjaC4NClRyeSB0byB1c2UgdXNsZWVw
X3JhbmdlKCkuDQoNCj4gK3JlZyB8PSBUQ1BDX1JPTEVfQ1RSTF9EUlA7DQo+ICtyZXQgPSByZWdt
YXBfd3JpdGUodGNwY2ktPnJlZ21hcCwgVENQQ19ST0xFX0NUUkwsIHJlZyk7IGlmIChyZXQgPCAw
KQ0KPiArcmV0dXJuIHJldDsgcmV0ID0gcmVnbWFwX3dyaXRlKHRjcGNpLT5yZWdtYXAsIFRDUENf
Q09NTUFORCwNCj4gK1RDUENfQ01EX0xPT0s0Q09OTkVDVElPTik7IGlmIChyZXQgPCAwKSByZXR1
cm4gcmV0OyByZXR1cm4gMDsNCj4gIH0NCj4NCj4gICBRNS4gVGhlIHRjcGNpX3NldF92YnVzIGlu
IFRDUENJIGRyaXZlciB1c2VzIGNvbW1hbmQgdG8gY29udHJvbCBTaW5rL1NvdXJjZSBWQlVTLg0K
PiBJZiBvdXIgY2hpcCBkb2VzIG5vdCBzdXBwb3J0IHBvd2VyIHBhdGgsIGkuZS4gU2luayAmIFNv
dXJjZSBhcmUgY29udHJvbGxlZCBieSBvdGhlciBjaGFyZ2VyIElDLiBPdXIgY2hpcCB3aWxsIGRv
IG5vdGhpbmcgd2hpbGUgc2V0dGluZyB0aGVzZSBjb21tYW5kcy4NCj4gSW4gdGhlIGZ1dHVyZSBU
Q1BDSSBkcml2ZXIsIHdpbGwgYSBmcmFtZXdvcmsgYmUgYXBwbGllZCB0byBub3RpZnkgdGhlc2Ug
ZXZlbnRzLiBpLmcuIHBvd2VyX3N1cHBseSBvciBub3RpZmllci4NCj4NCkkgd291bGQgdGhpbmsg
c28uDQoNCk5vdGUgdGhhdCB0aGUgZHJpdmVyIGlzLCBhdCB0aGlzIHBvaW50LCBmYWlyIGdhbWUg
dG8gY2hhbmdlIHRvIG1ha2UgaXQgd29yayB3aXRoIHlvdXIgY2hpcC4gVGhlIG9ubHkgY29uZGl0
aW9uIGlzIHRoYXQgYSBzdGFuZGFyZCBjaGlwIHNob3VsZCBzdGlsbCB3b3JrLCBpZSB5b3Ugc2hv
dWxkIG5vdCBtYWtlIGFueSBjaGFuZ2VzIHdoaWNoIHdvdWxkIGNhdXNlIHRoZSBkcml2ZXIgdG8g
X29ubHlfIHdvcmsgd2l0aCB5b3VyIGNoaXAuIEV2ZXJ5dGhpbmcgZWxzZSBpcyBmYWlyIGdhbWUu
DQoNClRoYW5rcywNCkd1ZW50ZXINCioqKioqKioqKioqKiogRW1haWwgQ29uZmlkZW50aWFsaXR5
IE5vdGljZSAqKioqKioqKioqKioqKioqKioqKg0KDQpUaGUgaW5mb3JtYXRpb24gY29udGFpbmVk
IGluIHRoaXMgZS1tYWlsIG1lc3NhZ2UgKGluY2x1ZGluZyBhbnkgYXR0YWNobWVudHMpIG1heSBi
ZSBjb25maWRlbnRpYWwsIHByb3ByaWV0YXJ5LCBwcml2aWxlZ2VkLCBvciBvdGhlcndpc2UgZXhl
bXB0IGZyb20gZGlzY2xvc3VyZSB1bmRlciBhcHBsaWNhYmxlIGxhd3MuIEl0IGlzIGludGVuZGVk
IHRvIGJlIGNvbnZleWVkIG9ubHkgdG8gdGhlIGRlc2lnbmF0ZWQgcmVjaXBpZW50KHMpLiBBbnkg
dXNlLCBkaXNzZW1pbmF0aW9uLCBkaXN0cmlidXRpb24sIHByaW50aW5nLCByZXRhaW5pbmcgb3Ig
Y29weWluZyBvZiB0aGlzIGUtbWFpbCAoaW5jbHVkaW5nIGl0cyBhdHRhY2htZW50cykgYnkgdW5p
bnRlbmRlZCByZWNpcGllbnQocykgaXMgc3RyaWN0bHkgcHJvaGliaXRlZCBhbmQgbWF5IGJlIHVu
bGF3ZnVsLiBJZiB5b3UgYXJlIG5vdCBhbiBpbnRlbmRlZCByZWNpcGllbnQgb2YgdGhpcyBlLW1h
aWwsIG9yIGJlbGlldmUgdGhhdCB5b3UgaGF2ZSByZWNlaXZlZCB0aGlzIGUtbWFpbCBpbiBlcnJv
ciwgcGxlYXNlIG5vdGlmeSB0aGUgc2VuZGVyIGltbWVkaWF0ZWx5IChieSByZXBseWluZyB0byB0
aGlzIGUtbWFpbCksIGRlbGV0ZSBhbnkgYW5kIGFsbCBjb3BpZXMgb2YgdGhpcyBlLW1haWwgKGlu
Y2x1ZGluZyBhbnkgYXR0YWNobWVudHMpIGZyb20geW91ciBzeXN0ZW0sIGFuZCBkbyBub3QgZGlz
Y2xvc2UgdGhlIGNvbnRlbnQgb2YgdGhpcyBlLW1haWwgdG8gYW55IG90aGVyIHBlcnNvbi4gVGhh
bmsgeW91IQ0K
---
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-30 23:25                         ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-30 23:25 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Tue, Jan 30, 2018 at 01:21:01PM +0000, shufan_lee(李書帆) wrote:
> Hi Guenter,
> 
>   For now, it looks like there are two ways to implement vendor data. It would be nice to hear your suggestion.
> 
>   1. Set vendor data in the data field of of_device_id.
> If I understand correctly, this would be the one more like you mentioned before.
> In this case, tcpci_rt1711h_data needs to be defined inside tcpci.c or defined by other file(tcpci_rt1711h.c) but extern in tcpci.c.
> 
> For example:
> static struct tcpci_vendor_data tcpci_rt1711h_data = {
> .init = rt1711h_init;
> .irq_handler = rt1711h_irq_handler
> };
> OR
> extern struct tcpci_vendor_data tcpci_rt1711h_data;
> 
> Then, put this structure here
> static const struct of_device_id tcpci_of_match[] = {
> { .compatible = "usb,tcpci", },
> { .compatible = "richtek,rt1711h", .data = (void *)&tcpci_rt1711h_data },
> {},
> };
> 
> For other vendors who want to handle vendor data also need to add these code inside tcpci.c.
> We are not sure that's what you expect or not.
> 

I would not say expect, but it is one possibility. Sure,
it requires rt1711h_init and rt1711h_irq_handler to be public,
and a bit of ifdefery, but it is simpler than option #2.

Another option would be to instantiate tcpci from vendor drivers.
In this case, there would be an exported registration function which
would be called from tcpci_rt1711h.c:rt1711h_init(), similar to
tcpm_register_port(). In that case, tcpci_rt1711h.c would have its
own init function and compatible property.

To do that, you would effectively split tcpci_probe() into two functions,
tcpci_probe() and tcpci_register_port(), and call tcpci_register_port()
from the probe function.

int tcpci_register_port(struct i2c_client *client,
			const struct tcpci_vendor_data *data)
{
	/* pretty much verything currently done in the probe function */
}
EXPORT_SYMBOL(tcpci_register_port);

static int tcpci_probe(struct i2c_client *client,
                       const struct i2c_device_id *i2c_id)
{
	return tcpci_register_port(client, NULL);
}

Maybe you can experiment with this and see if it makes sense.
If not, you can still fall back to option #1.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 51+ messages in thread

* USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-30 23:25                         ` Guenter Roeck
  0 siblings, 0 replies; 51+ messages in thread
From: Guenter Roeck @ 2018-01-30 23:25 UTC (permalink / raw)
  To: shufan_lee(李書帆)
  Cc: Heikki Krogerus, 'Jun Li',
	ShuFanLee, cy_huang(黃啟原),
	linux-kernel, linux-usb

On Tue, Jan 30, 2018 at 01:21:01PM +0000, shufan_lee(李書帆) wrote:
> Hi Guenter,
> 
>   For now, it looks like there are two ways to implement vendor data. It would be nice to hear your suggestion.
> 
>   1. Set vendor data in the data field of of_device_id.
> If I understand correctly, this would be the one more like you mentioned before.
> In this case, tcpci_rt1711h_data needs to be defined inside tcpci.c or defined by other file(tcpci_rt1711h.c) but extern in tcpci.c.
> 
> For example:
> static struct tcpci_vendor_data tcpci_rt1711h_data = {
> .init = rt1711h_init;
> .irq_handler = rt1711h_irq_handler
> };
> OR
> extern struct tcpci_vendor_data tcpci_rt1711h_data;
> 
> Then, put this structure here
> static const struct of_device_id tcpci_of_match[] = {
> { .compatible = "usb,tcpci", },
> { .compatible = "richtek,rt1711h", .data = (void *)&tcpci_rt1711h_data },
> {},
> };
> 
> For other vendors who want to handle vendor data also need to add these code inside tcpci.c.
> We are not sure that's what you expect or not.
> 

I would not say expect, but it is one possibility. Sure,
it requires rt1711h_init and rt1711h_irq_handler to be public,
and a bit of ifdefery, but it is simpler than option #2.

Another option would be to instantiate tcpci from vendor drivers.
In this case, there would be an exported registration function which
would be called from tcpci_rt1711h.c:rt1711h_init(), similar to
tcpm_register_port(). In that case, tcpci_rt1711h.c would have its
own init function and compatible property.

To do that, you would effectively split tcpci_probe() into two functions,
tcpci_probe() and tcpci_register_port(), and call tcpci_register_port()
from the probe function.

int tcpci_register_port(struct i2c_client *client,
			const struct tcpci_vendor_data *data)
{
	/* pretty much verything currently done in the probe function */
}
EXPORT_SYMBOL(tcpci_register_port);

static int tcpci_probe(struct i2c_client *client,
                       const struct i2c_device_id *i2c_id)
{
	return tcpci_register_port(client, NULL);
}

Maybe you can experiment with this and see if it makes sense.
If not, you can still fall back to option #1.

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
  2018-01-09 17:26     ` Randy Dunlap
@ 2018-01-10  2:49       ` shufan_lee(李書帆)
  0 siblings, 0 replies; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-10  2:49 UTC (permalink / raw)
  To: 'Randy Dunlap', heikki.krogerus
  Cc: linux-kernel, cy_huang(黃啟原)

Hi Randy,

  It is different. It seems that format is changed after sending.

  I'll try to fix that and send again, thank you!

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Randy Dunlap [mailto:rdunlap@infradead.org]
Sent: Wednesday, January 10, 2018 1:26 AM
To: shufan_lee(李書帆); heikki.krogerus@linux.intel.com
Cc: linux-kernel@vger.kernel.org; cy_huang(黃啟原)
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On 01/09/18 00:45, shufan_lee(李書帆) wrote:
> Hi Randy,
>
>  I've checked the email and tabs are still there.
>
>  Would your mind providing an example of what you see and highlight the incorrect format?
>
>  Thank you!
>
> Best Regards,

Please see your message in the email archive at:
https://marc.info/?l=linux-kernel&m=151546820509868&w=2

Is that how it was supposed to be formatted?

Thanks.

> *****************************
> Shu-Fan Lee
> Richtek Technology Corporation
> TEL: +886-3-5526789 #2359
> FAX: +886-3-5526612
> *****************************
>
> -----Original Message-----
> From: Randy Dunlap [mailto:rdunlap@infradead.org]
> Sent: Tuesday, January 09, 2018 2:19 PM
> To: shufan_lee(李書帆); heikki.krogerus@linux.intel.com
> Cc: linux-kernel@vger.kernel.org; cy_huang(黃啟原)
> Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
>
> On 01/08/18 19:13, shufan_lee(李書帆) wrote:
>> From: ShuFan Lee <shufan_lee@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: ShuFan Lee <shufan_lee@richtek.com> ---​
>>
>> Documentation/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(+)
>
> Hi,
> It looks like your mail client or your mail server (or something else) ate all of the tabs that would make this patch look normal.
>
> When you have that fixed, I guess that you should also Cc: the linux-usb mailing list with the patch.
>
> Thanks.
>
>> ************* Email Confidentiality Notice ********************
>>
>> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
>>
>
>
> --
> ~Randy
> ************* Email Confidentiality Notice ********************
>
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
>


--
~Randy
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
  2018-01-09  8:45   ` shufan_lee(李書帆)
@ 2018-01-09 17:26     ` Randy Dunlap
  2018-01-10  2:49       ` shufan_lee(李書帆)
  0 siblings, 1 reply; 51+ messages in thread
From: Randy Dunlap @ 2018-01-09 17:26 UTC (permalink / raw)
  To: shufan_lee(李書帆), heikki.krogerus
  Cc: linux-kernel, cy_huang(黃啟原)

On 01/09/18 00:45, shufan_lee(李書帆) wrote:
> Hi Randy,
> 
>  I've checked the email and tabs are still there.
> 
>  Would your mind providing an example of what you see and highlight the incorrect format?
> 
>  Thank you!
> 
> Best Regards,

Please see your message in the email archive at:
https://marc.info/?l=linux-kernel&m=151546820509868&w=2

Is that how it was supposed to be formatted?

Thanks.

> *****************************
> Shu-Fan Lee
> Richtek Technology Corporation
> TEL: +886-3-5526789 #2359
> FAX: +886-3-5526612
> *****************************
> 
> -----Original Message-----
> From: Randy Dunlap [mailto:rdunlap@infradead.org]
> Sent: Tuesday, January 09, 2018 2:19 PM
> To: shufan_lee(李書帆); heikki.krogerus@linux.intel.com
> Cc: linux-kernel@vger.kernel.org; cy_huang(黃啟原)
> Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
> 
> On 01/08/18 19:13, shufan_lee(李書帆) wrote:
>> From: ShuFan Lee <shufan_lee@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: ShuFan Lee <shufan_lee@richtek.com> ---​
>>
>> Documentation/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(+)
> 
> Hi,
> It looks like your mail client or your mail server (or something else) ate all of the tabs that would make this patch look normal.
> 
> When you have that fixed, I guess that you should also Cc: the linux-usb mailing list with the patch.
> 
> Thanks.
> 
>> ************* Email Confidentiality Notice ********************
>>
>> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
>>
> 
> 
> --
> ~Randy
> ************* Email Confidentiality Notice ********************
> 
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
> 


-- 
~Randy

^ permalink raw reply	[flat|nested] 51+ messages in thread

* RE: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
  2018-01-09  6:18 ` Randy Dunlap
@ 2018-01-09  8:45   ` shufan_lee(李書帆)
  2018-01-09 17:26     ` Randy Dunlap
  0 siblings, 1 reply; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-09  8:45 UTC (permalink / raw)
  To: 'Randy Dunlap', heikki.krogerus
  Cc: linux-kernel, cy_huang(黃啟原)

Hi Randy,

 I've checked the email and tabs are still there.

 Would your mind providing an example of what you see and highlight the incorrect format?

 Thank you!

Best Regards,
*****************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************

-----Original Message-----
From: Randy Dunlap [mailto:rdunlap@infradead.org]
Sent: Tuesday, January 09, 2018 2:19 PM
To: shufan_lee(李書帆); heikki.krogerus@linux.intel.com
Cc: linux-kernel@vger.kernel.org; cy_huang(黃啟原)
Subject: Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver

On 01/08/18 19:13, shufan_lee(李書帆) wrote:
> From: ShuFan Lee <shufan_lee@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: ShuFan Lee <shufan_lee@richtek.com> ---​
>
> Documentation/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(+)

Hi,
It looks like your mail client or your mail server (or something else) ate all of the tabs that would make this patch look normal.

When you have that fixed, I guess that you should also Cc: the linux-usb mailing list with the patch.

Thanks.

> ************* Email Confidentiality Notice ********************
>
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
>


--
~Randy
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
  2018-01-09  3:13 [PATCH] " shufan_lee(李書帆)
@ 2018-01-09  6:18 ` Randy Dunlap
  2018-01-09  8:45   ` shufan_lee(李書帆)
  0 siblings, 1 reply; 51+ messages in thread
From: Randy Dunlap @ 2018-01-09  6:18 UTC (permalink / raw)
  To: shufan_lee(李書帆), heikki.krogerus
  Cc: linux-kernel, cy_huang(黃啟原)

On 01/08/18 19:13, shufan_lee(李書帆) wrote:
> From: ShuFan Lee <shufan_lee@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: ShuFan Lee <shufan_lee@richtek.com>
> ---​
> 
> Documentation/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(+)

Hi,
It looks like your mail client or your mail server (or something else)
ate all of the tabs that would make this patch look normal.

When you have that fixed, I guess that you should also Cc: the linux-usb
mailing list with the patch.

Thanks.

> ************* Email Confidentiality Notice ********************
> 
> The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!
> 


-- 
~Randy

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH] USB TYPEC: RT1711H Type-C Chip Driver
@ 2018-01-09  3:13 shufan_lee(李書帆)
  2018-01-09  6:18 ` Randy Dunlap
  0 siblings, 1 reply; 51+ messages in thread
From: shufan_lee(李書帆) @ 2018-01-09  3:13 UTC (permalink / raw)
  To: heikki.krogerus; +Cc: linux-kernel, cy_huang(黃啟原)

From: ShuFan Lee <shufan_lee@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: ShuFan Lee <shufan_lee@richtek.com>
---​

Documentation/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(+)

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_ENTRIES1024
+#define LOG_BUFFER_ENTRY_SIZE128 /* 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@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_A0x2170
+#define RT1711H_DID_B0x2171
+#define RT1711H_DID_C0x2172
+#define RT1711H_DID_D0x2173
+
+/* 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_DISCONNECTBIT(11)
+#define RT1711H_REG_ALERT_RX_BUF_OVFBIT(10)
+#define RT1711H_REG_ALERT_FAULTBIT(9)
+#define RT1711H_REG_ALERT_LO_VOLTBIT(8)
+#define RT1711H_REG_ALERT_HI_VOLTBIT(7)
+#define RT1711H_REG_ALERT_TX_SUCCESSBIT(6)
+#define RT1711H_REG_ALERT_TX_DISCARDEDBIT(5)
+#define RT1711H_REG_ALERT_TX_FAILEDBIT(4)
+#define RT1711H_REG_ALERT_RX_HARD_RSTBIT(3)
+#define RT1711H_REG_ALERT_RX_STATUSBIT(2)
+#define RT1711H_REG_ALERT_POWER_STATUSBIT(1)
+#define RT1711H_REG_ALERT_CC_STATUSBIT(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_INITIALBIT(6)
+#define RT1711H_REG_POWER_STATUS_SRC_HVBIT(5)
+#define RT1711H_REG_POWER_STATUS_SRC_VBUSBIT(4)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRES_DETBIT(3)
+#define RT1711H_REG_POWER_STATUS_VBUS_PRESBIT(2)
+#define RT1711H_REG_POWER_STATUS_VCONN_PRESBIT(1)
+#define RT1711H_REG_POWER_STATUS_SINK_VBUSBIT(0)
+
+/*
+ * RT1711H_REG_FAULT_STATUS_MASK(0x15)
+ * RT1711H_REG_FAULT_STATUS(0x1F)
+ */
+#define RT1711H_REG_FAULT_STATUS_VCONN_OVBIT(7)
+#define RT1711H_REG_FAULT_STATUS_FORCE_OFF_VBUSBIT(6)
+#define RT1711H_REG_FAULT_STATUS_AUTO_DISC_FAILBIT(5)
+#define RT1711H_REG_FAULT_STATUS_FORCE_DISC_FAILBIT(4)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OCBIT(3)
+#define RT1711H_REG_FAULT_STATUS_VBUS_OVBIT(2)
+#define RT1711H_REG_FAULT_STATUS_VCONN_OCBIT(1)
+#define RT1711H_REG_FAULT_STATUS_I2C_ERRORBIT(0)
+
+/*
+ * RT1711H_REG_ROLE_CTRL(0x1A)
+ */
+#define RT1711H_REG_ROLE_CTRL_DRPBIT(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_MODEBIT(1)
+#define RT1711H_REG_TCPC_CTRL_PLUG_ORIENTBIT(0)
+
+/*
+ * RT1711H_REG_FAULT_CTRL(0x1B)
+ */
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OVBIT(7)
+#define RT1711H_REG_FAULT_CTRL_DIS_SNK_VBUS_OCBIT(2)
+#define RT1711H_REG_FAULT_CTRL_DIS_VCONN_OCBIT(0)
+
+/*
+ * RT1711H_REG_POWER_CTRL(0x1C)
+ */
+#define RT1711H_REG_POWER_CTRL_VCONNBIT(0)
+
+/*
+ * RT1711H_REG_CC_STATUS(0x1D)
+ */
+#define RT1711H_REG_CC_STATUS_DRP_TOGGLINGBIT(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_ENBIT(7)
+#define RT1711H_REG_CLK_BCLK2_ENBIT(6)
+#define RT1711H_REG_CLK_BCLK2_TG_ENBIT(5)
+#define RT1711H_REG_CLK_DIV_300K_ENBIT(3)
+#define RT1711H_REG_CLK_CK_300K_ENBIT(2)
+#define RT1711H_REG_CLK_BCLK_ENBIT(1)
+#define RT1711H_REG_CLK_BCLK_TH_ENBIT(0)
+
+/*
+ * RT1711H_REG_CLK_CTRL3(0x88)
+ */
+#define RT1711H_REG_CLK_OSCMUX_RG_ENBIT(7)
+#define RT1711H_REG_CLK_CK_24M_ENBIT(6)
+#define RT1711H_REG_CLK_OSC_RG_ENBIT(5)
+#define RT1711H_REG_CLK_DIV_2P4M_ENBIT(4)
+#define RT1711H_REG_CLK_CK_2P4M_ENBIT(3)
+#define RT1711H_REG_CLK_PCLK_ENBIT(2)
+#define RT1711H_REG_CLK_PCLK_RG_ENBIT(1)
+#define RT1711H_REG_CLK_PCLK_TG_ENBIT(0)
+
+/*
+ * RT1711H_REG_BMC_CTRL(0x90)
+ */
+#define RT1711H_REG_IDLE_ENBIT(6)
+#define RT1711H_REG_DISCHARGE_ENBIT(5)
+#define RT1711H_REG_BMCIO_LPRPRDBIT(4)
+#define RT1711H_REG_BMCIO_LPENBIT(3)
+#define RT1711H_REG_BMCIO_BG_ENBIT(2)
+#define RT1711H_REG_VBUS_DET_ENBIT(1)
+#define RT1711H_REG_BMCIO_OSC_ENBIT(0)
+
+/*
+ * RT1711H_REG_RT_STATUS(0x97)
+ */
+#define RT1711H_REG_RA_DETACHBIT(5)
+#define RT1711H_REG_VBUS_80BIT(1)
+
+/*
+ * RT1711H_REG_RT_INT(0x98)
+ */
+#define RT1711H_REG_INT_RA_DETACHBIT(5)
+#define RT1711H_REG_INT_WATCHDOGBIT(2)
+#define RT1711H_REG_INT_VBUS_80BIT(1)
+#define RT1711H_REG_INT_WAKEUPBIT(0)
+
+/*
+ * RT1711H_REG_RT_MASK(0x99)
+ */
+#define RT1711H_REG_M_RA_DETACHBIT(5)
+#define RT1711H_REG_M_WATCHDOGBIT(2)
+#define RT1711H_REG_M_VBUS_80BIT(1)
+#define RT1711H_REG_M_WAKEUPBIT(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_SELBIT(7)
+#define RT1711H_REG_SHIPPING_OFFBIT(5)
+#define RT1711H_REG_AUTOIDLE_ENBIT(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_ENBIT(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_ENBIT(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_ENBIT(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 */

Best Regards,
*****************************************
Shu-Fan Lee
Richtek Technology Corporation
TEL: +886-3-5526789 #2359
FAX: +886-3-5526612
*****************************************
************* Email Confidentiality Notice ********************

The information contained in this e-mail message (including any attachments) may be confidential, proprietary, privileged, or otherwise exempt from disclosure under applicable laws. It is intended to be conveyed only to the designated recipient(s). Any use, dissemination, distribution, printing, retaining or copying of this e-mail (including its attachments) by unintended recipient(s) is strictly prohibited and may be unlawful. If you are not an intended recipient of this e-mail, or believe that you have received this e-mail in error, please notify the sender immediately (by replying to this e-mail), delete any and all copies of this e-mail (including any attachments) from your system, and do not disclose the content of this e-mail to any other person. Thank you!

^ permalink raw reply related	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2018-01-30 23:25 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-10  6:59 [PATCH] USB TYPEC: RT1711H Type-C Chip Driver ShuFanLee
2018-01-10  6:59 ` ShuFanLee
2018-01-17  9:30 ` [PATCH] " shufan_lee(李書帆)
2018-01-17  9:30   ` shufan_lee(李書帆)
2018-01-17 11:08   ` [PATCH] " Heikki Krogerus
2018-01-17 11:08     ` Heikki Krogerus
2018-01-17 11:14     ` [PATCH] " Greg KH
2018-01-17 11:14       ` Greg Kroah-Hartman
2018-01-17 12:00       ` [PATCH] " Heikki Krogerus
2018-01-17 12:00         ` Heikki Krogerus
2018-01-17 13:31         ` [PATCH] " Greg KH
2018-01-17 13:31           ` Greg Kroah-Hartman
2018-01-17 13:33 ` [PATCH] " Greg KH
2018-01-17 13:33   ` Greg KH
2018-01-17 13:33 ` [PATCH] " Greg KH
2018-01-17 13:33   ` Greg KH
2018-01-17 13:42 ` [PATCH] " Greg KH
2018-01-17 13:42   ` Greg KH
2018-01-18 13:13   ` [PATCH] " shufan_lee(李書帆)
2018-01-18 13:13     ` shufan_lee(李書帆)
2018-01-19  8:03     ` [PATCH] " 'Greg KH'
2018-01-19  8:03       ` Greg KH
2018-01-19  3:09 ` [PATCH] " Jun Li
2018-01-19  3:09   ` Jun Li
2018-01-19  5:48   ` [PATCH] " shufan_lee(李書帆)
2018-01-19  5:48     ` shufan_lee(李書帆)
2018-01-19  8:22     ` [PATCH] " Heikki Krogerus
2018-01-19  8:22       ` Heikki Krogerus
2018-01-19  9:01       ` [PATCH] " shufan_lee(李書帆)
2018-01-19  9:01         ` shufan_lee(李書帆)
2018-01-19  9:24         ` [PATCH] " Heikki Krogerus
2018-01-19  9:24           ` Heikki Krogerus
2018-01-19 16:02           ` [PATCH] " Guenter Roeck
2018-01-19 16:02             ` Guenter Roeck
2018-01-22  2:01             ` [PATCH] " shufan_lee(李書帆)
2018-01-22  2:01               ` shufan_lee(李書帆)
2018-01-22 18:50               ` [PATCH] " Guenter Roeck
2018-01-22 18:50                 ` Guenter Roeck
2018-01-29  7:19                 ` [PATCH] " shufan_lee(李書帆)
2018-01-29  7:19                   ` shufan_lee(李書帆)
2018-01-29 19:57                   ` [PATCH] " Guenter Roeck
2018-01-29 19:57                     ` Guenter Roeck
2018-01-30 13:21                     ` [PATCH] " shufan_lee(李書帆)
2018-01-30 13:21                       ` shufan_lee(李書帆)
2018-01-30 23:25                       ` [PATCH] " Guenter Roeck
2018-01-30 23:25                         ` Guenter Roeck
  -- strict thread matches above, loose matches on Subject: below --
2018-01-09  3:13 [PATCH] " shufan_lee(李書帆)
2018-01-09  6:18 ` Randy Dunlap
2018-01-09  8:45   ` shufan_lee(李書帆)
2018-01-09 17:26     ` Randy Dunlap
2018-01-10  2:49       ` shufan_lee(李書帆)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.