UPSTREAM: usb: dwc2: Improve handling of host and device hwparams
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc2 / core.c
index ef73e498e98fc85501ff65cbeeaa20cd13ec9c4c..9dca83544b5e4bacc87aa567985c996605ec0b82 100644 (file)
@@ -481,64 +481,168 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
  * Do core a soft reset of the core.  Be careful with this because it
  * resets all the internal state machines of the core.
  */
-static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
+int dwc2_core_reset(struct dwc2_hsotg *hsotg)
 {
        u32 greset;
        int count = 0;
-       u32 gusbcfg;
 
        dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
-       /* Wait for AHB master IDLE state */
+       /* Core Soft Reset */
+       greset = dwc2_readl(hsotg->regs + GRSTCTL);
+       greset |= GRSTCTL_CSFTRST;
+       dwc2_writel(greset, hsotg->regs + GRSTCTL);
        do {
-               usleep_range(20000, 40000);
+               udelay(1);
                greset = dwc2_readl(hsotg->regs + GRSTCTL);
                if (++count > 50) {
                        dev_warn(hsotg->dev,
-                                "%s() HANG! AHB Idle GRSTCTL=%0x\n",
+                                "%s() HANG! Soft Reset GRSTCTL=%0x\n",
                                 __func__, greset);
                        return -EBUSY;
                }
-       } while (!(greset & GRSTCTL_AHBIDLE));
+       } while (greset & GRSTCTL_CSFTRST);
 
-       /* Core Soft Reset */
+       /* Wait for AHB master IDLE state */
        count = 0;
-       greset |= GRSTCTL_CSFTRST;
-       dwc2_writel(greset, hsotg->regs + GRSTCTL);
        do {
-               usleep_range(20000, 40000);
+               udelay(1);
                greset = dwc2_readl(hsotg->regs + GRSTCTL);
                if (++count > 50) {
                        dev_warn(hsotg->dev,
-                                "%s() HANG! Soft Reset GRSTCTL=%0x\n",
+                                "%s() HANG! AHB Idle GRSTCTL=%0x\n",
                                 __func__, greset);
                        return -EBUSY;
                }
-       } while (greset & GRSTCTL_CSFTRST);
+       } while (!(greset & GRSTCTL_AHBIDLE));
 
-       if (hsotg->dr_mode == USB_DR_MODE_HOST) {
-               gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-               gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
-               gusbcfg |= GUSBCFG_FORCEHOSTMODE;
-               dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
-       } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
-               gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-               gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
-               gusbcfg |= GUSBCFG_FORCEDEVMODE;
-               dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
-       } else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
-               gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-               gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
-               gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
-               dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
-       }
+       return 0;
+}
+
+/*
+ * Force the mode of the controller.
+ *
+ * Forcing the mode is needed for two cases:
+ *
+ * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
+ * controller to stay in a particular mode regardless of ID pin
+ * changes. We do this usually after a core reset.
+ *
+ * 2) During probe we want to read reset values of the hw
+ * configuration registers that are only available in either host or
+ * device mode. We may need to force the mode if the current mode does
+ * not allow us to access the register in the mode that we want.
+ *
+ * In either case it only makes sense to force the mode if the
+ * controller hardware is OTG capable.
+ *
+ * Checks are done in this function to determine whether doing a force
+ * would be valid or not.
+ *
+ * If a force is done, it requires a 25ms delay to take effect.
+ *
+ * Returns true if the mode was forced.
+ */
+static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+{
+       u32 gusbcfg;
+       u32 set;
+       u32 clear;
+
+       dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
+
+       /*
+        * Force mode has no effect if the hardware is not OTG.
+        */
+       if (!dwc2_hw_is_otg(hsotg))
+               return false;
+
+       /*
+        * If dr_mode is either peripheral or host only, there is no
+        * need to ever force the mode to the opposite mode.
+        */
+       if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
+               return false;
+
+       if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
+               return false;
+
+       gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+
+       set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
+       clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
+
+       /*
+        * If the force mode bit is already set, don't set it.
+        */
+       if ((gusbcfg & set) && !(gusbcfg & clear))
+               return false;
+
+       gusbcfg &= ~clear;
+       gusbcfg |= set;
+       dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
+
+       msleep(25);
+       return true;
+}
+
+/*
+ * Clears the force mode bits.
+ */
+static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+{
+       u32 gusbcfg;
+
+       gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+       gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+       gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+       dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
 
        /*
         * NOTE: This long sleep is _very_ important, otherwise the core will
         * not stay in host mode after a connector ID change!
         */
-       usleep_range(150000, 200000);
+       usleep_range(150000, 160000);
+}
+
+/*
+ * Sets or clears force mode based on the dr_mode parameter.
+ */
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+       switch (hsotg->dr_mode) {
+       case USB_DR_MODE_HOST:
+               dwc2_force_mode(hsotg, true);
+               break;
+       case USB_DR_MODE_PERIPHERAL:
+               dwc2_force_mode(hsotg, false);
+               break;
+       case USB_DR_MODE_OTG:
+               dwc2_clear_force_mode(hsotg);
+               break;
+       default:
+               dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
+                        __func__, hsotg->dr_mode);
+               break;
+       }
+}
+
+/*
+ * Do core a soft reset of the core.  Be careful with this because it
+ * resets all the internal state machines of the core.
+ *
+ * Additionally this will apply force mode as per the hsotg->dr_mode
+ * parameter.
+ */
+int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+       int retval;
 
+       retval = dwc2_core_reset(hsotg);
+       if (retval)
+               return retval;
+
+       dwc2_force_dr_mode(hsotg);
        return 0;
 }
 
@@ -553,16 +657,20 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
         */
        if (select_phy) {
                dev_dbg(hsotg->dev, "FS PHY selected\n");
+
                usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-               usbcfg |= GUSBCFG_PHYSEL;
-               dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+               if (!(usbcfg & GUSBCFG_PHYSEL)) {
+                       usbcfg |= GUSBCFG_PHYSEL;
+                       dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
 
-               /* Reset after a PHY select */
-               retval = dwc2_core_reset(hsotg);
-               if (retval) {
-                       dev_err(hsotg->dev, "%s() Reset failed, aborting",
-                                       __func__);
-                       return retval;
+                       /* Reset after a PHY select */
+                       retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+
+                       if (retval) {
+                               dev_err(hsotg->dev,
+                                       "%s: Reset failed, aborting", __func__);
+                               return retval;
+                       }
                }
        }
 
@@ -597,13 +705,13 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 
 static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 {
-       u32 usbcfg;
+       u32 usbcfg, usbcfg_old;
        int retval = 0;
 
        if (!select_phy)
                return 0;
 
-       usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+       usbcfg = usbcfg_old = dwc2_readl(hsotg->regs + GUSBCFG);
 
        /*
         * HS PHY parameters. These parameters are preserved during soft reset
@@ -631,14 +739,16 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
                break;
        }
 
-       dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+       if (usbcfg != usbcfg_old) {
+               dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
 
-       /* Reset after setting the PHY parameters */
-       retval = dwc2_core_reset(hsotg);
-       if (retval) {
-               dev_err(hsotg->dev, "%s() Reset failed, aborting",
-                               __func__);
-               return retval;
+               /* Reset after setting the PHY parameters */
+               retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+               if (retval) {
+                       dev_err(hsotg->dev,
+                               "%s: Reset failed, aborting", __func__);
+                       return retval;
+               }
        }
 
        return retval;
@@ -765,11 +875,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
  * dwc2_core_init() - Initializes the DWC_otg controller registers and
  * prepares the core for device mode or host mode operation
  *
- * @hsotg:      Programming view of the DWC_otg controller
- * @select_phy: If true then also set the Phy type
- * @irq:        If >= 0, the irq to register
+ * @hsotg:         Programming view of the DWC_otg controller
+ * @initial_setup: If true then this is the first init for this instance.
  */
-int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
 {
        u32 usbcfg, otgctl;
        int retval;
@@ -791,18 +900,26 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
 
        dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
 
-       /* Reset the Controller */
-       retval = dwc2_core_reset(hsotg);
-       if (retval) {
-               dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
-                               __func__);
-               return retval;
+       /*
+        * Reset the Controller
+        *
+        * We only need to reset the controller if this is a re-init.
+        * For the first init we know for sure that earlier code reset us (it
+        * needed to in order to properly detect various parameters).
+        */
+       if (!initial_setup) {
+               retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+               if (retval) {
+                       dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
+                                       __func__);
+                       return retval;
+               }
        }
 
        /*
         * This needs to happen in FS mode before any other programming occurs
         */
-       retval = dwc2_phy_init(hsotg, select_phy);
+       retval = dwc2_phy_init(hsotg, initial_setup);
        if (retval)
                return retval;
 
@@ -1707,6 +1824,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
        u32 hcchar;
        u32 hctsiz = 0;
        u16 num_packets;
+       u32 ec_mc;
 
        if (dbg_hc(chan))
                dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -1743,6 +1861,13 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 
                hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
                          TSIZ_XFERSIZE_MASK;
+
+               /* For split set ec_mc for immediate retries */
+               if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+                   chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+                       ec_mc = 3;
+               else
+                       ec_mc = 1;
        } else {
                if (dbg_hc(chan))
                        dev_vdbg(hsotg->dev, "no split\n");
@@ -1805,6 +1930,9 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 
                hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
                          TSIZ_XFERSIZE_MASK;
+
+               /* The ec_mc gets the multi_count for non-split */
+               ec_mc = chan->multi_count;
        }
 
        chan->start_pkt_count = num_packets;
@@ -1855,8 +1983,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 
        hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
        hcchar &= ~HCCHAR_MULTICNT_MASK;
-       hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
-                 HCCHAR_MULTICNT_MASK;
+       hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
        dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
 
        if (hcchar & HCCHAR_CHDIS)
@@ -1905,7 +2032,6 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
                                 struct dwc2_host_chan *chan)
 {
        u32 hcchar;
-       u32 hc_dma;
        u32 hctsiz = 0;
 
        if (chan->do_ping)
@@ -1934,14 +2060,14 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
 
        dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
 
-       hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK;
+       dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr,
+                                  chan->desc_list_sz, DMA_TO_DEVICE);
+
+       dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num));
 
-       /* Always start from first descriptor */
-       hc_dma &= ~HCDMA_CTD_MASK;
-       dwc2_writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num));
        if (dbg_hc(chan))
-               dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n",
-                        hc_dma, chan->hc_num);
+               dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
+                        &chan->desc_list_addr, chan->hc_num);
 
        hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
        hcchar &= ~HCCHAR_MULTICNT_MASK;
@@ -2485,6 +2611,29 @@ void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val)
        hsotg->core_params->dma_desc_enable = val;
 }
 
+void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
+{
+       int valid = 1;
+
+       if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
+                       !hsotg->hw_params.dma_desc_enable))
+               valid = 0;
+       if (val < 0)
+               valid = 0;
+
+       if (!valid) {
+               if (val >= 0)
+                       dev_err(hsotg->dev,
+                               "%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
+                               val);
+               val = (hsotg->core_params->dma_enable > 0 &&
+                       hsotg->hw_params.dma_desc_enable);
+       }
+
+       hsotg->core_params->dma_desc_fs_enable = val;
+       dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
+}
+
 void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
                                                 int val)
 {
@@ -3016,6 +3165,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
        dwc2_set_param_otg_cap(hsotg, params->otg_cap);
        dwc2_set_param_dma_enable(hsotg, params->dma_enable);
        dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
+       dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
        dwc2_set_param_host_support_fs_ls_low_power(hsotg,
                        params->host_support_fs_ls_low_power);
        dwc2_set_param_enable_dynamic_fifo(hsotg,
@@ -3052,17 +3202,93 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
        dwc2_set_param_hibernation(hsotg, params->hibernation);
 }
 
+/*
+ * Forces either host or device mode if the controller is not
+ * currently in that mode.
+ *
+ * Returns true if the mode was forced.
+ */
+static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
+{
+       if (host && dwc2_is_host_mode(hsotg))
+               return false;
+       else if (!host && dwc2_is_device_mode(hsotg))
+               return false;
+
+       return dwc2_force_mode(hsotg, host);
+}
+
+/*
+ * Gets host hardware parameters. Forces host mode if not currently in
+ * host mode. Should be called immediately after a core soft reset in
+ * order to get the reset values.
+ */
+static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       u32 gnptxfsiz;
+       u32 hptxfsiz;
+       bool forced;
+
+       if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+               return;
+
+       forced = dwc2_force_mode_if_needed(hsotg, true);
+
+       gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+       hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
+       dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+       dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
+
+       if (forced)
+               dwc2_clear_force_mode(hsotg);
+
+       hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                      FIFOSIZE_DEPTH_SHIFT;
+       hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                     FIFOSIZE_DEPTH_SHIFT;
+}
+
+/*
+ * Gets device hardware parameters. Forces device mode if not
+ * currently in device mode. Should be called immediately after a core
+ * soft reset in order to get the reset values.
+ */
+static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+       bool forced;
+       u32 gnptxfsiz;
+
+       if (hsotg->dr_mode == USB_DR_MODE_HOST)
+               return;
+
+       forced = dwc2_force_mode_if_needed(hsotg, false);
+
+       gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+       dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+
+       if (forced)
+               dwc2_clear_force_mode(hsotg);
+
+       hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+                                      FIFOSIZE_DEPTH_SHIFT;
+}
+
 /**
  * During device initialization, read various hardware configuration
  * registers and interpret the contents.
+ *
+ * This should be called during driver probe. It will perform a core
+ * soft reset in order to get the reset values of the parameters.
  */
 int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_hw_params *hw = &hsotg->hw_params;
        unsigned width;
        u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
-       u32 hptxfsiz, grxfsiz, gnptxfsiz;
-       u32 gusbcfg;
+       u32 grxfsiz;
+       int retval;
 
        /*
         * Attempt to ensure this device is really a DWC_otg Controller.
@@ -3082,6 +3308,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
                hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
                hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
 
+       retval = dwc2_core_reset(hsotg);
+       if (retval)
+               return retval;
+
        hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1);
        hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
        hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3);
@@ -3094,20 +3324,16 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
        dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
        dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
 
-       /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
-       gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-       gusbcfg |= GUSBCFG_FORCEHOSTMODE;
-       dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
-       usleep_range(100000, 150000);
+       /*
+        * Host specific hardware parameters. Reading these parameters
+        * requires the controller to be in host mode. The mode will
+        * be forced, if necessary, to read these values.
+        */
+       dwc2_get_host_hwparams(hsotg);
+       dwc2_get_dev_hwparams(hsotg);
 
-       gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
-       hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
-       dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
-       dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
-       gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-       gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
-       dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
-       usleep_range(100000, 150000);
+       /* hwcfg1 */
+       hw->dev_ep_dirs = hwcfg1;
 
        /* hwcfg2 */
        hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
@@ -3163,10 +3389,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
        /* fifo sizes */
        hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
                                GRXFSIZ_DEPTH_SHIFT;
-       hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
-                                      FIFOSIZE_DEPTH_SHIFT;
-       hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
-                                     FIFOSIZE_DEPTH_SHIFT;
 
        dev_dbg(hsotg->dev, "Detected values from hardware:\n");
        dev_dbg(hsotg->dev, "  op_mode=%d\n",
@@ -3275,6 +3497,43 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
        dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
 }
 
+/* Returns the controller's GHWCFG2.OTG_MODE. */
+unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
+{
+       u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
+
+       return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+               GHWCFG2_OP_MODE_SHIFT;
+}
+
+/* Returns true if the controller is capable of DRD. */
+bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
+{
+       unsigned op_mode = dwc2_op_mode(hsotg);
+
+       return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
+               (op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
+}
+
+/* Returns true if the controller is host-only. */
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
+{
+       unsigned op_mode = dwc2_op_mode(hsotg);
+
+       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
+}
+
+/* Returns true if the controller is device-only. */
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
+{
+       unsigned op_mode = dwc2_op_mode(hsotg);
+
+       return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
+               (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
+}
+
 MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
 MODULE_AUTHOR("Synopsys, Inc.");
 MODULE_LICENSE("Dual BSD/GPL");