USB: gadget: tegra: SW WAR for dTD issue
Krishna Yarlagadda [Mon, 18 Jun 2012 15:09:34 +0000 (20:09 +0530)]
SW WAR implementation for h/w issue observed on all tegra platforms
Adding a dTD to a Primed Endpoint May Not Get Recognized

TD freeing will be delayed until next TD is completed

Bug 1002166

Signed-off-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Reviewed-on: http://git-master/r/109562
(cherry picked from commit 8603480606af20444ed91e3010a22cc02edacb78)

Change-Id: I875d06eb2db78a18858590645df631478f3201bb
Reviewed-on: http://git-master/r/116972
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Tested-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

drivers/usb/gadget/tegra_udc.c
drivers/usb/gadget/tegra_udc.h

index 47e2e97..db5b728 100644 (file)
@@ -129,8 +129,9 @@ static void done(struct tegra_ep *ep, struct tegra_req *req, int status)
 {
        struct tegra_udc *udc = NULL;
        unsigned char stopped = ep->stopped;
-       struct ep_td_struct *curr_td, *next_td;
+       struct ep_td_struct *curr_td, *next_td = 0;
        int j;
+       int count;
        BUG_ON(!(in_irq() || irqs_disabled()));
        udc = (struct tegra_udc *)ep->udc;
        /* Removed the req from tegra_ep->queue */
@@ -143,12 +144,19 @@ static void done(struct tegra_ep *ep, struct tegra_req *req, int status)
                status = req->req.status;
 
        /* Free dtd for the request */
-       next_td = req->head;
-       for (j = 0; j < req->dtd_count; j++) {
+       count = 0;
+       if (ep->last_td) {
+               next_td = ep->last_td;
+               count = ep->last_dtd_count;
+       }
+       ep->last_td = req->head;
+       ep->last_dtd_count = req->dtd_count;
+
+       for (j = 0; j < count; j++) {
                curr_td = next_td;
-               if (j != req->dtd_count - 1)
+               if (j != count - 1) {
                        next_td = curr_td->next_td_virt;
-
+               }
                dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
        }
 
@@ -568,6 +576,8 @@ static int tegra_ep_enable(struct usb_ep *_ep,
        ep->ep.maxpacket = max;
        ep->desc = desc;
        ep->stopped = 0;
+       ep->last_td = 0;
+       ep->last_dtd_count = 0;
 
        /* Controller related setup
         * Init EPx Queue Head (Ep Capabilites field in QH
@@ -610,6 +620,8 @@ static int tegra_ep_disable(struct usb_ep *_ep)
        unsigned long flags = 0;
        u32 epctrl;
        int ep_num;
+       struct ep_td_struct *curr_td, *next_td;
+       int j;
 
        ep = container_of(_ep, struct tegra_ep, ep);
        if (!_ep || !ep->desc) {
@@ -638,6 +650,18 @@ static int tegra_ep_disable(struct usb_ep *_ep)
 
        ep->desc = NULL;
        ep->stopped = 1;
+       if (ep->last_td) {
+               next_td = ep->last_td;
+               for (j = 0; j < ep->last_dtd_count; j++) {
+                       curr_td = next_td;
+                       dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+                       if (j != ep->last_dtd_count - 1) {
+                               next_td = curr_td->next_td_virt;
+                       }
+               }
+       }
+       ep->last_td =0;
+       ep->last_dtd_count = 0;
        spin_unlock_irqrestore(&udc->lock, flags);
 
        VDBG("disabled %s OK", _ep->name);
index e94543f..50e2a31 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 NVIDIA Corporation
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
  *
  * Description:
  * High-speed USB device controller driver.
@@ -390,6 +390,8 @@ struct tegra_ep {
        struct ep_queue_head *qh;
        const struct usb_endpoint_descriptor *desc;
        struct usb_gadget *gadget;
+       struct ep_td_struct *last_td;
+       int last_dtd_count;
 
        char name[14];
        unsigned stopped:1;