]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/dwc2/core.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[karo-tx-linux.git] / drivers / usb / dwc2 / core.c
index b700a47e026a6e406b1a2ff6e635ea42d7c4f4c4..39a0fa8a4c0aea17b9ecf330c127b110b4b54ba4 100644 (file)
@@ -519,6 +519,114 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
        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!
+        */
+       msleep(25);
+}
+
+/*
+ * 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.
@@ -529,35 +637,12 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
 int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
 {
        int retval;
-       u32 gusbcfg;
 
        retval = dwc2_core_reset(hsotg);
        if (retval)
                return retval;
 
-       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);
-       }
-
-       /*
-        * NOTE: This long sleep is _very_ important, otherwise the core will
-        * not stay in host mode after a connector ID change!
-        */
-       usleep_range(150000, 160000);
-
+       dwc2_force_dr_mode(hsotg);
        return 0;
 }
 
@@ -3117,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 = 0;
+       u32 grxfsiz;
+       int retval;
 
        /*
         * Attempt to ensure this device is really a DWC_otg Controller.
@@ -3147,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);
@@ -3159,22 +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 */
-       if (hsotg->dr_mode != USB_DR_MODE_HOST) {
-               gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
-               dwc2_writel(gusbcfg | GUSBCFG_FORCEHOSTMODE,
-                           hsotg->regs + GUSBCFG);
-               usleep_range(25000, 50000);
-       }
+       /*
+        * 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);
-       if (hsotg->dr_mode != USB_DR_MODE_HOST) {
-               dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
-               usleep_range(25000, 50000);
-       }
+       /* hwcfg1 */
+       hw->dev_ep_dirs = hwcfg1;
 
        /* hwcfg2 */
        hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
@@ -3230,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",