[PATCH 10/15] usb: dwc3: dwc3-rtk-type_c: Add a rtk type c driver.
Stanley Chang
stanley_chang at realtek.com
Wed Oct 7 09:51:02 BST 2020
Realtek SoCs embeds a simple type c module to detect cc pin status.
This driver can switch usb host/device mode based on type c status.
Signed-off-by: Stanley Chang <stanley_chang at realtek.com>
---
drivers/usb/dwc3/Kconfig | 7 +
drivers/usb/dwc3/Makefile | 1 +
drivers/usb/dwc3/dwc3-rtk-type_c.c | 2243 ++++++++++++++++++++++++++++
3 files changed, 2251 insertions(+)
create mode 100644 drivers/usb/dwc3/dwc3-rtk-type_c.c
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index a04d280c9835..a59803f0d7f9 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -70,6 +70,13 @@ config USB_DWC3_RTK_DUAL_ROLE
Support Realtek dwc3 drd mode to dynamical host/device switch.
say 'Y' or 'M' if you have such device.
+config USB_DWC3_RTK_TYPE_C
+ bool "RTK DWC3 Type C Driver (dynamical host/device mode switch)"
+ default USB_DWC3_RTK if (USB_DWC3_DUAL_ROLE)
+ depends on USB_DWC3_DUAL_ROLE
+ help
+ Support Realtek dwc3 drd type c port
+
config USB_DWC3_OMAP
tristate "Texas Instruments OMAP5 and similar Platforms"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 25f8823e8811..19fcbc840982 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o dwc3-rtk-debugfs.o
obj-$(CONFIG_USB_DWC3_RTK_DUAL_ROLE) += dwc3-rtk-drd.o
+obj-$(CONFIG_USB_DWC3_RTK_TYPE_C) += dwc3-rtk-type_c.o
diff --git a/drivers/usb/dwc3/dwc3-rtk-type_c.c b/drivers/usb/dwc3/dwc3-rtk-type_c.c
new file mode 100644
index 000000000000..fd20255a9565
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk-type_c.c
@@ -0,0 +1,2243 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * * dwc3-rtk-type_c.c - Realtek DWC3 Type C driver
+ *
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/syscalls.h>
+#include <linux/debugfs.h>
+#include <linux/sys_soc.h>
+#include <linux/usb/otg.h>
+
+#include "dwc3-rtk-drd.h"
+
+static const struct soc_device_attribute rtk_soc_kylin[] = {
+ {
+ .family = "Realtek Kylin",
+ },
+ {
+ /* empty */
+ }
+};
+
+struct dwc3_rtk;
+
+struct type_c_data {
+ void __iomem *type_c_reg_base;
+ struct device *dev;
+
+ int chip_revision;
+
+ /* Parameters */
+ u32 cc1_rp;
+ u32 cc1_rp_code;
+ u32 cc1_rd_code;
+ u32 cc1_vref_ufp;
+ u32 cc1_vref_dfp_usb;
+ u32 cc1_vref_dfp_1_5;
+ u32 cc1_vref_dfp_3_0;
+ u32 cc2_rp;
+ u32 cc2_rp_code;
+ u32 cc2_rd_code;
+ u32 cc2_vref_ufp;
+ u32 cc2_vref_dfp_usb;
+ u32 cc2_vref_dfp_1_5;
+ u32 cc2_vref_dfp_3_0;
+ u32 debounce_val; /* 1b,1us 7f,4.7us */
+ int cc_dfp_mode;
+
+ /* Host/Device mode status */
+ struct dwc3_rtk *dwc3_rtk;
+ int dr_mode; /* current mode in type c cc status */
+
+ /* type_c state */
+ int connect_change;
+#define CONNECT_CHANGE 1
+#define CONNECT_NO_CHANGE 0
+ int cc_mode; /* cc is host or device */
+#define IN_HOST_MODE 1
+#define IN_DEVICE_MODE 0
+ int is_attach;
+#define IN_ATTACH 1
+#define TO_ATTACH 1
+#define IN_DETACH 0
+#define TO_DETACH 0
+ int at_cc1;
+#define AT_CC1 1
+#define AT_CC2 0
+ bool is_role_swap; /* if in role swap */
+#define ROLE_SWAP 1
+#define NO_ROLE_SWAP 0
+#define TO_SWAP_ROLE 1
+#define TO_RESTORE_ROLE 0
+
+ u32 int_status;
+ u32 cc_status;
+ spinlock_t lock;
+ struct delayed_work delayed_work;
+
+ struct work_struct start_work;
+
+ /* boot time check device mode transfer to host mode*/
+ bool check_at_boot;
+ int boot_check_time;
+ struct delayed_work boot_check_work;
+
+ bool rd_en_at_first;
+
+ /* A sw debounce to filter cc signal */
+ bool filter_config_channel_signal;
+
+ bool debug;
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+ struct dentry *debug_dir;
+#endif
+};
+
+/* Type C register offset */
+#define USB_TYPEC_CTRL_CC1_0 0x0
+#define USB_TYPEC_CTRL_CC1_1 0x4
+#define USB_TYPEC_CTRL_CC2_0 0x8
+#define USB_TYPEC_CTRL_CC2_1 0xC
+#define USB_TYPEC_STS 0x10
+#define USB_TYPEC_CTRL 0x14
+#define USB_DBUS_PWR_CTRL 0x18
+
+#define enable_cc1 0x1
+#define enable_cc2 0x2
+#define disable_cc 0x0
+
+/* Bit mapping USB_TYPEC_CTRL_CC1_0 and USB_TYPEC_CTRL_CC2_0 */
+#define PLR_EN BIT(29)
+#define rp4pk_code(val) ((0x1f & val) << 22)
+#define code_rp4pk(val) ((val >> 22) & 0x1f)
+#define rp36k_code(val) ((0x1f & val) << 17)
+#define code_rp36k(val) ((val >> 17) & 0x1f)
+#define rp12k_code(val) ((0x1f & val) << 12)
+#define code_rp12k(val) ((val >> 12) & 0x1f)
+#define rd_code(val) ((0x1f & val) << 7)
+#define code_rd(val) ((val >> 7) & 0x1f)
+#define cc_mode(val) ((0x3 & val) << 5)
+#define En_rp4p7k BIT(4)
+#define En_rp36k BIT(3)
+#define En_rp12k BIT(2)
+#define En_rd BIT(1)
+#define En_cc_det BIT(0)
+
+#define CC_MODE_UFP 0x0
+#define CC_MODE_DFP_USB 0x1
+#define CC_MODE_DFP_1_5 0x2
+#define CC_MODE_DFP_3_0 0x3
+
+/* Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 */
+#define vref_2p6v(val) ((0x7 & val) << 26)
+#define vref_1p23v(val) ((0xf & val) << 22)
+#define vref_0p8v(val) ((0xf & val) << 18)
+#define vref_0p66v(val) ((0xf & val) << 14)
+#define vref_0p4v(val) ((0x7 & val) << 11)
+#define vref_0p2v(val) ((0x7 & val) << 8)
+#define vref_1_1p6v(val) ((0xf & val) << 4)
+#define vref_0_1p6v(val) ((0xf & val) << 0)
+
+#define decode_2p6v(val) ((val >> 26) & 0x7)
+#define decode_1p23v(val) ((val >> 22) & 0xf)
+#define decode_0p8v(val) ((val >> 18) & 0xf)
+#define decode_0p66v(val) ((val >> 14) & 0xf)
+#define decode_0p4v(val) ((val >> 11) & 0x7)
+#define decode_0p2v(val) ((val >> 8) & 0x7)
+#define decode_1_1p6v(val) ((val >> 4) & 0xf)
+#define decode_0_1p6v(val) ((val >> 0) & 0xf)
+
+/* Bit mapping USB_TYPEC_STS */
+#define det_sts 0x7
+#define cc1_det_sts (det_sts)
+#define cc2_det_sts (det_sts << 3)
+#define det_sts_ra 0x1
+#define det_sts_rd 0x3
+#define det_sts_rp 0x1
+#define cc1_det_sts_ra (det_sts_ra)
+#define cc1_det_sts_rd (det_sts_rd)
+#define cc1_det_sts_rp (det_sts_rp)
+#define cc2_det_sts_ra (det_sts_ra << 3)
+#define cc2_det_sts_rd (det_sts_rd << 3)
+#define cc2_det_sts_rp (det_sts_rp << 3)
+
+/* Bit mapping USB_TYPEC_CTRL */
+#define cc2_int_en BIT(11)
+#define cc1_int_en BIT(10)
+#define cc2_int_sts BIT(9)
+#define cc1_int_sts BIT(8)
+#define debounce_time_MASK 0xff
+#define ENABLE_TYPE_C_DETECT (cc1_int_en | cc2_int_en)
+#define all_cc_int_sts (cc1_int_sts | cc2_int_sts)
+
+/* Parameter */
+#define DETECT_TIME 50 /* ms */
+
+static void enable_writel(int value, void __iomem *addr)
+{
+ writel(value | readl(addr), addr);
+}
+
+static void disable_writel(int value, void __iomem *addr)
+{
+ writel(~value & readl(addr), addr);
+}
+
+static inline int rtk_type_c_init(struct type_c_data *type_c)
+{
+ dev_info(type_c->dev, "%s\n", __func__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static int rtd129x_switch_type_c_plug_config(struct type_c_data *type_c,
+ int dr_mode, int cc)
+{
+ void __iomem *usb_typec_ctrl_cc1_0;
+ int val_cc;
+
+#define TYPE_C_EN_SWITCH BIT(29)
+#define TYPE_C_TxRX_sel (BIT(28) | BIT(27))
+#define TYPE_C_SWITCH_MASK (TYPE_C_EN_SWITCH | TYPE_C_TxRX_sel)
+#define TYPE_C_enable_cc1 TYPE_C_EN_SWITCH
+#define TYPE_C_enable_cc2 (TYPE_C_EN_SWITCH | TYPE_C_TxRX_sel)
+#define TYPE_C_disable_cc ~TYPE_C_SWITCH_MASK
+
+ usb_typec_ctrl_cc1_0 = type_c->type_c_reg_base + USB_TYPEC_CTRL_CC1_0;
+ val_cc = readl(usb_typec_ctrl_cc1_0);
+ val_cc &= ~TYPE_C_SWITCH_MASK;
+
+ if (cc == disable_cc) {
+ val_cc &= TYPE_C_disable_cc;
+ } else if (cc == enable_cc1) {
+ val_cc |= TYPE_C_enable_cc1;
+ } else if (cc == enable_cc2) {
+ val_cc |= TYPE_C_enable_cc2;
+ } else {
+ pr_err("%s: Error cc setting cc=0x%x\n", __func__, cc);
+ return -1;
+ }
+ writel(val_cc, usb_typec_ctrl_cc1_0);
+
+ mdelay(1);
+
+ pr_info("%s: cc=0x%x val_cc=0x%x usb_typec_ctrl_cc1_0=0x%x\n",
+ __func__, cc, val_cc, readl(usb_typec_ctrl_cc1_0));
+
+ return 0;
+}
+
+static inline void switch_type_c_plug_config(struct type_c_data *type_c,
+ int dr_mode, int cc)
+{
+ int ret = 0;
+
+ dev_info(type_c->dev, "%s dr_mode=%d cc=0x%x\n", __func__, dr_mode, cc);
+
+ if (soc_device_match(rtk_soc_kylin))
+ ret = rtd129x_switch_type_c_plug_config(type_c, dr_mode, cc);
+
+ if (ret < 0)
+ dev_err(type_c->dev, "%s: Error set type c plug config\n",
+ __func__);
+
+ /* TODO for gpio control switch */
+}
+
+static void switch_type_c_dr_mode(struct type_c_data *type_c,
+ int dr_mode, int cc)
+{
+ dev_dbg(type_c->dev, "%s START....", __func__);
+
+ switch_type_c_plug_config(type_c, dr_mode, cc);
+ if (cc == disable_cc)
+ msleep(1000);
+
+ type_c->dr_mode = dwc3_rtk_set_dr_mode(type_c->dwc3_rtk, dr_mode);
+
+ dev_dbg(type_c->dev, "%s END....", __func__);
+}
+
+/* device attached/detached */
+static int device_attached(struct type_c_data *type_c, u32 enable_cc)
+{
+ struct device *dev = type_c->dev;
+ void __iomem *type_c_reg_base = type_c->type_c_reg_base;
+
+ dev_info(dev, "%s: a device attach\n", __func__);
+
+ cancel_delayed_work(&type_c->delayed_work);
+
+ switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc);
+
+ enable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL);
+ return 0;
+}
+
+static int device_detached(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ void __iomem *type_c_reg_base = type_c->type_c_reg_base;
+
+ dev_info(dev, "%s: a device detach\n", __func__);
+
+ disable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL);
+
+ switch_type_c_dr_mode(type_c, 0, disable_cc);
+
+ schedule_delayed_work(&type_c->delayed_work,
+ msecs_to_jiffies(DETECT_TIME));
+
+ return 0;
+}
+
+/* host connect/disconnect*/
+static int host_connected(struct type_c_data *type_c, u32 enable_cc)
+{
+ struct device *dev = type_c->dev;
+ void __iomem *type_c_reg_base = type_c->type_c_reg_base;
+
+ dev_info(dev, "%s: a Host connect\n", __func__);
+
+ cancel_delayed_work(&type_c->delayed_work);
+
+ switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL, enable_cc);
+
+ enable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL);
+ return 0;
+}
+
+static int host_disconnected(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ void __iomem *type_c_reg_base = type_c->type_c_reg_base;
+
+ dev_info(dev, "%s: a Host disconnect\n", __func__);
+
+ disable_writel(ENABLE_TYPE_C_DETECT, type_c_reg_base + USB_TYPEC_CTRL);
+
+ switch_type_c_dr_mode(type_c, 0, disable_cc);
+
+ schedule_delayed_work(&type_c->delayed_work,
+ msecs_to_jiffies(DETECT_TIME));
+
+ return 0;
+}
+
+/* detect host device switch */
+static int detect_device(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ void __iomem *type_c_reg_base = type_c->type_c_reg_base;
+ u32 cc1_config, cc2_config, default_ctrl;
+ int cc_mode_sel = type_c->cc_dfp_mode;
+
+ /* For kylin to disable external rd control gpio */
+ if (soc_device_match(rtk_soc_kylin)) {
+ /* TODO add a gpio control to disable external rd */
+ }
+
+ default_ctrl = readl(type_c_reg_base + USB_TYPEC_CTRL) &
+ debounce_time_MASK;
+ writel(default_ctrl, type_c_reg_base + USB_TYPEC_CTRL);
+
+ disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0);
+ disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0);
+
+ switch (cc_mode_sel) {
+ case CC_MODE_DFP_USB:
+ writel(type_c->cc1_vref_dfp_usb,
+ type_c_reg_base + USB_TYPEC_CTRL_CC1_1);
+ writel(type_c->cc2_vref_dfp_usb,
+ type_c_reg_base + USB_TYPEC_CTRL_CC2_1);
+ break;
+ case CC_MODE_DFP_1_5:
+ writel(type_c->cc1_vref_dfp_1_5,
+ type_c_reg_base + USB_TYPEC_CTRL_CC1_1);
+ writel(type_c->cc2_vref_dfp_1_5,
+ type_c_reg_base + USB_TYPEC_CTRL_CC2_1);
+ break;
+ case CC_MODE_DFP_3_0:
+ writel(type_c->cc1_vref_dfp_3_0,
+ type_c_reg_base + USB_TYPEC_CTRL_CC1_1);
+ writel(type_c->cc2_vref_dfp_3_0,
+ type_c_reg_base + USB_TYPEC_CTRL_CC2_1);
+ break;
+ default:
+ dev_err(dev, "%s ERROR cc_mode_sel=%d\n",
+ __func__, cc_mode_sel);
+ break;
+ }
+ cc1_config = type_c->cc1_rp | type_c->cc1_rp_code |
+ cc_mode(cc_mode_sel);
+ cc2_config = type_c->cc2_rp | type_c->cc2_rp_code |
+ cc_mode(cc_mode_sel);
+
+ writel(cc1_config, type_c_reg_base + USB_TYPEC_CTRL_CC1_0);
+ writel(cc2_config, type_c_reg_base + USB_TYPEC_CTRL_CC2_0);
+
+ /* Do wmb */
+ wmb();
+
+ enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0);
+ enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0);
+
+ return 0;
+}
+
+static int detect_host(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ void __iomem *type_c_reg_base = type_c->type_c_reg_base;
+ u32 cc1_config, cc2_config, default_ctrl;
+ u32 cc_rd = En_rd;
+
+ if (type_c->rd_en_at_first) {
+ if (soc_device_match(rtk_soc_kylin)) {
+ cc_rd = 0;
+ /* TODO add a gpio control to enable external rd */
+ } else {
+ dev_info(dev, "%s set PLR_EN on rd_en_at_first\n",
+ __func__);
+ cc_rd = PLR_EN;
+ }
+ type_c->rd_en_at_first = false;
+ } else {
+ /* For kylin to disable external rd control gpio */
+ if (soc_device_match(rtk_soc_kylin)) {
+ /* TODO add a gpio control to disable external rd */
+ }
+ }
+
+ default_ctrl = readl(type_c_reg_base + USB_TYPEC_CTRL) &
+ debounce_time_MASK;
+ writel(default_ctrl, type_c_reg_base + USB_TYPEC_CTRL);
+
+ disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0);
+ disable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0);
+
+ writel(type_c->cc1_vref_ufp, type_c_reg_base + USB_TYPEC_CTRL_CC1_1);
+ writel(type_c->cc2_vref_ufp, type_c_reg_base + USB_TYPEC_CTRL_CC2_1);
+
+ cc1_config = cc_rd | type_c->cc1_rd_code | cc_mode(CC_MODE_UFP);
+ cc2_config = cc_rd | type_c->cc2_rd_code | cc_mode(CC_MODE_UFP);
+
+ writel(cc1_config, type_c_reg_base + USB_TYPEC_CTRL_CC1_0);
+ writel(cc2_config, type_c_reg_base + USB_TYPEC_CTRL_CC2_0);
+
+ /* Do wmb */
+ wmb();
+
+ enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC1_0);
+ enable_writel(En_cc_det, type_c_reg_base + USB_TYPEC_CTRL_CC2_0);
+
+ return 0;
+}
+
+static int host_device_switch_detection(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ int ret = 0;
+
+ if (type_c->debug)
+ dev_dbg(dev, "ENTER %s", __func__);
+ if (type_c->cc_mode) {
+ type_c->cc_mode = IN_DEVICE_MODE;
+ detect_host(type_c);
+ if (type_c->debug)
+ dev_dbg(dev, "Now device mode $$$$");
+ } else {
+ type_c->cc_mode = IN_HOST_MODE;
+ detect_device(type_c);
+ if (type_c->debug)
+ dev_dbg(dev, "Now host mode ####");
+ }
+
+ return ret;
+}
+
+static int detect_type_c_state(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ u32 int_status, cc_status, cc_status_check;
+ unsigned long flags;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ int_status = readl(type_c->type_c_reg_base + USB_TYPEC_CTRL);
+ cc_status = readl(type_c->type_c_reg_base + USB_TYPEC_STS);
+
+ type_c->connect_change = CONNECT_NO_CHANGE;
+
+ switch (type_c->cc_mode) {
+ case IN_HOST_MODE:
+ switch (type_c->is_attach) {
+ case IN_ATTACH:
+ if (((cc_status & cc1_det_sts) == cc1_det_sts) &&
+ (type_c->at_cc1 == AT_CC1)) {
+ dev_dbg(dev, "IN host mode and cc1 device detach (cc_status=0x%x)",
+ cc_status);
+ type_c->is_attach = TO_DETACH;
+ type_c->connect_change = CONNECT_CHANGE;
+ } else if (((cc_status & cc2_det_sts) == cc2_det_sts) &&
+ (type_c->at_cc1 == AT_CC2)) {
+ dev_dbg(dev, "IN host mode and cc2 device detach (cc_status=0x%x)",
+ cc_status);
+ type_c->is_attach = TO_DETACH;
+ type_c->connect_change = CONNECT_CHANGE;
+ }
+ break;
+ case IN_DETACH:
+ cc_status_check = readl(
+ type_c->type_c_reg_base + USB_TYPEC_STS);
+ if (cc_status_check != (cc1_det_sts | cc2_det_sts)) {
+ if (in_interrupt()) {
+ mdelay(300);
+ } else {
+ spin_unlock_irqrestore(&type_c->lock,
+ flags);
+ msleep(300);
+ spin_lock_irqsave(&type_c->lock, flags);
+ }
+ cc_status_check = readl(
+ type_c->type_c_reg_base +
+ USB_TYPEC_STS);
+ }
+ if (cc_status != cc_status_check) {
+ dev_warn(dev, "IN_HOST_MODE: cc_status (0x%x) != cc_status_check (0x%x)\n",
+ cc_status, cc_status_check);
+ cc_status = readl(type_c->type_c_reg_base +
+ USB_TYPEC_STS);
+ }
+
+ if ((cc_status & cc1_det_sts) == cc1_det_sts_rd) {
+ dev_dbg(dev, "IN host mode and cc1 device attach (cc_status=0x%x)",
+ cc_status);
+ type_c->is_attach = TO_ATTACH;
+ type_c->at_cc1 = AT_CC1;
+ type_c->connect_change = CONNECT_CHANGE;
+ } else if ((cc_status & cc2_det_sts) ==
+ cc2_det_sts_rd) {
+ dev_dbg(dev, "In host mode and cc2 device attach (cc_status=0x%x)",
+ cc_status);
+ type_c->is_attach = TO_ATTACH;
+ type_c->at_cc1 = AT_CC2;
+ type_c->connect_change = CONNECT_CHANGE;
+ }
+ break;
+ default:
+ dev_err(dev, "IN host_mode and error attach state (is_attach=%d)",
+ type_c->is_attach);
+ }
+ break;
+ case IN_DEVICE_MODE:
+ switch (type_c->is_attach) {
+ case IN_ATTACH:
+ if (type_c->filter_config_channel_signal &&
+ ((cc_status & cc1_det_sts) <
+ cc1_det_sts_rp ||
+ (cc_status & cc2_det_sts) <
+ cc2_det_sts_rp)) {
+ /* Add a sw debounce to filter cc signal sent
+ * from apple pd adapter
+ */
+ if (in_interrupt()) {
+ mdelay(5);
+ } else {
+ spin_unlock_irqrestore(
+ &type_c->lock, flags);
+ msleep(5);
+ spin_lock_irqsave(&type_c->lock, flags);
+ }
+ cc_status_check = readl(
+ type_c->type_c_reg_base +
+ USB_TYPEC_STS);
+
+ if (cc_status != cc_status_check) {
+ dev_dbg(dev, "IN_DEVICE_MODE: cc_status (0x%x) != cc_status_check (0x%x) maybe use a pd adapter\n",
+ cc_status, cc_status_check);
+ cc_status = cc_status_check;
+ }
+ }
+
+ if ((cc_status & cc1_det_sts) < cc1_det_sts_rp &&
+ type_c->at_cc1 == AT_CC1) {
+ dev_dbg(dev, "IN device mode and cc1 host disconnect (cc_status=0x%x)",
+ cc_status);
+ type_c->is_attach = TO_DETACH;
+ type_c->connect_change = CONNECT_CHANGE;
+ } else if ((cc_status & cc2_det_sts) < cc2_det_sts_rp &&
+ type_c->at_cc1 == AT_CC2) {
+ dev_dbg(dev, "IN device mode and cc2 host connect (cc_status=0x%x)",
+ cc_status);
+ type_c->is_attach = TO_DETACH;
+ type_c->connect_change = CONNECT_CHANGE;
+ }
+ break;
+ case IN_DETACH:
+ cc_status_check = readl(type_c->type_c_reg_base +
+ USB_TYPEC_STS);
+ if (cc_status_check != 0x0) {
+ if (in_interrupt()) {
+ mdelay(300);
+ } else {
+ spin_unlock_irqrestore(&type_c->lock,
+ flags);
+ msleep(300);
+ spin_lock_irqsave(&type_c->lock, flags);
+ }
+ cc_status_check = readl(
+ type_c->type_c_reg_base +
+ USB_TYPEC_STS);
+ }
+ if (cc_status != cc_status_check) {
+ dev_warn(dev, "IN_DEVICE_MODE: cc_status (0x%x) != cc_status_check (0x%x)\n",
+ cc_status, cc_status_check);
+ cc_status = readl(type_c->type_c_reg_base +
+ USB_TYPEC_STS);
+ }
+
+ if ((cc_status & cc1_det_sts) >= cc1_det_sts_rp) {
+ dev_dbg(dev, "IN device mode and cc1 host connect (cc_status=0x%x)",
+ cc_status);
+ type_c->at_cc1 = AT_CC1;
+ type_c->is_attach = TO_ATTACH;
+ type_c->connect_change = CONNECT_CHANGE;
+ } else if ((cc_status & cc2_det_sts) >=
+ cc2_det_sts_rp) {
+ dev_dbg(dev, "IN device mode and cc2 host connect (cc_status=0x%x)",
+ cc_status);
+ type_c->at_cc1 = AT_CC2;
+ type_c->is_attach = TO_ATTACH;
+ type_c->connect_change = CONNECT_CHANGE;
+ }
+ break;
+ default:
+ dev_err(dev, "IN device_mode and error attach state (is_attach=%d)",
+ type_c->is_attach);
+ }
+ break;
+ default:
+ dev_err(dev, "error host or device mode (cc_mode=%d)",
+ type_c->cc_mode);
+ }
+
+ type_c->int_status = int_status;
+ type_c->cc_status = cc_status;
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+ return 0;
+}
+
+static void host_device_switch(struct work_struct *work)
+{
+ struct type_c_data *type_c = container_of(work,
+ struct type_c_data, delayed_work.work);
+ struct device *dev = type_c->dev;
+ unsigned long flags;
+ int connect_change = 0;
+ int cc_mode = 0;
+ int is_attach = 0;
+ int at_cc1 = 0;
+
+ if (type_c->debug)
+ dev_dbg(type_c->dev, "ENTER %s", __func__);
+
+ spin_lock_irqsave(&type_c->lock, flags);
+ if (type_c->connect_change)
+ connect_change = type_c->connect_change;
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ if (!connect_change)
+ detect_type_c_state(type_c);
+
+ spin_lock_irqsave(&type_c->lock, flags);
+ if (type_c->connect_change) {
+ connect_change = type_c->connect_change;
+ cc_mode = type_c->cc_mode;
+ is_attach = type_c->is_attach;
+ at_cc1 = type_c->at_cc1;
+ type_c->connect_change = CONNECT_NO_CHANGE;
+ type_c->is_role_swap = NO_ROLE_SWAP;
+ } else {
+ host_device_switch_detection(type_c);
+
+ schedule_delayed_work(&type_c->delayed_work,
+ msecs_to_jiffies(DETECT_TIME));
+ }
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ if (connect_change) {
+ dev_info(dev, "%s: usb cable connection change\n", __func__);
+ if (cc_mode) {
+ if (is_attach && at_cc1)
+ device_attached(type_c, enable_cc1);
+ else if (is_attach && !at_cc1)
+ device_attached(type_c, enable_cc2);
+ else
+ device_detached(type_c);
+ } else {
+ if (is_attach && at_cc1)
+ host_connected(type_c, enable_cc1);
+ else if (is_attach && !at_cc1)
+ host_connected(type_c, enable_cc2);
+ else
+ host_disconnected(type_c);
+ }
+ dev_err(dev, "Connection change OK: IN %s mode to %s %s at %s (cc_status=0x%x)\n",
+ cc_mode?"host":"device",
+ cc_mode ?
+ (is_attach?"attach":"detach") :
+ (is_attach?"connect":"disconnect"),
+ cc_mode?"device":"host",
+ at_cc1?"cc1":"cc2", type_c->cc_status);
+
+ }
+
+ /* For special case, some boards use type c power and
+ * need use host mode.
+ * After 30s, We switch to host mode if in device mode
+ * but no host connect.
+ */
+ if (type_c->check_at_boot) {
+ if (connect_change &&
+ (cc_mode == IN_DEVICE_MODE) &&
+ is_attach) {
+ dev_info(dev, "%s: In Device mode check connection at boot time\n",
+ __func__);
+ schedule_delayed_work(&type_c->boot_check_work,
+ msecs_to_jiffies(type_c->boot_check_time));
+ }
+ type_c->check_at_boot = false;
+ }
+}
+
+static int host_device_role_swap(struct type_c_data *type_c, bool swap_role)
+{
+ int ret = 0;
+ int swap_to_xx_mode = USB_DR_MODE_UNKNOWN;
+ int at_cc1, cc_mode, is_attach;
+ unsigned long flags;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ at_cc1 = type_c->at_cc1;
+ cc_mode = type_c->cc_mode;
+ is_attach = type_c->is_attach;
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ if (!is_attach) {
+ dev_warn(type_c->dev, "Can not swap role!! It is Not attach\n");
+ return -1;
+ }
+
+ dev_info(type_c->dev, "%s: In %s Mode and now is%s role swap ",
+ __func__,
+ cc_mode == IN_HOST_MODE?"Host":"Device",
+ type_c->is_role_swap == ROLE_SWAP?"":" Not");
+
+ if (swap_role == TO_RESTORE_ROLE &&
+ type_c->is_role_swap == ROLE_SWAP)
+ /* Restore Role */
+ if (cc_mode == IN_DEVICE_MODE)
+ swap_to_xx_mode = USB_DR_MODE_PERIPHERAL;
+ else
+ swap_to_xx_mode = USB_DR_MODE_HOST;
+ else if (swap_role == TO_SWAP_ROLE &&
+ type_c->is_role_swap == NO_ROLE_SWAP)
+ /* Swap Role */
+ if (cc_mode == IN_DEVICE_MODE)
+ swap_to_xx_mode = USB_DR_MODE_HOST;
+ else
+ swap_to_xx_mode = USB_DR_MODE_PERIPHERAL;
+ else
+ dev_info(type_c->dev, "%s: Other Case swap_role=%x is_role_swap=%x\n",
+ __func__, swap_role, type_c->is_role_swap);
+
+ if (swap_to_xx_mode != USB_DR_MODE_UNKNOWN) {
+ u32 enable_cc = at_cc1?enable_cc1:enable_cc2;
+
+ dev_info(type_c->dev, "%s: now is%s role swap ==> to %s role (swap_to_xx_mode=%s\n",
+ __func__,
+ type_c->is_role_swap == ROLE_SWAP?"":" No",
+ swap_role == TO_SWAP_ROLE?"swap":"restore",
+ ({ char *tmp;
+ switch (swap_to_xx_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ tmp = "USB_DR_MODE_PERIPHERAL"; break;
+ case USB_DR_MODE_HOST:
+ tmp = "USB_DR_MODE_HOST"; break;
+ default:
+ tmp = "USB_DR_MODE_UNKNOWN"; break;
+ } tmp; }));
+
+ switch_type_c_plug_config(type_c, 0, disable_cc);
+ mdelay(100);
+ switch_type_c_dr_mode(type_c, swap_to_xx_mode, enable_cc);
+
+ type_c->is_role_swap = (swap_role == TO_SWAP_ROLE ?
+ ROLE_SWAP : NO_ROLE_SWAP);
+ } else {
+ dev_info(type_c->dev, "%s: No swap and No restore\n", __func__);
+ }
+
+ return ret;
+}
+
+static void boot_time_check(struct work_struct *work)
+{
+ struct type_c_data *type_c = container_of(work,
+ struct type_c_data, boot_check_work.work);
+ struct device *dev = type_c->dev;
+ int at_cc1, cc_mode, is_attach;
+ unsigned long flags;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ at_cc1 = type_c->at_cc1;
+ cc_mode = type_c->cc_mode;
+ is_attach = type_c->is_attach;
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ if ((cc_mode == IN_DEVICE_MODE) && is_attach) {
+
+ if (!dwc3_rtk_is_connected_on_device_mode(
+ type_c->dwc3_rtk)) {
+ dev_info(dev, "%s: In Device mode, NO host connect at boot time (After %dms), switch to Host mode\n",
+ __func__, type_c->boot_check_time);
+
+ host_device_role_swap(type_c, TO_SWAP_ROLE);
+ }
+ }
+}
+
+irqreturn_t type_c_detect_irq(int irq, void *__data)
+{
+ struct type_c_data *type_c = (struct type_c_data *) __data;
+ struct device *dev = type_c->dev;
+ unsigned long flags;
+
+ detect_type_c_state(type_c);
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ if (type_c->connect_change) {
+ dev_info(dev, "%s: IN %s mode to %s %s (at %s interrupt) int_status=0x%x, cc_status=0x%x",
+ __func__,
+ type_c->cc_mode?"host":"device",
+ type_c->cc_mode ?
+ (type_c->is_attach?"attach":"detach") :
+ (type_c->is_attach?"connect":"disconnect"),
+ type_c->cc_mode?"device":"host",
+ type_c->at_cc1?"cc1":"cc2",
+ type_c->int_status, type_c->cc_status);
+
+ /* clear interrupt status */
+ disable_writel(all_cc_int_sts,
+ type_c->type_c_reg_base + USB_TYPEC_CTRL);
+
+ cancel_delayed_work(&type_c->delayed_work);
+ schedule_delayed_work(&type_c->delayed_work,
+ msecs_to_jiffies(0));
+ } else {
+ static int local_count;
+
+ if (local_count++ > 10) {
+ /* clear interrupt status */
+ disable_writel(all_cc_int_sts,
+ type_c->type_c_reg_base + USB_TYPEC_CTRL);
+ local_count = 0;
+ }
+ if (type_c->debug)
+ dev_dbg(dev, "%s: ###NO change### Status: IN %s mode %s %s (at %s interrupt)\n",
+ __func__,
+ type_c->cc_mode?"host":"device",
+ type_c->cc_mode ?
+ (type_c->is_attach?"attach":"detach") :
+ (type_c->is_attach?"connect":"disconnect"),
+ type_c->cc_mode?"device":"host",
+ type_c->at_cc1?"cc1":"cc2");
+ dev_dbg(dev, "%s: int_status=0x%x, cc_status=0x%x\n",
+ __func__,
+ type_c->int_status, type_c->cc_status);
+ }
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t role_swap_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+ char *ptr = buf;
+ int count = PAGE_SIZE;
+ int n;
+
+ n = scnprintf(ptr, count,
+ "Now cc_mode is %s and cur_mode is %s ==> Now is%s role swap\n",
+ ({ char *tmp;
+ switch (type_c->cc_mode) {
+ case IN_DEVICE_MODE:
+ tmp = "IN_DEVICE_MODE"; break;
+ case IN_HOST_MODE:
+ tmp = "IN_HOST_MODE"; break;
+ default:
+ tmp = "UNKNOWN"; break;
+ } tmp; }),
+ ({ char *tmp;
+ switch (type_c->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ tmp = "USB_DR_MODE_PERIPHERAL"; break;
+ case USB_DR_MODE_HOST:
+ tmp = "USB_DR_MODE_HOST"; break;
+ default:
+ tmp = "USB_DR_MODE_UNKNOWN"; break;
+ } tmp; }),
+ type_c->is_role_swap == ROLE_SWAP?"":" not");
+ ptr += n;
+ count -= n;
+
+ n = scnprintf(ptr, count,
+ "write 1 to swap role, ex: echo 1 > role_swap\n");
+ ptr += n;
+ count -= n;
+
+ n = scnprintf(ptr, count,
+ "write 0 to restore role, ex: echo 0 > role_swap\n");
+ ptr += n;
+ count -= n;
+
+ return ptr - buf;
+}
+
+static ssize_t role_swap_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+
+ if (!strncmp(buf, "1", 1))
+ host_device_role_swap(type_c, TO_SWAP_ROLE);
+ else if (!strncmp(buf, "0", 1))
+ host_device_role_swap(type_c, TO_RESTORE_ROLE);
+
+ return count;
+}
+static DEVICE_ATTR_RW(role_swap);
+
+static ssize_t force_set_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+ char *ptr = buf;
+ int count = PAGE_SIZE;
+ int n;
+
+ n = scnprintf(ptr, count,
+ "Now cc_mode is %s and type c dr_mode is %s (dwc3_rtk dr_mode=%s)\n",
+ ({ char *tmp;
+ switch (type_c->cc_mode) {
+ case IN_DEVICE_MODE:
+ tmp = "IN_DEVICE_MODE"; break;
+ case IN_HOST_MODE:
+ tmp = "IN_HOST_MODE"; break;
+ default:
+ tmp = "UNKNOWN"; break;
+ } tmp; }),
+ ({ char *tmp;
+ switch (type_c->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ tmp = "USB_DR_MODE_PERIPHERAL"; break;
+ case USB_DR_MODE_HOST:
+ tmp = "USB_DR_MODE_HOST"; break;
+ default:
+ tmp = "USB_DR_MODE_UNKNOWN"; break;
+ } tmp; }),
+ ({ char *tmp;
+ switch (dwc3_rtk_get_dr_mode(type_c->dwc3_rtk)) {
+ case USB_DR_MODE_PERIPHERAL:
+ tmp = "USB_DR_MODE_PERIPHERAL"; break;
+ case USB_DR_MODE_HOST:
+ tmp = "USB_DR_MODE_HOST"; break;
+ default:
+ tmp = "USB_DR_MODE_UNKNOWN"; break;
+ } tmp; }));
+
+ ptr += n;
+ count -= n;
+
+ n = scnprintf(ptr, count,
+ "write host -> switch to Host mode\n");
+ ptr += n;
+ count -= n;
+
+ n = scnprintf(ptr, count,
+ "write device -> switch to Device mode\n");
+ ptr += n;
+ count -= n;
+
+ return ptr - buf;
+}
+
+static ssize_t force_set_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+ unsigned long flags;
+ u32 enable_cc;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+ enable_cc = type_c->at_cc1?enable_cc1:enable_cc2;
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+
+ if (!strncmp(buf, "host", 4)) {
+ switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc);
+ } else if (!strncmp(buf, "device", 6)) {
+ switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL,
+ enable_cc);
+ } else {
+ switch_type_c_dr_mode(type_c, 0, disable_cc);
+ }
+ return count;
+}
+static DEVICE_ATTR_RW(force_set_mode);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+static int type_c_parameter_show(struct seq_file *s, void *unused)
+{
+ struct type_c_data *type_c = s->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ seq_printf(s, "cc_dfp_mode %s\n",
+ ({ char *tmp;
+ switch (type_c->cc_dfp_mode) {
+ case CC_MODE_DFP_USB:
+ tmp = "CC_MODE_DFP_USB"; break;
+ case CC_MODE_DFP_1_5:
+ tmp = "CC_MODE_DFP_1_5"; break;
+ case CC_MODE_DFP_3_0:
+ tmp = "CC_MODE_DFP_3_0"; break;
+ default:
+ tmp = "?"; break;
+ } tmp; }));
+ seq_printf(s, "cc1_rp 0x%x\n", type_c->cc1_rp);
+ seq_printf(s, "cc1_rp_code 0x%x\n",
+ ({ int tmp;
+ switch (type_c->cc_dfp_mode) {
+ case CC_MODE_DFP_USB:
+ tmp = code_rp12k(type_c->cc1_rp_code); break;
+ case CC_MODE_DFP_1_5:
+ tmp = code_rp36k(type_c->cc1_rp_code); break;
+ case CC_MODE_DFP_3_0:
+ tmp = code_rp4pk(type_c->cc1_rp_code); break;
+ default:
+ tmp = -1; break;
+ } tmp; }));
+ seq_printf(s, "cc1_rd_code 0x%x\n",
+ code_rd(type_c->cc1_rd_code));
+ seq_printf(s, "cc1_vref_ufp vref_1p23v 0x%x vref_0p66v 0x%x vref_0p2v 0x%x\n",
+ decode_1p23v(type_c->cc1_vref_ufp),
+ decode_0p66v(type_c->cc1_vref_ufp),
+ decode_0p2v(type_c->cc1_vref_ufp));
+ seq_printf(s, "cc1_vref_dfp_usb vref_0_1p6v 0x%x vref_0p2v 0x%x\n",
+ decode_0_1p6v(type_c->cc1_vref_dfp_usb),
+ decode_0p2v(type_c->cc1_vref_dfp_usb));
+ seq_printf(s, "cc1_vref_dfp_1_5 vref_1_1p6v 0x%x vref_0p4v 0x%x vref_0p2v 0x%x\n",
+ decode_1_1p6v(type_c->cc1_vref_dfp_1_5),
+ decode_0p4v(type_c->cc1_vref_dfp_1_5),
+ decode_0p2v(type_c->cc1_vref_dfp_1_5));
+ seq_printf(s, "cc1_vref_dfp_3_0 vref_2p6v 0x%x vref_0p8v 0x%x vref_0p2v 0x%x\n",
+ decode_2p6v(type_c->cc1_vref_dfp_3_0),
+ decode_0p8v(type_c->cc1_vref_dfp_3_0),
+ decode_0p2v(type_c->cc1_vref_dfp_3_0));
+ seq_printf(s, "cc2_rp 0x%x\n", type_c->cc2_rp);
+ seq_printf(s, "cc2_rp_code 0x%x\n",
+ ({ int tmp;
+ switch (type_c->cc_dfp_mode) {
+ case CC_MODE_DFP_USB:
+ tmp = code_rp12k(type_c->cc2_rp_code); break;
+ case CC_MODE_DFP_1_5:
+ tmp = code_rp36k(type_c->cc2_rp_code); break;
+ case CC_MODE_DFP_3_0:
+ tmp = code_rp4pk(type_c->cc2_rp_code); break;
+ default:
+ tmp = -1; break;
+ } tmp; }));
+ seq_printf(s, "cc2_rd_code 0x%x\n",
+ code_rd(type_c->cc2_rd_code));
+ seq_printf(s, "cc2_vref_ufp vref_1p23v 0x%x vref_0p66v 0x%x vref_0p2v 0x%x\n",
+ decode_1p23v(type_c->cc2_vref_ufp),
+ decode_0p66v(type_c->cc2_vref_ufp),
+ decode_0p2v(type_c->cc2_vref_ufp));
+ seq_printf(s, "cc2_vref_dfp_usb vref_0_1p6v 0x%x vref_0p2v 0x%x\n",
+ decode_0_1p6v(type_c->cc2_vref_dfp_usb),
+ decode_0p2v(type_c->cc2_vref_dfp_usb));
+ seq_printf(s, "cc2_vref_dfp_1_5 vref_1_1p6v 0x%x vref_0p4v 0x%x vref_0p2v 0x%x\n",
+ decode_1_1p6v(type_c->cc2_vref_dfp_1_5),
+ decode_0p4v(type_c->cc2_vref_dfp_1_5),
+ decode_0p2v(type_c->cc2_vref_dfp_1_5));
+ seq_printf(s, "cc2_vref_dfp_3_0 vref_2p6v 0x%x vref_0p8v 0x%x vref_0p2v 0x%x\n",
+ decode_2p6v(type_c->cc2_vref_dfp_3_0),
+ decode_0p8v(type_c->cc2_vref_dfp_3_0),
+ decode_0p2v(type_c->cc2_vref_dfp_3_0));
+ seq_printf(s, "debounce_val 0x%x\n", type_c->debounce_val);
+ seq_puts(s, "\n");
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ return 0;
+}
+
+static int type_c_parameter_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, type_c_parameter_show, inode->i_private);
+}
+
+static const struct file_operations type_c_parameter_fops = {
+ .open = type_c_parameter_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int type_c_set_parameter_show(struct seq_file *s, void *unused)
+{
+ seq_puts(s, "cc_dfp_mode [CC_MODE_DFP_USB|CC_MODE_DFP_1_5|CC_MODE_DFP_3_0]\n");
+ seq_puts(s, "cc1_rp_code 0x_value\n");
+ seq_puts(s, "cc1_rd_code 0x_value\n");
+ seq_puts(s, "cc1_vref_ufp_vref_1p23v 0x_value\n");
+ seq_puts(s, "cc1_vref_ufp_vref_0p66v 0x_value\n");
+ seq_puts(s, "cc1_vref_ufp_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_usb_vref_1p6v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_usb_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_1_5_vref_1p6v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_1_5_vref_0p4v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_1_5_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_3_0_vref_2p6v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_3_0_vref_0p8v 0x_value\n");
+ seq_puts(s, "cc1_vref_dfp_3_0_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc2_rp_code 0x_value\n");
+ seq_puts(s, "cc2_rd_code 0x_value\n");
+ seq_puts(s, "cc2_vref_ufp_vref_1p23v 0x_value\n");
+ seq_puts(s, "cc2_vref_ufp_vref_0p66v 0x_value\n");
+ seq_puts(s, "cc2_vref_ufp_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_usb_vref_1p6v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_usb_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_1_5_vref_1p6v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_1_5_vref_0p4v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_1_5_vref_0p2v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_3_0_vref_2p6v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_3_0_vref_0p8v 0x_value\n");
+ seq_puts(s, "cc2_vref_dfp_3_0_vref_0p2v 0x_value\n");
+ seq_puts(s, "debounce_val value\n");
+
+ return 0;
+}
+
+static int type_c_set_parameter_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, type_c_set_parameter_show, inode->i_private);
+}
+
+static ssize_t type_c_set_parameter_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct type_c_data *type_c = s->private;
+ unsigned long flags;
+ char buffer[40];
+ char *buf = buffer;
+ u32 value;
+ int ret = 0;
+
+ if (copy_from_user(&buffer, ubuf,
+ min_t(size_t, sizeof(buffer) - 1, count)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+ if (!strncmp(buf, "cc_dfp_mode", 11)) {
+ buf = buf + 11;
+ buf = skip_spaces(buf);
+ if (!strncmp(buf, "CC_MODE_DFP_USB", 15)) {
+ type_c->cc_dfp_mode = CC_MODE_DFP_USB;
+ type_c->cc1_rp = En_rp12k;
+ type_c->cc2_rp = En_rp12k;
+ } else if (!strncmp(buf, "CC_MODE_DFP_1_5", 15)) {
+ type_c->cc_dfp_mode = CC_MODE_DFP_1_5;
+ type_c->cc1_rp = En_rp36k;
+ type_c->cc2_rp = En_rp36k;
+ } else if (!strncmp(buf, "CC_MODE_DFP_3_0", 15)) {
+ type_c->cc_dfp_mode = CC_MODE_DFP_3_0;
+ type_c->cc1_rp = En_rp4p7k;
+ type_c->cc2_rp = En_rp4p7k;
+ } else {
+ dev_err(type_c->dev, "cc_dfp_mode UNKNOWN (%s)", buf);
+ }
+ } else if (!strncmp(buf, "cc1_rp_code", 11)) {
+ buf = buf + 11;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ if (type_c->cc_dfp_mode == CC_MODE_DFP_USB)
+ type_c->cc1_rp_code = rp12k_code(value);
+ else if (type_c->cc_dfp_mode == CC_MODE_DFP_1_5)
+ type_c->cc1_rp_code = rp36k_code(value);
+ else if (type_c->cc_dfp_mode == CC_MODE_DFP_3_0)
+ type_c->cc1_rp_code = rp4pk_code(value);
+ } else if (!strncmp(buf, "cc1_rd_code", 11)) {
+ buf = buf + 11;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_rd_code = rd_code(value);
+ } else if (!strncmp(buf, "cc1_vref_ufp_vref_1p23v", 23)) {
+ buf = buf + 23;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_ufp = (type_c->cc1_vref_ufp &
+ (~vref_1p23v(0xf))) |
+ vref_1p23v(value);
+ } else if (!strncmp(buf, "cc1_vref_ufp_vref_0p66v", 23)) {
+ buf = buf + 23;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_ufp = (type_c->cc1_vref_ufp &
+ (~vref_0p66v(0xf))) |
+ vref_0p66v(value);
+ } else if (!strncmp(buf, "cc1_vref_ufp_vref_0p2v", 22)) {
+ buf = buf + 22;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_ufp = (type_c->cc1_vref_ufp &
+ (~vref_0p2v(0x7))) |
+ vref_0p2v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_usb_vref_1p6v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_usb = (type_c->cc1_vref_dfp_usb &
+ (~vref_0_1p6v(0xf))) | vref_0_1p6v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_usb_vref_0p2v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_usb = (type_c->cc1_vref_dfp_usb &
+ (~vref_0p2v(0x7))) | vref_0p2v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_1_5_vref_1p6v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_1_5 = (type_c->cc1_vref_dfp_1_5 &
+ (~vref_1_1p6v(0xf))) | vref_1_1p6v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_1_5_vref_0p4v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_1_5 = (type_c->cc1_vref_dfp_1_5 &
+ (~vref_0p4v(0x7))) | vref_0p4v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_1_5_vref_0p2v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_1_5 = (type_c->cc1_vref_dfp_1_5 &
+ (~vref_0p2v(0x7))) | vref_0p2v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_3_0_vref_2p6v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_ufp = (type_c->cc1_vref_dfp_1_5 &
+ (~vref_2p6v(0x7))) |
+ vref_2p6v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_3_0_vref_0p8v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_3_0 = (type_c->cc1_vref_dfp_3_0 &
+ (~vref_0p8v(0xf))) | vref_0p8v(value);
+ } else if (!strncmp(buf, "cc1_vref_dfp_3_0_vref_0p2v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc1_vref_dfp_3_0 = (type_c->cc1_vref_dfp_3_0 &
+ (~vref_0p2v(0x7))) | vref_0p2v(value);
+ } else if (!strncmp(buf, "cc2_rp_code", 11)) {
+ buf = buf + 11;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ if (type_c->cc_dfp_mode == CC_MODE_DFP_USB)
+ type_c->cc2_rp_code = rp12k_code(value);
+ else if (type_c->cc_dfp_mode == CC_MODE_DFP_1_5)
+ type_c->cc2_rp_code = rp36k_code(value);
+ else if (type_c->cc_dfp_mode == CC_MODE_DFP_3_0)
+ type_c->cc2_rp_code = rp4pk_code(value);
+ } else if (!strncmp(buf, "cc2_rd_code", 11)) {
+ buf = buf + 11;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_rd_code = rd_code(value);
+ } else if (!strncmp(buf, "cc2_vref_ufp_vref_1p23v", 23)) {
+ buf = buf + 23;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_ufp = (type_c->cc2_vref_ufp &
+ (~vref_1p23v(0xf))) |
+ vref_1p23v(value);
+ } else if (!strncmp(buf, "cc2_vref_ufp_vref_0p66v", 23)) {
+ buf = buf + 23;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_ufp = (type_c->cc2_vref_ufp &
+ (~vref_0p66v(0xf))) |
+ vref_0p66v(value);
+ } else if (!strncmp(buf, "cc2_vref_ufp_vref_0p2v", 22)) {
+ buf = buf + 22;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_ufp = (type_c->cc2_vref_ufp &
+ (~vref_0p2v(0x7))) |
+ vref_0p2v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_usb_vref_1p6v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_usb = (type_c->cc2_vref_dfp_usb &
+ (~vref_0_1p6v(0xf))) | vref_0_1p6v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_usb_vref_0p2v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_usb = (type_c->cc2_vref_dfp_usb &
+ (~vref_0p2v(0x7))) | vref_0p2v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_1_5_vref_1p6v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_1_5 = (type_c->cc2_vref_dfp_1_5 &
+ (~vref_1_1p6v(0xf))) | vref_1_1p6v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_1_5_vref_0p4v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_1_5 = (type_c->cc2_vref_dfp_1_5 &
+ (~vref_0p4v(0x7))) | vref_0p4v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_1_5_vref_0p2v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_1_5 = (type_c->cc2_vref_dfp_1_5 &
+ (~vref_0p2v(0x7))) | vref_0p2v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_3_0_vref_2p6v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_3_0 = (type_c->cc2_vref_dfp_3_0 &
+ (~vref_2p6v(0x7))) | vref_2p6v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_3_0_vref_0p8v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_3_0 = (type_c->cc2_vref_dfp_3_0 &
+ (~vref_0p8v(0xf))) | vref_0p8v(value);
+ } else if (!strncmp(buf, "cc2_vref_dfp_3_0_vref_0p2v", 26)) {
+ buf = buf + 26;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->cc2_vref_dfp_3_0 = (type_c->cc2_vref_dfp_3_0 &
+ (~vref_0p2v(0x7))) | vref_0p2v(value);
+ } else if (!strncmp(buf, "debounce_val", 12)) {
+ buf = buf + 12;
+ buf = skip_spaces(buf);
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0) {
+ dev_err(type_c->dev, "kstrtoint ret error (%d)", ret);
+ return -EFAULT;
+ }
+ type_c->debounce_val = value;
+ } else
+ dev_err(type_c->dev, "UNKNOWN input (%s)", buf);
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+ return count;
+}
+
+static const struct file_operations type_c_set_parameter_fops = {
+ .open = type_c_set_parameter_open,
+ .write = type_c_set_parameter_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int type_c_status_show(struct seq_file *s, void *unused)
+{
+ struct type_c_data *type_c = s->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ seq_printf(s, "Now dr_mode is %s (Is%s role swap)\n",
+ ({ char *tmp;
+ switch (type_c->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ tmp = "USB_DR_MODE_PERIPHERAL"; break;
+ case USB_DR_MODE_HOST:
+ tmp = "USB_DR_MODE_HOST"; break;
+ default:
+ tmp = "USB_DR_MODE_UNKNOWN"; break;
+ } tmp; }), type_c->is_role_swap == ROLE_SWAP?"":" not");
+
+ seq_printf(s, "In %s mode %s %s at %s (cc_status=0x%x)\n",
+ type_c->cc_mode?"host":"device",
+ type_c->cc_mode ?
+ (type_c->is_attach?"attach":"detach") :
+ (type_c->is_attach?"connect":"disconnect"),
+ type_c->cc_mode?"device":"host",
+ type_c->at_cc1?"cc1":"cc2", type_c->cc_status);
+
+ seq_printf(s, "Read Register (type_c_ctrl_cc1_0=0x%x)\n",
+ readl(type_c->type_c_reg_base + 0x0));
+ seq_printf(s, "Read Register (type_c_ctrl_cc1_1=0x%x)\n",
+ readl(type_c->type_c_reg_base + 0x4));
+ seq_printf(s, "Read Register (type_c_ctrl_cc2_0=0x%x)\n",
+ readl(type_c->type_c_reg_base + 0x8));
+ seq_printf(s, "Read Register (type_c_ctrl_cc2_1=0x%x)\n",
+ readl(type_c->type_c_reg_base + 0xc));
+ seq_printf(s, "Read Register (type_c_status=0x%x)\n",
+ readl(type_c->type_c_reg_base + 0x10));
+ seq_printf(s, "Read Register (type_c_ctrl=0x%x)\n",
+ readl(type_c->type_c_reg_base + 0x14));
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ return 0;
+}
+
+static int type_c_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, type_c_status_show, inode->i_private);
+}
+
+static const struct file_operations type_c_status_fops = {
+ .open = type_c_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int type_c_debug_show(struct seq_file *s, void *unused)
+{
+ struct type_c_data *type_c = s->private;
+
+ seq_printf(s, "Debug: %s\n",
+ type_c->debug?"Enable":"disable");
+
+ return 0;
+}
+
+static int type_c_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, type_c_debug_show, inode->i_private);
+}
+
+static ssize_t type_c_debug_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct type_c_data *type_c = s->private;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "enable", 6))
+ type_c->debug = true;
+ else if (!strncmp(buf, "disable", 7))
+ type_c->debug = false;
+
+ return count;
+}
+
+static const struct file_operations type_c_debug_fops = {
+ .open = type_c_debug_open,
+ .write = type_c_debug_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static inline void create_debug_files(struct type_c_data *type_c)
+{
+ dev_err(type_c->dev, "%s", __func__);
+
+ type_c->debug_dir = debugfs_create_dir("type_c", usb_debug_root);
+ if (!type_c->debug_dir) {
+ dev_err(type_c->dev, "%s Error debug_dir is NULL", __func__);
+ return;
+ }
+
+ if (!debugfs_create_file("parameter", 0444, type_c->debug_dir, type_c,
+ &type_c_parameter_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("set_parameter", 0644,
+ type_c->debug_dir, type_c,
+ &type_c_set_parameter_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("status", 0444, type_c->debug_dir, type_c,
+ &type_c_status_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("debug", 0644,
+ type_c->debug_dir, type_c,
+ &type_c_debug_fops))
+ goto file_error;
+
+ return;
+
+file_error:
+ debugfs_remove_recursive(type_c->debug_dir);
+}
+#endif //CONFIG_DYNAMIC_DEBUG
+
+#define DEFAULT_CHIP_REVISION 0xA00
+#define MAX_CHIP_REVISION 0xC00
+
+static int __get_chip_revision(void)
+{
+ int chip_revision = 0xFFF;
+ char revision[] = "FFF";
+ struct soc_device_attribute soc_att[] = {{.revision = revision}, {}};
+ struct soc_device_attribute *soc_att_match = NULL;
+
+ while (soc_att_match == NULL) {
+ chip_revision--;
+
+ if (chip_revision <= DEFAULT_CHIP_REVISION)
+ break;
+ if (chip_revision > MAX_CHIP_REVISION)
+ chip_revision = MAX_CHIP_REVISION;
+ else if ((chip_revision & 0xFF) > 0xF)
+ chip_revision = (chip_revision & 0xF00) + 0xF;
+
+ snprintf(revision, 4, "%X", chip_revision);
+
+ soc_att_match = (struct soc_device_attribute *)
+ soc_device_match(soc_att);
+ }
+
+ if (soc_att_match) {
+ pr_debug("%s get chip_revision %x\n", __func__, chip_revision);
+ return chip_revision;
+ }
+
+ pr_err("%s ERROR: no match chip_revision\n", __func__);
+ return DEFAULT_CHIP_REVISION;
+}
+
+/* Init and probe */
+static int dwc3_rtk_type_c_init(struct type_c_data *type_c)
+{
+ struct device *dev = type_c->dev;
+ u32 debounce_val = type_c->debounce_val;// 1b,1us 7f,4.7us
+ unsigned long flags;
+
+ enable_writel(debounce_val<<1,
+ type_c->type_c_reg_base + USB_TYPEC_CTRL);
+ dev_info(dev, "%s set debounce = 0x%x (check--> 0x%x)\n",
+ __func__, debounce_val,
+ readl(type_c->type_c_reg_base + USB_TYPEC_CTRL));
+
+ rtk_type_c_init(type_c);
+
+ switch_type_c_dr_mode(type_c, 0, disable_cc);
+
+ spin_lock_irqsave(&type_c->lock, flags);
+
+ dev_info(dev, "First check USB_DR_MODE_PERIPHERAL");
+ type_c->cc_mode = IN_DEVICE_MODE;
+ type_c->is_attach = IN_DETACH;
+ type_c->connect_change = CONNECT_NO_CHANGE;
+ if (type_c->boot_check_time < 0)
+ type_c->check_at_boot = false;
+ else
+ type_c->check_at_boot = true;
+ dev_info(dev, "First time device mode check is %s",
+ type_c->check_at_boot?"Enable":"Diable");
+
+ type_c->rd_en_at_first = true;
+
+ detect_host(type_c);
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ schedule_delayed_work(&type_c->delayed_work,
+ msecs_to_jiffies(0));
+
+ return 0;
+}
+
+static void dwc3_rtk_type_c_probe_work(struct work_struct *work)
+{
+ struct type_c_data *type_c = container_of(work,
+ struct type_c_data, start_work);
+ struct device *dev = type_c->dev;
+ int ret = 0;
+
+ unsigned long probe_time = jiffies;
+
+ dev_info(dev, "%s Start ...\n", __func__);
+
+ ret = dwc3_rtk_type_c_init(type_c);
+
+ if (ret)
+ dev_err(dev, "%s failed to init type_c\n", __func__);
+
+ dev_info(dev, "%s End ... ok! (take %d ms)\n", __func__,
+ jiffies_to_msecs(jiffies - probe_time));
+}
+
+static int dwc3_rtk_type_c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct type_c_data *type_c;
+ int irq;
+ int ret = 0;
+ unsigned long probe_time = jiffies;
+
+ dev_info(dev, "ENTER %s", __func__);
+ type_c = devm_kzalloc(dev, sizeof(*type_c), GFP_KERNEL);
+ if (!type_c)
+ return -ENOMEM;
+
+ type_c->type_c_reg_base = of_iomap(pdev->dev.of_node, 0);
+ if (type_c->type_c_reg_base == NULL) {
+ dev_err(&pdev->dev, "error mapping memory for reg_base\n");
+ ret = -EFAULT;
+ goto err1;
+ }
+
+ type_c->dev = dev;
+
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Type C driver with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ ret = request_irq(irq, type_c_detect_irq,
+ IRQF_SHARED, "type_c_detect", type_c);
+
+ spin_lock_init(&type_c->lock);
+ type_c->chip_revision = __get_chip_revision();
+
+ if (node && of_device_is_available(node)) {
+ ret = of_property_read_u32(node, "boot_check_time",
+ &type_c->boot_check_time);
+ if (ret)
+ type_c->boot_check_time = -1;
+
+ dev_info(dev,
+ "Set device mode boot_check_time %d ms ==> (%s to check)\n",
+ type_c->boot_check_time,
+ type_c->boot_check_time < 0?"Disable":"Enable");
+
+ if (of_property_read_bool(node, "filter_config_channel_signal"))
+ type_c->filter_config_channel_signal = true;
+ else
+ type_c->filter_config_channel_signal = false;
+ dev_info(dev, "Set filter_config_channel_signal is %s\n",
+ type_c->filter_config_channel_signal ?
+ "True":"False");
+
+ }
+
+ if (node && of_device_is_available(node)) {
+ const char *str;
+ u32 val;
+ struct device_node *sub_node;
+ u8 array_vals[3];
+ u32 u32_vals[3];
+ u32 default_revision;
+ char revision[4] = {0};
+
+ ret = of_property_read_u32(node, "default_revision",
+ &default_revision);
+ if (ret) {
+ default_revision = 0xA00;
+ dev_info(dev, "%s: No set default_revision (use %x)\n",
+ __func__, default_revision);
+ }
+
+ if (!type_c->chip_revision ||
+ type_c->chip_revision > default_revision)
+ snprintf(revision, 4, "%X", default_revision);
+ else
+ snprintf(revision, 4, "%X", type_c->chip_revision);
+ dev_info(dev, "Chip revision is %x (support revision %x) to load %s parameter\n",
+ type_c->chip_revision,
+ default_revision, revision);
+ sub_node = of_get_child_by_name(node, revision);
+
+ ret = of_property_read_string(sub_node, "cc_dfp_mode", &str);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc_dfp_mode error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ if (!strcmp(str, "dfp_usb")) {
+ type_c->cc_dfp_mode = CC_MODE_DFP_USB;
+ type_c->cc1_rp = En_rp12k;
+ type_c->cc2_rp = En_rp12k;
+ } else if (!strcmp(str, "dfp_1_5")) {
+ type_c->cc_dfp_mode = CC_MODE_DFP_1_5;
+ type_c->cc1_rp = En_rp36k;
+ type_c->cc2_rp = En_rp36k;
+ } else if (!strcmp(str, "dfp_3_0")) {
+ type_c->cc_dfp_mode = CC_MODE_DFP_3_0;
+ type_c->cc1_rp = En_rp4p7k;
+ type_c->cc2_rp = En_rp4p7k;
+ } else {
+ dev_err(&pdev->dev, "%s: unknown cc_dfp_mode %s\n",
+ __func__, str);
+ }
+
+ //cc1 parameters
+ ret = of_property_read_u32(sub_node, "cc1_rp_4p7k_code",
+ &u32_vals[0]);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_rp_4p7k_code error(%d)\n",
+ __func__, u32_vals[0]);
+ goto err1;
+ }
+ ret = of_property_read_u32(sub_node, "cc1_rp_36k_code",
+ &u32_vals[1]);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_rp_36k_code error(%d)\n",
+ __func__,
+ u32_vals[1]);
+ goto err1;
+ }
+ ret = of_property_read_u32(sub_node, "cc1_rp_12k_code",
+ &u32_vals[2]);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_rp_12k_code error(%d)\n",
+ __func__, u32_vals[2]);
+ goto err1;
+ }
+ type_c->cc1_rp_code = rp4pk_code(u32_vals[0])
+ | rp36k_code(u32_vals[1])
+ | rp12k_code(u32_vals[2]);
+
+ ret = of_property_read_u32(sub_node, "cc1_rd_code", &val);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_rd_code error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc1_rd_code = rd_code(val);
+
+ ret = of_property_read_u8_array(sub_node, "cc1_vref_ufp",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_vref_ufp error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc1_vref_ufp = vref_1p23v(array_vals[0]) |
+ vref_0p66v(array_vals[1]) |
+ vref_0p2v(array_vals[2]);
+
+ ret = of_property_read_u8_array(sub_node, "cc1_vref_dfp_usb",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_vref_dfp_usb error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc1_vref_dfp_usb = vref_0_1p6v(array_vals[0]) |
+ vref_0p2v(array_vals[1]);
+
+ ret = of_property_read_u8_array(sub_node, "cc1_vref_dfp_1_5",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_vref_dfp_1_5 error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc1_vref_dfp_1_5 = vref_1_1p6v(array_vals[0]) |
+ vref_0p4v(array_vals[1]) |
+ vref_0p2v(array_vals[2]);
+
+ ret = of_property_read_u8_array(sub_node, "cc1_vref_dfp_3_0",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc1_vref_dfp_3_0 error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc1_vref_dfp_3_0 = vref_2p6v(array_vals[0]) |
+ vref_0p8v(array_vals[1]) | vref_0p2v(array_vals[2]);
+
+ //cc2 parameters
+ ret = of_property_read_u32(sub_node, "cc2_rp_4p7k_code",
+ &u32_vals[0]);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_rp_4p7k_code error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ ret = of_property_read_u32(sub_node, "cc2_rp_36k_code",
+ &u32_vals[1]);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_rp_36k_code error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ ret = of_property_read_u32(sub_node, "cc2_rp_12k_code",
+ &u32_vals[2]);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_rp_12k_code error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc2_rp_code = rp4pk_code(array_vals[0])
+ | rp36k_code(array_vals[1])
+ | rp12k_code(array_vals[2]);
+
+ ret = of_property_read_u32(sub_node, "cc2_rd_code", &val);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_rd_code error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc2_rd_code = rd_code(val);
+
+ ret = of_property_read_u8_array(sub_node, "cc2_vref_ufp",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_vref_ufp error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc2_vref_ufp = vref_1p23v(array_vals[0]) |
+ vref_0p66v(array_vals[1]) |
+ vref_0p2v(array_vals[2]);
+
+ ret = of_property_read_u8_array(sub_node, "cc2_vref_dfp_usb",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_vref_dfp_usb error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc2_vref_dfp_usb = vref_0_1p6v(array_vals[0]) |
+ vref_0p2v(array_vals[1]);
+
+ ret = of_property_read_u8_array(sub_node, "cc2_vref_dfp_1_5",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_vref_dfp_1_5 error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc2_vref_dfp_1_5 = vref_1_1p6v(array_vals[0]) |
+ vref_0p4v(array_vals[1]) | vref_0p2v(array_vals[2]);
+
+ ret = of_property_read_u8_array(sub_node, "cc2_vref_dfp_3_0",
+ array_vals, 3);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cc2_vref_dfp_3_0 error(%d)\n",
+ __func__, ret);
+ goto err1;
+ }
+ type_c->cc2_vref_dfp_3_0 = vref_2p6v(array_vals[0]) |
+ vref_0p8v(array_vals[1]) | vref_0p2v(array_vals[2]);
+
+ type_c->debounce_val = 0x7f;/* 1b,1us 7f,4.7us */
+ }
+
+ if (node) {
+ struct device_node *parent_node;
+
+ parent_node = of_get_parent(node);
+ if (parent_node != NULL) {
+ type_c->dwc3_rtk = platform_get_drvdata(
+ of_find_device_by_node(parent_node));
+ } else {
+ dev_err(dev, "%s No find dwc3_rtk", __func__);
+ ret = -ENODEV;
+ goto err1;
+ }
+ }
+
+ type_c->is_attach = IN_DETACH;
+ type_c->is_role_swap = NO_ROLE_SWAP;
+
+ if (of_property_read_bool(node, "debug")) {
+ dev_info(&pdev->dev, "%s device tree set debug flag\n",
+ __func__);
+ type_c->debug = true;
+ } else {
+ type_c->debug = false;
+ }
+
+ INIT_DELAYED_WORK(&type_c->delayed_work, host_device_switch);
+ INIT_DELAYED_WORK(&type_c->boot_check_work, boot_time_check);
+
+ if (node) {
+ if (of_property_read_bool(node, "delay_probe_work")) {
+ INIT_WORK(&type_c->start_work,
+ dwc3_rtk_type_c_probe_work);
+
+ schedule_work(&type_c->start_work);
+ } else {
+ ret = dwc3_rtk_type_c_init(type_c);
+ if (ret) {
+ dev_err(dev, "%s failed to init type_c\n",
+ __func__);
+ goto err1;
+ }
+ }
+ } else {
+ dev_err(dev, "no device node, failed to init type_c\n");
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ platform_set_drvdata(pdev, type_c);
+
+ device_create_file(type_c->dev, &dev_attr_role_swap);
+ device_create_file(type_c->dev, &dev_attr_force_set_mode);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+ create_debug_files(type_c);
+#endif
+
+ dev_info(&pdev->dev, "Exit %s OK (take %d ms)\n", __func__,
+ jiffies_to_msecs(jiffies - probe_time));
+ return 0;
+
+err1:
+ dev_err(&pdev->dev, "%s: Probe fail, %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int dwc3_rtk_type_c_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+ u32 default_ctrl;
+ unsigned long flags;
+
+ dev_info(dev, "[USB] Enter %s", __func__);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+ debugfs_remove_recursive(type_c->debug_dir);
+#endif
+
+ device_remove_file(type_c->dev, &dev_attr_role_swap);
+ device_remove_file(type_c->dev, &dev_attr_force_set_mode);
+
+ cancel_delayed_work_sync(&type_c->delayed_work);
+ flush_delayed_work(&type_c->delayed_work);
+ BUG_ON(delayed_work_pending(&type_c->delayed_work));
+
+ cancel_delayed_work_sync(&type_c->boot_check_work);
+ flush_delayed_work(&type_c->boot_check_work);
+ BUG_ON(delayed_work_pending(&type_c->boot_check_work));
+
+ spin_lock_irqsave(&type_c->lock, flags);
+ /* disable interrupt */
+ default_ctrl = readl(type_c->type_c_reg_base + USB_TYPEC_CTRL) &
+ debounce_time_MASK;
+ writel(default_ctrl, type_c->type_c_reg_base + USB_TYPEC_CTRL);
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+ dev_info(&pdev->dev, "[USB] Exit %s\n", __func__);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtk_dwc3_type_c_match[] = {
+ { .compatible = "realtek,dwc3-type_c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtk_dwc3_type_c_match);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_rtk_type_c_prepare(struct device *dev)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+ u32 default_ctrl;
+ unsigned long flags;
+ int ret = 0;
+
+ dev_info(dev, "[USB] Enter %s\n", __func__);
+
+ if (!type_c) {
+ dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__);
+ goto out;
+ }
+
+ cancel_delayed_work_sync(&type_c->delayed_work);
+ flush_delayed_work(&type_c->delayed_work);
+ BUG_ON(delayed_work_pending(&type_c->delayed_work));
+
+ cancel_delayed_work_sync(&type_c->boot_check_work);
+ flush_delayed_work(&type_c->boot_check_work);
+ BUG_ON(delayed_work_pending(&type_c->boot_check_work));
+
+ spin_lock_irqsave(&type_c->lock, flags);
+ /* disable interrupt */
+ default_ctrl = readl(type_c->type_c_reg_base + USB_TYPEC_CTRL) &
+ debounce_time_MASK;
+ writel(default_ctrl, type_c->type_c_reg_base + USB_TYPEC_CTRL);
+
+ spin_unlock_irqrestore(&type_c->lock, flags);
+
+out:
+ dev_info(dev, "[USB] Exit %s\n", __func__);
+ return ret;
+}
+
+static void dwc3_rtk_type_c_complete(struct device *dev)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+
+ dev_info(dev, "[USB] Enter %s\n", __func__);
+
+ if (!type_c) {
+ dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__);
+ goto out;
+ }
+
+out:
+ dev_info(dev, "[USB] Exit %s\n", __func__);
+}
+
+static int dwc3_rtk_type_c_suspend(struct device *dev)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+
+ dev_info(dev, "[USB] Enter %s", __func__);
+
+ if (!type_c) {
+ dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__);
+ goto out;
+ }
+
+out:
+ dev_info(dev, "[USB] Exit %s\n", __func__);
+ return 0;
+}
+
+static int dwc3_rtk_type_c_resume(struct device *dev)
+{
+ struct type_c_data *type_c = dev_get_drvdata(dev);
+ bool reinit = false;
+
+ dev_info(dev, "[USB] Enter %s", __func__);
+
+ if (!type_c) {
+ dev_err(dev, "[USB] %s type_c_data is NULL\n", __func__);
+ goto out;
+ }
+
+ dev_info(dev, "[USB] %s reinit dwc3_rtk_type_c_init\n",
+ __func__);
+
+ if (type_c->is_attach == IN_ATTACH) {
+ u32 enable_cc;
+
+ rtk_type_c_init(type_c);
+ enable_writel(ENABLE_TYPE_C_DETECT,
+ type_c->type_c_reg_base + USB_TYPEC_CTRL);
+ enable_cc = type_c->at_cc1?enable_cc1:enable_cc2;
+ switch_type_c_plug_config(type_c,
+ type_c->dr_mode, enable_cc);
+ } else {
+ reinit = true;
+ }
+
+ if (reinit)
+ dwc3_rtk_type_c_init(type_c);
+
+out:
+ dev_info(dev, "[USB] Exit %s\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_rtk_type_c_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(
+ dwc3_rtk_type_c_suspend, dwc3_rtk_type_c_resume)
+ .prepare = dwc3_rtk_type_c_prepare,
+ .complete = dwc3_rtk_type_c_complete,
+};
+
+#define DEV_PM_OPS (&dwc3_rtk_type_c_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver dwc3_rtk_type_c_driver = {
+ .probe = dwc3_rtk_type_c_probe,
+ .remove = dwc3_rtk_type_c_remove,
+ .driver = {
+ .name = "rtk-dwc3-type_c",
+ .of_match_table = of_match_ptr(rtk_dwc3_type_c_match),
+ .pm = DEV_PM_OPS,
+ },
+};
+
+static int __init dwc3_rtk_type_c_driver_init(void)
+{
+ return platform_driver_register(&(dwc3_rtk_type_c_driver));
+}
+module_init(dwc3_rtk_type_c_driver_init);
+
+static void __exit dwc3_rtk_type_c_driver_exit(void)
+{
+ platform_driver_unregister(&(dwc3_rtk_type_c_driver));
+}
+module_exit(dwc3_rtk_type_c_driver_exit);
+
+MODULE_ALIAS("platform:rtk-dwc3-type_c");
+MODULE_LICENSE("GPL");
+
--
2.28.0
More information about the linux-realtek-soc
mailing list