summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-03-23 13:33:09 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-03-23 13:33:09 +0100
commit6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6 (patch)
treec2552531d33b5b4aa333015b5225be5c1e100d63
parent6555ad13a01952c16485c82a52ad1f3e07e34b3a (diff)
parentaaeab02ddcc830e31c33cdb72a3c117b2d499ae2 (diff)
downloadlinux-stable-6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6.tar.gz
linux-stable-6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6.tar.bz2
linux-stable-6d23ee9caa6790aea047f9aca7f3c03cb8d96eb6.zip
Merge tag 'usb-for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
Felipe writes: usb: changes for v4.17 merge window Quite a lot happened in this cycle, with a total of 95 non-merge commits. The most interesting parts are listed below: Synopsys has been adding better support for USB 3.1 to dwc3. The same series also sets g_mass_storage's max speed to SSP. Roger Quadros (TI) added support for dual-role using the OTG block available in some dwc3 implementations, this makes sure that AM437x can swap roles in runtime. We have a new SoC supported in dwc3 now - Amlogic Meson GX - thanks to the work of Martin Blumenstingl. We also have a ton of changes in dwc2 (51% of all changes, in fact). The most interesting part there is the support for Hibernation (a Synopsys PM feature). Apart from these, we have our regular set of non-critical fixes all over the place.
-rw-r--r--Documentation/devicetree/bindings/usb/amlogic,dwc3.txt42
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt16
-rw-r--r--drivers/usb/dwc2/core.c395
-rw-r--r--drivers/usb/dwc2/core.h136
-rw-r--r--drivers/usb/dwc2/core_intr.c304
-rw-r--r--drivers/usb/dwc2/debugfs.c7
-rw-r--r--drivers/usb/dwc2/gadget.c403
-rw-r--r--drivers/usb/dwc2/hcd.c445
-rw-r--r--drivers/usb/dwc2/hcd.h56
-rw-r--r--drivers/usb/dwc2/hw.h44
-rw-r--r--drivers/usb/dwc2/params.c91
-rw-r--r--drivers/usb/dwc2/pci.c27
-rw-r--r--drivers/usb/dwc2/platform.c16
-rw-r--r--drivers/usb/dwc3/Makefile2
-rw-r--r--drivers/usb/dwc3/core.c134
-rw-r--r--drivers/usb/dwc3/core.h146
-rw-r--r--drivers/usb/dwc3/debugfs.c84
-rw-r--r--drivers/usb/dwc3/drd.c489
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c31
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c2
-rw-r--r--drivers/usb/dwc3/ep0.c2
-rw-r--r--drivers/usb/dwc3/gadget.c39
-rw-r--r--drivers/usb/gadget/composite.c126
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_tcm.c2
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c2
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c158
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h4
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c3
-rw-r--r--drivers/usb/gadget/udc/core.c4
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c2
-rw-r--r--drivers/usb/gadget/udc/goku_udc.h2
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c2
-rw-r--r--drivers/usb/phy/phy-generic.c6
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c2
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c14
-rw-r--r--include/linux/usb/composite.h3
-rw-r--r--include/linux/usb/gadget.h1
38 files changed, 2462 insertions, 786 deletions
diff --git a/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt
new file mode 100644
index 000000000000..9a8b631904fd
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt
@@ -0,0 +1,42 @@
+Amlogic Meson GX DWC3 USB SoC controller
+
+Required properties:
+- compatible: depending on the SoC this should contain one of:
+ * amlogic,meson-axg-dwc3
+ * amlogic,meson-gxl-dwc3
+- clocks: a handle for the "USB general" clock
+- clock-names: must be "usb_general"
+- resets: a handle for the shared "USB OTG" reset line
+- reset-names: must be "usb_otg"
+
+Required child node:
+A child node must exist to represent the core DWC3 IP block. The name of
+the node is not important. The content of the node is defined in dwc3.txt.
+
+PHY documentation is provided in the following places:
+- Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
+- Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
+
+Example device nodes:
+ usb0: usb@ff500000 {
+ compatible = "amlogic,meson-axg-dwc3";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ clocks = <&clkc CLKID_USB>;
+ clock-names = "usb_general";
+ resets = <&reset RESET_USB_OTG>;
+ reset-names = "usb_otg";
+
+ dwc3: dwc3@ff500000 {
+ compatible = "snps,dwc3";
+ reg = <0x0 0xff500000 0x0 0x100000>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ dr_mode = "host";
+ maximum-speed = "high-speed";
+ snps,dis_u2_susphy_quirk;
+ phys = <&usb3_phy>, <&usb2_phy0>;
+ phy-names = "usb2-phy", "usb3-phy";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 44e8bab159ad..0dbd3083e7dd 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -57,6 +57,22 @@ Optional properties:
- snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ
register for post-silicon frame length adjustment when the
fladj_30mhz_sdbnd signal is invalid or incorrect.
+ - snps,rx-thr-num-pkt-prd: periodic ESS RX packet threshold count - host mode
+ only. Set this and rx-max-burst-prd to a valid,
+ non-zero value 1-16 (DWC_usb31 programming guide
+ section 1.2.4) to enable periodic ESS RX threshold.
+ - snps,rx-max-burst-prd: max periodic ESS RX burst size - host mode only. Set
+ this and rx-thr-num-pkt-prd to a valid, non-zero value
+ 1-16 (DWC_usb31 programming guide section 1.2.4) to
+ enable periodic ESS RX threshold.
+ - snps,tx-thr-num-pkt-prd: periodic ESS TX packet threshold count - host mode
+ only. Set this and tx-max-burst-prd to a valid,
+ non-zero value 1-16 (DWC_usb31 programming guide
+ section 1.2.3) to enable periodic ESS TX threshold.
+ - snps,tx-max-burst-prd: max periodic ESS TX burst size - host mode only. Set
+ this and tx-thr-num-pkt-prd to a valid, non-zero value
+ 1-16 (DWC_usb31 programming guide section 1.2.3) to
+ enable periodic ESS TX threshold.
- <DEPRECATED> tx-fifo-resize: determines if the FIFO *has* to be reallocated.
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 82a7d98c3436..18a0a1771289 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -64,10 +64,11 @@
*
* @hsotg: Programming view of the DWC_otg controller
*/
-static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
+int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
- int i;
+
+ dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup global regs */
gr = &hsotg->gr_backup;
@@ -78,10 +79,11 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- gr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- gr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
+ gr->pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
+ gr->glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ gr->gi2cctl = dwc2_readl(hsotg->regs + GI2CCTL);
+ gr->pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
gr->valid = true;
return 0;
@@ -94,10 +96,9 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
*
* @hsotg: Programming view of the DWC_otg controller
*/
-static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
+int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
- int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
@@ -117,26 +118,27 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
- dwc2_writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- dwc2_writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
+ dwc2_writel(gr->pcgcctl1, hsotg->regs + PCGCCTL1);
+ dwc2_writel(gr->glpmcfg, hsotg->regs + GLPMCFG);
+ dwc2_writel(gr->pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(gr->gi2cctl, hsotg->regs + GI2CCTL);
return 0;
}
/**
- * dwc2_exit_hibernation() - Exit controller from Partial Power Down.
+ * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
* @restore: Controller registers need to be restored
*/
-int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore)
{
u32 pcgcctl;
int ret = 0;
- if (!hsotg->params.hibernation)
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
return -ENOTSUPP;
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
@@ -167,7 +169,7 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
return ret;
}
} else {
- ret = dwc2_restore_device_registers(hsotg);
+ ret = dwc2_restore_device_registers(hsotg, 0);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
__func__);
@@ -180,16 +182,16 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
}
/**
- * dwc2_enter_hibernation() - Put controller in Partial Power Down.
+ * dwc2_enter_partial_power_down() - Put controller in Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
*/
-int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
{
u32 pcgcctl;
int ret = 0;
- if (!hsotg->params.hibernation)
+ if (!hsotg->params.power_down)
return -ENOTSUPP;
/* Backup all registers */
@@ -218,7 +220,7 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
/*
* Clear any pending interrupts since dwc2 will not be able to
- * clear them after entering hibernation.
+ * clear them after entering partial_power_down.
*/
dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
@@ -240,6 +242,142 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_restore_essential_regs() - Restore essiential regs of core.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rmode: Restore mode, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
+ */
+static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode,
+ int is_host)
+{
+ u32 pcgcctl;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_dregs_backup *dr;
+ struct dwc2_hregs_backup *hr;
+
+ gr = &hsotg->gr_backup;
+ dr = &hsotg->dr_backup;
+ hr = &hsotg->hr_backup;
+
+ dev_dbg(hsotg->dev, "%s: restoring essential regs\n", __func__);
+
+ /* Load restore values for [31:14] bits */
+ pcgcctl = (gr->pcgcctl & 0xffffc000);
+ /* If High Speed */
+ if (is_host) {
+ if (!(pcgcctl & PCGCTL_P2HD_PRT_SPD_MASK))
+ pcgcctl |= BIT(17);
+ } else {
+ if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK))
+ pcgcctl |= BIT(17);
+ }
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+
+ /* Umnask global Interrupt in GAHBCFG and restore it */
+ dwc2_writel(gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Unmask restore done interrupt */
+ dwc2_writel(GINTSTS_RESTOREDONE, hsotg->regs + GINTMSK);
+
+ /* Restore GUSBCFG and HCFG/DCFG */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+
+ if (is_host) {
+ dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+ if (rmode)
+ pcgcctl |= PCGCTL_RESTOREMODE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ } else {
+ dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
+ if (!rmode)
+ pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ }
+}
+
+/**
+ * dwc2_hib_restore_common() - Common part of restore routine.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
+ */
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int is_host)
+{
+ u32 gpwrdn;
+
+ /* Switch-on voltage to the core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Reset core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable restore from PMU */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(50);
+
+ if (!is_host && rem_wakeup)
+ udelay(70);
+
+ /* Deassert reset core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Set Restore Essential Regs bit in PCGCCTL register */
+ dwc2_restore_essential_regs(hsotg, rem_wakeup, is_host);
+
+ /*
+ * Wait For Restore_done Interrupt. This mechanism of polling the
+ * interrupt is introduced to avoid any possible race conditions
+ */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE,
+ 20000)) {
+ dev_dbg(hsotg->dev,
+ "%s: Restore Done wan't generated here\n",
+ __func__);
+ } else {
+ dev_dbg(hsotg->dev, "restore done generated here\n");
+ }
+}
+
+/**
* dwc2_wait_for_mode() - Waits for the controller mode.
* @hsotg: Programming view of the DWC_otg controller.
* @host_mode: If true, waits for host mode, otherwise device mode.
@@ -311,13 +449,50 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
}
/*
+ * dwc2_enter_hibernation() - Common function to enter hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host)
+{
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION)
+ return -ENOTSUPP;
+
+ if (is_host)
+ return dwc2_host_enter_hibernation(hsotg);
+ else
+ return dwc2_gadget_enter_hibernation(hsotg);
+}
+
+/*
+ * dwc2_exit_hibernation() - Common function to exit from hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @reset: Enabled in case of restore with reset.
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset, int is_host)
+{
+ if (is_host)
+ return dwc2_host_exit_hibernation(hsotg, rem_wakeup, reset);
+ else
+ return dwc2_gadget_exit_hibernation(hsotg, rem_wakeup, reset);
+}
+
+/*
* 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(struct dwc2_hsotg *hsotg, bool skip_wait)
{
u32 greset;
- int count = 0;
bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -346,29 +521,19 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- udelay(1);
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 50) {
- dev_warn(hsotg->dev,
- "%s() HANG! Soft Reset GRSTCTL=%0x\n",
- __func__, greset);
- return -EBUSY;
- }
- } while (greset & GRSTCTL_CSFTRST);
+
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
+ __func__);
+ return -EBUSY;
+ }
/* Wait for AHB master IDLE state */
- count = 0;
- do {
- udelay(1);
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 50) {
- dev_warn(hsotg->dev,
- "%s() HANG! AHB Idle GRSTCTL=%0x\n",
- __func__, greset);
- return -EBUSY;
- }
- } while (!(greset & GRSTCTL_AHBIDLE));
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 50)) {
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n",
+ __func__);
+ return -EBUSY;
+ }
if (wait_for_host_mode && !skip_wait)
dwc2_wait_for_mode(hsotg, true);
@@ -376,14 +541,14 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
return 0;
}
-/*
- * Force the mode of the controller.
+/**
+ * dwc2_force_mode() - 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.
+ * changes. We do this once during probe.
*
* 2) During probe we want to read reset values of the hw
* configuration registers that are only available in either host or
@@ -400,7 +565,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
* the filter is configured and enabled. We poll the current mode of
* the controller to account for this delay.
*/
-static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
u32 gusbcfg;
u32 set;
@@ -412,17 +577,17 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* Force mode has no effect if the hardware is not OTG.
*/
if (!dwc2_hw_is_otg(hsotg))
- return false;
+ return;
/*
* 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;
+ return;
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
- return false;
+ return;
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
@@ -434,7 +599,7 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
dwc2_wait_for_mode(hsotg, host);
- return true;
+ return;
}
/**
@@ -446,10 +611,15 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
*/
-void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
+ if (!dwc2_hw_is_otg(hsotg))
+ return;
+
+ dev_dbg(hsotg->dev, "Clearing force mode bits\n");
+
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
@@ -464,16 +634,13 @@ void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
*/
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
- bool ret;
-
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
- ret = dwc2_force_mode(hsotg, true);
/*
* NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2.
*/
- if (!ret)
+ if (!dwc2_hw_is_otg(hsotg))
msleep(50);
break;
@@ -491,22 +658,17 @@ void dwc2_force_dr_mode(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.
- *
- * Additionally this will apply force mode as per the hsotg->dr_mode
- * parameter.
+ * dwc2_enable_acg - enable active clock gating feature
*/
-int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg)
{
- int retval;
+ if (hsotg->params.acg_enable) {
+ u32 pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
- retval = dwc2_core_reset(hsotg, false);
- if (retval)
- return retval;
-
- dwc2_force_dr_mode(hsotg);
- return 0;
+ dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n");
+ pcgcctl1 |= PCGCCTL1_GATEEN;
+ dwc2_writel(pcgcctl1, hsotg->regs + PCGCCTL1);
+ }
}
/**
@@ -683,25 +845,21 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
{
u32 greset;
- int count = 0;
dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num);
+ /* Wait for AHB master IDLE state */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n",
+ __func__);
+
greset = GRSTCTL_TXFFLSH;
greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 10000) {
- dev_warn(hsotg->dev,
- "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
- __func__, greset,
- dwc2_readl(hsotg->regs + GNPTXSTS));
- break;
- }
- udelay(1);
- } while (greset & GRSTCTL_TXFFLSH);
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n",
+ __func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
@@ -715,43 +873,26 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
{
u32 greset;
- int count = 0;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
+ /* Wait for AHB master IDLE state */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n",
+ __func__);
+
greset = GRSTCTL_RXFFLSH;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 10000) {
- dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n",
- __func__, greset);
- break;
- }
- udelay(1);
- } while (greset & GRSTCTL_RXFFLSH);
+ /* Wait for RxFIFO flush done */
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_RXFFLSH\n",
+ __func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
-/*
- * Forces either host or device mode if the controller is not
- * currently in that mode.
- *
- * Returns true if the mode was forced.
- */
-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);
-}
-
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
{
if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff)
@@ -825,6 +966,52 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
}
+/**
+ * dwc2_hsotg_wait_bit_set - Waits for bit to be set.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+ u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (dwc2_readl(hsotg->regs + offset) & mask)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * dwc2_hsotg_wait_bit_clear - Waits for bit to be clear.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+ u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (!(dwc2_readl(hsotg->regs + offset) & mask))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
MODULE_AUTHOR("Synopsys, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index cd77af3b1565..d83be5651f87 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -217,7 +217,7 @@ struct dwc2_hsotg_ep {
unsigned char dir_in;
unsigned char index;
unsigned char mc;
- unsigned char interval;
+ u16 interval;
unsigned int halted:1;
unsigned int periodic:1;
@@ -408,7 +408,7 @@ enum dwc2_ep0_state {
* @ahbcfg: This field allows the default value of the GAHBCFG
* register to be overridden
* -1 - GAHBCFG value will be set to 0x06
- * (INCR4, default)
+ * (INCR, default)
* all others - GAHBCFG value will be overridden with
* this value
* Not all bits can be controlled like this, the
@@ -421,12 +421,26 @@ enum dwc2_ep0_state {
* case.
* 0 - No (default)
* 1 - Yes
- * @hibernation: Specifies whether the controller support hibernation.
- * If hibernation is enabled, the controller will enter
- * hibernation in both peripheral and host mode when
+ * @power_down: Specifies whether the controller support power_down.
+ * If power_down is enabled, the controller will enter
+ * power_down in both peripheral and host mode when
* needed.
* 0 - No (default)
+ * 1 - Partial power down
+ * 2 - Hibernation
+ * @lpm: Enable LPM support.
+ * 0 - No
* 1 - Yes
+ * @lpm_clock_gating: Enable core PHY clock gating.
+ * 0 - No
+ * 1 - Yes
+ * @besl: Enable LPM Errata support.
+ * 0 - No
+ * 1 - Yes
+ * @hird_threshold_en: HIRD or HIRD Threshold enable.
+ * 0 - No
+ * 1 - Yes
+ * @hird_threshold: Value of BESL or HIRD Threshold.
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
* register.
* 0 - Deactivate the transceiver (default)
@@ -479,12 +493,23 @@ struct dwc2_core_params {
bool enable_dynamic_fifo;
bool en_multiple_tx_fifo;
bool i2c_enable;
+ bool acg_enable;
bool ulpi_fs_ls;
bool ts_dline;
bool reload_ctl;
bool uframe_sched;
bool external_id_pin_ctl;
- bool hibernation;
+
+ int power_down;
+#define DWC2_POWER_DOWN_PARAM_NONE 0
+#define DWC2_POWER_DOWN_PARAM_PARTIAL 1
+#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2
+
+ bool lpm;
+ bool lpm_clock_gating;
+ bool besl;
+ bool hird_threshold_en;
+ u8 hird_threshold;
bool activate_stm_fs_transceiver;
u16 max_packet_count;
u32 max_transfer_size;
@@ -560,6 +585,7 @@ struct dwc2_core_params {
* 2 - FS pins shared with UTMI+ pins
* 3 - FS pins shared with ULPI pins
* @total_fifo_size: Total internal RAM for FIFOs (bytes)
+ * @hibernation Is hibernation enabled?
* @utmi_phy_data_width UTMI+ PHY data width
* 0 - 8 bits
* 1 - 16 bits
@@ -587,12 +613,15 @@ struct dwc2_hw_params {
unsigned hs_phy_type:2;
unsigned fs_phy_type:2;
unsigned i2c_enable:1;
+ unsigned acg_enable:1;
unsigned num_dev_ep:4;
unsigned num_dev_in_eps : 4;
unsigned num_dev_perio_in_ep:4;
unsigned total_fifo_size:16;
unsigned power_optimized:1;
+ unsigned hibernation:1;
unsigned utmi_phy_data_width:2;
+ unsigned lpm_mode:1;
u32 snpsid;
u32 dev_ep_dirs;
u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
@@ -611,9 +640,8 @@ struct dwc2_hw_params {
* @grxfsiz: Backup of GRXFSIZ register
* @gnptxfsiz: Backup of GNPTXFSIZ register
* @gi2cctl: Backup of GI2CCTL register
- * @hptxfsiz: Backup of HPTXFSIZ register
+ * @glpmcfg: Backup of GLPMCFG register
* @gdfifocfg: Backup of GDFIFOCFG register
- * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
* @gpwrdn: Backup of GPWRDN register
*/
struct dwc2_gregs_backup {
@@ -624,10 +652,10 @@ struct dwc2_gregs_backup {
u32 grxfsiz;
u32 gnptxfsiz;
u32 gi2cctl;
- u32 hptxfsiz;
+ u32 glpmcfg;
u32 pcgcctl;
+ u32 pcgcctl1;
u32 gdfifocfg;
- u32 dtxfsiz[MAX_EPS_CHANNELS];
u32 gpwrdn;
bool valid;
};
@@ -646,6 +674,7 @@ struct dwc2_gregs_backup {
* @doepctl: Backup of DOEPCTL register
* @doeptsiz: Backup of DOEPTSIZ register
* @doepdma: Backup of DOEPDMA register
+ * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
*/
struct dwc2_dregs_backup {
u32 dcfg;
@@ -659,6 +688,7 @@ struct dwc2_dregs_backup {
u32 doepctl[MAX_EPS_CHANNELS];
u32 doeptsiz[MAX_EPS_CHANNELS];
u32 doepdma[MAX_EPS_CHANNELS];
+ u32 dtxfsiz[MAX_EPS_CHANNELS];
bool valid;
};
@@ -670,6 +700,7 @@ struct dwc2_dregs_backup {
* @hcintmsk: Backup of HCINTMSK register
* @hptr0: Backup of HPTR0 register
* @hfir: Backup of HFIR register
+ * @hptxfsiz: Backup of HPTXFSIZ register
*/
struct dwc2_hregs_backup {
u32 hcfg;
@@ -677,6 +708,7 @@ struct dwc2_hregs_backup {
u32 hcintmsk[MAX_EPS_CHANNELS];
u32 hprt0;
u32 hfir;
+ u32 hptxfsiz;
bool valid;
};
@@ -780,12 +812,14 @@ struct dwc2_hregs_backup {
* @hcd_enabled Host mode sub-driver initialization indicator.
* @gadget_enabled Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled Status of low-level hardware resources.
+ * @hibernated: True if core is hibernated
* @phy: The otg phy transceiver structure for phy control.
* @uphy: The otg phy transceiver structure for old USB phy
* control.
* @plat: The platform specific configuration data. This can be
* removed once all SoCs support usb transceiver.
* @supplies: Definition of USB power supplies
+ * @vbus_supply: Regulator supplying vbus.
* @phyif: PHY interface width
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
@@ -897,6 +931,8 @@ struct dwc2_hregs_backup {
* @ctrl_req: Request for EP0 control packets.
* @ep0_state: EP0 control transfers state
* @test_mode: USB test mode requested by the host
+ * @remote_wakeup_allowed: True if device is allowed to wake-up host by
+ * remote-wakeup signalling
* @setup_desc_dma: EP0 setup stage desc chain DMA address
* @setup_desc: EP0 setup stage desc chain pointer
* @ctrl_in_desc_dma: EP0 IN data phase desc chain DMA address
@@ -917,11 +953,13 @@ struct dwc2_hsotg {
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
unsigned int ll_hw_enabled:1;
+ unsigned int hibernated:1;
struct phy *phy;
struct usb_phy *uphy;
struct dwc2_hsotg_plat *plat;
struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
+ struct regulator *vbus_supply;
u32 phyif;
spinlock_t lock;
@@ -947,6 +985,7 @@ struct dwc2_hsotg {
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
+#define DWC2_CORE_REV_2_80a 0x4f54280a
#define DWC2_CORE_REV_2_90a 0x4f54290a
#define DWC2_CORE_REV_2_91a 0x4f54291a
#define DWC2_CORE_REV_2_92a 0x4f54292a
@@ -956,6 +995,11 @@ struct dwc2_hsotg {
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
+ /* DWC OTG HW Core ID */
+#define DWC2_OTG_ID 0x4f540000
+#define DWC2_FS_IOT_ID 0x55310000
+#define DWC2_HS_IOT_ID 0x55320000
+
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
@@ -1016,24 +1060,6 @@ struct dwc2_hsotg {
struct kmem_cache *desc_gen_cache;
struct kmem_cache *desc_hsisoc_cache;
-#ifdef DEBUG
- u32 frrem_samples;
- u64 frrem_accum;
-
- u32 hfnum_7_samples_a;
- u64 hfnum_7_frrem_accum_a;
- u32 hfnum_0_samples_a;
- u64 hfnum_0_frrem_accum_a;
- u32 hfnum_other_samples_a;
- u64 hfnum_other_frrem_accum_a;
-
- u32 hfnum_7_samples_b;
- u64 hfnum_7_frrem_accum_b;
- u32 hfnum_0_samples_b;
- u64 hfnum_0_frrem_accum_b;
- u32 hfnum_other_samples_b;
- u64 hfnum_other_frrem_accum_b;
-#endif
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
@@ -1062,6 +1088,7 @@ struct dwc2_hsotg {
struct usb_gadget gadget;
unsigned int enabled:1;
unsigned int connected:1;
+ unsigned int remote_wakeup_allowed:1;
struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
@@ -1106,12 +1133,13 @@ static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg)
* and the DWC_otg controller
*/
int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait);
-int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
-int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
-int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg);
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore);
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host);
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset, int is_host);
-bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host);
-void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg);
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host);
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
@@ -1128,6 +1156,13 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int is_host);
+int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg);
+
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
+
/* This function should be called on every hardware interrupt. */
irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
@@ -1137,6 +1172,11 @@ extern const struct of_device_id dwc2_of_match_table[];
int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
+/* Common polling functions */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
+ u32 timeout);
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
+ u32 timeout);
/* Parameters */
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
int dwc2_init_params(struct dwc2_hsotg *hsotg);
@@ -1180,7 +1220,7 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
-int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset);
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
@@ -1188,10 +1228,14 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
+int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset);
int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
+void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
@@ -1199,7 +1243,7 @@ static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2)
{ return 0; }
-static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
+static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset) {}
@@ -1211,7 +1255,13 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
#define dwc2_is_device_connected(hsotg) (0)
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
-static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
+ int remote_wakeup)
+{ return 0; }
+static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset)
{ return 0; }
static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1219,6 +1269,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
+static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
@@ -1227,8 +1278,12 @@ int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
+int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset);
#else
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1239,12 +1294,19 @@ static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+{ return 0; }
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
+static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset)
+{ return 0; }
#endif
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index ab3fa1630853..2982a155734d 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -321,10 +321,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->lx_state == DWC2_L2) {
- ret = dwc2_exit_hibernation(hsotg, true);
+ ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev,
- "exit hibernation failed\n");
+ "exit power_down failed\n");
}
/*
@@ -335,6 +335,57 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
}
}
+/**
+ * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
+{
+ u32 glpmcfg;
+ u32 i = 0;
+
+ if (hsotg->lx_state != DWC2_L1) {
+ dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n");
+ return;
+ }
+
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ if (dwc2_is_device_mode(hsotg)) {
+ dev_dbg(hsotg->dev, "Exit from L1 state\n");
+ glpmcfg &= ~GLPMCFG_ENBLSLPM;
+ glpmcfg &= ~GLPMCFG_HIRD_THRES_EN;
+ dwc2_writel(glpmcfg, hsotg->regs + GLPMCFG);
+
+ do {
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+
+ if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK |
+ GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS)))
+ break;
+
+ udelay(1);
+ } while (++i < 200);
+
+ if (i == 200) {
+ dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n");
+ return;
+ }
+ dwc2_gadget_init_lpm(hsotg);
+ } else {
+ /* TODO */
+ dev_err(hsotg->dev, "Host side LPM is not supported.\n");
+ return;
+ }
+
+ /* Change to L0 state */
+ hsotg->lx_state = DWC2_L0;
+
+ /* Inform gadget to exit from L1 */
+ call_gadget(hsotg, resume);
+}
+
/*
* This interrupt indicates that the DWC_otg controller has detected a
* resume or remote wakeup sequence. If the DWC_otg controller is in
@@ -352,6 +403,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
+ if (hsotg->lx_state == DWC2_L1) {
+ dwc2_wakeup_from_lpm_l1(hsotg);
+ return;
+ }
+
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
dwc2_readl(hsotg->regs + DSTS));
@@ -361,16 +417,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Clear Remote Wakeup Signaling */
dctl &= ~DCTL_RMTWKUPSIG;
dwc2_writel(dctl, hsotg->regs + DCTL);
- ret = dwc2_exit_hibernation(hsotg, true);
+ ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
- dev_err(hsotg->dev, "exit hibernation failed\n");
+ dev_err(hsotg->dev, "exit power_down failed\n");
call_gadget(hsotg, resume);
}
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
- if (hsotg->params.hibernation)
+ if (hsotg->params.power_down)
return;
if (hsotg->lx_state != DWC2_L1) {
@@ -428,32 +484,44 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
* state is active
*/
dsts = dwc2_readl(hsotg->regs + DSTS);
- dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
+ dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts);
dev_dbg(hsotg->dev,
- "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
+ "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n",
!!(dsts & DSTS_SUSPSTS),
- hsotg->hw_params.power_optimized);
- if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
- /* Ignore suspend request before enumeration */
- if (!dwc2_is_device_connected(hsotg)) {
- dev_dbg(hsotg->dev,
- "ignore suspend request before enumeration\n");
- return;
+ hsotg->hw_params.power_optimized,
+ hsotg->hw_params.hibernation);
+
+ /* Ignore suspend request before enumeration */
+ if (!dwc2_is_device_connected(hsotg)) {
+ dev_dbg(hsotg->dev,
+ "ignore suspend request before enumeration\n");
+ return;
+ }
+ if (dsts & DSTS_SUSPSTS) {
+ if (hsotg->hw_params.power_optimized) {
+ ret = dwc2_enter_partial_power_down(hsotg);
+ if (ret) {
+ if (ret != -ENOTSUPP)
+ dev_err(hsotg->dev,
+ "%s: enter partial_power_down failed\n",
+ __func__);
+ goto skip_power_saving;
+ }
+
+ udelay(100);
+
+ /* Ask phy to be suspended */
+ if (!IS_ERR_OR_NULL(hsotg->uphy))
+ usb_phy_set_suspend(hsotg->uphy, true);
}
- ret = dwc2_enter_hibernation(hsotg);
- if (ret) {
- if (ret != -ENOTSUPP)
+ if (hsotg->hw_params.hibernation) {
+ ret = dwc2_enter_hibernation(hsotg, 0);
+ if (ret && ret != -ENOTSUPP)
dev_err(hsotg->dev,
- "enter hibernation failed\n");
- goto skip_power_saving;
+ "%s: enter hibernation failed\n",
+ __func__);
}
-
- udelay(100);
-
- /* Ask phy to be suspended */
- if (!IS_ERR_OR_NULL(hsotg->uphy))
- usb_phy_set_suspend(hsotg->uphy, true);
skip_power_saving:
/*
* Change to L2 (suspend) state before releasing
@@ -479,10 +547,75 @@ skip_power_saving:
}
}
+/**
+ * dwc2_handle_lpm_intr - GINTSTS_LPMTRANRCVD Interrupt handler
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg)
+{
+ u32 glpmcfg;
+ u32 pcgcctl;
+ u32 hird;
+ u32 hird_thres;
+ u32 hird_thres_en;
+ u32 enslpm;
+
+ /* Clear interrupt */
+ dwc2_writel(GINTSTS_LPMTRANRCVD, hsotg->regs + GINTSTS);
+
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+
+ if (!(glpmcfg & GLPMCFG_LPMCAP)) {
+ dev_err(hsotg->dev, "Unexpected LPM interrupt\n");
+ return;
+ }
+
+ hird = (glpmcfg & GLPMCFG_HIRD_MASK) >> GLPMCFG_HIRD_SHIFT;
+ hird_thres = (glpmcfg & GLPMCFG_HIRD_THRES_MASK &
+ ~GLPMCFG_HIRD_THRES_EN) >> GLPMCFG_HIRD_THRES_SHIFT;
+ hird_thres_en = glpmcfg & GLPMCFG_HIRD_THRES_EN;
+ enslpm = glpmcfg & GLPMCFG_ENBLSLPM;
+
+ if (dwc2_is_device_mode(hsotg)) {
+ dev_dbg(hsotg->dev, "HIRD_THRES_EN = %d\n", hird_thres_en);
+
+ if (hird_thres_en && hird >= hird_thres) {
+ dev_dbg(hsotg->dev, "L1 with utmi_l1_suspend_n\n");
+ } else if (enslpm) {
+ dev_dbg(hsotg->dev, "L1 with utmi_sleep_n\n");
+ } else {
+ dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n");
+
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl |= PCGCTL_ENBL_SLEEP_GATING;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ }
+ /**
+ * Examine prt_sleep_sts after TL1TokenTetry period max (10 us)
+ */
+ udelay(10);
+
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+
+ if (glpmcfg & GLPMCFG_SLPSTS) {
+ /* Save the current state */
+ hsotg->lx_state = DWC2_L1;
+ dev_dbg(hsotg->dev,
+ "Core is in L1 sleep glpmcfg=%08x\n", glpmcfg);
+
+ /* Inform gadget that we are in L1 state */
+ call_gadget(hsotg, suspend);
+ }
+ }
+}
+
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \
GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \
- GINTSTS_USBSUSP | GINTSTS_PRTINT)
+ GINTSTS_USBSUSP | GINTSTS_PRTINT | \
+ GINTSTS_LPMTRANRCVD)
/*
* This function returns the Core Interrupt register
@@ -510,6 +643,116 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
}
/*
+ * GPWRDN interrupt handler.
+ *
+ * The GPWRDN interrupts are those that occur in both Host and
+ * Device mode while core is in hibernated state.
+ */
+static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
+{
+ u32 gpwrdn;
+ int linestate;
+
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ /* clear all interrupt */
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT;
+ dev_dbg(hsotg->dev,
+ "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__,
+ gpwrdn);
+
+ if ((gpwrdn & GPWRDN_DISCONN_DET) &&
+ (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) {
+ u32 gpwrdn_tmp;
+
+ dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__);
+
+ /* Switch-on voltage to the core */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Reset core */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable Power Down Clamp */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Deassert reset core */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp |= GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+
+ hsotg->hibernated = 0;
+
+ if (gpwrdn & GPWRDN_IDSTS) {
+ hsotg->op_state = OTG_STATE_B_PERIPHERAL;
+ dwc2_core_init(hsotg, false);
+ dwc2_enable_global_interrupts(hsotg);
+ dwc2_hsotg_core_init_disconnected(hsotg, false);
+ dwc2_hsotg_core_connect(hsotg);
+ } else {
+ hsotg->op_state = OTG_STATE_A_HOST;
+
+ /* Initialize the Core for Host mode */
+ dwc2_core_init(hsotg, false);
+ dwc2_enable_global_interrupts(hsotg);
+ dwc2_hcd_start(hsotg);
+ }
+ }
+
+ if ((gpwrdn & GPWRDN_LNSTSCHG) &&
+ (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) {
+ dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__);
+ if (hsotg->hw_params.hibernation &&
+ hsotg->hibernated) {
+ if (gpwrdn & GPWRDN_IDSTS) {
+ dwc2_exit_hibernation(hsotg, 0, 0, 0);
+ call_gadget(hsotg, resume);
+ } else {
+ dwc2_exit_hibernation(hsotg, 1, 0, 1);
+ }
+ }
+ }
+ if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) {
+ dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__);
+ if (!linestate && (gpwrdn & GPWRDN_BSESSVLD))
+ dwc2_exit_hibernation(hsotg, 0, 1, 0);
+ }
+ if ((gpwrdn & GPWRDN_STS_CHGINT) &&
+ (gpwrdn & GPWRDN_STS_CHGINT_MSK) && linestate) {
+ dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__);
+ if (hsotg->hw_params.hibernation &&
+ hsotg->hibernated) {
+ if (gpwrdn & GPWRDN_IDSTS) {
+ dwc2_exit_hibernation(hsotg, 0, 0, 0);
+ call_gadget(hsotg, resume);
+ } else {
+ dwc2_exit_hibernation(hsotg, 1, 0, 1);
+ }
+ }
+ }
+}
+
+/*
* Common interrupt handler
*
* The common interrupts are those that occur in both Host and Device mode.
@@ -539,6 +782,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
if (gintsts & ~GINTSTS_PRTINT)
retval = IRQ_HANDLED;
+ /* In case of hibernated state gintsts must not work */
+ if (hsotg->hibernated) {
+ dwc2_handle_gpwrdn_intr(hsotg);
+ retval = IRQ_HANDLED;
+ goto out;
+ }
+
if (gintsts & GINTSTS_MODEMIS)
dwc2_handle_mode_mismatch_intr(hsotg);
if (gintsts & GINTSTS_OTGINT)
@@ -553,6 +803,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
dwc2_handle_wakeup_detected_intr(hsotg);
if (gintsts & GINTSTS_USBSUSP)
dwc2_handle_usb_suspend_intr(hsotg);
+ if (gintsts & GINTSTS_LPMTRANRCVD)
+ dwc2_handle_lpm_intr(hsotg);
if (gintsts & GINTSTS_PRTINT) {
/*
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index 5e0d7f2bd2af..58c691f882a8 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -718,7 +718,12 @@ static int params_show(struct seq_file *seq, void *v)
print_param_hex(seq, p, ahbcfg);
print_param(seq, p, uframe_sched);
print_param(seq, p, external_id_pin_ctl);
- print_param(seq, p, hibernation);
+ print_param(seq, p, power_down);
+ print_param(seq, p, lpm);
+ print_param(seq, p, lpm_clock_gating);
+ print_param(seq, p, besl);
+ print_param(seq, p, hird_threshold_en);
+ print_param(seq, p, hird_threshold);
print_param(seq, p, host_dma);
print_param(seq, p, g_dma);
print_param(seq, p, g_dma_desc);
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 5bcad1d869b5..6c32bf26e48e 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -47,12 +47,12 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
return container_of(gadget, struct dwc2_hsotg, gadget);
}
-static inline void __orr32(void __iomem *ptr, u32 val)
+static inline void dwc2_set_bit(void __iomem *ptr, u32 val)
{
dwc2_writel(dwc2_readl(ptr) | val, ptr);
}
-static inline void __bic32(void __iomem *ptr, u32 val)
+static inline void dwc2_clear_bit(void __iomem *ptr, u32 val)
{
dwc2_writel(dwc2_readl(ptr) & ~val, ptr);
}
@@ -116,10 +116,10 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
{
hs_ep->target_frame += hs_ep->interval;
if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) {
- hs_ep->frame_overrun = 1;
+ hs_ep->frame_overrun = true;
hs_ep->target_frame &= DSTS_SOFFN_LIMIT;
} else {
- hs_ep->frame_overrun = 0;
+ hs_ep->frame_overrun = false;
}
}
@@ -252,6 +252,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
unsigned int ep;
unsigned int addr;
int timeout;
+
u32 val;
u32 *txfsz = hsotg->params.g_tx_fifo_size;
@@ -1296,8 +1297,8 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
req->zero, req->short_not_ok);
/* Prevent new request submission when controller is suspended */
- if (hs->lx_state == DWC2_L2) {
- dev_dbg(hs->dev, "%s: don't submit request while suspended\n",
+ if (hs->lx_state != DWC2_L0) {
+ dev_dbg(hs->dev, "%s: submit request only in active state\n",
__func__);
return -EAGAIN;
}
@@ -1639,6 +1640,10 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
switch (recip) {
case USB_RECIP_DEVICE:
switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ hsotg->remote_wakeup_allowed = 1;
+ break;
+
case USB_DEVICE_TEST_MODE:
if ((wIndex & 0xff) != 0)
return -EINVAL;
@@ -2495,30 +2500,13 @@ bad_mps:
*/
static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
{
- int timeout;
- int val;
-
dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH,
hsotg->regs + GRSTCTL);
/* wait until the fifo is flushed */
- timeout = 100;
-
- while (1) {
- val = dwc2_readl(hsotg->regs + GRSTCTL);
-
- if ((val & (GRSTCTL_TXFFLSH)) == 0)
- break;
-
- if (--timeout == 0) {
- dev_err(hsotg->dev,
- "%s: timeout flushing fifo (GRSTCTL=%08x)\n",
- __func__, val);
- break;
- }
-
- udelay(1);
- }
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100))
+ dev_warn(hsotg->dev, "%s: timeout flushing fifo GRSTCTL_TXFFLSH\n",
+ __func__);
}
/**
@@ -3253,7 +3241,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_init_fifo(hsotg);
if (!is_usb_reset)
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
dcfg |= DCFG_EPMISCNT(1);
@@ -3282,7 +3270,8 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
GINTSTS_USBRST | GINTSTS_RESETDET |
GINTSTS_ENUMDONE | GINTSTS_OTGINT |
- GINTSTS_USBSUSP | GINTSTS_WKUPINT;
+ GINTSTS_USBSUSP | GINTSTS_WKUPINT |
+ GINTSTS_LPMTRANRCVD;
if (!using_desc_dma(hsotg))
intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT;
@@ -3294,12 +3283,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
if (using_dma(hsotg)) {
dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
- (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
+ hsotg->params.ahbcfg,
hsotg->regs + GAHBCFG);
/* Set DDMA mode support in the core if needed */
if (using_desc_dma(hsotg))
- __orr32(hsotg->regs + DCFG, DCFG_DESCDMA_EN);
+ dwc2_set_bit(hsotg->regs + DCFG, DCFG_DESCDMA_EN);
} else {
dwc2_writel(((hsotg->dedicated_fifos) ?
@@ -3332,7 +3321,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
/* Enable BNA interrupt for DDMA */
if (using_desc_dma(hsotg))
- __orr32(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK);
+ dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK);
dwc2_writel(0, hsotg->regs + DAINTMSK);
@@ -3356,9 +3345,9 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1);
if (!is_usb_reset) {
- __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
udelay(10); /* see openiboot */
- __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ dwc2_clear_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
}
dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL));
@@ -3385,7 +3374,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
if (!is_usb_reset)
val |= DCTL_SFTDISCON;
- __orr32(hsotg->regs + DCTL, val);
+ dwc2_set_bit(hsotg->regs + DCTL, val);
+
+ /* configure the core to support LPM */
+ dwc2_gadget_init_lpm(hsotg);
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
@@ -3402,13 +3394,13 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
{
/* set the soft-disconnect bit */
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
}
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
{
/* remove the soft-disconnect and let's go */
- __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_clear_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
}
/**
@@ -3428,14 +3420,21 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
{
struct dwc2_hsotg_ep *hs_ep;
u32 epctrl;
+ u32 daintmsk;
u32 idx;
dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
+ daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_in[idx];
+ /* Proceed only unmasked ISOC EPs */
+ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ continue;
+
epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx));
- if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
+ if ((epctrl & DXEPCTL_EPENA) &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
@@ -3464,16 +3463,24 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
{
u32 gintsts;
u32 gintmsk;
+ u32 daintmsk;
u32 epctrl;
struct dwc2_hsotg_ep *hs_ep;
int idx;
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
+ daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk >>= DAINT_OUTEP_SHIFT;
+
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
+ /* Proceed only unmasked ISOC EPs */
+ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ continue;
+
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
- if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
+ if ((epctrl & DXEPCTL_EPENA) &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
/* Unmask GOUTNAKEFF interrupt */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
@@ -3481,8 +3488,10 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
gintsts = dwc2_readl(hsotg->regs + GINTSTS);
- if (!(gintsts & GINTSTS_GOUTNAKEFF))
- __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ if (!(gintsts & GINTSTS_GOUTNAKEFF)) {
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ break;
+ }
}
}
@@ -3522,7 +3531,7 @@ irq_retry:
/* This event must be used only if controller is suspended */
if (hsotg->lx_state == DWC2_L2) {
- dwc2_exit_hibernation(hsotg, true);
+ dwc2_exit_partial_power_down(hsotg, true);
hsotg->lx_state = DWC2_L0;
}
}
@@ -3541,7 +3550,7 @@ irq_retry:
dwc2_hsotg_disconnect(hsotg);
/* Reset device address to zero */
- __bic32(hsotg->regs + DCFG, DCFG_DEVADDR_MASK);
+ dwc2_clear_bit(hsotg->regs + DCFG, DCFG_DEVADDR_MASK);
if (usb_status & GOTGCTL_BSESVLD && connected)
dwc2_hsotg_core_init_disconnected(hsotg, true);
@@ -3627,8 +3636,11 @@ irq_retry:
u8 idx;
u32 epctrl;
u32 gintmsk;
+ u32 daintmsk;
struct dwc2_hsotg_ep *hs_ep;
+ daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk >>= DAINT_OUTEP_SHIFT;
/* Mask this interrupt */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_GOUTNAKEFF;
@@ -3637,9 +3649,13 @@ irq_retry:
dev_dbg(hsotg->dev, "GOUTNakEff triggered\n");
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
+ /* Proceed only unmasked ISOC EPs */
+ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ continue;
+
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
- if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) {
+ if (epctrl & DXEPCTL_EPENA) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx));
@@ -3652,7 +3668,7 @@ irq_retry:
if (gintsts & GINTSTS_GINNAKEFF) {
dev_info(hsotg->dev, "GINNakEff triggered\n");
- __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK);
dwc2_hsotg_dump(hsotg);
}
@@ -3676,20 +3692,6 @@ irq_retry:
return IRQ_HANDLED;
}
-static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg,
- u32 bit, u32 timeout)
-{
- u32 i;
-
- for (i = 0; i < timeout; i++) {
- if (dwc2_readl(hs_otg->regs + reg) & bit)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_ep *hs_ep)
{
@@ -3706,7 +3708,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
if (hs_ep->dir_in) {
if (hsotg->dedicated_fifos || hs_ep->periodic) {
- __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
+ dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
/* Wait for Nak effect */
if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
DXEPINT_INEPNAKEFF, 100))
@@ -3714,7 +3716,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DIEPINT.NAKEFF\n",
__func__);
} else {
- __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGNPINNAK);
/* Wait for Nak effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
GINTSTS_GINNAKEFF, 100))
@@ -3724,7 +3726,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
}
} else {
if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF))
- __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK);
/* Wait for global nak to take effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
@@ -3734,7 +3736,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
}
/* Disable ep */
- __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
+ dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
/* Wait for ep to be disabled */
if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
@@ -3742,7 +3744,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DOEPCTL.EPDisable\n", __func__);
/* Clear EPDISBLD interrupt */
- __orr32(hsotg->regs + epint_reg, DXEPINT_EPDISBLD);
+ dwc2_set_bit(hsotg->regs + epint_reg, DXEPINT_EPDISBLD);
if (hs_ep->dir_in) {
unsigned short fifo_index;
@@ -3757,11 +3759,11 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
/* Clear Global In NP NAK in Shared FIFO for non periodic ep */
if (!hsotg->dedicated_fifos && !hs_ep->periodic)
- __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK);
} else {
/* Remove global NAKs */
- __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGOUTNAK);
}
}
@@ -4183,7 +4185,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
dwc2_writel(0, hsotg->regs + DAINTMSK);
/* Be in disconnected state until gadget is registered */
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
/* setup fifos */
@@ -4205,7 +4207,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
if (using_dma(hsotg))
- __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
+ dwc2_set_bit(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
}
/**
@@ -4352,6 +4354,8 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
if (is_on) {
hsotg->enabled = 1;
dwc2_hsotg_core_init_disconnected(hsotg, false);
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
} else {
dwc2_hsotg_core_disconnect(hsotg);
@@ -4374,18 +4378,21 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
spin_lock_irqsave(&hsotg->lock, flags);
/*
- * If controller is hibernated, it must exit from hibernation
+ * If controller is hibernated, it must exit from power_down
* before being initialized / de-initialized
*/
if (hsotg->lx_state == DWC2_L2)
- dwc2_exit_hibernation(hsotg, false);
+ dwc2_exit_partial_power_down(hsotg, false);
if (is_active) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_hsotg_core_init_disconnected(hsotg, false);
- if (hsotg->enabled)
+ if (hsotg->enabled) {
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
+ }
} else {
dwc2_hsotg_core_disconnect(hsotg);
dwc2_hsotg_disconnect(hsotg);
@@ -4606,9 +4613,8 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
/**
* dwc2_gadget_init - init function for gadget
* @dwc2: The data structure for the DWC2 driver.
- * @irq: The IRQ number for the controller.
*/
-int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{
struct device *dev = hsotg->dev;
int epnum;
@@ -4622,6 +4628,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
hsotg->gadget.name = dev_name(dev);
+ hsotg->remote_wakeup_allowed = 0;
+
+ if (hsotg->params.lpm)
+ hsotg->gadget.lpm_capable = true;
+
if (hsotg->dr_mode == USB_DR_MODE_OTG)
hsotg->gadget.is_otg = 1;
else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
@@ -4649,8 +4660,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
return ret;
}
- ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED,
- dev_name(hsotg->dev), hsotg);
+ ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
+ IRQF_SHARED, dev_name(hsotg->dev), hsotg);
if (ret < 0) {
dev_err(dev, "cannot claim IRQ for gadget\n");
return ret;
@@ -4751,8 +4762,11 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_core_init_disconnected(hsotg, false);
- if (hsotg->enabled)
+ if (hsotg->enabled) {
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
+ }
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -4806,6 +4820,7 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i));
dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i));
+ dr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
}
dr->valid = true;
return 0;
@@ -4817,11 +4832,13 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
+ * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
+ *
+ * Return: 0 if successful, negative error code otherwise
*/
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
{
struct dwc2_dregs_backup *dr;
- u32 dctl;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
@@ -4835,28 +4852,240 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
}
dr->valid = false;
- dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
- dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+ if (!remote_wakeup)
+ dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+
dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK);
dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK);
dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Restore IN EPs */
- dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
-
+ dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
+ /** WA for enabled EPx's IN in DDMA mode. On entering to
+ * hibernation wrong value read and saved from DIEPDMAx,
+ * as result BNA interrupt asserted on hibernation exit
+ * by restoring from saved area.
+ */
+ if (hsotg->params.g_dma_desc &&
+ (dr->diepctl[i] & DXEPCTL_EPENA))
+ dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma;
+ dwc2_writel(dr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
+ dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
/* Restore OUT EPs */
- dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
+ /* WA for enabled EPx's OUT in DDMA mode. On entering to
+ * hibernation wrong value read and saved from DOEPDMAx,
+ * as result BNA interrupt asserted on hibernation exit
+ * by restoring from saved area.
+ */
+ if (hsotg->params.g_dma_desc &&
+ (dr->doepctl[i] & DXEPCTL_EPENA))
+ dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma;
dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
+ dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
}
- /* Set the Power-On Programming done bit */
- dctl = dwc2_readl(hsotg->regs + DCTL);
- dctl |= DCTL_PWRONPRGDONE;
- dwc2_writel(dctl, hsotg->regs + DCTL);
-
return 0;
}
+
+/**
+ * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
+{
+ u32 val;
+
+ if (!hsotg->params.lpm)
+ return;
+
+ val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES;
+ val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0;
+ val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
+ val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
+ val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
+ dwc2_writel(val, hsotg->regs + GLPMCFG);
+ dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs
+ + GLPMCFG));
+}
+
+/**
+ * dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Return non-zero if failed to enter to hibernation.
+ */
+int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
+{
+ u32 gpwrdn;
+ int ret = 0;
+
+ /* Change to L2(suspend) state */
+ hsotg->lx_state = DWC2_L2;
+ dev_dbg(hsotg->dev, "Start of hibernation completed\n");
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_backup_device_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup device registers\n",
+ __func__);
+ return ret;
+ }
+
+ gpwrdn = GPWRDN_PWRDNRSTN;
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Set flag to indicate that we are in hibernation */
+ hsotg->hibernated = 1;
+
+ /* Enable interrupts from wake up logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Unmask device mode interrupts in GPWRDN */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_RST_DET_MSK;
+ gpwrdn |= GPWRDN_LNSTSCHG_MSK;
+ gpwrdn |= GPWRDN_STS_CHGINT_MSK;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Save gpwrdn register for further usage if stschng interrupt */
+ hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ dev_dbg(hsotg->dev, "Hibernation completed\n");
+
+ return ret;
+}
+
+/**
+ * dwc2_gadget_exit_hibernation()
+ * This function is for exiting from Device mode hibernation by host initiated
+ * resume/reset and device initiated remote-wakeup.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Device or Host.
+ * @param reset: indicates whether resume is initiated by Reset.
+ *
+ * Return non-zero if failed to exit from hibernation.
+ */
+int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset)
+{
+ u32 pcgcctl;
+ u32 gpwrdn;
+ u32 dctl;
+ int ret = 0;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_dregs_backup *dr;
+
+ gr = &hsotg->gr_backup;
+ dr = &hsotg->dr_backup;
+
+ if (!hsotg->hibernated) {
+ dev_dbg(hsotg->dev, "Already exited from Hibernation\n");
+ return 1;
+ }
+ dev_dbg(hsotg->dev,
+ "%s: called with rem_wakeup = %d reset = %d\n",
+ __func__, rem_wakeup, reset);
+
+ dwc2_hib_restore_common(hsotg, rem_wakeup, 0);
+
+ if (!reset) {
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ }
+
+ /* De-assert Restore */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ if (!rem_wakeup) {
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ }
+
+ /* Restore GUSBCFG, DCFG and DCTL */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
+ dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+
+ if (rem_wakeup) {
+ udelay(10);
+ /* Start Remote Wakeup Signaling */
+ dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL);
+ } else {
+ udelay(50);
+ /* Set Device programming done bit */
+ dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl |= DCTL_PWRONPRGDONE;
+ dwc2_writel(dctl, hsotg->regs + DCTL);
+ }
+ /* Wait for interrupts which must be cleared */
+ mdelay(2);
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Restore global registers */
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Restore device registers */
+ ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore device registers\n",
+ __func__);
+ return ret;
+ }
+
+ if (rem_wakeup) {
+ mdelay(10);
+ dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl &= ~DCTL_RMTWKUPSIG;
+ dwc2_writel(dctl, hsotg->regs + DCTL);
+ }
+
+ hsotg->hibernated = 0;
+ hsotg->lx_state = DWC2_L0;
+ dev_dbg(hsotg->dev, "Hibernation recovery completes here\n");
+
+ return ret;
+}
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index a5d72fcd1603..190f95964000 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -91,6 +91,9 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
GINTSTS_SESSREQINT;
+ if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm)
+ intmsk |= GINTSTS_LPMTRANRCVD;
+
dwc2_writel(intmsk, hsotg->regs + GINTMSK);
}
@@ -138,7 +141,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
/* Reset after a PHY select */
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval) {
dev_err(hsotg->dev,
@@ -236,7 +239,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
/* Reset after setting the PHY parameters */
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval) {
dev_err(hsotg->dev,
"%s: Reset failed, aborting", __func__);
@@ -308,22 +311,10 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
break;
}
- dev_dbg(hsotg->dev, "host_dma:%d dma_desc_enable:%d\n",
- hsotg->params.host_dma,
- hsotg->params.dma_desc_enable);
-
- if (hsotg->params.host_dma) {
- if (hsotg->params.dma_desc_enable)
- dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n");
- else
- dev_dbg(hsotg->dev, "Using Buffer DMA mode\n");
- } else {
- dev_dbg(hsotg->dev, "Using Slave mode\n");
- hsotg->params.dma_desc_enable = false;
- }
-
if (hsotg->params.host_dma)
ahbcfg |= GAHBCFG_DMA_EN;
+ else
+ hsotg->params.dma_desc_enable = false;
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
@@ -365,6 +356,23 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
}
+static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
+{
+ hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
+ if (IS_ERR(hsotg->vbus_supply))
+ return 0;
+
+ return regulator_enable(hsotg->vbus_supply);
+}
+
+static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->vbus_supply)
+ return regulator_disable(hsotg->vbus_supply);
+
+ return 0;
+}
+
/**
* dwc2_enable_host_interrupts() - Enables the Host mode interrupts
*
@@ -989,6 +997,24 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+ /*
+ * In buffer DMA or external DMA mode channel can't be halted
+ * for non-split periodic channels. At the end of the next
+ * uframe/frame (in the worst case), the core generates a channel
+ * halted and disables the channel automatically.
+ */
+ if ((hsotg->params.g_dma && !hsotg->params.g_dma_desc) ||
+ hsotg->hw_params.arch == GHWCFG2_EXT_DMA_ARCH) {
+ if (!chan->do_split &&
+ (chan->ep_type == USB_ENDPOINT_XFER_ISOC ||
+ chan->ep_type == USB_ENDPOINT_XFER_INT)) {
+ dev_err(hsotg->dev, "%s() Channel can't be halted\n",
+ __func__);
+ return;
+ }
+ }
+
if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS)
dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status);
@@ -2232,7 +2258,7 @@ static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg,
* @hsotg: Programming view of the DWC_otg controller
* @initial_setup: If true then this is the first init for this instance.
*/
-static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
{
u32 usbcfg, otgctl;
int retval;
@@ -2261,7 +2287,7 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
* needed to in order to properly detect various parameters).
*/
if (!initial_setup) {
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval) {
dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
__func__);
@@ -2322,10 +2348,22 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
*/
static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
{
- u32 hcfg, hfir, otgctl;
+ u32 hcfg, hfir, otgctl, usbcfg;
dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
+ /* Set HS/FS Timeout Calibration to 7 (max available value).
+ * The number of PHY clocks that the application programs in
+ * this field is added to the high/full speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This can be required, because the delay
+ * introduced by the PHY in generating the linestate condition
+ * can vary from one PHY to another.
+ */
+ usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg |= GUSBCFG_TOUTCAL(7);
+ dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+
/* Restart the Phy Clock */
dwc2_writel(0, hsotg->regs + PCGCTL);
@@ -2403,27 +2441,24 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* Halt all channels to put them into a known state */
for (i = 0; i < num_channels; i++) {
- int count = 0;
-
hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
hcchar &= ~HCCHAR_EPDIR;
dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
__func__, i);
- do {
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
- if (++count > 1000) {
- dev_err(hsotg->dev,
- "Unable to clear enable on channel %d\n",
- i);
- break;
- }
- udelay(1);
- } while (hcchar & HCCHAR_CHENA);
+
+ if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i),
+ HCCHAR_CHENA, 1000)) {
+ dev_warn(hsotg->dev, "Unable to clear enable on channel %d\n",
+ i);
+ }
}
}
+ /* Enable ACG feature in host mode, if supported */
+ dwc2_enable_acg(hsotg);
+
/* Turn on the vbus power */
dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state);
if (hsotg->op_state == OTG_STATE_A_HOST) {
@@ -3257,6 +3292,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
/* B-Device connector (Device Mode) */
if (gotgctl & GOTGCTL_CONID_B) {
+ dwc2_vbus_supply_exit(hsotg);
/* Wait for switch to device mode */
dev_dbg(hsotg->dev, "connId B\n");
if (hsotg->bus_suspended) {
@@ -3290,6 +3326,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_core_init_disconnected(hsotg, false);
spin_unlock_irqrestore(&hsotg->lock, flags);
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
} else {
host:
@@ -3377,10 +3415,10 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
hsotg->bus_suspended = true;
/*
- * If hibernation is supported, Phy clock will be suspended
+ * If power_down is supported, Phy clock will be suspended
* after registers are backuped.
*/
- if (!hsotg->params.hibernation) {
+ if (!hsotg->params.power_down) {
/* Suspend the Phy Clock */
pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgctl |= PCGCTL_STOPPCLK;
@@ -3412,10 +3450,10 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
/*
- * If hibernation is supported, Phy clock is already resumed
+ * If power_down is supported, Phy clock is already resumed
* after registers restore.
*/
- if (!hsotg->params.hibernation) {
+ if (!hsotg->params.power_down) {
pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
@@ -3486,8 +3524,12 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
- if (hsotg->bus_suspended)
- dwc2_port_resume(hsotg);
+ if (hsotg->bus_suspended) {
+ if (hsotg->hibernated)
+ dwc2_exit_hibernation(hsotg, 0, 0, 1);
+ else
+ dwc2_port_resume(hsotg);
+ }
break;
case USB_PORT_FEAT_POWER:
@@ -3695,7 +3737,10 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
if (windex != hsotg->otg_port)
goto error;
- dwc2_port_suspend(hsotg, windex);
+ if (hsotg->params.power_down == 2)
+ dwc2_enter_hibernation(hsotg, 1);
+ else
+ dwc2_port_suspend(hsotg, windex);
break;
case USB_PORT_FEAT_POWER:
@@ -3707,6 +3752,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
break;
case USB_PORT_FEAT_RESET:
+ if (hsotg->params.power_down == 2 &&
+ hsotg->hibernated)
+ dwc2_exit_hibernation(hsotg, 0, 1, 1);
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_RESET\n");
@@ -4002,7 +4050,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
(p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n",
(p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
- dwc2_hcd_dump_frrem(hsotg);
dwc2_dump_global_registers(hsotg);
dwc2_dump_host_registers(hsotg);
dev_dbg(hsotg->dev,
@@ -4011,75 +4058,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
#endif
}
-/*
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
- */
-void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg)
-{
-#ifdef DWC2_DUMP_FRREM
- dev_dbg(hsotg->dev, "Frame remaining at SOF:\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->frrem_samples, hsotg->frrem_accum,
- hsotg->frrem_samples > 0 ?
- hsotg->frrem_accum / hsotg->frrem_samples : 0);
- dev_dbg(hsotg->dev, "\n");
- dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 7):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_7_samples,
- hsotg->hfnum_7_frrem_accum,
- hsotg->hfnum_7_samples > 0 ?
- hsotg->hfnum_7_frrem_accum / hsotg->hfnum_7_samples : 0);
- dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 0):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_0_samples,
- hsotg->hfnum_0_frrem_accum,
- hsotg->hfnum_0_samples > 0 ?
- hsotg->hfnum_0_frrem_accum / hsotg->hfnum_0_samples : 0);
- dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 1-6):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_other_samples,
- hsotg->hfnum_other_frrem_accum,
- hsotg->hfnum_other_samples > 0 ?
- hsotg->hfnum_other_frrem_accum / hsotg->hfnum_other_samples :
- 0);
- dev_dbg(hsotg->dev, "\n");
- dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 7):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_7_samples_a, hsotg->hfnum_7_frrem_accum_a,
- hsotg->hfnum_7_samples_a > 0 ?
- hsotg->hfnum_7_frrem_accum_a / hsotg->hfnum_7_samples_a : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 0):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_0_samples_a, hsotg->hfnum_0_frrem_accum_a,
- hsotg->hfnum_0_samples_a > 0 ?
- hsotg->hfnum_0_frrem_accum_a / hsotg->hfnum_0_samples_a : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 1-6):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_other_samples_a, hsotg->hfnum_other_frrem_accum_a,
- hsotg->hfnum_other_samples_a > 0 ?
- hsotg->hfnum_other_frrem_accum_a / hsotg->hfnum_other_samples_a
- : 0);
- dev_dbg(hsotg->dev, "\n");
- dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 7):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_7_samples_b, hsotg->hfnum_7_frrem_accum_b,
- hsotg->hfnum_7_samples_b > 0 ?
- hsotg->hfnum_7_frrem_accum_b / hsotg->hfnum_7_samples_b : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 0):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_0_samples_b, hsotg->hfnum_0_frrem_accum_b,
- (hsotg->hfnum_0_samples_b > 0) ?
- hsotg->hfnum_0_frrem_accum_b / hsotg->hfnum_0_samples_b : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 1-6):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_other_samples_b, hsotg->hfnum_other_frrem_accum_b,
- (hsotg->hfnum_other_samples_b > 0) ?
- hsotg->hfnum_other_frrem_accum_b / hsotg->hfnum_other_samples_b
- : 0);
-#endif
-}
-
struct wrapper_priv_data {
struct dwc2_hsotg *hsotg;
};
@@ -4363,6 +4341,9 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
}
spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ dwc2_vbus_supply_init(hsotg);
+
return 0;
}
@@ -4390,6 +4371,8 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, flags);
+ dwc2_vbus_supply_exit(hsotg);
+
usleep_range(1000, 3000);
}
@@ -4414,7 +4397,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
goto unlock;
- if (!hsotg->params.hibernation)
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
goto skip_power_saving;
/*
@@ -4426,14 +4409,15 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
hprt0 |= HPRT0_SUSP;
hprt0 &= ~HPRT0_PWR;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_vbus_supply_exit(hsotg);
}
- /* Enter hibernation */
- ret = dwc2_enter_hibernation(hsotg);
+ /* Enter partial_power_down */
+ ret = dwc2_enter_partial_power_down(hsotg);
if (ret) {
if (ret != -ENOTSUPP)
dev_err(hsotg->dev,
- "enter hibernation failed\n");
+ "enter partial_power_down failed\n");
goto skip_power_saving;
}
@@ -4444,7 +4428,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
}
- /* After entering hibernation, hardware is no more accessible */
+ /* After entering partial_power_down, hardware is no more accessible */
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
skip_power_saving:
@@ -4469,7 +4453,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
if (hsotg->lx_state != DWC2_L2)
goto unlock;
- if (!hsotg->params.hibernation) {
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) {
hsotg->lx_state = DWC2_L0;
goto unlock;
}
@@ -4491,10 +4475,10 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
}
- /* Exit hibernation */
- ret = dwc2_exit_hibernation(hsotg, true);
+ /* Exit partial_power_down */
+ ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
- dev_err(hsotg->dev, "exit hibernation failed\n");
+ dev_err(hsotg->dev, "exit partial_power_down failed\n");
hsotg->lx_state = DWC2_L0;
@@ -4506,6 +4490,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_port_resume(hsotg);
} else {
+ dwc2_vbus_supply_init(hsotg);
+
/* Wait for controller to correctly update D+/D- level */
usleep_range(3000, 5000);
@@ -5368,6 +5354,7 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
hr->hprt0 = dwc2_read_hprt0(hsotg);
hr->hfir = dwc2_readl(hsotg->regs + HFIR);
+ hr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
hr->valid = true;
return 0;
@@ -5404,7 +5391,231 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
dwc2_writel(hr->hprt0, hsotg->regs + HPRT0);
dwc2_writel(hr->hfir, hsotg->regs + HFIR);
+ dwc2_writel(hr->hptxfsiz, hsotg->regs + HPTXFSIZ);
hsotg->frame_number = 0;
return 0;
}
+
+/**
+ * dwc2_host_enter_hibernation() - Put controller in Hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
+{
+ unsigned long flags;
+ int ret = 0;
+ u32 hprt0;
+ u32 pcgcctl;
+ u32 gusbcfg;
+ u32 gpwrdn;
+
+ dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_backup_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup host registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Enter USB Suspend Mode */
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 |= HPRT0_SUSP;
+ hprt0 &= ~HPRT0_ENA;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ /* Wait for the HPRT0.PrtSusp register field to be set */
+ if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300))
+ dev_warn(hsotg->dev, "Suspend wasn't generated\n");
+
+ /*
+ * We need to disable interrupts to prevent servicing of any IRQ
+ * during going to hibernation
+ */
+ spin_lock_irqsave(&hsotg->lock, flags);
+ hsotg->lx_state = DWC2_L2;
+
+ gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
+ /* ULPI interface */
+ /* Suspend the Phy Clock */
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+ } else {
+ /* UTMI+ Interface */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ }
+
+ /* Enable interrupts from wake up logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Unmask host mode interrupts in GPWRDN */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_DISCONN_DET_MSK;
+ gpwrdn |= GPWRDN_LNSTSCHG_MSK;
+ gpwrdn |= GPWRDN_STS_CHGINT_MSK;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+
+ hsotg->hibernated = 1;
+ hsotg->bus_suspended = 1;
+ dev_dbg(hsotg->dev, "Host hibernation completed\n");
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ return ret;
+}
+
+/*
+ * dwc2_host_exit_hibernation()
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Device or Host.
+ * @param reset: indicates whether resume is initiated by Reset.
+ *
+ * Return: non-zero if failed to enter to hibernation.
+ *
+ * This function is for exiting from Host mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ */
+int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset)
+{
+ u32 gpwrdn;
+ u32 hprt0;
+ int ret = 0;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_hregs_backup *hr;
+
+ gr = &hsotg->gr_backup;
+ hr = &hsotg->hr_backup;
+
+ dev_dbg(hsotg->dev,
+ "%s: called with rem_wakeup = %d reset = %d\n",
+ __func__, rem_wakeup, reset);
+
+ dwc2_hib_restore_common(hsotg, rem_wakeup, 1);
+ hsotg->hibernated = 0;
+
+ /*
+ * This step is not described in functional spec but if not wait for
+ * this delay, mismatch interrupts occurred because just after restore
+ * core is in Device mode(gintsts.curmode == 0)
+ */
+ mdelay(100);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* De-assert Restore */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Restore GUSBCFG, HCFG */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ hprt0 = hr->hprt0;
+ hprt0 |= HPRT0_PWR;
+ hprt0 &= ~HPRT0_ENA;
+ hprt0 &= ~HPRT0_SUSP;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ hprt0 = hr->hprt0;
+ hprt0 |= HPRT0_PWR;
+ hprt0 &= ~HPRT0_ENA;
+ hprt0 &= ~HPRT0_SUSP;
+
+ if (reset) {
+ hprt0 |= HPRT0_RST;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ /* Wait for Resume time and then program HPRT again */
+ mdelay(60);
+ hprt0 &= ~HPRT0_RST;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ } else {
+ hprt0 |= HPRT0_RES;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ /* Wait for Resume time and then program HPRT again */
+ mdelay(100);
+ hprt0 &= ~HPRT0_RES;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ }
+ /* Clear all interrupt status */
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 |= HPRT0_CONNDET;
+ hprt0 |= HPRT0_ENACHG;
+ hprt0 &= ~HPRT0_ENA;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Restore global registers */
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Restore host registers */
+ ret = dwc2_restore_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+ __func__);
+ return ret;
+ }
+
+ hsotg->hibernated = 0;
+ hsotg->bus_suspended = 0;
+ hsotg->lx_state = DWC2_L0;
+ dev_dbg(hsotg->dev, "Host hibernation restore complete\n");
+ return ret;
+}
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index ad60e46e66e1..96a9da5fb202 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -783,19 +783,6 @@ int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
*/
void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
-/**
- * dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF
- *
- * @hsotg: The DWC2 HCD
- *
- * This can be used to determine average interrupt latency. Frame remaining is
- * also shown for start transfer and two additional sample points.
- *
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
- */
-void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
-
/* URB interface */
/* Transfer flags */
@@ -813,47 +800,4 @@ int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
int status);
-#ifdef DEBUG
-/*
- * Macro to sample the remaining PHY clocks left in the current frame. This
- * may be used during debugging to determine the average time it takes to
- * execute sections of code. There are two possible sample points, "a" and
- * "b", so the _letter_ argument must be one of these values.
- *
- * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
- * example, "cat /sys/devices/lm0/hcd_frrem".
- */
-#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) \
-do { \
- struct hfnum_data _hfnum_; \
- struct dwc2_qtd *_qtd_; \
- \
- _qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \
- qtd_list_entry); \
- if (usb_pipeint(_qtd_->urb->pipe) && \
- (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
- _hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \
- switch (_hfnum_.b.frnum & 0x7) { \
- case 7: \
- (_hcd_)->hfnum_7_samples_##_letter_++; \
- (_hcd_)->hfnum_7_frrem_accum_##_letter_ += \
- _hfnum_.b.frrem; \
- break; \
- case 0: \
- (_hcd_)->hfnum_0_samples_##_letter_++; \
- (_hcd_)->hfnum_0_frrem_accum_##_letter_ += \
- _hfnum_.b.frrem; \
- break; \
- default: \
- (_hcd_)->hfnum_other_samples_##_letter_++; \
- (_hcd_)->hfnum_other_frrem_accum_##_letter_ += \
- _hfnum_.b.frrem; \
- break; \
- } \
- } \
-} while (0)
-#else
-#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) do {} while (0)
-#endif
-
#endif /* __DWC2_HCD_H__ */
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 2c906d8ee465..38391e48351f 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -231,6 +231,7 @@
#define GUID HSOTG_REG(0x003c)
#define GSNPSID HSOTG_REG(0x0040)
#define GHWCFG1 HSOTG_REG(0x0044)
+#define GSNPSID_ID_MASK GENMASK(31, 16)
#define GHWCFG2 HSOTG_REG(0x0048)
#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31)
@@ -309,6 +310,7 @@
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
+#define GHWCFG4_ACG_SUPPORTED BIT(12)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
@@ -320,28 +322,30 @@
#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0
#define GLPMCFG HSOTG_REG(0x0054)
-#define GLPMCFG_INV_SEL_HSIC BIT(31)
-#define GLPMCFG_HSIC_CONNECT BIT(30)
-#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25)
-#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25
-#define GLPMCFG_SEND_LPM BIT(24)
-#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21)
-#define GLPMCFG_RETRY_COUNT_SHIFT 21
-#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17)
-#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17
-#define GLPMCFG_SLEEP_STATE_RESUMEOK BIT(16)
-#define GLPMCFG_PRT_SLEEP_STS BIT(15)
-#define GLPMCFG_LPM_RESP_MASK (0x3 << 13)
-#define GLPMCFG_LPM_RESP_SHIFT 13
+#define GLPMCFG_INVSELHSIC BIT(31)
+#define GLPMCFG_HSICCON BIT(30)
+#define GLPMCFG_RSTRSLPSTS BIT(29)
+#define GLPMCFG_ENBESL BIT(28)
+#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25)
+#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25
+#define GLPMCFG_SNDLPM BIT(24)
+#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21)
+#define GLPMCFG_RETRY_CNT_SHIFT 21
+#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17)
+#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17
+#define GLPMCFG_L1RESUMEOK BIT(16)
+#define GLPMCFG_SLPSTS BIT(15)
+#define GLPMCFG_COREL1RES_MASK (0x3 << 13)
+#define GLPMCFG_COREL1RES_SHIFT 13
#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8)
#define GLPMCFG_HIRD_THRES_SHIFT 8
-#define GLPMCFG_HIRD_THRES_EN (0x10 << 8)
-#define GLPMCFG_EN_UTMI_SLEEP BIT(7)
-#define GLPMCFG_REM_WKUP_EN BIT(6)
+#define GLPMCFG_HIRD_THRES_EN (0x10 << 8)
+#define GLPMCFG_ENBLSLPM BIT(7)
+#define GLPMCFG_BREMOTEWAKE BIT(6)
#define GLPMCFG_HIRD_MASK (0xf << 2)
#define GLPMCFG_HIRD_SHIFT 2
-#define GLPMCFG_APPL_RESP BIT(1)
-#define GLPMCFG_LPM_CAP_EN BIT(0)
+#define GLPMCFG_APPL1RES BIT(1)
+#define GLPMCFG_LPMCAP BIT(0)
#define GPWRDN HSOTG_REG(0x0058)
#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24)
@@ -644,6 +648,10 @@
#define PCGCTL_GATEHCLK BIT(1)
#define PCGCTL_STOPPCLK BIT(0)
+#define PCGCCTL1 HSOTG_REG(0xe04)
+#define PCGCCTL1_TIMER (0x3 << 1)
+#define PCGCCTL1_GATEEN BIT(0)
+
#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000))
/* Host Mode Registers */
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index c4a47496d2fb..f03e41879224 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -252,6 +252,20 @@ static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
p->g_tx_fifo_size[i] = depth_average;
}
+static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg)
+{
+ int val;
+
+ if (hsotg->hw_params.hibernation)
+ val = 2;
+ else if (hsotg->hw_params.power_optimized)
+ val = 1;
+ else
+ val = 0;
+
+ hsotg->params.power_down = val;
+}
+
/**
* dwc2_set_default_params() - Set all core parameters to their
* auto-detected default values.
@@ -266,21 +280,27 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
dwc2_set_param_phy_type(hsotg);
dwc2_set_param_speed(hsotg);
dwc2_set_param_phy_utmi_width(hsotg);
+ dwc2_set_param_power_down(hsotg);
p->phy_ulpi_ddr = false;
p->phy_ulpi_ext_vbus = false;
p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
p->i2c_enable = hw->i2c_enable;
+ p->acg_enable = hw->acg_enable;
p->ulpi_fs_ls = false;
p->ts_dline = false;
p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a);
p->uframe_sched = true;
p->external_id_pin_ctl = false;
- p->hibernation = false;
+ p->lpm = true;
+ p->lpm_clock_gating = true;
+ p->besl = true;
+ p->hird_threshold_en = true;
+ p->hird_threshold = 4;
p->max_packet_count = hw->max_packet_count;
p->max_transfer_size = hw->max_transfer_size;
- p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT;
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
@@ -463,6 +483,38 @@ static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
dwc2_set_param_phy_utmi_width(hsotg);
}
+static void dwc2_check_param_power_down(struct dwc2_hsotg *hsotg)
+{
+ int param = hsotg->params.power_down;
+
+ switch (param) {
+ case DWC2_POWER_DOWN_PARAM_NONE:
+ break;
+ case DWC2_POWER_DOWN_PARAM_PARTIAL:
+ if (hsotg->hw_params.power_optimized)
+ break;
+ dev_dbg(hsotg->dev,
+ "Partial power down isn't supported by HW\n");
+ param = DWC2_POWER_DOWN_PARAM_NONE;
+ break;
+ case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+ if (hsotg->hw_params.hibernation)
+ break;
+ dev_dbg(hsotg->dev,
+ "Hibernation isn't supported by HW\n");
+ param = DWC2_POWER_DOWN_PARAM_NONE;
+ break;
+ default:
+ dev_err(hsotg->dev,
+ "%s: Invalid parameter power_down=%d\n",
+ __func__, param);
+ param = DWC2_POWER_DOWN_PARAM_NONE;
+ break;
+ }
+
+ hsotg->params.power_down = param;
+}
+
static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
{
int fifo_count;
@@ -523,10 +575,19 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
dwc2_check_param_phy_type(hsotg);
dwc2_check_param_speed(hsotg);
dwc2_check_param_phy_utmi_width(hsotg);
+ dwc2_check_param_power_down(hsotg);
CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo);
CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo);
CHECK_BOOL(i2c_enable, hw->i2c_enable);
+ CHECK_BOOL(acg_enable, hw->acg_enable);
CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a));
+ CHECK_BOOL(lpm, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_80a));
+ CHECK_BOOL(lpm, hw->lpm_mode);
+ CHECK_BOOL(lpm_clock_gating, hsotg->params.lpm);
+ CHECK_BOOL(besl, hsotg->params.lpm);
+ CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a));
+ CHECK_BOOL(hird_threshold_en, hsotg->params.lpm);
+ CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0);
CHECK_RANGE(max_packet_count,
15, hw->max_packet_count,
hw->max_packet_count);
@@ -579,19 +640,15 @@ 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);
+ dwc2_force_mode(hsotg, true);
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
hptxfsiz = dwc2_readl(hsotg->regs + 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) >>
@@ -606,14 +663,13 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
- bool forced;
u32 gnptxfsiz;
int fifo, fifo_count;
if (hsotg->dr_mode == USB_DR_MODE_HOST)
return;
- forced = dwc2_force_mode_if_needed(hsotg, false);
+ dwc2_force_mode(hsotg, false);
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
@@ -625,9 +681,6 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT;
}
- if (forced)
- dwc2_clear_force_mode(hsotg);
-
hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
}
@@ -646,14 +699,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
/*
* Attempt to ensure this device is really a DWC_otg Controller.
* Read and verify the GSNPSID register contents. The value should be
- * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3",
- * as in "OTG version 2.xx" or "OTG version 3.xx".
+ * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
*/
+
hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID);
- if ((hw->snpsid & 0xfffff000) != 0x4f542000 &&
- (hw->snpsid & 0xfffff000) != 0x4f543000 &&
- (hw->snpsid & 0xffff0000) != 0x55310000 &&
- (hw->snpsid & 0xffff0000) != 0x55320000) {
+ if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
hw->snpsid);
return -ENODEV;
@@ -706,6 +758,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
GHWCFG3_DFIFO_DEPTH_SHIFT;
+ hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN);
/* hwcfg4 */
hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
@@ -715,8 +768,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
GHWCFG4_NUM_IN_EPS_SHIFT;
hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
+ hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER);
hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
+ hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED);
/* fifo sizes */
hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index 3ecc951a1aea..7f21747007f1 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -83,7 +83,6 @@ static void dwc2_pci_remove(struct pci_dev *pci)
platform_device_unregister(glue->dwc2);
usb_phy_generic_unregister(glue->phy);
- kfree(glue);
pci_set_drvdata(pci, NULL);
}
@@ -105,10 +104,17 @@ static int dwc2_pci_probe(struct pci_dev *pci,
pci_set_master(pci);
+ phy = usb_phy_generic_register();
+ if (IS_ERR(phy)) {
+ dev_err(dev, "error registering generic PHY (%ld)\n",
+ PTR_ERR(phy));
+ return PTR_ERR(phy);
+ }
+
dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
if (!dwc2) {
dev_err(dev, "couldn't allocate dwc2 device\n");
- return -ENOMEM;
+ goto err;
}
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
@@ -125,32 +131,25 @@ static int dwc2_pci_probe(struct pci_dev *pci,
ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc2 device\n");
- return ret;
+ goto err;
}
dwc2->dev.parent = dev;
- phy = usb_phy_generic_register();
- if (IS_ERR(phy)) {
- dev_err(dev, "error registering generic PHY (%ld)\n",
- PTR_ERR(phy));
- return PTR_ERR(phy);
- }
-
ret = dwc2_pci_quirks(pci, dwc2);
if (ret)
goto err;
+ glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ goto err;
+
ret = platform_device_add(dwc2);
if (ret) {
dev_err(dev, "failed to register dwc2 device\n");
goto err;
}
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
- if (!glue)
- return -ENOMEM;
-
glue->phy = phy;
glue->dwc2 = dwc2;
pci_set_drvdata(pci, glue);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 4703478f702f..4c0819554bcd 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -382,8 +382,10 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (!dev->dev.dma_mask)
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
- if (retval)
+ if (retval) {
+ dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval);
return retval;
+ }
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
@@ -425,13 +427,20 @@ static int dwc2_driver_probe(struct platform_device *dev)
* Reset before dwc2_get_hwparams() then it could get power-on real
* reset value form registers.
*/
- dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
+ if (retval)
+ goto error;
/* Detect config values from hardware */
retval = dwc2_get_hwparams(hsotg);
if (retval)
goto error;
+ /*
+ * For OTG cores, set the force mode bits to reflect the value
+ * of dr_mode. Force mode bits should not be touched at any
+ * other time after this.
+ */
dwc2_force_dr_mode(hsotg);
retval = dwc2_init_params(hsotg);
@@ -439,7 +448,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
goto error;
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
- retval = dwc2_gadget_init(hsotg, hsotg->irq);
+ retval = dwc2_gadget_init(hsotg);
if (retval)
goto error;
hsotg->gadget_enabled = 1;
@@ -456,6 +465,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
}
platform_set_drvdata(dev, hsotg);
+ hsotg->hibernated = 0;
dwc2_debugfs_init(hsotg);
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 7ac725038f8d..025bc68094fc 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
-ifneq ($(CONFIG_FTRACE),)
+ifneq ($(CONFIG_TRACING),)
dwc3-y += trace.o
endif
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index e94bf91cc58a..a15648d25e30 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
-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;
@@ -110,13 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work)
unsigned long flags;
int ret;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ dwc3_otg_update(dwc, 0);
+
if (!dwc->desired_dr_role)
return;
if (dwc->desired_dr_role == dwc->current_dr_role)
return;
- if (dwc->dr_mode != USB_DR_MODE_OTG)
+ if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
return;
switch (dwc->current_dr_role) {
@@ -127,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
default:
break;
}
@@ -162,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work)
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_init(dwc);
+ dwc3_otg_update(dwc, 0);
+ break;
default:
break;
}
+
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -229,7 +244,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
- return 0;
+ goto done;
udelay(1);
} while (--retries);
@@ -238,6 +253,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
phy_exit(dwc->usb2_generic_phy);
return -ETIMEDOUT;
+
+done:
+ /*
+ * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
+ * we must wait at least 50ms before accessing the PHY domain
+ * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ */
+ if (dwc3_is_usb31(dwc))
+ msleep(50);
+
+ return 0;
}
/*
@@ -348,7 +374,7 @@ 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;
@@ -365,7 +391,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -846,6 +872,43 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ /*
+ * Must config both number of packets and max burst settings to enable
+ * RX and/or TX threshold.
+ */
+ if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
+ u8 rx_maxburst = dwc->rx_max_burst_prd;
+ u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
+ u8 tx_maxburst = dwc->tx_max_burst_prd;
+
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC31_RXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num);
+
+ reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC31_TXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num);
+
+ reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ }
+
return 0;
err4:
@@ -1016,6 +1079,10 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -1050,6 +1117,14 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&hird_threshold);
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
+ device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
+ &rx_thr_num_pkt_prd);
+ device_property_read_u8(dev, "snps,rx-max-burst-prd",
+ &rx_max_burst_prd);
+ device_property_read_u8(dev, "snps,tx-thr-num-pkt-prd",
+ &tx_thr_num_pkt_prd);
+ device_property_read_u8(dev, "snps,tx-max-burst-prd",
+ &tx_max_burst_prd);
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
@@ -1100,6 +1175,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
+ dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
+ dwc->rx_max_burst_prd = rx_max_burst_prd;
+
+ dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
+ dwc->tx_max_burst_prd = tx_max_burst_prd;
+
dwc->imod_interval = 0;
}
@@ -1326,6 +1407,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (!PMSG_IS_AUTO(msg))
dwc3_core_exit(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* do nothing during runtime_suspend */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_suspend(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ dwc3_otg_exit(dwc);
+ dwc3_core_exit(dwc);
+ break;
default:
/* do nothing */
break;
@@ -1345,6 +1440,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1355,7 +1451,29 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
ret = dwc3_core_init(dwc);
if (ret)
return ret;
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ }
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* nothing to do on runtime_resume */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_set_prtcap(dwc, dwc->current_dr_role);
+
+ dwc3_otg_init(dwc);
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
+ dwc3_otg_host_init(dwc);
+ } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_resume(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
}
+
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 860d2bc184d1..4f3b43809917 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -58,6 +58,11 @@
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+/* Controller's role while using the OTG block */
+#define DWC3_OTG_ROLE_IDLE 0
+#define DWC3_OTG_ROLE_HOST 1
+#define DWC3_OTG_ROLE_DEVICE 2
+
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
@@ -100,6 +105,11 @@
#define DWC3_GHWPARAMS7 0xc15c
#define DWC3_GDBGFIFOSPACE 0xc160
#define DWC3_GDBGLTSSM 0xc164
+#define DWC3_GDBGBMU 0xc16c
+#define DWC3_GDBGLSPMUX 0xc170
+#define DWC3_GDBGLSP 0xc174
+#define DWC3_GDBGEPINFO0 0xc178
+#define DWC3_GDBGEPINFO1 0xc17c
#define DWC3_GPRTBIMAP_HS0 0xc180
#define DWC3_GPRTBIMAP_HS1 0xc184
#define DWC3_GPRTBIMAP_FS0 0xc188
@@ -173,6 +183,26 @@
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
+/* Global RX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
+/* Global TX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN BIT(16)
@@ -201,6 +231,15 @@
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+/* Global Status Register */
+#define DWC3_GSTS_OTG_IP BIT(10)
+#define DWC3_GSTS_BC_IP BIT(9)
+#define DWC3_GSTS_ADP_IP BIT(8)
+#define DWC3_GSTS_HOST_IP BIT(7)
+#define DWC3_GSTS_DEVICE_IP BIT(6)
+#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
+#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
@@ -241,6 +280,8 @@
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
/* Global TX Fifo Size Register */
+#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
+#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
@@ -286,6 +327,11 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14)
+#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13)
+#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12)
+#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11)
+#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* Global HWPARAMS7 Register */
@@ -467,6 +513,74 @@
#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF BIT(5)
+#define DWC3_OCFG_HIBDISMASK BIT(4)
+#define DWC3_OCFG_SFTRSTMASK BIT(3)
+#define DWC3_OCFG_OTGVERSION BIT(2)
+#define DWC3_OCFG_HNPCAP BIT(1)
+#define DWC3_OCFG_SRPCAP BIT(0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR 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_TERMSELIDPULSE BIT(2)
+#define DWC3_OCTL_DEVSETHNPEN BIT(1)
+#define DWC3_OCTL_HSTSETHNPEN BIT(0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE BIT(31)
+#define DWC3_OEVT_XHCIRUNSTPSET BIT(27)
+#define DWC3_OEVT_DEVRUNSTPSET BIT(26)
+#define DWC3_OEVT_HIBENTRY BIT(25)
+#define DWC3_OEVT_CONIDSTSCHNG BIT(24)
+#define DWC3_OEVT_HRRCONFNOTIF BIT(23)
+#define DWC3_OEVT_HRRINITNOTIF BIT(22)
+#define DWC3_OEVT_ADEVIDLE BIT(21)
+#define DWC3_OEVT_ADEVBHOSTEND BIT(20)
+#define DWC3_OEVT_ADEVHOST BIT(19)
+#define DWC3_OEVT_ADEVHNPCHNG BIT(18)
+#define DWC3_OEVT_ADEVSRPDET BIT(17)
+#define DWC3_OEVT_ADEVSESSENDDET BIT(16)
+#define DWC3_OEVT_BDEVBHOSTEND BIT(11)
+#define DWC3_OEVT_BDEVHNPCHNG BIT(10)
+#define DWC3_OEVT_BDEVSESSVLDDET BIT(9)
+#define DWC3_OEVT_BDEVVBUSCHNG BIT(8)
+#define DWC3_OEVT_BSESSVLD BIT(3)
+#define DWC3_OEVT_HSTNEGSTS BIT(2)
+#define DWC3_OEVT_SESREQSTS BIT(1)
+#define DWC3_OEVT_ERROR BIT(0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26)
+#define DWC3_OEVTEN_HIBENTRYEN BIT(25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22)
+#define DWC3_OEVTEN_ADEVIDLEEN BIT(21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20)
+#define DWC3_OEVTEN_ADEVHOSTEN BIT(19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18)
+#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16)
+#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP BIT(13)
+#define DWC3_OSTS_XHCIRUNSTP BIT(12)
+#define DWC3_OSTS_PERIPHERALSTATE BIT(4)
+#define DWC3_OSTS_XHCIPRTPOWER BIT(3)
+#define DWC3_OSTS_BSESVLD BIT(2)
+#define DWC3_OSTS_VBUSVLD BIT(1)
+#define DWC3_OSTS_CONIDSTS BIT(0)
+
/* Structures */
struct dwc3_trb;
@@ -781,6 +895,10 @@ struct dwc3_scratchpad_array {
* @regs_size: address space size
* @fladj: frame length adjustment
* @irq_gadget: peripheral controller's IRQ number
+ * @otg_irq: IRQ number for OTG IRQs
+ * @current_otg_role: current role of operation while using the OTG block
+ * @desired_otg_role: desired role of operation while using the OTG block
+ * @otg_restart_host: flag that OTG controller needs to restart host
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -816,6 +934,10 @@ struct dwc3_scratchpad_array {
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
+ * @rx_thr_num_pkt_prd: periodic ESS receive packet count
+ * @rx_max_burst_prd: max periodic ESS receive burst size
+ * @tx_thr_num_pkt_prd: periodic ESS transmit packet count
+ * @tx_max_burst_prd: max periodic ESS transmit burst size
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @delayed_status: true when gadget driver asks for delayed status
@@ -914,6 +1036,10 @@ struct dwc3 {
u32 fladj;
u32 irq_gadget;
+ u32 otg_irq;
+ u32 current_otg_role;
+ u32 desired_otg_role;
+ bool otg_restart_host;
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
@@ -979,6 +1105,10 @@ struct dwc3 {
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
const char *hsphy_interface;
@@ -1175,6 +1305,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
@@ -1192,6 +1323,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc)
bool dwc3_has_imod(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
@@ -1235,11 +1369,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_drd_init(struct dwc3 *dwc);
void dwc3_drd_exit(struct dwc3 *dwc);
+void dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus);
+void dwc3_otg_host_init(struct dwc3 *dwc);
#else
static inline int dwc3_drd_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_drd_exit(struct dwc3 *dwc)
{ }
+static inline void dwc3_otg_init(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{ }
+static inline void dwc3_otg_host_init(struct dwc3 *dwc)
+{ }
#endif
/* power management interface */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 00e65530c81e..2f07be1e1f31 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -81,6 +81,11 @@ static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(GHWPARAMS7),
dump_register(GDBGFIFOSPACE),
dump_register(GDBGLTSSM),
+ dump_register(GDBGBMU),
+ dump_register(GDBGLSPMUX),
+ dump_register(GDBGLSP),
+ dump_register(GDBGEPINFO0),
+ dump_register(GDBGEPINFO1),
dump_register(GPRTBIMAP_HS0),
dump_register(GPRTBIMAP_HS1),
dump_register(GPRTBIMAP_FS0),
@@ -487,8 +492,8 @@ static const struct file_operations dwc3_link_state_fops = {
};
struct dwc3_ep_file_map {
- char name[25];
- int (*show)(struct seq_file *s, void *unused);
+ const char name[25];
+ const struct file_operations *const fops;
};
static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
@@ -596,7 +601,7 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused)
return 0;
}
-static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused)
+static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
{
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
@@ -632,7 +637,7 @@ out:
return 0;
}
-static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
+static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
{
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
@@ -670,58 +675,39 @@ out:
return 0;
}
-static struct dwc3_ep_file_map map[] = {
- { "tx_fifo_queue", dwc3_tx_fifo_queue_show, },
- { "rx_fifo_queue", dwc3_rx_fifo_queue_show, },
- { "tx_request_queue", dwc3_tx_request_queue_show, },
- { "rx_request_queue", dwc3_rx_request_queue_show, },
- { "rx_info_queue", dwc3_rx_info_queue_show, },
- { "descriptor_fetch_queue", dwc3_descriptor_fetch_queue_show, },
- { "event_queue", dwc3_event_queue_show, },
- { "transfer_type", dwc3_ep_transfer_type_show, },
- { "trb_ring", dwc3_ep_trb_ring_show, },
+DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type);
+DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring);
+
+static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
+ { "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, },
+ { "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, },
+ { "tx_request_queue", &dwc3_tx_request_queue_fops, },
+ { "rx_request_queue", &dwc3_rx_request_queue_fops, },
+ { "rx_info_queue", &dwc3_rx_info_queue_fops, },
+ { "descriptor_fetch_queue", &dwc3_descriptor_fetch_queue_fops, },
+ { "event_queue", &dwc3_event_queue_fops, },
+ { "transfer_type", &dwc3_transfer_type_fops, },
+ { "trb_ring", &dwc3_trb_ring_fops, },
};
-static int dwc3_endpoint_open(struct inode *inode, struct file *file)
-{
- const char *file_name = file_dentry(file)->d_iname;
- struct dwc3_ep_file_map *f_map;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(map); i++) {
- f_map = &map[i];
-
- if (strcmp(f_map->name, file_name) == 0)
- break;
- }
-
- return single_open(file, f_map->show, inode->i_private);
-}
-
-static const struct file_operations dwc3_endpoint_fops = {
- .open = dwc3_endpoint_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void dwc3_debugfs_create_endpoint_file(struct dwc3_ep *dep,
- struct dentry *parent, int type)
-{
- struct dentry *file;
- struct dwc3_ep_file_map *ep_file = &map[type];
-
- file = debugfs_create_file(ep_file->name, S_IRUGO, parent, dep,
- &dwc3_endpoint_fops);
-}
-
static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
struct dentry *parent)
{
int i;
- for (i = 0; i < ARRAY_SIZE(map); i++)
- dwc3_debugfs_create_endpoint_file(dep, parent, i);
+ for (i = 0; i < ARRAY_SIZE(dwc3_ep_file_map); i++) {
+ const struct file_operations *fops = dwc3_ep_file_map[i].fops;
+ const char *name = dwc3_ep_file_map[i].name;
+
+ debugfs_create_file(name, S_IRUGO, parent, dep, fops);
+ }
}
static void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep,
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index cc8ab9a8e9d2..1d8c557e97e0 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -8,22 +8,423 @@
*/
#include <linux/extcon.h>
+#include <linux/platform_device.h>
#include "debug.h"
#include "core.h"
#include "gadget.h"
-static void dwc3_drd_update(struct dwc3 *dwc)
+static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg &= ~(disable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg |= (enable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_clear_events(struct dwc3 *dwc)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
+ DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \
+ DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \
+ DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \
+ DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \
+ DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \
+ DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVBHOSTENDEN | \
+ DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \
+ DWC3_OEVTEN_BDEVVBUSCHNGEN)
+
+static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
{
+ struct dwc3 *dwc = _dwc;
+
+ spin_lock(&dwc->lock);
+ if (dwc->otg_restart_host) {
+ dwc3_otg_host_init(dwc);
+ dwc->otg_restart_host = 0;
+ }
+
+ spin_unlock(&dwc->lock);
+
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
+{
+ u32 reg;
+ struct dwc3 *dwc = _dwc;
+ irqreturn_t ret = IRQ_NONE;
+
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ if (reg) {
+ /* ignore non OTG events, we can't disable them in OEVTEN */
+ if (!(reg & DWC3_OTG_ALL_EVENTS)) {
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ return IRQ_NONE;
+ }
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
+ !(reg & DWC3_OEVT_DEVICEMODE))
+ dwc->otg_restart_host = 1;
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static void dwc3_otgregs_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Prevent host/device reset from resetting OTG core.
+ * If we don't do this then xhci_reset (USBCMD.HCRST) will reset
+ * the signal outputs sent to the PHY, the OTG FSM logic of the
+ * core and also the resets to the VBUS filters inside the core.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg |= DWC3_OCFG_SFTRSTMASK;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /* Disable hibernation for simplicity */
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /*
+ * Initialize OTG registers as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+ /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ /* OEVT = FFFF */
+ dwc3_otg_clear_events(dwc);
+ /* OEVTEN = 0 */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */
+ dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /*
+ * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
+ * OCTL.HNPReq = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PERIMODE;
+ reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
+ DWC3_OCTL_HNPREQ);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+static int dwc3_otg_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "otg");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing OTG IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
+void dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * As per Figure 11-4 OTG Driver Overall Programming Flow,
+ * block "Initialize GCTL for OTG operation".
+ */
+ /* GCTL.PrtCapDir=2'b11 */
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+ /* GUSB2PHYCFG0.SusPHY=0 */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ /* Initialize OTG registers */
+ dwc3_otgregs_init(dwc);
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+}
+
+/* should be called before Host controller driver is started */
+void dwc3_otg_host_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-10 A-Device Flow Diagram */
+ /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */
+
+ /*
+ * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
+ * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
+ DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+
+ /*
+ * OCFG.DisPrtPwrCutoff = 0/1
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /*
+ * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
+ * We don't want SRP/HNP for simple dual-role so leave
+ * these disabled.
+ */
+
+ /*
+ * OEVTEN.OTGADevHostEvntEn = 1
+ * OEVTEN.OTGADevSessEndDetEvntEn = 1
+ * We don't want HNP/role-swap so leave these disabled.
+ */
+
+ /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PRTPWRCTL;
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+/* should be called after Host controller driver is stopped */
+static void dwc3_otg_host_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from A-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0
+ * OEVTEN.OTGADevSessEndDetEvntEn=0,
+ * OEVTEN.OTGADevHostEvntEn = 0
+ * But we don't disable any OTG events
+ */
+
+ /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+/* should be called before the gadget controller driver is started */
+static void dwc3_otg_device_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-20 B-Device Flow Diagram */
+
+ /*
+ * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
+ * but we keep them 0 for simple dual-role operation.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ /* OCFG.OTGSftRstMsk = 0/1 */
+ reg |= DWC3_OCFG_SFTRSTMASK;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ /*
+ * OCTL.PeriMode = 1
+ * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
+ * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PERIMODE;
+ reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
+ DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
+ dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN);
+ /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+ /* GCTL.GblHibernationEn = 0. Already 0. */
+}
+
+/* should be called after the gadget controller driver is stopped */
+static void dwc3_otg_device_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from B-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * OEVTEN.OTGBDevHNPChngEvntEn = 0
+ * OEVTEN.OTGBDevVBusChngEvntEn = 0
+ * OEVTEN.OTGBDevBHostEndEvntEn = 0
+ */
+ dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN |
+ DWC3_OEVTEN_BDEVVBUSCHNGEN |
+ DWC3_OEVTEN_BDEVBHOSTENDEN);
+
+ /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
+ reg |= DWC3_OCTL_PERIMODE;
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{
+ int ret;
+ u32 reg;
int id;
+ unsigned long flags;
- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
- if (id < 0)
- id = 0;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
- dwc3_set_mode(dwc, id ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ /* don't do anything if debug user changed role to not OTG */
+ if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG)
+ return;
+
+ if (!ignore_idstatus) {
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ id = !!(reg & DWC3_OSTS_CONIDSTS);
+
+ dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
+ DWC3_OTG_ROLE_HOST;
+ }
+
+ if (dwc->desired_otg_role == dwc->current_otg_role)
+ return;
+
+ switch (dwc->current_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ dwc3_host_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otg_host_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ dwc3_gadget_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_otg_device_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc->current_otg_role = dwc->desired_otg_role;
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (dwc->desired_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_host_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to initialize host\n");
+ } else {
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, true);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_HOST);
+ }
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_device_init(dwc);
+ dwc3_event_buffers_setup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, false);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_DEVICE);
+ ret = dwc3_gadget_init(dwc);
+ if (ret)
+ dev_err(dwc->dev, "failed to initialize peripheral\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void dwc3_drd_update(struct dwc3 *dwc)
+{
+ int id;
+
+ if (dwc->edev) {
+ id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+ if (id < 0)
+ id = 0;
+ dwc3_set_mode(dwc, id ?
+ DWC3_GCTL_PRTCAP_HOST :
+ DWC3_GCTL_PRTCAP_DEVICE);
+ }
}
static int dwc3_drd_notifier(struct notifier_block *nb,
@@ -40,11 +441,11 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
int dwc3_drd_init(struct dwc3 *dwc)
{
- int ret;
+ int ret, irq;
- if (dwc->dev->of_node) {
- if (of_property_read_bool(dwc->dev->of_node, "extcon"))
- dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
+ if (dwc->dev->of_node &&
+ of_property_read_bool(dwc->dev->of_node, "extcon")) {
+ dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
@@ -56,19 +457,71 @@ int dwc3_drd_init(struct dwc3 *dwc)
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
}
- }
- dwc3_drd_update(dwc);
+ dwc3_drd_update(dwc);
+ } else {
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+ dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;
+
+ /* use OTG block to get ID event */
+ irq = dwc3_otg_get_irq(dwc);
+ if (irq < 0)
+ return irq;
+
+ dwc->otg_irq = irq;
+
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+
+ ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq,
+ dwc3_otg_thread_irq,
+ IRQF_SHARED, "dwc3-otg", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ dwc->otg_irq, ret);
+ ret = -ENODEV;
+ return ret;
+ }
+
+ dwc3_otg_init(dwc);
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ }
return 0;
}
void dwc3_drd_exit(struct dwc3 *dwc)
{
- extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
- &dwc->edev_nb);
+ unsigned long flags;
+
+ if (dwc->edev)
+ extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
+ &dwc->edev_nb);
+
+ cancel_work_sync(&dwc->drd_work);
+
+ /* debug user might have changed role, clean based on current role */
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_gadget_exit(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
+ default:
+ break;
+ }
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- flush_work(&dwc->drd_work);
- dwc3_gadget_exit(dwc);
+ if (!dwc->edev)
+ free_irq(dwc->otg_irq, dwc);
}
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index e54c3622eb28..cb2ee96fd3e8 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -27,6 +27,7 @@ struct dwc3_of_simple {
struct clk **clks;
int num_clocks;
struct reset_control *resets;
+ bool pulse_resets;
};
static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
@@ -83,6 +84,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
int ret;
int i;
+ bool shared_resets = false;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
@@ -91,16 +93,28 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, simple);
simple->dev = dev;
- simple->resets = of_reset_control_array_get_optional_exclusive(np);
+ if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
+ of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
+ shared_resets = true;
+ simple->pulse_resets = true;
+ }
+
+ simple->resets = of_reset_control_array_get(np, shared_resets, true);
if (IS_ERR(simple->resets)) {
ret = PTR_ERR(simple->resets);
dev_err(dev, "failed to get device resets, err=%d\n", ret);
return ret;
}
- ret = reset_control_deassert(simple->resets);
- if (ret)
- goto err_resetc_put;
+ if (simple->pulse_resets) {
+ ret = reset_control_reset(simple->resets);
+ if (ret)
+ goto err_resetc_put;
+ } else {
+ ret = reset_control_deassert(simple->resets);
+ if (ret)
+ goto err_resetc_put;
+ }
ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np,
"clocks", "#clock-cells"));
@@ -124,7 +138,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
return 0;
err_resetc_assert:
- reset_control_assert(simple->resets);
+ if (!simple->pulse_resets)
+ reset_control_assert(simple->resets);
err_resetc_put:
reset_control_put(simple->resets);
@@ -145,7 +160,9 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
}
simple->num_clocks = 0;
- reset_control_assert(simple->resets);
+ if (!simple->pulse_resets)
+ reset_control_assert(simple->resets);
+
reset_control_put(simple->resets);
pm_runtime_put_sync(dev);
@@ -196,6 +213,8 @@ static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
+ { .compatible = "amlogic,meson-axg-dwc3" },
+ { .compatible = "amlogic,meson-gxl-dwc3" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 3ba11136ebf0..c961a94d136b 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -222,7 +222,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
- return ret;
+ goto err;
}
dwc->pci = pci;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 18be31d5743a..5a991bca8ed7 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -814,7 +814,7 @@ out:
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- struct dwc3_request *r = NULL;
+ struct dwc3_request *r;
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2bda4eb1e9ac..550ee952c0d1 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1858,7 +1858,11 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ if (dwc3_is_usb31(dwc))
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc);
@@ -1950,6 +1954,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int epnum;
+ u32 tmo_eps = 0;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1960,6 +1965,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
struct dwc3_ep *dep = dwc->eps[epnum];
+ int ret;
if (!dep)
continue;
@@ -1967,9 +1973,24 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
continue;
- wait_event_lock_irq(dep->wait_end_transfer,
- !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
- dwc->lock);
+ ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock, msecs_to_jiffies(5));
+
+ if (ret <= 0) {
+ /* Timed out or interrupted! There's nothing much
+ * we can do so we just log here and print which
+ * endpoints timed out at the end.
+ */
+ tmo_eps |= 1 << epnum;
+ dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
+ }
+ }
+
+ if (tmo_eps) {
+ dev_err(dwc->dev,
+ "end transfer timed out on endpoints 0x%x [bitmap]\n",
+ tmo_eps);
}
out:
@@ -2023,7 +2044,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
reg |= DWC3_DCFG_SUPERSPEED;
break;
case USB_SPEED_SUPER_PLUS:
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ if (dwc3_is_usb31(dwc))
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED;
break;
default:
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
@@ -2101,7 +2125,10 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num));
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ else
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 77c7ecca816a..63a7cb87514a 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1422,11 +1422,12 @@ static int count_ext_compat(struct usb_configuration *c)
return res;
}
-static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
+static int fill_ext_compat(struct usb_configuration *c, u8 *buf)
{
int i, count;
count = 16;
+ buf += 16;
for (i = 0; i < c->next_interface_id; ++i) {
struct usb_function *f;
int j;
@@ -1449,10 +1450,12 @@ static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
buf += 23;
}
count += 24;
- if (count >= 4096)
- return;
+ if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
}
}
+
+ return count;
}
static int count_ext_prop(struct usb_configuration *c, int interface)
@@ -1497,25 +1500,21 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
struct usb_os_desc *d;
struct usb_os_desc_ext_prop *ext_prop;
int j, count, n, ret;
- u8 *start = buf;
f = c->interface[interface];
+ count = 10; /* header length */
+ buf += 10;
for (j = 0; j < f->os_desc_n; ++j) {
if (interface != f->os_desc_table[j].if_id)
continue;
d = f->os_desc_table[j].os_desc;
if (d)
list_for_each_entry(ext_prop, &d->ext_prop, entry) {
- /* 4kB minus header length */
- n = buf - start;
- if (n >= 4086)
- return 0;
-
- count = ext_prop->data_len +
+ n = ext_prop->data_len +
ext_prop->name_len + 14;
- if (count > 4086 - n)
- return -EINVAL;
- usb_ext_prop_put_size(buf, count);
+ if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
+ usb_ext_prop_put_size(buf, n);
usb_ext_prop_put_type(buf, ext_prop->type);
ret = usb_ext_prop_put_name(buf, ext_prop->name,
ext_prop->name_len);
@@ -1541,11 +1540,12 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
default:
return -EINVAL;
}
- buf += count;
+ buf += n;
+ count += n;
}
}
- return 0;
+ return count;
}
/*
@@ -1827,6 +1827,7 @@ unknown:
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
+ w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
memset(buf, 0, w_length);
buf[5] = 0x01;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
@@ -1834,24 +1835,16 @@ unknown:
if (w_index != 0x4 || (w_value >> 8))
break;
buf[6] = w_index;
- if (w_length == 0x10) {
- /* Number of ext compat interfaces */
- count = count_ext_compat(os_desc_cfg);
- buf[8] = count;
- count *= 24; /* 24 B/ext compat desc */
- count += 16; /* header */
- put_unaligned_le32(count, buf);
- value = w_length;
- } else {
- /* "extended compatibility ID"s */
- count = count_ext_compat(os_desc_cfg);
- buf[8] = count;
- count *= 24; /* 24 B/ext compat desc */
- count += 16; /* header */
- put_unaligned_le32(count, buf);
- buf += 16;
- fill_ext_compat(os_desc_cfg, buf);
- value = w_length;
+ /* Number of ext compat interfaces */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ if (w_length > 0x10) {
+ value = fill_ext_compat(os_desc_cfg, buf);
+ value = min_t(u16, w_length, value);
}
break;
case USB_RECIP_INTERFACE:
@@ -1859,47 +1852,23 @@ unknown:
break;
interface = w_value & 0xFF;
buf[6] = w_index;
- if (w_length == 0x0A) {
- count = count_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le16(count, buf + 8);
- count = len_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le32(count, buf);
-
- value = w_length;
- } else {
- count = count_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le16(count, buf + 8);
- count = len_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le32(count, buf);
- buf += 10;
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ if (w_length > 0x0A) {
value = fill_ext_prop(os_desc_cfg,
interface, buf);
- if (value < 0)
- return value;
-
- value = w_length;
+ if (value >= 0)
+ value = min_t(u16, w_length, value);
}
break;
}
- if (value >= 0) {
- req->length = value;
- req->context = cdev;
- req->zero = value < w_length;
- value = composite_ep0_queue(cdev, req,
- GFP_ATOMIC);
- if (value < 0) {
- DBG(cdev, "ep_queue --> %d\n", value);
- req->status = 0;
- composite_setup_complete(gadget->ep0,
- req);
- }
- }
- return value;
+ goto check_value;
}
VDBG(cdev,
@@ -1973,6 +1942,7 @@ try_fun_setup:
goto done;
}
+check_value:
/* respond with data transfer before status phase? */
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
@@ -2156,8 +2126,8 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
goto end;
}
- /* OS feature descriptor length <= 4kB */
- cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
+ cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ,
+ GFP_KERNEL);
if (!cdev->os_desc_req->buf) {
ret = -ENOMEM;
usb_ep_free_request(ep0, cdev->os_desc_req);
@@ -2172,6 +2142,7 @@ end:
void composite_dev_cleanup(struct usb_composite_dev *cdev)
{
struct usb_gadget_string_container *uc, *tmp;
+ struct usb_ep *ep, *tmp_ep;
list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
list_del(&uc->list);
@@ -2193,6 +2164,21 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
}
cdev->next_string_id = 0;
device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
+
+ /*
+ * Some UDC backends have a dynamic EP allocation scheme.
+ *
+ * In that case, the dispose() callback is used to notify the
+ * backend that the EPs are no longer in use.
+ *
+ * Note: The UDC backend can remove the EP from the ep_list as
+ * a result, so we need to use the _safe list iterator.
+ */
+ list_for_each_entry_safe(ep, tmp_ep,
+ &cdev->gadget->ep_list, ep_list) {
+ if (ep->ops->dispose)
+ ep->ops->dispose(ep);
+ }
}
static int composite_bind(struct usb_gadget *gadget,
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index d2428a9e8900..0294e4f18873 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -758,9 +758,13 @@ static void ffs_user_copy_worker(struct work_struct *work)
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
if (io_data->read && ret > 0) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(USER_DS);
use_mm(io_data->mm);
ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
unuse_mm(io_data->mm);
+ set_fs(oldfs);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
@@ -3238,7 +3242,7 @@ static int ffs_func_setup(struct usb_function *f,
__ffs_event_add(ffs, FUNCTIONFS_SETUP);
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
- return 0;
+ return USB_GADGET_DELAYED_STATUS;
}
static bool ffs_func_req_match(struct usb_function *f,
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index da81cf16b850..d78dbb73bde8 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1094,7 +1094,7 @@ static int usbg_submit_command(struct f_uas *fu,
struct command_iu *cmd_iu = cmdbuf;
struct usbg_cmd *cmd;
struct usbg_tpg *tpg = fu->tpg;
- struct tcm_usbg_nexus *tv_nexus = tpg->tpg_nexus;
+ struct tcm_usbg_nexus *tv_nexus;
u32 cmd_len;
u16 scsi_tag;
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index ef3d25259b0e..fd5595ac5bf7 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -225,7 +225,7 @@ static int msg_unbind(struct usb_composite_dev *cdev)
static struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
- .max_speed = USB_SPEED_SUPER,
+ .max_speed = USB_SPEED_SUPER_PLUS,
.needs_serial = 1,
.strings = dev_strings,
.bind = msg_bind,
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 075eaaa8a408..27c16399c7e8 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -23,7 +23,8 @@
#include <linux/usb/atmel_usba_udc.h>
#include <linux/delay.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
#include "atmel_usba_udc.h"
#define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \
@@ -415,8 +416,8 @@ static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
static int vbus_is_present(struct usba_udc *udc)
{
- if (gpio_is_valid(udc->vbus_pin))
- return gpio_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted;
+ if (udc->vbus_pin)
+ return gpiod_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted;
/* No Vbus detection: Assume always present */
return 1;
@@ -1975,8 +1976,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,
mutex_lock(&udc->vbus_mutex);
- if (gpio_is_valid(udc->vbus_pin))
- enable_irq(gpio_to_irq(udc->vbus_pin));
+ if (udc->vbus_pin)
+ enable_irq(gpiod_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
udc->vbus_prev = vbus_is_present(udc);
@@ -1990,8 +1991,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,
return 0;
err:
- if (gpio_is_valid(udc->vbus_pin))
- disable_irq(gpio_to_irq(udc->vbus_pin));
+ if (udc->vbus_pin)
+ disable_irq(gpiod_to_irq(udc->vbus_pin));
mutex_unlock(&udc->vbus_mutex);
@@ -2006,8 +2007,8 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
{
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
- if (gpio_is_valid(udc->vbus_pin))
- disable_irq(gpio_to_irq(udc->vbus_pin));
+ if (udc->vbus_pin)
+ disable_irq(gpiod_to_irq(udc->vbus_pin));
if (fifo_mode == 0)
udc->configured_ep = 1;
@@ -2019,7 +2020,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
return 0;
}
-#ifdef CONFIG_OF
static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on)
{
regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN,
@@ -2055,7 +2055,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
{
u32 val;
const char *name;
- enum of_gpio_flags flags;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *pp;
@@ -2075,9 +2074,9 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
udc->num_ep = 0;
- udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
- &flags);
- udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+ udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus",
+ GPIOD_IN);
+ udc->vbus_pin_inverted = gpiod_is_active_low(udc->vbus_pin);
if (fifo_mode == 0) {
pp = NULL;
@@ -2204,75 +2203,10 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
err:
return ERR_PTR(ret);
}
-#else
-static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
- struct usba_udc *udc)
-{
- return ERR_PTR(-ENOSYS);
-}
-#endif
-
-static struct usba_ep * usba_udc_pdata(struct platform_device *pdev,
- struct usba_udc *udc)
-{
- struct usba_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct usba_ep *eps;
- int i;
-
- if (!pdata)
- return ERR_PTR(-ENXIO);
-
- eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * pdata->num_ep,
- GFP_KERNEL);
- if (!eps)
- return ERR_PTR(-ENOMEM);
-
- udc->gadget.ep0 = &eps[0].ep;
-
- udc->vbus_pin = pdata->vbus_pin;
- udc->vbus_pin_inverted = pdata->vbus_pin_inverted;
- udc->num_ep = pdata->num_ep;
-
- INIT_LIST_HEAD(&eps[0].ep.ep_list);
-
- for (i = 0; i < pdata->num_ep; i++) {
- struct usba_ep *ep = &eps[i];
-
- ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
- ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
- ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
- ep->ep.ops = &usba_ep_ops;
- ep->ep.name = pdata->ep[i].name;
- ep->fifo_size = pdata->ep[i].fifo_size;
- usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
- ep->udc = udc;
- INIT_LIST_HEAD(&ep->queue);
- ep->nr_banks = pdata->ep[i].nr_banks;
- ep->index = pdata->ep[i].index;
- ep->can_dma = pdata->ep[i].can_dma;
- ep->can_isoc = pdata->ep[i].can_isoc;
-
- if (i == 0) {
- ep->ep.caps.type_control = true;
- } else {
- ep->ep.caps.type_iso = ep->can_isoc;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
- }
-
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
-
- if (i)
- list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- }
-
- return eps;
-}
static int usba_udc_probe(struct platform_device *pdev)
{
- struct resource *regs, *fifo;
+ struct resource *res;
struct clk *pclk, *hclk;
struct usba_udc *udc;
int irq, ret, i;
@@ -2284,10 +2218,18 @@ static int usba_udc_probe(struct platform_device *pdev)
udc->gadget = usba_gadget_template;
INIT_LIST_HEAD(&udc->gadget.ep_list);
- regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
- fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
- if (!regs || !fifo)
- return -ENXIO;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
+ udc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(udc->regs))
+ return PTR_ERR(udc->regs);
+ dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n",
+ res, udc->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
+ udc->fifo = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(udc->fifo))
+ return PTR_ERR(udc->fifo);
+ dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -2305,23 +2247,6 @@ static int usba_udc_probe(struct platform_device *pdev)
udc->pdev = pdev;
udc->pclk = pclk;
udc->hclk = hclk;
- udc->vbus_pin = -ENODEV;
-
- ret = -ENOMEM;
- udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
- if (!udc->regs) {
- dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n");
- return ret;
- }
- dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n",
- (unsigned long)regs->start, udc->regs);
- udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo));
- if (!udc->fifo) {
- dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n");
- return ret;
- }
- dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n",
- (unsigned long)fifo->start, udc->fifo);
platform_set_drvdata(pdev, udc);
@@ -2335,10 +2260,7 @@ static int usba_udc_probe(struct platform_device *pdev)
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(pclk);
- if (pdev->dev.of_node)
- udc->usba_ep = atmel_udc_of_init(pdev, udc);
- else
- udc->usba_ep = usba_udc_pdata(pdev, udc);
+ udc->usba_ep = atmel_udc_of_init(pdev, udc);
toggle_bias(udc, 0);
@@ -2354,24 +2276,18 @@ static int usba_udc_probe(struct platform_device *pdev)
}
udc->irq = irq;
- if (gpio_is_valid(udc->vbus_pin)) {
- if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
- irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
- IRQ_NOAUTOEN);
- ret = devm_request_threaded_irq(&pdev->dev,
- gpio_to_irq(udc->vbus_pin), NULL,
+ if (udc->vbus_pin) {
+ irq_set_status_flags(gpiod_to_irq(udc->vbus_pin), IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev,
+ gpiod_to_irq(udc->vbus_pin), NULL,
usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
"atmel_usba_udc", udc);
if (ret) {
- udc->vbus_pin = -ENODEV;
+ udc->vbus_pin = NULL;
dev_warn(&udc->pdev->dev,
"failed to request vbus irq; "
"assuming always on\n");
}
- } else {
- /* gpio_request fail so use -EINVAL for gpio_is_valid */
- udc->vbus_pin = -EINVAL;
- }
}
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
@@ -2423,9 +2339,9 @@ static int usba_udc_suspend(struct device *dev)
* Device may wake up. We stay clocked if we failed
* to request vbus irq, assuming always on.
*/
- if (gpio_is_valid(udc->vbus_pin)) {
+ if (udc->vbus_pin) {
usba_stop(udc);
- enable_irq_wake(gpio_to_irq(udc->vbus_pin));
+ enable_irq_wake(gpiod_to_irq(udc->vbus_pin));
}
out:
@@ -2441,8 +2357,8 @@ static int usba_udc_resume(struct device *dev)
if (!udc->driver)
return 0;
- if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
- disable_irq_wake(gpio_to_irq(udc->vbus_pin));
+ if (device_may_wakeup(dev) && udc->vbus_pin)
+ disable_irq_wake(gpiod_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
mutex_lock(&udc->vbus_mutex);
@@ -2462,7 +2378,7 @@ static struct platform_driver udc_driver = {
.driver = {
.name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
- .of_match_table = of_match_ptr(atmel_udc_dt_ids),
+ .of_match_table = atmel_udc_dt_ids,
},
};
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index 860a00a6fdd0..969ce8f3c3e2 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -7,6 +7,8 @@
#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
#define __LINUX_USB_GADGET_USBA_UDC_H__
+#include <linux/gpio/consumer.h>
+
/* USB register offsets */
#define USBA_CTRL 0x0000
#define USBA_FNUM 0x0004
@@ -323,7 +325,7 @@ struct usba_udc {
struct platform_device *pdev;
const struct usba_udc_errata *errata;
int irq;
- int vbus_pin;
+ struct gpio_desc *vbus_pin;
int vbus_pin_inverted;
int num_ep;
int configured_ep;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index f40d4c13cfa4..03149b9d7ea7 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -151,7 +151,7 @@ static int ep_bd_list_alloc(struct bdc_ep *ep)
if (!bd_table)
goto fail;
- bd_table->start_bd = dma_pool_alloc(bdc->bd_table_pool,
+ bd_table->start_bd = dma_pool_zalloc(bdc->bd_table_pool,
GFP_ATOMIC,
&dma);
if (!bd_table->start_bd) {
@@ -167,7 +167,6 @@ static int ep_bd_list_alloc(struct bdc_ep *ep)
(unsigned long long)bd_table->dma, prev_table);
ep->bd_list.bd_table_array[index] = bd_table;
- memset(bd_table->start_bd, 0, bd_p_tab * sizeof(struct bdc_bd));
if (prev_table)
chain_table(prev_table, bd_table, bd_p_tab);
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 1f8b19d9cf97..50988b21a21b 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1482,7 +1482,7 @@ ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
- return snprintf(buf, PAGE_SIZE, "%s\n", \
+ return scnprintf(buf, PAGE_SIZE, "%s\n", \
usb_speed_string(udc->gadget->param)); \
} \
static DEVICE_ATTR_RO(name)
@@ -1497,7 +1497,7 @@ ssize_t name##_show(struct device *dev, \
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
struct usb_gadget *gadget = udc->gadget; \
\
- return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
+ return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
} \
static DEVICE_ATTR_RO(name)
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index e744d4b7bfed..baf72f95f0f1 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -2366,7 +2366,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
{
int ep = usb_pipeendpoint(urb->pipe);
- return snprintf(buf, size,
+ return scnprintf(buf, size,
"urb/%p %s ep%d%s%s len %d/%d\n",
urb,
({ char *s;
diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
index 26601bf4e7a9..70023d401079 100644
--- a/drivers/usb/gadget/udc/goku_udc.h
+++ b/drivers/usb/gadget/udc/goku_udc.h
@@ -25,7 +25,7 @@ struct goku_udc_regs {
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
-#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */
+#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 7eb2b2b911e5..66143ab8c043 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -533,7 +533,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data)
{
struct ab8500_usb *ab = (struct ab8500_usb *) data;
- enum usb_phy_events event = UX500_MUSB_NONE;
+ enum usb_phy_events event = USB_EVENT_NONE;
/* Link status will not be updated till phy is disabled. */
if (ab->mode == USB_HOST) {
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 74ba88297991..a53b89be5324 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -63,9 +63,9 @@ static void nop_reset(struct usb_phy_generic *nop)
if (!nop->gpiod_reset)
return;
- gpiod_set_value(nop->gpiod_reset, 1);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 1);
usleep_range(10000, 20000);
- gpiod_set_value(nop->gpiod_reset, 0);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 0);
}
/* interface to regulator framework */
@@ -159,7 +159,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
{
struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
- gpiod_set_value(nop->gpiod_reset, 1);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 1);
if (!IS_ERR(nop->clk))
clk_disable_unprepare(nop->clk);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index fbec863350f6..e5aa24c1e4fd 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -578,7 +578,7 @@ static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x)
* It must be called after DP is pulled up, which is used to
* differentiate DCP and CDP.
*/
-enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x)
+static enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x)
{
struct regmap *regmap = x->regmap_anatop;
int val;
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index f668bfb708d3..0e8d23e51732 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -16,7 +16,7 @@
#include <linux/export.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -305,14 +305,10 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy)
static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
{
- unsigned long timeout = 2000;
- do {
- if ((readl(reg) & mask) == result)
- return 0;
- udelay(1);
- timeout--;
- } while (timeout);
- return -1;
+ u32 tmp;
+
+ return readl_poll_timeout(reg, tmp, (tmp & mask) == result,
+ 2000, 6000);
}
static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index cef0e44601f8..4b6b9283fa7b 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -54,6 +54,9 @@
/* big enough to hold our biggest descriptor */
#define USB_COMP_EP0_BUFSIZ 1024
+/* OS feature descriptor length <= 4kB */
+#define USB_COMP_EP0_OS_DESC_BUFSIZ 4096
+
#define USB_MS_TO_HS_INTERVAL(x) (ilog2((x * 1000 / 125)) + 1)
struct usb_configuration;
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 66a5cff7ee14..e3424234b23a 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -129,6 +129,7 @@ struct usb_ep_ops {
int (*enable) (struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc);
int (*disable) (struct usb_ep *ep);
+ void (*dispose) (struct usb_ep *ep);
struct usb_request *(*alloc_request) (struct usb_ep *ep,
gfp_t gfp_flags);