interval /= 1000;
        }
 
-       snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s\n",
+       snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s",
                 usb_speed_string(speed), usb_endpoint_num(epd),
                 usb_endpoint_dir_in(epd) ? "in" : "out",
                 usb_ep_type_string(usb_endpoint_type(epd)),
        int bw_index;
 
        virt_dev = xhci->devs[udev->slot_id];
+       if (WARN_ONCE(!virt_dev, "xhci-mtk: usb %s has no virt_dev\n",
+                               dev_name(&udev->dev)))
+               return NULL;
+       if (WARN_ONCE(!virt_dev->real_port, "xhci-mtk: usb %s has invalid port number\n",
+                       dev_name(&udev->dev)))
+               return NULL;
 
        if (udev->speed >= USB_SPEED_SUPER) {
                if (usb_endpoint_dir_out(&ep->desc))
                        }
                        return ERR_PTR(-ENOMEM);
                }
-               INIT_LIST_HEAD(&tt->ep_list);
                *ptt = tt;
        }
 
        }
 
        tt = *ptt;
-       if (!tt || !list_empty(&tt->ep_list))
+       if (!tt || tt->nr_eps > 0)
                return;         /* never allocated , or still in use*/
 
        *ptt = NULL;
        }
 }
 
-static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
-       struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx)
+static struct mu3h_sch_ep_info *create_sch_ep(struct xhci_hcd_mtk *mtk,
+               struct usb_device *udev, struct usb_host_endpoint *ep,
+               struct xhci_ep_ctx *ep_ctx)
 {
        struct mu3h_sch_ep_info *sch_ep;
        struct mu3h_sch_tt *tt = NULL;
        u32 len_bw_budget_table;
        size_t mem_size;
+       struct mu3h_sch_bw_info *bw_info;
+
+       bw_info = get_bw_info(mtk, udev, ep);
+       if (!bw_info)
+               return ERR_PTR(-ENODEV);
 
        if (is_fs_or_ls(udev->speed))
                len_bw_budget_table = TT_MICROFRAMES_MAX;
                }
        }
 
+       sch_ep->bw_info = bw_info;
        sch_ep->sch_tt = tt;
        sch_ep->ep = ep;
        sch_ep->speed = udev->speed;
-       INIT_LIST_HEAD(&sch_ep->endpoint);
-       INIT_LIST_HEAD(&sch_ep->tt_endpoint);
 
        return sch_ep;
 }
        }
 
        if (used)
-               list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
-       else
-               list_del(&sch_ep->tt_endpoint);
+               tt->nr_eps++;
+       else if (!WARN_ONCE(tt->nr_eps < 1, "unbalanced sch_tt's ep count"))
+               tt->nr_eps--;
 }
 
 static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw,
        return boundary;
 }
 
-static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw,
-                       struct mu3h_sch_ep_info *sch_ep)
+static int check_sch_bw(struct mu3h_sch_ep_info *sch_ep)
 {
+       struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info;
        const u32 esit_boundary = get_esit_boundary(sch_ep);
        const u32 bw_boundary = get_bw_boundary(sch_ep->speed);
        u32 offset;
        return load_ep_bw(sch_bw, sch_ep, true);
 }
 
-static void destroy_sch_ep(struct usb_device *udev,
-       struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
+static const struct rhashtable_params sch_ep_table_param = {
+       .key_len        = sizeof(struct usb_host_endpoint *),
+       .key_offset     = offsetof(struct mu3h_sch_ep_info, ep),
+       .head_offset    = offsetof(struct mu3h_sch_ep_info, ep_link),
+};
+
+static void destroy_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
+                          struct mu3h_sch_ep_info *sch_ep)
 {
        /* only release ep bw check passed by check_sch_bw() */
        if (sch_ep->allocated)
-               load_ep_bw(sch_bw, sch_ep, false);
+               load_ep_bw(sch_ep->bw_info, sch_ep, false);
 
        if (sch_ep->sch_tt)
                drop_tt(udev);
 
        list_del(&sch_ep->endpoint);
+       rhashtable_remove_fast(&mtk->sch_ep_table, &sch_ep->ep_link,
+                               sch_ep_table_param);
        kfree(sch_ep);
 }
 
-static bool need_bw_sch(struct usb_host_endpoint *ep,
-       enum usb_device_speed speed, int has_tt)
+static bool need_bw_sch(struct usb_device *udev,
+                       struct usb_host_endpoint *ep)
 {
+       bool has_tt = udev->tt && udev->tt->hub->parent;
+
        /* only for periodic endpoints */
        if (usb_endpoint_xfer_control(&ep->desc)
                || usb_endpoint_xfer_bulk(&ep->desc))
         * a TT are also ignored, root-hub will schedule them directly,
         * but need set @bpkts field of endpoint context to 1.
         */
-       if (is_fs_or_ls(speed) && !has_tt)
+       if (is_fs_or_ls(udev->speed) && !has_tt)
                return false;
 
        /* skip endpoint with zero maxpkt */
        struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
        struct mu3h_sch_bw_info *sch_array;
        int num_usb_bus;
-       int i;
+       int ret;
+
+       /* mu3h_sch_ep_info table, 'usb_host_endpoint*' as a key */
+       ret = rhashtable_init(&mtk->sch_ep_table, &sch_ep_table_param);
+       if (ret)
+               return ret;
 
        /* ss IN and OUT are separated */
        num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports;
        if (sch_array == NULL)
                return -ENOMEM;
 
-       for (i = 0; i < num_usb_bus; i++)
-               INIT_LIST_HEAD(&sch_array[i].bw_ep_list);
-
        mtk->sch_array = sch_array;
 
        INIT_LIST_HEAD(&mtk->bw_ep_chk_list);
 
 void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk)
 {
+       rhashtable_destroy(&mtk->sch_ep_table);
        kfree(mtk->sch_array);
 }
 
        ep_index = xhci_get_endpoint_index(&ep->desc);
        ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
 
-       xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
-
-       if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) {
+       if (!need_bw_sch(udev, ep)) {
                /*
                 * set @bpkts to 1 if it is LS or FS periodic endpoint, and its
                 * device does not connected through an external HS hub
                return 0;
        }
 
-       sch_ep = create_sch_ep(udev, ep, ep_ctx);
+       xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
+
+       sch_ep = create_sch_ep(mtk, udev, ep, ep_ctx);
        if (IS_ERR_OR_NULL(sch_ep))
                return -ENOMEM;
 
+       if (rhashtable_insert_fast(&mtk->sch_ep_table, &sch_ep->ep_link,
+                                  sch_ep_table_param))
+               return -EEXIST;
+
        setup_sch_info(ep_ctx, sch_ep);
 
        list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_chk_list);
 
+       xhci_dbg(xhci, "added sch_ep %p : %p\n", sch_ep, ep);
+
        return 0;
 }
 
 {
        struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       struct xhci_virt_device *virt_dev;
-       struct mu3h_sch_bw_info *sch_bw;
-       struct mu3h_sch_ep_info *sch_ep, *tmp;
-
-       virt_dev = xhci->devs[udev->slot_id];
-
-       xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
+       struct mu3h_sch_ep_info *sch_ep;
+       void *key = ep;
 
-       if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info))
+       if (!need_bw_sch(udev, ep))
                return;
 
-       sch_bw = get_bw_info(mtk, udev, ep);
+       xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
 
-       list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) {
-               if (sch_ep->ep == ep) {
-                       destroy_sch_ep(udev, sch_bw, sch_ep);
-                       break;
-               }
-       }
+       sch_ep = rhashtable_lookup_fast(&mtk->sch_ep_table, &key,
+                                       sch_ep_table_param);
+       if (sch_ep)
+               destroy_sch_ep(mtk, udev, sch_ep);
+       else
+               xhci_dbg(xhci, "ep %p is not on the bw table\n", ep);
 }
 
 int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
        struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
-       struct mu3h_sch_bw_info *sch_bw;
-       struct mu3h_sch_ep_info *sch_ep, *tmp;
+       struct mu3h_sch_ep_info *sch_ep;
        int ret;
 
        xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev));
 
        list_for_each_entry(sch_ep, &mtk->bw_ep_chk_list, endpoint) {
-               sch_bw = get_bw_info(mtk, udev, sch_ep->ep);
+               struct xhci_ep_ctx *ep_ctx;
+               struct usb_host_endpoint *ep = sch_ep->ep;
+               unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);
 
-               ret = check_sch_bw(sch_bw, sch_ep);
+               ret = check_sch_bw(sch_ep);
                if (ret) {
                        xhci_err(xhci, "Not enough bandwidth! (%s)\n",
                                 sch_error_string(-ret));
                        return -ENOSPC;
                }
-       }
-
-       list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) {
-               struct xhci_ep_ctx *ep_ctx;
-               struct usb_host_endpoint *ep = sch_ep->ep;
-               unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);
-
-               sch_bw = get_bw_info(mtk, udev, ep);
-               list_move_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
 
                ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
                ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(sch_ep->pkts)
                        sch_ep->offset, sch_ep->repeat);
        }
 
-       return xhci_check_bandwidth(hcd, udev);
+       ret = xhci_check_bandwidth(hcd, udev);
+       if (!ret)
+               INIT_LIST_HEAD(&mtk->bw_ep_chk_list);
+
+       return ret;
 }
 
 void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       struct mu3h_sch_bw_info *sch_bw;
        struct mu3h_sch_ep_info *sch_ep, *tmp;
 
        xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev));
 
-       list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) {
-               sch_bw = get_bw_info(mtk, udev, sch_ep->ep);
-               destroy_sch_ep(udev, sch_bw, sch_ep);
-       }
+       list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint)
+               destroy_sch_ep(mtk, udev, sch_ep);
 
        xhci_reset_bandwidth(hcd, udev);
 }
        if (ret)
                return ret;
 
-       if (ep->hcpriv)
-               drop_ep_quirk(hcd, udev, ep);
+       drop_ep_quirk(hcd, udev, ep);
 
        return 0;
 }