--- 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; +}