summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-05 15:15:38 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-05 15:15:38 -0700
commit933141509cefd64102a943d61d154c5c53bad889 (patch)
treed36112785cb0742ab3b6d6c91cfd4777978b3220 /drivers/usb/dwc3
parent94ff447cf275170042a7a05b972b6da8b94754ca (diff)
parent6f17f74b3d8345663f5fcfd834145685110075bd (diff)
Merge tag 'dwc3-for-v3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
usb: dwc3: patches for v3.6 merge window lots of changes for the dwc3 driver which will make the driver a lot more stable and some changes which are just cosmetic. The bulk of changes is related to mis-defined macros which were never used before, some fixes to error path which e.g. prevent the driver from initiating two transfer on ep0out in case of stall, some fixes to request dequeueing and the End Transfer Command. We have some changes to U1/U2 handling where we were enabling those transtions at the wrong spot (though we haven't seen a problem from that as of today). A few patches will make it easier to add support for newer releases of the core by adding definitions for new registers and changing the code to act accordingly when certain revisions are detected. There's also the usual comestic changes to make the driver easier to maintain and make it easier for new- comers to understand the driver. Also one patch fixing a double inclusion of a header.
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/core.c13
-rw-r--r--drivers/usb/dwc3/core.h107
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c1
-rw-r--r--drivers/usb/dwc3/ep0.c146
-rw-r--r--drivers/usb/dwc3/gadget.c363
-rw-r--r--drivers/usb/dwc3/gadget.h6
6 files changed, 407 insertions, 229 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 1040bdb8dc8..c34452a7304 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -148,6 +148,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ mdelay(100);
+
/* After PHYs are stable we can take Core out of reset state */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
@@ -255,7 +257,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
*
* Returns 0 on success otherwise negative errno.
*/
-static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
+static int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int n;
@@ -266,6 +268,8 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
evt->buf, (unsigned long long) evt->dma,
evt->length);
+ evt->lpos = 0;
+
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
@@ -285,6 +289,9 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
for (n = 0; n < dwc->num_event_buffers; n++) {
evt = dwc->ev_buffs[n];
+
+ evt->lpos = 0;
+
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
@@ -328,8 +335,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
}
dwc->revision = reg;
- dwc3_core_soft_reset(dwc);
-
/* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -347,6 +352,8 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
cpu_relax();
} while (true);
+ dwc3_core_soft_reset(dwc);
+
dwc3_cache_hwparams(dwc);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f69c877add0..151eca876df 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -67,6 +67,7 @@
#define DWC3_DEVICE_EVENT_CONNECT_DONE 2
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
#define DWC3_DEVICE_EVENT_WAKEUP 4
+#define DWC3_DEVICE_EVENT_HIBER_REQ 5
#define DWC3_DEVICE_EVENT_EOPF 6
#define DWC3_DEVICE_EVENT_SOF 7
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
@@ -171,28 +172,36 @@
#define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3
-#define DWC3_GCTL_CORESOFTRESET (1 << 11)
-#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
-#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
-#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
-#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+#define DWC3_GCTL_CORESOFTRESET (1 << 11)
+#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
+#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
+#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
+#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
+#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
/* Global USB2 PHY Configuration Register */
-#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
/* Global USB3 PIPE Control Register */
-#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
/* Global TX Fifo Size Register */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
-#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
+#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
/* Global HWPARAMS1 Register */
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
+#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
+#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+
+/* Global HWPARAMS4 Register */
+#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
+#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Device Configuration Register */
#define DWC3_DCFG_LPM_CAP (1 << 22)
@@ -206,24 +215,32 @@
#define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_FULLSPEED1 (3 << 0)
+#define DWC3_DCFG_LPM_CAP (1 << 22)
+
/* Device Control Register */
#define DWC3_DCTL_RUN_STOP (1 << 31)
#define DWC3_DCTL_CSFTRST (1 << 30)
#define DWC3_DCTL_LSFTRST (1 << 29)
#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
-#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24)
+#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
#define DWC3_DCTL_APPL1RES (1 << 23)
-#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
-#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
-
-#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
-#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
-#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
-#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
-#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+/* These apply for core versions 1.87a and earlier */
+#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
+#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
+#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
+#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
+#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
+#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
+#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
+#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
+#define DWC3_DCTL_CRS (1 << 17)
+#define DWC3_DCTL_CSS (1 << 16)
#define DWC3_DCTL_INITU2ENA (1 << 12)
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
@@ -249,6 +266,7 @@
#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
#define DWC3_DEVTEN_SOFEN (1 << 7)
#define DWC3_DEVTEN_EOPFEN (1 << 6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
@@ -256,7 +274,15 @@
#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
/* Device Status Register */
+#define DWC3_DSTS_DCNRD (1 << 29)
+
+/* This applies for core versions 1.87a and earlier */
#define DWC3_DSTS_PWRUPREQ (1 << 24)
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DSTS_RSS (1 << 25)
+#define DWC3_DSTS_SSS (1 << 24)
+
#define DWC3_DSTS_COREIDLE (1 << 23)
#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
@@ -265,7 +291,7 @@
#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
-#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3)
+#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
#define DWC3_DSTS_CONNECTSPD (7 << 0)
@@ -280,6 +306,11 @@
#define DWC3_DGCMD_SET_LMP 0x01
#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02
#define DWC3_DGCMD_XMIT_FUNCTION 0x03
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05
+
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
@@ -287,6 +318,15 @@
#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
#define DWC3_DGCMD_CMDACT (1 << 10)
+#define DWC3_DGCMD_CMDIOC (1 << 8)
+
+/* Device Generic Command Parameter Register */
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
+#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
+#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
+#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
/* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16
@@ -303,7 +343,10 @@
#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)
#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)
#define DWC3_DEPCMD_SETSTALL (0x04 << 0)
+/* This applies for core versions 1.90a and earlier */
#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)
+/* This applies for core versions 1.94a and later */
+#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0)
#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
@@ -361,7 +404,8 @@ struct dwc3_event_buffer {
* @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
- * @res_trans_idx: Resource transfer index
+ * @resource_index: Resource transfer index
+ * @current_uf: Current uf received through last event parameter
* @interval: the intervall on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
@@ -385,6 +429,7 @@ struct dwc3_ep {
#define DWC3_EP_WEDGE (1 << 2)
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
+#define DWC3_EP_MISSED_ISOC (1 << 6)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
@@ -393,7 +438,8 @@ struct dwc3_ep {
u8 number;
u8 type;
- u8 res_trans_idx;
+ u8 resource_index;
+ u16 current_uf;
u32 interval;
char name[20];
@@ -437,6 +483,8 @@ enum dwc3_link_state {
DWC3_LINK_STATE_HRESET = 0x09,
DWC3_LINK_STATE_CMPLY = 0x0a,
DWC3_LINK_STATE_LPBK = 0x0b,
+ DWC3_LINK_STATE_RESET = 0x0e,
+ DWC3_LINK_STATE_RESUME = 0x0f,
DWC3_LINK_STATE_MASK = 0x0f,
};
@@ -450,11 +498,12 @@ enum dwc3_device_state {
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24)
-#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28) >> 28))
+#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28)
#define DWC3_TRBSTS_OK 0
#define DWC3_TRBSTS_MISSED_ISOC 1
#define DWC3_TRBSTS_SETUP_PENDING 2
+#define DWC3_TRB_STS_XFER_IN_PROG 4
/* TRB Control */
#define DWC3_TRB_CTRL_HWO (1 << 0)
@@ -543,6 +592,14 @@ struct dwc3_request {
unsigned queued:1;
};
+/*
+ * struct dwc3_scratchpad_array - hibernation scratchpad array
+ * (format defined by hw)
+ */
+struct dwc3_scratchpad_array {
+ __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
+};
+
/**
* struct dwc3 - representation of our controller
* @ctrl_req: usb control request which is used for ep0
@@ -624,8 +681,10 @@ struct dwc3 {
#define DWC3_REVISION_180A 0x5533180a
#define DWC3_REVISION_183A 0x5533183a
#define DWC3_REVISION_185A 0x5533185a
+#define DWC3_REVISION_187A 0x5533187a
#define DWC3_REVISION_188A 0x5533188a
#define DWC3_REVISION_190A 0x5533190a
+#define DWC3_REVISION_194A 0x5533194a
#define DWC3_REVISION_200A 0x5533200a
#define DWC3_REVISION_202A 0x5533202a
#define DWC3_REVISION_210A 0x5533210a
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index d1903019808..b8f00389fa3 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -18,7 +18,6 @@
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
-#include <linux/module.h>
#include <linux/clk.h>
#include "core.h"
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 9e8a3dce69f..9b94886b66e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -54,7 +54,9 @@
#include "gadget.h"
#include "io.h"
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep, struct dwc3_request *req);
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
@@ -111,7 +113,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
}
dep->flags |= DWC3_EP_BUSY;
- dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
dep->number);
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
@@ -150,16 +152,15 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
return 0;
}
- ret = dwc3_ep0_start_trans(dwc, direction,
- req->request.dma, req->request.length,
- DWC3_TRBCTL_CONTROL_DATA);
+ __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
DWC3_EP0_DIR_IN);
} else if (dwc->delayed_status) {
dwc->delayed_status = false;
if (dwc->ep0state == EP0_STATUS_PHASE)
- dwc3_ep0_do_control_status(dwc, 1);
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[1]);
else
dev_dbg(dwc->dev, "too early for delayed status\n");
}
@@ -224,6 +225,16 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
dwc3_ep0_out_start(dwc);
}
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ dwc3_ep0_stall_and_restart(dwc);
+
+ return 0;
+}
+
void dwc3_ep0_out_start(struct dwc3 *dwc)
{
int ret;
@@ -463,6 +474,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
u32 cfg;
int ret;
+ u32 reg;
dwc->start_config_issued = false;
cfg = le16_to_cpu(ctrl->wValue);
@@ -477,6 +489,14 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
dwc->dev_state = DWC3_CONFIGURED_STATE;
+ /*
+ * Enable transition to U1/U2 state when
+ * nothing is pending from application.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
dwc->resize_fifos = true;
dev_dbg(dwc->dev, "resize fifos flag SET\n");
}
@@ -514,8 +534,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
dwc->u1sel = timing.u1sel;
dwc->u1pel = timing.u1pel;
- dwc->u2sel = timing.u2sel;
- dwc->u2pel = timing.u2pel;
+ dwc->u2sel = le16_to_cpu(timing.u2sel);
+ dwc->u2pel = le16_to_cpu(timing.u2pel);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU2ENA)
@@ -640,11 +660,11 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
- int ret;
+ int ret = -EINVAL;
u32 len;
if (!dwc->gadget_driver)
- goto err;
+ goto out;
len = le16_to_cpu(ctrl->wLength);
if (!len) {
@@ -665,11 +685,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
if (ret == USB_GADGET_DELAYED_STATUS)
dwc->delayed_status = true;
- if (ret >= 0)
- return;
-
-err:
- dwc3_ep0_stall_and_restart(dwc);
+out:
+ if (ret < 0)
+ dwc3_ep0_stall_and_restart(dwc);
}
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
@@ -723,7 +741,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
}
}
-static void dwc3_ep0_complete_req(struct dwc3 *dwc,
+static void dwc3_ep0_complete_status(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_request *r;
@@ -745,6 +763,7 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc,
dev_dbg(dwc->dev, "Invalid Test #%d\n",
dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
+ return;
}
}
@@ -758,7 +777,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
dep->flags &= ~DWC3_EP_BUSY;
- dep->res_trans_idx = 0;
+ dep->resource_index = 0;
dwc->setup_packet_pending = false;
switch (dwc->ep0state) {
@@ -774,7 +793,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
case EP0_STATUS_PHASE:
dev_vdbg(dwc->dev, "Status Phase\n");
- dwc3_ep0_complete_req(dwc, event);
+ dwc3_ep0_complete_status(dwc, event);
break;
default:
WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
@@ -787,68 +806,81 @@ static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
dwc3_ep0_out_start(dwc);
}
-static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep, struct dwc3_request *req)
{
- struct dwc3_ep *dep;
- struct dwc3_request *req;
int ret;
- dep = dwc->eps[0];
-
- if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
- dep->flags |= DWC3_EP_PENDING_REQUEST;
-
- if (event->endpoint_number)
- dep->flags |= DWC3_EP0_DIR_IN;
- return;
- }
-
- req = next_request(&dep->request_list);
- req->direction = !!event->endpoint_number;
+ req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
+ ret = dwc3_ep0_start_trans(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA);
- } else if ((req->request.length % dep->endpoint.maxpacket)
- && (event->endpoint_number == 0)) {
+ } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
+ && (dep->number == 0)) {
+ u32 transfer_size;
+
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- event->endpoint_number);
+ dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
return;
}
- WARN_ON(req->request.length > dep->endpoint.maxpacket);
+ WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
+
+ transfer_size = roundup(req->request.length,
+ (u32) dep->endpoint.maxpacket);
dwc->ep0_bounced = true;
/*
- * REVISIT in case request length is bigger than EP0
- * wMaxPacketSize, we will need two chained TRBs to handle
- * the transfer.
+ * REVISIT in case request length is bigger than
+ * DWC3_EP0_BOUNCE_SIZE we will need two chained
+ * TRBs to handle the transfer.
*/
- ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
- dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
+ ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- event->endpoint_number);
+ dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
return;
}
- ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
- req->request.dma, req->request.length,
- DWC3_TRBCTL_CONTROL_DATA);
+ ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ req->request.length, DWC3_TRBCTL_CONTROL_DATA);
}
WARN_ON(ret < 0);
}
+static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep;
+ struct dwc3_request *req;
+
+ dep = dwc->eps[0];
+
+ if (list_empty(&dep->request_list)) {
+ dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+
+ if (event->endpoint_number)
+ dep->flags |= DWC3_EP0_DIR_IN;
+ return;
+ }
+
+ req = next_request(&dep->request_list);
+ dep = dwc->eps[event->endpoint_number];
+
+ __dwc3_ep0_do_control_data(dwc, dep, req);
+}
+
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
@@ -861,10 +893,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
dwc->ctrl_req_addr, 0, type);
}
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
- struct dwc3_ep *dep = dwc->eps[epnum];
-
if (dwc->resize_fifos) {
dev_dbg(dwc->dev, "starting to resize fifos\n");
dwc3_gadget_resize_tx_fifos(dwc);
@@ -874,13 +904,21 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
WARN_ON(dwc3_ep0_start_control_status(dep));
}
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+
+ __dwc3_ep0_do_control_status(dwc, dep);
+}
+
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
dwc->setup_packet_pending = true;
/*
- * This part is very tricky: If we has just handled
+ * This part is very tricky: If we have just handled
* XferNotReady(Setup) and we're now expecting a
* XferComplete but, instead, we receive another
* XferNotReady(Setup), we should STALL and restart
@@ -974,7 +1012,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
return;
}
- dwc3_ep0_do_control_status(dwc, event->endpoint_number);
+ dwc3_ep0_do_control_status(dwc, event);
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 4f3e8811dae..58fdfad96b4 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -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,7 +124,15 @@ 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) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -265,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:
@@ -530,9 +555,37 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
- if (!list_empty(&dep->req_queued))
+ if (!list_empty(&dep->req_queued)) {
dwc3_stop_active_transfer(dwc, dep->number);
+ /*
+ * NOTICE: We are violating what the Databook says about the
+ * EndTransfer command. Ideally we would _always_ wait for the
+ * EndTransfer Command Completion IRQ, but that's causing too
+ * much trouble synchronizing between us and gadget driver.
+ *
+ * We have discussed this with the IP Provider and it was
+ * suggested to giveback all requests here, but give HW some
+ * extra time to synchronize with the interconnect. We're using
+ * an arbitraty 100us delay for that.
+ *
+ * Note also that a similar handling was tested by Synopsys
+ * (thanks a lot Paul) and nothing bad has come out of it.
+ * In short, what we're doing is:
+ *
+ * - Issue EndTransfer WITH CMDIOC bit set
+ * - Wait 100us
+ * - giveback all requests to gadget driver
+ */
+ udelay(100);
+
+ while (!list_empty(&dep->req_queued)) {
+ req = next_request(&dep->req_queued);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
+ }
+
while (!list_empty(&dep->request_list)) {
req = next_request(&dep->request_list);
@@ -741,8 +794,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
case USB_ENDPOINT_XFER_ISOC:
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
- /* IOC every DWC3_TRB_NUM / 4 so we can refill */
- if (!(cur_slot % (DWC3_TRB_NUM / 4)))
+ if (!req->request.no_interrupt)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
break;
@@ -958,14 +1010,42 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
dep->flags |= DWC3_EP_BUSY;
if (start_new) {
- dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
dep->number);
- WARN_ON_ONCE(!dep->res_trans_idx);
+ WARN_ON_ONCE(!dep->resource_index);
}
return 0;
}
+static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
+ struct dwc3_ep *dep, u32 cur_uf)
+{
+ u32 uf;
+
+ if (list_empty(&dep->request_list)) {
+ dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
+ dep->name);
+ return;
+ }
+
+ /* 4 micro frames in the future */
+ uf = cur_uf + dep->interval * 4;
+
+ __dwc3_gadget_kick_transfer(dep, uf, 1);
+}
+
+static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+{
+ u32 cur_uf, mask;
+
+ mask = ~(dep->interval - 1);
+ cur_uf = event->parameters & mask;
+
+ __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+}
+
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
@@ -995,11 +1075,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->request_list);
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
- dep->flags |= DWC3_EP_PENDING_REQUEST;
-
/*
- * There are two special cases:
+ * There are a few special cases:
*
* 1. XferNotReady with empty list of requests. We need to kick the
* transfer here in that situation, otherwise we will be NAKing
@@ -1008,31 +1085,46 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* able to receive the data until the next request is queued.
* The following code is handling exactly that.
*
- * 2. XferInProgress on Isoc EP with an active transfer. We need to
- * kick the transfer here after queuing a request, otherwise the
- * core may not see the modified TRB(s).
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
int ret;
- int start_trans = 1;
- u8 trans_idx = dep->res_trans_idx;
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (dep->flags & DWC3_EP_BUSY)) {
- start_trans = 0;
- WARN_ON_ONCE(!trans_idx);
- } else {
- trans_idx = 0;
+ ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ if (ret && ret != -EBUSY) {
+ struct dwc3 *dwc = dep->dwc;
+
+ dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dep->name);
}
+ }
- ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans);
+ /*
+ * 2. XferInProgress on Isoc EP with an active transfer. We need to
+ * kick the transfer here after queuing a request, otherwise the
+ * core may not see the modified TRB(s).
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (dep->flags & DWC3_EP_BUSY)) {
+ WARN_ON_ONCE(!dep->resource_index);
+ ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
+ false);
if (ret && ret != -EBUSY) {
struct dwc3 *dwc = dep->dwc;
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
}
- };
+ }
+
+ /*
+ * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
+ * uframe number.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (dep->flags & DWC3_EP_MISSED_ISOC)) {
+ __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
+ dep->flags &= ~DWC3_EP_MISSED_ISOC;
+ }
return 0;
}
@@ -1118,15 +1210,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
memset(&params, 0x00, sizeof(params));
if (value) {
- if (dep->number == 0 || dep->number == 1) {
- /*
- * Whenever EP0 is stalled, we will restart
- * the state machine, thus moving back to
- * Setup Phase
- */
- dwc->ep0state = EP0_SETUP_PHASE;
- }
-
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_SETSTALL, &params);
if (ret)
@@ -1186,7 +1269,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
dep->flags |= DWC3_EP_WEDGE;
spin_unlock_irqrestore(&dwc->lock, flags);
- return dwc3_gadget_ep_set_halt(ep, 1);
+ if (dep->number == 0 || dep->number == 1)
+ return dwc3_gadget_ep0_set_halt(ep, 1);
+ else
+ return dwc3_gadget_ep_set_halt(ep, 1);
}
/* -------------------------------------------------------------------------- */
@@ -1204,7 +1290,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
.free_request = dwc3_gadget_ep_free_request,
.queue = dwc3_gadget_ep0_queue,
.dequeue = dwc3_gadget_ep_dequeue,
- .set_halt = dwc3_gadget_ep_set_halt,
+ .set_halt = dwc3_gadget_ep0_set_halt,
.set_wedge = dwc3_gadget_ep_set_wedge,
};
@@ -1280,9 +1366,13 @@ 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_readl(dwc->regs, DWC3_DCTL);
+ 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);
@@ -1319,16 +1409,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
return 0;
}
-static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
{
u32 reg;
u32 timeout = 500;
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;
}
@@ -1346,7 +1441,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
}
timeout--;
if (!timeout)
- break;
+ return -ETIMEDOUT;
udelay(1);
} while (1);
@@ -1354,20 +1449,23 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
dwc->gadget_driver
? dwc->gadget_driver->function : "no-function",
is_on ? "connect" : "disconnect");
+
+ return 0;
}
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int ret;
is_on = !!is_on;
spin_lock_irqsave(&dwc->lock, flags);
- dwc3_gadget_run_stop(dwc, is_on);
+ ret = dwc3_gadget_run_stop(dwc, is_on);
spin_unlock_irqrestore(&dwc->lock, flags);
- return 0;
+ return ret;
}
static int dwc3_gadget_start(struct usb_gadget *g,
@@ -1468,6 +1566,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,
@@ -1558,6 +1657,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_trb *trb;
unsigned int count;
unsigned int s_pkt = 0;
+ unsigned int trb_status;
do {
req = next_request(&dep->req_queued);
@@ -1583,9 +1683,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (dep->direction) {
if (count) {
- dev_err(dwc->dev, "incomplete IN transfer %s\n",
- dep->name);
- status = -ECONNRESET;
+ trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+ dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ dep->current_uf = event->parameters &
+ ~(dep->interval - 1);
+ dep->flags |= DWC3_EP_MISSED_ISOC;
+ } else {
+ dev_err(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ status = -ECONNRESET;
+ }
}
} else {
if (count && (event->status & DEPEVT_STATUS_SHORT))
@@ -1604,7 +1713,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (s_pkt)
break;
if ((event->status & DEPEVT_STATUS_LST) &&
- (trb->ctrl & DWC3_TRB_CTRL_LST))
+ (trb->ctrl & (DWC3_TRB_CTRL_LST |
+ DWC3_TRB_CTRL_HWO)))
break;
if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -1657,65 +1767,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
}
}
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
- u32 uf, mask;
-
- if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
- dep->name);
- return;
- }
-
- mask = ~(dep->interval - 1);
- uf = event->parameters & mask;
- /* 4 micro frames in the future */
- uf += dep->interval * 4;
-
- __dwc3_gadget_kick_transfer(dep, uf, 1);
-}
-
-static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
-{
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_event_depevt mod_ev = *event;
-
- /*
- * We were asked to remove one request. It is possible that this
- * request and a few others were started together and have the same
- * transfer index. Since we stopped the complete endpoint we don't
- * know how many requests were already completed (and not yet)
- * reported and how could be done (later). We purge them all until
- * the end of the list.
- */
- mod_ev.status = DEPEVT_STATUS_LST;
- dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
- dep->flags &= ~DWC3_EP_BUSY;
- /* pending requests are ignored and are queued on XferNotReady */
-}
-
-static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
-{
- u32 param = event->parameters;
- u32 cmd_type = (param >> 8) & ((1 << 5) - 1);
-
- switch (cmd_type) {
- case DWC3_DEPCMD_ENDTRANSFER:
- dwc3_process_ep_cmd_complete(dep, event);
- break;
- case DWC3_DEPCMD_STARTTRANSFER:
- dep->res_trans_idx = param & 0x7f;
- break;
- default:
- printk(KERN_ERR "%s() unknown /unexpected type: %d\n",
- __func__, cmd_type);
- break;
- };
-}
-
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
@@ -1724,6 +1775,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep = dwc->eps[epnum];
+ if (!(dep->flags & DWC3_EP_ENABLED))
+ return;
+
dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
dwc3_ep_event_string(event->endpoint_event));
@@ -1734,7 +1788,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- dep->res_trans_idx = 0;
+ dep->resource_index = 0;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
@@ -1797,7 +1851,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- dwc3_ep_cmd_compl(dep, event);
+ dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
break;
}
}
@@ -1820,16 +1874,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
dep = dwc->eps[epnum];
- WARN_ON(!dep->res_trans_idx);
- if (dep->res_trans_idx) {
- cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
- cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
- memset(&params, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
- WARN_ON_ONCE(ret);
- dep->res_trans_idx = 0;
- }
+ if (!dep->resource_index)
+ return;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+ WARN_ON_ONCE(ret);
+ dep->resource_index = 0;
}
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -1872,11 +1926,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
+ int reg;
+
dev_vdbg(dwc->dev, "%s\n", __func__);
-#if 0
- XXX
- U1/U2 is powersave optimization. Skip it for now. Anyway we need to
- enable it before we can disable it.
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
@@ -1884,9 +1936,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-#endif
- dwc3_stop_active_transfers(dwc);
dwc3_disconnect_gadget(dwc);
dwc->start_config_issued = false;
@@ -1894,30 +1944,30 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->setup_packet_pending = false;
}
-static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- if (on)
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- else
+ if (suspend)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
}
-static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (on)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- else
+ if (suspend)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
@@ -1962,16 +2012,18 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
/* after reset -> Default State */
dwc->dev_state = DWC3_DEFAULT_STATE;
- /* Enable PHYs */
- dwc3_gadget_usb2_phy_power(dwc, true);
- dwc3_gadget_usb3_phy_power(dwc, true);
+ /* 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);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
- reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
dwc->test_mode = false;
@@ -2010,16 +2062,16 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
-static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
+static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
{
switch (speed) {
case USB_SPEED_SUPER:
- dwc3_gadget_usb2_phy_power(dwc, false);
+ dwc3_gadget_usb2_phy_suspend(dwc, true);
break;
case USB_SPEED_HIGH:
case USB_SPEED_FULL:
case USB_SPEED_LOW:
- dwc3_gadget_usb3_phy_power(dwc, false);
+ dwc3_gadget_usb3_phy_suspend(dwc, true);
break;
}
}
@@ -2082,8 +2134,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
break;
}
- /* Disable unneded PHY */
- dwc3_gadget_disable_phy(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);
@@ -2373,10 +2428,6 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
reg |= DWC3_DCFG_LPM_CAP;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
/* Enable all but Start and End of Frame IRQs */
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
DWC3_DEVTEN_EVNTOVERFLOWEN |
@@ -2389,6 +2440,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(28);
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ dwc3_gadget_usb2_phy_suspend(dwc, false);
+ dwc3_gadget_usb3_phy_suspend(dwc, false);
+ }
+
ret = device_register(&dwc->gadget.dev);
if (ret) {
dev_err(dwc->dev, "failed to register gadget device\n");
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 95ef6a2f776..99e6d724882 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -66,7 +66,12 @@ struct dwc3;
#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
+/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
+/* These apply for core versions 1.94a and later */
+#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
+#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
/* DEPXFERCFG parameter 0 */
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
@@ -106,6 +111,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);