OSDN Git Service

Merge tag 'v4.4.214' into 10
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / usb / dwc3 / core.c
index 591bc3f..a6716e1 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/of.h>
 #include <linux/acpi.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/irq.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
 /* -------------------------------------------------------------------------- */
 
+void dwc3_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
+{
+       u32                     reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+
+       if (suspend)
+               reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+       else
+               reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
+       dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+}
+
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
 {
        u32 reg;
@@ -57,35 +72,74 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
        reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
        reg |= DWC3_GCTL_PRTCAPDIR(mode);
+       /*
+        * Set this bit so that device attempts three more times at SS, even
+        * if it failed previously to operate in SS mode.
+        */
+       reg |= DWC3_GCTL_U2RSTECN;
+       reg &= ~(DWC3_GCTL_SOFITPSYNC);
+       reg &= ~(DWC3_GCTL_PWRDNSCALEMASK);
+       reg |= DWC3_GCTL_PWRDNSCALE(2);
+       reg |= DWC3_GCTL_U2EXIT_LFPS;
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+       if (mode == DWC3_GCTL_PRTCAP_OTG || mode == DWC3_GCTL_PRTCAP_HOST) {
+               /*
+                * Allow ITP generated off of ref clk based counter instead
+                * of UTMI/ULPI clk based counter, when superspeed only is
+                * active so that UTMI/ULPI PHY can be suspened.
+                *
+                * Starting with revision 2.50A, GFLADJ_REFCLK_LPM_SEL is used
+                * instead.
+                */
+               if (dwc->revision < DWC3_REVISION_250A) {
+                       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+                       reg |= DWC3_GCTL_SOFITPSYNC;
+                       dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+               } else {
+                       reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+                       reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
+                       dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+               }
+       }
 }
 
 /**
- * dwc3_core_soft_reset - Issues core soft reset and PHY reset
+ * Peforms initialization of HS and SS PHYs.
+ * If used as a part of POR or init sequence it is recommended
+ * that we should perform hard reset of the PHYs prior to invoking
+ * this function.
  * @dwc: pointer to our context structure
- */
-static int dwc3_core_soft_reset(struct dwc3 *dwc)
+*/
+static int dwc3_init_usb_phys(struct dwc3 *dwc)
 {
-       u32             reg;
        int             ret;
 
-       /* Before Resetting PHY, put Core in Reset */
-       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
-       reg |= DWC3_GCTL_CORESOFTRESET;
-       dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+       /* Bring up PHYs */
+       ret = usb_phy_init(dwc->usb2_phy);
+       if (ret) {
+               pr_err("%s: usb_phy_init(dwc->usb2_phy) returned %d\n",
+                               __func__, ret);
+               return ret;
+       }
 
-       /* Assert USB3 PHY reset */
-       reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
-       reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
-       dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       if (dwc->maximum_speed == USB_SPEED_HIGH)
+               goto generic_phy_init;
 
-       /* Assert USB2 PHY reset */
-       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
-       reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
-       dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+       ret = usb_phy_init(dwc->usb3_phy);
+       if (ret == -EBUSY) {
+               /*
+                * Setting Max speed as high when USB3 PHY initialiation
+                * is failing and USB superspeed can't be supported.
+                */
+               dwc->maximum_speed = USB_SPEED_HIGH;
+       } else if (ret) {
+               pr_err("%s: usb_phy_init(dwc->usb3_phy) returned %d\n",
+                               __func__, ret);
+               return ret;
+       }
 
-       usb_phy_init(dwc->usb2_phy);
-       usb_phy_init(dwc->usb3_phy);
+generic_phy_init:
        ret = phy_init(dwc->usb2_generic_phy);
        if (ret < 0)
                return ret;
@@ -95,24 +149,45 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
                phy_exit(dwc->usb2_generic_phy);
                return ret;
        }
-       mdelay(100);
 
-       /* Clear USB3 PHY reset */
+       return 0;
+}
+
+/**
+ * dwc3_core_reset - Issues core soft reset and PHY reset
+ * @dwc: pointer to our context structure
+ */
+static int dwc3_core_reset(struct dwc3 *dwc)
+{
+       int             ret;
+       u32     reg;
+
+       /* Reset PHYs */
+       usb_phy_reset(dwc->usb2_phy);
+
+       if (dwc->maximum_speed == USB_SPEED_SUPER)
+               usb_phy_reset(dwc->usb3_phy);
+
+       /* Initialize PHYs */
+       ret = dwc3_init_usb_phys(dwc);
+       if (ret) {
+               pr_err("%s: dwc3_init_phys returned %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
        reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
-       reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
-       dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       reg &= ~DWC3_GUSB3PIPECTL_DELAYP1TRANS;
 
-       /* Clear USB2 PHY reset */
-       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
-       reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
-       dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+       /* core exits U1/U2/U3 only in PHY power state P1/P2/P3 respectively */
+       if (dwc->revision <= DWC3_REVISION_310A)
+               reg |= DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX;
 
-       mdelay(100);
+       dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
 
-       /* After PHYs are stable we can take Core out of reset state */
-       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
-       reg &= ~DWC3_GCTL_CORESOFTRESET;
-       dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+       dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT, 0);
+
+       dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT, 0);
 
        return 0;
 }
@@ -190,7 +265,7 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
  * otherwise ERR_PTR(errno).
  */
 static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
-               unsigned length)
+               unsigned length, enum event_buf_type type)
 {
        struct dwc3_event_buffer        *evt;
 
@@ -200,6 +275,7 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
 
        evt->dwc        = dwc;
        evt->length     = length;
+       evt->type       = type;
        evt->buf        = dma_alloc_coherent(dwc->dev, length,
                        &evt->dma, GFP_KERNEL);
        if (!evt->buf)
@@ -234,26 +310,40 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
  */
 static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
 {
-       int                     num;
-       int                     i;
+       int     i;
+       int     j = 0;
 
-       num = DWC3_NUM_INT(dwc->hwparams.hwparams1);
-       dwc->num_event_buffers = num;
+       dwc->num_event_buffers = dwc->num_normal_event_buffers +
+               dwc->num_gsi_event_buffers;
 
-       dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
+       dwc->ev_buffs = devm_kzalloc(dwc->dev,
+                       sizeof(*dwc->ev_buffs) * dwc->num_event_buffers,
                        GFP_KERNEL);
        if (!dwc->ev_buffs)
                return -ENOMEM;
 
-       for (i = 0; i < num; i++) {
+       for (i = 0; i < dwc->num_normal_event_buffers; i++) {
                struct dwc3_event_buffer        *evt;
 
-               evt = dwc3_alloc_one_event_buffer(dwc, length);
+               evt = dwc3_alloc_one_event_buffer(dwc, length,
+                               EVT_BUF_TYPE_NORMAL);
                if (IS_ERR(evt)) {
                        dev_err(dwc->dev, "can't allocate event buffer\n");
                        return PTR_ERR(evt);
                }
-               dwc->ev_buffs[i] = evt;
+               dwc->ev_buffs[j++] = evt;
+       }
+
+       for (i = 0; i < dwc->num_gsi_event_buffers; i++) {
+               struct dwc3_event_buffer        *evt;
+
+               evt = dwc3_alloc_one_event_buffer(dwc, length,
+                               EVT_BUF_TYPE_GSI);
+               if (IS_ERR(evt)) {
+                       dev_err(dwc->dev, "can't allocate event buffer\n");
+                       return PTR_ERR(evt);
+               }
+               dwc->ev_buffs[j++] = evt;
        }
 
        return 0;
@@ -265,25 +355,40 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
 {
        struct dwc3_event_buffer        *evt;
        int                             n;
 
        for (n = 0; n < dwc->num_event_buffers; n++) {
                evt = dwc->ev_buffs[n];
-               dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
+               dev_dbg(dwc->dev, "Event buf %pK dma %08llx length %d\n",
                                evt->buf, (unsigned long long) evt->dma,
                                evt->length);
 
+               memset(evt->buf, 0, evt->length);
+
                evt->lpos = 0;
 
                dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
                                lower_32_bits(evt->dma));
-               dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
-                               upper_32_bits(evt->dma));
-               dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
-                               DWC3_GEVNTSIZ_SIZE(evt->length));
+
+               if (evt->type == EVT_BUF_TYPE_NORMAL) {
+                       dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
+                                       upper_32_bits(evt->dma));
+                       dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
+                                       DWC3_GEVNTSIZ_SIZE(evt->length));
+               } else {
+                       dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
+                               DWC3_GEVNTADRHI_EVNTADRHI_GSI_EN(
+                                       DWC3_GEVENT_TYPE_GSI) |
+                               DWC3_GEVNTADRHI_EVNTADRHI_GSI_IDX(n));
+
+                       dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
+                               DWC3_GEVNTCOUNT_EVNTINTRPTMASK |
+                               ((evt->length) & 0xffff));
+               }
+
                dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
        }
 
@@ -529,7 +634,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int dwc3_core_init(struct dwc3 *dwc)
+int dwc3_core_init(struct dwc3 *dwc)
 {
        u32                     hwparams4 = dwc->hwparams.hwparams4;
        u32                     reg;
@@ -559,16 +664,28 @@ static int dwc3_core_init(struct dwc3 *dwc)
        /* Handle USB2.0-only core configuration */
        if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
                        DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
-               if (dwc->maximum_speed == USB_SPEED_SUPER)
-                       dwc->maximum_speed = USB_SPEED_HIGH;
+               if (dwc->max_hw_supp_speed == USB_SPEED_SUPER) {
+                       dwc->max_hw_supp_speed = USB_SPEED_HIGH;
+                       dwc->maximum_speed = dwc->max_hw_supp_speed;
+               }
        }
 
-       /* issue device SoftReset too */
-       ret = dwc3_soft_reset(dwc);
+       /*
+        * Workaround for STAR 9000961433 which affects only version
+        * 3.00a of the DWC_usb3 core. This prevents the controller
+        * interrupt from being masked while handling events. IMOD
+        * allows us to work around this issue. Enable it for the
+        * affected version.
+        */
+        if (!dwc->imod_interval && (dwc->revision == DWC3_REVISION_300A))
+               dwc->imod_interval = 1;
+
+       ret = dwc3_core_reset(dwc);
        if (ret)
                goto err0;
 
-       ret = dwc3_core_soft_reset(dwc);
+       /* issue device SoftReset too */
+       ret = dwc3_soft_reset(dwc);
        if (ret)
                goto err0;
 
@@ -639,6 +756,15 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
        dwc3_core_num_eps(dwc);
 
+       /*
+        * Disable clock gating to work around a known HW bug that causes the
+        * internal RAM clock to get stuck when entering low power modes.
+        */
+       if (dwc->disable_clk_gating) {
+               dev_dbg(dwc->dev, "Disabling controller clock gating.\n");
+               reg |= DWC3_GCTL_DSBLCLKGTNG;
+       }
+
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 
        ret = dwc3_alloc_scratch_buffers(dwc);
@@ -649,6 +775,17 @@ static int dwc3_core_init(struct dwc3 *dwc)
        if (ret)
                goto err2;
 
+       /*
+        * clear Elastic buffer mode in GUSBPIPE_CTRL(0) register, otherwise
+        * it results in high link errors and could cause SS mode transfer
+        * failure.
+        */
+       if (!dwc->nominal_elastic_buffer) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+               reg &= ~DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE;
+               dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       }
+
        return 0;
 
 err2:
@@ -743,38 +880,16 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
 static int dwc3_core_init_mode(struct dwc3 *dwc)
 {
        struct device *dev = dwc->dev;
-       int ret;
 
        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
-               ret = dwc3_gadget_init(dwc);
-               if (ret) {
-                       dev_err(dev, "failed to initialize gadget\n");
-                       return ret;
-               }
                break;
        case USB_DR_MODE_HOST:
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
-               ret = dwc3_host_init(dwc);
-               if (ret) {
-                       dev_err(dev, "failed to initialize host\n");
-                       return ret;
-               }
                break;
        case USB_DR_MODE_OTG:
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
-               ret = dwc3_host_init(dwc);
-               if (ret) {
-                       dev_err(dev, "failed to initialize host\n");
-                       return ret;
-               }
-
-               ret = dwc3_gadget_init(dwc);
-               if (ret) {
-                       dev_err(dev, "failed to initialize gadget\n");
-                       return ret;
-               }
                break;
        default:
                dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
@@ -806,8 +921,107 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
        dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
 }
 
+/* XHCI reset, resets other CORE registers as well, re-init those */
+void dwc3_post_host_reset_core_init(struct dwc3 *dwc)
+{
+       dwc3_core_init(dwc);
+       dwc3_gadget_restart(dwc);
+}
+
+static void (*notify_event)(struct dwc3 *, unsigned, unsigned);
+void dwc3_set_notifier(void (*notify)(struct dwc3 *, unsigned, unsigned))
+{
+       notify_event = notify;
+}
+EXPORT_SYMBOL(dwc3_set_notifier);
+
+int dwc3_notify_event(struct dwc3 *dwc, unsigned event, unsigned value)
+{
+       int ret = 0;
+
+       if (dwc->notify_event)
+               dwc->notify_event(dwc, event, value);
+       else
+               ret = -ENODEV;
+
+       return ret;
+}
+EXPORT_SYMBOL(dwc3_notify_event);
+
+int dwc3_core_pre_init(struct dwc3 *dwc)
+{
+       int ret;
+
+       dwc3_cache_hwparams(dwc);
+
+       ret = dwc3_phy_setup(dwc);
+       if (ret)
+               goto err0;
+
+       if (!dwc->ev_buffs) {
+               ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
+               if (ret) {
+                       dev_err(dwc->dev, "failed to allocate event buffers\n");
+                       ret = -ENOMEM;
+                       goto err1;
+               }
+       }
+
+       ret = dwc3_core_init(dwc);
+       if (ret) {
+               dev_err(dwc->dev, "failed to initialize core\n");
+               goto err2;
+       }
+
+       ret = phy_power_on(dwc->usb2_generic_phy);
+       if (ret < 0)
+               goto err3;
+
+       ret = phy_power_on(dwc->usb3_generic_phy);
+       if (ret < 0)
+               goto err4;
+
+       ret = dwc3_event_buffers_setup(dwc);
+       if (ret) {
+               dev_err(dwc->dev, "failed to setup event buffers\n");
+               goto err5;
+       }
+
+       ret = dwc3_core_init_mode(dwc);
+       if (ret) {
+               dev_err(dwc->dev, "failed to set mode with dwc3 core\n");
+               goto err6;
+       }
+
+       return ret;
+
+err6:
+       dwc3_event_buffers_cleanup(dwc);
+err5:
+       phy_power_off(dwc->usb3_generic_phy);
+err4:
+       phy_power_off(dwc->usb2_generic_phy);
+err3:
+       dwc3_core_exit(dwc);
+err2:
+       dwc3_free_event_buffers(dwc);
+err1:
+       dwc3_ulpi_exit(dwc);
+err0:
+       return ret;
+}
+
 #define DWC3_ALIGN_MASK                (16 - 1)
 
+/* check whether the core supports IMOD */
+bool dwc3_has_imod(struct dwc3 *dwc)
+{
+       return ((dwc3_is_usb3(dwc) &&
+               dwc->revision >= DWC3_REVISION_300A) ||
+               (dwc3_is_usb31(dwc) &&
+               dwc->revision >= DWC3_USB31_REVISION_120A));
+}
+
 static int dwc3_probe(struct platform_device *pdev)
 {
        struct device           *dev = &pdev->dev;
@@ -818,7 +1032,8 @@ static int dwc3_probe(struct platform_device *pdev)
        u8                      tx_de_emphasis;
        u8                      hird_threshold;
        u32                     fladj = 0;
-
+       u32                     num_evt_buffs;
+       int                     irq;
        int                     ret;
 
        void __iomem            *regs;
@@ -832,6 +1047,7 @@ static int dwc3_probe(struct platform_device *pdev)
        dwc->mem = mem;
        dwc->dev = dev;
 
+       dwc->notify_event = notify_event;
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!res) {
                dev_err(dev, "missing IRQ\n");
@@ -842,12 +1058,27 @@ static int dwc3_probe(struct platform_device *pdev)
        dwc->xhci_resources[1].flags = res->flags;
        dwc->xhci_resources[1].name = res->name;
 
+       irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+
+       /* will be enabled in dwc3_msm_resume() */
+       irq_set_status_flags(irq, IRQ_NOAUTOEN);
+       ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3",
+                       dwc);
+       if (ret) {
+               dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+                               irq, ret);
+               return -ENODEV;
+       }
+
+       dwc->irq = irq;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "missing memory resource\n");
                return -ENODEV;
        }
 
+       dwc->reg_phys = res->start;
        dwc->xhci_resources[0].start = res->start;
        dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
                                        DWC3_XHCI_REGS_END;
@@ -882,6 +1113,7 @@ static int dwc3_probe(struct platform_device *pdev)
        hird_threshold = 12;
 
        dwc->maximum_speed = usb_get_maximum_speed(dev);
+       dwc->max_hw_supp_speed = dwc->maximum_speed;
        dwc->dr_mode = usb_get_dr_mode(dev);
 
        dwc->has_lpm_erratum = device_property_read_bool(dev,
@@ -930,8 +1162,32 @@ static int dwc3_probe(struct platform_device *pdev)
        device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
                                 &fladj);
 
+       dwc->nominal_elastic_buffer = device_property_read_bool(dev,
+                               "snps,nominal-elastic-buffer");
+       dwc->usb3_u1u2_disable = device_property_read_bool(dev,
+                               "snps,usb3-u1u2-disable");
+       dwc->disable_clk_gating = device_property_read_bool(dev,
+                               "snps,disable-clk-gating");
+       dwc->enable_bus_suspend = device_property_read_bool(dev,
+                               "snps,bus-suspend-enable");
+
+       dwc->num_normal_event_buffers = 1;
+       ret = device_property_read_u32(dev,
+               "snps,num-normal-evt-buffs", &num_evt_buffs);
+       if (!ret)
+               dwc->num_normal_event_buffers = num_evt_buffs;
+
+       ret = device_property_read_u32(dev,
+               "snps,num-gsi-evt-buffs", &dwc->num_gsi_event_buffers);
+
+       if (dwc->enable_bus_suspend) {
+               pm_runtime_set_autosuspend_delay(dev, 500);
+               pm_runtime_use_autosuspend(dev);
+       }
+
        if (pdata) {
                dwc->maximum_speed = pdata->maximum_speed;
+               dwc->max_hw_supp_speed = dwc->maximum_speed;
                dwc->has_lpm_erratum = pdata->has_lpm_erratum;
                if (pdata->lpm_nyet_threshold)
                        lpm_nyet_threshold = pdata->lpm_nyet_threshold;
@@ -965,7 +1221,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
        /* default to superspeed if no maximum_speed passed */
        if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
-               dwc->maximum_speed = USB_SPEED_SUPER;
+               dwc->max_hw_supp_speed = dwc->maximum_speed = USB_SPEED_SUPER;
 
        dwc->lpm_nyet_threshold = lpm_nyet_threshold;
        dwc->tx_de_emphasis = tx_de_emphasis;
@@ -973,104 +1229,84 @@ static int dwc3_probe(struct platform_device *pdev)
        dwc->hird_threshold = hird_threshold
                | (dwc->is_utmi_l1_suspend << 4);
 
+       init_waitqueue_head(&dwc->wait_linkstate);
        platform_set_drvdata(pdev, dwc);
-       dwc3_cache_hwparams(dwc);
-
-       ret = dwc3_phy_setup(dwc);
-       if (ret)
-               goto err0;
-
        ret = dwc3_core_get_phy(dwc);
        if (ret)
                goto err0;
 
        spin_lock_init(&dwc->lock);
 
-       if (!dev->dma_mask) {
-               dev->dma_mask = dev->parent->dma_mask;
-               dev->dma_parms = dev->parent->dma_parms;
-               dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+       dev->dma_mask   = dev->parent->dma_mask;
+       dev->dma_parms  = dev->parent->dma_parms;
+       dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+
+       dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI);
+       if (!dwc->dwc_wq) {
+               pr_err("%s: Unable to create workqueue dwc_wq\n", __func__);
+               return -ENOMEM;
        }
 
+       INIT_WORK(&dwc->bh_work, dwc3_bh_work);
+
+       pm_runtime_no_callbacks(dev);
+       pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
-       pm_runtime_get_sync(dev);
        pm_runtime_forbid(dev);
 
-       ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
-       if (ret) {
-               dev_err(dwc->dev, "failed to allocate event buffers\n");
-               ret = -ENOMEM;
-               goto err1;
-       }
-
        if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
                dwc->dr_mode = USB_DR_MODE_HOST;
        else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
                dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
 
-       if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+       if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) {
                dwc->dr_mode = USB_DR_MODE_OTG;
-
-       ret = dwc3_core_init(dwc);
-       if (ret) {
-               dev_err(dev, "failed to initialize core\n");
-               goto err1;
+               dwc->is_drd = true;
        }
 
        /* Adjust Frame Length */
        dwc3_frame_length_adjustment(dwc, fladj);
 
-       usb_phy_set_suspend(dwc->usb2_phy, 0);
-       usb_phy_set_suspend(dwc->usb3_phy, 0);
-       ret = phy_power_on(dwc->usb2_generic_phy);
-       if (ret < 0)
-               goto err2;
-
-       ret = phy_power_on(dwc->usb3_generic_phy);
-       if (ret < 0)
-               goto err3;
+       /* Hardcode number of eps */
+       dwc->num_in_eps = 16;
+       dwc->num_out_eps = 16;
 
-       ret = dwc3_event_buffers_setup(dwc);
-       if (ret) {
-               dev_err(dwc->dev, "failed to setup event buffers\n");
-               goto err4;
+       if (dwc->dr_mode == USB_DR_MODE_OTG ||
+               dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
+               ret = dwc3_gadget_init(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to initialize gadget\n");
+                       goto err0;
+               }
        }
 
-       ret = dwc3_core_init_mode(dwc);
-       if (ret)
-               goto err5;
+       if (dwc->dr_mode == USB_DR_MODE_OTG ||
+               dwc->dr_mode ==  USB_DR_MODE_HOST) {
+               ret = dwc3_host_init(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to initialize host\n");
+                       goto err_gadget;
+               }
+       }
 
        ret = dwc3_debugfs_init(dwc);
        if (ret) {
                dev_err(dev, "failed to initialize debugfs\n");
-               goto err6;
+               goto err_host;
        }
 
        pm_runtime_allow(dev);
 
        return 0;
 
-err6:
-       dwc3_core_exit_mode(dwc);
-
-err5:
-       dwc3_event_buffers_cleanup(dwc);
-
-err4:
-       phy_power_off(dwc->usb3_generic_phy);
-
-err3:
-       phy_power_off(dwc->usb2_generic_phy);
-
-err2:
-       usb_phy_set_suspend(dwc->usb2_phy, 1);
-       usb_phy_set_suspend(dwc->usb3_phy, 1);
-       dwc3_core_exit(dwc);
-
-err1:
-       dwc3_free_event_buffers(dwc);
-       dwc3_ulpi_exit(dwc);
-
+err_host:
+       if (dwc->dr_mode == USB_DR_MODE_OTG ||
+               dwc->dr_mode ==  USB_DR_MODE_HOST)
+               dwc3_host_exit(dwc);
+err_gadget:
+       if (dwc->dr_mode == USB_DR_MODE_OTG ||
+               dwc->dr_mode == USB_DR_MODE_PERIPHERAL)
+               dwc3_gadget_exit(dwc);
 err0:
        /*
         * restore res->start back to its original value so that, in case the
@@ -1078,6 +1314,7 @@ err0:
         * memory region the next time probe is called.
         */
        res->start -= DWC3_GLOBALS_REGS_START;
+       destroy_workqueue(dwc->dwc_wq);
 
        return ret;
 }
@@ -1099,14 +1336,14 @@ static int dwc3_remove(struct platform_device *pdev)
        dwc3_event_buffers_cleanup(dwc);
        dwc3_free_event_buffers(dwc);
 
-       usb_phy_set_suspend(dwc->usb2_phy, 1);
-       usb_phy_set_suspend(dwc->usb3_phy, 1);
        phy_power_off(dwc->usb2_generic_phy);
        phy_power_off(dwc->usb3_generic_phy);
 
        dwc3_core_exit(dwc);
        dwc3_ulpi_exit(dwc);
 
+       destroy_workqueue(dwc->dwc_wq);
+
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
@@ -1119,6 +1356,10 @@ static int dwc3_suspend(struct device *dev)
        struct dwc3     *dwc = dev_get_drvdata(dev);
        unsigned long   flags;
 
+       /* Check if platform glue driver handling PM, if not then handle here */
+       if (!dwc3_notify_event(dwc, DWC3_CORE_PM_SUSPEND_EVENT, 0))
+               return 0;
+
        spin_lock_irqsave(&dwc->lock, flags);
 
        switch (dwc->dr_mode) {
@@ -1151,6 +1392,10 @@ static int dwc3_resume(struct device *dev)
        unsigned long   flags;
        int             ret;
 
+       /* Check if platform glue driver handling PM, if not then handle here */
+       if (!dwc3_notify_event(dwc, DWC3_CORE_PM_RESUME_EVENT, 0))
+               return 0;
+
        pinctrl_pm_select_default_state(dev);
 
        usb_phy_init(dwc->usb3_phy);
@@ -1193,8 +1438,26 @@ err_usb2phy_init:
        return ret;
 }
 
+static int dwc3_pm_restore(struct device *dev)
+{
+       /*
+        * Set the core as runtime active to prevent the runtime
+        * PM ops being called before the PM restore is completed.
+        */
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       return 0;
+}
+
 static const struct dev_pm_ops dwc3_dev_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+       .suspend        = dwc3_suspend,
+       .resume         = dwc3_resume,
+       .freeze         = dwc3_suspend,
+       .thaw           = dwc3_pm_restore,
+       .poweroff       = dwc3_suspend,
+       .restore        = dwc3_pm_restore,
 };
 
 #define DWC3_PM_OPS    &(dwc3_dev_pm_ops)