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.
 -----BEGIN PGP SIGNATURE-----
 
 iQJRBAABCgA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAlq03lYdHGZlbGlwZS5i
 YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQYfDg/+KAupmSBOJDXKIMD6
 MZiIVLhSkBYFM5vwIs2+SwLl4H9wXmvoxqvcZaBH2qk5zWy0wdwnTLLCSDt/dCCy
 Rhrz//KFZStmPP1chJg8fU0yV4Wy4NIif3paTy4P2hEdPcn1XrDoekP+DhLKg6dz
 RbFRxEHnOhGvB4Wo2Rd/H1E6Pi6Vdd7iMCQdoDpatdrsIrplXZ8qnUOvIBY2Xi1K
 LEj7d+SAAEmwGm4QyzDzItR4wqKqhrcQ7Xlvi7pW/22AlJfMD82+Gr0JAnKCeshX
 3+ksnAbDz3p5AXJm5I+T3c4Zyd3BPyZevc72jTFkf0ggwNjMOtUuxTQhCRQSRRwA
 +gofFEvJ/vrWQWZzVhvzZQmTrc8sP2vUaAoGpfIBMBEVqxP89BfMgvf3wU9/if37
 WvICdpZ0o0pOGUkLduhyDIm0/SEBEIAqSY/uU1m5LsZzboR7B3Gm6IWsHhHSMZz8
 Dd8aljQxR1fNnHoqUlc2yu1IiI9PQCNJcY6Waah3CKMgp0EH1KbK00E34oxUQUwF
 Edl6jsOrkv4L4X8BonKuipezwl1TTXjiAK1fE3N8SkorcndIww35kVVLKqFrCQOf
 5VBuGYTgGNCubb2wk8wx/TSBXsM/zpQ6y0uPKg/1icpMmXqFrrv2emyTKa2YS7y8
 KPqBQmz6Cj9g8Sn4OcffWACMiHU=
 =F6OC
 -----END PGP SIGNATURE-----

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.
This commit is contained in:
Greg Kroah-Hartman 2018-03-23 13:33:09 +01:00
commit 6d23ee9caa
38 changed files with 2463 additions and 787 deletions

View File

@ -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";
};
};

View File

@ -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.

View File

@ -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);
@ -239,6 +241,142 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
return ret;
}
/**
* 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.
@ -310,6 +448,44 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
return true;
}
/*
* 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.
@ -317,7 +493,6 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
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");

View File

@ -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

View File

@ -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 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
}
}
/**
* 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
@ -509,6 +642,116 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
return 0;
}
/*
* 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
*
@ -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) {
/*

View File

@ -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);

View File

@ -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 @@ static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
*/
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 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
/* 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 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
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 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
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 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
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 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
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 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
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));
/* 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 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->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;
}

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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 */

View File

@ -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) >>

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 @@ static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused)
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 @@ static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
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,

View File

@ -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_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;
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
/* 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;
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);
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;
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
flush_work(&dwc->drd_work);
dwc3_gadget_exit(dwc);
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;
}
if (!dwc->edev)
free_irq(dwc->otg_irq, dwc);
}

View File

@ -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);

View File

@ -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;

View File

@ -814,7 +814,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
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;

View File

@ -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;

View File

@ -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 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
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 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
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 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
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 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
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 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
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,

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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,
},
};

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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);