185d5e707SKishon Vijay Abraham I /**
285d5e707SKishon Vijay Abraham I * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
385d5e707SKishon Vijay Abraham I *
430c31d58SKishon Vijay Abraham I * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
585d5e707SKishon Vijay Abraham I *
685d5e707SKishon Vijay Abraham I * Authors: Felipe Balbi <balbi@ti.com>,
785d5e707SKishon Vijay Abraham I * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
885d5e707SKishon Vijay Abraham I *
930c31d58SKishon Vijay Abraham I * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported
1030c31d58SKishon Vijay Abraham I * to uboot.
1185d5e707SKishon Vijay Abraham I *
1230c31d58SKishon Vijay Abraham I * commit c00552ebaf : Merge 3.18-rc7 into usb-next
1330c31d58SKishon Vijay Abraham I *
1430c31d58SKishon Vijay Abraham I * SPDX-License-Identifier: GPL-2.0
1585d5e707SKishon Vijay Abraham I */
16dc627159SJean-Jacques Hiblot #include <common.h>
1785d5e707SKishon Vijay Abraham I #include <linux/kernel.h>
1885d5e707SKishon Vijay Abraham I #include <linux/list.h>
1985d5e707SKishon Vijay Abraham I
2085d5e707SKishon Vijay Abraham I #include <linux/usb/ch9.h>
2185d5e707SKishon Vijay Abraham I #include <linux/usb/gadget.h>
2285d5e707SKishon Vijay Abraham I #include <linux/usb/composite.h>
2385d5e707SKishon Vijay Abraham I
2485d5e707SKishon Vijay Abraham I #include "core.h"
2585d5e707SKishon Vijay Abraham I #include "gadget.h"
2685d5e707SKishon Vijay Abraham I #include "io.h"
2785d5e707SKishon Vijay Abraham I
28b6d959acSKishon Vijay Abraham I #include "linux-compat.h"
29b6d959acSKishon Vijay Abraham I
3085d5e707SKishon Vijay Abraham I static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
3185d5e707SKishon Vijay Abraham I static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
3285d5e707SKishon Vijay Abraham I struct dwc3_ep *dep, struct dwc3_request *req);
3385d5e707SKishon Vijay Abraham I
dwc3_ep0_state_string(enum dwc3_ep0_state state)3485d5e707SKishon Vijay Abraham I static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
3585d5e707SKishon Vijay Abraham I {
3685d5e707SKishon Vijay Abraham I switch (state) {
3785d5e707SKishon Vijay Abraham I case EP0_UNCONNECTED:
3885d5e707SKishon Vijay Abraham I return "Unconnected";
3985d5e707SKishon Vijay Abraham I case EP0_SETUP_PHASE:
4085d5e707SKishon Vijay Abraham I return "Setup Phase";
4185d5e707SKishon Vijay Abraham I case EP0_DATA_PHASE:
4285d5e707SKishon Vijay Abraham I return "Data Phase";
4385d5e707SKishon Vijay Abraham I case EP0_STATUS_PHASE:
4485d5e707SKishon Vijay Abraham I return "Status Phase";
4585d5e707SKishon Vijay Abraham I default:
4685d5e707SKishon Vijay Abraham I return "UNKNOWN";
4785d5e707SKishon Vijay Abraham I }
4885d5e707SKishon Vijay Abraham I }
4985d5e707SKishon Vijay Abraham I
dwc3_ep0_start_trans(struct dwc3 * dwc,u8 epnum,dma_addr_t buf_dma,u32 len,u32 type,unsigned chain)5085d5e707SKishon Vijay Abraham I static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
518d488f3eSKishon Vijay Abraham I u32 len, u32 type, unsigned chain)
5285d5e707SKishon Vijay Abraham I {
5385d5e707SKishon Vijay Abraham I struct dwc3_gadget_ep_cmd_params params;
5485d5e707SKishon Vijay Abraham I struct dwc3_trb *trb;
5585d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
5685d5e707SKishon Vijay Abraham I
5785d5e707SKishon Vijay Abraham I int ret;
5885d5e707SKishon Vijay Abraham I
5985d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum];
6085d5e707SKishon Vijay Abraham I if (dep->flags & DWC3_EP_BUSY) {
619de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "%s still busy", dep->name);
6285d5e707SKishon Vijay Abraham I return 0;
6385d5e707SKishon Vijay Abraham I }
6485d5e707SKishon Vijay Abraham I
658d488f3eSKishon Vijay Abraham I trb = &dwc->ep0_trb[dep->free_slot];
668d488f3eSKishon Vijay Abraham I
678d488f3eSKishon Vijay Abraham I if (chain)
688d488f3eSKishon Vijay Abraham I dep->free_slot++;
6985d5e707SKishon Vijay Abraham I
7085d5e707SKishon Vijay Abraham I trb->bpl = lower_32_bits(buf_dma);
7185d5e707SKishon Vijay Abraham I trb->bph = upper_32_bits(buf_dma);
7285d5e707SKishon Vijay Abraham I trb->size = len;
7385d5e707SKishon Vijay Abraham I trb->ctrl = type;
7485d5e707SKishon Vijay Abraham I
7585d5e707SKishon Vijay Abraham I trb->ctrl |= (DWC3_TRB_CTRL_HWO
7685d5e707SKishon Vijay Abraham I | DWC3_TRB_CTRL_ISP_IMI);
7785d5e707SKishon Vijay Abraham I
788d488f3eSKishon Vijay Abraham I if (chain)
798d488f3eSKishon Vijay Abraham I trb->ctrl |= DWC3_TRB_CTRL_CHN;
808d488f3eSKishon Vijay Abraham I else
818d488f3eSKishon Vijay Abraham I trb->ctrl |= (DWC3_TRB_CTRL_IOC
828d488f3eSKishon Vijay Abraham I | DWC3_TRB_CTRL_LST);
838d488f3eSKishon Vijay Abraham I
84b7bf4a95SPhilipp Tomsich dwc3_flush_cache((uintptr_t)buf_dma, len);
85b7bf4a95SPhilipp Tomsich dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
86526a50f8SKishon Vijay Abraham I
878d488f3eSKishon Vijay Abraham I if (chain)
888d488f3eSKishon Vijay Abraham I return 0;
898d488f3eSKishon Vijay Abraham I
9085d5e707SKishon Vijay Abraham I memset(¶ms, 0, sizeof(params));
9185d5e707SKishon Vijay Abraham I params.param0 = upper_32_bits(dwc->ep0_trb_addr);
9285d5e707SKishon Vijay Abraham I params.param1 = lower_32_bits(dwc->ep0_trb_addr);
9385d5e707SKishon Vijay Abraham I
9485d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
9585d5e707SKishon Vijay Abraham I DWC3_DEPCMD_STARTTRANSFER, ¶ms);
9685d5e707SKishon Vijay Abraham I if (ret < 0) {
979de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "%s STARTTRANSFER failed", dep->name);
9885d5e707SKishon Vijay Abraham I return ret;
9985d5e707SKishon Vijay Abraham I }
10085d5e707SKishon Vijay Abraham I
10185d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_BUSY;
10285d5e707SKishon Vijay Abraham I dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
10385d5e707SKishon Vijay Abraham I dep->number);
10485d5e707SKishon Vijay Abraham I
10585d5e707SKishon Vijay Abraham I dwc->ep0_next_event = DWC3_EP0_COMPLETE;
10685d5e707SKishon Vijay Abraham I
10785d5e707SKishon Vijay Abraham I return 0;
10885d5e707SKishon Vijay Abraham I }
10985d5e707SKishon Vijay Abraham I
__dwc3_gadget_ep0_queue(struct dwc3_ep * dep,struct dwc3_request * req)11085d5e707SKishon Vijay Abraham I static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
11185d5e707SKishon Vijay Abraham I struct dwc3_request *req)
11285d5e707SKishon Vijay Abraham I {
11385d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc;
11485d5e707SKishon Vijay Abraham I
11585d5e707SKishon Vijay Abraham I req->request.actual = 0;
11685d5e707SKishon Vijay Abraham I req->request.status = -EINPROGRESS;
11785d5e707SKishon Vijay Abraham I req->epnum = dep->number;
11885d5e707SKishon Vijay Abraham I
11985d5e707SKishon Vijay Abraham I list_add_tail(&req->list, &dep->request_list);
12085d5e707SKishon Vijay Abraham I
12185d5e707SKishon Vijay Abraham I /*
12285d5e707SKishon Vijay Abraham I * Gadget driver might not be quick enough to queue a request
12385d5e707SKishon Vijay Abraham I * before we get a Transfer Not Ready event on this endpoint.
12485d5e707SKishon Vijay Abraham I *
12585d5e707SKishon Vijay Abraham I * In that case, we will set DWC3_EP_PENDING_REQUEST. When that
12685d5e707SKishon Vijay Abraham I * flag is set, it's telling us that as soon as Gadget queues the
12785d5e707SKishon Vijay Abraham I * required request, we should kick the transfer here because the
12885d5e707SKishon Vijay Abraham I * IRQ we were waiting for is long gone.
12985d5e707SKishon Vijay Abraham I */
13085d5e707SKishon Vijay Abraham I if (dep->flags & DWC3_EP_PENDING_REQUEST) {
13185d5e707SKishon Vijay Abraham I unsigned direction;
13285d5e707SKishon Vijay Abraham I
13385d5e707SKishon Vijay Abraham I direction = !!(dep->flags & DWC3_EP0_DIR_IN);
13485d5e707SKishon Vijay Abraham I
13585d5e707SKishon Vijay Abraham I if (dwc->ep0state != EP0_DATA_PHASE) {
13685d5e707SKishon Vijay Abraham I dev_WARN(dwc->dev, "Unexpected pending request\n");
13785d5e707SKishon Vijay Abraham I return 0;
13885d5e707SKishon Vijay Abraham I }
13985d5e707SKishon Vijay Abraham I
14085d5e707SKishon Vijay Abraham I __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
14185d5e707SKishon Vijay Abraham I
14285d5e707SKishon Vijay Abraham I dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
14385d5e707SKishon Vijay Abraham I DWC3_EP0_DIR_IN);
14485d5e707SKishon Vijay Abraham I
14585d5e707SKishon Vijay Abraham I return 0;
14685d5e707SKishon Vijay Abraham I }
14785d5e707SKishon Vijay Abraham I
14885d5e707SKishon Vijay Abraham I /*
14985d5e707SKishon Vijay Abraham I * In case gadget driver asked us to delay the STATUS phase,
15085d5e707SKishon Vijay Abraham I * handle it here.
15185d5e707SKishon Vijay Abraham I */
15285d5e707SKishon Vijay Abraham I if (dwc->delayed_status) {
15385d5e707SKishon Vijay Abraham I unsigned direction;
15485d5e707SKishon Vijay Abraham I
15585d5e707SKishon Vijay Abraham I direction = !dwc->ep0_expect_in;
15685d5e707SKishon Vijay Abraham I dwc->delayed_status = false;
15785d5e707SKishon Vijay Abraham I usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
15885d5e707SKishon Vijay Abraham I
15985d5e707SKishon Vijay Abraham I if (dwc->ep0state == EP0_STATUS_PHASE)
16085d5e707SKishon Vijay Abraham I __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
16185d5e707SKishon Vijay Abraham I else
1629de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "too early for delayed status");
16385d5e707SKishon Vijay Abraham I
16485d5e707SKishon Vijay Abraham I return 0;
16585d5e707SKishon Vijay Abraham I }
16685d5e707SKishon Vijay Abraham I
16785d5e707SKishon Vijay Abraham I /*
16885d5e707SKishon Vijay Abraham I * Unfortunately we have uncovered a limitation wrt the Data Phase.
16985d5e707SKishon Vijay Abraham I *
17085d5e707SKishon Vijay Abraham I * Section 9.4 says we can wait for the XferNotReady(DATA) event to
17185d5e707SKishon Vijay Abraham I * come before issueing Start Transfer command, but if we do, we will
17285d5e707SKishon Vijay Abraham I * miss situations where the host starts another SETUP phase instead of
17385d5e707SKishon Vijay Abraham I * the DATA phase. Such cases happen at least on TD.7.6 of the Link
17485d5e707SKishon Vijay Abraham I * Layer Compliance Suite.
17585d5e707SKishon Vijay Abraham I *
17685d5e707SKishon Vijay Abraham I * The problem surfaces due to the fact that in case of back-to-back
17785d5e707SKishon Vijay Abraham I * SETUP packets there will be no XferNotReady(DATA) generated and we
17885d5e707SKishon Vijay Abraham I * will be stuck waiting for XferNotReady(DATA) forever.
17985d5e707SKishon Vijay Abraham I *
18085d5e707SKishon Vijay Abraham I * By looking at tables 9-13 and 9-14 of the Databook, we can see that
18185d5e707SKishon Vijay Abraham I * it tells us to start Data Phase right away. It also mentions that if
18285d5e707SKishon Vijay Abraham I * we receive a SETUP phase instead of the DATA phase, core will issue
18385d5e707SKishon Vijay Abraham I * XferComplete for the DATA phase, before actually initiating it in
18485d5e707SKishon Vijay Abraham I * the wire, with the TRB's status set to "SETUP_PENDING". Such status
18585d5e707SKishon Vijay Abraham I * can only be used to print some debugging logs, as the core expects
18685d5e707SKishon Vijay Abraham I * us to go through to the STATUS phase and start a CONTROL_STATUS TRB,
18785d5e707SKishon Vijay Abraham I * just so it completes right away, without transferring anything and,
18885d5e707SKishon Vijay Abraham I * only then, we can go back to the SETUP phase.
18985d5e707SKishon Vijay Abraham I *
19085d5e707SKishon Vijay Abraham I * Because of this scenario, SNPS decided to change the programming
19185d5e707SKishon Vijay Abraham I * model of control transfers and support on-demand transfers only for
19285d5e707SKishon Vijay Abraham I * the STATUS phase. To fix the issue we have now, we will always wait
19385d5e707SKishon Vijay Abraham I * for gadget driver to queue the DATA phase's struct usb_request, then
19485d5e707SKishon Vijay Abraham I * start it right away.
19585d5e707SKishon Vijay Abraham I *
19685d5e707SKishon Vijay Abraham I * If we're actually in a 2-stage transfer, we will wait for
19785d5e707SKishon Vijay Abraham I * XferNotReady(STATUS).
19885d5e707SKishon Vijay Abraham I */
19985d5e707SKishon Vijay Abraham I if (dwc->three_stage_setup) {
20085d5e707SKishon Vijay Abraham I unsigned direction;
20185d5e707SKishon Vijay Abraham I
20285d5e707SKishon Vijay Abraham I direction = dwc->ep0_expect_in;
20385d5e707SKishon Vijay Abraham I dwc->ep0state = EP0_DATA_PHASE;
20485d5e707SKishon Vijay Abraham I
20585d5e707SKishon Vijay Abraham I __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
20685d5e707SKishon Vijay Abraham I
20785d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP0_DIR_IN;
20885d5e707SKishon Vijay Abraham I }
20985d5e707SKishon Vijay Abraham I
21085d5e707SKishon Vijay Abraham I return 0;
21185d5e707SKishon Vijay Abraham I }
21285d5e707SKishon Vijay Abraham I
dwc3_gadget_ep0_queue(struct usb_ep * ep,struct usb_request * request,gfp_t gfp_flags)21385d5e707SKishon Vijay Abraham I int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
21485d5e707SKishon Vijay Abraham I gfp_t gfp_flags)
21585d5e707SKishon Vijay Abraham I {
21685d5e707SKishon Vijay Abraham I struct dwc3_request *req = to_dwc3_request(request);
21785d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep);
21885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc;
21985d5e707SKishon Vijay Abraham I
2206e4c159cSWilliam Wu unsigned long flags = 0;
22185d5e707SKishon Vijay Abraham I
22285d5e707SKishon Vijay Abraham I int ret;
22385d5e707SKishon Vijay Abraham I
22485d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags);
22585d5e707SKishon Vijay Abraham I if (!dep->endpoint.desc) {
2269de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "trying to queue request %p to disabled %s",
22785d5e707SKishon Vijay Abraham I request, dep->name);
22885d5e707SKishon Vijay Abraham I ret = -ESHUTDOWN;
22985d5e707SKishon Vijay Abraham I goto out;
23085d5e707SKishon Vijay Abraham I }
23185d5e707SKishon Vijay Abraham I
23285d5e707SKishon Vijay Abraham I /* we share one TRB for ep0/1 */
23385d5e707SKishon Vijay Abraham I if (!list_empty(&dep->request_list)) {
23485d5e707SKishon Vijay Abraham I ret = -EBUSY;
23585d5e707SKishon Vijay Abraham I goto out;
23685d5e707SKishon Vijay Abraham I }
23785d5e707SKishon Vijay Abraham I
2389de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "queueing request %p to %s length %d state '%s'",
23985d5e707SKishon Vijay Abraham I request, dep->name, request->length,
24085d5e707SKishon Vijay Abraham I dwc3_ep0_state_string(dwc->ep0state));
24185d5e707SKishon Vijay Abraham I
24285d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep0_queue(dep, req);
24385d5e707SKishon Vijay Abraham I
24485d5e707SKishon Vijay Abraham I out:
24585d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags);
24685d5e707SKishon Vijay Abraham I
24785d5e707SKishon Vijay Abraham I return ret;
24885d5e707SKishon Vijay Abraham I }
24985d5e707SKishon Vijay Abraham I
dwc3_ep0_stall_and_restart(struct dwc3 * dwc)25085d5e707SKishon Vijay Abraham I static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
25185d5e707SKishon Vijay Abraham I {
25285d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
25385d5e707SKishon Vijay Abraham I
25485d5e707SKishon Vijay Abraham I /* reinitialize physical ep1 */
25585d5e707SKishon Vijay Abraham I dep = dwc->eps[1];
25685d5e707SKishon Vijay Abraham I dep->flags = DWC3_EP_ENABLED;
25785d5e707SKishon Vijay Abraham I
25885d5e707SKishon Vijay Abraham I /* stall is always issued on EP0 */
25985d5e707SKishon Vijay Abraham I dep = dwc->eps[0];
26085d5e707SKishon Vijay Abraham I __dwc3_gadget_ep_set_halt(dep, 1, false);
26185d5e707SKishon Vijay Abraham I dep->flags = DWC3_EP_ENABLED;
26285d5e707SKishon Vijay Abraham I dwc->delayed_status = false;
26385d5e707SKishon Vijay Abraham I
26485d5e707SKishon Vijay Abraham I if (!list_empty(&dep->request_list)) {
26585d5e707SKishon Vijay Abraham I struct dwc3_request *req;
26685d5e707SKishon Vijay Abraham I
26785d5e707SKishon Vijay Abraham I req = next_request(&dep->request_list);
26885d5e707SKishon Vijay Abraham I dwc3_gadget_giveback(dep, req, -ECONNRESET);
26985d5e707SKishon Vijay Abraham I }
27085d5e707SKishon Vijay Abraham I
27185d5e707SKishon Vijay Abraham I dwc->ep0state = EP0_SETUP_PHASE;
27285d5e707SKishon Vijay Abraham I dwc3_ep0_out_start(dwc);
27385d5e707SKishon Vijay Abraham I }
27485d5e707SKishon Vijay Abraham I
__dwc3_gadget_ep0_set_halt(struct usb_ep * ep,int value)27585d5e707SKishon Vijay Abraham I int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
27685d5e707SKishon Vijay Abraham I {
27785d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep);
27885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc;
27985d5e707SKishon Vijay Abraham I
28085d5e707SKishon Vijay Abraham I dwc3_ep0_stall_and_restart(dwc);
28185d5e707SKishon Vijay Abraham I
28285d5e707SKishon Vijay Abraham I return 0;
28385d5e707SKishon Vijay Abraham I }
28485d5e707SKishon Vijay Abraham I
dwc3_gadget_ep0_set_halt(struct usb_ep * ep,int value)28585d5e707SKishon Vijay Abraham I int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
28685d5e707SKishon Vijay Abraham I {
2876e4c159cSWilliam Wu unsigned long flags = 0;
28885d5e707SKishon Vijay Abraham I int ret;
28985d5e707SKishon Vijay Abraham I
29085d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags);
29185d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep0_set_halt(ep, value);
29285d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags);
29385d5e707SKishon Vijay Abraham I
29485d5e707SKishon Vijay Abraham I return ret;
29585d5e707SKishon Vijay Abraham I }
29685d5e707SKishon Vijay Abraham I
dwc3_ep0_out_start(struct dwc3 * dwc)29785d5e707SKishon Vijay Abraham I void dwc3_ep0_out_start(struct dwc3 *dwc)
29885d5e707SKishon Vijay Abraham I {
29985d5e707SKishon Vijay Abraham I int ret;
30085d5e707SKishon Vijay Abraham I
30185d5e707SKishon Vijay Abraham I ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
3028d488f3eSKishon Vijay Abraham I DWC3_TRBCTL_CONTROL_SETUP, 0);
30385d5e707SKishon Vijay Abraham I WARN_ON(ret < 0);
30485d5e707SKishon Vijay Abraham I }
30585d5e707SKishon Vijay Abraham I
dwc3_wIndex_to_dep(struct dwc3 * dwc,__le16 wIndex_le)30685d5e707SKishon Vijay Abraham I static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
30785d5e707SKishon Vijay Abraham I {
30885d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
30985d5e707SKishon Vijay Abraham I u32 windex = le16_to_cpu(wIndex_le);
31085d5e707SKishon Vijay Abraham I u32 epnum;
31185d5e707SKishon Vijay Abraham I
31285d5e707SKishon Vijay Abraham I epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1;
31385d5e707SKishon Vijay Abraham I if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
31485d5e707SKishon Vijay Abraham I epnum |= 1;
31585d5e707SKishon Vijay Abraham I
31685d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum];
31785d5e707SKishon Vijay Abraham I if (dep->flags & DWC3_EP_ENABLED)
31885d5e707SKishon Vijay Abraham I return dep;
31985d5e707SKishon Vijay Abraham I
32085d5e707SKishon Vijay Abraham I return NULL;
32185d5e707SKishon Vijay Abraham I }
32285d5e707SKishon Vijay Abraham I
dwc3_ep0_status_cmpl(struct usb_ep * ep,struct usb_request * req)32385d5e707SKishon Vijay Abraham I static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req)
32485d5e707SKishon Vijay Abraham I {
32585d5e707SKishon Vijay Abraham I }
32685d5e707SKishon Vijay Abraham I /*
32785d5e707SKishon Vijay Abraham I * ch 9.4.5
32885d5e707SKishon Vijay Abraham I */
dwc3_ep0_handle_status(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)32985d5e707SKishon Vijay Abraham I static int dwc3_ep0_handle_status(struct dwc3 *dwc,
33085d5e707SKishon Vijay Abraham I struct usb_ctrlrequest *ctrl)
33185d5e707SKishon Vijay Abraham I {
33285d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
33385d5e707SKishon Vijay Abraham I u32 recip;
33485d5e707SKishon Vijay Abraham I u32 reg;
33585d5e707SKishon Vijay Abraham I u16 usb_status = 0;
33685d5e707SKishon Vijay Abraham I __le16 *response_pkt;
33785d5e707SKishon Vijay Abraham I
33885d5e707SKishon Vijay Abraham I recip = ctrl->bRequestType & USB_RECIP_MASK;
33985d5e707SKishon Vijay Abraham I switch (recip) {
34085d5e707SKishon Vijay Abraham I case USB_RECIP_DEVICE:
34185d5e707SKishon Vijay Abraham I /*
34285d5e707SKishon Vijay Abraham I * LTM will be set once we know how to set this in HW.
34385d5e707SKishon Vijay Abraham I */
34485d5e707SKishon Vijay Abraham I usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
34585d5e707SKishon Vijay Abraham I
34685d5e707SKishon Vijay Abraham I if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
34785d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL);
34885d5e707SKishon Vijay Abraham I if (reg & DWC3_DCTL_INITU1ENA)
34985d5e707SKishon Vijay Abraham I usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
35085d5e707SKishon Vijay Abraham I if (reg & DWC3_DCTL_INITU2ENA)
35185d5e707SKishon Vijay Abraham I usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
35285d5e707SKishon Vijay Abraham I }
35385d5e707SKishon Vijay Abraham I
35485d5e707SKishon Vijay Abraham I break;
35585d5e707SKishon Vijay Abraham I
35685d5e707SKishon Vijay Abraham I case USB_RECIP_INTERFACE:
35785d5e707SKishon Vijay Abraham I /*
35885d5e707SKishon Vijay Abraham I * Function Remote Wake Capable D0
35985d5e707SKishon Vijay Abraham I * Function Remote Wakeup D1
36085d5e707SKishon Vijay Abraham I */
36185d5e707SKishon Vijay Abraham I break;
36285d5e707SKishon Vijay Abraham I
36385d5e707SKishon Vijay Abraham I case USB_RECIP_ENDPOINT:
36485d5e707SKishon Vijay Abraham I dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
36585d5e707SKishon Vijay Abraham I if (!dep)
36685d5e707SKishon Vijay Abraham I return -EINVAL;
36785d5e707SKishon Vijay Abraham I
36885d5e707SKishon Vijay Abraham I if (dep->flags & DWC3_EP_STALL)
36985d5e707SKishon Vijay Abraham I usb_status = 1 << USB_ENDPOINT_HALT;
37085d5e707SKishon Vijay Abraham I break;
37185d5e707SKishon Vijay Abraham I default:
37285d5e707SKishon Vijay Abraham I return -EINVAL;
37385d5e707SKishon Vijay Abraham I }
37485d5e707SKishon Vijay Abraham I
37585d5e707SKishon Vijay Abraham I response_pkt = (__le16 *) dwc->setup_buf;
37685d5e707SKishon Vijay Abraham I *response_pkt = cpu_to_le16(usb_status);
37785d5e707SKishon Vijay Abraham I
37885d5e707SKishon Vijay Abraham I dep = dwc->eps[0];
37985d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.dep = dep;
38085d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
38185d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.request.buf = dwc->setup_buf;
38285d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
38385d5e707SKishon Vijay Abraham I
38485d5e707SKishon Vijay Abraham I return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
38585d5e707SKishon Vijay Abraham I }
38685d5e707SKishon Vijay Abraham I
dwc3_ep0_handle_feature(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl,int set)38785d5e707SKishon Vijay Abraham I static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
38885d5e707SKishon Vijay Abraham I struct usb_ctrlrequest *ctrl, int set)
38985d5e707SKishon Vijay Abraham I {
39085d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
39185d5e707SKishon Vijay Abraham I u32 recip;
39285d5e707SKishon Vijay Abraham I u32 wValue;
39385d5e707SKishon Vijay Abraham I u32 wIndex;
39485d5e707SKishon Vijay Abraham I u32 reg;
39585d5e707SKishon Vijay Abraham I int ret;
39685d5e707SKishon Vijay Abraham I enum usb_device_state state;
39785d5e707SKishon Vijay Abraham I
39885d5e707SKishon Vijay Abraham I wValue = le16_to_cpu(ctrl->wValue);
39985d5e707SKishon Vijay Abraham I wIndex = le16_to_cpu(ctrl->wIndex);
40085d5e707SKishon Vijay Abraham I recip = ctrl->bRequestType & USB_RECIP_MASK;
40185d5e707SKishon Vijay Abraham I state = dwc->gadget.state;
40285d5e707SKishon Vijay Abraham I
40385d5e707SKishon Vijay Abraham I switch (recip) {
40485d5e707SKishon Vijay Abraham I case USB_RECIP_DEVICE:
40585d5e707SKishon Vijay Abraham I
40685d5e707SKishon Vijay Abraham I switch (wValue) {
40785d5e707SKishon Vijay Abraham I case USB_DEVICE_REMOTE_WAKEUP:
40885d5e707SKishon Vijay Abraham I break;
40985d5e707SKishon Vijay Abraham I /*
41085d5e707SKishon Vijay Abraham I * 9.4.1 says only only for SS, in AddressState only for
41185d5e707SKishon Vijay Abraham I * default control pipe
41285d5e707SKishon Vijay Abraham I */
41385d5e707SKishon Vijay Abraham I case USB_DEVICE_U1_ENABLE:
41485d5e707SKishon Vijay Abraham I if (state != USB_STATE_CONFIGURED)
41585d5e707SKishon Vijay Abraham I return -EINVAL;
41685d5e707SKishon Vijay Abraham I if (dwc->speed != DWC3_DSTS_SUPERSPEED)
41785d5e707SKishon Vijay Abraham I return -EINVAL;
41885d5e707SKishon Vijay Abraham I
41985d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL);
4209c946fbbSFrank Wang if (set && !dwc->dis_u1u2_quirk)
42185d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_INITU1ENA;
42285d5e707SKishon Vijay Abraham I else
42385d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_INITU1ENA;
42485d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg);
42585d5e707SKishon Vijay Abraham I break;
42685d5e707SKishon Vijay Abraham I
42785d5e707SKishon Vijay Abraham I case USB_DEVICE_U2_ENABLE:
42885d5e707SKishon Vijay Abraham I if (state != USB_STATE_CONFIGURED)
42985d5e707SKishon Vijay Abraham I return -EINVAL;
43085d5e707SKishon Vijay Abraham I if (dwc->speed != DWC3_DSTS_SUPERSPEED)
43185d5e707SKishon Vijay Abraham I return -EINVAL;
43285d5e707SKishon Vijay Abraham I
43385d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL);
4349c946fbbSFrank Wang if (set && !dwc->dis_u1u2_quirk)
43585d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_INITU2ENA;
43685d5e707SKishon Vijay Abraham I else
43785d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_INITU2ENA;
43885d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg);
43985d5e707SKishon Vijay Abraham I break;
44085d5e707SKishon Vijay Abraham I
44185d5e707SKishon Vijay Abraham I case USB_DEVICE_LTM_ENABLE:
44285d5e707SKishon Vijay Abraham I return -EINVAL;
44385d5e707SKishon Vijay Abraham I
44485d5e707SKishon Vijay Abraham I case USB_DEVICE_TEST_MODE:
44585d5e707SKishon Vijay Abraham I if ((wIndex & 0xff) != 0)
44685d5e707SKishon Vijay Abraham I return -EINVAL;
44785d5e707SKishon Vijay Abraham I if (!set)
44885d5e707SKishon Vijay Abraham I return -EINVAL;
44985d5e707SKishon Vijay Abraham I
45085d5e707SKishon Vijay Abraham I dwc->test_mode_nr = wIndex >> 8;
45185d5e707SKishon Vijay Abraham I dwc->test_mode = true;
45285d5e707SKishon Vijay Abraham I break;
45385d5e707SKishon Vijay Abraham I default:
45485d5e707SKishon Vijay Abraham I return -EINVAL;
45585d5e707SKishon Vijay Abraham I }
45685d5e707SKishon Vijay Abraham I break;
45785d5e707SKishon Vijay Abraham I
45885d5e707SKishon Vijay Abraham I case USB_RECIP_INTERFACE:
45985d5e707SKishon Vijay Abraham I switch (wValue) {
46085d5e707SKishon Vijay Abraham I case USB_INTRF_FUNC_SUSPEND:
46185d5e707SKishon Vijay Abraham I if (wIndex & USB_INTRF_FUNC_SUSPEND_LP)
46285d5e707SKishon Vijay Abraham I /* XXX enable Low power suspend */
46385d5e707SKishon Vijay Abraham I ;
46485d5e707SKishon Vijay Abraham I if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
46585d5e707SKishon Vijay Abraham I /* XXX enable remote wakeup */
46685d5e707SKishon Vijay Abraham I ;
46785d5e707SKishon Vijay Abraham I break;
46885d5e707SKishon Vijay Abraham I default:
46985d5e707SKishon Vijay Abraham I return -EINVAL;
47085d5e707SKishon Vijay Abraham I }
47185d5e707SKishon Vijay Abraham I break;
47285d5e707SKishon Vijay Abraham I
47385d5e707SKishon Vijay Abraham I case USB_RECIP_ENDPOINT:
47485d5e707SKishon Vijay Abraham I switch (wValue) {
47585d5e707SKishon Vijay Abraham I case USB_ENDPOINT_HALT:
47685d5e707SKishon Vijay Abraham I dep = dwc3_wIndex_to_dep(dwc, wIndex);
47785d5e707SKishon Vijay Abraham I if (!dep)
47885d5e707SKishon Vijay Abraham I return -EINVAL;
47985d5e707SKishon Vijay Abraham I if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
48085d5e707SKishon Vijay Abraham I break;
48185d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_set_halt(dep, set, true);
48285d5e707SKishon Vijay Abraham I if (ret)
48385d5e707SKishon Vijay Abraham I return -EINVAL;
48485d5e707SKishon Vijay Abraham I break;
48585d5e707SKishon Vijay Abraham I default:
48685d5e707SKishon Vijay Abraham I return -EINVAL;
48785d5e707SKishon Vijay Abraham I }
48885d5e707SKishon Vijay Abraham I break;
48985d5e707SKishon Vijay Abraham I
49085d5e707SKishon Vijay Abraham I default:
49185d5e707SKishon Vijay Abraham I return -EINVAL;
49285d5e707SKishon Vijay Abraham I }
49385d5e707SKishon Vijay Abraham I
49485d5e707SKishon Vijay Abraham I return 0;
49585d5e707SKishon Vijay Abraham I }
49685d5e707SKishon Vijay Abraham I
dwc3_ep0_set_address(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)49785d5e707SKishon Vijay Abraham I static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
49885d5e707SKishon Vijay Abraham I {
49985d5e707SKishon Vijay Abraham I enum usb_device_state state = dwc->gadget.state;
50085d5e707SKishon Vijay Abraham I u32 addr;
50185d5e707SKishon Vijay Abraham I u32 reg;
50285d5e707SKishon Vijay Abraham I
50385d5e707SKishon Vijay Abraham I addr = le16_to_cpu(ctrl->wValue);
50485d5e707SKishon Vijay Abraham I if (addr > 127) {
5059de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "invalid device address %d", addr);
50685d5e707SKishon Vijay Abraham I return -EINVAL;
50785d5e707SKishon Vijay Abraham I }
50885d5e707SKishon Vijay Abraham I
50985d5e707SKishon Vijay Abraham I if (state == USB_STATE_CONFIGURED) {
5109de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "trying to set address when configured");
51185d5e707SKishon Vijay Abraham I return -EINVAL;
51285d5e707SKishon Vijay Abraham I }
51385d5e707SKishon Vijay Abraham I
514*36c87911Swilliam.wu dwc->connected = 1;
51585d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCFG);
51685d5e707SKishon Vijay Abraham I reg &= ~(DWC3_DCFG_DEVADDR_MASK);
51785d5e707SKishon Vijay Abraham I reg |= DWC3_DCFG_DEVADDR(addr);
51885d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCFG, reg);
51985d5e707SKishon Vijay Abraham I
52085d5e707SKishon Vijay Abraham I if (addr)
52185d5e707SKishon Vijay Abraham I usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
52285d5e707SKishon Vijay Abraham I else
52385d5e707SKishon Vijay Abraham I usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
52485d5e707SKishon Vijay Abraham I
52585d5e707SKishon Vijay Abraham I return 0;
52685d5e707SKishon Vijay Abraham I }
52785d5e707SKishon Vijay Abraham I
dwc3_ep0_delegate_req(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)52885d5e707SKishon Vijay Abraham I static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
52985d5e707SKishon Vijay Abraham I {
53085d5e707SKishon Vijay Abraham I int ret;
53185d5e707SKishon Vijay Abraham I
53285d5e707SKishon Vijay Abraham I spin_unlock(&dwc->lock);
53385d5e707SKishon Vijay Abraham I ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
53485d5e707SKishon Vijay Abraham I spin_lock(&dwc->lock);
53585d5e707SKishon Vijay Abraham I return ret;
53685d5e707SKishon Vijay Abraham I }
53785d5e707SKishon Vijay Abraham I
dwc3_ep0_set_config(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)53885d5e707SKishon Vijay Abraham I static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
53985d5e707SKishon Vijay Abraham I {
54085d5e707SKishon Vijay Abraham I enum usb_device_state state = dwc->gadget.state;
54185d5e707SKishon Vijay Abraham I u32 cfg;
54285d5e707SKishon Vijay Abraham I int ret;
54385d5e707SKishon Vijay Abraham I u32 reg;
54485d5e707SKishon Vijay Abraham I
54585d5e707SKishon Vijay Abraham I dwc->start_config_issued = false;
54685d5e707SKishon Vijay Abraham I cfg = le16_to_cpu(ctrl->wValue);
54785d5e707SKishon Vijay Abraham I
54885d5e707SKishon Vijay Abraham I switch (state) {
54985d5e707SKishon Vijay Abraham I case USB_STATE_DEFAULT:
55085d5e707SKishon Vijay Abraham I return -EINVAL;
55185d5e707SKishon Vijay Abraham I
55285d5e707SKishon Vijay Abraham I case USB_STATE_ADDRESS:
55385d5e707SKishon Vijay Abraham I ret = dwc3_ep0_delegate_req(dwc, ctrl);
55485d5e707SKishon Vijay Abraham I /* if the cfg matches and the cfg is non zero */
55585d5e707SKishon Vijay Abraham I if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
55685d5e707SKishon Vijay Abraham I
55785d5e707SKishon Vijay Abraham I /*
55885d5e707SKishon Vijay Abraham I * only change state if set_config has already
55985d5e707SKishon Vijay Abraham I * been processed. If gadget driver returns
56085d5e707SKishon Vijay Abraham I * USB_GADGET_DELAYED_STATUS, we will wait
56185d5e707SKishon Vijay Abraham I * to change the state on the next usb_ep_queue()
56285d5e707SKishon Vijay Abraham I */
56385d5e707SKishon Vijay Abraham I if (ret == 0)
56485d5e707SKishon Vijay Abraham I usb_gadget_set_state(&dwc->gadget,
56585d5e707SKishon Vijay Abraham I USB_STATE_CONFIGURED);
56685d5e707SKishon Vijay Abraham I
56785d5e707SKishon Vijay Abraham I /*
56885d5e707SKishon Vijay Abraham I * Enable transition to U1/U2 state when
56985d5e707SKishon Vijay Abraham I * nothing is pending from application.
57085d5e707SKishon Vijay Abraham I */
57185d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL);
5729c946fbbSFrank Wang if (dwc->dis_u1u2_quirk)
5739c946fbbSFrank Wang reg &= ~(DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
5749c946fbbSFrank Wang else
57585d5e707SKishon Vijay Abraham I reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
57685d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg);
57785d5e707SKishon Vijay Abraham I
57885d5e707SKishon Vijay Abraham I dwc->resize_fifos = true;
5799de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "resize FIFOs flag SET");
58085d5e707SKishon Vijay Abraham I }
58185d5e707SKishon Vijay Abraham I break;
58285d5e707SKishon Vijay Abraham I
58385d5e707SKishon Vijay Abraham I case USB_STATE_CONFIGURED:
58485d5e707SKishon Vijay Abraham I ret = dwc3_ep0_delegate_req(dwc, ctrl);
58585d5e707SKishon Vijay Abraham I if (!cfg && !ret)
58685d5e707SKishon Vijay Abraham I usb_gadget_set_state(&dwc->gadget,
58785d5e707SKishon Vijay Abraham I USB_STATE_ADDRESS);
58885d5e707SKishon Vijay Abraham I break;
58985d5e707SKishon Vijay Abraham I default:
59085d5e707SKishon Vijay Abraham I ret = -EINVAL;
59185d5e707SKishon Vijay Abraham I }
59285d5e707SKishon Vijay Abraham I return ret;
59385d5e707SKishon Vijay Abraham I }
59485d5e707SKishon Vijay Abraham I
dwc3_ep0_set_sel_cmpl(struct usb_ep * ep,struct usb_request * req)59585d5e707SKishon Vijay Abraham I static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
59685d5e707SKishon Vijay Abraham I {
59785d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep);
59885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc;
59985d5e707SKishon Vijay Abraham I
60085d5e707SKishon Vijay Abraham I u32 param = 0;
60185d5e707SKishon Vijay Abraham I u32 reg;
60285d5e707SKishon Vijay Abraham I
60385d5e707SKishon Vijay Abraham I struct timing {
60485d5e707SKishon Vijay Abraham I u8 u1sel;
60585d5e707SKishon Vijay Abraham I u8 u1pel;
60685d5e707SKishon Vijay Abraham I u16 u2sel;
60785d5e707SKishon Vijay Abraham I u16 u2pel;
60885d5e707SKishon Vijay Abraham I } __packed timing;
60985d5e707SKishon Vijay Abraham I
61085d5e707SKishon Vijay Abraham I int ret;
61185d5e707SKishon Vijay Abraham I
61285d5e707SKishon Vijay Abraham I memcpy(&timing, req->buf, sizeof(timing));
61385d5e707SKishon Vijay Abraham I
61485d5e707SKishon Vijay Abraham I dwc->u1sel = timing.u1sel;
61585d5e707SKishon Vijay Abraham I dwc->u1pel = timing.u1pel;
61685d5e707SKishon Vijay Abraham I dwc->u2sel = le16_to_cpu(timing.u2sel);
61785d5e707SKishon Vijay Abraham I dwc->u2pel = le16_to_cpu(timing.u2pel);
61885d5e707SKishon Vijay Abraham I
61985d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL);
62085d5e707SKishon Vijay Abraham I if (reg & DWC3_DCTL_INITU2ENA)
62185d5e707SKishon Vijay Abraham I param = dwc->u2pel;
62285d5e707SKishon Vijay Abraham I if (reg & DWC3_DCTL_INITU1ENA)
62385d5e707SKishon Vijay Abraham I param = dwc->u1pel;
62485d5e707SKishon Vijay Abraham I
62585d5e707SKishon Vijay Abraham I /*
62685d5e707SKishon Vijay Abraham I * According to Synopsys Databook, if parameter is
62785d5e707SKishon Vijay Abraham I * greater than 125, a value of zero should be
62885d5e707SKishon Vijay Abraham I * programmed in the register.
62985d5e707SKishon Vijay Abraham I */
63085d5e707SKishon Vijay Abraham I if (param > 125)
63185d5e707SKishon Vijay Abraham I param = 0;
63285d5e707SKishon Vijay Abraham I
63385d5e707SKishon Vijay Abraham I /* now that we have the time, issue DGCMD Set Sel */
63485d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_generic_command(dwc,
63585d5e707SKishon Vijay Abraham I DWC3_DGCMD_SET_PERIODIC_PAR, param);
63685d5e707SKishon Vijay Abraham I WARN_ON(ret < 0);
63785d5e707SKishon Vijay Abraham I }
63885d5e707SKishon Vijay Abraham I
dwc3_ep0_set_sel(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)63985d5e707SKishon Vijay Abraham I static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
64085d5e707SKishon Vijay Abraham I {
64185d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
64285d5e707SKishon Vijay Abraham I enum usb_device_state state = dwc->gadget.state;
64385d5e707SKishon Vijay Abraham I u16 wLength;
64485d5e707SKishon Vijay Abraham I
64585d5e707SKishon Vijay Abraham I if (state == USB_STATE_DEFAULT)
64685d5e707SKishon Vijay Abraham I return -EINVAL;
64785d5e707SKishon Vijay Abraham I
64885d5e707SKishon Vijay Abraham I wLength = le16_to_cpu(ctrl->wLength);
64985d5e707SKishon Vijay Abraham I
65085d5e707SKishon Vijay Abraham I if (wLength != 6) {
65185d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n",
65285d5e707SKishon Vijay Abraham I wLength);
65385d5e707SKishon Vijay Abraham I return -EINVAL;
65485d5e707SKishon Vijay Abraham I }
65585d5e707SKishon Vijay Abraham I
65685d5e707SKishon Vijay Abraham I /*
65785d5e707SKishon Vijay Abraham I * To handle Set SEL we need to receive 6 bytes from Host. So let's
65885d5e707SKishon Vijay Abraham I * queue a usb_request for 6 bytes.
65985d5e707SKishon Vijay Abraham I *
66085d5e707SKishon Vijay Abraham I * Remember, though, this controller can't handle non-wMaxPacketSize
66185d5e707SKishon Vijay Abraham I * aligned transfers on the OUT direction, so we queue a request for
66285d5e707SKishon Vijay Abraham I * wMaxPacketSize instead.
66385d5e707SKishon Vijay Abraham I */
66485d5e707SKishon Vijay Abraham I dep = dwc->eps[0];
66585d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.dep = dep;
66685d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
66785d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.request.buf = dwc->setup_buf;
66885d5e707SKishon Vijay Abraham I dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
66985d5e707SKishon Vijay Abraham I
67085d5e707SKishon Vijay Abraham I return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
67185d5e707SKishon Vijay Abraham I }
67285d5e707SKishon Vijay Abraham I
dwc3_ep0_set_isoch_delay(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)67385d5e707SKishon Vijay Abraham I static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
67485d5e707SKishon Vijay Abraham I {
67585d5e707SKishon Vijay Abraham I u16 wLength;
67685d5e707SKishon Vijay Abraham I u16 wValue;
67785d5e707SKishon Vijay Abraham I u16 wIndex;
67885d5e707SKishon Vijay Abraham I
67985d5e707SKishon Vijay Abraham I wValue = le16_to_cpu(ctrl->wValue);
68085d5e707SKishon Vijay Abraham I wLength = le16_to_cpu(ctrl->wLength);
68185d5e707SKishon Vijay Abraham I wIndex = le16_to_cpu(ctrl->wIndex);
68285d5e707SKishon Vijay Abraham I
68385d5e707SKishon Vijay Abraham I if (wIndex || wLength)
68485d5e707SKishon Vijay Abraham I return -EINVAL;
68585d5e707SKishon Vijay Abraham I
68685d5e707SKishon Vijay Abraham I /*
68785d5e707SKishon Vijay Abraham I * REVISIT It's unclear from Databook what to do with this
68885d5e707SKishon Vijay Abraham I * value. For now, just cache it.
68985d5e707SKishon Vijay Abraham I */
69085d5e707SKishon Vijay Abraham I dwc->isoch_delay = wValue;
69185d5e707SKishon Vijay Abraham I
69285d5e707SKishon Vijay Abraham I return 0;
69385d5e707SKishon Vijay Abraham I }
69485d5e707SKishon Vijay Abraham I
dwc3_ep0_std_request(struct dwc3 * dwc,struct usb_ctrlrequest * ctrl)69585d5e707SKishon Vijay Abraham I static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
69685d5e707SKishon Vijay Abraham I {
69785d5e707SKishon Vijay Abraham I int ret;
69885d5e707SKishon Vijay Abraham I
69985d5e707SKishon Vijay Abraham I switch (ctrl->bRequest) {
70085d5e707SKishon Vijay Abraham I case USB_REQ_GET_STATUS:
7019de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS");
70285d5e707SKishon Vijay Abraham I ret = dwc3_ep0_handle_status(dwc, ctrl);
70385d5e707SKishon Vijay Abraham I break;
70485d5e707SKishon Vijay Abraham I case USB_REQ_CLEAR_FEATURE:
7059de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE");
70685d5e707SKishon Vijay Abraham I ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
70785d5e707SKishon Vijay Abraham I break;
70885d5e707SKishon Vijay Abraham I case USB_REQ_SET_FEATURE:
7099de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE");
71085d5e707SKishon Vijay Abraham I ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
71185d5e707SKishon Vijay Abraham I break;
71285d5e707SKishon Vijay Abraham I case USB_REQ_SET_ADDRESS:
7139de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS");
71485d5e707SKishon Vijay Abraham I ret = dwc3_ep0_set_address(dwc, ctrl);
71585d5e707SKishon Vijay Abraham I break;
71685d5e707SKishon Vijay Abraham I case USB_REQ_SET_CONFIGURATION:
7179de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION");
71885d5e707SKishon Vijay Abraham I ret = dwc3_ep0_set_config(dwc, ctrl);
71985d5e707SKishon Vijay Abraham I break;
72085d5e707SKishon Vijay Abraham I case USB_REQ_SET_SEL:
7219de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_SET_SEL");
72285d5e707SKishon Vijay Abraham I ret = dwc3_ep0_set_sel(dwc, ctrl);
72385d5e707SKishon Vijay Abraham I break;
72485d5e707SKishon Vijay Abraham I case USB_REQ_SET_ISOCH_DELAY:
7259de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY");
72685d5e707SKishon Vijay Abraham I ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
72785d5e707SKishon Vijay Abraham I break;
72885d5e707SKishon Vijay Abraham I default:
7299de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Forwarding to gadget driver");
73085d5e707SKishon Vijay Abraham I ret = dwc3_ep0_delegate_req(dwc, ctrl);
73185d5e707SKishon Vijay Abraham I break;
73285d5e707SKishon Vijay Abraham I }
73385d5e707SKishon Vijay Abraham I
73485d5e707SKishon Vijay Abraham I return ret;
73585d5e707SKishon Vijay Abraham I }
73685d5e707SKishon Vijay Abraham I
dwc3_ep0_inspect_setup(struct dwc3 * dwc,const struct dwc3_event_depevt * event)73785d5e707SKishon Vijay Abraham I static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
73885d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
73985d5e707SKishon Vijay Abraham I {
74085d5e707SKishon Vijay Abraham I struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
74185d5e707SKishon Vijay Abraham I int ret = -EINVAL;
74285d5e707SKishon Vijay Abraham I u32 len;
74385d5e707SKishon Vijay Abraham I
74485d5e707SKishon Vijay Abraham I if (!dwc->gadget_driver)
74585d5e707SKishon Vijay Abraham I goto out;
74685d5e707SKishon Vijay Abraham I
74785d5e707SKishon Vijay Abraham I len = le16_to_cpu(ctrl->wLength);
74885d5e707SKishon Vijay Abraham I if (!len) {
74985d5e707SKishon Vijay Abraham I dwc->three_stage_setup = false;
75085d5e707SKishon Vijay Abraham I dwc->ep0_expect_in = false;
75185d5e707SKishon Vijay Abraham I dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
75285d5e707SKishon Vijay Abraham I } else {
75385d5e707SKishon Vijay Abraham I dwc->three_stage_setup = true;
75485d5e707SKishon Vijay Abraham I dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN);
75585d5e707SKishon Vijay Abraham I dwc->ep0_next_event = DWC3_EP0_NRDY_DATA;
75685d5e707SKishon Vijay Abraham I }
75785d5e707SKishon Vijay Abraham I
75885d5e707SKishon Vijay Abraham I if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
75985d5e707SKishon Vijay Abraham I ret = dwc3_ep0_std_request(dwc, ctrl);
76085d5e707SKishon Vijay Abraham I else
76185d5e707SKishon Vijay Abraham I ret = dwc3_ep0_delegate_req(dwc, ctrl);
76285d5e707SKishon Vijay Abraham I
76385d5e707SKishon Vijay Abraham I if (ret == USB_GADGET_DELAYED_STATUS)
76485d5e707SKishon Vijay Abraham I dwc->delayed_status = true;
76585d5e707SKishon Vijay Abraham I
76685d5e707SKishon Vijay Abraham I out:
76785d5e707SKishon Vijay Abraham I if (ret < 0)
76885d5e707SKishon Vijay Abraham I dwc3_ep0_stall_and_restart(dwc);
76985d5e707SKishon Vijay Abraham I }
77085d5e707SKishon Vijay Abraham I
dwc3_ep0_complete_data(struct dwc3 * dwc,const struct dwc3_event_depevt * event)77185d5e707SKishon Vijay Abraham I static void dwc3_ep0_complete_data(struct dwc3 *dwc,
77285d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
77385d5e707SKishon Vijay Abraham I {
77485d5e707SKishon Vijay Abraham I struct dwc3_request *r = NULL;
77585d5e707SKishon Vijay Abraham I struct usb_request *ur;
77685d5e707SKishon Vijay Abraham I struct dwc3_trb *trb;
77785d5e707SKishon Vijay Abraham I struct dwc3_ep *ep0;
7781f78f8feSKishon Vijay Abraham I unsigned transfer_size = 0;
7791f78f8feSKishon Vijay Abraham I unsigned maxp;
7801f78f8feSKishon Vijay Abraham I void *buf;
7811f78f8feSKishon Vijay Abraham I u32 transferred = 0;
78285d5e707SKishon Vijay Abraham I u32 status;
78385d5e707SKishon Vijay Abraham I u32 length;
78485d5e707SKishon Vijay Abraham I u8 epnum;
78585d5e707SKishon Vijay Abraham I
78685d5e707SKishon Vijay Abraham I epnum = event->endpoint_number;
78785d5e707SKishon Vijay Abraham I ep0 = dwc->eps[0];
78885d5e707SKishon Vijay Abraham I
78985d5e707SKishon Vijay Abraham I dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
79085d5e707SKishon Vijay Abraham I
79185d5e707SKishon Vijay Abraham I trb = dwc->ep0_trb;
79285d5e707SKishon Vijay Abraham I
79385d5e707SKishon Vijay Abraham I r = next_request(&ep0->request_list);
79485d5e707SKishon Vijay Abraham I if (!r)
79585d5e707SKishon Vijay Abraham I return;
79685d5e707SKishon Vijay Abraham I
797b7bf4a95SPhilipp Tomsich dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
798526a50f8SKishon Vijay Abraham I
79985d5e707SKishon Vijay Abraham I status = DWC3_TRB_SIZE_TRBSTS(trb->size);
80085d5e707SKishon Vijay Abraham I if (status == DWC3_TRBSTS_SETUP_PENDING) {
8019de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "Setup Pending received");
80285d5e707SKishon Vijay Abraham I
80385d5e707SKishon Vijay Abraham I if (r)
80485d5e707SKishon Vijay Abraham I dwc3_gadget_giveback(ep0, r, -ECONNRESET);
80585d5e707SKishon Vijay Abraham I
80685d5e707SKishon Vijay Abraham I return;
80785d5e707SKishon Vijay Abraham I }
80885d5e707SKishon Vijay Abraham I
80985d5e707SKishon Vijay Abraham I ur = &r->request;
8101f78f8feSKishon Vijay Abraham I buf = ur->buf;
81185d5e707SKishon Vijay Abraham I
81285d5e707SKishon Vijay Abraham I length = trb->size & DWC3_TRB_SIZE_MASK;
81385d5e707SKishon Vijay Abraham I
8141f78f8feSKishon Vijay Abraham I maxp = ep0->endpoint.maxpacket;
81585d5e707SKishon Vijay Abraham I
8161f78f8feSKishon Vijay Abraham I if (dwc->ep0_bounced) {
8178d488f3eSKishon Vijay Abraham I /*
8188d488f3eSKishon Vijay Abraham I * Handle the first TRB before handling the bounce buffer if
8198d488f3eSKishon Vijay Abraham I * the request length is greater than the bounce buffer size.
8208d488f3eSKishon Vijay Abraham I */
8218d488f3eSKishon Vijay Abraham I if (ur->length > DWC3_EP0_BOUNCE_SIZE) {
8228d488f3eSKishon Vijay Abraham I transfer_size = (ur->length / maxp) * maxp;
8238d488f3eSKishon Vijay Abraham I transferred = transfer_size - length;
8248d488f3eSKishon Vijay Abraham I buf = (u8 *)buf + transferred;
8258d488f3eSKishon Vijay Abraham I ur->actual += transferred;
8268d488f3eSKishon Vijay Abraham I
8278d488f3eSKishon Vijay Abraham I trb++;
828b7bf4a95SPhilipp Tomsich dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
8298d488f3eSKishon Vijay Abraham I length = trb->size & DWC3_TRB_SIZE_MASK;
8308d488f3eSKishon Vijay Abraham I
8318d488f3eSKishon Vijay Abraham I ep0->free_slot = 0;
8328d488f3eSKishon Vijay Abraham I }
8338d488f3eSKishon Vijay Abraham I
8341f78f8feSKishon Vijay Abraham I transfer_size = roundup((ur->length - transfer_size),
8351f78f8feSKishon Vijay Abraham I maxp);
8361f78f8feSKishon Vijay Abraham I transferred = min_t(u32, ur->length - transferred,
83785d5e707SKishon Vijay Abraham I transfer_size - length);
838b7bf4a95SPhilipp Tomsich dwc3_flush_cache((uintptr_t)dwc->ep0_bounce, DWC3_EP0_BOUNCE_SIZE);
8391f78f8feSKishon Vijay Abraham I memcpy(buf, dwc->ep0_bounce, transferred);
84085d5e707SKishon Vijay Abraham I } else {
84185d5e707SKishon Vijay Abraham I transferred = ur->length - length;
84285d5e707SKishon Vijay Abraham I }
84385d5e707SKishon Vijay Abraham I
84485d5e707SKishon Vijay Abraham I ur->actual += transferred;
84585d5e707SKishon Vijay Abraham I
84685d5e707SKishon Vijay Abraham I if ((epnum & 1) && ur->actual < ur->length) {
84785d5e707SKishon Vijay Abraham I /* for some reason we did not get everything out */
84885d5e707SKishon Vijay Abraham I
84985d5e707SKishon Vijay Abraham I dwc3_ep0_stall_and_restart(dwc);
85085d5e707SKishon Vijay Abraham I } else {
85185d5e707SKishon Vijay Abraham I dwc3_gadget_giveback(ep0, r, 0);
85285d5e707SKishon Vijay Abraham I
85385d5e707SKishon Vijay Abraham I if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
85485d5e707SKishon Vijay Abraham I ur->length && ur->zero) {
85585d5e707SKishon Vijay Abraham I int ret;
85685d5e707SKishon Vijay Abraham I
85785d5e707SKishon Vijay Abraham I dwc->ep0_next_event = DWC3_EP0_COMPLETE;
85885d5e707SKishon Vijay Abraham I
85985d5e707SKishon Vijay Abraham I ret = dwc3_ep0_start_trans(dwc, epnum,
86085d5e707SKishon Vijay Abraham I dwc->ctrl_req_addr, 0,
8618d488f3eSKishon Vijay Abraham I DWC3_TRBCTL_CONTROL_DATA, 0);
86285d5e707SKishon Vijay Abraham I WARN_ON(ret < 0);
86385d5e707SKishon Vijay Abraham I }
86485d5e707SKishon Vijay Abraham I }
86585d5e707SKishon Vijay Abraham I }
86685d5e707SKishon Vijay Abraham I
dwc3_ep0_complete_status(struct dwc3 * dwc,const struct dwc3_event_depevt * event)86785d5e707SKishon Vijay Abraham I static void dwc3_ep0_complete_status(struct dwc3 *dwc,
86885d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
86985d5e707SKishon Vijay Abraham I {
87085d5e707SKishon Vijay Abraham I struct dwc3_request *r;
87185d5e707SKishon Vijay Abraham I struct dwc3_ep *dep;
87285d5e707SKishon Vijay Abraham I struct dwc3_trb *trb;
87385d5e707SKishon Vijay Abraham I u32 status;
87485d5e707SKishon Vijay Abraham I
87585d5e707SKishon Vijay Abraham I dep = dwc->eps[0];
87685d5e707SKishon Vijay Abraham I trb = dwc->ep0_trb;
87785d5e707SKishon Vijay Abraham I
87885d5e707SKishon Vijay Abraham I if (!list_empty(&dep->request_list)) {
87985d5e707SKishon Vijay Abraham I r = next_request(&dep->request_list);
88085d5e707SKishon Vijay Abraham I
88185d5e707SKishon Vijay Abraham I dwc3_gadget_giveback(dep, r, 0);
88285d5e707SKishon Vijay Abraham I }
88385d5e707SKishon Vijay Abraham I
88485d5e707SKishon Vijay Abraham I if (dwc->test_mode) {
88585d5e707SKishon Vijay Abraham I int ret;
88685d5e707SKishon Vijay Abraham I
88785d5e707SKishon Vijay Abraham I ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
88885d5e707SKishon Vijay Abraham I if (ret < 0) {
8899de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "Invalid Test #%d",
89085d5e707SKishon Vijay Abraham I dwc->test_mode_nr);
89185d5e707SKishon Vijay Abraham I dwc3_ep0_stall_and_restart(dwc);
89285d5e707SKishon Vijay Abraham I return;
89385d5e707SKishon Vijay Abraham I }
89485d5e707SKishon Vijay Abraham I }
89585d5e707SKishon Vijay Abraham I
89685d5e707SKishon Vijay Abraham I status = DWC3_TRB_SIZE_TRBSTS(trb->size);
89785d5e707SKishon Vijay Abraham I if (status == DWC3_TRBSTS_SETUP_PENDING)
8989de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "Setup Pending received");
89985d5e707SKishon Vijay Abraham I
90085d5e707SKishon Vijay Abraham I dwc->ep0state = EP0_SETUP_PHASE;
90185d5e707SKishon Vijay Abraham I dwc3_ep0_out_start(dwc);
90285d5e707SKishon Vijay Abraham I }
90385d5e707SKishon Vijay Abraham I
dwc3_ep0_xfer_complete(struct dwc3 * dwc,const struct dwc3_event_depevt * event)90485d5e707SKishon Vijay Abraham I static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
90585d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
90685d5e707SKishon Vijay Abraham I {
90785d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
90885d5e707SKishon Vijay Abraham I
90985d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP_BUSY;
91085d5e707SKishon Vijay Abraham I dep->resource_index = 0;
91185d5e707SKishon Vijay Abraham I dwc->setup_packet_pending = false;
91285d5e707SKishon Vijay Abraham I
91385d5e707SKishon Vijay Abraham I switch (dwc->ep0state) {
91485d5e707SKishon Vijay Abraham I case EP0_SETUP_PHASE:
9159de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Setup Phase");
91685d5e707SKishon Vijay Abraham I dwc3_ep0_inspect_setup(dwc, event);
91785d5e707SKishon Vijay Abraham I break;
91885d5e707SKishon Vijay Abraham I
91985d5e707SKishon Vijay Abraham I case EP0_DATA_PHASE:
9209de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Data Phase");
92185d5e707SKishon Vijay Abraham I dwc3_ep0_complete_data(dwc, event);
92285d5e707SKishon Vijay Abraham I break;
92385d5e707SKishon Vijay Abraham I
92485d5e707SKishon Vijay Abraham I case EP0_STATUS_PHASE:
9259de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Status Phase");
92685d5e707SKishon Vijay Abraham I dwc3_ep0_complete_status(dwc, event);
92785d5e707SKishon Vijay Abraham I break;
92885d5e707SKishon Vijay Abraham I default:
92985d5e707SKishon Vijay Abraham I WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
93085d5e707SKishon Vijay Abraham I }
93185d5e707SKishon Vijay Abraham I }
93285d5e707SKishon Vijay Abraham I
__dwc3_ep0_do_control_data(struct dwc3 * dwc,struct dwc3_ep * dep,struct dwc3_request * req)93385d5e707SKishon Vijay Abraham I static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
93485d5e707SKishon Vijay Abraham I struct dwc3_ep *dep, struct dwc3_request *req)
93585d5e707SKishon Vijay Abraham I {
93685d5e707SKishon Vijay Abraham I int ret;
93785d5e707SKishon Vijay Abraham I
93885d5e707SKishon Vijay Abraham I req->direction = !!dep->number;
93985d5e707SKishon Vijay Abraham I
94085d5e707SKishon Vijay Abraham I if (req->request.length == 0) {
94185d5e707SKishon Vijay Abraham I ret = dwc3_ep0_start_trans(dwc, dep->number,
94285d5e707SKishon Vijay Abraham I dwc->ctrl_req_addr, 0,
9438d488f3eSKishon Vijay Abraham I DWC3_TRBCTL_CONTROL_DATA, 0);
9448d488f3eSKishon Vijay Abraham I } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
9458d488f3eSKishon Vijay Abraham I (dep->number == 0)) {
9468d488f3eSKishon Vijay Abraham I u32 transfer_size = 0;
94785d5e707SKishon Vijay Abraham I u32 maxpacket;
94885d5e707SKishon Vijay Abraham I
94985d5e707SKishon Vijay Abraham I ret = usb_gadget_map_request(&dwc->gadget, &req->request,
95085d5e707SKishon Vijay Abraham I dep->number);
95185d5e707SKishon Vijay Abraham I if (ret) {
95285d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "failed to map request\n");
95385d5e707SKishon Vijay Abraham I return;
95485d5e707SKishon Vijay Abraham I }
95585d5e707SKishon Vijay Abraham I
95685d5e707SKishon Vijay Abraham I maxpacket = dep->endpoint.maxpacket;
9578d488f3eSKishon Vijay Abraham I if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
9588d488f3eSKishon Vijay Abraham I transfer_size = (req->request.length / maxpacket) *
9598d488f3eSKishon Vijay Abraham I maxpacket;
9608d488f3eSKishon Vijay Abraham I ret = dwc3_ep0_start_trans(dwc, dep->number,
9618d488f3eSKishon Vijay Abraham I req->request.dma,
9628d488f3eSKishon Vijay Abraham I transfer_size,
9638d488f3eSKishon Vijay Abraham I DWC3_TRBCTL_CONTROL_DATA, 1);
9648d488f3eSKishon Vijay Abraham I }
9658d488f3eSKishon Vijay Abraham I
9668d488f3eSKishon Vijay Abraham I transfer_size = roundup((req->request.length - transfer_size),
9678d488f3eSKishon Vijay Abraham I maxpacket);
96885d5e707SKishon Vijay Abraham I
96985d5e707SKishon Vijay Abraham I dwc->ep0_bounced = true;
97085d5e707SKishon Vijay Abraham I
97185d5e707SKishon Vijay Abraham I /*
97285d5e707SKishon Vijay Abraham I * REVISIT in case request length is bigger than
97385d5e707SKishon Vijay Abraham I * DWC3_EP0_BOUNCE_SIZE we will need two chained
97485d5e707SKishon Vijay Abraham I * TRBs to handle the transfer.
97585d5e707SKishon Vijay Abraham I */
97685d5e707SKishon Vijay Abraham I ret = dwc3_ep0_start_trans(dwc, dep->number,
97785d5e707SKishon Vijay Abraham I dwc->ep0_bounce_addr, transfer_size,
9788d488f3eSKishon Vijay Abraham I DWC3_TRBCTL_CONTROL_DATA, 0);
97985d5e707SKishon Vijay Abraham I } else {
98085d5e707SKishon Vijay Abraham I ret = usb_gadget_map_request(&dwc->gadget, &req->request,
98185d5e707SKishon Vijay Abraham I dep->number);
98285d5e707SKishon Vijay Abraham I if (ret) {
98385d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "failed to map request\n");
98485d5e707SKishon Vijay Abraham I return;
98585d5e707SKishon Vijay Abraham I }
98685d5e707SKishon Vijay Abraham I
98785d5e707SKishon Vijay Abraham I ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
9888d488f3eSKishon Vijay Abraham I req->request.length,
9898d488f3eSKishon Vijay Abraham I DWC3_TRBCTL_CONTROL_DATA, 0);
99085d5e707SKishon Vijay Abraham I }
99185d5e707SKishon Vijay Abraham I
99285d5e707SKishon Vijay Abraham I WARN_ON(ret < 0);
99385d5e707SKishon Vijay Abraham I }
99485d5e707SKishon Vijay Abraham I
dwc3_ep0_start_control_status(struct dwc3_ep * dep)99585d5e707SKishon Vijay Abraham I static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
99685d5e707SKishon Vijay Abraham I {
99785d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc;
99885d5e707SKishon Vijay Abraham I u32 type;
99985d5e707SKishon Vijay Abraham I
100085d5e707SKishon Vijay Abraham I type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
100185d5e707SKishon Vijay Abraham I : DWC3_TRBCTL_CONTROL_STATUS2;
100285d5e707SKishon Vijay Abraham I
100385d5e707SKishon Vijay Abraham I return dwc3_ep0_start_trans(dwc, dep->number,
10048d488f3eSKishon Vijay Abraham I dwc->ctrl_req_addr, 0, type, 0);
100585d5e707SKishon Vijay Abraham I }
100685d5e707SKishon Vijay Abraham I
__dwc3_ep0_do_control_status(struct dwc3 * dwc,struct dwc3_ep * dep)100785d5e707SKishon Vijay Abraham I static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
100885d5e707SKishon Vijay Abraham I {
100985d5e707SKishon Vijay Abraham I if (dwc->resize_fifos) {
10109de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "Resizing FIFOs");
101185d5e707SKishon Vijay Abraham I dwc3_gadget_resize_tx_fifos(dwc);
101285d5e707SKishon Vijay Abraham I dwc->resize_fifos = 0;
101385d5e707SKishon Vijay Abraham I }
101485d5e707SKishon Vijay Abraham I
101585d5e707SKishon Vijay Abraham I WARN_ON(dwc3_ep0_start_control_status(dep));
101685d5e707SKishon Vijay Abraham I }
101785d5e707SKishon Vijay Abraham I
dwc3_ep0_do_control_status(struct dwc3 * dwc,const struct dwc3_event_depevt * event)101885d5e707SKishon Vijay Abraham I static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
101985d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
102085d5e707SKishon Vijay Abraham I {
102185d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
102285d5e707SKishon Vijay Abraham I
102385d5e707SKishon Vijay Abraham I __dwc3_ep0_do_control_status(dwc, dep);
102485d5e707SKishon Vijay Abraham I }
102585d5e707SKishon Vijay Abraham I
dwc3_ep0_end_control_data(struct dwc3 * dwc,struct dwc3_ep * dep)102685d5e707SKishon Vijay Abraham I static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
102785d5e707SKishon Vijay Abraham I {
102885d5e707SKishon Vijay Abraham I struct dwc3_gadget_ep_cmd_params params;
102985d5e707SKishon Vijay Abraham I u32 cmd;
103085d5e707SKishon Vijay Abraham I int ret;
103185d5e707SKishon Vijay Abraham I
103285d5e707SKishon Vijay Abraham I if (!dep->resource_index)
103385d5e707SKishon Vijay Abraham I return;
103485d5e707SKishon Vijay Abraham I
103585d5e707SKishon Vijay Abraham I cmd = DWC3_DEPCMD_ENDTRANSFER;
103685d5e707SKishon Vijay Abraham I cmd |= DWC3_DEPCMD_CMDIOC;
103785d5e707SKishon Vijay Abraham I cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
103885d5e707SKishon Vijay Abraham I memset(¶ms, 0, sizeof(params));
103985d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
104085d5e707SKishon Vijay Abraham I WARN_ON_ONCE(ret);
104185d5e707SKishon Vijay Abraham I dep->resource_index = 0;
104285d5e707SKishon Vijay Abraham I }
104385d5e707SKishon Vijay Abraham I
dwc3_ep0_xfernotready(struct dwc3 * dwc,const struct dwc3_event_depevt * event)104485d5e707SKishon Vijay Abraham I static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
104585d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
104685d5e707SKishon Vijay Abraham I {
104785d5e707SKishon Vijay Abraham I dwc->setup_packet_pending = true;
104885d5e707SKishon Vijay Abraham I
104985d5e707SKishon Vijay Abraham I switch (event->status) {
105085d5e707SKishon Vijay Abraham I case DEPEVT_STATUS_CONTROL_DATA:
10519de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Control Data");
105285d5e707SKishon Vijay Abraham I
105385d5e707SKishon Vijay Abraham I /*
105485d5e707SKishon Vijay Abraham I * We already have a DATA transfer in the controller's cache,
105585d5e707SKishon Vijay Abraham I * if we receive a XferNotReady(DATA) we will ignore it, unless
105685d5e707SKishon Vijay Abraham I * it's for the wrong direction.
105785d5e707SKishon Vijay Abraham I *
105885d5e707SKishon Vijay Abraham I * In that case, we must issue END_TRANSFER command to the Data
105985d5e707SKishon Vijay Abraham I * Phase we already have started and issue SetStall on the
106085d5e707SKishon Vijay Abraham I * control endpoint.
106185d5e707SKishon Vijay Abraham I */
106285d5e707SKishon Vijay Abraham I if (dwc->ep0_expect_in != event->endpoint_number) {
106385d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
106485d5e707SKishon Vijay Abraham I
10659de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Wrong direction for Data phase");
106685d5e707SKishon Vijay Abraham I dwc3_ep0_end_control_data(dwc, dep);
106785d5e707SKishon Vijay Abraham I dwc3_ep0_stall_and_restart(dwc);
106885d5e707SKishon Vijay Abraham I return;
106985d5e707SKishon Vijay Abraham I }
107085d5e707SKishon Vijay Abraham I
107185d5e707SKishon Vijay Abraham I break;
107285d5e707SKishon Vijay Abraham I
107385d5e707SKishon Vijay Abraham I case DEPEVT_STATUS_CONTROL_STATUS:
107485d5e707SKishon Vijay Abraham I if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
107585d5e707SKishon Vijay Abraham I return;
107685d5e707SKishon Vijay Abraham I
10779de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Control Status");
107885d5e707SKishon Vijay Abraham I
107985d5e707SKishon Vijay Abraham I dwc->ep0state = EP0_STATUS_PHASE;
108085d5e707SKishon Vijay Abraham I
108185d5e707SKishon Vijay Abraham I if (dwc->delayed_status) {
108285d5e707SKishon Vijay Abraham I WARN_ON_ONCE(event->endpoint_number != 1);
10839de1115dSKishon Vijay Abraham I dev_vdbg(dwc->dev, "Delayed Status");
108485d5e707SKishon Vijay Abraham I return;
108585d5e707SKishon Vijay Abraham I }
108685d5e707SKishon Vijay Abraham I
108785d5e707SKishon Vijay Abraham I dwc3_ep0_do_control_status(dwc, event);
108885d5e707SKishon Vijay Abraham I }
108985d5e707SKishon Vijay Abraham I }
109085d5e707SKishon Vijay Abraham I
dwc3_ep0_interrupt(struct dwc3 * dwc,const struct dwc3_event_depevt * event)109185d5e707SKishon Vijay Abraham I void dwc3_ep0_interrupt(struct dwc3 *dwc,
109285d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event)
109385d5e707SKishon Vijay Abraham I {
109485d5e707SKishon Vijay Abraham I u8 epnum = event->endpoint_number;
109585d5e707SKishon Vijay Abraham I
10969de1115dSKishon Vijay Abraham I dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'",
109785d5e707SKishon Vijay Abraham I dwc3_ep_event_string(event->endpoint_event),
109885d5e707SKishon Vijay Abraham I epnum >> 1, (epnum & 1) ? "in" : "out",
109985d5e707SKishon Vijay Abraham I dwc3_ep0_state_string(dwc->ep0state));
110085d5e707SKishon Vijay Abraham I
110185d5e707SKishon Vijay Abraham I switch (event->endpoint_event) {
110285d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_XFERCOMPLETE:
110385d5e707SKishon Vijay Abraham I dwc3_ep0_xfer_complete(dwc, event);
110485d5e707SKishon Vijay Abraham I break;
110585d5e707SKishon Vijay Abraham I
110685d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_XFERNOTREADY:
110785d5e707SKishon Vijay Abraham I dwc3_ep0_xfernotready(dwc, event);
110885d5e707SKishon Vijay Abraham I break;
110985d5e707SKishon Vijay Abraham I
111085d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_XFERINPROGRESS:
111185d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_RXTXFIFOEVT:
111285d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_STREAMEVT:
111385d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_EPCMDCMPLT:
111485d5e707SKishon Vijay Abraham I break;
111585d5e707SKishon Vijay Abraham I }
111685d5e707SKishon Vijay Abraham I }
1117