When the USB headset is plug into an external hub, sometimes
can't set config due to not enough bandwidth, so need improve
LS/FS INT/ISOC bandwidth scheduling with TT.

Fixes: 54f6a8af3722 ("usb: xhci-mtk: skip dropping bandwidth of unchecked 
endpoints")
Cc: stable <sta...@vger.kernel.org>
Signed-off-by: Yaqii Wu <yaqii...@mediatek.com>
Signed-off-by: Chunfeng Yun <chunfeng....@mediatek.com>
---
v2: no changes
---
 drivers/usb/host/xhci-mtk-sch.c | 74 ++++++++++++++++++++++++++-------
 drivers/usb/host/xhci-mtk.h     |  6 ++-
 2 files changed, 64 insertions(+), 16 deletions(-)

diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index 5891f56c64da..8950d1f10a7f 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -378,6 +378,31 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
        sch_ep->allocated = used;
 }
 
+static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
+{
+       struct mu3h_sch_tt *tt = sch_ep->sch_tt;
+       u32 num_esit, tmp;
+       int base;
+       int i, j;
+
+       num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
+       for (i = 0; i < num_esit; i++) {
+               base = offset + i * sch_ep->esit;
+
+               /*
+                * Compared with hs bus, no matter what ep type,
+                * the hub will always delay one uframe to send data
+                */
+               for (j = 0; j < sch_ep->cs_count; j++) {
+                       tmp = tt->fs_bus_bw[base + j] + 
sch_ep->bw_cost_per_microframe;
+                       if (tmp > FS_PAYLOAD_MAX)
+                               return -ERANGE;
+               }
+       }
+
+       return 0;
+}
+
 static int check_sch_tt(struct usb_device *udev,
        struct mu3h_sch_ep_info *sch_ep, u32 offset)
 {
@@ -402,7 +427,7 @@ static int check_sch_tt(struct usb_device *udev,
                        return -ERANGE;
 
                for (i = 0; i < sch_ep->cs_count; i++)
-                       if (test_bit(offset + i, tt->split_bit_map))
+                       if (test_bit(offset + i, tt->ss_bit_map))
                                return -ERANGE;
 
        } else {
@@ -432,7 +457,7 @@ static int check_sch_tt(struct usb_device *udev,
                        cs_count = 7; /* HW limit */
 
                for (i = 0; i < cs_count + 2; i++) {
-                       if (test_bit(offset + i, tt->split_bit_map))
+                       if (test_bit(offset + i, tt->ss_bit_map))
                                return -ERANGE;
                }
 
@@ -448,24 +473,44 @@ static int check_sch_tt(struct usb_device *udev,
                        sch_ep->num_budget_microframes = sch_ep->esit;
        }
 
-       return 0;
+       return check_fs_bus_bw(sch_ep, offset);
 }
 
 static void update_sch_tt(struct usb_device *udev,
-       struct mu3h_sch_ep_info *sch_ep)
+       struct mu3h_sch_ep_info *sch_ep, bool used)
 {
        struct mu3h_sch_tt *tt = sch_ep->sch_tt;
        u32 base, num_esit;
+       int bw_updated;
+       int bits;
        int i, j;
 
        num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
+       bits = (sch_ep->ep_type == ISOC_OUT_EP) ? sch_ep->cs_count : 1;
+
+       if (used)
+               bw_updated = sch_ep->bw_cost_per_microframe;
+       else
+               bw_updated = -sch_ep->bw_cost_per_microframe;
+
        for (i = 0; i < num_esit; i++) {
                base = sch_ep->offset + i * sch_ep->esit;
-               for (j = 0; j < sch_ep->num_budget_microframes; j++)
-                       set_bit(base + j, tt->split_bit_map);
+
+               for (j = 0; j < bits; j++) {
+                       if (used)
+                               set_bit(base + j, tt->ss_bit_map);
+                       else
+                               clear_bit(base + j, tt->ss_bit_map);
+               }
+
+               for (j = 0; j < sch_ep->cs_count; j++)
+                       tt->fs_bus_bw[base + j] += bw_updated;
        }
 
-       list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
+       if (used)
+               list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
+       else
+               list_del(&sch_ep->tt_endpoint);
 }
 
 static int check_sch_bw(struct usb_device *udev,
@@ -535,7 +580,7 @@ static int check_sch_bw(struct usb_device *udev,
                if (!tt_offset_ok)
                        return -ERANGE;
 
-               update_sch_tt(udev, sch_ep);
+               update_sch_tt(udev, sch_ep, 1);
        }
 
        /* update bus bandwidth info */
@@ -548,15 +593,16 @@ static void destroy_sch_ep(struct usb_device *udev,
        struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
 {
        /* only release ep bw check passed by check_sch_bw() */
-       if (sch_ep->allocated)
+       if (sch_ep->allocated) {
                update_bus_bw(sch_bw, sch_ep, 0);
+               if (sch_ep->sch_tt)
+                       update_sch_tt(udev, sch_ep, 0);
+       }
 
-       list_del(&sch_ep->endpoint);
-
-       if (sch_ep->sch_tt) {
-               list_del(&sch_ep->tt_endpoint);
+       if (sch_ep->sch_tt)
                drop_tt(udev);
-       }
+
+       list_del(&sch_ep->endpoint);
        kfree(sch_ep);
 }
 
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index cbb09dfea62e..f42769c69249 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -20,13 +20,15 @@
 #define XHCI_MTK_MAX_ESIT      64
 
 /**
- * @split_bit_map: used to avoid split microframes overlay
+ * @ss_bit_map: used to avoid start split microframes overlay
+ * @fs_bus_bw: array to keep track of bandwidth already used for FS
  * @ep_list: Endpoints using this TT
  * @usb_tt: usb TT related
  * @tt_port: TT port number
  */
 struct mu3h_sch_tt {
-       DECLARE_BITMAP(split_bit_map, XHCI_MTK_MAX_ESIT);
+       DECLARE_BITMAP(ss_bit_map, XHCI_MTK_MAX_ESIT);
+       u32 fs_bus_bw[XHCI_MTK_MAX_ESIT];
        struct list_head ep_list;
        struct usb_tt *usb_tt;
        int tt_port;
-- 
2.18.0

Reply via email to