The usb controller of Kirin960 is DesignWare Cores SuperSpeed USB 3.0 
Controller.
The patch modifies dwc3 for support Kirin960 and adds codes for a USB Hub on 
board Hikey960.

Signed-off-by: Yu Chen <cheny...@huawei.com>
Signed-off-by: Ning Fan <fanni...@hisilicon.com>
Signed-off-by: Di Yang <yangd...@hisilicon.com>
Signed-off-by: Rui Li <liru...@hisilicon.com>

---
 arch/arm64/configs/defconfig             |    5 +
 drivers/usb/dwc3/Kconfig                 |   26 +
 drivers/usb/dwc3/Makefile                |    5 +
 drivers/usb/dwc3/core.c                  |   78 +-
 drivers/usb/dwc3/core.h                  |   19 +-
 drivers/usb/dwc3/dwc3-hi3660.c           |  310 +++++
 drivers/usb/dwc3/dwc3-hisi.c             | 1972 ++++++++++++++++++++++++++++++
 drivers/usb/dwc3/dwc3-hisi.h             |  293 +++++
 drivers/usb/dwc3/dwc3-otg.c              |  360 ++++++
 drivers/usb/dwc3/dwc3-otg.h              |  133 ++
 drivers/usb/dwc3/ep0.c                   |   55 +-
 drivers/usb/dwc3/gadget.c                |   20 +-
 drivers/usb/dwc3/hisi_hikey_gpio.c       |  300 +++++
 drivers/usb/dwc3/host.c                  |   13 +
 drivers/usb/dwc3/io.h                    |   14 +
 include/linux/hisi/log/hisi_log.h        |  143 +++
 include/linux/hisi/usb/hisi_hikey_gpio.h |   24 +
 include/linux/hisi/usb/hisi_usb.h        |   57 +
 18 files changed, 3819 insertions(+), 8 deletions(-)
 create mode 100644 drivers/usb/dwc3/dwc3-hi3660.c
 create mode 100644 drivers/usb/dwc3/dwc3-hisi.c
 create mode 100644 drivers/usb/dwc3/dwc3-hisi.h
 create mode 100644 drivers/usb/dwc3/dwc3-otg.c
 create mode 100644 drivers/usb/dwc3/dwc3-otg.h
 create mode 100644 drivers/usb/dwc3/hisi_hikey_gpio.c
 create mode 100644 include/linux/hisi/log/hisi_log.h
 create mode 100644 include/linux/hisi/usb/hisi_hikey_gpio.h
 create mode 100644 include/linux/hisi/usb/hisi_usb.h

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 34480e9af2e7..8e61b7d96bba 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -405,6 +405,7 @@ CONFIG_SND_SOC_SAMSUNG=y
 CONFIG_SND_SOC_RCAR=m
 CONFIG_SND_SOC_AK4613=m
 CONFIG_SND_SIMPLE_CARD=y
+CONFIG_HISI_HIKEY_GPIO=y
 CONFIG_USB=y
 CONFIG_USB_OTG=y
 CONFIG_USB_XHCI_HCD=y
@@ -419,6 +420,9 @@ CONFIG_USB_OHCI_HCD_PLATFORM=y
 CONFIG_USB_RENESAS_USBHS=m
 CONFIG_USB_STORAGE=y
 CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_DUAL_ROLE=y
+CONFIG_USB_DWC3_HISI=y
+CONFIG_USB_DWC3_OTG=y
 CONFIG_USB_DWC2=y
 CONFIG_USB_CHIPIDEA=y
 CONFIG_USB_CHIPIDEA_UDC=y
@@ -428,6 +432,7 @@ CONFIG_USB_HSIC_USB3503=y
 CONFIG_NOP_USB_XCEIV=y
 CONFIG_USB_MSM_OTG=y
 CONFIG_USB_QCOM_8X16_PHY=y
+CONFIG_EXTCON=y
 CONFIG_USB_ULPI=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_RENESAS_USBHS_UDC=m
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index ab8c0e0d3b60..5f7d9f19f503 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -106,4 +106,30 @@ config USB_DWC3_ST
          inside (i.e. STiH407).
          Say 'Y' or 'M' if you have one such device.
 
+config USB_DWC3_HISI
+       tristate "Hisilicon Platforms"
+       select USB_DWC3_OTG
+       depends on USB_DWC3
+       default n
+       help
+         Support of USB2/3 functionality in hisilicon platforms,
+         Say 'Y' or 'M' here if you have one such device.
+         Use for hisilicon device and it will select USB_DWC3_OTG
+         if Say 'Y' or 'M' here.
+
+config USB_DWC3_OTG
+       bool "Enable DWC3 OTG"
+       default n
+       help
+         Support of USB2/3 functionality in hisilicon platforms,
+         Say 'Y' or 'M' here if you have one such device.
+         Use for hisilicon device
+         if Say 'Y' or 'M' here.
+
+config HISI_HIKEY_GPIO
+    tristate "HISI_HIKEY_GPIO"
+    depends on GPIOLIB
+    default n
+    help
+       If you say yes here you get support for hisi hikey gpio.
 endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index f15fabbd1e59..c2c32a5effc7 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -1,6 +1,7 @@
 # define_trace.h needs to know how to find our header
 CFLAGS_trace.o                         := -I$(src)
 
+ccflags-$(CONFIG_USB_DWC3_OTG)         += -DDWC3_OTG_FORCE_MODE
 obj-$(CONFIG_USB_DWC3)                 += dwc3.o
 
 dwc3-y                                 := core.o
@@ -29,6 +30,8 @@ ifneq ($(CONFIG_DEBUG_FS),)
        dwc3-y                          += debugfs.o
 endif
 
+dwc3-$(CONFIG_USB_DWC3_OTG)            += dwc3-otg.o
+
 ##
 # Platform-specific glue layers go here
 #
@@ -47,3 +50,5 @@ obj-$(CONFIG_USB_DWC3_PCI)            += dwc3-pci.o
 obj-$(CONFIG_USB_DWC3_KEYSTONE)                += dwc3-keystone.o
 obj-$(CONFIG_USB_DWC3_OF_SIMPLE)       += dwc3-of-simple.o
 obj-$(CONFIG_USB_DWC3_ST)              += dwc3-st.o
+obj-$(CONFIG_USB_DWC3_HISI)            += dwc3-hisi.o  dwc3-hi3660.o
+obj-$(CONFIG_HISI_HIKEY_GPIO)          += hisi_hikey_gpio.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 03474d3575ab..d0105a26867d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -44,7 +44,7 @@
 #include "core.h"
 #include "gadget.h"
 #include "io.h"
-
+#include "dwc3-otg.h"
 #include "debug.h"
 
 #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
@@ -87,6 +87,8 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
                        mode = USB_DR_MODE_HOST;
                else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
                        mode = USB_DR_MODE_PERIPHERAL;
+               else if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE))
+                       mode = USB_DR_MODE_OTG;
        }
 
        if (mode != dwc->dr_mode) {
@@ -103,7 +105,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
 static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
 static int dwc3_event_buffers_setup(struct dwc3 *dwc);
 
-static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
 {
        u32 reg;
 
@@ -113,6 +115,7 @@ static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
+#ifndef CONFIG_USB_DWC3_HISI
 static void __dwc3_set_mode(struct work_struct *work)
 {
        struct dwc3 *dwc = work_to_dwc(work);
@@ -177,6 +180,7 @@ static void __dwc3_set_mode(struct work_struct *work)
                break;
        }
 }
+#endif
 
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
 {
@@ -362,6 +366,12 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
 
        evt = dwc->ev_buf;
        evt->lpos = 0;
+       #ifdef CONFIG_USB_DWC3_HISI
+       evt->count = 0;
+       evt->flags = 0;
+       memset(evt->buf, 0, evt->length);
+       #endif
+
        dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
                        lower_32_bits(evt->dma));
        dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
@@ -730,7 +740,13 @@ static void dwc3_core_setup_global_control(struct dwc3 
*dwc)
         */
        if (dwc->revision < DWC3_REVISION_190A)
                reg |= DWC3_GCTL_U2RSTECN;
-
+       #ifdef DWC3_OTG_FORCE_MODE
+       /*
+        * if ID status is detected by third module, default device mode.
+        */
+       reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
+       reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE);
+       #endif
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
@@ -957,6 +973,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
                }
                break;
        case USB_DR_MODE_OTG:
+               #ifndef CONFIG_USB_DWC3_HISI
                INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
                ret = dwc3_drd_init(dwc);
                if (ret) {
@@ -964,6 +981,30 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
                                dev_err(dev, "failed to initialize 
dual-role\n");
                        return ret;
                }
+               #else
+               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+
+               ret = dwc3_otg_init(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to initialize otg\n");
+                       return ret;
+               }
+
+               ret = dwc3_host_init(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to initialize host\n");
+                       dwc3_otg_exit(dwc);
+                       return ret;
+               }
+
+               ret = dwc3_gadget_init(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to initialize gadget\n");
+                       dwc3_host_exit(dwc);
+                       dwc3_otg_exit(dwc);
+                       return ret;
+               }
+               #endif
                break;
        default:
                dev_err(dev, "Unsupported mode of operation %d\n", 
dwc->dr_mode);
@@ -984,6 +1025,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
                break;
        case USB_DR_MODE_OTG:
                dwc3_drd_exit(dwc);
+               dwc3_otg_exit(dwc);
                break;
        default:
                /* do nothing */
@@ -1341,8 +1383,10 @@ static int dwc3_runtime_checks(struct dwc3 *dwc)
        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
        case USB_DR_MODE_OTG:
+#ifndef CONFIG_USB_DWC3_HISI
                if (dwc->connected)
                        return -EBUSY;
+#endif
                break;
        case USB_DR_MODE_HOST:
        default:
@@ -1367,6 +1411,7 @@ static int dwc3_runtime_suspend(struct device *dev)
 
        device_init_wakeup(dev, true);
 
+       pm_runtime_put(dev);
        return 0;
 }
 
@@ -1393,7 +1438,7 @@ static int dwc3_runtime_resume(struct device *dev)
        }
 
        pm_runtime_mark_last_busy(dev);
-       pm_runtime_put(dev);
+       pm_runtime_get(dev);
 
        return 0;
 }
@@ -1461,6 +1506,31 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
                        dwc3_runtime_idle)
 };
 
+int dwc3_resume_device(struct dwc3 *dwc)
+{
+       int status;
+
+       pr_info("[%s] +\n", __func__);
+       status = dwc3_runtime_resume(dwc->dev);
+       if (status < 0)
+               pr_err("dwc3_runtime_resume err, status:%d\n", status);
+
+       pr_info("[%s] -\n", __func__);
+       return status;
+}
+
+void dwc3_suspend_device(struct dwc3 *dwc)
+{
+       int status;
+
+       pr_info("[%s] +\n", __func__);
+       status = dwc3_runtime_suspend(dwc->dev);
+       if (status < 0)
+               pr_err("dwc3_runtime_suspend err, status:%d\n", status);
+
+       pr_info("[%s] -\n", __func__);
+}
+
 #ifdef CONFIG_OF
 static const struct of_device_id of_dwc3_match[] = {
        {
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index ea910acb4bb0..3b6dd99daf9a 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -750,6 +750,7 @@ struct dwc3_request {
        unsigned                mapped:1;
        unsigned                started:1;
        unsigned                zero:1;
+       unsigned                send_zlp:1;
 };
 
 /*
@@ -980,10 +981,17 @@ struct dwc3 {
        u8                      lpm_nyet_threshold;
        u8                      hird_threshold;
 
+       struct dwc3_otg *dwc_otg;
        const char              *hsphy_interface;
 
        unsigned                connected:1;
        unsigned                delayed_status:1;
+
+       /* the delayed status may come before notready interrupt,
+        * in this case, don't wait for delayed status
+        */
+       unsigned                status_queued:1;
+
        unsigned                ep0_bounced:1;
        unsigned                ep0_expect_in:1;
        unsigned                has_hibernation:1;
@@ -1175,7 +1183,7 @@ struct dwc3_gadget_ep_cmd_params {
 /* prototypes */
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
-
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
 /* check whether we are on the DWC_usb3 core */
 static inline bool dwc3_is_usb3(struct dwc3 *dwc)
 {
@@ -1209,6 +1217,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum 
dwc3_link_state state);
 int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
                struct dwc3_gadget_ep_cmd_params *params);
 int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 
param);
+int dwc3_conndone_notifier_register(struct notifier_block *nb);
+int dwc3_conndone_notifier_unregister(struct notifier_block *nb);
 #else
 static inline int dwc3_gadget_init(struct dwc3 *dwc)
 { return 0; }
@@ -1228,6 +1238,10 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep 
*dep, unsigned cmd,
 static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
                int cmd, u32 param)
 { return 0; }
+static inline int dwc3_conndone_notifier_register(struct notifier_block *nb)
+{ return 0; }
+static inline int dwc3_conndone_notifier_unregister(struct notifier_block *nb)
+{ return 0; }
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
@@ -1261,6 +1275,9 @@ static inline void 
dwc3_gadget_process_pending_events(struct dwc3 *dwc)
 }
 #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
 
+int dwc3_resume_device(struct dwc3 *dwc);
+void dwc3_suspend_device(struct dwc3 *dwc);
+
 #if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
 int dwc3_ulpi_init(struct dwc3 *dwc);
 void dwc3_ulpi_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/dwc3-hi3660.c b/drivers/usb/dwc3/dwc3-hi3660.c
new file mode 100644
index 000000000000..d8cdc0f7280b
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-hi3660.c
@@ -0,0 +1,310 @@
+/*
+ * dwc3-hi3660.c
+ *
+ * Copyright: (C) 2008-2018 hisilicon.
+ * Contact: wangbinghui<wangbing...@hisilicon.com>
+ *
+ * USB vbus for Hisilicon device
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose this file to be licensed under the terms
+ * of the GNU General Public License (GPL) Version 2 or the 2-clause
+ * BSD license listed below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include "dwc3-hisi.h"
+
+/*lint -e750 -esym(750,*)*/
+/* clk module will round to 228M */
+#define USB3OTG_ACLK_FREQ              229000000
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+#define SCTRL_SCDEEPSLEEPED                            0x08
+#define USB_REFCLK_ISO_EN               BIT(25)
+#define PCTRL_PERI_CTRL3                0x10
+#define USB_TCXO_EN                                            BIT(1)
+#define PERI_CTRL3_MSK_START            (16)
+#define SC_CLK_USB3PHY_3MUX1_SEL        BIT(25)
+
+#define SC_SEL_ABB_BACKUP               BIT(8)
+#define CLKDIV_MASK_START               (16)
+
+#define PERI_CRG_CLKDIV21               0xFC
+
+#define GT_CLK_ABB_BACKUP               BIT(22)
+#define PERI_CRG_CLK_DIS5               0x54
+
+#define PMC_PPLL3CTRL0                  0x048
+#define PPLL3_FBDIV_START               (8)
+#define PPLL3_EN                        BIT(0)
+#define PPLL3_BP                        BIT(1)
+#define PPLL3_LOCK                      BIT(26)
+
+#define PMC_PPLL3CTRL1                  0x04C
+#define PPLL3_INT_MOD                   BIT(24)
+#define GT_CLK_PPLL3                    BIT(26)
+
+#define PERI_CRG_CLK_EN5                0x50
+
+#define SC_USB3PHY_ABB_GT_EN            BIT(15)
+#define REF_SSP_EN                      BIT(16)
+/*lint -e750 +esym(750,*)*/
+
+static int usb3_regu_init(struct hisi_dwc3_device *hisi_dwc3)
+{
+       if (hisi_dwc3->is_regu_on != 0) {
+               usb_dbg("ldo already opened!\n");
+               return 0;
+       }
+
+       hisi_dwc3->is_regu_on = 1;
+
+       return 0;
+}
+
+static int usb3_regu_shutdown(struct hisi_dwc3_device *hisi_dwc3)
+{
+       if (hisi_dwc3->is_regu_on == 0) {
+               usb_dbg("regu already closed!\n");
+               return 0;
+       }
+
+       hisi_dwc3->is_regu_on = 0;
+
+       return 0;
+}
+
+static int usb3_clk_init(struct hisi_dwc3_device *hisi_dwc3)
+{
+       int ret;
+       u32 temp;
+       void __iomem *pctrl_base = hisi_dwc3->pctrl_reg_base;
+       void __iomem *pericfg_base = hisi_dwc3->pericfg_reg_base;
+
+       /* set usb aclk 240MHz to improve performance */
+       ret = clk_set_rate(hisi_dwc3->gt_aclk_usb3otg, USB3OTG_ACLK_FREQ);
+       if (ret)
+               usb_err("usb aclk set rate failed\n");
+
+       ret = clk_prepare_enable(hisi_dwc3->gt_aclk_usb3otg);
+       if (ret) {
+               usb_err("clk_prepare_enable gt_aclk_usb3otg failed\n");
+               return ret;
+       }
+
+       /* usb refclk iso enable */
+       writel(USB_REFCLK_ISO_EN, pericfg_base + PERI_CRG_ISODIS);
+
+       /* enable usb_tcxo_en */
+       writel(USB_TCXO_EN | (USB_TCXO_EN << PERI_CTRL3_MSK_START),
+              pctrl_base + PCTRL_PERI_CTRL3);
+
+       /* select usbphy clk from abb */
+       temp = readl(pctrl_base + PCTRL_PERI_CTRL24);
+       temp &= ~SC_CLK_USB3PHY_3MUX1_SEL;
+       writel(temp, pctrl_base + PCTRL_PERI_CTRL24);
+
+       /* open clk gate */
+       writel(GT_CLK_USB3OTG_REF | GT_ACLK_USB3OTG,
+              pericfg_base + PERI_CRG_CLK_EN4);
+
+       ret = clk_prepare_enable(hisi_dwc3->clk);
+       if (ret) {
+               usb_err("clk_prepare_enable clk failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void usb3_clk_shutdown(struct hisi_dwc3_device *hisi_dwc3)
+{
+       u32 temp;
+       void __iomem *pctrl_base = hisi_dwc3->pctrl_reg_base;
+       void __iomem *pericfg_base = hisi_dwc3->pericfg_reg_base;
+
+       writel(GT_CLK_USB3OTG_REF | GT_ACLK_USB3OTG,
+              pericfg_base + PERI_CRG_CLK_DIS4);
+
+       temp = readl(pctrl_base + PCTRL_PERI_CTRL24);
+       temp &= ~SC_CLK_USB3PHY_3MUX1_SEL;
+       writel(temp, pctrl_base + PCTRL_PERI_CTRL24);
+
+       /* disable usb_tcxo_en */
+       writel(0 | (USB_TCXO_EN << PERI_CTRL3_MSK_START),
+              pctrl_base + PCTRL_PERI_CTRL3);
+
+       clk_disable_unprepare(hisi_dwc3->clk);
+       clk_disable_unprepare(hisi_dwc3->gt_aclk_usb3otg);
+
+       msleep(20);
+}
+
+static void dwc3_release(struct hisi_dwc3_device *hisi_dwc3)
+{
+       u32 temp;
+       void __iomem *pericfg_base = hisi_dwc3->pericfg_reg_base;
+       void __iomem *otg_bc_base = hisi_dwc3->otg_bc_reg_base;
+
+       /* dis-reset the module */
+       writel(IP_RST_USB3OTG_MUX | IP_RST_USB3OTG_AHBIF | IP_RST_USB3OTG_32K,
+              pericfg_base + PERI_CRG_RSTDIS4);
+
+       /* reset phy */
+       writel(IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG,
+              pericfg_base + PERI_CRG_RSTEN4);
+
+       /* enable phy ref clk */
+       temp = readl(otg_bc_base + USBOTG3_CTRL0);
+       temp |= SC_USB3PHY_ABB_GT_EN;
+       writel(temp, otg_bc_base + USBOTG3_CTRL0);
+
+       temp = readl(otg_bc_base + USBOTG3_CTRL7);
+       temp |= REF_SSP_EN;
+       writel(temp, otg_bc_base + USBOTG3_CTRL7);
+
+       /* exit from IDDQ mode */
+       temp = readl(otg_bc_base + USBOTG3_CTRL2);
+       temp &= ~(USBOTG3CTRL2_POWERDOWN_HSP | USBOTG3CTRL2_POWERDOWN_SSP);
+       writel(temp, otg_bc_base + USBOTG3_CTRL2);
+
+       usleep_range(100, 120);
+
+       /* dis-reset phy */
+       writel(IP_RST_USB3OTGPHY_POR, pericfg_base + PERI_CRG_RSTDIS4);
+
+       /* dis-reset controller */
+       writel(IP_RST_USB3OTG, pericfg_base + PERI_CRG_RSTDIS4);
+
+       msleep(20);
+
+       /* fake vbus valid signal */
+       temp = readl(otg_bc_base + USBOTG3_CTRL3);
+       temp |= (USBOTG3_CTRL3_VBUSVLDEXT | USBOTG3_CTRL3_VBUSVLDEXTSEL);
+       writel(temp, otg_bc_base + USBOTG3_CTRL3);
+
+       usleep_range(100, 120);
+}
+
+static void dwc3_reset(struct hisi_dwc3_device *hisi_dwc3)
+{
+       void __iomem *pericfg_base = hisi_dwc3->pericfg_reg_base;
+
+       writel(IP_RST_USB3OTG, pericfg_base + PERI_CRG_RSTEN4);
+       writel(IP_RST_USB3OTGPHY_POR, pericfg_base + PERI_CRG_RSTEN4);
+       writel(IP_RST_USB3OTG_MUX | IP_RST_USB3OTG_AHBIF | IP_RST_USB3OTG_32K,
+              pericfg_base + PERI_CRG_RSTEN4);
+}
+
+static int hi3660_usb3phy_init(struct hisi_dwc3_device *hisi_dwc3)
+{
+       int ret;
+
+       usb_dbg("+\n");
+
+       ret = usb3_regu_init(hisi_dwc3);
+       if (ret)
+               return ret;
+
+       ret = usb3_clk_init(hisi_dwc3);
+       if (ret)
+               return ret;
+
+       dwc3_release(hisi_dwc3);
+       config_femtophy_param(hisi_dwc3);
+
+       set_hisi_dwc3_power_flag(1);
+
+       usb_dbg("-\n");
+
+       return 0;
+}
+
+static int hi3660_usb3phy_shutdown(struct hisi_dwc3_device *hisi_dwc3)
+{
+       int ret;
+
+       usb_dbg("+\n");
+
+       set_hisi_dwc3_power_flag(0);
+
+       dwc3_reset(hisi_dwc3);
+       usb3_clk_shutdown(hisi_dwc3);
+
+       ret = usb3_regu_shutdown(hisi_dwc3);
+       if (ret)
+               return ret;
+
+       usb_dbg("-\n");
+
+       return 0;
+}
+
+static struct usb3_phy_ops hi3660_phy_ops = {
+       .init           = hi3660_usb3phy_init,
+       .shutdown       = hi3660_usb3phy_shutdown,
+};
+
+static int dwc3_hi3660_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       ret = hisi_dwc3_probe(pdev, &hi3660_phy_ops);
+       if (ret)
+               usb_err("probe failed, ret=[%d]\n", ret);
+
+       return ret;
+}
+
+static int dwc3_hi3660_remove(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       ret = hisi_dwc3_remove(pdev);
+       if (ret)
+               usb_err("hisi_dwc3_remove failed, ret=[%d]\n", ret);
+
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id dwc3_hi3660_match[] = {
+       { .compatible = "hisilicon,hi3660-dwc3" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dwc3_hi3660_match);
+#else
+#define dwc3_hi3660_match NULL
+#endif
+
+static struct platform_driver dwc3_hi3660_driver = {
+       .probe          = dwc3_hi3660_probe,
+       .remove         = dwc3_hi3660_remove,
+       .driver         = {
+               .name   = "usb3-hi3660",
+               .of_match_table = of_match_ptr(dwc3_hi3660_match),
+               .pm     = HISI_DWC3_PM_OPS,
+       },
+};
+
+module_platform_driver(dwc3_hi3660_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 HI3660 Glue Layer");
+MODULE_AUTHOR("wangbinghui<wangbing...@hisilicon.com>");
diff --git a/drivers/usb/dwc3/dwc3-hisi.c b/drivers/usb/dwc3/dwc3-hisi.c
new file mode 100644
index 000000000000..f62921ca41d3
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-hisi.c
@@ -0,0 +1,1972 @@
+/*
+ * hisi_usb_vbus.c
+ *
+ * Copyright: (C) 2008-2018 hisilicon.
+ * Contact: wangbinghui<wangbing...@hisilicon.com>
+ *
+ * USB vbus for Hisilicon device
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose this file to be licensed under the terms
+ * of the GNU General Public License (GPL) Version 2 or the 2-clause
+ * BSD license listed below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/usb/ch9.h>
+
+#include "dwc3-hisi.h"
+#include "core.h"
+#include "dwc3-otg.h"
+
+#define ENABLE_USB_TEST_PORT
+
+#define BC_AGAIN_DELAY_TIME 8000 /* ms */
+
+struct hisi_dwc3_device *hisi_dwc3_dev;
+atomic_t hisi_dwc3_power_on = ATOMIC_INIT(0);
+
+void set_hisi_dwc3_power_flag(int val)
+{
+       unsigned long flags;
+       struct dwc3 *dwc = NULL;
+
+       if (dwc_otg_handler && dwc_otg_handler->dwc) {
+               dwc = dwc_otg_handler->dwc;
+               spin_lock_irqsave(&dwc->lock, flags);
+               usb_dbg("get dwc3 lock\n");
+       }
+
+       atomic_set(&hisi_dwc3_power_on, val);
+       usb_dbg("set hisi_dwc3_power_flag %d\n", val);
+
+       if (dwc) {
+               spin_unlock_irqrestore(&dwc->lock, flags);
+               usb_dbg("put dwc3 lock\n");
+       }
+}
+
+#ifdef ENABLE_USB_TEST_PORT
+
+static ssize_t plugusb_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct hisi_dwc3_device *hisi_dwc3 = platform_get_drvdata(pdev);
+       char *s;
+
+       if (!hisi_dwc3) {
+               usb_err("hisi_dwc3 NULL\n");
+               return scnprintf(buf, PAGE_SIZE, "hisi_dwc3 NULL\n");
+       }
+
+       switch (hisi_dwc3->state) {
+       case USB_STATE_UNKNOWN:
+               s = "USB_STATE_UNKNOWN";
+               break;
+       case USB_STATE_OFF:
+               s = "USB_STATE_OFF";
+               break;
+       case USB_STATE_DEVICE:
+               s = "USB_STATE_DEVICE";
+               break;
+       case USB_STATE_HOST:
+               s = "USB_STATE_HOST";
+               break;
+       default:
+               s = "unknown";
+               break;
+       }
+       return scnprintf(buf, PAGE_SIZE, "current state: %s\n usage: %s\n", s,
+                        "echo hoston/hostoff/deviceon/deviceoff > plugusb\n");
+}
+
+static ssize_t plugusb_store(struct device *dev,
+                            struct device_attribute *attr,
+                                const char *buf, size_t size)
+{
+       if (!strncmp(buf, "hoston", strlen("hoston")))
+               hisi_usb_otg_event(ID_FALL_EVENT);
+       else if (!strncmp(buf, "hostoff", strlen("hostoff")))
+               hisi_usb_otg_event(ID_RISE_EVENT);
+       else if (!strncmp(buf, "deviceon", strlen("deviceon")))
+               hisi_usb_otg_event(CHARGER_CONNECT_EVENT);
+       else if (!strncmp(buf, "deviceoff", strlen("deviceoff")))
+               hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+       else
+               usb_err("input state is ilegal!\n");
+
+       /* added for show message of plugusb status to com port */
+       pr_err("[USB.plugusb] %s\n", buf);
+
+       return size;
+}
+
+/*lint -save -e750 */
+DEVICE_ATTR(plugusb, (0644), plugusb_show, plugusb_store);
+/*lint -restore */
+
+static const char * const charger_type_array[] = {
+       [CHARGER_TYPE_SDP]     = "sdp",       /* Standard Downstreame Port */
+       [CHARGER_TYPE_CDP]     = "cdp",       /* Charging Downstreame Port */
+       [CHARGER_TYPE_DCP]     = "dcp",       /* Dedicate Charging Port */
+       [CHARGER_TYPE_UNKNOWN] = "unknown",   /* non-standard */
+       [CHARGER_TYPE_NONE]    = "none",      /* not connected */
+       [PLEASE_PROVIDE_POWER] = "provide"   /* host mode, provide power */
+};
+
+static enum hisi_charger_type get_charger_type_from_str(const char *buf,
+                                                       size_t size)
+{
+       int i = 0;
+       enum hisi_charger_type ret = CHARGER_TYPE_NONE;
+
+       for (i = 0; i < sizeof(charger_type_array) /
+            sizeof(charger_type_array[0]); i++) {
+               if (!strncmp(buf, charger_type_array[i], size - 1)) {
+                       ret = (enum hisi_charger_type)i;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+ssize_t hiusb_do_charger_show(void *dev_data, char *buf, size_t size)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+       enum hisi_charger_type charger_type = CHARGER_TYPE_NONE;
+
+       if (!hisi_dwc) {
+               pr_err("platform_get_drvdata return null\n");
+               return scnprintf(buf, size,
+                                "platform_get_drvdata return null\n");
+       }
+
+       mutex_lock(&hisi_dwc->lock);
+       charger_type = hisi_dwc->charger_type;
+       mutex_unlock(&hisi_dwc->lock);
+
+       return scnprintf(buf, size, "[(%d):Charger type = %s]\n"
+                        
"----------------------------------------------------------------\n"
+                        "usage: echo {str} > chargertest\n"
+                        "       sdp:        Standard Downstreame Port\n"
+                        "       cdp:        Charging Downstreame Port\n"
+                        "       dcp:        Dedicate Charging Port\n"
+                        "       unknown:    non-standard\n"
+                        "       none:       not connected\n"
+                        "       provide:    host mode, provide power\n"
+                        , charger_type, charger_type_array[charger_type]);
+}
+
+int hiusb_get_eyepattern_param(void *dev_data, char *buf, size_t len)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+       int ret = 0;
+
+       if (hisi_dwc) {
+               ret = scnprintf(buf, len, "device:0x%x\nhost:0x%x\n",
+                               hisi_dwc->eye_diagram_param,
+                               hisi_dwc->eye_diagram_host_param);
+       } else {
+               usb_err("hisi_dwc NULL\n");
+               ret = scnprintf(buf, len, "hisi_dwc NULL\n");
+       }
+
+       return ret;
+}
+
+int hiusb_set_eyepattern_param(void *dev_data, const char *buf, size_t size)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+       int eye_diagram_param;
+
+       if (!hisi_dwc) {
+               pr_err("seteye: hisi_dwc is null\n");
+               return size;
+       }
+
+       if (sscanf(buf, "%32x", &eye_diagram_param) != 1)
+               return size;
+
+       hisi_dwc->eye_diagram_param = eye_diagram_param;
+       hisi_dwc->eye_diagram_host_param = eye_diagram_param;
+
+       return size;
+}
+
+static void notify_charger_type(struct hisi_dwc3_device *hisi_dwc3);
+ssize_t hiusb_do_charger_store(void *dev_data, const char *buf, size_t size)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+       enum hisi_charger_type charger_type =
+               get_charger_type_from_str(buf, size);
+
+       if (!hisi_dwc) {
+               pr_err("platform_get_drvdata return null\n");
+               return size;
+       }
+
+       mutex_lock(&hisi_dwc->lock);
+       hisi_dwc->charger_type = charger_type;
+       notify_charger_type(hisi_dwc);
+       mutex_unlock(&hisi_dwc->lock);
+
+       return size;
+}
+
+#ifdef CONFIG_HISI_DEBUG_FS
+static ssize_t fakecharger_show(void *dev_data, char *buf, size_t size)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+
+       if (!hisi_dwc) {
+               pr_err("platform_get_drvdata return null\n");
+               return scnprintf(buf, size,
+                                "platform_get_drvdata return null\n");
+       }
+
+       return scnprintf(buf, size, "[fake charger type: %s]\n",
+                        hisi_dwc->fake_charger_type == CHARGER_TYPE_NONE ?
+                        "not fake" :
+                        charger_type_array[hisi_dwc->fake_charger_type]);
+}
+
+static ssize_t fakecharger_store(void *dev_data, const char *buf, size_t size)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+       enum hisi_charger_type charger_type =
+               get_charger_type_from_str(buf, size);
+
+       if (!hisi_dwc) {
+               pr_err("platform_get_drvdata return null\n");
+               return size;
+       }
+
+       mutex_lock(&hisi_dwc->lock);
+       hisi_dwc->fake_charger_type = charger_type;
+       mutex_unlock(&hisi_dwc->lock);
+
+       return size;
+}
+#endif
+ssize_t hiusb_do_eventmask_show(void *dev_data, char *buf, size_t size)
+{
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+
+       if (!hisi_dwc) {
+               pr_err("platform_get_drvdata return null\n");
+               return scnprintf(buf, size,
+                                "platform_get_drvdata return null\n");
+       }
+
+       return scnprintf(buf, size, "%d\n", hisi_dwc->eventmask);
+}
+
+ssize_t hiusb_do_eventmask_store(void *dev_data, const char *buf, size_t size)
+{
+       int eventmask;
+       struct hisi_dwc3_device *hisi_dwc = (struct hisi_dwc3_device *)dev_data;
+
+       if (!hisi_dwc) {
+               pr_err("platform_get_drvdata return null\n");
+               return size;
+       }
+
+       if (sscanf(buf, "%1d", &eventmask) != 1)
+               return size;
+
+       hisi_dwc->eventmask = eventmask;
+
+       return size;
+}
+
+static struct device_attribute *hisi_dwc3_attributes[] = {
+       &dev_attr_plugusb,
+       NULL
+};
+
+static int create_attr_file(struct device *dev)
+{
+       struct device_attribute **attrs = hisi_dwc3_attributes;
+       struct device_attribute *attr;
+       struct class *hisi_usb_class;
+       struct device *hisi_usb_dev;
+       int i;
+       int ret = 0;
+
+       usb_dbg("+\n");
+       for (i = 0; attrs[i]; i++) {
+               attr = attrs[i];
+               ret = device_create_file(dev, attr);
+               if (ret) {
+                       dev_err(dev, "create attr file error!\n");
+                       goto err;
+               }
+       }
+
+       hisi_usb_class = class_create(THIS_MODULE, "hisi_usb_class");
+       if (IS_ERR(hisi_usb_class)) {
+               usb_dbg("create hisi_usb_class error!\n");
+       } else {
+               hisi_usb_dev = device_create(hisi_usb_class, NULL, 0,
+                                            NULL, "hisi_usb_dev");
+               if (IS_ERR(hisi_usb_dev))
+                       usb_dbg("create hisi_usb_dev error!\n");
+               else
+                       ret |= sysfs_create_link(&hisi_usb_dev->kobj,
+                                                &dev->kobj, "interface");
+       }
+       if (ret)
+               usb_dbg("create attr file error!\n");
+
+#ifdef CONFIG_HISI_DEBUG_FS
+       hiusb_debug_quick_register(
+                       platform_get_drvdata(to_platform_device(dev)),
+                       (hiusb_debug_show_ops)fakecharger_show,
+                       (hiusb_debug_store_ops)fakecharger_store);
+       hiusb_debug_init(platform_get_drvdata(to_platform_device(dev)));
+#endif
+
+       usb_dbg("-\n");
+       return 0;
+
+err:
+       for (i-- ; i >= 0; i--) {
+               attr = attrs[i];
+               device_remove_file(dev, attr);
+       }
+
+       return ret;
+}
+
+static void remove_attr_file(struct device *dev)
+{
+       struct device_attribute **attrs = hisi_dwc3_attributes;
+       struct device_attribute *attr;
+
+       while ((attr = *attrs++))
+               device_remove_file(dev, attr);
+}
+#else
+static inline int create_attr_file(struct device *dev)
+{
+       return 0;
+}
+
+static inline void remove_attr_file(struct device *dev) {}
+#endif
+
+static void phy_cr_wait_ack(void __iomem *otg_bc_base)
+{
+       int i = 1000;
+
+       while (1) {
+               if ((readl(otg_bc_base + USB3PHY_CR_STS) &
+                   USB3OTG_PHY_CR_ACK) == 1)
+                       break;
+               usleep_range(50, 60);
+               if (i-- < 0) {
+                       usb_err("wait phy_cr_ack timeout!\n");
+                       break;
+               }
+       }
+}
+
+static void phy_cr_set_addr(void __iomem *otg_bc_base, u32 addr)
+{
+       u32 reg;
+
+       /* set addr */
+       reg = USB3OTG_PHY_CR_DATA_IN(addr);
+       writel(reg, otg_bc_base + USB3PHY_CR_CTRL);
+
+       usleep_range(100, 120);
+
+       /* cap addr */
+       reg = readl(otg_bc_base + USB3PHY_CR_CTRL);
+       reg |= USB3OTG_PHY_CR_CAP_ADDR;
+       writel(reg, otg_bc_base + USB3PHY_CR_CTRL);
+
+       phy_cr_wait_ack(otg_bc_base);
+
+       /* clear ctrl reg */
+       writel(0, otg_bc_base + USB3PHY_CR_CTRL);
+}
+
+static u16 phy_cr_read(void __iomem *otg_bc_base, u32 addr)
+{
+       u32 reg;
+       int i = 1000;
+
+       phy_cr_set_addr(otg_bc_base, addr);
+
+       /* read cap */
+       writel(USB3OTG_PHY_CR_READ, otg_bc_base + USB3PHY_CR_CTRL);
+
+       usleep_range(100, 120);
+
+       while (1) {
+               reg = readl(otg_bc_base + USB3PHY_CR_STS);
+               if ((reg & USB3OTG_PHY_CR_ACK) == 1)
+                       break;
+               usleep_range(50, 60);
+               if (i-- < 0) {
+                       usb_err("wait phy_cr_ack timeout!\n");
+                       break;
+               }
+       }
+
+       /* clear ctrl reg */
+       writel(0, otg_bc_base + USB3PHY_CR_CTRL);
+
+       return (u16)USB3OTG_PHY_CR_DATA_OUT(reg);
+}
+
+static void phy_cr_write(void __iomem *otg_bc_base, u32 addr, u32 value)
+{
+       u32 reg;
+
+       phy_cr_set_addr(otg_bc_base, addr);
+
+       reg = USB3OTG_PHY_CR_DATA_IN(value);
+       writel(reg, otg_bc_base + USB3PHY_CR_CTRL);
+
+       /* cap data */
+       reg = readl(otg_bc_base + USB3PHY_CR_CTRL);
+       reg |= USB3OTG_PHY_CR_CAP_DATA;
+       writel(reg, otg_bc_base + USB3PHY_CR_CTRL);
+
+       /* wait ack */
+       phy_cr_wait_ack(otg_bc_base);
+
+       /* clear ctrl reg */
+       writel(0, otg_bc_base + USB3PHY_CR_CTRL);
+
+       reg = USB3OTG_PHY_CR_WRITE;
+       writel(reg, otg_bc_base + USB3PHY_CR_CTRL);
+
+       /* wait ack */
+       phy_cr_wait_ack(otg_bc_base);
+}
+
+void set_usb3_phy_cr_param(u32 addr, u32 value)
+{
+       if (!hisi_dwc3_dev) {
+               pr_err("hisi dwc3 device not ready!\n");
+               return;
+       }
+
+       phy_cr_write(hisi_dwc3_dev->otg_bc_reg_base, addr, value);
+}
+EXPORT_SYMBOL_GPL(set_usb3_phy_cr_param);
+
+void read_usb3_phy_cr_param(u32 addr)
+{
+       if (!hisi_dwc3_dev) {
+               pr_err("hisi dwc3 device not ready!\n");
+               return;
+       }
+
+       usb_dbg("read usb3 phy cr param 0x%x\n",
+               phy_cr_read(hisi_dwc3_dev->otg_bc_reg_base, addr));
+}
+EXPORT_SYMBOL_GPL(read_usb3_phy_cr_param);
+
+void config_femtophy_param(struct hisi_dwc3_device *hisi_dwc)
+{
+       u32 reg;
+       void __iomem *otg_bc_base = hisi_dwc->otg_bc_reg_base;
+
+       if (hisi_dwc->fpga_flag != 0)
+               return;
+
+       /* set high speed phy parameter */
+       if (hisi_dwc->host_flag) {
+               writel(hisi_dwc->eye_diagram_host_param,
+                      otg_bc_base + USBOTG3_CTRL4);
+               usb_dbg("set hs phy param 0x%x for host\n",
+                       readl(otg_bc_base + USBOTG3_CTRL4));
+       } else {
+               writel(hisi_dwc->eye_diagram_param,
+                      otg_bc_base + USBOTG3_CTRL4);
+               usb_dbg("set hs phy param 0x%x for device\n",
+                       readl(otg_bc_base + USBOTG3_CTRL4));
+       }
+
+       /* set usb3 phy cr config for usb3.0 */
+
+       if (hisi_dwc->host_flag) {
+               phy_cr_write(otg_bc_base, DWC3_PHY_RX_OVRD_IN_HI,
+                            hisi_dwc->usb3_phy_host_cr_param);
+       } else {
+               phy_cr_write(otg_bc_base, DWC3_PHY_RX_OVRD_IN_HI,
+                            hisi_dwc->usb3_phy_cr_param);
+       }
+
+       usb_dbg("set ss phy rx equalization 0x%x\n",
+               phy_cr_read(otg_bc_base, DWC3_PHY_RX_OVRD_IN_HI));
+
+       /* enable RX_SCOPE_LFPS_EN for usb3.0 */
+       reg = phy_cr_read(otg_bc_base, DWC3_PHY_RX_SCOPE_VDCC);
+       reg |= RX_SCOPE_LFPS_EN;
+       phy_cr_write(otg_bc_base, DWC3_PHY_RX_SCOPE_VDCC, reg);
+
+       usb_dbg("set ss RX_SCOPE_VDCC 0x%x\n",
+               phy_cr_read(otg_bc_base, DWC3_PHY_RX_SCOPE_VDCC));
+
+       reg = readl(otg_bc_base + USBOTG3_CTRL6);
+       reg &= ~TX_VBOOST_LVL_MASK;
+       reg |= TX_VBOOST_LVL(hisi_dwc->usb3_phy_tx_vboost_lvl);
+       writel(reg, otg_bc_base + USBOTG3_CTRL6);
+       usb_dbg("set ss phy tx vboost lvl 0x%x\n",
+               readl(otg_bc_base + USBOTG3_CTRL6));
+}
+
+int hisi_charger_type_notifier_register(struct notifier_block *nb)
+{
+       if (!hisi_dwc3_dev) {
+               pr_err("hisi dwc3 device not ready!\n");
+               return -EBUSY;
+       }
+       if (!nb)
+               return -EINVAL;
+       return atomic_notifier_chain_register(
+                       &hisi_dwc3_dev->charger_type_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(hisi_charger_type_notifier_register);
+
+int hisi_charger_type_notifier_unregister(struct notifier_block *nb)
+{
+       if (!hisi_dwc3_dev) {
+               pr_err("hisi dwc3 device not ready!\n");
+               return -EBUSY;
+       }
+       if (!nb)
+               return -EINVAL;
+       return atomic_notifier_chain_unregister(
+                       &hisi_dwc3_dev->charger_type_notifier,
+                       nb);
+}
+EXPORT_SYMBOL_GPL(hisi_charger_type_notifier_unregister);
+
+/* BC1.2 Spec:
+ * If a PD detects that D+ is greater than VDAT_REF, it knows that it is
+ * attached to a DCP. It is then required to enable VDP_SRC or pull D+
+ * to VDP_UP through RDP_UP
+ */
+static void disable_vdp_src(struct hisi_dwc3_device *hisi_dwc3)
+{
+       void __iomem *base = hisi_dwc3->otg_bc_reg_base;
+       u32 reg;
+
+       usb_dbg("diaable VDP_SRC\n");
+
+       reg = readl(base + BC_CTRL2);
+       reg &= ~(BC_CTRL2_BC_PHY_VDATARCENB | BC_CTRL2_BC_PHY_VDATDETENB);
+       writel(reg, base + BC_CTRL2);
+
+       reg = readl(base + BC_CTRL0);
+       reg |= BC_CTRL0_BC_SUSPEND_N;
+       writel(reg, base + BC_CTRL0);
+
+       writel((readl(base + BC_CTRL1) & ~BC_CTRL1_BC_MODE), base + BC_CTRL1);
+}
+
+static void enable_vdp_src(struct hisi_dwc3_device *hisi_dwc3)
+{
+       void __iomem *base = hisi_dwc3->otg_bc_reg_base;
+       u32 reg;
+
+       reg = readl(base + BC_CTRL2);
+       reg &= ~BC_CTRL2_BC_PHY_CHRGSEL;
+       reg |= (BC_CTRL2_BC_PHY_VDATARCENB | BC_CTRL2_BC_PHY_VDATDETENB);
+       writel(reg, base + BC_CTRL2);
+}
+
+static enum hisi_charger_type detect_charger_type(struct hisi_dwc3_device
+                               *hisi_dwc3)
+{
+       enum hisi_charger_type type = CHARGER_TYPE_NONE;
+       void __iomem *base = hisi_dwc3->otg_bc_reg_base;
+       u32 reg;
+       unsigned long jiffies_expire;
+       int i = 0;
+
+       if (hisi_dwc3->fpga_flag) {
+               usb_dbg("this is fpga platform, charger is SDP\n");
+               return CHARGER_TYPE_SDP;
+       }
+
+       if (hisi_dwc3->fake_charger_type != CHARGER_TYPE_NONE) {
+               usb_dbg("fake type: %d\n", hisi_dwc3->fake_charger_type);
+               return hisi_dwc3->fake_charger_type;
+       }
+
+       writel(BC_CTRL1_BC_MODE, base + BC_CTRL1);
+
+       /* phy suspend */
+       reg = readl(base + BC_CTRL0);
+       reg &= ~BC_CTRL0_BC_SUSPEND_N;
+       writel(reg, base + BC_CTRL0);
+
+       /* enable DCD */
+       reg = readl(base + BC_CTRL2);
+       reg |= BC_CTRL2_BC_PHY_DCDENB;
+       writel(reg, base + BC_CTRL2);
+
+       reg = readl(base + BC_CTRL0);
+       reg |= BC_CTRL0_BC_DMPULLDOWN;
+       writel(reg, base + BC_CTRL0);
+
+       jiffies_expire = jiffies + msecs_to_jiffies(900);
+       msleep(50);
+       while (1) {
+               reg = readl(base + BC_STS0);
+               if ((reg & BC_STS0_BC_PHY_FSVPLUS) == 0) {
+                       i++;
+                       if (i >= 10)
+                               break;
+               } else {
+                       i = 0;
+               }
+
+               msleep(20);
+
+               if (time_after(jiffies, jiffies_expire)) {
+                       usb_dbg("DCD timeout!\n");
+                       type = CHARGER_TYPE_UNKNOWN;
+                       break;
+               }
+       }
+
+       reg = readl(base + BC_CTRL0);
+       reg &= ~BC_CTRL0_BC_DMPULLDOWN;
+       writel(reg, base + BC_CTRL0);
+
+       /* disable DCD */
+       reg = readl(base + BC_CTRL2);
+       reg &= ~BC_CTRL2_BC_PHY_DCDENB;
+       writel(reg, base + BC_CTRL2);
+
+       usb_dbg("DCD done\n");
+
+       if (type == CHARGER_TYPE_NONE) {
+               /* enable vdect */
+               reg = readl(base + BC_CTRL2);
+               reg &= ~BC_CTRL2_BC_PHY_CHRGSEL;
+               reg |= (BC_CTRL2_BC_PHY_VDATARCENB |
+                       BC_CTRL2_BC_PHY_VDATDETENB);
+               writel(reg, base + BC_CTRL2);
+
+               msleep(20);
+
+               /* we can detect sdp or cdp dcp */
+               reg = readl(base + BC_STS0);
+               if ((reg & BC_STS0_BC_PHY_CHGDET) == 0)
+                       type = CHARGER_TYPE_SDP;
+
+               /* disable vdect */
+               reg = readl(base + BC_CTRL2);
+               reg &= ~(BC_CTRL2_BC_PHY_VDATARCENB |
+                        BC_CTRL2_BC_PHY_VDATDETENB);
+               writel(reg, base + BC_CTRL2);
+       }
+
+       usb_dbg("Primary Detection done\n");
+
+       if (type == CHARGER_TYPE_NONE) {
+               /* enable vdect */
+               reg = readl(base + BC_CTRL2);
+               reg |= (BC_CTRL2_BC_PHY_VDATARCENB | BC_CTRL2_BC_PHY_VDATDETENB
+                               | BC_CTRL2_BC_PHY_CHRGSEL);
+               writel(reg, base + BC_CTRL2);
+
+               msleep(20);
+
+               /* we can detect sdp or cdp dcp */
+               reg = readl(base + BC_STS0);
+               if ((reg & BC_STS0_BC_PHY_CHGDET) == 0)
+                       type = CHARGER_TYPE_CDP;
+               else
+                       type = CHARGER_TYPE_DCP;
+
+               /* disable vdect */
+               reg = readl(base + BC_CTRL2);
+               reg &= ~(BC_CTRL2_BC_PHY_VDATARCENB | BC_CTRL2_BC_PHY_VDATDETENB
+                               | BC_CTRL2_BC_PHY_CHRGSEL);
+               writel(reg, base + BC_CTRL2);
+       }
+
+       usb_dbg("Secondary Detection done\n");
+
+       /* If a PD detects that D+ is greater than VDAT_REF, it knows that it is
+        * attached to a DCP. It is then required to enable VDP_SRC or pull D+
+        * to VDP_UP through RDP_UP
+        */
+       if (type == CHARGER_TYPE_DCP) {
+               usb_dbg("charger is DCP, enable VDP_SRC\n");
+               enable_vdp_src(hisi_dwc3);
+       } else {
+               /* bc_suspend = 1, nomal mode */
+               reg = readl(base + BC_CTRL0);
+               reg |= BC_CTRL0_BC_SUSPEND_N;
+               writel(reg, base + BC_CTRL0);
+
+               msleep(20);
+
+               /* disable BC */
+               writel((readl(base + BC_CTRL1) & ~BC_CTRL1_BC_MODE),
+                      base + BC_CTRL1);
+       }
+
+       usb_dbg("type: %d\n", type);
+
+       return type;
+}
+
+enum hisi_charger_type hisi_get_charger_type(void)
+{
+       if (!hisi_dwc3_dev) {
+               pr_err("[%s]hisi_dwc3 not yet probed!\n", __func__);
+               return CHARGER_TYPE_NONE;
+       }
+
+       pr_info("[%s]type: %d\n", __func__, hisi_dwc3_dev->charger_type);
+       return hisi_dwc3_dev->charger_type;
+}
+EXPORT_SYMBOL_GPL(hisi_get_charger_type);
+
+static void notify_charger_type(struct hisi_dwc3_device *hisi_dwc3)
+{
+       atomic_notifier_call_chain(&hisi_dwc3->charger_type_notifier,
+                                  hisi_dwc3->charger_type, hisi_dwc3);
+}
+
+static void set_vbus_power(struct hisi_dwc3_device *hisi_dwc3,
+                          unsigned int is_on)
+{
+       enum hisi_charger_type new;
+
+       if (is_on == 0)
+               new = CHARGER_TYPE_NONE;
+       else
+               new = PLEASE_PROVIDE_POWER;
+       if (hisi_dwc3->charger_type != new) {
+               usb_dbg("set port power %d\n", is_on);
+               hisi_dwc3->charger_type = new;
+               notify_charger_type(hisi_dwc3);
+       }
+}
+
+static void hisi_dwc3_wake_lock(struct hisi_dwc3_device *hisi_dwc3)
+{
+       if (!(hisi_dwc3->ws.active)) {
+               usb_dbg("usb otg wake lock\n");
+               __pm_stay_awake(&hisi_dwc3->ws);
+       }
+}
+
+static void hisi_dwc3_wake_unlock(struct hisi_dwc3_device *hisi_dwc3)
+{
+       if (hisi_dwc3->ws.active) {
+               usb_dbg("usb otg wake unlock\n");
+               __pm_relax(&hisi_dwc3->ws);
+       }
+}
+
+static inline bool enumerate_allowed(struct hisi_dwc3_device *hisi_dwc)
+{
+       /* do not start peripheral if real charger connected */
+       return ((hisi_dwc->charger_type == CHARGER_TYPE_SDP) ||
+               (hisi_dwc->charger_type == CHARGER_TYPE_CDP) ||
+               (hisi_dwc->charger_type == CHARGER_TYPE_UNKNOWN));
+}
+
+static inline bool sleep_allowed(struct hisi_dwc3_device *hisi_dwc)
+{
+       return ((hisi_dwc->charger_type == CHARGER_TYPE_DCP) ||
+               (hisi_dwc->charger_type == CHARGER_TYPE_UNKNOWN));
+}
+
+/*
+ * create event queue
+ * event_queue: event queue handle
+ * count: set the queue max node
+ */
+int event_queue_creat(struct hiusb_event_queue *event_queue, unsigned int 
count)
+{
+       if (!event_queue) {
+               pr_err(" %s bad argument (0x%p)\n",
+                      __func__, event_queue);
+               return -EINVAL;
+       }
+
+       count = (count >= MAX_EVENT_COUNT ? MAX_EVENT_COUNT : count);
+       event_queue->max_event = count;
+       event_queue->num_event = (count >= EVENT_QUEUE_UNIT ?
+                                 EVENT_QUEUE_UNIT : count);
+
+       event_queue->event = kzalloc(
+                       (event_queue->num_event *
+                       sizeof(enum otg_dev_event_type)), GFP_KERNEL);
+       if (!event_queue->event) {
+               pr_err(" %s :Can't alloc space:%d!\n",
+                      __func__, event_queue->num_event);
+               return -ENOMEM;
+       }
+
+       event_queue->enpos = 0;
+       event_queue->depos = 0;
+       event_queue->overlay = 0;
+       event_queue->overlay_index = 0;
+
+       return 0;
+}
+
+void event_queue_destroy(struct hiusb_event_queue *event_queue)
+{
+       if (!event_queue)
+               return;
+
+       kfree(event_queue->event);
+       event_queue->event = NULL;
+       event_queue->enpos = 0;
+       event_queue->depos = 0;
+       event_queue->num_event = 0;
+       event_queue->max_event = 0;
+       event_queue->overlay = 0;
+       event_queue->overlay_index = 0;
+}
+
+/*
+ * check if the queue is full
+ * return true means full, false is not.
+ */
+int event_queue_isfull(struct hiusb_event_queue *event_queue)
+{
+       if (!event_queue)
+               return -EINVAL;
+
+       return (((event_queue->enpos + 1) % event_queue->num_event) ==
+               (event_queue->depos));
+}
+
+/*
+ * check if the queue is full
+ * return true means empty, false or not.
+ */
+int event_queue_isempty(struct hiusb_event_queue *event_queue)
+{
+       if (!event_queue)
+               return -EINVAL;
+
+       return (event_queue->enpos == event_queue->depos);
+}
+
+static inline void event_queue_set_overlay(
+               struct hiusb_event_queue *event_queue)
+{
+       if (event_queue->overlay)
+               return;
+       event_queue->overlay = 1;
+       event_queue->overlay_index = event_queue->enpos;
+}
+
+static inline void event_queue_clear_overlay(
+               struct hiusb_event_queue *event_queue)
+{
+       event_queue->overlay = 0;
+       event_queue->overlay_index = 0;
+}
+
+/*
+ * put the new event en queue
+ * if the event_queue is full, return -ENOSPC
+ */
+int event_enqueue(struct hiusb_event_queue *event_queue,
+                 enum otg_dev_event_type event)
+{
+       /* no need verify argument, isfull will check it */
+       if (event_queue_isfull(event_queue)) {
+               pr_err("event queue full!\n");
+               return -ENOSPC;
+       }
+
+       if (event_queue->overlay) {
+               if (event_queue->overlay_index == event_queue->enpos) {
+                       event_queue->enpos = ((event_queue->enpos + 1) %
+                                             event_queue->num_event);
+               }
+
+               if (event_queue_isempty(event_queue)) {
+                       pr_err("overlay and queue isempty? just enqueue!\n");
+                       event_queue->overlay_index = (
+                                       (event_queue->overlay_index + 1) %
+                                       event_queue->num_event);
+                       event_queue->enpos = ((event_queue->enpos + 1) %
+                                             event_queue->num_event);
+                       event_queue->overlay = 0;
+               }
+
+               event_queue->event[event_queue->overlay_index] = event;
+       } else {
+               event_queue->event[event_queue->enpos] = event;
+               event_queue->enpos = ((event_queue->enpos + 1) %
+                                     event_queue->num_event);
+       }
+
+       return 0;
+}
+
+/*
+ * get event from event_queue
+ * this function never return fail
+ * if the event_queue is empty, return NONE_EVENT
+ */
+enum otg_dev_event_type event_dequeue(struct hiusb_event_queue *event_queue)
+{
+       enum otg_dev_event_type event;
+
+       /* no need verify argument, isempty will check it */
+       if (event_queue_isempty(event_queue))
+               return NONE_EVENT;
+
+       event = event_queue->event[event_queue->depos];
+       event_queue->depos = ((event_queue->depos + 1) %
+                             event_queue->num_event);
+
+       return event;
+}
+
+static void handle_event(struct hisi_dwc3_device *hisi_dwc,
+                        enum otg_dev_event_type event)
+{
+       int ret = 0;
+
+       usb_err("[%s] type: %d\n", __func__, event);
+       switch (event) {
+       case CHARGER_CONNECT_EVENT:
+               if (hisi_dwc->state == USB_STATE_DEVICE) {
+                       usb_dbg("Already in device mode, do nothing\n");
+               } else if (hisi_dwc->state == USB_STATE_OFF) {
+                       hisi_dwc->host_flag = 0;
+
+                       /* due to detect charger type, must resume hisi_dwc */
+                       ret = pm_runtime_get_sync(&hisi_dwc->pdev->dev);
+                       if (ret < 0) {
+                               usb_err("resume hisi_dwc failed (ret %d)\n",
+                                       ret);
+                               return;
+                       }
+
+                       /* detect charger type */
+                       hisi_dwc->charger_type = detect_charger_type(hisi_dwc);
+                       notify_charger_type(hisi_dwc);
+
+                       /* In some cases, DCP is detected as SDP wrongly.
+                        * To avoid this, start bc_again delay work to
+                        * detect charger type once more.
+                        * If later the enum process is executed,
+                        * then it's a real SDP, so
+                        * the work will be canceled.
+                        */
+                       if (hisi_dwc->bc_again_flag &&
+                           (hisi_dwc->charger_type == CHARGER_TYPE_SDP)) {
+                               ret = queue_delayed_work(
+                                       system_power_efficient_wq,
+                                       &hisi_dwc->bc_again_work,
+                                       msecs_to_jiffies(BC_AGAIN_DELAY_TIME));
+                               usb_dbg("schedule ret:%d, run bc_again_work 
%dms later\n",
+                                       ret, BC_AGAIN_DELAY_TIME);
+                       }
+
+                       /* do not start peripheral if real charger connected */
+                       if (enumerate_allowed(hisi_dwc)) {
+                               if (hisi_dwc->fpga_usb_mode_gpio > 0) {
+                                       gpio_direction_output(
+                                               hisi_dwc->fpga_usb_mode_gpio,
+                                               0);
+                                       usb_dbg("switch to device mode\n");
+                               }
+
+                               /* start peripheral */
+                               ret = dwc3_otg_work(dwc_otg_handler,
+                                                   DWC3_OTG_EVT_VBUS_SET);
+                               if (ret) {
+                                       pm_runtime_put(&hisi_dwc->pdev->dev);
+                                       hisi_dwc3_wake_unlock(hisi_dwc);
+                                       usb_err("start peripheral error\n");
+                                       return;
+                               }
+                       } else {
+                               usb_dbg("a real charger connected\n");
+                       }
+
+                       hisi_dwc->state = USB_STATE_DEVICE;
+
+                       if (sleep_allowed(hisi_dwc))
+                               hisi_dwc3_wake_unlock(hisi_dwc);
+                       else
+                               hisi_dwc3_wake_lock(hisi_dwc);
+
+                       usb_dbg("hisi usb status: OFF -> DEVICE\n");
+               } else if (hisi_dwc->state == USB_STATE_HOST) {
+                       usb_dbg("Charger connect interrupt in HOST mode\n");
+               }
+
+               break;
+
+       case CHARGER_DISCONNECT_EVENT:
+               hisi_dwc->need_disable_vdp = 0;
+
+               if (hisi_dwc->state == USB_STATE_OFF) {
+                       usb_dbg("Already in off mode, do nothing\n");
+               } else if (hisi_dwc->state == USB_STATE_DEVICE) {
+                       if (hisi_dwc->bc_again_flag) {
+                               ret = cancel_delayed_work_sync(
+                                               &hisi_dwc->bc_again_work);
+                               usb_dbg("cancel bc_again_work sync:%d\n", ret);
+                       }
+
+                       /* peripheral not started, if real charger connected */
+                       if (enumerate_allowed(hisi_dwc)) {
+                               /* stop peripheral */
+                               ret = dwc3_otg_work(dwc_otg_handler,
+                                                   DWC3_OTG_EVT_VBUS_CLEAR);
+                               if (ret) {
+                                       usb_err("stop peripheral error\n");
+                                       return;
+                               }
+                       } else {
+                               usb_dbg("connected is a real charger\n");
+                               disable_vdp_src(hisi_dwc);
+                       }
+
+                       /* usb cable disconnect, notify no charger */
+                       hisi_dwc->charger_type = CHARGER_TYPE_NONE;
+                       notify_charger_type(hisi_dwc);
+
+                       hisi_dwc->state = USB_STATE_OFF;
+                       hisi_dwc3_wake_unlock(hisi_dwc);
+                       pm_runtime_put(&hisi_dwc->pdev->dev);
+
+                       usb_dbg("hisi usb status: DEVICE -> OFF\n");
+               } else if (hisi_dwc->state == USB_STATE_HOST) {
+                       usb_dbg("Charger disconnect interrupt in HOST mode\n");
+               }
+
+               break;
+
+       case ID_FALL_EVENT:
+               if (hisi_dwc->state == USB_STATE_OFF) {
+                       set_vbus_power(hisi_dwc, 1);
+
+                       hisi_dwc->host_flag = 1;
+
+                       if (hisi_dwc->fpga_usb_mode_gpio > 0) {
+                               gpio_direction_output(
+                                               hisi_dwc->fpga_usb_mode_gpio,
+                                               1);
+                               usb_dbg("switch to host mode\n");
+                       }
+
+                       /* start host */
+                       ret = dwc3_otg_work(dwc_otg_handler,
+                                           DWC3_OTG_EVT_ID_CLEAR);
+                       if (ret) {
+                               usb_err("start host error\n");
+                               set_vbus_power(hisi_dwc, 0);
+                               return;
+                       }
+
+                       hisi_dwc->state = USB_STATE_HOST;
+                       hisi_dwc3_wake_lock(hisi_dwc);
+
+                       usb_dbg("hisi usb_status: OFF -> HOST\n");
+               } else if (hisi_dwc->state == USB_STATE_DEVICE) {
+                       usb_dbg("id fall interrupt in DEVICE mode\n");
+               } else if (hisi_dwc->state == USB_STATE_HOST) {
+                       usb_dbg("Already in host mode, do nothing\n");
+               }
+               break;
+       case ID_RISE_EVENT:
+               if (hisi_dwc->state == USB_STATE_HOST) {
+                       set_vbus_power(hisi_dwc, 0);
+
+                       /* stop host */
+                       ret = dwc3_otg_work(dwc_otg_handler,
+                                           DWC3_OTG_EVT_ID_SET);
+                       if (ret) {
+                               usb_err("stop host error\n");
+                               return;
+                       }
+
+                       hisi_dwc->state = USB_STATE_OFF;
+                       hisi_dwc3_wake_unlock(hisi_dwc);
+
+                       usb_dbg("hiusb_status: HOST -> OFF\n");
+               } else if (hisi_dwc->state == USB_STATE_DEVICE) {
+                       usb_dbg("id rise interrupt in DEVICE mode\n");
+               } else if (hisi_dwc->state == USB_STATE_OFF) {
+                       usb_dbg("Already in host mode, do nothing\n");
+               }
+
+               break;
+       default:
+               usb_dbg("illegal event type!\n");
+               break;
+       }
+}
+
+static void event_work(struct work_struct *work)
+{
+       unsigned long flags;
+       enum otg_dev_event_type event;
+       struct hisi_dwc3_device *hisi_dwc = container_of(work,
+                                   struct hisi_dwc3_device, event_work);
+
+       mutex_lock(&hisi_dwc->lock);
+
+       usb_dbg("+\n");
+
+       while (!event_queue_isempty(&hisi_dwc->event_queue)) {
+               spin_lock_irqsave(&hisi_dwc->event_lock, flags);
+               event = event_dequeue(&hisi_dwc->event_queue);
+               spin_unlock_irqrestore(&hisi_dwc->event_lock, flags);
+
+               handle_event(hisi_dwc, event);
+       }
+
+       event_queue_clear_overlay(&hisi_dwc->event_queue);
+
+       usb_dbg("-\n");
+       mutex_unlock(&hisi_dwc->lock);
+}
+
+static int event_check(enum otg_dev_event_type last_event,
+                      enum otg_dev_event_type new_event)
+{
+       int ret = 0;
+
+       if (last_event == NONE_EVENT)
+               return 1;
+
+       switch (new_event) {
+       case CHARGER_CONNECT_EVENT:
+               if ((last_event == CHARGER_DISCONNECT_EVENT) ||
+                   (last_event == ID_RISE_EVENT))
+                       ret = 1;
+               break;
+       case CHARGER_DISCONNECT_EVENT:
+               if (last_event == CHARGER_CONNECT_EVENT)
+                       ret = 1;
+               break;
+       case ID_FALL_EVENT:
+               if ((last_event == CHARGER_DISCONNECT_EVENT) ||
+                   (last_event == ID_RISE_EVENT))
+                       ret = 1;
+               break;
+       case ID_RISE_EVENT:
+               if (last_event == ID_FALL_EVENT)
+                       ret = 1;
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
+int hisi_usb_otg_event(enum otg_dev_event_type event)
+{
+       int ret = 0;
+#ifdef CONFIG_USB_DWC3_OTG
+       unsigned long flags;
+       struct hisi_dwc3_device *hisi_dwc3 = hisi_dwc3_dev;
+#endif
+       usb_err("%s in:%d\n", __func__, event);
+#ifdef CONFIG_USB_DWC3_OTG
+       usb_err("%s in otg:%d\n", __func__, event);
+
+       if (!hisi_dwc3) {
+               usb_dbg(" %s error:%d\n", __func__, event);
+               return -EBUSY;
+       }
+
+       if (hisi_dwc3->eventmask) {
+               usb_dbg("eventmask enabled, mask all events.\n");
+               return ret;
+       }
+
+       spin_lock_irqsave(&hisi_dwc3->event_lock, flags);
+
+       if (event_check(hisi_dwc3->event, event)) {
+               usb_dbg("event: %d\n", event);
+               hisi_dwc3->event = event;
+
+               if ((event == CHARGER_CONNECT_EVENT) ||
+                   (event == CHARGER_DISCONNECT_EVENT))
+                       hisi_dwc3_wake_lock(hisi_dwc3);
+
+               if (!event_enqueue(&hisi_dwc3->event_queue, event)) {
+                       ret = queue_work(system_power_efficient_wq,
+                                        &hisi_dwc3->event_work);
+                       if (!ret)
+                               usb_err("schedule event_work wait:%d]\n",
+                                       event);
+               } else {
+                       usb_err("%s can't enqueue event:%d\n",
+                               __func__, event);
+               }
+
+               if ((event == ID_RISE_EVENT) ||
+                   (event == CHARGER_DISCONNECT_EVENT))
+                       event_queue_set_overlay(&hisi_dwc3->event_queue);
+       }
+       spin_unlock_irqrestore(&hisi_dwc3->event_lock, flags);
+#endif
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_usb_otg_event);
+
+static void bc_again(struct hisi_dwc3_device *hisi_dwc)
+{
+       int ret;
+
+       /*
+        * STEP 1
+        */
+       /* stop peripheral which is started when detected as SDP before */
+       if (enumerate_allowed(hisi_dwc)) {
+               ret = dwc3_otg_work(dwc_otg_handler, DWC3_OTG_EVT_VBUS_CLEAR);
+               if (ret) {
+                       usb_err("stop peripheral error\n");
+                       return;
+               }
+       }
+
+       /*
+        * STEP 2
+        */
+       hisi_dwc->charger_type = detect_charger_type(hisi_dwc);
+       notify_charger_type(hisi_dwc);
+
+       /*
+        * STEP 3
+        */
+       /* must recheck enumerate_allowed, because charger_type maybe changed,
+        * and enumerate_allowed according to charger_type
+        */
+       if (enumerate_allowed(hisi_dwc)) {
+               /* start peripheral */
+               ret = dwc3_otg_work(dwc_otg_handler,
+                                   DWC3_OTG_EVT_VBUS_SET);
+               if (ret) {
+                       pm_runtime_put(&hisi_dwc->pdev->dev);
+                       hisi_dwc3_wake_unlock(hisi_dwc);
+                       usb_err("start peripheral error\n");
+                       return;
+               }
+       } else {
+               usb_dbg("a real charger connected\n");
+       }
+}
+
+void hisi_usb_otg_bc_again(void)
+{
+       struct hisi_dwc3_device *hisi_dwc = hisi_dwc3_dev;
+
+       usb_dbg("+\n");
+
+       if (!hisi_dwc) {
+               usb_err("No usb module, can't call bc again api\n");
+               return;
+       }
+
+       mutex_lock(&hisi_dwc->lock);
+
+       /* we are here because it's detected as SDP before */
+       if (hisi_dwc->charger_type == CHARGER_TYPE_UNKNOWN) {
+               usb_dbg("charger_type is UNKNOWN, start bc_again_work\n");
+               bc_again(hisi_dwc);
+       }
+
+       mutex_unlock(&hisi_dwc->lock);
+       usb_dbg("-\n");
+}
+EXPORT_SYMBOL_GPL(hisi_usb_otg_bc_again);
+
+static void bc_again_work(struct work_struct *work)
+{
+       struct hisi_dwc3_device *hisi_dwc = container_of(work,
+               struct hisi_dwc3_device, bc_again_work.work);
+
+       usb_dbg("+\n");
+       mutex_lock(&hisi_dwc->lock);
+
+       /* we are here because it's detected as SDP before */
+       if (hisi_dwc->charger_type == CHARGER_TYPE_SDP) {
+               usb_dbg("charger_type is  SDP, start %s\n", __func__);
+               bc_again(hisi_dwc);
+       }
+
+       mutex_unlock(&hisi_dwc->lock);
+       usb_dbg("-\n");
+}
+
+static int conndone_notifier_fn(struct notifier_block *nb,
+                               unsigned long action, void *data)
+{
+       int ret;
+       struct hisi_dwc3_device *hisi_dwc = container_of(nb,
+                       struct hisi_dwc3_device, conndone_nb);
+
+       ret = cancel_delayed_work(&hisi_dwc->bc_again_work);
+       usb_dbg("cancel bc_again_work:%d\n", ret);
+
+       return 0;
+}
+
+/**
+ * get_usb_state() - get current USB cable state.
+ * @hisi_dwc: the instance pointer of struct hisi_dwc3_device
+ *
+ * return current USB cable state according to VBUS status and ID status.
+ */
+static enum hisi_usb_state get_usb_state(struct hisi_dwc3_device *hisi_dwc)
+{
+       if (hisi_dwc->fpga_flag) {
+               usb_dbg("this is fpga platform, usb is device mode\n");
+               return USB_STATE_DEVICE;
+       }
+
+       if (dwc3_otg_id_value(dwc_otg_handler) == 0)
+               return USB_STATE_HOST;
+       else
+               return USB_STATE_OFF;
+}
+
+static void get_phy_param(struct hisi_dwc3_device *hisi_dwc3)
+{
+       struct device *dev = &hisi_dwc3->pdev->dev;
+
+       /* hs phy param for device mode */
+       if (of_property_read_u32(dev->of_node, "eye_diagram_param",
+                                &hisi_dwc3->eye_diagram_param)) {
+               usb_dbg("get eye diagram param form dt failed, use default 
value\n");
+               hisi_dwc3->eye_diagram_param = 0x1c466e3;
+       }
+       usb_dbg("eye diagram param: 0x%x\n", hisi_dwc3->eye_diagram_param);
+
+       /* hs phy param for host mode */
+       if (of_property_read_u32(dev->of_node, "eye_diagram_host_param",
+                                &hisi_dwc3->eye_diagram_host_param)) {
+               usb_dbg("get eye diagram host param form dt failed, use default 
value\n");
+               hisi_dwc3->eye_diagram_host_param = 0x1c466e3;
+       }
+       usb_dbg("eye diagram host param: 0x%x\n",
+               hisi_dwc3->eye_diagram_host_param);
+
+       /* ss phy Rx Equalization */
+       if (of_property_read_u32(dev->of_node, "usb3_phy_cr_param",
+                                &hisi_dwc3->usb3_phy_cr_param)) {
+               usb_dbg("get usb3_phy_cr_param form dt failed, use default 
value\n");
+               hisi_dwc3->usb3_phy_cr_param = (1 << 11) | (3 << 8) | (1 << 7);
+       }
+
+       /* ss phy Rx Equalization for host mode */
+       if (of_property_read_u32(dev->of_node, "usb3_phy_host_cr_param",
+                                &hisi_dwc3->usb3_phy_host_cr_param)) {
+               usb_dbg("get usb3_phy_host_cr_param form dt failed, use default 
value\n");
+               hisi_dwc3->usb3_phy_host_cr_param =
+                       (1 << 11) | (1 << 8) | (1 << 7);
+       }
+
+       usb_dbg("usb3_phy_cr_param: 0x%x\n", hisi_dwc3->usb3_phy_cr_param);
+       usb_dbg("usb3_phy_host_cr_param: 0x%x\n",
+               hisi_dwc3->usb3_phy_host_cr_param);
+
+       /* tx_vboost_lvl */
+       if (of_property_read_u32(dev->of_node, "usb3_phy_tx_vboost_lvl",
+                                &hisi_dwc3->usb3_phy_tx_vboost_lvl)) {
+               usb_dbg("get usb3_phy_tx_vboost_lvl form dt failed, use default 
value\n");
+               hisi_dwc3->usb3_phy_tx_vboost_lvl = 5;
+       }
+       usb_dbg("usb3_phy_tx_vboost_lvl: %d\n",
+               hisi_dwc3->usb3_phy_tx_vboost_lvl);
+}
+
+/**
+ * get_resource() - prepare resources
+ * @hisi_dwc3: the instance pointer of struct hisi_dwc3_device
+ *
+ * 1. get registers base address and map registers region.
+ * 2. get regulator handler.
+ */
+static int get_resource(struct hisi_dwc3_device *hisi_dwc3)
+{
+       struct device *dev = &hisi_dwc3->pdev->dev;
+       struct resource *res;
+       struct device_node *np;
+
+       /*
+        * map PERI CRG region
+        */
+       np = of_find_compatible_node(NULL, NULL, "hisilicon,hi3660-crgctrl");
+       if (!np) {
+               dev_err(dev, "get peri cfg node failed!\n");
+               return -EINVAL;
+       }
+       hisi_dwc3->pericfg_reg_base = of_iomap(np, 0);
+       if (!hisi_dwc3->pericfg_reg_base) {
+               dev_err(dev, "iomap pericfg_reg_base failed!\n");
+               return -EINVAL;
+       }
+
+       /*
+        * map PCTRL region
+        */
+       np = of_find_compatible_node(NULL, NULL, "hisilicon,hi3660-pctrl");
+       if (!np) {
+               dev_err(dev, "get pctrl node failed!\n");
+               return -EINVAL;
+       }
+       hisi_dwc3->pctrl_reg_base = of_iomap(np, 0);
+       if (!hisi_dwc3->pctrl_reg_base) {
+               dev_err(dev, "iomap pctrl_reg_base failed!\n");
+               return -EINVAL;
+       }
+
+       /*
+        * map SCTRL region
+        */
+       np = of_find_compatible_node(NULL, NULL, "hisilicon,hi3660-sctrl");
+       if (!np) {
+               dev_err(dev, "get sysctrl node failed!\n");
+               return -EINVAL;
+       }
+       hisi_dwc3->sctrl_reg_base = of_iomap(np, 0);
+       if (!hisi_dwc3->sctrl_reg_base) {
+               dev_err(dev, "iomap sctrl_reg_base failed!\n");
+               return -EINVAL;
+       }
+
+       /*
+        * map PMCTRL region
+        */
+       np = of_find_compatible_node(NULL, NULL, "hisilicon,hi3660-pmctrl");
+       if (!np) {
+               dev_err(dev, "get pmctrl node failed!\n");
+               return -EINVAL;
+       }
+
+       /*
+        * map OTG BC region
+        */
+       res = platform_get_resource(hisi_dwc3->pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "missing memory base resource\n");
+               return -EINVAL;
+       }
+
+       hisi_dwc3->otg_bc_reg_base = devm_ioremap_nocache(
+                       dev, res->start, resource_size(res));
+       if (IS_ERR_OR_NULL(hisi_dwc3->otg_bc_reg_base)) {
+               dev_err(dev, "ioremap res 0 failed\n");
+               return -ENOMEM;
+       }
+
+       get_phy_param(hisi_dwc3);
+
+       /* get abb clk handler */
+       hisi_dwc3->clk = devm_clk_get(&hisi_dwc3->pdev->dev, "clk_usb3phy_ref");
+       if (IS_ERR_OR_NULL(hisi_dwc3->clk)) {
+               dev_err(dev, "get usb3phy ref clk failed\n");
+               return -EINVAL;
+       }
+
+       /* get h clk handler */
+       hisi_dwc3->gt_aclk_usb3otg = devm_clk_get(
+                       &hisi_dwc3->pdev->dev, "aclk_usb3otg");
+       if (IS_ERR_OR_NULL(hisi_dwc3->gt_aclk_usb3otg)) {
+               dev_err(dev, "get aclk_usb3otg failed\n");
+               return -EINVAL;
+       }
+
+       /* judge fpga platform or not, from dts */
+       if (of_property_read_u32(dev->of_node, "fpga_flag",
+                                &hisi_dwc3->fpga_flag)) {
+               hisi_dwc3->fpga_flag = 0;
+       }
+       usb_dbg("this is %s platform (fpga flag %d)\n",
+               hisi_dwc3->fpga_flag ? "fpga" : "asic", hisi_dwc3->fpga_flag);
+
+       hisi_dwc3->fpga_usb_mode_gpio = -1;
+
+       if (of_property_read_u32(dev->of_node, "bc_again_flag",
+                                &hisi_dwc3->bc_again_flag)) {
+               hisi_dwc3->bc_again_flag = 0;
+       }
+
+       return 0;
+}
+
+static int hisi_dwc3_phy_init(struct hisi_dwc3_device *hisi_dwc)
+{
+       return hisi_dwc->phy_ops->init(hisi_dwc);
+}
+
+static int hisi_dwc3_phy_shutdown(struct hisi_dwc3_device *hisi_dwc)
+{
+       return hisi_dwc->phy_ops->shutdown(hisi_dwc);
+}
+
+int hisi_dwc3_probe(struct platform_device *pdev,
+                   struct usb3_phy_ops *phy_ops)
+{
+       int ret;
+       struct hisi_dwc3_device *hisi_dwc;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = pdev->dev.of_node;
+       enum hisi_usb_state init_state;
+
+       usb_dbg("+\n");
+
+       if (!phy_ops) {
+               usb_err("phy_ops is NULL\n");
+               return -EINVAL;
+       }
+
+       hisi_dwc = devm_kzalloc(dev, sizeof(*hisi_dwc), GFP_KERNEL);
+       if (!hisi_dwc)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, hisi_dwc);
+       hisi_dwc->pdev = pdev;
+       hisi_dwc->phy_ops = phy_ops;
+
+       hisi_dwc3_dev = hisi_dwc;
+
+       /*
+        * set hisi dwc3 dma mask, it should be 0xffffffff, because the ahb
+        * master of usb can only support 32bit width address.
+        */
+       if (!dev->dma_mask)
+               dev->dma_mask = &dev->coherent_dma_mask;
+       if (!dev->coherent_dma_mask)
+               dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+       /*
+        * get resources from dts.
+        */
+       ret = get_resource(hisi_dwc);
+       if (ret) {
+               dev_err(&pdev->dev, "get resource failed!\n");
+               return ret;
+       }
+
+       if (hisi_dwc->fpga_usb_mode_gpio > 0) {
+               ret = gpio_request(hisi_dwc->fpga_usb_mode_gpio, NULL);
+               if (ret) {
+                       /* request gpio failure! */
+                       usb_err("request gpio %d failed, ret=[%d]\n",
+                               hisi_dwc->fpga_usb_mode_gpio, ret);
+               }
+       }
+
+       /* create sysfs files. */
+       ret = create_attr_file(dev);
+       if (ret) {
+               dev_err(&pdev->dev, "create_attr_file failed!\n");
+               return ret;
+       }
+
+       /* initialize */
+       hisi_dwc->charger_type = CHARGER_TYPE_SDP;
+       hisi_dwc->fake_charger_type = CHARGER_TYPE_NONE;
+       hisi_dwc->event = NONE_EVENT;
+       hisi_dwc->host_flag = 0;
+       hisi_dwc->eventmask = 0;
+       spin_lock_init(&hisi_dwc->event_lock);
+       INIT_WORK(&hisi_dwc->event_work, event_work);
+       mutex_init(&hisi_dwc->lock);
+       wakeup_source_init(&hisi_dwc->ws, "usb_wake_lock");
+       ATOMIC_INIT_NOTIFIER_HEAD(&hisi_dwc->charger_type_notifier);
+       event_queue_creat(&hisi_dwc->event_queue, MAX_EVENT_COUNT);
+       hisi_dwc->disable_vdp_src = disable_vdp_src;
+       hisi_dwc->need_disable_vdp = 0;
+
+       /* power on */
+       hisi_dwc->is_regu_on = 0;
+       ret = hisi_dwc3_phy_init(hisi_dwc);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: hisi_dwc3_phy_init failed!\n",
+                       __func__);
+               remove_attr_file(dev);
+               return ret;
+       }
+
+       if (hisi_dwc->bc_again_flag) {
+               INIT_DELAYED_WORK(&hisi_dwc->bc_again_work, bc_again_work);
+               hisi_dwc->conndone_nb.notifier_call = conndone_notifier_fn;
+               ret = dwc3_conndone_notifier_register(&hisi_dwc->conndone_nb);
+               if (ret)
+                       usb_err("dwc3_conndone_notifier_register failed\n");
+       }
+
+       if (hisi_dwc->charger_type == CHARGER_TYPE_CDP) {
+               usb_dbg("it needs enable VDP_SRC while detect CDP!\n");
+               hisi_dwc->need_disable_vdp = 1;
+               enable_vdp_src(hisi_dwc);
+       }
+
+       /*
+        * enable runtime pm.
+        */
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+       pm_runtime_forbid(dev);
+
+       /*
+        * probe child deivces
+        */
+       ret = of_platform_populate(node, NULL, NULL, dev);
+       if (ret) {
+               pr_err("%s: register dwc3 failed!\n", __func__);
+               goto err1;
+       }
+
+#ifdef CONFIG_USB_DWC3_OTG
+       /* default device state  */
+       hisi_dwc->state = USB_STATE_DEVICE;
+
+       if (sleep_allowed(hisi_dwc))
+               hisi_dwc3_wake_unlock(hisi_dwc);
+       else
+               hisi_dwc3_wake_lock(hisi_dwc);
+
+       if (!enumerate_allowed(hisi_dwc)) {
+               /* stop peripheral */
+               ret = dwc3_otg_work(dwc_otg_handler, DWC3_OTG_EVT_VBUS_CLEAR);
+               if (ret)
+                       usb_err("stop peripheral error\n");
+       }
+
+       /* balance the put operation when disconnect */
+       pm_runtime_get(dev);
+
+       hisi_dwc->event = CHARGER_CONNECT_EVENT;
+       init_state = get_usb_state(hisi_dwc);
+       if (init_state == USB_STATE_OFF) {
+               usb_dbg("init state: OFF\n");
+               hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+       } else if (init_state == USB_STATE_HOST) {
+               usb_dbg("init state: HOST\n");
+               hisi_usb_otg_event(CHARGER_DISCONNECT_EVENT);
+               msleep(500);
+               hisi_usb_otg_event(ID_FALL_EVENT);
+       }
+#endif
+
+       pm_runtime_put_sync(dev);
+       pm_runtime_allow(dev);
+
+       usb_dbg("-\n");
+
+       return 0;
+
+err1:
+       pm_runtime_put_sync(dev);
+       pm_runtime_disable(dev);
+       remove_attr_file(dev);
+
+       return ret;
+}
+
+static int hisi_dwc3_remove_child(struct device *dev, void *unused)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       platform_device_unregister(pdev);
+       return 0;
+}
+
+int hisi_dwc3_remove(struct platform_device *pdev)
+{
+       struct hisi_dwc3_device *hisi_dwc3 = platform_get_drvdata(pdev);
+       int ret;
+
+       if (!hisi_dwc3) {
+               usb_err("hisi_dwc3 NULL\n");
+               return -EBUSY;
+       }
+
+       device_for_each_child(&pdev->dev, NULL, hisi_dwc3_remove_child);
+       pm_runtime_disable(&pdev->dev);
+
+       if (hisi_dwc3->bc_again_flag) {
+               dwc3_conndone_notifier_unregister(&hisi_dwc3->conndone_nb);
+               hisi_dwc3->conndone_nb.notifier_call = NULL;
+       }
+
+       ret = hisi_dwc3_phy_shutdown(hisi_dwc3);
+       if (ret)
+               usb_err("hisi_dwc3_phy_shutdown error\n");
+       hisi_dwc3->phy_ops = NULL;
+
+       event_queue_destroy(&hisi_dwc3->event_queue);
+
+       remove_attr_file(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+static int hisi_dwc3_prepare(struct device *dev)
+{
+       struct hisi_dwc3_device *hisi_dwc = platform_get_drvdata(
+                               to_platform_device(dev));
+       int ret = 0;
+
+       if (!hisi_dwc)
+               return -ENODEV;
+
+       mutex_lock(&hisi_dwc->lock);
+
+       switch (hisi_dwc->state) {
+       case USB_STATE_OFF:
+               pr_info("%s: off state.\n", __func__);
+               break;
+       case USB_STATE_DEVICE:
+               pr_info("%s: device state.\n", __func__);
+
+               if (enumerate_allowed(hisi_dwc)) {
+                       /* stop peripheral */
+                       ret = dwc3_otg_work(dwc_otg_handler,
+                                           DWC3_OTG_EVT_VBUS_CLEAR);
+                       if (ret) {
+                               usb_err("stop peripheral error\n");
+                               goto error;
+                       }
+               } else {
+                       usb_dbg("connected is a real charger\n");
+                       disable_vdp_src(hisi_dwc);
+               }
+
+               break;
+       case USB_STATE_HOST:
+               usb_err("%s: host mode, should not go to sleep!\n", __func__);
+               ret = -EFAULT;
+               goto error;
+       default:
+               pr_err("%s: ilegal state!\n", __func__);
+               ret = -EFAULT;
+               goto error;
+       }
+
+       return ret;
+error:
+       mutex_unlock(&hisi_dwc->lock);
+       return ret;
+}
+
+static void hisi_dwc3_complete(struct device *dev)
+{
+       struct hisi_dwc3_device *hisi_dwc = platform_get_drvdata(
+                               to_platform_device(dev));
+       int ret = 0;
+
+       if (!hisi_dwc) {
+               usb_err("hisi_dwc NULL !\n");
+               return;
+       }
+
+       switch (hisi_dwc->state) {
+       case USB_STATE_OFF:
+               usb_dbg("%s: off state.\n", __func__);
+               break;
+       case USB_STATE_DEVICE:
+               usb_dbg("%s: device state.\n", __func__);
+
+               /* update charger type */
+               hisi_dwc->charger_type = detect_charger_type(hisi_dwc);
+               if (sleep_allowed(hisi_dwc))
+                       hisi_dwc3_wake_unlock(hisi_dwc);
+               else
+                       hisi_dwc3_wake_lock(hisi_dwc);
+
+               /* do not start peripheral if real charger connected */
+               if (enumerate_allowed(hisi_dwc)) {
+                       /* start peripheral */
+                       ret = dwc3_otg_work(dwc_otg_handler,
+                                           DWC3_OTG_EVT_VBUS_SET);
+                       if (ret) {
+                               usb_err("start peripheral error\n");
+                               hisi_dwc->state = USB_STATE_OFF;
+                               pm_runtime_put(&hisi_dwc->pdev->dev);
+                               goto error;
+                       }
+               } else {
+                       usb_dbg("a real charger connected\n");
+               }
+
+               break;
+       case USB_STATE_HOST:
+               usb_err("%s: host mode, should not go to sleep!\n", __func__);
+               break;
+       default:
+               usb_err("%s: ilegal state!\n", __func__);
+               break;
+       }
+
+error:
+       mutex_unlock(&hisi_dwc->lock);
+}
+
+static int hisi_dwc3_suspend(struct device *dev)
+{
+       struct hisi_dwc3_device *hisi_dwc3 =
+               platform_get_drvdata(to_platform_device(dev));
+       int ret = 0;
+
+       usb_dbg("+\n");
+
+       if (!hisi_dwc3) {
+               usb_err("hisi_dwc3 NULL\n");
+               return -EBUSY;
+       }
+
+       if (hisi_dwc3->runtime_suspended) {
+               usb_dbg("runtime_suspended\n");
+       } else {
+               ret = hisi_dwc3_phy_shutdown(hisi_dwc3);
+               if (ret)
+                       usb_err("hisi_dwc3_phy_shutdown failed\n");
+       }
+
+       usb_dbg("-\n");
+
+       return ret;
+}
+
+static int hisi_dwc3_resume(struct device *dev)
+{
+       struct hisi_dwc3_device *hisi_dwc3 =
+               platform_get_drvdata(to_platform_device(dev));
+       int ret = 0;
+
+       usb_dbg("+\n");
+
+       if (!hisi_dwc3) {
+               usb_err("hisi_dwc3 NULL\n");
+               return -EBUSY;
+       }
+
+       if (hisi_dwc3->runtime_suspended) {
+               usb_dbg("runtime_suspended\n");
+       } else {
+               ret = hisi_dwc3_phy_init(hisi_dwc3);
+               if (ret)
+                       usb_err("hisi_dwc3_phy_init failed\n");
+
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
+
+       usb_dbg("-\n");
+
+       return ret;
+}
+#endif
+
+static int hisi_dwc3_runtime_suspend(struct device *dev)
+{
+       int ret;
+       struct hisi_dwc3_device *hisi_dwc3 =
+               platform_get_drvdata(to_platform_device(dev));
+
+       usb_dbg("+\n");
+
+       if (!hisi_dwc3) {
+               usb_err("hisi_dwc3 NULL\n");
+               return -EBUSY;
+       }
+
+       ret = hisi_dwc3_phy_shutdown(hisi_dwc3);
+       if (ret)
+               return ret;
+       hisi_dwc3->runtime_suspended = 1;
+       usb_dbg("-\n");
+
+       return 0;
+}
+
+static int hisi_dwc3_runtime_resume(struct device *dev)
+{
+       int ret = 0;
+       struct hisi_dwc3_device *hisi_dwc3 =
+               platform_get_drvdata(to_platform_device(dev));
+
+       usb_dbg("+\n");
+
+       if (!hisi_dwc3) {
+               usb_err("hisi_dwc3 NULL\n");
+               return -EBUSY;
+       }
+
+       ret = hisi_dwc3_phy_init(hisi_dwc3);
+       if (ret)
+               return ret;
+       hisi_dwc3->runtime_suspended = 0;
+       usb_dbg("-\n");
+
+       return ret;
+}
+
+static int hisi_dwc3_runtime_idle(struct device *dev)
+{
+       int ret;
+
+       usb_dbg("+\n");
+       ret = pm_runtime_autosuspend(dev);
+       if (ret)
+               dev_err(dev, "pm_runtime_autosuspend error\n");
+       usb_dbg("-\n");
+
+       return ret;
+}
+
+const struct dev_pm_ops hisi_dwc3_dev_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+       .prepare        = hisi_dwc3_prepare,
+       .complete       = hisi_dwc3_complete,
+       SET_SYSTEM_SLEEP_PM_OPS(hisi_dwc3_suspend, hisi_dwc3_resume)
+#endif
+       SET_RUNTIME_PM_OPS(hisi_dwc3_runtime_suspend, hisi_dwc3_runtime_resume,
+                          hisi_dwc3_runtime_idle)
+};
+#endif
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("wangbinghui<wangbing...@hisilicon.com>");
diff --git a/drivers/usb/dwc3/dwc3-hisi.h b/drivers/usb/dwc3/dwc3-hisi.h
new file mode 100644
index 000000000000..f497baff563a
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-hisi.h
@@ -0,0 +1,293 @@
+/*
+ * hisi_usb_vbus.h
+ *
+ * Copyright: (C) 2008-2018 hisilicon.
+ * Contact: wangbinghui<wangbing...@hisilicon.com>
+ *
+ * USB vbus for Hisilicon device
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose this file to be licensed under the terms
+ * of the GNU General Public License (GPL) Version 2 or the 2-clause
+ * BSD license listed below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ */
+#ifndef _DWC3_HISI_H_
+#define _DWC3_HISI_H_
+
+#include <linux/pm_wakeup.h>
+#include <linux/clk.h>
+#include <linux/hisi/usb/hisi_usb.h>
+#include <linux/regulator/consumer.h>
+
+#define REG_BASE_PERI_CRG                              (0xFFF35000)
+#define PERI_CRG_CLK_EN4                               (0x40)
+#define PERI_CRG_CLK_DIS4                              (0x44)
+#define PERI_CRG_RSTDIS4                               (0x94)
+#define PERI_CRG_RSTEN4                                (0x90)
+#define PERI_CRG_ISODIS                                (0x148)
+#define PERI_CRG_ISOSTAT                               (0x14C)
+#define STCL_ADDR                                      (0xFFF0A214)
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+#define PERI_CRG_ISOSTAT_MODEMSUBSYSISOEN               BIT(4)
+#define PERI_CRG_ISODIS_MODEMSUBSYSISOEN                BIT(4)
+
+#define PCTRL_PERI_CTRL24                              (0x64)
+#define PCTRL_PERI_CTRL48                              (0xC54)
+
+#define IP_RST_USB3OTG_MUX                              BIT(8)
+#define IP_RST_USB3OTG_AHBIF                            BIT(7)
+#define IP_RST_USB3OTG_32K                              BIT(6)
+#define IP_RST_USB3OTG                                  BIT(5)
+#define IP_RST_USB3OTGPHY_POR                           BIT(3)
+
+#define GT_CLK_USB3OTG_REF                              BIT(0)
+#define GT_ACLK_USB3OTG                                         BIT(1)
+#define GT_CLK_USB3PHY_REF                              BIT(2)
+
+/*
+ * hisi dwc3 phy registers
+ */
+#define DWC3_PHY_RX_OVRD_IN_HI 0x1006
+#define DWC3_PHY_RX_SCOPE_VDCC 0x1026
+
+/* DWC3_PHY_RX_SCOPE_VDCC */
+#define RX_SCOPE_LFPS_EN       BIT(0)
+
+/*
+ * hisi dwc3 otg bc registers
+ */
+#define USBOTG3_CTRL0          0x00
+#define USBOTG3_CTRL1          0x04
+#define USBOTG3_CTRL2          0x08
+#define USBOTG3_CTRL3          0x0C
+#define USBOTG3_CTRL4          0x10
+#define USBOTG3_CTRL5          0x14
+#define USBOTG3_CTRL6          0x18
+#define USBOTG3_CTRL7          0x1C
+#define USBOTG3_STS0           0x20
+#define USBOTG3_STS1           0x24
+#define USBOTG3_STS2           0x28
+#define USBOTG3_STS3           0x2C
+#define BC_CTRL0               0x30
+#define BC_CTRL1               0x34
+#define BC_CTRL2               0x38
+#define BC_STS0                        0x3C
+#define RAM_CTRL               0x40
+#define USBOTG3_STS4           0x44
+#define USB3PHY_CTRL           0x48
+#define USB3PHY_STS            0x4C
+#define USB3PHY_CR_STS         0x50
+#define USB3PHY_CR_CTRL                0x54
+#define USB3_RES               0x58
+
+/* USTOTG3_CTRL0 */
+# define USBOTG3CTRL0_SESSVLD_SEL              BIT(14)
+# define USBOTG3CTRL0_SC_SESSVLD               BIT(13)
+# define USBOTG3CTRL0_POWERPRESENT_SEL         BIT(12)
+# define USBOTG3CTRL0_SC_POWERPRESENT          BIT(11)
+# define USBOTG3CTRL0_BVALID_SEL               BIT(10)
+# define USBOTG3CTRL0_SC_BVALID                BIT(9)
+# define USBOTG3CTRL0_AVALID_SEL               BIT(8)
+# define USBOTG3CTRL0_SC_AVALID                BIT(7)
+# define USBOTG3CTRL0_VBUSVALID_SEL            BIT(6)
+# define USBOTG3CTRL0_DRVVBUS                  BIT(5)
+# define USBOTG3CTRL0_DRVVBUS_SEL              BIT(4)
+# define USBOTG3CTRL0_IDDIG                    BIT(3)
+# define USBOTG3CTRL0_IDDIG_SEL                BIT(2)
+# define USBOTG3CTRL0_IDPULLUP                 BIT(1)
+# define USBOTG3CTRL0_IDPULLUP_SEL             BIT(0)
+
+/* USTOTG3_CTRL2 */
+# define USBOTG3CTRL2_POWERDOWN_HSP             BIT(0)
+# define USBOTG3CTRL2_POWERDOWN_SSP             BIT(1)
+
+/* USBOTG3_CTRL3 */
+# define USBOTG3_CTRL3_VBUSVLDEXT      BIT(6)
+# define USBOTG3_CTRL3_VBUSVLDEXTSEL   BIT(5)
+# define USBOTG3_CTRL3_TXBITSTUFFEHN   BIT(4)
+# define USBOTG3_CTRL3_TXBITSTUFFEN    BIT(3)
+# define USBOTG3_CTRL3_RETENABLEN      BIT(2)
+# define USBOTG3_CTRL3_OTGDISABLE      BIT(1)
+# define USBOTG3_CTRL3_COMMONONN       BIT(0)
+
+/* USBOTG3_CTRL4 */
+# define USBOTG3_CTRL4_TXVREFTUNE(x)            (((x) << 22) & (0xf << 22))
+# define USBOTG3_CTRL4_TXRISETUNE(x)            (((x) << 20) & (3 << 20))
+# define USBOTG3_CTRL4_TXRESTUNE(x)             (((x) << 18) & (3 << 18))
+# define USBOTG3_CTRL4_TXPREEMPPULSETUNE        BIT(17)
+# define USBOTG3_CTRL4_TXPREEMPAMPTUNE(x)       (((x) << 15) & (3 << 15))
+# define USBOTG3_CTRL4_TXHSXVTUNE(x)            (((x) << 13) & (3 << 13))
+# define USBOTG3_CTRL4_TXFSLSTUNE(x)            (((x) << 9) & (0xf << 9))
+# define USBOTG3_CTRL4_SQRXTUNE(x)              (((x) << 6) & (7 << 6))
+# define USBOTG3_CTRL4_OTGTUNE_MASK             (7 << 3)
+# define USBOTG3_CTRL4_OTGTUNE(x)               \
+(((x) << 3) & USBOTG3_CTRL4_OTGTUNE_MASK)
+# define USBOTG3_CTRL4_COMPDISTUNE_MASK         7
+# define USBOTG3_CTRL4_COMPDISTUNE(x)           \
+((x) & USBOTG3_CTRL4_COMPDISTUNE_MASK)
+
+# define USBOTG3_CTRL7_REF_SSP_EN                              BIT(16)
+
+/* USBOTG3_CTRL6 */
+#define TX_VBOOST_LVL_MASK                     7
+#define TX_VBOOST_LVL(x)                       ((x) & TX_VBOOST_LVL_MASK)
+
+/* BC_CTRL0 */
+# define BC_CTRL0_BC_IDPULLUP          BIT(10)
+# define BC_CTRL0_BC_SUSPEND_N         BIT(9)
+# define BC_CTRL0_BC_DMPULLDOWN                BIT(8)
+# define BC_CTRL0_BC_DPPULLDOWN                BIT(7)
+# define BC_CTRL0_BC_TXVALIDH          BIT(6)
+# define BC_CTRL0_BC_TXVALID           BIT(5)
+# define BC_CTRL0_BC_TERMSELECT                BIT(4)
+# define BC_CTRL0_BC_XCVRSELECT(x)     (((x) << 2) & (3 << 2))
+# define BC_CTRL0_BC_OPMODE(x)         ((x) & 3)
+
+/* BC_CTRL1 */
+# define BC_CTRL1_BC_MODE      1
+
+/* BC_CTRL2 */
+# define BC_CTRL2_BC_PHY_VDATDETENB    BIT(4)
+# define BC_CTRL2_BC_PHY_VDATARCENB    BIT(3)
+# define BC_CTRL2_BC_PHY_CHRGSEL               BIT(2)
+# define BC_CTRL2_BC_PHY_DCDENB                BIT(1)
+# define BC_CTRL2_BC_PHY_ACAENB                BIT(0)
+
+/* BC_STS0 */
+# define BC_STS0_BC_LINESTATE(x)       (((x) << 9) & (3 << 9))
+# define BC_STS0_BC_PHY_CHGDET         BIT(8)
+# define BC_STS0_BC_PHY_FSVMINUS       BIT(7)
+# define BC_STS0_BC_PHY_FSVPLUS                BIT(6)
+# define BC_STS0_BC_RID_GND            BIT(5)
+# define BC_STS0_BC_RID_FLOAT          BIT(4)
+# define BC_STS0_BC_RID_C              BIT(3)
+# define BC_STS0_BC_RID_B              BIT(2)
+# define BC_STS0_BC_RID_A              BIT(1)
+# define BC_STS0_BC_SESSVLD            BIT(0)
+
+/* USB3PHY_CR_STS */
+#define USB3OTG_PHY_CR_DATA_OUT(x)     (((x) >> 1) & 0xffff)
+#define USB3OTG_PHY_CR_ACK             BIT(0)
+
+/* USB3PHY_CR_CTRL */
+#define USB3OTG_PHY_CR_DATA_IN(x)      (((x) << 4) & (0xffff << 4))
+#define USB3OTG_PHY_CR_WRITE           BIT(3)
+#define USB3OTG_PHY_CR_READ            BIT(2)
+#define USB3OTG_PHY_CR_CAP_DATA                BIT(1)
+#define USB3OTG_PHY_CR_CAP_ADDR                BIT(0)
+
+#define usb_dbg(format, arg...)    \
+               pr_err("[USB3][%s]"format, __func__, ##arg)
+
+#define usb_err(format, arg...)    \
+               pr_err("[USB3][%s]"format, __func__, ##arg)
+
+enum hisi_usb_state {
+       USB_STATE_UNKNOWN = 0,
+       USB_STATE_OFF,
+       USB_STATE_DEVICE,
+       USB_STATE_HOST,
+};
+
+struct hiusb_event_queue {
+       enum otg_dev_event_type *event;
+       unsigned int num_event;
+       unsigned int max_event;
+       unsigned int enpos, depos;
+       unsigned int overlay, overlay_index;
+};
+
+#define MAX_EVENT_COUNT 16
+#define EVENT_QUEUE_UNIT MAX_EVENT_COUNT
+
+struct hisi_dwc3_device {
+       struct platform_device *pdev;
+
+       void __iomem *otg_bc_reg_base;
+       void __iomem *pericfg_reg_base;
+       void __iomem *pctrl_reg_base;
+       void __iomem *sctrl_reg_base;
+
+       struct regulator *usb_regu;
+       unsigned int is_regu_on;
+       unsigned int runtime_suspended;
+
+       enum hisi_usb_state state;
+       enum hisi_charger_type charger_type;
+       enum hisi_charger_type fake_charger_type;
+
+       enum otg_dev_event_type event;
+       spinlock_t event_lock;
+
+       struct mutex lock;
+       struct wakeup_source ws;
+       struct atomic_notifier_head charger_type_notifier;
+       struct work_struct event_work;
+
+       u32 eye_diagram_param;  /* this param will be set to USBOTG3_CTRL4 */
+       u32 eye_diagram_host_param;
+       u32 usb3_phy_cr_param;
+       u32 usb3_phy_host_cr_param;
+       u32 usb3_phy_tx_vboost_lvl;
+       unsigned int host_flag;
+
+       u32 fpga_flag;
+       int fpga_usb_mode_gpio;
+
+       struct clk *clk;
+       struct clk *gt_aclk_usb3otg;
+
+       int eventmask;
+
+       /* for bc again */
+       u32 bc_again_flag;
+       struct delayed_work bc_again_work;
+       struct notifier_block conndone_nb;
+
+       /* event queue for handle event */
+       struct hiusb_event_queue event_queue;
+
+       struct usb3_phy_ops *phy_ops;
+
+       unsigned int need_disable_vdp;
+       void (*disable_vdp_src)(struct hisi_dwc3_device *hisi_dwc3);
+};
+
+#ifdef CONFIG_PM
+extern const struct dev_pm_ops hisi_dwc3_dev_pm_ops;
+#define HISI_DWC3_PM_OPS (&hisi_dwc3_dev_pm_ops)
+#else
+#define HISI_DWC3_PM_OPS NULL
+#endif
+
+struct usb3_phy_ops {
+       struct regulator *subsys_regu;
+
+       int (*init)(struct hisi_dwc3_device *hisi_dwc3);
+       int (*shutdown)(struct hisi_dwc3_device *hisi_dwc3);
+};
+
+typedef ssize_t (*hiusb_debug_show_ops)(void *, char *, ssize_t);
+typedef ssize_t (*hiusb_debug_store_ops)(void *, const char *, ssize_t);
+void hiusb_debug_init(void *data);
+void hiusb_debug_quick_register(void *dev_data,
+                               hiusb_debug_show_ops show,
+                               hiusb_debug_store_ops store);
+
+void set_hisi_dwc3_power_flag(int val);
+void config_femtophy_param(struct hisi_dwc3_device *hisi_dwc);
+int hisi_dwc3_probe(struct platform_device *pdev, struct usb3_phy_ops 
*phy_ops);
+int hisi_dwc3_remove(struct platform_device *pdev);
+#endif /* _DWC3_HISI_H_ */
diff --git a/drivers/usb/dwc3/dwc3-otg.c b/drivers/usb/dwc3/dwc3-otg.c
new file mode 100644
index 000000000000..fd3ef7d154ed
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-otg.c
@@ -0,0 +1,360 @@
+/*
+ * dwc3-otg.c
+ *
+ * Copyright: (C) 2008-2018 hisilicon.
+ * Contact: wangbinghui<wangbing...@hisilicon.com>
+ *
+ * USB vbus for Hisilicon device
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose this file to be licensed under the terms
+ * of the GNU General Public License (GPL) Version 2 or the 2-clause
+ * BSD license listed below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+
+#include "core.h"
+#include "io.h"
+#include "dwc3-otg.h"
+
+#define DBG(format, arg...) pr_info("[%s]" format, __func__, ##arg)
+
+struct dwc3_otg *dwc_otg_handler;
+
+static void dump_otg_regs(struct dwc3 *dwc)
+{
+#define DUMP_REG(__reg) pr_info("%s:\t0x%x\n", \
+               #__reg, dwc3_readl(dwc->regs, __reg))
+       DUMP_REG(DWC3_OCFG);
+       DUMP_REG(DWC3_OCTL);
+       DUMP_REG(DWC3_OEVT);
+       DUMP_REG(DWC3_OEVTEN);
+       DUMP_REG(DWC3_OSTS);
+
+       DUMP_REG(DWC3_BCFG);
+       DUMP_REG(DWC3_BCEVT);
+       DUMP_REG(DWC3_BCEVTEN);
+}
+
+#ifndef DWC3_OTG_FORCE_MODE
+static void dwc3_disable_otg_event(struct dwc3 *dwc)
+{
+       dwc3_writel(dwc->regs, DWC3_OEVT, 0x0ffffff0);
+       dwc3_writel(dwc->regs, DWC3_OEVTEN, 0);
+}
+
+static void dwc3_enable_otg_event(struct dwc3 *dwc)
+{
+       dwc3_writel(dwc, DWC3_OEVTEN, 0);
+       dwc3_writel(dwc->regs, DWC3_OEVT, 0x0ffffff0);
+       dwc3_writel(dwc->regs, DWC3_OEVTEN, DWC3_OEVT_OTGBDEVVBUSCHNGEVNT |
+                               DWC3_OEVT_OTGCONIDSTSCHNGEVNT);
+}
+#endif
+
+int dwc3_otg_resume(struct dwc3 *dwc)
+{
+       DBG("+\n");
+#ifndef DWC3_OTG_FORCE_MODE
+       u32 reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+       if (reg & DWC3_OSTS_CONIDSTS) {
+               DBG("%s: ID is 1, set peripheral mode\n", __func__);
+               reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+               reg |= DWC3_OCTL_PERIMODE;
+               dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+       } else {
+               DBG("%s: ID is 0, clear peripheral mode\n", __func__);
+               reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+               reg &= ~DWC3_OCTL_PERIMODE;
+               dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+       }
+#endif
+
+       DBG("-\n");
+
+       return 0;
+}
+
+int dwc3_otg_suspend(struct dwc3 *dwc)
+{
+       DBG("+\n");
+       DBG("-\n");
+       return 0;
+}
+
+static int dwc3_otg_start_host(struct dwc3_otg *dwc_otg)
+{
+       struct dwc3 *dwc = dwc_otg->dwc;
+       unsigned long flags;
+       int ret;
+       u32 reg;
+
+       DBG("+\n");
+
+       spin_lock_irqsave(&dwc->lock, flags);
+
+#ifdef DWC3_OTG_FORCE_MODE
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+       pr_debug("%s: GCTL value 0x%x\n", __func__, reg);
+       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+#else
+       /* check ID ststus */
+       DBG("+before read DWC3_OSTS\n");
+       reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+       if (reg & DWC3_OSTS_CONIDSTS) {
+               pr_warn("%s: CONIDSTS wrong!\n");
+               dump_otg_regs(dwc);
+       }
+       DBG("+before read DWC3_OCFG\n");
+       reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+       reg |= DWC3_OCFG_OTGSFTRSTMSK;
+       reg |= DWC3_OCFG_DISPRTPWRCUTOFF;
+       reg &= ~(DWC3_OCFG_HNPCAP | DWC3_OCFG_SRPCAP);
+       dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+       DBG("set OCFG 0x%x\n", dwc3_readl(dwc->regs, DWC3_OCFG));
+
+       reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+       reg &= ~DWC3_OCTL_PERIMODE;
+       reg |= DWC3_OCTL_PRTPWRCTL;
+       dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+
+       DBG("set OCTL 0x%x\n", dwc3_readl(dwc->regs, DWC3_OCTL));
+#endif
+
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       ret = platform_device_add(dwc->xhci);
+       if (ret) {
+               pr_err("%s: failed to register xHCI device\n", __func__);
+               return ret;
+       }
+
+#ifdef CONFIG_HISI_USB_DWC3_MASK_IRQ_WORKAROUND
+       if (dwc->irq_state == 0) {
+               enable_irq(dwc->irq);
+               dwc->irq_state = 1;
+               pr_info("[%s]enable irq\n", __func__);
+       }
+#endif
+
+       DBG("-\n");
+
+       return ret;
+}
+
+static void dwc3_otg_stop_host(struct dwc3_otg *dwc_otg)
+{
+       DBG("+\n");
+       platform_device_del(dwc_otg->dwc->xhci);
+       DBG("-\n");
+}
+
+static int dwc3_otg_start_peripheral(struct dwc3_otg *dwc_otg)
+{
+       int ret;
+       unsigned long flags;
+       struct dwc3 *dwc = dwc_otg->dwc;
+       u32 reg;
+
+       DBG("+\n");
+
+       spin_lock_irqsave(&dwc->lock, flags);
+
+#ifdef DWC3_OTG_FORCE_MODE
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+       pr_debug("%s: GCTL value 0x%x\n", __func__, reg);
+       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+#else
+       reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+       if (!(reg & DWC3_OSTS_CONIDSTS) || !(reg & DWC3_OSTS_BSESVLD)) {
+               pr_warn("%s: CONIDSTS or BSESVLD wrong!\n");
+               dump_otg_regs(dwc);
+       }
+
+       /* set mode as peripheral */
+       reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+       reg |= DWC3_OCTL_PERIMODE;
+       dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+#endif
+
+       ret = dwc3_gadget_resume(dwc);
+       if (ret)
+               pr_err("[%s] gadget resume error!", __func__);
+
+       spin_unlock_irqrestore(&dwc->lock, flags);
+       DBG("-\n");
+
+       return ret;
+}
+
+static int dwc3_otg_stop_peripheral(struct dwc3_otg *dwc_otg)
+{
+       int ret;
+       unsigned long flags;
+       struct dwc3 *dwc = dwc_otg->dwc;
+
+       DBG("+\n");
+       spin_lock_irqsave(&dwc->lock, flags);
+
+       ret = dwc3_gadget_suspend(dwc);
+       if (ret)
+               pr_err("[%s] gadget suspend error!", __func__);
+
+       spin_unlock_irqrestore(&dwc->lock, flags);
+       DBG("-\n");
+
+       return ret;
+}
+
+int dwc3_otg_id_value(struct dwc3_otg *dwc_otg)
+{
+       if (dwc_otg)
+               return !!(dwc3_readl(dwc_otg->dwc->regs, DWC3_OSTS)
+                               & DWC3_OSTS_CONIDSTS);
+       else
+               return 1;
+}
+
+int dwc3_otg_work(struct dwc3_otg *dwc_otg, int evt)
+{
+       int ret = 0;
+
+       DBG("+\n evt = %d", evt);
+
+       /* if otg is not enabled, do nothing */
+       if (!dwc_otg) {
+               pr_info("%s: dwc3 is not otg mode!\n", __func__);
+               return 0;
+       }
+
+       switch (evt) {
+       case DWC3_OTG_EVT_ID_SET:
+               dwc3_otg_stop_host(dwc_otg);
+               dwc3_suspend_device(dwc_otg->dwc);
+               break;
+       case DWC3_OTG_EVT_ID_CLEAR:
+               ret = dwc3_resume_device(dwc_otg->dwc);
+               if (ret) {
+                       pr_err("%s: resume device failed!\n", __func__);
+                       return ret;
+               }
+               ret = dwc3_otg_start_host(dwc_otg);
+               if (ret) {
+                       pr_err("%s: start host failed!\n", __func__);
+                       dwc3_suspend_device(dwc_otg->dwc);
+                       return ret;
+               }
+               break;
+       case DWC3_OTG_EVT_VBUS_SET:
+               ret = dwc3_resume_device(dwc_otg->dwc);
+               if (ret) {
+                       pr_err("%s: resume device failed!\n", __func__);
+                       return ret;
+               }
+               ret = dwc3_otg_start_peripheral(dwc_otg);
+               if (ret) {
+                       pr_err("%s: start peripheral failed!\n", __func__);
+                       dwc3_suspend_device(dwc_otg->dwc);
+                       return ret;
+               }
+               break;
+       case DWC3_OTG_EVT_VBUS_CLEAR:
+               ret = dwc3_otg_stop_peripheral(dwc_otg);
+               dwc3_suspend_device(dwc_otg->dwc);
+               break;
+       default:
+               break;
+       }
+       DBG("-\n");
+
+       return ret;
+}
+
+static void dwc3_otg_work_fun(struct work_struct *w)
+{
+       struct dwc3_otg *dwc_otg = container_of(
+                       w, struct dwc3_otg, otg_work.work);
+
+       mutex_lock(&dwc_otg->lock);
+       if (dwc3_otg_work(dwc_otg, atomic_read(&dwc_otg->otg_evt_flag)))
+               pr_err("%s: dwc3_otg_work failed\n", __func__);
+       mutex_unlock(&dwc_otg->lock);
+}
+
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+       struct dwc3_otg *dwc_otg;
+       u32 reg;
+
+       DBG("+\n");
+
+       dwc_otg = devm_kzalloc(dwc->dev, sizeof(struct dwc3_otg), GFP_KERNEL);
+       if (!dwc_otg)
+               return -ENOMEM;
+
+       dwc_otg->dwc = dwc;
+       dwc->dwc_otg = dwc_otg;
+
+       mutex_init(&dwc_otg->lock);
+       INIT_DELAYED_WORK(&dwc_otg->otg_work, dwc3_otg_work_fun);
+
+       dwc_otg_handler = dwc_otg;
+
+#ifdef DWC3_OTG_FORCE_MODE
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+       pr_debug("%s: GCTL value 0x%x\n", __func__, reg);
+
+       /* default device mode */
+       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+#else
+       /* disable hnp and srp */
+       reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+       reg &= ~(DWC3_OCFG_HNPCAP | DWC3_OCFG_SRPCAP);
+       dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+       reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+       if (reg & DWC3_OSTS_CONIDSTS) {
+               DBG("%s: ID is 1, set peripheral mode\n", __func__);
+               reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+               reg |= DWC3_OCTL_PERIMODE;
+               reg &= ~(DWC3_OCTL_HNPREQ | DWC3_OCTL_DEVSETHNPEN |
+                               DWC3_OCTL_HSTSETHNPEN);
+               dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+       } else {
+               DBG("%s: ID is 0, clear peripheral mode\n", __func__);
+               reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+               reg &= ~DWC3_OCTL_PERIMODE;
+               dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+       }
+#endif
+
+       dump_otg_regs(dwc);
+
+       DBG("-\n");
+
+       return 0;
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+       DBG("+\n");
+       dwc_otg_handler = NULL;
+       dwc->dwc_otg->dwc = NULL;
+       dwc->dwc_otg = NULL;
+       DBG("-\n");
+}
diff --git a/drivers/usb/dwc3/dwc3-otg.h b/drivers/usb/dwc3/dwc3-otg.h
new file mode 100644
index 000000000000..b9114b16f050
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-otg.h
@@ -0,0 +1,133 @@
+/*
+ * dwc3-otg.h
+ *
+ * Copyright: (C) 2008-2018 hisilicon.
+ * Contact: wangbinghui<wangbing...@hisilicon.com>
+ *
+ * USB vbus for Hisilicon device
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose this file to be licensed under the terms
+ * of the GNU General Public License (GPL) Version 2 or the 2-clause
+ * BSD license listed below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ */
+#ifndef __DRIVERS_USB_DWC3_OTG_H
+#define __DRIVERS_USB_DWC3_OTG_H
+
+/* BC Registers */
+#define DWC3_BCFG      0xcc30
+#define DWC3_BCEVT     0xcc38
+#define DWC3_BCEVTEN   0xcc3c
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+/*  OTG Configuration Register */
+#define DWC3_OCFG_DISPRTPWRCUTOFF      BIT(5)
+#define DWC3_OCFG_OTGHIBDISMASK                BIT(4)
+#define DWC3_OCFG_OTGSFTRSTMSK         BIT(3)
+#define DWC3_OCFG_HNPCAP               BIT(1)
+#define DWC3_OCFG_SRPCAP               1
+
+/*  OTG Control Register */
+#define        DWC3_OCTL_OTG3_GOERR            BIT(7)
+#define        DWC3_OCTL_PERIMODE              BIT(6)
+#define        DWC3_OCTL_PRTPWRCTL             BIT(5)
+#define        DWC3_OCTL_HNPREQ                BIT(4)
+#define        DWC3_OCTL_SESREQ                BIT(3)
+#define        DWC3_OCTL_TERMSELDLPULSE        BIT(2)
+#define        DWC3_OCTL_DEVSETHNPEN           BIT(1)
+#define        DWC3_OCTL_HSTSETHNPEN           BIT(0)
+
+/*  OTG Events Register */
+#define DWC3_OEVT_DEVICEMOD                    BIT(31)
+#define DWC3_OEVT_OTGXHCIRUNSTPSETEVNT         BIT(27)
+#define DWC3_OEVT_OTGDEVRUNSTPSETEVNT          BIT(26)
+#define DWC3_OEVT_OTGHIBENTRYEVNT              BIT(25)
+#define DWC3_OEVT_OTGCONIDSTSCHNGEVNT          BIT(24)
+#define DWC3_OEVT_HRRCONFNOTIFEVNT             BIT(23)
+#define DWC3_OEVT_HRRINITNOTIFEVNT             BIT(22)
+#define DWC3_OEVT_OTGADEVIDLEEVNT              BIT(21)
+#define DWC3_OEVT_OTGADEVBHOSTENDEVNT          BIT(20)
+#define DWC3_OEVT_OTGADEVHOSTEVNT              BIT(19)
+#define DWC3_OEVT_OTGADEVHNPCHNGEVNT           BIT(18)
+#define DWC3_OEVT_OTGADEVSRPDETEVNT            BIT(17)
+#define DWC3_OEVT_OTGADEVSESSENDDETEVNT                BIT(16)
+#define DWC3_OEVT_OTGBDEVBHOSTENDEVNT          BIT(11)
+#define DWC3_OEVT_OTGBDEVHNPCHNGEVNT           BIT(10)
+#define DWC3_OEVT_OTGBDEVSESSVLDDETEVNT                BIT(9)
+#define DWC3_OEVT_OTGBDEVVBUSCHNGEVNT          BIT(8)
+
+/*  OTG Status Register */
+#define DWC3_OSTS_OTGSTATE_MSK          (0xf << 8)
+#define DWC3_OSTS_PERIPHERALSTATE       BIT(4)
+#define DWC3_OSTS_XHCIPRTPOWER          BIT(3)
+#define DWC3_OSTS_BSESVLD               BIT(2)
+#define DWC3_OSTS_ASESVLD               BIT(1)
+#define DWC3_OSTS_CONIDSTS              BIT(0)
+
+struct dwc3_otg {
+       struct usb_otg otg;
+       struct dwc3 *dwc;
+       int otg_irq;
+       struct delayed_work otg_work;
+
+       atomic_t otg_evt_flag;
+#define DWC3_OTG_EVT_ID_SET 1
+#define DWC3_OTG_EVT_ID_CLEAR 2
+#define DWC3_OTG_EVT_VBUS_SET 3
+#define DWC3_OTG_EVT_VBUS_CLEAR 4
+
+       struct mutex lock;
+};
+
+#ifdef CONFIG_USB_DWC3_OTG
+extern struct dwc3_otg *dwc_otg_handler;
+int dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+int dwc3_otg_work(struct dwc3_otg *dwc_otg, int evt);
+int dwc3_otg_resume(struct dwc3 *dwc);
+int dwc3_otg_suspend(struct dwc3 *dwc);
+int dwc3_otg_id_value(struct dwc3_otg *dwc_otg);
+#else
+#define dwc_otg_handler ((struct dwc3_otg *)NULL)
+static inline int dwc3_otg_init(struct dwc3 *dwc)
+{
+       return 0;
+}
+
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{
+}
+
+static inline int dwc3_otg_work(struct dwc3_otg *dwc_otg, int evt)
+{
+       return 0;
+}
+
+static inline int dwc3_otg_resume(struct dwc3 *dwc)
+{
+       return 0;
+}
+
+static inline int dwc3_otg_suspend(struct dwc3 *dwc)
+{
+       return 0;
+}
+
+static inline int dwc3_otg_id_value(struct dwc3_otg *dwc_otg)
+{
+       return 0;
+};
+#endif
+
+#endif /* __DRIVERS_USB_DWC3_OTG_H */
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 75e6cb044eb2..e2c8d2ebfb64 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -98,11 +98,19 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
                struct dwc3_request *req)
 {
        struct dwc3             *dwc = dep->dwc;
+       int ret;
 
        req->request.actual     = 0;
        req->request.status     = -EINPROGRESS;
        req->epnum              = dep->number;
 
+       /* we share one TRB for ep0/1 */
+       if (!list_empty(&dep->pending_list)) {
+               dev_WARN(dwc->dev, "ep0 busy!\n");
+               ret = -EBUSY;
+               return ret;
+       }
+
        list_add_tail(&req->list, &dep->pending_list);
 
        /*
@@ -190,8 +198,18 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
                __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
 
                dep->flags &= ~DWC3_EP0_DIR_IN;
+
+               return 0;
        }
 
+       /* mark the status phase already queued */
+       if (dwc->ep0_next_event == DWC3_EP0_NRDY_STATUS)
+               dwc->status_queued = true;
+
+       if (req->request.length != 0)
+               dev_WARN(dwc->dev, "status phase len %d\n",
+                        req->request.length);
+
        return 0;
 }
 
@@ -241,6 +259,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
        __dwc3_gadget_ep_set_halt(dep, 1, false);
        dep->flags = DWC3_EP_ENABLED;
        dwc->delayed_status = false;
+       dwc->status_queued = false;
 
        if (!list_empty(&dep->pending_list)) {
                struct dwc3_request     *req;
@@ -329,6 +348,12 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
        if (value != 0)
                return -EINVAL;
 
+       if (!(ctrl->bRequestType & USB_DIR_IN))
+               return -EINVAL;
+
+       if (!le16_to_cpu(ctrl->wLength))
+               return -EINVAL;
+
        recip = ctrl->bRequestType & USB_RECIP_MASK;
        switch (recip) {
        case USB_RECIP_DEVICE:
@@ -714,6 +739,12 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct 
usb_ctrlrequest *ctrl)
        u16             wLength;
        u16             wValue;
 
+       if (unlikely(ctrl->bRequestType & USB_DIR_IN))
+               return -EINVAL;
+
+       if (unlikely(!le16_to_cpu(ctrl->wLength)))
+               return -EINVAL;
+
        if (state == USB_STATE_DEFAULT)
                return -EINVAL;
 
@@ -830,9 +861,25 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
        if (ret == USB_GADGET_DELAYED_STATUS)
                dwc->delayed_status = true;
 
+       if (dwc->status_queued) {
+               dwc->status_queued = false;
+               if (dwc->delayed_status) {
+                       pr_info("delayed status already come, will not wait for 
it.\n");
+                       dwc->delayed_status = false;
+                       usb_gadget_set_state(&dwc->gadget,
+                                            USB_STATE_CONFIGURED);
+               }
+       }
+
 out:
-       if (ret < 0)
+       if (ret < 0) {
+               dev_err(dwc->dev, "ep0 setup error, ret %d!\n", ret);
+               dev_err(dwc->dev, "ctrl: %02x %02x %04x %04x %04x\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       ctrl->wValue, ctrl->wIndex, ctrl->wLength);
                dwc3_ep0_stall_and_restart(dwc);
+       }
+
 }
 
 static void dwc3_ep0_complete_data(struct dwc3 *dwc,
@@ -858,8 +905,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
        trace_dwc3_complete_trb(ep0, trb);
 
        r = next_request(&ep0->pending_list);
-       if (!r)
+       if (!r) {
+               dev_err(dwc->dev, "ep0 request list empty while complete 
data\n");
                return;
+       }
 
        status = DWC3_TRB_SIZE_TRBSTS(trb->size);
        if (status == DWC3_TRBSTS_SETUP_PENDING) {
@@ -1135,6 +1184,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                        return;
                }
 
+               dwc->status_queued = false;
+
                dwc3_ep0_do_control_status(dwc, event);
        }
 }
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f064f1549333..069c6eb1cc5c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -34,6 +34,7 @@
 #include "core.h"
 #include "gadget.h"
 #include "io.h"
+#include "dwc3-hisi.h"
 
 /**
  * dwc3_gadget_set_test_mode - enables usb2 test modes
@@ -267,7 +268,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned 
cmd,
 {
        const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
        struct dwc3             *dwc = dep->dwc;
-       u32                     timeout = 500;
+       u32                     timeout = 3000;
        u32                     reg;
 
        int                     cmd_status = 0;
@@ -1476,6 +1477,9 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 
 out1:
        /* giveback the request */
+       if (!dep->queued_requests)
+               goto out0;
+
        dep->queued_requests--;
        dwc3_gadget_giveback(dep, req, -ECONNRESET);
 
@@ -2710,6 +2714,18 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
        dwc3_writel(dwc->regs, DWC3_DCFG, reg);
 }
 
+ATOMIC_NOTIFIER_HEAD(conndone_nh);
+
+int dwc3_conndone_notifier_register(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&conndone_nh, nb);
+}
+
+int dwc3_conndone_notifier_unregister(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&conndone_nh, nb);
+}
+
 static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 {
        struct dwc3_ep          *dep;
@@ -3236,7 +3252,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
        dwc->gadget.speed               = USB_SPEED_UNKNOWN;
        dwc->gadget.sg_supported        = true;
        dwc->gadget.name                = "dwc3-gadget";
+#ifndef CONFIG_USB_DWC3_HISI
        dwc->gadget.is_otg              = dwc->dr_mode == USB_DR_MODE_OTG;
+#endif
 
        /*
         * FIXME We might be setting max_speed to <SUPER, however versions
diff --git a/drivers/usb/dwc3/hisi_hikey_gpio.c 
b/drivers/usb/dwc3/hisi_hikey_gpio.c
new file mode 100644
index 000000000000..ae05bbf9dd4a
--- /dev/null
+++ b/drivers/usb/dwc3/hisi_hikey_gpio.c
@@ -0,0 +1,300 @@
+/*
+ * otgid_gpio_hub.c
+ *
+ * Copyright (c) Hisilicon Tech. Co., Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/timer.h>
+#include <linux/param.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/hisi/log/hisi_log.h>
+#include <linux/hisi/usb/hisi_usb.h>
+#include <linux/tifm.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/hisi/usb/hisi_hikey_gpio.h>
+#define DEVICE_DRIVER_NAME "gpio_hub_for_usb5734"
+
+#define GPIO_HUB_OTG_HOST 1
+#define GPIO_HUB_OTG_DEVICE 0
+#define GPIO_TYPEC_VBUS_POWER 1
+#define GPIO_TYPEC_NO_POWER 0
+#define GPIO_HUB_VBUS_POWER 1
+#define GPIO_HUB_VBUS_NO_POWER 0
+#define GPIO_HUB_HUB_VBUS_POWER 1
+
+/* SOC_CRGPERIPH_PEREN1_UNION */
+#define SOC_CRGPERIPH_PEREN1_ADDR(base)               ((base) + (0x010))
+
+#define HISILOG_TAG GPIO_HUB
+HISILOG_REGIST();
+
+struct gpio_hub_info {
+       struct platform_device *pdev;
+       int otg_switch_gpio;
+       int typec_vbus_gpio;
+       int typec_vbus_enable_val;
+       int hub_vbus_gpio;
+};
+
+static struct gpio_hub_info gpio_hub_driver_info = {
+       .otg_switch_gpio = -1,
+       .typec_vbus_gpio = -1,
+       .typec_vbus_enable_val = -1,
+       .hub_vbus_gpio = -1,
+};
+
+void gpio_hub_power_off(void)
+{
+       if (gpio_is_valid(gpio_hub_driver_info.hub_vbus_gpio)) {
+               gpio_set_value(gpio_hub_driver_info.hub_vbus_gpio,
+                              GPIO_HUB_VBUS_NO_POWER);
+               hisilog_info("%s: gpio hub hub vbus no power set success",
+                            __func__);
+       } else {
+               hisilog_err("%s: gpio hub hub vbus no power set err",
+                           __func__);
+       }
+}
+
+void gpio_hub_power_on(void)
+{
+       if (gpio_is_valid(gpio_hub_driver_info.hub_vbus_gpio))
+               gpio_set_value(gpio_hub_driver_info.hub_vbus_gpio,
+                              GPIO_HUB_VBUS_POWER);
+       else
+               hisilog_err("%s: gpio hub hub vbus set err", __func__);
+}
+
+void gpio_hub_switch_to_hub(void)
+{
+       int gpio = gpio_hub_driver_info.otg_switch_gpio;
+
+       if (!gpio_is_valid(gpio)) {
+               hisilog_err("%s: otg_switch_gpio is err\n", __func__);
+               return;
+       }
+
+       if (gpio_get_value(gpio)) {
+               hisilog_info("%s: already switch to hub\n", __func__);
+               return;
+       }
+
+       gpio_direction_output(gpio, 1);
+       hisilog_err("%s: switch to hub\n", __func__);
+}
+EXPORT_SYMBOL_GPL(gpio_hub_switch_to_hub);
+
+void gpio_hub_switch_to_typec(void)
+{
+       int gpio = gpio_hub_driver_info.otg_switch_gpio;
+
+       if (!gpio_is_valid(gpio)) {
+               hisilog_err("%s: otg_switch_gpio is err\n", __func__);
+               return;
+       }
+
+       if (!gpio_get_value(gpio)) {
+               hisilog_info("%s: already switch to typec\n", __func__);
+               return;
+       }
+
+       gpio_direction_output(gpio, 0);
+       hisilog_err("%s: switch to typec\n", __func__);
+}
+EXPORT_SYMBOL_GPL(gpio_hub_switch_to_typec);
+
+static void gpio_hub_change_typec_power(int gpio, int on)
+{
+       if (!gpio_is_valid(gpio)) {
+               hisilog_err("%s: typec power gpio is err\n", __func__);
+               return;
+       }
+
+       if (gpio_get_value(gpio) == on) {
+               hisilog_info("%s: typec power no change\n", __func__);
+               return;
+       }
+
+       gpio_direction_output(gpio, on);
+       hisilog_info("%s: set typec vbus gpio to %d\n", __func__, on);
+}
+
+void gpio_hub_typec_power_on(void)
+{
+       struct gpio_hub_info *info = &gpio_hub_driver_info;
+
+       gpio_hub_change_typec_power(info->typec_vbus_gpio,
+                                   info->typec_vbus_enable_val);
+}
+EXPORT_SYMBOL_GPL(gpio_hub_typec_power_on);
+
+void gpio_hub_typec_power_off(void)
+{
+       struct gpio_hub_info *info = &gpio_hub_driver_info;
+
+       gpio_hub_change_typec_power(info->typec_vbus_gpio,
+                                   !info->typec_vbus_enable_val);
+}
+EXPORT_SYMBOL_GPL(gpio_hub_typec_power_off);
+
+static int gpio_hub_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct device_node *root = pdev->dev.of_node;
+       struct gpio_hub_info *info = &gpio_hub_driver_info;
+
+       hisilog_info("%s: step in\n", __func__);
+
+       info->pdev = pdev;
+       if (!pdev)
+               return -EBUSY;
+
+       info->hub_vbus_gpio = of_get_named_gpio(root, "hub_vdd33_en_gpio", 0);
+       if (!gpio_is_valid(info->hub_vbus_gpio)) {
+               hisilog_err("%s: hub_vbus_gpio is err\n", __func__);
+               return info->hub_vbus_gpio;
+       }
+       ret = gpio_request(info->hub_vbus_gpio, "hub_vbus_int_gpio");
+       if (ret) {
+               hisilog_err("%s: request hub_vbus_gpio err\n", __func__);
+               return ret;
+       }
+
+       info->typec_vbus_gpio = of_get_named_gpio(root,
+               "typc_vbus_int_gpio,typec-gpios", 0);
+       if (!gpio_is_valid(info->hub_vbus_gpio)) {
+               hisilog_err("%s: typec_vbus_gpio is err\n", __func__);
+               ret = info->typec_vbus_gpio;
+               goto free_gpio1;
+       }
+       ret = gpio_request(info->typec_vbus_gpio, "typc_vbus_int_gpio");
+       if (ret) {
+               hisilog_err("%s: request typec_vbus_gpio err\n", __func__);
+               goto free_gpio1;
+       }
+
+       ret = of_property_read_u32(root, "typc_vbus_enable_val",
+                                  &info->typec_vbus_enable_val);
+       if (ret) {
+               hisilog_err("%s: typc_vbus_enable_val can't get\n", __func__);
+               goto free_gpio2;
+       }
+       info->typec_vbus_enable_val = !!info->typec_vbus_enable_val;
+
+       /* only for v2 */
+       info->otg_switch_gpio = of_get_named_gpio(root, "otg_gpio", 0);
+       if (!gpio_is_valid(info->otg_switch_gpio)) {
+               hisilog_info("%s: otg_switch_gpio is err\n", __func__);
+               info->otg_switch_gpio = -1;
+       }
+
+       ret = gpio_direction_output(info->hub_vbus_gpio, GPIO_HUB_VBUS_POWER);
+       if (ret) {
+               hisilog_err("%s: power on hub vbus err\n", __func__);
+               goto free_gpio2;
+       }
+
+       ret = gpio_direction_output(info->typec_vbus_gpio,
+                                   info->typec_vbus_enable_val);
+       if (ret) {
+               hisilog_err("%s: power on typec vbus err", __func__);
+               goto free_gpio2;
+       }
+
+       return 0;
+
+free_gpio2:
+       gpio_free(info->typec_vbus_gpio);
+       info->typec_vbus_gpio = -1;
+free_gpio1:
+       gpio_free(info->hub_vbus_gpio);
+       info->hub_vbus_gpio = -1;
+
+       return ret;
+}
+
+static int  gpio_hub_remove(struct platform_device *pdev)
+{
+       struct gpio_hub_info *info = &gpio_hub_driver_info;
+
+       if (gpio_is_valid(info->otg_switch_gpio)) {
+               gpio_free(info->otg_switch_gpio);
+               info->otg_switch_gpio = -1;
+       }
+
+       if (gpio_is_valid(info->typec_vbus_gpio)) {
+               gpio_free(info->typec_vbus_gpio);
+               info->typec_vbus_gpio = -1;
+       }
+
+       if (gpio_is_valid(info->hub_vbus_gpio)) {
+               gpio_free(info->hub_vbus_gpio);
+               info->hub_vbus_gpio = -1;
+       }
+       return 0;
+}
+
+static const struct of_device_id id_table_for_gpio_hub[] = {
+       {.compatible = "hisilicon,gpio_hubv1"},
+       {.compatible = "hisilicon,gpio_hubv2"},
+       {}
+};
+
+static struct platform_driver gpio_hub_driver = {
+       .probe = gpio_hub_probe,
+       .remove = gpio_hub_remove,
+       .driver = {
+               .name = DEVICE_DRIVER_NAME,
+               .of_match_table = of_match_ptr(id_table_for_gpio_hub),
+
+       },
+};
+
+static int __init gpio_hub_init(void)
+{
+       int ret = platform_driver_register(&gpio_hub_driver);
+
+       hisilog_info("%s:gpio hub init status:%d\n", __func__, ret);
+       return ret;
+}
+
+static void __exit gpio_hub_exit(void)
+{
+       platform_driver_unregister(&gpio_hub_driver);
+}
+
+module_init(gpio_hub_init);
+module_exit(gpio_hub_exit);
+
+MODULE_AUTHOR("wangbinghui<wangbing...@hisilicon.com>");
+MODULE_DESCRIPTION("HUB GPIO FOR OTG ID driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 76f0b0df37c1..ccbf0c35a9b1 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -96,6 +96,15 @@ int dwc3_host_init(struct dwc3 *dwc)
                goto err1;
        }
 
+#ifdef CONFIG_USB_DWC3_HISI
+       /* if otg, otg will do device_add */
+       if (dwc->dwc_otg) {
+               dev_err(dwc->dev, "%s if otg, otg will do device_add.\n",
+                       __func__);
+               return 0;
+       }
+#endif
+
        memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
 
        if (dwc->usb3_lpm_capable)
@@ -145,6 +154,10 @@ int dwc3_host_init(struct dwc3 *dwc)
 
 void dwc3_host_exit(struct dwc3 *dwc)
 {
+#ifdef CONFIG_USB_DWC3_HISI
+       if (dwc->dwc_otg)
+               return;
+#endif
        phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
                          dev_name(dwc->dev));
        phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index c69b06696824..adc8648c92b2 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -28,6 +28,13 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
 {
        u32 value;
 
+#ifdef CONFIG_USB_DWC3_HISI
+       extern atomic_t hisi_dwc3_power_on;
+
+       if (unlikely(atomic_read(&hisi_dwc3_power_on) == 0))
+               return 0;
+#endif
+
        /*
         * We requested the mem region starting from the Globals address
         * space, see dwc3_probe in core.c.
@@ -47,6 +54,13 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
 
 static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
 {
+#ifdef CONFIG_USB_DWC3_HISI
+       extern atomic_t hisi_dwc3_power_on;
+
+       if (unlikely(atomic_read(&hisi_dwc3_power_on) == 0))
+               return;
+#endif
+
        /*
         * We requested the mem region starting from the Globals address
         * space, see dwc3_probe in core.c.
diff --git a/include/linux/hisi/log/hisi_log.h 
b/include/linux/hisi/log/hisi_log.h
new file mode 100644
index 000000000000..cc3eda1c4f0f
--- /dev/null
+++ b/include/linux/hisi/log/hisi_log.h
@@ -0,0 +1,143 @@
+#ifndef _LINUX_HISILOG_H
+#define _LINUX_HISILOG_H
+
+#include <linux/printk.h>
+#include <linux/types.h>
+
+enum {
+       HISILOG_ERR         = 1U << 0,
+       HISILOG_WARNING     = 1U << 1,
+       HISILOG_INFO        = 1U << 2,
+       HISILOG_DEBUG       = 1U << 3,
+       HISILOG_DEBUG1      = 1U << 4,
+       HISILOG_DEBUG2      = 1U << 5,
+       HISILOG_DEBUG3      = 1U << 6,
+       HISILOG_DEBUG4      = 1U << 7,
+};
+
+#define HISILOG_TAG_DEFOUTL_LEVEL (HISILOG_ERR \
+               | HISILOG_WARNING \
+               | HISILOG_INFO)
+
+struct hisi_log_tag {
+       const char *name;
+       u32 level;
+};
+
+#define HISILOG_REGIST()       \
+       HISILOG_REGIST_TAG_LEVEL(HISILOG_TAG, HISILOG_TAG_DEFOUTL_LEVEL)
+
+#define HISILOG_REGIST_LEVEL(level)    \
+       HISILOG_REGIST_TAG_LEVEL(HISILOG_TAG, level)
+
+#define HISILOG_REGIST_TAG_LEVEL(name, level)  \
+       _HISILOG_REGIST_TAG_LEVEL(name, level)
+
+#define _HISILOG_REGIST_TAG_LEVEL(name, level) \
+       static struct hisi_log_tag TAG_STRUCT_NAME(name)        \
+__used                                                         \
+__attribute__ ((unused, __section__("__hisilog_tag"))) \
+= { #name, level}
+
+#define hisilog_err(x...) \
+       _hisilog_err(HISILOG_TAG, ##x)
+
+#define _hisilog_err(TAG, x...) \
+       __hisilog_err(TAG, ##x)
+
+#define __hisilog_err(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_ERR) \
+                       pr_err(hw_fmt_tag(TAG, E) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_warn(x...) \
+       _hisilog_warn(HISILOG_TAG, ##x)
+
+#define _hisilog_warn(TAG, x...) \
+       __hisilog_warn(TAG, ##x)
+
+#define __hisilog_warn(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_WARNING) \
+                       pr_err(hw_fmt_tag(TAG, W) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_info(x...) \
+       _hisilog_info(HISILOG_TAG, ##x)
+
+#define _hisilog_info(TAG, x...) \
+       __hisilog_info(TAG, ##x)
+
+#define __hisilog_info(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_INFO) \
+                       pr_info(hw_fmt_tag(TAG, I) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug(x...) \
+       _hisilog_debug(HISILOG_TAG, ##x)
+
+#define _hisilog_debug(TAG, x...) \
+       __hisilog_debug(TAG, ##x)
+
+#define __hisilog_debug(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG) \
+                       pr_err(hw_fmt_tag(TAG, D) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug1(x...) \
+       _hisilog_debug1(HISILOG_TAG, ##x)
+
+#define _hisilog_debug1(TAG, x...) \
+       __hisilog_debug1(TAG, ##x)
+
+#define __hisilog_debug1(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG1) \
+                       pr_err(hw_fmt_tag(TAG, D1) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug2(x...) \
+       _hisilog_debug2(HISILOG_TAG, ##x)
+
+#define _hisilog_debug2(TAG, x...) \
+       __hisilog_debug2(TAG, ##x)
+
+#define __hisilog_debug2(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG2) \
+                       pr_err(hw_fmt_tag(TAG, D2) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug3(x...) \
+       _hisilog_debug3(HISILOG_TAG, ##x)
+
+#define _hisilog_debug3(TAG, x...) \
+       __hisilog_debug3(TAG, ##x)
+
+#define __hisilog_debug3(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG3) \
+                       pr_err(hw_fmt_tag(TAG, D3) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define hisilog_debug4(x...) \
+       _hisilog_debug4(HISILOG_TAG, ##x)
+
+#define _hisilog_debug4(TAG, x...) \
+       __hisilog_debug4(TAG, ##x)
+
+#define __hisilog_debug4(TAG, fmt, ...) \
+       do { \
+               if (TAG_STRUCT_NAME(TAG).level & HISILOG_DEBUG4) \
+                       pr_err(hw_fmt_tag(TAG, D4) fmt, ##__VA_ARGS__);    \
+       } while (0)
+
+#define TAG_STRUCT_NAME(name) \
+       _hwtag_##name
+
+#define hw_fmt_tag(TAG, LEVEL) "[" #LEVEL "/" #TAG "] "
+
+#endif
diff --git a/include/linux/hisi/usb/hisi_hikey_gpio.h 
b/include/linux/hisi/usb/hisi_hikey_gpio.h
new file mode 100644
index 000000000000..99df5772df96
--- /dev/null
+++ b/include/linux/hisi/usb/hisi_hikey_gpio.h
@@ -0,0 +1,24 @@
+/*
+ * hub_usb5734.h
+ *
+ * Copyright (c) Hisilicon Tech. Co., Ltd. All rights reserved.
+ *
+ * Chenjun <chen...@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+void gpio_hub_power_on(void);
+void gpio_hub_power_off(void);
+void gpio_hub_switch_to_hub(void);
+void gpio_hub_switch_to_typec(void);
+void gpio_hub_typec_power_off(void);
+void gpio_hub_typec_power_on(void);
diff --git a/include/linux/hisi/usb/hisi_usb.h 
b/include/linux/hisi/usb/hisi_usb.h
new file mode 100644
index 000000000000..9ee216e32cd1
--- /dev/null
+++ b/include/linux/hisi/usb/hisi_usb.h
@@ -0,0 +1,57 @@
+#ifndef _HISI_USB_H_
+#define _HISI_USB_H_
+
+enum hisi_charger_type {
+       CHARGER_TYPE_SDP = 0,           /* Standard Downstreame Port */
+       CHARGER_TYPE_CDP,               /* Charging Downstreame Port */
+       CHARGER_TYPE_DCP,               /* Dedicate Charging Port */
+       CHARGER_TYPE_UNKNOWN,           /* non-standard */
+       CHARGER_TYPE_NONE,              /* not connected */
+
+       /* other messages */
+       PLEASE_PROVIDE_POWER,           /* host mode, provide power */
+};
+
+enum otg_dev_event_type {
+       CHARGER_CONNECT_EVENT = 0,
+       CHARGER_DISCONNECT_EVENT,
+       ID_FALL_EVENT,
+       ID_RISE_EVENT,
+       NONE_EVENT
+};
+
+#if defined(CONFIG_USB_SUSB_HDRC) || defined(CONFIG_USB_DWC3)
+int hisi_charger_type_notifier_register(struct notifier_block *nb);
+int hisi_charger_type_notifier_unregister(struct notifier_block *nb);
+enum hisi_charger_type hisi_get_charger_type(void);
+int hisi_usb_otg_event(enum otg_dev_event_type event_type);
+void hisi_usb_otg_bc_again(void);
+#else
+static inline int hisi_charger_type_notifier_register(
+               struct notifier_block *nb){return 0; }
+static inline int hisi_charger_type_notifier_unregister(
+               struct notifier_block *nb){return 0; }
+static inline enum hisi_charger_type hisi_get_charger_type(void)
+{
+       return CHARGER_TYPE_NONE;
+}
+
+static inline int hisi_usb_otg_event(enum otg_dev_event_type event_type)
+{
+       return 0;
+}
+
+static inline void hisi_usb_otg_bc_again(void)
+{
+}
+#endif /* CONFIG_USB_SUSB_HDRC || CONFIG_USB_DWC3 */
+
+static inline int hisi_usb_id_change(enum otg_dev_event_type event)
+{
+       if ((event == ID_FALL_EVENT) || (event == ID_RISE_EVENT))
+               return hisi_usb_otg_event(event);
+       else
+               return 0;
+}
+
+#endif /* _HISI_USB_H_*/
-- 
2.11.GIT


Reply via email to