]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
usb: xhci-mtk: improve bandwidth scheduling with TT
authorChunfeng Yun <chunfeng.yun@mediatek.com>
Mon, 8 Mar 2021 02:51:51 +0000 (10:51 +0800)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Wed, 26 May 2021 13:39:03 +0000 (15:39 +0200)
BugLink: https://bugs.launchpad.net/bugs/1929615
commit e19ee44a3d07c232f9241024dab1ebd0748cdf5f upstream.

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 <stable@vger.kernel.org>
Signed-off-by: Yaqii Wu <yaqii.wu@mediatek.com>
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/2f30e81400a59afef5f8231c98149169c7520519.1615170625.git.chunfeng.yun@mediatek.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
drivers/usb/host/xhci-mtk-sch.c
drivers/usb/host/xhci-mtk.h

index 5891f56c64da357df21fcd29ab98fb1be052e229..8950d1f10a7fb82d866f2d96d0f1a3f67ff1caf5 100644 (file)
@@ -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);
 }
 
index d9f438d078daf45beeb95cb21df5addb388836fa..985e7a19f6f6c92657aca4dc1bbde33d5ff77010 100644 (file)
 #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;