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

ShuFanLee
2018-01-10T06:59:28Z

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