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:
commit
6d23ee9caa
|
@ -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";
|
||||
};
|
||||
};
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) >>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue