|
@@ -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(¶ms, 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, ¶ms);
|
|
|
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(¶ms, 0, sizeof(params));
|
|
|
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
|
|
- 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(¶ms, 0, sizeof(params));
|
|
|
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
|
|
+ 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");
|