usb: dwc3: support new revisions of DWC3 core
authorPaul Zimmerman <Paul.Zimmerman@synopsys.com>
Fri, 27 Apr 2012 10:10:52 +0000 (13:10 +0300)
committerFelipe Balbi <balbi@ti.com>
Sun, 3 Jun 2012 20:08:23 +0000 (23:08 +0300)
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: Paul Zimmerman <paulz@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc3/gadget.c

index 852bde5cc13901a9929c82bb31f2e57cc18e509e..04048333ec1a3e971f86e0d964acf87e12070793 100644 (file)
@@ -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");