interval /= 1000;
}
- snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s",
+ snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s\n",
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 || tt->nr_eps > 0)
+ if (!tt || !list_empty(&tt->ep_list))
return; /* never allocated , or still in use*/
*ptt = NULL;
}
}
-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)
+static struct mu3h_sch_ep_info *create_sch_ep(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)
- tt->nr_eps++;
- else if (!WARN_ONCE(tt->nr_eps < 1, "unbalanced sch_tt's ep count"))
- tt->nr_eps--;
+ list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
+ else
+ list_del(&sch_ep->tt_endpoint);
}
static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw,
return boundary;
}
-static int check_sch_bw(struct mu3h_sch_ep_info *sch_ep)
+static int check_sch_bw(struct mu3h_sch_bw_info *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 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)
+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)
- load_ep_bw(sch_ep->bw_info, sch_ep, false);
+ load_ep_bw(sch_bw, 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_device *udev,
- struct usb_host_endpoint *ep)
+static bool need_bw_sch(struct usb_host_endpoint *ep,
+ enum usb_device_speed speed, int has_tt)
{
- 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(udev->speed) && !has_tt)
+ if (is_fs_or_ls(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 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;
+ int i;
/* 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);
- if (!need_bw_sch(udev, ep)) {
+ xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
+
+ if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) {
/*
* 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;
}
- xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
-
- sch_ep = create_sch_ep(mtk, udev, ep, ep_ctx);
+ sch_ep = create_sch_ep(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 mu3h_sch_ep_info *sch_ep;
- void *key = ep;
+ struct xhci_virt_device *virt_dev;
+ struct mu3h_sch_bw_info *sch_bw;
+ struct mu3h_sch_ep_info *sch_ep, *tmp;
- if (!need_bw_sch(udev, ep))
- return;
+ virt_dev = xhci->devs[udev->slot_id];
xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed));
- 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);
+ if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info))
+ return;
+
+ sch_bw = get_bw_info(mtk, udev, ep);
+
+ 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;
+ }
+ }
}
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_ep_info *sch_ep;
+ struct mu3h_sch_bw_info *sch_bw;
+ struct mu3h_sch_ep_info *sch_ep, *tmp;
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) {
- 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, sch_ep->ep);
- ret = check_sch_bw(sch_ep);
+ ret = check_sch_bw(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);
}
- ret = xhci_check_bandwidth(hcd, udev);
- if (!ret)
- INIT_LIST_HEAD(&mtk->bw_ep_chk_list);
-
- return ret;
+ return xhci_check_bandwidth(hcd, udev);
}
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)
- destroy_sch_ep(mtk, udev, sch_ep);
+ 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);
+ }
xhci_reset_bandwidth(hcd, udev);
}
if (ret)
return ret;
- drop_ep_quirk(hcd, udev, ep);
+ if (ep->hcpriv)
+ drop_ep_quirk(hcd, udev, ep);
return 0;
}