]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/dwc3/gadget.c
staging: comedi: rtd520: rename CamelCase rtdBoard
[karo-tx-linux.git] / drivers / usb / dwc3 / gadget.c
index 2fdd767f8fe890defe831c958d1d723af4b060ae..82e160e96fcafd20f15c4443cba953d569c071f1 100644 (file)
@@ -241,21 +241,23 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
                int status)
 {
        struct dwc3                     *dwc = dep->dwc;
+       int                             i;
 
        if (req->queued) {
-               if (req->request.num_mapped_sgs)
-                       dep->busy_slot += req->request.num_mapped_sgs;
-               else
+               i = 0;
+               do {
                        dep->busy_slot++;
-
-               /*
-                * Skip LINK TRB. We can't use req->trb and check for
-                * DWC3_TRBCTL_LINK_TRB because it points the TRB we just
-                * completed (not the LINK TRB).
-                */
-               if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+                       /*
+                        * Skip LINK TRB. We can't use req->trb and check for
+                        * DWC3_TRBCTL_LINK_TRB because it points the TRB we
+                        * just completed (not the LINK TRB).
+                        */
+                       if (((dep->busy_slot & DWC3_TRB_MASK) ==
+                               DWC3_TRB_NUM- 1) &&
                                usb_endpoint_xfer_isoc(dep->endpoint.desc))
-                       dep->busy_slot++;
+                               dep->busy_slot++;
+               } while(++i < req->request.num_mapped_sgs);
+               req->queued = false;
        }
        list_del(&req->list);
        req->trb = NULL;
@@ -749,33 +751,32 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
  */
 static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
                struct dwc3_request *req, dma_addr_t dma,
-               unsigned length, unsigned last, unsigned chain)
+               unsigned length, unsigned last, unsigned chain, unsigned node)
 {
        struct dwc3             *dwc = dep->dwc;
        struct dwc3_trb         *trb;
 
-       unsigned int            cur_slot;
-
        dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
                        dep->name, req, (unsigned long long) dma,
                        length, last ? " last" : "",
                        chain ? " chain" : "");
 
-       trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
-       cur_slot = dep->free_slot;
-       dep->free_slot++;
-
        /* Skip the LINK-TRB on ISOC */
-       if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+       if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
                        usb_endpoint_xfer_isoc(dep->endpoint.desc))
-               return;
+               dep->free_slot++;
+
+       trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
 
        if (!req->trb) {
                dwc3_gadget_move_request_queued(req);
                req->trb = trb;
                req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+               req->start_slot = dep->free_slot & DWC3_TRB_MASK;
        }
 
+       dep->free_slot++;
+
        trb->size = DWC3_TRB_SIZE_LENGTH(length);
        trb->bpl = lower_32_bits(dma);
        trb->bph = upper_32_bits(dma);
@@ -786,9 +787,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
                break;
 
        case USB_ENDPOINT_XFER_ISOC:
-               trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+               if (!node)
+                       trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+               else
+                       trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
 
-               if (!req->request.no_interrupt)
+               if (!req->request.no_interrupt && !chain)
                        trb->ctrl |= DWC3_TRB_CTRL_IOC;
                break;
 
@@ -807,14 +811,13 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
                trb->ctrl |= DWC3_TRB_CTRL_CSP;
-       } else {
-               if (chain)
-                       trb->ctrl |= DWC3_TRB_CTRL_CHN;
-
-               if (last)
-                       trb->ctrl |= DWC3_TRB_CTRL_LST;
+       } else if (last) {
+               trb->ctrl |= DWC3_TRB_CTRL_LST;
        }
 
+       if (chain)
+               trb->ctrl |= DWC3_TRB_CTRL_CHN;
+
        if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
                trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
 
@@ -885,6 +888,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
        list_for_each_entry_safe(req, n, &dep->request_list, list) {
                unsigned        length;
                dma_addr_t      dma;
+               last_one = false;
 
                if (req->request.num_mapped_sgs > 0) {
                        struct usb_request *request = &req->request;
@@ -900,7 +904,9 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
 
                                if (i == (request->num_mapped_sgs - 1) ||
                                                sg_is_last(s)) {
-                                       last_one = true;
+                                       if (list_is_last(&req->list,
+                                                       &dep->request_list))
+                                               last_one = true;
                                        chain = false;
                                }
 
@@ -912,7 +918,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
                                        chain = false;
 
                                dwc3_prepare_one_trb(dep, req, dma, length,
-                                               last_one, chain);
+                                               last_one, chain, i);
 
                                if (last_one)
                                        break;
@@ -930,7 +936,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
                                last_one = 1;
 
                        dwc3_prepare_one_trb(dep, req, dma, length,
-                                       last_one, false);
+                                       last_one, false, 0);
 
                        if (last_one)
                                break;
@@ -977,13 +983,14 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
        }
 
        memset(&params, 0, sizeof(params));
-       params.param0 = upper_32_bits(req->trb_dma);
-       params.param1 = lower_32_bits(req->trb_dma);
 
-       if (start_new)
+       if (start_new) {
+               params.param0 = upper_32_bits(req->trb_dma);
+               params.param1 = lower_32_bits(req->trb_dma);
                cmd = DWC3_DEPCMD_STARTTRANSFER;
-       else
+       } else {
                cmd = DWC3_DEPCMD_UPDATETRANSFER;
+       }
 
        cmd |= DWC3_DEPCMD_PARAM(cmd_param);
        ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
@@ -1082,8 +1089,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
         *
         */
        if (dep->flags & DWC3_EP_PENDING_REQUEST) {
-               int     ret;
-
                /*
                 * If xfernotready is already elapsed and it is a case
                 * of isoc transfer, then issue END TRANSFER, so that
@@ -1091,7 +1096,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
                 * notion of current microframe.
                 */
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
-                       dwc3_stop_active_transfer(dwc, dep->number);
+                       if (list_empty(&dep->req_queued)) {
+                               dwc3_stop_active_transfer(dwc, dep->number);
+                               dep->flags = DWC3_EP_ENABLED;
+                       }
                        return 0;
                }
 
@@ -1099,6 +1107,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
                if (ret && ret != -EBUSY)
                        dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
                                        dep->name);
+               return ret;
        }
 
        /*
@@ -1115,16 +1124,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
                if (ret && ret != -EBUSY)
                        dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
                                        dep->name);
-       }
-
-       /*
-        * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
-        * uframe number.
-        */
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
-               (dep->flags & DWC3_EP_MISSED_ISOC)) {
-                       __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
-                       dep->flags &= ~DWC3_EP_MISSED_ISOC;
+               return ret;
        }
 
        return 0;
@@ -1652,76 +1652,134 @@ static void dwc3_gadget_release(struct device *dev)
 }
 
 /* -------------------------------------------------------------------------- */
-static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
+static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
+               struct dwc3_request *req, struct dwc3_trb *trb,
                const struct dwc3_event_depevt *event, int status)
 {
-       struct dwc3_request     *req;
-       struct dwc3_trb         *trb;
        unsigned int            count;
        unsigned int            s_pkt = 0;
        unsigned int            trb_status;
 
+       if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+               /*
+                * We continue despite the error. There is not much we
+                * can do. If we don't clean it up we loop forever. If
+                * we skip the TRB then it gets overwritten after a
+                * while since we use them in a ring buffer. A BUG()
+                * would help. Lets hope that if this occurs, someone
+                * fixes the root cause instead of looking away :)
+                */
+               dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
+                               dep->name, trb);
+       count = trb->size & DWC3_TRB_SIZE_MASK;
+
+       if (dep->direction) {
+               if (count) {
+                       trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+                       if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+                               dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+                                               dep->name);
+                               /*
+                                * If missed isoc occurred and there is
+                                * no request queued then issue END
+                                * TRANSFER, so that core generates
+                                * next xfernotready and we will issue
+                                * a fresh START TRANSFER.
+                                * If there are still queued request
+                                * then wait, do not issue either END
+                                * or UPDATE TRANSFER, just attach next
+                                * request in request_list during
+                                * giveback.If any future queued request
+                                * is successfully transferred then we
+                                * will issue UPDATE TRANSFER for all
+                                * request in the request_list.
+                                */
+                               dep->flags |= DWC3_EP_MISSED_ISOC;
+                       } else {
+                               dev_err(dwc->dev, "incomplete IN transfer %s\n",
+                                               dep->name);
+                               status = -ECONNRESET;
+                       }
+               } else {
+                       dep->flags &= ~DWC3_EP_MISSED_ISOC;
+               }
+       } else {
+               if (count && (event->status & DEPEVT_STATUS_SHORT))
+                       s_pkt = 1;
+       }
+
+       /*
+        * We assume here we will always receive the entire data block
+        * which we should receive. Meaning, if we program RX to
+        * receive 4K but we receive only 2K, we assume that's all we
+        * should receive and we simply bounce the request back to the
+        * gadget driver for further processing.
+        */
+       req->request.actual += req->request.length - count;
+       if (s_pkt)
+               return 1;
+       if ((event->status & DEPEVT_STATUS_LST) &&
+                       (trb->ctrl & (DWC3_TRB_CTRL_LST |
+                               DWC3_TRB_CTRL_HWO)))
+               return 1;
+       if ((event->status & DEPEVT_STATUS_IOC) &&
+                       (trb->ctrl & DWC3_TRB_CTRL_IOC))
+               return 1;
+       return 0;
+}
+
+static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
+               const struct dwc3_event_depevt *event, int status)
+{
+       struct dwc3_request     *req;
+       struct dwc3_trb         *trb;
+       unsigned int            slot;
+       unsigned int            i;
+       int                     ret;
+
        do {
                req = next_request(&dep->req_queued);
                if (!req) {
                        WARN_ON_ONCE(1);
                        return 1;
                }
+               i = 0;
+               do {
+                       slot = req->start_slot + i;
+                       if ((slot == DWC3_TRB_NUM - 1) &&
+                               usb_endpoint_xfer_isoc(dep->endpoint.desc))
+                               slot++;
+                       slot %= DWC3_TRB_NUM;
+                       trb = &dep->trb_pool[slot];
+
+                       ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+                                       event, status);
+                       if (ret)
+                               break;
+               }while (++i < req->request.num_mapped_sgs);
 
-               trb = req->trb;
+               dwc3_gadget_giveback(dep, req, status);
+
+               if (ret)
+                       break;
+       } while (1);
 
-               if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+                       list_empty(&dep->req_queued)) {
+               if (list_empty(&dep->request_list)) {
                        /*
-                        * We continue despite the error. There is not much we
-                        * can do. If we don't clean it up we loop forever. If
-                        * we skip the TRB then it gets overwritten after a
-                        * while since we use them in a ring buffer. A BUG()
-                        * would help. Lets hope that if this occurs, someone
-                        * fixes the root cause instead of looking away :)
+                        * If there is no entry in request list then do
+                        * not issue END TRANSFER now. Just set PENDING
+                        * flag, so that END TRANSFER is issued when an
+                        * entry is added into request list.
                         */
-                       dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
-                                       dep->name, req->trb);
-               count = trb->size & DWC3_TRB_SIZE_MASK;
-
-               if (dep->direction) {
-                       if (count) {
-                               trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
-                               if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
-                                       dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
-                                                       dep->name);
-                                       dep->current_uf = event->parameters &
-                                               ~(dep->interval - 1);
-                                       dep->flags |= DWC3_EP_MISSED_ISOC;
-                               } else {
-                                       dev_err(dwc->dev, "incomplete IN transfer %s\n",
-                                                       dep->name);
-                                       status = -ECONNRESET;
-                               }
-                       }
+                       dep->flags = DWC3_EP_PENDING_REQUEST;
                } else {
-                       if (count && (event->status & DEPEVT_STATUS_SHORT))
-                               s_pkt = 1;
+                       dwc3_stop_active_transfer(dwc, dep->number);
+                       dep->flags = DWC3_EP_ENABLED;
                }
-
-               /*
-                * We assume here we will always receive the entire data block
-                * which we should receive. Meaning, if we program RX to
-                * receive 4K but we receive only 2K, we assume that's all we
-                * should receive and we simply bounce the request back to the
-                * gadget driver for further processing.
-                */
-               req->request.actual += req->request.length - count;
-               dwc3_gadget_giveback(dep, req, status);
-               if (s_pkt)
-                       break;
-               if ((event->status & DEPEVT_STATUS_LST) &&
-                               (trb->ctrl & (DWC3_TRB_CTRL_LST |
-                                               DWC3_TRB_CTRL_HWO)))
-                       break;
-               if ((event->status & DEPEVT_STATUS_IOC) &&
-                               (trb->ctrl & DWC3_TRB_CTRL_IOC))
-                       break;
-       } while (1);
+               return 1;
+       }
 
        if ((event->status & DEPEVT_STATUS_IOC) &&
                        (trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -2101,7 +2159,6 @@ static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
 
 static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 {
-       struct dwc3_gadget_ep_cmd_params params;
        struct dwc3_ep          *dep;
        int                     ret;
        u32                     reg;
@@ -2109,8 +2166,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 
        dev_vdbg(dwc->dev, "%s\n", __func__);
 
-       memset(&params, 0x00, sizeof(params));
-
        reg = dwc3_readl(dwc->regs, DWC3_DSTS);
        speed = reg & DWC3_DSTS_CONNECTSPD;
        dwc->speed = speed;
@@ -2157,6 +2212,26 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                break;
        }
 
+       /* Enable USB2 LPM Capability */
+
+       if ((dwc->revision > DWC3_REVISION_194A)
+                       && (speed != DWC3_DCFG_SUPERSPEED)) {
+               reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+               reg |= DWC3_DCFG_LPM_CAP;
+               dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+               /*
+                * TODO: This should be configurable. For now using
+                * maximum allowed HIRD threshold value of 0b1100
+                */
+               reg |= DWC3_DCTL_HIRD_THRES(12);
+
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+       }
+
        /* Recent versions support automatic phy suspend and don't need this */
        if (dwc->revision < DWC3_REVISION_194A) {
                /* Suspend unneeded PHY */
@@ -2463,20 +2538,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
                        DWC3_DEVTEN_DISCONNEVTEN);
        dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
 
-       /* Enable USB2 LPM and automatic phy suspend only on recent versions */
+       /* automatic phy suspend only on recent versions */
        if (dwc->revision >= DWC3_REVISION_194A) {
-               reg = dwc3_readl(dwc->regs, DWC3_DCFG);
-               reg |= DWC3_DCFG_LPM_CAP;
-               dwc3_writel(dwc->regs, DWC3_DCFG, reg);
-
-               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
-               reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
-
-               /* TODO: This should be configurable */
-               reg |= DWC3_DCTL_HIRD_THRES(28);
-
-               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
                dwc3_gadget_usb2_phy_suspend(dwc, false);
                dwc3_gadget_usb3_phy_suspend(dwc, false);
        }