Message ID | 20210208032716.20247-1-chunfeng.yun@mediatek.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [RFC,v4] usb: xhci-mtk: improve bandwidth scheduling with TT | expand |
On Mon, Feb 8, 2021 at 11:27 AM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote: > > 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: 08e469de87a2 ("usb: xhci-mtk: supports bandwidth scheduling with multi-TT") > Signed-off-by: Yaqii Wu <yaqii.wu@mediatek.com> > Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com> Tested-by: Ikjoon Jang <ikjn@chromium.org> > --- > drivers/usb/host/xhci-mtk-sch.c | 270 +++++++++++++++++++++++--------- > drivers/usb/host/xhci-mtk.h | 8 +- > 2 files changed, 201 insertions(+), 77 deletions(-) > > diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c > index b45e5bf08997..f3cdfcf4e5bf 100644 > --- a/drivers/usb/host/xhci-mtk-sch.c > +++ b/drivers/usb/host/xhci-mtk-sch.c > @@ -32,6 +32,35 @@ > #define EP_BOFFSET(p) ((p) & 0x3fff) > #define EP_BREPEAT(p) (((p) & 0x7fff) << 16) > > +enum mtk_sch_err_type { > + SCH_SUCCESS = 0, > + SCH_ERR_Y6, > + SCH_SS_OVERLAP, > + SCH_CS_OVERFLOW, > + SCH_BW_OVERFLOW, > + SCH_FIXME, > +}; > + > +static char *sch_error_string(enum mtk_sch_err_type error) > +{ > + switch (error) { > + case SCH_SUCCESS: > + return "Success"; > + case SCH_ERR_Y6: > + return "Can't schedule Start-Split in Y6"; > + case SCH_SS_OVERLAP: > + return "Can't find a suitable Start-Split location"; > + case SCH_CS_OVERFLOW: > + return "The last Complete-Split is greater than 7"; > + case SCH_BW_OVERFLOW: > + return "Bandwidth exceeds the max limit"; > + case SCH_FIXME: > + return "FIXME, to be resolved"; > + default: > + return "Unknown error type"; > + } > +} > + > static int is_fs_or_ls(enum usb_device_speed speed) > { > return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW; > @@ -81,11 +110,22 @@ static u32 get_esit(struct xhci_ep_ctx *ep_ctx) > return esit; > } > > +static u32 get_bw_boundary(enum usb_device_speed speed) > +{ > + switch (speed) { > + case USB_SPEED_SUPER_PLUS: > + return SSP_BW_BOUNDARY; > + case USB_SPEED_SUPER: > + return SS_BW_BOUNDARY; > + default: > + return HS_BW_BOUNDARY; > + } > +} > + > static struct mu3h_sch_tt *find_tt(struct usb_device *udev) > { > struct usb_tt *utt = udev->tt; > struct mu3h_sch_tt *tt, **tt_index, **ptt; > - unsigned int port; > bool allocated_index = false; > > if (!utt) > @@ -107,10 +147,9 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) > utt->hcpriv = tt_index; > allocated_index = true; > } > - port = udev->ttport - 1; > - ptt = &tt_index[port]; > + > + ptt = &tt_index[udev->ttport - 1]; > } else { > - port = 0; > ptt = (struct mu3h_sch_tt **) &utt->hcpriv; > } > > @@ -125,8 +164,7 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) > return ERR_PTR(-ENOMEM); > } > INIT_LIST_HEAD(&tt->ep_list); > - tt->usb_tt = utt; > - tt->tt_port = port; > + > *ptt = tt; > } > > @@ -206,6 +244,15 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, > return sch_ep; > } > > +static void delete_sch_ep(struct usb_device *udev, struct mu3h_sch_ep_info *sch_ep) > +{ > + if (sch_ep->sch_tt) > + drop_tt(udev); > + > + list_del(&sch_ep->endpoint); > + kfree(sch_ep); > +} > + > static void setup_sch_info(struct usb_device *udev, > struct xhci_ep_ctx *ep_ctx, struct mu3h_sch_ep_info *sch_ep) > { > @@ -375,21 +422,55 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, > sch_ep->bw_budget_table[j]; > } > } > - sch_ep->allocated = used; > } > > -static int check_sch_tt(struct usb_device *udev, > - struct mu3h_sch_ep_info *sch_ep, u32 offset) > +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, base; > + u32 i, j; > + u32 tmp; > + > + 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 us. As described in the figure below. > + */ > + if (sch_ep->ep_type == ISOC_OUT_EP) { > + for (j = 0; j < sch_ep->num_budget_microframes; j++) { > + tmp = tt->fs_bus_bw[base + 1 + j] > + + sch_ep->bw_cost_per_microframe; > + > + if (tmp > FS_PAYLOAD_MAX) > + return SCH_BW_OVERFLOW; > + } > + } else { > + for (j = 0; j < sch_ep->cs_count; j++) { > + tmp = tt->fs_bus_bw[base + 1 + j] > + + sch_ep->bw_cost_per_microframe; > + > + if (tmp > FS_PAYLOAD_MAX) > + return SCH_BW_OVERFLOW; > + } > + } > + } > + return SCH_SUCCESS; > +} > + > +static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, int offset) > { > struct mu3h_sch_tt *tt = sch_ep->sch_tt; > u32 extra_cs_count; > - u32 fs_budget_start; > u32 start_ss, last_ss; > u32 start_cs, last_cs; > - int i; > + u32 i; > > start_ss = offset % 8; > - fs_budget_start = (start_ss + 1) % 8; > > if (sch_ep->ep_type == ISOC_OUT_EP) { > last_ss = start_ss + sch_ep->cs_count - 1; > @@ -399,12 +480,12 @@ static int check_sch_tt(struct usb_device *udev, > * must never schedule Start-Split in Y6 > */ > if (!(start_ss == 7 || last_ss < 6)) > - return -ERANGE; > - > - for (i = 0; i < sch_ep->cs_count; i++) > - if (test_bit(offset + i, tt->split_bit_map)) > - return -ERANGE; > + return SCH_ERR_Y6; > > + for (i = 0; i < sch_ep->cs_count; i++) { > + if (test_bit(offset + i, tt->ss_bit_map)) > + return SCH_SS_OVERLAP; > + } > } else { > u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); > > @@ -413,27 +494,36 @@ static int check_sch_tt(struct usb_device *udev, > * must never schedule Start-Split in Y6 > */ > if (start_ss == 6) > - return -ERANGE; > + return SCH_ERR_Y6; > > /* one uframe for ss + one uframe for idle */ > start_cs = (start_ss + 2) % 8; > last_cs = start_cs + cs_count - 1; > > if (last_cs > 7) > - return -ERANGE; > + return SCH_CS_OVERFLOW; > > + /* > + * usb_20 spec section11.18, the bottom of page 378: > + * For interrupt endpoints, the maximum size of the LS/FS > + * transaction guarantees that it can never require more than > + * two complete-split transactions. > + */ > if (sch_ep->ep_type == ISOC_IN_EP) > extra_cs_count = (last_cs == 7) ? 1 : 2; > else /* ep_type : INTR IN / INTR OUT */ > - extra_cs_count = (fs_budget_start == 6) ? 1 : 2; > + extra_cs_count = 1; > > cs_count += extra_cs_count; > if (cs_count > 7) > cs_count = 7; /* HW limit */ > > - for (i = 0; i < cs_count + 2; i++) { > - if (test_bit(offset + i, tt->split_bit_map)) > - return -ERANGE; > + if (test_bit(offset, tt->ss_bit_map)) > + return SCH_SS_OVERLAP; > + > + if (sch_ep->ep_type == INT_OUT_EP) { > + if (test_bit(offset, tt->idle_bit_map)) > + return SCH_FIXME; > } > > sch_ep->cs_count = cs_count; > @@ -448,41 +538,88 @@ 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) > +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 i, j; > + u32 i, j; > > num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; > + > 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); > + > + if (sch_ep->ep_type == ISOC_OUT_EP) { > + for (j = 0; j < sch_ep->num_budget_microframes; j++) { > + if (used) { > + set_bit(base + j, tt->ss_bit_map); > + tt->fs_bus_bw[base + 1 + j] += > + sch_ep->bw_cost_per_microframe; > + } else { > + clear_bit(base + j, tt->ss_bit_map); > + tt->fs_bus_bw[base + 1 + j] -= > + sch_ep->bw_cost_per_microframe; > + } > + } > + } else { > + if (used) > + set_bit(base, tt->ss_bit_map); > + else > + clear_bit(base, tt->ss_bit_map); > + > + if (sch_ep->ep_type == INT_OUT_EP) { > + if (used) > + set_bit(base + 1, tt->idle_bit_map); > + else > + clear_bit(base + 1, tt->idle_bit_map); > + } > + > + for (j = 0; j < sch_ep->cs_count; j++) { > + if (used) > + tt->fs_bus_bw[base + 1 + j] += > + sch_ep->bw_cost_per_microframe; > + else > + tt->fs_bus_bw[base + 1 + j] -= > + sch_ep->bw_cost_per_microframe; > + } > + } > + } > +} > + > +static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw, > + struct mu3h_sch_ep_info *sch_ep, bool loaded) > +{ > + struct mu3h_sch_tt *tt = sch_ep->sch_tt; > + > + if (sch_ep->sch_tt) { > + update_sch_tt(sch_ep, loaded); > + if (loaded) > + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); > + else > + list_del(&sch_ep->tt_endpoint); > } > > - list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); > + /* update bus bandwidth info */ > + update_bus_bw(sch_bw, sch_ep, loaded); > + sch_ep->allocated = loaded; > + return 0; > } > > static int check_sch_bw(struct usb_device *udev, > struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) > { > u32 offset; > - u32 esit; > u32 min_bw; > u32 min_index; > u32 worst_bw; > u32 bw_boundary; > + u32 boundary; > u32 min_num_budget; > u32 min_cs_count; > - bool tt_offset_ok = false; > - int ret; > - > - esit = sch_ep->esit; > + int ret = 0; > > /* > * Search through all possible schedule microframes. > @@ -492,16 +629,20 @@ static int check_sch_bw(struct usb_device *udev, > min_index = 0; > min_cs_count = sch_ep->cs_count; > min_num_budget = sch_ep->num_budget_microframes; > - for (offset = 0; offset < esit; offset++) { > - if (is_fs_or_ls(udev->speed)) { > - ret = check_sch_tt(udev, sch_ep, offset); > - if (ret) > + > + if (is_fs_or_ls(udev->speed) && sch_ep->ep_type != ISOC_OUT_EP) > + boundary = sch_ep->esit + 1; > + else > + boundary = sch_ep->esit; > + > + for (offset = 0; offset < sch_ep->esit; offset++) { > + if (sch_ep->sch_tt) { > + ret = check_sch_tt(sch_ep, offset); > + if (ret != SCH_SUCCESS) > continue; > - else > - tt_offset_ok = true; > } > > - if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) > + if ((offset + sch_ep->num_budget_microframes) > boundary) > break; > > worst_bw = get_max_bw(sch_bw, sch_ep, offset); > @@ -515,33 +656,22 @@ static int check_sch_bw(struct usb_device *udev, > break; > } > > - if (udev->speed == USB_SPEED_SUPER_PLUS) > - bw_boundary = SSP_BW_BOUNDARY; > - else if (udev->speed == USB_SPEED_SUPER) > - bw_boundary = SS_BW_BOUNDARY; > - else > - bw_boundary = HS_BW_BOUNDARY; > - > + bw_boundary = get_bw_boundary(udev->speed); > /* check bandwidth */ > - if (min_bw > bw_boundary) > + if (min_bw > bw_boundary) { > + if (ret) { > + dev_err(&udev->dev, "%s %s\n", __func__, > + sch_error_string(ret)); > + return -ret; > + } > + > return -ERANGE; > + } > > sch_ep->offset = min_index; > sch_ep->cs_count = min_cs_count; > sch_ep->num_budget_microframes = min_num_budget; > - > - if (is_fs_or_ls(udev->speed)) { > - /* all offset for tt is not ok*/ > - if (!tt_offset_ok) > - return -ERANGE; > - > - update_sch_tt(udev, sch_ep); > - } > - > - /* update bus bandwidth info */ > - update_bus_bw(sch_bw, sch_ep, 1); > - > - return 0; > + return load_ep_bw(sch_bw, sch_ep, true); > } > > static void destroy_sch_ep(struct usb_device *udev, > @@ -549,23 +679,17 @@ static void destroy_sch_ep(struct usb_device *udev, > { > /* only release ep bw check passed by check_sch_bw() */ > if (sch_ep->allocated) > - update_bus_bw(sch_bw, sch_ep, 0); > + load_ep_bw(sch_bw, sch_ep, false); > > - list_del(&sch_ep->endpoint); > - > - if (sch_ep->sch_tt) { > - list_del(&sch_ep->tt_endpoint); > - drop_tt(udev); > - } > - kfree(sch_ep); > + delete_sch_ep(udev, sch_ep); > } > > static bool need_bw_sch(struct usb_host_endpoint *ep, > enum usb_device_speed speed, int has_tt) > { > /* only for periodic endpoints */ > - if (usb_endpoint_xfer_control(&ep->desc) > - || usb_endpoint_xfer_bulk(&ep->desc)) > + if (usb_endpoint_xfer_control(&ep->desc) || > + usb_endpoint_xfer_bulk(&ep->desc)) > return false; > > /* > diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h > index cbb09dfea62e..81e9b56958c5 100644 > --- a/drivers/usb/host/xhci-mtk.h > +++ b/drivers/usb/host/xhci-mtk.h > @@ -26,10 +26,10 @@ > * @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); > + DECLARE_BITMAP(idle_bit_map, XHCI_MTK_MAX_ESIT); > + u32 fs_bus_bw[XHCI_MTK_MAX_ESIT + 1]; > struct list_head ep_list; > - struct usb_tt *usb_tt; > - int tt_port; > }; > > /** > @@ -84,9 +84,9 @@ struct mu3h_sch_ep_info { > struct list_head endpoint; > struct list_head tt_endpoint; > struct mu3h_sch_tt *sch_tt; > + struct usb_host_endpoint *ep; > u32 ep_type; > u32 maxpkt; > - void *ep; > bool allocated; > /* > * mtk xHCI scheduling information put into reserved DWs > -- > 2.18.0
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index b45e5bf08997..f3cdfcf4e5bf 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -32,6 +32,35 @@ #define EP_BOFFSET(p) ((p) & 0x3fff) #define EP_BREPEAT(p) (((p) & 0x7fff) << 16) +enum mtk_sch_err_type { + SCH_SUCCESS = 0, + SCH_ERR_Y6, + SCH_SS_OVERLAP, + SCH_CS_OVERFLOW, + SCH_BW_OVERFLOW, + SCH_FIXME, +}; + +static char *sch_error_string(enum mtk_sch_err_type error) +{ + switch (error) { + case SCH_SUCCESS: + return "Success"; + case SCH_ERR_Y6: + return "Can't schedule Start-Split in Y6"; + case SCH_SS_OVERLAP: + return "Can't find a suitable Start-Split location"; + case SCH_CS_OVERFLOW: + return "The last Complete-Split is greater than 7"; + case SCH_BW_OVERFLOW: + return "Bandwidth exceeds the max limit"; + case SCH_FIXME: + return "FIXME, to be resolved"; + default: + return "Unknown error type"; + } +} + static int is_fs_or_ls(enum usb_device_speed speed) { return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW; @@ -81,11 +110,22 @@ static u32 get_esit(struct xhci_ep_ctx *ep_ctx) return esit; } +static u32 get_bw_boundary(enum usb_device_speed speed) +{ + switch (speed) { + case USB_SPEED_SUPER_PLUS: + return SSP_BW_BOUNDARY; + case USB_SPEED_SUPER: + return SS_BW_BOUNDARY; + default: + return HS_BW_BOUNDARY; + } +} + static struct mu3h_sch_tt *find_tt(struct usb_device *udev) { struct usb_tt *utt = udev->tt; struct mu3h_sch_tt *tt, **tt_index, **ptt; - unsigned int port; bool allocated_index = false; if (!utt) @@ -107,10 +147,9 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) utt->hcpriv = tt_index; allocated_index = true; } - port = udev->ttport - 1; - ptt = &tt_index[port]; + + ptt = &tt_index[udev->ttport - 1]; } else { - port = 0; ptt = (struct mu3h_sch_tt **) &utt->hcpriv; } @@ -125,8 +164,7 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) return ERR_PTR(-ENOMEM); } INIT_LIST_HEAD(&tt->ep_list); - tt->usb_tt = utt; - tt->tt_port = port; + *ptt = tt; } @@ -206,6 +244,15 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, return sch_ep; } +static void delete_sch_ep(struct usb_device *udev, struct mu3h_sch_ep_info *sch_ep) +{ + if (sch_ep->sch_tt) + drop_tt(udev); + + list_del(&sch_ep->endpoint); + kfree(sch_ep); +} + static void setup_sch_info(struct usb_device *udev, struct xhci_ep_ctx *ep_ctx, struct mu3h_sch_ep_info *sch_ep) { @@ -375,21 +422,55 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, sch_ep->bw_budget_table[j]; } } - sch_ep->allocated = used; } -static int check_sch_tt(struct usb_device *udev, - struct mu3h_sch_ep_info *sch_ep, u32 offset) +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, base; + u32 i, j; + u32 tmp; + + 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 us. As described in the figure below. + */ + if (sch_ep->ep_type == ISOC_OUT_EP) { + for (j = 0; j < sch_ep->num_budget_microframes; j++) { + tmp = tt->fs_bus_bw[base + 1 + j] + + sch_ep->bw_cost_per_microframe; + + if (tmp > FS_PAYLOAD_MAX) + return SCH_BW_OVERFLOW; + } + } else { + for (j = 0; j < sch_ep->cs_count; j++) { + tmp = tt->fs_bus_bw[base + 1 + j] + + sch_ep->bw_cost_per_microframe; + + if (tmp > FS_PAYLOAD_MAX) + return SCH_BW_OVERFLOW; + } + } + } + return SCH_SUCCESS; +} + +static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, int offset) { struct mu3h_sch_tt *tt = sch_ep->sch_tt; u32 extra_cs_count; - u32 fs_budget_start; u32 start_ss, last_ss; u32 start_cs, last_cs; - int i; + u32 i; start_ss = offset % 8; - fs_budget_start = (start_ss + 1) % 8; if (sch_ep->ep_type == ISOC_OUT_EP) { last_ss = start_ss + sch_ep->cs_count - 1; @@ -399,12 +480,12 @@ static int check_sch_tt(struct usb_device *udev, * must never schedule Start-Split in Y6 */ if (!(start_ss == 7 || last_ss < 6)) - return -ERANGE; - - for (i = 0; i < sch_ep->cs_count; i++) - if (test_bit(offset + i, tt->split_bit_map)) - return -ERANGE; + return SCH_ERR_Y6; + for (i = 0; i < sch_ep->cs_count; i++) { + if (test_bit(offset + i, tt->ss_bit_map)) + return SCH_SS_OVERLAP; + } } else { u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); @@ -413,27 +494,36 @@ static int check_sch_tt(struct usb_device *udev, * must never schedule Start-Split in Y6 */ if (start_ss == 6) - return -ERANGE; + return SCH_ERR_Y6; /* one uframe for ss + one uframe for idle */ start_cs = (start_ss + 2) % 8; last_cs = start_cs + cs_count - 1; if (last_cs > 7) - return -ERANGE; + return SCH_CS_OVERFLOW; + /* + * usb_20 spec section11.18, the bottom of page 378: + * For interrupt endpoints, the maximum size of the LS/FS + * transaction guarantees that it can never require more than + * two complete-split transactions. + */ if (sch_ep->ep_type == ISOC_IN_EP) extra_cs_count = (last_cs == 7) ? 1 : 2; else /* ep_type : INTR IN / INTR OUT */ - extra_cs_count = (fs_budget_start == 6) ? 1 : 2; + extra_cs_count = 1; cs_count += extra_cs_count; if (cs_count > 7) cs_count = 7; /* HW limit */ - for (i = 0; i < cs_count + 2; i++) { - if (test_bit(offset + i, tt->split_bit_map)) - return -ERANGE; + if (test_bit(offset, tt->ss_bit_map)) + return SCH_SS_OVERLAP; + + if (sch_ep->ep_type == INT_OUT_EP) { + if (test_bit(offset, tt->idle_bit_map)) + return SCH_FIXME; } sch_ep->cs_count = cs_count; @@ -448,41 +538,88 @@ 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) +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 i, j; + u32 i, j; num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; + 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); + + if (sch_ep->ep_type == ISOC_OUT_EP) { + for (j = 0; j < sch_ep->num_budget_microframes; j++) { + if (used) { + set_bit(base + j, tt->ss_bit_map); + tt->fs_bus_bw[base + 1 + j] += + sch_ep->bw_cost_per_microframe; + } else { + clear_bit(base + j, tt->ss_bit_map); + tt->fs_bus_bw[base + 1 + j] -= + sch_ep->bw_cost_per_microframe; + } + } + } else { + if (used) + set_bit(base, tt->ss_bit_map); + else + clear_bit(base, tt->ss_bit_map); + + if (sch_ep->ep_type == INT_OUT_EP) { + if (used) + set_bit(base + 1, tt->idle_bit_map); + else + clear_bit(base + 1, tt->idle_bit_map); + } + + for (j = 0; j < sch_ep->cs_count; j++) { + if (used) + tt->fs_bus_bw[base + 1 + j] += + sch_ep->bw_cost_per_microframe; + else + tt->fs_bus_bw[base + 1 + j] -= + sch_ep->bw_cost_per_microframe; + } + } + } +} + +static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw, + struct mu3h_sch_ep_info *sch_ep, bool loaded) +{ + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + + if (sch_ep->sch_tt) { + update_sch_tt(sch_ep, loaded); + if (loaded) + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); + else + list_del(&sch_ep->tt_endpoint); } - list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); + /* update bus bandwidth info */ + update_bus_bw(sch_bw, sch_ep, loaded); + sch_ep->allocated = loaded; + return 0; } static int check_sch_bw(struct usb_device *udev, struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) { u32 offset; - u32 esit; u32 min_bw; u32 min_index; u32 worst_bw; u32 bw_boundary; + u32 boundary; u32 min_num_budget; u32 min_cs_count; - bool tt_offset_ok = false; - int ret; - - esit = sch_ep->esit; + int ret = 0; /* * Search through all possible schedule microframes. @@ -492,16 +629,20 @@ static int check_sch_bw(struct usb_device *udev, min_index = 0; min_cs_count = sch_ep->cs_count; min_num_budget = sch_ep->num_budget_microframes; - for (offset = 0; offset < esit; offset++) { - if (is_fs_or_ls(udev->speed)) { - ret = check_sch_tt(udev, sch_ep, offset); - if (ret) + + if (is_fs_or_ls(udev->speed) && sch_ep->ep_type != ISOC_OUT_EP) + boundary = sch_ep->esit + 1; + else + boundary = sch_ep->esit; + + for (offset = 0; offset < sch_ep->esit; offset++) { + if (sch_ep->sch_tt) { + ret = check_sch_tt(sch_ep, offset); + if (ret != SCH_SUCCESS) continue; - else - tt_offset_ok = true; } - if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) + if ((offset + sch_ep->num_budget_microframes) > boundary) break; worst_bw = get_max_bw(sch_bw, sch_ep, offset); @@ -515,33 +656,22 @@ static int check_sch_bw(struct usb_device *udev, break; } - if (udev->speed == USB_SPEED_SUPER_PLUS) - bw_boundary = SSP_BW_BOUNDARY; - else if (udev->speed == USB_SPEED_SUPER) - bw_boundary = SS_BW_BOUNDARY; - else - bw_boundary = HS_BW_BOUNDARY; - + bw_boundary = get_bw_boundary(udev->speed); /* check bandwidth */ - if (min_bw > bw_boundary) + if (min_bw > bw_boundary) { + if (ret) { + dev_err(&udev->dev, "%s %s\n", __func__, + sch_error_string(ret)); + return -ret; + } + return -ERANGE; + } sch_ep->offset = min_index; sch_ep->cs_count = min_cs_count; sch_ep->num_budget_microframes = min_num_budget; - - if (is_fs_or_ls(udev->speed)) { - /* all offset for tt is not ok*/ - if (!tt_offset_ok) - return -ERANGE; - - update_sch_tt(udev, sch_ep); - } - - /* update bus bandwidth info */ - update_bus_bw(sch_bw, sch_ep, 1); - - return 0; + return load_ep_bw(sch_bw, sch_ep, true); } static void destroy_sch_ep(struct usb_device *udev, @@ -549,23 +679,17 @@ static void destroy_sch_ep(struct usb_device *udev, { /* only release ep bw check passed by check_sch_bw() */ if (sch_ep->allocated) - update_bus_bw(sch_bw, sch_ep, 0); + load_ep_bw(sch_bw, sch_ep, false); - list_del(&sch_ep->endpoint); - - if (sch_ep->sch_tt) { - list_del(&sch_ep->tt_endpoint); - drop_tt(udev); - } - kfree(sch_ep); + delete_sch_ep(udev, sch_ep); } static bool need_bw_sch(struct usb_host_endpoint *ep, enum usb_device_speed speed, int has_tt) { /* only for periodic endpoints */ - if (usb_endpoint_xfer_control(&ep->desc) - || usb_endpoint_xfer_bulk(&ep->desc)) + if (usb_endpoint_xfer_control(&ep->desc) || + usb_endpoint_xfer_bulk(&ep->desc)) return false; /* diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index cbb09dfea62e..81e9b56958c5 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -26,10 +26,10 @@ * @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); + DECLARE_BITMAP(idle_bit_map, XHCI_MTK_MAX_ESIT); + u32 fs_bus_bw[XHCI_MTK_MAX_ESIT + 1]; struct list_head ep_list; - struct usb_tt *usb_tt; - int tt_port; }; /** @@ -84,9 +84,9 @@ struct mu3h_sch_ep_info { struct list_head endpoint; struct list_head tt_endpoint; struct mu3h_sch_tt *sch_tt; + struct usb_host_endpoint *ep; u32 ep_type; u32 maxpkt; - void *ep; bool allocated; /* * mtk xHCI scheduling information put into reserved DWs