diff options
Diffstat (limited to 'drivers/usb/dwc3/ep0.c')
| -rw-r--r-- | drivers/usb/dwc3/ep0.c | 205 |
1 files changed, 155 insertions, 50 deletions
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 8efde178eef4..e0bad5708664 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -2,7 +2,7 @@ /* * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -30,6 +30,8 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req); +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl); static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, dma_addr_t buf_dma, u32 len, u32 type, bool chain) @@ -92,6 +94,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, req->request.actual = 0; req->request.status = -EINPROGRESS; req->epnum = dep->number; + req->status = DWC3_REQUEST_STATUS_QUEUED; list_add_tail(&req->list, &dep->pending_list); @@ -105,7 +108,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * IRQ we were waiting for is long gone. */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - unsigned direction; + unsigned int direction; direction = !!(dep->flags & DWC3_EP0_DIR_IN); @@ -127,11 +130,11 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * handle it here. */ if (dwc->delayed_status) { - unsigned direction; + unsigned int direction; direction = !dwc->ep0_expect_in; dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); @@ -143,7 +146,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * Unfortunately we have uncovered a limitation wrt the Data Phase. * * Section 9.4 says we can wait for the XferNotReady(DATA) event to - * come before issueing Start Transfer command, but if we do, we will + * come before issuing Start Transfer command, but if we do, we will * miss situations where the host starts another SETUP phase instead of * the DATA phase. Such cases happen at least on TD.7.6 of the Link * Layer Compliance Suite. @@ -172,7 +175,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * XferNotReady(STATUS). */ if (dwc->three_stage_setup) { - unsigned direction; + unsigned int direction; direction = dwc->ep0_expect_in; dwc->ep0state = EP0_DATA_PHASE; @@ -197,7 +200,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int ret; spin_lock_irqsave(&dwc->lock, flags); - if (!dep->endpoint.desc) { + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); ret = -ESHUTDOWN; @@ -218,27 +221,34 @@ out: return ret; } -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { struct dwc3_ep *dep; /* reinitialize physical ep1 */ dep = dwc->eps[1]; - dep->flags = DWC3_EP_ENABLED; + dep->flags &= DWC3_EP_RESOURCE_ALLOCATED; + dep->flags |= DWC3_EP_ENABLED; /* stall is always issued on EP0 */ dep = dwc->eps[0]; __dwc3_gadget_ep_set_halt(dep, 1, false); - dep->flags = DWC3_EP_ENABLED; + dep->flags &= DWC3_EP_RESOURCE_ALLOCATED | DWC3_EP_TRANSFER_STARTED; + dep->flags |= DWC3_EP_ENABLED; dwc->delayed_status = false; if (!list_empty(&dep->pending_list)) { struct dwc3_request *req; req = next_request(&dep->pending_list); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + if (!dwc->connected) + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + else + dwc3_gadget_giveback(dep, req, -ECONNRESET); } + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } @@ -271,6 +281,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) { struct dwc3_ep *dep; int ret; + int i; complete(&dwc->ep0_in_setup); @@ -278,7 +289,25 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8, DWC3_TRBCTL_CONTROL_SETUP, false); ret = dwc3_ep0_start_trans(dep); - WARN_ON(ret < 0); + if (ret < 0) + dev_err(dwc->dev, "ep0 out start transfer failed: %d\n", ret); + + for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) { + struct dwc3_ep *dwc3_ep; + + dwc3_ep = dwc->eps[i]; + if (!dwc3_ep) + continue; + + if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP)) + continue; + + dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP; + if (dwc->connected) + dwc3_stop_active_transfer(dwc3_ep, true, true); + else + dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN); + } } static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) @@ -292,6 +321,9 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) epnum |= 1; dep = dwc->eps[epnum]; + if (dep == NULL) + return NULL; + if (dep->flags & DWC3_EP_ENABLED) return dep; @@ -325,7 +357,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, /* * LTM will be set once we know how to set this in HW. */ - usb_status |= dwc->gadget.is_selfpowered; + usb_status |= dwc->gadget->is_selfpowered; if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { @@ -334,6 +366,9 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; if (reg & DWC3_DCTL_INITU2ENA) usb_status |= 1 << USB_DEV_STAT_U2_ENABLED; + } else { + usb_status |= dwc->gadget->wakeup_armed << + USB_DEVICE_REMOTE_WAKEUP; } break; @@ -343,7 +378,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, * Function Remote Wake Capable D0 * Function Remote Wakeup D1 */ - break; + return dwc3_ep0_delegate_req(dwc, ctrl); case USB_RECIP_ENDPOINT: dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); @@ -379,6 +414,8 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u1_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -401,6 +438,8 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u2_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -421,11 +460,11 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, return -EINVAL; switch (wIndex >> 8) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: + case USB_TEST_J: + case USB_TEST_K: + case USB_TEST_SE0_NAK: + case USB_TEST_PACKET: + case USB_TEST_FORCE_ENABLE: dwc->test_mode_nr = wIndex >> 8; dwc->test_mode = true; break; @@ -446,13 +485,17 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, wValue = le16_to_cpu(ctrl->wValue); wIndex = le16_to_cpu(ctrl->wIndex); - state = dwc->gadget.state; + state = dwc->gadget->state; switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: + if (dwc->wakeup_configured) + dwc->gadget->wakeup_armed = set; + else + ret = -EINVAL; break; /* - * 9.4.1 says only only for SS, in AddressState only for + * 9.4.1 says only for SS, in AddressState only for * default control pipe */ case USB_DEVICE_U1_ENABLE: @@ -484,13 +527,7 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc, switch (wValue) { case USB_INTRF_FUNC_SUSPEND: - /* - * REVISIT: Ideally we would enable some low power mode here, - * however it's unclear what we should be doing here. - * - * For now, we're not doing anything, just making sure we return - * 0 so USB Command Verifier tests pass without any errors. - */ + ret = dwc3_ep0_delegate_req(dwc, ctrl); break; default: ret = -EINVAL; @@ -520,6 +557,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, ret = __dwc3_gadget_ep_set_halt(dep, set, true); if (ret) return -EINVAL; + + /* ClearFeature(Halt) may need delayed status */ + if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) + return USB_GADGET_DELAYED_STATUS; + break; default: return -EINVAL; @@ -555,7 +597,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 addr; u32 reg; @@ -576,26 +618,28 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCFG, reg); if (addr) - usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); else - usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT); return 0; } static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - int ret; + int ret = -EINVAL; - spin_unlock(&dwc->lock); - ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); - spin_lock(&dwc->lock); + if (dwc->async_callbacks) { + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(dwc->gadget, ctrl); + spin_lock(&dwc->lock); + } return ret; } static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 cfg; int ret; u32 reg; @@ -607,6 +651,9 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return -EINVAL; case USB_STATE_ADDRESS: + dwc3_gadget_start_config(dwc, 2); + dwc3_gadget_clear_tx_fifos(dwc); + ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { @@ -618,7 +665,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * to change the state on the next usb_ep_queue() */ if (ret == 0) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); /* @@ -626,7 +673,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * nothing is pending from application. */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); - reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + if (!dwc->dis_u1_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU1ENA; + if (!dwc->dis_u2_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU2ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); } break; @@ -634,7 +684,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); if (!cfg && !ret) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); break; default: @@ -690,7 +740,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { struct dwc3_ep *dep; - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u16 wLength; if (state == USB_STATE_DEFAULT) @@ -734,7 +784,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct if (wIndex || wLength) return -EINVAL; - dwc->gadget.isoch_delay = wValue; + dwc->gadget->isoch_delay = wValue; return 0; } @@ -780,7 +830,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, int ret = -EINVAL; u32 len; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected) goto out; trace_dwc3_ctrl_req(ctrl); @@ -935,12 +985,16 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req) { + unsigned int trb_length = 0; int ret; req->direction = !!dep->number; if (req->request.length == 0) { - dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + + dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length, DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) @@ -987,9 +1041,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1]; + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + /* Now prepare one extra TRB to align transfer size */ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, - 0, DWC3_TRBCTL_CONTROL_DATA, + trb_length, DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); } else { @@ -1007,7 +1064,9 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dep); } - WARN_ON(ret < 0); + if (ret < 0) + dev_err(dwc->dev, + "ep0 data phase start transfer failed: %d\n", ret); } static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) @@ -1024,7 +1083,12 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { - WARN_ON(dwc3_ep0_start_control_status(dep)); + int ret; + + ret = dwc3_ep0_start_control_status(dep); + if (ret) + dev_err(dwc->dev, + "ep0 status phase start transfer failed: %d\n", ret); } static void dwc3_ep0_do_control_status(struct dwc3 *dwc, @@ -1035,13 +1099,31 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, __dwc3_ep0_do_control_status(dwc, dep); } -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) +{ + unsigned int direction = !dwc->ep0_expect_in; + + dwc->delayed_status = false; + dwc->clear_stall_protocol = 0; + + if (dwc->ep0state != EP0_STATUS_PHASE) + return; + + __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); +} + +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; u32 cmd; int ret; - if (!dep->resource_index) + /* + * For status/DATA OUT stage, TRB will be queued on ep0 out + * endpoint for which resource index is zero. Hence allow + * queuing ENDXFER command for ep0 out endpoint. + */ + if (!dep->resource_index && dep->number) return; cmd = DWC3_DEPCMD_ENDTRANSFER; @@ -1049,7 +1131,10 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - WARN_ON_ONCE(ret); + if (ret) + dev_err_ratelimited(dwc->dev, + "ep0 data phase end transfer failed: %d\n", ret); + dep->resource_index = 0; } @@ -1058,6 +1143,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, { switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: + if (!dwc->softconnect || !dwc->connected) + return; /* * We already have a DATA transfer in the controller's cache, * if we receive a XferNotReady(DATA) we will ignore it, unless @@ -1082,6 +1169,11 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; + if (dwc->setup_packet_pending) { + dwc3_ep0_stall_and_restart(dwc); + return; + } + dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { @@ -1095,7 +1187,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, */ if (!list_empty(&dep->pending_list)) { dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); dwc3_ep0_do_control_status(dwc, event); } @@ -1110,6 +1202,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + u8 cmd; + switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: dwc3_ep0_xfer_complete(dwc, event); @@ -1122,7 +1217,17 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_RXTXFIFOEVT: case DWC3_DEPEVT_STREAMEVT: + break; case DWC3_DEPEVT_EPCMDCMPLT: + cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd == DWC3_DEPCMD_ENDTRANSFER) { + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + } + break; + default: + dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event); break; } } |
