summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/gadget.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r--drivers/usb/dwc3/gadget.c1248
1 files changed, 1031 insertions, 217 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index d3bd1afd6302..94709587f238 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -22,19 +22,25 @@
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/ratelimit.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include "debug.h"
#include "core.h"
#include "gadget.h"
+#include "debug.h"
#include "io.h"
+static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup);
+static int dwc3_gadget_wakeup_int(struct dwc3 *dwc);
+
/**
* dwc3_gadget_set_test_mode - Enables USB2 Test Modes
* @dwc: pointer to our context structure
@@ -173,47 +179,56 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
int fifo_size;
int mdwidth;
int num;
+ int num_eps;
+ int max_packet = 1024;
+ struct usb_composite_dev *cdev = get_gadget_data(&dwc->gadget);
- if (!dwc->needs_fifo_resize)
+ if (!(cdev && cdev->config) || !dwc->needs_fifo_resize)
return 0;
+ num_eps = dwc->num_in_eps;
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth >>= 3;
+ last_fifo_depth = (dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)) & 0xFFFF);
+ dev_dbg(dwc->dev, "%s: num eps:%d max_packet:%d last_fifo_depth:%04x\n",
+ __func__, num_eps, max_packet, last_fifo_depth);
- /*
- * FIXME For now we will only allocate 1 wMaxPacketSize space
- * for each enabled endpoint, later patches will come to
- * improve this algorithm so that we better use the internal
- * FIFO space
- */
- for (num = 0; num < dwc->num_in_eps; num++) {
+ /* Don't resize ep0IN TxFIFO, start with ep1IN only. */
+ for (num = 1; num < num_eps; num++) {
/* bit0 indicates direction; 1 means IN ep */
struct dwc3_ep *dep = dwc->eps[(num << 1) | 1];
int mult = 1;
int tmp;
- if (!(dep->flags & DWC3_EP_ENABLED))
- continue;
+ tmp = max_packet + mdwidth;
+ /*
+ * Interfaces like MBIM or ECM is having multiple data
+ * interfaces. SET_CONFIG() happens before set_alt with
+ * data interface 1 which results into calling this API
+ * before GSI endpoint enabled. This results no txfifo
+ * resize with GSI endpoint causing low throughput. Hence
+ * use mult as 3 for GSI IN endpoint always irrespective
+ * USB speed.
+ */
+ if (dep->endpoint.ep_type == EP_TYPE_GSI ||
+ dep->endpoint.endless)
+ mult = 3;
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ dev_dbg(dwc->dev, "ep%dIn not enabled", num);
+ goto resize_fifo;
+ }
- if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
+ if (((dep->endpoint.maxburst > 1) &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc))
|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
mult = 3;
- /*
- * REVISIT: the following assumes we will always have enough
- * space available on the FIFO RAM for all possible use cases.
- * Make sure that's true somehow and change FIFO allocation
- * accordingly.
- *
- * If we have Bulk or Isochronous endpoints, we want
- * them to be able to be very, very fast. So we're giving
- * those endpoints a fifo_size which is enough for 3 full
- * packets
- */
- tmp = mult * (dep->endpoint.maxpacket + mdwidth);
+resize_fifo:
+ tmp *= mult;
tmp += mdwidth;
fifo_size = DIV_ROUND_UP(tmp, mdwidth);
@@ -223,9 +238,20 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d",
dep->name, last_fifo_depth, fifo_size & 0xffff);
+ last_fifo_depth += (fifo_size & 0xffff);
+ if (dwc->tx_fifo_size &&
+ (last_fifo_depth >= dwc->tx_fifo_size)) {
+ /*
+ * Fifo size allocated exceeded available RAM size.
+ * Hence return error.
+ */
+ dev_err(dwc->dev, "Fifosize(%d) > available RAM(%d)\n",
+ last_fifo_depth, dwc->tx_fifo_size);
+ return -ENOMEM;
+ }
+
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
- last_fifo_depth += (fifo_size & 0xffff);
}
return 0;
@@ -274,11 +300,12 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
&req->request, req->direction);
}
- dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
+ dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
req->request.length, status);
trace_dwc3_gadget_giveback(req);
+ dbg_done(dep->number, req->request.actual, req->request.status);
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
@@ -327,7 +354,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
struct dwc3_ep *dep = dwc->eps[ep];
- u32 timeout = 500;
+ u32 timeout = 3000;
u32 reg;
trace_dwc3_gadget_ep_cmd(dep, cmd, params);
@@ -343,7 +370,16 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
dwc3_trace(trace_dwc3_gadget,
"Command Complete --> %d",
DWC3_DEPCMD_STATUS(reg));
- if (DWC3_DEPCMD_STATUS(reg))
+
+ /* SW issues START TRANSFER command to isochronous ep
+ * with future frame interval. If future interval time
+ * has already passed when core recieves command, core
+ * will respond with an error(bit13 in Command complete
+ * event. Hence return error in this case.
+ */
+ if (reg & 0x2000)
+ return -EAGAIN;
+ else if (DWC3_DEPCMD_STATUS(reg))
return -EINVAL;
return 0;
}
@@ -356,36 +392,39 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
if (!timeout) {
dwc3_trace(trace_dwc3_gadget,
"Command Timed Out");
+ dev_err(dwc->dev, "%s command timeout for %s\n",
+ dwc3_gadget_ep_cmd_string(cmd), dep->name);
+ if (!(cmd & DWC3_DEPCMD_ENDTRANSFER)) {
+ dwc->ep_cmd_timeout_cnt++;
+ dwc3_notify_event(dwc,
+ DWC3_CONTROLLER_RESTART_USB_SESSION, 0);
+ }
return -ETIMEDOUT;
}
-
- udelay(1);
+ if ((cmd & DWC3_DEPCMD_SETTRANSFRESOURCE))
+ udelay(20);
+ else
+ udelay(1);
} while (1);
}
-static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
- struct dwc3_trb *trb)
-{
- u32 offset = (char *) trb - (char *) dep->trb_pool;
-
- return dep->trb_pool_dma + offset;
-}
-
static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
+ u32 num_trbs = DWC3_TRB_NUM;
if (dep->trb_pool)
return 0;
- dep->trb_pool = dma_alloc_coherent(dwc->dev,
- sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ dep->trb_pool = dma_zalloc_coherent(dwc->dev,
+ sizeof(struct dwc3_trb) * num_trbs,
&dep->trb_pool_dma, GFP_KERNEL);
if (!dep->trb_pool) {
dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
dep->name);
return -ENOMEM;
}
+ dep->num_trbs = num_trbs;
return 0;
}
@@ -394,11 +433,27 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
- dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
- dep->trb_pool, dep->trb_pool_dma);
+ /* Freeing of GSI EP TRBs are handled by GSI EP ops. */
+ if (dep->endpoint.ep_type == EP_TYPE_GSI)
+ return;
+
+ /*
+ * Clean up ep ring to avoid getting xferInProgress due to stale trbs
+ * with HWO bit set from previous composition when update transfer cmd
+ * is issued.
+ */
+ if (dep->number > 1 && dep->trb_pool && dep->trb_pool_dma) {
+ memset(&dep->trb_pool[0], 0,
+ sizeof(struct dwc3_trb) * dep->num_trbs);
+ dbg_event(dep->number, "Clr_TRB", 0);
- dep->trb_pool = NULL;
- dep->trb_pool_dma = 0;
+ dma_free_coherent(dwc->dev,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM, dep->trb_pool,
+ dep->trb_pool_dma);
+
+ dep->trb_pool = NULL;
+ dep->trb_pool_dma = 0;
+ }
}
static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
@@ -493,8 +548,15 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param2 |= dep->saved_state;
}
- params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
- | DWC3_DEPCFG_XFER_NOT_READY_EN;
+ if (!dep->endpoint.endless) {
+ pr_debug("%s(): enable xfer_complete_int for %s\n",
+ __func__, dep->endpoint.name);
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
+ | DWC3_DEPCFG_XFER_NOT_READY_EN;
+ } else {
+ pr_debug("%s(): disable xfer_complete_int for %s\n",
+ __func__, dep->endpoint.name);
+ }
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
@@ -502,7 +564,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
dep->stream_capable = true;
}
- if (!usb_endpoint_xfer_control(desc))
+ if (usb_endpoint_xfer_isoc(desc))
params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
/*
@@ -561,14 +623,19 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
if (!(dep->flags & DWC3_EP_ENABLED)) {
ret = dwc3_gadget_start_config(dwc, dep);
- if (ret)
+ if (ret) {
+ dev_err(dwc->dev, "start_config() failed for %s\n",
+ dep->name);
return ret;
+ }
}
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
restore);
- if (ret)
+ if (ret) {
+ dev_err(dwc->dev, "set_ep_config() failed for %s\n", dep->name);
return ret;
+ }
if (!(dep->flags & DWC3_EP_ENABLED)) {
struct dwc3_trb *trb_st_hw;
@@ -618,7 +685,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
@@ -656,7 +722,10 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dwc3_trace(trace_dwc3_gadget, "Disabling %s", dep->name);
- dwc3_remove_requests(dwc, dep);
+ if (dep->endpoint.ep_type == EP_TYPE_NORMAL)
+ dwc3_remove_requests(dwc, dep);
+ else if (dep->endpoint.ep_type == EP_TYPE_GSI)
+ dwc3_stop_active_transfer(dwc, dep->number, true);
/* make sure HW endpoint isn't stalled */
if (dep->flags & DWC3_EP_STALL)
@@ -672,9 +741,12 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->type = 0;
dep->flags = 0;
- snprintf(dep->name, sizeof(dep->name), "ep%d%s",
+ /* Keep GSI ep names with "-gsi" suffix */
+ if (!strnstr(dep->name, "gsi", 10)) {
+ snprintf(dep->name, sizeof(dep->name), "ep%d%s",
dep->number >> 1,
(dep->number & 1) ? "in" : "out");
+ }
return 0;
}
@@ -703,7 +775,8 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
int ret;
if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
- pr_debug("dwc3: invalid parameters\n");
+ pr_debug("dwc3: invalid parameters. ep=%pK, desc=%pK, DT=%d\n",
+ ep, desc, desc ? desc->bDescriptorType : 0);
return -EINVAL;
}
@@ -723,6 +796,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
+ dbg_event(dep->number, "ENABLE", ret);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -744,13 +818,14 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dwc = dep->dwc;
if (!(dep->flags & DWC3_EP_ENABLED)) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
- dep->name);
+ dev_dbg(dwc->dev, "%s is already disabled\n", dep->name);
+ dbg_event(dep->number, "ALRDY DISABLED", dep->flags);
return 0;
}
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_disable(dep);
+ dbg_event(dep->number, "DISABLE", ret);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -768,6 +843,7 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
req->epnum = dep->number;
req->dep = dep;
+ req->request.dma = DMA_ERROR_CODE;
trace_dwc3_alloc_request(req);
@@ -794,7 +870,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
{
struct dwc3_trb *trb;
- dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s",
+ dwc3_trace(trace_dwc3_gadget, "%s: req %pK dma %08llx length %d%s%s",
dep->name, req, (unsigned long long) dma,
length, last ? " last" : "",
chain ? " chain" : "");
@@ -829,11 +905,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
else
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+
+ if (!req->request.no_interrupt && !chain)
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
break;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
trb->ctrl = DWC3_TRBCTL_NORMAL;
+ if (req->request.num_mapped_sgs > 0) {
+ if (!last && !chain &&
+ !req->request.no_interrupt)
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
+ }
break;
default:
/*
@@ -843,9 +927,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
BUG();
}
- if (!req->request.no_interrupt && !chain)
- trb->ctrl |= DWC3_TRB_CTRL_IOC;
-
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
trb->ctrl |= DWC3_TRB_CTRL_CSP;
@@ -962,6 +1043,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
if (last_one)
break;
}
+ dbg_queue(dep->number, &req->request, trbs_left);
if (last_one)
break;
@@ -980,6 +1062,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
dwc3_prepare_one_trb(dep, req, dma, length,
last_one, false, 0);
+ dbg_queue(dep->number, &req->request, 0);
if (last_one)
break;
}
@@ -990,7 +1073,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
int start_new)
{
struct dwc3_gadget_ep_cmd_params params;
- struct dwc3_request *req;
+ struct dwc3_request *req, *req1, *n;
struct dwc3 *dwc = dep->dwc;
int ret;
u32 cmd;
@@ -1020,6 +1103,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
}
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
+ dbg_event(dep->number, "NO REQ", 0);
return 0;
}
@@ -1038,6 +1122,35 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
if (ret < 0) {
dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
+ if ((ret == -EAGAIN) && start_new &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ /* If bit13 in Command complete event is set, software
+ * must issue ENDTRANSFER command and wait for
+ * Xfernotready event to queue the requests again.
+ */
+ if (!dep->resource_index) {
+ dep->resource_index =
+ dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->number);
+ WARN_ON_ONCE(!dep->resource_index);
+ }
+ dwc3_stop_active_transfer(dwc, dep->number, true);
+ list_for_each_entry_safe_reverse(req1, n,
+ &dep->req_queued, list) {
+ req1->trb = NULL;
+ dwc3_gadget_move_request_list_front(req1);
+ if (req->request.num_mapped_sgs)
+ dep->busy_slot +=
+ req->request.num_mapped_sgs;
+ else
+ dep->busy_slot++;
+ if ((dep->busy_slot & DWC3_TRB_MASK) ==
+ DWC3_TRB_NUM - 1)
+ dep->busy_slot++;
+ }
+ return ret;
+ }
+
/*
* FIXME we need to iterate over the list of requests
* here and stop, unmap, free and del each of the linked
@@ -1064,6 +1177,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, u32 cur_uf)
{
u32 uf;
+ int ret;
+
+ dep->current_uf = cur_uf;
if (list_empty(&dep->request_list)) {
dwc3_trace(trace_dwc3_gadget,
@@ -1076,7 +1192,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
/* 4 micro frames in the future */
uf = cur_uf + dep->interval * 4;
- __dwc3_gadget_kick_transfer(dep, uf, 1);
+ ret = __dwc3_gadget_kick_transfer(dep, uf, 1);
+ if (ret < 0)
+ dbg_event(dep->number, "ISOC QUEUE", ret);
}
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
@@ -1095,6 +1213,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
struct dwc3 *dwc = dep->dwc;
int ret;
+ if (req->request.status == -EINPROGRESS) {
+ ret = -EBUSY;
+ dev_err(dwc->dev, "%s: %pK request already in queue",
+ dep->name, req);
+ return ret;
+ }
+
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->direction = dep->direction;
@@ -1122,20 +1247,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->request_list);
/*
- * If there are no pending requests and the endpoint isn't already
- * busy, we will just start the request straight away.
- *
- * This will save one IRQ (XFER_NOT_READY) and possibly make it a
- * little bit faster.
- */
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !usb_endpoint_xfer_int(dep->endpoint.desc) &&
- !(dep->flags & DWC3_EP_BUSY)) {
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- goto out;
- }
-
- /*
* There are a few special cases:
*
* 1. XferNotReady with empty list of requests. We need to kick the
@@ -1154,16 +1265,25 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* notion of current microframe.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if (list_empty(&dep->req_queued)) {
+ /* If xfernotready event is recieved before issuing
+ * START TRANSFER command, don't issue END TRANSFER.
+ * Rather start queueing the requests by issuing START
+ * TRANSFER command.
+ */
+ if (list_empty(&dep->req_queued) && dep->resource_index)
dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- }
+ else
+ __dwc3_gadget_start_isoc(dwc, dep,
+ dep->current_uf);
+ dep->flags &= ~DWC3_EP_PENDING_REQUEST;
return 0;
}
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
if (!ret)
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+ else if (ret != -EBUSY)
+ dbg_event(dep->number, "XfNR QUEUE", ret);
goto out;
}
@@ -1179,6 +1299,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
WARN_ON_ONCE(!dep->resource_index);
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
false);
+ if (ret && ret != -EBUSY)
+ dbg_event(dep->number, "XfIP QUEUE", ret);
goto out;
}
@@ -1191,15 +1313,59 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
out:
- if (ret && ret != -EBUSY)
+ if (ret && ret != -EBUSY) {
+ dbg_event(dep->number, "QUEUE err", ret);
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
+ }
if (ret == -EBUSY)
ret = 0;
return ret;
}
+static int dwc3_gadget_wakeup(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ schedule_work(&dwc->wakeup_work);
+ return 0;
+}
+
+static bool dwc3_gadget_is_suspended(struct dwc3 *dwc)
+{
+ if (atomic_read(&dwc->in_lpm) ||
+ dwc->link_state == DWC3_LINK_STATE_U3)
+ return true;
+ return false;
+}
+
+static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ dwc3_gadget_ep_free_request(ep, request);
+}
+
+static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct usb_request *request;
+ struct usb_ep *ep = &dep->endpoint;
+
+ dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
+ request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!request)
+ return -ENOMEM;
+
+ request->length = 0;
+ request->buf = dwc->zlp_buf;
+ request->complete = __dwc3_gadget_ep_zlp_complete;
+
+ req = to_dwc3_request(request);
+
+ return __dwc3_gadget_ep_queue(dep, req);
+}
+
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
@@ -1208,12 +1374,11 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
-
int ret;
spin_lock_irqsave(&dwc->lock, flags);
if (!dep->endpoint.desc) {
- dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
+ dev_dbg(dwc->dev, "trying to queue request %pK to disabled %s\n",
request, ep->name);
ret = -ESHUTDOWN;
goto out;
@@ -1225,8 +1390,39 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
+ /*
+ * Queuing endless request to USB endpoint through generic ep queue
+ * API should not be allowed.
+ */
+ if (dep->endpoint.endless) {
+ dev_dbg(dwc->dev, "trying to queue endless request %p to %s\n",
+ request, ep->name);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -EPERM;
+ }
+
+ if (dwc3_gadget_is_suspended(dwc)) {
+ if (dwc->gadget.remote_wakeup)
+ dwc3_gadget_wakeup(&dwc->gadget);
+ ret = dwc->gadget.remote_wakeup ? -EAGAIN : -ENOTSUPP;
+ goto out;
+ }
+
+ WARN(!dep->direction && (request->length % ep->desc->wMaxPacketSize),
+ "trying to queue unaligned request (%d)\n", request->length);
+
ret = __dwc3_gadget_ep_queue(dep, req);
+ /*
+ * Okay, here's the thing, if gadget driver has requested for a ZLP by
+ * setting request->zero, instead of doing magic, we will just queue an
+ * extra usb_request ourselves so that it gets handled the same way as
+ * any other request.
+ */
+ if (ret == 0 && request->zero && request->length &&
+ (request->length % ep->maxpacket == 0))
+ ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
+
out:
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1245,6 +1441,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
unsigned long flags;
int ret = 0;
+ if (atomic_read(&dwc->in_lpm)) {
+ dev_err(dwc->dev, "Unable to dequeue while in LPM\n");
+ return -EAGAIN;
+ }
+
trace_dwc3_ep_dequeue(req);
spin_lock_irqsave(&dwc->lock, flags);
@@ -1271,6 +1472,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
out1:
+ dbg_event(dep->number, "DEQUEUE", 0);
/* giveback the request */
dwc3_gadget_giveback(dep, req, -ECONNRESET);
@@ -1286,11 +1488,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
struct dwc3 *dwc = dep->dwc;
int ret;
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
- return -EINVAL;
- }
-
memset(&params, 0x00, sizeof(params));
if (value) {
@@ -1331,8 +1528,21 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
int ret;
+ if (!ep->desc) {
+ dev_err(dwc->dev, "(%s)'s desc is NULL.\n", dep->name);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
+ dbg_event(dep->number, "HALT", value);
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
+out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1346,6 +1556,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ dbg_event(dep->number, "WEDGE", 0);
dep->flags |= DWC3_EP_WEDGE;
if (dep->number == 0 || dep->number == 1)
@@ -1398,43 +1609,83 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return DWC3_DSTS_SOFFN(reg);
}
-static int dwc3_gadget_wakeup(struct usb_gadget *g)
+#define DWC3_PM_RESUME_RETRIES 20 /* Max Number of retries */
+#define DWC3_PM_RESUME_DELAY 100 /* 100 msec */
+
+static void dwc3_gadget_wakeup_work(struct work_struct *w)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3 *dwc;
+ int ret;
+ static int retry_count;
- unsigned long timeout;
- unsigned long flags;
+ dwc = container_of(w, struct dwc3, wakeup_work);
- u32 reg;
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret) {
+ /* pm_runtime_get_sync returns -EACCES error between
+ * late_suspend and early_resume, wait for system resume to
+ * finish and queue work again
+ */
+ pr_debug("PM runtime get sync failed, ret %d\n", ret);
+ if (ret == -EACCES) {
+ pm_runtime_put_noidle(dwc->dev);
+ if (retry_count == DWC3_PM_RESUME_RETRIES) {
+ retry_count = 0;
+ pr_err("pm_runtime_get_sync timed out\n");
+ return;
+ }
+ msleep(DWC3_PM_RESUME_DELAY);
+ retry_count++;
+ schedule_work(&dwc->wakeup_work);
+ return;
+ }
+ }
+ retry_count = 0;
+ dbg_event(0xFF, "Gdgwake gsyn",
+ atomic_read(&dwc->dev->power.usage_count));
- int ret = 0;
+ ret = dwc3_gadget_wakeup_int(dwc);
+ if (ret)
+ pr_err("Remote wakeup failed. ret = %d.\n", ret);
+ else
+ pr_debug("Remote wakeup succeeded.\n");
+
+ pm_runtime_put_noidle(dwc->dev);
+ dbg_event(0xFF, "Gdgwake put",
+ atomic_read(&dwc->dev->power.usage_count));
+}
+
+static int dwc3_gadget_wakeup_int(struct dwc3 *dwc)
+{
+ bool link_recover_only = false;
+
+ u32 reg;
+ int ret = 0;
u8 link_state;
- u8 speed;
+ unsigned long flags;
+ pr_debug("%s(): Entry\n", __func__);
+ disable_irq(dwc->irq);
spin_lock_irqsave(&dwc->lock, flags);
-
/*
* According to the Databook Remote wakeup request should
* be issued only when the device is in early suspend state.
*
* We can check that via USB Link State bits in DSTS register.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-
- speed = reg & DWC3_DSTS_CONNECTSPD;
- if (speed == DWC3_DSTS_SUPERSPEED) {
- dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
- ret = -EINVAL;
- goto out;
- }
-
- link_state = DWC3_DSTS_USBLNKST(reg);
+ link_state = dwc3_get_link_state(dwc);
switch (link_state) {
case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
break;
+ case DWC3_LINK_STATE_U1:
+ if (dwc->gadget.speed != USB_SPEED_SUPER) {
+ link_recover_only = true;
+ break;
+ }
+ /* Intentional fallthrough */
default:
dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
link_state);
@@ -1442,9 +1693,25 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
goto out;
}
+ /* Enable LINK STATUS change event */
+ reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
+ reg |= DWC3_DEVTEN_ULSTCNGEN;
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ /*
+ * memory barrier is required to make sure that required events
+ * with core is enabled before performing RECOVERY mechnism.
+ */
+ mb();
+
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
dev_err(dwc->dev, "failed to put link in Recovery\n");
+ /* Disable LINK STATUS change */
+ reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
+ reg &= ~DWC3_DEVTEN_ULSTCNGEN;
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ /* Required to complete this operation before returning */
+ mb();
goto out;
}
@@ -1456,24 +1723,94 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
- /* poll until Link State changes to ON */
- timeout = jiffies + msecs_to_jiffies(100);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ enable_irq(dwc->irq);
- while (!time_after(jiffies, timeout)) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ /*
+ * Have bigger value (16 sec) for timeout since some host PCs driving
+ * resume for very long time (e.g. 8 sec)
+ */
+ ret = wait_event_interruptible_timeout(dwc->wait_linkstate,
+ (dwc->link_state < DWC3_LINK_STATE_U3) ||
+ (dwc->link_state == DWC3_LINK_STATE_SS_DIS),
+ msecs_to_jiffies(16000));
- /* in HS, means ON */
- if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
- break;
- }
+ spin_lock_irqsave(&dwc->lock, flags);
+ /* Disable link status change event */
+ reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
+ reg &= ~DWC3_DEVTEN_ULSTCNGEN;
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ /*
+ * Complete this write before we go ahead and perform resume
+ * as we don't need link status change notificaiton anymore.
+ */
+ mb();
- if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
- dev_err(dwc->dev, "failed to send remote wakeup\n");
+ if (!ret) {
+ dev_dbg(dwc->dev, "Timeout moving into state(%d)\n",
+ dwc->link_state);
ret = -EINVAL;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ goto out1;
+ } else {
+ ret = 0;
+ /*
+ * If USB is disconnected OR received RESET from host,
+ * don't perform resume
+ */
+ if (dwc->link_state == DWC3_LINK_STATE_SS_DIS ||
+ dwc->gadget.state == USB_STATE_DEFAULT)
+ link_recover_only = true;
}
+ /*
+ * According to DWC3 databook, the controller does not
+ * trigger a wakeup event when remote-wakeup is used.
+ * Hence, after remote-wakeup sequence is complete, and
+ * the device is back at U0 state, it is required that
+ * the resume sequence is initiated by SW.
+ */
+ if (!link_recover_only)
+ dwc3_gadget_wakeup_interrupt(dwc, true);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ pr_debug("%s: Exit\n", __func__);
+ return ret;
+
out:
spin_unlock_irqrestore(&dwc->lock, flags);
+ enable_irq(dwc->irq);
+
+out1:
+ return ret;
+}
+
+static int dwc_gadget_func_wakeup(struct usb_gadget *g, int interface_id)
+{
+ int ret = 0;
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ if (!g || (g->speed != USB_SPEED_SUPER))
+ return -ENOTSUPP;
+
+ if (dwc3_gadget_is_suspended(dwc)) {
+ pr_debug("USB bus is suspended. Scheduling wakeup and returning -EAGAIN.\n");
+ dwc3_gadget_wakeup(&dwc->gadget);
+ return -EAGAIN;
+ }
+
+ if (dwc->revision < DWC3_REVISION_220A) {
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_XMIT_FUNCTION, interface_id);
+ } else {
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_XMIT_DEV, 0x1 | (interface_id << 4));
+ }
+
+ if (ret)
+ pr_err("Function wakeup HW command failed.\n");
+ else
+ pr_debug("Function wakeup HW command succeeded.\n");
return ret;
}
@@ -1491,13 +1828,16 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
return 0;
}
+#define DWC3_SOFT_RESET_TIMEOUT 10 /* 10 msec */
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
u32 reg;
u32 timeout = 500;
+ ktime_t start, diff;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
+ dbg_event(0xFF, "Pullup_enable", is_on);
if (dwc->revision <= DWC3_REVISION_187A) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= DWC3_DCTL_TRGTULST_RX_DET;
@@ -1505,6 +1845,29 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
if (dwc->revision >= DWC3_REVISION_194A)
reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
+ start = ktime_get();
+ /* issue device SoftReset */
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg | DWC3_DCTL_CSFTRST);
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ break;
+
+ diff = ktime_sub(ktime_get(), start);
+ /* poll for max. 10ms */
+ if (ktime_to_ms(diff) > DWC3_SOFT_RESET_TIMEOUT) {
+ printk_ratelimited(KERN_ERR
+ "%s:core Reset Timed Out\n", __func__);
+ break;
+ }
+ cpu_relax();
+ } while (true);
+
+
+ dwc3_event_buffers_setup(dwc);
+ dwc3_gadget_restart(dwc);
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= DWC3_DCTL_RUN_STOP;
if (dwc->has_hibernation)
@@ -1512,12 +1875,18 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
dwc->pullups_connected = true;
} else {
+ dbg_event(0xFF, "Pullup_disable", is_on);
+ dwc3_gadget_disable_irq(dwc);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+
reg &= ~DWC3_DCTL_RUN_STOP;
if (dwc->has_hibernation && !suspend)
reg &= ~DWC3_DCTL_KEEP_CONNECT;
dwc->pullups_connected = false;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
}
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -1532,8 +1901,15 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
break;
}
timeout--;
- if (!timeout)
+ if (!timeout) {
+ dev_err(dwc->dev, "failed to %s controller\n",
+ is_on ? "start" : "stop");
+ if (is_on)
+ dbg_event(0xFF, "STARTTOUT", reg);
+ else
+ dbg_event(0xFF, "STOPTOUT", reg);
return -ETIMEDOUT;
+ }
udelay(1);
} while (1);
@@ -1545,6 +1921,16 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
return 0;
}
+static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned mA)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ dwc->vbus_draw = mA;
+ dev_dbg(dwc->dev, "Notify controller from %s. mA = %d\n", __func__, mA);
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT, 0);
+ return 0;
+}
+
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
struct dwc3 *dwc = gadget_to_dwc(g);
@@ -1553,14 +1939,43 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
is_on = !!is_on;
+ dwc->softconnect = is_on;
+
+ if ((dwc->is_drd && !dwc->vbus_active) || !dwc->gadget_driver) {
+ /*
+ * Need to wait for vbus_session(on) from otg driver or to
+ * the udc_start.
+ */
+ return 0;
+ }
+
+ pm_runtime_get_sync(dwc->dev);
+ dbg_event(0xFF, "Pullup gsync",
+ atomic_read(&dwc->dev->power.usage_count));
+
spin_lock_irqsave(&dwc->lock, flags);
+
+ /*
+ * If we are here after bus suspend notify otg state machine to
+ * increment pm usage count of dwc to prevent pm_runtime_suspend
+ * during enumeration.
+ */
+ dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
+ dwc->b_suspend = false;
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
+
ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_mark_last_busy(dwc->dev);
+ pm_runtime_put_autosuspend(dwc->dev);
+ dbg_event(0xFF, "Pullup put",
+ atomic_read(&dwc->dev->power.usage_count));
+
return ret;
}
-static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
+void dwc3_gadget_enable_irq(struct dwc3 *dwc)
{
u32 reg;
@@ -1570,53 +1985,91 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
DWC3_DEVTEN_CMDCMPLTEN |
DWC3_DEVTEN_ERRTICERREN |
DWC3_DEVTEN_WKUPEVTEN |
- DWC3_DEVTEN_ULSTCNGEN |
DWC3_DEVTEN_CONNECTDONEEN |
DWC3_DEVTEN_USBRSTEN |
DWC3_DEVTEN_DISCONNEVTEN);
+ /*
+ * Enable SUSPENDEVENT(BIT:6) for version 230A and above
+ * else enable USB Link change event (BIT:3) for older version
+ */
+ if (dwc->revision < DWC3_REVISION_230A)
+ reg |= DWC3_DEVTEN_ULSTCNGEN;
+ else
+ reg |= DWC3_DEVTEN_SUSPEND;
+
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}
-static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
+void dwc3_gadget_disable_irq(struct dwc3 *dwc)
{
/* mask all interrupts */
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
}
-static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
+static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc);
-static int dwc3_gadget_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
- struct dwc3_ep *dep;
- unsigned long flags;
- int ret = 0;
- int irq;
- u32 reg;
+ struct dwc3 *dwc = gadget_to_dwc(_gadget);
+ unsigned long flags;
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
- IRQF_SHARED, "dwc3", dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
- irq, ret);
- goto err0;
- }
+ if (!dwc->is_drd)
+ return -EPERM;
+
+ is_active = !!is_active;
spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver) {
- dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
- dwc->gadget_driver->driver.name);
- ret = -EBUSY;
- goto err1;
+ /* Mark that the vbus was powered */
+ dwc->vbus_active = is_active;
+
+ /*
+ * Check if upper level usb_gadget_driver was already registerd with
+ * this udc controller driver (if dwc3_gadget_start was called)
+ */
+ if (dwc->gadget_driver && dwc->softconnect) {
+ if (dwc->vbus_active) {
+ /*
+ * Both vbus was activated by otg and pullup was
+ * signaled by the gadget driver.
+ */
+ dwc3_gadget_run_stop(dwc, 1, false);
+ } else {
+ dwc3_gadget_run_stop(dwc, 0, false);
+ }
}
- dwc->gadget_driver = driver;
+ /*
+ * Clearing run/stop bit might occur before disconnect event is seen.
+ * Make sure to let gadget driver know in that case.
+ */
+ if (!dwc->vbus_active) {
+ dev_dbg(dwc->dev, "calling disconnect from %s\n", __func__);
+ dwc3_gadget_disconnect_interrupt(dwc);
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return 0;
+}
+
+static int __dwc3_gadget_start(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret = 0;
+ u32 reg;
+
+ /*
+ * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
+ * the core supports IMOD, disable it.
+ */
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ } else if (dwc3_has_imod(dwc)) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ }
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -1655,23 +2108,40 @@ static int dwc3_gadget_start(struct usb_gadget *g,
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ /* Programs the number of outstanding pipelined transfer requests
+ * the AXI master pushes to the AXI slave.
+ */
+ if (dwc->revision >= DWC3_REVISION_270A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1);
+ reg &= ~DWC3_GSBUSCFG1_PIPETRANSLIMIT_MASK;
+ reg |= DWC3_GSBUSCFG1_PIPETRANSLIMIT(0xe);
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, reg);
+ }
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ dwc->delayed_status = false;
+ /* reinitialize physical ep0-1 */
dep = dwc->eps[0];
+ dep->flags = 0;
+ dep->endpoint.maxburst = 1;
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err2;
+ return ret;
}
dep = dwc->eps[1];
+ dep->flags = 0;
+ dep->endpoint.maxburst = 1;
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err3;
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ return ret;
}
/* begin to receive SETUP packets */
@@ -1680,22 +2150,45 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc3_gadget_enable_irq(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ return ret;
+}
- return 0;
+/* Required gadget re-initialization before switching to gadget in OTG mode */
+void dwc3_gadget_restart(struct dwc3 *dwc)
+{
+ __dwc3_gadget_start(dwc);
+}
-err3:
- __dwc3_gadget_ep_disable(dwc->eps[0]);
+static int dwc3_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+ int ret = 0;
-err2:
- dwc->gadget_driver = NULL;
+ spin_lock_irqsave(&dwc->lock, flags);
-err1:
- spin_unlock_irqrestore(&dwc->lock, flags);
+ if (dwc->gadget_driver) {
+ dev_err(dwc->dev, "%s is already bound to %s\n",
+ dwc->gadget.name,
+ dwc->gadget_driver->driver.name);
+ ret = -EBUSY;
+ goto err0;
+ }
- free_irq(irq, dwc);
+ dwc->gadget_driver = driver;
+
+ /*
+ * For DRD, this might get called by gadget driver during bootup
+ * even though host mode might be active. Don't actually perform
+ * device-specific initialization until device mode is activated.
+ * In that case dwc3_gadget_restart() will handle it.
+ */
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return 0;
err0:
+ spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
}
@@ -1703,40 +2196,58 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- int irq;
-
- spin_lock_irqsave(&dwc->lock, flags);
- dwc3_gadget_disable_irq(dwc);
- __dwc3_gadget_ep_disable(dwc->eps[0]);
- __dwc3_gadget_ep_disable(dwc->eps[1]);
+ spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = NULL;
-
spin_unlock_irqrestore(&dwc->lock, flags);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- free_irq(irq, dwc);
-
return 0;
}
+static int dwc3_gadget_restart_usb_session(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ return dwc3_notify_event(dwc, DWC3_CONTROLLER_RESTART_USB_SESSION, 0);
+}
+
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
+ .func_wakeup = dwc_gadget_func_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
+ .vbus_session = dwc3_gadget_vbus_session,
+ .vbus_draw = dwc3_gadget_vbus_draw,
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
+ .restart = dwc3_gadget_restart_usb_session,
};
/* -------------------------------------------------------------------------- */
+#define NUM_GSI_OUT_EPS 1
+#define NUM_GSI_IN_EPS 2
+
static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
u8 num, u32 direction)
{
struct dwc3_ep *dep;
- u8 i;
+ u8 i, gsi_ep_count, gsi_ep_index = 0;
+
+ /* Read number of event buffers to check if we need
+ * to update gsi_ep_count. For non GSI targets this
+ * will be 0 and we will skip reservation of GSI eps.
+ * There is one event buffer for each GSI EP.
+ */
+ gsi_ep_count = dwc->num_gsi_event_buffers;
+ /* OUT GSI EPs based on direction field */
+ if (gsi_ep_count && !direction)
+ gsi_ep_count = NUM_GSI_OUT_EPS;
+ /* IN GSI EPs */
+ else if (gsi_ep_count && direction)
+ gsi_ep_count = NUM_GSI_IN_EPS;
for (i = 0; i < num; i++) {
u8 epnum = (i << 1) | (!!direction);
@@ -1750,9 +2261,21 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->direction = !!direction;
dwc->eps[epnum] = dep;
- snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
- (epnum & 1) ? "in" : "out");
+ /* Reserve EPs at the end for GSI based on gsi_ep_count */
+ if ((gsi_ep_index < gsi_ep_count) &&
+ (i > (num - 1 - gsi_ep_count))) {
+ gsi_ep_index++;
+ /* For GSI EPs, name eps as "gsi-epin" or "gsi-epout" */
+ snprintf(dep->name, sizeof(dep->name), "%s",
+ (epnum & 1) ? "gsi-epin" : "gsi-epout");
+ /* Set ep type as GSI */
+ dep->endpoint.ep_type = EP_TYPE_GSI;
+ } else {
+ snprintf(dep->name, sizeof(dep->name), "ep%d%s",
+ epnum >> 1, (epnum & 1) ? "in" : "out");
+ }
+ dep->endpoint.ep_num = epnum >> 1;
dep->endpoint.name = dep->name;
dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
@@ -1848,7 +2371,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
/* -------------------------------------------------------------------------- */
static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
- struct dwc3_request *req, struct dwc3_trb *trb,
+ struct dwc3_request *req, struct dwc3_trb *trb, unsigned length,
const struct dwc3_event_depevt *event, int status)
{
unsigned int count;
@@ -1892,6 +2415,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
* request in the request_list.
*/
dep->flags |= DWC3_EP_MISSED_ISOC;
+ dbg_event(dep->number, "MISSED ISOC", status);
} else {
dev_err(dwc->dev, "incomplete IN transfer %s\n",
dep->name);
@@ -1905,6 +2429,14 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
s_pkt = 1;
}
+ /*
+ * We assume here we will always receive the entire data block
+ * which we should receive. Meaning, if we program RX to
+ * receive 4K but we receive only 2K, we assume that's all we
+ * should receive and we simply bounce the request back to the
+ * gadget driver for further processing.
+ */
+ req->request.actual += length - count;
if (s_pkt)
return 1;
if ((event->status & DEPEVT_STATUS_LST) &&
@@ -1924,15 +2456,21 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_trb *trb;
unsigned int slot;
unsigned int i;
- int count = 0;
+ unsigned int trb_len;
int ret;
do {
req = next_request(&dep->req_queued);
if (!req) {
- WARN_ON_ONCE(1);
+ dev_err(dwc->dev, "%s: evt sts %x for no req queued",
+ dep->name, event->status);
return 1;
}
+
+ /* Make sure that not to queue any TRB if HWO bit is set. */
+ if (req->trb->ctrl & DWC3_TRB_CTRL_HWO)
+ return 0;
+
i = 0;
do {
slot = req->start_slot + i;
@@ -1941,50 +2479,50 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
slot++;
slot %= DWC3_TRB_NUM;
trb = &dep->trb_pool[slot];
- count += trb->size & DWC3_TRB_SIZE_MASK;
+ if (req->request.num_mapped_sgs)
+ trb_len = sg_dma_len(&req->request.sg[i]);
+ else
+ trb_len = req->request.length;
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status);
+ trb_len, event, status);
if (ret)
break;
} while (++i < req->request.num_mapped_sgs);
- /*
- * We assume here we will always receive the entire data block
- * which we should receive. Meaning, if we program RX to
- * receive 4K but we receive only 2K, we assume that's all we
- * should receive and we simply bounce the request back to the
- * gadget driver for further processing.
- */
- req->request.actual += req->request.length - count;
dwc3_gadget_giveback(dep, req, status);
+ /* EP possibly disabled during giveback? */
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ dev_dbg(dwc->dev, "%s disabled while handling ep event\n",
+ dep->name);
+ return 0;
+ }
+
if (ret)
break;
} while (1);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) {
- if (list_empty(&dep->request_list)) {
+ if (list_empty(&dep->request_list))
/*
* If there is no entry in request list then do
* not issue END TRANSFER now. Just set PENDING
* flag, so that END TRANSFER is issued when an
* entry is added into request list.
*/
- dep->flags = DWC3_EP_PENDING_REQUEST;
- } else {
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+ else
dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- }
+ dep->flags &= ~DWC3_EP_MISSED_ISOC;
return 1;
}
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- return 0;
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ return 0;
return 1;
}
@@ -2029,14 +2567,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dwc->u1u2 = 0;
}
-
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- int ret;
-
- ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete);
- if (!ret || ret == -EBUSY)
- return;
- }
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -2055,9 +2585,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return;
}
+ dep->dbg_ep_events.total++;
+
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dep->resource_index = 0;
+ dep->dbg_ep_events.xfercomplete++;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
@@ -2068,22 +2601,37 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
+ dep->dbg_ep_events.xferinprogress++;
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n",
+ dep->name);
+ return;
+ }
+
dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
+ dep->dbg_ep_events.xfernotready++;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
- int active;
int ret;
- active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
-
dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
- dep->name, active ? "Transfer Active"
+ dep->name, event->status &
+ DEPEVT_STATUS_TRANSFER_ACTIVE
+ ? "Transfer Active"
: "Transfer Not Active");
- ret = __dwc3_gadget_kick_transfer(dep, 0, !active);
+ /*
+ * If XFERNOTREADY interrupt is received with event
+ * status as TRANSFER ACTIVE, don't kick next transfer.
+ * otherwise data stall is seen on that endpoint.
+ */
+ if (event->status & DEPEVT_STATUS_TRANSFER_ACTIVE)
+ return;
+
+ ret = __dwc3_gadget_kick_transfer(dep, 0, 1);
if (!ret || ret == -EBUSY)
return;
@@ -2093,6 +2641,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEPEVT_STREAMEVT:
+ dep->dbg_ep_events.streamevent++;
if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
dep->name);
@@ -2114,9 +2663,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
+ dep->dbg_ep_events.rxtxfifoevent++;
break;
case DWC3_DEPEVT_EPCMDCMPLT:
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
+ dep->dbg_ep_events.epcmdcomplete++;
break;
}
}
@@ -2134,6 +2685,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
+ dbg_event(0xFF, "SUSPEND", 0);
dwc->gadget_driver->suspend(&dwc->gadget);
spin_lock(&dwc->lock);
}
@@ -2143,6 +2695,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
{
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
+ dbg_event(0xFF, "RESUME", 0);
dwc->gadget_driver->resume(&dwc->gadget);
spin_lock(&dwc->lock);
}
@@ -2160,7 +2713,7 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
+void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
{
struct dwc3_ep *dep;
struct dwc3_gadget_ep_cmd_params params;
@@ -2172,6 +2725,10 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
if (!dep->resource_index)
return;
+ if (dep->endpoint.endless)
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER,
+ dep->number);
+
/*
* NOTICE: We are violating what the Databook says about the
* EndTransfer command. Ideally we would _always_ wait for the
@@ -2242,7 +2799,11 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_CLEARSTALL, &params);
- WARN_ON_ONCE(ret);
+ if (ret) {
+ dev_dbg(dwc->dev, "%s; send ep cmd CLEARSTALL failed",
+ dep->name);
+ dbg_event(dep->number, "ECLRSTALL", ret);
+ }
}
}
@@ -2250,6 +2811,10 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
int reg;
+ dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
+ dwc->b_suspend = false;
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -2257,11 +2822,14 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dbg_event(0xFF, "DISCONNECT", 0);
dwc3_disconnect_gadget(dwc);
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
+ dwc->link_state = DWC3_LINK_STATE_SS_DIS;
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+ wake_up_interruptible(&dwc->wait_linkstate);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -2299,7 +2867,15 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_gadget_disconnect_interrupt(dwc);
}
+ dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
+ dwc->b_suspend = false;
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
+
+ dwc3_usb3_phy_suspend(dwc, false);
+ usb_gadget_vbus_draw(&dwc->gadget, 100);
+
dwc3_reset_gadget(dwc);
+ dbg_event(0xFF, "BUS RST", 0);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
@@ -2309,10 +2885,16 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_stop_active_transfers(dwc);
dwc3_clear_stall_all_ep(dwc);
+ /* bus reset issued due to missing status stage of a control transfer */
+ dwc->resize_fifos = 0;
+
/* Reset device address to zero */
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->link_state = DWC3_LINK_STATE_U0;
+ wake_up_interruptible(&dwc->wait_linkstate);
}
static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
@@ -2426,6 +3008,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
+ /*
+ * In HS mode this allows SS phy suspend. In SS mode this allows ss phy
+ * suspend in P3 state and generates IN_P3 power event irq.
+ */
+ dwc3_usb3_phy_suspend(dwc, true);
+
dep = dwc->eps[0];
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
false);
@@ -2442,6 +3030,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
return;
}
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_CONNDONE_EVENT, 0);
+
/*
* Configure PHY via GUSB3PIPECTLn if required.
*
@@ -2451,14 +3041,45 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
*/
}
-static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
+static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup)
{
+ bool perform_resume = true;
+
+ dev_dbg(dwc->dev, "%s\n", __func__);
+
/*
- * TODO take core out of low power mode when that's
- * implemented.
+ * Identify if it is called from wakeup_interrupt() context for bus
+ * resume or as part of remote wakeup. And based on that check for
+ * U3 state. as we need to handle case of L1 resume i.e. where we
+ * don't want to perform resume.
*/
+ if (!remote_wakeup && dwc->link_state != DWC3_LINK_STATE_U3)
+ perform_resume = false;
- dwc->gadget_driver->resume(&dwc->gadget);
+ /* Only perform resume from L2 or Early Suspend states */
+ if (perform_resume) {
+ dbg_event(0xFF, "WAKEUP", 0);
+
+ /*
+ * In case of remote wake up dwc3_gadget_wakeup_work()
+ * is doing pm_runtime_get_sync().
+ */
+ dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
+ dwc->b_suspend = false;
+ dwc3_notify_event(dwc,
+ DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
+
+ /*
+ * set state to U0 as function level resume is trying to queue
+ * notification over USB interrupt endpoint which would fail
+ * due to state is not being updated.
+ */
+ dwc->link_state = DWC3_LINK_STATE_U0;
+ dwc3_resume_gadget(dwc);
+ return;
+ }
+
+ dwc->link_state = DWC3_LINK_STATE_U0;
}
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
@@ -2558,7 +3179,9 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
break;
}
+ dev_dbg(dwc->dev, "Going from (%d)--->(%d)\n", dwc->link_state, next);
dwc->link_state = next;
+ wake_up_interruptible(&dwc->wait_linkstate);
}
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
@@ -2585,21 +3208,82 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
/* enter hibernation here */
}
+static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+
+ dev_dbg(dwc->dev, "%s Entry to %d\n", __func__, next);
+
+ if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) {
+ /*
+ * When first connecting the cable, even before the initial
+ * DWC3_DEVICE_EVENT_RESET or DWC3_DEVICE_EVENT_CONNECT_DONE
+ * events, the controller sees a DWC3_DEVICE_EVENT_SUSPEND
+ * event. In such a case, ignore.
+ * Ignore suspend event until device side usb is not into
+ * CONFIGURED state.
+ */
+ if (dwc->gadget.state != USB_STATE_CONFIGURED) {
+ pr_err("%s(): state:%d. Ignore SUSPEND.\n",
+ __func__, dwc->gadget.state);
+ return;
+ }
+
+ dwc3_suspend_gadget(dwc);
+
+ dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
+ dwc->b_suspend = true;
+ dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
+ }
+
+ dwc->link_state = next;
+ dwc3_trace(trace_dwc3_gadget, "link state %d", dwc->link_state);
+}
+
+static void dwc3_dump_reg_info(struct dwc3 *dwc)
+{
+ dbg_event(0xFF, "REGDUMP", 0);
+
+ dbg_print_reg("GUSB3PIPCTL", dwc3_readl(dwc->regs,
+ DWC3_GUSB3PIPECTL(0)));
+ dbg_print_reg("GUSB2PHYCONFIG", dwc3_readl(dwc->regs,
+ DWC3_GUSB2PHYCFG(0)));
+ dbg_print_reg("GCTL", dwc3_readl(dwc->regs, DWC3_GCTL));
+ dbg_print_reg("GUCTL", dwc3_readl(dwc->regs, DWC3_GUCTL));
+ dbg_print_reg("GDBGLTSSM", dwc3_readl(dwc->regs, DWC3_GDBGLTSSM));
+ dbg_print_reg("DCFG", dwc3_readl(dwc->regs, DWC3_DCFG));
+ dbg_print_reg("DCTL", dwc3_readl(dwc->regs, DWC3_DCTL));
+ dbg_print_reg("DEVTEN", dwc3_readl(dwc->regs, DWC3_DEVTEN));
+ dbg_print_reg("DSTS", dwc3_readl(dwc->regs, DWC3_DSTS));
+ dbg_print_reg("DALPENA", dwc3_readl(dwc->regs, DWC3_DALEPENA));
+ dbg_print_reg("DGCMD", dwc3_readl(dwc->regs, DWC3_DGCMD));
+
+ dbg_print_reg("OCFG", dwc3_readl(dwc->regs, DWC3_OCFG));
+ dbg_print_reg("OCTL", dwc3_readl(dwc->regs, DWC3_OCTL));
+ dbg_print_reg("OEVT", dwc3_readl(dwc->regs, DWC3_OEVT));
+ dbg_print_reg("OSTS", dwc3_readl(dwc->regs, DWC3_OSTS));
+}
+
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
const struct dwc3_event_devt *event)
{
switch (event->type) {
case DWC3_DEVICE_EVENT_DISCONNECT:
dwc3_gadget_disconnect_interrupt(dwc);
+ dwc->dbg_gadget_events.disconnect++;
break;
case DWC3_DEVICE_EVENT_RESET:
dwc3_gadget_reset_interrupt(dwc);
+ dwc->dbg_gadget_events.reset++;
break;
case DWC3_DEVICE_EVENT_CONNECT_DONE:
dwc3_gadget_conndone_interrupt(dwc);
+ dwc->dbg_gadget_events.connect++;
break;
case DWC3_DEVICE_EVENT_WAKEUP:
- dwc3_gadget_wakeup_interrupt(dwc);
+ dwc3_gadget_wakeup_interrupt(dwc, false);
+ dwc->dbg_gadget_events.wakeup++;
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
@@ -2610,25 +3294,54 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
+ dwc->dbg_gadget_events.link_status_change++;
break;
- case DWC3_DEVICE_EVENT_EOPF:
- dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+ case DWC3_DEVICE_EVENT_SUSPEND:
+ if (dwc->revision < DWC3_REVISION_230A) {
+ dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+ dwc->dbg_gadget_events.eopf++;
+ } else {
+ dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
+ dbg_event(0xFF, "GAD SUS", 0);
+ dwc->dbg_gadget_events.suspend++;
+
+ /*
+ * Ignore suspend event if usb cable is not connected
+ * and speed is not being detected.
+ */
+ if (dwc->gadget.speed != USB_SPEED_UNKNOWN &&
+ dwc->vbus_active)
+ dwc3_gadget_suspend_interrupt(dwc,
+ event->event_info);
+ }
break;
case DWC3_DEVICE_EVENT_SOF:
dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
+ dwc->dbg_gadget_events.sof++;
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
dwc3_trace(trace_dwc3_gadget, "Erratic Error");
+ if (!dwc->err_evt_seen) {
+ dbg_event(0xFF, "ERROR", 0);
+ dwc3_dump_reg_info(dwc);
+ }
+ dwc->dbg_gadget_events.erratic_error++;
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
dwc3_trace(trace_dwc3_gadget, "Command Complete");
+ dwc->dbg_gadget_events.cmdcmplt++;
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
dwc3_trace(trace_dwc3_gadget, "Overflow");
+ dbg_event(0xFF, "OVERFL", 0);
+ dwc->dbg_gadget_events.overflow++;
break;
default:
dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ dwc->dbg_gadget_events.unknown_event++;
}
+
+ dwc->err_evt_seen = (event->type == DWC3_DEVICE_EVENT_ERRATIC_ERROR);
}
static void dwc3_process_event_entry(struct dwc3 *dwc,
@@ -2636,6 +3349,18 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
{
trace_dwc3_event(event->raw);
+ /* skip event processing in absence of vbus */
+ if (!dwc->vbus_active) {
+ dbg_print_reg("SKIP EVT", event->raw);
+ return;
+ }
+
+ /* If run/stop is cleared don't process any more events */
+ if (!dwc->pullups_connected) {
+ dbg_print_reg("SKIP_EVT_PULLUP", event->raw);
+ return;
+ }
+
/* Endpoint IRQ, handle it and return early */
if (event->type.is_devspec == 0) {
/* depevt */
@@ -2672,6 +3397,20 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
dwc3_process_event_entry(dwc, &event);
+ if (dwc->err_evt_seen) {
+ /*
+ * if erratic error, skip remaining events
+ * while controller undergoes reset
+ */
+ evt->lpos = (evt->lpos + left) %
+ DWC3_EVENT_BUFFERS_SIZE;
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), left);
+ if (dwc3_notify_event(dwc,
+ DWC3_CONTROLLER_ERROR_EVENT, 0))
+ dwc->err_evt_seen = 0;
+ break;
+ }
+
/*
* FIXME we wrap around correctly to the next entry as
* almost all entries are 4 bytes in size. There is one
@@ -2683,10 +3422,10 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
*/
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
left -= 4;
-
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
}
+ dwc->bh_handled_evt_cnt[dwc->bh_dbg_index] += (evt->count / 4);
+
evt->count = 0;
evt->flags &= ~DWC3_EVENT_PENDING;
ret = IRQ_HANDLED;
@@ -2696,23 +3435,45 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
reg &= ~DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+ if (dwc->imod_interval)
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf),
+ DWC3_GEVNTCOUNT_EHB);
+
return ret;
}
+void dwc3_bh_work(struct work_struct *w)
+{
+ struct dwc3 *dwc = container_of(w, struct dwc3, bh_work);
+
+ pm_runtime_get_sync(dwc->dev);
+ dwc3_thread_interrupt(dwc->irq, dwc);
+ pm_runtime_put(dwc->dev);
+}
+
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
{
struct dwc3 *dwc = _dwc;
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
int i;
+ unsigned temp_time;
+ ktime_t start_time;
+
+ start_time = ktime_get();
spin_lock_irqsave(&dwc->lock, flags);
+ dwc->bh_handled_evt_cnt[dwc->bh_dbg_index] = 0;
- for (i = 0; i < dwc->num_event_buffers; i++)
+ for (i = 0; i < dwc->num_normal_event_buffers; i++)
ret |= dwc3_process_event_buf(dwc, i);
spin_unlock_irqrestore(&dwc->lock, flags);
+ temp_time = ktime_to_us(ktime_sub(ktime_get(), start_time));
+ dwc->bh_completion_time[dwc->bh_dbg_index] = temp_time;
+ dwc->bh_dbg_index = (dwc->bh_dbg_index + 1) % 10;
+
return ret;
}
@@ -2729,6 +3490,13 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
if (!count)
return IRQ_NONE;
+ if (count > evt->length) {
+ dbg_event(0xFF, "HUGE_EVCNT", count);
+ evt->lpos = (evt->lpos + count) % DWC3_EVENT_BUFFERS_SIZE;
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), count);
+ return IRQ_HANDLED;
+ }
+
evt->count = count;
evt->flags |= DWC3_EVENT_PENDING;
@@ -2737,24 +3505,46 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
reg |= DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), count);
+
return IRQ_WAKE_THREAD;
}
-static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
+irqreturn_t dwc3_interrupt(int irq, void *_dwc)
{
struct dwc3 *dwc = _dwc;
int i;
irqreturn_t ret = IRQ_NONE;
+ unsigned temp_cnt = 0;
+ ktime_t start_time;
+
+ start_time = ktime_get();
+ dwc->irq_cnt++;
- for (i = 0; i < dwc->num_event_buffers; i++) {
+ /* controller reset is still pending */
+ if (dwc->err_evt_seen)
+ return IRQ_HANDLED;
+
+ for (i = 0; i < dwc->num_normal_event_buffers; i++) {
irqreturn_t status;
status = dwc3_check_event_buf(dwc, i);
if (status == IRQ_WAKE_THREAD)
ret = status;
+
+ temp_cnt += dwc->ev_buffs[i]->count;
}
- return ret;
+ dwc->irq_start_time[dwc->irq_dbg_index] = start_time;
+ dwc->irq_completion_time[dwc->irq_dbg_index] =
+ ktime_us_delta(ktime_get(), start_time);
+ dwc->irq_event_count[dwc->irq_dbg_index] = temp_cnt / 4;
+ dwc->irq_dbg_index = (dwc->irq_dbg_index + 1) % MAX_INTR_STATS;
+
+ if (ret == IRQ_WAKE_THREAD)
+ queue_work(dwc->dwc_wq, &dwc->bh_work);
+
+ return IRQ_HANDLED;
}
/**
@@ -2767,6 +3557,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
+ INIT_WORK(&dwc->wakeup_work, dwc3_gadget_wakeup_work);
+
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
if (!dwc->ctrl_req) {
@@ -2798,6 +3590,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}
+ dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
+ if (!dwc->zlp_buf) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
@@ -2839,16 +3637,26 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err4;
+ goto err5;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err4;
+ goto err5;
+ }
+
+ if (!dwc->is_drd) {
+ pm_runtime_no_callbacks(&dwc->gadget.dev);
+ pm_runtime_set_active(&dwc->gadget.dev);
+ pm_runtime_enable(&dwc->gadget.dev);
+ pm_runtime_get(&dwc->gadget.dev);
}
return 0;
+err5:
+ kfree(dwc->zlp_buf);
+
err4:
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
@@ -2873,6 +3681,11 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
+ if (dwc->is_drd) {
+ pm_runtime_put(&dwc->gadget.dev);
+ pm_runtime_disable(&dwc->gadget.dev);
+ }
+
usb_del_gadget_udc(&dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
@@ -2881,6 +3694,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
+ kfree(dwc->zlp_buf);
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);