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.
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;
}
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.
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);
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) >>
/* 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",