|
@@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|
|
int retries = 10000;
|
|
|
u32 reg;
|
|
|
|
|
|
+ /*
|
|
|
+ * Wait until device controller is ready. Only applies to 1.94a and
|
|
|
+ * later RTL.
|
|
|
+ */
|
|
|
+ if (dwc->revision >= DWC3_REVISION_194A) {
|
|
|
+ while (--retries) {
|
|
|
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
|
|
+ if (reg & DWC3_DSTS_DCNRD)
|
|
|
+ udelay(5);
|
|
|
+ else
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (retries <= 0)
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
|
|
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
|
|
|
|
@@ -107,6 +124,13 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
|
|
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
|
|
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
|
|
|
|
|
+ /*
|
|
|
+ * The following code is racy when called from dwc3_gadget_wakeup,
|
|
|
+ * and is not needed, at least on newer versions
|
|
|
+ */
|
|
|
+ if (dwc->revision >= DWC3_REVISION_194A)
|
|
|
+ return 0;
|
|
|
+
|
|
|
/* wait for a change in DSTS */
|
|
|
retries = 10000;
|
|
|
while (--retries) {
|
|
@@ -266,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
|
|
|
return "Clear Stall";
|
|
|
case DWC3_DEPCMD_SETSTALL:
|
|
|
return "Set Stall";
|
|
|
- case DWC3_DEPCMD_GETSEQNUMBER:
|
|
|
- return "Get Data Sequence Number";
|
|
|
+ case DWC3_DEPCMD_GETEPSTATE:
|
|
|
+ return "Get Endpoint State";
|
|
|
case DWC3_DEPCMD_SETTRANSFRESOURCE:
|
|
|
return "Set Endpoint Transfer Resource";
|
|
|
case DWC3_DEPCMD_SETEPCONFIG:
|
|
@@ -1280,9 +1304,12 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- /* write zeroes to Link Change Request */
|
|
|
- reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
|
|
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
|
|
+ /* Recent versions do this automatically */
|
|
|
+ if (dwc->revision < DWC3_REVISION_194A) {
|
|
|
+ /* write zeroes to Link Change Request */
|
|
|
+ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
|
|
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
|
|
+ }
|
|
|
|
|
|
/* poll until Link State changes to ON */
|
|
|
timeout = jiffies + msecs_to_jiffies(100);
|
|
@@ -1326,9 +1353,14 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
|
|
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
|
|
if (is_on) {
|
|
|
- reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
|
|
- reg |= (DWC3_DCTL_RUN_STOP
|
|
|
- | DWC3_DCTL_TRGTULST_RX_DET);
|
|
|
+ if (dwc->revision <= DWC3_REVISION_187A) {
|
|
|
+ reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
|
|
+ reg |= DWC3_DCTL_TRGTULST_RX_DET;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dwc->revision >= DWC3_REVISION_194A)
|
|
|
+ reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
|
|
+ reg |= DWC3_DCTL_RUN_STOP;
|
|
|
} else {
|
|
|
reg &= ~DWC3_DCTL_RUN_STOP;
|
|
|
}
|
|
@@ -1468,6 +1500,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|
|
.get_frame = dwc3_gadget_get_frame,
|
|
|
.wakeup = dwc3_gadget_wakeup,
|
|
@@ -1962,9 +1995,12 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|
|
/* after reset -> Default State */
|
|
|
dwc->dev_state = DWC3_DEFAULT_STATE;
|
|
|
|
|
|
- /* Resume PHYs */
|
|
|
- dwc3_gadget_usb2_phy_suspend(dwc, false);
|
|
|
- dwc3_gadget_usb3_phy_suspend(dwc, false);
|
|
|
+ /* Recent versions support automatic phy suspend and don't need this */
|
|
|
+ if (dwc->revision < DWC3_REVISION_194A) {
|
|
|
+ /* Resume PHYs */
|
|
|
+ dwc3_gadget_usb2_phy_suspend(dwc, false);
|
|
|
+ dwc3_gadget_usb3_phy_suspend(dwc, false);
|
|
|
+ }
|
|
|
|
|
|
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
|
|
|
dwc3_disconnect_gadget(dwc);
|
|
@@ -2082,8 +2118,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- /* Suspend unneded PHY */
|
|
|
- dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
|
|
|
+ /* Recent versions support automatic phy suspend and don't need this */
|
|
|
+ if (dwc->revision < DWC3_REVISION_194A) {
|
|
|
+ /* Suspend unneeded PHY */
|
|
|
+ dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
|
|
|
+ }
|
|
|
|
|
|
dep = dwc->eps[0];
|
|
|
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
|
@@ -2389,6 +2428,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
|
|
DWC3_DEVTEN_DISCONNEVTEN);
|
|
|
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
|
|
|
|
|
+ /* Enable USB2 LPM and automatic phy suspend only on recent versions */
|
|
|
+ if (dwc->revision >= DWC3_REVISION_194A) {
|
|
|
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
|
|
+ reg |= DWC3_DCFG_LPM_CAP;
|
|
|
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
|
|
+
|
|
|
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
|
|
+ reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
|
|
+
|
|
|
+ /* TODO: This should be configurable */
|
|
|
+ reg |= DWC3_DCTL_HIRD_THRES(31);
|
|
|
+
|
|
|
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
|
|
+
|
|
|
+ dwc3_gadget_usb2_phy_suspend(dwc, true);
|
|
|
+ dwc3_gadget_usb3_phy_suspend(dwc, true);
|
|
|
+ }
|
|
|
+
|
|
|
ret = device_register(&dwc->gadget.dev);
|
|
|
if (ret) {
|
|
|
dev_err(dwc->dev, "failed to register gadget device\n");
|