Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S964929AbeAJG72 (ORCPT + 1 other); Wed, 10 Jan 2018 01:59:28 -0500 Received: from mail-pg0-f65.google.com ([74.125.83.65]:34254 "EHLO mail-pg0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964891AbeAJG7V (ORCPT ); Wed, 10 Jan 2018 01:59:21 -0500 X-Google-Smtp-Source: ACJfBotvHnp1uays4dLsF1J8Ej7PdHMWH5Phtt1y3WojSIojTstGW21L7G6KdndDXUx/hEi02aGXxg== From: ShuFanLee 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 Date: Wed, 10 Jan 2018 14:59:12 +0800 Message-Id: <1515567552-7692-1-git-send-email-leechu729@gmail.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Return-Path: From: ShuFanLee 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 --- .../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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ¶m1[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, ¶m); + 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 "); +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