mirror of https://github.com/OpenIPC/firmware.git
977 lines
26 KiB
Diff
977 lines
26 KiB
Diff
--- linux-4.9.37/drivers/usb/dwc3/gadget.c 2017-07-12 16:42:41.000000000 +0300
|
|
+++ linux-4.9.y/drivers/usb/dwc3/gadget.c 2021-06-07 13:01:34.000000000 +0300
|
|
@@ -35,6 +35,15 @@
|
|
#include "gadget.h"
|
|
#include "io.h"
|
|
|
|
+static void dwc3_gadget_sync_connected_status(struct dwc3 *dwc);
|
|
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc);
|
|
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
|
|
+ const struct dwc3_event_depevt *event);
|
|
+static bool __dwc3_gadget_target_frame_elapsed(struct dwc3_ep *dep);
|
|
+
|
|
+#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
|
|
+ & ~((d)->interval - 1))
|
|
+
|
|
/**
|
|
* dwc3_gadget_set_test_mode - Enables USB2 Test Modes
|
|
* @dwc: pointer to our context structure
|
|
@@ -139,9 +148,6 @@
|
|
udelay(5);
|
|
}
|
|
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "link state change request timed out");
|
|
-
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
@@ -153,7 +159,7 @@
|
|
* if it is point to the link TRB, wrap around to the beginning. The
|
|
* link TRB is always at the last TRB entry.
|
|
*/
|
|
-static void dwc3_ep_inc_trb(u8 *index)
|
|
+static void dwc3_ep_inc_trb(u32 *index)
|
|
{
|
|
(*index)++;
|
|
if (*index == (DWC3_TRB_NUM - 1))
|
|
@@ -247,7 +253,7 @@
|
|
struct dwc3_gadget_ep_cmd_params *params)
|
|
{
|
|
struct dwc3 *dwc = dep->dwc;
|
|
- u32 timeout = 500;
|
|
+ u32 timeout = 5000;
|
|
u32 reg;
|
|
|
|
int cmd_status = 0;
|
|
@@ -563,8 +569,6 @@
|
|
u32 reg;
|
|
int ret;
|
|
|
|
- dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
|
|
-
|
|
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
|
ret = dwc3_gadget_start_config(dwc, dep);
|
|
if (ret)
|
|
@@ -590,7 +594,7 @@
|
|
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
|
|
|
if (usb_endpoint_xfer_control(desc))
|
|
- return 0;
|
|
+ goto out;
|
|
|
|
/* Initialize the TRB ring */
|
|
dep->trb_dequeue = 0;
|
|
@@ -608,15 +612,19 @@
|
|
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
|
|
}
|
|
|
|
+
|
|
+out:
|
|
+ trace_dwc3_gadget_ep_enable(dep);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
|
|
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force);
|
|
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|
{
|
|
struct dwc3_request *req;
|
|
|
|
- dwc3_stop_active_transfer(dwc, dep->number, true);
|
|
+ dwc3_stop_active_transfer(dep, true);
|
|
|
|
/* - giveback all requests to gadget driver */
|
|
while (!list_empty(&dep->started_list)) {
|
|
@@ -645,7 +653,7 @@
|
|
struct dwc3 *dwc = dep->dwc;
|
|
u32 reg;
|
|
|
|
- dwc3_trace(trace_dwc3_gadget, "Disabling %s", dep->name);
|
|
+ trace_dwc3_gadget_ep_disable(dep);
|
|
|
|
dwc3_remove_requests(dwc, dep);
|
|
|
|
@@ -787,10 +795,7 @@
|
|
struct dwc3 *dwc = dep->dwc;
|
|
struct usb_gadget *gadget = &dwc->gadget;
|
|
enum usb_device_speed speed = gadget->speed;
|
|
-
|
|
- dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
|
|
- dep->name, req, (unsigned long long) dma,
|
|
- length, chain ? " chain" : "");
|
|
+ unsigned int chain_skip = 0;
|
|
|
|
trb = &dep->trb_pool[dep->trb_enqueue];
|
|
|
|
@@ -816,12 +821,55 @@
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
if (!node) {
|
|
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
|
-
|
|
+ /*
|
|
+ * USB Specification 2.0 Section 5.9.2 states that: "If
|
|
+ * there is only a single transaction in the microframe,
|
|
+ * only a DATA0 data packet PID is used. If there are
|
|
+ * two transactions per microframe, DATA1 is used for
|
|
+ * the first transaction data packet and DATA0 is used
|
|
+ * for the second transaction data packet. If there are
|
|
+ * three transactions per microframe, DATA2 is used for
|
|
+ * the first transaction data packet, DATA1 is used for
|
|
+ * the second, and DATA0 is used for the third."
|
|
+ *
|
|
+ * IOW, we should satisfy the following cases:
|
|
+ *
|
|
+ * 1) length <= maxpacket
|
|
+ * - DATA0
|
|
+ *
|
|
+ * 2) maxpacket < length <= (2 * maxpacket)
|
|
+ * - DATA1, DATA0
|
|
+ *
|
|
+ * 3) (2 * maxpacket) < length <= (3 * maxpacket)
|
|
+ * - DATA2, DATA1, DATA0
|
|
+ */
|
|
if (speed == USB_SPEED_HIGH) {
|
|
struct usb_ep *ep = &dep->endpoint;
|
|
- trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
|
|
+ //unsigned int mult = ep->mult - 1;
|
|
+ unsigned int mult = 2; //backport commit ec5bb87e4e2a1d3a35563a7bcfac9febf67aba9d
|
|
+ unsigned int maxp = usb_endpoint_maxp(ep->desc);
|
|
+
|
|
+ if (length <= (2 * maxp))
|
|
+ mult--;
|
|
+
|
|
+ if (length <= maxp)
|
|
+ mult--;
|
|
+
|
|
+ trb->size |= DWC3_TRB_SIZE_PCM1(mult);
|
|
+
|
|
+ /*
|
|
+ * If there are three transactions per mframe,
|
|
+ * and each transcation length = 1024B, no any
|
|
+ * chain trb needed, so skip it.
|
|
+ */
|
|
+ if (length == (3 * maxp))
|
|
+ chain_skip = 1;
|
|
}
|
|
+
|
|
+ if (speed == USB_SPEED_SUPER)
|
|
+ chain_skip = 1;
|
|
} else {
|
|
+ chain_skip = 1;
|
|
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
|
|
}
|
|
|
|
@@ -848,7 +896,7 @@
|
|
(dwc3_calc_trbs_left(dep) == 0))
|
|
trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
|
|
|
|
- if (chain)
|
|
+ if ((!chain_skip) && chain)
|
|
trb->ctrl |= DWC3_TRB_CTRL_CHN;
|
|
|
|
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
|
|
@@ -868,9 +916,9 @@
|
|
* index is 0, we will wrap backwards, skip the link TRB, and return
|
|
* the one just before that.
|
|
*/
|
|
-static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
|
|
+static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u32 index)
|
|
{
|
|
- u8 tmp = index;
|
|
+ u32 tmp = index;
|
|
|
|
if (!tmp)
|
|
tmp = DWC3_TRB_NUM - 1;
|
|
@@ -881,7 +929,7 @@
|
|
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
|
|
{
|
|
struct dwc3_trb *tmp;
|
|
- u8 trbs_left;
|
|
+ u32 trbs_left;
|
|
|
|
/*
|
|
* If enqueue & dequeue are equal than it is either full or empty.
|
|
@@ -910,11 +958,13 @@
|
|
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
|
struct dwc3_request *req)
|
|
{
|
|
+ struct dwc3 *dwc = dep->dwc;
|
|
+ struct usb_gadget *gadget = &dwc->gadget;
|
|
+ enum usb_device_speed speed = gadget->speed;
|
|
struct scatterlist *sg = req->sg;
|
|
struct scatterlist *s;
|
|
- unsigned int length;
|
|
+ unsigned int length, i;
|
|
dma_addr_t dma;
|
|
- int i;
|
|
|
|
for_each_sg(sg, s, req->num_pending_sgs, i) {
|
|
unsigned chain = true;
|
|
@@ -925,8 +975,11 @@
|
|
if (sg_is_last(s))
|
|
chain = false;
|
|
|
|
- dwc3_prepare_one_trb(dep, req, dma, length,
|
|
- chain, i);
|
|
+ if ((speed == USB_SPEED_HIGH) &&
|
|
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
|
+ dwc3_prepare_one_trb(dep, req, dma, length, chain, 0);
|
|
+ else
|
|
+ dwc3_prepare_one_trb(dep, req, dma, length, chain, i);
|
|
|
|
if (!dwc3_calc_trbs_left(dep))
|
|
break;
|
|
@@ -994,11 +1047,22 @@
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
- if (starting) {
|
|
+ if (starting && !(dep->flags&DWC3_EP_UPDATE)) {
|
|
params.param0 = upper_32_bits(req->trb_dma);
|
|
params.param1 = lower_32_bits(req->trb_dma);
|
|
- cmd = DWC3_DEPCMD_STARTTRANSFER |
|
|
- DWC3_DEPCMD_PARAM(cmd_param);
|
|
+
|
|
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
+ while (__dwc3_gadget_target_frame_elapsed(dep))
|
|
+ dep->frame_number = DWC3_ALIGN_FRAME(dep);
|
|
+
|
|
+ dep->frame_number = DWC3_ALIGN_FRAME(dep);
|
|
+ cmd_param = dep->frame_number;
|
|
+ }
|
|
+
|
|
+ cmd = DWC3_DEPCMD_STARTTRANSFER | DWC3_DEPCMD_PARAM(cmd_param);
|
|
+
|
|
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
|
+ dep->flags |= DWC3_EP_UPDATE;
|
|
} else {
|
|
cmd = DWC3_DEPCMD_UPDATETRANSFER |
|
|
DWC3_DEPCMD_PARAM(dep->resource_index);
|
|
@@ -1027,34 +1091,36 @@
|
|
return 0;
|
|
}
|
|
|
|
-static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|
- struct dwc3_ep *dep, u32 cur_uf)
|
|
-{
|
|
- u32 uf;
|
|
+static bool __dwc3_gadget_target_frame_elapsed(struct dwc3_ep *dep) {
|
|
+ u16 cframe = __dwc3_gadget_get_frame(dep->dwc);
|
|
+ u16 eframe = dep->frame_number & DWC3_EVENT_PRAM_SOFFN_MASK;
|
|
+
|
|
+ if (eframe == cframe)
|
|
+ return true;
|
|
+
|
|
+ return (((eframe - cframe) & DWC3_EVENT_PRAM_SOFFN_MASK)
|
|
+ > DWC3_EVENT_PRAM_MAX_SOFFN / 2);
|
|
+}
|
|
|
|
+static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
|
+{
|
|
if (list_empty(&dep->pending_list)) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "ISOC ep %s run out for requests",
|
|
- dep->name);
|
|
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
|
return;
|
|
}
|
|
|
|
- /* 4 micro frames in the future */
|
|
- uf = cur_uf + dep->interval * 4;
|
|
+ while (__dwc3_gadget_target_frame_elapsed(dep))
|
|
+ dep->frame_number = DWC3_ALIGN_FRAME(dep);
|
|
|
|
- __dwc3_gadget_kick_transfer(dep, uf);
|
|
+ dep->frame_number = DWC3_ALIGN_FRAME(dep);
|
|
+ __dwc3_gadget_kick_transfer(dep, dep->frame_number);
|
|
}
|
|
|
|
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
|
|
+static void dwc3_gadget_start_isoc(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);
|
|
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
|
|
+ __dwc3_gadget_start_isoc(dep);
|
|
}
|
|
|
|
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|
@@ -1063,16 +1129,15 @@
|
|
int ret;
|
|
|
|
if (!dep->endpoint.desc) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "trying to queue request %p to disabled %s",
|
|
- &req->request, dep->endpoint.name);
|
|
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
|
|
+ dep->name);
|
|
return -ESHUTDOWN;
|
|
}
|
|
|
|
if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
|
|
&req->request, req->dep->name)) {
|
|
- dwc3_trace(trace_dwc3_gadget, "request %pK belongs to '%s'",
|
|
- &req->request, req->dep->name);
|
|
+ dev_err(dwc->dev, "%s: request %p belongs to '%s'\n",
|
|
+ dep->name, &req->request, req->dep->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -1106,7 +1171,7 @@
|
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
|
|
list_empty(&dep->started_list)) {
|
|
- dwc3_stop_active_transfer(dwc, dep->number, true);
|
|
+ dwc3_stop_active_transfer(dep, true);
|
|
dep->flags = DWC3_EP_ENABLED;
|
|
}
|
|
return 0;
|
|
@@ -1116,10 +1181,6 @@
|
|
return 0;
|
|
|
|
ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
- if (ret && ret != -EBUSY)
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "%s: failed to kick transfers",
|
|
- dep->name);
|
|
if (ret == -EBUSY)
|
|
ret = 0;
|
|
|
|
@@ -1138,7 +1199,6 @@
|
|
struct usb_request *request;
|
|
struct usb_ep *ep = &dep->endpoint;
|
|
|
|
- dwc3_trace(trace_dwc3_gadget, "queueing ZLP");
|
|
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
|
|
if (!request)
|
|
return -ENOMEM;
|
|
@@ -1197,6 +1257,10 @@
|
|
|
|
spin_lock_irqsave(&dwc->lock, flags);
|
|
|
|
+
|
|
+ if (list_empty(&dep->pending_list) && list_empty(&dep->started_list) )
|
|
+ goto out0 ;
|
|
+
|
|
list_for_each_entry(r, &dep->pending_list, list) {
|
|
if (r == req)
|
|
break;
|
|
@@ -1209,7 +1273,7 @@
|
|
}
|
|
if (r == req) {
|
|
/* wait until it is processed */
|
|
- dwc3_stop_active_transfer(dwc, dep->number, true);
|
|
+ dwc3_stop_active_transfer(dep, true);
|
|
goto out1;
|
|
}
|
|
dev_err(dwc->dev, "request %pK was not queued to %s\n",
|
|
@@ -1247,9 +1311,6 @@
|
|
unsigned transfer_in_flight;
|
|
unsigned started;
|
|
|
|
- if (dep->flags & DWC3_EP_STALL)
|
|
- return 0;
|
|
-
|
|
if (dep->number > 1)
|
|
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
|
|
else
|
|
@@ -1260,9 +1321,6 @@
|
|
|
|
if (!protocol && ((dep->direction && transfer_in_flight) ||
|
|
(!dep->direction && started))) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "%s: pending request, cannot halt",
|
|
- dep->name);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
@@ -1274,8 +1332,6 @@
|
|
else
|
|
dep->flags |= DWC3_EP_STALL;
|
|
} else {
|
|
- if (!(dep->flags & DWC3_EP_STALL))
|
|
- return 0;
|
|
|
|
ret = dwc3_send_clear_stall_ep_cmd(dep);
|
|
if (ret)
|
|
@@ -1355,15 +1411,21 @@
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
-static int dwc3_gadget_get_frame(struct usb_gadget *g)
|
|
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
|
{
|
|
- struct dwc3 *dwc = gadget_to_dwc(g);
|
|
u32 reg;
|
|
|
|
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
|
return DWC3_DSTS_SOFFN(reg);
|
|
}
|
|
|
|
+static int dwc3_gadget_get_frame(struct usb_gadget *g)
|
|
+{
|
|
+ struct dwc3 *dwc = gadget_to_dwc(g);
|
|
+
|
|
+ return __dwc3_gadget_get_frame(dwc);
|
|
+}
|
|
+
|
|
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
|
{
|
|
int retries;
|
|
@@ -1384,10 +1446,8 @@
|
|
|
|
speed = reg & DWC3_DSTS_CONNECTSPD;
|
|
if ((speed == DWC3_DSTS_SUPERSPEED) ||
|
|
- (speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
|
- dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed");
|
|
+ (speed == DWC3_DSTS_SUPERSPEED_PLUS))
|
|
return 0;
|
|
- }
|
|
|
|
link_state = DWC3_DSTS_USBLNKST(reg);
|
|
|
|
@@ -1396,9 +1456,6 @@
|
|
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
|
|
break;
|
|
default:
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "can't wakeup from '%s'",
|
|
- dwc3_gadget_link_string(link_state));
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -1503,11 +1560,6 @@
|
|
if (!timeout)
|
|
return -ETIMEDOUT;
|
|
|
|
- dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
|
|
- dwc->gadget_driver
|
|
- ? dwc->gadget_driver->function : "no-function",
|
|
- is_on ? "connect" : "disconnect");
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -1677,6 +1729,7 @@
|
|
|
|
/* begin to receive SETUP packets */
|
|
dwc->ep0state = EP0_SETUP_PHASE;
|
|
+ dwc->link_state = DWC3_LINK_STATE_SS_DIS;
|
|
dwc3_ep0_out_start(dwc);
|
|
|
|
dwc3_gadget_enable_irq(dwc);
|
|
@@ -1767,6 +1820,81 @@
|
|
.udc_stop = dwc3_gadget_stop,
|
|
};
|
|
|
|
+static int dwc3_gadget_init_hw_all_endpoints(struct dwc3 *dwc)
|
|
+{
|
|
+ struct dwc3_ep *dep;
|
|
+ struct dwc3_hwparams *parms = &dwc->hwparams;
|
|
+ u32 direction = dwc->eps_directions;
|
|
+ u8 num_eps = DWC3_NUM_EPS(parms);
|
|
+ u8 num_in_eps = 0;
|
|
+ u8 num_out_eps = 0;
|
|
+ u8 epnum = 0;
|
|
+ u8 i;
|
|
+
|
|
+ if (!direction)
|
|
+ direction = DWC3_EPS_DEFAULT_DIRECTIONS;
|
|
+
|
|
+ for (i = 0; i < num_eps; i++) {
|
|
+ if (direction & 0x1)
|
|
+ epnum = (num_in_eps++ << 1) + 1;
|
|
+ else
|
|
+ epnum = (num_out_eps++ << 1);
|
|
+
|
|
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
|
+ if (!dep)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ dep->dwc = dwc;
|
|
+ dep->number = epnum;
|
|
+ dep->direction = !!(direction & 0x1);
|
|
+ dep->regs = dwc->regs + DWC3_DEP_BASE(i);
|
|
+ dwc->eps[i] = dep;
|
|
+
|
|
+ snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
|
|
+ (epnum & 1) ? "in" : "out");
|
|
+
|
|
+ dep->endpoint.name = dep->name;
|
|
+ spin_lock_init(&dep->lock);
|
|
+
|
|
+ if (epnum == 0 || epnum == 1) {
|
|
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
|
|
+ dep->endpoint.maxburst = 1;
|
|
+ dep->endpoint.ops = &dwc3_gadget_ep0_ops;
|
|
+ if (!epnum)
|
|
+ dwc->gadget.ep0 = &dep->endpoint;
|
|
+ } else {
|
|
+ int ret;
|
|
+
|
|
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
|
|
+ dep->endpoint.max_streams = 15;
|
|
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
|
|
+ list_add_tail(&dep->endpoint.ep_list,
|
|
+ &dwc->gadget.ep_list);
|
|
+
|
|
+ ret = dwc3_alloc_trb_pool(dep);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (epnum == 0 || epnum == 1) {
|
|
+ dep->endpoint.caps.type_control = true;
|
|
+ } else {
|
|
+ dep->endpoint.caps.type_iso = true;
|
|
+ dep->endpoint.caps.type_bulk = true;
|
|
+ dep->endpoint.caps.type_int = true;
|
|
+ }
|
|
+
|
|
+ dep->endpoint.caps.dir_in = !!(direction & 0x1);
|
|
+ dep->endpoint.caps.dir_out = !(direction & 0x1);
|
|
+ direction = (direction >> 1);
|
|
+
|
|
+ INIT_LIST_HEAD(&dep->pending_list);
|
|
+ INIT_LIST_HEAD(&dep->started_list);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|
@@ -1794,8 +1922,6 @@
|
|
dep->endpoint.name = dep->name;
|
|
spin_lock_init(&dep->lock);
|
|
|
|
- dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
|
|
-
|
|
if (epnum == 0 || epnum == 1) {
|
|
usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
|
|
dep->endpoint.maxburst = 1;
|
|
@@ -1840,17 +1966,24 @@
|
|
|
|
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
|
|
|
+ if (dwc->eps_new_init) {
|
|
+ ret = dwc3_gadget_init_hw_all_endpoints(dwc);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
|
|
+ return ret;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
|
|
if (ret < 0) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "failed to allocate OUT endpoints");
|
|
+ dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
|
|
if (ret < 0) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "failed to allocate IN endpoints");
|
|
+ dev_err(dwc->dev, "failed to initialize IN endpoints\n");
|
|
return ret;
|
|
}
|
|
|
|
@@ -1886,14 +2019,12 @@
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
-static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
- struct dwc3_request *req, struct dwc3_trb *trb,
|
|
- const struct dwc3_event_depevt *event, int status,
|
|
- int chain)
|
|
+static int __dwc3_cleanup_done_trbs(struct dwc3_ep *dep, struct dwc3_request *req,
|
|
+ struct dwc3_trb *trb, const struct dwc3_event_depevt *event,
|
|
+ int status, int chain)
|
|
{
|
|
unsigned int count;
|
|
unsigned int s_pkt = 0;
|
|
- unsigned int trb_status;
|
|
|
|
dwc3_ep_inc_deq(dep);
|
|
|
|
@@ -1922,37 +2053,6 @@
|
|
req->request.actual += count;
|
|
|
|
if (dep->direction) {
|
|
- if (count) {
|
|
- trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
|
- if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "%s: incomplete IN transfer",
|
|
- dep->name);
|
|
- /*
|
|
- * If missed isoc occurred and there is
|
|
- * no request queued then issue END
|
|
- * TRANSFER, so that core generates
|
|
- * next xfernotready and we will issue
|
|
- * a fresh START TRANSFER.
|
|
- * If there are still queued request
|
|
- * then wait, do not issue either END
|
|
- * or UPDATE TRANSFER, just attach next
|
|
- * request in pending_list during
|
|
- * giveback.If any future queued request
|
|
- * is successfully transferred then we
|
|
- * will issue UPDATE TRANSFER for all
|
|
- * request in the pending_list.
|
|
- */
|
|
- dep->flags |= DWC3_EP_MISSED_ISOC;
|
|
- } else {
|
|
- dev_err(dwc->dev, "incomplete IN transfer %s\n",
|
|
- dep->name);
|
|
- status = -ECONNRESET;
|
|
- }
|
|
- } else {
|
|
- dep->flags &= ~DWC3_EP_MISSED_ISOC;
|
|
- }
|
|
- } else {
|
|
if (count && (event->status & DEPEVT_STATUS_SHORT))
|
|
s_pkt = 1;
|
|
}
|
|
@@ -1967,7 +2067,7 @@
|
|
return 0;
|
|
}
|
|
|
|
-static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
+static int dwc3_cleanup_done_reqs(struct dwc3_ep *dep,
|
|
const struct dwc3_event_depevt *event, int status)
|
|
{
|
|
struct dwc3_request *req, *n;
|
|
@@ -1994,14 +2094,14 @@
|
|
req->sg = sg_next(s);
|
|
req->num_pending_sgs--;
|
|
|
|
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
|
+ ret = __dwc3_cleanup_done_trbs(dep, req, trb,
|
|
event, status, chain);
|
|
if (ret)
|
|
break;
|
|
}
|
|
} else {
|
|
trb = &dep->trb_pool[dep->trb_dequeue];
|
|
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
|
+ ret = __dwc3_cleanup_done_trbs(dep, req, trb,
|
|
event, status, chain);
|
|
}
|
|
|
|
@@ -2045,9 +2145,9 @@
|
|
* flag, so that END TRANSFER is issued when an
|
|
* entry is added into request list.
|
|
*/
|
|
- dep->flags = DWC3_EP_PENDING_REQUEST;
|
|
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
|
|
} else {
|
|
- dwc3_stop_active_transfer(dwc, dep->number, true);
|
|
+ dwc3_stop_active_transfer(dep, true);
|
|
dep->flags = DWC3_EP_ENABLED;
|
|
}
|
|
return 1;
|
|
@@ -2059,19 +2159,28 @@
|
|
return 1;
|
|
}
|
|
|
|
-static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
|
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
|
|
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
|
|
+ const struct dwc3_event_depevt *event)
|
|
+{
|
|
+ dep->frame_number = event->parameters;
|
|
+}
|
|
+
|
|
+static void dwc3_endpoint_transfer_complete(struct dwc3_ep *dep,
|
|
+ const struct dwc3_event_depevt *event)
|
|
{
|
|
+ struct dwc3 *dwc = dep->dwc;
|
|
unsigned status = 0;
|
|
int clean_busy;
|
|
u32 is_xfer_complete;
|
|
|
|
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
|
|
+
|
|
is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
|
|
|
|
if (event->status & DEPEVT_STATUS_BUSERR)
|
|
status = -ECONNRESET;
|
|
|
|
- clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
|
|
+ clean_busy = dwc3_cleanup_done_reqs(dep, event, status);
|
|
if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
|
|
usb_endpoint_xfer_isoc(dep->endpoint.desc)))
|
|
dep->flags &= ~DWC3_EP_BUSY;
|
|
@@ -2109,12 +2218,16 @@
|
|
if (!dep->endpoint.desc)
|
|
return;
|
|
|
|
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) || (dep->flags&DWC3_EP_ENABLED)) {
|
|
int ret;
|
|
-
|
|
- ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
- if (!ret || ret == -EBUSY)
|
|
- return;
|
|
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
|
+ (!(dep->flags & (DWC3_EP_BUSY | DWC3_EP_UPDATE)))) {
|
|
+ __dwc3_gadget_start_isoc(dep);
|
|
+ } else {
|
|
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
+ if (!ret || ret == -EBUSY)
|
|
+ return;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -2139,37 +2252,24 @@
|
|
dep->resource_index = 0;
|
|
|
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "%s is an Isochronous endpoint",
|
|
- dep->name);
|
|
+ dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
|
|
return;
|
|
}
|
|
|
|
- dwc3_endpoint_transfer_complete(dwc, dep, event);
|
|
+ dwc3_endpoint_transfer_complete(dep, event);
|
|
break;
|
|
case DWC3_DEPEVT_XFERINPROGRESS:
|
|
- dwc3_endpoint_transfer_complete(dwc, dep, event);
|
|
+ dwc3_endpoint_transfer_complete(dep, event);
|
|
break;
|
|
case DWC3_DEPEVT_XFERNOTREADY:
|
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
|
- dwc3_gadget_start_isoc(dwc, dep, event);
|
|
+ dwc3_gadget_start_isoc(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"
|
|
- : "Transfer Not Active");
|
|
-
|
|
ret = __dwc3_gadget_kick_transfer(dep, 0);
|
|
if (!ret || ret == -EBUSY)
|
|
return;
|
|
-
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "%s: failed to kick transfers",
|
|
- dep->name);
|
|
}
|
|
|
|
break;
|
|
@@ -2179,26 +2279,9 @@
|
|
dep->name);
|
|
return;
|
|
}
|
|
-
|
|
- switch (event->status) {
|
|
- case DEPEVT_STREAMEVT_FOUND:
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "Stream %d found and started",
|
|
- event->parameters);
|
|
-
|
|
- break;
|
|
- case DEPEVT_STREAMEVT_NOTFOUND:
|
|
- /* FALLTHROUGH */
|
|
- default:
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "unable to find suitable stream");
|
|
- }
|
|
break;
|
|
case DWC3_DEPEVT_RXTXFIFOEVT:
|
|
- dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name);
|
|
- break;
|
|
case DWC3_DEPEVT_EPCMDCMPLT:
|
|
- dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
|
|
break;
|
|
}
|
|
}
|
|
@@ -2242,15 +2325,13 @@
|
|
}
|
|
}
|
|
|
|
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
|
|
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
|
|
{
|
|
- struct dwc3_ep *dep;
|
|
+ struct dwc3 *dwc = dep->dwc;
|
|
struct dwc3_gadget_ep_cmd_params params;
|
|
u32 cmd;
|
|
int ret;
|
|
|
|
- dep = dwc->eps[epnum];
|
|
-
|
|
if (!dep->resource_index)
|
|
return;
|
|
|
|
@@ -2596,8 +2677,6 @@
|
|
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
|
|
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
|
|
(next == DWC3_LINK_STATE_RESUME)) {
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "ignoring transition U3 -> Resume");
|
|
return;
|
|
}
|
|
}
|
|
@@ -2731,11 +2810,7 @@
|
|
break;
|
|
case DWC3_DEVICE_EVENT_EOPF:
|
|
/* It changed to be suspend event for version 2.30a and above */
|
|
- if (dwc->revision < DWC3_REVISION_230A) {
|
|
- dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
|
|
- } else {
|
|
- dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
|
|
-
|
|
+ if (dwc->revision >= DWC3_REVISION_230A) {
|
|
/*
|
|
* Ignore suspend event until the gadget enters into
|
|
* USB_STATE_CONFIGURED state.
|
|
@@ -2746,26 +2821,21 @@
|
|
}
|
|
break;
|
|
case DWC3_DEVICE_EVENT_SOF:
|
|
- dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
|
|
- break;
|
|
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
|
- dwc3_trace(trace_dwc3_gadget, "Erratic Error");
|
|
- break;
|
|
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
|
- dwc3_trace(trace_dwc3_gadget, "Command Complete");
|
|
- break;
|
|
case DWC3_DEVICE_EVENT_OVERFLOW:
|
|
- dwc3_trace(trace_dwc3_gadget, "Overflow");
|
|
break;
|
|
+
|
|
default:
|
|
dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
|
|
}
|
|
+ dwc3_gadget_sync_connected_status(dwc);
|
|
}
|
|
|
|
static void dwc3_process_event_entry(struct dwc3 *dwc,
|
|
const union dwc3_event *event)
|
|
{
|
|
- trace_dwc3_event(event->raw);
|
|
+ trace_dwc3_event(event->raw, dwc);
|
|
|
|
/* Endpoint IRQ, handle it and return early */
|
|
if (event->type.is_devspec == 0) {
|
|
@@ -2984,8 +3054,7 @@
|
|
* composite.c that we are USB 2.0 + LPM ECN.
|
|
*/
|
|
if (dwc->revision < DWC3_REVISION_220A)
|
|
- dwc3_trace(trace_dwc3_gadget,
|
|
- "Changing max_speed on rev %08x",
|
|
+ dev_info(dwc->dev, "changing max_speed on rev %08x\n",
|
|
dwc->revision);
|
|
|
|
dwc->gadget.max_speed = dwc->maximum_speed;
|
|
@@ -3005,6 +3074,8 @@
|
|
if (ret)
|
|
goto err5;
|
|
|
|
+ dwc3_proc_init(dwc);
|
|
+
|
|
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
|
if (ret) {
|
|
dev_err(dwc->dev, "failed to register udc\n");
|
|
@@ -3042,6 +3113,8 @@
|
|
{
|
|
usb_del_gadget_udc(&dwc->gadget);
|
|
|
|
+ dwc3_proc_shutdown(dwc);
|
|
+
|
|
dwc3_gadget_free_endpoints(dwc);
|
|
|
|
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
|
@@ -3106,3 +3179,70 @@
|
|
enable_irq(dwc->irq_gadget);
|
|
}
|
|
}
|
|
+
|
|
+/*
|
|
+ * dwc3_gadget_sync_connected_status() function just for
|
|
+ * user space get udc connected status. and this function
|
|
+ * just report three status: Disconnected, Connected host,
|
|
+ * Connected charger.
|
|
+ *
|
|
+ * After some tests, report connected udc connected status by
|
|
+ * DSTS register, [21:18]USB Link Status and [2:0] Connect Speed.
|
|
+ *
|
|
+ * How to identify whitch status is the UDC connected?
|
|
+ * 1. Host connected:
|
|
+ * Host would reset udc, so dwc3 core get reset event interrupt,
|
|
+ * dwc3_gadget_reset_interrupt() set dwc->connected = true,
|
|
+ * so check connected host by dwc->connected == true
|
|
+ *
|
|
+ * 2. Disconnected:
|
|
+ * When vbus detect 5V lose, dwc3 core generated a disconnect event intr.
|
|
+ * dwc3_gadget_disconnect_interrupt() set dwc->connected = false.
|
|
+ * so check disconnected by dwc->connected == false
|
|
+ *
|
|
+ * 3. Charger connected: (Fixedme)
|
|
+ * As dwc3 core size, DP keep pullup register when connected to charger.
|
|
+ * no any port reset action created, so dwc3 would entry FullSpeed mode.
|
|
+ * so chect connected charger by FullSpeed && dwc->connectd == false
|
|
+ *
|
|
+ */
|
|
+static void dwc3_gadget_sync_connected_status(struct dwc3 *dwc)
|
|
+{
|
|
+ u32 reg;
|
|
+ u8 speed;
|
|
+ u8 state;
|
|
+ static int prev = UDC_DISCONNECTED;
|
|
+
|
|
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
|
+ speed = reg & DWC3_DSTS_CONNECTSPD;
|
|
+ state = DWC3_DSTS_USBLNKST(reg);
|
|
+
|
|
+ /*
|
|
+ * step1 check is connected host?
|
|
+ */
|
|
+ if (dwc->connected == true) {
|
|
+ if (prev != UDC_CONNECT_HOST)
|
|
+ dwc->udc_connect_status = UDC_CONNECT_HOST;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * step2 disconectd status && fullspeed mode,
|
|
+ * as connected charger.
|
|
+ */
|
|
+ if ((speed == DWC3_DSTS_FULLSPEED) && (state != DWC3_LINK_STATE_SS_DIS)) {
|
|
+ if (prev != UDC_CONNECT_CHARGER)
|
|
+ dwc->udc_connect_status = UDC_CONNECT_CHARGER;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * step3 not host and charger connected, so just
|
|
+ * disconnectd.
|
|
+ */
|
|
+ if (prev != UDC_DISCONNECTED)
|
|
+ dwc->udc_connect_status = UDC_DISCONNECTED;
|
|
+
|
|
+out:
|
|
+ prev = dwc->udc_connect_status;
|
|
+}
|