diff options
Diffstat (limited to 'drivers/usb/host/xhci-mtk-sch.c')
-rw-r--r-- | drivers/usb/host/xhci-mtk-sch.c | 180 |
1 files changed, 77 insertions, 103 deletions
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index cffcaf4dfa9f..134f4789bd89 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -80,7 +80,7 @@ decode_ep(struct usb_host_endpoint *ep, enum usb_device_speed speed) 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)), @@ -129,6 +129,10 @@ get_bw_info(struct xhci_hcd_mtk *mtk, struct usb_device *udev, int bw_index; virt_dev = xhci->devs[udev->slot_id]; + if (!virt_dev->real_port) { + WARN_ONCE(1, "%s invalid real_port\n", dev_name(&udev->dev)); + return NULL; + } if (udev->speed >= USB_SPEED_SUPER) { if (usb_endpoint_dir_out(&ep->desc)) @@ -236,14 +240,20 @@ static void drop_tt(struct usb_device *udev) } } -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_bw_info *bw_info; struct mu3h_sch_tt *tt = NULL; u32 len_bw_budget_table; size_t mem_size; + 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; else if ((udev->speed >= USB_SPEED_SUPER) @@ -266,11 +276,13 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, } } + 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); + INIT_HLIST_NODE(&sch_ep->hentry); return sch_ep; } @@ -297,6 +309,7 @@ static void setup_sch_info(struct xhci_ep_ctx *ep_ctx, CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); sch_ep->esit = get_esit(ep_ctx); + sch_ep->num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; sch_ep->ep_type = ep_type; sch_ep->maxpkt = maxpkt; sch_ep->offset = 0; @@ -401,19 +414,16 @@ static void setup_sch_info(struct xhci_ep_ctx *ep_ctx, static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep, u32 offset) { - u32 num_esit; u32 max_bw = 0; u32 bw; - int i; - int j; + int i, j, k; - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { u32 base = offset + i * sch_ep->esit; for (j = 0; j < sch_ep->num_budget_microframes; j++) { - bw = sch_bw->bus_bw[base + j] + - sch_ep->bw_budget_table[j]; + k = XHCI_MTK_BW_INDEX(base + j); + bw = sch_bw->bus_bw[k] + sch_ep->bw_budget_table[j]; if (bw > max_bw) max_bw = bw; } @@ -424,21 +434,17 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw, static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep, bool used) { - u32 num_esit; u32 base; - int i; - int j; + int i, j, k; - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { base = sch_ep->offset + i * sch_ep->esit; for (j = 0; j < sch_ep->num_budget_microframes; j++) { + k = XHCI_MTK_BW_INDEX(base + j); if (used) - sch_bw->bus_bw[base + j] += - sch_ep->bw_budget_table[j]; + sch_bw->bus_bw[k] += sch_ep->bw_budget_table[j]; else - sch_bw->bus_bw[base + j] -= - sch_ep->bw_budget_table[j]; + sch_bw->bus_bw[k] -= sch_ep->bw_budget_table[j]; } } } @@ -446,20 +452,20 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, 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; + u32 tmp; int base; - int i, j; + int i, j, k; - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->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; + for (j = 0; j < sch_ep->num_budget_microframes; j++) { + k = XHCI_MTK_BW_INDEX(base + j); + tmp = tt->fs_bus_bw[k] + sch_ep->bw_budget_table[j]; if (tmp > FS_PAYLOAD_MAX) return -ESCH_BW_OVERFLOW; } @@ -533,22 +539,19 @@ static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset) static void update_sch_tt(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 i, j; - - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - - if (used) - bw_updated = sch_ep->bw_cost_per_microframe; - else - bw_updated = -sch_ep->bw_cost_per_microframe; + u32 base; + int i, j, k; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { base = sch_ep->offset + i * sch_ep->esit; - for (j = 0; j < sch_ep->cs_count; j++) - tt->fs_bus_bw[base + j] += bw_updated; + for (j = 0; j < sch_ep->num_budget_microframes; j++) { + k = XHCI_MTK_BW_INDEX(base + j); + if (used) + tt->fs_bus_bw[k] += sch_ep->bw_budget_table[j]; + else + tt->fs_bus_bw[k] -= sch_ep->bw_budget_table[j]; + } } if (used) @@ -570,25 +573,9 @@ static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw, return 0; } -static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep) -{ - u32 boundary = sch_ep->esit; - - if (sch_ep->sch_tt) { /* LS/FS with TT */ - /* tune for CS */ - if (sch_ep->ep_type != ISOC_OUT_EP) - boundary++; - else if (boundary > 1) /* normally esit >= 8 for FS/LS */ - boundary--; - } - - 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) { - const u32 esit_boundary = get_esit_boundary(sch_ep); + struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info; const u32 bw_boundary = get_bw_boundary(sch_ep->speed); u32 offset; u32 worst_bw; @@ -605,9 +592,6 @@ static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw, if (ret) continue; - if ((offset + sch_ep->num_budget_microframes) > esit_boundary) - break; - worst_bw = get_max_bw(sch_bw, sch_ep, offset); if (worst_bw > bw_boundary) continue; @@ -633,23 +617,26 @@ static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw, 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 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); + hlist_del(&sch_ep->hentry); 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)) @@ -660,7 +647,7 @@ static bool need_bw_sch(struct usb_host_endpoint *ep, * 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 */ @@ -675,7 +662,6 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd); struct mu3h_sch_bw_info *sch_array; int num_usb_bus; - int i; /* ss IN and OUT are separated */ num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports; @@ -684,12 +670,10 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) 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); + hash_init(mtk->sch_ep_hash); return 0; } @@ -713,9 +697,7 @@ static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, 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 @@ -727,13 +709,16 @@ static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, 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; setup_sch_info(ep_ctx, sch_ep); list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_chk_list); + hash_add(mtk->sch_ep_hash, &sch_ep->hentry, (unsigned long)ep); return 0; } @@ -743,22 +728,18 @@ static void drop_ep_quirk(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; - 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; + struct hlist_node *hn; - 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_err(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); - list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) { + hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep, + hn, hentry, (unsigned long)ep) { if (sch_ep->ep == ep) { - destroy_sch_ep(udev, sch_bw, sch_ep); + destroy_sch_ep(mtk, udev, sch_ep); break; } } @@ -769,30 +750,22 @@ 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) @@ -806,22 +779,23 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) 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); } |