Commit 802fde98 authored by Paul Zimmerman's avatar Paul Zimmerman Committed by Felipe Balbi

usb: dwc3: support new revisions of DWC3 core

Recent cores (>= 1.94a) have a set of new features,
commands and a slightly different programming model.

This patch aims to support those changes.
Signed-off-by: default avatarPaul Zimmerman <paulz@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent d7a46a8d
...@@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) ...@@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
int retries = 10000; int retries = 10000;
u32 reg; 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_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
...@@ -107,6 +124,13 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) ...@@ -107,6 +124,13 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
reg |= DWC3_DCTL_ULSTCHNGREQ(state); reg |= DWC3_DCTL_ULSTCHNGREQ(state);
dwc3_writel(dwc->regs, DWC3_DCTL, reg); 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 */ /* wait for a change in DSTS */
retries = 10000; retries = 10000;
while (--retries) { while (--retries) {
...@@ -266,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd) ...@@ -266,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
return "Clear Stall"; return "Clear Stall";
case DWC3_DEPCMD_SETSTALL: case DWC3_DEPCMD_SETSTALL:
return "Set Stall"; return "Set Stall";
case DWC3_DEPCMD_GETSEQNUMBER: case DWC3_DEPCMD_GETEPSTATE:
return "Get Data Sequence Number"; return "Get Endpoint State";
case DWC3_DEPCMD_SETTRANSFRESOURCE: case DWC3_DEPCMD_SETTRANSFRESOURCE:
return "Set Endpoint Transfer Resource"; return "Set Endpoint Transfer Resource";
case DWC3_DEPCMD_SETEPCONFIG: case DWC3_DEPCMD_SETEPCONFIG:
...@@ -1280,9 +1304,12 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) ...@@ -1280,9 +1304,12 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
goto out; goto out;
} }
/* Recent versions do this automatically */
if (dwc->revision < DWC3_REVISION_194A) {
/* write zeroes to Link Change Request */ /* write zeroes to Link Change Request */
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
/* poll until Link State changes to ON */ /* poll until Link State changes to ON */
timeout = jiffies + msecs_to_jiffies(100); timeout = jiffies + msecs_to_jiffies(100);
...@@ -1326,9 +1353,14 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) ...@@ -1326,9 +1353,14 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) { if (is_on) {
if (dwc->revision <= DWC3_REVISION_187A) {
reg &= ~DWC3_DCTL_TRGTULST_MASK; reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= (DWC3_DCTL_RUN_STOP reg |= DWC3_DCTL_TRGTULST_RX_DET;
| DWC3_DCTL_TRGTULST_RX_DET); }
if (dwc->revision >= DWC3_REVISION_194A)
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
} else { } else {
reg &= ~DWC3_DCTL_RUN_STOP; reg &= ~DWC3_DCTL_RUN_STOP;
} }
...@@ -1468,6 +1500,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g, ...@@ -1468,6 +1500,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
return 0; return 0;
} }
static const struct usb_gadget_ops dwc3_gadget_ops = { static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame, .get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup, .wakeup = dwc3_gadget_wakeup,
...@@ -1962,9 +1995,12 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) ...@@ -1962,9 +1995,12 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
/* after reset -> Default State */ /* after reset -> Default State */
dwc->dev_state = DWC3_DEFAULT_STATE; dwc->dev_state = DWC3_DEFAULT_STATE;
/* Recent versions support automatic phy suspend and don't need this */
if (dwc->revision < DWC3_REVISION_194A) {
/* Resume PHYs */ /* Resume PHYs */
dwc3_gadget_usb2_phy_suspend(dwc, false); dwc3_gadget_usb2_phy_suspend(dwc, false);
dwc3_gadget_usb3_phy_suspend(dwc, false); dwc3_gadget_usb3_phy_suspend(dwc, false);
}
if (dwc->gadget.speed != USB_SPEED_UNKNOWN) if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
dwc3_disconnect_gadget(dwc); dwc3_disconnect_gadget(dwc);
...@@ -2082,8 +2118,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) ...@@ -2082,8 +2118,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
break; break;
} }
/* Suspend unneded PHY */ /* 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); dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
}
dep = dwc->eps[0]; dep = dwc->eps[0];
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
...@@ -2389,6 +2428,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) ...@@ -2389,6 +2428,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
DWC3_DEVTEN_DISCONNEVTEN); DWC3_DEVTEN_DISCONNEVTEN);
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); 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); ret = device_register(&dwc->gadget.dev);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to register gadget device\n"); dev_err(dwc->dev, "failed to register gadget device\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment