usb: dwc3: gadget: refactor dwc3_repare_one_trb
[platform/kernel/linux-rpi.git] / drivers / usb / dwc3 / gadget.c
index 4519d06..65aa092 100644 (file)
@@ -310,13 +310,24 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
        if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
                int link_state;
 
+               /*
+                * Initiate remote wakeup if the link state is in U3 when
+                * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
+                * link state is in U1/U2, no remote wakeup is needed. The Start
+                * Transfer command will initiate the link recovery.
+                */
                link_state = dwc3_gadget_get_link_state(dwc);
-               if (link_state == DWC3_LINK_STATE_U1 ||
-                   link_state == DWC3_LINK_STATE_U2 ||
-                   link_state == DWC3_LINK_STATE_U3) {
+               switch (link_state) {
+               case DWC3_LINK_STATE_U2:
+                       if (dwc->gadget->speed >= USB_SPEED_SUPER)
+                               break;
+
+                       fallthrough;
+               case DWC3_LINK_STATE_U3:
                        ret = __dwc3_gadget_wakeup(dwc);
                        dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
                                        ret);
+                       break;
                }
        }
 
@@ -702,6 +713,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
                                   DWC31_GTXFIFOSIZ_TXFRAMNUM;
 
                dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+               dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
        }
        dwc->num_ep_resized = 0;
 }
@@ -747,6 +759,10 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
        if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
                return 0;
 
+       /* bail if already resized */
+       if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
+               return 0;
+
        ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
 
        if ((dep->endpoint.maxburst > 1 &&
@@ -807,6 +823,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
        }
 
        dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+       dep->flags |= DWC3_EP_TXFIFO_RESIZED;
        dwc->num_ep_resized++;
 
        return 0;
@@ -995,7 +1012,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
 
        dep->stream_capable = false;
        dep->type = 0;
-       dep->flags = 0;
+       dep->flags &= DWC3_EP_TXFIFO_RESIZED;
 
        return 0;
 }
@@ -1152,17 +1169,49 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
        return trbs_left;
 }
 
-static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
-               dma_addr_t dma, unsigned int length, unsigned int chain,
-               unsigned int node, unsigned int stream_id,
-               unsigned int short_not_ok, unsigned int no_interrupt,
-               unsigned int is_last, bool must_interrupt)
+/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @trb_length: buffer size of the TRB
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ * @use_bounce_buffer: set to use bounce buffer
+ * @must_interrupt: set to interrupt on TRB completion
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+               struct dwc3_request *req, unsigned int trb_length,
+               unsigned int chain, unsigned int node, bool use_bounce_buffer,
+               bool must_interrupt)
 {
+       struct dwc3_trb         *trb;
+       dma_addr_t              dma;
+       unsigned int            stream_id = req->request.stream_id;
+       unsigned int            short_not_ok = req->request.short_not_ok;
+       unsigned int            no_interrupt = req->request.no_interrupt;
+       unsigned int            is_last = req->request.is_last;
        struct dwc3             *dwc = dep->dwc;
        struct usb_gadget       *gadget = dwc->gadget;
        enum usb_device_speed   speed = gadget->speed;
 
-       trb->size = DWC3_TRB_SIZE_LENGTH(length);
+       if (use_bounce_buffer)
+               dma = dep->dwc->bounce_addr;
+       else if (req->request.num_sgs > 0)
+               dma = sg_dma_address(req->start_sg);
+       else
+               dma = req->request.dma;
+
+       trb = &dep->trb_pool[dep->trb_enqueue];
+
+       if (!req->trb) {
+               dwc3_gadget_move_started_request(req);
+               req->trb = trb;
+               req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+       }
+
+       req->num_trbs++;
+
+       trb->size = DWC3_TRB_SIZE_LENGTH(trb_length);
        trb->bpl = lower_32_bits(dma);
        trb->bph = upper_32_bits(dma);
 
@@ -1202,10 +1251,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
                                unsigned int mult = 2;
                                unsigned int maxp = usb_endpoint_maxp(ep->desc);
 
-                               if (length <= (2 * maxp))
+                               if (trb_length <= (2 * maxp))
                                        mult--;
 
-                               if (length <= maxp)
+                               if (trb_length <= maxp)
                                        mult--;
 
                                trb->size |= DWC3_TRB_SIZE_PCM1(mult);
@@ -1254,6 +1303,19 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
        if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
                trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
 
+       /*
+        * As per data book 4.2.3.2TRB Control Bit Rules section
+        *
+        * The controller autonomously checks the HWO field of a TRB to determine if the
+        * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
+        * is valid before setting the HWO field to '1'. In most systems, this means that
+        * software must update the fourth DWORD of a TRB last.
+        *
+        * However there is a possibility of CPU re-ordering here which can cause
+        * controller to observe the HWO bit set prematurely.
+        * Add a write memory barrier to prevent CPU re-ordering.
+        */
+       wmb();
        trb->ctrl |= DWC3_TRB_CTRL_HWO;
 
        dwc3_ep_inc_enq(dep);
@@ -1261,50 +1323,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
        trace_dwc3_prepare_trb(dep, trb);
 }
 
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- * @trb_length: buffer size of the TRB
- * @chain: should this TRB be chained to the next?
- * @node: only for isochronous endpoints. First TRB needs different type.
- * @use_bounce_buffer: set to use bounce buffer
- * @must_interrupt: set to interrupt on TRB completion
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
-               struct dwc3_request *req, unsigned int trb_length,
-               unsigned int chain, unsigned int node, bool use_bounce_buffer,
-               bool must_interrupt)
-{
-       struct dwc3_trb         *trb;
-       dma_addr_t              dma;
-       unsigned int            stream_id = req->request.stream_id;
-       unsigned int            short_not_ok = req->request.short_not_ok;
-       unsigned int            no_interrupt = req->request.no_interrupt;
-       unsigned int            is_last = req->request.is_last;
-
-       if (use_bounce_buffer)
-               dma = dep->dwc->bounce_addr;
-       else if (req->request.num_sgs > 0)
-               dma = sg_dma_address(req->start_sg);
-       else
-               dma = req->request.dma;
-
-       trb = &dep->trb_pool[dep->trb_enqueue];
-
-       if (!req->trb) {
-               dwc3_gadget_move_started_request(req);
-               req->trb = trb;
-               req->trb_dma = dwc3_trb_dma_offset(dep, trb);
-       }
-
-       req->num_trbs++;
-
-       __dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
-                       stream_id, short_not_ok, no_interrupt, is_last,
-                       must_interrupt);
-}
-
 static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
 {
        unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
@@ -1925,10 +1943,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
 static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
-       struct dwc3_request             *tmp;
        struct dwc3                     *dwc = dep->dwc;
 
-       list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+       while (!list_empty(&dep->cancelled_list)) {
+               req = next_request(&dep->cancelled_list);
                dwc3_gadget_ep_skip_trbs(dep, req);
                switch (req->status) {
                case DWC3_REQUEST_STATUS_DISCONNECTED:
@@ -1945,6 +1963,12 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
                        dwc3_gadget_giveback(dep, req, -ECONNRESET);
                        break;
                }
+               /*
+                * The endpoint is disabled, let the dwc3_remove_requests()
+                * handle the cleanup.
+                */
+               if (!dep->endpoint.desc)
+                       break;
        }
 }
 
@@ -3169,6 +3193,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event,
                struct dwc3_request *req, int status)
 {
+       int request_status;
        int ret;
 
        if (req->request.num_mapped_sgs)
@@ -3189,7 +3214,35 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
                req->needs_extra_trb = false;
        }
 
-       dwc3_gadget_giveback(dep, req, status);
+       /*
+        * The event status only reflects the status of the TRB with IOC set.
+        * For the requests that don't set interrupt on completion, the driver
+        * needs to check and return the status of the completed TRBs associated
+        * with the request. Use the status of the last TRB of the request.
+        */
+       if (req->request.no_interrupt) {
+               struct dwc3_trb *trb;
+
+               trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue);
+               switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) {
+               case DWC3_TRBSTS_MISSED_ISOC:
+                       /* Isoc endpoint only */
+                       request_status = -EXDEV;
+                       break;
+               case DWC3_TRB_STS_XFER_IN_PROG:
+                       /* Applicable when End Transfer with ForceRM=0 */
+               case DWC3_TRBSTS_SETUP_PENDING:
+                       /* Control endpoint only */
+               case DWC3_TRBSTS_OK:
+               default:
+                       request_status = 0;
+                       break;
+               }
+       } else {
+               request_status = status;
+       }
+
+       dwc3_gadget_giveback(dep, req, request_status);
 
 out:
        return ret;
@@ -3199,15 +3252,21 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event, int status)
 {
        struct dwc3_request     *req;
-       struct dwc3_request     *tmp;
 
-       list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+       while (!list_empty(&dep->started_list)) {
                int ret;
 
+               req = next_request(&dep->started_list);
                ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
                                req, status);
                if (ret)
                        break;
+               /*
+                * The endpoint is disabled, let the dwc3_remove_requests()
+                * handle the cleanup.
+                */
+               if (!dep->endpoint.desc)
+                       break;
        }
 }
 
@@ -3251,6 +3310,9 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
        if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
                goto out;
 
+       if (!dep->endpoint.desc)
+               return no_started_trb;
+
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                list_empty(&dep->started_list) &&
                (list_empty(&dep->pending_list) || status == -EXDEV))
@@ -3293,6 +3355,9 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
 {
        int status = 0;
 
+       if (!dep->endpoint.desc)
+               return;
+
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
                dwc3_gadget_endpoint_frame_from_event(dep, event);
 
@@ -3346,6 +3411,14 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
        if (cmd != DWC3_DEPCMD_ENDTRANSFER)
                return;
 
+       /*
+        * The END_TRANSFER command will cause the controller to generate a
+        * NoStream Event, and it's not due to the host DP NoStream rejection.
+        * Ignore the next NoStream event.
+        */
+       if (dep->stream_capable)
+               dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
        dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
        dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
        dwc3_gadget_ep_cleanup_cancelled_requests(dep);
@@ -3568,14 +3641,6 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
        WARN_ON_ONCE(ret);
        dep->resource_index = 0;
 
-       /*
-        * The END_TRANSFER command will cause the controller to generate a
-        * NoStream Event, and it's not due to the host DP NoStream rejection.
-        * Ignore the next NoStream event.
-        */
-       if (dep->stream_capable)
-               dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
-
        if (!interrupt)
                dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
        else
@@ -4072,7 +4137,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
        }
 
        evt->count = 0;
-       evt->flags &= ~DWC3_EVENT_PENDING;
        ret = IRQ_HANDLED;
 
        /* Unmask interrupt */
@@ -4085,6 +4149,9 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
                dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
        }
 
+       /* Keep the clearing of DWC3_EVENT_PENDING at the end */
+       evt->flags &= ~DWC3_EVENT_PENDING;
+
        return ret;
 }
 
@@ -4095,9 +4162,11 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
        unsigned long flags;
        irqreturn_t ret = IRQ_NONE;
 
+       local_bh_disable();
        spin_lock_irqsave(&dwc->lock, flags);
        ret = dwc3_process_event_buf(evt);
        spin_unlock_irqrestore(&dwc->lock, flags);
+       local_bh_enable();
 
        return ret;
 }